config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

lsp-fsharp.el (12934B)


      1 ;;; lsp-fsharp.el --- description -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2019  Reed Mullanix
      4 
      5 ;; Author: Reed Mullanix <reedmullanix@gmail.com>
      6 ;; Keywords:
      7 
      8 ;; This program is free software; you can redistribute it and/or modify
      9 ;; it under the terms of the GNU General Public License as published by
     10 ;; the Free Software Foundation, either version 3 of the License, or
     11 ;; (at your option) any later version.
     12 
     13 ;; This program is distributed in the hope that it will be useful,
     14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 ;; GNU General Public License for more details.
     17 
     18 ;; You should have received a copy of the GNU General Public License
     19 ;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
     20 
     21 ;;; Commentary:
     22 
     23 ;; lsp-fsharp client
     24 
     25 ;;; Code:
     26 
     27 (require 'lsp-mode)
     28 
     29 (defgroup lsp-fsharp nil
     30   "LSP support for the F# Programming Language, using the FsharpAutoComplete server."
     31   :link '(url-link "https://github.com/fsharp/FsAutoComplete")
     32   :group 'lsp-mode
     33   :package-version '(lsp-mode . "6.1"))
     34 
     35 (defcustom lsp-fsharp-server-install-dir (f-join lsp-server-install-dir "fsautocomplete/")
     36   "Install directory for fsautocomplete server.
     37 The slash is expected at the end."
     38   :group 'lsp-fsharp
     39   :risky t
     40   :type 'directory
     41   :package-version '(lsp-mode . "6.1"))
     42 
     43 (defcustom lsp-fsharp-server-args nil
     44   "Extra arguments for the F# language server."
     45   :type '(repeat string)
     46   :group 'lsp-fsharp
     47   :package-version '(lsp-mode . "6.1"))
     48 
     49 (defcustom lsp-fsharp-keywords-autocomplete t
     50   "Provides keywords in autocomplete list."
     51   :group 'lsp-fsharp
     52   :type 'boolean
     53   :package-version '(lsp-mode . "6.2"))
     54 
     55 (defcustom lsp-fsharp-external-autocomplete nil
     56   "Provides autocompletion for symbols from not opened namespaces/modules;
     57 inserts open on accept."
     58   :group 'lsp-fsharp
     59   :type 'boolean
     60   :package-version '(lsp-mode . "6.2"))
     61 
     62 (defcustom lsp-fsharp-linter t
     63   "Enables FSharpLint integration, provides additional warnings and code
     64 action fixes."
     65   :group 'lsp-fsharp
     66   :type 'boolean
     67   :package-version '(lsp-mode . "6.2"))
     68 
     69 (defcustom lsp-fsharp-union-case-stub-generation t
     70   "Enables a code action to generate pattern matching cases."
     71   :group 'lsp-fsharp
     72   :type 'boolean
     73   :package-version '(lsp-mode . "6.2"))
     74 
     75 (defcustom lsp-fsharp-union-case-stub-generation-body "failwith \"Not Implemented\""
     76   "Defines dummy body used by pattern matching generator."
     77   :group 'lsp-fsharp
     78   :type 'string
     79   :risky t
     80   :package-version '(lsp-mode . "6.2"))
     81 
     82 (defcustom lsp-fsharp-record-stub-generation t
     83   "Enables code action to generate record stub."
     84   :group 'lsp-fsharp
     85   :type 'boolean
     86   :package-version '(lsp-mode . "6.2"))
     87 
     88 (defcustom lsp-fsharp-record-stub-generation-body "failwith \"Not Implemented\""
     89   "Defines dummy body used by record stub generator."
     90   :group 'lsp-fsharp
     91   :type 'string
     92   :risky t
     93   :package-version '(lsp-mode . "6.2"))
     94 
     95 (defcustom lsp-fsharp-interface-stub-generation t
     96   "Enables code action to generate an interface stub."
     97   :group 'lsp-fsharp
     98   :type 'boolean
     99   :package-version '(lsp-mode . "6.2"))
    100 
    101 (defcustom lsp-fsharp-interface-stub-generation-object-identifier "this"
    102   "Defines object identifier used by interface stub generator,
    103 e.g. `this' or `self'."
    104   :group 'lsp-fsharp
    105   :type 'string
    106   :package-version '(lsp-mode . "6.2"))
    107 
    108 (defcustom lsp-fsharp-interface-stub-generation-method-body "failwith \"Not Implemented\""
    109   "Defines dummy body used by interface stub generator."
    110   :group 'lsp-fsharp
    111   :type 'string
    112   :risky t
    113   :package-version '(lsp-mode . "6.2"))
    114 
    115 (defcustom lsp-fsharp-unused-opens-analyzer t
    116   "Enables unused open detection."
    117   :group 'lsp-fsharp
    118   :type 'boolean
    119   :package-version '(lsp-mode . "6.2"))
    120 
    121 (defcustom lsp-fsharp-unused-declarations-analyzer t
    122   "Enables unused symbol detection."
    123   :group 'lsp-fsharp
    124   :type 'boolean
    125   :package-version '(lsp-mode . "6.2"))
    126 
    127 (defcustom lsp-fsharp-simplify-name-analyzer nil
    128   "Enables simplify name analyzer and remove redundant qualifier quick fix."
    129   :group 'lsp-fsharp
    130   :type 'boolean
    131   :package-version '(lsp-mode . "6.2"))
    132 
    133 (defcustom lsp-fsharp-resolve-namespaces t
    134   "Enables resolve namespace quick fix; adds `open' if symbol is from not yet
    135 opened module/namespace."
    136   :group 'lsp-fsharp
    137   :type 'boolean
    138   :package-version '(lsp-mode . "6.2"))
    139 
    140 (defcustom lsp-fsharp-enable-reference-code-lens t
    141   "Enables reference count code lenses."
    142   :group 'lsp-fsharp
    143   :type 'boolean
    144   :package-version '(lsp-mode . "6.2"))
    145 
    146 (defcustom lsp-fsharp-auto-workspace-init nil
    147   "Enable automatic workspace initialization.
    148 Do note that this can cause unexpected or challenging behaviors, as solutions
    149 with test projects are not autoloaded by FSharpAutoComplete."
    150   :group 'lsp-fsharp
    151   :type 'boolean
    152   :risky t)
    153 
    154 (defcustom lsp-fsharp-generate-binlog nil
    155   "Generate a binlog for debugging project cracking."
    156   :group 'lsp-fsharp
    157   :type 'boolean
    158   :package-version '(lsp-mode . "9.0.0"))
    159 
    160 (defun lsp-fsharp--fsac-install (_client callback error-callback update?)
    161   "Install/update fsautocomplete language server using `dotnet tool'.
    162 Will invoke CALLBACK or ERROR-CALLBACK based on result. Will update if
    163 UPDATE? is t."
    164   (lsp-async-start-process
    165    callback
    166    error-callback
    167    "dotnet" "tool" (if update? "update" "install") "-g" "fsautocomplete"))
    168 
    169 (defcustom lsp-fsharp-use-dotnet-tool-for-fsac t
    170   "Run FsAutoComplete as a dotnet tool.
    171 
    172 The binary will be invoked via \"dotnet fsautocomplete\" in the
    173 project's root directory, which will run a project-local tool if
    174 available, else the globally installed tool."
    175   :group 'lsp-fsharp
    176   :type 'boolean
    177   :risky t)
    178 
    179 (defun lsp-fsharp--fsac-cmd ()
    180   "The location of fsautocomplete executable."
    181   (or (-let [maybe-local-executable (expand-file-name "fsautocomplete" lsp-fsharp-server-install-dir)]
    182         (when (f-exists-p maybe-local-executable)
    183           maybe-local-executable))
    184       (executable-find "fsautocomplete")
    185       (f-join (or (getenv "USERPROFILE") (getenv "HOME"))
    186               ".dotnet" "tools" "fsautocomplete")))
    187 
    188 (defun lsp-fsharp--make-launch-cmd ()
    189   "Build the command required to launch fsautocomplete."
    190 
    191   ;; emacs-28.1 on macOS has an issue
    192   ;; that it launches processes using posix_spawn but does not reset sigmask properly
    193   ;; thus causing dotnet runtime to lockup awaiting a SIGCHLD signal that never comes
    194   ;; from subprocesses that quit
    195   ;;
    196   ;; as a workaround we will wrap fsautocomplete invocation in "/bin/ksh -c" (on macos)
    197   ;; so it launches with proper sigmask
    198   ;;
    199   ;; see https://lists.gnu.org/archive/html/emacs-devel/2022-02/msg00461.html
    200   ;; --
    201   ;; we also try to resolve full path to fsautocomplete using `executable-find' as
    202   ;; our `startup-wrapper' may use $PATH to interpret the location of fsautocomplete
    203   ;; and we want to actually use `exec-path' here
    204 
    205   (let ((startup-wrapper (cond ((and (eq 'darwin system-type)
    206                                      (version= "28.1" emacs-version))
    207                                 (list "/bin/ksh" "-c"))
    208 
    209                                (t nil)))
    210         (fsautocomplete-exec (lsp-fsharp--fsac-cmd)))
    211     (append startup-wrapper
    212             (list fsautocomplete-exec)
    213             lsp-fsharp-server-args)))
    214 
    215 (defun lsp-fsharp--test-fsautocomplete-present ()
    216   "Return non-nil if dotnet tool fsautocomplete is installed globally."
    217   (if lsp-fsharp-use-dotnet-tool-for-fsac
    218       (string-match-p "fsautocomplete"
    219                       (shell-command-to-string "dotnet tool list -g"))
    220     (f-exists? (lsp-fsharp--fsac-cmd))))
    221 
    222 (defun lsp-fsharp--project-list (workspace)
    223   "Get the list of files we need to send to fsharp/workspaceLoad."
    224   (let* ((response (lsp-request "fsharp/workspacePeek"
    225                                 `(:directory ,(lsp--workspace-root workspace)
    226                                              :deep 10
    227                                              :excludedDirs ["paket-files" ".git" "packages" "node_modules"])))
    228          (data (lsp--read-json (lsp-get response :content)))
    229          (found (-> data (lsp-get :Data) (lsp-get :Found)))
    230          (directory (seq-find (lambda (d) (equal "directory" (lsp-get d :Type))) found)))
    231     (-> directory (lsp-get :Data) (lsp-get :Fsprojs))))
    232 
    233 ;;;###autoload
    234 (defun lsp-fsharp--workspace-load (projects)
    235   "Load all of the provided PROJECTS."
    236   (lsp-request-async "fsharp/workspaceLoad"
    237                      `(:textDocuments ,(vconcat [] (mapcar (lambda (p) `(:uri ,p)) projects)))
    238                      (lambda (_)
    239                        (lsp--info "Workspace Loaded!"))))
    240 
    241 (defvar lsp-fsharp--default-init-options  (list)
    242   "Default init options to be passed to FSharpAutoComplete,
    243   updated conditionally by `lsp-fsharp--make-init-options'.")
    244 
    245 (defun lsp-fsharp--make-init-options ()
    246   "Init options for F#."
    247   (-let [opts lsp-fsharp--default-init-options]
    248     (if lsp-fsharp-auto-workspace-init
    249         (push '(:AutomaticWorkspaceInit . t) opts)
    250       opts)))
    251 
    252 (lsp-register-custom-settings
    253  `(("FSharp.KeywordsAutocomplete" lsp-fsharp-keywords-autocomplete t)
    254    ("FSharp.ExternalAutocomplete" lsp-fsharp-external-autocomplete t)
    255    ("FSharp.Linter" lsp-fsharp-linter t)
    256    ("FSharp.UnionCaseStubGeneration" lsp-fsharp-union-case-stub-generation t)
    257    ("FSharp.UnionCaseStubGenerationBody" lsp-fsharp-union-case-stub-generation-body)
    258    ("FSharp.RecordStubGeneration" lsp-fsharp-record-stub-generation t)
    259    ("FSharp.RecordStubGenerationBody" lsp-fsharp-record-stub-generation-body)
    260    ("FSharp.InterfaceStubGeneration" lsp-fsharp-interface-stub-generation t)
    261    ("FSharp.InterfaceStubGenerationObjectIdentifier" lsp-fsharp-interface-stub-generation-object-identifier)
    262    ("FSharp.InterfaceStubGenerationMethodBody" lsp-fsharp-interface-stub-generation-method-body)
    263    ("FSharp.UnusedOpensAnalyzer" lsp-fsharp-unused-opens-analyzer t)
    264    ("FSharp.UnusedDeclarationsAnalyzer" lsp-fsharp-unused-declarations-analyzer t)
    265    ("FSharp.SimplifyNameAnalyzer" lsp-fsharp-simplify-name-analyzer t)
    266    ("FSharp.ResolveNamespaces" lsp-fsharp-resolve-namespaces t)
    267    ("FSharp.EnableReferenceCodeLens" lsp-fsharp-enable-reference-code-lens t)
    268    ("FSharp.GenerateBinlog" lsp-fsharp-generate-binlog t)))
    269 
    270 (lsp-register-client
    271  (make-lsp-client :new-connection (lsp-stdio-connection
    272                                    #'lsp-fsharp--make-launch-cmd
    273                                    #'lsp-fsharp--test-fsautocomplete-present)
    274                   :major-modes '(fsharp-mode)
    275                   :notification-handlers (ht ("fsharp/notifyCancel" #'ignore)
    276                                              ("fsharp/notifyWorkspace" #'ignore)
    277                                              ("fsharp/fileParsed" #'ignore)
    278                                              ("fsharp/notifyWorkspacePeek" #'ignore)
    279                                              ("fsharp/documentAnalyzed" #'ignore)
    280                                              ("workspace/codeLens/refresh" #'ignore)
    281                                              ("fsharp/testDetected" #'ignore))
    282                   :initialization-options 'lsp-fsharp--make-init-options
    283                   :initialized-fn (lambda (workspace)
    284                                     (with-lsp-workspace workspace
    285                                       ;; Something needs to be calling lsp--set-configuration
    286                                       (progn
    287                                         (lsp--set-configuration
    288                                          (lsp-configuration-section "fsharp"))
    289                                         (lsp-fsharp--workspace-load
    290                                          (lsp-fsharp--project-list workspace)))))
    291                   :after-open-fn ;; workaround https://github.com/fsharp/FsAutoComplete/issues/833
    292                   (lambda ()
    293                     (setq-local lsp-default-create-error-handler-fn
    294                                 (lambda (method)
    295                                   (lambda (error)
    296                                     (when
    297                                         (not
    298                                          (seq-find (lambda (s)
    299                                                      (string= s (lsp-get error :message)))
    300                                                    '("Index was outside the bounds of the array."
    301                                                      "No symbol information found"
    302                                                      "No ident at this location")))
    303                                       (lsp--warn
    304                                        "%s"
    305                                        (or (lsp--error-string error)
    306                                            (format "%s Request has failed" method))))))))
    307                   :server-id 'fsac
    308                   :download-server-fn #'lsp-fsharp--fsac-install))
    309 
    310 (lsp-consistency-check lsp-fsharp)
    311 
    312 (provide 'lsp-fsharp)
    313 ;;; lsp-fsharp.el ends here