changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > org > blog / draft/outlines.org

changeset 28: 6d54ccb29de4
parent: 1204cefcfd28
author: Richard Westhaver <ellis@rwest.io>
date: Sun, 18 Aug 2024 22:16:12 -0400
permissions: -rw-r--r--
description: weekend warrior
1 #+title: outlines
2 #+setupfile: ../../clean.theme
3 #+property: header-args :eval no-export
4 * Overview
5 :PROPERTIES:
6 :ID: f49fa8a0-39a6-4a5f-8fb6-8d4086ea5476
7 :END:
8 Source code files are hard to manage. They can get unwieldly quickly and making the
9 wrong assumption about your whereabouts in the code tree can have unintended
10 consequences.
11 
12 There are many ways to solve this problem to different degrees. We'll be talking about
13 one strategy in particular which I use and recommend for any software project.
14 
15 Looking through the source code in the NAS-T repository you'll find some common
16 commenting patterns:
17 
18 - every file start with at least one comment line for example:
19 #+begin_src lisp
20 ;;; file-name.lisp --- file description
21 #+end_src
22 
23 - Before you see any code in a file, you'll likely encounter this line:
24 #+begin_src lisp
25 ;;; Code:
26 #+end_src
27 
28 - etc
29 
30 What's the deal here? To be clear, I'm of the mind that comments should be
31 significant. They should express to the reader something that is of a non-trivial nature
32 and 'where the code starts' doesn't quite qualify. Indeed, these comments don't fit that
33 model at all.
34 
35 The deal is that these comments aren't for the reader, they're for the developer. More
36 specifically, for the developer to treat as a special meta-language to describe the
37 structure of a source code file.
38 
39 * Outlines
40 :PROPERTIES:
41 :ID: 651aa74f-e634-4eac-8292-95efbe2aab9c
42 :END:
43 Like all my good ideas, this one is credited entirely to Emacs. In this case, the
44 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
45 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]].
46 
47 I've grown quite fond of it. Here's the summary:
48 
49 #+begin_quote
50 Outline mode is a major mode derived from Text mode, which is specialized for editing
51 outlines. It provides commands to navigate between entries in the outline structure, and
52 commands to make parts of a buffer temporarily invisible, so that the outline structure
53 may be more easily viewed.
54 #+end_quote
55 -- [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Outline-Mode.html][GNU]]
56 
57 ** Quickstart
58 :PROPERTIES:
59 :ID: 8e1927c5-5e2b-470a-9249-5fe0e375451c
60 :END:
61 If you want to jump in right away, I recommend using these keybinds in Emacs:
62 
63 #+tblname: outline-keys
64 | <backtab> | outline-cycle-buffer |
65 | M-TAB | outline-cycle |
66 | M-n | outline-next-visible-heading |
67 | M-p | outline-previous-visible-heading |
68 
69 Here's a snippet which will enable the keybinds I use:
70 
71 #+name: enable-outline-keys
72 #+begin_src emacs-lisp
73 (let ((keys
74  '(("<backtab>" #'outline-cycle-buffer)
75  ("M-TAB" #'outline-cycle)
76  ("M-n" #'outline-next-visible-heading)
77  ("M-p" #'outline-previous-visible-heading))))
78  (cl-loop for (k fn) in keys
79  do (keymap-set outline-minor-mode-map k fn)))
80 #+end_src
81 
82 Now open a file in the [[../../src/][src]] directory, like [[../../src/fs/btrfs/btrfs.lisp][this]] one, enable =outline-minor-mode= and
83 move around the file with the new keybinds above.
84 
85 ** Outlines4All
86 :PROPERTIES:
87 :ID: 0751feb7-7e44-44d3-befd-905d365d05a1
88 :END:
89 Not all programming modes have outline support built-in. The good news is that it's easy
90 to enable it.
91 
92 You only need to modify one variable: =outline-regexp= and enable a minor-mode:
93 =outline-minor-mode=.
94 
95 *** Using dir-locals
96 :PROPERTIES:
97 :ID: cf39a556-fc2c-46e3-a2f7-e659213a915f
98 :END:
99 The way it's done in the NAS-T codebase is with a [[../../.dir-locals.el][.dir-locals.el]] file.
100 
101 You just need to add this form for the mode of your choice, replacing the string
102 with a regular expression which matches on a /heading/. In this case we treat lines
103 starting with three comment chars or more as a new heading.
104 #+begin_src lisp-data
105 (makefile-mode . ((outline-regexp . "###+")))
106 #+end_src
107 
108 =outline-regexp= is declared as a safe local var, so no prompts will appear asking if
109 you trust these values. You will need to configure your keybinds and enable the
110 minor-mode separately though. For project-level support, that's all there is to it.
111 
112 *** Using init.el
113 :PROPERTIES:
114 :ID: c76e4c71-c77a-43b6-811c-0a83981a1dc5
115 :END:
116 You may also modify your config to enable =outline-minor-mode= for select major-modes at
117 startup. Here's a quick example from my config:
118 
119 #+begin_src emacs-lisp
120 ;;; Code:
121 (require 'default 'rw/fu)
122 
123 (defun outline-hook (rx)
124  "Enable `outline-minor-mode' and set `outline-regexp'."
125  (setq-local outline-regexp rx)
126  (outline-minor-mode t))
127 
128 (defun add-outline-hook (mode rx)
129  (let ((sym (symb mode "-hook")))
130  (add-hook sym (lambda () (outline-hook rx)))))
131 
132 (defmacro outline-hooks (&rest pairs)
133  `(mapc (lambda (x) (add-outline-hook (car x) (cadr x))) ',pairs))
134 
135 (outline-hooks (asm-mode ";;;+")
136  (nasm-mode ";;;+")
137  (rust-mode "\\(//!\\|////+\\)")
138  (sh-mode "###+")
139  (sh-script-mode "###+")
140  (makefile-mode "###+"))
141 
142 (provide 'outline-cfg)
143 ;;; outline-cfg.el ends here
144 #+end_src
145 ** Default Sections
146 :PROPERTIES:
147 :ID: c912d356-9688-4d48-91a8-ae234b410d46
148 :END:
149 Our default sections should look familiar - they're just Emacs Lisp defaults, with a few
150 choice extensions.
151 *** Source Header
152 :PROPERTIES:
153 :ID: 6e5856f0-3d75-4a4d-b44d-35e9015a63ab
154 :END:
155 First line of every source code file.
156 
157 Here is the prototype in lisp:
158 #+begin_src lisp
159 ;;; filename --- description -*- vars -*-
160 #+end_src
161 
162 In Rust we use:
163 #+begin_src rust
164 //! filename --- description -*- vars -*-
165 #+end_src
166 
167 etc.
168 **** Metadata :optional:
169 :PROPERTIES:
170 :ID: 56e5ccd3-b98a-4ba0-aaaa-eef49bc3fac7
171 :END:
172 Some files may insert a blank line and start the =Code= heading, while others will
173 include some additional information about the file such as a long-description, version,
174 list of exports, etc.
175 *** Commentary :optional:
176 :PROPERTIES:
177 :ID: a8e36d82-4788-422c-af83-3374f23f9dc9
178 :END:
179 An optional programmer commentary included in source code files after the =Source
180 Header= but before the =Code=. The contents are unpredictable but may include notes,
181 todos, diagrams, stack notations, test results, links, tips, etc.
182 *** Code
183 :PROPERTIES:
184 :ID: 7422d382-450b-46d6-898f-42daae455a47
185 :END:
186 The =Code= heading should be the final toplevel heading of any source code file. You
187 may see a number of sub-headings, starting with four or more comment chars.