• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1;; Copyright (c) 2011 The Chromium Authors. All rights reserved.
2;; Use of this source code is governed by a BSD-style license that can be
3;; found in the LICENSE file.
4
5;; Set up flymake for use with chromium code.  Uses ninja (since none of the
6;; other chromium build systems have latency that allows interactive use).
7;;
8;; Requires a modern emacs (GNU Emacs >= 23) and that gyp has already generated
9;; the build.ninja file(s).  See defcustoms below for settable knobs.
10
11
12(require 'flymake)
13
14(defcustom cr-flymake-ninja-build-file "out/Debug/build.ninja"
15  "Relative path from chromium's src/ directory to the
16  build.ninja file to use.")
17
18(defcustom cr-flymake-ninja-executable "ninja"
19  "Ninja executable location; either in $PATH or explicitly given.")
20
21(defun cr-flymake-absbufferpath ()
22  "Return the absolute path to the current buffer, or nil if the
23  current buffer has no path."
24  (when buffer-file-truename
25      (expand-file-name buffer-file-truename)))
26
27(defun cr-flymake-chromium-src ()
28  "Return chromium's src/ directory, or nil on failure."
29  (let ((srcdir (locate-dominating-file
30                 (cr-flymake-absbufferpath) cr-flymake-ninja-build-file)))
31    (when srcdir (expand-file-name srcdir))))
32
33(defun cr-flymake-string-prefix-p (prefix str)
34  "Return non-nil if PREFIX is a prefix of STR (23.2 has string-prefix-p but
35  that's case insensitive and also 23.1 doesn't have it)."
36  (string= prefix (substring str 0 (length prefix))))
37
38(defun cr-flymake-current-file-name ()
39  "Return the relative path from chromium's src/ directory to the
40  file backing the current buffer or nil if it doesn't look like
41  we're under chromium/src/."
42  (when (and (cr-flymake-chromium-src)
43             (cr-flymake-string-prefix-p
44              (cr-flymake-chromium-src) (cr-flymake-absbufferpath)))
45    (substring (cr-flymake-absbufferpath) (length (cr-flymake-chromium-src)))))
46
47(defun cr-flymake-from-build-to-src-root ()
48  "Return a path fragment for getting from the build.ninja file to src/."
49  (replace-regexp-in-string
50   "[^/]+" ".."
51   (substring
52    (file-name-directory
53     (file-truename (or (and (cr-flymake-string-prefix-p
54                              "/" cr-flymake-ninja-build-file)
55                             cr-flymake-ninja-build-file)
56                        (concat (cr-flymake-chromium-src)
57                                cr-flymake-ninja-build-file))))
58    (length (cr-flymake-chromium-src)))))
59
60(defun cr-flymake-getfname (file-name-from-error-message)
61  "Strip cruft from the passed-in filename to help flymake find the real file."
62  (file-name-nondirectory file-name-from-error-message))
63
64(defun cr-flymake-ninja-command-line ()
65  "Return the command-line for running ninja, as a list of strings, or nil if
66  we're not during a save"
67  (unless (buffer-modified-p)
68    (list cr-flymake-ninja-executable
69          (list "-C"
70                (concat (cr-flymake-chromium-src)
71                        (file-name-directory cr-flymake-ninja-build-file))
72                (concat (cr-flymake-from-build-to-src-root)
73                        (cr-flymake-current-file-name) "^")))))
74
75(defun cr-flymake-kick-off-check-after-save ()
76  "Kick off a syntax check after file save, if flymake-mode is on."
77  (when flymake-mode (flymake-start-syntax-check)))
78
79(defadvice next-error (around cr-flymake-next-error activate)
80  "If flymake has something to say, let it say it; otherwise
81   revert to normal next-error behavior."
82  (if (not flymake-err-info)
83      (condition-case msg
84          ad-do-it
85        (error (message "%s" (prin1-to-string msg))))
86    (flymake-goto-next-error)
87    ;; copy/pasted from flymake-display-err-menu-for-current-line because I
88    ;; couldn't find a way to have it tell me what the relevant error for this
89    ;; line was in a single call:
90    (let* ((line-no (flymake-current-line-no))
91           (line-err-info-list
92            (nth 0 (flymake-find-err-info flymake-err-info line-no)))
93           (menu-data (flymake-make-err-menu-data line-no line-err-info-list)))
94      (prin1 (car (car (car (cdr menu-data)))) t))))
95
96(defun cr-flymake-find-file ()
97  "Enable flymake, but only if it makes sense, and immediately
98  disable timer-based execution."
99  (when (and (not flymake-mode)
100             (not buffer-read-only)
101             (cr-flymake-current-file-name))
102    ;; Since flymake-allowed-file-name-masks requires static regexps to match
103    ;; against, can't use cr-flymake-chromium-src here.  Instead we add a
104    ;; generic regexp, but only to a buffer-local version of the variable.
105    (set (make-local-variable 'flymake-allowed-file-name-masks)
106         (list (list "\\.c\\(\\|c\\|pp\\)"
107                     'cr-flymake-ninja-command-line
108                     'ignore
109                     'cr-flymake-getfname)))
110    (flymake-find-file-hook)
111    (if flymake-mode
112        (cancel-timer flymake-timer)
113      (kill-local-variable 'flymake-allowed-file-name-masks))))
114
115(add-hook 'find-file-hook 'cr-flymake-find-file 'append)
116(add-hook 'after-save-hook 'cr-flymake-kick-off-check-after-save)
117
118;; Show flymake infrastructure ERRORs in hopes of fixing them.  Set to 3 for
119;; DEBUG-level output from flymake.el.
120(setq flymake-log-level 0)
121
122(provide 'flymake-chromium)
123