Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

187 lines
6.5 KiB

;;; macrostep-c.el --- macrostep interface to C preprocessor
;; Copyright (C) 2015 Jon Oddie <j.j.oddie@gmail.com>
;; Author: Jon Oddie <j.j.oddie@gmail.com>
;; Maintainer: Jon Oddie <j.j.oddie@gmail.com>
;; Created: 27 November 2015
;; Updated: 27 November 2015
;; Version: 0.9
;; Keywords: c, languages, macro, debugging
;; Url: https://github.com/joddie/macrostep
;; This file is NOT part of GNU Emacs.
;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see `http://www.gnu.org/licenses/'.
;;; Commentary:
;; A thin wrapper around Emacs's built-in `cmacexp' library to provide
;; basic support for expanding C macros using the `macrostep' user
;; interface. To use, position point on a macro use in a C buffer and
;; type `M-x macrostep-expand'. The variables `c-macro-preprocessor'
;; and especially `c-macro-cppflags' may need to be set correctly for
;; accurate expansion.
;; This is fairly basic compared to the Emacs Lisp `macrostep'. In
;; particular, there is no step-by-step expansion, since C macros are
;; expanded in a single "cpp" pass, and no pretty-printing.
;; To hide the buffer containing "cpp" warnings (not recommended), you
;; could do something like:
;;
;; (push `(,(regexp-quote macrostep-c-warning-buffer)
;; (display-buffer-no-window))
;; display-buffer-alist)
;;; Code:
(require 'macrostep)
(require 'cmacexp)
(require 'cl-lib)
(eval-and-compile
(if (require 'subr-x nil t)
(defalias 'macrostep-c-string-trim 'string-trim)
(defun macrostep-c-string-trim (string)
(when (string-match "\\`[ \t\n\r]+" string)
(setq string (replace-match "" t t string)))
(when (string-match "[ \t\n\r]+\\'" string)
(setq string (replace-match "" t t string)))
string)))
(put 'macrostep-c-non-macro 'error-conditions
'(macrostep-c-non-macro error))
(put 'macrostep-c-non-macro 'error-message
"Text around point is not a macro call.")
(put 'macrostep-c-expansion-failed 'error-conditions
'(macrostep-c-expansion-failed error))
(put 'macrostep-c-expansion-failed 'error-message
"Macro-expansion failed.")
(defvar macrostep-c-warning-buffer "*Macroexpansion Warnings*")
;;;###autoload
(defun macrostep-c-mode-hook ()
(setq macrostep-sexp-bounds-function
#'macrostep-c-sexp-bounds)
(setq macrostep-sexp-at-point-function
#'macrostep-c-sexp-at-point)
(setq macrostep-environment-at-point-function
#'ignore)
(setq macrostep-expand-1-function
#'macrostep-c-expand-1)
(setq macrostep-print-function
#'macrostep-c-print-function)
(add-hook 'macrostep-mode-off-hook
#'macrostep-c-mode-off nil t))
(defun macrostep-c-mode-off (&rest ignore)
(when (derived-mode-p 'c-mode)
(let ((warning-window
(get-buffer-window macrostep-c-warning-buffer)))
(when warning-window
(quit-window nil warning-window)))))
;;;###autoload
(add-hook 'c-mode-hook #'macrostep-c-mode-hook)
(defun macrostep-c-sexp-bounds ()
(save-excursion
(cl-loop
(let ((region (macrostep-c-sexp-bounds-1)))
(cond
((null region)
(signal 'macrostep-c-non-macro nil))
((macrostep-c-expandable-p region)
(cl-return region))
(t
(condition-case nil
(progn
(backward-up-list)
(skip-syntax-backward "-"))
(scan-error
(signal 'macrostep-c-non-macro nil)))))))))
(defun macrostep-c-sexp-bounds-1 ()
(let ((region (bounds-of-thing-at-point 'symbol)))
(when region
(cl-destructuring-bind (symbol-start . symbol-end) region
(save-excursion
(goto-char symbol-end)
(if (looking-at "[[:space:]]*(")
(cons symbol-start (scan-sexps symbol-end 1))
region))))))
(defun macrostep-c-expandable-p (region)
(cl-destructuring-bind (start . end) region
(condition-case nil
(cl-destructuring-bind (expansion warnings)
(macrostep-c-expand-region start end)
(declare (ignore warnings))
(and (cl-plusp (length expansion))
(not (string= expansion (buffer-substring start end)))))
(macrostep-c-expansion-failed nil))))
(defun macrostep-c-sexp-at-point (start end)
(cons start end))
(defun macrostep-c-expand-1 (region _ignore)
(cl-destructuring-bind (start . end) region
(cl-destructuring-bind (expansion warnings)
(macrostep-c-expand-region start end)
(when (cl-plusp (length warnings))
(with-current-buffer
(get-buffer-create macrostep-c-warning-buffer)
(let ((inhibit-read-only t))
(erase-buffer)
(insert warnings)
(goto-char (point-min)))
(special-mode)
(display-buffer (current-buffer)
'(display-buffer-pop-up-window
(inhibit-same-window . t)
(allow-no-window . t)))))
expansion)))
(defun macrostep-c-expand-region (start end)
(let ((expansion
(condition-case nil
(c-macro-expansion start end
(concat c-macro-preprocessor " "
c-macro-cppflags))
(search-failed
(signal 'macrostep-c-expansion-failed nil)))))
(with-temp-buffer
(save-excursion
(insert expansion))
(when (looking-at (regexp-quote "/*"))
(search-forward "*/"))
(let ((warnings (buffer-substring (point-min) (point)))
(expansion (buffer-substring (point) (point-max))))
(mapcar #'macrostep-c-string-trim (list expansion warnings))))))
(defun macrostep-c-print-function (expansion &rest _ignore)
(with-temp-buffer
(insert expansion)
(let ((exit-code
(shell-command-on-region (point-min) (point-max) "indent" nil t)))
(when (zerop exit-code)
(setq expansion (macrostep-c-string-trim (buffer-string))))))
(insert expansion))
(provide 'macrostep-c)
;;; macrostep-c.el ends here