Skip to content

Commit

Permalink
save
Browse files Browse the repository at this point in the history
  • Loading branch information
alixander committed Dec 26, 2024
1 parent 4d88199 commit 108b883
Show file tree
Hide file tree
Showing 2 changed files with 397 additions and 116 deletions.
156 changes: 119 additions & 37 deletions d2lsp/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func GetCompletionItems(text string, line, column int) ([]CompletionItem, error)
switch keyword {
case "style":
return getStyleCompletions(), nil
case "shape":
case "shape:":
return getShapeCompletions(), nil
case "shadow", "3d", "multiple", "animated", "bold", "italic", "underline", "filled", "double-border":
return getBooleanCompletions(), nil
Expand All @@ -42,21 +42,25 @@ func GetCompletionItems(text string, line, column int) ([]CompletionItem, error)
case "text-transform":
return getTextTransformCompletions(), nil
case "opacity", "stroke-width", "stroke-dash", "border-radius", "font-size",
"stroke", "fill", "font-color", "width", "height":
"stroke", "fill", "font-color", "width", "height", "top", "left":
return getValueCompletions(keyword), nil
case "source-arrowhead.shape:", "target-arrowhead.shape:":
return getArrowheadShapeCompletions(), nil
case "label":
return getLabelCompletions(), nil
case "icon:":
return getIconCompletions(), nil
case "icon.":
return getLabelCompletions(), nil
case "near":
case "near:":
return getNearCompletions(), nil
case "tooltip":
case "tooltip:":
return getTooltipCompletions(), nil
case "direction:":
return getDirectionCompletions(), nil
default:
if isOnEmptyLine(text, line) {
return getKeywordCompletions(), nil
return nil, nil
}
}

Expand Down Expand Up @@ -100,11 +104,49 @@ func getKeywordContext(text string, m *d2ast.Map, line, column int) string {

keyString := lastKey.ScalarString()

_, isHolder := d2ast.ReservedKeywordHolders[keyString]
if !isHolder {
_, isHolder = d2ast.CompositeReservedKeywords[keyString]
}
// For holder keywords like "style"
if _, ok := d2ast.ReservedKeywordHolders[keyString]; ok {
if isHolder {
if isPositionInMap(line, column, n.MapKey.Value.Map) {
// Special handling for source/target arrowhead maps
if (keyString == "source-arrowhead" || keyString == "target-arrowhead") && n.MapKey.Value.Map != nil {
// Look for shape keyword being typed
for _, mapNode := range n.MapKey.Value.Map.Nodes {
if mapNode.MapKey == nil || mapNode.MapKey.Key == nil || len(mapNode.MapKey.Key.Path) == 0 {
continue
}
mapKey := mapNode.MapKey.Key.Path[len(mapNode.MapKey.Key.Path)-1].Unbox()
if !mapKey.IsUnquoted() {
continue
}
if mapKey.ScalarString() != "shape" {
continue
}
mapKeyRange := mapNode.MapKey.Key.Range
if line == mapKeyRange.End.Line {
sourceLines := strings.Split(text, "\n")
if mapKeyRange.End.Line < len(sourceLines) {
lineText := sourceLines[mapKeyRange.End.Line]
// Find position of colon after shape
for i := mapKeyRange.End.Column; i < len(lineText); i++ {
if lineText[i] == ':' {
// If cursor is right after the colon + optional whitespace
rest := strings.TrimSpace(lineText[mapKeyRange.End.Column:i])
if rest == "" && column > i {
return keyString + ".shape:"
}
break
}
}
}
}
}
}
// If in a style block, check if we're after a style property
if keyString == "style" && n.MapKey.Value.Map != nil {
if (keyString == "style" || keyString == "label") && n.MapKey.Value.Map != nil {
// Look for style keywords being typed
for _, styleNode := range n.MapKey.Value.Map.Nodes {
if styleNode.MapKey == nil || styleNode.MapKey.Key == nil || len(styleNode.MapKey.Key.Path) == 0 {
Expand Down Expand Up @@ -139,13 +181,29 @@ func getKeywordContext(text string, m *d2ast.Map, line, column int) string {

// Check for dot syntax (e.g. style.)
keyRange := n.MapKey.Key.Range
if line == keyRange.End.Line && column == keyRange.End.Column+1 {
// Check if next character is a dot
if line == keyRange.End.Line {
sourceLines := strings.Split(text, "\n")
if keyRange.End.Line < len(sourceLines) {
lineText := sourceLines[keyRange.End.Line]
if keyRange.End.Column < len(lineText) && lineText[keyRange.End.Column] == '.' {
return keyString
if keyRange.End.Column < len(lineText) {
if keyString == "source-arrowhead" || keyString == "target-arrowhead" {
if strings.HasPrefix(lineText[keyRange.End.Column:], ".") && column == keyRange.End.Column+1 {
// After the dot
return keyString + "."
}
remaining := lineText[keyRange.End.Column:]
if strings.HasPrefix(remaining, ".shape:") {
// After the colon in .shape:
colonPos := keyRange.End.Column + 6 // length of .shape
if column > colonPos {
return keyString + ".shape:"
}
}
}
// Other dot syntax cases...
if lineText[keyRange.End.Column] == '.' && column == keyRange.End.Column+1 {
return keyString
}
}
}
}
Expand Down Expand Up @@ -225,17 +283,6 @@ func isPositionInMap(line, column int, m *d2ast.Map) bool {
return true
}

func isStyleKey(node d2ast.Node) bool {
if node == nil {
return false
}
s, ok := node.(d2ast.String)
if !ok {
return false
}
return s.ScalarString() == "style" && s.IsUnquoted()
}

func getShapeCompletions() []CompletionItem {
items := make([]CompletionItem, 0, len(d2target.Shapes))
for _, shape := range d2target.Shapes {
Expand Down Expand Up @@ -294,7 +341,7 @@ func getValueCompletions(property string) []CompletionItem {
Detail: "e.g. blue, #ff0000",
InsertText: "",
}}
case "width", "height":
case "width", "height", "top", "left":
return []CompletionItem{{
Label: "(pixels)",
Kind: KeywordCompletion,
Expand All @@ -319,20 +366,6 @@ func getStyleCompletions() []CompletionItem {
return items
}

func getKeywordCompletions() []CompletionItem {
items := make([]CompletionItem, 0, len(d2ast.SimpleReservedKeywords))
for keyword := range d2ast.SimpleReservedKeywords {
item := CompletionItem{
Label: keyword,
Kind: KeywordCompletion,
Detail: "keyword",
InsertText: keyword + ": ",
}
items = append(items, item)
}
return items
}

func getBooleanCompletions() []CompletionItem {
return []CompletionItem{
{
Expand Down Expand Up @@ -442,3 +475,52 @@ func getIconCompletions() []CompletionItem {
},
}
}

func getDirectionCompletions() []CompletionItem {
directions := []string{"up", "down", "right", "left"}
items := make([]CompletionItem, len(directions))
for i, dir := range directions {
items[i] = CompletionItem{
Label: dir,
Kind: KeywordCompletion,
Detail: "direction",
InsertText: dir,
}
}
return items
}

func getArrowheadShapeCompletions() []CompletionItem {
arrowheads := []string{
"triangle", // default
"arrow", // like triangle but pointier
"diamond",
"circle",
"cf-one", "cf-one-required",
"cf-many", "cf-many-required",
}

items := make([]CompletionItem, len(arrowheads))
details := map[string]string{
"triangle": "default",
"arrow": "like triangle but pointier",
"cf-one": "crows foot one",
"cf-one-required": "crows foot one (required)",
"cf-many": "crows foot many",
"cf-many-required": "crows foot many (required)",
}

for i, shape := range arrowheads {
detail := details[shape]
if detail == "" {
detail = "arrowhead shape"
}
items[i] = CompletionItem{
Label: shape,
Kind: ShapeCompletion,
Detail: detail,
InsertText: shape,
}
}
return items
}
Loading

0 comments on commit 108b883

Please sign in to comment.