18
|
1
|
#+title: outlines |
|
2
|
* Overview |
|
3
|
Source code files are hard to manage. They can get unwieldly quickly and making the |
|
4
|
wrong assumption about your whereabouts in the code tree can have unintended |
|
5
|
consequences. |
|
6
|
|
|
7
|
There are many ways to solve this problem to different degrees. We'll be talking about |
|
8
|
one strategy in particular which I use and recommend for any software project. |
|
9
|
|
|
10
|
Looking through the source code in the NAS-T repository you'll find some common |
|
11
|
commenting patterns: |
|
12
|
|
|
13
|
- every file start with at least one comment line for example: |
|
14
|
#+begin_src lisp |
|
15
|
;;; file-name.lisp --- file description |
|
16
|
#+end_src |
|
17
|
|
|
18
|
- Before you see any code in a file, you'll likely encounter this line: |
|
19
|
#+begin_src lisp |
|
20
|
;;; Code: |
|
21
|
#+end_src |
|
22
|
|
|
23
|
- etc |
|
24
|
|
|
25
|
What's the deal here? To be clear, I'm of the mind that comments should be |
|
26
|
significant. They should express to the reader something that is of a non-trivial nature |
|
27
|
and 'where the code starts' doesn't quite qualify. Indeed, these comments don't fit that |
|
28
|
model at all. |
|
29
|
|
|
30
|
The deal is that these comments aren't for the reader, they're for the developer. More |
|
31
|
specifically, for the developer to treat as a special meta-language to describe the |
|
32
|
structure of a source code file. |
|
33
|
|
|
34
|
* Outlines |
|
35
|
Like all my good ideas, this one is credited entirely to Emacs. In this case, the |
|
36
|
excellent [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Outline-Mode.html][Outline mode]]. If you are an Emacs user you've probably already used it without |
|
37
|
knowing -- Org mode, for example, is [[https://git.savannah.gnu.org/cgit/emacs/org-mode.git/tree/lisp/org.el?h=release_9.6.9#n4789][derived from outline-mode]]. |
|
38
|
|
|
39
|
I've grown quite fond of it. Here's the summary: |
|
40
|
|
|
41
|
#+begin_quote |
|
42
|
Outline mode is a major mode derived from Text mode, which is specialized for editing |
|
43
|
outlines. It provides commands to navigate between entries in the outline structure, and |
|
44
|
commands to make parts of a buffer temporarily invisible, so that the outline structure |
|
45
|
may be more easily viewed. |
|
46
|
#+end_quote |
|
47
|
-- [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Outline-Mode.html][GNU]] |
|
48
|
|
|
49
|
** Quickstart |
|
50
|
If you want to jump in right away, I recommend using these keybinds in Emacs: |
|
51
|
|
|
52
|
#+tblname: outline-keys |
|
53
|
| <backtab> | outline-cycle-buffer | |
|
54
|
| M-TAB | outline-cycle | |
|
55
|
| M-n | outline-next-visible-heading | |
|
56
|
| M-p | outline-previous-visible-heading | |
|
57
|
|
|
58
|
Here's a snippet which will enable the keybinds I use: |
|
59
|
|
|
60
|
#+name: enable-outline-keys |
|
61
|
#+begin_src emacs-lisp |
|
62
|
(let ((keys |
|
63
|
'(("<backtab>" #'outline-cycle-buffer) |
|
64
|
("M-TAB" #'outline-cycle) |
|
65
|
("M-n" #'outline-next-visible-heading) |
|
66
|
("M-p" #'outline-previous-visible-heading)))) |
|
67
|
(cl-loop for (k fn) in keys |
|
68
|
do (keymap-set outline-minor-mode-map k fn))) |
|
69
|
#+end_src |
|
70
|
|
|
71
|
Now open a file in the [[../../src/][src]] directory, like [[../../src/fs/btrfs/btrfs.lisp][this]] one, enable =outline-minor-mode= and |
|
72
|
move around the file with the new keybinds above. |
|
73
|
|
|
74
|
** Outlines4All |
|
75
|
Not all programming modes have outline support built-in. The good news is that it's easy |
|
76
|
to enable it. |
|
77
|
|
|
78
|
You only need to modify one variable: =outline-regexp= and enable a minor-mode: |
|
79
|
=outline-minor-mode=. |
|
80
|
|
|
81
|
*** Using dir-locals |
|
82
|
The way it's done in the NAS-T codebase is with a [[../../.dir-locals.el][.dir-locals.el]] file. |
|
83
|
|
|
84
|
You just need to add this form for the mode of your choice, replacing the string |
|
85
|
with a regular expression which matches on a /heading/. In this case we treat lines |
|
86
|
starting with three comment chars or more as a new heading. |
|
87
|
#+begin_src lisp-data |
|
88
|
(makefile-mode . ((outline-regexp . "###+"))) |
|
89
|
#+end_src |
|
90
|
|
|
91
|
=outline-regexp= is declared as a safe local var, so no prompts will appear asking if |
|
92
|
you trust these values. You will need to configure your keybinds and enable the |
|
93
|
minor-mode separately though. For project-level support, that's all there is to it. |
|
94
|
|
|
95
|
*** Using init.el |
|
96
|
You may also modify your config to enable =outline-minor-mode= for select major-modes at |
|
97
|
startup. Here's a quick example from my config: |
|
98
|
|
|
99
|
#+begin_src emacs-lisp |
|
100
|
;;; Code: |
|
101
|
(require 'default 'rw/fu) |
|
102
|
|
|
103
|
(defun outline-hook (rx) |
|
104
|
"Enable `outline-minor-mode' and set `outline-regexp'." |
|
105
|
(setq-local outline-regexp rx) |
|
106
|
(outline-minor-mode t)) |
|
107
|
|
|
108
|
(defun add-outline-hook (mode rx) |
|
109
|
(let ((sym (symb mode "-hook"))) |
|
110
|
(add-hook sym (lambda () (outline-hook rx))))) |
|
111
|
|
|
112
|
(defmacro outline-hooks (&rest pairs) |
|
113
|
`(mapc (lambda (x) (add-outline-hook (car x) (cadr x))) ',pairs)) |
|
114
|
|
|
115
|
(outline-hooks (asm-mode ";;;+") |
|
116
|
(nasm-mode ";;;+") |
|
117
|
(rust-mode "\\(//!\\|////+\\)") |
|
118
|
(sh-mode "###+") |
|
119
|
(sh-script-mode "###+") |
|
120
|
(makefile-mode "###+")) |
|
121
|
|
|
122
|
(provide 'outline-cfg) |
|
123
|
;;; outline-cfg.el ends here |
|
124
|
#+end_src |
|
125
|
** Default Sections |
|
126
|
Our default sections should look familiar - they're just Emacs Lisp defaults, with a few |
|
127
|
choice extensions. |
|
128
|
*** Source Header |
|
129
|
First line of every source code file. |
|
130
|
|
|
131
|
Here is the prototype in lisp: |
|
132
|
#+begin_src lisp |
|
133
|
;;; filename --- description -*- vars -*- |
|
134
|
#+end_src |
|
135
|
|
|
136
|
In Rust we use: |
|
137
|
#+begin_src rust |
|
138
|
//! filename --- description -*- vars -*- |
|
139
|
#+end_src |
|
140
|
|
|
141
|
etc. |
|
142
|
**** Metadata :optional: |
|
143
|
Some files may insert a blank line and start the =Code= heading, while others will |
|
144
|
include some additional information about the file such as a long-description, version, |
|
145
|
list of exports, etc. |
|
146
|
*** Commentary :optional: |
|
147
|
An optional programmer commentary included in source code files after the =Source |
|
148
|
Header= but before the =Code=. The contents are unpredictable but may include notes, |
|
149
|
todos, diagrams, stack notations, test results, links, tips, etc. |
|
150
|
*** Code |
|
151
|
The =Code= heading should be the final toplevel heading of any source code file. You |
|
152
|
may see a number of sub-headings, starting with four or more comment chars. |