-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.lit
265 lines (207 loc) · 9.31 KB
/
index.lit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# Srcweave Literate Mode
By: [**Eric Ihli**](https://owoga.com)
View the final code and other resources in the [GitHub repo](https://github.com/eihli/lit-mode).
This is an Emacs major-mode for literate programming in [srcweave](https://github.com/justinmeiners/srcweave), _written as a literate program using srcweave_.
This will be a bit of a rehash of [ModeTutorial](https://www.emacswiki.org/emacs/ModeTutorial) from the Emacs Wiki.
Thanks to [Scott Andrew Borton](https://www.emacswiki.org/emacs/ScottAndrewBorton) for that tutorial.
@toc
> **Note:** This tutorial is a [literate program](https://en.wikipedia.org/wiki/Literate_programming).
> This means you are reading the source code right now!
> Each piece of code will be shown and explained thoroughly, so you can be sure nothing is left out.
> The final code was created by ["tangling"](https://github.com/justinmeiners/srcweave) the blocks of code together.
# Hooks
First, we define some variables that all modes should define. ‘lit-mode-hook’ allows the user to run their own code when your mode is run.
--- Hooks
(defvar lit-mode-mode-hook nil)
---
# Keymap
Now we create a keymap. This map, here called ‘lit-mode-map’, allows both you and users to define their own keymaps. The keymap is immediately set to a default keymap. Then, using ‘define-key’, we insert an example keybinding into the keymap, which maps the ‘newline-and-indent’ function to Control-j (which is actually the default binding for this function, but is included anyway as an example). Of course, you may define as many keybindings as you wish.
If your keymap will have very few entries, then you may want to consider ‘make-sparse-keymap’ rather than ‘make-keymap’.
--- Keymap
(defvar lit-mode-map
(let ((map (make-keymap)))
@{Keybindings}
map)
"Keymap for Srcweave Literate major mode.")
---
--- Keybindings
(define-key map (kbd "C-j") 'newline-and-indent)
---
# Autoload
Here, we append a definition to ‘auto-mode-alist’. This tells emacs that when a buffer with a name ending with .wpd is opened, then wpdl-mode should be started in that buffer. Some modes leave this step to the user.
--- Autoload
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.lit\\'" . lit-mode))
---
# Syntax highlighting
Next let's define a minimal set of keywords for emacs to highlight. A ‘font-lock-keyword’ variable is a list of keywords to highlight. There are many ways to specify this list. I'll use the form (matcher . facename). With this form, I'll specify a pattern to match, and then a face name to use for the actual highlighting.
--- Syntax highlighting
(defconst lit-font-lock-code-blocks
(list
'("^\\(---\\)". font-lock-doc-markup-face)
'("^---[\t ]*\\([^\-\n/]+\\)" . (1 font-lock-constant-face))
'("@@{\\([^}]+\\)}" . (1 font-lock-constant-face)))
"Minimal highlighting expressions for lit mode.")
---
There are three elements to my list: the first element matches srcweave code block syntax, `---`.
The second element matches the declaration of the reference name (the name used to include the contents of one source code block into another).
And the third matches the _use_ of the reference name (e.g. `@@{SomeCodeBlock}`).
I'm going to borrow some existing faces with names close-enough to what I want.
For the open and close of a code block, I'm going to use `font-lock-doc-markup-face`.
For the code block reference names, I'll use `font-lock-constant-face`.
For my keyword list, I’ve selected those WPDL keywords which would benefit most from being highlighted: keywords that delimit blocks of information.
One may notice that the regexp used to specify these keywords is optimized.
I did not have to do this by hand.
Emacs provides the ‘regexp-opt’ function to save you from the tedious work of creating complicated regexps.
‘regexp-opt’ takes a list of strings and an additional optional argument.
This optional argument controls whether or not we want to wrap the entire regexp in parens.
In our case, we do.
For example, the following expression:
## Single " turning on highlighting
This is best demonstrated with an image.
It's hard to know when a `"` should be the start of a string that we want to highlight and when it's just a lone character that shouldn't turn on highlighting.
I'm finding it nicer to just have it turned off.
It wasn't easy to find.
(defvar font-lock-keywords-only nil
"Non-nil means Font Lock should not fontify comments or strings.
This is normally set via `font-lock-defaults'.")
Although this is technically a syntax highlighting topic, I'll put this in lit-mode-hooks so it will run when the mode is activated.
--- lit-mode-hooks
(setq font-lock-keywords-only t)
---
# Indentation
Indentation is very fundamental.
I'm going to default to tabs for indentation.
--- Indentation
(add-hook 'lit-mode-hook
(lambda ()
@{lit-mode-hooks}
(setq indent-tabs-mode t)
(setq indent-tabs-function #'tab-to-tab-stop)
(setq indent-line-function #'indent-to-left-margin)
(setq display-line-numbers-mode t)
(setq tab-width 4)))
---
# Editing a code block
I want to be able to take advantage of major modes for whatever language I'm working with.
Language-specific major modes are especially nice for things like formatting and alignment.
This gives us syntax highlighting but gives us errors from LSP.
https://emacs-lsp.github.io/lsp-mode/manual-language-docs/lsp-org/
--- Editing a code block
(defvar lit-mode-code-block-mode 'emacs-lisp-mode)
(defvar lit-mode-saved-mode nil)
(defun lit-mode-widen-advice ()
(funcall (symbol-function lit-mode-saved-mode))
(setq lit-mode-saved-mode nil)
(advice-remove 'widen #'lit-mode-widen-advice))
(defun lit-mode-narrow-to-code-block-for-editing ()
(interactive)
(let ((beg)
(end))
(setq lit-mode-saved-mode major-mode)
(advice-add 'widen :after #'lit-mode-widen-advice)
(save-excursion
(search-backward-regexp "^--- ")
(next-line)
(beginning-of-line)
(setq beg (point))
(search-forward-regexp "^---$")
(previous-line)
(end-of-line)
(setq end (point))
(narrow-to-region beg end)
(funcall lit-mode-code-block-mode))))
---
Narrowing a buffer makes it _look_ like you're only operating on a small section of a buffer.
But any buffer configuration, like the mode line, operate on the entire buffer even if you can't see the entire buffer in your window.
This becomes a problem if you narrow to a region of C code and then turn on c-mode.
The mode will complain because the buffer isn't valid C.
Emacs offers a `clone-indirect` that creates a copy of a buffer and changes made to one buffer get reflected in the other.
But in hindsight. That doesn't work either.
https://github.com/aaronbieber/fence-edit.el/blob/master/fence-edit.el
--- Editing a code block 2
(defvar lit-mode-code-block-mode 'c-mode)
(defmacro with-indirect-buffer (beg end &rest body)
(let ((saved-mode major-mode))
`(with-current-buffer (clone-indirect-buffer nil nil)
(narrow-to-region beg end)
(funcall lit-mode-code-block-mode)
(unwind-protect
,body
(kill-buffer (current-buffer))))))
(defun lit-mode-points-surrounding-code-block ()
(save-excursion
(let ((beg)
(end))
(search-backward-regexp "^--- ")
(next-line)
(beginning-of-line)
(setq beg (point))
(search-forward-regexp "^---$")
(previous-line)
(end-of-line)
(setq end (point))
(cl-values beg end))))
(defun lit-mode-edit-region-in-mode ()
(interactive "@r")
(let ((new-buffer (clone-indirect-buffer nil t)))
(cl-multiple-value-bind (beg end) (lit-mode-points-surrounding-code-block)
(narrow-to-region beg end)
(funcall lit-mode-code-block-mode))))
---
--- Keybindings +=
(define-key map (kbd "C-;") #'lit-mode-narrow-to-code-block-for-editing)
---
# The entry function
Finally, we will create the function that will be called by Emacs when the mode is started.
--- Entry function
(defun lit-mode ()
"Major mode for editing srcweave literate files."
(interactive)
(kill-all-local-variables)
(use-local-map lit-mode-map)
(set (make-local-variable 'font-lock-defaults) '(lit-font-lock-code-blocks))
(setq major-mode 'lit-mode)
(setq mode-name "Lit")
(run-hooks 'lit-mode-hook))
---
--- /lit-mode.el
@{Preamble}
@{Hooks}
@{Keymap}
@{Autoload}
@{Syntax highlighting}
@{Indentation}
@{Editing a code block}
@{Entry function}
(provide 'lit-mode)
@{Postamble}
---
# Preamble
`advice-add` requires a minimum Emacs version of 24.4, so we set that in the preamble.
--- Preamble
;;; lit-mode.el --- Description -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2022 Eric Ihli
;;
;; Author: Eric Ihli <eihli@owoga.com>
;; Maintainer: Eric Ihli <eihli@owoga.com>
;; Created: August 02, 2022
;; Modified: August 02, 2022
;; Version: 0.0.1
;; Keywords: abbrev bib c calendar comm convenience data docs emulations extensions faces files frames games hardware help hypermedia i18n internal languages lisp local maint mail matching mouse multimedia news outlines processes terminals tex tools unix vc wp
;; Homepage: https://github.com/eihli/lit-mode
;; Package-Requires: ((emacs "24.4"))
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;;
;; Description
;;
;;; Code:
---
### Postamble
--- Postamble
;;; lit-mode.el ends here
---