File: _text_dlist.tcl

package info (click to toggle)
tcllib 1.20%2Bdfsg-1
  • links: PTS
  • area: main
  • in suites: bullseye
  • size: 68,064 kB
  • sloc: tcl: 216,842; ansic: 14,250; sh: 2,846; xml: 1,766; yacc: 1,145; pascal: 881; makefile: 107; perl: 84; f90: 84; python: 33; ruby: 13; php: 11
file content (278 lines) | stat: -rw-r--r-- 7,546 bytes parent folder | download | duplicates (4)
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
# -*- tcl -*-
#
# Copyright (c) 2019 Andreas Kupries <andreas_kupries@sourceforge.net>
# Freely redistributable.
#
# _text_dlist.tcl -- Display list variables and accessors

#
# The engine maintains several data structures per document and pass.
# Most important is an internal representation of the text better
# suited to perform the final layouting, the display list. Elements of
# the display list are lists containing 2 elements, an operation, and
# its arguments, in this order. The arguments are a list again, its
# contents are specific to the operation.
#
# The operations are:
#
# - SECT	Section.    Title.
# - SUBSECT     Subsection. Title.
# - PARA	Paragraph.  Context reference and text.
#
# The PARA operation is the workhorse of the engine, dooing all the
# formatting, using the information in an "context" as the guide
# for doing so. The contexts themselves are generated during the
# second pass through the contents. They contain the information about
# nesting (i.e. indentation), bulleting and the like.
#

# # ## ### ##### ########
## State: Display list

global __dlist

# # ## ### ##### ########
## Internal: Extend

proc Store {op args} { global __dlist ; lappend __dlist [list $op $args] ; return}

# Debugging ...
#proc Store {op args} {puts_stderr "STO $op $args"; global __dlist; lappend __dlist [list $op $args]; return}

# # ## ### ##### ########
## API
#
# API Section		Add section
# API Subsection	Add subsection
# API CloseParagraph	Add paragraph using text and (current) env
#                       Boolean result indicates if something was added, or not

proc DListClear {} { global __dlist ; unset -nocomplain __dlist ; set __dlist {} }

proc Section    {name} {Store SECT    $name ; return}
proc Subsection {name} {Store SUBSECT $name ; return}

proc CloseParagraph {{id {}}} {
    set para [Text?]
    if {$para == {}} { return 0 }
    if {$id == {}} { set id [CAttrCurrent] }
    if {![ContextExists $id]} {
	error "Unknown context $id for paragraph"
    }
    Store PARA $id $para
    #puts_stderr "CloseParagraph $id [CAttrName $id]"
    #puts_stderr "  (($para))"
    TextClear
    return 1
} 

proc PostProcess {text} {
    #puts_stderr XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    #puts_stderr <<$text>>
    #puts_stderr XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    global __dlist
    # The argument is not relevant. Access the display list, perform
    # the final layouting and return its result.

    set lines {}
    array set state {lmargin 0 rmargin 0}
    foreach cmd $__dlist {
	lappend lines ""
	foreach {op arguments} $cmd break
	$op $arguments
    }

    #puts_stderr XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    return [Compose lines]\n
}

# # ## ### ##### ########
## PARA attributes
#
# Attributes
# - bullet      = bullet (template) to use for (un)ordered lists.
# - counter     = if present, item counter for enumeration list.
# - listtype    = type of list, if any.
# - lmargin     = left-indent, location of left margin for text.
# - prefix      = prefix to use for all lines of the parapgraph.
# - wspfx       = whitespace prefix for all but the first line of the paragraph.

proc BulletReset  {} { CAttrSet bullet {} }
proc ListNone     {} { CAttrSet listtype {} }
proc MarginIn     {} { CAttrIncr lmargin [LMI] }
proc MarginReset  {} { CAttrSet lmargin 0 }
proc PrefixReset  {} { CAttrSet prefix {} }
proc WPrefixReset {} { CAttrSet wspfx {} }

proc Prefix!   {p} { CAttrSet prefix $p }
proc Prefix+   {p} { CAttrAppend prefix $p }
proc WPrefix!  {p} { CAttrSet wspfx  $p }

proc Bullet?   {} { CAttrGet bullet }
proc ListType? {} { CAttrGet listtype }
proc Margin?   {} { CAttrGet lmargin }
proc Prefix?   {} { CAttrGet prefix }
proc WPrefix?  {} { CAttrGet wspfx }

proc List! {type bullet wprefix} {

    #puts_stderr L!(($type))
    #puts_stderr L!(($bullet))[string length $bullet],[string length [DeIce $bullet]]
    #puts_stderr L!(([Dots $wprefix]))
    
    CAttrSet listtype $type
    CAttrSet bullet   $bullet
    CAttrSet wspfx    $wprefix
}

proc EnumCounter {} {
    if {![CAttrHas counter]} {
	CAttrSet counter 1
    } else {
	CAttrIncr counter
    }
    ContextCommit	
    #puts_stderr "Counter ... [CAttrName] => [CAttrGet counter]"
    return [CAttrGet counter]
}

proc EnumId {} {
    # Handling the enumeration counter.
    #
    # Special case: An example as first paragraph in an item has to
    # use the counter in the context it is derived from to prevent
    # miscounting.

    #puts_stderr "EnumId: [CAttrName] | [CAttrName [Parent?]]"
    
    if {[Example?]} {
	ContextPush
	ContextSet [Parent?]
	set n [EnumCounter]
	ContextPop
    } else {
	set n [EnumCounter]
    }
    return $n
}

# # ## ### ##### ########
## Hooks

proc SECT {text} {
    #puts_stderr "SECT $text"
    #puts_stderr ""
    # text is actually the list of arguments, having one element, the text.
    upvar 1 lines lines
    set text [lindex $text 0]
    SectTitle lines $text
    return
}

proc SUBSECT {text} {
    #puts_stderr "SUBSECT $text"
    #puts_stderr ""
    # text is actually the list of arguments, having one element, the text.
    upvar 1 lines lines
    set text [lindex $text 0]
    SubsectTitle lines $text
    return
}

proc PARA {arguments} {
    upvar lines lines

    # Note. As the display list is processed at the very end we can
    # reuse the current context and accessors to hold and query the
    # context of each paragraph.
    
    foreach {env text} $arguments break
    ContextSet $env

    #puts_stderr "PARA $env [CAttrName $env]"
    #parray_stderr ::currentContext ;# consider capsulation
    #puts_stderr "    (($text))"
    #puts_stderr ""

    # Use the information in the referenced context to format the
    # paragraph.

    set lm    [Margin?]
    set lt    [ListType?]
    set blank [WPrefix?]
    
    if {[Verbatim?]} {
	set text [Undent $text]
	#puts_stderr "UN  (($text))"
    } else {
	set  plm $lm
	incr plm [string length $blank]
	set text [Reflow $text [RMargin $plm]]
    }

    # Now apply prefixes, (ws prefixes bulleting), at last indentation.

    set p [Prefix?]
    if {[string length $p]} {
	set text [Indent $text $p]
	#puts_stderr "IN  (($text))"
    }

    if {$lt != {}} {
	switch -exact $lt {
	    bullet {
		# Indent for bullet, but not the first line. This is
		# prefixed by the bullet itself.
		set thebullet [Bullet?]
	    }
	    enum {
		#puts_stderr EB

		set n [EnumId]
		set thebullet [string map [list % $n] [Bullet?]]

		#puts_stderr "E $n | $thebullet |"
	    }
	}

	set blank [WPrefix?]

	#puts_stderr B.(($lt))
	#puts_stderr B.(($thebullet))[string length $thebullet],[string length [DeIce $thebullet]]
	#puts_stderr B.(([Dots $blank]))

	if {[string length [DeIce $thebullet]] >= [string length $blank]} {
	    # The item's bullet is longer than the space for indenting.
	    # Put bullet and text on separate lines, indent text in full.

	    #puts_stderr B.DROP

	    set text "$thebullet\n[Indent $text $blank]"
	} else {
	    # The item's bullet fits into the space for
	    # indenting. Make hanging indent of text and place the
	    # bullet in front of the first line, with suitable partial
	    # spacing.

	    #puts_stderr B.SAME
	    #puts_stderr B.(([Dots [ReHead $blank $thebullet]]))

	    set text [Indent1 $text [ReHead $blank $thebullet] $blank]
	}
    }

    if {$lm} {
	#puts_stderr "LMA $lm"
	set text [Indent $text [Blank $lm]]
    }

    #puts_stderr "FIN (($text))"
    
    lappend lines $text
    return
}

# # ## ### ##### ########
return