flymake.el 77.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

Stefan Monnier's avatar
Stefan Monnier committed
52 53 54 55 56 57
(defalias 'flymake-float-time
  (if (fboundp 'float-time)
      'float-time
    (lambda ()
      (multiple-value-bind (s0 s1 s2) (current-time)
	(+ (* (float (ash 1 16)) s0) (float s1) (* 0.0000001 s2))))))
Kim F. Storm's avatar
Kim F. Storm committed
58 59

(defsubst flymake-replace-regexp-in-string (regexp rep str)
Stefan Monnier's avatar
Stefan Monnier committed
60 61 62
  (if (fboundp 'replace-regexp-in-string)
      (replace-regexp-in-string regexp rep str)
    (replace-in-string str regexp rep)))
Kim F. Storm's avatar
Kim F. Storm committed
63 64

(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
(defsubst flymake-get-temp-dir ()
Stefan Monnier's avatar
Stefan Monnier committed
74
  (if (fboundp 'temp-directory)
Kim F. Storm's avatar
Kim F. Storm committed
75 76
      (temp-directory)
    temporary-file-directory))
Eli Zaretskii's avatar
Eli Zaretskii committed
77

Stefan Monnier's avatar
Stefan Monnier committed
78 79 80 81
(defalias 'flymake-line-beginning-position
  (if (fboundp 'line-beginning-position)
      'line-beginning-position
    (lambda (&optional arg) (save-excursion (beginning-of-line arg) (point)))))
Eli Zaretskii's avatar
Eli Zaretskii committed
82

Stefan Monnier's avatar
Stefan Monnier committed
83 84 85 86
(defalias 'flymake-line-end-position
  (if (fboundp 'line-end-position)
      'line-end-position
    (lambda (&optional arg) (save-excursion (end-of-line arg) (point)))))
Richard M. Stallman's avatar
Richard M. Stallman committed
87 88

(defun flymake-popup-menu (pos menu-data)
Stefan Monnier's avatar
Stefan Monnier committed
89
  (if (and (fboundp 'popup-menu) (fboundp 'make-event))
Kim F. Storm's avatar
Kim F. Storm committed
90 91 92 93 94 95 96
      (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

Kim F. Storm's avatar
Kim F. Storm committed
107 108
(if (featurep 'xemacs) (progn

Stefan Monnier's avatar
Stefan Monnier committed
109 110
(defun flymake-nop ())

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."
Stefan Monnier's avatar
Stefan Monnier committed
137 138 139
  (if (fboundp 'window-edges)
      (+ (car (cdr (window-edges))) (count-lines (window-start) (point)))
    (count-lines (window-start) (point))))
Richard M. Stallman's avatar
Richard M. Stallman committed
140 141

(defun flymake-selected-frame ()
Stefan Monnier's avatar
Stefan Monnier committed
142 143 144
  (if (fboundp 'window-edges)
      (selected-frame)
    (selected-window)))
Eli Zaretskii's avatar
Eli Zaretskii committed
145 146 147 148

;;;; ]]

(defcustom flymake-log-level -1
Stefan Monnier's avatar
Stefan Monnier committed
149
  "Logging level, only messages with level lower or equal will 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
  (if (eq buffer (current-buffer))
      (symbol-value var-name)
Stefan Monnier's avatar
Stefan Monnier committed
202
    (with-current-buffer buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
203
      (symbol-value var-name))))
Eli Zaretskii's avatar
Eli Zaretskii committed
204

205
(defun flymake-set-buffer-var (buffer var-name var-value)
Richard M. Stallman's avatar
Richard M. Stallman committed
206 207 208
  "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
209

Richard M. Stallman's avatar
Richard M. Stallman committed
210 211
  (if (eq buffer (current-buffer))
      (set var-name var-value)
Stefan Monnier's avatar
Stefan Monnier committed
212
    (with-current-buffer buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
213
      (set var-name var-value))))
214 215 216

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

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

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

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

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

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

232
(defvar flymake-output-residual nil)
233

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

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

(defun flymake-set-buffer-output-residual (buffer residual)
Richard M. Stallman's avatar
Richard M. Stallman committed
240 241 242 243 244 245 246 247 248 249 250 251 252 253
  (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)
254 255 256 257 258 259 260 261 262
    ;; (".+\\.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
263 264 265 266
    )
  "*Files syntax checking is allowed for."
  :group 'flymake
  :type '(repeat (string symbol symbol symbol)))
Eli Zaretskii's avatar
Eli Zaretskii committed
267

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

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

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

293
(defun flymake-get-cleanup-function (file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
294 295
  "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
296

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

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

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

307
(defun flymake-get-buildfile-from-cache (dir-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
308
  (gethash dir-name flymake-find-buildfile-cache))
309 310

(defun flymake-add-buildfile-to-cache (dir-name buildfile)
Richard M. Stallman's avatar
Richard M. Stallman committed
311
  (puthash dir-name buildfile flymake-find-buildfile-cache))
312 313

(defun flymake-clear-buildfile-cache ()
Richard M. Stallman's avatar
Richard M. Stallman committed
314
  (clrhash flymake-find-buildfile-cache))
315 316

(defun flymake-find-buildfile (buildfile-name source-dir-name dirs)
Richard M. Stallman's avatar
Richard M. Stallman committed
317
  "Find buildfile starting from current directory.
318
Buildfile includes Makefile, build.xml etc.
Richard M. Stallman's avatar
Richard M. Stallman committed
319 320 321 322 323 324 325
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)
	   (found                  nil))
326 327
      (while (and (not found) dirs)
	(setq buildfile-dir (concat source-dir-name (car dirs)))
Richard M. Stallman's avatar
Richard M. Stallman committed
328 329 330
	(setq buildfile (concat buildfile-dir "/" buildfile-name))
	(when (file-exists-p buildfile)
	  (setq found t))
331
	(setq dirs (cdr dirs)))
Richard M. Stallman's avatar
Richard M. Stallman committed
332 333 334 335 336
      (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
337
	(progn
Richard M. Stallman's avatar
Richard M. Stallman committed
338 339 340 341 342 343 344 345 346 347 348 349
	  (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
350

351
(defun flymake-same-files (file-name-one file-name-two)
Richard M. Stallman's avatar
Richard M. Stallman committed
352
  "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file.
353
Return t if so, nil if not."
Richard M. Stallman's avatar
Richard M. Stallman committed
354 355 356 357 358
  (equal (flymake-fix-file-name file-name-one)
	 (flymake-fix-file-name file-name-two)))

(defun flymake-get-common-file-prefix (string-one string-two)
  "Return common prefix for two file names STRING-ONE and STRING-TWO."
359 360 361 362 363 364
  (setq string-one (file-name-as-directory string-one))
  (setq string-two (file-name-as-directory string-two))
  (let ((n (compare-strings string-one nil nil string-two nil nil)))
    (if (eq n t) string-one
      (setq n (abs (1+ n)))
      (file-name-directory (substring string-one 0 n)))))
Richard M. Stallman's avatar
Richard M. Stallman committed
365 366 367

(defun flymake-build-relative-filename (from-dir to-dir)
  "Return rel: FROM-DIR/rel == TO-DIR."
368
  ;; FIXME: Why not use `file-relative-name'?
Richard M. Stallman's avatar
Richard M. Stallman committed
369 370 371
  (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)
372 373
    (let* ((from (file-name-as-directory (flymake-fix-file-name from-dir)))
	   (to   (file-name-as-directory (flymake-fix-file-name to-dir)))
Richard M. Stallman's avatar
Richard M. Stallman committed
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
	   (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
393 394

(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest")
395
  "Dirs where to look for master files."
Richard M. Stallman's avatar
Richard M. Stallman committed
396 397
  :group 'flymake
  :type '(repeat (string)))
Eli Zaretskii's avatar
Eli Zaretskii committed
398 399

(defcustom flymake-master-file-count-limit 32
Richard M. Stallman's avatar
Richard M. Stallman committed
400 401 402
  "Max number of master files to check."
  :group 'flymake
  :type 'integer)
403

Kim F. Storm's avatar
Kim F. Storm committed
404 405
;; 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
406

407
(defun flymake-find-possible-master-files (file-name master-file-dirs masks)
408 409
  "Find (by name and location) all possible master files.
Master files are .cpp and .c for and .h. Files are searched for
410
starting from the .h directory and max max-level parent dirs.
Eli Zaretskii's avatar
Eli Zaretskii committed
411
File contents are not checked."
412
  (let* ((dirs master-file-dirs)
Richard M. Stallman's avatar
Richard M. Stallman committed
413
	 (files  nil)
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
	 (done   nil))

    (while (and (not done) dirs)
      (let* ((dir (concat (flymake-fix-file-name (file-name-directory file-name))
			  "/" (car dirs)))
	     (masks masks))
	(while (and (file-exists-p dir) (not done) masks)
	  (let* ((mask        (car masks))
		 (dir-files   (directory-files dir t mask)))

	    (flymake-log 3 "dir %s, %d file(s) for mask %s"
			 dir (length dir-files) mask)
	    (while (and (not done) dir-files)
	      (when (not (file-directory-p (car dir-files)))
		(setq files (cons (car dir-files) files))
Richard M. Stallman's avatar
Richard M. Stallman committed
429 430 431
		(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)))
432 433 434
	      (setq dir-files (cdr dir-files))))
	  (setq masks (cdr masks))))
      (setq dirs (cdr dirs)))
Richard M. Stallman's avatar
Richard M. Stallman committed
435
    (when files
Kim F. Storm's avatar
Kim F. Storm committed
436 437
      (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
438 439
    (flymake-log 3 "found %d possible master file(s)" (length files))
    files))
Eli Zaretskii's avatar
Eli Zaretskii committed
440

441
(defun flymake-master-file-compare (file-one file-two)
Richard M. Stallman's avatar
Richard M. Stallman committed
442
  "Compare two files speccified by FILE-ONE and FILE-TWO.
443 444
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
445 446 447
  (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
448 449

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

454
(defun flymake-check-patch-master-file-buffer (master-file-temp-buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
455 456 457 458
					       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.
459 460 461 462 463
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
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
  (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
492
		    ;; XEmacs with 'last match not a buffer' error as
Richard M. Stallman's avatar
Richard M. Stallman committed
493
		    ;; check-includes calls replace-in-string
494
		    (flymake-replace-region match-beg match-end
Richard M. Stallman's avatar
Richard M. Stallman committed
495 496 497 498
					    (file-name-nondirectory patched-source-file-name))))
		(forward-line 1)))
	    (when found
	      (flymake-save-buffer-in-file (current-buffer) patched-master-file-name)))
499
	;;+(flymake-log 3 "killing buffer %s" (buffer-name master-file-temp-buffer))
Richard M. Stallman's avatar
Richard M. Stallman committed
500
	(kill-buffer master-file-temp-buffer)))
501
    ;;+(flymake-log 3 "check-patch master file %s: %s" master-file-name found)
Richard M. Stallman's avatar
Richard M. Stallman committed
502 503 504
    (when found
      (flymake-log 2 "found master file %s" master-file-name))
    found))
Eli Zaretskii's avatar
Eli Zaretskii committed
505

506
(defun flymake-replace-region (beg end rep)
Richard M. Stallman's avatar
Richard M. Stallman committed
507 508
  "Replace text in BUFFER in region (BEG END) with REP."
  (save-excursion
509 510 511 512
    (goto-char end)
    ;; Insert before deleting, so as to better preserve markers's positions.
    (insert rep)
    (delete-region beg end)))
Eli Zaretskii's avatar
Eli Zaretskii committed
513

514
(defun flymake-read-file-to-temp-buffer (file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
515 516
  "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))))))
Stefan Monnier's avatar
Stefan Monnier committed
517
    (with-current-buffer temp-buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
518 519
      (insert-file-contents file-name))
    temp-buffer))
Eli Zaretskii's avatar
Eli Zaretskii committed
520

521
(defun flymake-copy-buffer-to-temp-buffer (buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
522 523 524
  "Copy contents of BUFFER into newly created temp buffer."
  (let ((contents     nil)
	(temp-buffer  nil))
Stefan Monnier's avatar
Stefan Monnier committed
525
    (with-current-buffer buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
526 527 528 529 530 531
      (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
532

533
(defun flymake-check-include (source-file-name inc-path inc-name include-dirs)
Richard M. Stallman's avatar
Richard M. Stallman committed
534
  "Check if SOURCE-FILE-NAME can be found in include path.
535
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
536 537
  (if (file-name-absolute-p inc-path)
      (flymake-same-files source-file-name (concat inc-path "/" inc-name))
538
    (let* ((file-name  nil)
Richard M. Stallman's avatar
Richard M. Stallman committed
539
	   (found      nil))
540 541 542
      (while (and (not found) include-dirs)
	(setq file-name (concat (file-name-directory source-file-name)
				"/" (car include-dirs)))
Richard M. Stallman's avatar
Richard M. Stallman committed
543 544 545 546 547
	(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))
548
	(setq include-dirs (cdr include-dirs)))
Richard M. Stallman's avatar
Richard M. Stallman committed
549
      found)))
Eli Zaretskii's avatar
Eli Zaretskii committed
550

551
(defun flymake-find-buffer-for-file (file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
552
  "Check if there exists a buffer visiting FILE-NAME.
553
Return t if so, nil if not."
Richard M. Stallman's avatar
Richard M. Stallman committed
554 555 556
  (let ((buffer-name (get-file-buffer file-name)))
    (if buffer-name
	(get-buffer buffer-name))))
Eli Zaretskii's avatar
Eli Zaretskii committed
557

558
(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
559
  "Save SOURCE-FILE-NAME with a different name.
560
Find master file, patch and save it."
Richard M. Stallman's avatar
Richard M. Stallman committed
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
  (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
591

592
(defun flymake-save-buffer-in-file (buffer file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
593 594
  (or buffer
      (error "Invalid buffer"))
Stefan Monnier's avatar
Stefan Monnier committed
595
  (with-current-buffer buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
596 597 598 599 600
    (save-restriction
      (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
601

602
(defun flymake-save-string-to-file (file-name data)
Richard M. Stallman's avatar
Richard M. Stallman committed
603 604
  "Save string DATA to file FILE-NAME."
  (write-region data nil file-name nil 566))
Eli Zaretskii's avatar
Eli Zaretskii committed
605

606
(defun flymake-read-file-to-string (file-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
607 608 609 610
  "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
611

612
(defun flymake-process-filter (process output)
Richard M. Stallman's avatar
Richard M. Stallman committed
613
  "Parse OUTPUT and highlight error lines.
614
It's flymake process filter."
Richard M. Stallman's avatar
Richard M. Stallman committed
615 616
  (let* ((pid               (process-id process))
	 (source-buffer     (get-buffer (flymake-get-source-buffer-name pid))))
Eli Zaretskii's avatar
Eli Zaretskii committed
617

Richard M. Stallman's avatar
Richard M. Stallman committed
618 619 620
    (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
621

622
(defun flymake-process-sentinel (process event)
Richard M. Stallman's avatar
Richard M. Stallman committed
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
  "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
Stefan Monnier's avatar
Stefan Monnier committed
641
		(with-current-buffer source-buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
642 643 644 645 646 647 648 649 650

		  (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
651

652
(defun flymake-post-syntax-check (source-buffer exit-status command)
Richard M. Stallman's avatar
Richard M. Stallman committed
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
  (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
678

679
(defun flymake-parse-output-and-residual (source-buffer output)
Richard M. Stallman's avatar
Richard M. Stallman committed
680
  "Split OUTPUT into lines, merge in residual if necessary."
Stefan Monnier's avatar
Stefan Monnier committed
681
  (with-current-buffer source-buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
682 683 684 685 686 687 688 689 690 691
    (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
692

693
(defun flymake-parse-residual (source-buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
694
  "Parse residual if it's non empty."
Stefan Monnier's avatar
Stefan Monnier committed
695
  (with-current-buffer source-buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
696 697 698 699 700 701
    (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
702 703

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

Eli Zaretskii's avatar
Eli Zaretskii committed
706
(make-variable-buffer-local 'flymake-err-info)
707 708

(defun flymake-get-buffer-err-info (buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
709
  (flymake-get-buffer-var buffer 'flymake-err-info))
710 711

(defun flymake-set-buffer-err-info (buffer err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
712
  (flymake-set-buffer-var buffer 'flymake-err-info err-info))
713 714

(defun flymake-er-make-er (line-no line-err-info-list)
Richard M. Stallman's avatar
Richard M. Stallman committed
715
  (list line-no line-err-info-list))
716 717

(defun flymake-er-get-line (err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
718
  (nth 0 err-info))
719 720

(defun flymake-er-get-line-err-info-list (err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
721
  (nth 1 err-info))
Eli Zaretskii's avatar
Eli Zaretskii committed
722 723

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

Eli Zaretskii's avatar
Eli Zaretskii committed
726
(make-variable-buffer-local 'flymake-new-err-info)
727 728

(defun flymake-get-buffer-new-err-info (buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
729
  (flymake-get-buffer-var buffer 'flymake-new-err-info))
730 731

(defun flymake-set-buffer-new-err-info (buffer new-err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
732
  (flymake-set-buffer-var buffer 'flymake-new-err-info new-err-info))
Eli Zaretskii's avatar
Eli Zaretskii committed
733 734

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

(defun flymake-ler-get-file (line-err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
739
  (nth 0 line-err-info))
740 741

(defun flymake-ler-get-line (line-err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
742
  (nth 1 line-err-info))
743 744

(defun flymake-ler-get-type (line-err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
745
  (nth 2 line-err-info))
746 747

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

(defun flymake-ler-get-full-file (line-err-info)
Richard M. Stallman's avatar
Richard M. Stallman committed
751
  (nth 4 line-err-info))
752 753

(defun flymake-ler-set-file (line-err-info file)
Richard M. Stallman's avatar
Richard M. Stallman committed
754
  (flymake-ler-make-ler file
Eli Zaretskii's avatar
Eli Zaretskii committed
755 756 757
			(flymake-ler-get-line line-err-info)
			(flymake-ler-get-type line-err-info)
			(flymake-ler-get-text line-err-info)
758 759 760
			(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
761
  (flymake-ler-make-ler (flymake-ler-get-file line-err-info)
Eli Zaretskii's avatar
Eli Zaretskii committed
762 763 764
			(flymake-ler-get-line line-err-info)
			(flymake-ler-get-type line-err-info)
			(flymake-ler-get-text line-err-info)
765 766 767
			full-file))

(defun flymake-ler-set-line (line-err-info line)
Richard M. Stallman's avatar
Richard M. Stallman committed
768
  (flymake-ler-make-ler (flymake-ler-get-file line-err-info)
Eli Zaretskii's avatar
Eli Zaretskii committed
769 770 771
			line
			(flymake-ler-get-type line-err-info)
			(flymake-ler-get-text line-err-info)
772
			(flymake-ler-get-full-file line-err-info)))
Eli Zaretskii's avatar
Eli Zaretskii committed
773

774
(defun flymake-get-line-err-count (line-err-info-list type)
Richard M. Stallman's avatar
Richard M. Stallman committed
775
  "Return number of errors of specified TYPE.
776
Value of TYPE is eigher e or w."
Richard M. Stallman's avatar
Richard M. Stallman committed
777 778 779
  (let* ((idx        0)
	 (count      (length line-err-info-list))
	 (err-count  0))
Eli Zaretskii's avatar
Eli Zaretskii committed
780

Richard M. Stallman's avatar
Richard M. Stallman committed
781 782 783 784 785
    (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
786

787
(defun flymake-get-err-count (err-info-list type)
Richard M. Stallman's avatar
Richard M. Stallman committed
788 789 790 791 792 793 794 795
  "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))
796 797

(defun flymake-fix-line-numbers (err-info-list min-line max-line)
Richard M. Stallman's avatar
Richard M. Stallman committed
798
  "Replace line numbers with fixed value.
799 800
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
801
The reason for this fix is because some compilers might report
802
line number outside the file being compiled."
Richard M. Stallman's avatar
Richard M. Stallman committed
803 804 805 806 807 808 809 810 811 812 813 814 815
  (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
816

817
(defun flymake-highlight-err-lines (buffer err-info-list)
Richard M. Stallman's avatar
Richard M. Stallman committed
818
  "Highlight error lines in BUFFER using info from ERR-INFO-LIST."
Stefan Monnier's avatar
Stefan Monnier committed
819
  (with-current-buffer buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
820 821 822 823 824
    (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
825

826
(defun flymake-overlay-p (ov)
Richard M. Stallman's avatar
Richard M. Stallman committed
827 828
  "Determine whether overlay OV was created by flymake."
  (and (overlayp ov) (overlay-get ov 'flymake-overlay)))
Eli Zaretskii's avatar
Eli Zaretskii committed
829

830
(defun flymake-make-overlay (beg end tooltip-text face mouse-face)
Richard M. Stallman's avatar
Richard M. Stallman committed
831 832 833 834 835 836 837 838
  "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)
839
      ;;+(flymake-log 3 "created overlay %s" ov)
Richard M. Stallman's avatar
Richard M. Stallman committed
840 841
      ov)
    (flymake-log 3 "created an overlay at (%d-%d)" beg end)))
Eli Zaretskii's avatar
Eli Zaretskii committed
842

843
(defun flymake-delete-own-overlays (buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
844
  "Delete all flymake overlays in BUFFER."
Stefan Monnier's avatar
Stefan Monnier committed
845
  (with-current-buffer buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
846 847 848 849
    (let ((ov (overlays-in (point-min) (point-max))))
      (while (consp ov)
	(when (flymake-overlay-p (car ov))
	  (delete-overlay (car ov))
850
	  ;;+(flymake-log 3 "deleted overlay %s" ov)
Richard M. Stallman's avatar
Richard M. Stallman committed
851 852
	  )
	(setq ov (cdr ov))))))
Eli Zaretskii's avatar
Eli Zaretskii committed
853

854
(defun flymake-region-has-flymake-overlays (beg end)
Richard M. Stallman's avatar
Richard M. Stallman committed
855
  "Check if region specified by BEG and END has overlay.
856
Return t if it has at least one flymake overlay, nil if no overlay."
Richard M. Stallman's avatar
Richard M. Stallman committed
857 858 859 860 861
  (let ((ov                  (overlays-in beg end))
	(has-flymake-overlays  nil))
    (while (consp ov)
      (when (flymake-overlay-p (car ov))
	(setq has-flymake-overlays t))
Stefan Monnier's avatar
Stefan Monnier committed
862 863
      (setq ov (cdr ov)))
    has-flymake-overlays))
Eli Zaretskii's avatar
Eli Zaretskii committed
864 865

(defface flymake-errline-face
866 867
  ;;+   '((((class color)) (:foreground "OrangeRed" :bold t :underline t))
  ;;+   '((((class color)) (:underline "OrangeRed"))
Richard M. Stallman's avatar
Richard M. Stallman committed
868 869 870 871
  '((((class color)) (:background "LightPink"))
    (t (:bold t)))
  "Face used for marking error lines."
  :group 'flymake)
Eli Zaretskii's avatar
Eli Zaretskii committed
872 873

(defface flymake-warnline-face
Richard M. Stallman's avatar
Richard M. Stallman committed
874 875 876 877
  '((((class color)) (:background "LightBlue2"))
    (t (:bold t)))
  "Face used for marking warning lines."
  :group 'flymake)
Eli Zaretskii's avatar
Eli Zaretskii committed
878

879
(defun flymake-highlight-line (line-no line-err-info-list)
Richard M. Stallman's avatar
Richard M. Stallman committed
880
  "Highlight line LINE-NO in current buffer.
881
Perhaps use text from LINE-ERR-INFO-ILST to enhance highlighting."
Richard M. Stallman's avatar
Richard M. Stallman committed
882 883 884 885 886 887 888 889 890 891 892
  (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
893

Richard M. Stallman's avatar
Richard M. Stallman committed
894
    (setq beg (point))
Eli Zaretskii's avatar
Eli Zaretskii committed
895

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

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

Richard M. Stallman's avatar
Richard M. Stallman committed
902 903 904
    (when (<= end beg)
      (setq beg line-beg)
      (setq end line-end))
905

Richard M. Stallman's avatar
Richard M. Stallman committed
906 907 908 909
    (when (= end beg)
      (goto-char end)
      (forward-line)
      (setq end (point)))
910

Richard M. Stallman's avatar
Richard M. Stallman committed
911 912 913
    (if (> (flymake-get-line-err-count line-err-info-list "e") 0)
	(setq face 'flymake-errline-face)
      (setq face 'flymake-warnline-face))
914

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