* progmodes/flymake.el (flymake-save-buffer-in-file):
[bpt/emacs.git] / lisp / progmodes / flymake.el
CommitLineData
4bcbcb9d
EZ
1;;; flymake.el -- a universal on-the-fly syntax checker
2
4e643dd2 3;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008
d7a0267c 4;; Free Software Foundation, Inc.
4bcbcb9d 5
1d9a4930
EZ
6;; Author: Pavel Kobyakov <pk_at_work@yahoo.com>
7;; Maintainer: Pavel Kobyakov <pk_at_work@yahoo.com>
4bcbcb9d
EZ
8;; Version: 0.3
9;; Keywords: c languages tools
10
11;; This file is part of GNU Emacs.
12
b1fc2b50 13;; GNU Emacs is free software: you can redistribute it and/or modify
4bcbcb9d 14;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
15;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
4bcbcb9d
EZ
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
b1fc2b50 24;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
4bcbcb9d
EZ
25
26;;; Commentary:
27;;
28;; Flymake is a minor Emacs mode performing on-the-fly syntax
29;; checks using the external syntax check tool (for C/C++ this
30;; is usually the compiler)
31
9f812d23
SM
32;;; Bugs/todo:
33
34;; - Only uses "Makefile", not "makefile" or "GNUmakefile"
35;; (from http://bugs.debian.org/337339).
36
4bcbcb9d
EZ
37;;; Code:
38
6fee12e6
SM
39(eval-when-compile (require 'cl))
40(if (featurep 'xemacs) (require 'overlay))
41
43ed65ac
SM
42(defvar flymake-is-running nil
43 "If t, flymake syntax check process is running for the current buffer.")
44(make-variable-buffer-local 'flymake-is-running)
c92d6023 45
43ed65ac
SM
46(defvar flymake-timer nil
47 "Timer for starting syntax check.")
48(make-variable-buffer-local 'flymake-timer)
c92d6023 49
43ed65ac
SM
50(defvar flymake-last-change-time nil
51 "Time of last buffer change.")
52(make-variable-buffer-local 'flymake-last-change-time)
53
54(defvar flymake-check-start-time nil
55 "Time at which syntax check was started.")
56(make-variable-buffer-local 'flymake-check-start-time)
57
58(defvar flymake-check-was-interrupted nil
59 "Non-nil if syntax check was killed by `flymake-compile'.")
60(make-variable-buffer-local 'flymake-check-was-interrupted)
61
62(defvar flymake-err-info nil
63 "Sorted list of line numbers and lists of err info in the form (file, err-text).")
64(make-variable-buffer-local 'flymake-err-info)
65
66(defvar flymake-new-err-info nil
67 "Same as `flymake-err-info', effective when a syntax check is in progress.")
68(make-variable-buffer-local 'flymake-new-err-info)
c92d6023 69
4bcbcb9d 70;;;; [[ cross-emacs compatibility routines
ce721de4
KS
71(defsubst flymake-makehash (&optional test)
72 (if (fboundp 'make-hash-table)
73 (if test (make-hash-table :test test) (make-hash-table))
adf8a02e
RS
74 (with-no-warnings
75 (makehash test))))
5323b4b0 76
cd2325cd
SM
77(defalias 'flymake-float-time
78 (if (fboundp 'float-time)
79 'float-time
7ea8834e
RS
80 (if (featurep 'xemacs)
81 (lambda ()
82 (multiple-value-bind (s0 s1 s2) (current-time)
83 (+ (* (float (ash 1 16)) s0) (float s1) (* 0.0000001 s2)))))))
ce721de4 84
43ed65ac
SM
85(defalias 'flymake-replace-regexp-in-string
86 (if (eval-when-compile (fboundp 'replace-regexp-in-string))
87 'replace-regexp-in-string
88 (lambda (regexp rep str)
89 (replace-in-string str regexp rep))))
90
91(defalias 'flymake-split-string
92 (if (condition-case nil (equal (split-string " bc " " " t) '("bc"))
93 (error nil))
94 (lambda (str pattern) (split-string str pattern t))
95 (lambda (str pattern)
96 "Split STR into a list of substrings bounded by PATTERN.
53ec26ed 97Zero-length substrings at the beginning and end of the list are omitted."
43ed65ac 98 (let ((split (split-string str pattern)))
587d108e
SM
99 (while (equal (car split) "") (setq split (cdr split)))
100 (setq split (nreverse split))
101 (while (equal (car split) "") (setq split (cdr split)))
102 (nreverse split)))))
43ed65ac
SM
103
104(defalias 'flymake-get-temp-dir
cd2325cd 105 (if (fboundp 'temp-directory)
43ed65ac
SM
106 'temp-directory
107 (lambda () temporary-file-directory)))
4bcbcb9d 108
cd2325cd
SM
109(defalias 'flymake-line-beginning-position
110 (if (fboundp 'line-beginning-position)
111 'line-beginning-position
112 (lambda (&optional arg) (save-excursion (beginning-of-line arg) (point)))))
4bcbcb9d 113
cd2325cd
SM
114(defalias 'flymake-line-end-position
115 (if (fboundp 'line-end-position)
116 'line-end-position
117 (lambda (&optional arg) (save-excursion (end-of-line arg) (point)))))
5323b4b0 118
1d9a4930
EZ
119(defun flymake-posn-at-point-as-event (&optional position window dx dy)
120 "Return pixel position of top left corner of glyph at POSITION,
121relative to top left corner of WINDOW, as a mouse-1 click
122event (identical to the event that would be triggered by clicking
123mouse button 1 at the top left corner of the glyph).
124
125POSITION and WINDOW default to the position of point in the
126selected window.
127
128DX and DY specify optional offsets from the top left of the glyph."
129 (unless window (setq window (selected-window)))
130 (unless position (setq position (window-point window)))
131 (unless dx (setq dx 0))
132 (unless dy (setq dy 0))
133
134 (let* ((pos (posn-at-point position window))
135 (x-y (posn-x-y pos))
136 (edges (window-inside-pixel-edges window))
137 (win-x-y (window-pixel-edges window)))
138 ;; adjust for window edges
139 (setcar (nthcdr 2 pos)
140 (cons (+ (car x-y) (car edges) (- (car win-x-y)) dx)
141 (+ (cdr x-y) (cadr edges) (- (cadr win-x-y)) dy)))
142 (list 'mouse-1 pos)))
43ed65ac
SM
143
144(defun flymake-popup-menu (menu-data)
145 "Pop up the flymake menu at point, using the data MENU-DATA.
53ec26ed
RS
146POS is a list of the form ((X Y) WINDOW), where X and Y are
147pixels positions from the top left corner of WINDOW's frame.
148MENU-DATA is a list of error and warning messages returned by
149`flymake-make-err-menu-data'."
150 (if (featurep 'xemacs)
43ed65ac
SM
151 (let* ((pos (flymake-get-point-pixel-pos))
152 (x-pos (nth 0 pos))
153 (y-pos (nth 1 pos))
ce721de4
KS
154 (fake-event-props '(button 1 x 1 y 1)))
155 (setq fake-event-props (plist-put fake-event-props 'x x-pos))
156 (setq fake-event-props (plist-put fake-event-props 'y y-pos))
43ed65ac
SM
157 (popup-menu (flymake-make-xemacs-menu menu-data)
158 (make-event 'button-press fake-event-props)))
159 (x-popup-menu (if (eval-when-compile (fboundp 'posn-at-point))
1d9a4930 160 (flymake-posn-at-point-as-event)
43ed65ac
SM
161 (list (flymake-get-point-pixel-pos) (selected-window)))
162 (flymake-make-emacs-menu menu-data))))
4bcbcb9d 163
5323b4b0 164(defun flymake-make-emacs-menu (menu-data)
53ec26ed
RS
165 "Return a menu specifier using MENU-DATA.
166MENU-DATA is a list of error and warning messages returned by
167`flymake-make-err-menu-data'.
168See `x-popup-menu' for the menu specifier format."
5323b4b0
RS
169 (let* ((menu-title (nth 0 menu-data))
170 (menu-items (nth 1 menu-data))
43ed65ac
SM
171 (menu-commands (mapcar (lambda (foo)
172 (cons (nth 0 foo) (nth 1 foo)))
173 menu-items)))
5323b4b0 174 (list menu-title (cons "" menu-commands))))
4bcbcb9d 175
ce721de4
KS
176(if (featurep 'xemacs) (progn
177
cd2325cd
SM
178(defun flymake-nop ())
179
2aa13ec4 180(defun flymake-make-xemacs-menu (menu-data)
53ec26ed 181 "Return a menu specifier using MENU-DATA."
5323b4b0
RS
182 (let* ((menu-title (nth 0 menu-data))
183 (menu-items (nth 1 menu-data))
184 (menu-commands nil))
185 (setq menu-commands (mapcar (lambda (foo)
186 (vector (nth 0 foo) (or (nth 1 foo) '(flymake-nop)) t))
187 menu-items))
188 (cons menu-title menu-commands)))
189
ce721de4
KS
190)) ;; xemacs
191
43ed65ac
SM
192(unless (eval-when-compile (fboundp 'posn-at-point))
193
5323b4b0
RS
194(defun flymake-current-row ()
195 "Return current row number in current frame."
cd2325cd
SM
196 (if (fboundp 'window-edges)
197 (+ (car (cdr (window-edges))) (count-lines (window-start) (point)))
198 (count-lines (window-start) (point))))
5323b4b0
RS
199
200(defun flymake-selected-frame ()
cd2325cd
SM
201 (if (fboundp 'window-edges)
202 (selected-frame)
203 (selected-window)))
4bcbcb9d 204
43ed65ac
SM
205(defun flymake-get-point-pixel-pos ()
206 "Return point position in pixels: (x, y)."
207 (let ((mouse-pos (mouse-position))
208 (pixel-pos nil)
209 (ret nil))
210 (if (car (cdr mouse-pos))
211 (progn
212 (set-mouse-position (flymake-selected-frame) (current-column) (flymake-current-row))
213 (setq pixel-pos (mouse-pixel-position))
214 (set-mouse-position (car mouse-pos) (car (cdr mouse-pos)) (cdr (cdr mouse-pos)))
215 (setq ret (list (car (cdr pixel-pos)) (cdr (cdr pixel-pos)))))
216 (progn
217 (setq ret '(0 0))))
218 (flymake-log 3 "mouse pos is %s" ret)
219 ret))
220
221) ;; End of (unless (fboundp 'posn-at-point)
222
4bcbcb9d
EZ
223;;;; ]]
224
225(defcustom flymake-log-level -1
cd2325cd 226 "Logging level, only messages with level lower or equal will be logged.
4bcbcb9d 227-1 = NONE, 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG"
5323b4b0
RS
228 :group 'flymake
229 :type 'integer)
4bcbcb9d 230
2aa13ec4 231(defun flymake-log (level text &rest args)
53ec26ed
RS
232 "Log a message at level LEVEL.
233If LEVEL is higher than `flymake-log-level', the message is
234ignored. Otherwise, it is printed using `message'.
235TEXT is a format control string, and the remaining arguments ARGS
236are the string substitutions (see `format')."
5323b4b0
RS
237 (if (<= level flymake-log-level)
238 (let* ((msg (apply 'format text args)))
29a4e67d 239 (message "%s" msg)
5323b4b0
RS
240 ;;(with-temp-buffer
241 ;; (insert msg)
242 ;; (insert "\n")
6df19241 243 ;; (flymake-save-buffer-in-file "d:/flymake.log" t) ; make log file name customizable
5323b4b0 244 ;;)
2aa13ec4 245 )))
4bcbcb9d 246
2aa13ec4 247(defun flymake-ins-after (list pos val)
5323b4b0
RS
248 "Insert VAL into LIST after position POS."
249 (let ((tmp (copy-sequence list))) ; (???)
250 (setcdr (nthcdr pos tmp) (cons val (nthcdr (1+ pos) tmp)))
251 tmp))
4bcbcb9d 252
2aa13ec4 253(defun flymake-set-at (list pos val)
9a686ad2 254 "Set VAL at position POS in LIST."
5323b4b0
RS
255 (let ((tmp (copy-sequence list))) ; (???)
256 (setcar (nthcdr pos tmp) val)
257 tmp))
4bcbcb9d 258
43ed65ac
SM
259(defvar flymake-processes nil
260 "List of currently active flymake processes.")
4bcbcb9d 261
9a686ad2 262(defvar flymake-output-residual nil)
2aa13ec4 263
4bcbcb9d 264(make-variable-buffer-local 'flymake-output-residual)
2aa13ec4 265
5323b4b0 266(defcustom flymake-allowed-file-name-masks
c07fa030
SM
267 '(("\\.c\\'" flymake-simple-make-init)
268 ("\\.cpp\\'" flymake-simple-make-init)
269 ("\\.xml\\'" flymake-xml-init)
270 ("\\.html?\\'" flymake-xml-init)
271 ("\\.cs\\'" flymake-simple-make-init)
eabd11d4
MH
272 ("\\.p[ml]\\'" flymake-perl-init)
273 ("\\.php[345]?\\'" flymake-php-init)
6df19241
SM
274 ("\\.h\\'" flymake-master-make-header-init flymake-master-cleanup)
275 ("\\.java\\'" flymake-simple-make-java-init flymake-simple-java-cleanup)
276 ("[0-9]+\\.tex\\'" flymake-master-tex-init flymake-master-cleanup)
c07fa030
SM
277 ("\\.tex\\'" flymake-simple-tex-init)
278 ("\\.idl\\'" flymake-simple-make-init)
6df19241
SM
279 ;; ("\\.cpp\\'" 1)
280 ;; ("\\.java\\'" 3)
281 ;; ("\\.h\\'" 2 ("\\.cpp\\'" "\\.c\\'")
9a686ad2 282 ;; ("[ \t]*#[ \t]*include[ \t]*\"\\([\w0-9/\\_\.]*[/\\]*\\)\\(%s\\)\"" 1 2))
6df19241
SM
283 ;; ("\\.idl\\'" 1)
284 ;; ("\\.odl\\'" 1)
285 ;; ("[0-9]+\\.tex\\'" 2 ("\\.tex\\'")
9a686ad2 286 ;; ("[ \t]*\\input[ \t]*{\\(.*\\)\\(%s\\)}" 1 2 ))
6df19241 287 ;; ("\\.tex\\'" 1)
5323b4b0
RS
288 )
289 "*Files syntax checking is allowed for."
290 :group 'flymake
291 :type '(repeat (string symbol symbol symbol)))
4bcbcb9d 292
2aa13ec4 293(defun flymake-get-file-name-mode-and-masks (file-name)
9a686ad2 294 "Return the corresponding entry from `flymake-allowed-file-name-masks'."
5323b4b0
RS
295 (unless (stringp file-name)
296 (error "Invalid file-name"))
17404091 297 (let ((fnm flymake-allowed-file-name-masks)
5323b4b0 298 (mode-and-masks nil))
17404091
SM
299 (while (and (not mode-and-masks) fnm)
300 (if (string-match (car (car fnm)) file-name)
301 (setq mode-and-masks (cdr (car fnm))))
302 (setq fnm (cdr fnm)))
5323b4b0
RS
303 (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks))
304 mode-and-masks))
4bcbcb9d 305
2aa13ec4 306(defun flymake-can-syntax-check-file (file-name)
5323b4b0 307 "Determine whether we can syntax check FILE-NAME.
2aa13ec4 308Return nil if we cannot, non-nil if we can."
5323b4b0 309 (if (flymake-get-init-function file-name) t nil))
4bcbcb9d 310
2aa13ec4 311(defun flymake-get-init-function (file-name)
5323b4b0
RS
312 "Return init function to be used for the file."
313 (let* ((init-f (nth 0 (flymake-get-file-name-mode-and-masks file-name))))
9a686ad2
SM
314 ;;(flymake-log 0 "calling %s" init-f)
315 ;;(funcall init-f (current-buffer))
5323b4b0 316 init-f))
4bcbcb9d 317
2aa13ec4 318(defun flymake-get-cleanup-function (file-name)
5323b4b0 319 "Return cleanup function to be used for the file."
c07fa030
SM
320 (or (nth 1 (flymake-get-file-name-mode-and-masks file-name))
321 'flymake-simple-cleanup))
4bcbcb9d 322
2aa13ec4 323(defun flymake-get-real-file-name-function (file-name)
6df19241
SM
324 (or (nth 2 (flymake-get-file-name-mode-and-masks file-name))
325 'flymake-get-real-file-name))
4bcbcb9d 326
4bcbcb9d 327(defvar flymake-find-buildfile-cache (flymake-makehash 'equal))
4bcbcb9d 328
2aa13ec4 329(defun flymake-get-buildfile-from-cache (dir-name)
5323b4b0 330 (gethash dir-name flymake-find-buildfile-cache))
2aa13ec4
RS
331
332(defun flymake-add-buildfile-to-cache (dir-name buildfile)
5323b4b0 333 (puthash dir-name buildfile flymake-find-buildfile-cache))
2aa13ec4
RS
334
335(defun flymake-clear-buildfile-cache ()
5323b4b0 336 (clrhash flymake-find-buildfile-cache))
2aa13ec4 337
5f73367d 338(defun flymake-find-buildfile (buildfile-name source-dir-name)
5323b4b0 339 "Find buildfile starting from current directory.
2aa13ec4 340Buildfile includes Makefile, build.xml etc.
5323b4b0 341Return its file name if found, or nil if not found."
5f73367d 342 (or (flymake-get-buildfile-from-cache source-dir-name)
8ebbfc80
SM
343 (let* ((file (locate-dominating-file
344 source-dir-name
345 (concat "\\`" (regexp-quote buildfile-name) "\\'"))))
346 (if file
5f73367d 347 (progn
8ebbfc80
SM
348 (flymake-log 3 "found buildfile at %s" file)
349 (setq file (file-name-directory file))
350 (flymake-add-buildfile-to-cache source-dir-name file)
351 file)
5f73367d
SM
352 (progn
353 (flymake-log 3 "buildfile for %s not found" source-dir-name)
354 nil)))))
5323b4b0
RS
355
356(defun flymake-fix-file-name (name)
57d3a1f3 357 "Replace all occurrences of '\' with '/'."
5323b4b0 358 (when name
587d108e
SM
359 (setq name (expand-file-name name))
360 (setq name (abbreviate-file-name name))
361 (setq name (directory-file-name name))
362 name))
4bcbcb9d 363
2aa13ec4 364(defun flymake-same-files (file-name-one file-name-two)
5323b4b0 365 "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file.
2aa13ec4 366Return t if so, nil if not."
5323b4b0
RS
367 (equal (flymake-fix-file-name file-name-one)
368 (flymake-fix-file-name file-name-two)))
369
4bcbcb9d 370(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest")
9a686ad2 371 "Dirs where to look for master files."
5323b4b0
RS
372 :group 'flymake
373 :type '(repeat (string)))
4bcbcb9d
EZ
374
375(defcustom flymake-master-file-count-limit 32
5323b4b0
RS
376 "Max number of master files to check."
377 :group 'flymake
378 :type 'integer)
2aa13ec4 379
ce721de4
KS
380;; This is bound dynamically to pass a parameter to a sort predicate below
381(defvar flymake-included-file-name)
4bcbcb9d 382
2aa13ec4 383(defun flymake-find-possible-master-files (file-name master-file-dirs masks)
9a686ad2 384 "Find (by name and location) all possible master files.
173569aa 385Master files are .cpp and .c for and .h. Files are searched for
2aa13ec4 386starting from the .h directory and max max-level parent dirs.
4bcbcb9d 387File contents are not checked."
17404091 388 (let* ((dirs master-file-dirs)
5323b4b0 389 (files nil)
17404091
SM
390 (done nil))
391
392 (while (and (not done) dirs)
587d108e 393 (let* ((dir (expand-file-name (car dirs) (file-name-directory file-name)))
17404091
SM
394 (masks masks))
395 (while (and (file-exists-p dir) (not done) masks)
396 (let* ((mask (car masks))
397 (dir-files (directory-files dir t mask)))
398
399 (flymake-log 3 "dir %s, %d file(s) for mask %s"
400 dir (length dir-files) mask)
401 (while (and (not done) dir-files)
402 (when (not (file-directory-p (car dir-files)))
403 (setq files (cons (car dir-files) files))
5323b4b0
RS
404 (when (>= (length files) flymake-master-file-count-limit)
405 (flymake-log 3 "master file count limit (%d) reached" flymake-master-file-count-limit)
406 (setq done t)))
17404091
SM
407 (setq dir-files (cdr dir-files))))
408 (setq masks (cdr masks))))
409 (setq dirs (cdr dirs)))
5323b4b0 410 (when files
ce721de4
KS
411 (let ((flymake-included-file-name (file-name-nondirectory file-name)))
412 (setq files (sort files 'flymake-master-file-compare))))
5323b4b0
RS
413 (flymake-log 3 "found %d possible master file(s)" (length files))
414 files))
4bcbcb9d 415
2aa13ec4 416(defun flymake-master-file-compare (file-one file-two)
173569aa 417 "Compare two files specified by FILE-ONE and FILE-TWO.
2aa13ec4 418This function is used in sort to move most possible file names
173569aa 419to the beginning of the list (File.h -> File.cpp moved to top)."
5323b4b0
RS
420 (and (equal (file-name-sans-extension flymake-included-file-name)
421 (file-name-sans-extension (file-name-nondirectory file-one)))
422 (not (equal file-one file-two))))
4bcbcb9d
EZ
423
424(defcustom flymake-check-file-limit 8192
5323b4b0
RS
425 "Max number of chars to look at when checking possible master file."
426 :group 'flymake
427 :type 'integer)
4bcbcb9d 428
587d108e
SM
429(defun flymake-check-patch-master-file-buffer
430 (master-file-temp-buffer
431 master-file-name patched-master-file-name
432 source-file-name patched-source-file-name
433 include-dirs regexp)
5323b4b0 434 "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME.
2aa13ec4
RS
435For .cpp master file this means it includes SOURCE-FILE-NAME (.h).
436If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME
437instead of SOURCE-FILE-NAME.
438Whether a buffer for MATER-FILE-NAME exists, use it as a source
439instead of reading master file from disk."
587d108e
SM
440 (let* ((source-file-nondir (file-name-nondirectory source-file-name))
441 (found nil)
5323b4b0
RS
442 (inc-name nil)
443 (search-limit flymake-check-file-limit))
587d108e
SM
444 (setq regexp
445 (format regexp ; "[ \t]*#[ \t]*include[ \t]*\"\\(.*%s\\)\""
446 (regexp-quote source-file-nondir)))
447 (unwind-protect
448 (with-current-buffer master-file-temp-buffer
449 (when (> search-limit (point-max))
450 (setq search-limit (point-max)))
451 (flymake-log 3 "checking %s against regexp %s"
452 master-file-name regexp)
453 (goto-char (point-min))
454 (while (and (< (point) search-limit)
455 (re-search-forward regexp search-limit t))
456 (let ((match-beg (match-beginning 1))
457 (match-end (match-end 1)))
458
459 (flymake-log 3 "found possible match for %s" source-file-nondir)
460 (setq inc-name (match-string 1))
461 (when (eq t (compare-strings
462 source-file-nondir nil nil
463 inc-name (- (length inc-name)
464 (length source-file-nondir)) nil))
465 (flymake-log 3 "inc-name=%s" inc-name)
466 (when (flymake-check-include source-file-name inc-name
467 include-dirs)
468 (setq found t)
469 ;; replace-match is not used here as it fails in
470 ;; XEmacs with 'last match not a buffer' error as
471 ;; check-includes calls replace-in-string
472 (flymake-replace-region
473 match-beg match-end
474 (file-name-nondirectory patched-source-file-name))))
475 (forward-line 1)))
476 (when found
477 (flymake-save-buffer-in-file patched-master-file-name)))
478 ;;+(flymake-log 3 "killing buffer %s"
479 ;; (buffer-name master-file-temp-buffer))
480 (kill-buffer master-file-temp-buffer))
9a686ad2 481 ;;+(flymake-log 3 "check-patch master file %s: %s" master-file-name found)
5323b4b0
RS
482 (when found
483 (flymake-log 2 "found master file %s" master-file-name))
484 found))
4bcbcb9d 485
4bd0a5d0 486(defun flymake-replace-region (beg end rep)
5323b4b0
RS
487 "Replace text in BUFFER in region (BEG END) with REP."
488 (save-excursion
4bd0a5d0
SM
489 (goto-char end)
490 ;; Insert before deleting, so as to better preserve markers's positions.
491 (insert rep)
492 (delete-region beg end)))
4bcbcb9d 493
2aa13ec4 494(defun flymake-read-file-to-temp-buffer (file-name)
5323b4b0
RS
495 "Insert contents of FILE-NAME into newly created temp buffer."
496 (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name))))))
cd2325cd 497 (with-current-buffer temp-buffer
5323b4b0
RS
498 (insert-file-contents file-name))
499 temp-buffer))
4bcbcb9d 500
2aa13ec4 501(defun flymake-copy-buffer-to-temp-buffer (buffer)
5323b4b0 502 "Copy contents of BUFFER into newly created temp buffer."
4dd68f44
SM
503 (with-current-buffer
504 (get-buffer-create (generate-new-buffer-name
505 (concat "flymake:" (buffer-name buffer))))
506 (insert-buffer-substring buffer)
507 (current-buffer)))
4bcbcb9d 508
587d108e 509(defun flymake-check-include (source-file-name inc-name include-dirs)
5323b4b0 510 "Check if SOURCE-FILE-NAME can be found in include path.
587d108e
SM
511Return t if it can be found via include path using INC-NAME."
512 (if (file-name-absolute-p inc-name)
513 (flymake-same-files source-file-name inc-name)
514 (while (and include-dirs
515 (not (flymake-same-files
516 source-file-name
517 (concat (file-name-directory source-file-name)
518 "/" (car include-dirs)
519 "/" inc-name))))
520 (setq include-dirs (cdr include-dirs)))
521 include-dirs))
4bcbcb9d 522
2aa13ec4 523(defun flymake-find-buffer-for-file (file-name)
5323b4b0 524 "Check if there exists a buffer visiting FILE-NAME.
2aa13ec4 525Return t if so, nil if not."
5323b4b0
RS
526 (let ((buffer-name (get-file-buffer file-name)))
527 (if buffer-name
528 (get-buffer buffer-name))))
4bcbcb9d 529
587d108e 530(defun flymake-create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp)
5323b4b0 531 "Save SOURCE-FILE-NAME with a different name.
2aa13ec4 532Find master file, patch and save it."
5323b4b0
RS
533 (let* ((possible-master-files (flymake-find-possible-master-files source-file-name flymake-master-file-dirs masks))
534 (master-file-count (length possible-master-files))
535 (idx 0)
536 (temp-buffer nil)
537 (master-file-name nil)
538 (patched-master-file-name nil)
539 (found nil))
540
541 (while (and (not found) (< idx master-file-count))
542 (setq master-file-name (nth idx possible-master-files))
543 (setq patched-master-file-name (funcall create-temp-f master-file-name "flymake_master"))
544 (if (flymake-find-buffer-for-file master-file-name)
545 (setq temp-buffer (flymake-copy-buffer-to-temp-buffer (flymake-find-buffer-for-file master-file-name)))
546 (setq temp-buffer (flymake-read-file-to-temp-buffer master-file-name)))
547 (setq found
548 (flymake-check-patch-master-file-buffer
549 temp-buffer
550 master-file-name
551 patched-master-file-name
552 source-file-name
553 patched-source-file-name
554 (funcall get-incl-dirs-f (file-name-directory master-file-name))
587d108e 555 include-regexp))
5323b4b0
RS
556 (setq idx (1+ idx)))
557 (if found
558 (list master-file-name patched-master-file-name)
559 (progn
560 (flymake-log 3 "none of %d master file(s) checked includes %s" master-file-count
561 (file-name-nondirectory source-file-name))
562 nil))))
4bcbcb9d 563
6df19241 564(defun flymake-save-buffer-in-file (file-name)
ab1d3835
SM
565 (make-directory (file-name-directory file-name) 1)
566 (write-region nil nil file-name nil 566)
6df19241 567 (flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name))
4bcbcb9d 568
2aa13ec4 569(defun flymake-save-string-to-file (file-name data)
5323b4b0
RS
570 "Save string DATA to file FILE-NAME."
571 (write-region data nil file-name nil 566))
4bcbcb9d 572
2aa13ec4 573(defun flymake-read-file-to-string (file-name)
5323b4b0
RS
574 "Read contents of file FILE-NAME and return as a string."
575 (with-temp-buffer
576 (insert-file-contents file-name)
577 (buffer-substring (point-min) (point-max))))
4bcbcb9d 578
2aa13ec4 579(defun flymake-process-filter (process output)
5323b4b0 580 "Parse OUTPUT and highlight error lines.
2aa13ec4 581It's flymake process filter."
43ed65ac 582 (let ((source-buffer (process-buffer process)))
4bcbcb9d 583
43ed65ac
SM
584 (flymake-log 3 "received %d byte(s) of output from process %d"
585 (length output) (process-id process))
5323b4b0 586 (when source-buffer
4dd68f44
SM
587 (with-current-buffer source-buffer
588 (flymake-parse-output-and-residual output)))))
4bcbcb9d 589
2aa13ec4 590(defun flymake-process-sentinel (process event)
5323b4b0 591 "Sentinel for syntax check buffers."
c07fa030
SM
592 (when (memq (process-status process) '(signal exit))
593 (let* ((exit-status (process-exit-status process))
594 (command (process-command process))
595 (source-buffer (process-buffer process))
596 (cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer))))
597
598 (flymake-log 2 "process %d exited with code %d"
599 (process-id process) exit-status)
600 (condition-case err
601 (progn
602 (flymake-log 3 "cleaning up using %s" cleanup-f)
603 (when (buffer-live-p source-buffer)
604 (with-current-buffer source-buffer
605 (funcall cleanup-f)))
606
607 (delete-process process)
608 (setq flymake-processes (delq process flymake-processes))
609
610 (when (buffer-live-p source-buffer)
611 (with-current-buffer source-buffer
612
613 (flymake-parse-residual)
614 (flymake-post-syntax-check exit-status command)
615 (setq flymake-is-running nil))))
616 (error
617 (let ((err-str (format "Error in process sentinel for buffer %s: %s"
618 source-buffer (error-message-string err))))
619 (flymake-log 0 err-str)
620 (with-current-buffer source-buffer
621 (setq flymake-is-running nil))))))))
4bcbcb9d 622
4dd68f44
SM
623(defun flymake-post-syntax-check (exit-status command)
624 (setq flymake-err-info flymake-new-err-info)
625 (setq flymake-new-err-info nil)
626 (setq flymake-err-info
627 (flymake-fix-line-numbers
628 flymake-err-info 1 (flymake-count-lines)))
629 (flymake-delete-own-overlays)
630 (flymake-highlight-err-lines flymake-err-info)
53ec26ed 631 (let (err-count warn-count)
4dd68f44
SM
632 (setq err-count (flymake-get-err-count flymake-err-info "e"))
633 (setq warn-count (flymake-get-err-count flymake-err-info "w"))
634 (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)"
635 (buffer-name) err-count warn-count
53ec26ed 636 (- (flymake-float-time) flymake-check-start-time))
4dd68f44 637 (setq flymake-check-start-time nil)
53ec26ed 638
5323b4b0
RS
639 (if (and (equal 0 err-count) (equal 0 warn-count))
640 (if (equal 0 exit-status)
4dd68f44
SM
641 (flymake-report-status "" "") ; PASSED
642 (if (not flymake-check-was-interrupted)
643 (flymake-report-fatal-status "CFGERR"
5323b4b0 644 (format "Configuration error has occured while running %s" command))
4dd68f44
SM
645 (flymake-report-status nil ""))) ; "STOPPED"
646 (flymake-report-status (format "%d/%d" err-count warn-count) ""))))
4bcbcb9d 647
4dd68f44 648(defun flymake-parse-output-and-residual (output)
5323b4b0 649 "Split OUTPUT into lines, merge in residual if necessary."
4dd68f44
SM
650 (let* ((buffer-residual flymake-output-residual)
651 (total-output (if buffer-residual (concat buffer-residual output) output))
652 (lines-and-residual (flymake-split-output total-output))
653 (lines (nth 0 lines-and-residual))
654 (new-residual (nth 1 lines-and-residual)))
655 (setq flymake-output-residual new-residual)
656 (setq flymake-new-err-info
657 (flymake-parse-err-lines
658 flymake-new-err-info lines))))
659
660(defun flymake-parse-residual ()
5323b4b0 661 "Parse residual if it's non empty."
4dd68f44
SM
662 (when flymake-output-residual
663 (setq flymake-new-err-info
664 (flymake-parse-err-lines
665 flymake-new-err-info
666 (list flymake-output-residual)))
667 (setq flymake-output-residual nil)))
4bcbcb9d 668
2aa13ec4 669(defun flymake-er-make-er (line-no line-err-info-list)
5323b4b0 670 (list line-no line-err-info-list))
2aa13ec4
RS
671
672(defun flymake-er-get-line (err-info)
5323b4b0 673 (nth 0 err-info))
2aa13ec4
RS
674
675(defun flymake-er-get-line-err-info-list (err-info)
5323b4b0 676 (nth 1 err-info))
4bcbcb9d 677
587d108e
SM
678(defstruct (flymake-ler
679 (:constructor nil)
680 (:constructor flymake-ler-make-ler (file line type text &optional full-file)))
681 file line type text full-file)
2aa13ec4
RS
682
683(defun flymake-ler-set-file (line-err-info file)
5323b4b0 684 (flymake-ler-make-ler file
587d108e
SM
685 (flymake-ler-line line-err-info)
686 (flymake-ler-type line-err-info)
687 (flymake-ler-text line-err-info)
688 (flymake-ler-full-file line-err-info)))
2aa13ec4
RS
689
690(defun flymake-ler-set-full-file (line-err-info full-file)
587d108e
SM
691 (flymake-ler-make-ler (flymake-ler-file line-err-info)
692 (flymake-ler-line line-err-info)
693 (flymake-ler-type line-err-info)
694 (flymake-ler-text line-err-info)
2aa13ec4
RS
695 full-file))
696
697(defun flymake-ler-set-line (line-err-info line)
587d108e 698 (flymake-ler-make-ler (flymake-ler-file line-err-info)
4bcbcb9d 699 line
587d108e
SM
700 (flymake-ler-type line-err-info)
701 (flymake-ler-text line-err-info)
702 (flymake-ler-full-file line-err-info)))
4bcbcb9d 703
2aa13ec4 704(defun flymake-get-line-err-count (line-err-info-list type)
5323b4b0 705 "Return number of errors of specified TYPE.
173569aa 706Value of TYPE is either \"e\" or \"w\"."
5323b4b0
RS
707 (let* ((idx 0)
708 (count (length line-err-info-list))
709 (err-count 0))
4bcbcb9d 710
5323b4b0 711 (while (< idx count)
587d108e 712 (when (equal type (flymake-ler-type (nth idx line-err-info-list)))
5323b4b0
RS
713 (setq err-count (1+ err-count)))
714 (setq idx (1+ idx)))
715 err-count))
4bcbcb9d 716
2aa13ec4 717(defun flymake-get-err-count (err-info-list type)
5323b4b0
RS
718 "Return number of errors of specified TYPE for ERR-INFO-LIST."
719 (let* ((idx 0)
720 (count (length err-info-list))
721 (err-count 0))
722 (while (< idx count)
723 (setq err-count (+ err-count (flymake-get-line-err-count (nth 1 (nth idx err-info-list)) type)))
724 (setq idx (1+ idx)))
725 err-count))
2aa13ec4
RS
726
727(defun flymake-fix-line-numbers (err-info-list min-line max-line)
5323b4b0 728 "Replace line numbers with fixed value.
2aa13ec4
RS
729If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE.
730If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE.
ce721de4 731The reason for this fix is because some compilers might report
2aa13ec4 732line number outside the file being compiled."
5323b4b0
RS
733 (let* ((count (length err-info-list))
734 (err-info nil)
735 (line 0))
736 (while (> count 0)
737 (setq err-info (nth (1- count) err-info-list))
738 (setq line (flymake-er-get-line err-info))
739 (when (or (< line min-line) (> line max-line))
740 (setq line (if (< line min-line) min-line max-line))
741 (setq err-info-list (flymake-set-at err-info-list (1- count)
742 (flymake-er-make-er line
743 (flymake-er-get-line-err-info-list err-info)))))
744 (setq count (1- count))))
745 err-info-list)
4bcbcb9d 746
4dd68f44 747(defun flymake-highlight-err-lines (err-info-list)
5323b4b0 748 "Highlight error lines in BUFFER using info from ERR-INFO-LIST."
4dd68f44
SM
749 (save-excursion
750 (dolist (err err-info-list)
751 (flymake-highlight-line (car err) (nth 1 err)))))
4bcbcb9d 752
2aa13ec4 753(defun flymake-overlay-p (ov)
5323b4b0
RS
754 "Determine whether overlay OV was created by flymake."
755 (and (overlayp ov) (overlay-get ov 'flymake-overlay)))
4bcbcb9d 756
2aa13ec4 757(defun flymake-make-overlay (beg end tooltip-text face mouse-face)
5323b4b0
RS
758 "Allocate a flymake overlay in range BEG and END."
759 (when (not (flymake-region-has-flymake-overlays beg end))
760 (let ((ov (make-overlay beg end nil t t)))
761 (overlay-put ov 'face face)
762 (overlay-put ov 'mouse-face mouse-face)
763 (overlay-put ov 'help-echo tooltip-text)
764 (overlay-put ov 'flymake-overlay t)
765 (overlay-put ov 'priority 100)
9a686ad2 766 ;;+(flymake-log 3 "created overlay %s" ov)
5323b4b0
RS
767 ov)
768 (flymake-log 3 "created an overlay at (%d-%d)" beg end)))
4bcbcb9d 769
4dd68f44 770(defun flymake-delete-own-overlays ()
5323b4b0 771 "Delete all flymake overlays in BUFFER."
4dd68f44
SM
772 (dolist (ol (overlays-in (point-min) (point-max)))
773 (when (flymake-overlay-p ol)
774 (delete-overlay ol)
775 ;;+(flymake-log 3 "deleted overlay %s" ol)
776 )))
4bcbcb9d 777
2aa13ec4 778(defun flymake-region-has-flymake-overlays (beg end)
5323b4b0 779 "Check if region specified by BEG and END has overlay.
2aa13ec4 780Return t if it has at least one flymake overlay, nil if no overlay."
5323b4b0
RS
781 (let ((ov (overlays-in beg end))
782 (has-flymake-overlays nil))
783 (while (consp ov)
784 (when (flymake-overlay-p (car ov))
785 (setq has-flymake-overlays t))
cd2325cd
SM
786 (setq ov (cdr ov)))
787 has-flymake-overlays))
4bcbcb9d 788
ded09abd 789(defface flymake-errline
9a686ad2
SM
790 ;;+ '((((class color)) (:foreground "OrangeRed" :bold t :underline t))
791 ;;+ '((((class color)) (:underline "OrangeRed"))
5323b4b0
RS
792 '((((class color)) (:background "LightPink"))
793 (t (:bold t)))
794 "Face used for marking error lines."
795 :group 'flymake)
4bcbcb9d 796
ded09abd 797(defface flymake-warnline
5323b4b0
RS
798 '((((class color)) (:background "LightBlue2"))
799 (t (:bold t)))
800 "Face used for marking warning lines."
801 :group 'flymake)
4bcbcb9d 802
2aa13ec4 803(defun flymake-highlight-line (line-no line-err-info-list)
5323b4b0 804 "Highlight line LINE-NO in current buffer.
173569aa 805Perhaps use text from LINE-ERR-INFO-LIST to enhance highlighting."
5323b4b0
RS
806 (goto-line line-no)
807 (let* ((line-beg (flymake-line-beginning-position))
808 (line-end (flymake-line-end-position))
809 (beg line-beg)
810 (end line-end)
587d108e 811 (tooltip-text (flymake-ler-text (nth 0 line-err-info-list)))
5323b4b0
RS
812 (face nil))
813
814 (goto-char line-beg)
815 (while (looking-at "[ \t]")
816 (forward-char))
4bcbcb9d 817
5323b4b0 818 (setq beg (point))
4bcbcb9d 819
5323b4b0
RS
820 (goto-char line-end)
821 (while (and (looking-at "[ \t\r\n]") (> (point) 1))
822 (backward-char))
4bcbcb9d 823
5323b4b0 824 (setq end (1+ (point)))
4bcbcb9d 825
5323b4b0
RS
826 (when (<= end beg)
827 (setq beg line-beg)
828 (setq end line-end))
2aa13ec4 829
5323b4b0
RS
830 (when (= end beg)
831 (goto-char end)
832 (forward-line)
833 (setq end (point)))
2aa13ec4 834
5323b4b0 835 (if (> (flymake-get-line-err-count line-err-info-list "e") 0)
ded09abd
MB
836 (setq face 'flymake-errline)
837 (setq face 'flymake-warnline))
2aa13ec4 838
5323b4b0 839 (flymake-make-overlay beg end tooltip-text face nil)))
4bcbcb9d 840
4dd68f44 841(defun flymake-parse-err-lines (err-info-list lines)
5323b4b0
RS
842 "Parse err LINES, store info in ERR-INFO-LIST."
843 (let* ((count (length lines))
844 (idx 0)
845 (line-err-info nil)
846 (real-file-name nil)
4dd68f44 847 (source-file-name buffer-file-name)
5323b4b0
RS
848 (get-real-file-name-f (flymake-get-real-file-name-function source-file-name)))
849
850 (while (< idx count)
851 (setq line-err-info (flymake-parse-line (nth idx lines)))
852 (when line-err-info
c07fa030 853 (setq real-file-name (funcall get-real-file-name-f
587d108e 854 (flymake-ler-file line-err-info)))
5323b4b0
RS
855 (setq line-err-info (flymake-ler-set-full-file line-err-info real-file-name))
856
857 (if (flymake-same-files real-file-name source-file-name)
858 (setq line-err-info (flymake-ler-set-file line-err-info nil))
859 (setq line-err-info (flymake-ler-set-file line-err-info (file-name-nondirectory real-file-name))))
860
861 (setq err-info-list (flymake-add-err-info err-info-list line-err-info)))
862 (flymake-log 3 "parsed '%s', %s line-err-info" (nth idx lines) (if line-err-info "got" "no"))
863 (setq idx (1+ idx)))
864 err-info-list))
4bcbcb9d 865
2aa13ec4 866(defun flymake-split-output (output)
5323b4b0 867 "Split OUTPUT into lines.
9a686ad2
SM
868Return last one as residual if it does not end with newline char.
869Returns ((LINES) RESIDUAL)."
5323b4b0
RS
870 (when (and output (> (length output) 0))
871 (let* ((lines (flymake-split-string output "[\n\r]+"))
872 (complete (equal "\n" (char-to-string (aref output (1- (length output))))))
873 (residual nil))
874 (when (not complete)
875 (setq residual (car (last lines)))
876 (setq lines (butlast lines)))
877 (list lines residual))))
4bcbcb9d 878
2aa13ec4 879(defun flymake-reformat-err-line-patterns-from-compile-el (original-list)
5323b4b0 880 "Grab error line patterns from ORIGINAL-LIST in compile.el format.
2aa13ec4 881Convert it to flymake internal format."
5323b4b0 882 (let* ((converted-list '()))
cd2325cd
SM
883 (dolist (item original-list)
884 (setq item (cdr item))
885 (let ((regexp (nth 0 item))
886 (file (nth 1 item))
887 (line (nth 2 item))
888 (col (nth 3 item)))
889 (if (consp file) (setq file (car file)))
890 (if (consp line) (setq line (car line)))
891 (if (consp col) (setq col (car col)))
d799c278 892
cd2325cd
SM
893 (when (not (functionp line))
894 (setq converted-list (cons (list regexp file line col) converted-list)))))
5323b4b0 895 converted-list))
2aa13ec4 896
c3b8339f 897(require 'compile)
5323b4b0
RS
898
899(defvar flymake-err-line-patterns ; regexp file-idx line-idx col-idx (optional) text-idx(optional), match-end to end of string is error text
900 (append
901 '(
9a686ad2 902 ;; MS Visual C++ 6.0
5323b4b0
RS
903 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) \: \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
904 1 3 nil 4)
9a686ad2 905 ;; jikes
5323b4b0
RS
906 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)\:\\([0-9]+\\)\:[0-9]+\:[0-9]+\:[0-9]+\: \\(\\(Error\\|Warning\\|Caution\\|Semantic Error\\):[ \t\n]*\\(.+\\)\\)"
907 1 3 nil 4)
9a686ad2 908 ;; MS midl
5323b4b0
RS
909 ("midl[ ]*:[ ]*\\(command line error .*\\)"
910 nil nil nil 1)
9a686ad2 911 ;; MS C#
5323b4b0
RS
912 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\),[0-9]+)\: \\(\\(error\\|warning\\|fatal error\\) \\(CS[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
913 1 3 nil 4)
9a686ad2 914 ;; perl
5323b4b0 915 ("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1)
eabd11d4
MH
916 ;; PHP
917 ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1)
9a686ad2
SM
918 ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1)
919 ;; ant/javac
a988c1c2 920 (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)\:\\([0-9]+\\)\:[ \t\n]*\\(.+\\)"
5323b4b0
RS
921 2 4 nil 5))
922 ;; compilation-error-regexp-alist)
ce721de4 923 (flymake-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist))
8c01db22
EZ
924 "Patterns for matching error/warning lines. Each pattern has the form
925\(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX).
9a686ad2
SM
926Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns
927from compile.el")
928
929;;(defcustom flymake-err-line-patterns
930;; '(
931;; ; MS Visual C++ 6.0
932;; ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) \: \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
933;; 1 3 4)
934;; ; jikes
935;; ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)\:\\([0-9]+\\)\:[0-9]+\:[0-9]+\:[0-9]+\: \\(\\(Error\\|Warning\\|Caution\\):[ \t\n]*\\(.+\\)\\)"
936;; 1 3 4))
937;; "patterns for matching error/warning lines, (regexp file-idx line-idx err-text-idx)"
938;; :group 'flymake
939;; :type '(repeat (string number number number))
940;;)
4bcbcb9d 941
2aa13ec4 942(defun flymake-parse-line (line)
173569aa
JB
943 "Parse LINE to see if it is an error or warning.
944Return its components if so, nil otherwise."
5323b4b0
RS
945 (let ((raw-file-name nil)
946 (line-no 0)
947 (err-type "e")
948 (err-text nil)
17404091 949 (patterns flymake-err-line-patterns)
5323b4b0 950 (matched nil))
17404091
SM
951 (while (and patterns (not matched))
952 (when (string-match (car (car patterns)) line)
953 (let* ((file-idx (nth 1 (car patterns)))
954 (line-idx (nth 2 (car patterns))))
5323b4b0
RS
955
956 (setq raw-file-name (if file-idx (match-string file-idx line) nil))
027a4b6b 957 (setq line-no (if line-idx (string-to-number (match-string line-idx line)) 0))
17404091
SM
958 (setq err-text (if (> (length (car patterns)) 4)
959 (match-string (nth 4 (car patterns)) line)
5323b4b0
RS
960 (flymake-patch-err-text (substring line (match-end 0)))))
961 (or err-text (setq err-text "<no error text>"))
962 (if (and err-text (string-match "^[wW]arning" err-text))
963 (setq err-type "w")
964 )
965 (flymake-log 3 "parse line: file-idx=%s line-idx=%s file=%s line=%s text=%s" file-idx line-idx
966 raw-file-name line-no err-text)
967 (setq matched t)))
17404091 968 (setq patterns (cdr patterns)))
5323b4b0
RS
969 (if matched
970 (flymake-ler-make-ler raw-file-name line-no err-type err-text)
971 ())))
4bcbcb9d 972
2aa13ec4 973(defun flymake-find-err-info (err-info-list line-no)
5323b4b0
RS
974 "Find (line-err-info-list pos) for specified LINE-NO."
975 (if err-info-list
976 (let* ((line-err-info-list nil)
977 (pos 0)
978 (count (length err-info-list)))
979
980 (while (and (< pos count) (< (car (nth pos err-info-list)) line-no))
981 (setq pos (1+ pos)))
982 (when (and (< pos count) (equal (car (nth pos err-info-list)) line-no))
983 (setq line-err-info-list (flymake-er-get-line-err-info-list (nth pos err-info-list))))
984 (list line-err-info-list pos))
985 '(nil 0)))
4bcbcb9d 986
2aa13ec4 987(defun flymake-line-err-info-is-less-or-equal (line-one line-two)
587d108e
SM
988 (or (string< (flymake-ler-type line-one) (flymake-ler-type line-two))
989 (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two))
990 (not (flymake-ler-file line-one)) (flymake-ler-file line-two))
991 (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two))
992 (or (and (flymake-ler-file line-one) (flymake-ler-file line-two))
993 (and (not (flymake-ler-file line-one)) (not (flymake-ler-file line-two)))))))
4bcbcb9d 994
2aa13ec4 995(defun flymake-add-line-err-info (line-err-info-list line-err-info)
53ec26ed
RS
996 "Update LINE-ERR-INFO-LIST with the error LINE-ERR-INFO.
997For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'.
998The new element is inserted in the proper position, according to
999the predicate `flymake-line-err-info-is-less-or-equal'.
1000The updated value of LINE-ERR-INFO-LIST is returned."
5323b4b0
RS
1001 (if (not line-err-info-list)
1002 (list line-err-info)
1003 (let* ((count (length line-err-info-list))
1004 (idx 0))
1005 (while (and (< idx count) (flymake-line-err-info-is-less-or-equal (nth idx line-err-info-list) line-err-info))
1006 (setq idx (1+ idx)))
1007 (cond ((equal 0 idx) (setq line-err-info-list (cons line-err-info line-err-info-list)))
1008 (t (setq line-err-info-list (flymake-ins-after line-err-info-list (1- idx) line-err-info))))
1009 line-err-info-list)))
4bcbcb9d 1010
2aa13ec4 1011(defun flymake-add-err-info (err-info-list line-err-info)
53ec26ed
RS
1012 "Update ERR-INFO-LIST with the error LINE-ERR-INFO, preserving sort order.
1013Returns the updated value of ERR-INFO-LIST.
1014For the format of ERR-INFO-LIST, see `flymake-err-info'.
1015For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'."
587d108e 1016 (let* ((line-no (if (flymake-ler-file line-err-info) 1 (flymake-ler-line line-err-info)))
5323b4b0
RS
1017 (info-and-pos (flymake-find-err-info err-info-list line-no))
1018 (exists (car info-and-pos))
1019 (pos (nth 1 info-and-pos))
1020 (line-err-info-list nil)
1021 (err-info nil))
1022
1023 (if exists
1024 (setq line-err-info-list (flymake-er-get-line-err-info-list (car (nthcdr pos err-info-list)))))
1025 (setq line-err-info-list (flymake-add-line-err-info line-err-info-list line-err-info))
1026
1027 (setq err-info (flymake-er-make-er line-no line-err-info-list))
1028 (cond (exists (setq err-info-list (flymake-set-at err-info-list pos err-info)))
1029 ((equal 0 pos) (setq err-info-list (cons err-info err-info-list)))
1030 (t (setq err-info-list (flymake-ins-after err-info-list (1- pos) err-info))))
1031 err-info-list))
4bcbcb9d 1032
2aa13ec4 1033(defun flymake-get-project-include-dirs-imp (basedir)
5323b4b0
RS
1034 "Include dirs for the project current file belongs to."
1035 (if (flymake-get-project-include-dirs-from-cache basedir)
1036 (progn
1037 (flymake-get-project-include-dirs-from-cache basedir))
9a686ad2 1038 ;;else
ff4e2883
KS
1039 (let* ((command-line (concat "make -C "
1040 (shell-quote-argument basedir)
1041 " DUMPVARS=INCLUDE_DIRS dumpvars"))
5323b4b0
RS
1042 (output (shell-command-to-string command-line))
1043 (lines (flymake-split-string output "\n"))
1044 (count (length lines))
1045 (idx 0)
1046 (inc-dirs nil))
1047 (while (and (< idx count) (not (string-match "^INCLUDE_DIRS=.*" (nth idx lines))))
1048 (setq idx (1+ idx)))
1049 (when (< idx count)
1050 (let* ((inc-lines (flymake-split-string (nth idx lines) " *-I"))
1051 (inc-count (length inc-lines)))
1052 (while (> inc-count 0)
1053 (when (not (string-match "^INCLUDE_DIRS=.*" (nth (1- inc-count) inc-lines)))
587d108e 1054 (push (flymake-replace-regexp-in-string "\"" "" (nth (1- inc-count) inc-lines)) inc-dirs))
5323b4b0
RS
1055 (setq inc-count (1- inc-count)))))
1056 (flymake-add-project-include-dirs-to-cache basedir inc-dirs)
1057 inc-dirs)))
4bcbcb9d
EZ
1058
1059(defcustom flymake-get-project-include-dirs-function 'flymake-get-project-include-dirs-imp
173569aa 1060 "Function used to get project include dirs, one parameter: basedir name."
5323b4b0
RS
1061 :group 'flymake
1062 :type 'function)
4bcbcb9d 1063
2aa13ec4 1064(defun flymake-get-project-include-dirs (basedir)
5323b4b0 1065 (funcall flymake-get-project-include-dirs-function basedir))
4bcbcb9d 1066
2aa13ec4 1067(defun flymake-get-system-include-dirs ()
5323b4b0
RS
1068 "System include dirs - from the 'INCLUDE' env setting."
1069 (let* ((includes (getenv "INCLUDE")))
1070 (if includes (flymake-split-string includes path-separator) nil)))
4bcbcb9d
EZ
1071
1072(defvar flymake-project-include-dirs-cache (flymake-makehash 'equal))
4bcbcb9d 1073
2aa13ec4 1074(defun flymake-get-project-include-dirs-from-cache (base-dir)
5323b4b0 1075 (gethash base-dir flymake-project-include-dirs-cache))
2aa13ec4
RS
1076
1077(defun flymake-add-project-include-dirs-to-cache (base-dir include-dirs)
5323b4b0 1078 (puthash base-dir include-dirs flymake-project-include-dirs-cache))
2aa13ec4
RS
1079
1080(defun flymake-clear-project-include-dirs-cache ()
5323b4b0 1081 (clrhash flymake-project-include-dirs-cache))
2aa13ec4
RS
1082
1083(defun flymake-get-include-dirs (base-dir)
5323b4b0
RS
1084 "Get dirs to use when resolving local file names."
1085 (let* ((include-dirs (append '(".") (flymake-get-project-include-dirs base-dir) (flymake-get-system-include-dirs))))
1086 include-dirs))
4bcbcb9d 1087
4dd68f44
SM
1088;; (defun flymake-restore-formatting ()
1089;; "Remove any formatting made by flymake."
1090;; )
4bcbcb9d 1091
c07fa030
SM
1092;; (defun flymake-get-program-dir (buffer)
1093;; "Get dir to start program in."
1094;; (unless (bufferp buffer)
1095;; (error "Invalid buffer"))
1096;; (with-current-buffer buffer
1097;; default-directory))
4bcbcb9d 1098
2aa13ec4 1099(defun flymake-safe-delete-file (file-name)
5323b4b0
RS
1100 (when (and file-name (file-exists-p file-name))
1101 (delete-file file-name)
1102 (flymake-log 1 "deleted file %s" file-name)))
4bcbcb9d 1103
2aa13ec4 1104(defun flymake-safe-delete-directory (dir-name)
5323b4b0
RS
1105 (condition-case err
1106 (progn
1107 (delete-directory dir-name)
1108 (flymake-log 1 "deleted dir %s" dir-name))
1109 (error
1110 (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name))))
4bcbcb9d 1111
ce721de4 1112(defcustom flymake-compilation-prevents-syntax-check t
5323b4b0
RS
1113 "If non-nil, syntax check won't be started in case compilation is running."
1114 :group 'flymake
1115 :type 'boolean)
4bcbcb9d 1116
4dd68f44
SM
1117(defun flymake-start-syntax-check ()
1118 "Start syntax checking for current buffer."
1119 (interactive)
1120 (flymake-log 3 "flymake is running: %s" flymake-is-running)
1121 (when (and (not flymake-is-running)
1122 (flymake-can-syntax-check-file buffer-file-name))
1123 (when (or (not flymake-compilation-prevents-syntax-check)
1124 (not (flymake-compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP")
1125 (flymake-clear-buildfile-cache)
1126 (flymake-clear-project-include-dirs-cache)
1127
1128 (setq flymake-check-was-interrupted nil)
4dd68f44
SM
1129
1130 (let* ((source-file-name buffer-file-name)
1131 (init-f (flymake-get-init-function source-file-name))
1132 (cleanup-f (flymake-get-cleanup-function source-file-name))
6df19241 1133 (cmd-and-args (funcall init-f))
4dd68f44
SM
1134 (cmd (nth 0 cmd-and-args))
1135 (args (nth 1 cmd-and-args))
1136 (dir (nth 2 cmd-and-args)))
1137 (if (not cmd-and-args)
1138 (progn
1139 (flymake-log 0 "init function %s for %s failed, cleaning up" init-f source-file-name)
c07fa030 1140 (funcall cleanup-f))
4dd68f44
SM
1141 (progn
1142 (setq flymake-last-change-time nil)
1143 (flymake-start-syntax-check-process cmd args dir)))))))
1144
1145(defun flymake-start-syntax-check-process (cmd args dir)
5323b4b0
RS
1146 "Start syntax check process."
1147 (let* ((process nil))
1148 (condition-case err
1149 (progn
1150 (when dir
1151 (let ((default-directory dir))
1152 (flymake-log 3 "starting process on dir %s" default-directory)))
43ed65ac 1153 (setq process (apply 'start-process "flymake-proc" (current-buffer) cmd args))
5323b4b0
RS
1154 (set-process-sentinel process 'flymake-process-sentinel)
1155 (set-process-filter process 'flymake-process-filter)
43ed65ac 1156 (push process flymake-processes)
5323b4b0 1157
4dd68f44
SM
1158 (setq flymake-is-running t)
1159 (setq flymake-last-change-time nil)
1160 (setq flymake-check-start-time (flymake-float-time))
5323b4b0 1161
4dd68f44 1162 (flymake-report-status nil "*")
5323b4b0 1163 (flymake-log 2 "started process %d, command=%s, dir=%s"
43ed65ac
SM
1164 (process-id process) (process-command process)
1165 default-directory)
5323b4b0
RS
1166 process)
1167 (error
1168 (let* ((err-str (format "Failed to launch syntax check process '%s' with args %s: %s"
1169 cmd args (error-message-string err)))
4dd68f44 1170 (source-file-name buffer-file-name)
5323b4b0
RS
1171 (cleanup-f (flymake-get-cleanup-function source-file-name)))
1172 (flymake-log 0 err-str)
c07fa030 1173 (funcall cleanup-f)
4dd68f44 1174 (flymake-report-fatal-status "PROCERR" err-str))))))
4bcbcb9d 1175
43ed65ac
SM
1176(defun flymake-kill-process (proc)
1177 "Kill process PROC."
1178 (kill-process proc)
1179 (let* ((buf (process-buffer proc)))
1180 (when (buffer-live-p buf)
1181 (with-current-buffer buf
53ec26ed 1182 (setq flymake-check-was-interrupted t))))
43ed65ac 1183 (flymake-log 1 "killed process %d" (process-id proc)))
4bcbcb9d 1184
2aa13ec4 1185(defun flymake-stop-all-syntax-checks ()
5323b4b0
RS
1186 "Kill all syntax check processes."
1187 (interactive)
43ed65ac
SM
1188 (while flymake-processes
1189 (flymake-kill-process (pop flymake-processes))))
4bcbcb9d 1190
2aa13ec4 1191(defun flymake-compilation-is-running ()
5323b4b0
RS
1192 (and (boundp 'compilation-in-progress)
1193 compilation-in-progress))
4bcbcb9d 1194
2aa13ec4 1195(defun flymake-compile ()
5323b4b0
RS
1196 "Kill all flymake syntax checks, start compilation."
1197 (interactive)
1198 (flymake-stop-all-syntax-checks)
1199 (call-interactively 'compile))
4bcbcb9d 1200
4bcbcb9d 1201(defcustom flymake-no-changes-timeout 0.5
5323b4b0
RS
1202 "Time to wait after last change before starting compilation."
1203 :group 'flymake
1204 :type 'number)
4bcbcb9d 1205
2aa13ec4 1206(defun flymake-on-timer-event (buffer)
5323b4b0 1207 "Start a syntax check for buffer BUFFER if necessary."
587d108e 1208 (when (buffer-live-p buffer)
cd2325cd 1209 (with-current-buffer buffer
53ec26ed
RS
1210 (when (and (not flymake-is-running)
1211 flymake-last-change-time
587d108e
SM
1212 (> (- (flymake-float-time) flymake-last-change-time)
1213 flymake-no-changes-timeout))
53ec26ed
RS
1214
1215 (setq flymake-last-change-time nil)
5323b4b0 1216 (flymake-log 3 "starting syntax check as more than 1 second passed since last change")
4dd68f44 1217 (flymake-start-syntax-check)))))
4bcbcb9d 1218
2aa13ec4 1219(defun flymake-current-line-no ()
5323b4b0 1220 "Return number of current line in current buffer."
587d108e 1221 (count-lines (point-min) (if (eobp) (point) (1+ (point)))))
4bcbcb9d 1222
4dd68f44 1223(defun flymake-count-lines ()
5323b4b0 1224 "Return number of lines in buffer BUFFER."
4dd68f44 1225 (count-lines (point-min) (point-max)))
4bcbcb9d 1226
2aa13ec4 1227(defun flymake-display-err-menu-for-current-line ()
5323b4b0
RS
1228 "Display a menu with errors/warnings for current line if it has errors and/or warnings."
1229 (interactive)
1230 (let* ((line-no (flymake-current-line-no))
53ec26ed 1231 (line-err-info-list (nth 0 (flymake-find-err-info flymake-err-info line-no)))
5323b4b0 1232 (menu-data (flymake-make-err-menu-data line-no line-err-info-list))
43ed65ac 1233 (choice nil))
5323b4b0
RS
1234 (if menu-data
1235 (progn
43ed65ac 1236 (setq choice (flymake-popup-menu menu-data))
5323b4b0
RS
1237 (flymake-log 3 "choice=%s" choice)
1238 (when choice
1239 (eval choice)))
1240 (flymake-log 1 "no errors for line %d" line-no))))
4bcbcb9d 1241
2aa13ec4 1242(defun flymake-make-err-menu-data (line-no line-err-info-list)
173569aa 1243 "Make a (menu-title (item-title item-action)*) list with errors/warnings from LINE-ERR-INFO-LIST."
5323b4b0
RS
1244 (let* ((menu-items nil))
1245 (when line-err-info-list
1246 (let* ((count (length line-err-info-list))
1247 (menu-item-text nil))
1248 (while (> count 0)
587d108e
SM
1249 (setq menu-item-text (flymake-ler-text (nth (1- count) line-err-info-list)))
1250 (let* ((file (flymake-ler-file (nth (1- count) line-err-info-list)))
1251 (full-file (flymake-ler-full-file (nth (1- count) line-err-info-list)))
1252 (line (flymake-ler-line (nth (1- count) line-err-info-list))))
5323b4b0
RS
1253 (if file
1254 (setq menu-item-text (concat menu-item-text " - " file "(" (format "%d" line) ")")))
1255 (setq menu-items (cons (list menu-item-text
1256 (if file (list 'flymake-goto-file-and-line full-file line) nil))
1257 menu-items)))
1258 (setq count (1- count)))
1259 (flymake-log 3 "created menu-items with %d item(s)" (length menu-items))))
1260 (if menu-items
1261 (let* ((menu-title (format "Line %d: %d error(s), %d warning(s)" line-no
1262 (flymake-get-line-err-count line-err-info-list "e")
1263 (flymake-get-line-err-count line-err-info-list "w"))))
1264 (list menu-title menu-items))
1265 nil)))
4bcbcb9d 1266
2aa13ec4 1267(defun flymake-goto-file-and-line (file line)
9a686ad2 1268 "Try to get buffer for FILE and goto line LINE in it."
5323b4b0 1269 (if (not (file-exists-p file))
caf790b6
MR
1270 (flymake-log 1 "File %s does not exist" file)
1271 (find-file file)
1272 (goto-line line)))
4bcbcb9d 1273
2aa13ec4 1274;; flymake minor mode declarations
4bd0a5d0 1275(defvar flymake-mode-line nil)
2aa13ec4 1276
4bcbcb9d 1277(make-variable-buffer-local 'flymake-mode-line)
2aa13ec4 1278
4bcbcb9d 1279(defvar flymake-mode-line-e-w nil)
2aa13ec4 1280
4bcbcb9d 1281(make-variable-buffer-local 'flymake-mode-line-e-w)
2aa13ec4 1282
4bcbcb9d 1283(defvar flymake-mode-line-status nil)
2aa13ec4 1284
4bcbcb9d 1285(make-variable-buffer-local 'flymake-mode-line-status)
4bcbcb9d 1286
4dd68f44 1287(defun flymake-report-status (e-w &optional status)
5323b4b0 1288 "Show status in mode line."
4dd68f44
SM
1289 (when e-w
1290 (setq flymake-mode-line-e-w e-w))
1291 (when status
1292 (setq flymake-mode-line-status status))
1293 (let* ((mode-line " Flymake"))
1294 (when (> (length flymake-mode-line-e-w) 0)
1295 (setq mode-line (concat mode-line ":" flymake-mode-line-e-w)))
1296 (setq mode-line (concat mode-line flymake-mode-line-status))
1297 (setq flymake-mode-line mode-line)
1298 (force-mode-line-update)))
4bcbcb9d 1299
2aa13ec4 1300(defun flymake-display-warning (warning)
5323b4b0
RS
1301 "Display a warning to user."
1302 (message-box warning))
4bcbcb9d
EZ
1303
1304(defcustom flymake-gui-warnings-enabled t
173569aa 1305 "Enables/disables GUI warnings."
5323b4b0
RS
1306 :group 'flymake
1307 :type 'boolean)
4bcbcb9d 1308
4dd68f44 1309(defun flymake-report-fatal-status (status warning)
5323b4b0
RS
1310 "Display a warning and switch flymake mode off."
1311 (when flymake-gui-warnings-enabled
1312 (flymake-display-warning (format "Flymake: %s. Flymake will be switched OFF" warning))
1313 )
4dd68f44
SM
1314 (flymake-mode 0)
1315 (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s"
1316 (buffer-name) status warning))
4bcbcb9d 1317
d57f6b5e
SM
1318(defcustom flymake-start-syntax-check-on-find-file t
1319 "Start syntax check on find file."
1320 :group 'flymake
1321 :type 'boolean)
1322
5ffc943b 1323;;;###autoload
4bd0a5d0
SM
1324(define-minor-mode flymake-mode
1325 "Minor mode to do on-the-fly syntax checking.
1326When called interactively, toggles the minor mode.
1327With arg, turn Flymake mode on if and only if arg is positive."
d799c278 1328 :group 'flymake :lighter flymake-mode-line
d57f6b5e
SM
1329 (cond
1330
1331 ;; Turning the mode ON.
1332 (flymake-mode
1333 (if (not (flymake-can-syntax-check-file buffer-file-name))
1334 (flymake-log 2 "flymake cannot check syntax in buffer %s" (buffer-name))
1335 (add-hook 'after-change-functions 'flymake-after-change-function nil t)
1336 (add-hook 'after-save-hook 'flymake-after-save-hook nil t)
1337 (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t)
1338 ;;+(add-hook 'find-file-hook 'flymake-find-file-hook)
1339
4dd68f44 1340 (flymake-report-status "" "")
c92d6023 1341
d57f6b5e
SM
1342 (setq flymake-timer
1343 (run-at-time nil 1 'flymake-on-timer-event (current-buffer)))
1344
1345 (when flymake-start-syntax-check-on-find-file
4dd68f44 1346 (flymake-start-syntax-check))))
d57f6b5e
SM
1347
1348 ;; Turning the mode OFF.
1349 (t
1350 (remove-hook 'after-change-functions 'flymake-after-change-function t)
1351 (remove-hook 'after-save-hook 'flymake-after-save-hook t)
1352 (remove-hook 'kill-buffer-hook 'flymake-kill-buffer-hook t)
1353 ;;+(remove-hook 'find-file-hook (function flymake-find-file-hook) t)
2aa13ec4 1354
4dd68f44 1355 (flymake-delete-own-overlays)
d57f6b5e
SM
1356
1357 (when flymake-timer
1358 (cancel-timer flymake-timer)
1359 (setq flymake-timer nil))
1360
bd6a83d5 1361 (setq flymake-is-running nil))))
4bcbcb9d 1362
4bcbcb9d 1363;;;###autoload
2aa13ec4 1364(defun flymake-mode-on ()
5323b4b0 1365 "Turn flymake mode on."
d57f6b5e
SM
1366 (flymake-mode 1)
1367 (flymake-log 1 "flymake mode turned ON for buffer %s" (buffer-name)))
4bcbcb9d
EZ
1368
1369;;;###autoload
2aa13ec4 1370(defun flymake-mode-off ()
5323b4b0 1371 "Turn flymake mode off."
d57f6b5e 1372 (flymake-mode 0)
bd6a83d5 1373 (flymake-log 1 "flymake mode turned OFF for buffer %s" (buffer-name)))
4bcbcb9d
EZ
1374
1375(defcustom flymake-start-syntax-check-on-newline t
5323b4b0
RS
1376 "Start syntax check if newline char was added/removed from the buffer."
1377 :group 'flymake
1378 :type 'boolean)
4bcbcb9d 1379
2aa13ec4 1380(defun flymake-after-change-function (start stop len)
9a686ad2
SM
1381 "Start syntax check for current buffer if it isn't already running."
1382 ;;+(flymake-log 0 "setting change time to %s" (flymake-float-time))
5323b4b0
RS
1383 (let((new-text (buffer-substring start stop)))
1384 (when (and flymake-start-syntax-check-on-newline (equal new-text "\n"))
1385 (flymake-log 3 "starting syntax check as new-line has been seen")
4dd68f44 1386 (flymake-start-syntax-check))
53ec26ed 1387 (setq flymake-last-change-time (flymake-float-time))))
4bcbcb9d 1388
2aa13ec4 1389(defun flymake-after-save-hook ()
5323b4b0
RS
1390 (if (local-variable-p 'flymake-mode (current-buffer)) ; (???) other way to determine whether flymake is active in buffer being saved?
1391 (progn
1392 (flymake-log 3 "starting syntax check as buffer was saved")
4dd68f44 1393 (flymake-start-syntax-check)))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???)
4bcbcb9d 1394
2aa13ec4 1395(defun flymake-kill-buffer-hook ()
53ec26ed
RS
1396 (when flymake-timer
1397 (cancel-timer flymake-timer)
1398 (setq flymake-timer nil)))
4bcbcb9d 1399
2aa13ec4 1400(defun flymake-find-file-hook ()
9a686ad2
SM
1401 ;;+(when flymake-start-syntax-check-on-find-file
1402 ;;+ (flymake-log 3 "starting syntax check on file open")
4dd68f44 1403 ;;+ (flymake-start-syntax-check)
9a686ad2 1404 ;;+)
5323b4b0 1405 (when (and (not (local-variable-p 'flymake-mode (current-buffer)))
d57f6b5e 1406 (flymake-can-syntax-check-file buffer-file-name))
5323b4b0
RS
1407 (flymake-mode)
1408 (flymake-log 3 "automatically turned ON flymake mode")))
4bcbcb9d 1409
2aa13ec4 1410(defun flymake-get-first-err-line-no (err-info-list)
5323b4b0
RS
1411 "Return first line with error."
1412 (when err-info-list
1413 (flymake-er-get-line (car err-info-list))))
4bcbcb9d 1414
2aa13ec4 1415(defun flymake-get-last-err-line-no (err-info-list)
5323b4b0
RS
1416 "Return last line with error."
1417 (when err-info-list
1418 (flymake-er-get-line (nth (1- (length err-info-list)) err-info-list))))
4bcbcb9d 1419
2aa13ec4 1420(defun flymake-get-next-err-line-no (err-info-list line-no)
5323b4b0
RS
1421 "Return next line with error."
1422 (when err-info-list
1423 (let* ((count (length err-info-list))
1424 (idx 0))
1425 (while (and (< idx count) (>= line-no (flymake-er-get-line (nth idx err-info-list))))
1426 (setq idx (1+ idx)))
1427 (if (< idx count)
1428 (flymake-er-get-line (nth idx err-info-list))))))
4bcbcb9d 1429
2aa13ec4 1430(defun flymake-get-prev-err-line-no (err-info-list line-no)
173569aa 1431 "Return previous line with error."
5323b4b0
RS
1432 (when err-info-list
1433 (let* ((count (length err-info-list)))
1434 (while (and (> count 0) (<= line-no (flymake-er-get-line (nth (1- count) err-info-list))))
1435 (setq count (1- count)))
1436 (if (> count 0)
1437 (flymake-er-get-line (nth (1- count) err-info-list))))))
4bcbcb9d 1438
2aa13ec4 1439(defun flymake-skip-whitespace ()
5323b4b0
RS
1440 "Move forward until non-whitespace is reached."
1441 (while (looking-at "[ \t]")
1442 (forward-char)))
4bcbcb9d 1443
2aa13ec4 1444(defun flymake-goto-line (line-no)
9a686ad2 1445 "Go to line LINE-NO, then skip whitespace."
5323b4b0
RS
1446 (goto-line line-no)
1447 (flymake-skip-whitespace))
4bcbcb9d 1448
2aa13ec4 1449(defun flymake-goto-next-error ()
9a686ad2 1450 "Go to next error in err ring."
5323b4b0 1451 (interactive)
53ec26ed 1452 (let ((line-no (flymake-get-next-err-line-no flymake-err-info (flymake-current-line-no))))
5323b4b0 1453 (when (not line-no)
53ec26ed 1454 (setq line-no (flymake-get-first-err-line-no flymake-err-info))
5323b4b0
RS
1455 (flymake-log 1 "passed end of file"))
1456 (if line-no
1457 (flymake-goto-line line-no)
1458 (flymake-log 1 "no errors in current buffer"))))
4bcbcb9d 1459
2aa13ec4 1460(defun flymake-goto-prev-error ()
173569aa 1461 "Go to previous error in err ring."
5323b4b0 1462 (interactive)
53ec26ed 1463 (let ((line-no (flymake-get-prev-err-line-no flymake-err-info (flymake-current-line-no))))
5323b4b0 1464 (when (not line-no)
53ec26ed 1465 (setq line-no (flymake-get-last-err-line-no flymake-err-info))
5323b4b0
RS
1466 (flymake-log 1 "passed beginning of file"))
1467 (if line-no
1468 (flymake-goto-line line-no)
1469 (flymake-log 1 "no errors in current buffer"))))
4bcbcb9d 1470
2aa13ec4 1471(defun flymake-patch-err-text (string)
5323b4b0
RS
1472 (if (string-match "^[\n\t :0-9]*\\(.*\\)$" string)
1473 (match-string 1 string)
1474 string))
4bcbcb9d
EZ
1475
1476;;;; general init-cleanup and helper routines
2aa13ec4 1477(defun flymake-create-temp-inplace (file-name prefix)
5323b4b0
RS
1478 (unless (stringp file-name)
1479 (error "Invalid file-name"))
1480 (or prefix
1481 (setq prefix "flymake"))
1482 (let* ((temp-name (concat (file-name-sans-extension file-name)
1483 "_" prefix
1484 (and (file-name-extension file-name)
1485 (concat "." (file-name-extension file-name))))))
1486 (flymake-log 3 "create-temp-inplace: file=%s temp=%s" file-name temp-name)
1487 temp-name))
4bcbcb9d 1488
2aa13ec4 1489(defun flymake-create-temp-with-folder-structure (file-name prefix)
5323b4b0
RS
1490 (unless (stringp file-name)
1491 (error "Invalid file-name"))
4bcbcb9d 1492
5323b4b0 1493 (let* ((dir (file-name-directory file-name))
6df19241
SM
1494 ;; Not sure what this slash-pos is all about, but I guess it's just
1495 ;; trying to remove the leading / of absolute file names.
5323b4b0 1496 (slash-pos (string-match "/" dir))
6df19241
SM
1497 (temp-dir (expand-file-name (substring dir (1+ slash-pos))
1498 (flymake-get-temp-dir))))
4bcbcb9d 1499
6df19241
SM
1500 (file-truename (expand-file-name (file-name-nondirectory file-name)
1501 temp-dir))))
4bcbcb9d 1502
2aa13ec4 1503(defun flymake-delete-temp-directory (dir-name)
9a686ad2 1504 "Attempt to delete temp dir created by `flymake-create-temp-with-folder-structure', do not fail on error."
5323b4b0 1505 (let* ((temp-dir (flymake-get-temp-dir))
587d108e 1506 (suffix (substring dir-name (1+ (length temp-dir)))))
5323b4b0
RS
1507
1508 (while (> (length suffix) 0)
6df19241 1509 (setq suffix (directory-file-name suffix))
9a686ad2 1510 ;;+(flymake-log 0 "suffix=%s" suffix)
6df19241
SM
1511 (flymake-safe-delete-directory
1512 (file-truename (expand-file-name suffix temp-dir)))
1513 (setq suffix (file-name-directory suffix)))))
4bcbcb9d 1514
5bcef417
SM
1515(defvar flymake-temp-source-file-name nil)
1516(make-variable-buffer-local 'flymake-temp-source-file-name)
1517
1518(defvar flymake-master-file-name nil)
1519(make-variable-buffer-local 'flymake-master-file-name)
1520
1521(defvar flymake-temp-master-file-name nil)
1522(make-variable-buffer-local 'flymake-temp-master-file-name)
1523
1524(defvar flymake-base-dir nil)
1525(make-variable-buffer-local 'flymake-base-dir)
1526
6df19241 1527(defun flymake-init-create-temp-buffer-copy (create-temp-f)
5323b4b0 1528 "Make a temporary copy of the current buffer, save its name in buffer data and return the name."
6df19241 1529 (let* ((source-file-name buffer-file-name)
5323b4b0 1530 (temp-source-file-name (funcall create-temp-f source-file-name "flymake")))
4bcbcb9d 1531
6df19241
SM
1532 (flymake-save-buffer-in-file temp-source-file-name)
1533 (setq flymake-temp-source-file-name temp-source-file-name)
5323b4b0 1534 temp-source-file-name))
4bcbcb9d 1535
c07fa030 1536(defun flymake-simple-cleanup ()
69df8d97 1537 "Do cleanup after `flymake-init-create-temp-buffer-copy'.
2aa13ec4 1538Delete temp file."
c07fa030
SM
1539 (flymake-safe-delete-file flymake-temp-source-file-name)
1540 (setq flymake-last-change-time nil))
4bcbcb9d 1541
c07fa030 1542(defun flymake-get-real-file-name (file-name-from-err-msg)
9a686ad2
SM
1543 "Translate file name from error message to \"real\" file name.
1544Return full-name. Names are real, not patched."
c07fa030
SM
1545 (let* ((real-name nil)
1546 (source-file-name buffer-file-name)
1547 (master-file-name flymake-master-file-name)
1548 (temp-source-file-name flymake-temp-source-file-name)
1549 (temp-master-file-name flymake-temp-master-file-name)
5bcef417 1550 (base-dirs
c07fa030 1551 (list flymake-base-dir
5bcef417
SM
1552 (file-name-directory source-file-name)
1553 (if master-file-name (file-name-directory master-file-name))))
c07fa030
SM
1554 (files (list (list source-file-name source-file-name)
1555 (list temp-source-file-name source-file-name)
1556 (list master-file-name master-file-name)
1557 (list temp-master-file-name master-file-name))))
5323b4b0
RS
1558
1559 (when (equal 0 (length file-name-from-err-msg))
1560 (setq file-name-from-err-msg source-file-name))
1561
1562 (setq real-name (flymake-get-full-patched-file-name file-name-from-err-msg base-dirs files))
9a686ad2 1563 ;; if real-name is nil, than file name from err msg is none of the files we've patched
5323b4b0
RS
1564 (if (not real-name)
1565 (setq real-name (flymake-get-full-nonpatched-file-name file-name-from-err-msg base-dirs)))
1566 (if (not real-name)
1567 (setq real-name file-name-from-err-msg))
1568 (setq real-name (flymake-fix-file-name real-name))
1569 (flymake-log 3 "get-real-file-name: file-name=%s real-name=%s" file-name-from-err-msg real-name)
1570 real-name))
4bcbcb9d 1571
2aa13ec4 1572(defun flymake-get-full-patched-file-name (file-name-from-err-msg base-dirs files)
5323b4b0
RS
1573 (let* ((base-dirs-count (length base-dirs))
1574 (file-count (length files))
1575 (real-name nil))
1576
1577 (while (and (not real-name) (> base-dirs-count 0))
1578 (setq file-count (length files))
1579 (while (and (not real-name) (> file-count 0))
1580 (let* ((this-dir (nth (1- base-dirs-count) base-dirs))
1581 (this-file (nth 0 (nth (1- file-count) files)))
1582 (this-real-name (nth 1 (nth (1- file-count) files))))
9a686ad2 1583 ;;+(flymake-log 0 "this-dir=%s this-file=%s this-real=%s msg-file=%s" this-dir this-file this-real-name file-name-from-err-msg)
5323b4b0 1584 (when (and this-dir this-file (flymake-same-files
b8471a02 1585 (expand-file-name file-name-from-err-msg this-dir)
5323b4b0
RS
1586 this-file))
1587 (setq real-name this-real-name)))
1588 (setq file-count (1- file-count)))
1589 (setq base-dirs-count (1- base-dirs-count)))
1590 real-name))
4bcbcb9d 1591
2aa13ec4 1592(defun flymake-get-full-nonpatched-file-name (file-name-from-err-msg base-dirs)
5323b4b0
RS
1593 (let* ((real-name nil))
1594 (if (file-name-absolute-p file-name-from-err-msg)
1595 (setq real-name file-name-from-err-msg)
1596 (let* ((base-dirs-count (length base-dirs)))
1597 (while (and (not real-name) (> base-dirs-count 0))
b8471a02
SM
1598 (let* ((full-name (expand-file-name file-name-from-err-msg
1599 (nth (1- base-dirs-count) base-dirs))))
5323b4b0
RS
1600 (if (file-exists-p full-name)
1601 (setq real-name full-name))
1602 (setq base-dirs-count (1- base-dirs-count))))))
1603 real-name))
4bcbcb9d 1604
6df19241 1605(defun flymake-init-find-buildfile-dir (source-file-name buildfile-name)
5323b4b0 1606 "Find buildfile, store its dir in buffer data and return its dir, if found."
5bcef417
SM
1607 (let* ((buildfile-dir
1608 (flymake-find-buildfile buildfile-name
5f73367d 1609 (file-name-directory source-file-name))))
5bcef417 1610 (if buildfile-dir
6df19241 1611 (setq flymake-base-dir buildfile-dir)
5bcef417 1612 (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name)
6df19241
SM
1613 (flymake-report-fatal-status
1614 "NOMK" (format "No buildfile (%s) found for %s"
1615 buildfile-name source-file-name)))))
4bcbcb9d 1616
587d108e 1617(defun flymake-init-create-temp-source-and-master-buffer-copy (get-incl-dirs-f create-temp-f master-file-masks include-regexp)
84bb5a0c 1618 "Find master file (or buffer), create its copy along with a copy of the source file."
c07fa030 1619 (let* ((source-file-name buffer-file-name)
6df19241 1620 (temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f))
5323b4b0
RS
1621 (master-and-temp-master (flymake-create-master-file
1622 source-file-name temp-source-file-name
1623 get-incl-dirs-f create-temp-f
587d108e 1624 master-file-masks include-regexp)))
5323b4b0
RS
1625
1626 (if (not master-and-temp-master)
1627 (progn
1628 (flymake-log 1 "cannot find master file for %s" source-file-name)
6df19241 1629 (flymake-report-status "!" "") ; NOMASTER
5bcef417 1630 nil)
6df19241
SM
1631 (setq flymake-master-file-name (nth 0 master-and-temp-master))
1632 (setq flymake-temp-master-file-name (nth 1 master-and-temp-master)))))
4bcbcb9d 1633
c07fa030
SM
1634(defun flymake-master-cleanup ()
1635 (flymake-simple-cleanup)
1636 (flymake-safe-delete-file flymake-temp-master-file-name))
4bcbcb9d
EZ
1637
1638;;;; make-specific init-cleanup routines
2aa13ec4 1639(defun flymake-get-syntax-check-program-args (source-file-name base-dir use-relative-base-dir use-relative-source get-cmd-line-f)
5323b4b0 1640 "Create a command line for syntax check using GET-CMD-LINE-F."
6fee12e6
SM
1641 (funcall get-cmd-line-f
1642 (if use-relative-source
1643 (file-relative-name source-file-name base-dir)
1644 source-file-name)
1645 (if use-relative-base-dir
1646 (file-relative-name base-dir
1647 (file-name-directory source-file-name))
1648 base-dir)))
4bcbcb9d 1649
2aa13ec4 1650(defun flymake-get-make-cmdline (source base-dir)
5323b4b0
RS
1651 (list "make"
1652 (list "-s"
1653 "-C"
1654 base-dir
1655 (concat "CHK_SOURCES=" source)
1656 "SYNTAX_CHECK_MODE=1"
1657 "check-syntax")))
4bcbcb9d 1658
2aa13ec4 1659(defun flymake-get-ant-cmdline (source base-dir)
5323b4b0
RS
1660 (list "ant"
1661 (list "-buildfile"
1662 (concat base-dir "/" "build.xml")
1663 (concat "-DCHK_SOURCES=" source)
1664 "check-syntax")))
4bcbcb9d 1665
6df19241 1666(defun flymake-simple-make-init-impl (create-temp-f use-relative-base-dir use-relative-source build-file-name get-cmdline-f)
5323b4b0 1667 "Create syntax check command line for a directly checked source file.
2aa13ec4 1668Use CREATE-TEMP-F for creating temp copy."
5323b4b0 1669 (let* ((args nil)
6df19241
SM
1670 (source-file-name buffer-file-name)
1671 (buildfile-dir (flymake-init-find-buildfile-dir source-file-name build-file-name)))
5323b4b0 1672 (if buildfile-dir
6df19241 1673 (let* ((temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)))
5323b4b0
RS
1674 (setq args (flymake-get-syntax-check-program-args temp-source-file-name buildfile-dir
1675 use-relative-base-dir use-relative-source
1676 get-cmdline-f))))
1677 args))
4bcbcb9d 1678
6df19241
SM
1679(defun flymake-simple-make-init ()
1680 (flymake-simple-make-init-impl 'flymake-create-temp-inplace t t "Makefile" 'flymake-get-make-cmdline))
4bcbcb9d 1681
587d108e 1682(defun flymake-master-make-init (get-incl-dirs-f master-file-masks include-regexp)
9a686ad2 1683 "Create make command line for a source file checked via master file compilation."
5323b4b0
RS
1684 (let* ((make-args nil)
1685 (temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy
6df19241 1686 get-incl-dirs-f 'flymake-create-temp-inplace
587d108e 1687 master-file-masks include-regexp)))
5323b4b0 1688 (when temp-master-file-name
6df19241 1689 (let* ((buildfile-dir (flymake-init-find-buildfile-dir temp-master-file-name "Makefile")))
5323b4b0
RS
1690 (if buildfile-dir
1691 (setq make-args (flymake-get-syntax-check-program-args
1692 temp-master-file-name buildfile-dir nil nil 'flymake-get-make-cmdline)))))
1693 make-args))
4bcbcb9d 1694
2aa13ec4 1695(defun flymake-find-make-buildfile (source-dir)
5f73367d 1696 (flymake-find-buildfile "Makefile" source-dir))
4bcbcb9d
EZ
1697
1698;;;; .h/make specific
6df19241
SM
1699(defun flymake-master-make-header-init ()
1700 (flymake-master-make-init 'flymake-get-include-dirs
1701 '("\\.cpp\\'" "\\.c\\'")
587d108e 1702 "[ \t]*#[ \t]*include[ \t]*\"\\([[:word:]0-9/\\_.]*%s\\)\""))
4bcbcb9d
EZ
1703
1704;;;; .java/make specific
6df19241
SM
1705(defun flymake-simple-make-java-init ()
1706 (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "Makefile" 'flymake-get-make-cmdline))
4bcbcb9d 1707
6df19241
SM
1708(defun flymake-simple-ant-java-init ()
1709 (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "build.xml" 'flymake-get-ant-cmdline))
4bcbcb9d 1710
c07fa030 1711(defun flymake-simple-java-cleanup ()
9a686ad2 1712 "Cleanup after `flymake-simple-make-java-init' -- delete temp file and dirs."
c07fa030
SM
1713 (flymake-safe-delete-file flymake-temp-source-file-name)
1714 (when flymake-temp-source-file-name
1715 (flymake-delete-temp-directory
1716 (file-name-directory flymake-temp-source-file-name))))
4bcbcb9d
EZ
1717
1718;;;; perl-specific init-cleanup routines
6df19241 1719(defun flymake-perl-init ()
4bd0a5d0 1720 (let* ((temp-file (flymake-init-create-temp-buffer-copy
6df19241 1721 'flymake-create-temp-inplace))
6fee12e6
SM
1722 (local-file (file-relative-name
1723 temp-file
1724 (file-name-directory buffer-file-name))))
5323b4b0 1725 (list "perl" (list "-wc " local-file))))
eabd11d4
MH
1726
1727;;;; php-specific init-cleanup routines
1728(defun flymake-php-init ()
1729 (let* ((temp-file (flymake-init-create-temp-buffer-copy
1730 'flymake-create-temp-inplace))
1731 (local-file (file-relative-name
1732 temp-file
1733 (file-name-directory buffer-file-name))))
1734 (list "php" (list "-f" local-file "-l"))))
4bcbcb9d
EZ
1735
1736;;;; tex-specific init-cleanup routines
2aa13ec4 1737(defun flymake-get-tex-args (file-name)
9a686ad2 1738 ;;(list "latex" (list "-c-style-errors" file-name))
5323b4b0 1739 (list "texify" (list "--pdf" "--tex-option=-c-style-errors" file-name)))
4bcbcb9d 1740
6df19241
SM
1741(defun flymake-simple-tex-init ()
1742 (flymake-get-tex-args (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace)))
4bcbcb9d 1743
6df19241 1744(defun flymake-master-tex-init ()
5323b4b0 1745 (let* ((temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy
6df19241
SM
1746 'flymake-get-include-dirs-dot 'flymake-create-temp-inplace
1747 '("\\.tex\\'")
587d108e 1748 "[ \t]*\\input[ \t]*{\\(.*%s\\)}")))
5323b4b0
RS
1749 (when temp-master-file-name
1750 (flymake-get-tex-args temp-master-file-name))))
4bcbcb9d 1751
2aa13ec4 1752(defun flymake-get-include-dirs-dot (base-dir)
5323b4b0 1753 '("."))
4bcbcb9d
EZ
1754
1755;;;; xml-specific init-cleanup routines
6df19241
SM
1756(defun flymake-xml-init ()
1757 (list "xml" (list "val" (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))))
2aa13ec4
RS
1758
1759(provide 'flymake)
4bcbcb9d 1760
9a686ad2 1761;; arch-tag: 8f0d6090-061d-4cac-8862-7c151c4a02dd
4bcbcb9d 1762;;; flymake.el ends here