Commit | Line | Data |
---|---|---|
4af1f509 | 1 | ;;; cmacexp.el --- expand C macros in a region |
c0274f38 | 2 | |
d733c5ec | 3 | ;; Copyright (C) 1992, 1994 Free Software Foundation, Inc. |
9750e079 | 4 | |
4af1f509 | 5 | ;; Author: Francesco Potorti` <pot@cnuce.cnr.it> |
b578f267 | 6 | ;; Version: $Id: cmacexp.el,v 1.20 1995/10/26 03:14:40 rms Exp erik $ |
4af1f509 | 7 | ;; Adapted-By: ESR |
e5167999 ER |
8 | ;; Keywords: c |
9 | ||
c17d15d0 JB |
10 | ;; This file is part of GNU Emacs. |
11 | ||
12 | ;; GNU Emacs is free software; you can redistribute it and/or modify | |
13 | ;; it under the terms of the GNU General Public License as published by | |
e5167999 | 14 | ;; the Free Software Foundation; either version 2, or (at your option) |
c17d15d0 JB |
15 | ;; any later version. |
16 | ||
17 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ;; GNU General Public License for more details. | |
21 | ||
22 | ;; You should have received a copy of the GNU General Public License | |
b578f267 EN |
23 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
24 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
25 | ;; Boston, MA 02111-1307, USA. | |
c17d15d0 | 26 | |
89fb82d6 | 27 | ;; USAGE ============================================================= |
e41b2db1 | 28 | |
074521be RS |
29 | ;; In C mode C-C C-e is bound to c-macro-expand. The result of the |
30 | ;; expansion is put in a separate buffer. A user option allows the | |
31 | ;; window displaying the buffer to be optimally sized. | |
e41b2db1 | 32 | ;; |
4af1f509 | 33 | ;; When called with a C-u prefix, c-macro-expand replaces the selected |
89fb82d6 | 34 | ;; region with the expansion. Both the preprocessor name and the |
074521be RS |
35 | ;; initial flag can be set by the user. If c-macro-prompt-flag is set |
36 | ;; to a non-nil value the user is offered to change the options to the | |
37 | ;; preprocessor each time c-macro-expand is invoked. Preprocessor | |
38 | ;; arguments default to the last ones entered. If c-macro-prompt-flag | |
39 | ;; is nil, one must use M-x set-variable to set a different value for | |
40 | ;; c-macro-cppflags. | |
4af1f509 RS |
41 | |
42 | ;; A c-macro-expansion function is provided for non-interactive use. | |
4af1f509 RS |
43 | |
44 | ;; INSTALLATION ====================================================== | |
45 | ||
074521be | 46 | ;; Put the following in your ~/.emacs file. |
4af1f509 | 47 | |
4af1f509 RS |
48 | ;; If you want the *Macroexpansion* window to be not higher than |
49 | ;; necessary: | |
074521be | 50 | ;;(setq c-macro-shrink-window-flag t) |
e41b2db1 | 51 | ;; |
4af1f509 RS |
52 | ;; If you use a preprocessor other than /lib/cpp (be careful to set a |
53 | ;; -C option or equivalent in order to make the preprocessor not to | |
54 | ;; strip the comments): | |
55 | ;;(setq c-macro-preprocessor "gpp -C") | |
56 | ;; | |
89fb82d6 RS |
57 | ;; If you often use a particular set of flags: |
58 | ;;(setq c-macro-cppflags "-I /usr/include/local -DDEBUG" | |
4af1f509 | 59 | ;; |
89fb82d6 | 60 | ;; If you want the "Preprocessor arguments: " prompt: |
074521be | 61 | ;;(setq c-macro-prompt-flag t) |
4af1f509 RS |
62 | |
63 | ;; BUG REPORTS ======================================================= | |
64 | ||
65 | ;; Please report bugs, suggestions, complaints and so on to | |
66 | ;; pot@cnuce.cnr.it (Francesco Potorti`). | |
67 | ||
68 | ;; IMPROVEMENTS OVER emacs 18.xx cmacexp.el ========================== | |
69 | ||
3332766c | 70 | ;; - A lot of user and programmer visible changes. See above. |
4af1f509 RS |
71 | ;; - #line directives are inserted, so __LINE__ and __FILE__ are |
72 | ;; correctly expanded. Works even with START inside a string, a | |
73 | ;; comment or a region #ifdef'd away by cpp. cpp is invoked with -C, | |
74 | ;; making comments visible in the expansion. | |
75 | ;; - All work is done in core memory, no need for temporary files. | |
4af1f509 RS |
76 | |
77 | ;; ACKNOWLEDGEMENTS ================================================== | |
78 | ||
79 | ;; A lot of thanks to Don Maszle who did a great work of testing, bug | |
074521be RS |
80 | ;; reporting and suggestion of new features. This work has been |
81 | ;; partially inspired by Don Maszle and Jonathan Segal's. | |
4af1f509 | 82 | |
89fb82d6 | 83 | ;; BUGS ============================================================== |
4af1f509 RS |
84 | |
85 | ;; If the start point of the region is inside a macro definition the | |
86 | ;; macro expansion is often inaccurate. | |
e41b2db1 | 87 | |
89fb82d6 | 88 | |
a9132c1f RS |
89 | (require 'cc-mode) |
90 | ||
89fb82d6 | 91 | (provide 'cmacexp) |
c17d15d0 | 92 | |
9c32788e | 93 | (defvar c-macro-shrink-window-flag nil |
4af1f509 RS |
94 | "*Non-nil means shrink the *Macroexpansion* window to fit its contents.") |
95 | ||
43427c5b | 96 | (defvar c-macro-prompt-flag nil |
9c32788e | 97 | "*Non-nil makes `c-macro-expand' prompt for preprocessor arguments.") |
4af1f509 | 98 | |
9c32788e RS |
99 | (defvar c-macro-preprocessor "/lib/cpp -C" |
100 | "The preprocessor used by the cmacexp package. | |
4af1f509 | 101 | |
440c10f8 | 102 | If you change this, be sure to preserve the `-C' (don't strip comments) |
4af1f509 RS |
103 | option, or to set an equivalent one.") |
104 | ||
89fb82d6 | 105 | (defvar c-macro-cppflags "" |
440c10f8 | 106 | "*Preprocessor flags used by `c-macro-expand'.") |
4af1f509 RS |
107 | |
108 | (defconst c-macro-buffer-name "*Macroexpansion*") | |
109 | ||
9c32788e RS |
110 | (defun c-macro-expand (start end subst) |
111 | "Expand C macros in the region, using the C preprocessor. | |
112 | Normally display output in temp buffer, but | |
113 | prefix arg means replace the region with it. | |
114 | ||
115 | `c-macro-preprocessor' specifies the preprocessor to use. | |
116 | Prompt for arguments to the preprocessor \(e.g. `-DDEBUG -I ./include') | |
117 | if the user option `c-macro-prompt-flag' is non-nil. | |
4af1f509 | 118 | |
89fb82d6 | 119 | Noninteractive args are START, END, SUBST. |
9c32788e | 120 | For use inside Lisp programs, see also `c-macro-expansion'." |
4af1f509 RS |
121 | |
122 | (interactive "r\nP") | |
89fb82d6 RS |
123 | (let ((inbuf (current-buffer)) |
124 | (displaybuf (if subst | |
125 | (get-buffer c-macro-buffer-name) | |
126 | (get-buffer-create c-macro-buffer-name))) | |
074521be | 127 | (expansion "")) |
4af1f509 | 128 | ;; Build the command string. |
9c32788e | 129 | (if c-macro-prompt-flag |
89fb82d6 | 130 | (setq c-macro-cppflags |
4af1f509 | 131 | (read-string "Preprocessor arguments: " |
89fb82d6 | 132 | c-macro-cppflags))) |
4af1f509 RS |
133 | ;; Decide where to display output. |
134 | (if (and subst | |
6c61e6a9 | 135 | (and buffer-read-only (not inhibit-read-only)) |
4af1f509 RS |
136 | (not (eq inbuf displaybuf))) |
137 | (progn | |
138 | (message | |
139 | "Buffer is read only: displaying expansion in alternate window") | |
140 | (sit-for 2) | |
141 | (setq subst nil) | |
142 | (or displaybuf | |
143 | (setq displaybuf (get-buffer-create c-macro-buffer-name))))) | |
144 | ;; Expand the macro and output it. | |
89fb82d6 RS |
145 | (setq expansion (c-macro-expansion start end |
146 | (concat c-macro-preprocessor " " | |
074521be | 147 | c-macro-cppflags) t)) |
4af1f509 RS |
148 | (if subst |
149 | (let ((exchange (= (point) start))) | |
150 | (delete-region start end) | |
151 | (insert expansion) | |
152 | (if exchange | |
153 | (exchange-point-and-mark))) | |
154 | (set-buffer displaybuf) | |
155 | (setq buffer-read-only nil) | |
3332766c | 156 | (buffer-disable-undo displaybuf) |
4af1f509 RS |
157 | (erase-buffer) |
158 | (insert expansion) | |
159 | (set-buffer-modified-p nil) | |
160 | (if (string= "" expansion) | |
161 | (message "Null expansion") | |
89fb82d6 | 162 | (c-macro-display-buffer)) |
4af1f509 | 163 | (setq buffer-read-only t) |
89fb82d6 | 164 | (setq buffer-auto-save-file-name nil) |
4af1f509 RS |
165 | (bury-buffer displaybuf)))) |
166 | ||
167 | ||
168 | ;; Display the current buffer in a window which is either just large | |
169 | ;; enough to contain the entire buffer, or half the size of the | |
89fb82d6 RS |
170 | ;; screen, whichever is smaller. Do not select the new |
171 | ;; window. | |
4af1f509 RS |
172 | ;; |
173 | ;; Several factors influence window resizing so that the window is | |
174 | ;; sized optimally if it is created anew, and so that it is messed | |
175 | ;; with minimally if it has been created by the user. If the window | |
176 | ;; chosen for display exists already but contains something else, the | |
177 | ;; window is not re-sized. If the window already contains the current | |
178 | ;; buffer, it is never shrunk, but possibly expanded. Finally, if the | |
9c32788e | 179 | ;; variable c-macro-shrink-window-flag is nil the window size is *never* |
4af1f509 | 180 | ;; changed. |
89fb82d6 | 181 | (defun c-macro-display-buffer () |
4af1f509 RS |
182 | (goto-char (point-min)) |
183 | (c-mode) | |
4af1f509 RS |
184 | (let ((oldwinheight (window-height)) |
185 | (alreadythere ;the window was already there | |
186 | (get-buffer-window (current-buffer))) | |
1dc5112c | 187 | (popped nil)) ;the window popped changing the layout |
4af1f509 RS |
188 | (or alreadythere |
189 | (progn | |
190 | (display-buffer (current-buffer) t) | |
191 | (setq popped (/= oldwinheight (window-height))))) | |
9c32788e | 192 | (if (and c-macro-shrink-window-flag ;user wants fancy shrinking :\) |
4af1f509 RS |
193 | (or alreadythere popped)) |
194 | ;; Enlarge up to half screen, or shrink properly. | |
195 | (let ((oldwin (selected-window)) | |
196 | (minheight 0) | |
197 | (maxheight 0)) | |
198 | (save-excursion | |
199 | (select-window (get-buffer-window (current-buffer))) | |
200 | (setq minheight (if alreadythere | |
201 | (window-height) | |
202 | window-min-height)) | |
203 | (setq maxheight (/ (screen-height) 2)) | |
204 | (enlarge-window (- (min maxheight | |
205 | (max minheight | |
89fb82d6 | 206 | (+ 2 (vertical-motion (point-max))))) |
4af1f509 RS |
207 | (window-height))) |
208 | (goto-char (point-min)) | |
209 | (select-window oldwin)))))) | |
210 | ||
211 | ||
074521be | 212 | (defun c-macro-expansion (start end cppcommand &optional display) |
9c32788e | 213 | "Run a preprocessor on region and return the output as a string. |
89fb82d6 RS |
214 | Expand the region between START and END in the current buffer using |
215 | the shell command CPPCOMMAND (e.g. \"/lib/cpp -C -DDEBUG\"). | |
074521be RS |
216 | Be sure to use a -C (don't strip comments) or equivalent option. |
217 | Optional arg DISPLAY non-nil means show messages in the echo area." | |
4af1f509 RS |
218 | |
219 | ;; Copy the current buffer's contents to a temporary hidden buffer. | |
220 | ;; Delete from END to end of buffer. Insert a preprocessor #line | |
221 | ;; directive at START and after each #endif following START that are | |
222 | ;; not inside a comment or a string. Put all the strings thus | |
223 | ;; inserted (without the "line" substring) in a list named linelist. | |
224 | ;; If START is inside a comment, prepend "*/" and append "/*" to the | |
225 | ;; #line directive. If inside a string, prepend and append "\"". | |
226 | ;; Preprocess the buffer contents, then look for all the lines stored | |
227 | ;; in linelist starting from end of buffer. The last line so found is | |
228 | ;; where START was, so return the substring from point to end of | |
229 | ;; buffer. | |
230 | (let ((inbuf (current-buffer)) | |
231 | (outbuf (get-buffer-create " *C Macro Expansion*")) | |
232 | (filename (if (and buffer-file-name | |
233 | (string-match (regexp-quote default-directory) | |
234 | buffer-file-name)) | |
235 | (substring buffer-file-name (match-end 0)) | |
236 | (buffer-name))) | |
074521be RS |
237 | (mymsg (format "Invoking %s%s%s on region..." |
238 | c-macro-preprocessor | |
239 | (if (string= "" c-macro-cppflags) "" " ") | |
240 | c-macro-cppflags)) | |
241 | (uniquestring "???!!!???!!! start of c-macro expansion ???!!!???!!!") | |
242 | (startlinenum 0) | |
4af1f509 | 243 | (linenum 0) |
074521be | 244 | (startstat ()) |
440c10f8 | 245 | (startmarker "") |
3332766c | 246 | (exit-status 0) |
440c10f8 | 247 | (tempname (make-temp-name "/tmp/"))) |
4af1f509 RS |
248 | (unwind-protect |
249 | (save-excursion | |
250 | (save-restriction | |
251 | (widen) | |
252 | (set-buffer outbuf) | |
253 | (setq buffer-read-only nil) | |
254 | (erase-buffer) | |
255 | (set-syntax-table c-mode-syntax-table) | |
256 | (insert-buffer-substring inbuf 1 end)) | |
257 | ||
258 | ;; We have copied inbuf to outbuf. Point is at end of | |
259 | ;; outbuf. Insert a space at the end, so cpp can correctly | |
260 | ;; parse a token ending at END. | |
4af1f509 RS |
261 | (insert " ") |
262 | ||
074521be RS |
263 | ;; Save sexp status and line number at START. |
264 | (setq startstat (parse-partial-sexp 1 start)) | |
265 | (setq startlinenum (+ (count-lines 1 (point)) | |
266 | (if (bolp) 1 0))) | |
267 | ||
4af1f509 | 268 | ;; Now we insert the #line directives after all #endif or |
074521be RS |
269 | ;; #else following START going backward, so the lines we |
270 | ;; insert don't change the line numbers. | |
4af1f509 | 271 | ;(switch-to-buffer outbuf) (debug) ;debugging instructions |
074521be | 272 | (goto-char (point-max)) |
4af1f509 | 273 | (while (re-search-backward "\n#\\(endif\\|else\\)\\>" start 'move) |
074521be RS |
274 | (if (equal (nthcdr 3 (parse-partial-sexp start (point) |
275 | nil nil startstat)) | |
9c32788e | 276 | '(nil nil nil 0 nil)) ;neither in string nor in |
074521be | 277 | ;comment nor after quote |
4af1f509 RS |
278 | (progn |
279 | (goto-char (match-end 0)) | |
074521be RS |
280 | (setq linenum (+ startlinenum |
281 | (count-lines start (point)))) | |
282 | (insert (format "\n#line %d \"%s\"\n" linenum filename)) | |
283 | (goto-char (match-beginning 0))))) | |
284 | ||
285 | ;; Now we are at START. Insert the first #line directive. | |
286 | ;; This must work even inside a string or comment, or after a | |
4af1f509 | 287 | ;; quote. |
074521be RS |
288 | (let* ((startinstring (nth 3 startstat)) |
289 | (startincomment (nth 4 startstat)) | |
290 | (startafterquote (nth 5 startstat)) | |
291 | (startinbcomment (nth 7 startstat))) | |
292 | (insert (if startafterquote " " "") | |
293 | (cond (startinstring | |
294 | (char-to-string startinstring)) | |
295 | (startincomment "*/") | |
296 | ("")) | |
074521be | 297 | (setq startmarker |
521fffcd | 298 | (concat "\n" uniquestring |
074521be RS |
299 | (cond (startinstring |
300 | (char-to-string startinstring)) | |
301 | (startincomment "/*") | |
302 | (startinbcomment "//")) | |
521fffcd FP |
303 | (if startafterquote "\\"))) |
304 | (format "\n#line %d \"%s\"\n" startlinenum filename))) | |
4af1f509 RS |
305 | |
306 | ;; Call the preprocessor. | |
074521be | 307 | (if display (message mymsg)) |
440c10f8 RS |
308 | (setq exit-status |
309 | (call-process-region 1 (point-max) "sh" t t nil "-c" | |
310 | (concat cppcommand " 2>" tempname))) | |
074521be | 311 | (if display (message (concat mymsg "done"))) |
eb426f3a RS |
312 | (if (= (buffer-size) 0) |
313 | ;; Empty output is normal after a fatal error. | |
314 | (insert "\nPreprocessor produced no output\n") | |
315 | ;; Find and delete the mark of the start of the expansion. | |
316 | ;; Look for `# nn "file.c"' lines and delete them. | |
317 | (goto-char (point-min)) | |
318 | (search-forward startmarker) | |
319 | (delete-region 1 (point))) | |
074521be RS |
320 | (while (re-search-forward (concat "^# [0-9]+ \"" |
321 | (regexp-quote filename) | |
322 | "\"") nil t) | |
323 | (beginning-of-line) | |
324 | (let ((beg (point))) | |
325 | (forward-line 1) | |
326 | (delete-region beg (point)))) | |
4af1f509 | 327 | |
440c10f8 RS |
328 | ;; If CPP got errors, show them at the beginning. |
329 | (or (eq exit-status 0) | |
330 | (progn | |
331 | (goto-char (point-min)) | |
332 | (insert (format "Preprocessor terminated with status %s\n" | |
333 | exit-status)) | |
334 | (insert-file-contents tempname) | |
335 | (insert "\n"))) | |
336 | (delete-file tempname) | |
337 | ||
4af1f509 RS |
338 | ;; Compute the return value, keeping in account the space |
339 | ;; inserted at the end of the buffer. | |
074521be | 340 | (buffer-substring 1 (max 1 (- (point-max) 1)))) |
4af1f509 RS |
341 | |
342 | ;; Cleanup. | |
343 | (kill-buffer outbuf)))) | |
344 | ||
89fb82d6 | 345 | ;;; cmacexp.el ends here |