-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactored Linting-Engine to Workspace modell and Resolver-Engine and…
… added support for @-Notations and fixed a lot of linter render and linking bugs. Added a lot of tooltips.
- Loading branch information
1 parent
e0a286d
commit 080e2df
Showing
57 changed files
with
1,577 additions
and
405 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,56 @@ | ||
package linter | ||
|
||
import "github.com/worldiety/dddl/parser" | ||
import ( | ||
"fmt" | ||
"github.com/worldiety/dddl/parser" | ||
"golang.org/x/exp/maps" | ||
"golang.org/x/exp/slices" | ||
) | ||
|
||
type AmbiguousDeclaration struct { | ||
hint | ||
Declarations []parser.Declaration | ||
} | ||
|
||
// CheckAmbiguous validates that defined terms (e.g. Context, Workflow and Data) | ||
// are all unique. | ||
func CheckAmbiguous(root parser.Node) []Hint { | ||
allDefs := map[string]parser.Node{} | ||
var res []Hint | ||
_ = parser.Walk(root, func(n parser.Node) error { | ||
var parent *parser.Ident | ||
var node parser.Node | ||
|
||
switch n := n.(type) { | ||
case *parser.Data: | ||
parent = n.Name | ||
node = n | ||
case *parser.Workflow: | ||
parent = n.Name | ||
node = n | ||
case *parser.Context: | ||
parent = n.Name | ||
node = n | ||
} | ||
ws := parser.WorkspaceOf(root) | ||
if ws == nil { | ||
return nil | ||
} | ||
|
||
if parent != nil { | ||
if other, ok := allDefs[parent.Value]; ok { | ||
res = append(res, Hint{ | ||
ParentIdent: parent, | ||
Node: other, | ||
Message: "Der Begriff %s wurde mehrfach definiert.", | ||
}) | ||
} else { | ||
allDefs[parent.Value] = node | ||
allDefs := map[string][]parser.Declaration{} | ||
err := parser.Walk(root, func(n parser.Node) error { | ||
if decl, ok := n.(parser.Declaration); ok { | ||
if _, isCtx := decl.(*parser.Context); isCtx { | ||
return nil | ||
} | ||
|
||
q, ok := ws.Resolve(decl.DeclaredName()) | ||
if ok { | ||
list := allDefs[q.String()] | ||
list = append(list, decl) | ||
allDefs[q.String()] = list | ||
} | ||
} | ||
|
||
return nil | ||
}) | ||
|
||
if err != nil { | ||
panic(fmt.Errorf("cannot happen: %w", err)) | ||
} | ||
|
||
var res []Hint | ||
keys := maps.Keys(allDefs) | ||
slices.Sort(keys) | ||
for _, key := range keys { | ||
list := allDefs[key] | ||
if len(list) > 1 { | ||
res = append(res, &AmbiguousDeclaration{Declarations: list}) | ||
} | ||
} | ||
|
||
return res | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
package linter | ||
|
||
import ( | ||
"fmt" | ||
"github.com/worldiety/dddl/parser" | ||
"golang.org/x/exp/maps" | ||
"golang.org/x/exp/slices" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
var regexAssignee = regexp.MustCompile(`@\w+:?\s[^.?!\n]+`) | ||
var regexName = regexp.MustCompile(`@\w+:?`) | ||
|
||
type AssignedTask struct { | ||
Assignee string | ||
Task string | ||
} | ||
|
||
func ParseAssignees(str string) []AssignedTask { | ||
var res []AssignedTask | ||
matches := regexAssignee.FindAllString(str, -1) | ||
for _, match := range matches { | ||
names := regexName.FindAllString(match, 1) | ||
if len(names) > 0 { | ||
name := names[0][1:] // cut of @ | ||
if strings.HasSuffix(name, ":") { | ||
name = name[:len(name)-1] | ||
} | ||
res = append(res, AssignedTask{ | ||
Assignee: name, | ||
Task: strings.TrimSpace(match[len(names[0]):]), | ||
}) | ||
} | ||
} | ||
|
||
return res | ||
} | ||
|
||
type AssignedContext struct { | ||
hint | ||
Task AssignedTask | ||
Context *parser.Context // from to-do or definition | ||
} | ||
|
||
type AssignedData struct { | ||
hint | ||
Task AssignedTask | ||
Data *parser.Data // from to-do or definition | ||
} | ||
|
||
type AssignedWorkflow struct { | ||
hint | ||
Task AssignedTask | ||
Workflow *parser.Workflow // from to-do or definition or nested to-do within the actual workflow | ||
} | ||
|
||
type AssignedTasks struct { | ||
hint | ||
Assignee string | ||
Contexts []AssignedContext | ||
Workflows []AssignedWorkflow | ||
Datas []AssignedData | ||
} | ||
|
||
// CheckAssignees inspects all definitions and Todos for assigned tasks. | ||
func CheckAssignees(root parser.Node) []Hint { | ||
var res []Hint | ||
clustered := map[string]*AssignedTasks{} | ||
getter := func(task AssignedTask) *AssignedTasks { | ||
tmp := clustered[task.Assignee] | ||
if tmp == nil { | ||
tmp = &AssignedTasks{Assignee: task.Assignee} | ||
clustered[task.Assignee] = tmp | ||
} | ||
|
||
return tmp | ||
} | ||
|
||
err := parser.Walk(root, func(n parser.Node) error { | ||
switch n := n.(type) { | ||
case *parser.Context: | ||
for _, task := range ParseAssignees(n.Definition.Value()) { | ||
tmp := getter(task) | ||
tmp.Contexts = append(tmp.Contexts, AssignedContext{Task: task, Context: n}) | ||
res = append(res, &AssignedContext{Task: task, Context: n}) | ||
} | ||
|
||
for _, task := range ParseAssignees(n.ToDo.Value()) { | ||
tmp := getter(task) | ||
tmp.Contexts = append(tmp.Contexts, AssignedContext{Task: task, Context: n}) | ||
res = append(res, &AssignedContext{Task: task, Context: n}) | ||
} | ||
|
||
case *parser.Workflow: | ||
for _, task := range ParseAssignees(n.Definition.Value()) { | ||
tmp := getter(task) | ||
tmp.Workflows = append(tmp.Workflows, AssignedWorkflow{Task: task, Workflow: n}) | ||
res = append(res, &AssignedWorkflow{Task: task, Workflow: n}) | ||
} | ||
|
||
for _, task := range ParseAssignees(n.ToDo.Value()) { | ||
tmp := getter(task) | ||
tmp.Workflows = append(tmp.Workflows, AssignedWorkflow{Task: task, Workflow: n}) | ||
res = append(res, &AssignedWorkflow{Task: task, Workflow: n}) | ||
} | ||
|
||
err := parser.Walk(n.Block, func(node parser.Node) error { | ||
if todo, ok := node.(*parser.ToDo); ok { | ||
for _, task := range ParseAssignees(todo.Value()) { | ||
tmp := getter(task) | ||
tmp.Workflows = append(tmp.Workflows, AssignedWorkflow{Task: task, Workflow: n}) | ||
res = append(res, &AssignedWorkflow{Task: task, Workflow: n}) | ||
} | ||
} | ||
|
||
return nil | ||
}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
case *parser.Data: | ||
for _, task := range ParseAssignees(n.Definition.Value()) { | ||
tmp := getter(task) | ||
tmp.Datas = append(tmp.Datas, AssignedData{Task: task, Data: n}) | ||
res = append(res, &AssignedData{Task: task, Data: n}) | ||
} | ||
|
||
for _, task := range ParseAssignees(n.ToDo.Value()) { | ||
tmp := getter(task) | ||
tmp.Datas = append(tmp.Datas, AssignedData{Task: task, Data: n}) | ||
res = append(res, &AssignedData{Task: task, Data: n}) | ||
} | ||
} | ||
|
||
return nil | ||
}) | ||
|
||
if err != nil { | ||
panic(fmt.Errorf("cannot happen: %w", err)) | ||
} | ||
|
||
keys := maps.Keys(clustered) | ||
slices.Sort(keys) | ||
for _, key := range keys { | ||
res = append(res, clustered[key]) | ||
} | ||
|
||
return res | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.