;;; 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
|