1 ! Copyright (C) 2003, 2010 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors classes.maybe combinators
4 combinators.short-circuit continuations hashtables io io.styles
5 kernel make math namespaces prettyprint.config sequences sets
6 splitting strings vocabs vocabs.parser words ;
7 IN: prettyprint.sections
11 SYMBOL: recursion-check
12 SYMBOL: pprinter-stack
14 ! We record vocabs of all words
18 TUPLE: pprinter last-newline line-count indent ;
20 : <pprinter> ( -- pprinter ) 0 1 0 pprinter boa ;
22 : (record-vocab) ( vocab -- )
23 dup pprinter-in get dup [ vocab-name ] when =
24 [ drop ] [ pprinter-use get adjoin ] if ;
26 GENERIC: vocabulary-name ( obj -- string )
28 M: word vocabulary-name
31 M: maybe vocabulary-name
32 class>> vocabulary>> ;
34 : record-vocab ( word -- )
42 : line-limit? ( -- ? )
43 line-limit get dup [ pprinter get line-count>> <= ] when ;
45 : do-indent ( -- ) pprinter get indent>> CHAR: \s <string> write ;
48 pprinter get 2dup last-newline>> = [
55 [ 1 + ] change-line-count drop
59 : text-fits? ( len -- ? )
63 [ pprinter get indent>> + ] dip <=
66 ! break only if position margin 2 / >
73 GENERIC: section-fits? ( section -- ? )
75 GENERIC: short-section ( section -- )
77 GENERIC: long-section ( section -- )
79 GENERIC: indent-section? ( section -- ? )
81 GENERIC: unindent-first-line? ( section -- ? )
83 GENERIC: newline-after? ( section -- ? )
85 GENERIC: short-section? ( section -- ? )
90 start-group? end-group?
93 : new-section ( length class -- section )
100 0 >>overhang ; inline
102 M: section section-fits? ( section -- ? )
103 [ end>> 1 - pprinter get last-newline>> - ]
104 [ overhang>> ] bi + text-fits? ;
106 M: section indent-section? drop f ;
108 M: section unindent-first-line? drop f ;
110 M: section newline-after? drop f ;
112 M: object short-section? section-fits? ;
114 : indent+ ( section n -- )
115 swap indent-section? [
116 pprinter get [ + ] change-indent drop
119 : <indent ( section -- ) tab-size get indent+ ;
121 : indent> ( section -- ) tab-size get neg indent+ ;
123 : <fresh-line ( section -- )
126 : fresh-line> ( section -- )
127 dup newline-after? [ end>> fresh-line ] [ drop ] if ;
129 : <long-section ( section -- )
130 dup unindent-first-line?
131 [ dup <fresh-line <indent ] [ dup <indent <fresh-line ] if ;
133 : long-section> ( section -- )
134 dup indent> fresh-line> ;
136 : pprint-section ( section -- )
138 dup style>> [ short-section ] with-style
141 [ dup style>> [ long-section ] with-style ]
147 TUPLE: line-break < section type ;
149 : <line-break> ( type -- section )
150 0 line-break new-section
153 M: line-break short-section drop ;
155 M: line-break long-section drop ;
158 TUPLE: block < section sections ;
160 : new-block ( class -- block )
162 V{ } clone >>sections ; inline
164 : <block> ( style -- block )
168 : pprinter-block ( -- block ) pprinter-stack get last ;
170 : add-section ( section -- )
171 pprinter-block sections>> push ;
173 : last-section ( -- section )
174 pprinter-block sections>>
175 [ line-break? not ] find-last nip ;
178 last-section t >>start-group? drop ;
181 last-section t >>end-group? drop ;
183 : advance ( section -- )
185 [ start>> pprinter get last-newline>> = not ]
189 : add-line-break ( type -- ) [ <line-break> add-section ] when* ;
191 M: block section-fits? ( section -- ? )
192 line-limit? [ drop t ] [ call-next-method ] if ;
194 : pprint-sections ( block advancer -- )
196 sections>> [ line-break? ] reject
197 unclip-slice pprint-section
199 [ [ pprint-section ] bi ] curry each ; inline
201 M: block short-section ( block -- )
202 [ advance ] pprint-sections ;
204 : do-break ( break -- )
207 [ end>> pprinter get last-newline>> - margin get 2/ > ] tri
208 or [ <fresh-line ] [ drop ] if ;
210 : empty-block? ( block -- ? ) sections>> empty? ;
212 : unless-empty-block ( block quot: ( block -- ) -- )
213 [ dup empty-block? [ drop ] ] dip if ; inline
215 : (<block) ( block -- ) pprinter-stack get push ;
217 : <block ( -- ) f <block> (<block) ;
219 : <object ( obj -- ) presented associate <block> (<block) ;
222 TUPLE: text-section < section string ;
224 : <text> ( string style -- text )
225 over length 1 + text-section new-section
229 M: text-section short-section string>> write ;
231 M: text-section long-section short-section ;
233 : styled-text ( string style -- ) <text> add-section ;
235 : text ( string -- ) f styled-text ;
238 TUPLE: inset < block narrow? ;
240 : <inset> ( narrow? -- block )
245 M: inset long-section
247 [ <fresh-line ] pprint-sections
252 M: inset indent-section? drop t ;
254 M: inset newline-after? drop t ;
256 : <inset ( narrow? -- ) <inset> (<block) ;
259 TUPLE: flow < block ;
261 : <flow> ( -- block )
264 M: flow short-section? ( section -- ? )
265 ! If we can make room for this entire block by inserting
266 ! a newline, do it; otherwise, don't bother, print it as
270 [ [ end>> 1 - ] [ start>> ] bi - text-fits? not ]
273 : <flow ( -- ) <flow> (<block) ;
275 ! Colon definition section
276 TUPLE: colon < block ;
278 : <colon> ( -- block )
281 M: colon long-section short-section ;
283 M: colon indent-section? drop t ;
285 M: colon unindent-first-line? drop t ;
287 : <colon ( -- ) <colon> (<block) ;
289 : save-end-position ( block -- )
290 position get >>end drop ;
293 pprinter-stack get pop [
294 [ save-end-position ] [ add-section ] bi
295 ] unless-empty-block ;
297 : do-pprint ( block -- )
298 <pprinter> pprinter [
308 ! Long section layout algorithm
309 : chop-break ( seq -- seq )
310 [ dup last line-break? ] [ but-last-slice ] while ;
315 : split-groups ( ? -- ) [ t , ] when ;
317 : split-before ( section -- )
319 [ start-group?>> prev get [ end-group?>> and ] when* ]
320 [ flow? prev get flow? not and ]
323 : split-after ( section -- )
324 [ end-group?>> ] [ f ] if* split-groups ;
326 : group-flow ( seq -- newseq )
329 2dup 1 - swap ?nth prev namespaces:set
330 2dup 1 + swap ?nth next namespaces:set
331 swap nth dup split-before dup , split-after
333 ] { } make { t } split harvest ;
335 : break-group? ( seq -- ? )
336 { [ first section-fits? ] [ last section-fits? not ] } 1&& ;
338 : ?break-group ( seq -- )
339 dup break-group? [ first <fresh-line ] [ drop ] if ;
341 M: block long-section ( block -- )
343 sections>> chop-break group-flow [
348 [ advance ] [ pprint-section ] bi
352 ] unless-empty-block ;
354 : pprinter-manifest ( -- manifest )
356 pprinter-use get members V{ } like >>search-vocabs
357 pprinter-in get >>current-vocab ;
359 : make-pprint ( obj quot manifest? -- block manifest/f )
361 0 position namespaces:set
362 HS{ } clone pprinter-use namespaces:set
363 V{ } clone recursion-check namespaces:set
364 V{ } clone pprinter-stack namespaces:set
366 [ over <object call pprinter-block ] dip
367 [ pprinter-manifest ] [ f ] if
368 ] with-scope ; inline
370 : with-pprint ( obj quot -- )
371 f make-pprint drop do-pprint ; inline