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