Add topological traversal: Create graph of files

This commit is contained in:
Jeroen Engels 2020-01-06 23:22:59 +01:00
parent 1219faf512
commit d8df52aa52
3 changed files with 115 additions and 7 deletions

View File

@ -16,6 +16,8 @@
"dependencies": {
"elm/core": "1.0.2 <= v < 2.0.0",
"elm/project-metadata-utils": "1.0.0 <= v < 2.0.0",
"elm-community/graph": "6.0.0 <= v < 7.0.0",
"elm-community/intdict": "3.0.0 <= v < 4.0.0",
"elm-explorations/test": "1.2.2 <= v < 2.0.0",
"stil4m/elm-syntax": "7.1.0 <= v < 8.0.0"
},

View File

@ -11,17 +11,16 @@
"elm/core": "1.0.2",
"elm/json": "1.1.3",
"elm/project-metadata-utils": "1.0.0",
"elm-community/graph": "6.0.0",
"elm-community/intdict": "3.0.0",
"stil4m/elm-syntax": "7.1.0"
},
"indirect": {
"elm/html": "1.0.0",
"avh4/elm-fifo": "1.0.4",
"elm/parser": "1.1.0",
"elm/random": "1.0.0",
"elm/time": "1.0.0",
"elm/virtual-dom": "1.0.2",
"elm-community/json-extra": "4.0.0",
"elm-community/list-extra": "8.2.2",
"elm-explorations/test": "1.2.2",
"rtfeldman/elm-hex": "1.0.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.3",
"stil4m/structured-writer": "1.0.2"
@ -29,6 +28,11 @@
},
"test-dependencies": {
"direct": {},
"indirect": {}
"indirect": {
"elm/html": "1.0.0",
"elm/random": "1.0.0",
"elm/virtual-dom": "1.0.2",
"elm-explorations/test": "1.2.2"
}
}
}
}

View File

@ -209,6 +209,8 @@ import Elm.Syntax.Module as Module exposing (Module)
import Elm.Syntax.ModuleName exposing (ModuleName)
import Elm.Syntax.Node as Node exposing (Node)
import Elm.Syntax.Range exposing (Range)
import Graph exposing (Graph)
import IntDict exposing (IntDict)
import Review.File exposing (ParsedFile, RawFile)
import Review.Fix exposing (Fix)
import Review.Project exposing (Project)
@ -555,9 +557,15 @@ type MultiSchema globalContext moduleContext
, elmJsonVisitors : List (Maybe Elm.Project.Project -> globalContext -> globalContext)
, dependenciesVisitors : List (Dict String Elm.Docs.Module -> globalContext -> globalContext)
, finalEvaluationFns : List (globalContext -> List Error)
, traversalType : TraversalType
}
type TraversalType
= AllFilesInParallel
| ImportedModulesFirst
newMultiSchema :
String
->
@ -581,6 +589,7 @@ newMultiSchema name_ { moduleVisitorSchema, initGlobalContext, initModuleContext
, elmJsonVisitors = []
, dependenciesVisitors = []
, finalEvaluationFns = []
, traversalType = AllFilesInParallel
}
@ -629,7 +638,17 @@ type alias MultiRuleCache context =
runMulti : MultiSchema globalContext moduleContext -> MultiRuleCache globalContext -> Project -> List ParsedFile -> ( List Error, Rule )
runMulti (MultiSchema schema) startCache project =
runMulti ((MultiSchema { traversalType }) as schema) =
case traversalType of
AllFilesInParallel ->
allFilesInParallelTraversal schema
ImportedModulesFirst ->
importedModulesFirst schema
allFilesInParallelTraversal : MultiSchema globalContext moduleContext -> MultiRuleCache globalContext -> Project -> List ParsedFile -> ( List Error, Rule )
allFilesInParallelTraversal (MultiSchema schema) startCache project =
let
initialContext : globalContext
initialContext =
@ -718,6 +737,89 @@ runMulti (MultiSchema schema) startCache project =
( errors, Multi schema.name (runMulti (MultiSchema schema) newCache) )
importedModulesFirst : MultiSchema globalContext moduleContext -> MultiRuleCache globalContext -> Project -> List ParsedFile -> ( List Error, Rule )
importedModulesFirst (MultiSchema schema) startCache project =
\files ->
let
graph : Graph ModuleName ()
graph =
buildModuleGraph files
|> Debug.log "graph"
in
-- TODO Implement
( [], Multi schema.name (runMulti (MultiSchema schema) startCache) )
buildModuleGraph : List ParsedFile -> Graph ModuleName ()
buildModuleGraph files =
-- This should be moved to `Review.Project`, to avoid having to do this for every rule
let
fileIds : Dict ModuleName Int
fileIds =
files
|> List.indexedMap Tuple.pair
|> List.foldl
(\( index, file ) dict ->
Dict.insert
(getModuleName file)
index
dict
)
Dict.empty
getFileId : ModuleName -> Int
getFileId moduleName =
case Dict.get moduleName fileIds of
Just fileId ->
fileId
Nothing ->
getFileId moduleName
( nodes, edges ) =
files
|> List.foldl
(\file ( resNodes, resEdges ) ->
let
( moduleNode, modulesEdges ) =
nodesAndEdges (\moduleName -> Dict.get moduleName fileIds) file (getFileId <| getModuleName file)
in
( moduleNode :: resNodes, List.concat [ modulesEdges, resEdges ] )
)
( [], [] )
in
Graph.fromNodesAndEdges nodes edges
nodesAndEdges : (ModuleName -> Maybe Int) -> ParsedFile -> Int -> ( Graph.Node ModuleName, List (Graph.Edge ()) )
nodesAndEdges getFileId file fileId =
let
moduleName =
getModuleName file
in
( Graph.Node fileId moduleName
, importedModules file
|> List.filterMap getFileId
|> List.map
(\importFileId ->
Graph.Edge fileId importFileId ()
)
)
getModuleName : ParsedFile -> ModuleName
getModuleName file =
file.ast.moduleDefinition
|> Node.value
|> Module.moduleName
importedModules : ParsedFile -> List ModuleName
importedModules file =
file.ast.imports
|> List.map (Node.value >> .moduleName >> Node.value)
{-| Concatenate the errors of the previous step and of the last step.
-}
makeFinalEvaluationForMulti : List (context -> List Error) -> context -> List Error