@@ -28,7 +28,20 @@ let hardLine = LineBreak Hard
28
28
let softLine = LineBreak Soft
29
29
let literalLine = LineBreak Literal
30
30
let text s = Text s
31
- let concat l = Concat l
31
+
32
+ (* Optimization. We eagerly collapse and reduce whatever allocation we can *)
33
+ let rec _concat acc l =
34
+ match l with
35
+ | Text s1 :: Text s2 :: rest -> Text (s1 ^ s2) :: _concat acc rest
36
+ | Nil :: rest -> _concat acc rest
37
+ | Concat l2 :: rest -> _concat (_concat acc rest) l2 (* notice the order here *)
38
+ | x :: rest ->
39
+ let rest1 = _concat acc rest in
40
+ if rest1 == rest then l else x :: rest1
41
+ | [] -> acc
42
+
43
+ let concat l = Concat (_concat [] l)
44
+
32
45
let indent d = Indent d
33
46
let ifBreaks t f = IfBreaks {yes = t; no = f}
34
47
let lineSuffix d = LineSuffix d
@@ -118,39 +131,53 @@ let join ~sep docs =
118
131
| [x] -> List. rev (x::acc)
119
132
| x ::xs -> loop (sep::x::acc) sep xs
120
133
in
121
- Concat (loop [] sep docs)
134
+ concat(loop [] sep docs)
135
+
136
+ let fits w stack =
137
+ let width = ref w in
138
+ let result = ref None in
122
139
123
- let rec fits w doc = match doc with
124
- | _ when w < 0 -> false
125
- | [] -> true
126
- | (_ind , _mode , Text txt )::rest -> fits (w - String. length txt) rest
127
- | (ind , mode , Indent doc )::rest -> fits w ((ind + 2 , mode, doc)::rest)
128
- | (_ind , Flat, LineBreak break )::rest ->
129
- if break = Hard || break = Literal then true
130
- else
131
- let w = if break = Classic then w - 1 else w in
132
- fits w rest
133
- | (_ind , _mode , Nil)::rest -> fits w rest
134
- | (_ind , Break, LineBreak _break )::_rest -> true
135
- | (ind , mode , Group {shouldBreak = forceBreak ; doc} )::rest ->
136
- let mode = if forceBreak then Break else mode in
137
- fits w ((ind, mode, doc)::rest)
138
- | (ind , mode , IfBreaks {yes = breakDoc ; no = flatDoc } )::rest ->
139
- if mode = Break then
140
- fits w ((ind, mode, breakDoc)::rest)
141
- else
142
- fits w ((ind, mode, flatDoc)::rest)
143
- | (ind , mode , Concat docs )::rest ->
144
- let ops = List. map (fun doc -> (ind, mode, doc)) docs in
145
- fits w (List. append ops rest)
146
- (* | (_ind, _mode, Cursor)::rest -> fits w rest *)
147
- | (_ind , _mode , LineSuffix _ )::rest -> fits w rest
148
- | (_ind , _mode , BreakParent)::rest -> fits w rest
149
- | (ind , mode , CustomLayout (hd ::_ ))::rest ->
150
- (* TODO: if we have nested custom layouts, what we should do here? *)
151
- fits w ((ind, mode, hd)::rest)
152
- | (_ind , _mode , CustomLayout _ )::rest ->
153
- fits w rest
140
+ let rec calculate indent mode doc =
141
+ match mode, doc with
142
+ | _ when result.contents != None -> ()
143
+ | _ when width.contents < 0 -> result := Some false
144
+ | _, Nil
145
+ | _, LineSuffix _
146
+ | _ , BreakParent -> ()
147
+ | _ , Text txt -> width := width.contents - (String. length txt)
148
+ | _ , Indent doc -> calculate (indent + 2 ) mode doc
149
+ | Flat , LineBreak Hard
150
+ | Flat , LineBreak Literal -> result := Some true
151
+ | Flat , LineBreak Classic -> width := width.contents - 1
152
+ | Flat , LineBreak Soft -> ()
153
+ | Break , LineBreak _ -> result := Some true
154
+ | _ , Group {shouldBreak = true ; doc} -> calculate indent Break doc
155
+ | _ , Group {doc} -> calculate indent mode doc
156
+ | Break , IfBreaks {yes = breakDoc } -> calculate indent mode breakDoc
157
+ | Flat , IfBreaks {no = flatDoc } -> calculate indent mode flatDoc
158
+ | _ , Concat docs -> calculateConcat indent mode docs
159
+ | _ , CustomLayout (hd ::_ ) ->
160
+ (* TODO: if we have nested custom layouts, what we should do here? *)
161
+ calculate indent mode hd
162
+ | _ , CustomLayout [] -> ()
163
+ and calculateConcat indent mode docs =
164
+ if result.contents == None then (
165
+ match docs with
166
+ | [] -> ()
167
+ | doc ::rest ->
168
+ calculate indent mode doc;
169
+ calculateConcat indent mode rest
170
+ )
171
+ in
172
+ let rec calculateAll stack =
173
+ match result.contents, stack with
174
+ | Some r , _ -> r
175
+ | None , [] -> ! width > = 0
176
+ | None , (indent , mode , doc )::rest ->
177
+ calculate indent mode doc;
178
+ calculateAll rest
179
+ in
180
+ calculateAll stack
154
181
155
182
let toString ~width doc =
156
183
let doc = propagateForcedBreaks doc in
@@ -226,7 +253,7 @@ let toString ~width doc =
226
253
process ~pos: 0 [] (List. rev suffices)
227
254
end
228
255
in
229
- process ~pos: 0 [] [0 , Flat , doc];
256
+ process ~pos: 0 [] [( 0 , Flat , doc) ];
230
257
MiniBuffer. contents buffer
231
258
232
259
0 commit comments