Skip to content

Commit 1001364

Browse files
committed
WIP format
1 parent ae9ea30 commit 1001364

File tree

19 files changed

+6053
-842
lines changed

19 files changed

+6053
-842
lines changed

src/Common/Format.elm

Lines changed: 615 additions & 592 deletions
Large diffs are not rendered by default.

src/Common/Format/Bimap.elm

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module Common.Format.Bimap exposing
2+
( Bimap
3+
, fromList
4+
)
5+
6+
import Data.Map as Map exposing (Dict)
7+
8+
9+
type Bimap a b
10+
= Bimap (Dict String a b) (Dict String b a)
11+
12+
13+
fromList : (a -> String) -> (b -> String) -> List ( a, b ) -> Bimap a b
14+
fromList toComparableA toComparableB list =
15+
Bimap (Map.fromList toComparableA list)
16+
(Map.fromList toComparableB (List.map (\( a, b ) -> ( b, a )) list))

src/Common/Format/Box.elm

Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
module Common.Format.Box exposing
2+
( Line, identifier, keyword, punc, literal, row, space
3+
, blankLine, line, mustBreak, stack1, andThen
4+
, isLine, allSingles, lineLength
5+
, indent, prefix, addSuffix
6+
, render
7+
, Box(..), stack_
8+
)
9+
10+
{-| Ref.: `elm-format-lib/src/Box.hs`
11+
12+
@docs Line, identifier, keyword, punc, literal, row, space
13+
@docs Box(SingleLine, MustBreak), blankLine, line, mustBreak, stack', stack1, andThen
14+
@docs isLine, allSingles, lineLength
15+
@docs indent, prefix, addSuffix
16+
@docs render
17+
18+
-}
19+
20+
import Basics.Extra exposing (flip)
21+
import Prelude
22+
import Result.Extra as Result
23+
import Utils.Crash exposing (crash)
24+
import Utils.Main as Utils
25+
26+
27+
{-| A line is ALWAYS just one line.
28+
29+
Space is self-explanatory,
30+
Tab aligns to the nearest multiple of 4 spaces,
31+
Text brings any string into the data structure,
32+
Row joins more of these elements onto one line.
33+
34+
-}
35+
type Line
36+
= Text String
37+
| Row (List Line)
38+
| Space
39+
| Tab
40+
41+
42+
identifier : String -> Line
43+
identifier =
44+
Text
45+
46+
47+
keyword : String -> Line
48+
keyword =
49+
Text
50+
51+
52+
punc : String -> Line
53+
punc =
54+
Text
55+
56+
57+
literal : String -> Line
58+
literal =
59+
Text
60+
61+
62+
{-| join more Line elements into one
63+
-}
64+
row : List Line -> Line
65+
row =
66+
Row
67+
68+
69+
space : Line
70+
space =
71+
Space
72+
73+
74+
{-| Box contains Lines (at least one - can't be empty).
75+
Box either:
76+
77+
- can appear in the middle of a line
78+
(Stack someLine [], thus can be joined without problems), or
79+
- has to appear on its own
80+
(Stack someLine moreLines OR MustBreak someLine).
81+
82+
MustBreak is only used for `--` comments.
83+
84+
Stack contains two or more lines.
85+
86+
Sometimes (see `prefix`) the first line of Stack
87+
gets different treatment than the other lines.
88+
89+
-}
90+
type Box
91+
= SingleLine Line
92+
| Stack Line Line (List Line)
93+
| MustBreak Line
94+
95+
96+
blankLine : Box
97+
blankLine =
98+
line (literal "")
99+
100+
101+
line : Line -> Box
102+
line l =
103+
SingleLine l
104+
105+
106+
mustBreak : Line -> Box
107+
mustBreak l =
108+
MustBreak l
109+
110+
111+
stack_ : Box -> Box -> Box
112+
stack_ b1 b2 =
113+
let
114+
( line1first, line1rest ) =
115+
destructure b1
116+
117+
( line2first, line2rest ) =
118+
destructure b2
119+
in
120+
case line1rest ++ line2first :: line2rest of
121+
[] ->
122+
crash "the list will contain at least line2first"
123+
124+
first :: rest ->
125+
Stack line1first first rest
126+
127+
128+
andThen : List Box -> Box -> Box
129+
andThen rest first =
130+
List.foldl (flip stack_) first rest
131+
132+
133+
stack1 : List Box -> Box
134+
stack1 children =
135+
case children of
136+
[] ->
137+
crash "stack1: empty structure"
138+
139+
[ first ] ->
140+
first
141+
142+
boxes ->
143+
Utils.foldr1 stack_ boxes
144+
145+
146+
mapLines : (Line -> Line) -> Box -> Box
147+
mapLines fn =
148+
mapFirstLine fn fn
149+
150+
151+
mapFirstLine : (Line -> Line) -> (Line -> Line) -> Box -> Box
152+
mapFirstLine firstFn restFn b =
153+
case b of
154+
SingleLine l1 ->
155+
SingleLine (firstFn l1)
156+
157+
Stack l1 l2 ls ->
158+
Stack (firstFn l1) (restFn l2) (List.map restFn ls)
159+
160+
MustBreak l1 ->
161+
MustBreak (firstFn l1)
162+
163+
164+
indent : Box -> Box
165+
indent =
166+
mapLines (\l -> row [ Tab, l ])
167+
168+
169+
isLine : Box -> Result Box Line
170+
isLine b =
171+
case b of
172+
SingleLine l ->
173+
Ok l
174+
175+
_ ->
176+
Err b
177+
178+
179+
destructure : Box -> ( Line, List Line )
180+
destructure b =
181+
case b of
182+
SingleLine l1 ->
183+
( l1, [] )
184+
185+
Stack l1 l2 rest ->
186+
( l1, l2 :: rest )
187+
188+
MustBreak l1 ->
189+
( l1, [] )
190+
191+
192+
allSingles : List Box -> Result (List Box) (List Line)
193+
allSingles boxes =
194+
case Result.combine (List.map isLine boxes) of
195+
Ok lines_ ->
196+
Ok lines_
197+
198+
_ ->
199+
Err boxes
200+
201+
202+
{-| Add the prefix to the first line,
203+
pad the other lines with spaces of the same length
204+
205+
EXAMPLE:
206+
abcde
207+
xyz
208+
----->
209+
myPrefix abcde
210+
xyz
211+
212+
-}
213+
prefix : Line -> Box -> Box
214+
prefix pref =
215+
let
216+
prefixLength =
217+
lineLength 0 pref
218+
219+
paddingSpaces =
220+
List.repeat prefixLength space
221+
222+
padLineWithSpaces l =
223+
row [ row paddingSpaces, l ]
224+
225+
addPrefixToLine l =
226+
row [ pref, l ]
227+
in
228+
mapFirstLine addPrefixToLine padLineWithSpaces
229+
230+
231+
addSuffix : Line -> Box -> Box
232+
addSuffix suffix b =
233+
case destructure b of
234+
( l, [] ) ->
235+
line (row [ l, suffix ])
236+
237+
( l1, ls ) ->
238+
line l1
239+
|> andThen (List.map line (Prelude.init ls))
240+
|> andThen [ line (row [ Prelude.last ls, suffix ]) ]
241+
242+
243+
renderLine : Int -> Line -> String
244+
renderLine startColumn line_ =
245+
case line_ of
246+
Text text ->
247+
text
248+
249+
Space ->
250+
" "
251+
252+
Tab ->
253+
String.fromList (List.repeat (tabLength startColumn) ' ')
254+
255+
Row lines_ ->
256+
renderRow startColumn lines_
257+
258+
259+
render : Box -> String
260+
render box =
261+
case box of
262+
SingleLine line_ ->
263+
String.trimRight (renderLine 0 line_) ++ "\n"
264+
265+
Stack l1 l2 rest ->
266+
String.join "\n" (List.map (String.trimRight << renderLine 0) (l1 :: l2 :: rest))
267+
268+
MustBreak line_ ->
269+
String.trimRight (renderLine 0 line_) ++ "\n"
270+
271+
272+
lineLength : Int -> Line -> Int
273+
lineLength startColumn line_ =
274+
startColumn
275+
+ (case line_ of
276+
Text string ->
277+
String.length string
278+
279+
Space ->
280+
1
281+
282+
Tab ->
283+
tabLength startColumn
284+
285+
Row lines_ ->
286+
rowLength startColumn lines_
287+
)
288+
289+
290+
initRow : Int -> ( String, Int )
291+
initRow startColumn =
292+
( "", startColumn )
293+
294+
295+
spacesInTab : Int
296+
spacesInTab =
297+
4
298+
299+
300+
spacesToNextTab : Int -> Int
301+
spacesToNextTab startColumn =
302+
modBy spacesInTab startColumn
303+
304+
305+
tabLength : Int -> Int
306+
tabLength startColumn =
307+
spacesInTab - spacesToNextTab startColumn
308+
309+
310+
{-| What happens here is we take a row and start building its contents
311+
along with the resulting length of the string. We need to have that
312+
because of Tabs, which need to be passed the current column in arguments
313+
in order to determine how many Spaces are they going to span.
314+
(See `tabLength`.)
315+
316+
So for example if we have a Box [Space, Tab, Text "abc", Tab, Text "x"],
317+
it goes like this:
318+
319+
string | column | todo
320+
"" | 0 | [Space, Tab, Text "abc", Tab, Text "x"]
321+
" " | 1 | [Tab, Text "abc", Tab, Text "x"]
322+
" " | 4 | [Text "abc", Tab, Text "x"]
323+
" abc" | 7 | [Tab, Text "x"]
324+
" abc " | 8 | [Text "x"]
325+
" abc x" | 9 | []
326+
327+
Thus we get the result string with correctly rendered Tabs.
328+
329+
The (String, Int) type here means the (string, column) from the table above.
330+
331+
Then we just need to do one final modification to get from endColumn to resultLength,
332+
which is what we are after in the function `rowLength`.
333+
334+
-}
335+
renderRow_ : Int -> List Line -> ( String, Int )
336+
renderRow_ startColumn lines_ =
337+
let
338+
( result, endColumn ) =
339+
List.foldl addLine (initRow startColumn) lines_
340+
341+
resultLength =
342+
endColumn - startColumn
343+
in
344+
( result, resultLength )
345+
346+
347+
{-| A step function for renderRow\_.
348+
349+
addLine Tab ( " ", 1 ) == ( " ", 4 )
350+
351+
-}
352+
addLine : Line -> ( String, Int ) -> ( String, Int )
353+
addLine line_ ( string, startColumn_ ) =
354+
let
355+
newString =
356+
string ++ renderLine startColumn_ line_
357+
358+
newStartColumn =
359+
lineLength startColumn_ line_
360+
in
361+
( newString, newStartColumn )
362+
363+
364+
{-| Extract the final string from renderRow\_
365+
-}
366+
renderRow : Int -> List Line -> String
367+
renderRow startColumn lines_ =
368+
Tuple.first (renderRow_ startColumn lines_)
369+
370+
371+
{-| Extract the final length from renderRow\_
372+
-}
373+
rowLength : Int -> List Line -> Int
374+
rowLength startColumn lines_ =
375+
Tuple.second (renderRow_ startColumn lines_)

0 commit comments

Comments
 (0)