summaryrefslogtreecommitdiff
path: root/notes.org
blob: f7adf8b956c5ecfccfa9b649db0e22103f59009c (plain)
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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
#+PROPERTY: logging nil
#+TODO: TODO UNDERWAY | DONE CANCELED
#+TYP_TODO: MAYBE | CANCELED
#+TYP_TODO: TEMPLATE

* Plans

** UNDERWAY 0.5

*** TODO Release 0.5

+  [ ] Check comment TODOs (using =magit-todos=).
+  [ ] Check issues.
+  [ ] Check plans (in this file).
+  [ ] Check linters.
+  [ ] Check tests.
+  [ ] Update version numbers in file headers.
     -  [ ] org-make-toc.el
+  [ ] Update changelog in =README.org=.
+  [ ] Tag and sign new version (using Magit's =t r=).
+  [ ] Push =master=.
+  [ ] Push tags.
+  [ ] Post-release changes:
     -  [ ] Bump version numbers to n+1-pre:
          +  [ ] org-make-toc.el
          +  [ ] README.org

* Notes

** DONE Get document tree
CLOSED: [2017-08-01 Tue 22:41]
:PROPERTIES:
:TESTING:  testing
:toc:      ignore
:END:
:LOGBOOK:
-  State "DONE"       from "TODO"       [2017-08-01 Tue 22:41]
:END:

There are different ways to get a tree of the document structure.

+  =org-imenu-get-tree= returns one that should be useful.
+  =org-element-parse-buffer= is more semantic and is probably better for my uses.  It looks like it returns metadata, while =org-imenu-get-tree= is bare-bones, just what =imenu= needs. 

Yeah, looks like =org-element-parse-buffer= is the way to go.

*** =org-element-parse-buffer=

#+BEGIN_SRC elisp :results code
  (org-element-parse-buffer 'headline)
#+END_SRC

#+RESULTS:
#+BEGIN_SRC elisp
(org-data nil
          (headline
           (:raw-value "Notes" :begin 1 :end 2266 :pre-blank 0 :contents-begin 9 :contents-end 2266 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 1 :title "Notes" :parent #0)
           (headline
            (:raw-value "Build document tree" :begin 9 :end 2266 :pre-blank 0 :contents-begin 37 :contents-end 2266 :level 2 :priority nil :tags nil :todo-keyword
                        #("TODO" 0 4
                          (fontified t line-prefix
                                     #("*" 0 1
                                       (face org-hide))
                                     wrap-prefix
                                     #("*** " 0 1
                                       (face org-indent)
                                       1 4
                                       (face org-indent))
                                     org-todo-head "TODO" face org-todo))
                        :todo-type todo :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 9 :TESTING "testing" :title "Build document tree" :parent #1)
            (headline
             (:raw-value "=org-element-parse-buffer=" :begin 390 :end 2266 :pre-blank 1 :contents-begin 422 :contents-end 2266 :level 3 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 390 :title "=org-element-parse-buffer=" :parent #2)))))
#+END_SRC

The structure of this appears to be like:

+  'headline
     -  List of properties
     -  List for child heading
          +  'headline
          +  List of properties

** DONE Walk tree
CLOSED: [2017-08-02 Wed 01:08]
:LOGBOOK:
-  State "DONE"       from "UNDERWAY"   [2017-08-02 Wed 01:08]
-  State "UNDERWAY"   from "DONE"       [2017-08-02 Wed 00:08]
:END:

*** cl-loop 
:PROPERTIES:
:ID:       b75e4923-0a34-4f3f-830c-f5157397add1
:END:

This works, but it helped me figure out how to use =org-element-map=, so I guess I'll use that.

#+BEGIN_SRC elisp :results verbatim
  (defun org-walk-tree (tree element-pred)
    (cl-loop for element in tree
             when (eql 'headline (car element))
             when (funcall element-pred element)
             collect (list 'headline
                           :name (org-element-property :title element)
                           :children (org-walk-tree (cddr element) element-pred))))

  (with-current-buffer (find-buffer-visiting "~/src/org-make-toc/notes.org")
    (let ((tree (cddr (org-element-parse-buffer 'headline))))
      (org-walk-tree tree (lambda (element)
                            (not (string= (org-element-property :TOC element)
                                          "ignore"))))))
#+END_SRC

#+RESULTS:
: ((headline :name "Notes" :children ((headline :name "Filter tree" :children ((headline :name "Remove ignored headings" :children ((headline :name "org-element-map" :children nil) (headline :name "Other loop" :children nil))) (headline :name "Remove too-deep headings" :children nil) (headline :name "Remove higher headings" :children nil))) (headline :name "Transform tree to org list" :children nil) (headline :name "Find TOC element" :children nil) (headline :name "Replace TOC contents with list" :children nil))))

*** org-element-map
:PROPERTIES:
:ID:       fbe83744-e0e9-4d44-8abc-e48809c96478
:END:

#+BEGIN_SRC elisp :results verbatim
    (defun org-walk-tree (tree element-pred)
      (cl-loop for element in tree
               when (eql 'headline (car element))
               when (funcall element-pred element)
               collect (list 'headline
                             :name (org-element-property :title element)
                             :children (org-walk-tree (cdddr element) element-pred))))

    (defun argh (element)
      (unless (string= (org-element-property :TOC element)
                       "ignore")
        (list 'headline
              :name (org-element-property :title element)
              :children (org-element-map (cddr element) '(headline)
                                 #'argh nil nil '(headline)))))

    (with-current-buffer (find-buffer-visiting "~/src/org-make-toc/notes.org")
      (let ((tree (cddr (org-element-parse-buffer 'headline))))
        (org-element-map tree '(headline) #'argh nil nil '(headline))))

#+END_SRC

#+RESULTS:
: ((headline :name "Notes" :children ((headline :name "Filter tree" :children ((headline :name "Use treepy to walk the tree" :children nil) (headline :name "Remove ignored headings" :children ((headline :name "cl-loop" :children nil) (headline :name "org-element-map" :children nil))) (headline :name "Remove too-deep headings" :children nil) (headline :name "Remove higher headings" :children nil))) (headline :name "Transform tree to org list" :children nil) (headline :name "Find TOC element" :children nil) (headline :name "Replace TOC contents with list" :children nil))))

** DONE Add =:local= to examples.org

** TODO Filter tree

*** DONE Remove higher headings
CLOSED: [2017-08-02 Wed 03:42]
:LOGBOOK:
-  State "DONE"       from "TODO"       [2017-08-02 Wed 03:42]
:END:

**** DONE Get TOC level
CLOSED: [2017-08-02 Wed 02:06]
:PROPERTIES:
:ID:       b2f133c6-7d1e-4eb9-986a-cfb5aecb3905
:END:
:LOGBOOK:
-  State "DONE"       from              [2017-08-02 Wed 02:06]
:END:

Ugly, but it works.  I guess the rather non-standard structure of the element tree makes this necessary.  I tried to get =treepy= working with zippers, and I almost did...

#+BEGIN_SRC elisp
  (defun org-make-toc--first-in-tree (tree element-pred)
    (cl-loop for element in tree
             for type = (car element)
             if (eq 'headline type)
             for result = (funcall element-pred element)
             if result
             return result
             else
             for children = (cddr element)
             when children
             for result = (org-make-toc--first-in-tree children element-pred)
             when result
             return result))
#+END_SRC

#+BEGIN_SRC elisp
  (with-current-buffer (find-buffer-visiting "~/src/org-make-toc/test/data.org")
    (let* ((tree (cddr (org-element-parse-buffer 'headline)))
           (tree (org-make-toc--remove-ignored-entries tree))
           (toc-level (org-make-toc--first-in-tree tree
                                                   #'org-make-toc--is-toc-entry
                                                   #'org-make-toc--element-level)))
      toc-level))
#+END_SRC

#+RESULTS:
: 2

**** DONE Remove higher ones 
CLOSED: [2017-08-02 Wed 03:41]
:LOGBOOK:
-  State "DONE"       from "TODO"       [2017-08-02 Wed 03:41]
:END:

#+BEGIN_SRC elisp :results list
  (with-current-buffer (find-buffer-visiting "~/src/org-make-toc/test/data.org")
    (let* ((tree (cddr (org-element-parse-buffer 'headline)))
           (tree (org-make-toc--remove-ignored-entries tree)))
      (org-make-toc--remove-higher-level-than-toc tree)))
#+END_SRC


*** MAYBE Remove too-deep headings
:LOGBOOK:
-  State "MAYBE"      from "TODO"       [2017-08-02 Wed 01:08]
:END:

*** CANCELED Use treepy to walk the tree
CLOSED: [2017-08-02 Wed 00:07]
:LOGBOOK:
-  State "CANCELED"   from "TODO"       [2017-08-02 Wed 00:07]
:END:

I guess I'm just not smart enough to use treepy, because all I could get out of its walking functions were errors.  Or maybe it's just not suitable for the kind of structure =org-element-map= returns.  I don't know.

#+BEGIN_SRC elisp
  (require 'treepy)

  (treepy-walk  #'identity (cddr (org-element-parse-buffer 'headline)))
#+END_SRC

*** DONE Remove ignored headings
CLOSED: [2017-08-02 Wed 00:57]
:LOGBOOK:
-  State "DONE"       from "TODO"       [2017-08-02 Wed 00:57]
:END:

#+BEGIN_SRC elisp 
  (with-current-buffer (find-buffer-visiting "~/src/org-make-toc/test/data.org")
    (let ((tree (cddr (org-element-parse-buffer 'headline))))
      (org-make-toc--remove-ignored-entries tree)))
#+END_SRC

#+RESULTS:
| headline | A               | ((headline Contents ((headline Hmm nil))) (headline A1 nil) (headline A2 nil) (headline A3 nil)) |
| headline | B               | ((headline B1 nil) (headline B2 nil) (headline B3 nil))                                          |
| headline | Ignore-Children |                                                                                                  |
| headline | C               | ((headline C1 nil) (headline C2 nil) (headline C3 nil))                                          |
| headline | Invalid         | nil                                                                                              |

** TODO Use =org-list-to-org= from Org 9.1
:PROPERTIES:
:ID:       bafab4a8-6084-40ff-ae0e-5b601b5c034e
:END:

Should be a nicer way to turn the ToC into the list string.

** TODO Check out [[https://github.com/Fuco1/orgpath][orgpath]]

This looks really cool, although he seems to have abandoned it.  I cloned a local copy to src/misc/org.  Studying his functions that build a tree should be very helpful.

** UNDERWAY Transform tree to org list
:LOGBOOK:
-  State "UNDERWAY"   from "TODO"       [2017-08-02 Wed 04:16]
:END:

With each element being a link to the real heading.  Need to support both GitHub links and Org links.

*** DONE GitHub-style
CLOSED: [2017-08-02 Wed 04:16]
:LOGBOOK:
-  State "DONE"       from "TODO"       [2017-08-02 Wed 04:16]
:END:

#+BEGIN_SRC elisp :results org
(with-current-buffer (find-buffer-visiting "~/src/org-make-toc/test/data.org")
    (let* ((tree (cddr (org-element-parse-buffer 'headline)))
           (tree (org-make-toc--remove-ignored-entries tree))
(tree (org-make-toc--remove-higher-level-than-toc tree)))
      (org-make-toc--tree-to-list tree)))
#+END_SRC

#+RESULTS:
#+BEGIN_SRC org
    - [[Hmm][#hmm]]
   - [[A1][#a1]]
   - [[A2][#a2]]
   - [[A3][#a3]]
   - [[B1][#b1]]
   - [[B2][#b2]]
   - [[B3][#b3]]
   - [[C1][#c1]]
   - [[C2][#c2]]
   - [[C3][#c3]]
#+END_SRC

** TODO Replace TOC contents with list

#+BEGIN_SRC elisp
  (with-current-buffer (find-buffer-visiting "~/src/org-make-toc/test/data.org")
    (-when-let* ((tree (cddr (org-element-parse-buffer 'headline)))
                 (tree (org-make-toc--remove-ignored-entries tree))
                 (tree (org-make-toc--remove-higher-level-than-toc tree))
                 (list (org-make-toc--tree-to-list tree))
                 (pos (org-find-property "TOC" "this")))
      (org-make-toc--replace-entry-contents pos list)))

#+END_SRC

#+RESULTS:
#+begin_example
    - [[#hmm][Hmm]]
    - [[#oho][Oho]]
   - [[#a1][A1]]
   - [[#a2][A2]]
   - [[#a3][A3]]
   - [[#b1][B1]]
   - [[#b2][B2]]
   - [[#b3][B3]]
   - [[#c1][C1]]
   - [[#c2][C2]]
   - [[#c3][C3]]
#+end_example

** Treepy

#+BEGIN_SRC elisp
  (require 'treepy)

  (with-current-buffer (find-buffer-visiting "~/src/org-make-toc/test/data.org")
    (let* ((tree (cddr (org-element-parse-buffer 'headline)))
           (zipper (treepy-zipper #'treepy-branch-p
                                  #'treepy-children
                                  #'treepy-make-node
                                  tree)))
      (treepy-children zipper)))

  (with-current-buffer (find-buffer-visiting "~/src/org-make-toc/test/data.org")
    (let* ((tree (cddr (org-element-parse-buffer 'headline)))
           (zipper (treepy-zipper (lambda (loc) (eql 'headline (car loc)))
                                  (lambda (loc) (cddr loc))
                                  (lambda (loc) (list 'headline (cdr loc) (cddr loc)))
                                  tree)))
      zipper))
#+END_SRC

* Checklists

** TEMPLATE Release

+  [ ] Check comment TODOs (using =magit-todos=).
+  [ ] Check issues.
+  [ ] Check plans (in this file).
+  [ ] Check linters.
+  [ ] Check tests.
+  [ ] Update version numbers in file headers.
     -  [ ] org-make-toc.el
+  [ ] Update changelog in =README.org=.
+  [ ] Tag and sign new version (using Magit's =t r=).
+  [ ] Push =master=.
+  [ ] Push tags.
+  [ ] Post-release changes:
     -  [ ] Bump version numbers to n+1-pre:
          +  [ ] org-make-toc.el
          +  [ ] README.org