@@ -4,11 +4,12 @@ import Code.Hash as Hash exposing (Hash)
44import Code.Syntax.SyntaxConfig exposing (SyntaxConfig )
55import Code.Syntax.SyntaxSegment as SyntaxSegment exposing (SyntaxSegment )
66import Html exposing (Html , code , div , header , pre , span , text )
7- import Html.Attributes exposing (class )
7+ import Html.Attributes exposing (class , style )
88import Json.Decode as Decode
99import Json.Decode.Extra exposing (when )
1010import Json.Decode.Pipeline exposing (required , requiredAt )
11- import Lib.Decode.Helpers exposing (nonEmptyList )
11+ import Lib.Decode.Helpers exposing (nonEmptyList , whenKindIs )
12+ import List.Extra as ListE
1213import List.Nonempty as NEL
1314import UI.Tooltip as Tooltip
1415
@@ -18,23 +19,38 @@ type alias DiffSyntaxSegments =
1819
1920
2021type DiffSegment
21- = Old DiffSyntaxSegments
22- | New DiffSyntaxSegments
23- | Both DiffSyntaxSegments
22+ = Both DiffSyntaxSegments
23+ | OneSided DiffSyntaxSegments
2424 | AnnotationChange { segment : SyntaxSegment , fromHash : Hash , toHash : Hash }
2525 | SegmentChange { from : SyntaxSegment , to : SyntaxSegment }
2626
2727
28+ type DiffLine
29+ = ChangedLine ( List DiffSegment )
30+ | UnchangedLine ( List DiffSegment )
31+ -- Spacer includes numLines such that we can avoid a jagged background
32+ -- pattern when it spans over multiple lines by making it 1 tall DOM
33+ -- element instead of small 1 line height elements
34+ | Spacer { numLines : Int }
35+
36+
2837type alias DiffDetails =
2938 { type_ : DefinitionType
30- , newDef : DiffSyntaxSegments
31- , oldDef : DiffSyntaxSegments
39+ , left : List DiffLine
40+ , right : List DiffLine
41+ }
42+
43+
44+ type alias MismatchedDetails =
45+ { type_ : DefinitionType
46+ , left : DiffSyntaxSegments
47+ , right : DiffSyntaxSegments
3248 }
3349
3450
3551type DefinitionDiff
36- = Diff DiffDetails ( NEL . Nonempty DiffSegment )
37- | Mismatched DiffDetails
52+ = Diff DiffDetails
53+ | Mismatched MismatchedDetails
3854
3955
4056
@@ -75,10 +91,8 @@ viewTooltip content =
7591 |> Tooltip . withArrow Tooltip . Start
7692
7793
78- {- | View diff segments from the perspective of viewing the old definition
79- -}
80- viewOldDiffSegment : SyntaxConfig msg -> DiffSegment -> List (Html msg )
81- viewOldDiffSegment syntaxConfig segment =
94+ viewDiffSegment : SyntaxConfig msg -> DiffSegment -> List (Html msg )
95+ viewDiffSegment syntaxConfig segment =
8296 let
8397 viewSegment =
8498 SyntaxSegment . view syntaxConfig
@@ -87,42 +101,11 @@ viewOldDiffSegment syntaxConfig segment =
87101 viewSegments syntaxConfig className
88102 in
89103 case segment of
90- Old segments ->
91- viewSegments_ " diff-segment old" segments
92-
93104 Both segments ->
94105 viewSegments_ " diff-segment both" segments
95106
96- New _ ->
97- []
98-
99- AnnotationChange change ->
100- [ viewSegment change. segment ]
101-
102- SegmentChange { from } ->
103- [ viewSegment from ]
104-
105-
106- {- | View diff segments from the perspective of viewing the new definition
107- -}
108- viewNewDiffSegment : SyntaxConfig msg -> DiffSegment -> List (Html msg )
109- viewNewDiffSegment syntaxConfig segment =
110- let
111- viewSegment =
112- SyntaxSegment . view syntaxConfig
113-
114- viewSegments_ className =
115- viewSegments syntaxConfig className
116- in
117- case segment of
118- Old _ ->
119- []
120-
121- New segments ->
122- viewSegments_ " diff-segment new" segments
123-
124- Both segments ->
125- viewSegments_ " diff-segment both" segments
107+ OneSided segments ->
108+ viewSegments_ " diff-segment one-sided" segments
126109
127110 AnnotationChange change ->
128111 [ viewTooltip
@@ -152,41 +135,110 @@ viewNewDiffSegment syntaxConfig segment =
152135 ]
153136
154137
155- viewDiff : (Bool -> SyntaxConfig msg ) -> NEL .Nonempty DiffSegment -> Html msg
156- viewDiff toSyntaxConfig segments =
138+ viewDiffLine : (DiffSegment -> List (Html msg )) -> String -> Int -> ( Maybe Int , DiffLine ) -> Html msg
139+ viewDiffLine viewSeg changeIndicator gutterWidth ( ln, line ) =
140+ let
141+ gutter indicator =
142+ span [ class " gutter" ]
143+ [ span [ class " line-number" ]
144+ [ text
145+ ( String . padLeft
146+ gutterWidth
147+ ' '
148+ ( ln |> Maybe . map String . fromInt |> Maybe . withDefault " " )
149+ )
150+ ]
151+ , text " "
152+ , span [ class " change-indicator" ] [ text indicator ]
153+ , text " "
154+ ]
155+ in
156+ case line of
157+ ChangedLine segments ->
158+ div [ class " diff-line changed-line" ]
159+ [ gutter changeIndicator
160+ , span [] ( List . concatMap viewSeg segments)
161+ ]
162+
163+ UnchangedLine segments ->
164+ div [ class " diff-line unchanged-line" ]
165+ [ gutter " "
166+ , span [] ( List . concatMap viewSeg segments)
167+ ]
168+
169+ Spacer { numLines } ->
170+ div
171+ [ class " diff-line spacer-line"
172+ , style " height" ( " calc(var(--diff-line-height) * " ++ String . fromInt numLines ++ " )" )
173+ ]
174+ []
175+
176+
177+ viewDiff : (Bool -> SyntaxConfig msg ) -> DiffDetails -> Html msg
178+ viewDiff toSyntaxConfig { left, right } =
157179 let
158- old =
159- segments
160- |> NEL . toList
161- |> List . concatMap ( viewOldDiffSegment ( toSyntaxConfig False ))
162-
163- new =
164- segments
165- |> NEL . toList
166- |> List . concatMap ( viewNewDiffSegment ( toSyntaxConfig True ))
180+ toGutterWidth len =
181+ String . length ( String . fromInt len)
182+
183+ toViewDiffSegment isNew =
184+ viewDiffSegment ( toSyntaxConfig isNew)
185+
186+ withLineNumbers diffLine ( i, lines ) =
187+ case diffLine of
188+ ChangedLine _ ->
189+ ( i + 1 , lines ++ [ ( Just ( i + 1 ) , diffLine ) ] )
190+
191+ UnchangedLine _ ->
192+ ( i + 1 , lines ++ [ ( Just ( i + 1 ) , diffLine ) ] )
193+
194+ Spacer _ ->
195+ case ListE . unconsLast lines of
196+ Just ( ( _, Spacer { numLines } ) , lines_ ) ->
197+ ( i, lines_ ++ [ ( Nothing , Spacer { numLines = numLines + 1 } ) ] )
198+
199+ _ ->
200+ ( i, lines ++ [ ( Nothing , diffLine ) ] )
201+
202+ viewLeftDiffLine =
203+ viewDiffLine ( toViewDiffSegment False ) " -" ( toGutterWidth ( List . length left))
204+
205+ viewRightDiffLine =
206+ viewDiffLine ( toViewDiffSegment False ) " -" ( toGutterWidth ( List . length right))
207+
208+ before =
209+ left
210+ |> List . foldl withLineNumbers ( 0 , [] )
211+ |> Tuple . second
212+ |> List . map viewLeftDiffLine
213+
214+ after =
215+ right
216+ |> List . foldl withLineNumbers ( 0 , [] )
217+ |> Tuple . second
218+ |> List . map viewRightDiffLine
167219 in
168220 div [ class " diff-side-by-side" ]
169- [ pre [ class " monochrome diff-side old " ]
170- [ header [ class " diff-old -header" ] [ text " Before" ]
171- , code [] old
221+ [ pre [ class " monochrome diff-side left " ]
222+ [ header [ class " diff-left -header" ] [ text " Before" ]
223+ , code [] before
172224 ]
173- , pre [ class " monochrome diff-side new " ]
174- [ header [ class " diff-new -header" ] [ text " After" ]
175- , code [] new
225+ , pre [ class " monochrome diff-side right " ]
226+ [ header [ class " diff-right -header" ] [ text " After" ]
227+ , code [] after
176228 ]
177229 ]
178230
179231
180232view : (Bool -> SyntaxConfig msg ) -> DefinitionDiff -> Html msg
181233view toSyntaxConfig defDiff =
182234 case defDiff of
183- Diff _ diff ->
184- div [] [ viewDiff toSyntaxConfig diff ]
235+ Diff details ->
236+ div [] [ viewDiff toSyntaxConfig details ]
185237
186- Mismatched { oldDef , newDef } ->
238+ Mismatched { left , right } ->
187239 div [ class " diff-side-by-side" ]
188- [ pre [ class " monochrome diff-side" ] [ code [] ( viewSegments ( toSyntaxConfig False ) " mismatched old" oldDef ) ]
189- , pre [ class " monochrome diff-side" ] [ code [] ( viewSegments ( toSyntaxConfig True ) " mismatched new" newDef ) ]
240+ [ pre [ class " monochrome diff-side" ] [ code [] ( viewSegments ( toSyntaxConfig False ) " mismatched old" left ) ]
241+ , pre [ class " monochrome diff-side" ] [ code [] ( viewSegments ( toSyntaxConfig True ) " mismatched new" right ) ]
190242 ]
191243
192244
@@ -199,24 +251,25 @@ decodeDiffSyntaxSegments =
199251 nonEmptyList SyntaxSegment . decode
200252
201253
254+ decodeSingleDiffSyntaxSegment : Decode .Decoder DiffSyntaxSegments
255+ decodeSingleDiffSyntaxSegment =
256+ Decode . map NEL . fromElement SyntaxSegment . decode
257+
258+
202259decodeSegment : Decode .Decoder DiffSegment
203260decodeSegment =
204261 let
205262 decodeDiffTag =
206263 Decode . field " diffTag" Decode . string
207264
208- decodeOld =
209- Decode . succeed Old
210- |> required " elements" decodeDiffSyntaxSegments
211-
212- decodeNew =
213- Decode . succeed New
214- |> required " elements" decodeDiffSyntaxSegments
215-
216265 decodeBoth =
217266 Decode . succeed Both
218267 |> required " elements" decodeDiffSyntaxSegments
219268
269+ decodeOneSided =
270+ Decode . succeed OneSided
271+ |> required " elements" decodeDiffSyntaxSegments
272+
220273 mkAnnotationChange segment fromHash toHash =
221274 AnnotationChange
222275 { segment = segment
@@ -241,61 +294,62 @@ decodeSegment =
241294 ( SyntaxSegment . decode_ { segmentField = " toSegment" , annotationField = " annotation" } )
242295 in
243296 Decode . oneOf
244- [ when decodeDiffTag ( (==) " old" ) decodeOld
245- , when decodeDiffTag ( (==) " new" ) decodeNew
246- , when decodeDiffTag ( (==) " both" ) decodeBoth
297+ [ when decodeDiffTag ( (==) " both" ) decodeBoth
298+ , when decodeDiffTag ( (==) " oneSided" ) decodeOneSided
247299 , when decodeDiffTag ( (==) " annotationChange" ) decodeAnnotationChange
248300 , when decodeDiffTag ( (==) " segmentChange" ) decodeSegmentChange
249301 ]
250302
251303
304+ decodeDiffLine : Decode .Decoder DiffLine
305+ decodeDiffLine =
306+ Decode . oneOf
307+ [ whenKindIs " changed" ( Decode . map ChangedLine ( Decode . field " value" ( Decode . list decodeSegment)))
308+ , whenKindIs " unchanged" ( Decode . map UnchangedLine ( Decode . field " value" ( Decode . list decodeSegment)))
309+
310+ -- The spacer numLines will be flatten later on
311+ -- TODO: we should probably do the flattening and add line numbers during parsing...
312+ , whenKindIs " spacer" ( Decode . succeed ( Spacer { numLines = 1 } ))
313+ ]
314+
315+
252316decodeDiff : DefinitionType -> Decode .Decoder DefinitionDiff
253317decodeDiff definitionType =
254318 let
255- ( oldKey, newKey, definitionKey ) =
256- case definitionType of
257- Term ->
258- ( " left" , " right" , " termDefinition" )
259-
260- Type ->
261- ( " left" , " right" , " typeDefinition" )
262-
263- mkDiff diff oldDef newDef =
319+ mkDiff left right =
264320 Diff
265321 { type_ = definitionType
266- , oldDef = oldDef
267- , newDef = newDef
322+ , left = left
323+ , right = right
268324 }
269- diff
270325 in
271326 Decode . succeed mkDiff
272- |> requiredAt [ " diff" , " diff" , " contents" ] ( nonEmptyList decodeSegment)
273- |> requiredAt [ oldKey, definitionKey, " contents" ] decodeDiffSyntaxSegments
274- |> requiredAt [ newKey, definitionKey, " contents" ] decodeDiffSyntaxSegments
327+ |> requiredAt [ " diff" , " diff" , " contents" , " left" ] ( Decode . list decodeDiffLine)
328+ |> requiredAt [ " diff" , " diff" , " contents" , " right" ] ( Decode . list decodeDiffLine)
275329
276330
277331decodeMismatched : DefinitionType -> Decode .Decoder DefinitionDiff
278332decodeMismatched definitionType =
279333 let
280- ( oldKey , newKey , definitionKey ) =
334+ definitionKey =
281335 case definitionType of
282336 Term ->
283- ( " left " , " right " , " termDefinition" )
337+ " termDefinition"
284338
285339 Type ->
286- ( " left " , " right " , " typeDefinition" )
340+ " typeDefinition"
287341
288- mkMismatched oldDef newDef =
342+ mkMismatched left right =
289343 Mismatched
290344 { type_ = definitionType
291- , oldDef = oldDef
292- , newDef = newDef
345+ , left = left
346+ , right = right
293347 }
294348 in
295349 Decode . succeed mkMismatched
296350 -- TODO: what about builtins?
297- |> requiredAt [ oldKey , definitionKey, " contents" ] decodeDiffSyntaxSegments
298- |> requiredAt [ newKey , definitionKey, " contents" ] decodeDiffSyntaxSegments
351+ |> requiredAt [ " left " , definitionKey, " contents" ] decodeDiffSyntaxSegments
352+ |> requiredAt [ " right " , definitionKey, " contents" ] decodeDiffSyntaxSegments
299353
300354
301355decode : DefinitionType -> Decode .Decoder DefinitionDiff
0 commit comments