emacs/code/elpa/lsp-mode-20230823.446/lsp-fsharp.el

314 lines
13 KiB
EmacsLisp

;;; lsp-fsharp.el --- description -*- lexical-binding: t; -*-
;; Copyright (C) 2019 Reed Mullanix
;; Author: Reed Mullanix <reedmullanix@gmail.com>
;; Keywords:
;; 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:
;; lsp-fsharp client
;;; Code:
(require 'lsp-mode)
(defgroup lsp-fsharp nil
"LSP support for the F# Programming Language, using the FsharpAutoComplete server."
:link '(url-link "https://github.com/fsharp/FsAutoComplete")
:group 'lsp-mode
:package-version '(lsp-mode . "6.1"))
(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
:risky t
:type 'directory
:package-version '(lsp-mode . "6.1"))
(defcustom lsp-fsharp-server-args nil
"Extra arguments for the F# language server."
:type '(repeat string)
:group 'lsp-fsharp
:package-version '(lsp-mode . "6.1"))
(defcustom lsp-fsharp-keywords-autocomplete t
"Provides keywords in autocomplete list."
:group 'lsp-fsharp
:type 'boolean
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-external-autocomplete nil
"Provides autocompletion for symbols from not opened namespaces/modules;
inserts open on accept."
:group 'lsp-fsharp
:type 'boolean
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-linter t
"Enables FSharpLint integration, provides additional warnings and code
action fixes."
:group 'lsp-fsharp
:type 'boolean
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-union-case-stub-generation t
"Enables a code action to generate pattern matching cases."
:group 'lsp-fsharp
:type 'boolean
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-union-case-stub-generation-body "failwith \"Not Implemented\""
"Defines dummy body used by pattern matching generator."
:group 'lsp-fsharp
:type 'string
:risky t
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-record-stub-generation t
"Enables code action to generate record stub."
:group 'lsp-fsharp
:type 'boolean
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-record-stub-generation-body "failwith \"Not Implemented\""
"Defines dummy body used by record stub generator."
:group 'lsp-fsharp
:type 'string
:risky t
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-interface-stub-generation t
"Enables code action to generate an interface stub."
:group 'lsp-fsharp
:type 'boolean
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-interface-stub-generation-object-identifier "this"
"Defines object identifier used by interface stub generator,
e.g. `this' or `self'."
:group 'lsp-fsharp
:type 'string
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-interface-stub-generation-method-body "failwith \"Not Implemented\""
"Defines dummy body used by interface stub generator."
:group 'lsp-fsharp
:type 'string
:risky t
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-unused-opens-analyzer t
"Enables unused open detection."
:group 'lsp-fsharp
:type 'boolean
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-unused-declarations-analyzer t
"Enables unused symbol detection."
:group 'lsp-fsharp
:type 'boolean
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-simplify-name-analyzer nil
"Enables simplify name analyzer and remove redundant qualifier quick fix."
:group 'lsp-fsharp
:type 'boolean
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-resolve-namespaces t
"Enables resolve namespace quick fix; adds `open' if symbol is from not yet
opened module/namespace."
:group 'lsp-fsharp
:type 'boolean
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-enable-reference-code-lens t
"Enables reference count code lenses."
:group 'lsp-fsharp
:type 'boolean
:package-version '(lsp-mode . "6.2"))
(defcustom lsp-fsharp-auto-workspace-init nil
"Enable automatic workspace initialization.
Do note that this can cause unexpected or challenging behaviors, as solutions
with test projects are not autoloaded by FSharpAutoComplete."
:group 'lsp-fsharp
:type 'boolean
:risky t)
(defcustom lsp-fsharp-generate-binlog nil
"Generate a binlog for debugging project cracking."
:group 'lsp-fsharp
: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.
The binary will be invoked via \"dotnet fsautocomplete\" in the
project's root directory, which will run a project-local tool if
available, else the globally installed tool."
:group 'lsp-fsharp
:type 'boolean
:risky t)
(defun lsp-fsharp--fsac-cmd ()
"The location of fsautocomplete executable."
(or (-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")
(f-join (or (getenv "USERPROFILE") (getenv "HOME"))
".dotnet" "tools" "fsautocomplete")))
(defun lsp-fsharp--make-launch-cmd ()
"Build the command required to launch fsautocomplete."
;; emacs-28.1 on macOS has an issue
;; that it launches processes using posix_spawn but does not reset sigmask properly
;; thus causing dotnet runtime to lockup awaiting a SIGCHLD signal that never comes
;; from subprocesses that quit
;;
;; as a workaround we will wrap fsautocomplete invocation in "/bin/ksh -c" (on macos)
;; so it launches with proper sigmask
;;
;; see https://lists.gnu.org/archive/html/emacs-devel/2022-02/msg00461.html
;; --
;; we also try to resolve full path to fsautocomplete using `executable-find' as
;; our `startup-wrapper' may use $PATH to interpret the location of fsautocomplete
;; and we want to actually use `exec-path' here
(let ((startup-wrapper (cond ((and (eq 'darwin system-type)
(version= "28.1" emacs-version))
(list "/bin/ksh" "-c"))
(t nil)))
(fsautocomplete-exec (lsp-fsharp--fsac-cmd)))
(append startup-wrapper
(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"))
(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"
`(:directory ,(lsp--workspace-root workspace)
:deep 10
:excludedDirs ["paket-files" ".git" "packages" "node_modules"])))
(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."
(lsp-request-async "fsharp/workspaceLoad"
`(:textDocuments ,(vconcat [] (mapcar (lambda (p) `(:uri ,p)) projects)))
(lambda (_)
(lsp--info "Workspace Loaded!"))))
(defvar lsp-fsharp--default-init-options (list)
"Default init options to be passed to FSharpAutoComplete,
updated conditionally by `lsp-fsharp--make-init-options'.")
(defun lsp-fsharp--make-init-options ()
"Init options for F#."
(-let [opts lsp-fsharp--default-init-options]
(if lsp-fsharp-auto-workspace-init
(push '(:AutomaticWorkspaceInit . t) opts)
opts)))
(lsp-register-custom-settings
`(("FSharp.KeywordsAutocomplete" lsp-fsharp-keywords-autocomplete t)
("FSharp.ExternalAutocomplete" lsp-fsharp-external-autocomplete t)
("FSharp.Linter" lsp-fsharp-linter t)
("FSharp.UnionCaseStubGeneration" lsp-fsharp-union-case-stub-generation t)
("FSharp.UnionCaseStubGenerationBody" lsp-fsharp-union-case-stub-generation-body)
("FSharp.RecordStubGeneration" lsp-fsharp-record-stub-generation t)
("FSharp.RecordStubGenerationBody" lsp-fsharp-record-stub-generation-body)
("FSharp.InterfaceStubGeneration" lsp-fsharp-interface-stub-generation t)
("FSharp.InterfaceStubGenerationObjectIdentifier" lsp-fsharp-interface-stub-generation-object-identifier)
("FSharp.InterfaceStubGenerationMethodBody" lsp-fsharp-interface-stub-generation-method-body)
("FSharp.UnusedOpensAnalyzer" lsp-fsharp-unused-opens-analyzer t)
("FSharp.UnusedDeclarationsAnalyzer" lsp-fsharp-unused-declarations-analyzer t)
("FSharp.SimplifyNameAnalyzer" lsp-fsharp-simplify-name-analyzer t)
("FSharp.ResolveNamespaces" lsp-fsharp-resolve-namespaces t)
("FSharp.EnableReferenceCodeLens" lsp-fsharp-enable-reference-code-lens t)
("FSharp.GenerateBinlog" lsp-fsharp-generate-binlog t)))
(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection
#'lsp-fsharp--make-launch-cmd
#'lsp-fsharp--test-fsautocomplete-present)
:major-modes '(fsharp-mode)
:notification-handlers (ht ("fsharp/notifyCancel" #'ignore)
("fsharp/notifyWorkspace" #'ignore)
("fsharp/fileParsed" #'ignore)
("fsharp/notifyWorkspacePeek" #'ignore)
("fsharp/documentAnalyzed" #'ignore)
("workspace/codeLens/refresh" #'ignore)
("fsharp/testDetected" #'ignore))
:initialization-options 'lsp-fsharp--make-init-options
:initialized-fn (lambda (workspace)
(with-lsp-workspace workspace
;; Something needs to be calling lsp--set-configuration
(progn
(lsp--set-configuration
(lsp-configuration-section "fsharp"))
(lsp-fsharp--workspace-load
(lsp-fsharp--project-list workspace)))))
:after-open-fn ;; workaround https://github.com/fsharp/FsAutoComplete/issues/833
(lambda ()
(setq-local lsp-default-create-error-handler-fn
(lambda (method)
(lambda (error)
(when
(not
(seq-find (lambda (s)
(string= s (lsp-get error :message)))
'("Index was outside the bounds of the array."
"No symbol information found"
"No ident at this location")))
(lsp--warn
"%s"
(or (lsp--error-string error)
(format "%s Request has failed" method))))))))
:server-id 'fsac
:download-server-fn #'lsp-fsharp--fsac-install))
(lsp-consistency-check lsp-fsharp)
(provide 'lsp-fsharp)
;;; lsp-fsharp.el ends here