flymake.el 79.2 KB
Newer Older
Eli Zaretskii's avatar
Eli Zaretskii committed
1 2
;;; flymake.el -- a universal on-the-fly syntax checker

3
;; Copyright (C) 2003, 2005  Free Software Foundation
Eli Zaretskii's avatar
Eli Zaretskii committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

;; Author:  Pavel Kobiakov <pk_at_work@yahoo.com>
;; Maintainer: Pavel Kobiakov <pk_at_work@yahoo.com>
;; Version: 0.3
;; Keywords: c languages tools

;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:
;;
;; Flymake is a minor Emacs mode performing on-the-fly syntax
;; checks using the external syntax check tool (for C/C++ this
;; is usually the compiler)

;;; Code:

Kim F. Storm's avatar
Kim F. Storm committed
35 36
;;;; [[ Xemacs overlay compatibility
(if (featurep 'xemacs) (progn
Eli Zaretskii's avatar
Eli Zaretskii committed
37 38 39 40 41 42
(autoload 'make-overlay            "overlay" "Overlay compatibility kit." t)
(autoload 'overlayp                "overlay" "Overlay compatibility kit." t)
(autoload 'overlays-in             "overlay" "Overlay compatibility kit." t)
(autoload 'delete-overlay          "overlay" "Overlay compatibility kit." t)
(autoload 'overlay-put             "overlay" "Overlay compatibility kit." t)
(autoload 'overlay-get             "overlay" "Overlay compatibility kit." t)
Kim F. Storm's avatar
Kim F. Storm committed
43
))
Eli Zaretskii's avatar
Eli Zaretskii committed
44 45 46
;;;; ]]

;;;; [[ cross-emacs compatibility routines
Kim F. Storm's avatar
Kim F. Storm committed
47 48 49 50
(defsubst flymake-makehash (&optional test)
  (if (fboundp 'make-hash-table)
      (if test (make-hash-table :test test) (make-hash-table))
    (makehash test)))
Richard M. Stallman's avatar
Richard M. Stallman committed
51 52

(defun flymake-float-time ()
Kim F. Storm's avatar
Kim F. Storm committed
53 54 55 56 57 58 59 60 61 62 63 64
  (if (featurep 'xemacs)
      (let ((tm (current-time)))
	(multiple-value-bind (s0 s1 s2) (current-time)
			     (+ (* (float (ash 1 16)) s0) (float s1) (* 0.0000001 s2))))
    (float-time)))

(defsubst flymake-replace-regexp-in-string (regexp rep str)
  (if (featurep 'xemacs)
      (replace-in-string str regexp rep)
    (replace-regexp-in-string regexp rep str)))

(defun flymake-split-string (str pattern)
Richard M. Stallman's avatar
Richard M. Stallman committed
65 66 67 68 69 70 71 72
  "Split, then remove first and/or last in case it's empty."
  (let* ((splitted (split-string str pattern)))
    (if (and (> (length splitted) 0) (= 0 (length (elt splitted 0))))
	(setq splitted (cdr splitted)))
    (if (and (> (length splitted) 0) (= 0 (length (elt splitted (1- (length splitted))))))
	(setq splitted (reverse (cdr (reverse splitted)))))
    splitted))

Kim F. Storm's avatar
Kim F. Storm committed
73 74 75 76
(defsubst flymake-get-temp-dir ()
  (if (featurep 'xemacs)
      (temp-directory)
    temporary-file-directory))
Eli Zaretskii's avatar
Eli Zaretskii committed
77

78
(defun flymake-line-beginning-position ()
Richard M. Stallman's avatar
Richard M. Stallman committed
79 80 81
  (save-excursion
    (beginning-of-line)
    (point)))
Eli Zaretskii's avatar
Eli Zaretskii committed
82

83
(defun flymake-line-end-position ()
Richard M. Stallman's avatar
Richard M. Stallman committed
84 85 86 87 88
  (save-excursion
    (end-of-line)
    (point)))

(defun flymake-popup-menu (pos menu-data)
Kim F. Storm's avatar
Kim F. Storm committed
89 90 91 92 93 94 95 96
  (if (featurep 'xemacs)
      (let* ((x-pos       (nth 0 (nth 0 pos)))
	     (y-pos       (nth 1 (nth 0 pos)))
	     (fake-event-props  '(button 1 x 1 y 1)))
	(setq fake-event-props (plist-put fake-event-props 'x x-pos))
	(setq fake-event-props (plist-put fake-event-props 'y y-pos))
	(popup-menu (flymake-make-xemacs-menu menu-data) (make-event 'button-press fake-event-props)))
    (x-popup-menu pos (flymake-make-emacs-menu menu-data))))
Eli Zaretskii's avatar
Eli Zaretskii committed
97

Richard M. Stallman's avatar
Richard M. Stallman committed
98 99 100 101 102 103 104 105
(defun flymake-make-emacs-menu (menu-data)
  (let* ((menu-title     (nth 0 menu-data))
	 (menu-items     (nth 1 menu-data))
	 (menu-commands  nil))
    (setq menu-commands (mapcar (lambda (foo)
				  (cons (nth 0 foo) (nth 1 foo)))
				menu-items))
    (list menu-title (cons "" menu-commands))))
Eli Zaretskii's avatar
Eli Zaretskii committed
106

107
(defun flymake-nop ())
Eli Zaretskii's avatar
Eli Zaretskii committed
108

Kim F. Storm's avatar
Kim F. Storm committed
109 110
(if (featurep 'xemacs) (progn

111
(defun flymake-make-xemacs-menu (menu-data)
Richard M. Stallman's avatar
Richard M. Stallman committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
  (let* ((menu-title     (nth 0 menu-data))
	 (menu-items     (nth 1 menu-data))
	 (menu-commands  nil))
    (setq menu-commands (mapcar (lambda (foo)
				  (vector (nth 0 foo) (or (nth 1 foo) '(flymake-nop)) t))
				menu-items))
    (cons menu-title menu-commands)))

(defun flymake-xemacs-window-edges (&optional window)
  (let ((edges  (window-pixel-edges window))
	tmp)
    (setq tmp edges)
    (setcar tmp (/ (car tmp) (face-width 'default)))
    (setq tmp (cdr tmp))
    (setcar tmp (/ (car tmp) (face-height 'default)))
    (setq tmp (cdr tmp))
    (setcar tmp (/ (car tmp) (face-width 'default)))
    (setq tmp (cdr tmp))
    (setcar tmp (/ (car tmp) (face-height 'default)))
    edges))

Kim F. Storm's avatar
Kim F. Storm committed
133 134
)) ;; xemacs

Richard M. Stallman's avatar
Richard M. Stallman committed
135 136
(defun flymake-current-row ()
  "Return current row number in current frame."
Kim F. Storm's avatar
Kim F. Storm committed
137 138 139
  (if (featurep 'xemacs)
      (count-lines (window-start) (point))
    (+ (car (cdr (window-edges))) (count-lines (window-start) (point)))))
Richard M. Stallman's avatar
Richard M. Stallman committed
140 141

(defun flymake-selected-frame ()
Kim F. Storm's avatar
Kim F. Storm committed
142 143 144
  (if (featurep 'xemacs)
      (selected-window)
    (selected-frame)))
Eli Zaretskii's avatar
Eli Zaretskii committed
145 146 147 148

;;;; ]]

(defcustom flymake-log-level -1
Richard M. Stallman's avatar
Richard M. Stallman committed
149
  "Logging level, only messages with level > flymake-log-level will not be logged
Eli Zaretskii's avatar
Eli Zaretskii committed
150
-1 = NONE, 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG"
Richard M. Stallman's avatar
Richard M. Stallman committed
151 152
  :group 'flymake
  :type 'integer)
Eli Zaretskii's avatar
Eli Zaretskii committed
153

154
(defun flymake-log (level text &rest args)
Richard M. Stallman's avatar
Richard M. Stallman committed
155 156 157 158 159 160 161 162 163
  "Log a message with optional arguments."
  (if (<= level flymake-log-level)
      (let* ((msg (apply 'format text args)))
	(message msg)
	;;(with-temp-buffer
	;;    (insert msg)
	;;   (insert "\n")
	;;   (flymake-save-buffer-in-file (current-buffer) "d:/flymake.log" t)  ; make log file name customizable
	;;)
164
	)))
Eli Zaretskii's avatar
Eli Zaretskii committed
165

166
(defun flymake-ins-after (list pos val)
Richard M. Stallman's avatar
Richard M. Stallman committed
167 168 169 170
  "Insert VAL into LIST after position POS."
  (let ((tmp (copy-sequence list)))	; (???)
    (setcdr (nthcdr pos tmp) (cons val (nthcdr (1+ pos) tmp)))
    tmp))
Eli Zaretskii's avatar
Eli Zaretskii committed
171

172
(defun flymake-set-at (list pos val)
173
  "Set VAL at position POS in LIST."
Richard M. Stallman's avatar
Richard M. Stallman committed
174 175 176
  (let ((tmp (copy-sequence list)))	; (???)
    (setcar (nthcdr pos tmp) val)
    tmp))
Eli Zaretskii's avatar
Eli Zaretskii committed
177

178 179
(defvar flymake-pid-to-names (flymake-makehash)
  "pid -> source buffer name, output file name mapping.")
Eli Zaretskii's avatar
Eli Zaretskii committed
180

181
(defun flymake-reg-names (pid source-buffer-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
182 183 184 185
  "Save into in PID map."
  (unless (stringp source-buffer-name)
    (error "Invalid buffer name"))
  (puthash pid (list source-buffer-name) flymake-pid-to-names))
Eli Zaretskii's avatar
Eli Zaretskii committed
186

187
(defun flymake-get-source-buffer-name (pid)
Richard M. Stallman's avatar
Richard M. Stallman committed
188 189
  "Return buffer name stored in PID map."
  (nth 0 (gethash pid flymake-pid-to-names)))
Eli Zaretskii's avatar
Eli Zaretskii committed
190

191
(defun flymake-unreg-names (pid)
Richard M. Stallman's avatar
Richard M. Stallman committed
192 193
  "Delete PID->buffer name mapping."
  (remhash pid flymake-pid-to-names))
Eli Zaretskii's avatar
Eli Zaretskii committed
194

195
(defun flymake-get-buffer-var (buffer var-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
196 197 198
  "Switch to BUFFER if necessary and return local variable VAR-NAME."
  (unless (bufferp buffer)
    (error "Invalid buffer"))
Eli Zaretskii's avatar
Eli Zaretskii committed
199

Richard M. Stallman's avatar
Richard M. Stallman committed
200 201 202 203 204
  (if (eq buffer (current-buffer))
      (symbol-value var-name)
    (save-excursion
      (set-buffer buffer)
      (symbol-value var-name))))
Eli Zaretskii's avatar
Eli Zaretskii committed
205

206
(defun flymake-set-buffer-var (buffer var-name var-value)
Richard M. Stallman's avatar
Richard M. Stallman committed
207 208 209
  "Switch to BUFFER if necessary and set local variable VAR-NAME to VAR-VALUE."
  (unless (bufferp buffer)
    (error "Invalid buffer"))
Eli Zaretskii's avatar
Eli Zaretskii committed
210

Richard M. Stallman's avatar
Richard M. Stallman committed
211 212 213 214 215
  (if (eq buffer (current-buffer))
      (set var-name var-value)
    (save-excursion
      (set-buffer buffer)
      (set var-name var-value))))
216 217 218

(defvar flymake-buffer-data (flymake-makehash)
  "Data specific to syntax check tool, in name-value pairs.")
Eli Zaretskii's avatar
Eli Zaretskii committed
219 220 221

(make-variable-buffer-local 'flymake-buffer-data)

222
(defun flymake-get-buffer-data (buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
223
  (flymake-get-buffer-var buffer 'flymake-buffer-data))
224 225

(defun flymake-set-buffer-data (buffer data)
Richard M. Stallman's avatar
Richard M. Stallman committed
226
  (flymake-set-buffer-var buffer 'flymake-buffer-data data))
227 228

(defun flymake-get-buffer-value (buffer name)
Richard M. Stallman's avatar
Richard M. Stallman committed
229
  (gethash name (flymake-get-buffer-data buffer)))
230 231

(defun flymake-set-buffer-value (buffer name value)
Richard M. Stallman's avatar
Richard M. Stallman committed
232
  (puthash name value (flymake-get-buffer-data buffer)))
233

234
(defvar flymake-output-residual nil)
235

Eli Zaretskii's avatar
Eli Zaretskii committed
236
(make-variable-buffer-local 'flymake-output-residual)
237 238

(defun flymake-get-buffer-output-residual (buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
239
  (flymake-get-buffer-var buffer 'flymake-output-residual))
240 241

(defun flymake-set-buffer-output-residual (buffer residual)
Richard M. Stallman's avatar
Richard M. Stallman committed
242 243 244 245 246 247 248 249 250 251 252 253 254 255
  (flymake-set-buffer-var buffer 'flymake-output-residual residual))

(defcustom flymake-allowed-file-name-masks
  '((".+\\.c$" flymake-simple-make-init flymake-simple-cleanup flymake-get-real-file-name)
    (".+\\.cpp$" flymake-simple-make-init flymake-simple-cleanup flymake-get-real-file-name)
    (".+\\.xml$" flymake-xml-init flymake-simple-cleanup flymake-get-real-file-name)
    (".+\\.html?$" flymake-xml-init flymake-simple-cleanup flymake-get-real-file-name)
    (".+\\.cs$" flymake-simple-make-init flymake-simple-cleanup flymake-get-real-file-name)
    (".+\\.pl$" flymake-perl-init flymake-simple-cleanup flymake-get-real-file-name)
    (".+\\.h$" flymake-master-make-header-init flymake-master-cleanup flymake-get-real-file-name)
    (".+\\.java$" flymake-simple-make-java-init flymake-simple-java-cleanup flymake-get-real-file-name)
    (".+[0-9]+\\.tex$" flymake-master-tex-init flymake-master-cleanup flymake-get-real-file-name)
    (".+\\.tex$" flymake-simple-tex-init flymake-simple-cleanup flymake-get-real-file-name)
    (".+\\.idl$" flymake-simple-make-init flymake-simple-cleanup flymake-get-real-file-name)
256 257 258 259 260 261 262 263 264
    ;; (".+\\.cpp$" 1)
    ;; (".+\\.java$" 3)
    ;; (".+\\.h$" 2 (".+\\.cpp$" ".+\\.c$")
    ;; ("[ \t]*#[ \t]*include[ \t]*\"\\([\w0-9/\\_\.]*[/\\]*\\)\\(%s\\)\"" 1 2))
    ;; (".+\\.idl$" 1)
    ;; (".+\\.odl$" 1)
    ;; (".+[0-9]+\\.tex$" 2 (".+\\.tex$")
    ;; ("[ \t]*\\input[ \t]*{\\(.*\\)\\(%s\\)}" 1 2 ))
    ;; (".+\\.tex$" 1)
Richard M. Stallman's avatar
Richard M. Stallman committed
265 266 267 268
    )
  "*Files syntax checking is allowed for."
  :group 'flymake
  :type '(repeat (string symbol symbol symbol)))
Eli Zaretskii's avatar
Eli Zaretskii committed
269

270
(defun flymake-get-file-name-mode-and-masks (file-name)
271
  "Return the corresponding entry from `flymake-allowed-file-name-masks'."
Richard M. Stallman's avatar
Richard M. Stallman committed
272 273 274 275 276 277 278 279 280 281 282
  (unless (stringp file-name)
    (error "Invalid file-name"))
  (let ((count           (length flymake-allowed-file-name-masks))
	(idx             0)
	(mode-and-masks  nil))
    (while (and (not mode-and-masks) (< idx count))
      (if (string-match (nth 0 (nth idx flymake-allowed-file-name-masks)) file-name)
	  (setq mode-and-masks (cdr (nth idx flymake-allowed-file-name-masks))))
      (setq idx (1+ idx)))
    (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks))
    mode-and-masks))
Eli Zaretskii's avatar
Eli Zaretskii committed
283

284
(defun flymake-can-syntax-check-file (file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
285
  "Determine whether we can syntax check FILE-NAME.
286
Return nil if we cannot, non-nil if we can."
Richard M. Stallman's avatar
Richard M. Stallman committed
287
  (if (flymake-get-init-function file-name) t nil))
Eli Zaretskii's avatar
Eli Zaretskii committed
288

289
(defun flymake-get-init-function (file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
290 291
  "Return init function to be used for the file."
  (let* ((init-f  (nth 0 (flymake-get-file-name-mode-and-masks file-name))))
292 293
    ;;(flymake-log 0 "calling %s" init-f)
    ;;(funcall init-f (current-buffer))
Richard M. Stallman's avatar
Richard M. Stallman committed
294
    init-f))
Eli Zaretskii's avatar
Eli Zaretskii committed
295

296
(defun flymake-get-cleanup-function (file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
297 298
  "Return cleanup function to be used for the file."
  (nth 1 (flymake-get-file-name-mode-and-masks file-name)))
Eli Zaretskii's avatar
Eli Zaretskii committed
299

300
(defun flymake-get-real-file-name-function (file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
301
  (or (nth 2 (flymake-get-file-name-mode-and-masks file-name)) 'flymake-get-real-file-name))
Eli Zaretskii's avatar
Eli Zaretskii committed
302 303

(defcustom flymake-buildfile-dirs '("." ".." "../.." "../../.." "../../../.." "../../../../.." "../../../../../.." "../../../../../../.." "../../../../../../../.." "../../../../../../../../.." "../../../../../../../../../.." "../../../../../../../../../../..")
Richard M. Stallman's avatar
Richard M. Stallman committed
304 305 306
  "Dirs to look for buildfile."
  :group 'flymake
  :type '(repeat (string)))
Eli Zaretskii's avatar
Eli Zaretskii committed
307 308 309

(defvar flymake-find-buildfile-cache (flymake-makehash 'equal))

310
(defun flymake-get-buildfile-from-cache (dir-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
311
  (gethash dir-name flymake-find-buildfile-cache))
312 313

(defun flymake-add-buildfile-to-cache (dir-name buildfile)
Richard M. Stallman's avatar
Richard M. Stallman committed
314
  (puthash dir-name buildfile flymake-find-buildfile-cache))
315 316

(defun flymake-clear-buildfile-cache ()
Richard M. Stallman's avatar
Richard M. Stallman committed
317
  (clrhash flymake-find-buildfile-cache))
318 319

(defun flymake-find-buildfile (buildfile-name source-dir-name dirs)
Richard M. Stallman's avatar
Richard M. Stallman committed
320
  "Find buildfile starting from current directory.
321
Buildfile includes Makefile, build.xml etc.
Richard M. Stallman's avatar
Richard M. Stallman committed
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
Return its file name if found, or nil if not found."
  (if (flymake-get-buildfile-from-cache source-dir-name)
      (progn
	(flymake-get-buildfile-from-cache source-dir-name))
    (let* ((buildfile-dir          nil)
	   (buildfile              nil)
	   (dir-count              (length dirs))
	   (dir-idx                0)
	   (found                  nil))
      (while (and (not found) (< dir-idx dir-count))
	(setq buildfile-dir (concat source-dir-name (nth dir-idx dirs)))
	(setq buildfile (concat buildfile-dir "/" buildfile-name))
	(when (file-exists-p buildfile)
	  (setq found t))
	(setq dir-idx (1+ dir-idx)))
      (if found
	  (progn
	    (flymake-log 3 "found buildfile at %s/%s" buildfile-dir buildfile-name)
	    (flymake-add-buildfile-to-cache source-dir-name buildfile-dir)
	    buildfile-dir)
Eli Zaretskii's avatar
Eli Zaretskii committed
342
	(progn
Richard M. Stallman's avatar
Richard M. Stallman committed
343 344 345 346 347 348 349 350 351 352 353 354
	  (flymake-log 3 "buildfile for %s not found" source-dir-name)
	  nil)))))

(defun flymake-fix-file-name (name)
  "Replace all occurences of '\' with '/'."
  (when name
    (let* ((new-name (flymake-replace-regexp-in-string "[\\]" "/" (expand-file-name name)))
	   (last-char (elt new-name (1- (length new-name)))))
      (setq new-name (flymake-replace-regexp-in-string "\\./" "" new-name))
      (if (equal "/" (char-to-string last-char))
	  (setq new-name (substring new-name 0 (1- (length new-name)))))
      new-name)))
Eli Zaretskii's avatar
Eli Zaretskii committed
355

356
(defun flymake-same-files (file-name-one file-name-two)
Richard M. Stallman's avatar
Richard M. Stallman committed
357
  "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file.
358
Return t if so, nil if not."
Richard M. Stallman's avatar
Richard M. Stallman committed
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
  (equal (flymake-fix-file-name file-name-one)
	 (flymake-fix-file-name file-name-two)))

(defun flymake-ensure-ends-with-slash (filename)
  ;; Should this really be file-name-as-directory?
  (if (not (= (elt filename (1- (length filename))) (string-to-char "/")))
      (concat filename "/")
    filename))

(defun flymake-get-common-file-prefix (string-one string-two)
  "Return common prefix for two file names STRING-ONE and STRING-TWO."
  (when (and string-one string-two)
    (let* ((slash-pos-one  -1)
	   (slash-pos-two  -1)
	   (done           nil)
	   (prefix         nil))
      (setq string-one (flymake-ensure-ends-with-slash string-one))
      (setq string-two (flymake-ensure-ends-with-slash string-two))
      (while (not done)
	(setq slash-pos-one (string-match "/" string-one (1+ slash-pos-one)))
	(setq slash-pos-two (string-match "/" string-two (1+ slash-pos-two)))
	(if (and slash-pos-one slash-pos-two
		 (= slash-pos-one slash-pos-two)
		 (string= (substring string-one 0 slash-pos-one) (substring string-two 0 slash-pos-two)))
	    (progn
	      (setq prefix (substring string-one 0 (1+ slash-pos-one))))
	  (setq done t)))
      prefix)))

(defun flymake-build-relative-filename (from-dir to-dir)
  "Return rel: FROM-DIR/rel == TO-DIR."
390
  ;; FIXME: Why not use `file-relative-name'?
Richard M. Stallman's avatar
Richard M. Stallman committed
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
  (if (not (equal (elt from-dir 0) (elt to-dir 0)))
      (error "First chars in file names %s, %s must be equal (same drive)"
	     from-dir to-dir)
    (let* ((from (flymake-ensure-ends-with-slash (flymake-fix-file-name from-dir)))
	   (to   (flymake-ensure-ends-with-slash (flymake-fix-file-name to-dir)))
	   (prefix      (flymake-get-common-file-prefix from to))
	   (from-suffix (substring from (length prefix)))
	   (up-count    (length (flymake-split-string from-suffix "[/]")))
	   (to-suffix   (substring to   (length prefix)))
	   (idx         0)
	   (rel         nil))
      (if (and (> (length to-suffix) 0) (equal "/" (char-to-string (elt to-suffix 0))))
	  (setq to-suffix (substring to-suffix 1)))

      (while (< idx up-count)
	(if (> (length rel) 0)
	    (setq rel (concat rel "/")))
	(setq rel (concat rel ".."))
	(setq idx (1+ idx)))
      (if (> (length rel) 0)
	  (setq rel (concat rel "/")))
      (if (> (length to-suffix) 0)
	  (setq rel (concat rel to-suffix)))
      (or rel "./"))))
Eli Zaretskii's avatar
Eli Zaretskii committed
415 416

(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest")
417
  "Dirs where to look for master files."
Richard M. Stallman's avatar
Richard M. Stallman committed
418 419
  :group 'flymake
  :type '(repeat (string)))
Eli Zaretskii's avatar
Eli Zaretskii committed
420 421

(defcustom flymake-master-file-count-limit 32
Richard M. Stallman's avatar
Richard M. Stallman committed
422 423 424
  "Max number of master files to check."
  :group 'flymake
  :type 'integer)
425

Kim F. Storm's avatar
Kim F. Storm committed
426 427
;; This is bound dynamically to pass a parameter to a sort predicate below
(defvar flymake-included-file-name)
Eli Zaretskii's avatar
Eli Zaretskii committed
428

429
(defun flymake-find-possible-master-files (file-name master-file-dirs masks)
430 431
  "Find (by name and location) all possible master files.
Master files are .cpp and .c for and .h. Files are searched for
432
starting from the .h directory and max max-level parent dirs.
Eli Zaretskii's avatar
Eli Zaretskii committed
433
File contents are not checked."
Richard M. Stallman's avatar
Richard M. Stallman committed
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
  (let* ((dir-idx    0)
	 (dir-count  (length master-file-dirs))
	 (files  nil)
	 (done   nil)
	 (masks-count (length masks)))

    (while (and (not done) (< dir-idx dir-count))
      (let* ((dir (concat (flymake-fix-file-name (file-name-directory file-name)) "/" (nth dir-idx master-file-dirs)))
	     (masks-idx 0))
	(while (and (file-exists-p dir) (not done) (< masks-idx masks-count))
	  (let* ((mask        (nth masks-idx masks))
		 (dir-files   (directory-files dir t mask))
		 (file-count  (length dir-files))
		 (file-idx    0))

	    (flymake-log 3 "dir %s, %d file(s) for mask %s" dir file-count mask)
	    (while (and (not done) (< file-idx file-count))
	      (when (not (file-directory-p (nth file-idx dir-files)))
		(setq files (cons (nth file-idx dir-files) files))
		(when (>= (length files) flymake-master-file-count-limit)
		  (flymake-log 3 "master file count limit (%d) reached" flymake-master-file-count-limit)
		  (setq done t)))
	      (setq file-idx (1+ file-idx))))
	  (setq masks-idx (1+ masks-idx))))
      (setq dir-idx (1+ dir-idx)))
    (when files
Kim F. Storm's avatar
Kim F. Storm committed
460 461
      (let ((flymake-included-file-name (file-name-nondirectory file-name)))
	(setq files (sort files 'flymake-master-file-compare))))
Richard M. Stallman's avatar
Richard M. Stallman committed
462 463
    (flymake-log 3 "found %d possible master file(s)" (length files))
    files))
Eli Zaretskii's avatar
Eli Zaretskii committed
464

465
(defun flymake-master-file-compare (file-one file-two)
Richard M. Stallman's avatar
Richard M. Stallman committed
466
  "Compare two files speccified by FILE-ONE and FILE-TWO.
467 468
This function is used in sort to move most possible file names
to the beginning of the list (File.h -> File.cpp moved to top."
Richard M. Stallman's avatar
Richard M. Stallman committed
469 470 471
  (and (equal (file-name-sans-extension flymake-included-file-name)
	      (file-name-sans-extension (file-name-nondirectory file-one)))
       (not (equal file-one file-two))))
Eli Zaretskii's avatar
Eli Zaretskii committed
472 473

(defcustom flymake-check-file-limit 8192
Richard M. Stallman's avatar
Richard M. Stallman committed
474 475 476
  "Max number of chars to look at when checking possible master file."
  :group 'flymake
  :type 'integer)
Eli Zaretskii's avatar
Eli Zaretskii committed
477

478
(defun flymake-check-patch-master-file-buffer (master-file-temp-buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
479 480 481 482
					       master-file-name patched-master-file-name
					       source-file-name patched-source-file-name
					       include-dirs regexp-list)
  "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME.
483 484 485 486 487
For .cpp master file this means it includes SOURCE-FILE-NAME (.h).
If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME
instead of SOURCE-FILE-NAME.
Whether a buffer for MATER-FILE-NAME exists, use it as a source
instead of reading master file from disk."
Richard M. Stallman's avatar
Richard M. Stallman committed
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
  (let* ((found                     nil)
	 (regexp                    (format (nth 0 regexp-list)	; "[ \t]*#[ \t]*include[ \t]*\"\\([\w0-9/\\_\.]*[/\\]*\\)\\(%s\\)\""
					    (file-name-nondirectory source-file-name)))
	 (path-idx                  (nth 1 regexp-list))
	 (name-idx                  (nth 2 regexp-list))
	 (inc-path                  nil)
	 (inc-name                  nil)
	 (search-limit              flymake-check-file-limit))
    (save-excursion
      (unwind-protect
	  (progn
	    (set-buffer master-file-temp-buffer)
	    (when (> search-limit (point-max))
	      (setq search-limit (point-max)))
	    (flymake-log 3 "checking %s against regexp %s" master-file-name regexp)
	    (goto-char (point-min))
	    (while (and (< (point) search-limit) (re-search-forward regexp search-limit t))
	      (let* ((match-beg   (match-beginning name-idx))
		     (match-end   (match-end name-idx)))

		(flymake-log 3 "found possible match for %s" (file-name-nondirectory source-file-name))
		(setq inc-path (match-string path-idx))
		(setq inc-name (match-string name-idx))
		(when (string= inc-name (file-name-nondirectory source-file-name))
		  (flymake-log 3 "inc-path=%s inc-name=%s" inc-path inc-name)
		  (when (flymake-check-include source-file-name inc-path inc-name include-dirs)
		    (setq found t)
		    ;;  replace-match is not used here as it fails in
516
		    ;; XEmacs with 'last match not a buffer' error as
Richard M. Stallman's avatar
Richard M. Stallman committed
517 518 519 520 521 522
		    ;; check-includes calls replace-in-string
		    (flymake-replace-region (current-buffer) match-beg match-end
					    (file-name-nondirectory patched-source-file-name))))
		(forward-line 1)))
	    (when found
	      (flymake-save-buffer-in-file (current-buffer) patched-master-file-name)))
523
	;;+(flymake-log 3 "killing buffer %s" (buffer-name master-file-temp-buffer))
Richard M. Stallman's avatar
Richard M. Stallman committed
524
	(kill-buffer master-file-temp-buffer)))
525
    ;;+(flymake-log 3 "check-patch master file %s: %s" master-file-name found)
Richard M. Stallman's avatar
Richard M. Stallman committed
526 527 528
    (when found
      (flymake-log 2 "found master file %s" master-file-name))
    found))
Eli Zaretskii's avatar
Eli Zaretskii committed
529

530
(defun flymake-replace-region (buffer beg end rep)
Richard M. Stallman's avatar
Richard M. Stallman committed
531 532 533 534 535
  "Replace text in BUFFER in region (BEG END) with REP."
  (save-excursion
    (delete-region beg end)
    (goto-char beg)
    (insert rep)))
Eli Zaretskii's avatar
Eli Zaretskii committed
536

537
(defun flymake-read-file-to-temp-buffer (file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
538 539 540 541 542 543
  "Insert contents of FILE-NAME into newly created temp buffer."
  (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name))))))
    (save-excursion
      (set-buffer temp-buffer)
      (insert-file-contents file-name))
    temp-buffer))
Eli Zaretskii's avatar
Eli Zaretskii committed
544

545
(defun flymake-copy-buffer-to-temp-buffer (buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
546 547 548 549 550 551 552 553 554 555 556
  "Copy contents of BUFFER into newly created temp buffer."
  (let ((contents     nil)
	(temp-buffer  nil))
    (save-excursion
      (set-buffer buffer)
      (setq contents (buffer-string))

      (setq temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (buffer-name buffer)))))
      (set-buffer temp-buffer)
      (insert contents))
    temp-buffer))
Eli Zaretskii's avatar
Eli Zaretskii committed
557

558
(defun flymake-check-include (source-file-name inc-path inc-name include-dirs)
Richard M. Stallman's avatar
Richard M. Stallman committed
559
  "Check if SOURCE-FILE-NAME can be found in include path.
560
Return t if it can be found via include path using INC-PATH and INC-NAME."
Richard M. Stallman's avatar
Richard M. Stallman committed
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
  (if (file-name-absolute-p inc-path)
      (flymake-same-files source-file-name (concat inc-path "/" inc-name))
    (let* ((count      (length include-dirs))
	   (idx        0)
	   (file-name  nil)
	   (found      nil))
      (while (and (not found) (< idx count))
	(setq file-name (concat (file-name-directory source-file-name) "/" (nth idx include-dirs)))
	(if (> (length inc-path) 0)
	    (setq file-name (concat file-name "/" inc-path)))
	(setq file-name (concat file-name "/" inc-name))
	(when (flymake-same-files source-file-name file-name)
	  (setq found t))
	(setq idx (1+ idx)))
      found)))
Eli Zaretskii's avatar
Eli Zaretskii committed
576

577
(defun flymake-find-buffer-for-file (file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
578
  "Check if there exists a buffer visiting FILE-NAME.
579
Return t if so, nil if not."
Richard M. Stallman's avatar
Richard M. Stallman committed
580 581 582
  (let ((buffer-name (get-file-buffer file-name)))
    (if buffer-name
	(get-buffer buffer-name))))
Eli Zaretskii's avatar
Eli Zaretskii committed
583

584
(defun flymake-create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp-list)
Richard M. Stallman's avatar
Richard M. Stallman committed
585
  "Save SOURCE-FILE-NAME with a different name.
586
Find master file, patch and save it."
Richard M. Stallman's avatar
Richard M. Stallman committed
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
  (let* ((possible-master-files     (flymake-find-possible-master-files source-file-name flymake-master-file-dirs masks))
	 (master-file-count         (length possible-master-files))
	 (idx                       0)
	 (temp-buffer               nil)
	 (master-file-name          nil)
	 (patched-master-file-name  nil)
	 (found                     nil))

    (while (and (not found) (< idx master-file-count))
      (setq master-file-name (nth idx possible-master-files))
      (setq patched-master-file-name (funcall create-temp-f master-file-name "flymake_master"))
      (if (flymake-find-buffer-for-file master-file-name)
	  (setq temp-buffer (flymake-copy-buffer-to-temp-buffer (flymake-find-buffer-for-file master-file-name)))
	(setq temp-buffer (flymake-read-file-to-temp-buffer master-file-name)))
      (setq found
	    (flymake-check-patch-master-file-buffer
	     temp-buffer
	     master-file-name
	     patched-master-file-name
	     source-file-name
	     patched-source-file-name
	     (funcall get-incl-dirs-f (file-name-directory master-file-name))
	     include-regexp-list))
      (setq idx (1+ idx)))
    (if found
	(list master-file-name patched-master-file-name)
      (progn
	(flymake-log 3 "none of %d master file(s) checked includes %s" master-file-count
		     (file-name-nondirectory source-file-name))
	nil))))
Eli Zaretskii's avatar
Eli Zaretskii committed
617

618
(defun flymake-save-buffer-in-file (buffer file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
619 620 621 622 623 624 625 626 627
  (or buffer
      (error "Invalid buffer"))
  (save-excursion
    (save-restriction
      (set-buffer buffer)
      (widen)
      (make-directory (file-name-directory file-name) 1)
      (write-region (point-min) (point-max) file-name nil 566)))
  (flymake-log 3 "saved buffer %s in file %s" (buffer-name buffer) file-name))
Eli Zaretskii's avatar
Eli Zaretskii committed
628

629
(defun flymake-save-string-to-file (file-name data)
Richard M. Stallman's avatar
Richard M. Stallman committed
630 631
  "Save string DATA to file FILE-NAME."
  (write-region data nil file-name nil 566))
Eli Zaretskii's avatar
Eli Zaretskii committed
632

633
(defun flymake-read-file-to-string (file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
634 635 636 637
  "Read contents of file FILE-NAME and return as a string."
  (with-temp-buffer
    (insert-file-contents file-name)
    (buffer-substring (point-min) (point-max))))
Eli Zaretskii's avatar
Eli Zaretskii committed
638

639
(defun flymake-process-filter (process output)
Richard M. Stallman's avatar
Richard M. Stallman committed
640
  "Parse OUTPUT and highlight error lines.
641
It's flymake process filter."
Richard M. Stallman's avatar
Richard M. Stallman committed
642 643
  (let* ((pid               (process-id process))
	 (source-buffer     (get-buffer (flymake-get-source-buffer-name pid))))
Eli Zaretskii's avatar
Eli Zaretskii committed
644

Richard M. Stallman's avatar
Richard M. Stallman committed
645 646 647
    (flymake-log 3 "received %d byte(s) of output from process %d" (length output) pid)
    (when source-buffer
      (flymake-parse-output-and-residual source-buffer output))))
Eli Zaretskii's avatar
Eli Zaretskii committed
648

649
(defun flymake-process-sentinel (process event)
Richard M. Stallman's avatar
Richard M. Stallman committed
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
  "Sentinel for syntax check buffers."
  (if (memq (process-status process) '(signal exit))
      (let*((exit-status       (process-exit-status process))
	    (command           (process-command process))
	    (pid               (process-id process))
	    (source-buffer     (get-buffer (flymake-get-source-buffer-name pid)))
	    (cleanup-f         (flymake-get-cleanup-function (buffer-file-name source-buffer))))

	(flymake-log 2 "process %d exited with code %d" pid exit-status)
	(condition-case err
	    (progn
	      (flymake-log 3 "cleaning up using %s" cleanup-f)
	      (funcall cleanup-f source-buffer)

	      (flymake-unreg-names pid)
	      (delete-process process)

	      (when source-buffer
		(save-excursion
		  (set-buffer source-buffer)

		  (flymake-parse-residual source-buffer)
		  (flymake-post-syntax-check source-buffer exit-status command)
		  (flymake-set-buffer-is-running source-buffer nil))))
	  (error
	   (let ((err-str (format "Error in process sentinel for buffer %s: %s"
				  source-buffer (error-message-string err))))
	     (flymake-log 0 err-str)
	     (flymake-set-buffer-is-running source-buffer nil)))))))
Eli Zaretskii's avatar
Eli Zaretskii committed
679

680
(defun flymake-post-syntax-check (source-buffer exit-status command)
Richard M. Stallman's avatar
Richard M. Stallman committed
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
  (flymake-set-buffer-err-info source-buffer (flymake-get-buffer-new-err-info source-buffer))
  (flymake-set-buffer-new-err-info source-buffer nil)

  (flymake-set-buffer-err-info source-buffer (flymake-fix-line-numbers
					      (flymake-get-buffer-err-info source-buffer)
					      1
					      (flymake-count-lines source-buffer)))
  (flymake-delete-own-overlays source-buffer)
  (flymake-highlight-err-lines source-buffer (flymake-get-buffer-err-info source-buffer))

  (let ((err-count   (flymake-get-err-count (flymake-get-buffer-err-info source-buffer) "e"))
	(warn-count  (flymake-get-err-count (flymake-get-buffer-err-info source-buffer) "w")))

    (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)"
		 (buffer-name source-buffer) err-count warn-count
		 (- (flymake-float-time) (flymake-get-buffer-check-start-time source-buffer)))
    (flymake-set-buffer-check-start-time source-buffer nil)
    (if (and (equal 0 err-count) (equal 0 warn-count))
	(if (equal 0 exit-status)
	    (flymake-report-status source-buffer "" "")	; PASSED
	  (if (not (flymake-get-buffer-check-was-interrupted source-buffer))
	      (flymake-report-fatal-status (current-buffer) "CFGERR"
					   (format "Configuration error has occured while running %s" command))
	    (flymake-report-status source-buffer nil ""))) ; "STOPPED"
      (flymake-report-status source-buffer (format "%d/%d" err-count warn-count) ""))))
Eli Zaretskii's avatar
Eli Zaretskii committed
706

707
(defun flymake-parse-output-and-residual (source-buffer output)
Richard M. Stallman's avatar
Richard M. Stallman committed
708 709 710 711 712 713 714 715 716 717 718 719 720
  "Split OUTPUT into lines, merge in residual if necessary."
  (save-excursion
    (set-buffer source-buffer)
    (let* ((buffer-residual     (flymake-get-buffer-output-residual source-buffer))
	   (total-output        (if buffer-residual (concat buffer-residual output) output))
	   (lines-and-residual  (flymake-split-output total-output))
	   (lines               (nth 0 lines-and-residual))
	   (new-residual        (nth 1 lines-and-residual)))

      (flymake-set-buffer-output-residual source-buffer new-residual)
      (flymake-set-buffer-new-err-info source-buffer (flymake-parse-err-lines
						      (flymake-get-buffer-new-err-info source-buffer)
						      source-buffer lines)))))
Eli Zaretskii's avatar
Eli Zaretskii committed
721

722
(defun flymake-parse-residual (source-buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
723 724 725 726 727 728 729 730 731
  "Parse residual if it's non empty."
  (save-excursion
    (set-buffer source-buffer)
    (when (flymake-get-buffer-output-residual source-buffer)
      (flymake-set-buffer-new-err-info source-buffer (flymake-parse-err-lines
						      (flymake-get-buffer-new-err-info source-buffer)
						      source-buffer
						      (list (flymake-get-buffer-output-residual source-buffer))))
      (flymake-set-buffer-output-residual source-buffer nil))))
Eli Zaretskii's avatar
Eli Zaretskii committed
732 733

(defvar flymake-err-info nil
734 735
  "Sorted list of line numbers and lists of err info in the form (file, err-text).")

Eli Zaretskii's avatar
Eli Zaretskii committed
736
(make-variable-buffer-local 'flymake-err-info)
737 738

(defun flymake-get-buffer-err-info (buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
739
  (flymake-get-buffer-var buffer 'flymake-err-info))
740 741

(defun flymake-set-buffer-err-info (buffer err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
742
  (flymake-set-buffer-var buffer 'flymake-err-info err-info))
743 744

(defun flymake-er-make-er (line-no line-err-info-list)
Richard M. Stallman's avatar
Richard M. Stallman committed
745
  (list line-no line-err-info-list))
746 747

(defun flymake-er-get-line (err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
748
  (nth 0 err-info))
749 750

(defun flymake-er-get-line-err-info-list (err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
751
  (nth 1 err-info))
Eli Zaretskii's avatar
Eli Zaretskii committed
752 753

(defvar flymake-new-err-info nil
754 755
  "Same as 'flymake-err-info', effective when a syntax check is in progress.")

Eli Zaretskii's avatar
Eli Zaretskii committed
756
(make-variable-buffer-local 'flymake-new-err-info)
757 758

(defun flymake-get-buffer-new-err-info (buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
759
  (flymake-get-buffer-var buffer 'flymake-new-err-info))
760 761

(defun flymake-set-buffer-new-err-info (buffer new-err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
762
  (flymake-set-buffer-var buffer 'flymake-new-err-info new-err-info))
Eli Zaretskii's avatar
Eli Zaretskii committed
763 764

;; getters/setters for line-err-info: (file, line, type, text).
765
(defun flymake-ler-make-ler (file line type text &optional full-file)
Richard M. Stallman's avatar
Richard M. Stallman committed
766
  (list file line type text full-file))
767 768

(defun flymake-ler-get-file (line-err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
769
  (nth 0 line-err-info))
770 771

(defun flymake-ler-get-line (line-err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
772
  (nth 1 line-err-info))
773 774

(defun flymake-ler-get-type (line-err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
775
  (nth 2 line-err-info))
776 777

(defun flymake-ler-get-text (line-err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
778
  (nth 3 line-err-info))
779 780

(defun flymake-ler-get-full-file (line-err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
781
  (nth 4 line-err-info))
782 783

(defun flymake-ler-set-file (line-err-info file)
Richard M. Stallman's avatar
Richard M. Stallman committed
784
  (flymake-ler-make-ler file
Eli Zaretskii's avatar
Eli Zaretskii committed
785 786 787
			(flymake-ler-get-line line-err-info)
			(flymake-ler-get-type line-err-info)
			(flymake-ler-get-text line-err-info)
788 789 790
			(flymake-ler-get-full-file line-err-info)))

(defun flymake-ler-set-full-file (line-err-info full-file)
Richard M. Stallman's avatar
Richard M. Stallman committed
791
  (flymake-ler-make-ler (flymake-ler-get-file line-err-info)
Eli Zaretskii's avatar
Eli Zaretskii committed
792 793 794
			(flymake-ler-get-line line-err-info)
			(flymake-ler-get-type line-err-info)
			(flymake-ler-get-text line-err-info)
795 796 797
			full-file))

(defun flymake-ler-set-line (line-err-info line)
Richard M. Stallman's avatar
Richard M. Stallman committed
798
  (flymake-ler-make-ler (flymake-ler-get-file line-err-info)
Eli Zaretskii's avatar
Eli Zaretskii committed
799 800 801
			line
			(flymake-ler-get-type line-err-info)
			(flymake-ler-get-text line-err-info)
802
			(flymake-ler-get-full-file line-err-info)))
Eli Zaretskii's avatar
Eli Zaretskii committed
803

804
(defun flymake-get-line-err-count (line-err-info-list type)
Richard M. Stallman's avatar
Richard M. Stallman committed
805
  "Return number of errors of specified TYPE.
806
Value of TYPE is eigher e or w."
Richard M. Stallman's avatar
Richard M. Stallman committed
807 808 809
  (let* ((idx        0)
	 (count      (length line-err-info-list))
	 (err-count  0))
Eli Zaretskii's avatar
Eli Zaretskii committed
810

Richard M. Stallman's avatar
Richard M. Stallman committed
811 812 813 814 815
    (while (< idx count)
      (when (equal type (flymake-ler-get-type (nth idx line-err-info-list)))
	(setq err-count (1+ err-count)))
      (setq idx (1+ idx)))
    err-count))
Eli Zaretskii's avatar
Eli Zaretskii committed
816

817
(defun flymake-get-err-count (err-info-list type)
Richard M. Stallman's avatar
Richard M. Stallman committed
818 819 820 821 822 823 824 825
  "Return number of errors of specified TYPE for ERR-INFO-LIST."
  (let* ((idx        0)
	 (count      (length err-info-list))
	 (err-count  0))
    (while (< idx count)
      (setq err-count (+ err-count (flymake-get-line-err-count (nth 1 (nth idx err-info-list)) type)))
      (setq idx (1+ idx)))
    err-count))
826 827

(defun flymake-fix-line-numbers (err-info-list min-line max-line)
Richard M. Stallman's avatar
Richard M. Stallman committed
828
  "Replace line numbers with fixed value.
829 830
If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE.
If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE.
Kim F. Storm's avatar
Kim F. Storm committed
831
The reason for this fix is because some compilers might report
832
line number outside the file being compiled."
Richard M. Stallman's avatar
Richard M. Stallman committed
833 834 835 836 837 838 839 840 841 842 843 844 845
  (let* ((count     (length err-info-list))
	 (err-info  nil)
	 (line      0))
    (while (> count 0)
      (setq err-info (nth (1- count) err-info-list))
      (setq line (flymake-er-get-line err-info))
      (when (or (< line min-line) (> line max-line))
	(setq line (if (< line min-line) min-line max-line))
	(setq err-info-list (flymake-set-at err-info-list (1- count)
					    (flymake-er-make-er line
								(flymake-er-get-line-err-info-list err-info)))))
      (setq count (1- count))))
  err-info-list)
Eli Zaretskii's avatar
Eli Zaretskii committed
846

847
(defun flymake-highlight-err-lines (buffer err-info-list)
Richard M. Stallman's avatar
Richard M. Stallman committed
848 849 850 851 852 853 854 855
  "Highlight error lines in BUFFER using info from ERR-INFO-LIST."
  (save-excursion
    (set-buffer buffer)
    (let* ((idx    0)
	   (count  (length err-info-list)))
      (while (< idx count)
	(flymake-highlight-line (car (nth idx err-info-list)) (nth 1 (nth idx err-info-list)))
	(setq idx (1+ idx))))))
Eli Zaretskii's avatar
Eli Zaretskii committed
856

857
(defun flymake-overlay-p (ov)
Richard M. Stallman's avatar
Richard M. Stallman committed
858 859
  "Determine whether overlay OV was created by flymake."
  (and (overlayp ov) (overlay-get ov 'flymake-overlay)))
Eli Zaretskii's avatar
Eli Zaretskii committed
860

861
(defun flymake-make-overlay (beg end tooltip-text face mouse-face)
Richard M. Stallman's avatar
Richard M. Stallman committed
862 863 864 865 866 867 868 869
  "Allocate a flymake overlay in range BEG and END."
  (when (not (flymake-region-has-flymake-overlays beg end))
    (let ((ov (make-overlay beg end nil t t)))
      (overlay-put ov 'face           face)
      (overlay-put ov 'mouse-face     mouse-face)
      (overlay-put ov 'help-echo      tooltip-text)
      (overlay-put ov 'flymake-overlay  t)
      (overlay-put ov 'priority 100)
870
      ;;+(flymake-log 3 "created overlay %s" ov)
Richard M. Stallman's avatar
Richard M. Stallman committed
871 872
      ov)
    (flymake-log 3 "created an overlay at (%d-%d)" beg end)))
Eli Zaretskii's avatar
Eli Zaretskii committed
873

874
(defun flymake-delete-own-overlays (buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
875 876 877 878 879 880 881
  "Delete all flymake overlays in BUFFER."
  (save-excursion
    (set-buffer buffer)
    (let ((ov (overlays-in (point-min) (point-max))))
      (while (consp ov)
	(when (flymake-overlay-p (car ov))
	  (delete-overlay (car ov))
882
	  ;;+(flymake-log 3 "deleted overlay %s" ov)
Richard M. Stallman's avatar
Richard M. Stallman committed
883 884
	  )
	(setq ov (cdr ov))))))
Eli Zaretskii's avatar
Eli Zaretskii committed
885

886
(defun flymake-region-has-flymake-overlays (beg end)
Richard M. Stallman's avatar
Richard M. Stallman committed
887
  "Check if region specified by BEG and END has overlay.
888
Return t if it has at least one flymake overlay, nil if no overlay."
Richard M. Stallman's avatar
Richard M. Stallman committed
889 890 891 892 893 894
  (let ((ov                  (overlays-in beg end))
	(has-flymake-overlays  nil))
    (while (consp ov)
      (when (flymake-overlay-p (car ov))
	(setq has-flymake-overlays t))
      (setq ov (cdr ov)))))
Eli Zaretskii's avatar
Eli Zaretskii committed
895 896

(defface flymake-errline-face
897 898
  ;;+   '((((class color)) (:foreground "OrangeRed" :bold t :underline t))
  ;;+   '((((class color)) (:underline "OrangeRed"))
Richard M. Stallman's avatar
Richard M. Stallman committed
899 900 901 902
  '((((class color)) (:background "LightPink"))
    (t (:bold t)))
  "Face used for marking error lines."
  :group 'flymake)
Eli Zaretskii's avatar
Eli Zaretskii committed
903 904

(defface flymake-warnline-face
Richard M. Stallman's avatar
Richard M. Stallman committed
905 906 907 908
  '((((class color)) (:background "LightBlue2"))
    (t (:bold t)))
  "Face used for marking warning lines."
  :group 'flymake)
Eli Zaretskii's avatar
Eli Zaretskii committed
909

910
(defun flymake-highlight-line (line-no line-err-info-list)
Richard M. Stallman's avatar
Richard M. Stallman committed
911
  "Highlight line LINE-NO in current buffer.
912
Perhaps use text from LINE-ERR-INFO-ILST to enhance highlighting."
Richard M. Stallman's avatar
Richard M. Stallman committed
913 914 915 916 917 918 919 920 921 922 923
  (goto-line line-no)
  (let* ((line-beg (flymake-line-beginning-position))
	 (line-end (flymake-line-end-position))
	 (beg      line-beg)
	 (end      line-end)
	 (tooltip-text (flymake-ler-get-text (nth 0 line-err-info-list)))
	 (face     nil))

    (goto-char line-beg)
    (while (looking-at "[ \t]")
      (forward-char))
Eli Zaretskii's avatar
Eli Zaretskii committed
924

Richard M. Stallman's avatar
Richard M. Stallman committed
925
    (setq beg (point))
Eli Zaretskii's avatar
Eli Zaretskii committed
926

Richard M. Stallman's avatar
Richard M. Stallman committed
927 928 929
    (goto-char line-end)
    (while (and (looking-at "[ \t\r\n]") (> (point) 1))
      (backward-char))
Eli Zaretskii's avatar
Eli Zaretskii committed
930

Richard M. Stallman's avatar
Richard M. Stallman committed
931
    (setq end (1+ (point)))
Eli Zaretskii's avatar
Eli Zaretskii committed
932

Richard M. Stallman's avatar
Richard M. Stallman committed
933 934 935
    (when (<= end beg)
      (setq beg line-beg)
      (setq end line-end))
936

Richard M. Stallman's avatar
Richard M. Stallman committed
937 938 939 940
    (when (= end beg)
      (goto-char end)
      (forward-line)
      (setq end (point)))
941

Richard M. Stallman's avatar
Richard M. Stallman committed
942 943 944
    (if (> (flymake-get-line-err-count line-err-info-list "e") 0)
	(setq face 'flymake-errline-face)
      (setq face 'flymake-warnline-face))
945

Richard M. Stallman's avatar
Richard M. Stallman committed
946
    (flymake-make-overlay beg end tooltip-text face nil)))
Eli Zaretskii's avatar
Eli Zaretskii committed
947

948
(defun flymake-parse-err-lines (err-info-list source-buffer lines)
Richard M. Stallman's avatar
Richard M. Stallman committed
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
  "Parse err LINES, store info in ERR-INFO-LIST."
  (let* ((count              (length lines))
	 (idx                0)
	 (line-err-info      nil)
	 (real-file-name     nil)
	 (source-file-name   (buffer-file-name source-buffer))
	 (get-real-file-name-f (flymake-get-real-file-name-function source-file-name)))

    (while (< idx count)
      (setq line-err-info (flymake-parse-line (nth idx lines)))
      (when line-err-info
	(setq real-file-name (funcall get-real-file-name-f source-buffer (flymake-ler-get-file line-err-info)))
	(setq line-err-info (flymake-ler-set-full-file line-err-info real-file-name))

	(if (flymake-same-files real-file-name source-file-name)
	    (setq line-err-info (flymake-ler-set-file line-err-info nil))
	  (setq line-err-info (flymake-ler-set-file line-err-info (file-name-nondirectory real-file-name))))

	(setq err-info-list (flymake-add-err-info err-info-list line-err-info)))
      (flymake-log 3 "parsed '%s', %s line-err-info" (nth idx lines) (if line-err-info "got" "no"))
      (setq idx (1+ idx)))
    err-info-list))
Eli Zaretskii's avatar
Eli Zaretskii committed
971

972
(defun flymake-split-output (output)
Richard M. Stallman's avatar
Richard M. Stallman committed
973
  "Split OUTPUT into lines.
974 975
Return last one as residual if it does not end with newline char.
Returns ((LINES) RESIDUAL)."
Richard M. Stallman's avatar
Richard M. Stallman committed
976 977 978 979 980 981 982 983
  (when (and output (> (length output) 0))
    (let* ((lines (flymake-split-string output "[\n\r]+"))
	   (complete (equal "\n" (char-to-string (aref output (1- (length output))))))
	   (residual nil))
      (when (not complete)
	(setq residual (car (last lines)))
	(setq lines (butlast lines)))
      (list lines residual))))
Eli Zaretskii's avatar
Eli Zaretskii committed
984

985
(defun flymake-reformat-err-line-patterns-from-compile-el (original-list)
Richard M. Stallman's avatar
Richard M. Stallman committed
986
  "Grab error line patterns from ORIGINAL-LIST in compile.el format.
987
Convert it to flymake internal format."
Richard M. Stallman's avatar
Richard M. Stallman committed
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
  (let* ((converted-list '()))
    (mapcar
     (lambda (item)
       (setq item (cdr item))
       (let ((regexp (nth 0 item))
	     (file (nth 1 item))
	     (line (nth 2 item))
	     (col (nth 3 item))
	     end-line)
	 (if (consp file)	(setq file (car file)))
	 (if (consp line)	(setq end-line (cdr line) line (car line)))
	 (if (consp col)	(setq col (car col)))

	 (when (not (functionp line))
	   (setq converted-list (cons (list regexp file line col) converted-list)))))
     original-list)
    converted-list))
1005 1006

(eval-when-compile
Richard M. Stallman's avatar
Richard M. Stallman committed
1007 1008 1009 1010 1011
  (require 'compile))

(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
  (append
   '(
1012
     ;; MS Visual C++ 6.0
Richard M. Stallman's avatar
Richard M. Stallman committed
1013 1014
     ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) \: \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)"
      1 3 nil 4)