diff --git a/clients/lsp-csharp.el b/clients/lsp-csharp.el index 2916bdc4f0..c2b8da3264 100644 --- a/clients/lsp-csharp.el +++ b/clients/lsp-csharp.el @@ -113,6 +113,20 @@ Usually this is to be set in your .dir-locals.el on the project root directory." :group 'lsp-csharp-omnisharp :type 'file) +(defcustom lsp-csharp-csharpls-use-dotnet-tool t + "Whether to use a dotnet tool version of the expected C# language server; only available for csharp-ls" + :group 'lsp-csharp + :type 'boolean + :risky t) + +(defcustom lsp-csharp-csharpls-use-local-tool nil + "Whether to use csharp-ls as a global or local dotnet tool. + +Note: this variable has no effect if lsp-csharp-csharpls-use-dotnet-tool is nil." + :group 'lsp-csharp + :type 'boolean + :risky t) + (lsp-dependency 'omnisharp-roslyn `(:download :url lsp-csharp-omnisharp-roslyn-download-url @@ -413,6 +427,15 @@ filename is returned so lsp-mode can display this file." (with-temp-buffer (insert-file-contents metadata-file-name) (buffer-string)))))) +(defun lsp-csharp--cls-find-executable () + (or (when lsp-csharp-csharpls-use-dotnet-tool + (-flatten (list "dotnet" (if lsp-csharp-csharpls-use-local-tool (list "tool" "run") "") "csharp-ls"))) + (executable-find "csharp-ls") + ;; NOTE[gastove|2023-02-03] This approach might be remove-able if we + ;; standardize on going through the `dotnet' cli. + (f-join (or (getenv "USERPROFILE") (getenv "HOME")) + ".dotnet" "tools" "csharp-ls"))) + (defun lsp-csharp--cls-make-launch-cmd () "Return command line to invoke csharp-ls." @@ -432,20 +455,24 @@ filename is returned so lsp-mode can display this file." (t nil))) - (csharp-ls-exec (or (executable-find "csharp-ls") - (f-join (or (getenv "USERPROFILE") (getenv "HOME")) - ".dotnet" "tools" "csharp-ls"))) + (csharp-ls-exec (lsp-csharp--cls-find-executable)) (solution-file-params (when lsp-csharp-solution-file (list "-s" lsp-csharp-solution-file)))) (append startup-wrapper - (list csharp-ls-exec) + (if (listp csharp-ls-exec) + csharp-ls-exec + (list csharp-ls-exec)) solution-file-params))) (defun lsp-csharp--cls-test-csharp-ls-present () - "Return non-nil if dotnet tool csharp-ls is installed globally." + "Return non-nil if dotnet tool csharp-ls is installed as a dotnet tool." (string-match-p "csharp-ls" - (shell-command-to-string "dotnet tool list -g"))) + (shell-command-to-string + (if lsp-csharp-csharpls-use-local-tool + "dotnet tool list" + "dotnet tool list -g"))) + ) (defun lsp-csharp--cls-download-server (_client callback error-callback update?) "Install/update csharp-ls language server using `dotnet tool'. @@ -455,7 +482,7 @@ Will update if UPDATE? is t" (lsp-async-start-process callback error-callback - "dotnet" "tool" (if update? "update" "install") "-g" "csharp-ls")) + "dotnet" "tool" (if update? "update" "install") (if lsp-csharp-csharpls-use-local-tool "" "-g") "csharp-ls")) (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection #'lsp-csharp--cls-make-launch-cmd diff --git a/clients/lsp-fsharp.el b/clients/lsp-fsharp.el index 59c203c1f0..4d35cc2dd3 100644 --- a/clients/lsp-fsharp.el +++ b/clients/lsp-fsharp.el @@ -157,15 +157,6 @@ with test projects are not autoloaded by FSharpAutoComplete." :type 'boolean :package-version '(lsp-mode . "8.0.1")) -(defun lsp-fsharp--fsac-install (_client callback error-callback update?) - "Install/update fsautocomplete language server using `dotnet tool'. -Will invoke CALLBACK or ERROR-CALLBACK based on result. Will update if -UPDATE? is t." - (lsp-async-start-process - callback - error-callback - "dotnet" "tool" (if update? "update" "install") "-g" "fsautocomplete")) - (defcustom lsp-fsharp-use-dotnet-tool-for-fsac t "Run FsAutoComplete as a dotnet tool. @@ -176,9 +167,38 @@ available, else the globally installed tool." :type 'boolean :risky t) +(defcustom lsp-fsharp-use-dotnet-local-tool nil + "When running FsAutoComplete as a dotnet tool, use the local version. + +This variable will have no effect if +`lsp-fsharp-use-dotnet-tool-for-fsac' is nil. + +This variable is risky as a buffer-local, and should instead be +set per-project (e.g. in a .dir-locals.el at the root of a +repository)." + :group 'lsp-fsharp + :type 'boolean + :risky t) + +(defcustom lsp-fsharp-workspace-extra-exclude-dirs '() + "Additional directories to exclude from FsAutoComplete workspace loading / discovery." + :group 'lsp-fsharp + :type '(repeat string)) + +(defun lsp-fsharp--fsac-install (_client callback error-callback update?) + "Install/update fsautocomplete language server using `dotnet tool'. +Will invoke CALLBACK or ERROR-CALLBACK based on result. Will update if +UPDATE? is t." + (lsp-async-start-process + callback + error-callback + "dotnet" "tool" (if update? "update" "install") (when lsp-fsharp-use-dotnet-local-tool "-g") "fsautocomplete")) + (defun lsp-fsharp--fsac-cmd () "The location of fsautocomplete executable." - (or (-let [maybe-local-executable (expand-file-name "fsautocomplete" lsp-fsharp-server-install-dir)] + (or (when lsp-fsharp-use-dotnet-tool-for-fsac + (list "dotnet" (if lsp-fsharp-use-dotnet-local-tool "" "tool") "run" "fsautocomplete")) + (-let [maybe-local-executable (expand-file-name "fsautocomplete" lsp-fsharp-server-install-dir)] (when (f-exists-p maybe-local-executable) maybe-local-executable)) (executable-find "fsautocomplete") @@ -209,27 +229,38 @@ available, else the globally installed tool." (t nil))) (fsautocomplete-exec (lsp-fsharp--fsac-cmd))) (append startup-wrapper - (list fsautocomplete-exec) + (if (listp fsautocomplete-exec) + fsautocomplete-exec + (list fsautocomplete-exec)) lsp-fsharp-server-args))) (defun lsp-fsharp--test-fsautocomplete-present () "Return non-nil if dotnet tool fsautocomplete is installed globally." (if lsp-fsharp-use-dotnet-tool-for-fsac - (string-match-p "fsautocomplete" - (shell-command-to-string "dotnet tool list -g")) + (-let* ((cmd-str (if lsp-fsharp-use-dotnet-local-tool + "dotnet tool list" + "dotnet tool list -g")) + (res (string-match-p "fsautocomplete" + (shell-command-to-string cmd-str)))) + (if res res + (error "Failed to locate fsautocomplete binary; due to lsp-fsharp-use-dotnet-local-tool == %s, checked with command %s" lsp-fsharp-use-dotnet-local-tool cmd-str))) + (f-exists? (lsp-fsharp--fsac-cmd)))) (defun lsp-fsharp--project-list (workspace) "Get the list of files we need to send to fsharp/workspaceLoad." - (let* ((response (lsp-request "fsharp/workspacePeek" + (let* ((base-exlude-dirs ["paket-files" ".git" "packages" "node_modules"]) + (exclude-dirs (apply 'vector (append base-exlude-dirs lsp-fsharp-workspace-extra-exclude-dirs))) + (response (lsp-request "fsharp/workspacePeek" `(:directory ,(lsp--workspace-root workspace) :deep 10 - :excludedDirs ["paket-files" ".git" "packages" "node_modules"]))) + :excludedDirs ,exclude-dirs))) (data (lsp--read-json (lsp-get response :content))) (found (-> data (lsp-get :Data) (lsp-get :Found))) (directory (seq-find (lambda (d) (equal "directory" (lsp-get d :Type))) found))) (-> directory (lsp-get :Data) (lsp-get :Fsprojs)))) + ;;;###autoload (defun lsp-fsharp--workspace-load (projects) "Load all of the provided PROJECTS."