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