Skip to content

Commit ee341e2

Browse files
authored
Merge pull request #133 from unisoncomputing/sort-docs
Sort diff changes such that docs are next to their definitions
2 parents 001f007 + 77de513 commit ee341e2

File tree

5 files changed

+232
-2
lines changed

5 files changed

+232
-2
lines changed

elm-git.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
1-
{"git-dependencies":{"direct":{"https://github.com/unisonweb/ui-core":"4a9d32ef5c10cf2e755df8c158e3088ea3953641"},"indirect":{}}}
1+
{
2+
"git-dependencies": {
3+
"direct": {
4+
"https://github.com/unisonweb/ui-core": "7312db2df048f55cc118b9a7d9268ae76aa5da8a"
5+
},
6+
"indirect": {}
7+
}
8+
}

src/UnisonShare/BranchDiff.elm

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ module UnisonShare.BranchDiff exposing (..)
22

33
import Code.BranchRef as BranchRef exposing (BranchRef)
44
import Code.FullyQualifiedName as FQN
5+
import Code.FullyQualifiedNameSet as FQNSet exposing (FQNSet)
56
import Code.Hash as Hash exposing (Hash)
7+
import Dict exposing (Dict)
68
import Json.Decode as Decode exposing (Decoder)
79
import Json.Decode.Pipeline exposing (required, requiredAt)
810
import List.Extra as ListE
@@ -154,6 +156,114 @@ condense changeLines =
154156
List.foldl f [] changeLines
155157

156158

159+
type alias DocDefPairings =
160+
Dict
161+
String
162+
{ doc : ChangeLine
163+
, def : ChangeLine
164+
, docFqn : FQN.FQN
165+
, defFqn : FQN.FQN
166+
}
167+
168+
169+
{-| all doc/definition pairs that exist in the list (shallow), indexed by the
170+
doc fqn
171+
-}
172+
toDocDefPairings : List ChangeLine -> DocDefPairings
173+
toDocDefPairings lines =
174+
let
175+
( docs, defs ) =
176+
List.foldr
177+
(\cl ( docAcc, defAcc ) ->
178+
case cl of
179+
Namespace _ ->
180+
( docAcc, defAcc )
181+
182+
_ ->
183+
if ChangeLine.isDefinitionDoc cl then
184+
( cl :: docAcc, defAcc )
185+
186+
else
187+
( docAcc, cl :: defAcc )
188+
)
189+
( [], [] )
190+
lines
191+
192+
pair doc =
193+
let
194+
docFqn =
195+
ChangeLine.fullName doc
196+
in
197+
defs
198+
|> ListE.find (ChangeLine.fullName >> FQN.isDefinitionDocOf docFqn)
199+
|> Maybe.map
200+
(\def ->
201+
( FQN.toString docFqn
202+
, { doc = doc
203+
, def = def
204+
, docFqn = docFqn
205+
, defFqn = ChangeLine.fullName def
206+
}
207+
)
208+
)
209+
in
210+
docs
211+
|> List.filterMap pair
212+
|> Dict.fromList
213+
214+
215+
{-| Sort change lines so that definitions and their corresponding .doc changes
216+
appear next to each other, with the doc appearing before the definition.
217+
For example, if both MyType and MyType.doc changed, they will be adjacent
218+
in the sorted list with MyType.doc appearing first.
219+
-}
220+
sortWithDocs : List ChangeLine -> List ChangeLine
221+
sortWithDocs lines =
222+
let
223+
pairings =
224+
toDocDefPairings lines
225+
226+
-- Look up pairing by the doc
227+
findPairing : FQN.FQN -> Maybe { doc : ChangeLine, def : ChangeLine, docFqn : FQN.FQN, defFqn : FQN.FQN }
228+
findPairing docFqn =
229+
Dict.get (FQN.toString docFqn) pairings
230+
231+
go : List ChangeLine -> FQNSet -> List ChangeLine
232+
go remaining processed =
233+
case remaining of
234+
[] ->
235+
[]
236+
237+
head :: tail ->
238+
case head of
239+
Namespace ns ->
240+
Namespace { ns | lines = sortWithDocs ns.lines } :: go tail processed
241+
242+
_ ->
243+
let
244+
docFqn =
245+
head
246+
|> ChangeLine.fullName
247+
|> FQN.toDefinitionDoc
248+
in
249+
if FQNSet.member docFqn processed then
250+
go tail processed
251+
252+
else
253+
case findPairing docFqn of
254+
Just pairing ->
255+
-- This item is part of a pair, output both and mark doc as processed
256+
pairing.doc
257+
:: pairing.def
258+
:: go tail (FQNSet.insert pairing.docFqn processed)
259+
260+
Nothing ->
261+
-- Standalone item
262+
head :: go tail processed
263+
in
264+
go lines FQNSet.empty
265+
266+
157267
changeLineById : ChangeLineId -> BranchDiff -> Maybe ChangeLine
158268
changeLineById changeLineId branchDiff =
159269
let

src/UnisonShare/BranchDiff/ChangeLine.elm

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@ isPropagated cl =
9090
False
9191

9292

93+
isDoc : ChangeLine -> Bool
94+
isDoc cl =
95+
case definitionType cl of
96+
Just DefinitionType.Doc ->
97+
True
98+
99+
_ ->
100+
False
101+
102+
103+
isDefinitionDoc : ChangeLine -> Bool
104+
isDefinitionDoc cl =
105+
isDoc cl && FQN.isDefinitionDoc (fullName cl)
106+
107+
93108
shouldBeCollapsedByDefault : ChangeLine -> Bool
94109
shouldBeCollapsedByDefault changeLine =
95110
case changeLine of

src/UnisonShare/Page/ProjectContributionChangesPage.elm

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,12 @@ update appContext projectRef contribRef msg model =
124124
( { model
125125
| branchDiff =
126126
BranchDiffState.Computed
127-
{ bd | lines = BranchDiff.condense bd.lines }
127+
{ bd
128+
| lines =
129+
bd.lines
130+
|> BranchDiff.condense
131+
|> BranchDiff.sortWithDocs
132+
}
128133
, toggledChangeLines = toggledChangeLines
129134
}
130135
, cmd_

tests/UnisonShare/BranchDiffTests.elm

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Code.BranchRef as BranchRef
44
import Code.Definition.Reference as Reference
55
import Code.FullyQualifiedName as FQN
66
import Code.Hash as Hash
7+
import Code.Syntax as Syntax
78
import Code.Syntax.SyntaxSegment as SyntaxSegment
89
import Expect
910
import List.Nonempty as NEL
@@ -162,3 +163,95 @@ diff =
162163
}
163164
in
164165
DefinitionDiff.Diff diffDetails
166+
167+
168+
sortWithDocs : Test
169+
sortWithDocs =
170+
describe "BranchDiff.sortWithDocs"
171+
[ test "pairs definitions with their .doc changes (doc before definition) regardless of input order" <|
172+
\_ ->
173+
let
174+
-- Definitions with docs in various orders
175+
aDoc =
176+
addedChangeLine (FQN.fromString "a.doc") (FQN.fromString "a.doc") DefinitionType.Doc
177+
178+
a =
179+
addedChangeLine (FQN.fromString "a") (FQN.fromString "a") DefinitionType.Term
180+
181+
b =
182+
addedChangeLine (FQN.fromString "b") (FQN.fromString "b") DefinitionType.Term
183+
184+
bDoc =
185+
addedChangeLine (FQN.fromString "b.doc") (FQN.fromString "b.doc") DefinitionType.Doc
186+
187+
-- Definition without doc
188+
c =
189+
addedChangeLine (FQN.fromString "c") (FQN.fromString "c") DefinitionType.Term
190+
191+
-- Standalone doc (no corresponding definition)
192+
dDoc =
193+
addedChangeLine (FQN.fromString "d.doc") (FQN.fromString "d.doc") DefinitionType.Doc
194+
195+
-- Input: doc first, def later, def first, doc later, def without doc, standalone doc
196+
input =
197+
[ aDoc, b, c, a, dDoc, bDoc ]
198+
199+
-- Expected: pairs together with doc first (paired when encountered)
200+
expected =
201+
[ aDoc, a, bDoc, b, c, dDoc ]
202+
203+
result =
204+
BranchDiff.sortWithDocs input
205+
in
206+
Expect.equal expected result
207+
, test "recursively sorts within namespaces" <|
208+
\_ ->
209+
let
210+
innerDef =
211+
addedChangeLine (FQN.fromString "data.List.map") (FQN.fromString "List.map") DefinitionType.Term
212+
213+
innerDoc =
214+
addedChangeLine (FQN.fromString "data.List.map.doc") (FQN.fromString "List.map.doc") DefinitionType.Doc
215+
216+
innerOther =
217+
addedChangeLine (FQN.fromString "data.List.filter") (FQN.fromString "List.filter") DefinitionType.Term
218+
219+
namespace =
220+
ChangeLine.Namespace
221+
{ name = FQN.fromString "data.List"
222+
, lines = [ innerDef, innerOther, innerDoc ]
223+
}
224+
225+
expectedNamespace =
226+
ChangeLine.Namespace
227+
{ name = FQN.fromString "data.List"
228+
, lines = [ innerDoc, innerDef, innerOther ]
229+
}
230+
231+
input =
232+
[ namespace ]
233+
234+
expected =
235+
[ expectedNamespace ]
236+
237+
result =
238+
BranchDiff.sortWithDocs input
239+
in
240+
Expect.equal expected result
241+
]
242+
243+
244+
addedChangeLine : FQN.FQN -> FQN.FQN -> DefinitionType.DefinitionType -> ChangeLine.ChangeLine
245+
addedChangeLine fullName shortName type_ =
246+
let
247+
source =
248+
Syntax.fromNEL (NEL.singleton (SyntaxSegment.SyntaxSegment SyntaxSegment.TextLiteral "test"))
249+
in
250+
ChangeLine.Added
251+
type_
252+
{ hash = Hash.unsafeFromString "#testHash"
253+
, shortName = shortName
254+
, fullName = fullName
255+
, ref = Reference.fromFQN (DefinitionType.toReferenceConstructor type_) fullName
256+
, source = source
257+
}

0 commit comments

Comments
 (0)