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

4 years ago
  1. ;;; macrostep-c.el --- macrostep interface to C preprocessor
  2. ;; Copyright (C) 2015 Jon Oddie <j.j.oddie@gmail.com>
  3. ;; Author: Jon Oddie <j.j.oddie@gmail.com>
  4. ;; Maintainer: Jon Oddie <j.j.oddie@gmail.com>
  5. ;; Created: 27 November 2015
  6. ;; Updated: 27 November 2015
  7. ;; Version: 0.9
  8. ;; Keywords: c, languages, macro, debugging
  9. ;; Url: https://github.com/joddie/macrostep
  10. ;; This file is NOT part of GNU Emacs.
  11. ;; This program is free software: you can redistribute it and/or
  12. ;; modify it under the terms of the GNU General Public License as
  13. ;; published by the Free Software Foundation, either version 3 of the
  14. ;; License, or (at your option) any later version.
  15. ;;
  16. ;; This program is distributed in the hope that it will be useful, but
  17. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  18. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. ;; General Public License for more details.
  20. ;;
  21. ;; You should have received a copy of the GNU General Public License
  22. ;; along with this program. If not, see `http://www.gnu.org/licenses/'.
  23. ;;; Commentary:
  24. ;; A thin wrapper around Emacs's built-in `cmacexp' library to provide
  25. ;; basic support for expanding C macros using the `macrostep' user
  26. ;; interface. To use, position point on a macro use in a C buffer and
  27. ;; type `M-x macrostep-expand'. The variables `c-macro-preprocessor'
  28. ;; and especially `c-macro-cppflags' may need to be set correctly for
  29. ;; accurate expansion.
  30. ;; This is fairly basic compared to the Emacs Lisp `macrostep'. In
  31. ;; particular, there is no step-by-step expansion, since C macros are
  32. ;; expanded in a single "cpp" pass, and no pretty-printing.
  33. ;; To hide the buffer containing "cpp" warnings (not recommended), you
  34. ;; could do something like:
  35. ;;
  36. ;; (push `(,(regexp-quote macrostep-c-warning-buffer)
  37. ;; (display-buffer-no-window))
  38. ;; display-buffer-alist)
  39. ;;; Code:
  40. (require 'macrostep)
  41. (require 'cmacexp)
  42. (require 'cl-lib)
  43. (eval-and-compile
  44. (if (require 'subr-x nil t)
  45. (defalias 'macrostep-c-string-trim 'string-trim)
  46. (defun macrostep-c-string-trim (string)
  47. (when (string-match "\\`[ \t\n\r]+" string)
  48. (setq string (replace-match "" t t string)))
  49. (when (string-match "[ \t\n\r]+\\'" string)
  50. (setq string (replace-match "" t t string)))
  51. string)))
  52. (put 'macrostep-c-non-macro 'error-conditions
  53. '(macrostep-c-non-macro error))
  54. (put 'macrostep-c-non-macro 'error-message
  55. "Text around point is not a macro call.")
  56. (put 'macrostep-c-expansion-failed 'error-conditions
  57. '(macrostep-c-expansion-failed error))
  58. (put 'macrostep-c-expansion-failed 'error-message
  59. "Macro-expansion failed.")
  60. (defvar macrostep-c-warning-buffer "*Macroexpansion Warnings*")
  61. ;;;###autoload
  62. (defun macrostep-c-mode-hook ()
  63. (setq macrostep-sexp-bounds-function
  64. #'macrostep-c-sexp-bounds)
  65. (setq macrostep-sexp-at-point-function
  66. #'macrostep-c-sexp-at-point)
  67. (setq macrostep-environment-at-point-function
  68. #'ignore)
  69. (setq macrostep-expand-1-function
  70. #'macrostep-c-expand-1)
  71. (setq macrostep-print-function
  72. #'macrostep-c-print-function)
  73. (add-hook 'macrostep-mode-off-hook
  74. #'macrostep-c-mode-off nil t))
  75. (defun macrostep-c-mode-off (&rest ignore)
  76. (when (derived-mode-p 'c-mode)
  77. (let ((warning-window
  78. (get-buffer-window macrostep-c-warning-buffer)))
  79. (when warning-window
  80. (quit-window nil warning-window)))))
  81. ;;;###autoload
  82. (add-hook 'c-mode-hook #'macrostep-c-mode-hook)
  83. (defun macrostep-c-sexp-bounds ()
  84. (save-excursion
  85. (cl-loop
  86. (let ((region (macrostep-c-sexp-bounds-1)))
  87. (cond
  88. ((null region)
  89. (signal 'macrostep-c-non-macro nil))
  90. ((macrostep-c-expandable-p region)
  91. (cl-return region))
  92. (t
  93. (condition-case nil
  94. (progn
  95. (backward-up-list)
  96. (skip-syntax-backward "-"))
  97. (scan-error
  98. (signal 'macrostep-c-non-macro nil)))))))))
  99. (defun macrostep-c-sexp-bounds-1 ()
  100. (let ((region (bounds-of-thing-at-point 'symbol)))
  101. (when region
  102. (cl-destructuring-bind (symbol-start . symbol-end) region
  103. (save-excursion
  104. (goto-char symbol-end)
  105. (if (looking-at "[[:space:]]*(")
  106. (cons symbol-start (scan-sexps symbol-end 1))
  107. region))))))
  108. (defun macrostep-c-expandable-p (region)
  109. (cl-destructuring-bind (start . end) region
  110. (condition-case nil
  111. (cl-destructuring-bind (expansion warnings)
  112. (macrostep-c-expand-region start end)
  113. (declare (ignore warnings))
  114. (and (cl-plusp (length expansion))
  115. (not (string= expansion (buffer-substring start end)))))
  116. (macrostep-c-expansion-failed nil))))
  117. (defun macrostep-c-sexp-at-point (start end)
  118. (cons start end))
  119. (defun macrostep-c-expand-1 (region _ignore)
  120. (cl-destructuring-bind (start . end) region
  121. (cl-destructuring-bind (expansion warnings)
  122. (macrostep-c-expand-region start end)
  123. (when (cl-plusp (length warnings))
  124. (with-current-buffer
  125. (get-buffer-create macrostep-c-warning-buffer)
  126. (let ((inhibit-read-only t))
  127. (erase-buffer)
  128. (insert warnings)
  129. (goto-char (point-min)))
  130. (special-mode)
  131. (display-buffer (current-buffer)
  132. '(display-buffer-pop-up-window
  133. (inhibit-same-window . t)
  134. (allow-no-window . t)))))
  135. expansion)))
  136. (defun macrostep-c-expand-region (start end)
  137. (let ((expansion
  138. (condition-case nil
  139. (c-macro-expansion start end
  140. (concat c-macro-preprocessor " "
  141. c-macro-cppflags))
  142. (search-failed
  143. (signal 'macrostep-c-expansion-failed nil)))))
  144. (with-temp-buffer
  145. (save-excursion
  146. (insert expansion))
  147. (when (looking-at (regexp-quote "/*"))
  148. (search-forward "*/"))
  149. (let ((warnings (buffer-substring (point-min) (point)))
  150. (expansion (buffer-substring (point) (point-max))))
  151. (mapcar #'macrostep-c-string-trim (list expansion warnings))))))
  152. (defun macrostep-c-print-function (expansion &rest _ignore)
  153. (with-temp-buffer
  154. (insert expansion)
  155. (let ((exit-code
  156. (shell-command-on-region (point-min) (point-max) "indent" nil t)))
  157. (when (zerop exit-code)
  158. (setq expansion (macrostep-c-string-trim (buffer-string))))))
  159. (insert expansion))
  160. (provide 'macrostep-c)
  161. ;;; macrostep-c.el ends here