Skip to content

Commit

Permalink
Implement automatic downloading facilities
Browse files Browse the repository at this point in the history
- Fixes emacs-lsp#506

- Implemented base facilities for downloading and installing language servers
- added new field :download-server-fn in lsp--client structure which will be
called with (client callback update?).
- Implemented automatic installation via npm for jsts-ls and ts-ls.
- All servers(when feasible) should be installed under `lsp-server-install-dir`.

@akirak - check the cl-defgenerics in lsp-mode - let me know if they are
sufficient to implement the nix variands.

@razzmatazz - I have ported CSharp implementation but I havent converted it into
async.

@seagle0128 - lsp-python-ms is not ported. In general, it will work as it is but
it will ask to install the server even if you are in different file.

@TOTBWF - F# same as C#.

@kiennq powershell installation is converted to async one.
  • Loading branch information
Ivan authored and yyoncho committed Jan 5, 2020
1 parent 0fc9420 commit 9ef7960
Show file tree
Hide file tree
Showing 8 changed files with 442 additions and 218 deletions.
96 changes: 54 additions & 42 deletions lsp-clients.el
Original file line number Diff line number Diff line change
Expand Up @@ -153,19 +153,17 @@ This directory shoud contain a file matching groovy-language-server-*.jar"
:server-id 'groovy-ls))

;;; TypeScript/JavaScript

(lsp-dependency javascript-typescript-langserver
(:system "javascript-typescript-stdio")
(:npm :package "javascript-typescript-langserver"
:path ".bin/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-javascript-typescript-server "javascript-typescript-stdio"
"The javascript-typescript-stdio executable to use.
Leave as just the executable name to use the default behavior of
finding the executable with variable `exec-path'."
:group 'lsp-typescript-javascript
:risky t
:type 'file)

(defcustom lsp-clients-typescript-javascript-server-args '()
"Extra arguments for the typescript-language-server language server."
:group 'lsp-typescript-javascript
Expand All @@ -179,13 +177,17 @@ finding the executable with variable `exec-path'."

(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection (lambda ()
(cons lsp-clients-javascript-typescript-server
(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
:ignore-messages '("readFile .*? requested by TypeScript but content not available")
:server-id 'jsts-ls))
:server-id 'jsts-ls
:download-server-fn (lambda (_client callback error-callback _update?)
(lsp-package-ensure
'javascript-typescript-langserver
callback
error-callback))))


;;; TypeScript
Expand All @@ -194,14 +196,6 @@ finding the executable with variable `exec-path'."
:group 'lsp-mode
:link '(url-link "https://github.com/theia-ide/typescript-language-server"))

(defcustom lsp-clients-typescript-server "typescript-language-server"
"The typescript-language-server executable to use.
Leave as just the executable name to use the default behavior of
finding the executable with variable `exec-path'."
:group 'lsp-typescript
:risky t
:type 'file)

(defcustom lsp-clients-typescript-server-args '("--stdio")
"Extra arguments for the typescript-language-server language server."
:group 'lsp-typescript
Expand Down Expand Up @@ -230,18 +224,37 @@ directory containing the package. Example:
(and name location))
xs)))))

(lsp-dependency typescript-language-server
(:system "typescript-language-server")
(:npm :package "typescript-language-server"
:path ".bin/typescript-language-server"))

(lsp-dependency typescript
(:system "tsserver")
(:npm :package "typescript"
:path ".bin/tsserver"))

(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection (lambda ()
(cons lsp-clients-typescript-server
(cons (lsp-package-path 'typescript-language-server)
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))
: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))
:server-id 'ts-ls
: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))))



Expand Down Expand Up @@ -338,19 +351,18 @@ particular FILE-NAME and MODE."

(defun lsp-php--create-connection ()
"Create lsp connection."
(plist-put
(lsp-stdio-connection
(lambda () lsp-clients-php-server-command))
:test? (lambda ()
(if (and (cdr lsp-clients-php-server-command)
(eq (string-match-p "php[0-9.]*\\'" (car lsp-clients-php-server-command)) 0))
;; Start with the php command and the list has more elems. Test the existence of the PHP script.
(let ((php-file (nth 1 lsp-clients-php-server-command)))
(or (file-exists-p php-file)
(progn
(lsp-log "%s is not present." php-file)
nil)))
t))))
(lsp-stdio-connection
(lambda () lsp-clients-php-server-command)
(lambda ()
(if (and (cdr lsp-clients-php-server-command)
(eq (string-match-p "php[0-9.]*\\'" (car lsp-clients-php-server-command)) 0))
;; Start with the php command and the list has more elems. Test the existence of the PHP script.
(let ((php-file (nth 1 lsp-clients-php-server-command)))
(or (file-exists-p php-file)
(progn
(lsp-log "%s is not present." php-file)
nil)))
t))))

(lsp-register-client
(make-lsp-client :new-connection (lsp-php--create-connection)
Expand Down Expand Up @@ -390,7 +402,9 @@ particular FILE-NAME and MODE."
"LSP support for OCaml, using ocaml-lsp-server."
:group 'lsp-mode
:link '(url-link "https://github.com/ocaml/ocaml-lsp"))

(define-obsolete-variable-alias 'lsp-merlin 'lsp-ocaml-lsp-server)
(define-obsolete-variable-alias 'lsp-merlin-command 'lsp-ocaml-lsp-server-command)

(defcustom lsp-ocaml-lsp-server-command
'("ocamllsp")
Expand All @@ -400,7 +414,6 @@ particular FILE-NAME and MODE."
(string :tag "Single string value")
(repeat :tag "List of string values"
string)))
(define-obsolete-variable-alias 'lsp-merlin-command 'lsp-ocaml-lsp-server-command)

(lsp-register-client
(make-lsp-client
Expand Down Expand Up @@ -734,12 +747,11 @@ responsiveness at the cost of possibile stability issues."

(defun lsp-clients-emmy-lua--create-connection ()
"Create connection to emmy lua language server."
(plist-put
(lsp-stdio-connection
(lambda ()
(list lsp-clients-emmy-lua-java-path "-jar" lsp-clients-emmy-lua-jar-path)))
:test? (lambda ()
(f-exists? lsp-clients-emmy-lua-jar-path))))
(lsp-stdio-connection
(lambda ()
(list lsp-clients-emmy-lua-java-path "-jar" lsp-clients-emmy-lua-jar-path))
(lambda ()
(f-exists? lsp-clients-emmy-lua-jar-path))))

(lsp-register-client
(make-lsp-client :new-connection (lsp-clients-emmy-lua--create-connection)
Expand Down
51 changes: 30 additions & 21 deletions lsp-csharp.el
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Version 1.34.3 minimum is required."
:link '(url-link "https://github.com/OmniSharp/omnisharp-roslyn"))

(defcustom lsp-csharp-server-install-dir
(locate-user-emacs-file ".cache/omnisharp-roslyn/")
(f-join lsp-server-install-dir "omnisharp-roslyn/")
"Installation directory for OmniSharp Roslyn server."
:group 'lsp-csharp
:type 'directory)
Expand Down Expand Up @@ -77,7 +77,7 @@ Set this if you have the binary installed or have it built yourself."
(defun lsp-csharp--server-dir (version)
"The location of the installed OmniSharp server for VERSION."
(when version
(f-join (expand-file-name lsp-csharp-server-install-dir) version)))
(f-join (expand-file-name lsp-csharp-server-install-dir) version)))

(defun lsp-csharp--server-bin (version)
"The location of OmniSharp executable/script to use to start the server."
Expand All @@ -101,8 +101,8 @@ of Emacs. See https://lists.nongnu.org/archive/html/bug-gnu-emacs/2017-06/msg008
((eq system-type 'darwin)
"omnisharp-osx.tar.gz")
((and (eq system-type 'gnu/linux)
(or (eq (string-match "^x86_64" system-configuration) 0)
(eq (string-match "^i[3-6]86" system-configuration) 0)))
(or (eq (string-match "^x86_64" system-configuration) 0)
(eq (string-match "^i[3-6]86" system-configuration) 0)))
"omnisharp-linux-x64.tar.gz")
(t "omnisharp-mono.tar.gz")))

Expand Down Expand Up @@ -159,18 +159,16 @@ available on github and if so, downloads and installs a newer version."
target-version)
(if (or (not ask-confirmation)
(yes-or-no-p (format "OmniSharp Roslyn Server %s. Do you want to download and install %s now?"
(if installed-version
(format "can be updated, currently installed version is %s" installed-version)
"is not installed")
target-version)))
(let ((cache-dir (expand-file-name (locate-user-emacs-file ".cache/")))
(o-r-dir (expand-file-name (locate-user-emacs-file ".cache/omnisharp-roslyn/")))
(new-server-dir (lsp-csharp--server-dir target-version))
(if installed-version
(format "can be updated, currently installed version is %s" installed-version)
"is not installed")
target-version)))
(let ((new-server-dir (lsp-csharp--server-dir target-version))
(new-server-bin (lsp-csharp--server-bin target-version))
(package-filename (lsp-csharp--server-package-filename))
(package-url (lsp-csharp--server-package-url target-version)))

(f-mkdir cache-dir o-r-dir new-server-dir)
(mkdir new-server-dir t)

(lsp-csharp--extract-server package-url
(f-join new-server-dir package-filename)
Expand Down Expand Up @@ -201,12 +199,12 @@ the file exist already."
(unless (f-exists-p filename)
(message (format "lsp-csharp: downloading from \"%s\"..." url))
(let ((gnutls-algorithm-priority
(if (and (not gnutls-algorithm-priority)
(boundp 'libgnutls-version)
(>= libgnutls-version 30603)
(version<= emacs-version "26.2"))
"NORMAL:-VERS-TLS1.3"
gnutls-algorithm-priority)))
(if (and (not gnutls-algorithm-priority)
(boundp 'libgnutls-version)
(>= libgnutls-version 30603)
(version<= emacs-version "26.2"))
"NORMAL:-VERS-TLS1.3"
gnutls-algorithm-priority)))
(url-copy-file url filename nil))))

(defun lsp-csharp--extract (filename target-dir)
Expand Down Expand Up @@ -242,13 +240,24 @@ Will attempt to install the server if it is not installed already for the
current platform."
(if lsp-csharp-server-path
(list lsp-csharp-server-path "-lsp")
(list (lsp-csharp--get-or-install-server) "-lsp")))
(list (lsp-csharp--server-bin (lsp-csharp--latest-installed-version)) "-lsp")))

(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection
#'lsp-csharp--language-server-command)
#'lsp-csharp--language-server-command
(lambda ()
(when-let (binary (lsp-csharp--server-bin (lsp-csharp--latest-installed-version)))
(f-exists? binary))))

:major-modes '(csharp-mode)
:server-id 'csharp))
:server-id 'csharp
:download-server-fn
(lambda (_client callback error-callback _update?)
(condition-case err
(progn
(lsp-csharp--install-server nil nil)
(funcall callback))
(error (funcall error-callback (error-message-string err)))))))

(provide 'lsp-csharp)
;;; lsp-csharp.el ends here
12 changes: 5 additions & 7 deletions lsp-eslint.el
Original file line number Diff line number Diff line change
Expand Up @@ -198,16 +198,14 @@
(interactive)
(lsp-send-execute-command "eslint.applyAllFixes" (vector (lsp--versioned-text-document-identifier))))



(lsp-register-client
(make-lsp-client
:new-connection
(plist-put (lsp-stdio-connection
(lambda () lsp-eslint-server-command))
:test? (lambda ()
(and (cl-second lsp-eslint-server-command)
(file-exists-p (cl-second lsp-eslint-server-command)))))
(lsp-stdio-connection
(lambda () lsp-eslint-server-command)
(lambda ()
(and (cl-second lsp-eslint-server-command)
(file-exists-p (cl-second lsp-eslint-server-command)))))
:activation-fn (lambda (filename &optional _)
(or (string-match-p (rx (one-or-more anything) "."
(or "ts" "js" "jsx" "tsx" "html" "vue"))
Expand Down
27 changes: 11 additions & 16 deletions lsp-fsharp.el
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
(const :tag "Use .Net Framework" net-framework))
:package-version '(lsp-mode . "6.1"))

(defcustom lsp-fsharp-server-install-dir (locate-user-emacs-file "fsautocomplete/")
(defcustom lsp-fsharp-server-install-dir (f-join lsp-server-install-dir "fsautocomplete/")
"Install directory for fsautocomplete server.
The slash is expected at the end."
:group 'lsp-fsharp
Expand Down Expand Up @@ -179,16 +179,7 @@ disable if `--backgorund-service-enabled' is not used"
".exe")))
(expand-file-name (concat "fsautocomplete" file-ext) lsp-fsharp-server-install-dir)))

(defun lsp-fsharp--fsac-locate ()
"Return the location of the fsautocomplete langauge server."
(let ((fsac (lsp-fsharp--fsac-cmd)))
(unless (file-exists-p fsac)
(if (yes-or-no-p "Server is not installed. Do you want to install it?")
(lsp-fsharp--fsac-install)
(error "LSP F# cannot be started without FsAutoComplete Server")))
fsac))

(defun lsp-fsharp--fsac-install ()
(defun lsp-fsharp--fsac-install (_client callback error-callback _update?)
"Download the latest version of fsautocomplete and extract it to `lsp-fsharp-server-install-dir'."
(let* ((temp-file (make-temp-file "fsautocomplete" nil ".zip"))
(install-dir-full (expand-file-name lsp-fsharp-server-install-dir))
Expand All @@ -197,18 +188,19 @@ disable if `--backgorund-service-enabled' is not used"
(t (user-error (format "Unable to unzip server - file %s cannot be extracted, please extract it manually" temp-file))))))
(url-copy-file lsp-fsharp-server-download-url temp-file t)
(shell-command unzip-script)
(shell-command (format "%s %s --version" (lsp-fsharp--fsac-runtime-cmd) (lsp-fsharp--fsac-cmd)))))
(shell-command (format "%s %s --version" (lsp-fsharp--fsac-runtime-cmd) (lsp-fsharp--fsac-cmd)))
(funcall callback)))

(defun lsp-fsharp-update-fsac ()
"Update fsautocomplete to the latest version."
(interactive)
(-let [install-dir (f-expand lsp-fsharp-server-install-dir)]
(f-delete install-dir t)
(lsp-fsharp--fsac-install)))
(lsp-fsharp--fsac-install nil #'ignore t)))

(defun lsp-fsharp--make-launch-cmd ()
"Build the command required to launch fsautocomplete."
(append (list (lsp-fsharp--fsac-runtime-cmd) (lsp-fsharp--fsac-locate) "--background-service-enabled")
(append (list (lsp-fsharp--fsac-runtime-cmd) (lsp-fsharp--fsac-cmd) "--background-service-enabled")
lsp-fsharp-server-args))

(defun lsp-fsharp--project-list ()
Expand Down Expand Up @@ -260,7 +252,9 @@ disable if `--backgorund-service-enabled' is not used"
("FSharp.EnableReferenceCodeLens" lsp-fsharp-enable-reference-code-lens t)))

(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection 'lsp-fsharp--make-launch-cmd)
(make-lsp-client :new-connection (lsp-stdio-connection
#'lsp-fsharp--make-launch-cmd
(lambda () (f-exists? (lsp-fsharp--fsac-cmd))))
:major-modes '(fsharp-mode)
:notification-handlers (ht ("fsharp/notifyCancel" #'ignore)
("fsharp/notifyWorkspace" #'ignore)
Expand All @@ -275,7 +269,8 @@ disable if `--backgorund-service-enabled' is not used"
(lsp-configuration-section "fsharp"))
(lsp-fsharp--workspace-load
(lsp-fsharp--project-list)))))
:server-id 'fsac))
:server-id 'fsac
:download-server-fn #'lsp-fsharp--fsac-install))

(provide 'lsp-fsharp)
;;; lsp-fsharp.el ends here
Loading

0 comments on commit 9ef7960

Please sign in to comment.