emacs/code/elpa/lsp-mode-20240319.1043/lsp-zig.el

346 lines
12 KiB
EmacsLisp

;;; lsp-zig.el --- lsp-mode Zig integration -*- lexical-binding: t; -*-
;; Copyright (C) 2021 Riccardo Binetti
;; Author: Riccardo Binetti <rbino@gmx.com>
;; Keywords: languages,tools
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; client for zls, the Zig language server
;;; Code:
(require 'lsp-mode)
(defgroup lsp-zig nil
"LSP support for Zig via zls."
:group 'lsp-mode
:link '(url-link "https://github.com/zigtools/zls"))
(defcustom lsp-zig-zls-executable "zls"
"The zls executable to use.
Leave as just the executable name to use the default behavior of finding the
executable with variable `exec-path'."
:group 'lsp-zig
:type 'string)
(defcustom lsp-zig-trace-server "off"
"Traces the communication between Emacs and the language server."
:group 'lsp-zig
:type '(choice (const "off")
(const "messages")
(const "verbose")))
(defcustom lsp-zls-enable-snippets t
"Enables snippet completions when the client also supports them."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-enable-argument-placeholders t
"Whether to enable function argument placeholder completions."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-enable-build-on-save nil
"Whether to enable build-on-save diagnostics."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-build-on-save-step "install"
"Select which step should be executed on build-on-save."
:group 'lsp-zig
:type 'string)
(defcustom lsp-zig-enable-autofix nil
"Whether to automatically fix errors on save.
Currently supports adding and removing discards."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-semantic-tokens "partial"
"Traces the communication between Emacs and the language server."
:group 'lsp-zig
:type '(choice (const "off")
(const "messages")
(const "verbose")))
(defcustom lsp-zig-enable-inlay-hints t
"Enables inlay hint support when the client also supports it."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-inlay-hints-show-variable-type-hints t
"Enable inlay hints for variable type."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-inlay-hints-show-parameter-name t
"Enable inlay hints for parameter names."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-inlay-hints-exclude-single-argument t
"Don't show inlay hints for single argument calls."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-inlay-hints-show-builtin t
"Don't show inlay hints for single argument calls."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-inlay-hints-hide-redundant-param-names nil
"Hides inlay hints when parameter name matches the identifier (e.g. foo: foo)."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-inlay-hints-hide-redundant-param-names-last-token nil
"Hides inlay hints when parameter name matches the last token of a parameter
node (e.g. foo: bar.foo, foo: &foo)."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-warn-style nil
"Enables warnings for style guideline mismatches."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-highlight-global-var-declarations nil
"Whether to highlight global var declarations."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-dangerous-comptime-experiments-do-not-enable nil
"When true, skips searching for references in std.
Improves lookup speed for functions in user's code. Renaming and
go-to-definition will continue to work as is."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-skip-std-references nil
"hen true, skips searching for references in std.
Improves lookup speed for functions in user's code. Renaming and
go-to-definition will continue to work as is."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-prefer-ast-check-as-child-process t
"Favor using `zig ast-check` instead of ZLS's fork."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-record-session nil
"When true, zls will record all request is receives and write in into
`record_session_path`, so that they can replayed with `zls replay`."
:group 'lsp-zig
:type 'boolean)
(defcustom lsp-zig-record-session-path ""
"Output file path when `record_session` is set.
The recommended file extension *.zlsreplay."
:group 'lsp-zig
:type 'string)
(defcustom lsp-zig-replay-session-path ""
"Used when calling `zls replay` for specifying the replay file.
If no extra argument is given `record_session_path` is used as the default path."
:group 'lsp-zig
:type 'string)
(defcustom lsp-zig-builtin-path ""
"Path to `builtin'; useful for debugging, automatically set if let null."
:group 'lsp-zig
:type 'string)
(defcustom lsp-zig-zig-lib-path ""
"Zig library path.
e.g. `/path/to/zig/lib/zig`, used to analyze std library imports."
:group 'lsp-zig
:type 'string)
(defcustom lsp-zig-zig-exe-path ""
" Zig executable path.
e.g. /path/to/zig/zig, used to run the custom build runner. If null, zig is
looked up in PATH. Will be used to infer the zig standard library path if none
is provided."
:group 'lsp-zig
:type 'string)
(defcustom lsp-zig-build-runner-path ""
"Path to the `build_runner.zig` file provided by zls.
null is equivalent to `${executable_directory}/build_runner.zig`."
:group 'lsp-zig
:type 'string)
(defcustom lsp-zig-global-cache-path ""
"Path to a directory that will be used as zig's cache.
null is equivalent to `${KnownFolders.Cache}/zls`."
:group 'lsp-zig
:type 'string)
(defcustom lsp-zig-build-runner-global-cache-path ""
"Path to a directory that will be used as the global cache path when executing
a projects build.zig. null is equivalent to the path shown by `zig env`."
:group 'lsp-zig
:type 'string)
(defcustom lsp-zig-completions-with-replace nil
"Completions confirm behavior.
If `true', replace the text after the cursor."
:group 'lsp-zig
:type 'boolean)
;;
;;; Util
(defmacro lsp-zig--mute-apply (&rest body)
"Execute BODY without message."
(declare (indent 0) (debug t))
`(let (message-log-max)
(with-temp-message (or (current-message) nil)
(let ((inhibit-message t)) ,@body))))
(defun lsp-zig--execute (cmd &rest args)
"Return non-nil if CMD executed succesfully with ARGS."
(save-window-excursion
(lsp-zig--mute-apply
(= 0 (shell-command (concat cmd " "
(mapconcat #'shell-quote-argument
(cl-remove-if #'null args)
" ")))))))
;;
;;; Installation
(defcustom lsp-zig-server-store-path
(expand-file-name "zig/" lsp-server-install-dir)
"The path to the file in which zls will be stored."
:type 'file
:group 'lsp-zig)
(defcustom lsp-zig-server-version "0.11.0"
"The zls version to install."
:type 'file
:group 'lsp-zig)
(defconst lsp-zig-download-url-format
"https://github.com/zigtools/zls/releases/download/%s/zls-%s-%s.%s"
"Format to the download url link.")
(defun lsp-zig--zls-url ()
"Return Url points to the zls' zip/tar file."
(let* ((x86 (string-prefix-p "x86_64" system-configuration))
(arch (if x86 "x86_64" "aarch64")))
(cl-case system-type
((cygwin windows-nt ms-dos)
(format lsp-zig-download-url-format
lsp-zig-server-version arch "windows" "zip"))
(darwin
(format lsp-zig-download-url-format
lsp-zig-server-version arch "macos" "tar.gz"))
(gnu/linux
(format lsp-zig-download-url-format
lsp-zig-server-version arch "linux" "tar.gz")))))
(defvar lsp-zig--server-download-url (lsp-zig--zls-url)
"The actual url used to download language server.")
(defvar lsp-zig--downloaded-file (f-join lsp-zig-server-store-path "temp.tar")
"The full file path after downloading the server zipped file.")
(defun lsp-zig--stored-zls-executable ()
"Return the stored zls executable.
This is differ from the variable `lsp-zig-zls-executable'; this is local storage
and not the global storage."
(executable-find (f-join lsp-zig-server-store-path "bin/zls")))
(defun lsp-zig--extract-compressed-file (callback)
"Install zls."
(cond ((file-exists-p lsp-zig--downloaded-file)
;; Suprisingly, you can just use `tar' to unzip a zip file on Windows.
;; Therefore, just use the same command.
(lsp-zig--execute "tar" "-xvzf" lsp-zig--downloaded-file "-C" lsp-zig-server-store-path)
;; Delete the zip file.
(ignore-errors (delete-file lsp-zig--downloaded-file)))
(t
(error "Can't extract the downloaded file: %s" lsp-zig--downloaded-file)))
(funcall callback))
(lsp-dependency
'zls
'(:system "zls")
`(:download :url ,lsp-zig--server-download-url
:store-path ,lsp-zig--downloaded-file))
;;
;;; Core
(lsp-register-custom-settings
'(("zls.enable_snippets" lsp-zls-enable-snippets t)
("zls.enable_argument_placeholders" lsp-zig-enable-argument-placeholders t)
("zls.enable_build_on_save" lsp-zig-enable-build-on-save t)
("zls.build_on_save_step" lsp-zig-build-on-save-step)
("zls.enable_autofix" lsp-zig-enable-autofix t)
("zls.semantic_tokens" lsp-zig-semantic-tokens)
("zls.enable_inlay_hints" lsp-zig-enable-inlay-hints t)
("zls.inlay_hints_show_variable_type_hints" lsp-zig-inlay-hints-show-variable-type-hints t)
("zls.inlay_hints_show_parameter_name" lsp-zig-inlay-hints-show-parameter-name t)
("zls.inlay_hints_show_builtin" lsp-zig-inlay-hints-show-builtin t)
("zls.inlay_hints_exclude_single_argument" lsp-zig-inlay-hints-exclude-single-argument t)
("zls.inlay_hints_hide_redundant_param_names" lsp-zig-inlay-hints-hide-redundant-param-names t)
("zls.inlay_hints_hide_redundant_param_names_last_token" lsp-zig-inlay-hints-hide-redundant-param-names-last-token t)
("zls.warn_style" lsp-zig-warn-style t)
("zls.highlight_global_var_declarations" lsp-zig-highlight-global-var-declarations t)
("zls.dangerous_comptime_experiments_do_not_enable" lsp-zig-dangerous-comptime-experiments-do-not-enable t)
("zls.skip_std_references" lsp-zig-skip-std-references t)
("zls.prefer_ast_check_as_child_process" lsp-zig-prefer-ast-check-as-child-process t)
("zls.record_session" lsp-zig-record-session t)
("zls.record_session_path" lsp-zig-record-session-path)
("zls.replay_session_path" lsp-zig-replay-session-path)
("zls.builtin_path" lsp-zig-builtin-path)
("zls.zig_lib_path" lsp-zig-zig-lib-path)
("zls.zig_exe_path" lsp-zig-zig-exe-path)
("zls.build_runner_path" lsp-zig-build-runner-path)
("zls.global_cache_path" lsp-zig-global-cache-path)
("zls.build_runner_global_cache_path" lsp-zig-build-runner-global-cache-path)
("zls.completion_label_details" lsp-zig-completions-with-replace t)))
(lsp-register-client
(make-lsp-client
:new-connection (lsp-stdio-connection
(lambda () (or (executable-find lsp-zig-zls-executable)
(lsp-zig--stored-zls-executable)))
(lambda ()
(or (executable-find lsp-zig-zls-executable)
(file-executable-p (lsp-zig--stored-zls-executable)))))
:activation-fn (lsp-activate-on "zig")
:priority -1
:server-id 'zls
:download-server-fn
(lambda (_client callback error-callback _update?)
(lsp-package-ensure 'zls
(lambda (&rest _)
(lsp-zig--extract-compressed-file callback))
error-callback))))
(lsp-consistency-check lsp-zig)
(provide 'lsp-zig)
;;; lsp-zig.el ends here