Add custom GPRBuild mode

This commit is contained in:
Camden Dixie O'Brien 2021-01-01 00:00:37 +00:00
parent 21754206dd
commit 645afc563f

View File

@ -1009,6 +1009,127 @@
(ada-indent-region . ada-indent)))
#+end_src
*** GPRBuild files
GPRBuild files are pretty straightforward, but there doesn't seem
to be a major mode on ELPA for them so going to make a basic one.
**** Custom major mode
Start out by defining a list of keywords:
#+begin_src emacs-lisp
(defvar gpr-keywords
'("case" "end" "external" "for" "is" "null"
"package" "project" "use" "when" "with"))
#+end_src
And a list of builtins:
#+begin_src emacs-lisp
(defvar gpr-builtins
'("Compiler" "Default_Switches" "Exec_Dir" "Library_Dir"
"Library_Kind" "Library_Name" "Local_Configuration_Pragmas"
"Main" "Object_Dir" "Source_Dirs"))
#+end_src
Define some font lock regexes:
#+begin_src emacs-lisp
(defvar gpr-font-lock-defaults
(let ((string-regex (rx (sequence "\"" (*? (not "\"")) "\"")))
(constant-regex (rx (or (+ digit) "True" "False"))))
`((,string-regex . font-lock-string-face)
(,constant-regex . font-lock-constant-face)
(,(regexp-opt gpr-builtins 'words) . font-lock-builtin-face)
(,(regexp-opt gpr-keywords 'words) . font-lock-keyword-face))))
#+end_src
Create a variable for the indent width:
#+begin_src emacs-lisp
(defvar gpr-indent-width 4)
#+end_src
We then need to define a function for indentation, which is
non-trivial. A simple set of rules that gets us most of the way is:
1. Start at indentation level 0
2. Decrease indentation level if the line starts with "end"
3. Indent to same level as a previous "end" line
4. Increase indentation level if the previous line ends with "is"
5. Otherwise indent to level 0
#+begin_src emacs-lisp
(defconst gpr-block-start-regex
(rx (sequence line-start
(zero-or-more not-newline)
"is"
(zero-or-more blank)
line-end)))
(defconst gpr-block-end-regex
(rx (sequence line-start
(zero-or-more blank)
"end")))
(defun gpr-indent-line ()
"Indent the current line as GPRBuild code"
(interactive)
(beginning-of-line)
(indent-line-to (gpr-get-indent-level)))
(defun gpr-get-indent-level ()
(cond ((bobp) 0)
((looking-at-p gpr-block-end-regex)
(save-excursion
(forward-line -1)
(max (- (current-indentation) gpr-indent-width) 0)))
(t (gpr-get-indent-level-from-previous))))
(defun gpr-get-indent-level-from-previous ()
(save-excursion
(let (indent)
(while (not indent)
(forward-line -1)
(setq indent
(cond ((looking-at-p gpr-block-start-regex)
(+ (current-indentation) gpr-indent-width))
((looking-at-p gpr-block-end-regex)
(current-indentation))
((bobp) 0))))
indent)))
#+end_src
Define the mode, inheriting from =prog-mode=:
#+begin_src emacs-lisp
(define-derived-mode gpr-mode prog-mode "GPRBuild"
"GPR Mode is a major mode for editing GPRBuild files"
(set (make-local-variable 'comment-start) "--")
(set (make-local-variable 'comment-end) "")
(set (make-local-variable 'font-lock-defaults)
'(gpr-font-lock-defaults))
(set (make-local-variable 'indent-line-function)
'gpr-indent-line))
#+end_src
Finally, add an [[help:auto-load-alist][auto-load-alist]] entry for =.gpr= files:
#+begin_src emacs-lisp
(add-to-list 'auto-mode-alist '("\\.gpr\\'" . gpr-mode))
#+end_src
**** Extra tweaks
Want to use tabs for indentation so adding a hook for that:
#+begin_src emacs-lisp
(add-hook 'gpr-mode-hook
(lambda ()
(setq tab-width gpr-indent-width)
(setq indent-tabs-mode t)))
#+end_src
* Tool Integrations
** Git
=magit= is truly a wonderful creation! Add keybinding for