-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathkcell.el
268 lines (231 loc) · 9.42 KB
/
kcell.el
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
;;; kcell.el --- Internal representation of koutline kcells used by kviews -*- lexical-binding: t; -*-
;;
;; Author: Bob Weiner
;;
;; Orig-Date: 1-May-93
;; Last-Mod: 25-Nov-23 at 16:33:20 by Mats Lidell
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
;; Copyright (C) 1993-2022 Free Software Foundation, Inc.
;; See the "../HY-COPY" file for license information.
;;
;; This file is part of GNU Hyperbole.
;;; Commentary:
;;
;; Defines kcells, in-memory, individually addressable elements of Koutlines,
;; along with a persistent representation called kcell-data for writing to
;; files. Node text content is stored separately in the kview for efficiency.
;;
;; To obtain the kcell at a point in the buffer, use `kcell-view:cell'.
;; To move point to a specific cell given a reference string, use
;; `kotl-mode:goto-cell' or `kview:goto-cell-id'.
;;
;; For compatibility between kcell and kcell-data representations,
;; the unique per Koutline permanent idstamp for each kcell is also stored
;; separately. This also allows fast retrieval.
;;; Code:
;;; ************************************************************************
;;; Other required Elisp libraries
;;; ************************************************************************
(eval-and-compile (mapc #'require '(hinit htz kview kproperty)))
;;; ************************************************************************
;;; Public declarations
;;; ************************************************************************
(declare-function kotl-mode:goto-heading "kotl/kotl-mode")
;;; ************************************************************************
;;; Public variables
;;; ************************************************************************
(defvar kcell:read-only-attributes
'(idstamp creator create-time modifier mod-time)
"List of kcell attributes which may not be modified by a user.
Add to this list but don't remove any of the default elements.")
;;; ************************************************************************
;;; Public functions
;;; ************************************************************************
;;;
;;; kcell - In-memory representation of Koutline cells
;;;
(defun kcell:copy (kcell)
"Return a copy of KCELL."
(copy-tree kcell))
(defun kcell:create (&optional plist)
"Return a new kcell with optional property list, PLIST.
User id of `creator' of cell and `create-time' are added to cell's PLIST if
not already there."
(nconc
(unless (memq 'creator plist)
(list 'creator hyperb:user-email
'create-time (htz:date-sortable-gmt)))
plist))
(defun kcell:create-top (&optional top-cell-attributes)
"Return a new top cell with optional property list of TOP-CELL-ATTRIBUTES.
The idstamp of the top cell is always 0 and this cell stores the
idstamp-counter."
(kcell:create top-cell-attributes))
(defalias 'kcell:get-attr 'plist-get)
(defun kcell:is-p (object)
"Is OBJECT a kcell?"
(and (listp object) (plist-get object 'creator)))
;;;###autoload
(defun kcell:parse-cell-ref (cell-ref)
"Parse CELL-REF string and return list of strings (<cell-id> <viewspec>).
If any item in the list is missing, it is nil."
(let (cell-id
kvspec)
;; !! TODO: Remove any relative specs and view specs from
;; cell-ref to form cell-id. Really should account for Augment-style
;; relative specs here, but we don't yet support them.
(cond ((and (stringp cell-ref)
(string-match "\\(\\.[a-zA-Z]+\\)?\\([|:][^|: \t\n\r\f]+\\)\\|\\.[a-zA-Z]+"
cell-ref))
;; relative cell id and optional kvspec
(setq cell-id (substring cell-ref 0 (match-beginning 0))
kvspec (when (match-beginning 2)
(match-string 2 cell-ref))))
((and (stringp cell-ref)
(string-match "[^|\n\r\f]+?\\([|:][^|: \t\n\r\f]+\\)"
cell-ref))
;; string heading and optional kvspec
(setq cell-id (string-trim (substring cell-ref 0 (match-beginning 0)))
kvspec (when (match-beginning 1)
(match-string 1 cell-ref))))
;; idstring with no kvspec
(t (setq cell-id cell-ref
kvspec nil)))
(list cell-id kvspec)))
(defun kcell:plist (kcell)
"Return the property list of KCELL."
(identity kcell))
;;;###autoload
(defun kcell:ref-to-id (cell-ref &optional kviewspec-flag)
"Return a CELL-REF string converted to a cell idstamp (integer).
If CELL-REF contains both a relative and a permanent id, the permanent id is
returned. If CELL-REF is invalid or does not exist, nil is returned.
If optional KVIEWSPEC-FLAG is non-nil and CELL-REF includes a
viewspec, return the the idstamp concatenated with the viewspec
(begins with a | character) as a string.
CELL-REF may be a whole number:
12 - permanent idstamp
or may be composed from these string forms:
1 or 1b - relative id, augment style
1.2 - relative id, legal style
012 - permanent idstamp
1a=012 - both relative and permanent ids (in that order) separated by =
|viewspec - a koutliner viewspec setting, rather than a cell reference
:viewspec - an augment viewspec, ignored for now.
Optionally, any of these id forms (or the relative form) may be followed by
zero or more whitespace characters and optionally a comma, followed by
the '|' character and some view specification characters.
Augment capabilities not yet implemented and ignored for now:
1. Augment viewspec characters preceded by a colon
2. Any of the above id forms followed by a period and some
alpha characters indicating a location relative to the id."
(cond ((integerp cell-ref)
(if (zerop cell-ref)
0
(when (kproperty:position 'idstamp cell-ref)
cell-ref)))
((stringp cell-ref)
(let (kviewspec
relative-id-string
idstamp-string)
;; Remove whitespace and any comma
(setq cell-ref (replace-regexp-in-string "\\s-*,?\\s-*" "" cell-ref nil t))
(if (string-equal cell-ref "0")
(setq idstamp-string "0")
;; Ignore Augment :viewspec.
(when (string-match ":" cell-ref)
(setq cell-ref (substring cell-ref 0 (match-beginning 0))))
;; Separate koutline |kviewspec from cell id.
(when (string-match "|" cell-ref)
(setq kviewspec (substring cell-ref (match-beginning 0))
cell-ref (substring cell-ref 0 (match-beginning 0))))
(setq idstamp-string
(cond
((string-match-p "[^.= \t\n\r\f0-9a-zA-Z]" cell-ref) nil)
((or
;; relative cell ref and idstamp
(string-match "\\`\\([1-9][.0-9a-zA-Z]*\\)=\\(0[0-9]*\\)\\'" cell-ref)
;; idstamp only
(string-match "\\`\\(\\)\\(0[0-9]*\\)\\'" cell-ref))
(setq idstamp-string (match-string 2 cell-ref))
;; Validate that idstamp value exists, else return nil
(when (kproperty:position 'idstamp (string-to-number idstamp-string))
idstamp-string))
((string-match "\\`\\([1-9][.0-9a-zA-Z]*\\)\\'" cell-ref)
;; relative cell ref
(setq relative-id-string (match-string 1 cell-ref))
(save-excursion
(goto-char (point-min))
(when (re-search-forward (concat "^[ \t]*" (regexp-quote relative-id-string)
(regexp-quote (kview:label-separator kotl-kview)))
nil t)
(setq idstamp-string (kcell-view:idstamp))
;; Validate that idstamp value exists, else return nil
(when (kproperty:position 'idstamp (string-to-number idstamp-string))
idstamp-string))))
((save-excursion
(when (kotl-mode:goto-heading cell-ref)
;; textual label at the beginning of a cell
(setq idstamp-string (kcell-view:idstamp))))))))
(if idstamp-string
(if (and kviewspec-flag kviewspec)
(concat idstamp-string kviewspec)
(string-to-number idstamp-string))
kviewspec)))))
(defun kcell:remove-attr (kcell attribute)
"Remove KCELL's ATTRIBUTE, if any, and return modified KCELL."
(let ((tail kcell)
sym
prev)
(setq sym (car tail))
(while (and sym (eq sym attribute))
(setq tail (cddr tail)
sym (car tail)))
(setq kcell tail
prev tail
tail (cddr tail))
(while tail
(setq sym (car tail))
(if (eq sym attribute)
(setcdr (cdr prev) (cddr tail)))
(setq prev tail
tail (cddr tail)))
kcell))
(defalias 'kcell:set-attr 'plist-put)
(defun kcell:set-create-time (kcell)
"Store the time of creation of KCELL."
(kcell:set-attr kcell 'create-time (htz:date-sortable-gmt)))
(defun kcell:set-creator (kcell)
"Store the current user's id as the creator of KCELL."
(kcell:set-attr kcell 'creator hyperb:user-email))
;;;
;;; kcell-data - Persistent representation of Koutline cells (written to files)
;;;
(defun kcell-data:create (cell idstamp)
"Given a kotl CELL and IDSTAMP (an integer), return a kcell-data structure.
If CELL, its idstamp, or its property list are nil, this repairs the cell by
assuming it is the cell at point and filling in the missing information."
(let ((plist (kcell:plist cell)))
(if (and cell idstamp plist)
(vector idstamp plist)
(kcell-data:create
(kcell:create plist)
(or idstamp (kview:id-increment kotl-kview))))))
(defun kcell-data:idstamp (kcell-data)
(aref kcell-data 0))
(defun kcell-data:plist-v2 (kcell-data)
(aref kcell-data 2))
(defun kcell-data:plist-v3 (kcell-data)
(aref kcell-data 1))
(defun kcell-data:to-kcell-v2 (kcell-data)
(if (vectorp kcell-data)
(kcell:create (kcell-data:plist-v2 kcell-data))
(kcell:create)))
(defun kcell-data:to-kcell-v3 (kcell-data)
(if (vectorp kcell-data)
(kcell:create (kcell-data:plist-v3 kcell-data))
(kcell:create)))
(provide 'kcell)
;;; kcell.el ends here