Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(yamlls): add config for yamlls enable per filepattern #93

Merged
merged 1 commit into from
Jul 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 29 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,37 @@
\/ /_/ \___|_|_| |_| |_\____/___/
</pre>

## Helm Language Server
## Helm Language Server

Helm-ls is a [helm](https://github.com/helm/helm) language server protocol [LSP](https://microsoft.github.io/language-server-protocol/) implementation.

<!-- vim-markdown-toc GFM -->

* [Demo](#demo)
* [Getting Started](#getting-started)
* [Installation with a package manager](#installation-with-a-package-manager)
* [Homebrew](#homebrew)
* [Nix](#nix)
* [Arch Linux](#arch-linux)
* [Windows](#windows)
* [mason (neovim)](#mason-neovim)
* [Manual download](#manual-download)
* [Make it executable](#make-it-executable)
* [Integration with yaml-language-server](#integration-with-yaml-language-server)
* [Configuration options](#configuration-options)
* [General](#general)
* [Values Files](#values-files)
* [yaml-language-server config](#yaml-language-server-config)
* [Default Configuration](#default-configuration)
* [Editor Config examples](#editor-config-examples)
* [Neovim](#neovim-using-nvim-lspconfig)
* [Vim Helm Plugin](#vim-helm-plugin)
* [nvim-lspconfig setup](#nvim-lspconfig-setup)
* [coc.nvim setup](#cocnvim-setup)
* [VSCode](#vscode)
* [Emacs eglot setup](#emacs-eglot-setup)
* [Contributing](#contributing)
* [License](#license)
- [Demo](#demo)
- [Getting Started](#getting-started)
- [Installation with a package manager](#installation-with-a-package-manager)
- [Homebrew](#homebrew)
- [Nix](#nix)
- [Arch Linux](#arch-linux)
- [Windows](#windows)
- [mason (neovim)](#mason-neovim)
- [Manual download](#manual-download)
- [Make it executable](#make-it-executable)
- [Integration with yaml-language-server](#integration-with-yaml-language-server)
- [Configuration options](#configuration-options)
- [General](#general)
- [Values Files](#values-files)
- [yaml-language-server config](#yaml-language-server-config)
- [Default Configuration](#default-configuration)
- [Editor Config examples](#editor-config-examples)
- [Neovim](#neovim)
- [Vim Helm Plugin](#vim-helm-plugin)
- [nvim-lspconfig setup](#nvim-lspconfig-setup)
- [coc.nvim setup](#cocnvim-setup)
- [VSCode](#vscode)
- [Emacs eglot setup](#emacs-eglot-setup)
- [Contributing](#contributing)
- [License](#license)

<!-- vim-markdown-toc -->

Expand Down Expand Up @@ -78,7 +78,7 @@ You can install it from the [aur](https://aur.archlinux.org/packages/helm-ls/) u

```bash
yay -S helm-ls
# or
# or
yay -S helm-ls-bin
```

Expand Down Expand Up @@ -157,6 +157,7 @@ You can configure helm-ls with lsp workspace configurations.
### yaml-language-server config

- **Enable yaml-language-server**: Toggle support of this feature.
- **EnabledForFilesGlob**: A glob pattern defining for which files yaml-language-server should be enabled.
- **Path to yaml-language-server**: Specify the executable location.
- **Diagnostics Settings**:

Expand All @@ -181,6 +182,7 @@ settings = {
},
yamlls = {
enabled = true,
enabledForFilesGlob = "*.{yaml,yml}",
diagnosticsLimit = 50,
showDiagnosticsDirectly = false,
path = "yaml-language-server",
Expand Down
2 changes: 1 addition & 1 deletion internal/adapter/yamlls/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

func (yamllsConnector Connector) CallCompletion(ctx context.Context, params *lsp.CompletionParams) (*lsp.CompletionList, error) {
if yamllsConnector.server == nil {
if !yamllsConnector.shouldRun(params.TextDocumentPositionParams.TextDocument.URI) {
return &lsp.CompletionList{}, nil
}

Expand Down
5 changes: 5 additions & 0 deletions internal/adapter/yamlls/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,28 @@ func (c Connector) PublishDiagnostics(ctx context.Context, params *protocol.Publ

func filterDiagnostics(diagnostics []lsp.Diagnostic, ast *sitter.Tree, content string) (filtered []lsp.Diagnostic) {
filtered = []lsp.Diagnostic{}

for _, diagnostic := range diagnostics {
node := lsplocal.NodeAtPosition(ast, diagnostic.Range.Start)
childNode := lsplocal.FindRelevantChildNode(ast.RootNode(), lsplocal.GetSitterPointForLspPos(diagnostic.Range.Start))

if node.Type() == "text" && childNode.Type() == "text" {
logger.Debug("Diagnostic", diagnostic)
logger.Debug("Node", node.Content([]byte(content)))

if diagnisticIsRelevant(diagnostic, childNode) {
diagnostic.Message = "Yamlls: " + diagnostic.Message
filtered = append(filtered, diagnostic)
}
}
}

return filtered
}

func diagnisticIsRelevant(diagnostic lsp.Diagnostic, node *sitter.Node) bool {
logger.Debug("Checking if diagnostic is relevant", diagnostic.Message)

switch diagnostic.Message {
case "Map keys must be unique":
return !lsplocal.IsInElseBranch(node)
Expand Down
17 changes: 13 additions & 4 deletions internal/adapter/yamlls/documentSync.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ func (yamllsConnector Connector) InitiallySyncOpenDocuments(docs []*lsplocal.Doc
if yamllsConnector.server == nil {
return
}

for _, doc := range docs {
if !doc.IsOpen {
continue
}

doc.IsYaml = lsplocal.IsYamlDocument(doc.URI, yamllsConnector.config)
if !yamllsConnector.isRelevantFile(doc.URI) {
continue
}

yamllsConnector.DocumentDidOpen(doc.Ast, lsp.DidOpenTextDocumentParams{
TextDocument: lsp.TextDocumentItem{
URI: doc.URI,
Expand All @@ -28,7 +35,8 @@ func (yamllsConnector Connector) InitiallySyncOpenDocuments(docs []*lsplocal.Doc

func (yamllsConnector Connector) DocumentDidOpen(ast *sitter.Tree, params lsp.DidOpenTextDocumentParams) {
logger.Debug("YamllsConnector DocumentDidOpen", params.TextDocument.URI)
if yamllsConnector.server == nil {

if !yamllsConnector.shouldRun(params.TextDocument.URI) {
return
}
params.TextDocument.Text = lsplocal.TrimTemplate(ast, params.TextDocument.Text)
Expand All @@ -40,9 +48,10 @@ func (yamllsConnector Connector) DocumentDidOpen(ast *sitter.Tree, params lsp.Di
}

func (yamllsConnector Connector) DocumentDidSave(doc *lsplocal.Document, params lsp.DidSaveTextDocumentParams) {
if yamllsConnector.server == nil {
if !yamllsConnector.shouldRun(doc.URI) {
return
}

params.Text = lsplocal.TrimTemplate(doc.Ast, doc.Content)

err := yamllsConnector.server.DidSave(context.Background(), &params)
Expand All @@ -58,7 +67,7 @@ func (yamllsConnector Connector) DocumentDidSave(doc *lsplocal.Document, params
}

func (yamllsConnector Connector) DocumentDidChange(doc *lsplocal.Document, params lsp.DidChangeTextDocumentParams) {
if yamllsConnector.server == nil {
if !yamllsConnector.shouldRun(doc.URI) {
return
}
trimmedText := lsplocal.TrimTemplate(doc.Ast, doc.Content)
Expand Down Expand Up @@ -87,7 +96,7 @@ func (yamllsConnector Connector) DocumentDidChange(doc *lsplocal.Document, param
}

func (yamllsConnector Connector) DocumentDidChangeFullSync(doc *lsplocal.Document, params lsp.DidChangeTextDocumentParams) {
if yamllsConnector.server == nil {
if !yamllsConnector.shouldRun(doc.URI) {
return
}

Expand Down
2 changes: 1 addition & 1 deletion internal/adapter/yamlls/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
// Yamlls can not handle hover if the schema validation returns error,
// thats why we fall back to calling completion
func (yamllsConnector Connector) CallHover(ctx context.Context, params lsp.HoverParams, word string) (*lsp.Hover, error) {
if yamllsConnector.server == nil {
if !yamllsConnector.shouldRun(params.TextDocumentPositionParams.TextDocument.URI) {
return &lsp.Hover{}, nil
}

Expand Down
17 changes: 17 additions & 0 deletions internal/adapter/yamlls/yamlls.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"github.com/mrjosh/helm-ls/internal/util"
"go.lsp.dev/jsonrpc2"
"go.lsp.dev/protocol"
lsp "go.lsp.dev/protocol"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -64,7 +65,7 @@
}

go func() {
io.Copy(os.Stderr, strderr)

Check failure on line 68 in internal/adapter/yamlls/yamlls.go

View workflow job for this annotation

GitHub Actions / lint (1.21.5, ubuntu-latest)

Error return value of `io.Copy` is not checked (errcheck)
}()

yamllsConnector := Connector{documents: documents, config: yamllsConfiguration, client: client}
Expand All @@ -75,3 +76,19 @@
yamllsConnector.server = server
return &yamllsConnector
}

func (yamllsConnector *Connector) isRelevantFile(uri lsp.URI) bool {
doc, ok := yamllsConnector.documents.Get(uri)
if !ok {
logger.Error("Could not find document", uri)
return true
}
return doc.IsYaml
}

func (yamllsConnector *Connector) shouldRun(uri lsp.DocumentURI) bool {
if yamllsConnector.server == nil {
return false
}
return yamllsConnector.isRelevantFile(uri)
}
37 changes: 37 additions & 0 deletions internal/adapter/yamlls/yamlls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package yamlls

import (
"testing"

lsplocal "github.com/mrjosh/helm-ls/internal/lsp"
"github.com/mrjosh/helm-ls/internal/util"
"github.com/stretchr/testify/assert"
"go.lsp.dev/uri"
)

func TestIsRelevantFile(t *testing.T) {
connector := Connector{
config: util.YamllsConfiguration{
Enabled: true,
},
}

connector.documents = &lsplocal.DocumentStore{}
yamlFile := uri.File("../../../testdata/example/templates/deployment.yaml")
nonYamlFile := uri.File("../../../testdata/example/templates/_helpers.tpl")
connector.documents.Store(yamlFile, util.DefaultConfig)
connector.documents.Store(nonYamlFile, util.DefaultConfig)

assert.True(t, connector.isRelevantFile(yamlFile))
assert.False(t, connector.isRelevantFile(nonYamlFile))
}

func TestShouldRun(t *testing.T) {
connector := Connector{
config: util.YamllsConfiguration{
Enabled: true,
},
}
assert.False(t, connector.shouldRun(uri.File("test.yaml")))
assert.False(t, connector.shouldRun(uri.File("_helpers.tpl")))
}
16 changes: 14 additions & 2 deletions internal/handler/initialization.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"os"

"github.com/gobwas/glob"
"github.com/mrjosh/helm-ls/internal/adapter/yamlls"
"github.com/mrjosh/helm-ls/internal/charts"
"github.com/mrjosh/helm-ls/internal/util"
Expand Down Expand Up @@ -58,17 +59,28 @@ func (h *langHandler) Initialized(ctx context.Context, _ *lsp.InitializedParams)
func (h *langHandler) initializationWithConfig(ctx context.Context) {
configureLogLevel(h.helmlsConfig)
h.chartStore.SetValuesFilesConfig(h.helmlsConfig.ValuesFilesConfig)
configureYamlls(ctx, h)
h.configureYamlls(ctx)
}

func configureYamlls(ctx context.Context, h *langHandler) {
func (h *langHandler) configureYamlsEnabledGlob() {
globObject, err := glob.Compile(h.helmlsConfig.YamllsConfiguration.EnabledForFilesGlob)
if err != nil {
logger.Error("Error compiling glob for yamlls EnabledForFilesGlob", err)
globObject = util.DefaultConfig.YamllsConfiguration.EnabledForFilesGlobObject
}
h.helmlsConfig.YamllsConfiguration.EnabledForFilesGlobObject = globObject
}

func (h *langHandler) configureYamlls(ctx context.Context) {
config := h.helmlsConfig
if config.YamllsConfiguration.Enabled {
h.configureYamlsEnabledGlob()
h.yamllsConnector = yamlls.NewConnector(ctx, config.YamllsConfiguration, h.client, h.documents)
err := h.yamllsConnector.CallInitialize(ctx, h.chartStore.RootURI)
if err != nil {
logger.Error("Error initializing yamlls", err)
}

h.yamllsConnector.InitiallySyncOpenDocuments(h.documents.GetAllDocs())
}
}
Expand Down
1 change: 1 addition & 0 deletions internal/lsp/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Document struct {
DiagnosticsCache DiagnosticsCache
IsOpen bool
SymbolTable *SymbolTable
IsYaml bool
}

// ApplyChanges updates the content of the document from LSP textDocument/didChange events.
Expand Down
12 changes: 12 additions & 0 deletions internal/lsp/document_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,19 @@ func (s *DocumentStore) DidOpen(params *lsp.DidOpenTextDocumentParams, helmlsCon
DiagnosticsCache: NewDiagnosticsCache(helmlsConfig),
IsOpen: true,
SymbolTable: NewSymbolTable(ast, []byte(params.TextDocument.Text)),
IsYaml: IsYamlDocument(uri, helmlsConfig.YamllsConfiguration),
}
logger.Debug("Storing doc ", path)
s.documents.Store(path, doc)
return doc, nil
}

func (s *DocumentStore) Store(uri uri.URI, helmlsConfig util.HelmlsConfiguration) error {
_, ok := s.documents.Load(uri.Filename())
if ok {
return nil
}

content, err := os.ReadFile(uri.Filename())
if err != nil {
logger.Error("Could not open file ", uri.Filename(), " ", err)
Expand All @@ -67,6 +73,7 @@ func (s *DocumentStore) Store(uri uri.URI, helmlsConfig util.HelmlsConfiguration
DiagnosticsCache: NewDiagnosticsCache(helmlsConfig),
IsOpen: false,
SymbolTable: NewSymbolTable(ast, content),
IsYaml: IsYamlDocument(uri, helmlsConfig.YamllsConfiguration),
},
)
return nil
Expand All @@ -75,8 +82,13 @@ func (s *DocumentStore) Store(uri uri.URI, helmlsConfig util.HelmlsConfiguration
func (s *DocumentStore) Get(docuri uri.URI) (*Document, bool) {
path := docuri.Filename()
d, ok := s.documents.Load(path)

if !ok {
return nil, false
}
return d.(*Document), ok
}

func IsYamlDocument(uri lsp.URI, yamllsConfiguration util.YamllsConfiguration) bool {
return yamllsConfiguration.EnabledForFilesGlobObject.Match(uri.Filename())
}
17 changes: 17 additions & 0 deletions internal/lsp/document_store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package lsp

import (
"testing"

"github.com/mrjosh/helm-ls/internal/util"
"github.com/stretchr/testify/assert"
"go.lsp.dev/uri"
)

func TestIsYamlDocument(t *testing.T) {
assert := assert.New(t)
assert.True(IsYamlDocument(uri.File("test.yaml"), util.DefaultConfig.YamllsConfiguration))
assert.False(IsYamlDocument(uri.File("test.tpl"), util.DefaultConfig.YamllsConfiguration))
assert.True(IsYamlDocument(uri.File("../../testdata/example/templates/hpa.yaml"), util.DefaultConfig.YamllsConfiguration))
assert.False(IsYamlDocument(uri.File("../../testdata/example/templates/_helpers.tpl"), util.DefaultConfig.YamllsConfiguration))
}
Loading
Loading