diff options
Diffstat (limited to 'elpa/lsp-mode-20220505.630/lsp-eslint.el')
-rw-r--r-- | elpa/lsp-mode-20220505.630/lsp-eslint.el | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/elpa/lsp-mode-20220505.630/lsp-eslint.el b/elpa/lsp-mode-20220505.630/lsp-eslint.el new file mode 100644 index 0000000..74df3dd --- /dev/null +++ b/elpa/lsp-mode-20220505.630/lsp-eslint.el @@ -0,0 +1,408 @@ +;;; lsp-eslint.el --- lsp-mode eslint integration -*- lexical-binding: t; -*- + +;; Copyright (C) 2019 Ivan Yonchovski + +;; Author: Ivan Yonchovski <yyoncho@gmail.com> +;; Keywords: languages + +;; 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: + +;; + +;;; Code: + +(require 'lsp-protocol) +(require 'lsp-mode) + +(defconst lsp-eslint/status-ok 1) +(defconst lsp-eslint/status-warn 2) +(defconst lsp-eslint/status-error 3) + +(defgroup lsp-eslint nil + "ESlint language server group." + :group 'lsp-mode + :link '(url-link "https://github.com/Microsoft/vscode-eslint")) + +(defcustom lsp-eslint-unzipped-path (f-join lsp-server-install-dir "eslint/unzipped") + "The path to the file in which `eslint' will be stored." + :type 'file + :group 'lsp-eslint + :package-version '(lsp-mode . "8.0.0")) + +(defcustom lsp-eslint-download-url "https://github.com/emacs-lsp/lsp-server-binaries/blob/master/dbaeumer.vscode-eslint-2.2.2.vsix?raw=true" + "Eslint language server download url." + :type 'string + :group 'lsp-eslint + :package-version '(lsp-mode . "8.0.1")) + +(defcustom lsp-eslint-server-command `("node" + "~/server/out/eslintServer.js" + "--stdio") + "Command to start eslint server." + :risky t + :type '(repeat string) + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-enable t + "Controls whether eslint is enabled for JavaScript files or not." + :type 'boolean + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-package-manager "npm" + "The package manager you use to install node modules." + :type '(choice (const :tag "npm" "npm") + (const :tag "yarn" "yarn") + (const :tag "pnpm" "pnpm") + (string :tag "other")) + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-format t + "Whether to perform format." + :type 'boolean + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-node-path nil + "A path added to NODE_PATH when resolving the eslint module." + :type '(repeat string) + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-node "node" + "Path to nodejs." + :type 'file + :package-version '(lsp-mode . "8.0.0")) + +(defcustom lsp-eslint-options nil + "The eslint options object to provide args normally passed to + eslint when executed from a command line (see + http://eslint.org/docs/developer-guide/nodejs-api#cliengine)." + :type 'alist) + +(defcustom lsp-eslint-trace-server "off" + "Traces the communication between VSCode and the eslint linter service." + :type 'string) + +(defcustom lsp-eslint-run "onType" + "Run the linter on save (onSave) or on type (onType)" + :type '(choice (const :tag "onSave" "onSave") + (const :tag "onType" "onType")) + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-auto-fix-on-save nil + "Turns auto fix on save on or off." + :type 'boolean + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-fix-all-problem-type "all" + "Determines which problems are fixed when running the +source.fixAll code action." + :type '(choice + (const "all") + (const "problems") + string) + :package-version '(lsp-mode . "7.0.1")) + +(defcustom lsp-eslint-quiet nil + "Turns on quiet mode, which ignores warnings." + :type 'boolean + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-working-directories [] + "" + :type 'lsp-string-vector + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-validate '("svelte") + "An array of language ids which should always be validated by eslint." + :type '(repeat string) + :package-version '(lsp-mode . "8.0.0")) + +(defcustom lsp-eslint-provide-lint-task nil + "Controls whether a task for linting the whole workspace will be available." + :type 'boolean + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-lint-task-enable nil + "Controls whether a task for linting the whole workspace will be available." + :type 'boolean + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-lint-task-options "." + "Command line options applied when running the task for linting the whole +workspace (see https://eslint.org/docs/user-guide/command-line-interface)." + :type 'string + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-runtime nil + "The location of the node binary to run ESLint under." + :type '(repeat string) + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-code-action-disable-rule-comment t + "Controls whether code actions to add a rule-disabling comment should be shown." + :type 'bool + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-code-action-disable-rule-comment-location "separateLine" + "Controls where the disable rule code action places comments. + +Accepts the following values: +- \"separateLine\": Add the comment above the line to be disabled (default). +- \"sameLine\": Add the comment on the same line that will be disabled." + :type '(choice + (const "separateLine") + (const "sameLine")) + :package-version '(lsp-mode . "8.0.0")) + +(defcustom lsp-eslint-code-action-show-documentation t + "Controls whether code actions to show documentation for an eslint rule should +be shown." + :type 'bool + :package-version '(lsp-mode . "8.0.0")) + +(defcustom lsp-eslint-warn-on-ignored-files nil + "Controls whether a warning should be emitted when a file is ignored." + :type 'bool + :package-version '(lsp-mode . "8.0.0")) + +(defcustom lsp-eslint-rules-customizations [] + "Controls severity overrides for eslint rules. + +The value is a vector of alists, with each alist containing the following keys: +- rule - The rule to match. Can match wildcards with *, or be prefixed with ! + to negate the match. +- severity - The severity to report this rule as. Can be one of the following: + - \"off\": Disable the rule. + - \"info\": Report as informational. + - \"warn\": Report as a warning. + - \"error\": Report as an error. + - \"upgrade\": Increase by 1 severity level (eg. warning -> error). + - \"downgrade\": Decrease by 1 severity level (eg. warning -> info). + - \"default\": Report as the same severity specified in the eslint config." + :type '(lsp-repeatable-vector + (alist :options ((rule string) + (severity (choice + (const "off") + (const "info") + (const "warn") + (const "error") + (const "upgrade") + (const "downgrade") + (const "default")))))) + :package-version '(lsp-mode . "8.0.0")) + +(defcustom lsp-eslint-experimental-incremental-sync t + "Controls whether the new incremental text document synchronization should +be used." + :type 'boolean + :package-version '(lsp-mode . "6.3")) + +(defcustom lsp-eslint-save-library-choices t + "Controls whether to remember choices made to permit or deny ESLint libraries +from running." + :type 'boolean + :package-version '(lsp-mode . "8.0.0")) + +(defcustom lsp-eslint-library-choices-file (expand-file-name (locate-user-emacs-file ".lsp-eslint-choices")) + "The file where choices to permit or deny ESLint libraries from running is +stored." + :type 'string + :package-version '(lsp-mode . "8.0.0")) + +(defun lsp--find-eslint () + (or + (when-let ((workspace-folder (lsp-find-session-folder (lsp-session) default-directory))) + (let ((eslint-local-path (f-join workspace-folder "node_modules" ".bin" + (if (eq system-type 'windows-nt) "eslint.cmd" "eslint")))) + (when (f-exists? eslint-local-path) + eslint-local-path))) + "eslint")) + +(defun lsp-eslint-create-default-configuration () + "Create default eslint configuration." + (interactive) + (unless (lsp-session-folders (lsp-session)) + (user-error "There are no workspace folders")) + (pcase (->> (lsp-session) + lsp-session-folders + (-filter (lambda (dir) + (-none? + (lambda (file) (f-exists? (f-join dir file))) + '(".eslintrc.js" ".eslintrc.yaml" ".eslintrc.yml" ".eslintrc" ".eslintrc.json"))))) + (`nil (user-error "All workspace folders contain eslint configuration")) + (folders (let ((default-directory (completing-read "Select project folder: " folders nil t))) + (async-shell-command (format "%s --init" (lsp--find-eslint))))))) + +(lsp-defun lsp-eslint-status-handler (workspace (&eslint:StatusParams :state)) + (setf (lsp--workspace-status-string workspace) + (propertize "ESLint" + 'face (cond + ((eq state lsp-eslint/status-error) 'error) + ((eq state lsp-eslint/status-warn) 'warn) + (t 'success))))) + +(lsp-defun lsp-eslint--configuration (_workspace (&ConfigurationParams :items)) + (->> items + (seq-map (-lambda ((&ConfigurationItem :scope-uri?)) + (-when-let* ((file (lsp--uri-to-path scope-uri?)) + (buffer (find-buffer-visiting file)) + (workspace-folder (lsp-find-session-folder (lsp-session) file))) + (with-current-buffer buffer + (list :validate (if (member (lsp-buffer-language) lsp-eslint-validate) "on" "probe") + :packageManager lsp-eslint-package-manager + :codeAction (list + :disableRuleComment (list + :enable (lsp-json-bool lsp-eslint-code-action-disable-rule-comment) + :location lsp-eslint-code-action-disable-rule-comment-location) + :showDocumentation (list + :enable (lsp-json-bool lsp-eslint-code-action-show-documentation))) + :codeActionOnSave (list :enable (lsp-json-bool lsp-eslint-auto-fix-on-save) + :mode lsp-eslint-fix-all-problem-type) + :format (lsp-json-bool lsp-eslint-format) + :quiet (lsp-json-bool lsp-eslint-quiet) + :onIgnoredFiles (if lsp-eslint-warn-on-ignored-files "warn" "off") + :options (or lsp-eslint-options (ht)) + :rulesCustomizations lsp-eslint-rules-customizations + :run lsp-eslint-run + :nodePath lsp-eslint-node-path + :workspaceFolder (list :uri (lsp--path-to-uri workspace-folder) + :name (f-filename workspace-folder))))))) + (apply #'vector))) + +(lsp-defun lsp-eslint--open-doc (_workspace (&eslint:OpenESLintDocParams :url)) + "Open documentation." + (browse-url url)) + +(defun lsp-eslint-apply-all-fixes () + "Apply all autofixes in the current buffer." + (interactive) + (lsp-send-execute-command "eslint.applyAllFixes" (vector (lsp--versioned-text-document-identifier)))) + +;; XXX: replace with `lsp-make-interactive-code-action' macro +;; (lsp-make-interactive-code-action eslint-fix-all "source.fixAll.eslint") + +(defun lsp-eslint-fix-all () + "Perform the source.fixAll.eslint code action, if available." + (interactive) + (condition-case nil + (lsp-execute-code-action-by-kind "source.fixAll.eslint") + (lsp-no-code-actions + (when (called-interactively-p 'any) + (lsp--info "source.fixAll.eslint action not available"))))) + +(defun lsp-eslint-server-command () + (if (lsp-eslint-server-exists? lsp-eslint-server-command) + lsp-eslint-server-command + `(,lsp-eslint-node ,(f-join lsp-eslint-unzipped-path + "extension/server/out/eslintServer.js") + "--stdio"))) + +(defun lsp-eslint-server-exists? (eslint-server-command) + (let* ((command-name (f-base (f-filename (cl-first eslint-server-command)))) + (first-argument (cl-second eslint-server-command)) + (first-argument-exist (and first-argument (file-exists-p first-argument)))) + (if (equal command-name lsp-eslint-node) + first-argument-exist + (executable-find (cl-first eslint-server-command))))) + +(defvar lsp-eslint--stored-libraries (ht) + "Hash table defining if a given path to an ESLint library is allowed to run. +If the value for a key is 4, it will be allowed. If it is 1, it will not. If a +value does not exist for the key, or the value is nil, the user will be prompted +to allow or deny it.") + +(when (and (file-exists-p lsp-eslint-library-choices-file) + lsp-eslint-save-library-choices) + (setq lsp-eslint--stored-libraries (lsp--read-from-file lsp-eslint-library-choices-file))) + +(lsp-defun lsp-eslint--confirm-local (_workspace (&eslint:ConfirmExecutionParams :library-path) callback) + (if-let ((option-alist '(("Always" 4 . t) + ("Yes" 4 . nil) + ("No" 1 . nil) + ("Never" 1 . t))) + (remembered-answer (gethash library-path lsp-eslint--stored-libraries))) + (funcall callback remembered-answer) + (lsp-ask-question + (format + "Allow lsp-mode to execute %s? Note: The latest versions of the ESLint language server no longer create this prompt." + library-path) + (mapcar 'car option-alist) + (lambda (response) + (let ((option (cdr (assoc response option-alist)))) + (when (cdr option) + (puthash library-path (car option) lsp-eslint--stored-libraries) + (when lsp-eslint-save-library-choices + (lsp--persist lsp-eslint-library-choices-file lsp-eslint--stored-libraries))) + (funcall callback (car option))))))) + +(defun lsp-eslint--probe-failed (_workspace _message) + "Called when the server detects a misconfiguration in ESLint." + (lsp--error "ESLint is not configured correctly. Please ensure your eslintrc is set up for the languages you are using.")) + +(lsp-register-client + (make-lsp-client + :new-connection + (lsp-stdio-connection + (lambda () (lsp-eslint-server-command)) + (lambda () (lsp-eslint-server-exists? (lsp-eslint-server-command)))) + :activation-fn (lambda (filename &optional _) + (when lsp-eslint-enable + (or (string-match-p (rx (one-or-more anything) "." + (or "ts" "js" "jsx" "tsx" "html" "vue" "svelte")eos) + filename) + (and (derived-mode-p 'js-mode 'js2-mode 'typescript-mode 'html-mode 'svelte-mode) + (not (string-match-p "\\.json\\'" filename)))))) + :priority -1 + :completion-in-comments? t + :add-on? t + :multi-root t + :notification-handlers (ht ("eslint/status" #'lsp-eslint-status-handler)) + :request-handlers (ht ("workspace/configuration" #'lsp-eslint--configuration) + ("eslint/openDoc" #'lsp-eslint--open-doc) + ("eslint/probeFailed" #'lsp-eslint--probe-failed)) + :async-request-handlers (ht ("eslint/confirmESLintExecution" #'lsp-eslint--confirm-local)) + :server-id 'eslint + :initialized-fn (lambda (workspace) + (with-lsp-workspace workspace + (lsp--server-register-capability + (lsp-make-registration + :id "random-id" + :method "workspace/didChangeWatchedFiles" + :register-options? (lsp-make-did-change-watched-files-registration-options + :watchers + `[,(lsp-make-file-system-watcher + :glob-pattern "**/.eslintr{c.js,c.yaml,c.yml,c,c.json}") + ,(lsp-make-file-system-watcher + :glob-pattern "**/.eslintignore") + ,(lsp-make-file-system-watcher + :glob-pattern "**/package.json")]))))) + :download-server-fn (lambda (_client callback error-callback _update?) + (let ((tmp-zip (make-temp-file "ext" nil ".zip"))) + (delete-file tmp-zip) + (lsp-download-install + (lambda (&rest _) + (condition-case err + (progn + (lsp-unzip tmp-zip lsp-eslint-unzipped-path) + (funcall callback)) + (error (funcall error-callback err)))) + error-callback + :url lsp-eslint-download-url + :store-path tmp-zip))))) + +(lsp-consistency-check lsp-eslint) + +(provide 'lsp-eslint) +;;; lsp-eslint.el ends here |