]> gitweb.factorcode.org Git - factor.git/blob - basis/prettyprint/sections/sections.factor
cleanup some use of with-scope.
[factor.git] / basis / prettyprint / sections / sections.factor
1 ! Copyright (C) 2003, 2010 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: arrays generic hashtables io kernel math assocs
4 namespaces make sequences strings io.styles vectors words
5 prettyprint.config splitting classes continuations
6 accessors sets vocabs.parser combinators vocabs
7 classes.maybe ;
8 FROM: namespaces => set ;
9 IN: prettyprint.sections
10
11 ! State
12 SYMBOL: position
13 SYMBOL: recursion-check
14 SYMBOL: pprinter-stack
15
16 ! We record vocabs of all words
17 SYMBOL: pprinter-in
18 SYMBOL: pprinter-use
19
20 TUPLE: pprinter last-newline line-count indent ;
21
22 : <pprinter> ( -- pprinter ) 0 1 0 pprinter boa ;
23
24 : (record-vocab) ( vocab -- )
25     dup pprinter-in get dup [ vocab-name ] when =
26     [ drop ] [ pprinter-use get conjoin ] if ;
27
28 GENERIC: vocabulary-name ( obj -- string )
29
30 M: word vocabulary-name
31     vocabulary>> ;
32
33 M: maybe vocabulary-name
34     class>> vocabulary>> ;
35
36 : record-vocab ( word -- )
37     vocabulary-name {
38         { f [ ] }
39         { "syntax" [ ] }
40         [ (record-vocab) ]
41     } case ;
42
43 ! Utility words
44 : line-limit? ( -- ? )
45     line-limit get dup [ pprinter get line-count>> <= ] when ;
46
47 : do-indent ( -- ) pprinter get indent>> CHAR: \s <string> write ;
48
49 : fresh-line ( n -- )
50     dup pprinter get last-newline>> = [
51         drop
52     ] [
53         pprinter get last-newline<<
54         line-limit? [
55             "..." write pprinter get return
56         ] when
57         pprinter get [ 1 + ] change-line-count drop
58         nl do-indent
59     ] if ;
60
61 : text-fits? ( len -- ? )
62     margin get
63     [ drop t ] [ [ pprinter get indent>> + ] dip <= ] if-zero ;
64
65 ! break only if position margin 2 / >
66 SYMBOL: soft
67
68 ! always breaks
69 SYMBOL: hard
70
71 ! Section protocol
72 GENERIC: section-fits? ( section -- ? )
73
74 GENERIC: short-section ( section -- )
75
76 GENERIC: long-section ( section -- )
77
78 GENERIC: indent-section? ( section -- ? )
79
80 GENERIC: unindent-first-line? ( section -- ? )
81
82 GENERIC: newline-after? ( section -- ? )
83
84 GENERIC: short-section? ( section -- ? )
85
86 ! Sections
87 TUPLE: section
88 start end
89 start-group? end-group?
90 style overhang ;
91
92 : new-section ( length class -- section )
93     new
94         position get >>start
95         swap position [ + ] change
96         position get >>end
97         0 >>overhang ; inline
98
99 M: section section-fits? ( section -- ? )
100     [ end>> pprinter get last-newline>> - ]
101     [ overhang>> ] bi
102     + text-fits? ;
103
104 M: section indent-section? drop f ;
105
106 M: section unindent-first-line? drop f ;
107
108 M: section newline-after? drop f ;
109
110 M: object short-section? section-fits? ;
111
112 : indent+ ( section n -- )
113     swap indent-section? [
114         pprinter get [ + ] change-indent drop
115     ] [ drop ] if ;
116
117 : <indent ( section -- ) tab-size get indent+ ;
118
119 : indent> ( section -- ) tab-size get neg indent+ ;
120
121 : <fresh-line ( section -- )
122     start>> fresh-line ;
123
124 : fresh-line> ( section -- )
125     dup newline-after? [ end>> fresh-line ] [ drop ] if ;
126
127 : <long-section ( section -- )
128     dup unindent-first-line?
129     [ dup <fresh-line <indent ] [ dup <indent <fresh-line ] if ;
130
131 : long-section> ( section -- )
132     dup indent> fresh-line> ;
133
134 : pprint-section ( section -- )
135     dup short-section? [
136         dup style>> [ short-section ] with-style
137     ] [
138         [ <long-section ]
139         [ dup style>> [ long-section ] with-style ]
140         [ long-section> ]
141         tri
142     ] if ;
143
144 ! Break section
145 TUPLE: line-break < section type ;
146
147 : <line-break> ( type -- section )
148     0 \ line-break new-section
149         swap >>type ;
150
151 M: line-break short-section drop ;
152
153 M: line-break long-section drop ;
154
155 ! Block sections
156 TUPLE: block < section sections ;
157
158 : new-block ( style class -- block )
159     0 swap new-section
160         V{ } clone >>sections
161         swap >>style ; inline
162
163 : <block> ( style -- block )
164     block new-block ;
165
166 : pprinter-block ( -- block ) pprinter-stack get last ;
167
168 : add-section ( section -- )
169     pprinter-block sections>> push ;
170
171 : last-section ( -- section )
172     pprinter-block sections>>
173     [ line-break? not ] find-last nip ;
174
175 : start-group ( -- )
176     last-section t >>start-group? drop ;
177
178 : end-group ( -- )
179     last-section t >>end-group? drop ;
180
181 : advance ( section -- )
182     [ start>> pprinter get last-newline>> = not ]
183     [ short-section? ] bi
184     and [ bl ] when ;
185
186 : add-line-break ( type -- ) [ <line-break> add-section ] when* ;
187
188 M: block section-fits? ( section -- ? )
189     line-limit? [ drop t ] [ call-next-method ] if ;
190
191 : pprint-sections ( block advancer -- )
192     [
193         sections>> [ line-break? not ] filter
194         unclip-slice pprint-section
195     ] dip
196     [ [ pprint-section ] bi ] curry each ; inline
197
198 M: block short-section ( block -- )
199     [ advance ] pprint-sections ;
200
201 : do-break ( break -- )
202     [ ]
203     [ type>> hard eq? ]
204     [ end>> pprinter get last-newline>> - margin get 2/ > ] tri
205     or [ <fresh-line ] [ drop ] if ;
206
207 : empty-block? ( block -- ? ) sections>> empty? ;
208
209 : if-nonempty ( block quot -- )
210     [ dup empty-block? [ drop ] ] dip if ; inline
211
212 : (<block) ( block -- ) pprinter-stack get push ;
213
214 : <block ( -- ) f <block> (<block) ;
215
216 : <object ( obj -- ) presented associate <block> (<block) ;
217
218 ! Text section
219 TUPLE: text < section string ;
220
221 : <text> ( string style -- text )
222     over length 1 + \ text new-section
223         swap >>style
224         swap >>string ;
225
226 M: text short-section string>> write ;
227
228 M: text long-section short-section ;
229
230 : styled-text ( string style -- ) <text> add-section ;
231
232 : text ( string -- ) H{ } styled-text ;
233
234 ! Inset section
235 TUPLE: inset < block narrow? ;
236
237 : <inset> ( narrow? -- block )
238     H{ } inset new-block
239         2 >>overhang
240         swap >>narrow? ;
241
242 M: inset long-section
243     dup narrow?>> [
244         [ <fresh-line ] pprint-sections
245     ] [
246         call-next-method
247     ] if ;
248
249 M: inset indent-section? drop t ;
250
251 M: inset newline-after? drop t ;
252
253 : <inset ( narrow? -- ) <inset> (<block) ;
254
255 ! Flow section
256 TUPLE: flow < block ;
257
258 : <flow> ( -- block )
259     H{ } flow new-block ;
260
261 M: flow short-section? ( section -- ? )
262     #! If we can make room for this entire block by inserting
263     #! a newline, do it; otherwise, don't bother, print it as
264     #! a short section
265     [ section-fits? ]
266     [ [ end>> ] [ start>> ] bi - text-fits? not ] bi
267     or ;
268
269 : <flow ( -- ) <flow> (<block) ;
270
271 ! Colon definition section
272 TUPLE: colon < block ;
273
274 : <colon> ( -- block )
275     H{ } colon new-block ;
276
277 M: colon long-section short-section ;
278
279 M: colon indent-section? drop t ;
280
281 M: colon unindent-first-line? drop t ;
282
283 : <colon ( -- ) <colon> (<block) ;
284
285 : save-end-position ( block -- )
286     position get >>end drop ;
287
288 : block> ( -- )
289     pprinter-stack get pop
290     [ [ save-end-position ] [ add-section ] bi ] if-nonempty ;
291
292 : do-pprint ( block -- )
293     <pprinter> pprinter [
294         [
295             dup style>> [
296                 [
297                     short-section
298                 ] curry with-return
299             ] with-nesting
300         ] if-nonempty
301     ] with-variable ;
302
303 ! Long section layout algorithm
304 : chop-break ( seq -- seq )
305     dup last line-break? [ but-last-slice chop-break ] when ;
306
307 SYMBOL: prev
308 SYMBOL: next
309
310 : split-groups ( ? -- ) [ t , ] when ;
311
312 : split-before ( section -- )
313     [ start-group?>> prev get [ end-group?>> ] [ t ] if* and ]
314     [ flow? prev get flow? not and ]
315     bi or split-groups ;
316
317 : split-after ( section -- )
318     [ end-group?>> ] [ f ] if* split-groups ;
319
320 : group-flow ( seq -- newseq )
321     [
322         dup length iota [
323             2dup 1 - swap ?nth prev set
324             2dup 1 + swap ?nth next set
325             swap nth dup split-before dup , split-after
326         ] with each
327     ] { } make { t } split harvest ;
328
329 : break-group? ( seq -- ? )
330     [ first section-fits? ] [ last section-fits? not ] bi and ;
331
332 : ?break-group ( seq -- )
333     dup break-group? [ first <fresh-line ] [ drop ] if ;
334
335 M: block long-section ( block -- )
336     [
337         sections>> chop-break group-flow [
338             dup ?break-group [
339                 dup line-break? [
340                     do-break
341                 ] [
342                     [ advance ] [ pprint-section ] bi
343                 ] if
344             ] each
345         ] each
346     ] if-nonempty ;
347
348 : pprinter-manifest ( -- manifest )
349     <manifest>
350     [ [ pprinter-use get keys >vector ] dip search-vocabs<< ]
351     [ [ pprinter-in get ] dip current-vocab<< ]
352     [ ]
353     tri ;
354
355 : make-pprint ( obj quot -- block manifest )
356     [
357         0 position ,,
358         H{ } clone pprinter-use ,,
359         V{ } clone recursion-check ,,
360         V{ } clone pprinter-stack ,,
361     ] H{ } make [
362         over <object
363         call
364         pprinter-block
365         pprinter-manifest
366     ] with-variables ; inline
367
368 : with-pprint ( obj quot -- )
369     make-pprint drop do-pprint ; inline