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