diff --git a/config.org b/config.org index 642d1af..f386d40 100644 --- a/config.org +++ b/config.org @@ -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