changelog shortlog graph tags branches changeset files file revisions raw help

Mercurial > org > blog / annotate draft/outlines.org

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