aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoão Távora2017-08-17 15:38:12 +0100
committerJoão Távora2017-10-03 13:49:04 +0100
commitfe9dc7a087ad2b1ac94d32f975f649a2d7dfeb65 (patch)
tree67ae0d6f361a8c4b8227e403f2554e1435f98510
parentae42ae79f5b045456c49c7fd8a9b6ee8983080bf (diff)
downloademacs-fe9dc7a087ad2b1ac94d32f975f649a2d7dfeb65.tar.gz
emacs-fe9dc7a087ad2b1ac94d32f975f649a2d7dfeb65.zip
Split Flymake into flymake.el into flymake-proc.el (again!)
After deciding that this work would continue on master only, which caused two commits named Revert "Split flymake.el into flymake-proc.el and flymake-ui.el" and Revert "Add flymake-backends defcustom" to be added to the emacs-26 branch, further discussion reversed that decision. See: https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg01020.html https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg01030.html This means that those two commits MUST be merged to master AFTER ALL. flymke-proc.el contains the main syntax-checking backend, while flymake.el keeps mostly the UI part. * lisp/progmodes/flymake-proc.el: New file. Require flymake. * lisp/progmodes/flymake.el: Require flymake-proc.el at the end.
-rw-r--r--lisp/progmodes/flymake-proc.el1070
-rw-r--r--lisp/progmodes/flymake.el1096
2 files changed, 1105 insertions, 1061 deletions
diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el
new file mode 100644
index 00000000000..89633feaabd
--- /dev/null
+++ b/lisp/progmodes/flymake-proc.el
@@ -0,0 +1,1070 @@
1;;; flymake-proc.el --- Flymake for external syntax checker processes -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2003-2017 Free Software Foundation, Inc.
4
5;; Author: Pavel Kobyakov <pk_at_work@yahoo.com>
6;; Maintainer: Leo Liu <sdl.web@gmail.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 checks.
28;;
29;; This file contains the most original implementation of flymake's
30;; main source of on-the-fly diagnostic info, the external syntax
31;; checker backend.
32;;
33;;; Bugs/todo:
34
35;; - Only uses "Makefile", not "makefile" or "GNUmakefile"
36;; (from http://bugs.debian.org/337339).
37
38;;; Code:
39
40(require 'flymake)
41
42(defcustom flymake-compilation-prevents-syntax-check t
43 "If non-nil, don't start syntax check if compilation is running."
44 :group 'flymake
45 :type 'boolean)
46
47(defcustom flymake-xml-program
48 (if (executable-find "xmlstarlet") "xmlstarlet" "xml")
49 "Program to use for XML validation."
50 :type 'file
51 :group 'flymake
52 :version "24.4")
53
54(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest")
55 "Dirs where to look for master files."
56 :group 'flymake
57 :type '(repeat (string)))
58
59(defcustom flymake-master-file-count-limit 32
60 "Max number of master files to check."
61 :group 'flymake
62 :type 'integer)
63
64(defcustom flymake-allowed-file-name-masks
65 '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" flymake-simple-make-init)
66 ("\\.xml\\'" flymake-xml-init)
67 ("\\.html?\\'" flymake-xml-init)
68 ("\\.cs\\'" flymake-simple-make-init)
69 ("\\.p[ml]\\'" flymake-perl-init)
70 ("\\.php[345]?\\'" flymake-php-init)
71 ("\\.h\\'" flymake-master-make-header-init flymake-master-cleanup)
72 ("\\.java\\'" flymake-simple-make-java-init flymake-simple-java-cleanup)
73 ("[0-9]+\\.tex\\'" flymake-master-tex-init flymake-master-cleanup)
74 ("\\.tex\\'" flymake-simple-tex-init)
75 ("\\.idl\\'" flymake-simple-make-init)
76 ;; ("\\.cpp\\'" 1)
77 ;; ("\\.java\\'" 3)
78 ;; ("\\.h\\'" 2 ("\\.cpp\\'" "\\.c\\'")
79 ;; ("[ \t]*#[ \t]*include[ \t]*\"\\([\w0-9/\\_\.]*[/\\]*\\)\\(%s\\)\"" 1 2))
80 ;; ("\\.idl\\'" 1)
81 ;; ("\\.odl\\'" 1)
82 ;; ("[0-9]+\\.tex\\'" 2 ("\\.tex\\'")
83 ;; ("[ \t]*\\input[ \t]*{\\(.*\\)\\(%s\\)}" 1 2 ))
84 ;; ("\\.tex\\'" 1)
85 )
86 "Files syntax checking is allowed for.
87This is an alist with elements of the form:
88 REGEXP INIT [CLEANUP [NAME]]
89REGEXP is a regular expression that matches a file name.
90INIT is the init function to use.
91CLEANUP is the cleanup function to use, default `flymake-simple-cleanup'.
92NAME is the file name function to use, default `flymake-get-real-file-name'."
93 :group 'flymake
94 :type '(alist :key-type (regexp :tag "File regexp")
95 :value-type
96 (list :tag "Handler functions"
97 (function :tag "Init function")
98 (choice :tag "Cleanup function"
99 (const :tag "flymake-simple-cleanup" nil)
100 function)
101 (choice :tag "Name function"
102 (const :tag "flymake-get-real-file-name" nil)
103 function))))
104
105(defvar flymake-processes nil
106 "List of currently active flymake processes.")
107
108(defvar-local flymake-output-residual nil)
109
110(defun flymake-get-file-name-mode-and-masks (file-name)
111 "Return the corresponding entry from `flymake-allowed-file-name-masks'."
112 (unless (stringp file-name)
113 (error "Invalid file-name"))
114 (let ((fnm flymake-allowed-file-name-masks)
115 (mode-and-masks nil))
116 (while (and (not mode-and-masks) fnm)
117 (if (string-match (car (car fnm)) file-name)
118 (setq mode-and-masks (cdr (car fnm))))
119 (setq fnm (cdr fnm)))
120 (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks))
121 mode-and-masks))
122
123(defun flymake-can-syntax-check-file (file-name)
124 "Determine whether we can syntax check FILE-NAME.
125Return nil if we cannot, non-nil if we can."
126 (if (flymake-get-init-function file-name) t nil))
127
128(defun flymake-get-init-function (file-name)
129 "Return init function to be used for the file."
130 (let* ((init-f (nth 0 (flymake-get-file-name-mode-and-masks file-name))))
131 ;;(flymake-log 0 "calling %s" init-f)
132 ;;(funcall init-f (current-buffer))
133 init-f))
134
135(defun flymake-get-cleanup-function (file-name)
136 "Return cleanup function to be used for the file."
137 (or (nth 1 (flymake-get-file-name-mode-and-masks file-name))
138 'flymake-simple-cleanup))
139
140(defun flymake-get-real-file-name-function (file-name)
141 (or (nth 2 (flymake-get-file-name-mode-and-masks file-name))
142 'flymake-get-real-file-name))
143
144(defvar flymake-find-buildfile-cache (make-hash-table :test #'equal))
145
146(defun flymake-get-buildfile-from-cache (dir-name)
147 "Look up DIR-NAME in cache and return its associated value.
148If DIR-NAME is not found, return nil."
149 (gethash dir-name flymake-find-buildfile-cache))
150
151(defun flymake-add-buildfile-to-cache (dir-name buildfile)
152 "Associate DIR-NAME with BUILDFILE in the buildfile cache."
153 (puthash dir-name buildfile flymake-find-buildfile-cache))
154
155(defun flymake-clear-buildfile-cache ()
156 "Clear the buildfile cache."
157 (clrhash flymake-find-buildfile-cache))
158
159(defun flymake-find-buildfile (buildfile-name source-dir-name)
160 "Find buildfile starting from current directory.
161Buildfile includes Makefile, build.xml etc.
162Return its file name if found, or nil if not found."
163 (or (flymake-get-buildfile-from-cache source-dir-name)
164 (let* ((file (locate-dominating-file source-dir-name buildfile-name)))
165 (if file
166 (progn
167 (flymake-log 3 "found buildfile at %s" file)
168 (flymake-add-buildfile-to-cache source-dir-name file)
169 file)
170 (progn
171 (flymake-log 3 "buildfile for %s not found" source-dir-name)
172 nil)))))
173
174(defun flymake-fix-file-name (name)
175 "Replace all occurrences of `\\' with `/'."
176 (when name
177 (setq name (expand-file-name name))
178 (setq name (abbreviate-file-name name))
179 (setq name (directory-file-name name))
180 name))
181
182(defun flymake-same-files (file-name-one file-name-two)
183 "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file.
184Return t if so, nil if not."
185 (equal (flymake-fix-file-name file-name-one)
186 (flymake-fix-file-name file-name-two)))
187
188;; This is bound dynamically to pass a parameter to a sort predicate below
189(defvar flymake-included-file-name)
190
191(defun flymake-find-possible-master-files (file-name master-file-dirs masks)
192 "Find (by name and location) all possible master files.
193
194Name is specified by FILE-NAME and location is specified by
195MASTER-FILE-DIRS. Master files include .cpp and .c for .h.
196Files are searched for starting from the .h directory and max
197max-level parent dirs. File contents are not checked."
198 (let* ((dirs master-file-dirs)
199 (files nil)
200 (done nil))
201
202 (while (and (not done) dirs)
203 (let* ((dir (expand-file-name (car dirs) (file-name-directory file-name)))
204 (masks masks))
205 (while (and (file-exists-p dir) (not done) masks)
206 (let* ((mask (car masks))
207 (dir-files (directory-files dir t mask)))
208
209 (flymake-log 3 "dir %s, %d file(s) for mask %s"
210 dir (length dir-files) mask)
211 (while (and (not done) dir-files)
212 (when (not (file-directory-p (car dir-files)))
213 (setq files (cons (car dir-files) files))
214 (when (>= (length files) flymake-master-file-count-limit)
215 (flymake-log 3 "master file count limit (%d) reached" flymake-master-file-count-limit)
216 (setq done t)))
217 (setq dir-files (cdr dir-files))))
218 (setq masks (cdr masks))))
219 (setq dirs (cdr dirs)))
220 (when files
221 (let ((flymake-included-file-name (file-name-nondirectory file-name)))
222 (setq files (sort files 'flymake-master-file-compare))))
223 (flymake-log 3 "found %d possible master file(s)" (length files))
224 files))
225
226(defun flymake-master-file-compare (file-one file-two)
227 "Compare two files specified by FILE-ONE and FILE-TWO.
228This function is used in sort to move most possible file names
229to the beginning of the list (File.h -> File.cpp moved to top)."
230 (and (equal (file-name-sans-extension flymake-included-file-name)
231 (file-name-base file-one))
232 (not (equal file-one file-two))))
233
234(defvar flymake-check-file-limit 8192
235 "Maximum number of chars to look at when checking possible master file.
236Nil means search the entire file.")
237
238(defun flymake-check-patch-master-file-buffer
239 (master-file-temp-buffer
240 master-file-name patched-master-file-name
241 source-file-name patched-source-file-name
242 include-dirs regexp)
243 "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME.
244If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME
245instead of SOURCE-FILE-NAME.
246
247For example, foo.cpp is a master file if it includes foo.h.
248
249When a buffer for MASTER-FILE-NAME exists, use it as a source
250instead of reading master file from disk."
251 (let* ((source-file-nondir (file-name-nondirectory source-file-name))
252 (source-file-extension (file-name-extension source-file-nondir))
253 (source-file-nonext (file-name-sans-extension source-file-nondir))
254 (found nil)
255 (inc-name nil)
256 (search-limit flymake-check-file-limit))
257 (setq regexp
258 (format regexp ; "[ \t]*#[ \t]*include[ \t]*\"\\(.*%s\\)\""
259 ;; Hack for tex files, where \include often excludes .tex.
260 ;; Maybe this is safe generally.
261 (if (and (> (length source-file-extension) 1)
262 (string-equal source-file-extension "tex"))
263 (format "%s\\(?:\\.%s\\)?"
264 (regexp-quote source-file-nonext)
265 (regexp-quote source-file-extension))
266 (regexp-quote source-file-nondir))))
267 (unwind-protect
268 (with-current-buffer master-file-temp-buffer
269 (if (or (not search-limit)
270 (> search-limit (point-max)))
271 (setq search-limit (point-max)))
272 (flymake-log 3 "checking %s against regexp %s"
273 master-file-name regexp)
274 (goto-char (point-min))
275 (while (and (< (point) search-limit)
276 (re-search-forward regexp search-limit t))
277 (let ((match-beg (match-beginning 1))
278 (match-end (match-end 1)))
279
280 (flymake-log 3 "found possible match for %s" source-file-nondir)
281 (setq inc-name (match-string 1))
282 (and (> (length source-file-extension) 1)
283 (string-equal source-file-extension "tex")
284 (not (string-match (format "\\.%s\\'" source-file-extension)
285 inc-name))
286 (setq inc-name (concat inc-name "." source-file-extension)))
287 (when (eq t (compare-strings
288 source-file-nondir nil nil
289 inc-name (- (length inc-name)
290 (length source-file-nondir)) nil))
291 (flymake-log 3 "inc-name=%s" inc-name)
292 (when (flymake-check-include source-file-name inc-name
293 include-dirs)
294 (setq found t)
295 ;; replace-match is not used here as it fails in
296 ;; XEmacs with 'last match not a buffer' error as
297 ;; check-includes calls replace-in-string
298 (flymake-replace-region
299 match-beg match-end
300 (file-name-nondirectory patched-source-file-name))))
301 (forward-line 1)))
302 (when found
303 (flymake-save-buffer-in-file patched-master-file-name)))
304 ;;+(flymake-log 3 "killing buffer %s"
305 ;; (buffer-name master-file-temp-buffer))
306 (kill-buffer master-file-temp-buffer))
307 ;;+(flymake-log 3 "check-patch master file %s: %s" master-file-name found)
308 (when found
309 (flymake-log 2 "found master file %s" master-file-name))
310 found))
311
312;;; XXX: remove
313(defun flymake-replace-region (beg end rep)
314 "Replace text in BUFFER in region (BEG END) with REP."
315 (save-excursion
316 (goto-char end)
317 ;; Insert before deleting, so as to better preserve markers's positions.
318 (insert rep)
319 (delete-region beg end)))
320
321(defun flymake-read-file-to-temp-buffer (file-name)
322 "Insert contents of FILE-NAME into newly created temp buffer."
323 (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name))))))
324 (with-current-buffer temp-buffer
325 (insert-file-contents file-name))
326 temp-buffer))
327
328(defun flymake-copy-buffer-to-temp-buffer (buffer)
329 "Copy contents of BUFFER into newly created temp buffer."
330 (with-current-buffer
331 (get-buffer-create (generate-new-buffer-name
332 (concat "flymake:" (buffer-name buffer))))
333 (insert-buffer-substring buffer)
334 (current-buffer)))
335
336(defun flymake-check-include (source-file-name inc-name include-dirs)
337 "Check if SOURCE-FILE-NAME can be found in include path.
338Return t if it can be found via include path using INC-NAME."
339 (if (file-name-absolute-p inc-name)
340 (flymake-same-files source-file-name inc-name)
341 (while (and include-dirs
342 (not (flymake-same-files
343 source-file-name
344 (concat (file-name-directory source-file-name)
345 "/" (car include-dirs)
346 "/" inc-name))))
347 (setq include-dirs (cdr include-dirs)))
348 include-dirs))
349
350(defun flymake-find-buffer-for-file (file-name)
351 "Check if there exists a buffer visiting FILE-NAME.
352Return t if so, nil if not."
353 (let ((buffer-name (get-file-buffer file-name)))
354 (if buffer-name
355 (get-buffer buffer-name))))
356
357(defun flymake-create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp)
358 "Save SOURCE-FILE-NAME with a different name.
359Find master file, patch and save it."
360 (let* ((possible-master-files (flymake-find-possible-master-files source-file-name flymake-master-file-dirs masks))
361 (master-file-count (length possible-master-files))
362 (idx 0)
363 (temp-buffer nil)
364 (master-file-name nil)
365 (patched-master-file-name nil)
366 (found nil))
367
368 (while (and (not found) (< idx master-file-count))
369 (setq master-file-name (nth idx possible-master-files))
370 (setq patched-master-file-name (funcall create-temp-f master-file-name "flymake_master"))
371 (if (flymake-find-buffer-for-file master-file-name)
372 (setq temp-buffer (flymake-copy-buffer-to-temp-buffer (flymake-find-buffer-for-file master-file-name)))
373 (setq temp-buffer (flymake-read-file-to-temp-buffer master-file-name)))
374 (setq found
375 (flymake-check-patch-master-file-buffer
376 temp-buffer
377 master-file-name
378 patched-master-file-name
379 source-file-name
380 patched-source-file-name
381 (funcall get-incl-dirs-f (file-name-directory master-file-name))
382 include-regexp))
383 (setq idx (1+ idx)))
384 (if found
385 (list master-file-name patched-master-file-name)
386 (progn
387 (flymake-log 3 "none of %d master file(s) checked includes %s" master-file-count
388 (file-name-nondirectory source-file-name))
389 nil))))
390
391(defun flymake-save-buffer-in-file (file-name)
392 "Save the entire buffer contents into file FILE-NAME.
393Create parent directories as needed."
394 (make-directory (file-name-directory file-name) 1)
395 (write-region nil nil file-name nil 566)
396 (flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name))
397
398(defun flymake-process-filter (process output)
399 "Parse OUTPUT and highlight error lines.
400It's flymake process filter."
401 (let ((source-buffer (process-buffer process)))
402
403 (flymake-log 3 "received %d byte(s) of output from process %d"
404 (length output) (process-id process))
405 (when (buffer-live-p source-buffer)
406 (with-current-buffer source-buffer
407 (flymake-parse-output-and-residual output)))))
408
409(defun flymake-process-sentinel (process _event)
410 "Sentinel for syntax check buffers."
411 (when (memq (process-status process) '(signal exit))
412 (let* ((exit-status (process-exit-status process))
413 (command (process-command process))
414 (source-buffer (process-buffer process))
415 (cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer))))
416
417 (flymake-log 2 "process %d exited with code %d"
418 (process-id process) exit-status)
419 (condition-case err
420 (progn
421 (flymake-log 3 "cleaning up using %s" cleanup-f)
422 (when (buffer-live-p source-buffer)
423 (with-current-buffer source-buffer
424 (funcall cleanup-f)))
425
426 (delete-process process)
427 (setq flymake-processes (delq process flymake-processes))
428
429 (when (buffer-live-p source-buffer)
430 (with-current-buffer source-buffer
431
432 (flymake-parse-residual)
433 (flymake-post-syntax-check exit-status command)
434 (setq flymake-is-running nil))))
435 (error
436 (let ((err-str (format "Error in process sentinel for buffer %s: %s"
437 source-buffer (error-message-string err))))
438 (flymake-log 0 err-str)
439 (with-current-buffer source-buffer
440 (setq flymake-is-running nil))))))))
441
442(defun flymake-post-syntax-check (exit-status command)
443 (save-restriction
444 (widen)
445 (setq flymake-err-info flymake-new-err-info)
446 (setq flymake-new-err-info nil)
447 (setq flymake-err-info
448 (flymake-fix-line-numbers
449 flymake-err-info 1 (count-lines (point-min) (point-max))))
450 (flymake-delete-own-overlays)
451 (flymake-highlight-err-lines flymake-err-info)
452 (let (err-count warn-count)
453 (setq err-count (flymake-get-err-count flymake-err-info "e"))
454 (setq warn-count (flymake-get-err-count flymake-err-info "w"))
455 (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)"
456 (buffer-name) err-count warn-count
457 (- (float-time) flymake-check-start-time))
458 (setq flymake-check-start-time nil)
459
460 (if (and (equal 0 err-count) (equal 0 warn-count))
461 (if (equal 0 exit-status)
462 (flymake-report-status "" "") ; PASSED
463 (if (not flymake-check-was-interrupted)
464 (flymake-report-fatal-status "CFGERR"
465 (format "Configuration error has occurred while running %s" command))
466 (flymake-report-status nil ""))) ; "STOPPED"
467 (flymake-report-status (format "%d/%d" err-count warn-count) "")))))
468
469(defun flymake-parse-output-and-residual (output)
470 "Split OUTPUT into lines, merge in residual if necessary."
471 (let* ((buffer-residual flymake-output-residual)
472 (total-output (if buffer-residual (concat buffer-residual output) output))
473 (lines-and-residual (flymake-split-output total-output))
474 (lines (nth 0 lines-and-residual))
475 (new-residual (nth 1 lines-and-residual)))
476 (setq flymake-output-residual new-residual)
477 (setq flymake-new-err-info
478 (flymake-parse-err-lines
479 flymake-new-err-info lines))))
480
481(defvar-local flymake-new-err-info nil
482 "Same as `flymake-err-info', effective when a syntax check is in progress.")
483
484(defun flymake-parse-residual ()
485 "Parse residual if it's non empty."
486 (when flymake-output-residual
487 (setq flymake-new-err-info
488 (flymake-parse-err-lines
489 flymake-new-err-info
490 (list flymake-output-residual)))
491 (setq flymake-output-residual nil)))
492
493(defun flymake-parse-err-lines (err-info-list lines)
494 "Parse err LINES, store info in ERR-INFO-LIST."
495 (let* ((count (length lines))
496 (idx 0)
497 (line-err-info nil)
498 (real-file-name nil)
499 (source-file-name buffer-file-name)
500 (get-real-file-name-f (flymake-get-real-file-name-function source-file-name)))
501
502 (while (< idx count)
503 (setq line-err-info (flymake-parse-line (nth idx lines)))
504 (when line-err-info
505 (setq real-file-name (funcall get-real-file-name-f
506 (flymake-ler-file line-err-info)))
507 (setq line-err-info (flymake-ler-set-full-file line-err-info real-file-name))
508
509 (when (flymake-same-files real-file-name source-file-name)
510 (setq line-err-info (flymake-ler-set-file line-err-info nil))
511 (setq err-info-list (flymake-add-err-info err-info-list line-err-info))))
512 (flymake-log 3 "parsed `%s', %s line-err-info" (nth idx lines) (if line-err-info "got" "no"))
513 (setq idx (1+ idx)))
514 err-info-list))
515
516(defun flymake-split-output (output)
517 "Split OUTPUT into lines.
518Return last one as residual if it does not end with newline char.
519Returns ((LINES) RESIDUAL)."
520 (when (and output (> (length output) 0))
521 (let* ((lines (split-string output "[\n\r]+" t))
522 (complete (equal "\n" (char-to-string (aref output (1- (length output))))))
523 (residual nil))
524 (when (not complete)
525 (setq residual (car (last lines)))
526 (setq lines (butlast lines)))
527 (list lines residual))))
528
529(defun flymake-reformat-err-line-patterns-from-compile-el (original-list)
530 "Grab error line patterns from ORIGINAL-LIST in compile.el format.
531Convert it to flymake internal format."
532 (let* ((converted-list '()))
533 (dolist (item original-list)
534 (setq item (cdr item))
535 (let ((regexp (nth 0 item))
536 (file (nth 1 item))
537 (line (nth 2 item))
538 (col (nth 3 item)))
539 (if (consp file) (setq file (car file)))
540 (if (consp line) (setq line (car line)))
541 (if (consp col) (setq col (car col)))
542
543 (when (not (functionp line))
544 (setq converted-list (cons (list regexp file line col) converted-list)))))
545 converted-list))
546
547(require 'compile)
548
549(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
550 (append
551 '(
552 ;; MS Visual C++ 6.0
553 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) : \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
554 1 3 nil 4)
555 ;; jikes
556 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\):[0-9]+:[0-9]+:[0-9]+: \\(\\(Error\\|Warning\\|Caution\\|Semantic Error\\):[ \t\n]*\\(.+\\)\\)"
557 1 3 nil 4)
558 ;; MS midl
559 ("midl[ ]*:[ ]*\\(command line error .*\\)"
560 nil nil nil 1)
561 ;; MS C#
562 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\),[0-9]+): \\(\\(error\\|warning\\|fatal error\\) \\(CS[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
563 1 3 nil 4)
564 ;; perl
565 ("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1)
566 ;; PHP
567 ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1)
568 ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1)
569 ;; ant/javac. Note this also matches gcc warnings!
570 (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::[0-9]+\\)?:[ \t\n]*\\(.+\\)"
571 2 4 nil 5))
572 ;; compilation-error-regexp-alist)
573 (flymake-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist))
574 "Patterns for matching error/warning lines. Each pattern has the form
575\(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX).
576Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns
577from compile.el")
578
579(define-obsolete-variable-alias 'flymake-warning-re 'flymake-warning-predicate "24.4")
580(defvar flymake-warning-predicate "^[wW]arning"
581 "Predicate matching against error text to detect a warning.
582Takes a single argument, the error's text and should return non-nil
583if it's a warning.
584Instead of a function, it can also be a regular expression.")
585
586(defun flymake-parse-line (line)
587 "Parse LINE to see if it is an error or warning.
588Return its components if so, nil otherwise."
589 (let ((raw-file-name nil)
590 (line-no 0)
591 (err-type "e")
592 (err-text nil)
593 (patterns flymake-err-line-patterns)
594 (matched nil))
595 (while (and patterns (not matched))
596 (when (string-match (car (car patterns)) line)
597 (let* ((file-idx (nth 1 (car patterns)))
598 (line-idx (nth 2 (car patterns))))
599
600 (setq raw-file-name (if file-idx (match-string file-idx line) nil))
601 (setq line-no (if line-idx (string-to-number
602 (match-string line-idx line)) 0))
603 (setq err-text (if (> (length (car patterns)) 4)
604 (match-string (nth 4 (car patterns)) line)
605 (flymake-patch-err-text
606 (substring line (match-end 0)))))
607 (if (null err-text)
608 (setq err-text "<no error text>")
609 (when (cond ((stringp flymake-warning-predicate)
610 (string-match flymake-warning-predicate err-text))
611 ((functionp flymake-warning-predicate)
612 (funcall flymake-warning-predicate err-text)))
613 (setq err-type "w")))
614 (flymake-log
615 3 "parse line: file-idx=%s line-idx=%s file=%s line=%s text=%s"
616 file-idx line-idx raw-file-name line-no err-text)
617 (setq matched t)))
618 (setq patterns (cdr patterns)))
619 (if matched
620 (flymake-ler-make-ler raw-file-name line-no err-type err-text)
621 ())))
622
623(defun flymake-get-project-include-dirs-imp (basedir)
624 "Include dirs for the project current file belongs to."
625 (if (flymake-get-project-include-dirs-from-cache basedir)
626 (progn
627 (flymake-get-project-include-dirs-from-cache basedir))
628 ;;else
629 (let* ((command-line (concat "make -C "
630 (shell-quote-argument basedir)
631 " DUMPVARS=INCLUDE_DIRS dumpvars"))
632 (output (shell-command-to-string command-line))
633 (lines (split-string output "\n" t))
634 (count (length lines))
635 (idx 0)
636 (inc-dirs nil))
637 (while (and (< idx count) (not (string-match "^INCLUDE_DIRS=.*" (nth idx lines))))
638 (setq idx (1+ idx)))
639 (when (< idx count)
640 (let* ((inc-lines (split-string (nth idx lines) " *-I" t))
641 (inc-count (length inc-lines)))
642 (while (> inc-count 0)
643 (when (not (string-match "^INCLUDE_DIRS=.*" (nth (1- inc-count) inc-lines)))
644 (push (replace-regexp-in-string "\"" "" (nth (1- inc-count) inc-lines)) inc-dirs))
645 (setq inc-count (1- inc-count)))))
646 (flymake-add-project-include-dirs-to-cache basedir inc-dirs)
647 inc-dirs)))
648
649(defvar flymake-get-project-include-dirs-function #'flymake-get-project-include-dirs-imp
650 "Function used to get project include dirs, one parameter: basedir name.")
651
652(defun flymake-get-project-include-dirs (basedir)
653 (funcall flymake-get-project-include-dirs-function basedir))
654
655(defun flymake-get-system-include-dirs ()
656 "System include dirs - from the `INCLUDE' env setting."
657 (let* ((includes (getenv "INCLUDE")))
658 (if includes (split-string includes path-separator t) nil)))
659
660(defvar flymake-project-include-dirs-cache (make-hash-table :test #'equal))
661
662(defun flymake-get-project-include-dirs-from-cache (base-dir)
663 (gethash base-dir flymake-project-include-dirs-cache))
664
665(defun flymake-add-project-include-dirs-to-cache (base-dir include-dirs)
666 (puthash base-dir include-dirs flymake-project-include-dirs-cache))
667
668(defun flymake-clear-project-include-dirs-cache ()
669 (clrhash flymake-project-include-dirs-cache))
670
671(defun flymake-get-include-dirs (base-dir)
672 "Get dirs to use when resolving local file names."
673 (let* ((include-dirs (append '(".") (flymake-get-project-include-dirs base-dir) (flymake-get-system-include-dirs))))
674 include-dirs))
675
676;; (defun flymake-restore-formatting ()
677;; "Remove any formatting made by flymake."
678;; )
679
680;; (defun flymake-get-program-dir (buffer)
681;; "Get dir to start program in."
682;; (unless (bufferp buffer)
683;; (error "Invalid buffer"))
684;; (with-current-buffer buffer
685;; default-directory))
686
687(defun flymake-safe-delete-file (file-name)
688 (when (and file-name (file-exists-p file-name))
689 (delete-file file-name)
690 (flymake-log 1 "deleted file %s" file-name)))
691
692(defun flymake-safe-delete-directory (dir-name)
693 (condition-case nil
694 (progn
695 (delete-directory dir-name)
696 (flymake-log 1 "deleted dir %s" dir-name))
697 (error
698 (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name))))
699
700(defun flymake-start-syntax-check ()
701 "Start syntax checking for current buffer."
702 (interactive)
703 (flymake-log 3 "flymake is running: %s" flymake-is-running)
704 (when (and (not flymake-is-running)
705 (flymake-can-syntax-check-file buffer-file-name))
706 (when (or (not flymake-compilation-prevents-syntax-check)
707 (not (flymake-compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP")
708 (flymake-clear-buildfile-cache)
709 (flymake-clear-project-include-dirs-cache)
710
711 (setq flymake-check-was-interrupted nil)
712
713 (let* ((source-file-name buffer-file-name)
714 (init-f (flymake-get-init-function source-file-name))
715 (cleanup-f (flymake-get-cleanup-function source-file-name))
716 (cmd-and-args (funcall init-f))
717 (cmd (nth 0 cmd-and-args))
718 (args (nth 1 cmd-and-args))
719 (dir (nth 2 cmd-and-args)))
720 (if (not cmd-and-args)
721 (progn
722 (flymake-log 0 "init function %s for %s failed, cleaning up" init-f source-file-name)
723 (funcall cleanup-f))
724 (progn
725 (setq flymake-last-change-time nil)
726 (flymake-start-syntax-check-process cmd args dir)))))))
727
728(defun flymake-start-syntax-check-process (cmd args dir)
729"Start syntax check process."
730(condition-case err
731 (let* ((process
732 (let ((default-directory (or dir default-directory)))
733 (when dir
734 (flymake-log 3 "starting process on dir %s" dir))
735 (apply 'start-file-process
736 "flymake-proc" (current-buffer) cmd args))))
737 (set-process-sentinel process 'flymake-process-sentinel)
738 (set-process-filter process 'flymake-process-filter)
739 (set-process-query-on-exit-flag process nil)
740 (push process flymake-processes)
741
742 (setq flymake-is-running t)
743 (setq flymake-last-change-time nil)
744
745 (flymake-report-status nil "*")
746 (flymake-log 2 "started process %d, command=%s, dir=%s"
747 (process-id process) (process-command process)
748 default-directory)
749 process)
750 (error
751 (let* ((err-str
752 (format-message
753 "Failed to launch syntax check process `%s' with args %s: %s"
754 cmd args (error-message-string err)))
755 (source-file-name buffer-file-name)
756 (cleanup-f (flymake-get-cleanup-function source-file-name)))
757 (flymake-log 0 err-str)
758 (funcall cleanup-f)
759 (flymake-report-fatal-status "PROCERR" err-str)))))
760
761(defun flymake-kill-process (proc)
762 "Kill process PROC."
763 (kill-process proc)
764 (let* ((buf (process-buffer proc)))
765 (when (buffer-live-p buf)
766 (with-current-buffer buf
767 (setq flymake-check-was-interrupted t))))
768 (flymake-log 1 "killed process %d" (process-id proc)))
769
770(defun flymake-stop-all-syntax-checks ()
771 "Kill all syntax check processes."
772 (interactive)
773 (while flymake-processes
774 (flymake-kill-process (pop flymake-processes))))
775
776(defun flymake-compilation-is-running ()
777 (and (boundp 'compilation-in-progress)
778 compilation-in-progress))
779
780(defun flymake-compile ()
781 "Kill all flymake syntax checks, start compilation."
782 (interactive)
783 (flymake-stop-all-syntax-checks)
784 (call-interactively 'compile))
785
786;;;; general init-cleanup and helper routines
787(defun flymake-create-temp-inplace (file-name prefix)
788 (unless (stringp file-name)
789 (error "Invalid file-name"))
790 (or prefix
791 (setq prefix "flymake"))
792 (let* ((ext (file-name-extension file-name))
793 (temp-name (file-truename
794 (concat (file-name-sans-extension file-name)
795 "_" prefix
796 (and ext (concat "." ext))))))
797 (flymake-log 3 "create-temp-inplace: file=%s temp=%s" file-name temp-name)
798 temp-name))
799
800(defun flymake-create-temp-with-folder-structure (file-name _prefix)
801 (unless (stringp file-name)
802 (error "Invalid file-name"))
803
804 (let* ((dir (file-name-directory file-name))
805 ;; Not sure what this slash-pos is all about, but I guess it's just
806 ;; trying to remove the leading / of absolute file names.
807 (slash-pos (string-match "/" dir))
808 (temp-dir (expand-file-name (substring dir (1+ slash-pos))
809 temporary-file-directory)))
810
811 (file-truename (expand-file-name (file-name-nondirectory file-name)
812 temp-dir))))
813
814(defun flymake-delete-temp-directory (dir-name)
815 "Attempt to delete temp dir created by `flymake-create-temp-with-folder-structure', do not fail on error."
816 (let* ((temp-dir temporary-file-directory)
817 (suffix (substring dir-name (1+ (length temp-dir)))))
818
819 (while (> (length suffix) 0)
820 (setq suffix (directory-file-name suffix))
821 ;;+(flymake-log 0 "suffix=%s" suffix)
822 (flymake-safe-delete-directory
823 (file-truename (expand-file-name suffix temp-dir)))
824 (setq suffix (file-name-directory suffix)))))
825
826(defvar-local flymake-temp-source-file-name nil)
827(defvar-local flymake-master-file-name nil)
828(defvar-local flymake-temp-master-file-name nil)
829(defvar-local flymake-base-dir nil)
830
831(defun flymake-init-create-temp-buffer-copy (create-temp-f)
832 "Make a temporary copy of the current buffer, save its name in buffer data and return the name."
833 (let* ((source-file-name buffer-file-name)
834 (temp-source-file-name (funcall create-temp-f source-file-name "flymake")))
835
836 (flymake-save-buffer-in-file temp-source-file-name)
837 (setq flymake-temp-source-file-name temp-source-file-name)
838 temp-source-file-name))
839
840(defun flymake-simple-cleanup ()
841 "Do cleanup after `flymake-init-create-temp-buffer-copy'.
842Delete temp file."
843 (flymake-safe-delete-file flymake-temp-source-file-name)
844 (setq flymake-last-change-time nil))
845
846(defun flymake-get-real-file-name (file-name-from-err-msg)
847 "Translate file name from error message to \"real\" file name.
848Return full-name. Names are real, not patched."
849 (let* ((real-name nil)
850 (source-file-name buffer-file-name)
851 (master-file-name flymake-master-file-name)
852 (temp-source-file-name flymake-temp-source-file-name)
853 (temp-master-file-name flymake-temp-master-file-name)
854 (base-dirs
855 (list flymake-base-dir
856 (file-name-directory source-file-name)
857 (if master-file-name (file-name-directory master-file-name))))
858 (files (list (list source-file-name source-file-name)
859 (list temp-source-file-name source-file-name)
860 (list master-file-name master-file-name)
861 (list temp-master-file-name master-file-name))))
862
863 (when (equal 0 (length file-name-from-err-msg))
864 (setq file-name-from-err-msg source-file-name))
865
866 (setq real-name (flymake-get-full-patched-file-name file-name-from-err-msg base-dirs files))
867 ;; if real-name is nil, than file name from err msg is none of the files we've patched
868 (if (not real-name)
869 (setq real-name (flymake-get-full-nonpatched-file-name file-name-from-err-msg base-dirs)))
870 (if (not real-name)
871 (setq real-name file-name-from-err-msg))
872 (setq real-name (flymake-fix-file-name real-name))
873 (flymake-log 3 "get-real-file-name: file-name=%s real-name=%s" file-name-from-err-msg real-name)
874 real-name))
875
876(defun flymake-get-full-patched-file-name (file-name-from-err-msg base-dirs files)
877 (let* ((base-dirs-count (length base-dirs))
878 (file-count (length files))
879 (real-name nil))
880
881 (while (and (not real-name) (> base-dirs-count 0))
882 (setq file-count (length files))
883 (while (and (not real-name) (> file-count 0))
884 (let* ((this-dir (nth (1- base-dirs-count) base-dirs))
885 (this-file (nth 0 (nth (1- file-count) files)))
886 (this-real-name (nth 1 (nth (1- file-count) files))))
887 ;;+(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)
888 (when (and this-dir this-file (flymake-same-files
889 (expand-file-name file-name-from-err-msg this-dir)
890 this-file))
891 (setq real-name this-real-name)))
892 (setq file-count (1- file-count)))
893 (setq base-dirs-count (1- base-dirs-count)))
894 real-name))
895
896(defun flymake-get-full-nonpatched-file-name (file-name-from-err-msg base-dirs)
897 (let* ((real-name nil))
898 (if (file-name-absolute-p file-name-from-err-msg)
899 (setq real-name file-name-from-err-msg)
900 (let* ((base-dirs-count (length base-dirs)))
901 (while (and (not real-name) (> base-dirs-count 0))
902 (let* ((full-name (expand-file-name file-name-from-err-msg
903 (nth (1- base-dirs-count) base-dirs))))
904 (if (file-exists-p full-name)
905 (setq real-name full-name))
906 (setq base-dirs-count (1- base-dirs-count))))))
907 real-name))
908
909(defun flymake-init-find-buildfile-dir (source-file-name buildfile-name)
910 "Find buildfile, store its dir in buffer data and return its dir, if found."
911 (let* ((buildfile-dir
912 (flymake-find-buildfile buildfile-name
913 (file-name-directory source-file-name))))
914 (if buildfile-dir
915 (setq flymake-base-dir buildfile-dir)
916 (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name)
917 (flymake-report-fatal-status
918 "NOMK" (format "No buildfile (%s) found for %s"
919 buildfile-name source-file-name)))))
920
921(defun flymake-init-create-temp-source-and-master-buffer-copy (get-incl-dirs-f create-temp-f master-file-masks include-regexp)
922 "Find master file (or buffer), create its copy along with a copy of the source file."
923 (let* ((source-file-name buffer-file-name)
924 (temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f))
925 (master-and-temp-master (flymake-create-master-file
926 source-file-name temp-source-file-name
927 get-incl-dirs-f create-temp-f
928 master-file-masks include-regexp)))
929
930 (if (not master-and-temp-master)
931 (progn
932 (flymake-log 1 "cannot find master file for %s" source-file-name)
933 (flymake-report-status "!" "") ; NOMASTER
934 nil)
935 (setq flymake-master-file-name (nth 0 master-and-temp-master))
936 (setq flymake-temp-master-file-name (nth 1 master-and-temp-master)))))
937
938(defun flymake-master-cleanup ()
939 (flymake-simple-cleanup)
940 (flymake-safe-delete-file flymake-temp-master-file-name))
941
942;;;; make-specific init-cleanup routines
943(defun flymake-get-syntax-check-program-args (source-file-name base-dir use-relative-base-dir use-relative-source get-cmd-line-f)
944 "Create a command line for syntax check using GET-CMD-LINE-F."
945 (funcall get-cmd-line-f
946 (if use-relative-source
947 (file-relative-name source-file-name base-dir)
948 source-file-name)
949 (if use-relative-base-dir
950 (file-relative-name base-dir
951 (file-name-directory source-file-name))
952 base-dir)))
953
954(defun flymake-get-make-cmdline (source base-dir)
955 (list "make"
956 (list "-s"
957 "-C"
958 base-dir
959 (concat "CHK_SOURCES=" source)
960 "SYNTAX_CHECK_MODE=1"
961 "check-syntax")))
962
963(defun flymake-get-ant-cmdline (source base-dir)
964 (list "ant"
965 (list "-buildfile"
966 (concat base-dir "/" "build.xml")
967 (concat "-DCHK_SOURCES=" source)
968 "check-syntax")))
969
970(defun flymake-simple-make-init-impl (create-temp-f use-relative-base-dir use-relative-source build-file-name get-cmdline-f)
971 "Create syntax check command line for a directly checked source file.
972Use CREATE-TEMP-F for creating temp copy."
973 (let* ((args nil)
974 (source-file-name buffer-file-name)
975 (buildfile-dir (flymake-init-find-buildfile-dir source-file-name build-file-name)))
976 (if buildfile-dir
977 (let* ((temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)))
978 (setq args (flymake-get-syntax-check-program-args temp-source-file-name buildfile-dir
979 use-relative-base-dir use-relative-source
980 get-cmdline-f))))
981 args))
982
983(defun flymake-simple-make-init ()
984 (flymake-simple-make-init-impl 'flymake-create-temp-inplace t t "Makefile" 'flymake-get-make-cmdline))
985
986(defun flymake-master-make-init (get-incl-dirs-f master-file-masks include-regexp)
987 "Create make command line for a source file checked via master file compilation."
988 (let* ((make-args nil)
989 (temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy
990 get-incl-dirs-f 'flymake-create-temp-inplace
991 master-file-masks include-regexp)))
992 (when temp-master-file-name
993 (let* ((buildfile-dir (flymake-init-find-buildfile-dir temp-master-file-name "Makefile")))
994 (if buildfile-dir
995 (setq make-args (flymake-get-syntax-check-program-args
996 temp-master-file-name buildfile-dir nil nil 'flymake-get-make-cmdline)))))
997 make-args))
998
999(defun flymake-find-make-buildfile (source-dir)
1000 (flymake-find-buildfile "Makefile" source-dir))
1001
1002;;;; .h/make specific
1003(defun flymake-master-make-header-init ()
1004 (flymake-master-make-init
1005 'flymake-get-include-dirs
1006 '("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'")
1007 "[ \t]*#[ \t]*include[ \t]*\"\\([[:word:]0-9/\\_.]*%s\\)\""))
1008
1009;;;; .java/make specific
1010(defun flymake-simple-make-java-init ()
1011 (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "Makefile" 'flymake-get-make-cmdline))
1012
1013(defun flymake-simple-ant-java-init ()
1014 (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "build.xml" 'flymake-get-ant-cmdline))
1015
1016(defun flymake-simple-java-cleanup ()
1017 "Cleanup after `flymake-simple-make-java-init' -- delete temp file and dirs."
1018 (flymake-safe-delete-file flymake-temp-source-file-name)
1019 (when flymake-temp-source-file-name
1020 (flymake-delete-temp-directory
1021 (file-name-directory flymake-temp-source-file-name))))
1022
1023;;;; perl-specific init-cleanup routines
1024(defun flymake-perl-init ()
1025 (let* ((temp-file (flymake-init-create-temp-buffer-copy
1026 'flymake-create-temp-inplace))
1027 (local-file (file-relative-name
1028 temp-file
1029 (file-name-directory buffer-file-name))))
1030 (list "perl" (list "-wc " local-file))))
1031
1032;;;; php-specific init-cleanup routines
1033(defun flymake-php-init ()
1034 (let* ((temp-file (flymake-init-create-temp-buffer-copy
1035 'flymake-create-temp-inplace))
1036 (local-file (file-relative-name
1037 temp-file
1038 (file-name-directory buffer-file-name))))
1039 (list "php" (list "-f" local-file "-l"))))
1040
1041;;;; tex-specific init-cleanup routines
1042(defun flymake-get-tex-args (file-name)
1043 ;;(list "latex" (list "-c-style-errors" file-name))
1044 (list "texify" (list "--pdf" "--tex-option=-c-style-errors" file-name)))
1045
1046(defun flymake-simple-tex-init ()
1047 (flymake-get-tex-args (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace)))
1048
1049;; Perhaps there should be a buffer-local variable flymake-master-file
1050;; that people can set to override this stuff. Could inherit from
1051;; the similar AUCTeX variable.
1052(defun flymake-master-tex-init ()
1053 (let* ((temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy
1054 'flymake-get-include-dirs-dot 'flymake-create-temp-inplace
1055 '("\\.tex\\'")
1056 "[ \t]*\\in\\(?:put\\|clude\\)[ \t]*{\\(.*%s\\)}")))
1057 (when temp-master-file-name
1058 (flymake-get-tex-args temp-master-file-name))))
1059
1060(defun flymake-get-include-dirs-dot (_base-dir)
1061 '("."))
1062
1063;;;; xml-specific init-cleanup routines
1064(defun flymake-xml-init ()
1065 (list flymake-xml-program
1066 (list "val" (flymake-init-create-temp-buffer-copy
1067 'flymake-create-temp-inplace))))
1068
1069(provide 'flymake-proc)
1070;;; flymake-proc.el ends here
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el
index 866116fbecd..441784c8a17 100644
--- a/lisp/progmodes/flymake.el
+++ b/lisp/progmodes/flymake.el
@@ -1,4 +1,4 @@
1;;; flymake.el --- a universal on-the-fly syntax checker -*- lexical-binding: t; -*- 1;;; flymake.el --- A universal on-the-fly syntax checker -*- lexical-binding: t; -*-
2 2
3;; Copyright (C) 2003-2017 Free Software Foundation, Inc. 3;; Copyright (C) 2003-2017 Free Software Foundation, Inc.
4 4
@@ -20,19 +20,16 @@
20;; GNU General Public License for more details. 20;; GNU General Public License for more details.
21 21
22;; You should have received a copy of the GNU General Public License 22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 24
25;;; Commentary: 25;;; Commentary:
26;; 26;;
27;; Flymake is a minor Emacs mode performing on-the-fly syntax checks 27;; Flymake is a minor Emacs mode performing on-the-fly syntax checks.
28;; using the external syntax check tool (for C/C++ this is usually the 28;;
29;; compiler). 29;; This file contains the UI for displaying and interacting with the
30 30;; results of such checks, as well as entry points for backends to
31;;; Bugs/todo: 31;; hook on to. Backends are sources of diagnostic info.
32 32;;
33;; - Only uses "Makefile", not "makefile" or "GNUmakefile"
34;; (from http://bugs.debian.org/337339).
35
36;;; Code: 33;;; Code:
37 34
38(eval-when-compile (require 'cl-lib)) 35(eval-when-compile (require 'cl-lib))
@@ -83,11 +80,6 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'."
83 (const right-fringe) 80 (const right-fringe)
84 (const :tag "No fringe indicators" nil))) 81 (const :tag "No fringe indicators" nil)))
85 82
86(defcustom flymake-compilation-prevents-syntax-check t
87 "If non-nil, don't start syntax check if compilation is running."
88 :group 'flymake
89 :type 'boolean)
90
91(defcustom flymake-start-syntax-check-on-newline t 83(defcustom flymake-start-syntax-check-on-newline t
92 "Start syntax check if newline char was added/removed from the buffer." 84 "Start syntax check if newline char was added/removed from the buffer."
93 :group 'flymake 85 :group 'flymake
@@ -116,69 +108,6 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'."
116 :group 'flymake 108 :group 'flymake
117 :type 'integer) 109 :type 'integer)
118 110
119(defcustom flymake-xml-program
120 (if (executable-find "xmlstarlet") "xmlstarlet" "xml")
121 "Program to use for XML validation."
122 :type 'file
123 :group 'flymake
124 :version "24.4")
125
126(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest")
127 "Dirs where to look for master files."
128 :group 'flymake
129 :type '(repeat (string)))
130
131(defcustom flymake-master-file-count-limit 32
132 "Max number of master files to check."
133 :group 'flymake
134 :type 'integer)
135
136(defcustom flymake-allowed-file-name-masks
137 '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" flymake-simple-make-init)
138 ("\\.xml\\'" flymake-xml-init)
139 ("\\.html?\\'" flymake-xml-init)
140 ("\\.cs\\'" flymake-simple-make-init)
141 ("\\.p[ml]\\'" flymake-perl-init)
142 ("\\.php[345]?\\'" flymake-php-init)
143 ("\\.h\\'" flymake-master-make-header-init flymake-master-cleanup)
144 ("\\.java\\'" flymake-simple-make-java-init flymake-simple-java-cleanup)
145 ("[0-9]+\\.tex\\'" flymake-master-tex-init flymake-master-cleanup)
146 ("\\.tex\\'" flymake-simple-tex-init)
147 ("\\.idl\\'" flymake-simple-make-init)
148 ;; ("\\.cpp\\'" 1)
149 ;; ("\\.java\\'" 3)
150 ;; ("\\.h\\'" 2 ("\\.cpp\\'" "\\.c\\'")
151 ;; ("[ \t]*#[ \t]*include[ \t]*\"\\([\w0-9/\\_\.]*[/\\]*\\)\\(%s\\)\"" 1 2))
152 ;; ("\\.idl\\'" 1)
153 ;; ("\\.odl\\'" 1)
154 ;; ("[0-9]+\\.tex\\'" 2 ("\\.tex\\'")
155 ;; ("[ \t]*\\input[ \t]*{\\(.*\\)\\(%s\\)}" 1 2 ))
156 ;; ("\\.tex\\'" 1)
157 )
158 "Files syntax checking is allowed for.
159This is an alist with elements of the form:
160 REGEXP INIT [CLEANUP [NAME]]
161REGEXP is a regular expression that matches a file name.
162INIT is the init function to use, missing means disable `flymake-mode'.
163CLEANUP is the cleanup function to use, default `flymake-simple-cleanup'.
164NAME is the file name function to use, default `flymake-get-real-file-name'."
165 :group 'flymake
166 :type '(alist :key-type (regexp :tag "File regexp")
167 :value-type
168 (list :tag "Handler functions"
169 (choice :tag "Init function"
170 (const :tag "disable" nil)
171 function)
172 (choice :tag "Cleanup function"
173 (const :tag "flymake-simple-cleanup" nil)
174 function)
175 (choice :tag "Name function"
176 (const :tag "flymake-get-real-file-name" nil)
177 function))))
178
179(defvar-local flymake-is-running nil
180 "If t, flymake syntax check process is running for the current buffer.")
181
182(defvar-local flymake-timer nil 111(defvar-local flymake-timer nil
183 "Timer for starting syntax check.") 112 "Timer for starting syntax check.")
184 113
@@ -221,392 +150,6 @@ POS counts from zero."
221 (setcar (nthcdr pos tmp) val) 150 (setcar (nthcdr pos tmp) val)
222 tmp)) 151 tmp))
223 152
224(defvar flymake-processes nil
225 "List of currently active flymake processes.")
226
227(defvar-local flymake-output-residual nil)
228
229(defun flymake-get-file-name-mode-and-masks (file-name)
230 "Return the corresponding entry from `flymake-allowed-file-name-masks'."
231 (unless (stringp file-name)
232 (error "Invalid file-name"))
233 (let ((fnm flymake-allowed-file-name-masks)
234 (mode-and-masks nil))
235 (while (and (not mode-and-masks) fnm)
236 (let ((item (pop fnm)))
237 (when (string-match (car item) file-name)
238 (setq mode-and-masks item)))) ; (cdr item) may be nil
239 (setq mode-and-masks (cdr mode-and-masks))
240 (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks))
241 mode-and-masks))
242
243(defun flymake-can-syntax-check-file (file-name)
244 "Determine whether we can syntax check FILE-NAME.
245Return nil if we cannot, non-nil if we can."
246 (if (flymake-get-init-function file-name) t nil))
247
248(defun flymake-get-init-function (file-name)
249 "Return init function to be used for the file."
250 (let* ((init-f (nth 0 (flymake-get-file-name-mode-and-masks file-name))))
251 ;;(flymake-log 0 "calling %s" init-f)
252 ;;(funcall init-f (current-buffer))
253 init-f))
254
255(defun flymake-get-cleanup-function (file-name)
256 "Return cleanup function to be used for the file."
257 (or (nth 1 (flymake-get-file-name-mode-and-masks file-name))
258 'flymake-simple-cleanup))
259
260(defun flymake-get-real-file-name-function (file-name)
261 (or (nth 2 (flymake-get-file-name-mode-and-masks file-name))
262 'flymake-get-real-file-name))
263
264(defvar flymake-find-buildfile-cache (make-hash-table :test #'equal))
265
266(defun flymake-get-buildfile-from-cache (dir-name)
267 "Look up DIR-NAME in cache and return its associated value.
268If DIR-NAME is not found, return nil."
269 (gethash dir-name flymake-find-buildfile-cache))
270
271(defun flymake-add-buildfile-to-cache (dir-name buildfile)
272 "Associate DIR-NAME with BUILDFILE in the buildfile cache."
273 (puthash dir-name buildfile flymake-find-buildfile-cache))
274
275(defun flymake-clear-buildfile-cache ()
276 "Clear the buildfile cache."
277 (clrhash flymake-find-buildfile-cache))
278
279(defun flymake-find-buildfile (buildfile-name source-dir-name)
280 "Find buildfile starting from current directory.
281Buildfile includes Makefile, build.xml etc.
282Return its file name if found, or nil if not found."
283 (or (flymake-get-buildfile-from-cache source-dir-name)
284 (let* ((file (locate-dominating-file source-dir-name buildfile-name)))
285 (if file
286 (progn
287 (flymake-log 3 "found buildfile at %s" file)
288 (flymake-add-buildfile-to-cache source-dir-name file)
289 file)
290 (progn
291 (flymake-log 3 "buildfile for %s not found" source-dir-name)
292 nil)))))
293
294(defun flymake-fix-file-name (name)
295 "Replace all occurrences of `\\' with `/'."
296 (when name
297 (setq name (expand-file-name name))
298 (setq name (abbreviate-file-name name))
299 (setq name (directory-file-name name))
300 name))
301
302(defun flymake-same-files (file-name-one file-name-two)
303 "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file.
304Return t if so, nil if not."
305 (equal (flymake-fix-file-name file-name-one)
306 (flymake-fix-file-name file-name-two)))
307
308;; This is bound dynamically to pass a parameter to a sort predicate below
309(defvar flymake-included-file-name)
310
311(defun flymake-find-possible-master-files (file-name master-file-dirs masks)
312 "Find (by name and location) all possible master files.
313
314Name is specified by FILE-NAME and location is specified by
315MASTER-FILE-DIRS. Master files include .cpp and .c for .h.
316Files are searched for starting from the .h directory and max
317max-level parent dirs. File contents are not checked."
318 (let* ((dirs master-file-dirs)
319 (files nil)
320 (done nil))
321
322 (while (and (not done) dirs)
323 (let* ((dir (expand-file-name (car dirs) (file-name-directory file-name)))
324 (masks masks))
325 (while (and (file-exists-p dir) (not done) masks)
326 (let* ((mask (car masks))
327 (dir-files (directory-files dir t mask)))
328
329 (flymake-log 3 "dir %s, %d file(s) for mask %s"
330 dir (length dir-files) mask)
331 (while (and (not done) dir-files)
332 (when (not (file-directory-p (car dir-files)))
333 (setq files (cons (car dir-files) files))
334 (when (>= (length files) flymake-master-file-count-limit)
335 (flymake-log 3 "master file count limit (%d) reached" flymake-master-file-count-limit)
336 (setq done t)))
337 (setq dir-files (cdr dir-files))))
338 (setq masks (cdr masks))))
339 (setq dirs (cdr dirs)))
340 (when files
341 (let ((flymake-included-file-name (file-name-nondirectory file-name)))
342 (setq files (sort files 'flymake-master-file-compare))))
343 (flymake-log 3 "found %d possible master file(s)" (length files))
344 files))
345
346(defun flymake-master-file-compare (file-one file-two)
347 "Compare two files specified by FILE-ONE and FILE-TWO.
348This function is used in sort to move most possible file names
349to the beginning of the list (File.h -> File.cpp moved to top)."
350 (and (equal (file-name-sans-extension flymake-included-file-name)
351 (file-name-base file-one))
352 (not (equal file-one file-two))))
353
354(defvar flymake-check-file-limit 8192
355 "Maximum number of chars to look at when checking possible master file.
356Nil means search the entire file.")
357
358(defun flymake-check-patch-master-file-buffer
359 (master-file-temp-buffer
360 master-file-name patched-master-file-name
361 source-file-name patched-source-file-name
362 include-dirs regexp)
363 "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME.
364If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME
365instead of SOURCE-FILE-NAME.
366
367For example, foo.cpp is a master file if it includes foo.h.
368
369When a buffer for MASTER-FILE-NAME exists, use it as a source
370instead of reading master file from disk."
371 (let* ((source-file-nondir (file-name-nondirectory source-file-name))
372 (source-file-extension (file-name-extension source-file-nondir))
373 (source-file-nonext (file-name-sans-extension source-file-nondir))
374 (found nil)
375 (inc-name nil)
376 (search-limit flymake-check-file-limit))
377 (setq regexp
378 (format regexp ; "[ \t]*#[ \t]*include[ \t]*\"\\(.*%s\\)\""
379 ;; Hack for tex files, where \include often excludes .tex.
380 ;; Maybe this is safe generally.
381 (if (and (> (length source-file-extension) 1)
382 (string-equal source-file-extension "tex"))
383 (format "%s\\(?:\\.%s\\)?"
384 (regexp-quote source-file-nonext)
385 (regexp-quote source-file-extension))
386 (regexp-quote source-file-nondir))))
387 (unwind-protect
388 (with-current-buffer master-file-temp-buffer
389 (if (or (not search-limit)
390 (> search-limit (point-max)))
391 (setq search-limit (point-max)))
392 (flymake-log 3 "checking %s against regexp %s"
393 master-file-name regexp)
394 (goto-char (point-min))
395 (while (and (< (point) search-limit)
396 (re-search-forward regexp search-limit t))
397 (let ((match-beg (match-beginning 1))
398 (match-end (match-end 1)))
399
400 (flymake-log 3 "found possible match for %s" source-file-nondir)
401 (setq inc-name (match-string 1))
402 (and (> (length source-file-extension) 1)
403 (string-equal source-file-extension "tex")
404 (not (string-match (format "\\.%s\\'" source-file-extension)
405 inc-name))
406 (setq inc-name (concat inc-name "." source-file-extension)))
407 (when (eq t (compare-strings
408 source-file-nondir nil nil
409 inc-name (- (length inc-name)
410 (length source-file-nondir)) nil))
411 (flymake-log 3 "inc-name=%s" inc-name)
412 (when (flymake-check-include source-file-name inc-name
413 include-dirs)
414 (setq found t)
415 ;; replace-match is not used here as it fails in
416 ;; XEmacs with 'last match not a buffer' error as
417 ;; check-includes calls replace-in-string
418 (flymake-replace-region
419 match-beg match-end
420 (file-name-nondirectory patched-source-file-name))))
421 (forward-line 1)))
422 (when found
423 (flymake-save-buffer-in-file patched-master-file-name)))
424 ;;+(flymake-log 3 "killing buffer %s"
425 ;; (buffer-name master-file-temp-buffer))
426 (kill-buffer master-file-temp-buffer))
427 ;;+(flymake-log 3 "check-patch master file %s: %s" master-file-name found)
428 (when found
429 (flymake-log 2 "found master file %s" master-file-name))
430 found))
431
432;;; XXX: remove
433(defun flymake-replace-region (beg end rep)
434 "Replace text in BUFFER in region (BEG END) with REP."
435 (save-excursion
436 (goto-char end)
437 ;; Insert before deleting, so as to better preserve markers's positions.
438 (insert rep)
439 (delete-region beg end)))
440
441(defun flymake-read-file-to-temp-buffer (file-name)
442 "Insert contents of FILE-NAME into newly created temp buffer."
443 (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name))))))
444 (with-current-buffer temp-buffer
445 (insert-file-contents file-name))
446 temp-buffer))
447
448(defun flymake-copy-buffer-to-temp-buffer (buffer)
449 "Copy contents of BUFFER into newly created temp buffer."
450 (with-current-buffer
451 (get-buffer-create (generate-new-buffer-name
452 (concat "flymake:" (buffer-name buffer))))
453 (insert-buffer-substring buffer)
454 (current-buffer)))
455
456(defun flymake-check-include (source-file-name inc-name include-dirs)
457 "Check if SOURCE-FILE-NAME can be found in include path.
458Return t if it can be found via include path using INC-NAME."
459 (if (file-name-absolute-p inc-name)
460 (flymake-same-files source-file-name inc-name)
461 (while (and include-dirs
462 (not (flymake-same-files
463 source-file-name
464 (concat (file-name-directory source-file-name)
465 "/" (car include-dirs)
466 "/" inc-name))))
467 (setq include-dirs (cdr include-dirs)))
468 include-dirs))
469
470(defun flymake-find-buffer-for-file (file-name)
471 "Check if there exists a buffer visiting FILE-NAME.
472Return t if so, nil if not."
473 (let ((buffer-name (get-file-buffer file-name)))
474 (if buffer-name
475 (get-buffer buffer-name))))
476
477(defun flymake-create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp)
478 "Save SOURCE-FILE-NAME with a different name.
479Find master file, patch and save it."
480 (let* ((possible-master-files (flymake-find-possible-master-files source-file-name flymake-master-file-dirs masks))
481 (master-file-count (length possible-master-files))
482 (idx 0)
483 (temp-buffer nil)
484 (master-file-name nil)
485 (patched-master-file-name nil)
486 (found nil))
487
488 (while (and (not found) (< idx master-file-count))
489 (setq master-file-name (nth idx possible-master-files))
490 (setq patched-master-file-name (funcall create-temp-f master-file-name "flymake_master"))
491 (if (flymake-find-buffer-for-file master-file-name)
492 (setq temp-buffer (flymake-copy-buffer-to-temp-buffer (flymake-find-buffer-for-file master-file-name)))
493 (setq temp-buffer (flymake-read-file-to-temp-buffer master-file-name)))
494 (setq found
495 (flymake-check-patch-master-file-buffer
496 temp-buffer
497 master-file-name
498 patched-master-file-name
499 source-file-name
500 patched-source-file-name
501 (funcall get-incl-dirs-f (file-name-directory master-file-name))
502 include-regexp))
503 (setq idx (1+ idx)))
504 (if found
505 (list master-file-name patched-master-file-name)
506 (progn
507 (flymake-log 3 "none of %d master file(s) checked includes %s" master-file-count
508 (file-name-nondirectory source-file-name))
509 nil))))
510
511(defun flymake-save-buffer-in-file (file-name)
512 "Save the entire buffer contents into file FILE-NAME.
513Create parent directories as needed."
514 (make-directory (file-name-directory file-name) 1)
515 (write-region nil nil file-name nil 566)
516 (flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name))
517
518(defun flymake-process-filter (process output)
519 "Parse OUTPUT and highlight error lines.
520It's flymake process filter."
521 (let ((source-buffer (process-buffer process)))
522
523 (flymake-log 3 "received %d byte(s) of output from process %d"
524 (length output) (process-id process))
525 (when (buffer-live-p source-buffer)
526 (with-current-buffer source-buffer
527 (flymake-parse-output-and-residual output)))))
528
529(defun flymake-process-sentinel (process _event)
530 "Sentinel for syntax check buffers."
531 (when (memq (process-status process) '(signal exit))
532 (let* ((exit-status (process-exit-status process))
533 (command (process-command process))
534 (source-buffer (process-buffer process))
535 (cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer))))
536
537 (flymake-log 2 "process %d exited with code %d"
538 (process-id process) exit-status)
539 (condition-case err
540 (progn
541 (flymake-log 3 "cleaning up using %s" cleanup-f)
542 (when (buffer-live-p source-buffer)
543 (with-current-buffer source-buffer
544 (funcall cleanup-f)))
545
546 (delete-process process)
547 (setq flymake-processes (delq process flymake-processes))
548
549 (when (buffer-live-p source-buffer)
550 (with-current-buffer source-buffer
551
552 (flymake-parse-residual)
553 (flymake-post-syntax-check exit-status command)
554 (setq flymake-is-running nil))))
555 (error
556 (let ((err-str (format "Error in process sentinel for buffer %s: %s"
557 source-buffer (error-message-string err))))
558 (flymake-log 0 err-str)
559 (with-current-buffer source-buffer
560 (setq flymake-is-running nil))))))))
561
562(defun flymake-post-syntax-check (exit-status command)
563 (save-restriction
564 (widen)
565 (setq flymake-err-info flymake-new-err-info)
566 (setq flymake-new-err-info nil)
567 (setq flymake-err-info
568 (flymake-fix-line-numbers
569 flymake-err-info 1 (count-lines (point-min) (point-max))))
570 (flymake-delete-own-overlays)
571 (flymake-highlight-err-lines flymake-err-info)
572 (let (err-count warn-count)
573 (setq err-count (flymake-get-err-count flymake-err-info "e"))
574 (setq warn-count (flymake-get-err-count flymake-err-info "w"))
575 (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)"
576 (buffer-name) err-count warn-count
577 (- (float-time) flymake-check-start-time))
578 (setq flymake-check-start-time nil)
579
580 (if (and (equal 0 err-count) (equal 0 warn-count))
581 (if (equal 0 exit-status)
582 (flymake-report-status "" "") ; PASSED
583 (if (not flymake-check-was-interrupted)
584 (flymake-report-fatal-status "CFGERR"
585 (format "Configuration error has occurred while running %s" command))
586 (flymake-report-status nil ""))) ; "STOPPED"
587 (flymake-report-status (format "%d/%d" err-count warn-count) "")))))
588
589(defun flymake-parse-output-and-residual (output)
590 "Split OUTPUT into lines, merge in residual if necessary."
591 (let* ((buffer-residual flymake-output-residual)
592 (total-output (if buffer-residual (concat buffer-residual output) output))
593 (lines-and-residual (flymake-split-output total-output))
594 (lines (nth 0 lines-and-residual))
595 (new-residual (nth 1 lines-and-residual)))
596 (setq flymake-output-residual new-residual)
597 (setq flymake-new-err-info
598 (flymake-parse-err-lines
599 flymake-new-err-info lines))))
600
601(defun flymake-parse-residual ()
602 "Parse residual if it's non empty."
603 (when flymake-output-residual
604 (setq flymake-new-err-info
605 (flymake-parse-err-lines
606 flymake-new-err-info
607 (list flymake-output-residual)))
608 (setq flymake-output-residual nil)))
609
610(defun flymake-er-make-er (line-no line-err-info-list) 153(defun flymake-er-make-er (line-no line-err-info-list)
611 (list line-no line-err-info-list)) 154 (list line-no line-err-info-list))
612 155
@@ -665,26 +208,6 @@ Value of TYPE is either \"e\" or \"w\"."
665 (setq idx (1+ idx))) 208 (setq idx (1+ idx)))
666 err-count)) 209 err-count))
667 210
668(defun flymake-fix-line-numbers (err-info-list min-line max-line)
669 "Replace line numbers with fixed value.
670If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE.
671If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE.
672The reason for this fix is because some compilers might report
673line number outside the file being compiled."
674 (let* ((count (length err-info-list))
675 (err-info nil)
676 (line 0))
677 (while (> count 0)
678 (setq err-info (nth (1- count) err-info-list))
679 (setq line (flymake-er-get-line err-info))
680 (when (or (< line min-line) (> line max-line))
681 (setq line (if (< line min-line) min-line max-line))
682 (setq err-info-list (flymake-set-at err-info-list (1- count)
683 (flymake-er-make-er line
684 (flymake-er-get-line-err-info-list err-info)))))
685 (setq count (1- count))))
686 err-info-list)
687
688(defun flymake-highlight-err-lines (err-info-list) 211(defun flymake-highlight-err-lines (err-info-list)
689 "Highlight error lines in BUFFER using info from ERR-INFO-LIST." 212 "Highlight error lines in BUFFER using info from ERR-INFO-LIST."
690 (save-excursion 213 (save-excursion
@@ -771,136 +294,6 @@ Perhaps use text from LINE-ERR-INFO-LIST to enhance highlighting."
771 (list 'flymake-warnline flymake-warning-bitmap)))) 294 (list 'flymake-warnline flymake-warning-bitmap))))
772 (flymake-make-overlay beg end tooltip-text face bitmap))) 295 (flymake-make-overlay beg end tooltip-text face bitmap)))
773 296
774(defun flymake-parse-err-lines (err-info-list lines)
775 "Parse err LINES, store info in ERR-INFO-LIST."
776 (let* ((count (length lines))
777 (idx 0)
778 (line-err-info nil)
779 (real-file-name nil)
780 (source-file-name buffer-file-name)
781 (get-real-file-name-f (flymake-get-real-file-name-function source-file-name)))
782
783 (while (< idx count)
784 (setq line-err-info (flymake-parse-line (nth idx lines)))
785 (when line-err-info
786 (setq real-file-name (funcall get-real-file-name-f
787 (flymake-ler-file line-err-info)))
788 (setq line-err-info (flymake-ler-set-full-file line-err-info real-file-name))
789
790 (when (flymake-same-files real-file-name source-file-name)
791 (setq line-err-info (flymake-ler-set-file line-err-info nil))
792 (setq err-info-list (flymake-add-err-info err-info-list line-err-info))))
793 (flymake-log 3 "parsed `%s', %s line-err-info" (nth idx lines) (if line-err-info "got" "no"))
794 (setq idx (1+ idx)))
795 err-info-list))
796
797(defun flymake-split-output (output)
798 "Split OUTPUT into lines.
799Return last one as residual if it does not end with newline char.
800Returns ((LINES) RESIDUAL)."
801 (when (and output (> (length output) 0))
802 (let* ((lines (split-string output "[\n\r]+" t))
803 (complete (equal "\n" (char-to-string (aref output (1- (length output))))))
804 (residual nil))
805 (when (not complete)
806 (setq residual (car (last lines)))
807 (setq lines (butlast lines)))
808 (list lines residual))))
809
810(defun flymake-reformat-err-line-patterns-from-compile-el (original-list)
811 "Grab error line patterns from ORIGINAL-LIST in compile.el format.
812Convert it to flymake internal format."
813 (let* ((converted-list '()))
814 (dolist (item original-list)
815 (setq item (cdr item))
816 (let ((regexp (nth 0 item))
817 (file (nth 1 item))
818 (line (nth 2 item))
819 (col (nth 3 item)))
820 (if (consp file) (setq file (car file)))
821 (if (consp line) (setq line (car line)))
822 (if (consp col) (setq col (car col)))
823
824 (when (not (functionp line))
825 (setq converted-list (cons (list regexp file line col) converted-list)))))
826 converted-list))
827
828(require 'compile)
829
830(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
831 (append
832 '(
833 ;; MS Visual C++ 6.0
834 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) : \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
835 1 3 nil 4)
836 ;; jikes
837 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\):[0-9]+:[0-9]+:[0-9]+: \\(\\(Error\\|Warning\\|Caution\\|Semantic Error\\):[ \t\n]*\\(.+\\)\\)"
838 1 3 nil 4)
839 ;; MS midl
840 ("midl[ ]*:[ ]*\\(command line error .*\\)"
841 nil nil nil 1)
842 ;; MS C#
843 ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\),[0-9]+): \\(\\(error\\|warning\\|fatal error\\) \\(CS[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
844 1 3 nil 4)
845 ;; perl
846 ("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1)
847 ;; PHP
848 ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1)
849 ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1)
850 ;; ant/javac. Note this also matches gcc warnings!
851 (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::[0-9]+\\)?:[ \t\n]*\\(.+\\)"
852 2 4 nil 5))
853 ;; compilation-error-regexp-alist)
854 (flymake-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist))
855 "Patterns for matching error/warning lines. Each pattern has the form
856\(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX).
857Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns
858from compile.el")
859
860(define-obsolete-variable-alias 'flymake-warning-re 'flymake-warning-predicate "24.4")
861(defvar flymake-warning-predicate "^[wW]arning"
862 "Predicate matching against error text to detect a warning.
863Takes a single argument, the error's text and should return non-nil
864if it's a warning.
865Instead of a function, it can also be a regular expression.")
866
867(defun flymake-parse-line (line)
868 "Parse LINE to see if it is an error or warning.
869Return its components if so, nil otherwise."
870 (let ((raw-file-name nil)
871 (line-no 0)
872 (err-type "e")
873 (err-text nil)
874 (patterns flymake-err-line-patterns)
875 (matched nil))
876 (while (and patterns (not matched))
877 (when (string-match (car (car patterns)) line)
878 (let* ((file-idx (nth 1 (car patterns)))
879 (line-idx (nth 2 (car patterns))))
880
881 (setq raw-file-name (if file-idx (match-string file-idx line) nil))
882 (setq line-no (if line-idx (string-to-number
883 (match-string line-idx line)) 0))
884 (setq err-text (if (> (length (car patterns)) 4)
885 (match-string (nth 4 (car patterns)) line)
886 (flymake-patch-err-text
887 (substring line (match-end 0)))))
888 (if (null err-text)
889 (setq err-text "<no error text>")
890 (when (cond ((stringp flymake-warning-predicate)
891 (string-match flymake-warning-predicate err-text))
892 ((functionp flymake-warning-predicate)
893 (funcall flymake-warning-predicate err-text)))
894 (setq err-type "w")))
895 (flymake-log
896 3 "parse line: file-idx=%s line-idx=%s file=%s line=%s text=%s"
897 file-idx line-idx raw-file-name line-no err-text)
898 (setq matched t)))
899 (setq patterns (cdr patterns)))
900 (if matched
901 (flymake-ler-make-ler raw-file-name line-no err-type err-text)
902 ())))
903
904(defun flymake-find-err-info (err-info-list line-no) 297(defun flymake-find-err-info (err-info-list line-no)
905 "Find (line-err-info-list pos) for specified LINE-NO." 298 "Find (line-err-info-list pos) for specified LINE-NO."
906 (if err-info-list 299 (if err-info-list
@@ -961,169 +354,8 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'."
961 (t (setq err-info-list (flymake-ins-after err-info-list (1- pos) err-info)))) 354 (t (setq err-info-list (flymake-ins-after err-info-list (1- pos) err-info))))
962 err-info-list)) 355 err-info-list))
963 356
964(defun flymake-get-project-include-dirs-imp (basedir) 357(defvar-local flymake-is-running nil
965 "Include dirs for the project current file belongs to." 358 "If t, flymake syntax check process is running for the current buffer.")
966 (if (flymake-get-project-include-dirs-from-cache basedir)
967 (progn
968 (flymake-get-project-include-dirs-from-cache basedir))
969 ;;else
970 (let* ((command-line (concat "make -C "
971 (shell-quote-argument basedir)
972 " DUMPVARS=INCLUDE_DIRS dumpvars"))
973 (output (shell-command-to-string command-line))
974 (lines (split-string output "\n" t))
975 (count (length lines))
976 (idx 0)
977 (inc-dirs nil))
978 (while (and (< idx count) (not (string-match "^INCLUDE_DIRS=.*" (nth idx lines))))
979 (setq idx (1+ idx)))
980 (when (< idx count)
981 (let* ((inc-lines (split-string (nth idx lines) " *-I" t))
982 (inc-count (length inc-lines)))
983 (while (> inc-count 0)
984 (when (not (string-match "^INCLUDE_DIRS=.*" (nth (1- inc-count) inc-lines)))
985 (push (replace-regexp-in-string "\"" "" (nth (1- inc-count) inc-lines)) inc-dirs))
986 (setq inc-count (1- inc-count)))))
987 (flymake-add-project-include-dirs-to-cache basedir inc-dirs)
988 inc-dirs)))
989
990(defvar flymake-get-project-include-dirs-function #'flymake-get-project-include-dirs-imp
991 "Function used to get project include dirs, one parameter: basedir name.")
992
993(defun flymake-get-project-include-dirs (basedir)
994 (funcall flymake-get-project-include-dirs-function basedir))
995
996(defun flymake-get-system-include-dirs ()
997 "System include dirs - from the `INCLUDE' env setting."
998 (let* ((includes (getenv "INCLUDE")))
999 (if includes (split-string includes path-separator t) nil)))
1000
1001(defvar flymake-project-include-dirs-cache (make-hash-table :test #'equal))
1002
1003(defun flymake-get-project-include-dirs-from-cache (base-dir)
1004 (gethash base-dir flymake-project-include-dirs-cache))
1005
1006(defun flymake-add-project-include-dirs-to-cache (base-dir include-dirs)
1007 (puthash base-dir include-dirs flymake-project-include-dirs-cache))
1008
1009(defun flymake-clear-project-include-dirs-cache ()
1010 (clrhash flymake-project-include-dirs-cache))
1011
1012(defun flymake-get-include-dirs (base-dir)
1013 "Get dirs to use when resolving local file names."
1014 (let* ((include-dirs (append '(".") (flymake-get-project-include-dirs base-dir) (flymake-get-system-include-dirs))))
1015 include-dirs))
1016
1017;; (defun flymake-restore-formatting ()
1018;; "Remove any formatting made by flymake."
1019;; )
1020
1021;; (defun flymake-get-program-dir (buffer)
1022;; "Get dir to start program in."
1023;; (unless (bufferp buffer)
1024;; (error "Invalid buffer"))
1025;; (with-current-buffer buffer
1026;; default-directory))
1027
1028(defun flymake-safe-delete-file (file-name)
1029 (when (and file-name (file-exists-p file-name))
1030 (delete-file file-name)
1031 (flymake-log 1 "deleted file %s" file-name)))
1032
1033(defun flymake-safe-delete-directory (dir-name)
1034 (condition-case nil
1035 (progn
1036 (delete-directory dir-name)
1037 (flymake-log 1 "deleted dir %s" dir-name))
1038 (error
1039 (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name))))
1040
1041(defun flymake-start-syntax-check ()
1042 "Start syntax checking for current buffer."
1043 (interactive)
1044 (flymake-log 3 "flymake is running: %s" flymake-is-running)
1045 (when (and (not flymake-is-running)
1046 (flymake-can-syntax-check-file buffer-file-name))
1047 (when (or (not flymake-compilation-prevents-syntax-check)
1048 (not (flymake-compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP")
1049 (flymake-clear-buildfile-cache)
1050 (flymake-clear-project-include-dirs-cache)
1051
1052 (setq flymake-check-was-interrupted nil)
1053
1054 (let* ((source-file-name buffer-file-name)
1055 (init-f (flymake-get-init-function source-file-name))
1056 (cleanup-f (flymake-get-cleanup-function source-file-name))
1057 (cmd-and-args (funcall init-f))
1058 (cmd (nth 0 cmd-and-args))
1059 (args (nth 1 cmd-and-args))
1060 (dir (nth 2 cmd-and-args)))
1061 (if (not cmd-and-args)
1062 (progn
1063 (flymake-log 0 "init function %s for %s failed, cleaning up" init-f source-file-name)
1064 (funcall cleanup-f))
1065 (progn
1066 (setq flymake-last-change-time nil)
1067 (flymake-start-syntax-check-process cmd args dir)))))))
1068
1069(defun flymake-start-syntax-check-process (cmd args dir)
1070 "Start syntax check process."
1071 (condition-case err
1072 (let* ((process
1073 (let ((default-directory (or dir default-directory)))
1074 (when dir
1075 (flymake-log 3 "starting process on dir %s" dir))
1076 (apply 'start-file-process
1077 "flymake-proc" (current-buffer) cmd args))))
1078 (set-process-sentinel process 'flymake-process-sentinel)
1079 (set-process-filter process 'flymake-process-filter)
1080 (set-process-query-on-exit-flag process nil)
1081 (push process flymake-processes)
1082
1083 (setq flymake-is-running t)
1084 (setq flymake-last-change-time nil)
1085 (setq flymake-check-start-time (float-time))
1086
1087 (flymake-report-status nil "*")
1088 (flymake-log 2 "started process %d, command=%s, dir=%s"
1089 (process-id process) (process-command process)
1090 default-directory)
1091 process)
1092 (error
1093 (let* ((err-str
1094 (format-message
1095 "Failed to launch syntax check process `%s' with args %s: %s"
1096 cmd args (error-message-string err)))
1097 (source-file-name buffer-file-name)
1098 (cleanup-f (flymake-get-cleanup-function source-file-name)))
1099 (flymake-log 0 err-str)
1100 (funcall cleanup-f)
1101 (flymake-report-fatal-status "PROCERR" err-str)))))
1102
1103(defun flymake-kill-process (proc)
1104 "Kill process PROC."
1105 (kill-process proc)
1106 (let* ((buf (process-buffer proc)))
1107 (when (buffer-live-p buf)
1108 (with-current-buffer buf
1109 (setq flymake-check-was-interrupted t))))
1110 (flymake-log 1 "killed process %d" (process-id proc)))
1111
1112(defun flymake-stop-all-syntax-checks ()
1113 "Kill all syntax check processes."
1114 (interactive)
1115 (while flymake-processes
1116 (flymake-kill-process (pop flymake-processes))))
1117
1118(defun flymake-compilation-is-running ()
1119 (and (boundp 'compilation-in-progress)
1120 compilation-in-progress))
1121
1122(defun flymake-compile ()
1123 "Kill all flymake syntax checks, start compilation."
1124 (interactive)
1125 (flymake-stop-all-syntax-checks)
1126 (call-interactively 'compile))
1127 359
1128(defun flymake-on-timer-event (buffer) 360(defun flymake-on-timer-event (buffer)
1129 "Start a syntax check for buffer BUFFER if necessary." 361 "Start a syntax check for buffer BUFFER if necessary."
@@ -1210,6 +442,26 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'."
1210 (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s" 442 (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s"
1211 (buffer-name) status warning)) 443 (buffer-name) status warning))
1212 444
445(defun flymake-fix-line-numbers (err-info-list min-line max-line)
446 "Replace line numbers with fixed value.
447If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE.
448If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE.
449The reason for this fix is because some compilers might report
450line number outside the file being compiled."
451 (let* ((count (length err-info-list))
452 (err-info nil)
453 (line 0))
454 (while (> count 0)
455 (setq err-info (nth (1- count) err-info-list))
456 (setq line (flymake-er-get-line err-info))
457 (when (or (< line min-line) (> line max-line))
458 (setq line (if (< line min-line) min-line max-line))
459 (setq err-info-list (flymake-set-at err-info-list (1- count)
460 (flymake-er-make-er line
461 (flymake-er-get-line-err-info-list err-info)))))
462 (setq count (1- count))))
463 err-info-list)
464
1213;;;###autoload 465;;;###autoload
1214(define-minor-mode flymake-mode nil 466(define-minor-mode flymake-mode nil
1215 :group 'flymake :lighter flymake-mode-line 467 :group 'flymake :lighter flymake-mode-line
@@ -1365,288 +617,10 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'."
1365 (match-string 1 string) 617 (match-string 1 string)
1366 string)) 618 string))
1367 619
1368;;;; general init-cleanup and helper routines
1369(defun flymake-create-temp-inplace (file-name prefix)
1370 (unless (stringp file-name)
1371 (error "Invalid file-name"))
1372 (or prefix
1373 (setq prefix "flymake"))
1374 (let* ((ext (file-name-extension file-name))
1375 (temp-name (file-truename
1376 (concat (file-name-sans-extension file-name)
1377 "_" prefix
1378 (and ext (concat "." ext))))))
1379 (flymake-log 3 "create-temp-inplace: file=%s temp=%s" file-name temp-name)
1380 temp-name))
1381
1382(defun flymake-create-temp-with-folder-structure (file-name _prefix)
1383 (unless (stringp file-name)
1384 (error "Invalid file-name"))
1385
1386 (let* ((dir (file-name-directory file-name))
1387 ;; Not sure what this slash-pos is all about, but I guess it's just
1388 ;; trying to remove the leading / of absolute file names.
1389 (slash-pos (string-match "/" dir))
1390 (temp-dir (expand-file-name (substring dir (1+ slash-pos))
1391 temporary-file-directory)))
1392
1393 (file-truename (expand-file-name (file-name-nondirectory file-name)
1394 temp-dir))))
1395
1396(defun flymake-delete-temp-directory (dir-name)
1397 "Attempt to delete temp dir created by `flymake-create-temp-with-folder-structure', do not fail on error."
1398 (let* ((temp-dir temporary-file-directory)
1399 (suffix (substring dir-name (1+ (length temp-dir)))))
1400
1401 (while (> (length suffix) 0)
1402 (setq suffix (directory-file-name suffix))
1403 ;;+(flymake-log 0 "suffix=%s" suffix)
1404 (flymake-safe-delete-directory
1405 (file-truename (expand-file-name suffix temp-dir)))
1406 (setq suffix (file-name-directory suffix)))))
1407
1408(defvar-local flymake-temp-source-file-name nil)
1409(defvar-local flymake-master-file-name nil)
1410(defvar-local flymake-temp-master-file-name nil)
1411(defvar-local flymake-base-dir nil)
1412
1413(defun flymake-init-create-temp-buffer-copy (create-temp-f)
1414 "Make a temporary copy of the current buffer, save its name in buffer data and return the name."
1415 (let* ((source-file-name buffer-file-name)
1416 (temp-source-file-name (funcall create-temp-f source-file-name "flymake")))
1417
1418 (flymake-save-buffer-in-file temp-source-file-name)
1419 (setq flymake-temp-source-file-name temp-source-file-name)
1420 temp-source-file-name))
1421
1422(defun flymake-simple-cleanup ()
1423 "Do cleanup after `flymake-init-create-temp-buffer-copy'.
1424Delete temp file."
1425 (flymake-safe-delete-file flymake-temp-source-file-name)
1426 (setq flymake-last-change-time nil))
1427
1428(defun flymake-get-real-file-name (file-name-from-err-msg)
1429 "Translate file name from error message to \"real\" file name.
1430Return full-name. Names are real, not patched."
1431 (let* ((real-name nil)
1432 (source-file-name buffer-file-name)
1433 (master-file-name flymake-master-file-name)
1434 (temp-source-file-name flymake-temp-source-file-name)
1435 (temp-master-file-name flymake-temp-master-file-name)
1436 (base-dirs
1437 (list flymake-base-dir
1438 (file-name-directory source-file-name)
1439 (if master-file-name (file-name-directory master-file-name))))
1440 (files (list (list source-file-name source-file-name)
1441 (list temp-source-file-name source-file-name)
1442 (list master-file-name master-file-name)
1443 (list temp-master-file-name master-file-name))))
1444
1445 (when (equal 0 (length file-name-from-err-msg))
1446 (setq file-name-from-err-msg source-file-name))
1447
1448 (setq real-name (flymake-get-full-patched-file-name file-name-from-err-msg base-dirs files))
1449 ;; if real-name is nil, than file name from err msg is none of the files we've patched
1450 (if (not real-name)
1451 (setq real-name (flymake-get-full-nonpatched-file-name file-name-from-err-msg base-dirs)))
1452 (if (not real-name)
1453 (setq real-name file-name-from-err-msg))
1454 (setq real-name (flymake-fix-file-name real-name))
1455 (flymake-log 3 "get-real-file-name: file-name=%s real-name=%s" file-name-from-err-msg real-name)
1456 real-name))
1457
1458(defun flymake-get-full-patched-file-name (file-name-from-err-msg base-dirs files)
1459 (let* ((base-dirs-count (length base-dirs))
1460 (file-count (length files))
1461 (real-name nil))
1462
1463 (while (and (not real-name) (> base-dirs-count 0))
1464 (setq file-count (length files))
1465 (while (and (not real-name) (> file-count 0))
1466 (let* ((this-dir (nth (1- base-dirs-count) base-dirs))
1467 (this-file (nth 0 (nth (1- file-count) files)))
1468 (this-real-name (nth 1 (nth (1- file-count) files))))
1469 ;;+(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)
1470 (when (and this-dir this-file (flymake-same-files
1471 (expand-file-name file-name-from-err-msg this-dir)
1472 this-file))
1473 (setq real-name this-real-name)))
1474 (setq file-count (1- file-count)))
1475 (setq base-dirs-count (1- base-dirs-count)))
1476 real-name))
1477
1478(defun flymake-get-full-nonpatched-file-name (file-name-from-err-msg base-dirs)
1479 (let* ((real-name nil))
1480 (if (file-name-absolute-p file-name-from-err-msg)
1481 (setq real-name file-name-from-err-msg)
1482 (let* ((base-dirs-count (length base-dirs)))
1483 (while (and (not real-name) (> base-dirs-count 0))
1484 (let* ((full-name (expand-file-name file-name-from-err-msg
1485 (nth (1- base-dirs-count) base-dirs))))
1486 (if (file-exists-p full-name)
1487 (setq real-name full-name))
1488 (setq base-dirs-count (1- base-dirs-count))))))
1489 real-name))
1490
1491(defun flymake-init-find-buildfile-dir (source-file-name buildfile-name)
1492 "Find buildfile, store its dir in buffer data and return its dir, if found."
1493 (let* ((buildfile-dir
1494 (flymake-find-buildfile buildfile-name
1495 (file-name-directory source-file-name))))
1496 (if buildfile-dir
1497 (setq flymake-base-dir buildfile-dir)
1498 (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name)
1499 (flymake-report-fatal-status
1500 "NOMK" (format "No buildfile (%s) found for %s"
1501 buildfile-name source-file-name)))))
1502
1503(defun flymake-init-create-temp-source-and-master-buffer-copy (get-incl-dirs-f create-temp-f master-file-masks include-regexp)
1504 "Find master file (or buffer), create its copy along with a copy of the source file."
1505 (let* ((source-file-name buffer-file-name)
1506 (temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f))
1507 (master-and-temp-master (flymake-create-master-file
1508 source-file-name temp-source-file-name
1509 get-incl-dirs-f create-temp-f
1510 master-file-masks include-regexp)))
1511
1512 (if (not master-and-temp-master)
1513 (progn
1514 (flymake-log 1 "cannot find master file for %s" source-file-name)
1515 (flymake-report-status "!" "") ; NOMASTER
1516 nil)
1517 (setq flymake-master-file-name (nth 0 master-and-temp-master))
1518 (setq flymake-temp-master-file-name (nth 1 master-and-temp-master)))))
1519
1520(defun flymake-master-cleanup ()
1521 (flymake-simple-cleanup)
1522 (flymake-safe-delete-file flymake-temp-master-file-name))
1523
1524;;;; make-specific init-cleanup routines
1525(defun flymake-get-syntax-check-program-args (source-file-name base-dir use-relative-base-dir use-relative-source get-cmd-line-f)
1526 "Create a command line for syntax check using GET-CMD-LINE-F."
1527 (funcall get-cmd-line-f
1528 (if use-relative-source
1529 (file-relative-name source-file-name base-dir)
1530 source-file-name)
1531 (if use-relative-base-dir
1532 (file-relative-name base-dir
1533 (file-name-directory source-file-name))
1534 base-dir)))
1535
1536(defun flymake-get-make-cmdline (source base-dir)
1537 (list "make"
1538 (list "-s"
1539 "-C"
1540 base-dir
1541 (concat "CHK_SOURCES=" source)
1542 "SYNTAX_CHECK_MODE=1"
1543 "check-syntax")))
1544
1545(defun flymake-get-ant-cmdline (source base-dir)
1546 (list "ant"
1547 (list "-buildfile"
1548 (concat base-dir "/" "build.xml")
1549 (concat "-DCHK_SOURCES=" source)
1550 "check-syntax")))
1551
1552(defun flymake-simple-make-init-impl (create-temp-f use-relative-base-dir use-relative-source build-file-name get-cmdline-f)
1553 "Create syntax check command line for a directly checked source file.
1554Use CREATE-TEMP-F for creating temp copy."
1555 (let* ((args nil)
1556 (source-file-name buffer-file-name)
1557 (buildfile-dir (flymake-init-find-buildfile-dir source-file-name build-file-name)))
1558 (if buildfile-dir
1559 (let* ((temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)))
1560 (setq args (flymake-get-syntax-check-program-args temp-source-file-name buildfile-dir
1561 use-relative-base-dir use-relative-source
1562 get-cmdline-f))))
1563 args))
1564
1565(defun flymake-simple-make-init ()
1566 (flymake-simple-make-init-impl 'flymake-create-temp-inplace t t "Makefile" 'flymake-get-make-cmdline))
1567
1568(defun flymake-master-make-init (get-incl-dirs-f master-file-masks include-regexp)
1569 "Create make command line for a source file checked via master file compilation."
1570 (let* ((make-args nil)
1571 (temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy
1572 get-incl-dirs-f 'flymake-create-temp-inplace
1573 master-file-masks include-regexp)))
1574 (when temp-master-file-name
1575 (let* ((buildfile-dir (flymake-init-find-buildfile-dir temp-master-file-name "Makefile")))
1576 (if buildfile-dir
1577 (setq make-args (flymake-get-syntax-check-program-args
1578 temp-master-file-name buildfile-dir nil nil 'flymake-get-make-cmdline)))))
1579 make-args))
1580
1581(defun flymake-find-make-buildfile (source-dir)
1582 (flymake-find-buildfile "Makefile" source-dir))
1583
1584;;;; .h/make specific
1585(defun flymake-master-make-header-init ()
1586 (flymake-master-make-init
1587 'flymake-get-include-dirs
1588 '("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'")
1589 "[ \t]*#[ \t]*include[ \t]*\"\\([[:word:]0-9/\\_.]*%s\\)\""))
1590
1591;;;; .java/make specific
1592(defun flymake-simple-make-java-init ()
1593 (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "Makefile" 'flymake-get-make-cmdline))
1594
1595(defun flymake-simple-ant-java-init ()
1596 (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "build.xml" 'flymake-get-ant-cmdline))
1597
1598(defun flymake-simple-java-cleanup ()
1599 "Cleanup after `flymake-simple-make-java-init' -- delete temp file and dirs."
1600 (flymake-safe-delete-file flymake-temp-source-file-name)
1601 (when flymake-temp-source-file-name
1602 (flymake-delete-temp-directory
1603 (file-name-directory flymake-temp-source-file-name))))
1604
1605;;;; perl-specific init-cleanup routines
1606(defun flymake-perl-init ()
1607 (let* ((temp-file (flymake-init-create-temp-buffer-copy
1608 'flymake-create-temp-inplace))
1609 (local-file (file-relative-name
1610 temp-file
1611 (file-name-directory buffer-file-name))))
1612 (list "perl" (list "-wc " local-file))))
1613
1614;;;; php-specific init-cleanup routines
1615(defun flymake-php-init ()
1616 (let* ((temp-file (flymake-init-create-temp-buffer-copy
1617 'flymake-create-temp-inplace))
1618 (local-file (file-relative-name
1619 temp-file
1620 (file-name-directory buffer-file-name))))
1621 (list "php" (list "-f" local-file "-l"))))
1622
1623;;;; tex-specific init-cleanup routines
1624(defun flymake-get-tex-args (file-name)
1625 ;;(list "latex" (list "-c-style-errors" file-name))
1626 (list "texify" (list "--pdf" "--tex-option=-c-style-errors" file-name)))
1627
1628(defun flymake-simple-tex-init ()
1629 (flymake-get-tex-args (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace)))
1630
1631;; Perhaps there should be a buffer-local variable flymake-master-file
1632;; that people can set to override this stuff. Could inherit from
1633;; the similar AUCTeX variable.
1634(defun flymake-master-tex-init ()
1635 (let* ((temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy
1636 'flymake-get-include-dirs-dot 'flymake-create-temp-inplace
1637 '("\\.tex\\'")
1638 "[ \t]*\\in\\(?:put\\|clude\\)[ \t]*{\\(.*%s\\)}")))
1639 (when temp-master-file-name
1640 (flymake-get-tex-args temp-master-file-name))))
1641
1642(defun flymake-get-include-dirs-dot (_base-dir)
1643 '("."))
1644
1645;;;; xml-specific init-cleanup routines
1646(defun flymake-xml-init ()
1647 (list flymake-xml-program
1648 (list "val" (flymake-init-create-temp-buffer-copy
1649 'flymake-create-temp-inplace))))
1650
1651(provide 'flymake) 620(provide 'flymake)
621
622(declare-function flymake-start-syntax-check "flymake-proc")
623(declare-function flymake-can-syntax-check-file "flymake-proc")
624
625(require 'flymake-proc)
1652;;; flymake.el ends here 626;;; flymake.el ends here