1if not modules then modules = { } end modules ['typo-chr'] = {
2 version = 1.001,
3 comment = "companion to typo-bld.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12local insert, remove = table.insert, table.remove
13
14local context = context
15local ctx_doifelse = commands.doifelse
16
17local nodecodes = nodes.nodecodes
18local boundarycodes = nodes.boundarycodes
19local subtypes = nodes.subtypes
20
21local glyph_code <const> = nodecodes.glyph
22local par_code <const> = nodecodes.par
23local boundary_code <const> = nodecodes.boundary
24
25local wordboundary_code <const> = boundarycodes.word
26
27local texgetnest = tex.getnest
28local texsetcount = tex.setcount
29
30local flushnode = nodes.flushnode
31local flushlist = nodes.flushlist
32
33local settexattribute = tex.setattribute
34local ispunctuation = characters.is_punctuation
35
36local variables = interfaces.variables
37local v_all <const> = variables.all
38local v_reset <const> = variables.reset
39
40local stack = { }
41
42local a_marked <const> = attributes.numbers['marked']
43local lastmarked = 0
44local marked = {
45 [v_all] = 1,
46 [""] = 1,
47 [v_reset] = attributes.unsetvalue,
48}
49
50local function pickup()
51 local list = texgetnest()
52 if list then
53 local tail = list.tail
54 if tail and tail.id == glyph_code and ispunctuation[tail.char] then
55 local prev = tail.prev
56 list.tail = prev
57 if prev then
58 prev.next = nil
59 end
60 list.tail = prev
61 tail.prev = nil
62 return tail
63 end
64 end
65end
66
67local actions = {
68 remove = function(specification)
69 local n = pickup()
70 if n then
71 flushnode(n)
72 end
73 end,
74 push = function(specification)
75 local n = pickup()
76 if n then
77 insert(stack,n or false)
78 end
79 end,
80 pop = function(specification)
81 local n = remove(stack)
82 if n then
83 context(n)
84 end
85 end,
86}
87
88local function pickuppunctuation(specification)
89 local action = actions[specification.action or "remove"]
90 if action then
91 action(specification)
92 end
93end
94
95
96
97
98
99
100local function pickup(head,tail,mark,backtrack)
101 if tail.id == par_code then
102
103 else
104 local last = tail
105 if backtrack then
106 local current = last
107 while current do
108 if current[a_marked] == mark then
109 last = current
110 break
111 else
112 current = current.prev
113 end
114 end
115 end
116 if last and last[a_marked] == mark then
117 local first = last
118 while true do
119 local prev = first.prev
120 if prev and prev[a_marked] == mark then
121 if prev.id == par_code then
122 break
123 else
124 first = prev
125 end
126 else
127 break
128 end
129 end
130 return first, last
131 end
132 end
133end
134
135local function found(str)
136 local list = texgetnest()
137 if list then
138 local tail = list.tail
139 if tail then
140 local mark = marked[str]
141 return mark and mark == tail[a_marked]
142 end
143 end
144end
145
146local actions = {
147 remove = function(specification)
148 local mark = marked[specification.mark]
149 if mark then
150 local list = texgetnest()
151 if list then
152 local head = list.head
153 local tail = list.tail
154 local first, last = pickup(head,tail,mark,specification.backtrack)
155 if first then
156 if first == head then
157 list.head = nil
158 list.tail = nil
159 else
160 local prev = first.prev
161 list.tail = prev
162 prev.next = nil
163 end
164 flushlist(first)
165 end
166 end
167 end
168 end,
169}
170
171local function pickupmarkedcontent(specification)
172 local action = actions[specification.action or "remove"]
173 if action then
174 action(specification)
175 end
176end
177
178local function markcontent(str)
179 local currentmarked = marked[str or v_all]
180 if not currentmarked then
181 lastmarked = lastmarked + 1
182 currentmarked = lastmarked
183 marked[str] = currentmarked
184 end
185 settexattribute(a_marked,currentmarked)
186end
187
188interfaces.implement {
189 name = "pickuppunctuation",
190 actions = pickuppunctuation,
191 arguments = {
192 {
193 { "action" }
194 }
195 }
196}
197
198interfaces.implement {
199 name = "pickupmarkedcontent",
200 actions = pickupmarkedcontent,
201 arguments = {
202 {
203 { "action" },
204 { "mark" },
205 { "backtrack" , "boolean" },
206 }
207 }
208}
209
210interfaces.implement {
211 name = "markcontent",
212 actions = markcontent,
213 arguments = "string",
214}
215
216interfaces.implement {
217 name = "doifelsemarkedcontent",
218 actions = function(str) ctx_doifelse(found(str)) end,
219 arguments = "string",
220}
221
222
223
224interfaces.implement {
225 name = "lastnodeidstring",
226 public = true,
227 actions = function()
228 local list = texgetnest()
229 local okay = false
230 if list then
231 local tail = list.tail
232 if tail then
233 okay = nodecodes[tail.id]
234 end
235 end
236 context(okay or "")
237 end,
238}
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254local c_syst_last_node_id <const> = tex.iscount("c_syst_last_node_id")
255
256interfaces.implement {
257 name = "lastnodeid",
258 actions = function()
259 local list = texgetnest()
260 local okay = -1
261 if list then
262 local tail = list.tail
263 if tail then
264 okay = tail.id
265 end
266 end
267 texsetcount(c_syst_last_node_id,okay)
268 end,
269}
270
271interfaces.implement {
272 name = "lastnodesubtypestring",
273 public = true,
274 actions = function()
275 local list = texgetnest()
276 local okay = false
277 if list then
278 local tail = list.tail
279 if head then
280 okay = subtypes[tail.id][tail.subtype]
281 end
282 end
283 context(okay or "")
284 end,
285}
286
287local function lastnodeequals(id,subtype)
288 local list = texgetnest()
289 local okay = false
290 if list then
291 local tail = list.tail
292 if tail then
293 local i = tail.id
294 okay = i == id or i == nodecodes[id]
295 if subtype then
296 local s = tail.subtype
297 okay = s == subtype or s == subtypes[i][subtype]
298 end
299 end
300 end
301 ctx_doifelse(okay)
302end
303
304interfaces.implement {
305 name = "lastnodeequals",
306 arguments = "2 strings",
307 actions = lastnodeequals,
308}
309
310interfaces.implement {
311 name = "atwordboundary",
312 actions = function()
313 lastnodeequals(boundary_code,wordboundary_code)
314 end,
315}
316
317 |