Skip to content

Commit d26c755

Browse files
feat: add additional LSP support for YAML by adding helm-ls support (#4665)
* feat: add `helm-ls` support * update: docs * update: CHANGELOG.org * fix: replace `:major-modes` with `:activation-fn` * refact: prepend `YAML` to naming in docs * feat: set priority to 0 * fix: misnamed variables * fix: `let` formatting and variable names --------- Co-authored-by: Jen-Chieh Shen <[email protected]>
1 parent f8fce0d commit d26c755

File tree

4 files changed

+345
-0
lines changed

4 files changed

+345
-0
lines changed

CHANGELOG.org

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* Add support for [[https://github.com/nextflow-io/language-server][Nextflow]]
2626
* Add TypeSpec support
2727
* Add Tree-sitter query support
28+
* Add [[https://github.com/mrjosh/helm-ls][helm-ls]] (YAML Kubernetes Helm) support.
2829

2930
** 9.0.0
3031
* Add language server config for QML (Qt Modeling Language) using qmlls.

clients/lsp-kubernetes-helm.el

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
;;; lsp-kubernetes-helm.el --- LSP YAML server integration -*- lexical-binding: t; -*-
2+
3+
;; Copyright (C) 2024 Aaron Gonzales
4+
5+
;; Author: Aaron Gonzales <[email protected]>
6+
;; Keywords: lsp, kubernetes, helm, yaml
7+
8+
;; This program is free software; you can redistribute it and/or modify
9+
;; it under the terms of the GNU General Public License as published by
10+
;; the Free Software Foundation, either version 3 of the License, or
11+
;; (at your option) any later version.
12+
13+
;; This program is distributed in the hope that it will be useful,
14+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
;; GNU General Public License for more details.
17+
18+
;; You should have received a copy of the GNU General Public License
19+
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
21+
;;; Commentary:
22+
23+
;;
24+
25+
;;; Code:
26+
27+
(require 'lsp-mode)
28+
(require 'dash)
29+
30+
(defgroup lsp-kubernetes-helm nil
31+
"LSP support for YAML, using Helm Language Server (helm-ls)."
32+
:group 'lsp-mode
33+
:link '(url-link "https://github.com/mrjosh/helm-ls")
34+
:package-version '(lsp-mode . "9.0.0"))
35+
36+
(defconst lsp-kubernetes-helm--lsp-configuration-section-name "helm-ls"
37+
"Key used to grab the lsp configuration section for helm-ls.")
38+
39+
(defcustom lsp-kubernetes-helm-ls-server-path "helm_ls"
40+
"Path to the Helm Language Server binary."
41+
:group 'lsp-kubernetes-helm
42+
:risky t
43+
:type 'file)
44+
45+
(defcustom lsp-kubernetes-helm-ls-log-level "info"
46+
"Options for the log level of the Helm Language Server."
47+
:group 'lsp-kubernetes-helm
48+
:type '(choice
49+
(const "trace")
50+
(const "debug")
51+
(const "info")
52+
(const "warning")
53+
(const "error")
54+
(const "fatal")
55+
(const "panic"))
56+
:package-version '(lsp-mode . "9.0.0"))
57+
58+
(defcustom lsp-kubernetes-helm-ls-main-values-file-path "values.yaml"
59+
"Path to main values file for Helm Chart."
60+
:group 'lsp-kubernetes-helm
61+
:type 'file
62+
:package-version '(lsp-mode . "9.0.0"))
63+
64+
(defcustom lsp-kubernetes-helm-overlay-values-file-path "values.lint.yaml"
65+
"Path to values file that may be merged with main values files for Helm Chart."
66+
:group 'lsp-kubernetes-helm
67+
:type 'file
68+
:package-version '(lsp-mode . "9.0.0"))
69+
70+
(defcustom lsp-kubernetes-helm-additional-values-files-pattern "values*.yaml"
71+
"Pattern for additional values files, which will be shown for completion and hover."
72+
:group 'lsp-kubernetes-helm
73+
:type 'string
74+
:package-version '(lsp-mode . "9.0.0"))
75+
76+
(defcustom lsp-kubernetes-helm-yaml-ls-server-path "yaml-language-server"
77+
"Path to the Yaml Language Server binary that supports the Helm Language Server."
78+
:group 'lsp-kubernetes-helm
79+
:link '(url-link :tag "Yaml Language Server"
80+
"https://github.com/redhat-developer/yaml-language-server")
81+
:risky t
82+
:type 'file)
83+
84+
(defcustom lsp-kubernetes-helm-yaml-ls-enable t
85+
"Enable/disable default YAML Language Server."
86+
:group 'lsp-kubernetes-helm
87+
:type 'boolean
88+
:package-version '(lsp-mode . "9.0.0"))
89+
90+
(defcustom lsp-kubernetes-helm-yaml-ls-enable-for-globs "*.{yaml,yml}"
91+
"Enable/disable default YAML Language Server."
92+
:group 'lsp-kubernetes-helm
93+
:type 'string
94+
:package-version '(lsp-mode . "9.0.0"))
95+
96+
(defcustom lsp-kubernetes-helm-yaml-ls-diagnostics-limit 25
97+
"Limit the amount of yaml diagnostics to return.
98+
Should typically be set to a low number when editing helm files."
99+
:group 'lsp-kubernetes-helm
100+
:type 'number
101+
:package-version '(lsp-mode . "9.0.0"))
102+
103+
(defcustom lsp-kubernetes-helm-yaml-ls-schemas '((https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.30.3-standalone-strict/all.json
104+
. ["*.y*"])
105+
(kubernetes . [""]))
106+
"List used by yaml language server to match schemas to globs.
107+
This list is prioritized over the schema store schemas. Recommended to set
108+
kubernetes to an empty string and at the end of the list to override the
109+
default provided in yaml-language-server."
110+
:group 'lsp-kubernetes-helm
111+
:type '(alist :key-type (symbol :tag "schema") :value-type (lsp-repeatable-vector :tag "files (glob)"))
112+
:package-version '(lsp-mode . "9.0.0"))
113+
114+
(defcustom lsp-kubernetes-helm-yaml-ls-format-enable t
115+
"Enable/disable default YAML formatter."
116+
:group 'lsp-kubernetes-helm
117+
:type 'boolean
118+
:package-version '(lsp-mode . "9.0.0"))
119+
120+
(defcustom lsp-kubernetes-helm-yaml-ls-single-quote nil
121+
"Use single quote instead of double quotes."
122+
:group 'lsp-kubernetes-helm
123+
:type 'boolean
124+
:package-version '(lsp-mode . "9.0.0"))
125+
126+
(defcustom lsp-kubernetes-helm-yaml-ls-bracket-spacing t
127+
"Print spaces between brackets in objects."
128+
:group 'lsp-kubernetes-helm
129+
:type 'boolean
130+
:package-version '(lsp-mode . "9.0.0"))
131+
132+
(defcustom lsp-kubernetes-helm-yaml-ls-prose-wrap "preserve"
133+
"Options for prose-wrap.
134+
Always: wrap prose if it exceeds the print width.
135+
Never: never wrap the prose.
136+
Preserve: wrap prose as-is."
137+
:group 'lsp-kubernetes-helm
138+
:type '(choice
139+
(const "always")
140+
(const "never")
141+
(const "preserve"))
142+
:package-version '(lsp-mode . "9.0.0"))
143+
144+
(defcustom lsp-kubernetes-helm-yaml-ls-print-width 80
145+
"Specify the line length that the printer will wrap on."
146+
:group 'lsp-kubernetes-helm
147+
:type 'number
148+
:package-version '(lsp-mode . "9.0.0"))
149+
150+
(defcustom lsp-kubernetes-helm-yaml-ls-validate t
151+
"Enable/disable validation feature."
152+
:group 'lsp-kubernetes-helm
153+
:type 'boolean
154+
:package-version '(lsp-mode . "9.0.0"))
155+
156+
(defcustom lsp-kubernetes-helm-yaml-ls-hover t
157+
"Enable/disable hover feature."
158+
:group 'lsp-kubernetes-helm
159+
:type 'boolean
160+
:package-version '(lsp-mode . "9.0.0"))
161+
162+
(defcustom lsp-kubernetes-helm-yaml-ls-completion t
163+
"Enable/disable completion feature."
164+
:group 'lsp-kubernetes-helm
165+
:type 'boolean
166+
:package-version '(lsp-mode . "9.0.0"))
167+
168+
(defcustom lsp-kubernetes-helm-yaml-ls-schema-store-extensions '(((name . "Kubernetes v1.30.3")
169+
(description . "Kubernetes v1.30.3 manifest schema definition")
170+
(url . "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.30.3-standalone-strict/all.json")))
171+
"Schemas defined by user schemas to files in a glob pattern.
172+
Used by Yaml Language Server to determine which schema to use for which types of files."
173+
:group 'lsp-kubernetes-helm
174+
:type '(list (list (alist
175+
:key-type (choice
176+
(const :tag "Name" name)
177+
(const :tag "Description" description)
178+
(const :tag "URL" url))
179+
:value-type string)))
180+
:package-version '(lsp-mode . "9.0.0"))
181+
182+
(defcustom lsp-kubernetes-helm-yaml-ls-schema-store-enable nil
183+
"Enable/disable JSON Schema store. When set to true, available YAML \
184+
schemas will be automatically pulled from
185+
`lsp-kubernetes-helm-yaml-ls-schema-store-uri'."
186+
:group 'lsp-kubernetes-helm
187+
:type 'boolean
188+
:package-version '(lsp-mode . "9.0.0"))
189+
190+
(defcustom lsp-kubernetes-helm-yaml-ls-schema-store-uri "https://www.schemastore.org/api/json/catalog.json"
191+
"URL of schema store catalog to use."
192+
:group 'lsp-kubernetes-helm
193+
:type 'string
194+
:package-version '(lsp-mode . "9.0.0"))
195+
196+
(defcustom lsp-kubernetes-helm-yaml-ls-schema-store-local-db
197+
(expand-file-name
198+
(locate-user-emacs-file
199+
(f-join ".cache" "lsp" "lsp-kubernetes-helm-schemas.json")))
200+
"Cached database of schema store."
201+
:group 'lsp-kubernetes-helm
202+
:type 'file
203+
:package-version '(lsp-mode . "9.0.0"))
204+
205+
(defcustom lsp-kubernetes-helm-yaml-ls-custom-tags nil
206+
"Custom tags for the parser to use."
207+
:group 'lsp-kubernetes-helm
208+
:type '(lsp-repeatable-vector string)
209+
:package-version '(lsp-mode . "9.0.0"))
210+
211+
(defcustom lsp-kubernetes-helm-yaml-ls-max-items-computed 5000
212+
"The maximum number of outline symbols and folding regions computed.
213+
Limited for performance reasons."
214+
:group 'lsp-kubernetes-helm
215+
:type 'number
216+
:package-version '(lsp-mode . "9.0.0"))
217+
218+
(defcustom lsp-kubernetes-helm-server-arguments '("serve" "--stdio")
219+
"Command to start helm-ls. Minimally needs serve otherwise the server wont start properly."
220+
:type '(repeat string)
221+
:group 'lsp-kubernetes-helm
222+
:package-version '(lsp-mode . "9.0.0"))
223+
224+
(defvar lsp-kubernetes-helm-yaml-ls--schema-store nil
225+
"A list of schemas provided by schema store uri.")
226+
227+
(defun lsp-kubernetes-helm-download-or-refresh-schema-store-db (&optional force-download)
228+
"Download remote schema store at `lsp-yaml-schema-store-uri' into local cache.
229+
Set FORCE-DOWNLOAD to non-nil to force re-download the database.
230+
FORCE-DOWNLOADING is set to t by default"
231+
(interactive "P")
232+
(let ((local-db-directory (file-name-directory lsp-kubernetes-helm-yaml-ls-schema-store-local-db))
233+
(force-download (or force-download t)))
234+
(when (not (file-exists-p lsp-kubernetes-helm-yaml-ls-schema-store-local-db))
235+
(unless (file-directory-p local-db-directory)
236+
(mkdir local-db-directory t))
237+
(url-copy-file lsp-kubernetes-helm-yaml-ls-schema-store-uri lsp-kubernetes-helm-yaml-ls-schema-store-local-db force-download))))
238+
239+
(defun lsp-kubernetes-helm--get-available-schemas ()
240+
"Get list of supported schemas."
241+
(when (and lsp-kubernetes-helm-yaml-ls-schema-store-enable
242+
(not lsp-kubernetes-helm-yaml-ls--schema-store))
243+
(lsp-kubernetes-helm-download-or-refresh-schema-store-db nil)
244+
(setq lsp-kubernetes-helm-yaml-ls--schema-store
245+
(alist-get 'schemas (json-read-file lsp-kubernetes-helm-yaml-ls-schema-store-local-db))))
246+
(seq-concatenate 'list lsp-kubernetes-helm-yaml-ls-schema-store-extensions lsp-kubernetes-helm-yaml-ls--schema-store))
247+
248+
(defun lsp-kubernetes-helm-set-buffer-schema (schema-uri-string)
249+
"Set yaml schema for the current buffer to SCHEMA-URI-STRING.
250+
Remove buffer from all other schema associations."
251+
(interactive "MURI: ")
252+
(let* ((schema-uri (intern schema-uri-string))
253+
(buffer-file-path (file-relative-name
254+
(lsp--uri-to-path (lsp--buffer-uri))
255+
(lsp-workspace-root (lsp--buffer-uri))))
256+
;; yaml language server can do partial path matching
257+
(glob (concat "/" buffer-file-path))
258+
(current-config (assoc schema-uri lsp-kubernetes-helm-yaml-ls-schemas))
259+
(current-patterns (and current-config (cdr current-config))))
260+
(if current-config
261+
(or (member glob (append current-patterns nil))
262+
(setq lsp-kubernetes-helm-yaml-ls-schemas
263+
(cl-acons schema-uri
264+
(vconcat (vector glob) current-patterns)
265+
(assq-delete-all schema-uri (mapcar (lambda (x) (lsp-kubernetes-helm--remove-glob-from-all-schemas x glob)) lsp-kubernetes-helm-yaml-ls-schemas)))))
266+
(setq lsp-kubernetes-helm-yaml-ls-schemas
267+
(cl-acons schema-uri (vector glob) (mapcar (lambda (x) (lsp-kubernetes-helm--remove-glob-from-all-schemas x glob)) lsp-kubernetes-helm-yaml-ls-schemas))))
268+
(lsp--set-configuration (lsp-configuration-section lsp-kubernetes-helm--lsp-configuration-section-name))))
269+
270+
(defun lsp-kubernetes-helm-select-buffer-schema ()
271+
"Select schema for the current buffer based on the list of supported schemas."
272+
(interactive)
273+
(let* ((schema (lsp--completing-read "Select buffer schema: "
274+
(lsp-kubernetes-helm--get-available-schemas)
275+
(lambda (schema)
276+
(format "%s: %s" (alist-get 'name schema)(alist-get 'description schema)))
277+
nil t))
278+
(uri (alist-get 'url schema)))
279+
(lsp-kubernetes-helm-set-buffer-schema uri)))
280+
281+
(defun lsp-kubernetes-helm--remove-glob-from-all-schemas (schemas glob)
282+
"Removes GLOB from all keys in SCHEMAS."
283+
(let ((patterns (cdr schemas)))
284+
(cons (car schemas)
285+
(vconcat (-filter (lambda (p)
286+
(not (equal p glob)))
287+
(append patterns nil)) nil))))
288+
289+
(lsp-register-custom-settings
290+
'(("helm-ls.logLevel" lsp-kubernetes-helm-ls-log-level)
291+
("helm-ls.valuesFiles.mainValuesFile" lsp-kubernetes-helm-ls-main-values-file-path)
292+
("helm-ls.valuesFiles.lintOverlayValuesFile" lsp-kubernetes-helm-overlay-values-file-path)
293+
("helm-ls.valuesFiles.additionalValuesFilesGlobPattern" lsp-kubernetes-helm-additional-values-files-pattern)
294+
("helm-ls.yamlls.enabled" lsp-kubernetes-helm-yaml-ls-enable t)
295+
("helm-ls.yamlls.enabledForFilesGlob" lsp-kubernetes-helm-yaml-ls-enable-for-globs)
296+
("helm-ls.yamlls.diagnosticsLimit" lsp-kubernetes-helm-yaml-ls-diagnostics-limit)
297+
("helm-ls.yamlls.path" lsp-kubernetes-helm-yaml-ls-server-path)
298+
("helm-ls.yamlls.config.format.enable" lsp-kubernetes-helm-yaml-ls-format-enable t)
299+
("helm-ls.yamlls.config.format.singleQuote" lsp-kubernetes-helm-yaml-ls-single-quote t)
300+
("helm-ls.yamlls.config.format.bracketSpacing" lsp-kubernetes-helm-yaml-ls-bracket-spacing)
301+
("helm-ls.yamlls.config.format.proseWrap" lsp-kubernetes-helm-yaml-ls-prose-wrap)
302+
("helm-ls.yamlls.config.format.printWidth" lsp-kubernetes-helm-yaml-ls-print-width)
303+
("helm-ls.yamlls.config.validate" lsp-kubernetes-helm-yaml-ls-validate t)
304+
("helm-ls.yamlls.config.hover" lsp-kubernetes-helm-yaml-ls-hover t)
305+
("helm-ls.yamlls.config.completion" lsp-kubernetes-helm-yaml-ls-completion t)
306+
("helm-ls.yamlls.config.schemas" lsp-kubernetes-helm-yaml-ls-schemas)
307+
("helm-ls.yamlls.config.schemaStore.enable" lsp-kubernetes-helm-yaml-ls-schema-store-enable nil)
308+
("helm-ls.yamlls.config.schemaStore.url" lsp-kubernetes-helm-yaml-ls-schema-store-uri)
309+
("helm-ls.yamlls.config.customTags" lsp-kubernetes-helm-yaml-ls-custom-tags)
310+
("helm-ls.yamlls.config.maxItemsComputed" lsp-kubernetes-helm-yaml-ls-max-items-computed)))
311+
312+
(lsp-dependency 'kubernetes-helm-language-server
313+
`(:system ,lsp-kubernetes-helm-ls-server-path)
314+
`(:system ,lsp-kubernetes-helm-yaml-ls-server-path)
315+
`(:npm :package "yaml-language-server"
316+
:path ,lsp-kubernetes-helm-yaml-ls-server-path))
317+
318+
(lsp-register-client
319+
(make-lsp-client :new-connection (lsp-stdio-connection
320+
(lambda ()
321+
`(,(or (executable-find lsp-kubernetes-helm-ls-server-path)
322+
(lsp-package-path 'kubernetes-helm-language-server))
323+
,@lsp-kubernetes-helm-server-arguments)))
324+
:activation-fn (lsp-activate-on "helm-ls")
325+
:priority 0
326+
:server-id 'helm-ls
327+
:initialized-fn (lambda (workspace)
328+
(with-lsp-workspace workspace
329+
(lsp--set-configuration
330+
(lsp-configuration-section lsp-kubernetes-helm--lsp-configuration-section-name))))))
331+
332+
(lsp-consistency-check lsp-kubernetes-helm)
333+
334+
(provide 'lsp-kubernetes-helm)
335+
;;; lsp-kubernetes-helm.el ends here

docs/lsp-clients.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,14 @@
13061306
"lsp-install-server": "yamlls",
13071307
"debugger": "Not available"
13081308
},
1309+
{
1310+
"name": "helm",
1311+
"full-name": "YAML (helm-ls)",
1312+
"server-name": "helm-ls",
1313+
"server-url": "https://github.com/mrjosh/helm-ls",
1314+
"installation-url": "https://github.com/mrjosh/helm-ls?tab=readme-ov-file#installation-with-a-package-manager",
1315+
"debugger": "Not available"
1316+
},
13091317
{
13101318
"name": "yang",
13111319
"full-name": "YANG",

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ nav:
189189
- wgsl: page/lsp-wgsl.md
190190
- XML: page/lsp-xml.md
191191
- YAML: page/lsp-yaml.md
192+
- YAML (helm-ls): page/lsp-kubernetes-helm.md
192193
- YANG: page/lsp-yang.md
193194
- Zig: page/lsp-zig.md
194195
- Debugging:

0 commit comments

Comments
 (0)