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