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