1 ;; *****************************************************************************
4 ;; Font-lock and indentation support for Jam files
6 ;; Copyright (C) 2006, Google Inc (www.google.com)
7 ;; Author Kai Backman <kaib at google.com>
8 ;; 19 June 2006 - 0.5 - Extended indentation for most common structures.
10 ;; Copyright (C) 2003, 2004, Rob Walker <rob at tenfoot.org.uk>
11 ;; http://www.tenfoot.org.uk/emacs/
12 ;; 12 May 2004 - 0.3 - Fix keyword quoting, XEmacs support
13 ;; 22 Mar 2003 - 0.2 - Autoload
14 ;; 04 Mar 2003 - 0.1 - Added imenu support and basic indentation
16 ;; Copyright (C) 2000, Eric Scouten
17 ;; Started Sat, 05 Aug 2000
19 ;; *****************************************************************************
21 ;; This is free software; you can redistribute it and/or modify
22 ;; it under the terms of the GNU General Public License as published by
23 ;; the Free Software Foundation; either version 2, or (at your option)
26 ;; jam-mode.el is distributed in the hope that it will be useful,
27 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
28 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29 ;; General Public License for more details.
31 ;; You should have received a copy of the GNU General Public License
32 ;; along with GNU Emacs; see the file COPYING. If not, write to the
33 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
34 ;; Boston, MA 02111-1307, USA.
36 ;; *****************************************************************************
38 ;; To add font-lock support for Jam files, simply add the line
39 ;; (require 'jam-mode) to your .emacs file. Make sure generic-mode.el
40 ;; is visible in your load-path as well.
42 ;; *****************************************************************************
45 ;; Generic-mode is a meta-mode which can be used to define small modes
46 ;; which provide basic comment and font-lock support. Jam-mode depends on
49 ;; generic.el for GNU emacs, generic-mode.el for XEmacs
50 (if (string-match "XEmacs\\|Lucid" emacs-version
)
51 (require 'generic-mode
)
54 (defun jam-mode-quote-keywords (keywords)
55 "Returns a list of expressions that match each element in KEYWORDS.
56 For generic-mode, each element is quoted. For generic, each element is unchanged."
57 (if (featurep 'generic-mode
)
58 (mapcar 'regexp-quote keywords
)
62 (define-generic-mode 'jam-mode
64 ; Jam comments always start with '#'
67 ; Jam keywords (defined later)
70 ; Extra stuff to colorize
74 (generic-make-keywords-list
75 (list "actions" "bind" "case" "default" "else" "existing" "for" "if"
76 "ignore" "in" "include" "local" "on" "piecemeal" "quietly" "rule" "switch"
78 'font-lock-keyword-face
)
80 ; Jam built-in variables
81 (generic-make-keywords-list
83 "JAMDATE" "JAMSHELL" "JAMUNAME" "JAMVERSION" "MAC" "NT" "OS" "OS2"
84 "OSPLAT" "OSVER" "UNIX" "VMS")
85 'font-lock-constant-face
)
87 ; Jam built-in targets
88 (generic-make-keywords-list
90 "ALWAYS" "DEPENDS" "ECHO" "INCLUDES" "LEAVES" "LOCATE" "NOCARE"
91 "NOTFILE" "NOUPDATE" "SEARCH" "TEMPORARY")
92 'font-lock-builtin-face
)
94 ; Jam built-in targets (warnings)
95 (generic-make-keywords-list
98 'font-lock-warning-face
)
101 (generic-make-keywords-list
102 (jam-mode-quote-keywords
104 "Archive" "As" "Bulk" "Cc" "CcMv" "C++" "Chgrp" "Chmod" "Chown" "Clean" "CreLib"
105 "Depends" "File" "Fortran" "GenFile" "GenFile1" "HardLink"
106 "HdrRule" "Install" "InstallBin" "InstallFile" "InstallInto" "InstallLib" "InstallMan"
107 "InstallShell" "Lex" "Library" "LibraryFromObjects" "Link" "LinkLibraries"
108 "Main" "MainFromObjects" "MakeLocate" "MkDir" "MkDir1" "Object" "ObjectC++Flags"
109 "ObjectCcFlags" "ObjectHdrs" "Objects" "Ranlib" "RmTemps" "Setuid" "SubDir"
110 "SubDirC++Flags" "SubDirCcFlags" "SubDirHdrs" "SubInclude" "Shell" "Undefines"
111 "UserObject" "Yacc" "Yacc1" "BULK" "FILE" "HDRRULE" "INSTALL" "INSTALLBIN" "INSTALLLIB"
112 "INSTALLMAN" "LIBRARY" "LIBS" "LINK" "MAIN" "SETUID" "SHELL" "UNDEFINES"
113 "addDirName" "makeCommon" "makeDirName" "makeGrist" "makeGristedName" "makeRelPath"
114 "makeString" "makeSubDir" "makeSuffixed" "unmakeDir"))
115 'font-lock-function-name-face
)
117 ; Jambase built-in targets
118 (generic-make-keywords-list
120 "all" "clean" "dirs" "exe" "files" "first" "install" "lib" "obj" "shell" "uninstall")
121 'font-lock-builtin-face
)
123 ; Jambase built-in variables
124 (generic-make-keywords-list
125 (jam-mode-quote-keywords
127 "ALL_LOCATE_TARGET" "AR" "ARFLAGS" "AS" "ASFLAGS" "AWK" "BCCROOT" "BINDIR" "CC" "CCFLAGS"
128 "C++" "C++FLAGS" "CHMOD" "CP" "CRELIB" "CW" "CWGUSI" "CWMAC" "CWMSL" "DOT" "DOTDOT"
129 "EXEMODE" "FILEMODE" "FORTRAN" "FORTRANFLAGS" "GROUP" "HDRGRIST" "HDRPATTERN" "HDRRULE"
130 "HDRS" "HDRSCAN" "HDRSEARCH" "INSTALL" "JAMFILE" "JAMRULES" "LEX" "LIBDIR" "LINK"
131 "LINKFLAGS" "LINKLIBS" "LOCATE_SOURCE" "LOCATE_TARGET" "LN" "MACINC" "MANDIR" "MKDIR"
132 "MODE" "MSLIB" "MSLINK" "MSIMPLIB" "MSRC" "MSVC" "MSVCNT" "MV" "NEEDLIBS" "NOARSCAN"
133 "OSFULL" "OPTIM" "OWNER" "RANLIB" "RCP" "RELOCATE" "RM" "RSH" "RUNVMS" "SEARCH_SOURCE"
134 "SED" "SHELLHEADER" "SHELLMODE" "SLASH" "SLASHINC" "SOURCE_GRIST" "STDHDRS" "STDLIBPATH"
135 "SUBDIR" "SUBDIRASFLAGS" "SUBDIRC++FLAGS" "SUBDIRCCFLAGS" "SUBDIRHDRS" "SUBDIR_TOKENS"
136 "SUFEXE" "SUFLIB" "SUFOBJ" "UNDEFFLAG" "UNDEFS" "WATCOM" "YACC" "YACCFLAGS" "YACCFILES"))
137 'font-lock-function-name-face
)
139 ; Jam variable references $(foo)
140 '("$(\\([^ :\\[()\t\r\n]+\\)[)\\[:]" 1 font-lock-variable-name-face
))
142 ; Apply this mode to all files called Jamfile, Jamrules or Jambase
143 (list "\\(Jamfile\\|Jamrules\\|Jambase\\)\\'")
145 ; Attach setup function so we can modify syntax table.
146 (list 'jam-mode-setup-function
)
149 "Generic mode for Jam rules files")
151 (defun jam-mode-setup-function ()
152 (modify-syntax-entry ?_
"w")
153 (modify-syntax-entry ?.
"w")
154 (modify-syntax-entry ?
/ "w")
155 (modify-syntax-entry ?
+ "w")
156 (modify-syntax-entry ?
# "<")
157 (modify-syntax-entry ?
\n ">")
158 (setq imenu-generic-expression
159 '(("Rules" "^rule\\s-+\\([A-Za-z0-9_]+\\)" 1)
160 ("Actions" "^actions\\s-+\\([A-Za-z0-9_]+\\)" 1)))
161 (imenu-add-to-menubar "Jam")
162 (make-local-variable 'indent-line-function
)
163 (setq indent-line-function
'jam-indent-line
)
164 (run-hooks 'jam-mode-hook
)
167 (defvar jam-mode-hook nil
)
169 (defvar jam-indent-size
2
170 "Amount to indent by in jam-mode")
172 (defvar jam-case-align-to-colon t
173 "Whether to align case statements to the colons")
175 (defun jam-indent-line (&optional whole-exp
)
176 "Indent current line"
178 (let ((indent (jam-indent-level))
179 (pos (- (point-max) (point))) beg
)
182 (skip-chars-forward " \t")
183 (if (zerop (- indent
(current-column)))
185 (delete-region beg
(point))
187 (if (> (- (point-max) pos
) (point))
188 (goto-char (- (point-max) pos
)))
191 (defun jam-skip-chars-backward (pattern)
192 (let ((line-start (save-excursion
195 (skip-chars-backward pattern line-start
)
196 (while (and (not (bobp))
197 (= (point) line-start
))
199 (setq line-start
(point))
200 (skip-chars-forward "^\n\r#")
201 (skip-chars-backward pattern line-start
))))
204 (defun jam-goto-block-start ()
205 "Goto the start of the block containing point (or beginning of buffer if not
208 (while (and (not (bobp)) (> l
0))
209 (jam-skip-chars-backward "^{}")
213 ((eq (char-after) ?
{) (1- l
))
214 ((eq (char-after) ?
}) (1+ l
))
220 (defun jam-indent-level ()
229 ;; see what's on this line
231 (setq is-block-end
(looking-at "^[^#{\n\r]*}\\s-*\\(#.*\\)?$"))
232 (setq is-block-start
(looking-at "^[^#\n]*{\\s-*\\(#.*\\)?$"))
233 (setq is-case
(looking-at "\\s-*case.*:"))
235 ;; Find indentation for standard line
238 ;; If we are at beginning the indent is 0
241 ;; Now check if we are in the middle of an opened statement
242 (let ((line-start (point))
246 (if (jam-goto-block-start)
248 (setq block-ind
(+ (current-indentation) jam-indent-size
)))
249 ;; Check if this block is an actions block
250 (if (and (not (bobp))
251 (looking-at "^\\s-*{\\s-*\\(#.*\\)?$"))
254 (if (looking-at "^\\s-*actions")
255 (throw 'find-ind block-ind
)
257 (setq block-start
(point)))
260 ;; Block start is on previous line, then we automatically take the block indent
261 (if (= (point) block-start
)
262 (throw 'find-ind block-ind
))
263 ;; Loop backwards until we hit a line with a semicolon or block start
264 (while (and (> (point) block-start
)
265 (not (looking-at "^[^#\n\r]*[;}]\\s-*\\(#.*\\)?$")))
267 ;; Now our deciding statement is on next non-empty line
269 (while (and (< (point) line-start
)
270 (looking-at "^\\s-*\\(#.*\\)?$"))
272 ;; If we ended up on our original line, just take block indent
273 (if (= (point) line-start
)
274 (throw 'find-ind block-ind
))
275 ;; Now we need to pick the indentation based on if we are a block start or not.
277 (current-indentation)
278 (+ (current-indentation) jam-indent-size
))
281 ;; increase indent in switch statements (not cases)
282 (setq is-switch
(re-search-backward "^\\s-*switch" (- (point) 100) t
))
283 (when (and is-switch
(not (or is-block-end is-case
)))
285 (setq ind
(if (and jam-case-align-to-colon
286 (re-search-backward "^\\s-*case.*?\\(:\\)"))
287 (+ (- (match-beginning 1) (match-beginning 0))
289 (+ ind jam-indent-size
)))
292 ;; indentation of this line is jam-indent-size more than that of the
294 (cond (is-block-start ind
)
295 (is-block-end (- ind jam-indent-size
))
303 ;; jam-mode.el ends here