forked from dotnet/fsharp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlayout.fs
More file actions
454 lines (404 loc) · 20.2 KB
/
Copy pathlayout.fs
File metadata and controls
454 lines (404 loc) · 20.2 KB
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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
module internal Microsoft.FSharp.Compiler.Layout
open System
open System.Collections.Generic
open System.IO
open Internal.Utilities.StructuredFormat
open Microsoft.FSharp.Core.Printf
#nowarn "62" // This construct is for ML compatibility.
type layout = Internal.Utilities.StructuredFormat.Layout
type TaggedText = Internal.Utilities.StructuredFormat.TaggedText
let spaces n = new String(' ',n)
//--------------------------------------------------------------------------
// INDEX: support
//--------------------------------------------------------------------------
let rec juxtLeft = function
| ObjLeaf (jl,_text,_jr) -> jl
| Leaf (jl,_text,_jr) -> jl
| Node (jl,_l,_jm,_r,_jr,_joint) -> jl
| Attr (_tag,_attrs,l) -> juxtLeft l
let rec juxtRight = function
| ObjLeaf (_jl,_text,jr) -> jr
| Leaf (_jl,_text,jr) -> jr
| Node (_jl,_l,_jm,_r,jr,_joint) -> jr
| Attr (_tag,_attrs,l) -> juxtRight l
// NOTE: emptyL might be better represented as a constructor, so then (Sep"") would have true meaning
let emptyL = Leaf (true,TaggedText.Text "",true)
let isEmptyL = function Leaf(true,tag,true) when tag.Value = "" -> true | _ -> false
let mkNode l r joint =
if isEmptyL l then r else
if isEmptyL r then l else
let jl = juxtLeft l
let jm = juxtRight l || juxtLeft r
let jr = juxtRight r
Node(jl,l,jm,r,jr,joint)
//--------------------------------------------------------------------------
//INDEX: constructors
//--------------------------------------------------------------------------
let wordL (str:TaggedText) = Leaf (false,str,false)
let sepL (str:TaggedText) = Leaf (true ,str,true)
let rightL (str:TaggedText) = Leaf (true ,str,false)
let leftL (str:TaggedText) = Leaf (false,str,true)
module TaggedTextOps =
let tagActivePatternCase = Internal.Utilities.StructuredFormat.TaggedText.ActivePatternCase
let tagActivePatternResult = Internal.Utilities.StructuredFormat.TaggedText.ActivePatternResult
let tagAlias = Internal.Utilities.StructuredFormat.TaggedTextOps.tagAlias
let tagClass = Internal.Utilities.StructuredFormat.TaggedTextOps.tagClass
let tagUnion = Internal.Utilities.StructuredFormat.TaggedText.Union
let tagUnionCase = Internal.Utilities.StructuredFormat.TaggedTextOps.tagUnionCase
let tagDelegate = Internal.Utilities.StructuredFormat.TaggedTextOps.tagDelegate
let tagEnum = Internal.Utilities.StructuredFormat.TaggedTextOps.tagEnum
let tagEvent = Internal.Utilities.StructuredFormat.TaggedTextOps.tagEvent
let tagField = Internal.Utilities.StructuredFormat.TaggedTextOps.tagField
let tagInterface = Internal.Utilities.StructuredFormat.TaggedTextOps.tagInterface
let tagKeyword = Internal.Utilities.StructuredFormat.TaggedTextOps.tagKeyword
let tagLineBreak = Internal.Utilities.StructuredFormat.TaggedTextOps.tagLineBreak
let tagLocal = Internal.Utilities.StructuredFormat.TaggedTextOps.tagLocal
let tagRecord = Internal.Utilities.StructuredFormat.TaggedTextOps.tagRecord
let tagRecordField = Internal.Utilities.StructuredFormat.TaggedTextOps.tagRecordField
let tagMethod = Internal.Utilities.StructuredFormat.TaggedTextOps.tagMethod
let tagMember = Internal.Utilities.StructuredFormat.TaggedText.Member
let tagModule = Internal.Utilities.StructuredFormat.TaggedTextOps.tagModule
let tagModuleBinding = Internal.Utilities.StructuredFormat.TaggedTextOps.tagModuleBinding
let tagNamespace = Internal.Utilities.StructuredFormat.TaggedTextOps.tagNamespace
let tagNumericLiteral = Internal.Utilities.StructuredFormat.TaggedTextOps.tagNumericLiteral
let tagOperator = Internal.Utilities.StructuredFormat.TaggedTextOps.tagOperator
let tagParameter = Internal.Utilities.StructuredFormat.TaggedTextOps.tagParameter
let tagProperty = Internal.Utilities.StructuredFormat.TaggedTextOps.tagProperty
let tagSpace = Internal.Utilities.StructuredFormat.TaggedTextOps.tagSpace
let tagStringLiteral = Internal.Utilities.StructuredFormat.TaggedTextOps.tagStringLiteral
let tagStruct = Internal.Utilities.StructuredFormat.TaggedTextOps.tagStruct
let tagTypeParameter = Internal.Utilities.StructuredFormat.TaggedTextOps.tagTypeParameter
let tagText = Internal.Utilities.StructuredFormat.TaggedTextOps.tagText
let tagPunctuation = Internal.Utilities.StructuredFormat.TaggedTextOps.tagPunctuation
let tagUnknownEntity = Internal.Utilities.StructuredFormat.TaggedText.UnknownEntity
let tagUnknownType = Internal.Utilities.StructuredFormat.TaggedText.UnknownType
module Literals =
// common tagged literals
let lineBreak = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.lineBreak
let space = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.space
let comma = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.comma
let semicolon = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.semicolon
let leftParen = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.leftParen
let rightParen = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.rightParen
let leftBracket = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.leftBracket
let rightBracket = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.rightBracket
let leftBrace = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.leftBrace
let rightBrace = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.rightBrace
let equals = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.equals
let arrow = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.arrow
let questionMark = Internal.Utilities.StructuredFormat.TaggedTextOps.Literals.questionMark
let dot = tagPunctuation "."
let leftAngle = tagPunctuation "<"
let rightAngle = tagPunctuation ">"
let star = tagOperator "*"
let colon = tagPunctuation ":"
let minus = tagPunctuation "-"
let keywordNew = tagKeyword "new"
let leftBracketAngle = tagPunctuation "[<"
let rightBracketAngle = tagPunctuation ">]"
let structUnit = tagStruct "unit"
let keywordStatic = tagKeyword "static"
let keywordMember = tagKeyword "member"
let keywordVal = tagKeyword "val"
let keywordEvent = tagKeyword "event"
let keywordWith = tagKeyword "with"
let keywordSet = tagKeyword "set"
let keywordGet = tagKeyword "get"
let keywordTrue = tagKeyword "true"
let keywordFalse = tagKeyword "false"
let bar = tagPunctuation "|"
let keywordStruct = tagKeyword "struct"
let keywordInherit = tagKeyword "inherit"
let keywordEnd = tagKeyword "end"
let keywordNested = tagKeyword "nested"
let keywordType = tagKeyword "type"
let keywordDelegate = tagKeyword "delegate"
let keywordOf = tagKeyword "of"
let keywordInternal = tagKeyword "internal"
let keywordPrivate = tagKeyword "private"
let keywordAbstract = tagKeyword "abstract"
let keywordOverride = tagKeyword "override"
let keywordEnum = tagKeyword "enum"
let leftBracketBar = tagPunctuation "[|"
let rightBracketBar = tagPunctuation "|]"
let keywordTypeof = tagKeyword "typeof"
let keywordTypedefof = tagKeyword "typedefof"
open TaggedTextOps
module SepL =
let dot = sepL Literals.dot
let star = sepL Literals.star
let colon = sepL Literals.colon
let questionMark = sepL Literals.questionMark
let leftParen = sepL Literals.leftParen
let comma = sepL Literals.comma
let space = sepL Literals.space
let leftBracket = sepL Literals.leftBracket
let leftAngle = sepL Literals.leftAngle
let lineBreak = sepL Literals.lineBreak
let rightParen = sepL Literals.rightParen
module WordL =
let arrow = wordL Literals.arrow
let star = wordL Literals.star
let colon = wordL Literals.colon
let equals = wordL Literals.equals
let keywordNew = wordL Literals.keywordNew
let structUnit = wordL Literals.structUnit
let keywordStatic = wordL Literals.keywordStatic
let keywordMember = wordL Literals.keywordMember
let keywordVal = wordL Literals.keywordVal
let keywordEvent = wordL Literals.keywordEvent
let keywordWith = wordL Literals.keywordWith
let keywordSet = wordL Literals.keywordSet
let keywordGet = wordL Literals.keywordGet
let keywordTrue = wordL Literals.keywordTrue
let keywordFalse = wordL Literals.keywordFalse
let bar = wordL Literals.bar
let keywordStruct = wordL Literals.keywordStruct
let keywordInherit = wordL Literals.keywordInherit
let keywordEnd = wordL Literals.keywordEnd
let keywordNested = wordL Literals.keywordNested
let keywordType = wordL Literals.keywordType
let keywordDelegate = wordL Literals.keywordDelegate
let keywordOf = wordL Literals.keywordOf
let keywordInternal = wordL Literals.keywordInternal
let keywordPrivate = wordL Literals.keywordPrivate
let keywordAbstract = wordL Literals.keywordAbstract
let keywordOverride = wordL Literals.keywordOverride
let keywordEnum = wordL Literals.keywordEnum
module LeftL =
let leftParen = leftL Literals.leftParen
let questionMark = leftL Literals.questionMark
let colon = leftL Literals.colon
let leftBracketAngle = leftL Literals.leftBracketAngle
let leftBracketBar = leftL Literals.leftBracketBar
let keywordTypeof = leftL Literals.keywordTypeof
let keywordTypedefof = leftL Literals.keywordTypedefof
module RightL =
let comma = rightL Literals.comma
let rightParen = rightL Literals.rightParen
let colon = rightL Literals.colon
let rightBracket = rightL Literals.rightBracket
let rightAngle = rightL Literals.rightAngle
let rightBracketAngle = rightL Literals.rightBracketAngle
let rightBracketBar = rightL Literals.rightBracketBar
let aboveL l r = mkNode l r (Broken 0)
let tagAttrL str attrs ly = Attr (str,attrs,ly)
//--------------------------------------------------------------------------
//INDEX: constructors derived
//--------------------------------------------------------------------------
let apply2 f l r = if isEmptyL l then r else
if isEmptyL r then l else f l r
let (^^) l r = mkNode l r (Unbreakable)
let (++) l r = mkNode l r (Breakable 0)
let (--) l r = mkNode l r (Breakable 1)
let (---) l r = mkNode l r (Breakable 2)
let (----) l r = mkNode l r (Breakable 3)
let (-----) l r = mkNode l r (Breakable 4)
let (@@) l r = apply2 (fun l r -> mkNode l r (Broken 0)) l r
let (@@-) l r = apply2 (fun l r -> mkNode l r (Broken 1)) l r
let (@@--) l r = apply2 (fun l r -> mkNode l r (Broken 2)) l r
let tagListL tagger = function
| [] -> emptyL
| [x] -> x
| x::xs ->
let rec process' prefixL = function
| [] -> prefixL
| y::ys -> process' ((tagger prefixL) ++ y) ys in
process' x xs
let commaListL x = tagListL (fun prefixL -> prefixL ^^ rightL Literals.comma) x
let semiListL x = tagListL (fun prefixL -> prefixL ^^ rightL Literals.semicolon) x
let spaceListL x = tagListL (fun prefixL -> prefixL) x
let sepListL x y = tagListL (fun prefixL -> prefixL ^^ x) y
let bracketL l = leftL Literals.leftParen ^^ l ^^ rightL Literals.rightParen
let tupleL xs = bracketL (sepListL (sepL Literals.comma) xs)
let aboveListL = function
| [] -> emptyL
| [x] -> x
| x::ys -> List.fold (fun pre y -> pre @@ y) x ys
let optionL xL = function
| None -> wordL (tagUnionCase "None")
| Some x -> wordL (tagUnionCase "Some") -- (xL x)
let listL xL xs = leftL Literals.leftBracket ^^ sepListL (sepL Literals.semicolon) (List.map xL xs) ^^ rightL Literals.rightBracket
//--------------------------------------------------------------------------
//INDEX: breaks v2
//--------------------------------------------------------------------------
// A very quick implementation of break stack.
type breaks = Breaks of
/// pos of next free slot
int *
/// pos of next possible "outer" break - OR - outer=next if none possible
int *
/// stack of savings, -ve means it has been broken
int array
// next is next slot to push into - aka size of current occupied stack.
// outer counts up from 0, and is next slot to break if break forced.
// - if all breaks forced, then outer=next.
// - popping under these conditions needs to reduce outer and next.
let chunkN = 400
let breaks0 () = Breaks(0,0,Array.create chunkN 0)
let pushBreak saving (Breaks(next,outer,stack)) =
let stack = if next = stack.Length then
Array.append stack (Array.create chunkN 0) (* expand if full *)
else
stack
stack.[next] <- saving
Breaks(next+1,outer,stack)
let popBreak (Breaks(next,outer,stack)) =
if next=0 then raise (Failure "popBreak: underflow")
let topBroke = stack.[next-1] < 0
let outer = if outer=next then outer-1 else outer (* if all broken, unwind *)
let next = next - 1
Breaks(next,outer,stack),topBroke
let forceBreak (Breaks(next,outer,stack)) =
if outer=next then
(* all broken *)
None
else
let saving = stack.[outer]
stack.[outer] <- -stack.[outer]
let outer = outer+1
Some (Breaks(next,outer,stack),saving)
let squashTo maxWidth layout =
// breaks = break context, can force to get indentation savings.
// pos = current position in line
// layout = to fit
//------
// returns:
// breaks
// layout - with breaks put in to fit it.
// pos - current pos in line = rightmost position of last line of block.
// offset - width of last line of block
// NOTE: offset <= pos -- depending on tabbing of last block
let rec fit breaks (pos,layout) =
(*printf "\n\nCalling pos=%d layout=[%s]\n" pos (showL layout)*)
let breaks,layout,pos,offset =
match layout with
| ObjLeaf _ -> failwith "ObjLeaf should not appear here"
| Attr (tag,attrs,l) ->
let breaks,layout,pos,offset = fit breaks (pos,l)
let layout = Attr (tag,attrs,layout)
breaks,layout,pos,offset
| Leaf (_jl,text,_jr) ->
let textWidth = text.Length
let rec fitLeaf breaks pos =
if pos + textWidth <= maxWidth then
breaks,layout,pos + textWidth,textWidth (* great, it fits *)
else
match forceBreak breaks with
None -> (breaks,layout,pos + textWidth,textWidth (* tough, no more breaks *))
| Some (breaks,saving) -> (let pos = pos - saving in fitLeaf breaks pos)
fitLeaf breaks pos
| Node (jl,l,jm,r,jr,joint) ->
let mid = if jm then 0 else 1
match joint with
| Unbreakable ->
let breaks,l,pos,offsetl = fit breaks (pos,l) (* fit left *)
let pos = pos + mid (* fit space if juxt says so *)
let breaks,r,pos,offsetr = fit breaks (pos,r) (* fit right *)
breaks,Node (jl,l,jm,r,jr,Unbreakable),pos,offsetl + mid + offsetr
| Broken indent ->
let breaks,l,pos,offsetl = fit breaks (pos,l) (* fit left *)
let pos = pos - offsetl + indent (* broken so - offset left + indent *)
let breaks,r,pos,offsetr = fit breaks (pos,r) (* fit right *)
breaks,Node (jl,l,jm,r,jr,Broken indent),pos,indent + offsetr
| Breakable indent ->
let breaks,l,pos,offsetl = fit breaks (pos,l) (* fit left *)
(* have a break possibility, with saving *)
let saving = offsetl + mid - indent
let pos = pos + mid
if saving>0 then
let breaks = pushBreak saving breaks
let breaks,r,pos,offsetr = fit breaks (pos,r)
let breaks,broken = popBreak breaks
if broken then
breaks,Node (jl,l,jm,r,jr,Broken indent) ,pos,indent + offsetr
else
breaks,Node (jl,l,jm,r,jr,Breakable indent),pos,offsetl + mid + offsetr
else
(* actually no saving so no break *)
let breaks,r,pos,offsetr = fit breaks (pos,r)
breaks,Node (jl,l,jm,r,jr,Breakable indent) ,pos,offsetl + mid + offsetr
(*printf "\nDone: pos=%d offset=%d" pos offset*)
breaks,layout,pos,offset
let breaks = breaks0 ()
let pos = 0
let _breaks,layout,_pos,_offset = fit breaks (pos,layout)
layout
//--------------------------------------------------------------------------
//INDEX: LayoutRenderer
//--------------------------------------------------------------------------
type LayoutRenderer<'a,'b> =
abstract Start : unit -> 'b
abstract AddText : 'b -> TaggedText -> 'b
abstract AddBreak : 'b -> int -> 'b
abstract AddTag : 'b -> string * (string * string) list * bool -> 'b
abstract Finish : 'b -> 'a
let renderL (rr: LayoutRenderer<_,_>) layout =
let rec addL z pos i layout k =
match layout with
| ObjLeaf _ -> failwith "ObjLeaf should never apper here"
(* pos is tab level *)
| Leaf (_,text,_) ->
k(rr.AddText z text,i + text.Length)
| Node (_,l,_,r,_,Broken indent) ->
addL z pos i l <|
fun (z,_i) ->
let z,i = rr.AddBreak z (pos+indent),(pos+indent)
addL z (pos+indent) i r k
| Node (_,l,jm,r,_,_) ->
addL z pos i l <|
fun (z, i) ->
let z,i = if jm then z,i else rr.AddText z Literals.space, i+1
let pos = i
addL z pos i r k
| Attr (tag,attrs,l) ->
let z = rr.AddTag z (tag,attrs,true)
addL z pos i l <|
fun (z, i) ->
let z = rr.AddTag z (tag,attrs,false)
k(z,i)
let pos = 0
let z,i = rr.Start(),0
let z,_i = addL z pos i layout id
rr.Finish z
/// string render
let stringR =
{ new LayoutRenderer<string,string list> with
member x.Start () = []
member x.AddText rstrs text = text.Value::rstrs
member x.AddBreak rstrs n = (spaces n) :: "\n" :: rstrs
member x.AddTag z (_,_,_) = z
member x.Finish rstrs = String.Join("",Array.ofList (List.rev rstrs)) }
type NoState = NoState
type NoResult = NoResult
/// string render
let taggedTextListR collector =
{ new LayoutRenderer<NoResult, NoState> with
member x.Start () = NoState
member x.AddText z text = collector text; z
member x.AddBreak rstrs n = collector Literals.lineBreak; collector (tagSpace(spaces n)); rstrs
member x.AddTag z (_,_,_) = z
member x.Finish rstrs = NoResult }
/// channel LayoutRenderer
let channelR (chan:TextWriter) =
{ new LayoutRenderer<NoResult,NoState> with
member r.Start () = NoState
member r.AddText z s = chan.Write s.Value; z
member r.AddBreak z n = chan.WriteLine(); chan.Write (spaces n); z
member r.AddTag z (tag,attrs,start) = z
member r.Finish z = NoResult }
/// buffer render
let bufferR os =
{ new LayoutRenderer<NoResult,NoState> with
member r.Start () = NoState
member r.AddText z s = bprintf os "%s" s.Value; z
member r.AddBreak z n = bprintf os "\n"; bprintf os "%s" (spaces n); z
member r.AddTag z (tag,attrs,start) = z
member r.Finish z = NoResult }
//--------------------------------------------------------------------------
//INDEX: showL, outL are most common
//--------------------------------------------------------------------------
let showL layout = renderL stringR layout
let outL (chan:TextWriter) layout = renderL (channelR chan) layout |> ignore
let bufferL os layout = renderL (bufferR os) layout |> ignore