From e03cbfa79bf5941113fc2a889c313344031b4d87 Mon Sep 17 00:00:00 2001 From: qvalentin <valentin.theodor@web.de> Date: Thu, 4 Jul 2024 17:27:20 +0200 Subject: [PATCH] perf(ast): incremental parsing of ts ast --- internal/lsp/ast.go | 4 ++- internal/lsp/document.go | 17 +++++++++++- internal/lsp/document_test.go | 52 +++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/internal/lsp/ast.go b/internal/lsp/ast.go index c258829b..e918b00b 100644 --- a/internal/lsp/ast.go +++ b/internal/lsp/ast.go @@ -8,6 +8,7 @@ import ( lsp "go.lsp.dev/protocol" ) +// TODO: use byte instead of string func ParseAst(oldTree *sitter.Tree, content string) *sitter.Tree { parser := sitter.NewParser() parser.SetLanguage(gotemplate.GetLanguage()) @@ -69,7 +70,8 @@ func isPointLargerOrEq(a sitter.Point, b sitter.Point) bool { return a.Row > b.Row } -func (d *Document) ApplyChangesToAst(newContent string) { +func (d *Document) ApplyChangesToAst(editInput sitter.EditInput, newContent string) { + d.Ast.Edit(editInput) d.Ast = ParseAst(nil, newContent) } diff --git a/internal/lsp/document.go b/internal/lsp/document.go index 70265950..02cd51cb 100644 --- a/internal/lsp/document.go +++ b/internal/lsp/document.go @@ -35,15 +35,30 @@ func (d *Document) ApplyChanges(changes []lsp.TextDocumentContentChangeEvent) { for _, change := range changes { start, end := util.PositionToIndex(change.Range.Start, content), util.PositionToIndex(change.Range.End, content) + newEnd := start + len(change.Text) + var buf bytes.Buffer + buf.Write(content[:start]) buf.Write([]byte(change.Text)) buf.Write(content[end:]) + content = buf.Bytes() + + editInput := sitter.EditInput{ + StartIndex: uint32(start), + OldEndIndex: uint32(end), + NewEndIndex: uint32(newEnd), + StartPoint: util.PositionToPoint(change.Range.Start), + OldEndPoint: util.PositionToPoint(change.Range.End), + NewEndPoint: util.PositionToPoint(lsp.Position{Line: change.Range.Start.Line, Character: change.Range.Start.Character + uint32(len(change.Text))}), + } + + d.Ast.Edit(editInput) } d.Content = string(content) - d.ApplyChangesToAst(d.Content) + d.Ast = ParseAst(d.Ast, string(content)) d.SymbolTable = NewSymbolTable(d.Ast, []byte(d.Content)) d.lines = nil diff --git a/internal/lsp/document_test.go b/internal/lsp/document_test.go index c09d0ca3..6ff2fa86 100644 --- a/internal/lsp/document_test.go +++ b/internal/lsp/document_test.go @@ -35,3 +35,55 @@ func TestDocumentStore(t *testing.T) { assert.NotNil(doc) assert.True(ok) } + +func TestApplyChanges(t *testing.T) { + assert := assert.New(t) + + documentStore := NewDocumentStore() + documentStore.DidOpen(&protocol.DidOpenTextDocumentParams{ + TextDocument: protocol.TextDocumentItem{ + URI: uri.File("test.yaml"), + LanguageID: "helm", + Text: `{{ .Values.test }}`, + }, + }, util.DefaultConfig) + + doc, ok := documentStore.Get(uri.File("test.yaml")) + assert.True(ok) + assert.Equal("{{ .Values.test }}", doc.Content) + + doc.ApplyChanges([]protocol.TextDocumentContentChangeEvent{ + {Range: protocol.Range{Start: protocol.Position{Line: 0, Character: 18}, End: protocol.Position{Line: 0, Character: 18}}, Text: "\n"}, + {Range: protocol.Range{Start: protocol.Position{Line: 1, Character: 0}, End: protocol.Position{Line: 1, Character: 0}}, Text: "\n"}, + {Range: protocol.Range{Start: protocol.Position{Line: 1, Character: 0}, End: protocol.Position{Line: 1, Character: 0}}, Text: "spec:\n replicas: {{ .Values.replicaCount }}\n selector:\n matchLabels:\n {{- include \"hello-world.selectorLabels\" . | nindent 6 }}\n template:\n metadata:\n labels:"}, + {Range: protocol.Range{Start: protocol.Position{Line: 8, Character: 13}, End: protocol.Position{Line: 9, Character: 0}}, Text: "\n \n"}, + {Range: protocol.Range{Start: protocol.Position{Line: 9, Character: 6}, End: protocol.Position{Line: 9, Character: 0}}, Text: "{{- if .Values.serviceAccount.create -}}\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: {{ include \"hello-world.serviceAccountName\" . }}\n labels:\n {{- include \"hello-world.labels\" . | nindent 4 }}\n {{- with .Values.serviceAccount.annotations }}\n annotations:\n {{- toYaml . | nindent 4 }}\n {{- end }}\n{{- end }}"}, + {Range: protocol.Range{Start: protocol.Position{Line: 17, Character: 0}, End: protocol.Position{Line: 17, Character: 0}}, Text: ""}, + {Range: protocol.Range{Start: protocol.Position{Line: 18, Character: 0}, End: protocol.Position{Line: 19, Character: 0}}, Text: ""}, + }) + + print(doc.Content) + expected := `{{ .Values.test }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "hello-world.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "hello-world.serviceAccountName" . }} + labels: + {{- include "hello-world.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- end }} +{{- end }} +` + + assert.Equal(expected, doc.Content) +}