Skip to content

Commit

Permalink
Merge pull request #5 from chrehall68/3-improve-syntax-highlighting-a…
Browse files Browse the repository at this point in the history
…nd-autocomplete

[Improve] Syntax Highlighting and Autocomplete
  • Loading branch information
chrehall68 authored Nov 11, 2024
2 parents 80eeb2b + f314a6e commit 655004a
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 40 deletions.
19 changes: 9 additions & 10 deletions server/internal/lang/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,20 @@ import (
)

type Interpreter struct {
Modules []ModuleNode
builtins map[string]bool
Defines []DefineNode
defines []DefineNode
Diagnostics []protocol.Diagnostic
moduleMap map[string]ModuleNode
Log *zap.Logger
log *zap.Logger
}

func NewInterpreter(logger *zap.Logger, modules map[string][]ModuleNode, defines map[string][]DefineNode) *Interpreter {
moduleMap := map[string]ModuleNode{}
flattenedModules := []ModuleNode{}
flattenedDefines := []DefineNode{}
for _, mods := range modules {
for _, module := range mods {
moduleMap[module.Identifier.Value] = module
}
flattenedModules = append(flattenedModules, mods...)
}
for _, defs := range defines {
flattenedDefines = append(flattenedDefines, defs...)
Expand All @@ -43,11 +40,10 @@ func NewInterpreter(logger *zap.Logger, modules map[string][]ModuleNode, defines
}

return &Interpreter{
Modules: flattenedModules,
Defines: flattenedDefines,
defines: flattenedDefines,
Diagnostics: []protocol.Diagnostic{},
moduleMap: moduleMap,
Log: logger,
log: logger,
builtins: builtins,
}
}
Expand Down Expand Up @@ -141,8 +137,11 @@ func (i *Interpreter) diagnoseInteriorNode(node InteriorNode, curSymbols map[str
return
}

func (i *Interpreter) DiagnoseModule(module ModuleNode) {
func (i *Interpreter) diagnoseModule(module ModuleNode) {
knownSymbols := map[string]bool{}
for _, define := range i.defines {
knownSymbols[define.Identifier.Value] = true
}
for _, statement := range module.Interior {
knownSymbols = i.diagnoseInteriorNode(statement, knownSymbols)
}
Expand All @@ -151,7 +150,7 @@ func (i *Interpreter) DiagnoseModule(module ModuleNode) {
func (i *Interpreter) Interpret(FileNode FileNode) []protocol.Diagnostic {
for _, topLevelStatement := range FileNode.Statements {
if topLevelStatement.Module != nil {
i.DiagnoseModule(*topLevelStatement.Module)
i.diagnoseModule(*topLevelStatement.Module)
}
}

Expand Down
113 changes: 112 additions & 1 deletion server/internal/lang/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ type Parser struct {

func NewParser() *Parser {
return &Parser{
skipTokens: []string{ "whitespace","comment", "newline"},
skipTokens: []string{"whitespace", "comment", "newline"},
}
}

Expand Down Expand Up @@ -1789,6 +1789,117 @@ func (p *Parser) ParseFile(tokens []Token) (result FileNode, err error) {

return
}
func getInteriorStatementsFromAlwaysStatements(statements []AlwaysStatement) []InteriorNode {
var result []InteriorNode
for _, statement := range statements {
result = append(result, getInteriorStatementsFromAlwaysStatement(statement)...)
}
return result
}
func getInteriorStatementsFromAlwaysStatement(statement AlwaysStatement) []InteriorNode {
var result []InteriorNode
if statement.BeginBlock != nil {
result = append(result, getInteriorStatementsFromAlwaysStatements(statement.BeginBlock.Statements)...)
} else if statement.CaseNode != nil {
// add regular cases
for _, caseNode := range statement.CaseNode.Cases {
result = append(result, getInteriorStatementsFromAlwaysStatement(caseNode.Statement)...)
}
// add default case
if statement.CaseNode.Default != nil {
result = append(result, getInteriorStatementsFromAlwaysStatement(*statement.CaseNode.Default)...)
}
} else if statement.ForBlock != nil {
result = append(result, getInteriorStatementsFromAlwaysStatement(statement.ForBlock.Body)...)
} else if statement.IfBlock != nil {
result = append(result, getInteriorStatementsFromAlwaysStatement(statement.IfBlock.Body)...)
} else if statement.InteriorNode != nil {
result = append(result, getInteriorStatementsFromInteriorNode(*statement.InteriorNode)...)
}
return result
}
func getInteriorStatementsFromInteriorNode(interiorNode InteriorNode) []InteriorNode {
var result []InteriorNode
if interiorNode.AlwaysNode != nil {
result = append(result, getInteriorStatementsFromAlwaysStatement(interiorNode.AlwaysNode.Statement)...)
} else if interiorNode.GenerateNode != nil {
result = append(result, getInteriorStatementsFromAlwaysStatements(interiorNode.GenerateNode.Statements)...)
} else if interiorNode.InitialNode != nil {
result = append(result, getInteriorStatementsFromAlwaysStatement(interiorNode.InitialNode.Statement)...)
} else {
// this belongs to the result
result = append(result, interiorNode)
}
return result
}

func GetInteriorStatements(fileNode FileNode) []InteriorNode {
var result []InteriorNode
for _, statements := range fileNode.Statements {
if statements.Module != nil {
interior := statements.Module.Interior
for _, statement := range interior {
result = append(result, getInteriorStatementsFromInteriorNode(statement)...)
}
}
}
return result
}
func getFunctionStatementsFromAlwaysStatements(statements []AlwaysStatement) []FunctionNode {
var result []FunctionNode
for _, statement := range statements {
result = append(result, getFunctionStatementsFromAlwaysStatement(statement)...)
}
return result
}
func getFunctionStatementsFromAlwaysStatement(statement AlwaysStatement) []FunctionNode {
var result []FunctionNode
if statement.BeginBlock != nil {
result = append(result, getFunctionStatementsFromAlwaysStatements(statement.BeginBlock.Statements)...)
} else if statement.CaseNode != nil {
// add regular cases
for _, caseNode := range statement.CaseNode.Cases {
result = append(result, getFunctionStatementsFromAlwaysStatement(caseNode.Statement)...)
}
// add default case
if statement.CaseNode.Default != nil {
result = append(result, getFunctionStatementsFromAlwaysStatement(*statement.CaseNode.Default)...)
}
} else if statement.ForBlock != nil {
result = append(result, getFunctionStatementsFromAlwaysStatement(statement.ForBlock.Body)...)
} else if statement.FunctionNode != nil {
result = append(result, *statement.FunctionNode)
} else if statement.IfBlock != nil {
result = append(result, getFunctionStatementsFromAlwaysStatement(statement.IfBlock.Body)...)
} else if statement.InteriorNode != nil {
result = append(result, getFunctionStatementsFromInteriorNode(*statement.InteriorNode)...)
}
return result
}
func getFunctionStatementsFromInteriorNode(interiorNode InteriorNode) []FunctionNode {
var result []FunctionNode
if interiorNode.AlwaysNode != nil {
result = append(result, getFunctionStatementsFromAlwaysStatement(interiorNode.AlwaysNode.Statement)...)
} else if interiorNode.GenerateNode != nil {
result = append(result, getFunctionStatementsFromAlwaysStatements(interiorNode.GenerateNode.Statements)...)
} else if interiorNode.InitialNode != nil {
result = append(result, getFunctionStatementsFromAlwaysStatement(interiorNode.InitialNode.Statement)...)
}
return result
}

func GetFunctionNodes(fileNode FileNode) []FunctionNode {
var result []FunctionNode
for _, statements := range fileNode.Statements {
if statements.Module != nil {
interior := statements.Module.Interior
for _, statement := range interior {
result = append(result, getFunctionStatementsFromInteriorNode(statement)...)
}
}
}
return result
}

/**
Grammar:
Expand Down
20 changes: 17 additions & 3 deletions server/internal/vlsp/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@ package vlsp

import (
"context"
"fmt"
"strings"

"github.com/chrehall68/vls/internal/lang"
"github.com/chrehall68/vls/internal/mappers"
"go.lsp.dev/protocol"
)

func (h Handler) formatModuleApplication(module lang.ModuleNode) string {
params := make([]string, len(module.PortList.Ports))
i := 2
for _, param := range module.PortList.Ports {
params[i-2] = fmt.Sprintf(".%s($%d)", param.Value, i)
i++
}
return fmt.Sprintf("%s ${1:name} (%s);", module.Identifier.Value, strings.Join(params, ", "))
}

func (h Handler) Completion(ctx context.Context, params *protocol.CompletionParams) (result *protocol.CompletionList, err error) {
h.state.log.Sugar().Infof("Completion called")
var completionItems []protocol.CompletionItem
Expand All @@ -31,9 +44,10 @@ func (h Handler) Completion(ctx context.Context, params *protocol.CompletionPara
for _, modules := range h.state.modules {
for _, module := range modules {
completionItems = append(completionItems, protocol.CompletionItem{
Label: module.Identifier.Value,
Detail: "module",
InsertText: module.Identifier.Value,
Label: module.Identifier.Value,
Detail: "module",
InsertText: h.formatModuleApplication(module),
InsertTextFormat: protocol.InsertTextFormatSnippet,
})
}
}
Expand Down
112 changes: 88 additions & 24 deletions server/internal/vlsp/semtokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ func GetSemanticTokensOptions() SemanticTokensOptions {
return SemanticTokensOptions{
Legend: protocol.SemanticTokensLegend{
TokenTypes: []protocol.SemanticTokenTypes{
protocol.SemanticTokenType, // 0
protocol.SemanticTokenComment, // 1
protocol.SemanticTokenNumber, // 2
protocol.SemanticTokenMacro, // 3
protocol.SemanticTokenVariable, // 4
protocol.SemanticTokenType, // 0
protocol.SemanticTokenComment, // 1
protocol.SemanticTokenNumber, // 2
protocol.SemanticTokenMacro, // 3
protocol.SemanticTokenVariable, // 4
protocol.SemanticTokenClass, // 5
protocol.SemanticTokenParameter, // 6
protocol.SemanticTokenFunction, // 7
},
TokenModifiers: []protocol.SemanticTokenModifiers{},
},
Expand All @@ -51,25 +54,36 @@ func Encode(tokens []lang.Token) []uint32 {
// maps token type to int
// the tokens to ignore are not in here
tokenTypeToInt := map[string]uint32{
"comment": 1,
"type": 0,
"literal": 2,
"module": 3,
"endmodule": 3,
"begin": 3,
"end": 3,
"case": 3,
"endcase": 3,
"generate": 3,
"endgenerate": 3,
"for": 3,
"if": 3,
"else": 3,
"assign": 3,
"initial": 3,
"time": 3,
"default": 3,
"identifier": 4,
"comment": 1,
"type": 0,
"defparam": 0,
"literal": 2,
"module": 3,
"endmodule": 3,
"begin": 3,
"end": 3,
"case": 3,
"endcase": 3,
"generate": 3,
"endgenerate": 3,
"for": 3,
"if": 3,
"else": 3,
"assign": 3,
"initial": 3,
"always": 3,
"time": 3,
"default": 3,
"include": 3,
"timescale": 3,
"define": 3,
"identifier": 4,
"existing_module": 5,
"port": 6,
"funcliteral": 7,
"signed": 7,
"dollar": 7,
"pound": 7,
}

addToken := func(token lang.Token) {
Expand Down Expand Up @@ -108,6 +122,56 @@ func (h Handler) SemanticTokensFull(ctx context.Context, params *protocol.Semant
lexer := lang.NewVLexer(h.state.log)
tokens, _ := lexer.Lex(contents)

// extract ast if possible
ast, err := lang.NewParser().ParseFile(tokens)
if err == nil {
h.state.log.Sugar().Info("Getting statements for file: ", err)
interiorNodes := lang.GetInteriorStatements(ast)
tokensIdx := 0

for _, interiorNode := range interiorNodes {
if interiorNode.ModuleApplicationNode != nil {
// get to the current module
for tokens[tokensIdx] != interiorNode.ModuleApplicationNode.ModuleName && tokensIdx < len(tokens) {
tokensIdx++
}

// then label it as a module name
if tokensIdx < len(tokens) {
tokens[tokensIdx].Type = "existing_module"
}

for _, argument := range interiorNode.ModuleApplicationNode.Arguments {
if argument.Label != nil {
// get to this label and label it as a port
for tokens[tokensIdx] != *argument.Label && tokensIdx < len(tokens) {
tokensIdx++
}

if tokensIdx < len(tokens) {
tokens[tokensIdx].Type = "port"
}
}
}
}
}

// do similar thing for functions
tokensIdx = 0
functionNodes := lang.GetFunctionNodes(ast)

for _, functionNode := range functionNodes {
// get to the function name
for tokens[tokensIdx] != functionNode.Function && tokensIdx < len(tokens) {
tokensIdx++
}

if tokensIdx < len(tokens) {
tokens[tokensIdx].Type = "funcliteral"
}
}
}

// encode
result := &protocol.SemanticTokens{
Data: Encode(tokens),
Expand Down
14 changes: 12 additions & 2 deletions server/internal/vlsp/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ func (h Handler) DidChange(ctx context.Context, params *protocol.DidChangeTextDo

if strings.HasSuffix(file, ".v") {
// update file
h.state.files[file].SetContents(params.ContentChanges[len(params.ContentChanges)-1].Text)
fnode, ok := h.state.files[file]
if !ok {
h.state.files[file] = NewFile(file)
fnode = h.state.files[file]
}
fnode.SetContents(params.ContentChanges[len(params.ContentChanges)-1].Text)

// update symbols
h.GetSymbolsForFile(file, false)
Expand All @@ -26,7 +31,12 @@ func (h Handler) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocume

if strings.HasSuffix(file, ".v") {
// update file
h.state.files[file].SetContents(params.TextDocument.Text)
fnode, ok := h.state.files[file]
if !ok {
h.state.files[file] = NewFile(file)
fnode = h.state.files[file]
}
fnode.SetContents(params.TextDocument.Text)

// update symbols
h.GetSymbolsForFile(file, false)
Expand Down

0 comments on commit 655004a

Please sign in to comment.