;;; lsp-javascript.el --- description -*- lexical-binding: t; -*- ;; Copyright (C) 2020 emacs-lsp maintainers ;; Author: emacs-lsp maintainers ;; Keywords: lsp, ;; 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 . ;;; Commentary: ;; LSP Clients for the JavaScript and TypeScript Programming Languages. ;;; Code: (require 'lsp-mode) (lsp-dependency 'javascript-typescript-langserver '(:system "javascript-typescript-stdio") '(:npm :package "javascript-typescript-langserver" :path "javascript-typescript-stdio")) (defgroup lsp-typescript-javascript nil "Support for TypeScript/JavaScript, using Sourcegraph's JavaScript/TypeScript language server." :group 'lsp-mode :link '(url-link "https://github.com/sourcegraph/javascript-typescript-langserver")) (defcustom lsp-clients-typescript-javascript-server-args '() "Extra arguments for the typescript-language-server language server." :group 'lsp-typescript-javascript :risky t :type '(repeat string)) (defun lsp-typescript-javascript-tsx-jsx-activate-p (filename &optional _) "Check if the javascript-typescript language server should be enabled based on FILENAME." (or (string-match-p "\\.mjs\\|\\.[jt]sx?\\'" filename) (and (derived-mode-p 'js-mode 'typescript-mode) (not (derived-mode-p 'json-mode))))) (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection (lambda () (cons (lsp-package-path 'javascript-typescript-langserver) lsp-clients-typescript-javascript-server-args))) :activation-fn 'lsp-typescript-javascript-tsx-jsx-activate-p :priority -3 :completion-in-comments? t :server-id 'jsts-ls :download-server-fn (lambda (_client callback error-callback _update?) (lsp-package-ensure 'javascript-typescript-langserver callback error-callback)))) (defgroup lsp-typescript nil "LSP support for TypeScript, using Theia/Typefox's TypeScript Language Server." :group 'lsp-mode :link '(url-link "https://github.com/theia-ide/typescript-language-server")) (defcustom lsp-clients-typescript-tls-path "typescript-language-server" "Path to the typescript-language-server binary." :group 'lsp-typescript :risky t :type 'string) (defcustom lsp-clients-typescript-server-args '("--stdio") "Extra arguments for the typescript-language-server language server." :group 'lsp-typescript :risky t :type '(repeat string)) (defcustom lsp-clients-typescript-log-verbosity "info" "The server log verbosity." :group 'lsp-typescript :type 'string) (defcustom lsp-clients-typescript-plugins (vector) "The list of plugins to load. It should be a vector of plist with keys `:location' and `:name' where `:name' is the name of the package and `:location' is the directory containing the package. Example: \(vector \(list :name \"@vsintellicode/typescript-intellicode-plugin\" :location \".vscode/extensions/visualstudioexptteam. vscodeintellicode-1.1.9/\"))" :group 'lsp-typescript :type '(restricted-sexp :tag "Vector" :match-alternatives (lambda (xs) (and (vectorp xs) (seq-every-p (-lambda ((&plist :name :location)) (and name location)) xs))))) (lsp-dependency 'typescript-language-server '(:system lsp-clients-typescript-tls-path) '(:npm :package "typescript-language-server" :path "typescript-language-server")) (lsp-dependency 'typescript '(:system "tsserver") '(:npm :package "typescript" :path "tsserver")) (defun lsp-javascript--rename (_workspace args) (let ((path (lsp--uri-to-path (lsp-get (lsp-get args :textDocument) :uri)))) (if (f-exists? path) (with-current-buffer (find-file path) (goto-char (lsp--position-to-point (lsp-get args :position)))) (error "There is no file %s" path))) (call-interactively #'lsp-rename) nil) (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection (lambda () `(,(lsp-package-path 'typescript-language-server) "--tsserver-path" ,(lsp-package-path 'typescript) ,@lsp-clients-typescript-server-args))) :activation-fn 'lsp-typescript-javascript-tsx-jsx-activate-p :priority -2 :completion-in-comments? t :initialization-options (lambda () (list :plugins lsp-clients-typescript-plugins :logVerbosity lsp-clients-typescript-log-verbosity :tsServerPath (lsp-package-path 'typescript))) :ignore-messages '("readFile .*? requested by TypeScript but content not available") :server-id 'ts-ls :request-handlers (ht ("_typescript.rename" #'lsp-javascript--rename)) :download-server-fn (lambda (_client callback error-callback _update?) (lsp-package-ensure 'typescript (-partial #'lsp-package-ensure 'typescript-language-server callback error-callback) error-callback)))) (defgroup lsp-flow nil "LSP support for the Flow Javascript type checker." :group 'lsp-mode :link '(url-link "https://flow.org")) (defcustom lsp-clients-flow-server "flow" "The Flow executable to use. Leave as just the executable name to use the default behavior of finding the executable with variable `exec-path'." :group 'lsp-flow :risky t :type 'file) (defcustom lsp-clients-flow-server-args '("lsp") "Extra arguments for starting the Flow language server." :group 'lsp-flow :risky t :type '(repeat string)) (defun lsp-clients-flow-tag-file-present-p (file-name) "Check if the '// @flow' or `/* @flow */' tag is present in the contents of FILE-NAME." (if-let ((buffer (find-buffer-visiting file-name))) (with-current-buffer buffer (lsp-clients-flow-tag-string-present-p)) (with-temp-buffer (insert-file-contents file-name) (lsp-clients-flow-tag-string-present-p)))) (defun lsp-clients-flow-tag-string-present-p () "Helper for `lsp-clients-flow-tag-file-present-p' that works with the file contents." (save-excursion (goto-char (point-min)) (let (stop found) (while (not stop) (unless (re-search-forward "[^\n[:space:]]" nil t) (setq stop t)) (if (= (point) (point-min)) (setq stop t) (backward-char)) (cond ((or (looking-at "//+[ ]*@flow") (looking-at "/\\**[ ]*@flow") (looking-at "[ ]*\\*[ ]*@flow")) (setq found t) (setq stop t)) ((or (looking-at "//") (looking-at "*")) (forward-line)) ((looking-at "/\\*") (save-excursion (unless (re-search-forward "*/" nil t) (setq stop t))) (forward-line)) (t (setq stop t)))) found))) (defun lsp-clients-flow-project-p (file-name) "Check if FILE-NAME is part of a Flow project, that is, if there is a .flowconfig file in the folder hierarchy." (locate-dominating-file file-name ".flowconfig")) (defun lsp-clients-flow-activate-p (file-name _mode) "Check if the Flow language server should be enabled for a particular FILE-NAME and MODE." (and (derived-mode-p 'js-mode 'web-mode 'js2-mode 'flow-js2-mode 'rjsx-mode) (not (derived-mode-p 'json-mode)) (or (lsp-clients-flow-project-p file-name) (lsp-clients-flow-tag-file-present-p file-name)))) (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection (lambda () (cons lsp-clients-flow-server lsp-clients-flow-server-args))) :priority -1 :activation-fn 'lsp-clients-flow-activate-p :server-id 'flow-ls)) (defgroup lsp-deno nil "LSP support for the Deno language server." :group 'lsp-mode :link '(url-link "https://deno.land/")) (defcustom lsp-clients-deno-server "deno" "The Deno executable to use. Leave as just the executable name to use the default behavior of finding the executable with variable `exec-path'." :group 'lsp-deno :risky t :type 'file :package-version '(lsp-mode . "7.1.0")) (defcustom lsp-clients-deno-server-args '("lsp") "Extra arguments for starting the Deno language server." :group 'lsp-deno :risky t :type '(repeat string) :package-version '(lsp-mode . "7.1.0")) (defcustom lsp-clients-deno-enable-lint t "Controls if linting information will be provided by the Deno Language Server." :group 'lsp-deno :risky t :type 'boolean :package-version '(lsp-mode . "7.1.0")) (defcustom lsp-clients-deno-enable-code-lens-references t "Enables or disables the display of code lens information." :group 'lsp-deno :risky t :type 'boolean :package-version '(lsp-mode . "7.1.0")) (defcustom lsp-clients-deno-enable-code-lens-references-all-functions t "Enables or disables the display of code lens information for all functions. Setting this variable to `non-nil' implicitly enables `lsp-clients-deno-enable-code-lens-references'." :group 'lsp-deno :risky t :type 'boolean :package-version '(lsp-mode . "7.1.0")) (defcustom lsp-clients-deno-enable-code-lens-implementations t "Enables or disables the display of code lens information for implementations." :group 'lsp-deno :risky t :type 'boolean :package-version '(lsp-mode . "7.1.0")) (defcustom lsp-clients-deno-config nil "The file path to a tsconfig.json file. The path can be either be relative to the workspace, or an absolute path. Examples: `./tsconfig.json', `/path/to/tsconfig.json', `C:\\path\\to\\tsconfig.json'" :group 'lsp-deno :risky t :type 'file :package-version '(lsp-mode . "7.1.0")) (defcustom lsp-clients-deno-import-map nil "The file path to an import map. Import maps provide a way to relocate modules based on their specifiers. The path can either be relative to the workspace, or an absolute path. Examples: `./import-map.json', `/path/to/import-map.json', `C:\\path\\to\\import-map.json'." :group 'lsp-deno :risky t :type 'file :package-version '(lsp-mode . "7.1.0")) (defcustom lsp-clients-deno-enable-unstable nil "Controls if code will be type checked with Deno's unstable APIs." :group 'lsp-deno :risky t :type 'boolean :package-version '(lsp-mode . "7.1.0")) (defun lsp-clients-deno--make-init-options () "Initialization options for the Deno language server." `(:enable t :config ,lsp-clients-deno-config :importMap ,lsp-clients-deno-import-map :lint ,(lsp-json-bool lsp-clients-deno-enable-lint) :unstable ,(lsp-json-bool lsp-clients-deno-enable-unstable) :codeLens (:implementations ,(lsp-json-bool lsp-clients-deno-enable-code-lens-implementations) :references ,(lsp-json-bool (or lsp-clients-deno-enable-code-lens-references lsp-clients-deno-enable-code-lens-references-all-functions)) :referencesAllFunctions ,(lsp-json-bool lsp-clients-deno-enable-code-lens-references-all-functions)))) (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection (lambda () (cons lsp-clients-deno-server lsp-clients-deno-server-args))) :initialization-options #'lsp-clients-deno--make-init-options :priority -5 :activation-fn #'lsp-typescript-javascript-tsx-jsx-activate-p :server-id 'deno-ls)) (lsp-consistency-check lsp-javascript) (provide 'lsp-javascript) ;;; lsp-javascript.el ends here