config

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

lsp-mode.el (433671B)


      1 ;;; lsp-mode.el --- LSP mode                              -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2020-2024 emacs-lsp maintainers
      4 
      5 ;; Author: Vibhav Pant, Fangrui Song, Ivan Yonchovski
      6 ;; Keywords: languages
      7 ;; Package-Requires: ((emacs "27.1") (dash "2.18.0") (f "0.20.0") (ht "2.3") (spinner "1.7.3") (markdown-mode "2.3") (lv "0") (eldoc "1.11"))
      8 ;; Version: 9.0.1
      9 
     10 ;; URL: https://github.com/emacs-lsp/lsp-mode
     11 ;; This program is free software; you can redistribute it and/or modify
     12 ;; it under the terms of the GNU General Public License as published by
     13 ;; the Free Software Foundation, either version 3 of the License, or
     14 ;; (at your option) any later version.
     15 
     16 ;; This program is distributed in the hope that it will be useful,
     17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     19 ;; GNU General Public License for more details.
     20 
     21 ;; You should have received a copy of the GNU General Public License
     22 ;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
     23 
     24 ;;; Commentary:
     25 
     26 ;; Emacs client/library for the Language Server Protocol
     27 
     28 ;;; Code:
     29 
     30 (require 'cl-generic)
     31 (require 'cl-lib)
     32 (require 'compile)
     33 (require 'dash)
     34 (require 'epg)
     35 (require 'ewoc)
     36 (require 'f)
     37 (require 'filenotify)
     38 (require 'files)
     39 (require 'ht)
     40 (require 'imenu)
     41 (require 'inline)
     42 (require 'json)
     43 (require 'lv)
     44 (require 'markdown-mode)
     45 (require 'network-stream)
     46 (require 'pcase)
     47 (require 'rx)
     48 (require 's)
     49 (require 'seq)
     50 (require 'spinner)
     51 (require 'subr-x)
     52 (require 'tree-widget)
     53 (require 'url-parse)
     54 (require 'url-util)
     55 (require 'widget)
     56 (require 'xref)
     57 (require 'minibuffer)
     58 (require 'help-mode)
     59 (require 'lsp-protocol)
     60 
     61 (defgroup lsp-mode nil
     62   "Language Server Protocol client."
     63   :group 'tools
     64   :tag "Language Server (lsp-mode)")
     65 
     66 (declare-function evil-set-command-property "ext:evil-common")
     67 (declare-function projectile-project-root "ext:projectile")
     68 (declare-function yas-expand-snippet "ext:yasnippet")
     69 (declare-function dap-mode "ext:dap-mode")
     70 (declare-function dap-auto-configure-mode "ext:dap-mode")
     71 
     72 (defvar yas-inhibit-overlay-modification-protection)
     73 (defvar yas-indent-line)
     74 (defvar yas-wrap-around-region)
     75 (defvar yas-also-auto-indent-first-line)
     76 (defvar dap-auto-configure-mode)
     77 (defvar dap-ui-menu-items)
     78 (defvar company-minimum-prefix-length)
     79 
     80 (defconst lsp--message-type-face
     81   `((1 . ,compilation-error-face)
     82     (2 . ,compilation-warning-face)
     83     (3 . ,compilation-message-face)
     84     (4 . ,compilation-info-face)))
     85 
     86 (defconst lsp--errors
     87   '((-32700 "Parse Error")
     88     (-32600 "Invalid Request")
     89     (-32601 "Method not Found")
     90     (-32602 "Invalid Parameters")
     91     (-32603 "Internal Error")
     92     (-32099 "Server Start Error")
     93     (-32000 "Server End Error")
     94     (-32002 "Server Not Initialized")
     95     (-32001 "Unknown Error Code")
     96     (-32800 "Request Cancelled"))
     97   "Alist of error codes to user friendly strings.")
     98 
     99 (defconst lsp--empty-ht (make-hash-table))
    100 
    101 (eval-and-compile
    102   (defun dash-expand:&lsp-wks (key source)
    103     `(,(intern-soft (format "lsp--workspace-%s" (eval key))) ,source))
    104 
    105   (defun dash-expand:&lsp-cln (key source)
    106     `(,(intern-soft (format "lsp--client-%s" (eval key))) ,source)))
    107 
    108 (define-obsolete-variable-alias 'lsp-print-io 'lsp-log-io "lsp-mode 6.1")
    109 
    110 (defcustom lsp-log-io nil
    111   "If non-nil, log all messages from the language server to a *lsp-log* buffer."
    112   :group 'lsp-mode
    113   :type 'boolean)
    114 
    115 (defcustom lsp-log-io-allowlist-methods '()
    116   "The methods to filter before print to lsp-log-io."
    117   :group 'lsp-mode
    118   :type '(repeat string)
    119   :package-version '(lsp-mode . "9.0.0"))
    120 
    121 (defcustom lsp-log-max message-log-max
    122   "Maximum number of lines to keep in the log buffer.
    123 If nil, disable message logging.  If t, log messages but don’t truncate
    124 the buffer when it becomes large."
    125   :group 'lsp-mode
    126   :type '(choice (const :tag "Disable" nil)
    127                  (integer :tag "lines")
    128                  (const :tag "Unlimited" t))
    129   :package-version '(lsp-mode . "6.1"))
    130 
    131 (defcustom lsp-io-messages-max t
    132   "Maximum number of messages that can be locked in a `lsp-io' buffer."
    133   :group 'lsp-mode
    134   :type '(choice (const :tag "Unlimited" t)
    135                  (integer :tag "Messages"))
    136   :package-version '(lsp-mode . "6.1"))
    137 
    138 (defcustom lsp-keep-workspace-alive t
    139   "If non nil keep workspace alive when the last workspace buffer is closed."
    140   :group 'lsp-mode
    141   :type 'boolean)
    142 
    143 (defcustom lsp-enable-snippet t
    144   "Enable/disable snippet completion support."
    145   :group 'lsp-completion
    146   :type 'boolean)
    147 
    148 (defcustom lsp-enable-folding t
    149   "Enable/disable code folding support."
    150   :group 'lsp-mode
    151   :type 'boolean
    152   :package-version '(lsp-mode . "6.1"))
    153 
    154 (define-obsolete-variable-alias 'lsp-enable-semantic-highlighting 'lsp-semantic-tokens-enable "lsp-mode 8.0.0")
    155 
    156 (defcustom lsp-semantic-tokens-enable nil
    157   "Enable/disable support for semantic tokens.
    158 As defined by the Language Server Protocol 3.16."
    159   :group 'lsp-semantic-tokens
    160   :type 'boolean)
    161 
    162 (defcustom lsp-folding-range-limit nil
    163   "The maximum number of folding ranges to receive from the language server."
    164   :group 'lsp-mode
    165   :type '(choice (const :tag "No limit." nil)
    166                  (integer :tag "Number of lines."))
    167   :package-version '(lsp-mode . "6.1"))
    168 
    169 (defcustom lsp-folding-line-folding-only nil
    170   "If non-nil, only fold complete lines."
    171   :group 'lsp-mode
    172   :type 'boolean
    173   :package-version '(lsp-mode . "6.1"))
    174 
    175 (defcustom lsp-client-packages
    176   '( ccls lsp-actionscript lsp-ada lsp-angular lsp-ansible lsp-asm lsp-astro
    177      lsp-autotools lsp-awk lsp-bash lsp-beancount lsp-bufls lsp-clangd
    178      lsp-clojure lsp-cmake lsp-cobol lsp-credo lsp-crystal lsp-csharp lsp-css
    179      lsp-cucumber lsp-cypher lsp-d lsp-dart lsp-dhall lsp-docker lsp-dockerfile
    180      lsp-earthly lsp-elixir lsp-elm lsp-emmet lsp-erlang lsp-eslint lsp-fortran lsp-fsharp
    181      lsp-gdscript lsp-gleam lsp-glsl lsp-go lsp-golangci-lint lsp-grammarly
    182      lsp-graphql lsp-groovy lsp-hack lsp-haskell lsp-haxe lsp-idris lsp-java
    183      lsp-javascript lsp-jq lsp-json lsp-kotlin lsp-latex lsp-lisp lsp-ltex
    184      lsp-lua lsp-magik lsp-markdown lsp-marksman lsp-mdx lsp-meson lsp-metals lsp-mint
    185      lsp-mojo lsp-move lsp-mssql lsp-nginx lsp-nim lsp-nix lsp-nushell lsp-ocaml
    186      lsp-openscad lsp-pascal lsp-perl lsp-perlnavigator lsp-php lsp-pls
    187      lsp-purescript lsp-pwsh lsp-pyls lsp-pylsp lsp-pyright lsp-python-ms
    188      lsp-qml lsp-r lsp-racket lsp-remark lsp-rf lsp-roslyn lsp-rubocop lsp-ruby-lsp
    189      lsp-ruby-syntax-tree lsp-ruff-lsp lsp-rust lsp-semgrep lsp-shader
    190      lsp-solargraph lsp-solidity lsp-sonarlint lsp-sorbet lsp-sourcekit
    191      lsp-sql lsp-sqls lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform
    192      lsp-tex lsp-tilt lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-v
    193      lsp-vala lsp-verilog lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl
    194      lsp-xml lsp-yaml lsp-yang lsp-zig)
    195   "List of the clients to be automatically required."
    196   :group 'lsp-mode
    197   :type '(repeat symbol))
    198 
    199 (defcustom lsp-progress-via-spinner t
    200   "If non-nil, display LSP $/progress reports via a spinner in the modeline."
    201   :group 'lsp-mode
    202   :type 'boolean)
    203 
    204 (defcustom lsp-progress-spinner-type 'progress-bar
    205   "Holds the type of spinner to be used in the mode-line.
    206 Takes a value accepted by `spinner-start'."
    207   :group 'lsp-mode
    208   :type `(choice :tag "Choose a spinner by name"
    209                  ,@(mapcar (lambda (c) (list 'const (car c)))
    210                            spinner-types)))
    211 
    212 (defvar-local lsp-use-workspace-root-for-server-default-directory nil
    213   "Use `lsp-workspace-root' for `default-directory' when starting LSP process.")
    214 
    215 (defvar-local lsp--cur-workspace nil)
    216 
    217 (defvar-local lsp--cur-version 0)
    218 (defvar-local lsp--virtual-buffer-connections nil)
    219 (defvar-local lsp--virtual-buffer nil)
    220 (defvar lsp--virtual-buffer-mappings (ht))
    221 
    222 (defvar lsp--uri-file-prefix (pcase system-type
    223                                (`windows-nt "file:///")
    224                                (_ "file://"))
    225   "Prefix for a file-uri.")
    226 
    227 (defvar-local lsp-buffer-uri nil
    228   "If set, return it instead of calculating it using `buffer-file-name'.")
    229 
    230 (define-error 'lsp-error "Unknown lsp-mode error")
    231 (define-error 'lsp-empty-response-error
    232   "Empty response from the language server" 'lsp-error)
    233 (define-error 'lsp-timed-out-error
    234   "Timed out while waiting for a response from the language server" 'lsp-error)
    235 (define-error 'lsp-capability-not-supported
    236   "Capability not supported by the language server" 'lsp-error)
    237 (define-error 'lsp-file-scheme-not-supported
    238   "Unsupported file scheme" 'lsp-error)
    239 (define-error 'lsp-client-already-exists-error
    240   "A client with this server-id already exists" 'lsp-error)
    241 (define-error 'lsp-no-code-actions
    242   "No code actions" 'lsp-error)
    243 
    244 (defcustom lsp-auto-guess-root nil
    245   "Automatically guess the project root using projectile/project.
    246 Do *not* use this setting unless you are familiar with `lsp-mode'
    247 internals and you are sure that all of your projects are
    248 following `projectile'/`project.el' conventions."
    249   :group 'lsp-mode
    250   :type 'boolean)
    251 
    252 (defcustom lsp-guess-root-without-session nil
    253   "Ignore the session file when calculating the project root.
    254 You almost always want to set lsp-auto-guess-root too.
    255 Do *not* use this setting unless you are familiar with `lsp-mode'
    256 internals and you are sure that all of your projects are
    257 following `projectile'/`project.el' conventions."
    258   :group 'lsp-mode
    259   :type 'boolean)
    260 
    261 (defcustom lsp-restart 'interactive
    262   "Defines how server-exited events must be handled."
    263   :group 'lsp-mode
    264   :type '(choice (const interactive)
    265                  (const auto-restart)
    266                  (const ignore)))
    267 
    268 (defcustom lsp-session-file (expand-file-name (locate-user-emacs-file ".lsp-session-v1"))
    269   "File where session information is stored."
    270   :group 'lsp-mode
    271   :type 'file)
    272 
    273 (defcustom lsp-auto-configure t
    274   "Auto configure `lsp-mode' main features.
    275 When set to t `lsp-mode' will auto-configure completion,
    276 code-actions, breadcrumb, `flycheck', `flymake', `imenu', symbol highlighting,
    277 lenses, links, and so on.
    278 
    279 For finer granularity you may use `lsp-enable-*' properties."
    280   :group 'lsp-mode
    281   :type 'boolean
    282   :package-version '(lsp-mode . "6.1"))
    283 
    284 (defcustom lsp-disabled-clients nil
    285   "A list of disabled/blocklisted clients.
    286 Each entry in the list can be either:
    287 a symbol, the server-id for the LSP client, or
    288 a cons pair (MAJOR-MODE . CLIENTS), where MAJOR-MODE is the major-mode,
    289 and CLIENTS is either a client or a list of clients.
    290 
    291 This option can also be used as a file- or directory-local variable to
    292 disable a language server for individual files or directories/projects
    293 respectively."
    294   :group 'lsp-mode
    295   :type '(repeat (symbol))
    296   :safe 'listp
    297   :package-version '(lsp-mode . "6.1"))
    298 
    299 (defvar lsp-clients (make-hash-table :test 'eql)
    300   "Hash table server-id -> client.
    301 It contains all of the clients that are currently registered.")
    302 
    303 (defvar lsp-enabled-clients nil
    304   "List of clients allowed to be used for projects.
    305 When nil, all registered clients are considered candidates.")
    306 
    307 (defvar lsp-last-id 0
    308   "Last request id.")
    309 
    310 (defcustom lsp-before-initialize-hook nil
    311   "List of functions to be called before a Language Server has been initialized
    312 for a new workspace."
    313   :type 'hook
    314   :group 'lsp-mode)
    315 
    316 (defcustom lsp-after-initialize-hook nil
    317   "List of functions to be called after a Language Server has been initialized
    318 for a new workspace."
    319   :type 'hook
    320   :group 'lsp-mode)
    321 
    322 (defcustom lsp-before-open-hook nil
    323   "List of functions to be called before a new file with LSP support is opened."
    324   :type 'hook
    325   :group 'lsp-mode)
    326 
    327 (defcustom lsp-after-open-hook nil
    328   "List of functions to be called after a new file with LSP support is opened."
    329   :type 'hook
    330   :group 'lsp-mode)
    331 
    332 (defcustom lsp-enable-file-watchers t
    333   "If non-nil lsp-mode will watch the files in the workspace if
    334 the server has requested that."
    335   :type 'boolean
    336   :group 'lsp-mode
    337   :package-version '(lsp-mode . "6.1"))
    338 ;;;###autoload(put 'lsp-enable-file-watchers 'safe-local-variable #'booleanp)
    339 
    340 (define-obsolete-variable-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "8.0.0")
    341 
    342 (defcustom lsp-file-watch-ignored-directories
    343   '(; SCM tools
    344     "[/\\\\]\\.git\\'"
    345     "[/\\\\]\\.github\\'"
    346     "[/\\\\]\\.gitlab\\'"
    347     "[/\\\\]\\.circleci\\'"
    348     "[/\\\\]\\.hg\\'"
    349     "[/\\\\]\\.bzr\\'"
    350     "[/\\\\]_darcs\\'"
    351     "[/\\\\]\\.svn\\'"
    352     "[/\\\\]_FOSSIL_\\'"
    353     ;; IDE or build tools
    354     "[/\\\\]\\.idea\\'"
    355     "[/\\\\]\\.ensime_cache\\'"
    356     "[/\\\\]\\.eunit\\'"
    357     "[/\\\\]node_modules"
    358     "[/\\\\]\\.yarn\\'"
    359     "[/\\\\]\\.fslckout\\'"
    360     "[/\\\\]\\.tox\\'"
    361     "[/\\\\]\\.nox\\'"
    362     "[/\\\\]dist\\'"
    363     "[/\\\\]dist-newstyle\\'"
    364     "[/\\\\]\\.stack-work\\'"
    365     "[/\\\\]\\.bloop\\'"
    366     "[/\\\\]\\.metals\\'"
    367     "[/\\\\]target\\'"
    368     "[/\\\\]\\.ccls-cache\\'"
    369     "[/\\\\]\\.vs\\'"
    370     "[/\\\\]\\.vscode\\'"
    371     "[/\\\\]\\.venv\\'"
    372     "[/\\\\]\\.mypy_cache\\'"
    373     "[/\\\\]\\.pytest_cache\\'"
    374     ;; Swift Package Manager
    375     "[/\\\\]\\.build\\'"
    376     ;; Python
    377     "[/\\\\]__pycache__\\'"
    378     ;; Autotools output
    379     "[/\\\\]\\.deps\\'"
    380     "[/\\\\]build-aux\\'"
    381     "[/\\\\]autom4te.cache\\'"
    382     "[/\\\\]\\.reference\\'"
    383     ;; Bazel
    384     "[/\\\\]bazel-[^/\\\\]+\\'"
    385     ;; CSharp
    386     "[/\\\\]\\.cache[/\\\\]lsp-csharp\\'"
    387     "[/\\\\]\\.meta\\'"
    388     "[/\\\\]\\.nuget\\'"
    389     ;; Unity
    390     "[/\\\\]Library\\'"
    391     ;; Clojure
    392     "[/\\\\]\\.lsp\\'"
    393     "[/\\\\]\\.clj-kondo\\'"
    394     "[/\\\\]\\.shadow-cljs\\'"
    395     "[/\\\\]\\.babel_cache\\'"
    396     "[/\\\\]\\.cpcache\\'"
    397     "[/\\\\]\\checkouts\\'"
    398     ;; Gradle
    399     "[/\\\\]\\.gradle\\'"
    400     ;; Maven
    401     "[/\\\\]\\.m2\\'"
    402     ;; .Net Core build-output
    403     "[/\\\\]bin/Debug\\'"
    404     "[/\\\\]obj\\'"
    405     ;; OCaml and Dune
    406     "[/\\\\]_opam\\'"
    407     "[/\\\\]_build\\'"
    408     ;; Elixir
    409     "[/\\\\]\\.elixir_ls\\'"
    410     ;; Elixir Credo
    411     "[/\\\\]\\.elixir-tools\\'"
    412     ;; terraform and terragrunt
    413     "[/\\\\]\\.terraform\\'"
    414     "[/\\\\]\\.terragrunt-cache\\'"
    415     ;; nix-direnv
    416     "[/\\\\]\\result"
    417     "[/\\\\]\\result-bin"
    418     "[/\\\\]\\.direnv\\'")
    419   "List of regexps matching directory paths which won't be monitored when
    420 creating file watches. Customization of this variable is only honored at
    421 the global level or at a root of an lsp workspace."
    422   :group 'lsp-mode
    423   :type '(repeat string)
    424   :package-version '(lsp-mode . "8.0.0"))
    425 
    426 (define-obsolete-function-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "7.0.1")
    427 
    428 (defun lsp-file-watch-ignored-directories ()
    429   lsp-file-watch-ignored-directories)
    430 
    431 ;; Allow lsp-file-watch-ignored-directories as a file or directory-local variable
    432 ;;;###autoload(put 'lsp-file-watch-ignored-directories 'safe-local-variable 'lsp--string-listp)
    433 
    434 (defcustom lsp-file-watch-ignored-files
    435   '(
    436     ;; Flycheck tempfiles
    437     "[/\\\\]flycheck_[^/\\\\]+\\'"
    438     ;; lockfiles
    439     "[/\\\\]\\.#[^/\\\\]+\\'"
    440     ;; backup files
    441     "[/\\\\][^/\\\\]+~\\'" )
    442   "List of regexps matching files for which change events will
    443 not be sent to the server.
    444 
    445 This setting has no impact on whether a file-watch is created for
    446 a directory; it merely prevents notifications pertaining to
    447 matched files from being sent to the server.  To prevent a
    448 file-watch from being created for a directory, customize
    449 `lsp-file-watch-ignored-directories'
    450 
    451 Customization of this variable is only honored at the global
    452 level or at a root of an lsp workspace."
    453   :group 'lsp-mode
    454   :type '(repeat string)
    455   :package-version '(lsp-mode . "8.0.0"))
    456 
    457 ;; Allow lsp-file-watch-ignored-files as a file or directory-local variable
    458 ;;;###autoload(put 'lsp-file-watch-ignored-files 'safe-local-variable 'lsp--string-listp)
    459 
    460 (defcustom lsp-after-uninitialized-functions nil
    461   "List of functions to be called after a Language Server has been uninitialized."
    462   :type 'hook
    463   :group 'lsp-mode
    464   :package-version '(lsp-mode . "6.3"))
    465 
    466 (defconst lsp--sync-full 1)
    467 (defconst lsp--sync-incremental 2)
    468 
    469 (defcustom lsp-debounce-full-sync-notifications t
    470   "If non-nil debounce full sync events.
    471 This flag affects only servers which do not support incremental updates."
    472   :type 'boolean
    473   :group 'lsp-mode
    474   :package-version '(lsp-mode . "6.1"))
    475 
    476 (defcustom lsp-debounce-full-sync-notifications-interval 1.0
    477   "Time to wait before sending full sync synchronization after buffer modification."
    478   :type 'float
    479   :group 'lsp-mode
    480   :package-version '(lsp-mode . "6.1"))
    481 
    482 (defvar lsp--stderr-index 0)
    483 
    484 (defvar lsp--delayed-requests nil)
    485 (defvar lsp--delay-timer nil)
    486 
    487 (defcustom lsp-document-sync-method nil
    488   "How to sync the document with the language server."
    489   :type '(choice (const :tag "Documents are synced by always sending the full content of the document." lsp--sync-full)
    490                  (const :tag "Documents are synced by always sending incremental changes to the document." lsp--sync-incremental)
    491                  (const :tag "Use the method recommended by the language server." nil))
    492   :group 'lsp-mode)
    493 
    494 (defcustom lsp-auto-execute-action t
    495   "Auto-execute single action."
    496   :type 'boolean
    497   :group 'lsp-mode)
    498 
    499 (defcustom lsp-enable-links t
    500   "If non-nil, all references to links in a file will be made clickable, if
    501 supported by the language server."
    502   :type 'boolean
    503   :group 'lsp-mode
    504   :package-version '(lsp-mode . "6.1"))
    505 
    506 (defcustom lsp-enable-imenu t
    507   "If non-nil, automatically enable `imenu' integration when server provides
    508 `textDocument/documentSymbol'."
    509   :type 'boolean
    510   :group 'lsp-mode
    511   :package-version '(lsp-mode . "6.2"))
    512 
    513 (defcustom lsp-enable-dap-auto-configure t
    514   "If non-nil, enable `dap-auto-configure-mode`."
    515   :type 'boolean
    516   :group 'lsp-mode
    517   :package-version '(lsp-mode . "7.0"))
    518 
    519 (defcustom lsp-eldoc-enable-hover t
    520   "If non-nil, `eldoc' will display hover info when it is present."
    521   :type 'boolean
    522   :group 'lsp-mode)
    523 
    524 (defcustom lsp-eldoc-render-all nil
    525   "Display all of the info returned by document/onHover.
    526 If this is set to nil, `eldoc' will show only the symbol information."
    527   :type 'boolean
    528   :group 'lsp-mode)
    529 
    530 (define-obsolete-variable-alias 'lsp-enable-completion-at-point
    531   'lsp-completion-enable "lsp-mode 7.0.1")
    532 
    533 (defcustom lsp-completion-enable t
    534   "Enable `completion-at-point' integration."
    535   :type 'boolean
    536   :group 'lsp-completion)
    537 
    538 (defcustom lsp-enable-symbol-highlighting t
    539   "Highlight references of the symbol at point."
    540   :type 'boolean
    541   :group 'lsp-mode)
    542 
    543 (defcustom lsp-enable-xref t
    544   "Enable xref integration."
    545   :type 'boolean
    546   :group 'lsp-mode)
    547 
    548 (defcustom lsp-references-exclude-definition nil
    549   "If non-nil, exclude declarations when finding references."
    550   :type 'boolean
    551   :group 'lsp-mode)
    552 
    553 (defcustom lsp-enable-indentation t
    554   "Indent regions using the file formatting functionality provided by the
    555 language server."
    556   :type 'boolean
    557   :group 'lsp-mode)
    558 
    559 (defcustom lsp-enable-on-type-formatting t
    560   "Enable `textDocument/onTypeFormatting' integration."
    561   :type 'boolean
    562   :group 'lsp-mode)
    563 
    564 (defcustom lsp-enable-text-document-color t
    565   "Enable `textDocument/documentColor' integration."
    566   :type 'boolean
    567   :group 'lsp-mode)
    568 
    569 (defcustom lsp-before-save-edits t
    570   "If non-nil, `lsp-mode' will apply edits suggested by the language server
    571 before saving a document."
    572   :type 'boolean
    573   :group 'lsp-mode)
    574 
    575 (defcustom lsp-after-apply-edits-hook nil
    576   "Hooks to run when text edit is applied.
    577 It contains the operation source."
    578   :type 'hook
    579   :group 'lsp-mode
    580   :package-version '(lsp-mode . "8.0.0"))
    581 
    582 (defcustom lsp-apply-edits-after-file-operations t
    583   "Whether to apply edits returned by server after file operations if any.
    584 Applicable only if server supports workspace.fileOperations for operations:
    585 `workspace/willRenameFiles', `workspace/willCreateFiles' and
    586 `workspace/willDeleteFiles'."
    587   :group 'lsp-mode
    588   :type 'boolean)
    589 
    590 (defcustom lsp-modeline-code-actions-enable t
    591   "Whether to show code actions on modeline."
    592   :type 'boolean
    593   :group 'lsp-modeline)
    594 
    595 (defcustom lsp-modeline-diagnostics-enable t
    596   "Whether to show diagnostics on modeline."
    597   :type 'boolean
    598   :group 'lsp-modeline)
    599 
    600 (defcustom lsp-modeline-workspace-status-enable t
    601   "Whether to show workspace status on modeline."
    602   :type 'boolean
    603   :group 'lsp-modeline
    604   :package-version '(lsp-mode . "8.0.0"))
    605 
    606 (defcustom lsp-headerline-breadcrumb-enable t
    607   "Whether to enable breadcrumb on headerline."
    608   :type 'boolean
    609   :group 'lsp-headerline)
    610 
    611 (defcustom lsp-configure-hook nil
    612   "Hooks to run when `lsp-configure-buffer' is called."
    613   :type 'hook
    614   :group 'lsp-mode)
    615 
    616 (defcustom lsp-unconfigure-hook nil
    617   "Hooks to run when `lsp-unconfig-buffer' is called."
    618   :type 'hook
    619   :group 'lsp-mode)
    620 
    621 (defcustom lsp-after-diagnostics-hook nil
    622   "Hooks to run after diagnostics are received.
    623 Note: it runs only if the receiving buffer is open. Use
    624 `lsp-diagnostics-updated-hook'if you want to be notified when
    625 diagnostics have changed."
    626   :type 'hook
    627   :group 'lsp-mode)
    628 
    629 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook
    630   'lsp-diagnostics-updated-hook "lsp-mode 6.4")
    631 
    632 (defcustom lsp-diagnostics-updated-hook nil
    633   "Hooks to run after diagnostics are received."
    634   :type 'hook
    635   :group 'lsp-mode)
    636 
    637 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook
    638   'lsp-workspace-folders-changed-functions "lsp-mode 6.3")
    639 
    640 (defcustom lsp-workspace-folders-changed-functions nil
    641   "Hooks to run after the folders has changed.
    642 The hook will receive two parameters list of added and removed folders."
    643   :type 'hook
    644   :group 'lsp-mode)
    645 
    646 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0")
    647 
    648 (defcustom lsp-before-apply-edits-hook nil
    649   "Hooks to run before applying edits."
    650   :type 'hook
    651   :group 'lsp-mode)
    652 
    653 (defgroup lsp-imenu nil
    654   "LSP Imenu."
    655   :group 'lsp-mode
    656   :tag "LSP Imenu")
    657 
    658 (defcustom lsp-imenu-show-container-name t
    659   "Display the symbol's container name in an imenu entry."
    660   :type 'boolean
    661   :group 'lsp-imenu)
    662 
    663 (defcustom lsp-imenu-container-name-separator "/"
    664   "Separator string to use to separate the container name from the symbol while
    665 displaying imenu entries."
    666   :type 'string
    667   :group 'lsp-imenu)
    668 
    669 (defcustom lsp-imenu-sort-methods '(kind name)
    670   "How to sort the imenu items.
    671 
    672 The value is a list of `kind' `name' or `position'.  Priorities
    673 are determined by the index of the element."
    674   :type '(repeat (choice (const name)
    675                          (const position)
    676                          (const kind)))
    677   :group 'lsp-imenu)
    678 
    679 (defcustom lsp-imenu-index-symbol-kinds nil
    680   "Which symbol kinds to show in imenu."
    681   :type '(repeat (choice (const :tag "Miscellaneous" nil)
    682                          (const :tag "File" File)
    683                          (const :tag "Module" Module)
    684                          (const :tag "Namespace" Namespace)
    685                          (const :tag "Package" Package)
    686                          (const :tag "Class" Class)
    687                          (const :tag "Method" Method)
    688                          (const :tag "Property" Property)
    689                          (const :tag "Field" Field)
    690                          (const :tag "Constructor" Constructor)
    691                          (const :tag "Enum" Enum)
    692                          (const :tag "Interface" Interface)
    693                          (const :tag "Function" Function)
    694                          (const :tag "Variable" Variable)
    695                          (const :tag "Constant" Constant)
    696                          (const :tag "String" String)
    697                          (const :tag "Number" Number)
    698                          (const :tag "Boolean" Boolean)
    699                          (const :tag "Array" Array)
    700                          (const :tag "Object" Object)
    701                          (const :tag "Key" Key)
    702                          (const :tag "Null" Null)
    703                          (const :tag "Enum Member" EnumMember)
    704                          (const :tag "Struct" Struct)
    705                          (const :tag "Event" Event)
    706                          (const :tag "Operator" Operator)
    707                          (const :tag "Type Parameter" TypeParameter)))
    708   :group 'lsp-imenu)
    709 
    710 ;; vibhavp: Should we use a lower value (5)?
    711 (defcustom lsp-response-timeout 10
    712   "Number of seconds to wait for a response from the language server before
    713 timing out. Nil if no timeout."
    714   :type '(choice
    715           (number :tag "Seconds")
    716           (const :tag "No timeout" nil))
    717   :group 'lsp-mode)
    718 
    719 (defcustom lsp-tcp-connection-timeout 2
    720   "The timeout for tcp connection in seconds."
    721   :type 'number
    722   :group 'lsp-mode
    723   :package-version '(lsp-mode . "6.2"))
    724 
    725 (defconst lsp--imenu-compare-function-alist
    726   (list (cons 'name #'lsp--imenu-compare-name)
    727         (cons 'kind #'lsp--imenu-compare-kind)
    728         (cons 'position #'lsp--imenu-compare-line-col))
    729   "An alist of (METHOD . FUNCTION).
    730 METHOD is one of the symbols accepted by
    731 `lsp-imenu-sort-methods'.
    732 
    733 FUNCTION takes two hash tables representing DocumentSymbol.  It
    734 returns a negative number, 0, or a positive number indicating
    735 whether the first parameter is less than, equal to, or greater
    736 than the second parameter.")
    737 
    738 (defcustom lsp-diagnostic-clean-after-change nil
    739   "When non-nil, clean the diagnostics on change.
    740 
    741 Note that when that setting is nil, `lsp-mode' will show stale
    742 diagnostics until server publishes the new set of diagnostics"
    743   :type 'boolean
    744   :group 'lsp-diagnostics
    745   :package-version '(lsp-mode . "7.0.1"))
    746 
    747 (defcustom lsp-server-trace nil
    748   "Request tracing on the server side.
    749 The actual trace output at each level depends on the language server in use.
    750 Changes take effect only when a new session is started."
    751   :type '(choice (const :tag "Disabled" "off")
    752                  (const :tag "Messages only" "messages")
    753                  (const :tag "Verbose" "verbose")
    754                  (const :tag "Default (disabled)" nil))
    755   :group 'lsp-mode
    756   :package-version '(lsp-mode . "6.1"))
    757 
    758 (defcustom lsp-auto-touch-files t
    759   "If non-nil ensure the files exist before sending
    760 `textDocument/didOpen' notification."
    761   :type 'boolean
    762   :group 'lsp-mode
    763   :package-version '(lsp-mode . "9.0.0"))
    764 
    765 (defvar lsp-language-id-configuration
    766   '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake")
    767     ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile")
    768     ("\\.astro$" . "astro")
    769     ("\\.cs\\'" . "csharp")
    770     ("\\.css$" . "css")
    771     ("\\.cypher$" . "cypher")
    772     ("Earthfile" . "earthfile")
    773     ("\\.ebuild$" . "shellscript")
    774     ("\\.go\\'" . "go")
    775     ("\\.html$" . "html")
    776     ("\\.hx$" . "haxe")
    777     ("\\.hy$" . "hy")
    778     ("\\.java\\'" . "java")
    779     ("\\.jq$"  . "jq")
    780     ("\\.js$" . "javascript")
    781     ("\\.json$" . "json")
    782     ("\\.jsonc$" . "jsonc")
    783     ("\\.jsonnet$" . "jsonnet")
    784     ("\\.jsx$" . "javascriptreact")
    785     ("\\.lua$" . "lua")
    786     ("\\.mdx\\'" . "mdx")
    787     ("\\.nu$" . "nushell")
    788     ("\\.php$" . "php")
    789     ("\\.ps[dm]?1\\'" . "powershell")
    790     ("\\.rs\\'" . "rust")
    791     ("\\.spec\\'" . "rpm-spec")
    792     ("\\.sql$" . "sql")
    793     ("\\.svelte$" . "svelte")
    794     ("\\.toml\\'" . "toml")
    795     ("\\.ts$" . "typescript")
    796     ("\\.tsx$" . "typescriptreact")
    797     ("\\.ttcn3$" . "ttcn3")
    798     ("\\.vue$" . "vue")
    799     ("\\.xml$" . "xml")
    800     ("\\ya?ml$" . "yaml")
    801     ("^PKGBUILD$" . "shellscript")
    802     ("^go\\.mod\\'" . "go.mod")
    803     ("^settings\\.json$" . "jsonc")
    804     ("^yang\\.settings$" . "jsonc")
    805     ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson")
    806     (ada-mode . "ada")
    807     (ada-ts-mode . "ada")
    808     (gpr-mode . "gpr")
    809     (gpr-ts-mode . "gpr")
    810     (awk-mode . "awk")
    811     (awk-ts-mode . "awk")
    812     (nxml-mode . "xml")
    813     (sql-mode . "sql")
    814     (vimrc-mode . "vim")
    815     (vimscript-ts-mode . "vim")
    816     (sh-mode . "shellscript")
    817     (bash-ts-mode . "shellscript")
    818     (ebuild-mode . "shellscript")
    819     (pkgbuild-mode . "shellscript")
    820     (envrc-file-mode . "shellscript")
    821     (scala-mode . "scala")
    822     (scala-ts-mode . "scala")
    823     (julia-mode . "julia")
    824     (julia-ts-mode . "julia")
    825     (clojure-mode . "clojure")
    826     (clojurec-mode . "clojure")
    827     (clojurescript-mode . "clojurescript")
    828     (clojure-ts-mode . "clojure")
    829     (clojure-ts-clojurec-mode . "clojure")
    830     (clojure-ts-clojurescript-mode . "clojurescript")
    831     (java-mode . "java")
    832     (java-ts-mode . "java")
    833     (jdee-mode . "java")
    834     (groovy-mode . "groovy")
    835     (python-mode . "python")
    836     (python-ts-mode . "python")
    837     (cython-mode . "python")
    838     ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo")
    839     (lsp--render-markdown . "markdown")
    840     (move-mode . "move")
    841     (rust-mode . "rust")
    842     (rust-ts-mode . "rust")
    843     (rustic-mode . "rust")
    844     (kotlin-mode . "kotlin")
    845     (kotlin-ts-mode . "kotlin")
    846     (css-mode . "css")
    847     (css-ts-mode . "css")
    848     (less-mode . "less")
    849     (less-css-mode . "less")
    850     (lua-mode . "lua")
    851     (lua-ts-mode . "lua")
    852     (sass-mode . "sass")
    853     (ssass-mode . "sass")
    854     (scss-mode . "scss")
    855     (scad-mode . "openscad")
    856     (xml-mode . "xml")
    857     (c-mode . "c")
    858     (c-ts-mode . "c")
    859     (c++-mode . "cpp")
    860     (c++-ts-mode . "cpp")
    861     (cuda-mode . "cuda")
    862     (objc-mode . "objective-c")
    863     (html-mode . "html")
    864     (html-ts-mode . "html")
    865     (sgml-mode . "html")
    866     (mhtml-mode . "html")
    867     (mint-mode . "mint")
    868     (go-dot-mod-mode . "go.mod")
    869     (go-mod-ts-mode . "go.mod")
    870     (go-mode . "go")
    871     (go-ts-mode . "go")
    872     (graphql-mode . "graphql")
    873     (haskell-mode . "haskell")
    874     (hack-mode . "hack")
    875     (php-mode . "php")
    876     (php-ts-mode . "php")
    877     (powershell-mode . "powershell")
    878     (powershell-mode . "PowerShell")
    879     (powershell-ts-mode . "powershell")
    880     (json-mode . "json")
    881     (json-ts-mode . "json")
    882     (jsonc-mode . "jsonc")
    883     (rjsx-mode . "javascript")
    884     (js2-mode . "javascript")
    885     (js-mode . "javascript")
    886     (js-ts-mode . "javascript")
    887     (typescript-mode . "typescript")
    888     (typescript-ts-mode . "typescript")
    889     (tsx-ts-mode . "typescriptreact")
    890     (svelte-mode . "svelte")
    891     (fsharp-mode . "fsharp")
    892     (reason-mode . "reason")
    893     (caml-mode . "ocaml")
    894     (tuareg-mode . "ocaml")
    895     (swift-mode . "swift")
    896     (elixir-mode . "elixir")
    897     (elixir-ts-mode . "elixir")
    898     (heex-ts-mode . "elixir")
    899     (conf-javaprop-mode . "spring-boot-properties")
    900     (yaml-mode . "yaml")
    901     (yaml-ts-mode . "yaml")
    902     (ruby-mode . "ruby")
    903     (enh-ruby-mode . "ruby")
    904     (ruby-ts-mode . "ruby")
    905     (feature-mode . "cucumber")
    906     (fortran-mode . "fortran")
    907     (f90-mode . "fortran")
    908     (elm-mode . "elm")
    909     (dart-mode . "dart")
    910     (erlang-mode . "erlang")
    911     (dockerfile-mode . "dockerfile")
    912     (dockerfile-ts-mode . "dockerfile")
    913     (csharp-mode . "csharp")
    914     (csharp-tree-sitter-mode . "csharp")
    915     (csharp-ts-mode . "csharp")
    916     (plain-tex-mode . "plaintex")
    917     (context-mode . "context")
    918     (cypher-mode . "cypher")
    919     (latex-mode . "latex")
    920     (LaTeX-mode . "latex")
    921     (v-mode . "v")
    922     (vhdl-mode . "vhdl")
    923     (vhdl-ts-mode . "vhdl")
    924     (verilog-mode . "verilog")
    925     (terraform-mode . "terraform")
    926     (ess-julia-mode . "julia")
    927     (ess-r-mode . "r")
    928     (crystal-mode . "crystal")
    929     (nim-mode . "nim")
    930     (dhall-mode . "dhall")
    931     (cmake-mode . "cmake")
    932     (cmake-ts-mode . "cmake")
    933     (purescript-mode . "purescript")
    934     (gdscript-mode . "gdscript")
    935     (gdscript-ts-mode . "gdscript")
    936     (perl-mode . "perl")
    937     (cperl-mode . "perl")
    938     (robot-mode . "robot")
    939     (racket-mode . "racket")
    940     (nix-mode . "nix")
    941     (nix-ts-mode . "Nix")
    942     (prolog-mode . "prolog")
    943     (vala-mode . "vala")
    944     (actionscript-mode . "actionscript")
    945     (d-mode . "d")
    946     (zig-mode . "zig")
    947     (text-mode . "plaintext")
    948     (markdown-mode . "markdown")
    949     (gfm-mode . "markdown")
    950     (beancount-mode . "beancount")
    951     (conf-toml-mode . "toml")
    952     (toml-ts-mode . "toml")
    953     (org-mode . "org")
    954     (org-journal-mode . "org")
    955     (nginx-mode . "nginx")
    956     (magik-mode . "magik")
    957     (magik-ts-mode . "magik")
    958     (idris-mode . "idris")
    959     (idris2-mode . "idris2")
    960     (gleam-mode . "gleam")
    961     (gleam-ts-mode . "gleam")
    962     (graphviz-dot-mode . "dot")
    963     (tiltfile-mode . "tiltfile")
    964     (solidity-mode . "solidity")
    965     (bibtex-mode . "bibtex")
    966     (rst-mode . "restructuredtext")
    967     (glsl-mode . "glsl")
    968     (shader-mode . "shaderlab")
    969     (wgsl-mode . "wgsl")
    970     (jq-mode . "jq")
    971     (jq-ts-mode . "jq")
    972     (protobuf-mode . "protobuf")
    973     (nushell-mode . "nushell")
    974     (nushell-ts-mode . "nushell")
    975     (meson-mode . "meson")
    976     (yang-mode . "yang"))
    977   "Language id configuration.")
    978 
    979 (defvar lsp--last-active-workspaces nil
    980   "Keep track of last active workspace.
    981 We want to try the last workspace first when jumping into a library
    982 directory")
    983 
    984 (defvar lsp-method-requirements
    985   '(("textDocument/callHierarchy" :capability :callHierarchyProvider)
    986     ("textDocument/codeAction" :capability :codeActionProvider)
    987     ("codeAction/resolve"
    988      :check-command (lambda (workspace)
    989                       (with-lsp-workspace workspace
    990                         (lsp:code-action-options-resolve-provider?
    991                          (lsp--capability-for-method "textDocument/codeAction")))))
    992     ("textDocument/codeLens" :capability :codeLensProvider)
    993     ("textDocument/completion" :capability :completionProvider)
    994     ("completionItem/resolve"
    995      :check-command (lambda (wk)
    996                       (with-lsp-workspace wk
    997                         (lsp:completion-options-resolve-provider?
    998                          (lsp--capability-for-method "textDocument/completion")))))
    999     ("textDocument/declaration" :capability :declarationProvider)
   1000     ("textDocument/definition" :capability :definitionProvider)
   1001     ("textDocument/documentColor" :capability :colorProvider)
   1002     ("textDocument/documentLink" :capability :documentLinkProvider)
   1003     ("textDocument/inlayHint" :capability :inlayHintProvider)
   1004     ("textDocument/documentHighlight" :capability :documentHighlightProvider)
   1005     ("textDocument/documentSymbol" :capability :documentSymbolProvider)
   1006     ("textDocument/foldingRange" :capability :foldingRangeProvider)
   1007     ("textDocument/formatting" :capability :documentFormattingProvider)
   1008     ("textDocument/hover" :capability :hoverProvider)
   1009     ("textDocument/implementation" :capability :implementationProvider)
   1010     ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider)
   1011     ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider)
   1012     ("textDocument/prepareRename"
   1013      :check-command (lambda (workspace)
   1014                       (with-lsp-workspace workspace
   1015                         (lsp:rename-options-prepare-provider?
   1016                          (lsp--capability-for-method "textDocument/rename")))))
   1017     ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider)
   1018     ("textDocument/references" :capability :referencesProvider)
   1019     ("textDocument/rename" :capability :renameProvider)
   1020     ("textDocument/selectionRange" :capability :selectionRangeProvider)
   1021     ("textDocument/semanticTokens" :capability :semanticTokensProvider)
   1022     ("textDocument/semanticTokensFull"
   1023      :check-command (lambda (workspace)
   1024                       (with-lsp-workspace workspace
   1025                         (lsp-get (lsp--capability :semanticTokensProvider) :full))))
   1026     ("textDocument/semanticTokensFull/Delta"
   1027      :check-command (lambda (workspace)
   1028                       (with-lsp-workspace workspace
   1029                         (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full)))
   1030                           (and (not (booleanp capFull)) (lsp-get capFull :delta))))))
   1031     ("textDocument/semanticTokensRangeProvider"
   1032      :check-command (lambda (workspace)
   1033                       (with-lsp-workspace workspace
   1034                         (lsp-get (lsp--capability :semanticTokensProvider) :range))))
   1035     ("textDocument/signatureHelp" :capability :signatureHelpProvider)
   1036     ("textDocument/typeDefinition" :capability :typeDefinitionProvider)
   1037     ("textDocument/typeHierarchy" :capability :typeHierarchyProvider)
   1038     ("textDocument/diagnostic" :capability :diagnosticProvider)
   1039     ("workspace/executeCommand" :capability :executeCommandProvider)
   1040     ("workspace/symbol" :capability :workspaceSymbolProvider))
   1041 
   1042   "Map methods to requirements.
   1043 It is used by request-sending functions to determine which server
   1044 must be used for handling a particular message.")
   1045 
   1046 (defconst lsp--file-change-type
   1047   `((created . 1)
   1048     (changed . 2)
   1049     (deleted . 3)))
   1050 
   1051 (defconst lsp--watch-kind
   1052   `((create . 1)
   1053     (change . 2)
   1054     (delete . 4)))
   1055 
   1056 (defvar lsp-window-body-width 40
   1057   "Window body width when rendering doc.")
   1058 
   1059 (defface lsp-face-highlight-textual
   1060   '((t :inherit highlight))
   1061   "Face used for textual occurrences of symbols."
   1062   :group 'lsp-mode)
   1063 
   1064 (defface lsp-face-highlight-read
   1065   '((t :inherit highlight :underline t))
   1066   "Face used for highlighting symbols being read."
   1067   :group 'lsp-mode)
   1068 
   1069 (defface lsp-face-highlight-write
   1070   '((t :inherit highlight :weight bold))
   1071   "Face used for highlighting symbols being written to."
   1072   :group 'lsp-mode)
   1073 
   1074 (define-obsolete-variable-alias 'lsp-lens-auto-enable
   1075   'lsp-lens-enable "lsp-mode 7.0.1")
   1076 
   1077 (defcustom lsp-lens-enable t
   1078   "Auto enable lenses if server supports."
   1079   :group 'lsp-lens
   1080   :type 'boolean
   1081   :package-version '(lsp-mode . "6.3"))
   1082 
   1083 (defcustom lsp-symbol-highlighting-skip-current nil
   1084   "If non-nil skip current symbol when setting symbol highlights."
   1085   :group 'lsp-mode
   1086   :type 'boolean)
   1087 
   1088 (defcustom lsp-file-watch-threshold 1000
   1089   "Show warning if the files to watch are more than.
   1090 Set to nil to disable the warning."
   1091   :type 'number
   1092   :group 'lsp-mode)
   1093 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i))))
   1094 
   1095 (defvar lsp-custom-markup-modes
   1096   '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic"))
   1097   "Mode to uses with markdown code blocks.
   1098 They are added to `markdown-code-lang-modes'")
   1099 
   1100 (defcustom lsp-signature-render-documentation t
   1101   "Display signature documentation in `eldoc'."
   1102   :type 'boolean
   1103   :group 'lsp-mode
   1104   :package-version '(lsp-mode . "6.2"))
   1105 
   1106 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request)
   1107   "Auto activate signature conditions."
   1108   :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char)
   1109                          (const :tag "After selected completion." :after-completion)
   1110                          (const :tag "When the server has sent show signature help." :on-server-request)))
   1111   :group 'lsp-mode
   1112   :package-version '(lsp-mode . "6.2"))
   1113 
   1114 (defcustom lsp-signature-doc-lines 20
   1115   "If number, limit the number of lines to show in the docs."
   1116   :type 'number
   1117   :group 'lsp-mode
   1118   :package-version '(lsp-mode . "6.3"))
   1119 
   1120 (defcustom lsp-signature-function 'lsp-lv-message
   1121   "The function used for displaying signature info.
   1122 It will be called with one param - the signature info. When
   1123 called with nil the signature info must be cleared."
   1124   :type 'function
   1125   :group 'lsp-mode
   1126   :package-version '(lsp-mode . "6.3"))
   1127 
   1128 (defcustom lsp-keymap-prefix "s-l"
   1129   "LSP-mode keymap prefix."
   1130   :group 'lsp-mode
   1131   :type 'string
   1132   :package-version '(lsp-mode . "6.3"))
   1133 
   1134 (defvar-local lsp--buffer-workspaces ()
   1135   "List of the buffer workspaces.")
   1136 
   1137 (defvar-local lsp--buffer-deferred nil
   1138   "Whether buffer was loaded via `lsp-deferred'.")
   1139 
   1140 (defvar lsp--session nil
   1141   "Contain the `lsp-session' for the current Emacs instance.")
   1142 
   1143 (defvar lsp--tcp-port 10000)
   1144 
   1145 (defvar lsp--client-packages-required nil
   1146   "If nil, `lsp-client-packages' are yet to be required.")
   1147 
   1148 (defvar lsp--tcp-server-port 0
   1149   "The server socket which is opened when using `lsp-tcp-server' (a server
   1150 socket is opened in Emacs and the language server connects to it).  The
   1151 default value of 0 ensures that a random high port is used. Set it to a positive
   1152 integer to use a specific port.")
   1153 
   1154 (defvar lsp--tcp-server-wait-seconds 10
   1155   "Wait this amount of time for the client to connect to our server socket
   1156 when using `lsp-tcp-server'.")
   1157 
   1158 (defvar-local lsp--document-symbols nil
   1159   "The latest document symbols.")
   1160 
   1161 (defvar-local lsp--document-selection-range-cache nil
   1162   "The document selection cache.")
   1163 
   1164 (defvar-local lsp--document-symbols-request-async nil
   1165   "If non-nil, request document symbols asynchronously.")
   1166 
   1167 (defvar-local lsp--document-symbols-tick -1
   1168   "The value of `buffer-chars-modified-tick' when document
   1169   symbols were last retrieved.")
   1170 
   1171 (defvar-local lsp--have-document-highlights nil
   1172   "Set to `t' on symbol highlighting, cleared on
   1173 `lsp--cleanup-highlights-if-needed'. Checking a separately
   1174 defined flag is substantially faster than unconditionally
   1175 calling `remove-overlays'.")
   1176 
   1177 ;; Buffer local variable for storing number of lines.
   1178 (defvar lsp--log-lines)
   1179 
   1180 (defvar-local lsp--eldoc-saved-message nil)
   1181 
   1182 (defvar lsp--on-change-timer nil)
   1183 (defvar lsp--on-idle-timer nil)
   1184 
   1185 (defvar-local lsp--signature-last nil)
   1186 (defvar-local lsp--signature-last-index nil)
   1187 (defvar lsp--signature-last-buffer nil)
   1188 
   1189 (defvar-local lsp--virtual-buffer-point-max nil)
   1190 
   1191 (cl-defmethod lsp-execute-command (_server _command _arguments)
   1192   "Ask SERVER to execute COMMAND with ARGUMENTS.")
   1193 
   1194 (defun lsp-elt (sequence n)
   1195   "Return Nth element of SEQUENCE or nil if N is out of range."
   1196   (cond
   1197    ((listp sequence) (elt sequence n))
   1198    ((arrayp sequence)
   1199     (and (> (length sequence) n) (aref sequence n)))
   1200    (t (and (> (length sequence) n) (elt sequence n)))))
   1201 
   1202 ;; define seq-first and seq-rest for older emacs
   1203 (defun lsp-seq-first (sequence)
   1204   "Return the first element of SEQUENCE."
   1205   (lsp-elt sequence 0))
   1206 
   1207 (defun lsp-seq-rest (sequence)
   1208   "Return a sequence of the elements of SEQUENCE except the first one."
   1209   (seq-drop sequence 1))
   1210 
   1211 ;;;###autoload
   1212 (defun lsp--string-listp (sequence)
   1213   "Return t if all elements of SEQUENCE are strings, else nil."
   1214   (not (seq-find (lambda (x) (not (stringp x))) sequence)))
   1215 
   1216 (defun lsp--string-vector-p (candidate)
   1217   "Returns true if CANDIDATE is a vector data structure and
   1218 every element of it is of type string, else nil."
   1219   (and
   1220    (vectorp candidate)
   1221    (seq-every-p #'stringp candidate)))
   1222 
   1223 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0")
   1224 
   1225 (defun lsp--editable-vector-match (widget value)
   1226   "Function for `lsp-editable-vector' :match."
   1227   ;; Value must be a list or a vector and all the members must match the type.
   1228   (and (or (listp value) (vectorp value))
   1229        (length (cdr (lsp--editable-vector-match-inline widget value)))))
   1230 
   1231 (defun lsp--editable-vector-match-inline (widget value)
   1232   "Value for `lsp-editable-vector' :match-inline."
   1233   (let ((type (nth 0 (widget-get widget :args)))
   1234         (ok t)
   1235         found)
   1236     (while (and value ok)
   1237       (let ((answer (widget-match-inline type value)))
   1238         (if answer
   1239             (let ((head (if (vectorp answer) (aref answer 0) (car answer)))
   1240                   (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer))))
   1241               (setq found (append found head)
   1242                     value tail))
   1243           (setq ok nil))))
   1244     (cons found value)))
   1245 
   1246 (defun lsp--editable-vector-value-to-external (_widget internal-value)
   1247   "Convert the internal list value to a vector."
   1248   (if (listp internal-value)
   1249       (apply 'vector internal-value)
   1250     internal-value))
   1251 
   1252 (defun lsp--editable-vector-value-to-internal (_widget external-value)
   1253   "Convert the external vector value to a list."
   1254   (if (vectorp external-value)
   1255       (append external-value nil)
   1256     external-value))
   1257 
   1258 (define-widget 'lsp--editable-vector 'editable-list
   1259   "A subclass of `editable-list' that accepts and returns a
   1260 vector instead of a list."
   1261   :value-to-external 'lsp--editable-vector-value-to-external
   1262   :value-to-internal 'lsp--editable-vector-value-to-internal
   1263   :match 'lsp--editable-vector-match
   1264   :match-inline 'lsp--editable-vector-match-inline)
   1265 
   1266 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector
   1267   "A variable length homogeneous vector."
   1268   :tag "Repeat"
   1269   :format "%{%t%}:\n%v%i\n")
   1270 
   1271 (define-widget 'lsp-string-vector 'lazy
   1272   "A vector of zero or more elements, every element of which is a string.
   1273 Appropriate for any language-specific `defcustom' that needs to
   1274 serialize as a JSON array of strings.
   1275 
   1276 Deprecated. Use `lsp-repeatable-vector' instead. "
   1277   :offset 4
   1278   :tag "Vector"
   1279   :type '(lsp-repeatable-vector string))
   1280 
   1281 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0")
   1282 
   1283 (defvar lsp--show-message t
   1284   "If non-nil, show debug message from `lsp-mode'.")
   1285 
   1286 (defun lsp--message  (format &rest args)
   1287   "Wrapper for `message'
   1288 
   1289 We `inhibit-message' the message when the cursor is in the
   1290 minibuffer and when emacs version is before emacs 27 due to the
   1291 fact that we often use `lsp--info', `lsp--warn' and `lsp--error'
   1292 in async context and the call to these function is removing the
   1293 minibuffer prompt. The issue with async messages is already fixed
   1294 in emacs 27.
   1295 
   1296 See #2049"
   1297   (when lsp--show-message
   1298     (let ((inhibit-message (or inhibit-message
   1299                                (and (minibufferp)
   1300                                     (version< emacs-version "27.0")))))
   1301       (apply #'message format args))))
   1302 
   1303 (defun lsp--info (format &rest args)
   1304   "Display lsp info message with FORMAT with ARGS."
   1305   (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args)))
   1306 
   1307 (defun lsp--warn (format &rest args)
   1308   "Display lsp warn message with FORMAT with ARGS."
   1309   (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args)))
   1310 
   1311 (defun lsp--error (format &rest args)
   1312   "Display lsp error message with FORMAT with ARGS."
   1313   (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args)))
   1314 
   1315 (defun lsp-log (format &rest args)
   1316   "Log message to the ’*lsp-log*’ buffer.
   1317 
   1318 FORMAT and ARGS i the same as for `message'."
   1319   (when lsp-log-max
   1320     (let ((log-buffer (get-buffer "*lsp-log*"))
   1321           (inhibit-read-only t))
   1322       (unless log-buffer
   1323         (setq log-buffer (get-buffer-create "*lsp-log*"))
   1324         (with-current-buffer log-buffer
   1325           (buffer-disable-undo)
   1326           (view-mode 1)
   1327           (set (make-local-variable 'lsp--log-lines) 0)))
   1328       (with-current-buffer log-buffer
   1329         (save-excursion
   1330           (let* ((message (apply 'format format args))
   1331                  ;; Count newlines in message.
   1332                  (newlines (1+ (cl-loop with start = 0
   1333                                         for count from 0
   1334                                         while (string-match "\n" message start)
   1335                                         do (setq start (match-end 0))
   1336                                         finally return count))))
   1337             (goto-char (point-max))
   1338 
   1339             ;; in case the buffer is not empty insert before last \n to preserve
   1340             ;; the point position(in case it is in the end)
   1341             (if (eq (point) (point-min))
   1342                 (progn
   1343                   (insert "\n")
   1344                   (backward-char))
   1345               (backward-char)
   1346               (insert "\n"))
   1347             (insert message)
   1348 
   1349             (setq lsp--log-lines (+ lsp--log-lines newlines))
   1350 
   1351             (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max))
   1352               (let ((to-delete (- lsp--log-lines lsp-log-max)))
   1353                 (goto-char (point-min))
   1354                 (forward-line to-delete)
   1355                 (delete-region (point-min) (point))
   1356                 (setq lsp--log-lines lsp-log-max)))))))))
   1357 
   1358 (defalias 'lsp-message 'lsp-log)
   1359 
   1360 (defalias 'lsp-ht 'ht)
   1361 
   1362 (defalias 'lsp-file-local-name 'file-local-name)
   1363 
   1364 (defun lsp-f-canonical (file-name)
   1365   "Return the canonical FILE-NAME, without a trailing slash."
   1366   (directory-file-name (expand-file-name file-name)))
   1367 
   1368 (defalias 'lsp-canonical-file-name 'lsp-f-canonical)
   1369 
   1370 (defun lsp-f-same? (path-a path-b)
   1371   "Return t if PATH-A and PATH-B are references to the same file.
   1372 Symlinks are not followed."
   1373   (when (and (f-exists? path-a)
   1374              (f-exists? path-b))
   1375     (equal
   1376      (lsp-f-canonical (directory-file-name (f-expand path-a)))
   1377      (lsp-f-canonical (directory-file-name (f-expand path-b))))))
   1378 
   1379 (defun lsp-f-parent (path)
   1380   "Return the parent directory to PATH.
   1381 Symlinks are not followed."
   1382   (let ((parent (file-name-directory
   1383                  (directory-file-name (f-expand path default-directory)))))
   1384     (unless (lsp-f-same? path parent)
   1385       (if (f-relative? path)
   1386           (f-relative parent)
   1387         (directory-file-name parent)))))
   1388 
   1389 (defun lsp-f-ancestor-of? (path-a path-b)
   1390   "Return t if PATH-A is an ancestor of PATH-B.
   1391 Symlinks are not followed."
   1392   (unless (lsp-f-same? path-a path-b)
   1393     (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator))
   1394                (lsp-f-canonical path-b))))
   1395 
   1396 (defun lsp--merge-results (results method)
   1397   "Merge RESULTS by filtering the empty hash-tables and merging
   1398 the lists according to METHOD."
   1399   (pcase (--map (if (vectorp it)
   1400                     (append it nil) it)
   1401                 (-filter #'identity results))
   1402     (`() ())
   1403     ;; only one result - simply return it
   1404     (`(,fst) fst)
   1405     ;; multiple results merge it based on strategy
   1406     (results
   1407      (pcase method
   1408        ("textDocument/hover" (pcase (seq-filter
   1409                                      (-compose #'not #'lsp-empty?)
   1410                                      results)
   1411                                (`(,hover) hover)
   1412                                (hovers (lsp-make-hover
   1413                                         :contents
   1414                                         (-mapcat
   1415                                          (-lambda ((&Hover :contents))
   1416                                            (if (and (sequencep contents)
   1417                                                     (not (stringp contents)))
   1418                                                (append contents ())
   1419                                              (list contents)))
   1420                                          hovers)))))
   1421        ("textDocument/completion"
   1422         (lsp-make-completion-list
   1423          :is-incomplete (seq-some
   1424                          #'lsp:completion-list-is-incomplete
   1425                          results)
   1426          :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it)
   1427                                                     (lsp:completion-list-items it)
   1428                                                   it)
   1429                                                 nil))
   1430                            results)))
   1431        ("completionItem/resolve"
   1432         (let ((item (cl-first results)))
   1433           (when-let ((details (seq-filter #'identity
   1434                                           (seq-map #'lsp:completion-item-detail? results))))
   1435             (lsp:set-completion-item-detail?
   1436              item
   1437              (string-join details " ")))
   1438           (when-let ((docs (seq-filter #'identity
   1439                                        (seq-map #'lsp:completion-item-documentation? results))))
   1440             (lsp:set-completion-item-documentation?
   1441              item
   1442              (lsp-make-markup-content
   1443               :kind (or (seq-some (lambda (it)
   1444                                     (when (equal (lsp:markup-content-kind it)
   1445                                                  lsp/markup-kind-markdown)
   1446                                       lsp/markup-kind-markdown))
   1447                                   docs)
   1448                         lsp/markup-kind-plain-text)
   1449               :value (string-join (seq-map (lambda (doc)
   1450                                              (or (lsp:markup-content-value doc)
   1451                                                  (and (stringp doc) doc)))
   1452                                            docs)
   1453                                   "\n"))))
   1454           (when-let ((edits (seq-filter #'identity
   1455                                         (seq-map #'lsp:completion-item-additional-text-edits? results))))
   1456             (lsp:set-completion-item-additional-text-edits?
   1457              item
   1458              (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits)))
   1459           item))
   1460        (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results))))))
   1461 
   1462 (defun lsp--spinner-start ()
   1463   "Start spinner indication."
   1464   (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error)))
   1465 
   1466 (defun lsp--propertize (str type)
   1467   "Propertize STR as per TYPE."
   1468   (propertize str 'face (alist-get type lsp--message-type-face)))
   1469 
   1470 (defun lsp-workspaces ()
   1471   "Return the lsp workspaces associated with the current project."
   1472   (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces))
   1473 
   1474 (defun lsp--completing-read (prompt collection transform-fn &optional predicate
   1475                                     require-match initial-input
   1476                                     hist def inherit-input-method)
   1477   "Wrap `completing-read' to provide transformation function and disable sort.
   1478 
   1479 TRANSFORM-FN will be used to transform each of the items before displaying.
   1480 
   1481 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF
   1482 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes."
   1483   (let* ((col (--map (cons (funcall transform-fn it) it) collection))
   1484          (completion (completing-read prompt
   1485                                       (lambda (string pred action)
   1486                                         (if (eq action 'metadata)
   1487                                             `(metadata (display-sort-function . identity))
   1488                                           (complete-with-action action col string pred)))
   1489                                       predicate require-match initial-input hist
   1490                                       def inherit-input-method)))
   1491     (cdr (assoc completion col))))
   1492 
   1493 (defconst lsp--system-arch (lambda ()
   1494                              (setq lsp--system-arch
   1495                                    (pcase system-type
   1496                                      ('windows-nt
   1497                                       (pcase system-configuration
   1498                                         ((rx bol "x86_64-") 'x64)
   1499                                         (_ 'x86)))
   1500                                      ('darwin
   1501                                       (pcase system-configuration
   1502                                         ((rx "aarch64-") 'arm64)
   1503                                         (_ 'x64)))
   1504                                      ('gnu/linux
   1505                                        (pcase system-configuration
   1506                                          ((rx bol "aarch64-") 'arm64)
   1507                                          ((rx bol "x86_64") 'x64)
   1508                                          ((rx bol (| "i386" "i886")) 'x32)))
   1509                                      (_
   1510                                       (pcase system-configuration
   1511                                         ((rx bol "x86_64") 'x64)
   1512                                         ((rx bol (| "i386" "i886")) 'x32))))))
   1513   "Return the system architecture of `Emacs'.
   1514 Special values:
   1515   `x64'       64bit
   1516   `x32'       32bit
   1517   `arm64'     ARM 64bit")
   1518 
   1519 (defmacro lsp-with-current-buffer (buffer-id &rest body)
   1520   (declare (indent 1) (debug t))
   1521   `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer)))
   1522        (with-lsp-workspaces (plist-get ,buffer-id :workspaces)
   1523          (funcall wcb (lambda () ,@body)))
   1524      (with-current-buffer ,buffer-id
   1525        ,@body)))
   1526 
   1527 (defvar lsp--throw-on-input nil
   1528   "Make `lsp-*-while-no-input' throws `input' on interrupted.")
   1529 
   1530 (defmacro lsp--catch (tag bodyform &rest handlers)
   1531   "Catch TAG thrown in BODYFORM.
   1532 The return value from TAG will be handled in HANDLERS by `pcase'."
   1533   (declare (debug (form form &rest (pcase-PAT body))) (indent 2))
   1534   (let ((re-sym (make-symbol "re")))
   1535     `(let ((,re-sym (catch ,tag ,bodyform)))
   1536        (pcase ,re-sym
   1537          ,@handlers))))
   1538 
   1539 (defmacro lsp--while-no-input (&rest body)
   1540   "Wrap BODY in `while-no-input' and respecting `non-essential'.
   1541 If `lsp--throw-on-input' is set, will throw if input is pending, else
   1542 return value of `body' or nil if interrupted."
   1543   (declare (debug t) (indent 0))
   1544   `(if non-essential
   1545        (let ((res (while-no-input ,@body)))
   1546          (cond
   1547           ((and lsp--throw-on-input (equal res t))
   1548            (throw 'input :interrupted))
   1549           ((booleanp res) nil)
   1550           (t res)))
   1551      ,@body))
   1552 
   1553 ;; A ‘lsp--client’ object describes the client-side behavior of a language
   1554 ;; server.  It is used to start individual server processes, each of which is
   1555 ;; represented by a ‘lsp--workspace’ object.  Client objects are normally
   1556 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’.  Each
   1557 ;; workspace refers to exactly one client, but there can be multiple workspaces
   1558 ;; for a single client.
   1559 (cl-defstruct lsp--client
   1560   ;; ‘language-id’ is a function that receives a buffer as a single argument
   1561   ;; and should return the language identifier for that buffer.  See
   1562   ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem
   1563   ;; for a list of language identifiers.  Also consult the documentation for
   1564   ;; the language server represented by this client to find out what language
   1565   ;; identifiers it supports or expects.
   1566   (language-id nil)
   1567 
   1568   ;; ‘add-on?’ when set to t the server will be started no matter whether there
   1569   ;; is another server handling the same mode.
   1570   (add-on? nil)
   1571   ;; ‘new-connection’ is a function that should start a language server process
   1572   ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS).
   1573   ;; COMMAND-PROCESS must be a process object representing the server process
   1574   ;; just started.  COMMUNICATION-PROCESS must be a process (including pipe and
   1575   ;; network processes) that ‘lsp-mode’ uses to communicate with the language
   1576   ;; server using the language server protocol.  COMMAND-PROCESS and
   1577   ;; COMMUNICATION-PROCESS may be the same process; in that case
   1578   ;; ‘new-connection’ may also return that process as a single
   1579   ;; object. ‘new-connection’ is called with two arguments, FILTER and
   1580   ;; SENTINEL.  FILTER should be used as process filter for
   1581   ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for
   1582   ;; COMMAND-PROCESS.
   1583   (new-connection nil)
   1584 
   1585   ;; ‘ignore-regexps’ is a list of regexps.  When a data packet from the
   1586   ;; language server matches any of these regexps, it will be ignored.  This is
   1587   ;; intended for dealing with language servers that output non-protocol data.
   1588   (ignore-regexps nil)
   1589 
   1590   ;; ‘ignore-messages’ is a list of regexps.  When a message from the language
   1591   ;; server matches any of these regexps, it will be ignored.  This is useful
   1592   ;; for filtering out unwanted messages; such as servers that send nonstandard
   1593   ;; message types, or extraneous log messages.
   1594   (ignore-messages nil)
   1595 
   1596   ;; ‘notification-handlers’ is a hash table mapping notification method names
   1597   ;; (strings) to functions handling the respective notifications.  Upon
   1598   ;; receiving a notification, ‘lsp-mode’ will call the associated handler
   1599   ;; function passing two arguments, the ‘lsp--workspace’ object and the
   1600   ;; deserialized notification parameters.
   1601   (notification-handlers (make-hash-table :test 'equal))
   1602 
   1603   ;; ‘request-handlers’ is a hash table mapping request method names
   1604   ;; (strings) to functions handling the respective notifications.  Upon
   1605   ;; receiving a request, ‘lsp-mode’ will call the associated handler function
   1606   ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized
   1607   ;; request parameters.
   1608   (request-handlers (make-hash-table :test 'equal))
   1609 
   1610   ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request
   1611   ;; identifiers for pending asynchronous requests to functions handling the
   1612   ;; respective responses.  Upon receiving a response from the language server,
   1613   ;; ‘lsp-mode’ will call the associated response handler function with a
   1614   ;; single argument, the deserialized response parameters.
   1615   (response-handlers (make-hash-table :test 'eql))
   1616 
   1617   ;; ‘prefix-function’ is called for getting the prefix for completion.
   1618   ;; The function takes no parameter and returns a cons (start . end) representing
   1619   ;; the start and end bounds of the prefix. If it's not set, the client uses a
   1620   ;; default prefix function."
   1621   (prefix-function nil)
   1622 
   1623   ;; Contains mapping of scheme to the function that is going to be used to load
   1624   ;; the file.
   1625   (uri-handlers (make-hash-table :test #'equal))
   1626 
   1627   ;; ‘action-handlers’ is a hash table mapping action to a handler function. It
   1628   ;; can be used in `lsp-execute-code-action' to determine whether the action
   1629   ;; current client is interested in executing the action instead of sending it
   1630   ;; to the server.
   1631   (action-handlers (make-hash-table :test 'equal))
   1632 
   1633   ;; `action-filter' can be set to a function that modifies any incoming
   1634   ;; `CodeAction' in place before it is executed. The return value is ignored.
   1635   ;; This can be used to patch up broken code action requests before they are
   1636   ;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an
   1637   ;; example of a function that can be useful here.
   1638   (action-filter nil)
   1639 
   1640   ;; major modes supported by the client.
   1641   major-modes
   1642   ;; Function that will be called to decide if this language client
   1643   ;; should manage a particular buffer. The function will be passed
   1644   ;; the file name and major mode to inform the decision. Setting
   1645   ;; `activation-fn' will override `major-modes', if
   1646   ;; present.
   1647   activation-fn
   1648   ;; Break the tie when major-mode is supported by multiple clients.
   1649   (priority 0)
   1650   ;; Unique identifier for representing the client object.
   1651   server-id
   1652   ;; defines whether the client supports multi root workspaces.
   1653   multi-root
   1654   ;; Initialization options or a function that returns initialization options.
   1655   initialization-options
   1656   ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or
   1657   ;; completely replace, the faces used for semantic highlighting on a
   1658   ;; client-by-client basis.
   1659   ;;
   1660   ;; It recognizes four members, all of which are optional: `:types’ and
   1661   ;; `:modifiers’, respectively, should be face definition lists akin to
   1662   ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be
   1663   ;; merged with the default face definition list.
   1664   ;;
   1665   ;; Alternatively, if the plist members `:discard-default-types’ or
   1666   ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers'
   1667   ;; face definitions will be replaced entirely by their respective overrides.
   1668   ;;
   1669   ;; For example, setting `:semantic-tokens-faces-overrides' to
   1670   ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from
   1671   ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'.
   1672   ;;
   1673   ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))'
   1674   ;; will also remap "macro", but on top of that associate the fictional token type
   1675   ;; "not-quite-a-macro" with the face named `some-face'.
   1676   ;;
   1677   ;; `(:types (("macro" . font-lock-keyword-face))
   1678   ;;   :modifiers (("declaration" . lsp-face-semhl-interface))
   1679   ;;   :discard-default-types t
   1680   ;;   :discard-default-modifiers t)'
   1681   ;; will discard all default face definitions, hence leaving the client with
   1682   ;; only one token type "macro", mapped to `font-lock-keyword-face', and one
   1683   ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'.
   1684   semantic-tokens-faces-overrides
   1685   ;; Provides support for registering LSP Server specific capabilities.
   1686   custom-capabilities
   1687   ;; Function which returns the folders that are considered to be not projects but library files.
   1688   ;; The function accepts one parameter currently active workspace.
   1689   ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225.
   1690   library-folders-fn
   1691   ;; function which will be called when opening file in the workspace to perform
   1692   ;; client specific initialization. The function accepts one parameter
   1693   ;; currently active workspace.
   1694   before-file-open-fn
   1695   ;; Function which will be called right after a workspace has been initialized.
   1696   initialized-fn
   1697   ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP.
   1698   (remote? nil)
   1699 
   1700   ;; ‘completion-in-comments?’ t if the client supports completion in comments.
   1701   (completion-in-comments? nil)
   1702 
   1703   ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client.
   1704   (path->uri-fn nil)
   1705 
   1706   ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client.
   1707   (uri->path-fn nil)
   1708   ;; Function that returns an environment structure that will be used
   1709   ;; to set some environment variables when starting the language
   1710   ;; server process. These environment variables enable some
   1711   ;; additional features in the language server. The environment
   1712   ;; structure is an alist of the form (KEY . VALUE), where KEY is a
   1713   ;; string (regularly in all caps), and VALUE may be a string, a
   1714   ;; boolean, or a sequence of strings.
   1715   environment-fn
   1716 
   1717   ;; ‘after-open-fn’ workspace after open specific hooks.
   1718   (after-open-fn nil)
   1719 
   1720   ;; ‘async-request-handlers’ is a hash table mapping request method names
   1721   ;; (strings) to functions handling the respective requests that may take
   1722   ;; time to finish.  Upon receiving a request, ‘lsp-mode’ will call the
   1723   ;; associated handler function passing three arguments, the ‘lsp--workspace’
   1724   ;; object, the deserialized request parameters and the callback which accept
   1725   ;; result as its parameter.
   1726   (async-request-handlers (make-hash-table :test 'equal))
   1727   download-server-fn
   1728   download-in-progress?
   1729   buffers
   1730   synchronize-sections)
   1731 
   1732 (defun lsp-clients-executable-find (find-command &rest args)
   1733   "Finds an executable by invoking a search command.
   1734 
   1735 FIND-COMMAND is the executable finder that searches for the
   1736 actual language server executable. ARGS is a list of arguments to
   1737 give to FIND-COMMAND to find the language server.  Returns the
   1738 output of FIND-COMMAND if it exits successfully, nil otherwise.
   1739 
   1740 Typical uses include finding an executable by invoking `find' in
   1741 a project, finding LLVM commands on macOS with `xcrun', or
   1742 looking up project-specific language servers for projects written
   1743 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv'
   1744 etc."
   1745   (when-let* ((find-command-path (executable-find find-command))
   1746               (executable-path
   1747                (with-temp-buffer
   1748                  (when (zerop (apply 'call-process find-command-path nil t nil args))
   1749                    (buffer-substring-no-properties (point-min) (point-max))))))
   1750     (string-trim executable-path)))
   1751 
   1752 (defvar lsp--already-widened nil)
   1753 
   1754 (defmacro lsp-save-restriction-and-excursion (&rest form)
   1755   (declare (indent 0) (debug t))
   1756   `(if lsp--already-widened
   1757        (save-excursion ,@form)
   1758      (-let [lsp--already-widened t]
   1759        (save-restriction
   1760          (widen)
   1761          (save-excursion ,@form)))))
   1762 
   1763 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number
   1764 (defun lsp--line-character-to-point (line character)
   1765   "Return the point for character CHARACTER on line LINE."
   1766   (or (lsp-virtual-buffer-call :line/character->point line character)
   1767       (let ((inhibit-field-text-motion t))
   1768         (lsp-save-restriction-and-excursion
   1769           (goto-char (point-min))
   1770           (forward-line line)
   1771           ;; server may send character position beyond the current line and we
   1772           ;; should fallback to line end.
   1773           (-let [line-end (line-end-position)]
   1774             (if (> character (- line-end (point)))
   1775                 line-end
   1776               (forward-char character)
   1777               (point)))))))
   1778 
   1779 (lsp-defun lsp--position-to-point ((&Position :line :character))
   1780   "Convert `Position' object in PARAMS to a point."
   1781   (lsp--line-character-to-point line character))
   1782 
   1783 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end))
   1784   (cons start end))
   1785 
   1786 (lsp-defun lsp--range-text ((&RangeToPoint :start :end))
   1787   (buffer-substring start end))
   1788 
   1789 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end)))
   1790   (cond
   1791    ((and
   1792      (region-active-p)
   1793      (<= start (region-beginning) end)
   1794      (<= start (region-end) end)
   1795      (or (not (= start (region-beginning)))
   1796          (not (= end (region-end)))))
   1797     (cons start end))
   1798    ((and (<= start (point) end)
   1799          (not (region-active-p)))
   1800     (cons start end))
   1801    (parent? (lsp--find-wrapping-range parent?))))
   1802 
   1803 (defun lsp--get-selection-range ()
   1804   (or
   1805    (-when-let ((cache . cache-tick) lsp--document-selection-range-cache)
   1806      (when (= cache-tick (buffer-modified-tick)) cache))
   1807    (let ((response (cl-first
   1808                     (lsp-request
   1809                      "textDocument/selectionRange"
   1810                      (list :textDocument (lsp--text-document-identifier)
   1811                            :positions (vector (lsp--cur-position)))))))
   1812      (setq lsp--document-selection-range-cache
   1813            (cons response (buffer-modified-tick)))
   1814      response)))
   1815 
   1816 (defun lsp-extend-selection ()
   1817   "Extend selection."
   1818   (interactive)
   1819   (unless (lsp-feature? "textDocument/selectionRange")
   1820     (signal 'lsp-capability-not-supported (list "selectionRangeProvider")))
   1821   (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range)))
   1822     (goto-char start)
   1823     (set-mark (point))
   1824     (goto-char end)
   1825     (exchange-point-and-mark)))
   1826 
   1827 (defun lsp-warn (message &rest args)
   1828   "Display a warning message made from (`format-message' MESSAGE ARGS...).
   1829 This is equivalent to `display-warning', using `lsp-mode' as the type and
   1830 `:warning' as the level."
   1831   (display-warning 'lsp-mode (apply #'format-message message args)))
   1832 
   1833 (defun lsp--get-uri-handler (scheme)
   1834   "Get uri handler for SCHEME in the current workspace."
   1835   (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it)))
   1836           (or (lsp-workspaces) (lsp--session-workspaces (lsp-session)))))
   1837 
   1838 (defun lsp--fix-path-casing (path)
   1839   "On windows, downcases path because the windows file system is
   1840 case-insensitive.
   1841 
   1842 On other systems, returns path without change."
   1843   (if (eq system-type 'windows-nt) (downcase path) path))
   1844 
   1845 (defun lsp--uri-to-path (uri)
   1846   "Convert URI to a file path."
   1847   (if-let ((fn (->> (lsp-workspaces)
   1848                     (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client))
   1849                     (cl-first))))
   1850       (funcall fn uri)
   1851     (lsp--uri-to-path-1 uri)))
   1852 
   1853 (defun lsp-remap-path-if-needed (file-name)
   1854   (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings))
   1855       (propertize (buffer-local-value 'buffer-file-name buffer)
   1856                   'lsp-virtual-buffer virtual-buffer)
   1857     file-name))
   1858 
   1859 (defun lsp--uri-to-path-1 (uri)
   1860   "Convert URI to a file path."
   1861   (let* ((url (url-generic-parse-url (url-unhex-string uri)))
   1862          (type (url-type url))
   1863          (target (url-target url))
   1864          (file
   1865           (concat (decode-coding-string (url-filename url)
   1866                                         (or locale-coding-system 'utf-8))
   1867                   (when (and target
   1868                              (not (s-match
   1869                                    (rx "#" (group (1+ num)) (or "," "#")
   1870                                        (group (1+ num))
   1871                                        string-end)
   1872                                    uri)))
   1873                     (concat "#" target))))
   1874          (file-name (if (and type (not (string= type "file")))
   1875                         (if-let ((handler (lsp--get-uri-handler type)))
   1876                             (funcall handler uri)
   1877                           uri)
   1878                       ;; `url-generic-parse-url' is buggy on windows:
   1879                       ;; https://github.com/emacs-lsp/lsp-mode/pull/265
   1880                       (or (and (eq system-type 'windows-nt)
   1881                                (eq (elt file 0) ?\/)
   1882                                (substring file 1))
   1883                           file))))
   1884     (->> file-name
   1885          (concat (-some #'lsp--workspace-host-root (lsp-workspaces)))
   1886          (lsp-remap-path-if-needed))))
   1887 
   1888 (defun lsp--buffer-uri ()
   1889   "Return URI of the current buffer."
   1890   (or lsp-buffer-uri
   1891       (plist-get lsp--virtual-buffer :buffer-uri)
   1892       (lsp--path-to-uri
   1893        (or (buffer-file-name) (buffer-file-name (buffer-base-buffer))))))
   1894 
   1895 (defun lsp-register-client-capabilities (&rest _args)
   1896   "Implemented only to make `company-lsp' happy.
   1897 DELETE when `lsp-mode.el' is deleted.")
   1898 
   1899 (defconst lsp--url-path-allowed-chars
   1900   (url--allowed-chars (append '(?/) url-unreserved-chars))
   1901   "`url-unreserved-chars' with additional delim ?/.
   1902 This set of allowed chars is enough for hexifying local file paths.")
   1903 
   1904 (defun lsp--path-to-uri-1 (path)
   1905   (concat lsp--uri-file-prefix
   1906           (--> path
   1907             (expand-file-name it)
   1908             (or (file-remote-p it 'localname t) it)
   1909             (url-hexify-string it lsp--url-path-allowed-chars))))
   1910 
   1911 (defun lsp--path-to-uri (path)
   1912   "Convert PATH to a uri."
   1913   (if-let ((uri-fn (->> (lsp-workspaces)
   1914                         (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client))
   1915                         (cl-first))))
   1916       (funcall uri-fn path)
   1917     (lsp--path-to-uri-1 path)))
   1918 
   1919 (defun lsp--string-match-any (regex-list str)
   1920   "Return the first regex, if any, within REGEX-LIST matching STR."
   1921   (--first (string-match it str) regex-list))
   1922 
   1923 (cl-defstruct lsp-watch
   1924   (descriptors (make-hash-table :test 'equal))
   1925   root-directory)
   1926 
   1927 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories)
   1928   (let ((file-name (cl-third event))
   1929         (event-type (cl-second event)))
   1930     (cond
   1931      ((and (file-directory-p file-name)
   1932            (equal 'created event-type)
   1933            (not (lsp--string-match-any ignored-directories file-name)))
   1934 
   1935       (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch)
   1936 
   1937       ;; process the files that are already present in
   1938       ;; the directory.
   1939       (->> (directory-files-recursively file-name ".*" t)
   1940            (seq-do (lambda (f)
   1941                      (unless (file-directory-p f)
   1942                        (funcall callback (list nil 'created f)))))))
   1943      ((and (memq event-type '(created deleted changed))
   1944            (not (file-directory-p file-name))
   1945            (not (lsp--string-match-any ignored-files file-name)))
   1946       (funcall callback event))
   1947      ((and (memq event-type '(renamed))
   1948            (not (file-directory-p file-name))
   1949            (not (lsp--string-match-any ignored-files file-name)))
   1950       (funcall callback `(,(cl-first event) deleted ,(cl-third event)))
   1951       (funcall callback `(,(cl-first event) created ,(cl-fourth event)))))))
   1952 
   1953 (defun lsp--ask-about-watching-big-repo (number-of-directories dir)
   1954   "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR.
   1955 This is useful when there is a lot of files in a repository, as
   1956 that may slow Emacs down. Returns t if the user wants to watch
   1957 the entire repository, nil otherwise."
   1958   (prog1
   1959       (yes-or-no-p
   1960        (format
   1961         "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down.
   1962 Do you want to watch all files in %s? "
   1963         dir
   1964         number-of-directories
   1965         dir))
   1966     (lsp--info
   1967      (concat "You can configure this warning with the `lsp-enable-file-watchers' "
   1968              "and `lsp-file-watch-threshold' variables"))))
   1969 
   1970 
   1971 (defun lsp--path-is-watchable-directory (path dir ignored-directories)
   1972   "Figure out whether PATH (inside of DIR) is meant to have a file watcher set.
   1973 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't
   1974 want to watch."
   1975   (let
   1976       ((full-path (f-join dir path)))
   1977     (and (file-accessible-directory-p full-path)
   1978          (not (equal path "."))
   1979          (not (equal path ".."))
   1980          (not (lsp--string-match-any ignored-directories full-path)))))
   1981 
   1982 
   1983 (defun lsp--all-watchable-directories (dir ignored-directories)
   1984   "Traverse DIR recursively returning a list of paths that should have watchers.
   1985 IGNORED-DIRECTORIES will be used for exclusions"
   1986   (let* ((dir (if (f-symlink? dir)
   1987                   (file-truename dir)
   1988                 dir)))
   1989     (apply #'nconc
   1990            ;; the directory itself is assumed to be part of the set
   1991            (list dir)
   1992            ;; collect all subdirectories that are watchable
   1993            (-map
   1994             (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories))
   1995             ;; but only look at subdirectories that are watchable
   1996             (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories))
   1997                      (directory-files dir))))))
   1998 
   1999 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?)
   2000   "Create recursive file notification watch in DIR.
   2001 CALLBACK will be called when there are changes in any of
   2002 the monitored files. WATCHES is a hash table directory->file
   2003 notification handle which contains all of the watch that
   2004 already have been created. Watches will not be created for
   2005 any directory that matches any regex in IGNORED-DIRECTORIES.
   2006 Watches will not be created for any file that matches any
   2007 regex in IGNORED-FILES."
   2008   (let* ((dir (if (f-symlink? dir)
   2009                   (file-truename dir)
   2010                 dir))
   2011          (watch (or watch (make-lsp-watch :root-directory dir)))
   2012          (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories)))
   2013     (lsp-log "Creating watchers for following %s folders:\n  %s"
   2014              (length dirs-to-watch)
   2015              (s-join "\n  " dirs-to-watch))
   2016     (when (or
   2017            (not warn-big-repo?)
   2018            (not lsp-file-watch-threshold)
   2019            (let ((number-of-directories (length dirs-to-watch)))
   2020              (or
   2021               (< number-of-directories lsp-file-watch-threshold)
   2022               (condition-case nil
   2023                   (lsp--ask-about-watching-big-repo number-of-directories dir)
   2024                 (quit)))))
   2025       (dolist (current-dir dirs-to-watch)
   2026         (condition-case err
   2027             (progn
   2028               (puthash
   2029                current-dir
   2030                (file-notify-add-watch current-dir
   2031                                       '(change)
   2032                                       (lambda (event)
   2033                                         (lsp--folder-watch-callback event callback watch ignored-files ignored-directories)))
   2034                (lsp-watch-descriptors watch)))
   2035           (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))
   2036           (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err))))))
   2037     watch))
   2038 
   2039 (defun lsp-kill-watch (watch)
   2040   "Delete WATCH."
   2041   (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch))
   2042   (ht-clear! (lsp-watch-descriptors watch)))
   2043 
   2044 (defun lsp-json-bool (val)
   2045   "Convert VAL to JSON boolean."
   2046   (if val t :json-false))
   2047 
   2048 (defmacro with-lsp-workspace (workspace &rest body)
   2049   "Helper macro for invoking BODY in WORKSPACE context."
   2050   (declare (debug (form body))
   2051            (indent 1))
   2052   `(let ((lsp--cur-workspace ,workspace)) ,@body))
   2053 
   2054 (defmacro with-lsp-workspaces (workspaces &rest body)
   2055   "Helper macro for invoking BODY against multiple WORKSPACES."
   2056   (declare (debug (form body))
   2057            (indent 1))
   2058   `(let ((lsp--buffer-workspaces ,workspaces)) ,@body))
   2059 
   2060 
   2061 
   2062 (defmacro lsp-consistency-check (package)
   2063   `(defconst ,(intern (concat (symbol-name package)
   2064                               "-plist-value-when-compiled"))
   2065      (eval-when-compile lsp-use-plists)))
   2066 
   2067 
   2068 ;; loading code-workspace files
   2069 
   2070 ;;;###autoload
   2071 (defun lsp-load-vscode-workspace (file)
   2072   "Load vscode workspace from FILE"
   2073   (interactive "fSelect file to import: ")
   2074   (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session)))
   2075 
   2076   (let ((dir (f-dirname file)))
   2077     (->> file
   2078          (json-read-file)
   2079          (alist-get 'folders)
   2080          (-map (-lambda ((&alist 'path))
   2081                  (lsp-workspace-folders-add (expand-file-name path dir)))))))
   2082 
   2083 ;;;###autoload
   2084 (defun lsp-save-vscode-workspace (file)
   2085   "Save vscode workspace to FILE"
   2086   (interactive "FSelect file to save to: ")
   2087 
   2088   (let ((json-encoding-pretty-print t))
   2089     (f-write-text (json-encode
   2090                    `((folders . ,(->> (lsp-session)
   2091                                       (lsp-session-folders)
   2092                                       (--map `((path . ,it)))))))
   2093                   'utf-8
   2094                   file)))
   2095 
   2096 
   2097 (defmacro lsp-foreach-workspace (&rest body)
   2098   "Execute BODY for each of the current workspaces."
   2099   (declare (debug (form body)))
   2100   `(--map (with-lsp-workspace it ,@body) (lsp-workspaces)))
   2101 
   2102 (defmacro when-lsp-workspace (workspace &rest body)
   2103   "Helper macro for invoking BODY in WORKSPACE context if present."
   2104   (declare (debug (form body))
   2105            (indent 1))
   2106   `(when-let ((lsp--cur-workspace ,workspace)) ,@body))
   2107 
   2108 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items))
   2109   (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read))
   2110             (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label))
   2111                                  items))
   2112             (result (funcall-interactively
   2113                      selectfunc
   2114                      (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels))
   2115             (choices (if (listp result)
   2116                          (if (equal result '("*"))
   2117                              itemLabels
   2118                            result)
   2119                        (list result))))
   2120       (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data))
   2121                                                  (if (member label choices)
   2122                                                      (lsp-make-quick-pick-item :label label :picked t :user-data user-data)
   2123                                                    nil))
   2124                                                items)))))
   2125 
   2126 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?))
   2127   (read-string (format "%s: " prompt) (or value? "")))
   2128 
   2129 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type))
   2130   "Send the server's messages to log.
   2131 PARAMS - the data sent from _WORKSPACE."
   2132   (funcall (cl-case type
   2133              (1 'lsp--error)
   2134              (2 'lsp--warn)
   2135              (t 'lsp--info))
   2136            "%s"
   2137            message))
   2138 
   2139 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type))
   2140   "Send the server's messages to log.
   2141 PARAMS - the data sent from WORKSPACE."
   2142   (ignore
   2143    (let ((client (lsp--workspace-client workspace)))
   2144      (when (or (not client)
   2145                (cl-notany (-rpartial #'string-match-p message)
   2146                           (lsp--client-ignore-messages client)))
   2147        (lsp-log "%s" (lsp--propertize message type))))))
   2148 
   2149 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?))
   2150   "Display a message request to user sending the user selection back to server."
   2151   (let* ((message (lsp--propertize message type))
   2152          (choices (seq-map #'lsp:message-action-item-title actions?)))
   2153     (if choices
   2154         (completing-read (concat message " ") (seq-into choices 'list) nil t)
   2155       (lsp-log message))))
   2156 
   2157 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?))
   2158   "Show document URI in a buffer and go to SELECTION if any."
   2159   (let ((path (lsp--uri-to-path uri)))
   2160     (when (f-exists? path)
   2161       (with-current-buffer (find-file path)
   2162         (when selection?
   2163           (goto-char (lsp--position-to-point (lsp:range-start selection?))))
   2164         t))))
   2165 
   2166 (defcustom lsp-progress-prefix "⌛ "
   2167   "Progress prefix."
   2168   :group 'lsp-mode
   2169   :type 'string
   2170   :package-version '(lsp-mode . "8.0.0"))
   2171 
   2172 (defcustom lsp-progress-function #'lsp-on-progress-modeline
   2173   "Function for handling the progress notifications."
   2174   :group 'lsp-mode
   2175   :type '(choice
   2176           (const :tag "Use modeline" lsp-on-progress-modeline)
   2177           (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')"
   2178                  lsp-on-progress-legacy)
   2179           (const :tag "Ignore" ignore)
   2180           (function :tag "Other function"))
   2181   :package-version '(lsp-mode . "8.0.0"))
   2182 
   2183 (defcustom lsp-request-while-no-input-may-block nil
   2184   "Have `lsp-request-while-no-input` block unless `non-essential` is t."
   2185   :group 'lsp-mode
   2186   :type 'boolean)
   2187 
   2188 (defun lsp--progress-status ()
   2189   "Returns the status of the progress for the current workspaces."
   2190   (-let ((progress-status
   2191           (s-join
   2192            "|"
   2193            (-keep
   2194             (lambda (workspace)
   2195               (let ((tokens (lsp--workspace-work-done-tokens workspace)))
   2196                 (unless (ht-empty? tokens)
   2197                   (mapconcat
   2198                    (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?))
   2199                      (concat (if percentage?
   2200                                  (if (numberp percentage?)
   2201                                      (format "%.0f%%%% " percentage?)
   2202                                    (format "%s%%%% " percentage?))
   2203                                "")
   2204                              (or message? title)))
   2205                    (ht-values tokens)
   2206                    "|"))))
   2207             (lsp-workspaces)))))
   2208     (unless (s-blank? progress-status)
   2209       (concat lsp-progress-prefix progress-status " "))))
   2210 
   2211 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value
   2212                                                                 (value &as &WorkDoneProgress :kind)))
   2213   "PARAMS contains the progress data.
   2214 WORKSPACE is the workspace that contains the progress token."
   2215   (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status))))
   2216   (pcase kind
   2217     ("begin" (lsp-workspace-set-work-done-token token value workspace))
   2218     ("report" (lsp-workspace-set-work-done-token token value workspace))
   2219     ("end" (lsp-workspace-rem-work-done-token token workspace)))
   2220   (force-mode-line-update))
   2221 
   2222 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value
   2223                                                               (value &as &WorkDoneProgress :kind)))
   2224   "PARAMS contains the progress data.
   2225 WORKSPACE is the workspace that contains the progress token."
   2226   (pcase kind
   2227     ("begin"
   2228      (-let* (((&WorkDoneProgressBegin :title :percentage?) value)
   2229              (reporter
   2230               (if lsp-progress-via-spinner
   2231                   (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types))
   2232                          ;; Set message as a tooltip for the spinner strings
   2233                          (propertized-strings
   2234                           (seq-map (lambda (string) (propertize string 'help-echo title))
   2235                                    spinner-strings))
   2236                          (spinner-type (vconcat propertized-strings)))
   2237                     ;; The progress relates to the server as a whole,
   2238                     ;; display it on all buffers.
   2239                     (mapcar (lambda (buffer)
   2240                               (lsp-with-current-buffer buffer
   2241                                 (spinner-start spinner-type))
   2242                               buffer)
   2243                             (lsp--workspace-buffers workspace)))
   2244                 (if percentage?
   2245                     (make-progress-reporter title 0 100 percentage?)
   2246                   ;; No percentage, just progress
   2247                   (make-progress-reporter title nil nil)))))
   2248        (lsp-workspace-set-work-done-token token reporter workspace)))
   2249     ("report"
   2250      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2251        (unless lsp-progress-via-spinner
   2252          (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value)))))
   2253 
   2254     ("end"
   2255      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2256        (if lsp-progress-via-spinner
   2257            (mapc (lambda (buffer)
   2258                    (when (lsp-buffer-live-p buffer)
   2259                      (lsp-with-current-buffer buffer
   2260                        (spinner-stop))))
   2261                  reporter)
   2262          (progress-reporter-done reporter))
   2263        (lsp-workspace-rem-work-done-token token workspace)))))
   2264 
   2265 
   2266 ;; diagnostics
   2267 
   2268 (defvar lsp-diagnostic-filter nil
   2269   "A a function which will be called with
   2270   `&PublishDiagnosticsParams' and `workspace' which can be used
   2271   to filter out the diagnostics. The function should return
   2272   `&PublishDiagnosticsParams'.
   2273 
   2274 Common usecase are:
   2275 1. Filter the diagnostics for a particular language server.
   2276 2. Filter out the diagnostics under specific level.")
   2277 
   2278 (defvar lsp-diagnostic-stats (ht))
   2279 
   2280 (defun lsp-diagnostics (&optional current-workspace?)
   2281   "Return the diagnostics from all workspaces."
   2282   (or (pcase (if current-workspace?
   2283                  (lsp-workspaces)
   2284                (lsp--session-workspaces (lsp-session)))
   2285         (`() ())
   2286         (`(,workspace) (lsp--workspace-diagnostics workspace))
   2287         (`,workspaces (let ((result (make-hash-table :test 'equal)))
   2288                         (mapc (lambda (workspace)
   2289                                 (->> workspace
   2290                                      (lsp--workspace-diagnostics)
   2291                                      (maphash (lambda (file-name diagnostics)
   2292                                                 (puthash file-name
   2293                                                          (append (gethash file-name result) diagnostics)
   2294                                                          result)))))
   2295                               workspaces)
   2296                         result)))
   2297       (ht)))
   2298 
   2299 (defun lsp-diagnostics-stats-for (path)
   2300   "Get diagnostics statistics for PATH.
   2301 The result format is vector [_ errors warnings infos hints] or nil."
   2302   (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats))
   2303 
   2304 (defun lsp-diagnostics--request-pull-diagnostics (workspace)
   2305   "Request new diagnostics for the current file within WORKSPACE.
   2306 This is only executed if the server supports pull diagnostics."
   2307   (when (lsp-feature? "textDocument/diagnostic")
   2308     (let ((path (lsp--fix-path-casing (buffer-file-name))))
   2309       (lsp-request-async "textDocument/diagnostic"
   2310                          (list :textDocument (lsp--text-document-identifier))
   2311                          (-lambda ((&DocumentDiagnosticReport :kind :items?))
   2312                            (lsp-diagnostics--apply-pull-diagnostics workspace path kind items?))
   2313                          :mode 'tick))))
   2314 
   2315 (defun lsp-diagnostics--update-path (path new-stats)
   2316   (let ((new-stats (copy-sequence new-stats))
   2317         (path (lsp--fix-path-casing (directory-file-name path))))
   2318     (if-let ((old-data (gethash path lsp-diagnostic-stats)))
   2319         (dotimes (idx 5)
   2320           (cl-callf + (aref old-data idx)
   2321             (aref new-stats idx)))
   2322       (puthash path new-stats lsp-diagnostic-stats))))
   2323 
   2324 (defun lsp-diagnostics--convert-and-update-path-stats (workspace path diagnostics)
   2325   (let ((path (lsp--fix-path-casing path))
   2326         (new-stats (make-vector 5 0)))
   2327     (mapc (-lambda ((&Diagnostic :severity?))
   2328             (cl-incf (aref new-stats (or severity? 1))))
   2329           diagnostics)
   2330     (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace))))
   2331       (mapc (-lambda ((&Diagnostic :severity?))
   2332               (cl-decf (aref new-stats (or severity? 1))))
   2333             old-diags))
   2334     (lsp-diagnostics--update-path path new-stats)
   2335     (while (not (string= path (setf path (file-name-directory
   2336                                           (directory-file-name path)))))
   2337       (lsp-diagnostics--update-path path new-stats))))
   2338 
   2339 (lsp-defun lsp--on-diagnostics-update-stats (workspace
   2340                                              (&PublishDiagnosticsParams :uri :diagnostics))
   2341   (lsp-diagnostics--convert-and-update-path-stats workspace (lsp--uri-to-path uri) diagnostics))
   2342 
   2343 (defun lsp-diagnostics--apply-pull-diagnostics (workspace path kind diagnostics?)
   2344   "Update WORKSPACE diagnostics at PATH with DIAGNOSTICS?.
   2345 Depends on KIND being a \\='full\\=' update."
   2346   (cond
   2347    ((equal kind "full")
   2348     ;; TODO support `lsp-diagnostic-filter'
   2349     ;; (the params types differ from the published diagnostics response)
   2350     (lsp-diagnostics--convert-and-update-path-stats workspace path diagnostics?)
   2351     (-let* ((lsp--virtual-buffer-mappings (ht))
   2352             (workspace-diagnostics (lsp--workspace-diagnostics workspace)))
   2353       (if (seq-empty-p diagnostics?)
   2354           (remhash path workspace-diagnostics)
   2355         (puthash path (append diagnostics? nil) workspace-diagnostics))
   2356       (run-hooks 'lsp-diagnostics-updated-hook)))
   2357     ((equal kind "unchanged") t)
   2358     (t (lsp--error "Unknown pull diagnostic result kind '%s'" kind))))
   2359 
   2360 (defun lsp--on-diagnostics (workspace params)
   2361   "Callback for textDocument/publishDiagnostics.
   2362 interface PublishDiagnosticsParams {
   2363     uri: string;
   2364     diagnostics: Diagnostic[];
   2365 }
   2366 PARAMS contains the diagnostics data.
   2367 WORKSPACE is the workspace that contains the diagnostics."
   2368   (when lsp-diagnostic-filter
   2369     (setf params (funcall lsp-diagnostic-filter params workspace)))
   2370 
   2371   (lsp--on-diagnostics-update-stats workspace params)
   2372 
   2373   (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params)
   2374           (lsp--virtual-buffer-mappings (ht))
   2375           (file (lsp--fix-path-casing (lsp--uri-to-path uri)))
   2376           (workspace-diagnostics (lsp--workspace-diagnostics workspace)))
   2377 
   2378     (if (seq-empty-p diagnostics)
   2379         (remhash file workspace-diagnostics)
   2380       (puthash file (append diagnostics nil) workspace-diagnostics))
   2381 
   2382     (run-hooks 'lsp-diagnostics-updated-hook)))
   2383 
   2384 (defun lsp-diagnostics--workspace-cleanup (workspace)
   2385   (->> workspace
   2386        (lsp--workspace-diagnostics)
   2387        (maphash (lambda (key _)
   2388                   (lsp--on-diagnostics-update-stats
   2389                    workspace
   2390                    (lsp-make-publish-diagnostics-params
   2391                     :uri (lsp--path-to-uri key)
   2392                     :diagnostics [])))))
   2393   (clrhash (lsp--workspace-diagnostics workspace)))
   2394 
   2395 
   2396 
   2397 ;; textDocument/foldingRange support
   2398 
   2399 (cl-defstruct lsp--folding-range beg end kind children)
   2400 
   2401 (defvar-local lsp--cached-folding-ranges nil)
   2402 (defvar-local lsp--cached-nested-folding-ranges nil)
   2403 
   2404 (defun lsp--folding-range-width (range)
   2405   (- (lsp--folding-range-end range)
   2406      (lsp--folding-range-beg range)))
   2407 
   2408 (defun lsp--get-folding-ranges ()
   2409   "Get the folding ranges for the current buffer."
   2410   (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges))
   2411     (let* ((ranges (lsp-request "textDocument/foldingRange"
   2412                                 `(:textDocument ,(lsp--text-document-identifier))))
   2413            (sorted-line-col-pairs (->> ranges
   2414                                        (cl-mapcan (-lambda ((&FoldingRange :start-line
   2415                                                                            :start-character?
   2416                                                                            :end-line
   2417                                                                            :end-character?))
   2418                                                     (list (cons start-line start-character?)
   2419                                                           (cons end-line end-character?))))
   2420                                        (-sort #'lsp--line-col-comparator)))
   2421            (line-col-to-point-map (lsp--convert-line-col-to-points-batch
   2422                                    sorted-line-col-pairs)))
   2423       (setq lsp--cached-folding-ranges
   2424             (cons (buffer-chars-modified-tick)
   2425                   (--> ranges
   2426                     (seq-map (-lambda ((range &as
   2427                                               &FoldingRange :start-line
   2428                                               :start-character?
   2429                                               :end-line
   2430                                               :end-character?
   2431                                               :kind?))
   2432                                (make-lsp--folding-range
   2433                                 :beg (ht-get line-col-to-point-map
   2434                                              (cons start-line start-character?))
   2435                                 :end (ht-get line-col-to-point-map
   2436                                              (cons end-line end-character?))
   2437                                 :kind kind?))
   2438                              it)
   2439                     (seq-filter (lambda (folding-range)
   2440                                   (< (lsp--folding-range-beg folding-range)
   2441                                      (lsp--folding-range-end folding-range)))
   2442                                 it)
   2443                     (seq-into it 'list)
   2444                     (delete-dups it))))))
   2445   (cdr lsp--cached-folding-ranges))
   2446 
   2447 (defun lsp--get-nested-folding-ranges ()
   2448   "Get a list of nested folding ranges for the current buffer."
   2449   (-let [(tick . _) lsp--cached-folding-ranges]
   2450     (if (and (eq tick (buffer-chars-modified-tick))
   2451              lsp--cached-nested-folding-ranges)
   2452         lsp--cached-nested-folding-ranges
   2453       (setq lsp--cached-nested-folding-ranges
   2454             (lsp--folding-range-build-trees (lsp--get-folding-ranges))))))
   2455 
   2456 (defun lsp--folding-range-build-trees (ranges)
   2457   (setq ranges (seq-sort #'lsp--range-before-p ranges))
   2458   (let* ((dummy-node (make-lsp--folding-range
   2459                       :beg most-negative-fixnum
   2460                       :end most-positive-fixnum))
   2461          (stack (list dummy-node)))
   2462     (dolist (range ranges)
   2463       (while (not (lsp--range-inside-p range (car stack)))
   2464         (pop stack))
   2465       (push range (lsp--folding-range-children (car stack)))
   2466       (push range stack))
   2467     (lsp--folding-range-children dummy-node)))
   2468 
   2469 (defun lsp--range-inside-p (r1 r2)
   2470   "Return non-nil if folding range R1 lies inside R2"
   2471   (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2))
   2472        (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2))))
   2473 
   2474 (defun lsp--range-before-p (r1 r2)
   2475   "Return non-nil if folding range R1 ends before R2"
   2476   ;; Ensure r1 comes before r2
   2477   (or (< (lsp--folding-range-beg r1)
   2478          (lsp--folding-range-beg r2))
   2479       ;; If beg(r1) == beg(r2) make sure r2 ends first
   2480       (and (= (lsp--folding-range-beg r1)
   2481               (lsp--folding-range-beg r2))
   2482            (< (lsp--folding-range-end r2)
   2483               (lsp--folding-range-end r1)))))
   2484 
   2485 (defun lsp--point-inside-range-p (point range)
   2486   "Return non-nil if POINT lies inside folding range RANGE."
   2487   (and (>= point (lsp--folding-range-beg range))
   2488        (<= point (lsp--folding-range-end range))))
   2489 
   2490 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point)))
   2491   "Return the innermost folding range POINT lies in."
   2492   (seq-reduce (lambda (innermost-range curr-range)
   2493                 (if (and (lsp--point-inside-range-p point curr-range)
   2494                          (or (null innermost-range)
   2495                              (lsp--range-inside-p curr-range innermost-range)))
   2496                     curr-range
   2497                   innermost-range))
   2498               (lsp--get-folding-ranges)
   2499               nil))
   2500 
   2501 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point)))
   2502   "Return the outermost folding range POINT lies in."
   2503   (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range)
   2504                      (let ((curr-width (lsp--folding-range-width curr-range)))
   2505                        (if (and (lsp--point-inside-range-p point curr-range)
   2506                                 (or (null best-pair)
   2507                                     (> curr-width outermost-width)))
   2508                            (cons curr-width curr-range)
   2509                          best-pair)))
   2510                    (lsp--get-folding-ranges)
   2511                    nil)))
   2512 
   2513 (defun lsp--folding-range-at-point-bounds ()
   2514   (when (and lsp-enable-folding
   2515              (lsp-feature? "textDocument/foldingRange"))
   2516     (if-let ((range (lsp--get-current-innermost-folding-range)))
   2517         (cons (lsp--folding-range-beg range)
   2518               (lsp--folding-range-end range)))))
   2519 (put 'lsp--folding-range 'bounds-of-thing-at-point
   2520      #'lsp--folding-range-at-point-bounds)
   2521 
   2522 (defun lsp--get-nearest-folding-range (&optional backward)
   2523   (let ((point (point))
   2524         (found nil))
   2525     (while (not
   2526             (or found
   2527                 (if backward
   2528                     (<= point (point-min))
   2529                   (>= point (point-max)))))
   2530       (if backward (cl-decf point) (cl-incf point))
   2531       (setq found (lsp--get-current-innermost-folding-range point)))
   2532     found))
   2533 
   2534 (defun lsp--folding-range-at-point-forward-op (n)
   2535   (when (and lsp-enable-folding
   2536              (not (zerop n))
   2537              (lsp-feature? "textDocument/foldingRange"))
   2538     (cl-block break
   2539       (dotimes (_ (abs n))
   2540         (if-let ((range (lsp--get-nearest-folding-range (< n 0))))
   2541             (goto-char (if (< n 0)
   2542                            (lsp--folding-range-beg range)
   2543                          (lsp--folding-range-end range)))
   2544           (cl-return-from break))))))
   2545 (put 'lsp--folding-range 'forward-op
   2546      #'lsp--folding-range-at-point-forward-op)
   2547 
   2548 (defun lsp--folding-range-at-point-beginning-op ()
   2549   (goto-char (car (lsp--folding-range-at-point-bounds))))
   2550 (put 'lsp--folding-range 'beginning-op
   2551      #'lsp--folding-range-at-point-beginning-op)
   2552 
   2553 (defun lsp--folding-range-at-point-end-op ()
   2554   (goto-char (cdr (lsp--folding-range-at-point-bounds))))
   2555 (put 'lsp--folding-range 'end-op
   2556      #'lsp--folding-range-at-point-end-op)
   2557 
   2558 (defun lsp--range-at-point-bounds ()
   2559   (or (lsp--folding-range-at-point-bounds)
   2560       (when-let ((range (and
   2561                          (lsp-feature? "textDocument/hover")
   2562                          (->> (lsp--text-document-position-params)
   2563                               (lsp-request "textDocument/hover")
   2564                               (lsp:hover-range?)))))
   2565         (lsp--range-to-region range))))
   2566 
   2567 ;; A more general purpose "thing", useful for applications like focus.el
   2568 (put 'lsp--range 'bounds-of-thing-at-point
   2569      #'lsp--range-at-point-bounds)
   2570 
   2571 (defun lsp--log-io-p (method)
   2572   "Return non nil if should log for METHOD."
   2573   (and lsp-log-io
   2574        (or (not lsp-log-io-allowlist-methods)
   2575            (member method lsp-log-io-allowlist-methods))))
   2576 
   2577 
   2578 ;; toggles
   2579 
   2580 (defun lsp-toggle-trace-io ()
   2581   "Toggle client-server protocol logging."
   2582   (interactive)
   2583   (setq lsp-log-io (not lsp-log-io))
   2584   (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled")))
   2585 
   2586 (defun lsp-toggle-signature-auto-activate ()
   2587   "Toggle signature auto activate."
   2588   (interactive)
   2589   (setq lsp-signature-auto-activate
   2590         (unless lsp-signature-auto-activate '(:on-trigger-char)))
   2591   (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled"))
   2592   (lsp--update-signature-help-hook))
   2593 
   2594 (defun lsp-toggle-on-type-formatting ()
   2595   "Toggle on type formatting."
   2596   (interactive)
   2597   (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting))
   2598   (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled"))
   2599   (lsp--update-on-type-formatting-hook))
   2600 
   2601 (defun lsp-toggle-symbol-highlight ()
   2602   "Toggle symbol highlighting."
   2603   (interactive)
   2604   (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting))
   2605 
   2606   (cond
   2607    ((and lsp-enable-symbol-highlighting
   2608          (lsp-feature? "textDocument/documentHighlight"))
   2609     (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)
   2610     (lsp--info "Symbol highlighting enabled in current buffer."))
   2611    ((not lsp-enable-symbol-highlighting)
   2612     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   2613     (lsp--remove-overlays 'lsp-highlight)
   2614     (lsp--info "Symbol highlighting disabled in current buffer."))))
   2615 
   2616 
   2617 ;; keybindings
   2618 (defvar lsp--binding-descriptions nil
   2619   "List of key binding/short description pair.")
   2620 
   2621 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings)
   2622   "In KEYMAP, define key sequence KEY as DEF conditionally.
   2623 This is like `define-key', except the definition disappears
   2624 whenever COND evaluates to nil.
   2625 DESC is the short-description for the binding.
   2626 BINDINGS is a list of (key def desc cond)."
   2627   (declare (indent defun)
   2628            (debug (form form form form form &rest sexp)))
   2629   (->> (cl-list* key def desc cond bindings)
   2630        (-partition 4)
   2631        (-mapcat (-lambda ((key def desc cond))
   2632                   `((define-key ,keymap ,key
   2633                       '(menu-item
   2634                         ,(format "maybe-%s" def)
   2635                         ,def
   2636                         :filter
   2637                         (lambda (item)
   2638                           (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer)
   2639                                                            lsp--describe-buffer)
   2640                                                          (current-buffer))
   2641                                   ,cond)
   2642                             item))))
   2643                     (when (stringp ,key)
   2644                       (setq lsp--binding-descriptions
   2645                             (append lsp--binding-descriptions '(,key ,desc)))))))
   2646        macroexp-progn))
   2647 
   2648 (defvar lsp--describe-buffer nil)
   2649 
   2650 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus)
   2651   (let ((lsp--describe-buffer buffer))
   2652     (funcall fn buffer prefix menus)))
   2653 
   2654 (advice-add 'describe-buffer-bindings
   2655             :around
   2656             #'lsp-describe-buffer-bindings-advice)
   2657 
   2658 (defun lsp--prepend-prefix (mappings)
   2659   (->> mappings
   2660        (-partition 2)
   2661        (-mapcat (-lambda ((key description))
   2662                   (list (concat lsp-keymap-prefix " " key)
   2663                         description)))))
   2664 
   2665 (defvar lsp-command-map
   2666   (-doto (make-sparse-keymap)
   2667     (lsp-define-conditional-key
   2668       ;; workspaces
   2669       "wD" lsp-disconnect "disconnect" (lsp-workspaces)
   2670       "wd" lsp-describe-session "describe session" t
   2671       "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces)
   2672       "wr" lsp-workspace-restart "restart server" (lsp-workspaces)
   2673       "ws" lsp "start server" t
   2674 
   2675       ;; formatting
   2676       "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting")
   2677                                                  (lsp-feature? "textDocument/formatting"))
   2678       "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting")
   2679 
   2680       ;; folders
   2681       "Fa" lsp-workspace-folders-add "add folder" t
   2682       "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t
   2683       "Fr" lsp-workspace-folders-remove "remove folder" t
   2684 
   2685       ;; toggles
   2686       "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature?
   2687                                                                         "textDocument/publishDiagnostics")
   2688       "TL" lsp-toggle-trace-io "toggle log io" t
   2689       "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline)
   2690       "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs)
   2691       "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature?
   2692                                                                           "textDocument/codeAction")
   2693       "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature?
   2694                                                                "textDocument/documentSymbol")
   2695       "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc)
   2696       "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature?
   2697                                                                       "textDocument/onTypeFormatting")
   2698       "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight")
   2699       "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens")
   2700       "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp")
   2701 
   2702       ;; goto
   2703       "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol")
   2704       "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration")
   2705       "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list)
   2706       "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition")
   2707       "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls")
   2708                                                              (fboundp 'lsp-treemacs-call-hierarchy))
   2709       "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation")
   2710       "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references")
   2711       "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition")
   2712 
   2713       ;; help
   2714       "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc)
   2715                                                   (lsp-feature? "textDocument/hover"))
   2716       "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover")
   2717       "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp")
   2718 
   2719       ;; refactoring
   2720       "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction")
   2721       "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename")
   2722 
   2723       ;; actions
   2724       "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction")
   2725       "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight")
   2726       "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy))
   2727 
   2728       ;; peeks
   2729       "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition")
   2730                                                                 (fboundp 'lsp-ui-peek-find-definitions))
   2731       "Gi" lsp-ui-peek-find-implementation "peek implementations" (and
   2732                                                                    (fboundp 'lsp-ui-peek-find-implementation)
   2733                                                                    (lsp-feature? "textDocument/implementation"))
   2734       "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references)
   2735                                                               (lsp-feature? "textDocument/references"))
   2736       "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp
   2737                                                                            'lsp-ui-peek-find-workspace-symbol)
   2738                                                                           (lsp-feature? "workspace/symbol")))))
   2739 
   2740 
   2741 ;; which-key integration
   2742 
   2743 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key")
   2744 (declare-function which-key-add-key-based-replacements "ext:which-key")
   2745 
   2746 (defun lsp-enable-which-key-integration (&optional all-modes)
   2747   "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current
   2748 active `major-mode', or for all major modes when ALL-MODES is t."
   2749   (cl-flet ((which-key-fn (if all-modes
   2750                               'which-key-add-key-based-replacements
   2751                             (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode))))
   2752     (apply
   2753      #'which-key-fn
   2754      (lsp--prepend-prefix
   2755       (cl-list*
   2756        ""    "lsp"
   2757        "w"   "workspaces"
   2758        "F"   "folders"
   2759        "="   "formatting"
   2760        "T"   "toggle"
   2761        "g"   "goto"
   2762        "h"   "help"
   2763        "r"   "refactor"
   2764        "a"   "code actions"
   2765        "G"   "peek"
   2766        lsp--binding-descriptions)))))
   2767 
   2768 
   2769 ;; Globbing syntax
   2770 
   2771 ;; We port VSCode's glob-to-regexp code
   2772 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts)
   2773 ;; since the LSP globbing syntax seems to be the same as that of
   2774 ;; VSCode.
   2775 
   2776 (defconst lsp-globstar "**"
   2777   "Globstar pattern.")
   2778 
   2779 (defconst lsp-glob-split ?/
   2780   "The character by which we split path components in a glob
   2781 pattern.")
   2782 
   2783 (defconst lsp-path-regexp "[/\\\\]"
   2784   "Forward or backslash to be used as a path separator in
   2785 computed regexps.")
   2786 
   2787 (defconst lsp-non-path-regexp "[^/\\\\]"
   2788   "A regexp matching anything other than a slash.")
   2789 
   2790 (defconst lsp-globstar-regexp
   2791   (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?"
   2792           lsp-path-regexp
   2793           lsp-non-path-regexp lsp-path-regexp
   2794           lsp-path-regexp lsp-non-path-regexp)
   2795   "Globstar in regexp form.")
   2796 
   2797 (defun lsp-split-glob-pattern (pattern split-char)
   2798   "Split PATTERN at SPLIT-CHAR while respecting braces and brackets."
   2799   (when pattern
   2800     (let ((segments nil)
   2801           (in-braces nil)
   2802           (in-brackets nil)
   2803           (current-segment ""))
   2804       (dolist (char (string-to-list pattern))
   2805         (cl-block 'exit-point
   2806           (if (eq char split-char)
   2807               (when (and (null in-braces)
   2808                          (null in-brackets))
   2809                 (push current-segment segments)
   2810                 (setq current-segment "")
   2811                 (cl-return-from 'exit-point))
   2812             (pcase char
   2813               (?{
   2814                (setq in-braces t))
   2815               (?}
   2816                (setq in-braces nil))
   2817               (?\[
   2818                (setq in-brackets t))
   2819               (?\]
   2820                (setq in-brackets nil))))
   2821           (setq current-segment (concat current-segment
   2822                                         (char-to-string char)))))
   2823       (unless (string-empty-p current-segment)
   2824         (push current-segment segments))
   2825       (nreverse segments))))
   2826 
   2827 (defun lsp--glob-to-regexp (pattern)
   2828   "Helper function to convert a PATTERN from LSP's glob syntax to
   2829 an Elisp regexp."
   2830   (if (string-empty-p pattern)
   2831       ""
   2832     (let ((current-regexp "")
   2833           (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split)))
   2834       (if (-all? (lambda (segment) (eq segment lsp-globstar))
   2835                  glob-segments)
   2836           ".*"
   2837         (let ((prev-segment-was-globstar nil))
   2838           (seq-do-indexed
   2839            (lambda (segment index)
   2840              (if (string-equal segment lsp-globstar)
   2841                  (unless prev-segment-was-globstar
   2842                    (setq current-regexp (concat current-regexp
   2843                                                 lsp-globstar-regexp))
   2844                    (setq prev-segment-was-globstar t))
   2845                (let ((in-braces nil)
   2846                      (brace-val "")
   2847                      (in-brackets nil)
   2848                      (bracket-val ""))
   2849                  (dolist (char (string-to-list segment))
   2850                    (cond
   2851                     ((and (not (char-equal char ?\}))
   2852                           in-braces)
   2853                      (setq brace-val (concat brace-val
   2854                                              (char-to-string char))))
   2855                     ((and in-brackets
   2856                           (or (not (char-equal char ?\]))
   2857                               (string-empty-p bracket-val)))
   2858                      (let ((curr (cond
   2859                                   ((char-equal char ?-)
   2860                                    "-")
   2861                                   ;; NOTE: ?\^ and ?^ are different characters
   2862                                   ((and (memq char '(?^ ?!))
   2863                                         (string-empty-p bracket-val))
   2864                                    "^")
   2865                                   ((char-equal char lsp-glob-split)
   2866                                    "")
   2867                                   (t
   2868                                    (regexp-quote (char-to-string char))))))
   2869                        (setq bracket-val (concat bracket-val curr))))
   2870                     (t
   2871                      (cl-case char
   2872                        (?{
   2873                         (setq in-braces t))
   2874                        (?\[
   2875                         (setq in-brackets t))
   2876                        (?}
   2877                         (let* ((choices (lsp-split-glob-pattern brace-val ?\,))
   2878                                (brace-regexp (concat "\\(?:"
   2879                                                      (mapconcat #'lsp--glob-to-regexp choices "\\|")
   2880                                                      "\\)")))
   2881                           (setq current-regexp (concat current-regexp
   2882                                                        brace-regexp))
   2883                           (setq in-braces nil)
   2884                           (setq brace-val "")))
   2885                        (?\]
   2886                         (setq current-regexp
   2887                               (concat current-regexp
   2888                                       "[" bracket-val "]"))
   2889                         (setq in-brackets nil)
   2890                         (setq bracket-val ""))
   2891                        (??
   2892                         (setq current-regexp
   2893                               (concat current-regexp
   2894                                       lsp-non-path-regexp)))
   2895                        (?*
   2896                         (setq current-regexp
   2897                               (concat current-regexp
   2898                                       lsp-non-path-regexp "*?")))
   2899                        (t
   2900                         (setq current-regexp
   2901                               (concat current-regexp
   2902                                       (regexp-quote (char-to-string char)))))))))
   2903                  (when (and (< index (1- (length glob-segments)))
   2904                             (or (not (string-equal (nth (1+ index) glob-segments)
   2905                                                    lsp-globstar))
   2906                                 (< (+ index 2)
   2907                                    (length glob-segments))))
   2908                    (setq current-regexp
   2909                          (concat current-regexp
   2910                                  lsp-path-regexp)))
   2911                  (setq prev-segment-was-globstar nil))))
   2912            glob-segments)
   2913           current-regexp)))))
   2914 
   2915 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365
   2916 (defun lsp-glob-unbrace-at-top-level (glob-pattern)
   2917   "If GLOB-PATTERN does not start with a brace, return a singleton list
   2918 containing GLOB-PATTERN.
   2919 
   2920 If GLOB-PATTERN does start with a brace, return a list of the
   2921 comma-separated globs within the top-level braces."
   2922   (if (not (string-prefix-p "{" glob-pattern))
   2923       (list glob-pattern)
   2924     (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,)))
   2925 
   2926 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern)
   2927   "Convert GLOB-PATTERN to a regexp wrapped with the beginning-
   2928 and end-of-string meta-characters."
   2929   (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'"))
   2930 
   2931 (defun lsp-glob-to-regexps (glob-pattern)
   2932   "Convert a GLOB-PATTERN to a list of Elisp regexps."
   2933   (when-let*
   2934       ((glob-pattern (cond ((hash-table-p glob-pattern)
   2935                             (ht-get glob-pattern "pattern"))
   2936                            ((stringp glob-pattern) glob-pattern)
   2937                            (t (error "Unknown glob-pattern type: %s" glob-pattern))))
   2938        (trimmed-pattern (string-trim glob-pattern))
   2939        (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern)))
   2940     (seq-map #'lsp-glob-convert-to-wrapped-regexp
   2941              top-level-unbraced-patterns)))
   2942 
   2943 
   2944 
   2945 (defvar lsp-mode-menu)
   2946 
   2947 (defun lsp-mouse-click (event)
   2948   (interactive "e")
   2949   (let* ((ec (event-start event))
   2950          (choice (x-popup-menu event lsp-mode-menu))
   2951          (action (lookup-key lsp-mode-menu (apply 'vector choice))))
   2952 
   2953     (select-window (posn-window ec))
   2954 
   2955     (unless (and (region-active-p) (eq action 'lsp-execute-code-action))
   2956       (goto-char (posn-point ec)))
   2957     (run-with-idle-timer
   2958      0.001 nil
   2959      (lambda ()
   2960        (cl-labels ((check (value) (not (null value))))
   2961          (when choice
   2962            (call-interactively action)))))))
   2963 
   2964 (defvar lsp-mode-map
   2965   (let ((map (make-sparse-keymap)))
   2966     (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse)
   2967     (define-key map (kbd "C-<mouse-1>") #'ignore)
   2968     (define-key map (kbd "<mouse-3>") #'lsp-mouse-click)
   2969     (define-key map (kbd "C-S-SPC") #'lsp-signature-activate)
   2970     (when lsp-keymap-prefix
   2971       (define-key map (kbd lsp-keymap-prefix) lsp-command-map))
   2972     map)
   2973   "Keymap for `lsp-mode'.")
   2974 
   2975 (define-minor-mode lsp-mode "Mode for LSP interaction."
   2976   :keymap lsp-mode-map
   2977   :lighter
   2978   (" LSP["
   2979    (lsp--buffer-workspaces
   2980     (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "]["))
   2981     (:propertize "Disconnected" face warning))
   2982    "]")
   2983   :group 'lsp-mode
   2984   (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred))
   2985     ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp'
   2986     (lsp)))
   2987 
   2988 (defvar lsp-mode-menu
   2989   (easy-menu-create-menu
   2990    nil
   2991    `(["Go to definition" lsp-find-definition
   2992       :active (lsp-feature? "textDocument/definition")]
   2993      ["Find references" lsp-find-references
   2994       :active (lsp-feature? "textDocument/references")]
   2995      ["Find implementations" lsp-find-implementation
   2996       :active (lsp-feature? "textDocument/implementation")]
   2997      ["Find declarations" lsp-find-declaration
   2998       :active (lsp-feature? "textDocument/declaration")]
   2999      ["Go to type declaration" lsp-find-type-definition
   3000       :active (lsp-feature? "textDocument/typeDefinition")]
   3001      "--"
   3002      ["Describe" lsp-describe-thing-at-point]
   3003      ["Code action" lsp-execute-code-action]
   3004      ["Format" lsp-format-buffer]
   3005      ["Highlight references" lsp-document-highlight]
   3006      ["Type Hierarchy" lsp-java-type-hierarchy
   3007       :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")]
   3008      ["Type Hierarchy" lsp-treemacs-type-hierarchy
   3009       :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy"))
   3010                     (functionp 'lsp-treemacs-type-hierarchy)
   3011                     (lsp-feature? "textDocument/typeHierarchy"))]
   3012      ["Call Hierarchy" lsp-treemacs-call-hierarchy
   3013       :visible (and (functionp 'lsp-treemacs-call-hierarchy)
   3014                     (lsp-feature? "textDocument/callHierarchy"))]
   3015      ["Rename" lsp-rename
   3016       :active (lsp-feature? "textDocument/rename")]
   3017      "--"
   3018      ("Session"
   3019       ["View logs" lsp-workspace-show-log]
   3020       ["Describe" lsp-describe-session]
   3021       ["Shutdown" lsp-shutdown-workspace]
   3022       ["Restart" lsp-restart-workspace])
   3023      ("Workspace Folders"
   3024       ["Add" lsp-workspace-folders-add]
   3025       ["Remove" lsp-workspace-folders-remove]
   3026       ["Open" lsp-workspace-folders-open])
   3027      ("Toggle features"
   3028       ["Lenses" lsp-lens-mode]
   3029       ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode]
   3030       ["Modeline code actions" lsp-modeline-code-actions-mode]
   3031       ["Modeline diagnostics" lsp-modeline-diagnostics-mode])
   3032      "---"
   3033      ("Debug"
   3034       :active (bound-and-true-p dap-ui-mode)
   3035       :filter ,(lambda (_)
   3036                  (and (boundp 'dap-ui-menu-items)
   3037                       (nthcdr 3 dap-ui-menu-items))))))
   3038   "Menu for lsp-mode.")
   3039 
   3040 (defalias 'make-lsp-client 'make-lsp--client)
   3041 
   3042 (cl-defstruct lsp--registered-capability
   3043   (id "")
   3044   (method " ")
   3045   (options nil))
   3046 
   3047 ;; A ‘lsp--workspace’ object represents exactly one language server process.
   3048 (cl-defstruct lsp--workspace
   3049   ;; the `ewoc' object for displaying I/O to and from the server
   3050   (ewoc nil)
   3051 
   3052   ;; ‘server-capabilities’ is a hash table of the language server capabilities.
   3053   ;; It is the hash table representation of a LSP ServerCapabilities structure;
   3054   ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize.
   3055   (server-capabilities nil)
   3056 
   3057   ;; ‘registered-server-capabilities’ is a list of hash tables that represent
   3058   ;; dynamically-registered Registration objects.  See
   3059   ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability.
   3060   (registered-server-capabilities nil)
   3061 
   3062   ;; ‘root’ is a directory name or a directory file name for the workspace
   3063   ;; root.  ‘lsp-mode’ passes this directory to the ‘initialize’ method of the
   3064   ;; language server; see
   3065   ;; https://microsoft.github.io/language-server-protocol/specification#initialize.
   3066   (root nil)
   3067 
   3068   ;; ‘client’ is the ‘lsp--client’ object associated with this workspace.
   3069   (client nil)
   3070 
   3071   ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It
   3072   ;; used to derive the file path in `lsp--uri-to-path' when using tramp
   3073   ;; connection.
   3074   (host-root nil)
   3075 
   3076   ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or
   3077   ;; a network connection.  ‘lsp-mode’ communicates with ‘proc’ using the
   3078   ;; language server protocol.  ‘proc’ corresponds to the COMMUNICATION-PROCESS
   3079   ;; element of the return value of the client’s ‘get-root’ field, which see.
   3080   (proc nil)
   3081 
   3082   ;; ‘proc’ is a process object; it must represent a regular process, not a
   3083   ;; pipe or network process.  It represents the actual server process that
   3084   ;; corresponds to this workspace.  ‘cmd-proc’ corresponds to the
   3085   ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’
   3086   ;; field, which see.
   3087   (cmd-proc nil)
   3088 
   3089   ;; ‘buffers’ is a list of buffers associated with this workspace.
   3090   (buffers nil)
   3091 
   3092   ;; if semantic tokens is enabled, `semantic-tokens-faces' contains
   3093   ;; one face (or nil) for each token type supported by the language server.
   3094   (semantic-tokens-faces nil)
   3095 
   3096   ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces'
   3097   ;; contains one face (or nil) for each modifier type supported by the language
   3098   ;; server
   3099   (semantic-tokens-modifier-faces nil)
   3100 
   3101   ;; Extra client capabilities provided by third-party packages using
   3102   ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME
   3103   ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name,
   3104   ;; and CAPS is either a plist of the client capabilities, or a function that
   3105   ;; takes no argument and returns a plist of the client capabilities or nil.
   3106   (extra-client-capabilities nil)
   3107 
   3108   ;; Workspace status
   3109   (status nil)
   3110 
   3111   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3112   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3113   (metadata (make-hash-table :test 'equal))
   3114 
   3115   ;; contains all the file notification watches that have been created for the
   3116   ;; current workspace in format filePath->file notification handle.
   3117   (watches (make-hash-table :test 'equal))
   3118 
   3119   ;; list of workspace folders
   3120   (workspace-folders nil)
   3121 
   3122   ;; ‘last-id’ the last request id for the current workspace.
   3123   (last-id 0)
   3124 
   3125   ;; ‘status-string’ allows extensions to specify custom status string based on
   3126   ;; the Language Server specific messages.
   3127   (status-string nil)
   3128 
   3129   ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it
   3130   ;; was stopped).
   3131   shutdown-action
   3132 
   3133   ;; ‘diagnostics’ a hashmap with workspace diagnostics.
   3134   (diagnostics (make-hash-table :test 'equal))
   3135 
   3136   ;; contains all the workDone progress tokens that have been created
   3137   ;; for the current workspace.
   3138   (work-done-tokens (make-hash-table :test 'equal)))
   3139 
   3140 
   3141 (cl-defstruct lsp-session
   3142   ;; contains the folders that are part of the current session
   3143   folders
   3144   ;; contains the folders that must not be imported in the current workspace.
   3145   folders-blocklist
   3146   ;; contains the list of folders that must be imported in a project in case of
   3147   ;; multi root LSP server.
   3148   (server-id->folders (make-hash-table :test 'equal))
   3149   ;; folder to list of the servers that are associated with the folder.
   3150   (folder->servers (make-hash-table :test 'equal))
   3151   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3152   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3153   (metadata (make-hash-table :test 'equal)))
   3154 
   3155 (defun lsp-workspace-status (status-string &optional workspace)
   3156   "Set current workspace status to STATUS-STRING.
   3157 If WORKSPACE is not specified defaults to lsp--cur-workspace."
   3158   (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string))))
   3159     (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string)))
   3160 
   3161 (defun lsp-session-set-metadata (key value &optional _workspace)
   3162   "Associate KEY with VALUE in the WORKSPACE metadata.
   3163 If WORKSPACE is not provided current workspace will be used."
   3164   (puthash key value (lsp-session-metadata (lsp-session))))
   3165 
   3166 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata)
   3167 
   3168 (defun lsp-session-get-metadata (key &optional _workspace)
   3169   "Lookup KEY in WORKSPACE metadata.
   3170 If WORKSPACE is not provided current workspace will be used."
   3171   (gethash key (lsp-session-metadata (lsp-session))))
   3172 
   3173 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata)
   3174 
   3175 (defun lsp-workspace-set-work-done-token (token value workspace)
   3176   "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens."
   3177   (puthash token value (lsp--workspace-work-done-tokens workspace)))
   3178 
   3179 (defun lsp-workspace-get-work-done-token (token workspace)
   3180   "Lookup TOKEN in the WORKSPACE work-done-tokens."
   3181   (gethash token (lsp--workspace-work-done-tokens workspace)))
   3182 
   3183 (defun lsp-workspace-rem-work-done-token (token workspace)
   3184   "Remove TOKEN from the WORKSPACE work-done-tokens."
   3185   (remhash token (lsp--workspace-work-done-tokens workspace)))
   3186 
   3187 
   3188 (defun lsp--make-notification (method &optional params)
   3189   "Create notification body for method METHOD and parameters PARAMS."
   3190   (list :jsonrpc "2.0" :method method :params params))
   3191 
   3192 (defalias 'lsp--make-request 'lsp--make-notification)
   3193 (defalias 'lsp-make-request 'lsp--make-notification)
   3194 
   3195 (defun lsp--make-response (id result)
   3196   "Create response for REQUEST with RESULT."
   3197   `(:jsonrpc "2.0" :id ,id :result ,result))
   3198 
   3199 (defun lsp-make-notification (method &optional params)
   3200   "Create notification body for method METHOD and parameters PARAMS."
   3201   (lsp--make-notification method params))
   3202 
   3203 (defmacro lsp--json-serialize (params)
   3204   (if (progn
   3205         (require 'json)
   3206         (fboundp 'json-serialize))
   3207       `(json-serialize ,params
   3208                        :null-object nil
   3209                        :false-object :json-false)
   3210     `(let ((json-false :json-false))
   3211        (json-encode ,params))))
   3212 
   3213 (defun lsp--make-message (params)
   3214   "Create a LSP message from PARAMS, after encoding it to a JSON string."
   3215   (let ((body (lsp--json-serialize params)))
   3216     (concat "Content-Length: "
   3217             (number-to-string (1+ (string-bytes body)))
   3218             "\r\n\r\n"
   3219             body
   3220             "\n")))
   3221 
   3222 (cl-defstruct lsp--log-entry timestamp process-time type method id body)
   3223 
   3224 (defun lsp--make-log-entry (method id body type &optional process-time)
   3225   "Create an outgoing log object from BODY with method METHOD and id ID.
   3226 If ID is non-nil, then the body is assumed to be a notification.
   3227 TYPE can either be `incoming' or `outgoing'"
   3228   (cl-assert (memq type '(incoming-req outgoing-req incoming-notif
   3229                                        outgoing-notif incoming-resp
   3230                                        outgoing-resp)))
   3231   (make-lsp--log-entry
   3232    :timestamp (format-time-string "%I:%M:%S %p")
   3233    :process-time process-time
   3234    :method method
   3235    :id id
   3236    :type type
   3237    :body body))
   3238 
   3239 (defun lsp--log-font-lock-json (body)
   3240   "Font lock JSON BODY."
   3241   (with-temp-buffer
   3242     (insert body)
   3243     ;; We set the temp buffer file-name extension to .json and call `set-auto-mode'
   3244     ;; so the users configured json mode is used which could be
   3245     ;; `json-mode', `json-ts-mode', `jsonian-mode', etc.
   3246     (let ((buffer-file-name "lsp-log.json"))
   3247       (delay-mode-hooks
   3248         (set-auto-mode)
   3249         (if (fboundp 'font-lock-ensure)
   3250             (font-lock-ensure)
   3251           (with-no-warnings
   3252             (font-lock-fontify-buffer)))))
   3253     (buffer-string)))
   3254 
   3255 (defun lsp--log-entry-pp (entry)
   3256   (cl-assert (lsp--log-entry-p entry))
   3257   (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time
   3258                           body)
   3259                entry)
   3260               (json-false :json-false)
   3261               (json-encoding-pretty-print t)
   3262               (str nil))
   3263     (setq str
   3264           (concat (format "[Trace - %s] " timestamp)
   3265                   (pcase type
   3266                     ('incoming-req (format "Received request '%s - (%s)." method id))
   3267                     ('outgoing-req (format "Sending request '%s - (%s)'." method id))
   3268 
   3269                     ('incoming-notif (format "Received notification '%s'." method))
   3270                     ('outgoing-notif (format "Sending notification '%s'." method))
   3271 
   3272                     ('incoming-resp (format "Received response '%s - (%s)' in %dms."
   3273                                             method id process-time))
   3274                     ('outgoing-resp
   3275                      (format
   3276                       "Sending response '%s - (%s)'. Processing request took %dms"
   3277                       method id process-time)))
   3278                   "\n"
   3279                   (if (memq type '(incoming-resp ougoing-resp))
   3280                       "Result: "
   3281                     "Params: ")
   3282                   (lsp--log-font-lock-json (json-encode body))
   3283                   "\n\n\n"))
   3284     (setq str (propertize str 'mouse-face 'highlight 'read-only t))
   3285     (insert str)))
   3286 
   3287 (defvar-local lsp--log-io-ewoc nil)
   3288 
   3289 (defun lsp--get-create-io-ewoc (workspace)
   3290   (if (and (lsp--workspace-ewoc workspace)
   3291            (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace))))
   3292       (lsp--workspace-ewoc workspace)
   3293     (with-current-buffer (lsp--get-log-buffer-create workspace)
   3294       (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode))
   3295       (setq-local window-point-insertion-type t)
   3296       (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t))
   3297       (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc))
   3298     (lsp--workspace-ewoc workspace)))
   3299 
   3300 (defun lsp--ewoc-count (ewoc)
   3301   (let* ((count 0)
   3302          (count-fn (lambda (_) (setq count (1+ count)))))
   3303     (ewoc-map count-fn ewoc)
   3304     count))
   3305 
   3306 (defun lsp--log-entry-new (entry workspace)
   3307   (let* ((ewoc (lsp--get-create-io-ewoc workspace))
   3308          (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc)))
   3309          (node (if (or (eq lsp-io-messages-max t)
   3310                        (>= lsp-io-messages-max count))
   3311                    nil
   3312                  (ewoc-nth ewoc (1- lsp-io-messages-max))))
   3313          (prev nil)
   3314          (inhibit-read-only t))
   3315     (while node
   3316       (setq prev (ewoc-prev ewoc node))
   3317       (ewoc-delete ewoc node)
   3318       (setq node prev))
   3319     (ewoc-enter-last ewoc entry)))
   3320 
   3321 (defun lsp--send-notification (body)
   3322   "Send BODY as a notification to the language server."
   3323   (lsp-foreach-workspace
   3324    (when (lsp--log-io-p (plist-get body :method))
   3325      (lsp--log-entry-new (lsp--make-log-entry
   3326                           (plist-get body :method)
   3327                           nil (plist-get body :params) 'outgoing-notif)
   3328                          lsp--cur-workspace))
   3329    (lsp--send-no-wait body
   3330                       (lsp--workspace-proc lsp--cur-workspace))))
   3331 
   3332 (defalias 'lsp-send-notification 'lsp--send-notification)
   3333 
   3334 (defun lsp-notify (method params)
   3335   "Send notification METHOD with PARAMS."
   3336   (lsp--send-notification (lsp--make-notification method params)))
   3337 
   3338 (defun lsp--cur-workspace-check ()
   3339   "Check whether buffer lsp workspace(s) are set."
   3340   (cl-assert (lsp-workspaces) nil
   3341              "No language server(s) is associated with this buffer."))
   3342 
   3343 (defun lsp--send-request (body &optional no-wait no-merge)
   3344   "Send BODY as a request to the language server, get the response.
   3345 If NO-WAIT is non-nil, don't synchronously wait for a response.
   3346 If NO-MERGE is non-nil, don't merge the results but return an
   3347 alist mapping workspace->result."
   3348   (lsp-request (plist-get body :method)
   3349                (plist-get body :params)
   3350                :no-wait no-wait
   3351                :no-merge no-merge))
   3352 
   3353 (defalias 'lsp-send-request 'lsp--send-request
   3354   "Send BODY as a request to the language server and return the response
   3355 synchronously.
   3356 \n(fn BODY)")
   3357 
   3358 (cl-defun lsp-request (method params &key no-wait no-merge)
   3359   "Send request METHOD with PARAMS.
   3360 If NO-MERGE is non-nil, don't merge the results but return alist
   3361 workspace->result.
   3362 If NO-WAIT is non-nil send the request as notification."
   3363   (if no-wait
   3364       (lsp-notify method params)
   3365     (let* ((send-time (float-time))
   3366            ;; max time by which we must get a response
   3367            (expected-time
   3368             (and
   3369              lsp-response-timeout
   3370              (+ send-time lsp-response-timeout)))
   3371            resp-result resp-error done?)
   3372       (unwind-protect
   3373           (progn
   3374             (lsp-request-async method params
   3375                                (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3376                                :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3377                                :no-merge no-merge
   3378                                :mode 'detached
   3379                                :cancel-token :sync-request)
   3380             (while (not (or resp-error resp-result))
   3381               (if (functionp 'json-rpc-connection)
   3382                   (catch 'lsp-done (sit-for 0.01))
   3383                 (catch 'lsp-done
   3384                   (accept-process-output
   3385                    nil
   3386                    (if expected-time (- expected-time send-time) 1))))
   3387               (setq send-time (float-time))
   3388               (when (and expected-time (< expected-time send-time))
   3389                 (error "Timeout while waiting for response.  Method: %s" method)))
   3390             (setq done? t)
   3391             (cond
   3392              ((eq resp-result :finished) nil)
   3393              (resp-result resp-result)
   3394              ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3395              ((lsp-json-error? (cl-first resp-error))
   3396               (error (lsp:json-error-message (cl-first resp-error))))))
   3397         (unless done?
   3398           (lsp-cancel-request-by-token :sync-request))))))
   3399 
   3400 (cl-defun lsp-request-while-no-input (method params)
   3401   "Send request METHOD with PARAMS and waits until there is no input.
   3402 Return same value as `lsp--while-no-input' and respecting `non-essential'."
   3403   (if (or non-essential (not lsp-request-while-no-input-may-block))
   3404       (let* ((send-time (float-time))
   3405              ;; max time by which we must get a response
   3406              (expected-time
   3407               (and
   3408                lsp-response-timeout
   3409                (+ send-time lsp-response-timeout)))
   3410              resp-result resp-error done?)
   3411         (unwind-protect
   3412             (progn
   3413               (lsp-request-async method params
   3414                                  (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3415                                  :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3416                                  :mode 'detached
   3417                                  :cancel-token :sync-request)
   3418               (while (not (or resp-error resp-result (input-pending-p)))
   3419                 (catch 'lsp-done
   3420                   (sit-for
   3421                    (if expected-time (- expected-time send-time) 1)))
   3422                 (setq send-time (float-time))
   3423                 (when (and expected-time (< expected-time send-time))
   3424                   (error "Timeout while waiting for response.  Method: %s" method)))
   3425               (setq done? (or resp-error resp-result))
   3426               (cond
   3427                ((eq resp-result :finished) nil)
   3428                (resp-result resp-result)
   3429                ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3430                ((lsp-json-error? (cl-first resp-error))
   3431                 (error (lsp:json-error-message (cl-first resp-error))))))
   3432           (unless done?
   3433             (lsp-cancel-request-by-token :sync-request))
   3434           (when (and (input-pending-p) lsp--throw-on-input)
   3435             (throw 'input :interrupted))))
   3436     (lsp-request method params)))
   3437 
   3438 (defvar lsp--cancelable-requests (ht))
   3439 
   3440 (cl-defun lsp-request-async (method params callback
   3441                                     &key mode error-handler cancel-handler no-merge cancel-token)
   3442   "Send METHOD with PARAMS as a request to the language server.
   3443 Call CALLBACK with the response received from the server
   3444 asynchronously.
   3445 MODE determines when the callback will be called depending on the
   3446 condition of the original buffer.  It could be:
   3447 - `detached' which means that the callback will be executed no
   3448 matter what has happened to the buffer.
   3449 - `alive' - the callback will be executed only if the buffer from
   3450 which the call was executed is still alive.
   3451 - `current' the callback will be executed only if the original buffer
   3452 is still selected.
   3453 - `tick' - the callback will be executed only if the buffer was not modified.
   3454 - `unchanged' - the callback will be executed only if the buffer hasn't
   3455 changed and if the buffer is not modified.
   3456 
   3457 ERROR-HANDLER will be called in case the request has failed.
   3458 CANCEL-HANDLER will be called in case the request is being canceled.
   3459 If NO-MERGE is non-nil, don't merge the results but return alist
   3460 workspace->result.
   3461 CANCEL-TOKEN is the token that can be used to cancel request."
   3462   (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params)
   3463                            callback mode error-handler cancel-handler no-merge cancel-token))
   3464 
   3465 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback)
   3466   (lambda (&rest _)
   3467     (unless (and (equal 'post-command-hook hook)
   3468                  (equal (current-buffer) buf))
   3469       (lsp--request-cleanup-hooks id)
   3470       (with-lsp-workspaces workspaces
   3471         (lsp--cancel-request id)
   3472         (when cancel-callback (funcall cancel-callback)))
   3473       (lsp-log "Cancelling %s(%s) in hook %s" method id hook))))
   3474 
   3475 (defun lsp--create-async-callback
   3476     (callback method no-merge workspaces)
   3477   "Create async handler expecting COUNT results, merge them and call CALLBACK.
   3478 MODE determines when the callback will be called depending on the
   3479 condition of the original buffer. METHOD is the invoked method.
   3480 If NO-MERGE is non-nil, don't merge the results but return alist
   3481 workspace->result. ID is the request id."
   3482   (let (results errors)
   3483     (lambda (result)
   3484       (push (cons lsp--cur-workspace result)
   3485             (if (eq result :error) errors results))
   3486       (when (and (not (eq (length errors) (length workspaces)))
   3487                  (eq (+ (length errors) (length results)) (length workspaces)))
   3488         (funcall callback
   3489                  (if no-merge
   3490                      results
   3491                    (lsp--merge-results (-map #'cl-rest results) method)))))))
   3492 
   3493 (defcustom lsp-default-create-error-handler-fn nil
   3494   "Default error handler customization.
   3495 Handler should give METHOD as argument and return function of one argument
   3496 ERROR."
   3497   :type 'function
   3498   :group 'lsp-mode
   3499   :package-version '(lsp-mode . "9.0.0"))
   3500 
   3501 (defun lsp--create-default-error-handler (method)
   3502   "Default error handler.
   3503 METHOD is the executed method."
   3504   (if lsp-default-create-error-handler-fn
   3505       (funcall lsp-default-create-error-handler-fn method)
   3506     (lambda (error)
   3507       (lsp--warn "%s" (or (lsp--error-string error)
   3508                           (format "%s Request has failed" method))))))
   3509 
   3510 (defvar lsp--request-cleanup-hooks (ht))
   3511 
   3512 (defun lsp--request-cleanup-hooks (request-id)
   3513   (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks)))
   3514     (funcall cleanup-function)
   3515     (remhash request-id lsp--request-cleanup-hooks)))
   3516 
   3517 (defun lsp-cancel-request-by-token (cancel-token)
   3518   "Cancel request using CANCEL-TOKEN."
   3519   (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests))
   3520     (with-lsp-workspaces workspaces
   3521       (lsp--cancel-request request-id))
   3522     (remhash cancel-token lsp--cancelable-requests)
   3523     (lsp--request-cleanup-hooks request-id)))
   3524 
   3525 (defun lsp--send-request-async (body callback
   3526                                      &optional mode error-callback cancel-callback
   3527                                      no-merge cancel-token)
   3528   "Send BODY as a request to the language server.
   3529 Call CALLBACK with the response received from the server
   3530 asynchronously.
   3531 MODE determines when the callback will be called depending on the
   3532 condition of the original buffer.  It could be:
   3533 - `detached' which means that the callback will be executed no
   3534 matter what has happened to the buffer.
   3535 - `alive' - the callback will be executed only if the buffer from
   3536 which the call was executed is still alive.
   3537 - `current' the callback will be executed only if the original buffer
   3538 is still selected.
   3539 - `tick' - the callback will be executed only if the buffer was not modified.
   3540 - `unchanged' - the callback will be executed only if the buffer hasn't
   3541 changed and if the buffer is not modified.
   3542 
   3543 ERROR-CALLBACK will be called in case the request has failed.
   3544 CANCEL-CALLBACK will be called in case the request is being canceled.
   3545 If NO-MERGE is non-nil, don't merge the results but return alist
   3546 workspace->result.
   3547 CANCEL-TOKEN is the token that can be used to cancel request."
   3548   (when cancel-token
   3549     (lsp-cancel-request-by-token cancel-token))
   3550 
   3551   (if-let ((target-workspaces (lsp--find-workspaces-for body)))
   3552       (let* ((start-time (current-time))
   3553              (method (plist-get body :method))
   3554              (id (cl-incf lsp-last-id))
   3555              (buf (current-buffer))
   3556              (cancel-callback (when cancel-callback
   3557                                 (pcase mode
   3558                                   ((or 'alive 'tick 'unchanged)
   3559                                    (lambda ()
   3560                                      (with-current-buffer buf
   3561                                        (funcall cancel-callback))))
   3562                                   (_ cancel-callback))))
   3563              ;; calculate what are the (hook . local) pairs which will cancel
   3564              ;; the request
   3565              (hooks (pcase mode
   3566                       ('alive     '((kill-buffer-hook . t)))
   3567                       ('tick      '((kill-buffer-hook . t) (after-change-functions . t)))
   3568                       ('unchanged '((after-change-functions . t) (post-command-hook . nil)))
   3569                       ('current   '((post-command-hook . nil)))))
   3570              ;; note: lambdas in emacs can be compared but we should make sure
   3571              ;; that all of the captured arguments are the same - in our case
   3572              ;; `lsp--create-request-cancel' will return the same lambda when
   3573              ;; called with the same params.
   3574              (cleanup-hooks
   3575               (lambda () (mapc
   3576                           (-lambda ((hook . local))
   3577                             (if local
   3578                                 (when (buffer-live-p buf)
   3579                                   (with-current-buffer buf
   3580                                     (remove-hook hook
   3581                                                  (lsp--create-request-cancel
   3582                                                   id target-workspaces hook buf method cancel-callback)
   3583                                                  t)))
   3584                               (remove-hook hook (lsp--create-request-cancel
   3585                                                  id target-workspaces hook buf method cancel-callback))))
   3586                           hooks)
   3587                 (remhash cancel-token lsp--cancelable-requests)))
   3588              (callback (pcase mode
   3589                          ((or 'alive 'tick 'unchanged) (lambda (&rest args)
   3590                                                          (with-current-buffer buf
   3591                                                            (apply callback args))))
   3592                          (_ callback)))
   3593              (callback (lsp--create-async-callback callback
   3594                                                    method
   3595                                                    no-merge
   3596                                                    target-workspaces))
   3597              (callback (lambda (result)
   3598                          (lsp--request-cleanup-hooks id)
   3599                          (funcall callback result)))
   3600              (error-callback (lsp--create-async-callback
   3601                               (or error-callback
   3602                                   (lsp--create-default-error-handler method))
   3603                               method
   3604                               nil
   3605                               target-workspaces))
   3606              (error-callback (lambda (error)
   3607                                (funcall callback :error)
   3608                                (lsp--request-cleanup-hooks id)
   3609                                (funcall error-callback error)))
   3610              (body (plist-put body :id id)))
   3611 
   3612         ;; cancel request in any of the hooks
   3613         (mapc (-lambda ((hook . local))
   3614                 (add-hook hook
   3615                           (lsp--create-request-cancel
   3616                            id target-workspaces hook buf method cancel-callback)
   3617                           nil local))
   3618               hooks)
   3619         (puthash id cleanup-hooks lsp--request-cleanup-hooks)
   3620 
   3621         (setq lsp--last-active-workspaces target-workspaces)
   3622 
   3623         (when cancel-token
   3624           (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests))
   3625 
   3626         (seq-doseq (workspace target-workspaces)
   3627           (when (lsp--log-io-p method)
   3628             (lsp--log-entry-new (lsp--make-log-entry method id
   3629                                                      (plist-get body :params)
   3630                                                      'outgoing-req)
   3631                                 workspace))
   3632           (puthash id
   3633                    (list callback error-callback method start-time (current-time))
   3634                    (-> workspace
   3635                        (lsp--workspace-client)
   3636                        (lsp--client-response-handlers)))
   3637           (lsp--send-no-wait body (lsp--workspace-proc workspace)))
   3638         body)
   3639     (error "The connected server(s) does not support method %s.
   3640 To find out what capabilities support your server use `M-x lsp-describe-session'
   3641 and expand the capabilities section"
   3642            (plist-get body :method))))
   3643 
   3644 ;; deprecated, use lsp-request-async.
   3645 (defalias 'lsp-send-request-async 'lsp--send-request-async)
   3646 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1")
   3647 
   3648 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any
   3649 ;; pending language servers.
   3650 (add-hook 'kill-emacs-hook #'lsp--global-teardown)
   3651 
   3652 (defun lsp--global-teardown ()
   3653   "Unload working workspaces."
   3654   (lsp-foreach-workspace (lsp--shutdown-workspace)))
   3655 
   3656 (defun lsp--shutdown-workspace (&optional restart)
   3657   "Shut down the language server process for ‘lsp--cur-workspace’."
   3658   (with-demoted-errors "LSP error: %S"
   3659     (let ((lsp-response-timeout 0.5))
   3660       (condition-case err
   3661           (lsp-request "shutdown" nil)
   3662         (error (lsp--error "%s" err))))
   3663     (lsp-notify "exit" nil))
   3664   (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown))
   3665   (lsp--uninitialize-workspace))
   3666 
   3667 (defcustom lsp-inlay-hint-enable nil
   3668   "If non-nil it will enable inlay hints."
   3669   :type 'boolean
   3670   :group 'lsp-mode
   3671   :package-version '(lsp-mode . "9.0.0"))
   3672 
   3673 (defun lsp--uninitialize-workspace ()
   3674   "Cleanup buffer state.
   3675 When a workspace is shut down, by request or from just
   3676 disappearing, unset all the variables related to it."
   3677   (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace]
   3678     (lsp-process-kill cmd-proc)
   3679     (mapc (lambda (buf)
   3680             (when (lsp-buffer-live-p buf)
   3681               (lsp-with-current-buffer buf
   3682                                        (lsp-managed-mode -1))))
   3683           buffers)
   3684     (lsp-diagnostics--workspace-cleanup lsp--cur-workspace)))
   3685 
   3686 (defun lsp--client-capabilities (&optional custom-capabilities)
   3687   "Return the client capabilities appending CUSTOM-CAPABILITIES."
   3688   (append
   3689    `((general . ((positionEncodings . ["utf-32", "utf-16"])))
   3690      (workspace . ((workspaceEdit . ((documentChanges . t)
   3691                                      (resourceOperations . ["create" "rename" "delete"])))
   3692                    (applyEdit . t)
   3693                    (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))))
   3694                    (executeCommand . ((dynamicRegistration . :json-false)))
   3695                    ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t)))))
   3696                    (workspaceFolders . t)
   3697                    (configuration . t)
   3698                    ,@(when lsp-semantic-tokens-enable
   3699                        `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests)
   3700                                                                         lsp-semantic-tokens-honor-refresh-requests)
   3701                                                                    :json-false))))))
   3702                    ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t)))))
   3703                    ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false)))))
   3704                    (fileOperations . ((didCreate . :json-false)
   3705                                       (willCreate . :json-false)
   3706                                       (didRename . t)
   3707                                       (willRename . t)
   3708                                       (didDelete . :json-false)
   3709                                       (willDelete . :json-false)))))
   3710      (textDocument . ((declaration . ((dynamicRegistration . t)
   3711                                       (linkSupport . t)))
   3712                       (definition . ((dynamicRegistration . t)
   3713                                      (linkSupport . t)))
   3714                       (references . ((dynamicRegistration . t)))
   3715                       (implementation . ((dynamicRegistration . t)
   3716                                          (linkSupport . t)))
   3717                       (typeDefinition . ((dynamicRegistration . t)
   3718                                          (linkSupport . t)))
   3719                       (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t)))
   3720                       (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))
   3721                                          (hierarchicalDocumentSymbolSupport . t)))
   3722                       (formatting . ((dynamicRegistration . t)))
   3723                       (rangeFormatting . ((dynamicRegistration . t)))
   3724                       (onTypeFormatting . ((dynamicRegistration . t)))
   3725                       ,@(when (and lsp-semantic-tokens-enable
   3726                                    (functionp 'lsp--semantic-tokens-capabilities))
   3727                           (lsp--semantic-tokens-capabilities))
   3728                       (rename . ((dynamicRegistration . t) (prepareSupport . t)))
   3729                       (codeAction . ((dynamicRegistration . t)
   3730                                      (isPreferredSupport . t)
   3731                                      (codeActionLiteralSupport . ((codeActionKind . ((valueSet . [""
   3732                                                                                                   "quickfix"
   3733                                                                                                   "refactor"
   3734                                                                                                   "refactor.extract"
   3735                                                                                                   "refactor.inline"
   3736                                                                                                   "refactor.rewrite"
   3737                                                                                                   "source"
   3738                                                                                                   "source.organizeImports"])))))
   3739                                      (resolveSupport . ((properties . ["edit" "command"])))
   3740                                      (dataSupport . t)))
   3741                       (completion . ((completionItem . ((snippetSupport . ,(cond
   3742                                                                             ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode)))
   3743                                                                              (lsp--warn (concat
   3744                                                                                          "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. "
   3745                                                                                          "You must either install yasnippet, or disable snippet support."))
   3746                                                                              :json-false)
   3747                                                                             (lsp-enable-snippet t)
   3748                                                                             (t :json-false)))
   3749                                                         (documentationFormat . ["markdown" "plaintext"])
   3750                                                         ;; Remove this after jdtls support resolveSupport
   3751                                                         (resolveAdditionalTextEditsSupport . t)
   3752                                                         (insertReplaceSupport . t)
   3753                                                         (deprecatedSupport . t)
   3754                                                         (resolveSupport
   3755                                                          . ((properties . ["documentation"
   3756                                                                            "detail"
   3757                                                                            "additionalTextEdits"
   3758                                                                            "command"])))
   3759                                                         (insertTextModeSupport . ((valueSet . [1 2])))))
   3760                                      (contextSupport . t)
   3761                                      (dynamicRegistration . t)))
   3762                       (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t)))))
   3763                                         (dynamicRegistration . t)))
   3764                       (documentLink . ((dynamicRegistration . t)
   3765                                        (tooltipSupport . t)))
   3766                       (hover . ((contentFormat . ["markdown" "plaintext"])
   3767                                 (dynamicRegistration . t)))
   3768                       ,@(when lsp-enable-folding
   3769                           `((foldingRange . ((dynamicRegistration . t)
   3770                                              ,@(when lsp-folding-range-limit
   3771                                                  `((rangeLimit . ,lsp-folding-range-limit)))
   3772                                              ,@(when lsp-folding-line-folding-only
   3773                                                  `((lineFoldingOnly . t)))))))
   3774                       (selectionRange . ((dynamicRegistration . t)))
   3775                       (callHierarchy . ((dynamicRegistration . :json-false)))
   3776                       (typeHierarchy . ((dynamicRegistration . t)))
   3777                       (publishDiagnostics . ((relatedInformation . t)
   3778                                              (tagSupport . ((valueSet . [1 2])))
   3779                                              (versionSupport . t)))
   3780                       (diagnostic . ((dynamicRegistration . :json-false)
   3781                                      (relatedDocumentSupport . :json-false)))
   3782                       (linkedEditingRange . ((dynamicRegistration . t)))))
   3783      (window . ((workDoneProgress . t)
   3784                 (showDocument . ((support . t))))))
   3785    custom-capabilities))
   3786 
   3787 (defun lsp-find-roots-for-workspace (workspace session)
   3788   "Get all roots for the WORKSPACE."
   3789   (-filter #'identity (ht-map (lambda (folder workspaces)
   3790                                 (when (-contains? workspaces workspace)
   3791                                   folder))
   3792                               (lsp-session-folder->servers session))))
   3793 
   3794 (defun lsp-session-watches (&optional session)
   3795   "Get watches created for SESSION."
   3796   (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session))))
   3797       (-let [res (make-hash-table :test 'equal)]
   3798         (puthash "__watches" res (lsp-session-metadata (or session (lsp-session))))
   3799         res)))
   3800 
   3801 (defun lsp--file-process-event (session root-folder event)
   3802   "Process file event."
   3803   (let* ((changed-file (cl-third event))
   3804          (rel-changed-file (f-relative changed-file root-folder))
   3805          (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type))
   3806          (bit-position (1- event-numeric-kind))
   3807          (watch-bit (ash 1 bit-position)))
   3808     (->>
   3809      session
   3810      lsp-session-folder->servers
   3811      (gethash root-folder)
   3812      (seq-do (lambda (workspace)
   3813                (when (->>
   3814                       workspace
   3815                       lsp--workspace-registered-server-capabilities
   3816                       (-any?
   3817                        (lambda (capability)
   3818                          (and
   3819                           (equal (lsp--registered-capability-method capability)
   3820                                  "workspace/didChangeWatchedFiles")
   3821                           (->>
   3822                            capability
   3823                            lsp--registered-capability-options
   3824                            (lsp:did-change-watched-files-registration-options-watchers)
   3825                            (seq-find
   3826                             (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp))
   3827                               (when (or (null kind?)
   3828                                         (> (logand kind? watch-bit) 0))
   3829                                 (-let [regexes (or cached-regexp
   3830                                                    (let ((regexp (lsp-glob-to-regexps glob-pattern)))
   3831                                                      (lsp-put fs-watcher :_cachedRegexp regexp)
   3832                                                      regexp))]
   3833                                   (-any? (lambda (re)
   3834                                            (or (string-match re changed-file)
   3835                                                (string-match re rel-changed-file)))
   3836                                          regexes))))))))))
   3837                  (with-lsp-workspace workspace
   3838                    (lsp-notify
   3839                     "workspace/didChangeWatchedFiles"
   3840                     `((changes . [((type . ,event-numeric-kind)
   3841                                    (uri . ,(lsp--path-to-uri changed-file)))]))))))))))
   3842 
   3843 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?))
   3844   "Register capability REG."
   3845   (when (and lsp-enable-file-watchers
   3846              (equal method "workspace/didChangeWatchedFiles"))
   3847     (-let* ((created-watches (lsp-session-watches (lsp-session)))
   3848             (root-folders (cl-set-difference
   3849                            (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session))
   3850                            (ht-keys created-watches))))
   3851       ;; create watch for each root folder without such
   3852       (dolist (folder root-folders)
   3853         (let* ((watch (make-lsp-watch :root-directory folder))
   3854                (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder))
   3855                (ignored-files-regex-list (car ignored-things))
   3856                (ignored-directories-regex-list (cadr ignored-things)))
   3857           (puthash folder watch created-watches)
   3858           (lsp-watch-root-folder (file-truename folder)
   3859                                  (-partial #'lsp--file-process-event (lsp-session) folder)
   3860                                  ignored-files-regex-list
   3861                                  ignored-directories-regex-list
   3862                                  watch
   3863                                  t)))))
   3864 
   3865   (push
   3866    (make-lsp--registered-capability :id id :method method :options register-options?)
   3867    (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3868 
   3869 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body)
   3870   "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to
   3871 access dir-local variables."
   3872   (declare (indent 1) (debug t))
   3873   `(with-temp-buffer
   3874      ;; Set the buffer's name to something under the root so that we can hack the local variables
   3875      ;; This file doesn't need to exist and will not be created due to this.
   3876      (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root)))
   3877      (hack-local-variables)
   3878      (prog1 ,@body
   3879        (setq-local buffer-file-name nil))))
   3880 
   3881 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root)
   3882   "Return a list of the form
   3883 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given
   3884 WORKSPACE-ROOT."
   3885   ;; The intent of this function is to provide per-root workspace-level customization of the
   3886   ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables.
   3887   (lsp--with-workspace-temp-buffer workspace-root
   3888     (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories))))
   3889 
   3890 
   3891 (defun lsp--cleanup-hanging-watches ()
   3892   "Cleanup watches in case there are no more workspaces that are interested
   3893 in that particular folder."
   3894   (let* ((session (lsp-session))
   3895          (watches (lsp-session-watches session)))
   3896     (dolist (watched-folder (ht-keys watches))
   3897       (when (-none? (lambda (workspace)
   3898                       (with-lsp-workspace workspace
   3899                         (lsp--registered-capability "workspace/didChangeWatchedFiles")))
   3900                     (gethash watched-folder (lsp-session-folder->servers (lsp-session))))
   3901         (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder)
   3902         (lsp-kill-watch (gethash watched-folder watches))
   3903         (remhash watched-folder watches)))))
   3904 
   3905 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method))
   3906   "Unregister capability UNREG."
   3907   (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace)
   3908         (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id))
   3909                     (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3910   (when (equal method "workspace/didChangeWatchedFiles")
   3911     (lsp--cleanup-hanging-watches)))
   3912 
   3913 (defun lsp--server-capabilities ()
   3914   "Return the capabilities of the language server associated with the buffer."
   3915   (->> (lsp-workspaces)
   3916        (-keep #'lsp--workspace-server-capabilities)
   3917        (apply #'lsp-merge)))
   3918 
   3919 (defun lsp--send-open-close-p ()
   3920   "Return whether open and close notifications should be sent to the server."
   3921   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3922     (or (memq sync '(1 2))
   3923         (lsp:text-document-sync-options-open-close? sync))))
   3924 
   3925 (defun lsp--send-will-save-p ()
   3926   "Return whether willSave notifications should be sent to the server."
   3927   (-> (lsp--server-capabilities)
   3928       (lsp:server-capabilities-text-document-sync?)
   3929       (lsp:text-document-sync-options-will-save?)))
   3930 
   3931 (defun lsp--send-will-save-wait-until-p ()
   3932   "Return whether willSaveWaitUntil notifications should be sent to the server."
   3933   (-> (lsp--server-capabilities)
   3934       (lsp:server-capabilities-text-document-sync?)
   3935       (lsp:text-document-sync-options-will-save-wait-until?)))
   3936 
   3937 (defun lsp--send-did-save-p ()
   3938   "Return whether didSave notifications should be sent to the server."
   3939   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3940     (or (memq sync '(1 2))
   3941         (lsp:text-document-sync-options-save? sync))))
   3942 
   3943 (defun lsp--save-include-text-p ()
   3944   "Return whether save notifications should include the text document's contents."
   3945   (->> (lsp--server-capabilities)
   3946        (lsp:server-capabilities-text-document-sync?)
   3947        (lsp:text-document-sync-options-save?)
   3948        (lsp:text-document-save-registration-options-include-text?)))
   3949 
   3950 (defun lsp--send-will-rename-files-p (path)
   3951   "Return whether willRenameFiles request should be sent to the server.
   3952 If any filters, checks if it applies for PATH."
   3953   (let* ((will-rename (-> (lsp--server-capabilities)
   3954                           (lsp:server-capabilities-workspace?)
   3955                           (lsp:workspace-server-capabilities-file-operations?)
   3956                           (lsp:workspace-file-operations-will-rename?)))
   3957          (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list)))
   3958     (and will-rename
   3959          (or (seq-empty-p filters)
   3960              (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob)))
   3961                       (-let [regexes (lsp-glob-to-regexps glob)]
   3962                         (and (or (not scheme?)
   3963                                  (string-prefix-p scheme? (lsp--path-to-uri path)))
   3964                              (-any? (lambda (re)
   3965                                       (string-match re path))
   3966                                     regexes))))
   3967                     filters)))))
   3968 
   3969 (defun lsp--send-did-rename-files-p ()
   3970   "Return whether didRenameFiles notification should be sent to the server."
   3971   (-> (lsp--server-capabilities)
   3972       (lsp:server-capabilities-workspace?)
   3973       (lsp:workspace-server-capabilities-file-operations?)
   3974       (lsp:workspace-file-operations-did-rename?)))
   3975 
   3976 (declare-function project-roots "ext:project" (project) t)
   3977 (declare-function project-root "ext:project" (project) t)
   3978 
   3979 (defun lsp--suggest-project-root ()
   3980   "Get project root."
   3981   (or
   3982    (when (fboundp 'projectile-project-root)
   3983      (condition-case nil
   3984          (projectile-project-root)
   3985        (error nil)))
   3986    (when (fboundp 'project-current)
   3987      (when-let ((project (project-current)))
   3988        (if (fboundp 'project-root)
   3989            (project-root project)
   3990          (car (with-no-warnings
   3991                 (project-roots project))))))
   3992    default-directory))
   3993 
   3994 (defun lsp--read-from-file (file)
   3995   "Read FILE content."
   3996   (when (file-exists-p file)
   3997     (cl-first (read-from-string (f-read-text file 'utf-8)))))
   3998 
   3999 (defun lsp--persist (file-name to-persist)
   4000   "Persist TO-PERSIST in FILE-NAME.
   4001 
   4002 This function creates the parent directories if they don't exist
   4003 yet."
   4004   (let ((print-length nil)
   4005         (print-level nil))
   4006     ;; Create all parent directories:
   4007     (make-directory (f-parent file-name) t)
   4008     (f-write-text (prin1-to-string to-persist) 'utf-8 file-name)))
   4009 
   4010 (defun lsp-workspace-folders-add (project-root)
   4011   "Add PROJECT-ROOT to the list of workspace folders."
   4012   (interactive
   4013    (list (read-directory-name "Select folder to add: "
   4014                               (or (lsp--suggest-project-root) default-directory) nil t)))
   4015   (cl-pushnew (lsp-f-canonical project-root)
   4016               (lsp-session-folders (lsp-session)) :test 'equal)
   4017   (lsp--persist-session (lsp-session))
   4018 
   4019   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil))
   4020 
   4021 (defun lsp-workspace-folders-remove (project-root)
   4022   "Remove PROJECT-ROOT from the list of workspace folders."
   4023   (interactive (list (completing-read "Select folder to remove: "
   4024                                       (lsp-session-folders (lsp-session))
   4025                                       nil t nil nil
   4026                                       (lsp-find-session-folder (lsp-session) default-directory))))
   4027 
   4028   (setq project-root (lsp-f-canonical project-root))
   4029 
   4030   ;; send remove folder to each multiroot workspace associated with the folder
   4031   (dolist (wks (->> (lsp-session)
   4032                     (lsp-session-folder->servers)
   4033                     (gethash project-root)
   4034                     (--filter (lsp--client-multi-root (lsp--workspace-client it)))))
   4035     (with-lsp-workspace wks
   4036       (lsp-notify "workspace/didChangeWorkspaceFolders"
   4037                   (lsp-make-did-change-workspace-folders-params
   4038                    :event (lsp-make-workspace-folders-change-event
   4039                            :removed (vector (lsp-make-workspace-folder
   4040                                              :uri (lsp--path-to-uri project-root)
   4041                                              :name (f-filename project-root)))
   4042                            :added [])))))
   4043 
   4044   ;; turn off servers in the removed directory
   4045   (let* ((session (lsp-session))
   4046          (folder->servers (lsp-session-folder->servers session))
   4047          (server-id->folders (lsp-session-server-id->folders session))
   4048          (workspaces (gethash project-root folder->servers)))
   4049 
   4050     (remhash project-root folder->servers)
   4051 
   4052     ;; turn off the servers without root folders
   4053     (dolist (workspace workspaces)
   4054       (when (--none? (-contains? it workspace) (ht-values folder->servers))
   4055         (lsp--info "Shutdown %s since folder %s is removed..."
   4056                    (lsp--workspace-print workspace) project-root)
   4057         (with-lsp-workspace workspace (lsp--shutdown-workspace))))
   4058 
   4059     (setf (lsp-session-folders session)
   4060           (-remove-item project-root (lsp-session-folders session)))
   4061 
   4062     (ht-aeach (puthash key
   4063                        (-remove-item project-root value)
   4064                        server-id->folders)
   4065               server-id->folders)
   4066     (lsp--persist-session (lsp-session)))
   4067 
   4068   (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root)))
   4069 
   4070 (defun lsp-workspace-blocklist-remove (project-root)
   4071   "Remove PROJECT-ROOT from the workspace blocklist."
   4072   (interactive (list (completing-read "Select folder to remove:"
   4073                                       (lsp-session-folders-blocklist (lsp-session))
   4074                                       nil t)))
   4075   (setf (lsp-session-folders-blocklist (lsp-session))
   4076         (delete project-root
   4077                 (lsp-session-folders-blocklist (lsp-session))))
   4078   (lsp--persist-session (lsp-session)))
   4079 
   4080 (define-obsolete-function-alias 'lsp-workspace-folders-switch
   4081   'lsp-workspace-folders-open "lsp-mode 6.1")
   4082 
   4083 (defun lsp-workspace-folders-open (project-root)
   4084   "Open the directory located at PROJECT-ROOT"
   4085   (interactive (list (completing-read "Open folder: "
   4086                                       (lsp-session-folders (lsp-session))
   4087                                       nil t)))
   4088   (find-file project-root))
   4089 
   4090 (defun lsp--maybe-enable-signature-help (trigger-characters)
   4091   (let ((ch last-command-event))
   4092     (when (cl-find ch trigger-characters :key #'string-to-char)
   4093       (lsp-signature-activate))))
   4094 
   4095 (defun lsp--on-type-formatting-handler-create ()
   4096   (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" )))
   4097     (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character?
   4098                                              :first-trigger-character) provider]
   4099       (lambda ()
   4100         (lsp--on-type-formatting first-trigger-character
   4101                                  more-trigger-character?)))))
   4102 
   4103 (defun lsp--update-on-type-formatting-hook (&optional cleanup?)
   4104   (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create)))
   4105     (cond
   4106      ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?))
   4107       (add-hook 'post-self-insert-hook on-type-formatting-handler nil t))
   4108      ((or cleanup?
   4109           (not lsp-enable-on-type-formatting))
   4110       (remove-hook 'post-self-insert-hook on-type-formatting-handler t)))))
   4111 
   4112 (defun lsp--signature-help-handler-create ()
   4113   (-when-let ((&SignatureHelpOptions? :trigger-characters?)
   4114               (lsp--capability-for-method "textDocument/signatureHelp"))
   4115     (lambda ()
   4116       (lsp--maybe-enable-signature-help trigger-characters?))))
   4117 
   4118 (defun lsp--update-signature-help-hook (&optional cleanup?)
   4119   (let ((signature-help-handler (lsp--signature-help-handler-create)))
   4120     (cond
   4121      ((and (or (equal lsp-signature-auto-activate t)
   4122                (memq :on-trigger-char lsp-signature-auto-activate))
   4123            signature-help-handler)
   4124       (add-hook 'post-self-insert-hook signature-help-handler nil t))
   4125 
   4126      ((or cleanup?
   4127           (not (or (equal lsp-signature-auto-activate t)
   4128                    (memq :on-trigger-char lsp-signature-auto-activate))))
   4129       (remove-hook 'post-self-insert-hook signature-help-handler t)))))
   4130 
   4131 (defun lsp--after-set-visited-file-name ()
   4132   (lsp-disconnect)
   4133   (lsp))
   4134 
   4135 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27
   4136 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099
   4137 (defvar eldoc-documentation-default) ; CI
   4138 (when (< emacs-major-version 28)
   4139   (unless (boundp 'eldoc-documentation-functions)
   4140     (load "eldoc" nil 'nomessage))
   4141   (when (memq (default-value 'eldoc-documentation-function) '(nil ignore))
   4142     ;; actually `eldoc-documentation-strategy', but CI was failing
   4143     (setq-default eldoc-documentation-function 'eldoc-documentation-default)))
   4144 
   4145 (define-minor-mode lsp-managed-mode
   4146   "Mode for source buffers managed by lsp-mode."
   4147   :lighter nil
   4148   (cond
   4149    (lsp-managed-mode
   4150     (when (lsp-feature? "textDocument/hover")
   4151       (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t)
   4152       (eldoc-mode 1))
   4153 
   4154     (add-hook 'after-change-functions #'lsp-on-change nil t)
   4155     (add-hook 'after-revert-hook #'lsp-on-revert nil t)
   4156     (add-hook 'after-save-hook #'lsp-on-save nil t)
   4157     (add-hook 'auto-save-hook #'lsp--on-auto-save nil t)
   4158     (add-hook 'before-change-functions #'lsp-before-change nil t)
   4159     (add-hook 'before-save-hook #'lsp--before-save nil t)
   4160     (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t)
   4161     (add-hook 'post-command-hook #'lsp--post-command nil t)
   4162 
   4163     (lsp--update-on-type-formatting-hook)
   4164     (lsp--update-signature-help-hook)
   4165 
   4166     (when lsp-enable-xref
   4167       (add-hook 'xref-backend-functions #'lsp--xref-backend nil t))
   4168 
   4169     (lsp-configure-buffer)
   4170 
   4171     ;; make sure we turn off lsp-mode in case major mode changes, because major
   4172     ;; mode change will wipe the buffer locals.
   4173     (add-hook 'change-major-mode-hook #'lsp-disconnect nil t)
   4174     (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t)
   4175 
   4176     (let ((buffer (lsp-current-buffer)))
   4177       (run-with-idle-timer
   4178        0.0 nil
   4179        (lambda ()
   4180          (when (lsp-buffer-live-p buffer)
   4181            (lsp-with-current-buffer buffer
   4182              (lsp--on-change-debounce buffer)
   4183              (lsp--on-idle buffer)))))))
   4184    (t
   4185     (lsp-unconfig-buffer)
   4186 
   4187     (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t)
   4188     (remove-hook 'post-command-hook #'lsp--post-command t)
   4189     (remove-hook 'after-change-functions #'lsp-on-change t)
   4190     (remove-hook 'after-revert-hook #'lsp-on-revert t)
   4191     (remove-hook 'after-save-hook #'lsp-on-save t)
   4192     (remove-hook 'auto-save-hook #'lsp--on-auto-save t)
   4193     (remove-hook 'before-change-functions #'lsp-before-change t)
   4194     (remove-hook 'before-save-hook #'lsp--before-save t)
   4195     (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t)
   4196 
   4197     (lsp--update-on-type-formatting-hook :cleanup)
   4198     (lsp--update-signature-help-hook :cleanup)
   4199 
   4200     (when lsp--on-idle-timer
   4201       (cancel-timer lsp--on-idle-timer)
   4202       (setq lsp--on-idle-timer nil))
   4203 
   4204     (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4205     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4206 
   4207     (lsp--remove-overlays 'lsp-highlight)
   4208     (lsp--remove-overlays 'lsp-links)
   4209 
   4210     (remove-hook 'xref-backend-functions #'lsp--xref-backend t)
   4211     (remove-hook 'change-major-mode-hook #'lsp-disconnect t)
   4212     (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t)
   4213     (setq-local lsp-buffer-uri nil))))
   4214 
   4215 (defun lsp-configure-buffer ()
   4216   "Configure LSP features for current buffer."
   4217   ;; make sure the core is running in the context of all available workspaces
   4218   ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context
   4219   (let ((lsp--buffer-workspaces (cond
   4220                                  (lsp--buffer-workspaces)
   4221                                  (lsp--cur-workspace (list lsp--cur-workspace))))
   4222         lsp--cur-workspace)
   4223     (when lsp-auto-configure
   4224       (lsp--auto-configure)
   4225 
   4226       (when (and lsp-enable-text-document-color
   4227                  (lsp-feature? "textDocument/documentColor"))
   4228         (add-hook 'lsp-on-change-hook #'lsp--document-color nil t))
   4229 
   4230       (when (and lsp-enable-imenu
   4231                  (lsp-feature? "textDocument/documentSymbol"))
   4232         (lsp-enable-imenu))
   4233 
   4234       (when (and lsp-enable-indentation
   4235                  (lsp-feature? "textDocument/rangeFormatting"))
   4236         (add-function :override (local 'indent-region-function) #'lsp-format-region))
   4237 
   4238       (when (and lsp-enable-symbol-highlighting
   4239                  (lsp-feature? "textDocument/documentHighlight"))
   4240         (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t))
   4241 
   4242       (when (and lsp-enable-links
   4243                  (lsp-feature? "textDocument/documentLink"))
   4244         (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t))
   4245 
   4246       (when (and lsp-inlay-hint-enable
   4247                  (lsp-feature? "textDocument/inlayHint"))
   4248         (lsp-inlay-hints-mode))
   4249 
   4250       (when (and lsp-enable-dap-auto-configure
   4251                  (functionp 'dap-mode))
   4252         (dap-auto-configure-mode 1)))
   4253     (run-hooks 'lsp-configure-hook)))
   4254 
   4255 (defun lsp-unconfig-buffer ()
   4256   "Unconfigure LSP features for buffer."
   4257   (lsp--remove-overlays 'lsp-color)
   4258 
   4259   (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function)
   4260     (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   4261     (setq-local imenu-menubar-modified-tick 0)
   4262     (setq-local imenu--index-alist nil)
   4263     (imenu--cleanup))
   4264 
   4265   (remove-function (local 'indent-region-function) #'lsp-format-region)
   4266 
   4267   (remove-hook 'lsp-on-change-hook #'lsp--document-color t)
   4268   (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4269   (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4270 
   4271   (when (and lsp-enable-dap-auto-configure
   4272              (functionp 'dap-mode))
   4273     (dap-auto-configure-mode -1))
   4274 
   4275   (run-hooks 'lsp-unconfigure-hook))
   4276 
   4277 (defun lsp--buffer-content ()
   4278   (lsp-save-restriction-and-excursion
   4279     (or (lsp-virtual-buffer-call :buffer-string)
   4280         (buffer-substring-no-properties (point-min)
   4281                                         (point-max)))))
   4282 
   4283 (defun lsp--text-document-did-open ()
   4284   "`document/didOpen' event."
   4285   (run-hooks 'lsp-before-open-hook)
   4286   (when (and lsp-auto-touch-files
   4287              (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri)))))
   4288     (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri))
   4289     (save-buffer))
   4290 
   4291   (setq lsp--cur-version (or lsp--cur-version 0))
   4292   (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   4293   (lsp-notify
   4294    "textDocument/didOpen"
   4295    (list :textDocument
   4296          (list :uri (lsp--buffer-uri)
   4297                :languageId (lsp-buffer-language)
   4298                :version lsp--cur-version
   4299                :text (lsp--buffer-content))))
   4300 
   4301   (lsp-managed-mode 1)
   4302 
   4303   (lsp-diagnostics--request-pull-diagnostics lsp--cur-workspace)
   4304 
   4305   (run-hooks 'lsp-after-open-hook)
   4306   (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client))))
   4307     (-some-> (lsp--client-after-open-fn client)
   4308       (funcall))
   4309     (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client))
   4310       (intern-soft)
   4311       (run-hooks))))
   4312 
   4313 (defun lsp--text-document-identifier ()
   4314   "Make TextDocumentIdentifier."
   4315   (list :uri (lsp--buffer-uri)))
   4316 
   4317 (defun lsp--versioned-text-document-identifier ()
   4318   "Make VersionedTextDocumentIdentifier."
   4319   (plist-put (lsp--text-document-identifier) :version lsp--cur-version))
   4320 
   4321 (defun lsp--cur-line (&optional point)
   4322   (1- (line-number-at-pos point)))
   4323 
   4324 (defun lsp--cur-position ()
   4325   "Make a Position object for the current point."
   4326   (or (lsp-virtual-buffer-call :cur-position)
   4327       (lsp-save-restriction-and-excursion
   4328         (list :line (lsp--cur-line)
   4329               :character (- (point) (line-beginning-position))))))
   4330 
   4331 (defun lsp--point-to-position (point)
   4332   "Convert POINT to Position."
   4333   (lsp-save-restriction-and-excursion
   4334     (goto-char point)
   4335     (lsp--cur-position)))
   4336 
   4337 (defun lsp--range (start end)
   4338   "Make Range body from START and END."
   4339   ;; make sure start and end are Position objects
   4340   (list :start start :end end))
   4341 
   4342 (defun lsp--region-to-range (start end)
   4343   "Make Range object for the current region."
   4344   (lsp--range (lsp--point-to-position start)
   4345               (lsp--point-to-position end)))
   4346 
   4347 (defun lsp--region-or-line ()
   4348   "The active region or the current line."
   4349   (if (use-region-p)
   4350       (lsp--region-to-range (region-beginning) (region-end))
   4351     (lsp--region-to-range (line-beginning-position) (line-end-position))))
   4352 
   4353 (defun lsp--check-document-changes-version (document-changes)
   4354   "Verify that DOCUMENT-CHANGES have the proper version."
   4355   (unless (seq-every-p
   4356            (-lambda ((&TextDocumentEdit :text-document))
   4357              (or
   4358               (not text-document)
   4359               (let* ((filename (-> text-document
   4360                                    lsp:versioned-text-document-identifier-uri
   4361                                    lsp--uri-to-path))
   4362                      (version (lsp:versioned-text-document-identifier-version? text-document)))
   4363                 (with-current-buffer (find-file-noselect filename)
   4364                   (or (null version) (zerop version) (= -1 version)
   4365                       (equal version lsp--cur-version))))))
   4366            document-changes)
   4367     (error "Document changes cannot be applied due to different document version")))
   4368 
   4369 (defun lsp--apply-workspace-edit (workspace-edit &optional operation)
   4370   "Apply the WorkspaceEdit object WORKSPACE-EDIT.
   4371 OPERATION is symbol representing the source of this text edit."
   4372   (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit))
   4373     (if-let ((document-changes (seq-reverse document-changes?)))
   4374         (progn
   4375           (lsp--check-document-changes-version document-changes)
   4376           (->> document-changes
   4377                (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create")))
   4378                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4379           (->> document-changes
   4380                (seq-filter (-lambda ((&CreateFile :kind))
   4381                              (and (or (not kind) (equal kind "edit"))
   4382                                   (not (equal kind "create")))))
   4383                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4384           (->> document-changes
   4385                (seq-filter (-lambda ((&CreateFile :kind))
   4386                              (and (not (or (not kind) (equal kind "edit")))
   4387                                   (not (equal kind "create")))))
   4388                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))))
   4389       (lsp-map
   4390        (lambda (uri text-edits)
   4391          (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect)
   4392            (lsp--apply-text-edits text-edits operation)))
   4393        changes?))))
   4394 
   4395 (defmacro lsp-with-filename (file &rest body)
   4396   "Execute BODY with FILE as a context.
   4397 Need to handle the case when FILE indicates virtual buffer."
   4398   (declare (indent 1) (debug t))
   4399   `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file)))
   4400        (lsp-with-current-buffer lsp--virtual-buffer
   4401          ,@body)
   4402      ,@body))
   4403 
   4404 (defun lsp--apply-text-document-edit (edit &optional operation)
   4405   "Apply the TextDocumentEdit object EDIT.
   4406 OPERATION is symbol representing the source of this text edit.
   4407 If the file is not being visited by any buffer, it is opened with
   4408 `find-file-noselect'.
   4409 Because lsp-mode does not store previous document versions, the edit is only
   4410 applied if the version of the textDocument matches the version of the
   4411 corresponding file.
   4412 
   4413 interface TextDocumentEdit {
   4414   textDocument: VersionedTextDocumentIdentifier;
   4415   edits: TextEdit[];
   4416 }"
   4417   (pcase (lsp:edit-kind edit)
   4418     ("create" (-let* (((&CreateFile :uri :options?) edit)
   4419                       (file-name (lsp--uri-to-path uri)))
   4420                 (mkdir (f-dirname file-name) t)
   4421                 (f-touch file-name)
   4422                 (when (lsp:create-file-options-overwrite? options?)
   4423                   (f-write-text "" nil file-name))
   4424                 (find-file-noselect file-name)))
   4425     ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit))
   4426                 (f-delete (lsp--uri-to-path uri) recursive?)))
   4427     ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit)
   4428                       (old-file-name (lsp--uri-to-path old-uri))
   4429                       (new-file-name (lsp--uri-to-path new-uri))
   4430                       (buf (find-buffer-visiting old-file-name)))
   4431                 (when buf
   4432                   (lsp-with-current-buffer buf
   4433                     (save-buffer)
   4434                     (lsp--text-document-did-close)))
   4435                 (mkdir (f-dirname new-file-name) t)
   4436                 (rename-file old-file-name new-file-name overwrite?)
   4437                 (when buf
   4438                   (lsp-with-current-buffer buf
   4439                     (set-buffer-modified-p nil)
   4440                     (setq lsp-buffer-uri nil)
   4441                     (set-visited-file-name new-file-name)
   4442                     (lsp)))))
   4443     (_ (let ((file-name (->> edit
   4444                              (lsp:text-document-edit-text-document)
   4445                              (lsp:versioned-text-document-identifier-uri)
   4446                              (lsp--uri-to-path))))
   4447          (lsp-with-current-buffer (find-buffer-visiting file-name)
   4448            (lsp-with-filename file-name
   4449              (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation)))))))
   4450 
   4451 (lsp-defun lsp--position-compare ((&Position :line left-line
   4452                                              :character left-character)
   4453                                   (&Position :line right-line
   4454                                              :character right-character))
   4455   "Return t if position LEFT is greater than RIGHT."
   4456   (if (= left-line right-line)
   4457       (> left-character right-character)
   4458     (> left-line right-line)))
   4459 
   4460 (lsp-defun lsp-point-in-range? (position (&Range :start :end))
   4461   "Returns if POINT is in RANGE."
   4462   (not (or (lsp--position-compare start position)
   4463            (lsp--position-compare position end))))
   4464 
   4465 (lsp-defun lsp--position-equal ((&Position :line left-line
   4466                                            :character left-character)
   4467                                 (&Position :line right-line
   4468                                            :character right-character))
   4469   "Return whether LEFT and RIGHT positions are equal."
   4470   (and (= left-line right-line)
   4471        (= left-character right-character)))
   4472 
   4473 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end))
   4474                                           (&TextEdit :range (&Range :start right-start :end right-end)))
   4475   (if (lsp--position-equal left-start right-start)
   4476       (lsp--position-compare left-end right-end)
   4477     (lsp--position-compare left-start right-start)))
   4478 
   4479 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text))
   4480   "Apply the edits described in the TextEdit object in TEXT-EDIT."
   4481   (setq new-text (s-replace "\r" "" (or new-text "")))
   4482   (lsp:set-text-edit-new-text edit new-text)
   4483   (goto-char start)
   4484   (delete-region start end)
   4485   (insert new-text))
   4486 
   4487 ;; WORKAROUND: typescript-language might send -1 when applying code actions.
   4488 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582
   4489 (lsp-defun lsp--fix-point ((point &as &Position :character :line))
   4490   (-doto point
   4491     (lsp:set-position-line (max 0 line))
   4492     (lsp:set-position-character (max 0 character))))
   4493 
   4494 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as
   4495                                                                &TextEdit
   4496                                                                :range (&Range :start :end)
   4497                                                                :new-text))
   4498   "Apply the edits described in the TextEdit object in TEXT-EDIT.
   4499 The method uses `replace-buffer-contents'."
   4500   (setq new-text (s-replace "\r" "" (or new-text "")))
   4501   (lsp:set-text-edit-new-text edit new-text)
   4502   (-let* ((source (current-buffer))
   4503           ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start)
   4504                                                              :end (lsp--fix-point end)))))
   4505     (with-temp-buffer
   4506       (insert new-text)
   4507       (let ((temp (current-buffer)))
   4508         (with-current-buffer source
   4509           (save-excursion
   4510             (save-restriction
   4511               (narrow-to-region beg end)
   4512 
   4513               ;; On emacs versions < 26.2,
   4514               ;; `replace-buffer-contents' is buggy - it calls
   4515               ;; change functions with invalid arguments - so we
   4516               ;; manually call the change functions here.
   4517               ;;
   4518               ;; See emacs bugs #32237, #32278:
   4519               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237
   4520               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278
   4521               (let ((inhibit-modification-hooks t)
   4522                     (length (- end beg)))
   4523                 (run-hook-with-args 'before-change-functions
   4524                                     beg end)
   4525                 (replace-buffer-contents temp)
   4526                 (run-hook-with-args 'after-change-functions
   4527                                     beg (+ beg (length new-text))
   4528                                     length)))))))))
   4529 
   4530 (defun lsp--to-yasnippet-snippet (snippet)
   4531   "Convert LSP SNIPPET to yasnippet snippet."
   4532   ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it.
   4533   (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`")))
   4534                             (rx "\\" (backref 1))
   4535                             snippet
   4536                             nil nil 1))
   4537 
   4538 (defvar-local lsp-enable-relative-indentation nil
   4539   "Enable relative indentation when insert texts, snippets ...
   4540 from language server.")
   4541 
   4542 (defun lsp--expand-snippet (snippet &optional start end expand-env)
   4543   "Wrapper of `yas-expand-snippet' with all of it arguments.
   4544 The snippet will be convert to LSP style and indent according to
   4545 LSP server result."
   4546   (require 'yasnippet nil t)
   4547   (let* ((inhibit-field-text-motion t)
   4548          (yas-wrap-around-region nil)
   4549          (yas-indent-line 'none)
   4550          (yas-also-auto-indent-first-line nil))
   4551     (yas-expand-snippet
   4552      (lsp--to-yasnippet-snippet snippet)
   4553      start end expand-env)))
   4554 
   4555 (defun lsp--indent-lines (start end &optional insert-text-mode?)
   4556   "Indent from START to END based on INSERT-TEXT-MODE? value.
   4557 - When INSERT-TEXT-MODE? is provided
   4558   - if it's `lsp/insert-text-mode-as-it', do no editor indentation.
   4559   - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading
   4560     whitespaces to match the line where text is inserted.
   4561 - When it's not provided, using `indent-line-function' for each line."
   4562   (save-excursion
   4563     (goto-char end)
   4564     (let* ((end-line (line-number-at-pos))
   4565            (offset (save-excursion
   4566                      (goto-char start)
   4567                      (current-indentation)))
   4568            (indent-line-function
   4569             (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it)
   4570                    #'ignore)
   4571                   ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation)
   4572                        lsp-enable-relative-indentation
   4573                        ;; Indenting snippets is extremely slow in `org-mode' buffers
   4574                        ;; since it has to calculate indentation based on SRC block
   4575                        ;; position.  Thus we use relative indentation as default.
   4576                        (derived-mode-p 'org-mode))
   4577                    (lambda () (save-excursion
   4578                                 (beginning-of-line)
   4579                                 (indent-to-column offset))))
   4580                   (t indent-line-function))))
   4581       (goto-char start)
   4582       (forward-line)
   4583       (while (and (not (eobp))
   4584                   (<= (line-number-at-pos) end-line))
   4585         (funcall indent-line-function)
   4586         (forward-line)))))
   4587 
   4588 (defun lsp--apply-text-edits (edits &optional operation)
   4589   "Apply the EDITS described in the TextEdit[] object.
   4590 OPERATION is symbol representing the source of this text edit."
   4591   (unless (seq-empty-p edits)
   4592     (atomic-change-group
   4593       (run-hooks 'lsp-before-apply-edits-hook)
   4594       (let* ((change-group (prepare-change-group))
   4595              (howmany (length edits))
   4596              (message (format "Applying %s edits to `%s' ..." howmany (current-buffer)))
   4597              (_ (lsp--info message))
   4598              (reporter (make-progress-reporter message 0 howmany))
   4599              (done 0)
   4600              (apply-edit (if (not lsp--virtual-buffer)
   4601                              #'lsp--apply-text-edit-replace-buffer-contents
   4602                            #'lsp--apply-text-edit)))
   4603         (unwind-protect
   4604             (->> edits
   4605                  ;; We sort text edits so as to apply edits that modify latter
   4606                  ;; parts of the document first. Furthermore, because the LSP
   4607                  ;; spec dictates that: "If multiple inserts have the same
   4608                  ;; position, the order in the array defines which edit to
   4609                  ;; apply first."  We reverse the initial list and sort stably
   4610                  ;; to make sure the order among edits with the same position
   4611                  ;; is preserved.
   4612                  (nreverse)
   4613                  (seq-sort #'lsp--text-edit-sort-predicate)
   4614                  (mapc (lambda (edit)
   4615                          (progress-reporter-update reporter (cl-incf done))
   4616                          (funcall apply-edit edit)
   4617                          (when (lsp:snippet-text-edit-insert-text-format? edit)
   4618                            (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start)
   4619                                                          :insert-text-format? :new-text) edit)
   4620                              (when (eq insert-text-format? lsp/insert-text-format-snippet)
   4621                                ;; No `save-excursion' needed since expand snippet will change point anyway
   4622                                (goto-char (+ start (length new-text)))
   4623                                (lsp--indent-lines start (point))
   4624                                (lsp--expand-snippet new-text start (point)))))
   4625                          (run-hook-with-args 'lsp-after-apply-edits-hook operation))))
   4626           (undo-amalgamate-change-group change-group)
   4627           (progress-reporter-done reporter))))))
   4628 
   4629 (defun lsp--create-apply-text-edits-handlers ()
   4630   "Create (handler cleanup-fn) for applying text edits in async request.
   4631 Only works when mode is `tick or `alive."
   4632   (let* (first-edited
   4633          (func (lambda (start &rest _)
   4634                  (setq first-edited (if first-edited
   4635                                         (min start first-edited)
   4636                                       start)))))
   4637     (add-hook 'before-change-functions func nil t)
   4638     (list
   4639      (lambda (edits)
   4640        (if (and first-edited
   4641                 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end)))
   4642                             ;; Text edit region is overlapped
   4643                             (> end first-edited))
   4644                           edits))
   4645            (lsp--warn "TextEdits will not be applied since document has been modified before of them.")
   4646          (lsp--apply-text-edits edits 'completion-cleanup)))
   4647      (lambda ()
   4648        (remove-hook 'before-change-functions func t)))))
   4649 
   4650 (defun lsp--capability (cap &optional capabilities)
   4651   "Get the value of capability CAP.  If CAPABILITIES is non-nil, use them instead."
   4652   (when (stringp cap)
   4653     (setq cap (intern (concat ":" cap))))
   4654 
   4655   (lsp-get (or capabilities
   4656                (lsp--server-capabilities))
   4657            cap))
   4658 
   4659 (defun lsp--registered-capability (method)
   4660   "Check whether there is workspace providing METHOD."
   4661   (->> (lsp-workspaces)
   4662        (--keep (seq-find (lambda (reg)
   4663                            (equal (lsp--registered-capability-method reg) method))
   4664                          (lsp--workspace-registered-server-capabilities it)))
   4665        cl-first))
   4666 
   4667 (defun lsp--capability-for-method (method)
   4668   "Get the value of capability for METHOD."
   4669   (-let* ((reqs (cdr (assoc method lsp-method-requirements)))
   4670           ((&plist :capability) reqs))
   4671     (or (and capability (lsp--capability capability))
   4672         (-some-> (lsp--registered-capability method)
   4673           (lsp--registered-capability-options)))))
   4674 
   4675 (defvar-local lsp--before-change-vals nil
   4676   "Store the positions from the `lsp-before-change' function call, for
   4677 validation and use in the `lsp-on-change' function.")
   4678 
   4679 (defun lsp--text-document-content-change-event (start end length)
   4680   "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH."
   4681   ;; So (47 54 0) means add    7 chars starting at pos 47
   4682   ;; must become
   4683   ;;   {"range":{"start":{"line":5,"character":6}
   4684   ;;             ,"end" :{"line":5,"character":6}}
   4685   ;;             ,"rangeLength":0
   4686   ;;             ,"text":"\nbb = 5"}
   4687   ;;
   4688   ;; And (47 47 7) means delete 7 chars starting at pos 47
   4689   ;; must become
   4690   ;;   {"range":{"start":{"line":6,"character":0}
   4691   ;;            ,"end"  :{"line":7,"character":0}}
   4692   ;;            ,"rangeLength":7
   4693   ;;            ,"text":""}
   4694   ;;
   4695   ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with
   4696   ;; 13 chars. So it must become
   4697   ;;   {"range":{"start":{"line":5,"character":8}
   4698   ;;             ,"end" :{"line":5,"character":11}}
   4699   ;;             ,"rangeLength":3
   4700   ;;             ,"text":"new-chars-xxx"}
   4701   ;;
   4702 
   4703   ;; Adding text:
   4704   ;;   lsp-before-change:(start,end)=(33,33)
   4705   ;;   lsp-on-change:(start,end,length)=(33,34,0)
   4706   ;;
   4707   ;; Changing text:
   4708   ;;   lsp-before-change:(start,end)=(208,211)
   4709   ;;   lsp-on-change:(start,end,length)=(208,221,3)
   4710   ;;
   4711   ;; Deleting text:
   4712   ;;   lsp-before-change:(start,end)=(19,27)
   4713   ;;   lsp-on-change:(start,end,length)=(19,19,8)
   4714   (if (zerop length)
   4715       ;; Adding something only, work from start only
   4716       `( :range ,(lsp--range
   4717                   (lsp--point-to-position start)
   4718                   (lsp--point-to-position start))
   4719          :rangeLength 0
   4720          :text ,(buffer-substring-no-properties start end))
   4721 
   4722     (if (eq start end)
   4723         ;; Deleting something only
   4724         (if (lsp--bracketed-change-p start length)
   4725             ;; The before-change value is bracketed, use it
   4726             `( :range ,(lsp--range
   4727                         (lsp--point-to-position start)
   4728                         (plist-get lsp--before-change-vals :end-pos))
   4729                :rangeLength ,length
   4730                :text "")
   4731           ;; If the change is not bracketed, send a full change event instead.
   4732           (lsp--full-change-event))
   4733 
   4734       ;; Deleting some things, adding others
   4735       (if (lsp--bracketed-change-p start length)
   4736           ;; The before-change value is valid, use it
   4737           `( :range ,(lsp--range
   4738                       (lsp--point-to-position start)
   4739                       (plist-get lsp--before-change-vals :end-pos))
   4740              :rangeLength ,length
   4741              :text ,(buffer-substring-no-properties start end))
   4742         (lsp--full-change-event)))))
   4743 
   4744 (defun lsp--bracketed-change-p (start length)
   4745   "If the before and after positions are the same, and the length
   4746 is the size of the start range, we are probably good."
   4747   (-let [(&plist :end before-end :start before-start) lsp--before-change-vals]
   4748     (and (eq start before-start)
   4749          (eq length (- before-end before-start)))))
   4750 
   4751 (defun lsp--full-change-event ()
   4752   `(:text ,(lsp--buffer-content)))
   4753 
   4754 (defun lsp-before-change (start end)
   4755   "Executed before a file is changed.
   4756 Added to `before-change-functions'."
   4757   ;; Note:
   4758   ;;
   4759   ;; This variable holds a list of functions to call when Emacs is about to
   4760   ;; modify a buffer. Each function gets two arguments, the beginning and end of
   4761   ;; the region that is about to change, represented as integers. The buffer
   4762   ;; that is about to change is always the current buffer when the function is
   4763   ;; called.
   4764   ;;
   4765   ;; WARNING:
   4766   ;;
   4767   ;; Do not expect the before-change hooks and the after-change hooks be called
   4768   ;; in balanced pairs around each buffer change. Also don't expect the
   4769   ;; before-change hooks to be called for every chunk of text Emacs is about to
   4770   ;; delete. These hooks are provided on the assumption that Lisp programs will
   4771   ;; use either before- or the after-change hooks, but not both, and the
   4772   ;; boundaries of the region where the changes happen might include more than
   4773   ;; just the actual changed text, or even lump together several changes done
   4774   ;; piecemeal.
   4775   (save-match-data
   4776     (lsp-save-restriction-and-excursion
   4777       (setq lsp--before-change-vals
   4778             (list :start start
   4779                   :end end
   4780                   :end-pos (lsp--point-to-position end))))))
   4781 
   4782 (defun lsp--flush-delayed-changes ()
   4783   (let ((inhibit-quit t))
   4784     (when lsp--delay-timer
   4785       (cancel-timer lsp--delay-timer))
   4786     (mapc (-lambda ((workspace buffer document change))
   4787             (with-current-buffer buffer
   4788               (with-lsp-workspace workspace
   4789                 (lsp-notify "textDocument/didChange"
   4790                             (list :textDocument document
   4791                                   :contentChanges (vector change))))))
   4792           (prog1 (nreverse lsp--delayed-requests)
   4793             (setq lsp--delayed-requests nil)))))
   4794 
   4795 (defun lsp--workspace-sync-method (workspace)
   4796   (let ((sync (-> workspace
   4797                   (lsp--workspace-server-capabilities)
   4798                   (lsp:server-capabilities-text-document-sync?))))
   4799     (if (lsp-text-document-sync-options? sync)
   4800         (lsp:text-document-sync-options-change? sync)
   4801       sync)))
   4802 
   4803 (defun lsp-on-change (start end length &optional content-change-event-fn)
   4804   "Executed when a file is changed.
   4805 Added to `after-change-functions'."
   4806   ;; Note:
   4807   ;;
   4808   ;; Each function receives three arguments: the beginning and end of the region
   4809   ;; just changed, and the length of the text that existed before the change.
   4810   ;; All three arguments are integers. The buffer that has been changed is
   4811   ;; always the current buffer when the function is called.
   4812   ;;
   4813   ;; The length of the old text is the difference between the buffer positions
   4814   ;; before and after that text as it was before the change. As for the
   4815   ;; changed text, its length is simply the difference between the first two
   4816   ;; arguments.
   4817   ;;
   4818   ;; So (47 54 0) means add    7 chars starting at pos 47
   4819   ;; So (47 47 7) means delete 7 chars starting at pos 47
   4820   (save-match-data
   4821     (let ((inhibit-quit t)
   4822           ;; make sure that `lsp-on-change' is called in multi-workspace context
   4823           ;; see #2901
   4824           lsp--cur-workspace)
   4825       ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done
   4826       ;; by auto-revert-mode) will cause this handler to get called with a nil
   4827       ;; buffer-file-name. We need the buffer-file-name to send notifications;
   4828       ;; so we skip handling revert-buffer-caused changes and instead handle
   4829       ;; reverts separately in lsp-on-revert
   4830       (when (not revert-buffer-in-progress-p)
   4831         (cl-incf lsp--cur-version)
   4832         (mapc
   4833          (lambda (workspace)
   4834            (pcase (or lsp-document-sync-method
   4835                       (lsp--workspace-sync-method workspace))
   4836              (1
   4837               (if lsp-debounce-full-sync-notifications
   4838                   (setq lsp--delayed-requests
   4839                         (->> lsp--delayed-requests
   4840                              (-remove (-lambda ((_ buffer))
   4841                                         (equal (current-buffer) buffer)))
   4842                              (cons (list workspace
   4843                                          (current-buffer)
   4844                                          (lsp--versioned-text-document-identifier)
   4845                                          (lsp--full-change-event)))))
   4846                 (with-lsp-workspace workspace
   4847                   (lsp-notify "textDocument/didChange"
   4848                               (list :contentChanges (vector (lsp--full-change-event))
   4849                                     :textDocument (lsp--versioned-text-document-identifier)))
   4850                   (lsp-diagnostics--request-pull-diagnostics workspace))))
   4851              (2
   4852               (with-lsp-workspace workspace
   4853                 (lsp-notify
   4854                  "textDocument/didChange"
   4855                  (list :textDocument (lsp--versioned-text-document-identifier)
   4856                        :contentChanges (vector
   4857                                         (if content-change-event-fn
   4858                                             (funcall content-change-event-fn start end length)
   4859                                           (lsp--text-document-content-change-event
   4860                                            start end length)))))
   4861                 (lsp-diagnostics--request-pull-diagnostics workspace)))))
   4862          (lsp-workspaces))
   4863         (when lsp--delay-timer (cancel-timer lsp--delay-timer))
   4864         (setq lsp--delay-timer (run-with-idle-timer
   4865                                 lsp-debounce-full-sync-notifications-interval
   4866                                 nil
   4867                                 #'lsp--flush-delayed-changes))
   4868         ;; force cleanup overlays after each change
   4869         (lsp--remove-overlays 'lsp-highlight)
   4870         (lsp--after-change (current-buffer))))))
   4871 
   4872 
   4873 
   4874 ;; facilities for on change hooks. We do not want to make lsp calls on each
   4875 ;; change event so we add debounce to avoid flooding the server with events.
   4876 ;; Additionally, we want to have a mechanism for stopping the server calls in
   4877 ;; particular cases like, e. g. when performing completion.
   4878 
   4879 (defvar lsp-inhibit-lsp-hooks nil
   4880   "Flag to control.")
   4881 
   4882 (defcustom lsp-on-change-hook nil
   4883   "Hooks to run when buffer has changed."
   4884   :type 'hook
   4885   :group 'lsp-mode)
   4886 
   4887 (defcustom lsp-idle-delay 0.500
   4888   "Debounce interval for `after-change-functions'."
   4889   :type 'number
   4890   :group 'lsp-mode)
   4891 
   4892 (defcustom lsp-on-idle-hook nil
   4893   "Hooks to run after `lsp-idle-delay'."
   4894   :type 'hook
   4895   :group 'lsp-mode)
   4896 
   4897 (defun lsp--idle-reschedule (buffer)
   4898   (when lsp--on-idle-timer
   4899     (cancel-timer lsp--on-idle-timer))
   4900 
   4901   (setq lsp--on-idle-timer (run-with-idle-timer
   4902                             lsp-idle-delay
   4903                             nil
   4904                             #'lsp--on-idle
   4905                             buffer)))
   4906 
   4907 (defun lsp--post-command ()
   4908   (lsp--cleanup-highlights-if-needed)
   4909   (lsp--idle-reschedule (current-buffer)))
   4910 
   4911 (defun lsp--on-idle (buffer)
   4912   "Start post command loop."
   4913   (when (and (buffer-live-p buffer)
   4914              (equal buffer (current-buffer))
   4915              (not lsp-inhibit-lsp-hooks)
   4916              lsp-managed-mode)
   4917     (run-hooks 'lsp-on-idle-hook)))
   4918 
   4919 (defun lsp--on-change-debounce (buffer)
   4920   (when (and (buffer-live-p buffer)
   4921              (equal buffer (current-buffer))
   4922              (not lsp-inhibit-lsp-hooks)
   4923              lsp-managed-mode)
   4924     (run-hooks 'lsp-on-change-hook)))
   4925 
   4926 (defun lsp--after-change (buffer)
   4927   "Called after most textDocument/didChange events."
   4928   (setq lsp--signature-last-index nil
   4929         lsp--signature-last nil)
   4930 
   4931   ;; cleanup diagnostics
   4932   (when lsp-diagnostic-clean-after-change
   4933     (dolist (workspace (lsp-workspaces))
   4934       (-let [diagnostics (lsp--workspace-diagnostics workspace)]
   4935         (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics))))
   4936 
   4937   (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled)
   4938     (lsp--semantic-tokens-refresh-if-enabled buffer))
   4939   (when lsp--on-change-timer
   4940     (cancel-timer lsp--on-change-timer))
   4941   (setq lsp--on-change-timer (run-with-idle-timer
   4942                               lsp-idle-delay
   4943                               nil
   4944                               #'lsp--on-change-debounce
   4945                               buffer))
   4946   (lsp--idle-reschedule buffer))
   4947 
   4948 
   4949 (defcustom lsp-trim-trailing-whitespace t
   4950   "Trim trailing whitespace on a line."
   4951   :group 'lsp-mode
   4952   :type 'boolean)
   4953 
   4954 (defcustom lsp-insert-final-newline t
   4955   "Insert a newline character at the end of the file if one does not exist."
   4956   :group 'lsp-mode
   4957   :type 'boolean)
   4958 
   4959 (defcustom lsp-trim-final-newlines t
   4960   "Trim all newlines after the final newline at the end of the file."
   4961   :group 'lsp-mode
   4962   :type 'boolean)
   4963 
   4964 
   4965 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters)
   4966   "Self insert handling.
   4967 Applies on type formatting."
   4968   (let ((ch last-command-event))
   4969     (when (or (eq (string-to-char first-trigger-characters) ch)
   4970               (cl-find ch more-trigger-characters :key #'string-to-char))
   4971       (lsp-request-async "textDocument/onTypeFormatting"
   4972                          (lsp-make-document-on-type-formatting-params
   4973                           :text-document (lsp--text-document-identifier)
   4974                           :options (lsp-make-formatting-options
   4975                                     :tab-size (symbol-value (lsp--get-indent-width major-mode))
   4976                                     :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   4977                                     :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   4978                                     :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   4979                                     :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))
   4980                           :ch (char-to-string ch)
   4981                           :position (lsp--cur-position))
   4982                          (lambda (data) (lsp--apply-text-edits data 'format))
   4983                          :mode 'tick))))
   4984 
   4985 
   4986 ;; links
   4987 (defun lsp--document-links ()
   4988   (when (lsp-feature? "textDocument/documentLink")
   4989     (lsp-request-async
   4990      "textDocument/documentLink"
   4991      `(:textDocument ,(lsp--text-document-identifier))
   4992      (lambda (links)
   4993        (lsp--remove-overlays 'lsp-link)
   4994        (seq-do
   4995         (-lambda ((link &as &DocumentLink :range (&Range :start :end)))
   4996           (-doto (make-button (lsp--position-to-point start)
   4997                               (lsp--position-to-point end)
   4998                               'action (lsp--document-link-keymap link)
   4999                               'keymap (let ((map (make-sparse-keymap)))
   5000                                         (define-key map [M-return] 'push-button)
   5001                                         (define-key map [mouse-2] 'push-button)
   5002                                         map)
   5003                               'help-echo "mouse-2, M-RET: Visit this link")
   5004             (overlay-put 'lsp-link t)))
   5005         links))
   5006      :mode 'unchanged)))
   5007 
   5008 (defun lsp--document-link-handle-target (url)
   5009   (let* ((parsed-url (url-generic-parse-url (url-unhex-string url)))
   5010          (type (url-type parsed-url)))
   5011     (pcase type
   5012       ("file"
   5013        (xref-push-marker-stack)
   5014        (find-file (lsp--uri-to-path url))
   5015        (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url))
   5016          (goto-char (lsp--position-to-point
   5017                      (lsp-make-position :character (1- (string-to-number column))
   5018                                         :line (1- (string-to-number line)))))))
   5019       ((or "http" "https") (browse-url url))
   5020       (type (if-let ((handler (lsp--get-uri-handler type)))
   5021                 (funcall handler url)
   5022               (signal 'lsp-file-scheme-not-supported (list url)))))))
   5023 
   5024 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?))
   5025   (if target?
   5026       (lambda (_)
   5027         (interactive)
   5028         (lsp--document-link-handle-target target?))
   5029     (lambda (_)
   5030       (interactive)
   5031       (when (lsp:document-link-registration-options-resolve-provider?
   5032              (lsp--capability-for-method "textDocument/documentLink"))
   5033         (lsp-request-async
   5034          "documentLink/resolve"
   5035          link
   5036          (-lambda ((&DocumentLink :target?))
   5037            (lsp--document-link-handle-target target?)))))))
   5038 
   5039 
   5040 
   5041 (defcustom lsp-warn-no-matched-clients t
   5042   "Whether to show messages when there are no supported clients."
   5043   :group 'lsp-mode
   5044   :type 'boolean)
   5045 
   5046 (defun lsp-buffer-language--configured-id ()
   5047   "Return nil when not registered."
   5048   (->> lsp-language-id-configuration
   5049        (-first
   5050         (-lambda ((mode-or-pattern . language))
   5051           (cond
   5052            ((and (stringp mode-or-pattern)
   5053                  (s-matches? mode-or-pattern (buffer-file-name)))
   5054             language)
   5055            ((eq mode-or-pattern major-mode) language))))
   5056        cl-rest))
   5057 
   5058 (defvar-local lsp--buffer-language nil
   5059   "Locally cached returned value of `lsp-buffer-language'.")
   5060 
   5061 (defun lsp-buffer-language ()
   5062   "Get language corresponding current buffer."
   5063   (or lsp--buffer-language
   5064       (let* ((configured-language (lsp-buffer-language--configured-id)))
   5065         (setq lsp--buffer-language
   5066               (or configured-language
   5067                   ;; ensure non-nil
   5068                   (string-remove-suffix "-mode" (symbol-name major-mode))))
   5069         (when (and lsp-warn-no-matched-clients
   5070                    (null configured-language))
   5071           (lsp-warn "Unable to calculate the languageId for buffer `%s'. \
   5072 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s"
   5073                     (buffer-name)
   5074                     major-mode))
   5075         lsp--buffer-language)))
   5076 
   5077 (defun lsp-activate-on (&rest languages)
   5078   "Returns language activation function.
   5079 The function will return t when the `lsp-buffer-language' returns
   5080 one of the LANGUAGES."
   5081   (lambda (_file-name _mode)
   5082     (-contains? languages (lsp-buffer-language))))
   5083 
   5084 (defun lsp-workspace-root (&optional path)
   5085   "Find the workspace root for the current file or PATH."
   5086   (-when-let* ((file-name (or path (buffer-file-name)))
   5087                (file-name (lsp-f-canonical file-name)))
   5088     (->> (lsp-session)
   5089          (lsp-session-folders)
   5090          (--filter (and (lsp--files-same-host it file-name)
   5091                         (or (lsp-f-ancestor-of? it file-name)
   5092                             (equal it file-name))))
   5093          (--max-by (> (length it) (length other))))))
   5094 
   5095 (defun lsp-on-revert ()
   5096   "Executed when a file is reverted.
   5097 Added to `after-revert-hook'."
   5098   (let ((n (buffer-size))
   5099         (revert-buffer-in-progress-p nil))
   5100     (lsp-on-change 0 n n)))
   5101 
   5102 (defun lsp--text-document-did-close (&optional keep-workspace-alive)
   5103   "Executed when the file is closed, added to `kill-buffer-hook'.
   5104 
   5105 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace
   5106 if it's closing the last buffer in the workspace."
   5107   (lsp-foreach-workspace
   5108    (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   5109    (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S"
   5110      (lsp-notify "textDocument/didClose"
   5111                  `(:textDocument ,(lsp--text-document-identifier))))
   5112    (when (and (not lsp-keep-workspace-alive)
   5113               (not keep-workspace-alive)
   5114               (not (lsp--workspace-buffers lsp--cur-workspace)))
   5115      (lsp--shutdown-workspace))))
   5116 
   5117 (defun lsp--will-save-text-document-params (reason)
   5118   (list :textDocument (lsp--text-document-identifier)
   5119         :reason reason))
   5120 
   5121 (defun lsp--before-save ()
   5122   "Before save handler."
   5123   (with-demoted-errors "Error in ‘lsp--before-save’: %S"
   5124     (let ((params (lsp--will-save-text-document-params 1)))
   5125       (when (lsp--send-will-save-p)
   5126         (lsp-notify "textDocument/willSave" params))
   5127       (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits)
   5128         (let ((lsp-response-timeout 0.1))
   5129           (condition-case nil
   5130               (lsp--apply-text-edits
   5131                (lsp-request "textDocument/willSaveWaitUntil"
   5132                             params)
   5133                'before-save)
   5134             (error)))))))
   5135 
   5136 (defun lsp--on-auto-save ()
   5137   "Handler for auto-save."
   5138   (when (lsp--send-will-save-p)
   5139     (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S"
   5140       (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2)))))
   5141 
   5142 (defun lsp--text-document-did-save ()
   5143   "Executed when the file is closed, added to `after-save-hook''."
   5144   (when (lsp--send-did-save-p)
   5145     (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’"
   5146       (lsp-notify "textDocument/didSave"
   5147                   `( :textDocument ,(lsp--versioned-text-document-identifier)
   5148                      ,@(when (lsp--save-include-text-p)
   5149                          (list :text (lsp--buffer-content))))))))
   5150 
   5151 (defun lsp--text-document-position-params (&optional identifier position)
   5152   "Make TextDocumentPositionParams for the current point in the current document.
   5153 If IDENTIFIER and POSITION are non-nil, they will be used as the document
   5154 identifier and the position respectively."
   5155   (list :textDocument (or identifier (lsp--text-document-identifier))
   5156         :position (or position (lsp--cur-position))))
   5157 
   5158 (defun lsp--get-buffer-diagnostics ()
   5159   "Return buffer diagnostics."
   5160   (gethash (or
   5161             (plist-get lsp--virtual-buffer :buffer-file-name)
   5162             (lsp--fix-path-casing (buffer-file-name)))
   5163            (lsp-diagnostics t)))
   5164 
   5165 (defun lsp-cur-line-diagnostics ()
   5166   "Return any diagnostics that apply to the current line."
   5167   (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)]
   5168     (cl-coerce (-filter
   5169                 (-lambda ((&Diagnostic :range (&Range :start (&Position :line))))
   5170                   (and (>= line start) (<= line end)))
   5171                 (lsp--get-buffer-diagnostics))
   5172                'vector)))
   5173 
   5174 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end)
   5175                                   (right &as &Range :start right-start :end right-end))
   5176   (or (lsp-point-in-range? right-start left)
   5177       (lsp-point-in-range? right-end left)
   5178       (lsp-point-in-range? left-start right)
   5179       (lsp-point-in-range? left-end right)))
   5180 
   5181 (defun lsp-make-position-1 (position)
   5182   (lsp-make-position :line (plist-get position :line)
   5183                      :character (plist-get position :character)))
   5184 
   5185 (defun lsp-cur-possition-diagnostics ()
   5186   "Return any diagnostics that apply to the current line."
   5187   (-let* ((start (if (use-region-p) (region-beginning) (point)))
   5188           (end (if (use-region-p) (region-end) (point)))
   5189           (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start))
   5190                                          :end (lsp-make-position-1 (lsp-point-to-position end)))))
   5191     (->> (lsp--get-buffer-diagnostics)
   5192          (-filter
   5193           (-lambda ((&Diagnostic :range))
   5194             (lsp-range-overlapping? range current-range)))
   5195          (apply 'vector))))
   5196 
   5197 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics)
   5198 
   5199 (defun lsp--extract-line-from-buffer (pos)
   5200   "Return the line pointed to by POS (a Position object) in the current buffer."
   5201   (let* ((point (lsp--position-to-point pos))
   5202          (inhibit-field-text-motion t))
   5203     (save-excursion
   5204       (goto-char point)
   5205       (buffer-substring (line-beginning-position) (line-end-position)))))
   5206 
   5207 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line)
   5208                                                  :end (end &as &Position :character end-char)))
   5209   "Return a xref-item from a RANGE in FILENAME."
   5210   (let* ((line (lsp--extract-line-from-buffer start))
   5211          (len (length line)))
   5212     (add-face-text-property (max (min start-char len) 0)
   5213                             (max (min end-char len) 0)
   5214                             'xref-match t line)
   5215     ;; LINE is nil when FILENAME is not being current visited by any buffer.
   5216     (xref-make-match (or line filename)
   5217                      (xref-make-file-location
   5218                       filename
   5219                       (lsp-translate-line (1+ start-line))
   5220                       (lsp-translate-column start-char))
   5221                      (- end-char start-char))))
   5222 
   5223 (defun lsp--location-uri (loc)
   5224   (if (lsp-location? loc)
   5225       (lsp:location-uri loc)
   5226     (lsp:location-link-target-uri loc)))
   5227 
   5228 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start)))
   5229   "Go to location."
   5230   (let ((path (lsp--uri-to-path uri)))
   5231     (if (f-exists? path)
   5232         (with-current-buffer (find-file path)
   5233           (goto-char (lsp--position-to-point start)))
   5234       (error "There is no file %s" path))))
   5235 
   5236 (defun lsp--location-range (loc)
   5237   (if (lsp-location? loc)
   5238       (lsp:location-range loc)
   5239     (lsp:location-link-target-selection-range loc)))
   5240 
   5241 (defun lsp--locations-to-xref-items (locations)
   5242   "Return a list of `xref-item' given LOCATIONS, which can be of
   5243 type Location, LocationLink, Location[] or LocationLink[]."
   5244   (setq locations
   5245         (pcase locations
   5246           ((seq (or (Location)
   5247                     (LocationLink)))
   5248            (append locations nil))
   5249           ((or (Location)
   5250                (LocationLink))
   5251            (list locations))))
   5252 
   5253   (cl-labels ((get-xrefs-in-file
   5254                (file-locs)
   5255                (-let [(filename . matches) file-locs]
   5256                  (condition-case err
   5257                      (let ((visiting (find-buffer-visiting filename))
   5258                            (fn (lambda (loc)
   5259                                  (lsp-with-filename filename
   5260                                    (lsp--xref-make-item filename
   5261                                                         (lsp--location-range loc))))))
   5262                        (if visiting
   5263                            (with-current-buffer visiting
   5264                              (seq-map fn matches))
   5265                          (when (file-readable-p filename)
   5266                            (with-temp-buffer
   5267                              (insert-file-contents-literally filename)
   5268                              (seq-map fn matches)))))
   5269                    (error (lsp-warn "Failed to process xref entry for filename '%s': %s"
   5270                                     filename (error-message-string err)))
   5271                    (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s"
   5272                                          filename (error-message-string err)))))))
   5273 
   5274     (->> locations
   5275          (seq-sort #'lsp--location-before-p)
   5276          (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri))
   5277          (seq-map #'get-xrefs-in-file)
   5278          (apply #'nconc))))
   5279 
   5280 (defun lsp--location-before-p (left right)
   5281   "Sort first by file, then by line, then by column."
   5282   (let ((left-uri (lsp--location-uri left))
   5283         (right-uri (lsp--location-uri right)))
   5284     (if (not (string= left-uri right-uri))
   5285         (string< left-uri right-uri)
   5286       (-let (((&Range :start left-start) (lsp--location-range left))
   5287              ((&Range :start right-start) (lsp--location-range right)))
   5288         (lsp--position-compare right-start left-start)))))
   5289 
   5290 (defun lsp--make-reference-params (&optional td-position exclude-declaration)
   5291   "Make a ReferenceParam object.
   5292 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead.
   5293 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations."
   5294   (let ((json-false :json-false))
   5295     (plist-put (or td-position (lsp--text-document-position-params))
   5296                :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration))))))
   5297 
   5298 (defun lsp--cancel-request (id)
   5299   "Cancel request with ID in all workspaces."
   5300   (lsp-foreach-workspace
   5301    (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id))
   5302    (lsp-notify "$/cancelRequest" `(:id ,id))))
   5303 
   5304 (defvar-local lsp--hover-saved-bounds nil)
   5305 
   5306 (defun lsp-eldoc-function (cb &rest _ignored)
   5307   "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')."
   5308   (if (and lsp--hover-saved-bounds
   5309            (lsp--point-in-bounds-p lsp--hover-saved-bounds))
   5310       lsp--eldoc-saved-message
   5311     (setq lsp--hover-saved-bounds nil
   5312           lsp--eldoc-saved-message nil)
   5313     (if (looking-at-p "[[:space:]\n]")
   5314         (setq lsp--eldoc-saved-message nil) ; And returns nil.
   5315       (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover"))
   5316         (lsp-request-async
   5317          "textDocument/hover"
   5318          (lsp--text-document-position-params)
   5319          (-lambda ((hover &as &Hover? :range? :contents))
   5320            (setq lsp--hover-saved-bounds (when range?
   5321                                            (lsp--range-to-region range?)))
   5322            (funcall cb (setq lsp--eldoc-saved-message
   5323                              (when contents
   5324                                (lsp--render-on-hover-content
   5325                                 contents
   5326                                 lsp-eldoc-render-all)))))
   5327          :error-handler #'ignore
   5328          :mode 'tick
   5329          :cancel-token :eldoc-hover)))))
   5330 
   5331 (defun lsp--point-on-highlight? ()
   5332   (-some? (lambda (overlay)
   5333             (overlay-get overlay 'lsp-highlight))
   5334           (overlays-at (point))))
   5335 
   5336 (defun lsp--cleanup-highlights-if-needed ()
   5337   (when (and lsp-enable-symbol-highlighting
   5338              lsp--have-document-highlights
   5339              (not (lsp--point-on-highlight?)))
   5340     (lsp--remove-overlays 'lsp-highlight)
   5341     (setq lsp--have-document-highlights nil)
   5342     (lsp-cancel-request-by-token :highlights)))
   5343 
   5344 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil
   5345   "The bounds of the symbol from which `lsp--document-highlight'
   5346   most recently requested highlights.")
   5347 
   5348 (defun lsp--document-highlight ()
   5349   (when (lsp-feature? "textDocument/documentHighlight")
   5350     (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol)))
   5351       (unless (or (looking-at-p "[[:space:]\n]")
   5352                   (not lsp-enable-symbol-highlighting)
   5353                   (and lsp--have-document-highlights
   5354                        curr-sym-bounds
   5355                        (equal curr-sym-bounds
   5356                               lsp--symbol-bounds-of-last-highlight-invocation)))
   5357         (setq lsp--symbol-bounds-of-last-highlight-invocation
   5358               curr-sym-bounds)
   5359         (lsp-request-async "textDocument/documentHighlight"
   5360                            (lsp--text-document-position-params)
   5361                            #'lsp--document-highlight-callback
   5362                            :mode 'tick
   5363                            :cancel-token :highlights)))))
   5364 
   5365 (defun lsp--help-open-link (&rest _)
   5366   "Open markdown link at point via mouse or keyboard."
   5367   (interactive "P")
   5368   (let ((buffer-list-update-hook nil))
   5369     (-let [(buffer point) (if-let* ((valid (and (listp last-input-event)
   5370                                                 (eq (car last-input-event) 'mouse-2)))
   5371                                     (event (cadr last-input-event))
   5372                                     (win (posn-window event))
   5373                                     (buffer (window-buffer win)))
   5374                               `(,buffer ,(posn-point event))
   5375                             `(,(current-buffer) ,(point)))]
   5376       (with-current-buffer buffer
   5377         (when-let* ((face (get-text-property point 'face))
   5378                     (url (or (and (eq face 'markdown-link-face)
   5379                                   (get-text-property point 'help-echo))
   5380                              (and (memq face '(markdown-url-face markdown-plain-url-face))
   5381                                   (nth 3 (markdown-link-at-pos point))))))
   5382           (lsp--document-link-handle-target url))))))
   5383 
   5384 (defvar lsp-help-mode-map
   5385   (-doto (make-sparse-keymap)
   5386     (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link))
   5387   "Keymap for `lsp-help-mode'.")
   5388 
   5389 (define-derived-mode lsp-help-mode help-mode "LspHelp"
   5390   "Major mode for displaying lsp help.")
   5391 
   5392 (defun lsp-describe-thing-at-point ()
   5393   "Display the type signature and documentation of the thing at point."
   5394   (interactive)
   5395   (let ((contents (-some->> (lsp--text-document-position-params)
   5396                     (lsp--make-request "textDocument/hover")
   5397                     (lsp--send-request)
   5398                     (lsp:hover-contents))))
   5399     (if (and contents (not (equal contents "")))
   5400         (let ((lsp-help-buf-name "*lsp-help*"))
   5401           (with-current-buffer (get-buffer-create lsp-help-buf-name)
   5402             (delay-mode-hooks
   5403               (lsp-help-mode)
   5404               (with-help-window lsp-help-buf-name
   5405                 (insert (string-trim-right (lsp--render-on-hover-content contents t)))))
   5406             (run-mode-hooks)))
   5407       (lsp--info "No content at point."))))
   5408 
   5409 (defun lsp--point-in-bounds-p (bounds)
   5410   "Return whether the current point is within BOUNDS."
   5411   (and (<= (car bounds) (point)) (< (point) (cdr bounds))))
   5412 
   5413 (defun lsp-get-renderer (language)
   5414   "Get renderer for LANGUAGE."
   5415   (lambda (str)
   5416     (lsp--render-string str language)))
   5417 
   5418 (defun lsp--setup-markdown (mode)
   5419   "Setup the ‘markdown-mode’ in the frame.
   5420 MODE is the mode used in the parent frame."
   5421   (make-local-variable 'markdown-code-lang-modes)
   5422   (dolist (mark (alist-get mode lsp-custom-markup-modes))
   5423     (add-to-list 'markdown-code-lang-modes (cons mark mode)))
   5424   (setq-local markdown-fontify-code-blocks-natively t)
   5425   (setq-local markdown-fontify-code-block-default-mode mode)
   5426   (setq-local markdown-hide-markup t)
   5427 
   5428   ;; Render some common HTML entities.
   5429   ;; This should really happen in markdown-mode instead,
   5430   ;; but it doesn't, so we do it here for now.
   5431   (setq prettify-symbols-alist
   5432         (cl-loop for i from 0 to 255
   5433                  collect (cons (format "&#x%02X;" i) i)))
   5434   (push '("&lt;" . ?<) prettify-symbols-alist)
   5435   (push '("&gt;" . ?>) prettify-symbols-alist)
   5436   (push '("&amp;" . ?&) prettify-symbols-alist)
   5437   (push '("&nbsp;" . ? ) prettify-symbols-alist)
   5438   (setq prettify-symbols-compose-predicate
   5439         (lambda (_start _end _match) t))
   5440   (prettify-symbols-mode 1))
   5441 
   5442 (defvar lsp-help-link-keymap
   5443   (let ((map (make-sparse-keymap)))
   5444     (define-key map [mouse-2] #'lsp--help-open-link)
   5445     (define-key map "\r" #'lsp--help-open-link)
   5446     map)
   5447   "Keymap active on links in *lsp-help* mode.")
   5448 
   5449 (defun lsp--fix-markdown-links ()
   5450   (let ((inhibit-read-only t)
   5451         (inhibit-modification-hooks t)
   5452         (prop))
   5453     (save-restriction
   5454       (goto-char (point-min))
   5455       (while (setq prop (markdown-find-next-prop 'face))
   5456         (let ((end (or (next-single-property-change (car prop) 'face)
   5457                        (point-max))))
   5458           (when (memq (get-text-property (car prop) 'face)
   5459                       '(markdown-link-face
   5460                         markdown-url-face
   5461                         markdown-plain-url-face))
   5462             (add-text-properties (car prop) end
   5463                                  (list 'button t
   5464                                        'category 'lsp-help-link
   5465                                        'follow-link t
   5466                                        'keymap lsp-help-link-keymap)))
   5467           (goto-char end))))))
   5468 
   5469 (defun lsp--buffer-string-visible ()
   5470   "Return visible buffer string.
   5471 Stolen from `org-copy-visible'."
   5472   (let ((temp (generate-new-buffer " *temp*"))
   5473         (beg (point-min))
   5474         (end (point-max)))
   5475     (while (/= beg end)
   5476       (when (get-char-property beg 'invisible)
   5477         (setq beg (next-single-char-property-change beg 'invisible nil end)))
   5478       (let* ((next (next-single-char-property-change beg 'invisible nil end))
   5479              (substring (buffer-substring beg next)))
   5480         (with-current-buffer temp (insert substring))
   5481         ;; (setq result (concat result substring))
   5482         (setq beg next)))
   5483     (setq deactivate-mark t)
   5484     (prog1 (with-current-buffer temp
   5485              (s-chop-suffix "\n" (buffer-string)))
   5486       (kill-buffer temp))))
   5487 
   5488 (defvar lsp-buffer-major-mode nil
   5489   "Holds the major mode when fontification function is running.
   5490 See #2588")
   5491 
   5492 (defvar view-inhibit-help-message)
   5493 
   5494 (defun lsp--render-markdown ()
   5495   "Render markdown."
   5496 
   5497   (let ((markdown-enable-math nil))
   5498     (goto-char (point-min))
   5499     (while (re-search-forward
   5500             (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/"
   5501                                      "{" "}" "[" "]" "(" ")"
   5502                                      "#" "+" "-" "." "!" "|"))))
   5503             nil t)
   5504       (replace-match (rx (backref 1))))
   5505 
   5506     ;; markdown-mode v2.3 does not yet provide gfm-view-mode
   5507     (if (fboundp 'gfm-view-mode)
   5508         (let ((view-inhibit-help-message t))
   5509           (gfm-view-mode))
   5510       (gfm-mode))
   5511 
   5512     (lsp--setup-markdown lsp-buffer-major-mode)))
   5513 
   5514 (defvar lsp--display-inline-image-alist
   5515   '((lsp--render-markdown
   5516      (:regexp
   5517       "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)"
   5518       :sexp
   5519       (create-image
   5520        (base64-decode-string
   5521         (buffer-substring-no-properties (match-beginning 1) (match-end 1)))
   5522        nil t))))
   5523   "Replaced string regexp and function returning image.
   5524 Each element should have the form (MODE . (PROPERTY-LIST...)).
   5525 MODE (car) is function which is defined in `lsp-language-id-configuration'.
   5526 Cdr should be list of PROPERTY-LIST.
   5527 
   5528 Each PROPERTY-LIST should have properties:
   5529 :regexp  Regexp which determines what string is relpaced to image.
   5530          You should also get information of image, by parenthesis constructs.
   5531          By default, all matched string is replaced to image, but you can
   5532          change index of replaced string by keyword :replaced-index.
   5533 
   5534 :sexp    Return image when evaluated. You can use information of regexp
   5535          by using (match-beggining N), (match-end N) or (match-substring N).
   5536 
   5537 In addition, each can have property:
   5538 :replaced-index  Determine index which is used to replace regexp to image.
   5539                  The value means first argument of `match-beginning' and
   5540                  `match-end'. If omitted, interpreted as index 0.")
   5541 
   5542 (defcustom lsp-display-inline-image t
   5543   "Showing inline image or not."
   5544   :group 'lsp-mode
   5545   :type 'boolean)
   5546 
   5547 (defcustom lsp-enable-suggest-server-download t
   5548   "When non-nil enable server downloading suggestions."
   5549   :group 'lsp-mode
   5550   :type 'boolean
   5551   :package-version '(lsp-mode . "9.0.0"))
   5552 
   5553 (defcustom lsp-auto-register-remote-clients t
   5554   "When non-nil register remote when registering the local one."
   5555   :group 'lsp-mode
   5556   :type 'boolean
   5557   :package-version '(lsp-mode . "9.0.0"))
   5558 
   5559 (defun lsp--display-inline-image (mode)
   5560   "Add image property if available."
   5561   (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist))))
   5562     (when (and (display-images-p) lsp-display-inline-image)
   5563       (cl-loop
   5564        for plist in plist-list
   5565        with regexp with replaced-index
   5566        do
   5567        (setq regexp (plist-get plist :regexp))
   5568        (setq replaced-index (or (plist-get plist :replaced-index) 0))
   5569 
   5570        (font-lock-remove-keywords nil (list regexp replaced-index))
   5571        (let ((inhibit-read-only t))
   5572          (save-excursion
   5573            (goto-char (point-min))
   5574            (while (re-search-forward regexp nil t)
   5575              (set-text-properties
   5576               (match-beginning replaced-index) (match-end replaced-index)
   5577               nil)
   5578              (add-text-properties
   5579               (match-beginning replaced-index) (match-end replaced-index)
   5580               `(display ,(eval (plist-get plist :sexp)))))))))))
   5581 
   5582 (defun lsp--fontlock-with-mode (str mode)
   5583   "Fontlock STR with MODE."
   5584   (let ((lsp-buffer-major-mode major-mode))
   5585     (with-temp-buffer
   5586       (with-demoted-errors "Error during doc rendering: %s"
   5587         (insert str)
   5588         (delay-mode-hooks (funcall mode))
   5589         (cl-flet ((window-body-width () lsp-window-body-width))
   5590           ;; This can go wrong in some cases, and the fontification would
   5591           ;; not work as expected.
   5592           ;;
   5593           ;; See #2984
   5594           (ignore-errors (font-lock-ensure))
   5595           (lsp--display-inline-image mode)
   5596           (when (eq mode 'lsp--render-markdown)
   5597             (lsp--fix-markdown-links))))
   5598       (lsp--buffer-string-visible))))
   5599 
   5600 (defun lsp--render-string (str language)
   5601   "Render STR using `major-mode' corresponding to LANGUAGE.
   5602 When language is nil render as markup if `markdown-mode' is loaded."
   5603   (setq str (s-replace "\r" "" (or str "")))
   5604   (if-let* ((modes (-keep (-lambda ((mode . lang))
   5605                             (when (and (equal lang language) (functionp mode))
   5606                               mode))
   5607                           lsp-language-id-configuration))
   5608             (mode (car (or (member major-mode modes) modes))))
   5609       (lsp--fontlock-with-mode str mode)
   5610     str))
   5611 
   5612 (defun lsp--render-element (content)
   5613   "Render CONTENT element."
   5614   (let ((inhibit-message t))
   5615     (or
   5616      (pcase content
   5617        ((MarkedString :value :language)
   5618         (lsp--render-string value language))
   5619        ((MarkupContent :value :kind)
   5620         (lsp--render-string value kind))
   5621        ;; plain string
   5622        ((pred stringp) (lsp--render-string content "markdown"))
   5623        ((pred null) "")
   5624        (_ (error "Failed to handle %s" content)))
   5625      "")))
   5626 
   5627 (defun lsp--create-unique-string-fn ()
   5628   (let (elements)
   5629     (lambda (element)
   5630       (let ((count (cl-count element elements :test #'string=)))
   5631         (prog1 (if (zerop count)
   5632                    element
   5633                  (format "%s (%s)" element count))
   5634           (push element elements))))))
   5635 
   5636 (defun lsp--select-action (actions)
   5637   "Select an action to execute from ACTIONS."
   5638   (cond
   5639    ((seq-empty-p actions) (signal 'lsp-no-code-actions nil))
   5640    ((and (eq (seq-length actions) 1) lsp-auto-execute-action)
   5641     (lsp-seq-first actions))
   5642    (t (let ((completion-ignore-case t))
   5643         (lsp--completing-read "Select code action: "
   5644                               (seq-into actions 'list)
   5645                               (-compose (lsp--create-unique-string-fn)
   5646                                         #'lsp:code-action-title)
   5647                               nil t)))))
   5648 
   5649 (defun lsp--workspace-server-id (workspace)
   5650   "Return the server ID of WORKSPACE."
   5651   (-> workspace lsp--workspace-client lsp--client-server-id))
   5652 
   5653 (defun lsp--handle-rendered-for-echo-area (contents)
   5654   "Return a single line from RENDERED, appropriate for display in the echo area."
   5655   (pcase (lsp-workspaces)
   5656     (`(,workspace)
   5657      (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace)))
   5658     ;; For projects with multiple active workspaces we also default to
   5659     ;; render the first line.
   5660     (_ (lsp-clients-extract-signature-on-hover contents nil))))
   5661 
   5662 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id)
   5663   "Extract a representative line from CONTENTS, to show in the echo area."
   5664   (car (s-lines (s-trim (lsp--render-element contents)))))
   5665 
   5666 (defun lsp--render-on-hover-content (contents render-all)
   5667   "Render the content received from `document/onHover' request.
   5668 CONTENTS  - MarkedString | MarkedString[] | MarkupContent
   5669 RENDER-ALL - nil if only the signature should be rendered."
   5670   (cond
   5671    ((lsp-markup-content? contents)
   5672     ;; MarkupContent.
   5673     ;; It tends to be long and is not suitable to display fully in the echo area.
   5674     ;; Just display the first line which is typically the signature.
   5675     (if render-all
   5676         (lsp--render-element contents)
   5677       (lsp--handle-rendered-for-echo-area contents)))
   5678    ((and (stringp contents) (not (string-match-p "\n" contents)))
   5679     ;; If the contents is a single string containing a single line,
   5680     ;; render it always.
   5681     (lsp--render-element contents))
   5682    (t
   5683     ;; MarkedString -> MarkedString[]
   5684     (when (or (lsp-marked-string? contents) (stringp contents))
   5685       (setq contents (list contents)))
   5686     ;; Consider the signature consisting of the elements who have a renderable
   5687     ;; "language" property. When render-all is nil, ignore other elements.
   5688     (string-join
   5689      (seq-map
   5690       #'lsp--render-element
   5691       (if render-all
   5692           contents
   5693         ;; Only render contents that have an available renderer.
   5694         (seq-take
   5695          (seq-filter
   5696           (-andfn #'lsp-marked-string?
   5697                   (-compose #'lsp-get-renderer #'lsp:marked-string-language))
   5698           contents)
   5699          1)))
   5700      (if (bound-and-true-p page-break-lines-mode)
   5701          "\n\n"
   5702        "\n")))))
   5703 
   5704 
   5705 
   5706 (defvar lsp-signature-mode-map
   5707   (-doto (make-sparse-keymap)
   5708     (define-key (kbd "M-n") #'lsp-signature-next)
   5709     (define-key (kbd "M-p") #'lsp-signature-previous)
   5710     (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs)
   5711     (define-key (kbd "C-c C-k") #'lsp-signature-stop)
   5712     (define-key (kbd "C-g") #'lsp-signature-stop))
   5713   "Keymap for `lsp-signature-mode'.")
   5714 
   5715 (define-minor-mode lsp-signature-mode
   5716   "Mode used to show signature popup."
   5717   :keymap lsp-signature-mode-map
   5718   :lighter ""
   5719   :group 'lsp-mode)
   5720 
   5721 (defun lsp-signature-stop ()
   5722   "Stop showing current signature help."
   5723   (interactive)
   5724   (lsp-cancel-request-by-token :signature)
   5725   (remove-hook 'post-command-hook #'lsp-signature)
   5726   (funcall lsp-signature-function nil)
   5727   (lsp-signature-mode -1))
   5728 
   5729 (declare-function page-break-lines--update-display-tables "ext:page-break-lines")
   5730 
   5731 (defun lsp--setup-page-break-mode-if-present ()
   5732   "Enable `page-break-lines-mode' in current buffer."
   5733   (when (fboundp 'page-break-lines-mode)
   5734     (page-break-lines-mode)
   5735     ;; force page-break-lines-mode to update the display tables.
   5736     (page-break-lines--update-display-tables)))
   5737 
   5738 (defun lsp-lv-message (message)
   5739   (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)
   5740   (if message
   5741       (progn
   5742         (setq lsp--signature-last-buffer (current-buffer))
   5743         (let ((lv-force-update t))
   5744           (lv-message "%s" message)))
   5745     (lv-delete-window)
   5746     (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)))
   5747 
   5748 (declare-function posframe-show "ext:posframe")
   5749 (declare-function posframe-hide "ext:posframe")
   5750 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe")
   5751 
   5752 (defface lsp-signature-posframe
   5753   '((t :inherit tooltip))
   5754   "Background and foreground for `lsp-signature-posframe'."
   5755   :group 'lsp-mode)
   5756 
   5757 (defvar lsp-signature-posframe-params
   5758   (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward
   5759         :height 10
   5760         :width 60
   5761         :border-width 1
   5762         :min-width 60)
   5763   "Params for signature and `posframe-show'.")
   5764 
   5765 (defun lsp-signature-posframe (str)
   5766   "Use posframe to show the STR signatureHelp string."
   5767   (if str
   5768       (apply #'posframe-show
   5769              (with-current-buffer (get-buffer-create " *lsp-signature*")
   5770                (erase-buffer)
   5771                (insert str)
   5772                (visual-line-mode 1)
   5773                (lsp--setup-page-break-mode-if-present)
   5774                (current-buffer))
   5775              (append
   5776               lsp-signature-posframe-params
   5777               (list :position (point)
   5778                     :background-color (face-attribute 'lsp-signature-posframe :background nil t)
   5779                     :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t)
   5780                     :border-color (face-attribute 'font-lock-comment-face :foreground nil t))))
   5781     (posframe-hide " *lsp-signature*")))
   5782 
   5783 (defun lsp--handle-signature-update (signature)
   5784   (let ((message
   5785          (if (lsp-signature-help? signature)
   5786              (lsp--signature->message signature)
   5787            (mapconcat #'lsp--signature->message signature "\n"))))
   5788     (if (s-present? message)
   5789         (funcall lsp-signature-function message)
   5790       (lsp-signature-stop))))
   5791 
   5792 (defun lsp-signature-activate ()
   5793   "Activate signature help.
   5794 It will show up only if current point has signature help."
   5795   (interactive)
   5796   (setq lsp--signature-last nil
   5797         lsp--signature-last-index nil
   5798         lsp--signature-last-buffer (current-buffer))
   5799   (add-hook 'post-command-hook #'lsp-signature)
   5800   (lsp-signature-mode t))
   5801 
   5802 (defcustom lsp-signature-cycle t
   5803   "Whether `lsp-signature-next' and prev should cycle."
   5804   :type 'boolean
   5805   :group 'lsp-mode)
   5806 
   5807 (defun lsp-signature-next ()
   5808   "Show next signature."
   5809   (interactive)
   5810   (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last))))
   5811     (when (and lsp--signature-last-index
   5812                lsp--signature-last
   5813                (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs)))
   5814       (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs))
   5815       (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))))
   5816 
   5817 (defun lsp-signature-previous ()
   5818   "Next signature."
   5819   (interactive)
   5820   (when (and lsp--signature-last-index
   5821              lsp--signature-last
   5822              (or lsp-signature-cycle (not (zerop lsp--signature-last-index))))
   5823     (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index)
   5824                                             (length (lsp:signature-help-signatures lsp--signature-last))
   5825                                           lsp--signature-last-index)))
   5826     (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))
   5827 
   5828 (defun lsp-signature-toggle-full-docs ()
   5829   "Toggle full/partial signature documentation."
   5830   (interactive)
   5831   (let ((all? (not (numberp lsp-signature-doc-lines))))
   5832     (setq lsp-signature-doc-lines (if all?
   5833                                       (or (car-safe lsp-signature-doc-lines)
   5834                                           20)
   5835                                     (list lsp-signature-doc-lines))))
   5836   (lsp-signature-activate))
   5837 
   5838 (defun lsp--signature->message (signature-help)
   5839   "Generate eldoc message from SIGNATURE-HELP response."
   5840   (setq lsp--signature-last signature-help)
   5841 
   5842   (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help))))
   5843     (-let* (((&SignatureHelp :active-signature?
   5844                              :active-parameter?
   5845                              :signatures) signature-help)
   5846             (active-signature? (or lsp--signature-last-index active-signature? 0))
   5847             (_ (setq lsp--signature-last-index active-signature?))
   5848             ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?))
   5849             (prefix (if (= (length signatures) 1)
   5850                         ""
   5851                       (concat (propertize (format " %s/%s"
   5852                                                   (1+ active-signature?)
   5853                                                   (length signatures))
   5854                                           'face 'success)
   5855                               " ")))
   5856             (method-docs (when
   5857                              (and lsp-signature-render-documentation
   5858                                   (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines)))
   5859                            (let ((docs (lsp--render-element
   5860                                         (lsp:parameter-information-documentation? signature))))
   5861                              (when (s-present? docs)
   5862                                (concat
   5863                                 "\n"
   5864                                 (if (fboundp 'page-break-lines-mode)
   5865                                     "\n"
   5866                                   "")
   5867                                 (if (and (numberp lsp-signature-doc-lines)
   5868                                          (> (length (s-lines docs)) lsp-signature-doc-lines))
   5869                                     (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs)))
   5870                                             (propertize "\nTruncated..." 'face 'highlight))
   5871                                   docs)))))))
   5872       (when (and active-parameter? (not (seq-empty-p parameters?)))
   5873         (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?)))
   5874                               (seq-elt parameters? active-parameter?)))
   5875                      (selected-param-label (let ((label (lsp:parameter-information-label param)))
   5876                                              (if (stringp label) label (append label nil))))
   5877                      (start (if (stringp selected-param-label)
   5878                                 (s-index-of selected-param-label label)
   5879                               (cl-first selected-param-label)))
   5880                      (end (if (stringp selected-param-label)
   5881                               (+ start (length selected-param-label))
   5882                             (cl-second selected-param-label))))
   5883           (add-face-text-property start end 'eldoc-highlight-function-argument nil label)))
   5884       (concat prefix label method-docs))))
   5885 
   5886 (defun lsp-signature ()
   5887   "Display signature info (based on `textDocument/signatureHelp')"
   5888   (if (and lsp--signature-last-buffer
   5889            (not (equal (current-buffer) lsp--signature-last-buffer)))
   5890       (lsp-signature-stop)
   5891     (lsp-request-async "textDocument/signatureHelp"
   5892                        (lsp--text-document-position-params)
   5893                        #'lsp--handle-signature-update
   5894                        :cancel-token :signature)))
   5895 
   5896 
   5897 (defcustom lsp-overlay-document-color-char "■"
   5898   "Display the char represent the document color in overlay"
   5899   :type 'string
   5900   :group 'lsp-mode)
   5901 
   5902 ;; color presentation
   5903 (defun lsp--color-create-interactive-command (color range)
   5904   (lambda ()
   5905     (interactive)
   5906     (-let [(&ColorPresentation? :text-edit?
   5907                                 :additional-text-edits?)
   5908            (lsp--completing-read
   5909             "Select color presentation: "
   5910             (lsp-request
   5911              "textDocument/colorPresentation"
   5912              `( :textDocument ,(lsp--text-document-identifier)
   5913                 :color ,color
   5914                 :range ,range))
   5915             #'lsp:color-presentation-label
   5916             nil
   5917             t)]
   5918       (when text-edit?
   5919         (lsp--apply-text-edit text-edit?))
   5920       (when additional-text-edits?
   5921         (lsp--apply-text-edits additional-text-edits? 'color-presentation)))))
   5922 
   5923 (defun lsp--number->color (number)
   5924   (let ((result (format "%x"
   5925                         (round (* (or number 0) 255.0)))))
   5926     (if (= 1 (length result))
   5927         (concat "0" result)
   5928       result)))
   5929 
   5930 (defun lsp--document-color ()
   5931   "Document color handler."
   5932   (when (lsp-feature? "textDocument/documentColor")
   5933     (lsp-request-async
   5934      "textDocument/documentColor"
   5935      `(:textDocument ,(lsp--text-document-identifier))
   5936      (lambda (result)
   5937        (lsp--remove-overlays 'lsp-color)
   5938        (seq-do
   5939         (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue)
   5940                                      :range))
   5941           (-let* (((beg . end) (lsp--range-to-region range))
   5942                   (overlay (make-overlay beg end))
   5943                   (command (lsp--color-create-interactive-command color range)))
   5944             (overlay-put overlay 'lsp-color t)
   5945             (overlay-put overlay 'evaporate t)
   5946             (overlay-put overlay
   5947                          'before-string
   5948                          (propertize
   5949                           lsp-overlay-document-color-char
   5950                           'face `((:foreground ,(format
   5951                                                  "#%s%s%s"
   5952                                                  (lsp--number->color red)
   5953                                                  (lsp--number->color green)
   5954                                                  (lsp--number->color blue))))
   5955                           'action command
   5956                           'mouse-face 'lsp-lens-mouse-face
   5957                           'local-map (-doto (make-sparse-keymap)
   5958                                        (define-key [mouse-1] command))))))
   5959         result))
   5960      :mode 'unchanged
   5961      :cancel-token :document-color-token)))
   5962 
   5963 
   5964 
   5965 (defun lsp--action-trigger-parameter-hints (_command)
   5966   "Handler for editor.action.triggerParameterHints."
   5967   (when (member :on-server-request lsp-signature-auto-activate)
   5968     (lsp-signature-activate)))
   5969 
   5970 (defun lsp--action-trigger-suggest (_command)
   5971   "Handler for editor.action.triggerSuggest."
   5972   (cond
   5973    ((and (bound-and-true-p company-mode)
   5974          (fboundp 'company-auto-begin)
   5975          (fboundp 'company-post-command))
   5976     (run-at-time 0 nil
   5977                  (lambda ()
   5978                    (let ((this-command 'company-idle-begin)
   5979                          (company-minimum-prefix-length 0))
   5980                      (company-auto-begin)
   5981                      (company-post-command)))))
   5982    (t
   5983     (completion-at-point))))
   5984 
   5985 (defconst lsp--default-action-handlers
   5986   (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints)
   5987       ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest))
   5988   "Default action handlers.")
   5989 
   5990 (defun lsp--find-action-handler (command)
   5991   "Find action handler for particular COMMAND."
   5992   (or
   5993    (--some (-some->> it
   5994              (lsp--workspace-client)
   5995              (lsp--client-action-handlers)
   5996              (gethash command))
   5997            (lsp-workspaces))
   5998    (gethash command lsp--default-action-handlers)))
   5999 
   6000 (defun lsp--text-document-code-action-params (&optional kind)
   6001   "Code action params."
   6002   (list :textDocument (lsp--text-document-identifier)
   6003         :range (if (use-region-p)
   6004                    (lsp--region-to-range (region-beginning) (region-end))
   6005                  (lsp--region-to-range (point) (point)))
   6006         :context `( :diagnostics ,(lsp-cur-possition-diagnostics)
   6007                     ,@(when kind (list :only (vector kind))))))
   6008 
   6009 (defun lsp-code-actions-at-point (&optional kind)
   6010   "Retrieve the code actions for the active region or the current line.
   6011 It will filter by KIND if non nil."
   6012   (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind)))
   6013 
   6014 (defun lsp-execute-code-action-by-kind (command-kind)
   6015   "Execute code action by COMMAND-KIND."
   6016   (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind)
   6017                         (-filter (-lambda ((&CodeAction :kind?))
   6018                                    (and kind? (s-prefix? command-kind kind?))))
   6019                         lsp--select-action)))
   6020       (lsp-execute-code-action action)
   6021     (signal 'lsp-no-code-actions '(command-kind))))
   6022 
   6023 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point)
   6024 
   6025 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?))
   6026   "Parse and execute a code ACTION represented as a Command LSP type."
   6027   (let ((server-id (->> (lsp-workspaces)
   6028                         (cl-first)
   6029                         (or lsp--cur-workspace)
   6030                         (lsp--workspace-client)
   6031                         (lsp--client-server-id))))
   6032     (condition-case nil
   6033         (with-no-warnings
   6034           (lsp-execute-command server-id (intern command) arguments?))
   6035       (cl-no-applicable-method
   6036        (if-let ((action-handler (lsp--find-action-handler command)))
   6037            (funcall action-handler action)
   6038          (lsp-send-execute-command command arguments?))))))
   6039 
   6040 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?))
   6041   "Execute code action ACTION. For example, when text under the
   6042 caret has a suggestion to apply a fix from an lsp-server, calling
   6043 this function will do so.
   6044 If ACTION is not set it will be selected from `lsp-code-actions-at-point'.
   6045 Request codeAction/resolve for more info if server supports."
   6046   (interactive (list (lsp--select-action (lsp-code-actions-at-point))))
   6047   (if (and (lsp-feature? "codeAction/resolve")
   6048            (not command?)
   6049            (not edit?))
   6050       (lsp--execute-code-action (lsp-request "codeAction/resolve" action))
   6051     (lsp--execute-code-action action)))
   6052 
   6053 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?))
   6054   "Execute code action ACTION."
   6055   (when edit?
   6056     (lsp--apply-workspace-edit edit? 'code-action))
   6057 
   6058   (cond
   6059    ((stringp command?) (lsp--execute-command action))
   6060    ((lsp-command? command?) (progn
   6061                               (when-let ((action-filter (->> (lsp-workspaces)
   6062                                                              (cl-first)
   6063                                                              (or lsp--cur-workspace)
   6064                                                              (lsp--workspace-client)
   6065                                                              (lsp--client-action-filter))))
   6066                                 (funcall action-filter command?))
   6067                               (lsp--execute-command command?)))))
   6068 
   6069 (lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments)
   6070   "Patch incorrect boolean argument values in the provided `CodeAction' command
   6071 in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values
   6072 in this list can be either symbols or lists of symbols that
   6073 represent paths to boolean arguments in code actions:
   6074 
   6075 > (lsp-fix-code-action-booleans command `(:foo :bar (:some :nested :boolean)))
   6076 
   6077 When there are available code actions, the server sends
   6078 `lsp-mode' a list of possible command names and arguments as
   6079 JSON. `lsp-mode' parses all boolean false values as `nil'. As a
   6080 result code action arguments containing falsy values don't
   6081 roundtrip correctly because `lsp-mode' will end up sending null
   6082 values back to the client. This list makes it possible to
   6083 selectively transform `nil' values back into `:json-false'."
   6084   (seq-doseq (path boolean-action-arguments)
   6085     (seq-doseq (args arguments?)
   6086       (lsp--fix-nested-boolean args (if (listp path) path (list path))))))
   6087 
   6088 (defun lsp--fix-nested-boolean (structure path)
   6089   "Traverse STRUCTURE using the paths from the PATH list, changing the value to
   6090 `:json-false' if it was `nil'. PATH should be a list containing
   6091 one or more symbols, and STRUCTURE should be compatible with
   6092 `lsp-member?', `lsp-get', and `lsp-put'."
   6093   (let ((key (car path))
   6094         (rest (cdr path)))
   6095     (if (null rest)
   6096         ;; `lsp-put' returns `nil' both when the key doesn't exist and when the
   6097         ;; value is `nil', so we need to explicitly check its presence here
   6098         (when (and (lsp-member? structure key) (not (lsp-get structure key)))
   6099           (lsp-put structure key :json-false))
   6100       ;; If `key' does not exist, then we'll silently ignore it
   6101       (when-let ((child (lsp-get structure key)))
   6102         (lsp--fix-nested-boolean child rest)))))
   6103 
   6104 (defvar lsp--formatting-indent-alist
   6105   ;; Taken from `dtrt-indent-mode'
   6106   '(
   6107     (ada-mode                   . ada-indent)                       ; Ada
   6108     (ada-ts-mode                . ada-ts-mode-indent-offset)
   6109     (c++-mode                   . c-basic-offset)                   ; C++
   6110     (c++-ts-mode                . c-ts-mode-indent-offset)
   6111     (c-mode                     . c-basic-offset)                   ; C
   6112     (c-ts-mode                  . c-ts-mode-indent-offset)
   6113     (cperl-mode                 . cperl-indent-level)               ; Perl
   6114     (crystal-mode               . crystal-indent-level)             ; Crystal (Ruby)
   6115     (csharp-mode                . c-basic-offset)                   ; C#
   6116     (csharp-tree-sitter-mode    . csharp-tree-sitter-indent-offset) ; C#
   6117     (csharp-ts-mode             . csharp-ts-mode-indent-offset)     ; C# (tree-sitter, Emacs29)
   6118     (css-mode                   . css-indent-offset)                ; CSS
   6119     (d-mode                     . c-basic-offset)                   ; D
   6120     (enh-ruby-mode              . enh-ruby-indent-level)            ; Ruby
   6121     (erlang-mode                . erlang-indent-level)              ; Erlang
   6122     (ess-mode                   . ess-indent-offset)                ; ESS (R)
   6123     (go-ts-mode                 . go-ts-mode-indent-offset)
   6124     (gpr-mode                   . gpr-indent-offset)                ; GNAT Project
   6125     (gpr-ts-mode                . gpr-ts-mode-indent-offset)
   6126     (hack-mode                  . hack-indent-offset)               ; Hack
   6127     (java-mode                  . c-basic-offset)                   ; Java
   6128     (java-ts-mode               . java-ts-mode-indent-offset)
   6129     (jde-mode                   . c-basic-offset)                   ; Java (JDE)
   6130     (js-mode                    . js-indent-level)                  ; JavaScript
   6131     (js-ts-mode                 . js-indent-level)
   6132     (js2-mode                   . js2-basic-offset)                 ; JavaScript-IDE
   6133     (js3-mode                   . js3-indent-level)                 ; JavaScript-IDE
   6134     (json-mode                  . js-indent-level)                  ; JSON
   6135     (json-ts-mode               . json-ts-mode-indent-offset)
   6136     (lua-mode                   . lua-indent-level)                 ; Lua
   6137     (lua-ts-mode                . lua-ts-indent-offset)
   6138     (nxml-mode                  . nxml-child-indent)                ; XML
   6139     (objc-mode                  . c-basic-offset)                   ; Objective C
   6140     (pascal-mode                . pascal-indent-level)              ; Pascal
   6141     (perl-mode                  . perl-indent-level)                ; Perl
   6142     (php-mode                   . c-basic-offset)                   ; PHP
   6143     (php-ts-mode                . php-ts-mode-indent-offset)        ; PHP
   6144     (powershell-mode            . powershell-indent)                ; PowerShell
   6145     (powershell-ts-mode         . powershell-ts-mode-indent-offset) ; PowerShell
   6146     (raku-mode                  . raku-indent-offset)               ; Perl6/Raku
   6147     (ruby-mode                  . ruby-indent-level)                ; Ruby
   6148     (rust-mode                  . rust-indent-offset)               ; Rust
   6149     (rust-ts-mode               . rust-ts-mode-indent-offset)
   6150     (rustic-mode                . rustic-indent-offset)             ; Rust
   6151     (scala-mode                 . scala-indent:step)                ; Scala
   6152     (sgml-mode                  . sgml-basic-offset)                ; SGML
   6153     (sh-mode                    . sh-basic-offset)                  ; Shell Script
   6154     (toml-ts-mode               . toml-ts-mode-indent-offset)
   6155     (typescript-mode            . typescript-indent-level)          ; Typescript
   6156     (typescript-ts-mode         . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29)
   6157     (yaml-mode                  . yaml-indent-offset)               ; YAML
   6158     (yang-mode                  . c-basic-offset)                   ; YANG (yang-mode)
   6159 
   6160     (default                    . standard-indent))                 ; default fallback
   6161   "A mapping from `major-mode' to its indent variable.")
   6162 
   6163 (defun lsp--get-indent-width (mode)
   6164   "Get indentation offset for MODE."
   6165   (or (alist-get mode lsp--formatting-indent-alist)
   6166       (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default))))
   6167 
   6168 (defun lsp--make-document-formatting-params ()
   6169   "Create document formatting params."
   6170   (lsp-make-document-formatting-params
   6171    :text-document (lsp--text-document-identifier)
   6172    :options (lsp-make-formatting-options
   6173              :tab-size (symbol-value (lsp--get-indent-width major-mode))
   6174              :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   6175              :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   6176              :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   6177              :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))))
   6178 
   6179 (defun lsp-format-buffer ()
   6180   "Ask the server to format this document."
   6181   (interactive "*")
   6182   (cond ((lsp-feature? "textDocument/formatting")
   6183          (let ((edits (lsp-request "textDocument/formatting"
   6184                                    (lsp--make-document-formatting-params))))
   6185            (if (seq-empty-p edits)
   6186                (lsp--info "No formatting changes provided")
   6187              (lsp--apply-text-edits edits 'format))))
   6188         ((lsp-feature? "textDocument/rangeFormatting")
   6189          (save-restriction
   6190            (widen)
   6191            (lsp-format-region (point-min) (point-max))))
   6192         (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider")))))
   6193 
   6194 (defun lsp-format-region (s e)
   6195   "Ask the server to format the region, or if none is selected, the current line."
   6196   (interactive "r")
   6197   (let ((edits (lsp-request
   6198                 "textDocument/rangeFormatting"
   6199                 (lsp--make-document-range-formatting-params s e))))
   6200     (if (seq-empty-p edits)
   6201         (lsp--info "No formatting changes provided")
   6202       (lsp--apply-text-edits edits 'format))))
   6203 
   6204 (defmacro lsp-make-interactive-code-action (func-name code-action-kind)
   6205   "Define an interactive function FUNC-NAME that attempts to
   6206 execute a CODE-ACTION-KIND action."
   6207   `(defun ,(intern (concat "lsp-" (symbol-name func-name))) ()
   6208      ,(format "Perform the %s code action, if available." code-action-kind)
   6209      (interactive)
   6210      ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to
   6211      ;; auto-execute here: the user has specified exactly what they want.
   6212      (let ((lsp-auto-execute-action t))
   6213        (condition-case nil
   6214            (lsp-execute-code-action-by-kind ,code-action-kind)
   6215          (lsp-no-code-actions
   6216           (when (called-interactively-p 'any)
   6217             (lsp--info ,(format "%s action not available" code-action-kind))))))))
   6218 
   6219 (lsp-make-interactive-code-action organize-imports "source.organizeImports")
   6220 
   6221 (defun lsp--make-document-range-formatting-params (start end)
   6222   "Make DocumentRangeFormattingParams for selected region."
   6223   (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params)
   6224                                                   (lsp--region-to-range start end)))
   6225 
   6226 (defconst lsp--highlight-kind-face
   6227   '((1 . lsp-face-highlight-textual)
   6228     (2 . lsp-face-highlight-read)
   6229     (3 . lsp-face-highlight-write)))
   6230 
   6231 (defun lsp--remove-overlays (name)
   6232   (save-restriction
   6233     (widen)
   6234     (remove-overlays (point-min) (point-max) name t)))
   6235 
   6236 (defun lsp-document-highlight ()
   6237   "Highlight all relevant references to the symbol under point."
   6238   (interactive)
   6239   (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights
   6240   (setq lsp--have-document-highlights nil
   6241         lsp--symbol-bounds-of-last-highlight-invocation nil)
   6242   (let ((lsp-enable-symbol-highlighting t))
   6243     (lsp--document-highlight)))
   6244 
   6245 (defun lsp--document-highlight-callback (highlights)
   6246   "Create a callback to process the reply of a
   6247 `textDocument/documentHighlight' message for the buffer BUF.
   6248 A reference is highlighted only if it is visible in a window."
   6249   (lsp--remove-overlays 'lsp-highlight)
   6250 
   6251   (let* ((wins-visible-pos (-map (lambda (win)
   6252                                    (cons (1- (line-number-at-pos (window-start win) t))
   6253                                          (1+ (line-number-at-pos (window-end win) t))))
   6254                                  (get-buffer-window-list nil nil 'visible))))
   6255     (setq lsp--have-document-highlights t)
   6256     (-map
   6257      (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line)
   6258                                                   :end (end &as &Position :line end-line))
   6259                                    :kind?))
   6260        (-map
   6261         (-lambda ((start-window . end-window))
   6262           ;; Make the overlay only if the reference is visible
   6263           (let ((start-point (lsp--position-to-point start))
   6264                 (end-point (lsp--position-to-point end)))
   6265             (when (and (> (1+ start-line) start-window)
   6266                        (< (1+ end-line) end-window)
   6267                        (not (and lsp-symbol-highlighting-skip-current
   6268                                  (<= start-point (point) end-point))))
   6269               (-doto (make-overlay start-point end-point)
   6270                 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face)))
   6271                 (overlay-put 'lsp-highlight t)))))
   6272         wins-visible-pos))
   6273      highlights)))
   6274 
   6275 (defcustom lsp-symbol-kinds
   6276   '((1 . "File")
   6277     (2 . "Module")
   6278     (3 . "Namespace")
   6279     (4 . "Package")
   6280     (5 . "Class")
   6281     (6 . "Method")
   6282     (7 . "Property")
   6283     (8 . "Field")
   6284     (9 . "Constructor")
   6285     (10 . "Enum")
   6286     (11 . "Interface")
   6287     (12 . "Function")
   6288     (13 . "Variable")
   6289     (14 . "Constant")
   6290     (15 . "String")
   6291     (16 . "Number")
   6292     (17 . "Boolean")
   6293     (18 . "Array")
   6294     (19 . "Object")
   6295     (20 . "Key")
   6296     (21 . "Null")
   6297     (22 . "Enum Member")
   6298     (23 . "Struct")
   6299     (24 . "Event")
   6300     (25 . "Operator")
   6301     (26 . "Type Parameter"))
   6302   "Alist mapping SymbolKinds to human-readable strings.
   6303 Various Symbol objects in the LSP protocol have an integral type,
   6304 specifying what they are. This alist maps such type integrals to
   6305 readable representations of them. See
   6306 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/',
   6307 namespace SymbolKind."
   6308   :group 'lsp-mode
   6309   :type '(alist :key-type integer :value-type string))
   6310 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds)
   6311 
   6312 (lsp-defun lsp--symbol-information-to-xref
   6313   ((&SymbolInformation :kind :name
   6314                        :location (&Location :uri :range (&Range :start
   6315                                                                 (&Position :line :character)))))
   6316   "Return a `xref-item' from SYMBOL information."
   6317   (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name)
   6318              (xref-make-file-location (lsp--uri-to-path uri)
   6319                                       line
   6320                                       character)))
   6321 
   6322 (defun lsp--get-document-symbols ()
   6323   "Get document symbols.
   6324 
   6325 If the buffer has not been modified since symbols were last
   6326 retrieved, simply return the latest result.
   6327 
   6328 Else, if the request was initiated by Imenu updating its menu-bar
   6329 entry, perform it asynchronously; i.e., give Imenu the latest
   6330 result and then force a refresh when a new one is available.
   6331 
   6332 Else (e.g., due to interactive use of `imenu' or `xref'),
   6333 perform the request synchronously."
   6334   (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick)
   6335       lsp--document-symbols
   6336     (let ((method "textDocument/documentSymbol")
   6337           (params `(:textDocument ,(lsp--text-document-identifier)))
   6338           (tick (buffer-chars-modified-tick)))
   6339       (if (not lsp--document-symbols-request-async)
   6340           (prog1
   6341               (setq lsp--document-symbols (lsp-request method params))
   6342             (setq lsp--document-symbols-tick tick))
   6343         (lsp-request-async method params
   6344                            (lambda (document-symbols)
   6345                              (setq lsp--document-symbols document-symbols
   6346                                    lsp--document-symbols-tick tick)
   6347                              (lsp--imenu-refresh))
   6348                            :mode 'alive
   6349                            :cancel-token :document-symbols)
   6350         lsp--document-symbols))))
   6351 
   6352 (advice-add 'imenu-update-menubar :around
   6353             (lambda (oldfun &rest r)
   6354               (let ((lsp--document-symbols-request-async t))
   6355                 (apply oldfun r))))
   6356 
   6357 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position)
   6358   "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION."
   6359   (-let (((symbol &as &DocumentSymbol? :children?)
   6360           (seq-find (-lambda ((&DocumentSymbol :range))
   6361                       (lsp-point-in-range? current-position range))
   6362                     document-symbols)))
   6363     (if children?
   6364         (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position))
   6365       (when symbol
   6366         (list symbol)))))
   6367 
   6368 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?))
   6369   "Convert a SymbolInformation to a DocumentInformation"
   6370   (lsp-make-document-symbol :name name
   6371                             :kind kind
   6372                             :range (lsp:location-range location)
   6373                             :children? nil
   6374                             :deprecated? deprecated?
   6375                             :selection-range (lsp:location-range location)
   6376                             :detail? container-name?))
   6377 
   6378 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position)
   6379   "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION."
   6380   (--> symbols-informations
   6381     (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range)))
   6382              (when (lsp-point-in-range? current-position range)
   6383                (lsp--symbol-information->document-symbol symbol)))
   6384            it)
   6385     (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position))
   6386                        (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position)))
   6387                (and (lsp--position-compare b-start-position a-start-position)
   6388                     (lsp--position-compare a-end-position b-end-position))))))
   6389 
   6390 (defun lsp--symbols->document-symbols-hierarchy (symbols)
   6391   "Convert SYMBOLS to symbols-hierarchy."
   6392   (when-let ((first-symbol (lsp-seq-first symbols)))
   6393     (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line)
   6394                                            :character (plist-get (lsp--cur-position) :character))))
   6395       (if (lsp-symbol-information? first-symbol)
   6396           (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position)
   6397         (lsp--document-symbols->document-symbols-hierarchy symbols cur-position)))))
   6398 
   6399 (defun lsp--xref-backend () 'xref-lsp)
   6400 
   6401 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp)))
   6402   (propertize (or (thing-at-point 'symbol) "")
   6403               'identifier-at-point t))
   6404 
   6405 (defun lsp--xref-elements-index (symbols path)
   6406   (-mapcat
   6407    (-lambda (sym)
   6408      (pcase-exhaustive sym
   6409        ((DocumentSymbol :name :children? :selection-range (Range :start))
   6410         (cons (cons (concat path name)
   6411                     (lsp--position-to-point start))
   6412               (lsp--xref-elements-index children? (concat path name " / "))))
   6413        ((SymbolInformation :name :location (Location :range (Range :start)))
   6414         (list (cons (concat path name)
   6415                     (lsp--position-to-point start))))))
   6416    symbols))
   6417 
   6418 (defvar-local lsp--symbols-cache nil)
   6419 
   6420 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp)))
   6421   (if (lsp--find-workspaces-for "textDocument/documentSymbol")
   6422       (progn
   6423         (setq lsp--symbols-cache (lsp--xref-elements-index
   6424                                   (lsp--get-document-symbols) nil))
   6425         lsp--symbols-cache)
   6426     (list (propertize (or (thing-at-point 'symbol) "")
   6427                       'identifier-at-point t))))
   6428 
   6429 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier)
   6430   (save-excursion
   6431     (unless (get-text-property 0 'identifier-at-point identifier)
   6432       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6433                               (user-error "Unable to find symbol %s in current document" identifier)))))
   6434     (lsp--locations-to-xref-items (lsp-request "textDocument/definition"
   6435                                                (lsp--text-document-position-params)))))
   6436 
   6437 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier)
   6438   (save-excursion
   6439     (unless (get-text-property 0 'identifier-at-point identifier)
   6440       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6441                               (user-error "Unable to find symbol %s" identifier)))))
   6442     (lsp--locations-to-xref-items (lsp-request "textDocument/references"
   6443                                                (lsp--make-reference-params nil lsp-references-exclude-definition)))))
   6444 
   6445 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern)
   6446   (seq-map #'lsp--symbol-information-to-xref
   6447            (lsp-request "workspace/symbol" `(:query ,pattern))))
   6448 
   6449 (defcustom lsp-rename-use-prepare t
   6450   "Whether `lsp-rename' should do a prepareRename first.
   6451 For some language servers, textDocument/prepareRename might be
   6452 too slow, in which case this variable may be set to nil.
   6453 `lsp-rename' will then use `thing-at-point' `symbol' to determine
   6454 the symbol to rename at point."
   6455   :group 'lsp-mode
   6456   :type 'boolean)
   6457 
   6458 (defun lsp--get-symbol-to-rename ()
   6459   "Get a symbol to rename and placeholder at point.
   6460 Returns a cons ((START . END) . PLACEHOLDER?), and nil if
   6461 renaming is generally supported but cannot be done at point.
   6462 START and END are the bounds of the identifiers being renamed,
   6463 while PLACEHOLDER?, is either nil or a string suggested by the
   6464 language server as the initial input of a new-name prompt."
   6465   (unless (lsp-feature? "textDocument/rename")
   6466     (error "The connected server(s) doesn't support renaming"))
   6467   (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename"))
   6468       (when-let ((response
   6469                   (lsp-request "textDocument/prepareRename"
   6470                                (lsp--text-document-position-params))))
   6471         (let* ((bounds (lsp--range-to-region
   6472                         (if (lsp-range? response)
   6473                             response
   6474                           (lsp:prepare-rename-result-range response))))
   6475                (placeholder
   6476                 (and (not (lsp-range? response))
   6477                      (lsp:prepare-rename-result-placeholder response))))
   6478           (cons bounds placeholder)))
   6479     (when-let ((bounds (bounds-of-thing-at-point 'symbol)))
   6480       (cons bounds nil))))
   6481 
   6482 (defface lsp-face-rename '((t :underline t))
   6483   "Face used to highlight the identifier being renamed.
   6484 Renaming can be done using `lsp-rename'."
   6485   :group 'lsp-mode)
   6486 
   6487 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face))
   6488   "Face used to display the rename placeholder in.
   6489 When calling `lsp-rename' interactively, this will be the face of
   6490 the new name."
   6491   :group 'lsp-mode)
   6492 
   6493 (defvar lsp-rename-history '()
   6494   "History for `lsp--read-rename'.")
   6495 
   6496 (defun lsp--read-rename (at-point)
   6497   "Read a new name for a `lsp-rename' at `point' from the user.
   6498 AT-POINT shall be a structure as returned by
   6499 `lsp--get-symbol-to-rename'.
   6500 
   6501 Returns a string, which should be the new name for the identifier
   6502 at point. If renaming cannot be done at point (as determined from
   6503 AT-POINT), throw a `user-error'.
   6504 
   6505 This function is for use in `lsp-rename' only, and shall not be
   6506 relied upon."
   6507   (unless at-point
   6508     (user-error "`lsp-rename' is invalid here"))
   6509   (-let* ((((start . end) . placeholder?) at-point)
   6510           ;; Do the `buffer-substring' first to not include `lsp-face-rename'
   6511           (rename-me (buffer-substring start end))
   6512           (placeholder (or placeholder? rename-me))
   6513           (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face))
   6514 
   6515           overlay)
   6516     ;; We need unwind protect, as the user might cancel here, causing the
   6517     ;; overlay to linger.
   6518     (unwind-protect
   6519         (progn
   6520           (setq overlay (make-overlay start end))
   6521           (overlay-put overlay 'face 'lsp-face-rename)
   6522 
   6523           (read-string (format "Rename %s to: " rename-me) placeholder
   6524                        'lsp-rename-history))
   6525       (and overlay (delete-overlay overlay)))))
   6526 
   6527 (defun lsp-rename (newname)
   6528   "Rename the symbol (and all references to it) under point to NEWNAME."
   6529   (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename))))
   6530   (when-let ((edits (lsp-request "textDocument/rename"
   6531                                  `( :textDocument ,(lsp--text-document-identifier)
   6532                                     :position ,(lsp--cur-position)
   6533                                     :newName ,newname))))
   6534     (lsp--apply-workspace-edit edits 'rename)))
   6535 
   6536 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?)
   6537   "Advice around function `rename-file'.
   6538 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?.
   6539 
   6540 This advice sends workspace/willRenameFiles before renaming file
   6541 to check if server wants to apply any workspaceEdits after renamed."
   6542   (if (and lsp-apply-edits-after-file-operations
   6543            (lsp--send-will-rename-files-p old-name))
   6544       (let ((params (lsp-make-rename-files-params
   6545                      :files (vector (lsp-make-file-rename
   6546                                      :oldUri (lsp--path-to-uri old-name)
   6547                                      :newUri (lsp--path-to-uri new-name))))))
   6548         (when-let ((edits (lsp-request "workspace/willRenameFiles" params)))
   6549           (lsp--apply-workspace-edit edits 'rename-file)
   6550           (funcall old-func old-name new-name ok-if-already-exists?)
   6551           (when (lsp--send-did-rename-files-p)
   6552             (lsp-notify "workspace/didRenameFiles" params))))
   6553     (funcall old-func old-name new-name ok-if-already-exists?)))
   6554 
   6555 (advice-add 'rename-file :around #'lsp--on-rename-file)
   6556 
   6557 (defcustom lsp-xref-force-references nil
   6558   "If non-nil threat everything as references(e. g. jump if only one item.)"
   6559   :group 'lsp-mode
   6560   :type 'boolean)
   6561 
   6562 (defun lsp-show-xrefs (xrefs display-action references?)
   6563   (unless (region-active-p) (push-mark nil t))
   6564   (if (boundp 'xref-show-definitions-function)
   6565       (with-no-warnings
   6566         (xref-push-marker-stack)
   6567         (funcall (if (and references? (not lsp-xref-force-references))
   6568                      xref-show-xrefs-function
   6569                    xref-show-definitions-function)
   6570                  (-const xrefs)
   6571                  `((window . ,(selected-window))
   6572                    (display-action . ,display-action)
   6573                    ,(if (and references? (not lsp-xref-force-references))
   6574                         `(auto-jump . ,xref-auto-jump-to-first-xref)
   6575                       `(auto-jump . ,xref-auto-jump-to-first-definition)))))
   6576     (xref--show-xrefs xrefs display-action)))
   6577 
   6578 (cl-defmethod seq-empty-p ((ht hash-table))
   6579   "Function `seq-empty-p' for hash-table."
   6580   (hash-table-empty-p ht))
   6581 
   6582 (cl-defun lsp-find-locations (method &optional extra &key display-action references?)
   6583   "Send request named METHOD and get cross references of the symbol under point.
   6584 EXTRA is a plist of extra parameters.
   6585 REFERENCES? t when METHOD returns references."
   6586   (let ((loc (lsp-request method
   6587                           (append (lsp--text-document-position-params) extra))))
   6588     (if (seq-empty-p loc)
   6589         (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) ""))
   6590       (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?))))
   6591 
   6592 (cl-defun lsp-find-declaration (&key display-action)
   6593   "Find declarations of the symbol under point."
   6594   (interactive)
   6595   (lsp-find-locations "textDocument/declaration" nil :display-action display-action))
   6596 
   6597 (cl-defun lsp-find-definition (&key display-action)
   6598   "Find definitions of the symbol under point."
   6599   (interactive)
   6600   (lsp-find-locations "textDocument/definition" nil :display-action display-action))
   6601 
   6602 (defun lsp-find-definition-mouse (click)
   6603   "Click to start `lsp-find-definition' at clicked point."
   6604   (interactive "e")
   6605   (let* ((ec (event-start click))
   6606          (p1 (posn-point ec))
   6607          (w1 (posn-window ec)))
   6608     (select-window w1)
   6609     (goto-char p1)
   6610     (lsp-find-definition)))
   6611 
   6612 (cl-defun lsp-find-implementation (&key display-action)
   6613   "Find implementations of the symbol under point."
   6614   (interactive)
   6615   (lsp-find-locations "textDocument/implementation"
   6616                       nil
   6617                       :display-action display-action
   6618                       :references? t))
   6619 
   6620 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action)
   6621   "Find references of the symbol under point."
   6622   (interactive "P")
   6623   (lsp-find-locations "textDocument/references"
   6624                       (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition)))))
   6625                       :display-action display-action
   6626                       :references? t))
   6627 
   6628 (cl-defun lsp-find-type-definition (&key display-action)
   6629   "Find type definitions of the symbol under point."
   6630   (interactive)
   6631   (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action))
   6632 
   6633 (defalias 'lsp-find-custom #'lsp-find-locations)
   6634 (defalias 'lsp-goto-implementation #'lsp-find-implementation)
   6635 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition)
   6636 
   6637 (with-eval-after-load 'evil
   6638   (evil-set-command-property 'lsp-find-definition :jump t)
   6639   (evil-set-command-property 'lsp-find-implementation :jump t)
   6640   (evil-set-command-property 'lsp-find-references :jump t)
   6641   (evil-set-command-property 'lsp-find-type-definition :jump t))
   6642 
   6643 (defun lsp--workspace-method-supported? (check-command method capability workspace)
   6644   (with-lsp-workspace workspace
   6645     (if check-command
   6646         (funcall check-command workspace)
   6647       (or
   6648        (when capability (lsp--capability capability))
   6649        (lsp--registered-capability method)
   6650        (and (not capability) (not check-command))))))
   6651 
   6652 (defun lsp-disable-method-for-server (method server-id)
   6653   "Disable METHOD for SERVER-ID."
   6654   (cl-callf
   6655       (lambda (reqs)
   6656         (-let (((&plist :check-command :capability) reqs))
   6657           (list :check-command
   6658                 (lambda (workspace)
   6659                   (unless (-> workspace
   6660                               lsp--workspace-client
   6661                               lsp--client-server-id
   6662                               (eq server-id))
   6663                     (lsp--workspace-method-supported? check-command
   6664                                                       method
   6665                                                       capability
   6666                                                       workspace))))))
   6667       (alist-get method lsp-method-requirements nil nil 'string=)))
   6668 
   6669 (defun lsp--find-workspaces-for (msg-or-method)
   6670   "Find all workspaces in the current project that can handle MSG."
   6671   (let ((method (if (stringp msg-or-method)
   6672                     msg-or-method
   6673                   (plist-get msg-or-method :method))))
   6674     (-if-let (reqs (cdr (assoc method lsp-method-requirements)))
   6675         (-let (((&plist :capability :check-command) reqs))
   6676           (-filter
   6677            (-partial #'lsp--workspace-method-supported?
   6678                      check-command method capability)
   6679            (lsp-workspaces)))
   6680       (lsp-workspaces))))
   6681 
   6682 (defun lsp-can-execute-command? (command-name)
   6683   "Returns non-nil if current language server(s) can execute COMMAND-NAME.
   6684 The command is executed via `workspace/executeCommand'"
   6685   (cl-position
   6686    command-name
   6687    (lsp:execute-command-options-commands
   6688     (lsp:server-capabilities-execute-command-provider?
   6689      (lsp--server-capabilities)))
   6690    :test #'equal))
   6691 
   6692 (defalias 'lsp-feature? 'lsp--find-workspaces-for)
   6693 
   6694 (cl-defmethod lsp-execute-command (_server _command _arguments)
   6695   "Dispatch COMMAND execution."
   6696   (signal 'cl-no-applicable-method nil))
   6697 
   6698 (defun lsp-workspace-command-execute (command &optional args)
   6699   "Execute workspace COMMAND with ARGS."
   6700   (condition-case-unless-debug err
   6701       (let ((params (if args
   6702                         (list :command command :arguments args)
   6703                       (list :command command))))
   6704         (lsp-request "workspace/executeCommand" params))
   6705     (error
   6706      (error "`workspace/executeCommand' with `%s' failed.\n\n%S"
   6707             command err))))
   6708 
   6709 (defun lsp-send-execute-command (command &optional args)
   6710   "Create and send a `workspace/executeCommand' message having command COMMAND
   6711 and optional ARGS."
   6712   (lsp-workspace-command-execute command args))
   6713 
   6714 (defalias 'lsp-point-to-position #'lsp--point-to-position)
   6715 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier)
   6716 (defalias 'lsp--send-execute-command #'lsp-send-execute-command)
   6717 (defalias 'lsp-on-open #'lsp--text-document-did-open)
   6718 (defalias 'lsp-on-save #'lsp--text-document-did-save)
   6719 
   6720 (defun lsp--set-configuration (settings)
   6721   "Set the SETTINGS for the lsp server."
   6722   (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings)))
   6723 
   6724 (defun lsp-current-buffer ()
   6725   (or lsp--virtual-buffer
   6726       (current-buffer)))
   6727 
   6728 (defun lsp-buffer-live-p (buffer-id)
   6729   (if-let ((buffer-live (plist-get buffer-id :buffer-live?)))
   6730       (funcall buffer-live buffer-id)
   6731     (buffer-live-p buffer-id)))
   6732 
   6733 (defun lsp--on-set-visited-file-name (old-func &rest args)
   6734   "Advice around function `set-visited-file-name'.
   6735 
   6736 This advice sends textDocument/didClose for the old file and
   6737 textDocument/didOpen for the new file."
   6738   (when lsp--cur-workspace
   6739     (lsp--text-document-did-close t))
   6740   (prog1 (apply old-func args)
   6741     (when lsp--cur-workspace
   6742       (lsp--text-document-did-open))))
   6743 
   6744 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name)
   6745 
   6746 (defcustom lsp-flush-delayed-changes-before-next-message t
   6747   "If non-nil send the document changes update before sending other messages.
   6748 
   6749 If nil, and `lsp-debounce-full-sync-notifications' is non-nil,
   6750  change notifications will be throttled by
   6751  `lsp-debounce-full-sync-notifications-interval' regardless of
   6752  other messages."
   6753   :group 'lsp-mode
   6754   :type 'boolean)
   6755 
   6756 (defvar lsp--not-flushing-delayed-changes t)
   6757 
   6758 (defun lsp--send-no-wait (message proc)
   6759   "Send MESSAGE to PROC without waiting for further output."
   6760 
   6761   (when (and lsp--not-flushing-delayed-changes
   6762              lsp-flush-delayed-changes-before-next-message)
   6763     (let ((lsp--not-flushing-delayed-changes nil))
   6764       (lsp--flush-delayed-changes)))
   6765   (lsp-process-send proc message))
   6766 
   6767 (define-error 'lsp-parse-error
   6768   "Error parsing message from language server" 'lsp-error)
   6769 (define-error 'lsp-unknown-message-type
   6770   "Unknown message type" '(lsp-error lsp-parse-error))
   6771 (define-error 'lsp-unknown-json-rpc-version
   6772   "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error))
   6773 (define-error 'lsp-no-content-length
   6774   "Content-Length header missing in message" '(lsp-error lsp-parse-error))
   6775 (define-error 'lsp-invalid-header-name
   6776   "Invalid header name" '(lsp-error lsp-parse-error))
   6777 
   6778 ;;  id  method
   6779 ;;   x    x     request
   6780 ;;   x    .     response
   6781 ;;   .    x     notification
   6782 (defun lsp--get-message-type (json-data)
   6783   "Get the message type from JSON-DATA."
   6784   (if (lsp:json-message-id? json-data)
   6785       (if (lsp:json-message-error? json-data)
   6786           'response-error
   6787         (if (lsp:json-message-method? json-data)
   6788             'request
   6789           'response))
   6790     'notification))
   6791 
   6792 (defconst lsp--default-notification-handlers
   6793   (ht ("window/showMessage" #'lsp--window-show-message)
   6794       ("window/logMessage" #'lsp--window-log-message)
   6795       ("window/showInputBox" #'lsp--window-show-input-box)
   6796       ("window/showQuickPick" #'lsp--window-show-quick-pick)
   6797       ("textDocument/publishDiagnostics" #'lsp--on-diagnostics)
   6798       ("textDocument/diagnosticsEnd" #'ignore)
   6799       ("textDocument/diagnosticsBegin" #'ignore)
   6800       ("telemetry/event" #'ignore)
   6801       ("$/progress" (lambda (workspace params)
   6802                       (funcall lsp-progress-function workspace params)))))
   6803 
   6804 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method))
   6805   "Call the appropriate handler for NOTIFICATION."
   6806   (-let ((client (lsp--workspace-client workspace)))
   6807     (when (lsp--log-io-p method)
   6808       (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif)
   6809                           lsp--cur-workspace))
   6810     (if-let ((handler (or (gethash method (lsp--client-notification-handlers client))
   6811                           (gethash method lsp--default-notification-handlers))))
   6812         (funcall handler workspace params)
   6813       (when (and method (not (string-prefix-p "$" method)))
   6814         (lsp-warn "Unknown notification: %s" method)))))
   6815 
   6816 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items))
   6817   "Get section configuration.
   6818 PARAMS are the `workspace/configuration' request params"
   6819   (->> items
   6820        (-map (-lambda ((&ConfigurationItem :section?))
   6821                (-let* ((path-parts (split-string section? "\\."))
   6822                        (path-without-last (s-join "." (-slice path-parts 0 -1)))
   6823                        (path-parts-len (length path-parts)))
   6824                  (cond
   6825                   ((<= path-parts-len 1)
   6826                    (ht-get (lsp-configuration-section section?)
   6827                            (car-safe path-parts)
   6828                            (ht-create)))
   6829                   ((> path-parts-len 1)
   6830                    (when-let ((section (lsp-configuration-section path-without-last))
   6831                               (keys path-parts))
   6832                      (while (and keys section)
   6833                        (setf section (ht-get section (pop keys))))
   6834                      section))))))
   6835        (apply #'vector)))
   6836 
   6837 (defun lsp--ms-since (timestamp)
   6838   "Integer number of milliseconds since TIMESTAMP.  Fractions discarded."
   6839   (floor (* 1000 (float-time (time-since timestamp)))))
   6840 
   6841 (defun lsp--send-request-response (workspace recv-time request response)
   6842   "Send the RESPONSE for REQUEST in WORKSPACE and log if needed."
   6843   (-let* (((&JSONResponse :params :method :id) request)
   6844           (process (lsp--workspace-proc workspace))
   6845           (response (lsp--make-response id response))
   6846           (req-entry (and lsp-log-io
   6847                           (lsp--make-log-entry method id params 'incoming-req)))
   6848           (resp-entry (and lsp-log-io
   6849                            (lsp--make-log-entry method id response 'outgoing-resp
   6850                                                 (lsp--ms-since recv-time)))))
   6851     ;; Send response to the server.
   6852     (when (lsp--log-io-p method)
   6853       (lsp--log-entry-new req-entry workspace)
   6854       (lsp--log-entry-new resp-entry workspace))
   6855     (lsp--send-no-wait response process)))
   6856 
   6857 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method))
   6858   "Call the appropriate handler for REQUEST, and send the return value to the
   6859 server. WORKSPACE is the active workspace."
   6860   (-let* ((recv-time (current-time))
   6861           (client (lsp--workspace-client workspace))
   6862           (buffers (lsp--workspace-buffers workspace))
   6863           handler
   6864           (response (cond
   6865                      ((setq handler (gethash method (lsp--client-request-handlers client) nil))
   6866                       (funcall handler workspace params))
   6867                      ((setq handler (gethash method (lsp--client-async-request-handlers client) nil))
   6868                       (funcall handler workspace params
   6869                                (-partial #'lsp--send-request-response
   6870                                          workspace recv-time request))
   6871                       'delay-response)
   6872                      ((equal method "client/registerCapability")
   6873                       (mapc #'lsp--server-register-capability
   6874                             (lsp:registration-params-registrations params))
   6875                       (mapc (lambda (buf)
   6876                               (when (lsp-buffer-live-p buf)
   6877                                 (lsp-with-current-buffer buf
   6878                                   (lsp-unconfig-buffer)
   6879                                   (lsp-configure-buffer))))
   6880                             buffers)
   6881                       nil)
   6882                      ((equal method "window/showMessageRequest")
   6883                       (let ((choice (lsp--window-log-message-request params)))
   6884                         `(:title ,choice)))
   6885                      ((equal method "window/showDocument")
   6886                       (let ((success? (lsp--window-show-document params)))
   6887                         (lsp-make-show-document-result :success (or success?
   6888                                                                     :json-false))))
   6889                      ((equal method "client/unregisterCapability")
   6890                       (mapc #'lsp--server-unregister-capability
   6891                             (lsp:unregistration-params-unregisterations params))
   6892                       (mapc (lambda (buf)
   6893                               (when (lsp-buffer-live-p buf)
   6894                                 (lsp-with-current-buffer buf
   6895                                   (lsp-unconfig-buffer)
   6896                                   (lsp-configure-buffer))))
   6897                             buffers)
   6898                       nil)
   6899                      ((equal method "workspace/applyEdit")
   6900                       (list :applied (condition-case err
   6901                                          (prog1 t
   6902                                            (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested))
   6903                                        (error
   6904                                         (lsp--error "Failed to apply edits with message %s"
   6905                                                     (error-message-string err))
   6906                                         :json-false))))
   6907                      ((equal method "workspace/configuration")
   6908                       (with-lsp-workspace workspace
   6909                         (if-let ((buf (car buffers)))
   6910                             (lsp-with-current-buffer buf
   6911                               (lsp--build-workspace-configuration-response params))
   6912                           (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace)
   6913                             (lsp--build-workspace-configuration-response params)))))
   6914                      ((equal method "workspace/workspaceFolders")
   6915                       (let ((folders (or (-> workspace
   6916                                              (lsp--workspace-client)
   6917                                              (lsp--client-server-id)
   6918                                              (gethash (lsp-session-server-id->folders (lsp-session))))
   6919                                          (lsp-session-folders (lsp-session)))))
   6920                         (->> folders
   6921                              (-distinct)
   6922                              (-map (lambda (folder)
   6923                                      (list :uri (lsp--path-to-uri folder))))
   6924                              (apply #'vector))))
   6925                      ((equal method "window/workDoneProgress/create")
   6926                       nil ;; no specific reply, no processing required
   6927                       )
   6928                      ((equal method "workspace/semanticTokens/refresh")
   6929                       (when (and lsp-semantic-tokens-enable
   6930                                  (fboundp 'lsp--semantic-tokens-on-refresh))
   6931                         (lsp--semantic-tokens-on-refresh workspace))
   6932                       nil)
   6933                      ((equal method "workspace/codeLens/refresh")
   6934                       (when (and lsp-lens-enable
   6935                                  (fboundp 'lsp--lens-on-refresh))
   6936                         (lsp--lens-on-refresh workspace))
   6937                       nil)
   6938                      (t (lsp-warn "Unknown request method: %s" method) nil))))
   6939     ;; Send response to the server.
   6940     (unless (eq response 'delay-response)
   6941       (lsp--send-request-response workspace recv-time request response))))
   6942 
   6943 (lsp-defun lsp--error-string ((&JSONError :message :code))
   6944   "Format ERR as a user friendly string."
   6945   (format "Error from the Language Server: %s (%s)"
   6946           message
   6947           (or (car (alist-get code lsp--errors)) "Unknown error")))
   6948 
   6949 (defun lsp--get-body-length (headers)
   6950   (let ((content-length (cdr (assoc "Content-Length" headers))))
   6951     (if content-length
   6952         (string-to-number content-length)
   6953 
   6954       ;; This usually means either the server or our parser is
   6955       ;; screwed up with a previous Content-Length
   6956       (error "No Content-Length header"))))
   6957 
   6958 (defun lsp--parse-header (s)
   6959   "Parse string S as a LSP (KEY . VAL) header."
   6960   (let ((pos (string-match "\:" s))
   6961         key val)
   6962     (unless pos
   6963       (signal 'lsp-invalid-header-name (list s)))
   6964     (setq key (substring s 0 pos)
   6965           val (s-trim-left (substring s (+ 1 pos))))
   6966     (when (equal key "Content-Length")
   6967       (cl-assert (cl-loop for c across val
   6968                           when (or (> c ?9) (< c ?0)) return nil
   6969                           finally return t)
   6970                  nil (format "Invalid Content-Length value: %s" val)))
   6971     (cons key val)))
   6972 
   6973 (defmacro lsp--read-json (str)
   6974   "Read json string STR."
   6975   (if (progn
   6976         (require 'json)
   6977         (fboundp 'json-parse-string))
   6978       `(json-parse-string ,str
   6979                           :object-type (if lsp-use-plists
   6980                                            'plist
   6981                                          'hash-table)
   6982                           :null-object nil
   6983                           :false-object nil)
   6984     `(let ((json-array-type 'vector)
   6985            (json-object-type (if lsp-use-plists
   6986                                  'plist
   6987                                'hash-table))
   6988            (json-false nil))
   6989        (json-read-from-string ,str))))
   6990 
   6991 (defmacro lsp-json-read-buffer ()
   6992   "Read json from the current buffer."
   6993   (if (progn
   6994         (require 'json)
   6995         (fboundp 'json-parse-buffer))
   6996       `(json-parse-buffer :object-type (if lsp-use-plists
   6997                                            'plist
   6998                                          'hash-table)
   6999                           :null-object nil
   7000                           :false-object nil)
   7001     `(let ((json-array-type 'vector)
   7002            (json-object-type (if lsp-use-plists
   7003                                  'plist
   7004                                'hash-table))
   7005            (json-false nil))
   7006        (json-read))))
   7007 
   7008 (defun lsp--read-json-file (file-path)
   7009   "Read json file."
   7010   (-> file-path
   7011     (f-read-text)
   7012     (lsp--read-json)))
   7013 
   7014 (defun lsp--parser-on-message (json-data workspace)
   7015   "Called when the parser P read a complete MSG from the server."
   7016   (with-demoted-errors "Error processing message %S."
   7017     (with-lsp-workspace workspace
   7018       (let* ((client (lsp--workspace-client workspace))
   7019              (id (--when-let (lsp:json-response-id json-data)
   7020                    (if (stringp it) (string-to-number it) it)))
   7021              (data (lsp:json-response-result json-data)))
   7022         (pcase (lsp--get-message-type json-data)
   7023           ('response
   7024            (cl-assert id)
   7025            (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))]
   7026              (when (lsp--log-io-p method)
   7027                (lsp--log-entry-new
   7028                 (lsp--make-log-entry method id data 'incoming-resp
   7029                                      (lsp--ms-since before-send))
   7030                 workspace))
   7031              (when callback
   7032                (remhash id (lsp--client-response-handlers client))
   7033                (funcall callback (lsp:json-response-result json-data)))))
   7034           ('response-error
   7035            (cl-assert id)
   7036            (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))]
   7037              (when (lsp--log-io-p method)
   7038                (lsp--log-entry-new
   7039                 (lsp--make-log-entry method id (lsp:json-response-error-error json-data)
   7040                                      'incoming-resp (lsp--ms-since before-send))
   7041                 workspace))
   7042              (when callback
   7043                (remhash id (lsp--client-response-handlers client))
   7044                (funcall callback (lsp:json-response-error-error json-data)))))
   7045           ('notification
   7046            (lsp--on-notification workspace json-data))
   7047           ('request (lsp--on-request workspace json-data)))))))
   7048 
   7049 (defun lsp--create-filter-function (workspace)
   7050   "Make filter for the workspace."
   7051   (let ((body-received 0)
   7052         leftovers body-length body chunk)
   7053     (lambda (_proc input)
   7054       (setf chunk (if (s-blank? leftovers)
   7055                       input
   7056                     (concat leftovers input)))
   7057 
   7058       (let (messages)
   7059         (while (not (s-blank? chunk))
   7060           (if (not body-length)
   7061               ;; Read headers
   7062               (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk)))
   7063                   ;; We've got all the headers, handle them all at once:
   7064                   (setf body-length (lsp--get-body-length
   7065                                      (mapcar #'lsp--parse-header
   7066                                              (split-string
   7067                                               (substring-no-properties chunk
   7068                                                                        (or (string-match-p "Content-Length" chunk)
   7069                                                                            (error "Unable to find Content-Length header."))
   7070                                                                        body-sep-pos)
   7071                                               "\r\n")))
   7072                         body-received 0
   7073                         leftovers nil
   7074                         chunk (substring-no-properties chunk (+ body-sep-pos 4)))
   7075 
   7076                 ;; Haven't found the end of the headers yet. Save everything
   7077                 ;; for when the next chunk arrives and await further input.
   7078                 (setf leftovers chunk
   7079                       chunk nil))
   7080             (let* ((chunk-length (string-bytes chunk))
   7081                    (left-to-receive (- body-length body-received))
   7082                    (this-body (if (< left-to-receive chunk-length)
   7083                                   (prog1 (substring-no-properties chunk 0 left-to-receive)
   7084                                     (setf chunk (substring-no-properties chunk left-to-receive)))
   7085                                 (prog1 chunk
   7086                                   (setf chunk nil))))
   7087                    (body-bytes (string-bytes this-body)))
   7088               (push this-body body)
   7089               (setf body-received (+ body-received body-bytes))
   7090               (when (>= chunk-length left-to-receive)
   7091                 (condition-case err
   7092                     (with-temp-buffer
   7093                       (apply #'insert
   7094                              (nreverse
   7095                               (prog1 body
   7096                                 (setf leftovers nil
   7097                                       body-length nil
   7098                                       body-received nil
   7099                                       body nil))))
   7100                       (decode-coding-region (point-min)
   7101                                             (point-max)
   7102                                             'utf-8)
   7103                       (goto-char (point-min))
   7104                       (push (lsp-json-read-buffer) messages))
   7105 
   7106                   (error
   7107                    (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s"
   7108                              (concat leftovers input)
   7109                              err)))))))
   7110         (mapc (lambda (msg)
   7111                 (lsp--parser-on-message msg workspace))
   7112               (nreverse messages))))))
   7113 
   7114 (defvar-local lsp--line-col-to-point-hash-table nil
   7115   "Hash table with keys (line . col) and values that are either point positions
   7116 or markers.")
   7117 
   7118 (defcustom lsp-imenu-detailed-outline t
   7119   "Whether `lsp-imenu' should include signatures.
   7120 This will be ignored if the server doesn't provide the necessary
   7121 information, for example if it doesn't support DocumentSymbols."
   7122   :group 'lsp-imenu
   7123   :type 'boolean)
   7124 
   7125 (defcustom lsp-imenu-hide-parent-details t
   7126   "Whether `lsp-imenu' should hide signatures of parent nodes."
   7127   :group 'lsp-imenu
   7128   :type 'boolean)
   7129 
   7130 (defface lsp-details-face '((t :height 0.8 :inherit shadow))
   7131   "Used to display additional information throughout `lsp'.
   7132 Things like line numbers, signatures, ... are considered
   7133 additional information. Often, additional faces are defined that
   7134 inherit from this face by default, like `lsp-signature-face', and
   7135 they may be customized for finer control."
   7136   :group 'lsp-mode)
   7137 
   7138 (defface lsp-signature-face '((t :inherit lsp-details-face))
   7139   "Used to display signatures in `imenu', ...."
   7140   :group 'lsp-mode)
   7141 
   7142 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?)
   7143                               show-detail?)
   7144   "Render INPUT0, an `&DocumentSymbol', to a string.
   7145 If SHOW-DETAIL? is set, make use of its `:detail?' field (often
   7146 the signature)."
   7147   (let ((detail (and show-detail? (s-present? detail?)
   7148                      (propertize (concat " " (s-trim-left detail?))
   7149                                  'face 'lsp-signature-face)))
   7150         (name (if deprecated?
   7151                   (propertize name 'face 'lsp-face-semhl-deprecated) name)))
   7152     (concat name detail)))
   7153 
   7154 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?)
   7155                                           separator)
   7156   "Render a piece of SymbolInformation.
   7157 Handle :deprecated?. If SEPARATOR is non-nil, the
   7158 symbol's (optional) parent, SEPARATOR and the symbol itself are
   7159 concatenated."
   7160   (when (and separator container-name? (not (string-empty-p container-name?)))
   7161     (setq name (concat name separator container-name?)))
   7162   (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name))
   7163 
   7164 (defun lsp--symbol-to-imenu-elem (sym)
   7165   "Convert SYM to imenu element.
   7166 
   7167 SYM is a SymbolInformation message.
   7168 
   7169 Return a cons cell (full-name . start-point)."
   7170   (let ((start-point (ht-get lsp--line-col-to-point-hash-table
   7171                              (lsp--get-line-and-col sym))))
   7172     (cons (lsp-render-symbol-information
   7173            sym (and lsp-imenu-show-container-name
   7174                     lsp-imenu-container-name-separator))
   7175           start-point)))
   7176 
   7177 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?))
   7178   "Convert SYM to hierarchical imenu elements.
   7179 
   7180 SYM is a DocumentSymbol message.
   7181 
   7182 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if
   7183 SYM doesn't have any children. Otherwise return a cons cell with
   7184 an alist
   7185 
   7186   (\"symbol-name\" . ((\"(symbol-kind)\" . start-point)
   7187                     cons-cells-from-children))"
   7188   (let ((filtered-children (lsp--imenu-filter-symbols children?))
   7189         (signature (lsp-render-symbol sym lsp-imenu-detailed-outline)))
   7190     (if (seq-empty-p filtered-children)
   7191         (cons signature
   7192               (ht-get lsp--line-col-to-point-hash-table
   7193                       (lsp--get-line-and-col sym)))
   7194       (cons signature
   7195             (lsp--imenu-create-hierarchical-index filtered-children)))))
   7196 
   7197 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind))
   7198   "Determine if SYM is for the current document and is to be shown."
   7199   ;; It's a SymbolInformation or DocumentSymbol, which is always in the
   7200   ;; current buffer file.
   7201   (and lsp-imenu-index-symbol-kinds
   7202        (numberp kind)
   7203        (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup))
   7204                                kind
   7205                              0)))
   7206          (not (memql (aref lsp/symbol-kind-lookup clamped-kind)
   7207                      lsp-imenu-index-symbol-kinds)))))
   7208 
   7209 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind))
   7210   "The string name of the kind of SYM."
   7211   (alist-get kind lsp-symbol-kinds "Other"))
   7212 
   7213 (defun lsp--get-line-and-col (sym)
   7214   "Obtain the line and column corresponding to SYM."
   7215   (-let* ((location (lsp:symbol-information-location sym))
   7216           (name-range (or (and location (lsp:location-range location))
   7217                           (lsp:document-symbol-selection-range sym)))
   7218           ((&Range :start (&Position :line :character)) name-range))
   7219     (cons line character)))
   7220 
   7221 (defun lsp--collect-lines-and-cols (symbols)
   7222   "Return a sorted list ((line . col) ...) of the locations of SYMBOLS."
   7223   (let ((stack (mapcar 'identity symbols))
   7224         line-col-list)
   7225     (while stack
   7226       (let ((sym (pop stack)))
   7227         (push (lsp--get-line-and-col sym) line-col-list)
   7228         (unless (seq-empty-p (lsp:document-symbol-children? sym))
   7229           (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack)))))
   7230     (-sort #'lsp--line-col-comparator line-col-list)))
   7231 
   7232 (defun lsp--convert-line-col-to-points-batch (line-col-list)
   7233   "Convert a sorted list of positions from line-column
   7234 representation to point representation."
   7235   (let ((line-col-to-point-map (ht-create))
   7236         (inhibit-field-text-motion t)
   7237         (curr-line 0))
   7238     (lsp-save-restriction-and-excursion
   7239       (goto-char (point-min))
   7240       (cl-loop for (line . col) in line-col-list do
   7241                (forward-line (- line curr-line))
   7242                (setq curr-line line)
   7243                (let ((line-end (line-end-position)))
   7244                  (if (or (not col) (> col (- line-end (point))))
   7245                      (goto-char line-end)
   7246                    (forward-char col)))
   7247                (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers
   7248                                                                   (point-marker)
   7249                                                                 (point)))))
   7250     line-col-to-point-map))
   7251 
   7252 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2))
   7253   (or (< l1 l2)
   7254       (and (= l1 l2)
   7255            (cond ((and c1 c2)
   7256                   (< c1 c2))
   7257                  (c1 t)))))
   7258 
   7259 (defun lsp-imenu-create-uncategorized-index (symbols)
   7260   "Create imenu index from document SYMBOLS.
   7261 This function, unlike `lsp-imenu-create-categorized-index', does
   7262 not categorize by type, but instead returns an `imenu' index
   7263 corresponding to the symbol hierarchy returned by the server
   7264 directly."
   7265   (let* ((lsp--line-col-to-point-hash-table (-> symbols
   7266                                                 lsp--collect-lines-and-cols
   7267                                                 lsp--convert-line-col-to-points-batch)))
   7268     (if (lsp--imenu-hierarchical-p symbols)
   7269         (lsp--imenu-create-hierarchical-index symbols)
   7270       (lsp--imenu-create-non-hierarchical-index symbols))))
   7271 
   7272 (defcustom lsp-imenu-symbol-kinds
   7273   '((1 . "Files")
   7274     (2 . "Modules")
   7275     (3 . "Namespaces")
   7276     (4 . "Packages")
   7277     (5 . "Classes")
   7278     (6 . "Methods")
   7279     (7 . "Properties")
   7280     (8 . "Fields")
   7281     (9 . "Constructors")
   7282     (10 . "Enums")
   7283     (11 . "Interfaces")
   7284     (12 . "Functions")
   7285     (13 . "Variables")
   7286     (14 . "Constants")
   7287     (15 . "Strings")
   7288     (16 . "Numbers")
   7289     (17 . "Booleans")
   7290     (18 . "Arrays")
   7291     (19 . "Objects")
   7292     (20 . "Keys")
   7293     (21 . "Nulls")
   7294     (22 . "Enum Members")
   7295     (23 . "Structs")
   7296     (24 . "Events")
   7297     (25 . "Operators")
   7298     (26 . "Type Parameters"))
   7299   "`lsp-symbol-kinds', but only used by `imenu'.
   7300 A new variable is needed, as it is `imenu' convention to use
   7301 pluralized categories, which `lsp-symbol-kinds' doesn't. If the
   7302 non-pluralized names are preferred, this can be set to
   7303 `lsp-symbol-kinds'."
   7304   :type '(alist :key-type integer :value-type string))
   7305 
   7306 (defun lsp--imenu-kind->name (kind)
   7307   (alist-get kind lsp-imenu-symbol-kinds "?"))
   7308 
   7309 (defun lsp-imenu-create-top-level-categorized-index (symbols)
   7310   "Create an `imenu' index categorizing SYMBOLS by type.
   7311 Only root symbols are categorized.
   7312 
   7313 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS
   7314 shall be a list of DocumentSymbols or SymbolInformation."
   7315   (mapcan
   7316    (-lambda ((type . symbols))
   7317      (let ((cat (lsp--imenu-kind->name type))
   7318            (symbols (lsp-imenu-create-uncategorized-index symbols)))
   7319        ;; If there is no :kind (this is being defensive), or we couldn't look it
   7320        ;; up, just display the symbols inline, without categories.
   7321        (if cat (list (cons cat symbols)) symbols)))
   7322    (sort (seq-group-by #'lsp:document-symbol-kind symbols)
   7323          (-lambda ((kinda) (kindb)) (< kinda kindb)))))
   7324 
   7325 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start)))
   7326   "Convert an `&DocumentSymbol' to an `imenu' entry."
   7327   (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start))
   7328 
   7329 (defun lsp--imenu-create-categorized-index-1 (symbols)
   7330   "Returns an `imenu' index from SYMBOLS categorized by type.
   7331 The result looks like this: ((\"Variables\" . (...)))."
   7332   (->>
   7333    symbols
   7334    (mapcan
   7335     (-lambda ((sym &as &DocumentSymbol :kind :children?))
   7336       (if (seq-empty-p children?)
   7337           (list (list kind (lsp--symbol->imenu sym)))
   7338         (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline
   7339                                                   (not lsp-imenu-hide-parent-details)))))
   7340           (cons
   7341            (list kind (lsp--symbol->imenu sym))
   7342            (mapcar (-lambda ((type .  imenu-items))
   7343                      (list type (cons parent (mapcan #'cdr imenu-items))))
   7344                    (-group-by #'car (lsp--imenu-create-categorized-index-1 children?))))))))
   7345    (-group-by #'car)
   7346    (mapcar
   7347     (-lambda ((kind . syms))
   7348       (cons kind (mapcan #'cdr syms))))))
   7349 
   7350 (defun lsp--imenu-create-categorized-index (symbols)
   7351   (let ((syms (lsp--imenu-create-categorized-index-1 symbols)))
   7352     (dolist (sym syms)
   7353       (setcar sym (lsp--imenu-kind->name (car sym))))
   7354     syms))
   7355 
   7356 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start))))
   7357   (cons (lsp-render-symbol-information sym nil) start))
   7358 
   7359 (defun lsp--imenu-create-categorized-index-flat (symbols)
   7360   "Create a kind-categorized index for SymbolInformation."
   7361   (mapcar (-lambda ((kind . syms))
   7362             (cons (lsp--imenu-kind->name kind)
   7363                   (mapcan (-lambda ((parent . children))
   7364                             (let ((children (mapcar #'lsp--symbol-information->imenu children)))
   7365                               (if parent (list (cons parent children)) children)))
   7366                           (-group-by #'lsp:symbol-information-container-name? syms))))
   7367           (seq-group-by #'lsp:symbol-information-kind symbols)))
   7368 
   7369 (defun lsp-imenu-create-categorized-index (symbols)
   7370   (if (lsp--imenu-hierarchical-p symbols)
   7371       (lsp--imenu-create-categorized-index symbols)
   7372     (lsp--imenu-create-categorized-index-flat symbols)))
   7373 
   7374 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index
   7375   "Function that should create an `imenu' index.
   7376 It will be called with a list of SymbolInformation or
   7377 DocumentSymbols, whose first level is already filtered. It shall
   7378 then return an appropriate `imenu' index (see
   7379 `imenu-create-index-function').
   7380 
   7381 Note that this interface is not stable, and subject to change any
   7382 time."
   7383   :group 'lsp-imenu
   7384   :type '(radio
   7385           (const :tag "Categorize by type"
   7386                  lsp-imenu-create-categorized-index)
   7387           (const :tag "Categorize root symbols by type"
   7388                  lsp-imenu-create-top-level-categorized-index)
   7389           (const :tag "Uncategorized, inline entries"
   7390                  lsp-imenu-create-uncategorized-index)
   7391           (function :tag "Custom function")))
   7392 
   7393 (defun lsp--imenu-create-index ()
   7394   "Create an `imenu' index based on the language server.
   7395 Respects `lsp-imenu-index-function'."
   7396   (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols))))
   7397     (funcall lsp-imenu-index-function symbols)))
   7398 
   7399 (defun lsp--imenu-filter-symbols (symbols)
   7400   "Filter out unsupported symbols from SYMBOLS."
   7401   (seq-remove #'lsp--symbol-ignore symbols))
   7402 
   7403 (defun lsp--imenu-hierarchical-p (symbols)
   7404   "Determine whether any element in SYMBOLS has children."
   7405   (seq-some #'lsp-document-symbol? symbols))
   7406 
   7407 (defun lsp--imenu-create-non-hierarchical-index (symbols)
   7408   "Create imenu index for non-hierarchical SYMBOLS.
   7409 
   7410 SYMBOLS are a list of DocumentSymbol messages.
   7411 
   7412 Return a nested alist keyed by symbol names. e.g.
   7413 
   7414    ((\"SomeClass\" (\"(Class)\" . 10)
   7415                  (\"someField (Field)\" . 20)
   7416                  (\"someFunction (Function)\" . 25)
   7417                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7418                                   (\"someSubField (Field)\" . 35))
   7419     (\"someFunction (Function)\" . 40))"
   7420   (seq-map (lambda (nested-alist)
   7421              (cons (car nested-alist)
   7422                    (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist))))
   7423            (seq-group-by #'lsp--get-symbol-type symbols)))
   7424 
   7425 (defun lsp--imenu-create-hierarchical-index (symbols)
   7426   "Create imenu index for hierarchical SYMBOLS.
   7427 
   7428 SYMBOLS are a list of DocumentSymbol messages.
   7429 
   7430 Return a nested alist keyed by symbol names. e.g.
   7431 
   7432    ((\"SomeClass\" (\"(Class)\" . 10)
   7433                  (\"someField (Field)\" . 20)
   7434                  (\"someFunction (Function)\" . 25)
   7435                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7436                                   (\"someSubField (Field)\" . 35))
   7437     (\"someFunction (Function)\" . 40))"
   7438   (seq-map #'lsp--symbol-to-hierarchical-imenu-elem
   7439            (seq-sort #'lsp--imenu-symbol-lessp symbols)))
   7440 
   7441 (defun lsp--imenu-symbol-lessp (sym1 sym2)
   7442   (let* ((compare-results (mapcar (lambda (method)
   7443                                     (funcall (alist-get method lsp--imenu-compare-function-alist)
   7444                                              sym1 sym2))
   7445                                   lsp-imenu-sort-methods))
   7446          (result (seq-find (lambda (result)
   7447                              (not (= result 0)))
   7448                            compare-results
   7449                            0)))
   7450     (and (numberp result) (< result 0))))
   7451 
   7452 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left)
   7453                                     (&SymbolInformation :kind right))
   7454   "Compare SYM1 and SYM2 by kind."
   7455   (- left right))
   7456 
   7457 (defun lsp--imenu-compare-line-col (sym1 sym2)
   7458   (if (lsp--line-col-comparator
   7459        (lsp--get-line-and-col sym1)
   7460        (lsp--get-line-and-col sym2))
   7461       -1
   7462     1))
   7463 
   7464 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1)
   7465                                     (&SymbolInformation :name name2))
   7466   "Compare SYM1 and SYM2 by name."
   7467   (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2))))
   7468     (if (numberp result) result 0)))
   7469 
   7470 (defun lsp--imenu-refresh ()
   7471   "Force Imenu to refresh itself."
   7472   (imenu--menubar-select imenu--rescan-item))
   7473 
   7474 (defun lsp-enable-imenu ()
   7475   "Use lsp-imenu for the current buffer."
   7476   (imenu--cleanup)
   7477   (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   7478   (setq-local imenu-menubar-modified-tick -1)
   7479   (setq-local imenu--index-alist nil)
   7480   (when menu-bar-mode
   7481     (lsp--imenu-refresh)))
   7482 
   7483 (defun lsp-resolve-final-command (command &optional test?)
   7484   "Resolve final function COMMAND."
   7485   (let* ((command (lsp-resolve-value command))
   7486          (command (cl-etypecase command
   7487                     (list
   7488                      (cl-assert (seq-every-p (apply-partially #'stringp) command) nil
   7489                                 "Invalid command list")
   7490                      command)
   7491                     (string (list command)))))
   7492     (if (and (file-remote-p default-directory) (not test?))
   7493         (list shell-file-name "-c"
   7494               (string-join (cons "stty raw > /dev/null;"
   7495                                  (mapcar #'shell-quote-argument command))
   7496                            " "))
   7497       command)))
   7498 
   7499 (defun lsp-server-present? (final-command)
   7500   "Check whether FINAL-COMMAND is present."
   7501   (let ((binary-found? (executable-find (cl-first final-command) t)))
   7502     (if binary-found?
   7503         (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command))
   7504       (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command)))
   7505     binary-found?))
   7506 
   7507 (defun lsp--value-to-string (value)
   7508   "Convert VALUE to a string that can be set as value in an environment
   7509 variable."
   7510   (cond
   7511    ((stringp value) value)
   7512    ((booleanp value) (if value
   7513                          "1"
   7514                        "0"))
   7515    ((and (sequencep value)
   7516          (seq-every-p #'stringp value)) (string-join value ":"))
   7517    (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables"))))
   7518 
   7519 (defun lsp--compute-process-environment (environment-fn)
   7520   "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'.
   7521 Ignore non-boolean keys whose value is nil."
   7522   (let ((environment (if environment-fn
   7523                          (funcall environment-fn)
   7524                        nil)))
   7525     (-flatten (cons (cl-loop for (key . value) in environment
   7526                              if (or (eval value)
   7527                                     (eq (get value 'custom-type) 'boolean))
   7528                              collect (concat key "=" (lsp--value-to-string
   7529                                                       (eval value))))
   7530                     process-environment))))
   7531 
   7532 (defun lsp--default-directory-for-connection (&optional path)
   7533   "Return path to be used for the working directory of a LSP process.
   7534 
   7535 If `lsp-use-workspace-root-for-server-default-directory' is
   7536 non-nil, uses `lsp-workspace-root' to find the directory
   7537 corresponding to PATH, else returns `default-directory'."
   7538   (if lsp-use-workspace-root-for-server-default-directory
   7539       (lsp-workspace-root path)
   7540     default-directory))
   7541 
   7542 (defun lsp--fix-remote-cmd (program)
   7543   "Helper for `lsp-stdio-connection'.
   7544 Originally coppied from eglot."
   7545 
   7546   (if (file-remote-p default-directory)
   7547       (list shell-file-name "-c"
   7548             (string-join (cons "stty raw > /dev/null;"
   7549                                (mapcar #'shell-quote-argument program))
   7550                          " "))
   7551     program))
   7552 
   7553 (defvar tramp-use-ssh-controlmaster-options)
   7554 (defvar tramp-ssh-controlmaster-options)
   7555 
   7556 (defun lsp-stdio-connection (command &optional test-command)
   7557   "Returns a connection property list using COMMAND.
   7558 COMMAND can be: A string, denoting the command to launch the
   7559 language server. A list of strings, denoting an executable with
   7560 its command line arguments. A function, that either returns a
   7561 string or a list of strings. In all cases, the launched language
   7562 server should send and receive messages on standard I/O.
   7563 TEST-COMMAND is a function with no arguments which returns
   7564 whether the command is present or not. When not specified
   7565 `lsp-mode' will check whether the first element of the list
   7566 returned by COMMAND is available via `executable-find'"
   7567   (cl-check-type command (or string
   7568                              function
   7569                              (and list
   7570                                   (satisfies (lambda (l)
   7571                                                (seq-every-p (lambda (el)
   7572                                                               (stringp el))
   7573                                                             l))))))
   7574   (list :connect (lambda (filter sentinel name environment-fn workspace)
   7575                    (if (and (functionp 'json-rpc-connection)
   7576                             (not (file-remote-p default-directory)))
   7577                        (lsp-json-rpc-connection workspace (lsp-resolve-final-command command))
   7578                      (let ((final-command (lsp-resolve-final-command command))
   7579                            (process-name (generate-new-buffer-name name))
   7580                            (process-environment
   7581                             (lsp--compute-process-environment environment-fn)))
   7582                        (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name)))
   7583                               (default-directory (lsp--default-directory-for-connection))
   7584                               (tramp-use-ssh-controlmaster-options 'suppress)
   7585                               (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none")
   7586                               (proc (make-process
   7587                                      :name process-name
   7588                                      :connection-type 'pipe
   7589                                      :buffer (format "*%s*" process-name)
   7590                                      :coding 'no-conversion
   7591                                      :command final-command
   7592                                      :filter filter
   7593                                      :sentinel sentinel
   7594                                      :stderr stderr-buf
   7595                                      :noquery t
   7596                                      :file-handler t)))
   7597                          (set-process-query-on-exit-flag proc nil)
   7598                          (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil)
   7599                          (with-current-buffer (get-buffer stderr-buf)
   7600                            ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc.
   7601                            (special-mode))
   7602                          (cons proc proc)))))
   7603         :test? (or
   7604                 test-command
   7605                 (lambda ()
   7606                   (lsp-server-present? (lsp-resolve-final-command command t))))))
   7607 
   7608 (defun lsp--open-network-stream (host port name)
   7609   "Open network stream to HOST:PORT.
   7610   NAME will be passed to `open-network-stream'.
   7611   RETRY-COUNT is the number of the retries.
   7612   SLEEP-INTERVAL is the sleep interval between each retry."
   7613   (let* ((retries 0)
   7614          (sleep-interval 0.01)
   7615          (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval))
   7616          connection)
   7617     (while (and (not connection) (< retries number-of-retries))
   7618       (condition-case err
   7619           (setq connection (open-network-stream name nil host port
   7620                                                 :type 'plain
   7621                                                 :coding 'no-conversion))
   7622         (file-error
   7623          (let ((inhibit-message t))
   7624            (lsp--warn "Failed to connect to %s:%s with error message %s"
   7625                       host
   7626                       port
   7627                       (error-message-string err))
   7628            (sleep-for sleep-interval)
   7629            (cl-incf retries)))))
   7630     (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port))))
   7631 
   7632 (defun lsp--port-available (host port)
   7633   "Return non-nil if HOST and PORT are available."
   7634   (condition-case _err
   7635       (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain))
   7636     (file-error t)))
   7637 
   7638 (defun lsp--find-available-port (host starting-port)
   7639   "Find available port on HOST starting from STARTING-PORT."
   7640   (let ((port starting-port))
   7641     (while (not (lsp--port-available host port))
   7642       (cl-incf port))
   7643     port))
   7644 
   7645 (defun lsp-tcp-connection (command-fn)
   7646   "Returns a connection property list similar to `lsp-stdio-connection'.
   7647 COMMAND-FN can only be a function that takes a single argument, a
   7648 port number. It should return a command for launches a language server
   7649 process listening for TCP connections on the provided port."
   7650   (cl-check-type command-fn function)
   7651   (list
   7652    :connect (lambda (filter sentinel name environment-fn _workspace)
   7653               (let* ((host "localhost")
   7654                      (port (lsp--find-available-port host (cl-incf lsp--tcp-port)))
   7655                      (command (funcall command-fn port))
   7656                      (final-command (if (consp command) command (list command)))
   7657                      (_ (unless (lsp-server-present? final-command)
   7658                           (user-error (format "Couldn't find executable %s" (cl-first final-command)))))
   7659                      (process-environment
   7660                       (lsp--compute-process-environment environment-fn))
   7661                      (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion
   7662                                          :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t))
   7663                      (tcp-proc (lsp--open-network-stream host port (concat name "::tcp"))))
   7664 
   7665                 ;; TODO: Same :noquery issue (see above)
   7666                 (set-process-query-on-exit-flag proc nil)
   7667                 (set-process-query-on-exit-flag tcp-proc nil)
   7668                 (set-process-filter tcp-proc filter)
   7669                 (cons tcp-proc proc)))
   7670    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7671 
   7672 (defalias 'lsp-tcp-server 'lsp-tcp-server-command)
   7673 
   7674 (defun lsp-tcp-server-command (command-fn)
   7675   "Create tcp server connection.
   7676 In this mode Emacs is TCP server and the language server connects
   7677 to it. COMMAND is function with one parameter(the port) and it
   7678 should return the command to start the LS server."
   7679   (cl-check-type command-fn function)
   7680   (list
   7681    :connect (lambda (filter sentinel name environment-fn _workspace)
   7682               (let* (tcp-client-connection
   7683                      (tcp-server (make-network-process :name (format "*tcp-server-%s*" name)
   7684                                                        :buffer (format "*tcp-server-%s*" name)
   7685                                                        :family 'ipv4
   7686                                                        :service lsp--tcp-server-port
   7687                                                        :sentinel (lambda (proc _string)
   7688                                                                    (lsp-log "Language server %s is connected." name)
   7689                                                                    (setf tcp-client-connection proc))
   7690                                                        :server 't))
   7691                      (port (process-contact tcp-server :service))
   7692                      (final-command (funcall command-fn port))
   7693                      (process-environment
   7694                       (lsp--compute-process-environment environment-fn))
   7695                      (cmd-proc (make-process :name name
   7696                                              :connection-type 'pipe
   7697                                              :coding 'no-conversion
   7698                                              :command final-command
   7699                                              :stderr (format "*tcp-server-%s*::stderr" name)
   7700                                              :noquery t)))
   7701                 (let ((retries 0))
   7702                   ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds)
   7703                   (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds)))
   7704                     (lsp--info "Waiting for connection for %s, retries: %s" name retries)
   7705                     (sit-for 0.500)
   7706                     (cl-incf retries)))
   7707 
   7708                 (unless tcp-client-connection
   7709                   (condition-case nil (delete-process tcp-server) (error))
   7710                   (condition-case nil (delete-process cmd-proc) (error))
   7711                   (error "Failed to create connection to %s on port %s" name port))
   7712                 (lsp--info "Successfully connected to %s" name)
   7713 
   7714                 (set-process-query-on-exit-flag cmd-proc nil)
   7715                 (set-process-query-on-exit-flag tcp-client-connection nil)
   7716                 (set-process-query-on-exit-flag tcp-server nil)
   7717 
   7718                 (set-process-filter tcp-client-connection filter)
   7719                 (set-process-sentinel tcp-client-connection sentinel)
   7720                 (cons tcp-client-connection cmd-proc)))
   7721    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7722 
   7723 (defalias 'lsp-tramp-connection 'lsp-stdio-connection)
   7724 
   7725 (defun lsp--auto-configure ()
   7726   "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed."
   7727   (when (functionp 'lsp-ui-mode)
   7728     (lsp-ui-mode))
   7729 
   7730   (if lsp-headerline-breadcrumb-enable
   7731       (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)
   7732     (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode))
   7733   (if lsp-modeline-code-actions-enable
   7734       (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)
   7735     (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode))
   7736   (if lsp-modeline-diagnostics-enable
   7737       (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)
   7738     (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode))
   7739   (if lsp-modeline-workspace-status-enable
   7740       (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)
   7741     (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode))
   7742   (if lsp-lens-enable
   7743       (add-hook 'lsp-configure-hook 'lsp-lens--enable)
   7744     (remove-hook 'lsp-configure-hook 'lsp-lens--enable))
   7745   (if lsp-semantic-tokens-enable
   7746       (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)
   7747     (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable))
   7748 
   7749   ;; yas-snippet config
   7750   (setq-local yas-inhibit-overlay-modification-protection t))
   7751 
   7752 (defun lsp--restart-if-needed (workspace)
   7753   "Handler restart for WORKSPACE."
   7754   (when (or (eq lsp-restart 'auto-restart)
   7755             (eq (lsp--workspace-shutdown-action workspace) 'restart)
   7756             (and (eq lsp-restart 'interactive)
   7757                  (let ((query (format
   7758                                "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?"
   7759                                (lsp--workspace-print workspace))))
   7760                    (y-or-n-p query))))
   7761     (--each (lsp--workspace-buffers workspace)
   7762       (when (lsp-buffer-live-p it)
   7763         (lsp-with-current-buffer it
   7764           (if lsp--buffer-deferred
   7765               (lsp-deferred)
   7766             (lsp--info "Restarting LSP in buffer %s" (buffer-name))
   7767             (lsp)))))))
   7768 
   7769 (defun lsp--update-key (table key fn)
   7770   "Apply FN on value corresponding to KEY in TABLE."
   7771   (let ((existing-value (gethash key table)))
   7772     (if-let ((new-value (funcall fn existing-value)))
   7773         (puthash key new-value table)
   7774       (remhash key table))))
   7775 
   7776 (defun lsp--process-sentinel (workspace process exit-str)
   7777   "Create the sentinel for WORKSPACE."
   7778   (unless (process-live-p process)
   7779     (lsp--handle-process-exit workspace exit-str)))
   7780 
   7781 (defun lsp--handle-process-exit (workspace exit-str)
   7782   (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session)))
   7783          (proc (lsp--workspace-proc workspace)))
   7784     (lsp--warn "%s has exited (%s)"
   7785                (lsp-process-name proc)
   7786                (string-trim-right (or exit-str "")))
   7787     (with-lsp-workspace workspace
   7788       ;; Clean workspace related data in each of the buffers
   7789       ;; in the workspace.
   7790       (--each (lsp--workspace-buffers workspace)
   7791         (when (lsp-buffer-live-p it)
   7792           (lsp-with-current-buffer it
   7793             (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces))
   7794             (lsp--uninitialize-workspace)
   7795             (lsp--spinner-stop)
   7796             (lsp--remove-overlays 'lsp-highlight))))
   7797 
   7798       ;; Cleanup session from references to the closed workspace.
   7799       (--each (hash-table-keys folder->workspaces)
   7800         (lsp--update-key folder->workspaces it (apply-partially 'delete workspace)))
   7801 
   7802       (lsp-process-cleanup proc))
   7803 
   7804     (run-hook-with-args 'lsp-after-uninitialized-functions workspace)
   7805 
   7806     (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown)
   7807         (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace))
   7808       (lsp--restart-if-needed workspace))
   7809     (lsp--cleanup-hanging-watches)))
   7810 
   7811 (defun lsp-workspace-folders (workspace)
   7812   "Return all folders associated with WORKSPACE."
   7813   (let (result)
   7814     (->> (lsp-session)
   7815          (lsp-session-folder->servers)
   7816          (maphash (lambda (folder workspaces)
   7817                     (when (-contains? workspaces workspace)
   7818                       (push folder result)))))
   7819     result))
   7820 
   7821 (defun lsp--start-workspace (session client-template root &optional initialization-options)
   7822   "Create new workspace for CLIENT-TEMPLATE with project root ROOT.
   7823 INITIALIZATION-OPTIONS are passed to initialize function.
   7824 SESSION is the active session."
   7825   (lsp--spinner-start)
   7826   (-let* ((default-directory root)
   7827           (client (copy-lsp--client client-template))
   7828           (workspace (make-lsp--workspace
   7829                       :root root
   7830                       :client client
   7831                       :status 'starting
   7832                       :buffers (list (lsp-current-buffer))
   7833                       :host-root (file-remote-p root)))
   7834           ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities
   7835                      'multi-root 'initialized-fn) client)
   7836           ((proc . cmd-proc) (funcall
   7837                               (or (plist-get new-connection :connect)
   7838                                   (user-error "Client %s is configured incorrectly" client))
   7839                               (lsp--create-filter-function workspace)
   7840                               (apply-partially #'lsp--process-sentinel workspace)
   7841                               (format "%s" server-id)
   7842                               environment-fn
   7843                               workspace))
   7844           (workspace-folders (gethash server-id (lsp-session-server-id->folders session))))
   7845     (setf (lsp--workspace-proc workspace) proc
   7846           (lsp--workspace-cmd-proc workspace) cmd-proc)
   7847 
   7848     ;; update (lsp-session-folder->servers) depending on whether we are starting
   7849     ;; multi/single folder workspace
   7850     (mapc (lambda (project-root)
   7851             (->> session
   7852                  (lsp-session-folder->servers)
   7853                  (gethash project-root)
   7854                  (cl-pushnew workspace)))
   7855           (or workspace-folders (list root)))
   7856 
   7857     (with-lsp-workspace workspace
   7858       (run-hooks 'lsp-before-initialize-hook)
   7859       (lsp-request-async
   7860        "initialize"
   7861        (append
   7862         (list :processId (unless (file-remote-p (buffer-file-name))
   7863                            (emacs-pid))
   7864               :rootPath (lsp-file-local-name (expand-file-name root))
   7865               :clientInfo (list :name "emacs"
   7866                                 :version (emacs-version))
   7867               :rootUri (lsp--path-to-uri root)
   7868               :capabilities (lsp--client-capabilities custom-capabilities)
   7869               :initializationOptions initialization-options
   7870               :workDoneToken "1")
   7871         (when lsp-server-trace
   7872           (list :trace lsp-server-trace))
   7873         (when multi-root
   7874           (->> workspace-folders
   7875                (-distinct)
   7876                (-map (lambda (folder)
   7877                        (list :uri (lsp--path-to-uri folder)
   7878                              :name (f-filename folder))))
   7879                (apply 'vector)
   7880                (list :workspaceFolders))))
   7881        (-lambda ((&InitializeResult :capabilities))
   7882          ;; we know that Rust Analyzer will send {} which will be parsed as null
   7883          ;; when using plists
   7884          (when (equal 'rust-analyzer server-id)
   7885            (-> capabilities
   7886                (lsp:server-capabilities-text-document-sync?)
   7887                (lsp:set-text-document-sync-options-save? t)))
   7888 
   7889          (setf (lsp--workspace-server-capabilities workspace) capabilities
   7890                (lsp--workspace-status workspace) 'initialized)
   7891 
   7892          (with-lsp-workspace workspace
   7893            (lsp-notify "initialized" lsp--empty-ht))
   7894 
   7895          (when initialized-fn (funcall initialized-fn workspace))
   7896 
   7897          (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace))
   7898          (->> workspace
   7899               (lsp--workspace-buffers)
   7900               (mapc (lambda (buffer)
   7901                       (lsp-with-current-buffer buffer
   7902                         (lsp--open-in-workspace workspace)))))
   7903 
   7904          (with-lsp-workspace workspace
   7905            (run-hooks 'lsp-after-initialize-hook))
   7906          (lsp--info "%s initialized successfully in folders: %s"
   7907                     (lsp--workspace-print workspace)
   7908                     (lsp-workspace-folders workspace)))
   7909        :mode 'detached))
   7910     workspace))
   7911 
   7912 (defun lsp--load-default-session ()
   7913   "Load default session."
   7914   (setq lsp--session (or (condition-case err
   7915                              (lsp--read-from-file lsp-session-file)
   7916                            (error (lsp--error "Failed to parse the session %s, starting with clean one."
   7917                                               (error-message-string err))
   7918                                   nil))
   7919                          (make-lsp-session))))
   7920 
   7921 (defun lsp-session ()
   7922   "Get the session associated with the current buffer."
   7923   (or lsp--session (setq lsp--session (lsp--load-default-session))))
   7924 
   7925 (defun lsp--client-disabled-p (buffer-major-mode client)
   7926   (seq-some
   7927    (lambda (entry)
   7928      (pcase entry
   7929        ((pred symbolp) (eq entry client))
   7930        (`(,mode . ,client-or-list)
   7931         (and (eq mode buffer-major-mode)
   7932              (if (listp client-or-list)
   7933                  (memq client client-or-list)
   7934                (eq client client-or-list))))))
   7935    lsp-disabled-clients))
   7936 
   7937 
   7938 ;; download server
   7939 
   7940 (defcustom lsp-server-install-dir (expand-file-name
   7941                                    (locate-user-emacs-file (f-join ".cache" "lsp")))
   7942   "Directory in which the servers will be installed."
   7943   :risky t
   7944   :type 'directory
   7945   :package-version '(lsp-mode . "6.3")
   7946   :group 'lsp-mode)
   7947 
   7948 (defcustom lsp-verify-signature t
   7949   "Whether to check GPG signatures of downloaded files."
   7950   :type 'boolean
   7951   :package-version '(lsp-mode . "8.0.0")
   7952   :group 'lsp-mode)
   7953 
   7954 (defvar lsp--dependencies (ht))
   7955 
   7956 (defun lsp-dependency (name &rest definitions)
   7957   "Used to specify a language server DEPENDENCY, the server
   7958 executable or other required file path. Typically, the
   7959 DEPENDENCY is found by locating it on the system path using
   7960 `executable-find'.
   7961 
   7962 You can explicitly call lsp-dependency in your environment to
   7963 specify the absolute path to the DEPENDENCY. For example, the
   7964 typescript-language-server requires both the server and the
   7965 typescript compiler. If you have installed them in a team shared
   7966 read-only location, you can instruct lsp-mode to use them via
   7967 
   7968  (eval-after-load `lsp-mode
   7969    `(progn
   7970       (require lsp-javascript)
   7971       (lsp-dependency typescript-language-server (:system ,tls-exe))
   7972       (lsp-dependency typescript (:system ,ts-js))))
   7973 
   7974 where tls-exe is the absolute path to the typescript-language-server
   7975 executable and ts-js is the absolute path to the typescript compiler
   7976 JavaScript file, tsserver.js (the *.js is required for Windows)."
   7977   (ht-set lsp--dependencies name definitions))
   7978 
   7979 (defun lsp--server-binary-present? (client)
   7980   (unless (equal (lsp--client-server-id client) 'lsp-pwsh)
   7981     (condition-case ()
   7982         (-some-> client lsp--client-new-connection (plist-get :test?) funcall)
   7983       (error nil)
   7984       (args-out-of-range nil))))
   7985 
   7986 (define-minor-mode lsp-installation-buffer-mode
   7987   "Mode used in *lsp-installation* buffers.
   7988 It can be used to set-up keybindings, etc. Disabling this mode
   7989 detaches the installation buffer from commands like
   7990 `lsp-select-installation-buffer'."
   7991   :init-value nil
   7992   :lighter nil)
   7993 
   7994 (defface lsp-installation-finished-buffer-face '((t :foreground "orange"))
   7995   "Face used for finished installation buffers.
   7996 Used in `lsp-select-installation-buffer'."
   7997   :group 'lsp-mode)
   7998 
   7999 (defface lsp-installation-buffer-face '((t :foreground "green"))
   8000   "Face used for installation buffers still in progress.
   8001 Used in `lsp-select-installation-buffer'."
   8002   :group 'lsp-mode)
   8003 
   8004 (defun lsp--installation-buffer? (buf)
   8005   "Check whether BUF is an `lsp-async-start-process' buffer."
   8006   (buffer-local-value 'lsp-installation-buffer-mode buf))
   8007 
   8008 (defun lsp-select-installation-buffer (&optional show-finished)
   8009   "Interactively choose an installation buffer.
   8010 If SHOW-FINISHED is set, leftover (finished) installation buffers
   8011 are still shown."
   8012   (interactive "P")
   8013   (let ((bufs (--filter (and (lsp--installation-buffer? it)
   8014                              (or show-finished (get-buffer-process it)))
   8015                         (buffer-list))))
   8016     (pcase bufs
   8017       (`nil (user-error "No installation buffers"))
   8018       (`(,buf) (pop-to-buffer buf))
   8019       (bufs (pop-to-buffer (completing-read "Select installation buffer: "
   8020                                             (--map (propertize (buffer-name it) 'face
   8021                                                                (if (get-buffer-process it)
   8022                                                                    'lsp-installation-buffer-face
   8023                                                                  'lsp-installation-finished-buffer-face))
   8024                                                    bufs)))))))
   8025 
   8026 (defun lsp-cleanup-installation-buffers ()
   8027   "Delete finished *lsp-installation* buffers."
   8028   (interactive)
   8029   (dolist (buf (buffer-list))
   8030     (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf)))
   8031       (kill-buffer buf))))
   8032 
   8033 (defun lsp--download-status ()
   8034   (-some--> #'lsp--client-download-in-progress?
   8035     (lsp--filter-clients it)
   8036     (-map (-compose #'symbol-name #'lsp--client-server-id) it)
   8037     (format "%s" it)
   8038     (propertize it 'face 'success)
   8039     (format " Installing following servers: %s" it)
   8040     (propertize it
   8041                 'local-map (make-mode-line-mouse-map
   8042                             'mouse-1 #'lsp-select-installation-buffer)
   8043                 'mouse-face 'highlight)))
   8044 
   8045 (defun lsp--install-server-internal (client &optional update?)
   8046   (unless (lsp--client-download-server-fn client)
   8047     (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation."
   8048                 (lsp--client-server-id client)))
   8049 
   8050   (setf (lsp--client-download-in-progress? client) t)
   8051   (add-to-list 'global-mode-string '(t (:eval (lsp--download-status))))
   8052   (cl-flet ((done
   8053              (success? &optional error-message)
   8054              ;; run with idle timer to make sure the lsp command is executed in
   8055              ;; the main thread, see #2739.
   8056              (run-with-timer
   8057               0.0
   8058               nil
   8059               (lambda ()
   8060                 (-let [(&lsp-cln 'server-id 'buffers) client]
   8061                   (setf (lsp--client-download-in-progress? client) nil
   8062                         (lsp--client-buffers client) nil)
   8063                   (if success?
   8064                       (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id
   8065                                  (length buffers))
   8066                     (lsp--error "Server %s install process failed with the following error message: %s.
   8067 Check `*lsp-install*' and `*lsp-log*' buffer."
   8068                                 server-id
   8069                                 error-message))
   8070                   (seq-do
   8071                    (lambda (buffer)
   8072                      (when (lsp-buffer-live-p buffer)
   8073                        (lsp-with-current-buffer buffer
   8074                          (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   8075                                     global-mode-string)
   8076                          (when success? (lsp)))))
   8077                    buffers)
   8078                   (unless (lsp--filter-clients #'lsp--client-download-in-progress?)
   8079                     (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   8080                                global-mode-string)))))))
   8081     (lsp--info "Download %s started." (lsp--client-server-id client))
   8082     (condition-case err
   8083         (funcall
   8084          (lsp--client-download-server-fn client)
   8085          client
   8086          (lambda () (done t))
   8087          (lambda (msg) (done nil msg))
   8088          update?)
   8089       (error
   8090        (done nil (error-message-string err))))))
   8091 
   8092 (defun lsp--require-packages ()
   8093   "Load `lsp-client-packages' if needed."
   8094   (when (and lsp-auto-configure (not lsp--client-packages-required))
   8095     (seq-do (lambda (package)
   8096               ;; loading client is slow and `lsp' can be called repeatedly
   8097               (unless (featurep package)
   8098                 (require package nil t)))
   8099             lsp-client-packages)
   8100     (setq lsp--client-packages-required t)))
   8101 
   8102 ;;;###autoload
   8103 (defun lsp-install-server (update? &optional server-id)
   8104   "Interactively install or re-install server.
   8105 When prefix UPDATE? is t force installation even if the server is present."
   8106   (interactive "P")
   8107   (lsp--require-packages)
   8108   (let* ((chosen-client (or (gethash server-id lsp-clients)
   8109                             (lsp--completing-read
   8110                              "Select server to install/re-install: "
   8111                              (or (->> lsp-clients
   8112                                       (ht-values)
   8113                                       (-filter (-andfn
   8114                                                 (-not #'lsp--client-download-in-progress?)
   8115                                                 #'lsp--client-download-server-fn)))
   8116                                  (user-error "There are no servers with automatic installation"))
   8117                              (lambda (client)
   8118                                (let ((server-name (-> client lsp--client-server-id symbol-name)))
   8119                                  (if (lsp--server-binary-present? client)
   8120                                      (concat server-name " (Already installed)")
   8121                                    server-name)))
   8122                              nil
   8123                              t)))
   8124          (update? (or update?
   8125                       (and (not (lsp--client-download-in-progress? chosen-client))
   8126                            (lsp--server-binary-present? chosen-client)))))
   8127     (lsp--install-server-internal chosen-client update?)))
   8128 
   8129 ;;;###autoload
   8130 (defun lsp-uninstall-server (dir)
   8131   "Delete a LSP server from `lsp-server-install-dir'."
   8132   (interactive
   8133    (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir))))
   8134   (unless (file-directory-p dir)
   8135     (user-error "Couldn't find %s directory" dir))
   8136   (delete-directory dir 'recursive)
   8137   (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir))))
   8138 
   8139 ;;;###autoload
   8140 (defun lsp-uninstall-servers ()
   8141   "Uninstall all installed servers."
   8142   (interactive)
   8143   (let* ((dir lsp-server-install-dir)
   8144          (servers (ignore-errors
   8145                     (directory-files dir t
   8146                                      directory-files-no-dot-files-regexp))))
   8147     (if (or (not (file-directory-p dir)) (zerop (length servers)))
   8148         (user-error "No servers to uninstall")
   8149       (when (yes-or-no-p
   8150              (format "Servers to uninstall: %d (%s), proceed? "
   8151                      (length servers)
   8152                      (mapconcat (lambda (server)
   8153                                   (file-name-nondirectory (directory-file-name server)))
   8154                                 servers " ")))
   8155         (mapc #'lsp-uninstall-server servers)
   8156         (message "All servers uninstalled")))))
   8157 
   8158 ;;;###autoload
   8159 (defun lsp-update-server (&optional server-id)
   8160   "Interactively update (reinstall) a server."
   8161   (interactive)
   8162   (lsp--require-packages)
   8163   (let ((chosen-client (or (gethash server-id lsp-clients)
   8164                            (lsp--completing-read
   8165                             "Select server to update (if not on the list, probably you need to `lsp-install-server`): "
   8166                             (or (->> lsp-clients
   8167                                      (ht-values)
   8168                                      (-filter (-andfn
   8169                                                (-not #'lsp--client-download-in-progress?)
   8170                                                #'lsp--client-download-server-fn
   8171                                                #'lsp--server-binary-present?)))
   8172                                 (user-error "There are no servers to update"))
   8173                             (lambda (client)
   8174                               (-> client lsp--client-server-id symbol-name))
   8175                             nil
   8176                             t))))
   8177     (lsp--install-server-internal chosen-client t)))
   8178 
   8179 ;;;###autoload
   8180 (defun lsp-update-servers ()
   8181   "Update (reinstall) all installed servers."
   8182   (interactive)
   8183   (lsp--require-packages)
   8184   (mapc (lambda (client) (lsp--install-server-internal client t))
   8185         (-filter (-andfn
   8186                   (-not #'lsp--client-download-in-progress?)
   8187                   #'lsp--client-download-server-fn
   8188                   #'lsp--server-binary-present?) (hash-table-values lsp-clients))))
   8189 
   8190 ;;;###autoload
   8191 (defun lsp-ensure-server (server-id)
   8192   "Ensure server SERVER-ID"
   8193   (lsp--require-packages)
   8194   (if-let ((client (gethash server-id lsp-clients)))
   8195       (unless (lsp--server-binary-present? client)
   8196         (lsp--info "Server `%s' is not preset, installing..." server-id)
   8197         (lsp-install-server nil server-id))
   8198     (warn "Unable to find server registration with id %s" server-id)))
   8199 
   8200 (defun lsp-async-start-process (callback error-callback &rest command)
   8201   "Start async process COMMAND with CALLBACK and ERROR-CALLBACK."
   8202   (let ((name (cl-first command)))
   8203     (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd)
   8204                                                                                          (not (null cmd)))
   8205                                                                                        command)
   8206                                                        " ") t
   8207                                             (lambda (&rest _)
   8208                                               (generate-new-buffer-name (format "*lsp-install: %s*" name))))
   8209       (lsp-installation-buffer-mode +1)
   8210       (view-mode +1)
   8211       (add-hook
   8212        'compilation-finish-functions
   8213        (lambda (_buf status)
   8214          (if (string= "finished\n" status)
   8215              (condition-case err
   8216                  (funcall callback)
   8217                (error
   8218                 (funcall error-callback (error-message-string err))))
   8219            (funcall error-callback (s-trim-right status))))
   8220        nil t))))
   8221 
   8222 (defun lsp-resolve-value (value)
   8223   "Resolve VALUE's value.
   8224 If it is function - call it.
   8225 If it is a variable - return it's value
   8226 Otherwise returns value itself."
   8227   (cond
   8228    ((functionp value) (funcall value))
   8229    ((and (symbolp value) (boundp value)) (symbol-value value))
   8230    (value)))
   8231 
   8232 (defvar lsp-deps-providers
   8233   (list :npm (list :path #'lsp--npm-dependency-path
   8234                    :install #'lsp--npm-dependency-install)
   8235         :cargo (list :path #'lsp--cargo-dependency-path
   8236                      :install #'lsp--cargo-dependency-install)
   8237         :system (list :path #'lsp--system-path)
   8238         :download (list :path #'lsp-download-path
   8239                         :install #'lsp-download-install)))
   8240 
   8241 (defun lsp--system-path (path)
   8242   "If PATH is absolute and exists return it as is. Otherwise,
   8243 return the absolute path to the executable defined by PATH or
   8244 nil."
   8245   ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the
   8246   ;; typescript-language-server. When lsp invokes the server, lsp needs to
   8247   ;; supply the path to the typescript compiler, tsserver.js, as an argument. To
   8248   ;; make code platform independent, one must pass the absolute path to the
   8249   ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript
   8250   ;; child process spawn command that is invoked by the
   8251   ;; typescript-language-server). This is why we check for existence and not
   8252   ;; that the path is executable.
   8253   (let ((path (lsp-resolve-value path)))
   8254     (cond
   8255      ((and (f-absolute? path)
   8256            (f-exists? path))
   8257       path)
   8258      ((executable-find path t) path))))
   8259 
   8260 (defun lsp-package-path (dependency)
   8261   "Path to the DEPENDENCY each of the registered providers."
   8262   (let (path)
   8263     (-first (-lambda ((provider . rest))
   8264               (setq path (-some-> lsp-deps-providers
   8265                            (plist-get provider)
   8266                            (plist-get :path)
   8267                            (apply rest))))
   8268             (gethash dependency lsp--dependencies))
   8269     path))
   8270 
   8271 (defun lsp-package-ensure (dependency callback error-callback)
   8272   "Asynchronously ensure a package."
   8273   (or (-first (-lambda ((provider . rest))
   8274                 (-some-> lsp-deps-providers
   8275                   (plist-get provider)
   8276                   (plist-get :install)
   8277                   (apply (cl-list* callback error-callback rest))))
   8278               (gethash dependency lsp--dependencies))
   8279       (funcall error-callback (format "Unable to find a way to install %s" dependency))))
   8280 
   8281 
   8282 ;; npm handling
   8283 
   8284 ;; https://docs.npmjs.com/files/folders#executables
   8285 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys)
   8286   "Return npm dependency PATH for PACKAGE."
   8287   (let ((path (executable-find
   8288                (f-join lsp-server-install-dir "npm" package
   8289                        (cond ((eq system-type 'windows-nt) "")
   8290                              (t "bin"))
   8291                        path)
   8292                t)))
   8293     (unless (and path (f-exists? path))
   8294       (error "The package %s is not installed.  Unable to find %s" package path))
   8295     path))
   8296 
   8297 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys)
   8298   (if-let ((npm-binary (executable-find "npm")))
   8299       (progn
   8300         ;; Explicitly `make-directory' to work around NPM bug in
   8301         ;; versions 7.0.0 through 7.4.1. See
   8302         ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for
   8303         ;; discussion.
   8304         (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents)
   8305         (lsp-async-start-process (lambda ()
   8306                                    (if (string-empty-p
   8307                                         (string-trim (shell-command-to-string
   8308                                                       (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " "))))
   8309                                        (funcall callback)
   8310                                      (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json")))))
   8311                                            (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx
   8312                                        (when (f-dir-p default-directory)
   8313                                          (lsp-async-start-process callback
   8314                                                                   error-callback
   8315                                                                   (executable-find "npx")
   8316                                                                   "npm-install-peers")))))
   8317                                  error-callback
   8318                                  npm-binary
   8319                                  "-g"
   8320                                  "--prefix"
   8321                                  (f-join lsp-server-install-dir "npm" package)
   8322                                  "install"
   8323                                  package))
   8324     (lsp-log "Unable to install %s via `npm' because it is not present" package)
   8325     nil))
   8326 
   8327 
   8328 ;; Cargo dependency handling
   8329 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys)
   8330   (let ((path (executable-find
   8331                (f-join lsp-server-install-dir
   8332                        "cargo"
   8333                        package
   8334                        "bin"
   8335                        path)
   8336                t)))
   8337     (unless (and path (f-exists? path))
   8338       (error "The package %s is not installed.  Unable to find %s" package path))
   8339     path))
   8340 
   8341 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys)
   8342   (if-let ((cargo-binary (executable-find "cargo")))
   8343       (lsp-async-start-process
   8344        callback
   8345        error-callback
   8346        cargo-binary
   8347        "install"
   8348        package
   8349        (when git
   8350          "--git")
   8351        git
   8352        "--root"
   8353        (f-join lsp-server-install-dir "cargo" package))
   8354     (lsp-log "Unable to install %s via `cargo' because it is not present" package)
   8355     nil))
   8356 
   8357 
   8358 
   8359 ;; Download URL handling
   8360 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys)
   8361   (let* ((url (lsp-resolve-value url))
   8362          (store-path (lsp-resolve-value store-path))
   8363          ;; (decompress (lsp-resolve-value decompress))
   8364          (download-path
   8365           (pcase decompress
   8366             (:gzip (concat store-path ".gz"))
   8367             (:zip (concat store-path ".zip"))
   8368             (:targz (concat store-path ".tar.gz"))
   8369             (`nil store-path)
   8370             (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'")))))
   8371     (make-thread
   8372      (lambda ()
   8373        (condition-case err
   8374            (progn
   8375              (when (f-exists? download-path)
   8376                (f-delete download-path))
   8377              (when (f-exists? store-path)
   8378                (f-delete store-path))
   8379              (lsp--info "Starting to download %s to %s..." url download-path)
   8380              (mkdir (f-parent download-path) t)
   8381              (url-copy-file url download-path)
   8382              (lsp--info "Finished downloading %s..." download-path)
   8383              (when (and lsp-verify-signature asc-url pgp-key)
   8384                (if (executable-find epg-gpg-program)
   8385                    (let ((asc-download-path (concat download-path ".asc"))
   8386                          (context (epg-make-context))
   8387                          (fingerprint)
   8388                          (signature))
   8389                      (when (f-exists? asc-download-path)
   8390                        (f-delete asc-download-path))
   8391                      (lsp--info "Starting to download %s to %s..." asc-url asc-download-path)
   8392                      (url-copy-file asc-url asc-download-path)
   8393                      (lsp--info "Finished downloading %s..." asc-download-path)
   8394                      (epg-import-keys-from-string context pgp-key)
   8395                      (setq fingerprint (epg-import-status-fingerprint
   8396                                         (car
   8397                                          (epg-import-result-imports
   8398                                           (epg-context-result-for context 'import)))))
   8399                      (lsp--info "Verifying signature %s..." asc-download-path)
   8400                      (epg-verify-file context asc-download-path download-path)
   8401                      (setq signature (car (epg-context-result-for context 'verify)))
   8402                      (unless (and
   8403                               (eq (epg-signature-status signature) 'good)
   8404                               (equal (epg-signature-fingerprint signature) fingerprint))
   8405                        (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature))))
   8406                  (lsp--warn "GPG is not installed, skipping the signature check.")))
   8407              (when decompress
   8408                (lsp--info "Decompressing %s..." download-path)
   8409                (pcase decompress
   8410                  (:gzip
   8411                   (lsp-gunzip download-path))
   8412                  (:zip (lsp-unzip download-path (f-parent store-path)))
   8413                  (:targz (lsp-tar-gz-decompress download-path (f-parent store-path))))
   8414                (lsp--info "Decompressed %s..." store-path))
   8415              (funcall callback))
   8416          (error (funcall error-callback err)))))))
   8417 
   8418 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys)
   8419   "Download URL and store it into STORE-PATH.
   8420 
   8421 SET-EXECUTABLE? when non-nil change the executable flags of
   8422 STORE-PATH to make it executable. BINARY-PATH can be specified
   8423 when the binary to start does not match the name of the
   8424 archive (e.g. when the archive has multiple files)"
   8425   (let ((store-path (or (lsp-resolve-value binary-path)
   8426                         (lsp-resolve-value store-path))))
   8427     (cond
   8428      ((executable-find store-path) store-path)
   8429      ((and set-executable? (f-exists? store-path))
   8430       (set-file-modes store-path #o0700)
   8431       store-path)
   8432      ((f-exists? store-path) store-path))))
   8433 
   8434 (defun lsp--find-latest-gh-release-url (url regex)
   8435   "Fetch the latest version in the releases given by URL by using REGEX."
   8436   (let ((url-request-method "GET"))
   8437     (with-current-buffer (url-retrieve-synchronously url)
   8438       (goto-char (point-min))
   8439       (re-search-forward "\n\n" nil 'noerror)
   8440       (delete-region (point-min) (point))
   8441       (let* ((json-result (lsp-json-read-buffer)))
   8442         (message "Latest version found: %s" (lsp-get json-result :tag_name))
   8443         (--> json-result
   8444              (lsp-get it :assets)
   8445              (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it)
   8446              (lsp-get it :browser_download_url))))))
   8447 
   8448 ;; unzip
   8449 
   8450 (defconst lsp-ext-pwsh-script "powershell -noprofile -noninteractive \
   8451 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'"
   8452   "Powershell script to unzip file.")
   8453 
   8454 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'"
   8455   "Unzip script to unzip file.")
   8456 
   8457 (defcustom lsp-unzip-script (lambda ()
   8458                               (cond ((executable-find "unzip") lsp-ext-unzip-script)
   8459                                     ((executable-find "powershell") lsp-ext-pwsh-script)
   8460                                     (t nil)))
   8461   "The script to unzip."
   8462   :group 'lsp-mode
   8463   :type 'string
   8464   :package-version '(lsp-mode . "8.0.0"))
   8465 
   8466 (defun lsp-unzip (zip-file dest)
   8467   "Unzip ZIP-FILE to DEST."
   8468   (unless lsp-unzip-script
   8469     (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'"))
   8470   (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest)))
   8471 
   8472 ;; gunzip
   8473 
   8474 (defconst lsp-ext-gunzip-script "gzip -d %1$s"
   8475   "Script to decompress a gzippped file with gzip.")
   8476 
   8477 (defcustom lsp-gunzip-script (lambda ()
   8478                                (cond ((executable-find "gzip") lsp-ext-gunzip-script)
   8479                                      (t nil)))
   8480   "The script to decompress a gzipped file.
   8481 Should be a format string with one argument for the file to be decompressed
   8482 in place."
   8483   :group 'lsp-mode
   8484   :type 'string
   8485   :package-version '(lsp-mode . "8.0.0"))
   8486 
   8487 (defun lsp-gunzip (gz-file)
   8488   "Decompress GZ-FILE in place."
   8489   (unless lsp-gunzip-script
   8490     (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file))
   8491   (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file)))
   8492 
   8493 ;; tar.gz decompression
   8494 
   8495 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'"
   8496   "Script to decompress a .tar.gz file.")
   8497 
   8498 (defcustom lsp-tar-script (lambda ()
   8499                             (cond ((executable-find "tar") lsp-ext-tar-script)
   8500                                   (t nil)))
   8501   "The script to decompress a .tar.gz file.
   8502 Should be a format string with one argument for the file to be decompressed
   8503 in place."
   8504   :group 'lsp-mode
   8505   :type 'string)
   8506 
   8507 (defun lsp-tar-gz-decompress (targz-file dest)
   8508   "Decompress TARGZ-FILE in DEST."
   8509   (unless lsp-tar-script
   8510     (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file))
   8511   (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest)))
   8512 
   8513 
   8514 ;; VSCode marketplace
   8515 
   8516 (defcustom lsp-vscode-ext-url
   8517   "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s"
   8518   "Vscode extension template url."
   8519   :group 'lsp-mode
   8520   :type 'string
   8521   :package-version '(lsp-mode . "8.0.0"))
   8522 
   8523 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform)
   8524   "Return the URL to vscode extension.
   8525 PUBLISHER is the extension publisher.
   8526 NAME is the name of the extension.
   8527 VERSION is the version of the extension.
   8528 TARGETPLATFORM is the targetPlatform of the extension."
   8529   (format lsp-vscode-ext-url publisher name version (or targetPlatform "")))
   8530 
   8531 
   8532 
   8533 ;; Queueing prompts
   8534 
   8535 (defvar lsp--question-queue nil
   8536   "List of questions yet to be asked by `lsp-ask-question'.")
   8537 
   8538 (defun lsp-ask-question (question options callback)
   8539   "Prompt the user to answer the QUESTION with one of the OPTIONS from the
   8540 minibuffer. Once the user selects an option, the CALLBACK function will be
   8541 called, passing the selected option to it.
   8542 
   8543 If the user is currently being shown a question, the question will be stored in
   8544 `lsp--question-queue', and will be asked once the user has answered the current
   8545 question."
   8546   (add-to-list 'lsp--question-queue `(("question" . ,question)
   8547                                       ("options" . ,options)
   8548                                       ("callback" . ,callback)) t)
   8549   (when (eq (length lsp--question-queue) 1)
   8550     (lsp--process-question-queue)))
   8551 
   8552 (defun lsp--process-question-queue ()
   8553   "Take the first question from `lsp--question-queue', process it, then process
   8554 the next question until the queue is empty."
   8555   (-let* (((&alist "question" "options" "callback") (car lsp--question-queue))
   8556           (answer (completing-read question options nil t)))
   8557     (pop lsp--question-queue)
   8558     (funcall callback answer)
   8559     (when lsp--question-queue
   8560       (lsp--process-question-queue))))
   8561 
   8562 (defun lsp--supports-buffer? (client)
   8563   (and
   8564    ;; both file and client remote or both local
   8565    (eq (---truthy? (file-remote-p (buffer-file-name)))
   8566        (---truthy? (lsp--client-remote? client)))
   8567 
   8568    ;; activation function or major-mode match.
   8569    (if-let ((activation-fn (lsp--client-activation-fn client)))
   8570        (funcall activation-fn (buffer-file-name) major-mode)
   8571      (-contains? (lsp--client-major-modes client) major-mode))
   8572 
   8573    ;; check whether it is enabled if `lsp-enabled-clients' is not null
   8574    (or (null lsp-enabled-clients)
   8575        (or (member (lsp--client-server-id client) lsp-enabled-clients)
   8576            (ignore (lsp--info "Client %s is not in lsp-enabled-clients"
   8577                               (lsp--client-server-id client)))))
   8578 
   8579    ;; check whether it is not disabled.
   8580    (not (lsp--client-disabled-p major-mode (lsp--client-server-id client)))))
   8581 
   8582 (defun lsp--filter-clients (pred)
   8583   (->> lsp-clients hash-table-values (-filter pred)))
   8584 
   8585 (defun lsp--find-clients ()
   8586   "Find clients which can handle current buffer."
   8587   (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   8588                                                             #'lsp--server-binary-present?)))
   8589     (lsp-log "Found the following clients for %s: %s"
   8590              (buffer-file-name)
   8591              (s-join ", "
   8592                      (-map (lambda (client)
   8593                              (format "(server-id %s, priority %s)"
   8594                                      (lsp--client-server-id client)
   8595                                      (lsp--client-priority client)))
   8596                            matching-clients)))
   8597     (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients))
   8598             (selected-clients (if-let ((main-client (and main-clients
   8599                                                          (--max-by (> (lsp--client-priority it)
   8600                                                                       (lsp--client-priority other))
   8601                                                                    main-clients))))
   8602                                   (cons main-client add-on-clients)
   8603                                 add-on-clients)))
   8604       (lsp-log "The following clients were selected based on priority: %s"
   8605                (s-join ", "
   8606                        (-map (lambda (client)
   8607                                (format "(server-id %s, priority %s)"
   8608                                        (lsp--client-server-id client)
   8609                                        (lsp--client-priority client)))
   8610                              selected-clients)))
   8611       selected-clients)))
   8612 
   8613 (defun lsp-workspace-remove-all-folders()
   8614   "Delete all lsp tracked folders."
   8615   (interactive)
   8616   (--each (lsp-session-folders (lsp-session))
   8617     (lsp-workspace-folders-remove it)))
   8618 
   8619 (defun lsp-register-client (client)
   8620   "Registers LSP client CLIENT."
   8621   (let ((client-id (lsp--client-server-id client)))
   8622     (puthash client-id client lsp-clients)
   8623     (setplist (intern (format "lsp-%s-after-open-hook" client-id))
   8624               `( standard-value (nil) custom-type hook
   8625                  custom-package-version (lsp-mode . "7.0.1")
   8626                  variable-documentation ,(format "Hooks to run after `%s' server is run." client-id)
   8627                  custom-requests nil)))
   8628   (when (and lsp-auto-register-remote-clients
   8629              (not (lsp--client-remote? client)))
   8630     (let ((remote-client (copy-lsp--client client)))
   8631       (setf (lsp--client-remote? remote-client) t
   8632             (lsp--client-server-id remote-client) (intern
   8633                                                    (format "%s-tramp"
   8634                                                            (lsp--client-server-id client)))
   8635             ;; disable automatic download
   8636             (lsp--client-download-server-fn remote-client) nil)
   8637       (lsp-register-client remote-client))))
   8638 
   8639 (defun lsp--create-initialization-options (_session client)
   8640   "Create initialization-options from SESSION and CLIENT.
   8641 Add workspace folders depending on server being multiroot and
   8642 session workspace folder configuration for the server."
   8643   (let* ((initialization-options-or-fn (lsp--client-initialization-options client)))
   8644     (if (functionp initialization-options-or-fn)
   8645         (funcall initialization-options-or-fn)
   8646       initialization-options-or-fn)))
   8647 
   8648 (defvar lsp-client-settings (make-hash-table :test 'equal)
   8649   "For internal use, any external users please use
   8650   `lsp-register-custom-settings' function instead")
   8651 
   8652 (defun lsp-register-custom-settings (props)
   8653   "Register PROPS.
   8654 PROPS is list of triple (path value boolean?) where PATH is the path to the
   8655 property; VALUE can be a literal value, symbol to be evaluated, or either a
   8656 function or lambda function to be called without arguments; BOOLEAN? is an
   8657 optional flag that should be non-nil for boolean settings, when it is nil the
   8658 property will be ignored if the VALUE is nil.
   8659 
   8660 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))'
   8661 \(note the double parentheses)"
   8662   (mapc
   8663    (-lambda ((path . rest))
   8664      (puthash path rest lsp-client-settings))
   8665    props))
   8666 
   8667 (defun lsp-region-text (region)
   8668   "Get the text for REGION in current buffer."
   8669   (-let (((start . end) (lsp--range-to-region region)))
   8670     (buffer-substring-no-properties start end)))
   8671 
   8672 (defun lsp-ht-set (tbl paths value)
   8673   "Set nested hash table value.
   8674 TBL - a hash table, PATHS is the path to the nested VALUE."
   8675   (pcase paths
   8676     (`(,path) (ht-set! tbl path value))
   8677     (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl)
   8678                                            (let ((temp-tbl (ht)))
   8679                                              (ht-set! tbl path temp-tbl)
   8680                                              temp-tbl))))
   8681                        (lsp-ht-set nested-tbl rst value)))))
   8682 
   8683 ;; sections
   8684 
   8685 (defalias 'defcustom-lsp 'lsp-defcustom)
   8686 
   8687 (defmacro lsp-defcustom (symbol standard doc &rest args)
   8688   "Defines `lsp-mode' server property."
   8689   (declare (doc-string 3) (debug (name body))
   8690            (indent defun))
   8691   (let ((path (plist-get args :lsp-path))
   8692         (setter (intern (concat (symbol-name symbol) "--set"))))
   8693     (cl-remf args :lsp-path)
   8694     `(progn
   8695        (lsp-register-custom-settings
   8696         (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type))))))
   8697 
   8698        (defcustom ,symbol ,standard ,doc ,@args)
   8699 
   8700        ;; Use a variable watcher instead of registering a `defcustom'
   8701        ;; setter since `hack-local-variables' is not aware of custom
   8702        ;; setters and won't invoke them.
   8703 
   8704        (defun ,setter (sym val op _where)
   8705          (when (eq op 'set)
   8706            (lsp--set-custom-property sym val ,path)))
   8707 
   8708        (add-variable-watcher ',symbol #',setter))))
   8709 
   8710 (defun lsp--set-custom-property (sym val path)
   8711   (set sym val)
   8712   (let ((section (cl-first (s-split "\\." path))))
   8713     (mapc (lambda (workspace)
   8714             (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace))
   8715                               section)
   8716               (with-lsp-workspace workspace
   8717                 (lsp--set-configuration (lsp-configuration-section section)))))
   8718           (lsp--session-workspaces (lsp-session)))))
   8719 
   8720 (defun lsp-configuration-section (section)
   8721   "Get settings for SECTION."
   8722   (let ((ret (ht-create)))
   8723     (maphash (-lambda (path (variable boolean?))
   8724                (when (s-matches? (concat (regexp-quote section) "\\..*") path)
   8725                  (let* ((symbol-value (-> variable
   8726                                           lsp-resolve-value
   8727                                           lsp-resolve-value))
   8728                         (value (if (and boolean? (not symbol-value))
   8729                                    :json-false
   8730                                  symbol-value)))
   8731                    (when (or boolean? value)
   8732                      (lsp-ht-set ret (s-split "\\." path) value)))))
   8733              lsp-client-settings)
   8734     ret))
   8735 
   8736 
   8737 (defun lsp--start-connection (session client project-root)
   8738   "Initiates connection created from CLIENT for PROJECT-ROOT.
   8739 SESSION is the active session."
   8740   (when (lsp--client-multi-root client)
   8741     (cl-pushnew project-root (gethash (lsp--client-server-id client)
   8742                                       (lsp-session-server-id->folders session))))
   8743   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)
   8744 
   8745   (unwind-protect
   8746       (lsp--start-workspace session client project-root (lsp--create-initialization-options session client))
   8747     (lsp--spinner-stop)))
   8748 
   8749 ;; lsp-log-io-mode
   8750 
   8751 (defvar lsp-log-io-mode-map
   8752   (let ((map (make-sparse-keymap)))
   8753     (define-key map (kbd "M-n") #'lsp-log-io-next)
   8754     (define-key map (kbd "M-p") #'lsp-log-io-prev)
   8755     (define-key map (kbd "k") #'lsp--erase-log-buffer)
   8756     (define-key map (kbd "K") #'lsp--erase-session-log-buffers)
   8757     map)
   8758   "Keymap for lsp log buffer mode.")
   8759 
   8760 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo"
   8761   "Special mode for viewing IO logs.")
   8762 
   8763 (defun lsp-workspace-show-log (workspace)
   8764   "Display the log buffer of WORKSPACE."
   8765   (interactive
   8766    (list (if lsp-log-io
   8767              (if (eq (length (lsp-workspaces)) 1)
   8768                  (cl-first (lsp-workspaces))
   8769                (lsp--completing-read "Workspace: " (lsp-workspaces)
   8770                                      #'lsp--workspace-print nil t))
   8771            (user-error "IO logging is disabled"))))
   8772   (pop-to-buffer (lsp--get-log-buffer-create workspace)))
   8773 
   8774 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log)
   8775 
   8776 (defun lsp--get-log-buffer-create (workspace)
   8777   "Return the lsp log buffer of WORKSPACE, creating a new one if needed."
   8778   (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8779          (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id)))
   8780     (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid))))
   8781 
   8782 (defun lsp--erase-log-buffer (&optional all)
   8783   "Delete contents of current lsp log buffer.
   8784 When ALL is t, erase all log buffers of the running session."
   8785   (interactive)
   8786   (let* ((workspaces (lsp--session-workspaces (lsp-session)))
   8787          (current-log-buffer (current-buffer)))
   8788     (dolist (w workspaces)
   8789       (let ((b (lsp--get-log-buffer-create w)))
   8790         (when (or all (eq b current-log-buffer))
   8791           (with-current-buffer b
   8792             (let ((inhibit-read-only t))
   8793               (erase-buffer))))))))
   8794 
   8795 (defun lsp--erase-session-log-buffers ()
   8796   "Erase log buffers of the running session."
   8797   (interactive)
   8798   (lsp--erase-log-buffer t))
   8799 
   8800 (defun lsp-log-io-next (arg)
   8801   "Move to next log entry."
   8802   (interactive "P")
   8803   (ewoc-goto-next lsp--log-io-ewoc (or arg 1)))
   8804 
   8805 (defun lsp-log-io-prev (arg)
   8806   "Move to previous log entry."
   8807   (interactive "P")
   8808   (ewoc-goto-prev lsp--log-io-ewoc (or arg 1)))
   8809 
   8810 
   8811 
   8812 (cl-defmethod lsp-process-id ((process process))
   8813   (process-id process))
   8814 
   8815 (cl-defmethod lsp-process-name ((process process)) (process-name process))
   8816 
   8817 (cl-defmethod lsp-process-status ((process process)) (process-status process))
   8818 
   8819 (cl-defmethod lsp-process-kill ((process process))
   8820   (when (process-live-p process)
   8821     (kill-process process)))
   8822 
   8823 (cl-defmethod lsp-process-send ((process process) message)
   8824   (condition-case err
   8825       (process-send-string process (lsp--make-message message))
   8826     (error (lsp--error "Sending to process failed with the following error: %s"
   8827                        (error-message-string err)))))
   8828 
   8829 (cl-defmethod lsp-process-cleanup (process)
   8830   ;; Kill standard error buffer only if the process exited normally.
   8831   ;; Leave it intact otherwise for debugging purposes.
   8832   (let ((buffer (-> process process-name get-buffer)))
   8833     (when (and (eq (process-status process) 'exit)
   8834                (zerop (process-exit-status process))
   8835                (buffer-live-p buffer))
   8836       (kill-buffer buffer))))
   8837 
   8838 
   8839 ;; native JSONRPC
   8840 
   8841 (declare-function json-rpc "ext:json")
   8842 (declare-function json-rpc-connection "ext:json")
   8843 (declare-function json-rpc-send "ext:json")
   8844 (declare-function json-rpc-shutdown "ext:json")
   8845 (declare-function json-rpc-stderr "ext:json")
   8846 (declare-function json-rpc-pid "ext:json")
   8847 
   8848 (defvar lsp-json-rpc-thread nil)
   8849 (defvar lsp-json-rpc-queue nil)
   8850 (defvar lsp-json-rpc-done nil)
   8851 (defvar lsp-json-rpc-mutex (make-mutex))
   8852 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex))
   8853 
   8854 (defun lsp-json-rpc-process-queue ()
   8855   (while (not lsp-json-rpc-done)
   8856     (while lsp-json-rpc-queue
   8857       (-let (((proc . message) (pop lsp-json-rpc-queue)))
   8858         (json-rpc-send
   8859          proc message
   8860          :null-object nil
   8861          :false-object :json-false)))
   8862     (with-mutex lsp-json-rpc-mutex
   8863       (condition-wait lsp-json-rpc-condition))))
   8864 
   8865 (cl-defmethod lsp-process-id (process) (json-rpc-pid process))
   8866 
   8867 (cl-defmethod lsp-process-name (_process) "TBD")
   8868 
   8869 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process))
   8870 
   8871 (cl-defmethod lsp-process-send (proc message)
   8872   (unless lsp-json-rpc-thread
   8873     (with-current-buffer (get-buffer-create " *json-rpc*")
   8874       (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*"))))
   8875 
   8876   (with-mutex lsp-json-rpc-mutex
   8877     (setq lsp-json-rpc-queue (append lsp-json-rpc-queue
   8878                                      (list (cons proc message))))
   8879     (condition-notify lsp-json-rpc-condition)))
   8880 
   8881 (cl-defmethod lsp-process-cleanup (_proc))
   8882 
   8883 (defun lsp-json-rpc-connection (workspace command)
   8884   (let ((con (apply #'json-rpc-connection command))
   8885         (object-type (if lsp-use-plists 'plist 'hash-table)))
   8886     (with-current-buffer (get-buffer-create " *json-rpc*")
   8887       (make-thread
   8888        (lambda ()
   8889          (json-rpc
   8890           con
   8891           (lambda (result err done)
   8892             (run-with-timer
   8893              0.0
   8894              nil
   8895              (lambda ()
   8896                (cond
   8897                 (result (lsp--parser-on-message result workspace))
   8898                 (err (warn "Json parsing failed with the following error: %s" err))
   8899                 (done (lsp--handle-process-exit workspace ""))))))
   8900           :object-type object-type
   8901           :null-object nil
   8902           :false-object nil))
   8903        "*json-rpc-connection*"))
   8904     (cons con con)))
   8905 
   8906 (defun lsp-json-rpc-stderr ()
   8907   (interactive)
   8908   (--when-let (pcase (lsp-workspaces)
   8909                 (`nil (user-error "There are no active servers in the current buffer"))
   8910                 (`(,workspace) workspace)
   8911                 (workspaces (lsp--completing-read "Select server: "
   8912                                                   workspaces
   8913                                                   'lsp--workspace-print nil t)))
   8914     (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it)))
   8915           (buffer (format "*stderr-%s*" (lsp--workspace-print it)) ))
   8916       (with-current-buffer (get-buffer-create buffer)
   8917         (with-help-window buffer
   8918           (insert content))))))
   8919 
   8920 
   8921 (defun lsp--workspace-print (workspace)
   8922   "Visual representation WORKSPACE."
   8923   (let* ((proc (lsp--workspace-cmd-proc workspace))
   8924          (status (lsp--workspace-status workspace))
   8925          (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8926          (pid (lsp-process-id proc)))
   8927 
   8928     (if (eq 'initialized status)
   8929         (format "%s:%s" server-id pid)
   8930       (format "%s:%s/%s" server-id pid status))))
   8931 
   8932 (defun lsp--map-tree-widget (m)
   8933   "Build `tree-widget' from a hash-table or plist M."
   8934   (when (lsp-structure-p m)
   8935     (let (nodes)
   8936       (lsp-map (lambda (k v)
   8937                  (push `(tree-widget
   8938                          :tag ,(if (lsp-structure-p v)
   8939                                    (format "%s:" k)
   8940                                  (format "%s: %s" k
   8941                                          (propertize (format "%s" v)
   8942                                                      'face
   8943                                                      'font-lock-string-face)))
   8944                          :open t
   8945                          ,@(lsp--map-tree-widget v))
   8946                        nodes))
   8947                m)
   8948       nodes)))
   8949 
   8950 (defun lsp-buffer-name (buffer-id)
   8951   (if-let ((buffer-name (plist-get buffer-id :buffer-name)))
   8952       (funcall buffer-name buffer-id)
   8953     (buffer-name buffer-id)))
   8954 
   8955 (defun lsp--render-workspace (workspace)
   8956   "Tree node representation of WORKSPACE."
   8957   `(tree-widget :tag ,(lsp--workspace-print workspace)
   8958                 :open t
   8959                 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face)
   8960                              :open t
   8961                              ,@(->> workspace
   8962                                     (lsp--workspace-buffers)
   8963                                     (--map `(tree-widget
   8964                                              :tag ,(when (lsp-buffer-live-p it)
   8965                                                      (let ((buffer-name (lsp-buffer-name it)))
   8966                                                        (if (lsp-with-current-buffer it buffer-read-only)
   8967                                                            (propertize buffer-name 'face 'font-lock-constant-face)
   8968                                                          buffer-name)))))))
   8969                 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face)
   8970                              ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget))))
   8971 
   8972 (define-derived-mode lsp-browser-mode special-mode "LspBrowser"
   8973   "Define mode for displaying lsp sessions."
   8974   (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t)))))
   8975 
   8976 (defun lsp-describe-session ()
   8977   "Describes current `lsp-session'."
   8978   (interactive)
   8979   (let ((session (lsp-session))
   8980         (buf (get-buffer-create "*lsp session*"))
   8981         (root (lsp-workspace-root)))
   8982     (with-current-buffer buf
   8983       (lsp-browser-mode)
   8984       (let ((inhibit-read-only t))
   8985         (erase-buffer)
   8986         (--each (lsp-session-folders session)
   8987           (widget-create
   8988            `(tree-widget
   8989              :tag ,(propertize it 'face 'font-lock-keyword-face)
   8990              :open t
   8991              ,@(->> session
   8992                     (lsp-session-folder->servers)
   8993                     (gethash it)
   8994                     (-map 'lsp--render-workspace)))))))
   8995     (pop-to-buffer buf)
   8996     (goto-char (point-min))
   8997     (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag)
   8998              until (or (and root (string= tag root)) (eobp))
   8999              do (goto-char (next-overlay-change (point))))))
   9000 
   9001 (defun lsp--session-workspaces (session)
   9002   "Get all workspaces that are part of the SESSION."
   9003   (-> session lsp-session-folder->servers hash-table-values -flatten -uniq))
   9004 
   9005 (defun lsp--find-multiroot-workspace (session client project-root)
   9006   "Look for a multiroot connection in SESSION created from CLIENT for
   9007 PROJECT-ROOT and BUFFER-MAJOR-MODE."
   9008   (when (lsp--client-multi-root client)
   9009     (-when-let (multi-root-workspace (->> session
   9010                                           (lsp--session-workspaces)
   9011                                           (--first (eq (-> it lsp--workspace-client lsp--client-server-id)
   9012                                                        (lsp--client-server-id client)))))
   9013       (with-lsp-workspace multi-root-workspace
   9014         (lsp-notify "workspace/didChangeWorkspaceFolders"
   9015                     (lsp-make-did-change-workspace-folders-params
   9016                      :event (lsp-make-workspace-folders-change-event
   9017                              :added (vector (lsp-make-workspace-folder
   9018                                              :uri (lsp--path-to-uri project-root)
   9019                                              :name (f-filename project-root)))
   9020                              :removed []))))
   9021 
   9022       (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace))
   9023       (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root))
   9024 
   9025       (lsp--persist-session session)
   9026 
   9027       (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace))
   9028       (lsp--open-in-workspace multi-root-workspace)
   9029 
   9030       multi-root-workspace)))
   9031 
   9032 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder)
   9033   "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT.
   9034 IGNORE-MULTI-FOLDER to ignore multi folder server."
   9035   (-map (lambda (client)
   9036           (or
   9037            (lsp--find-workspace session client project-root)
   9038            (unless ignore-multi-folder
   9039              (lsp--find-multiroot-workspace session client project-root))
   9040            (lsp--start-connection session client project-root)))
   9041         clients))
   9042 
   9043 (defun lsp--spinner-stop ()
   9044   "Stop the spinner in case all of the workspaces are started."
   9045   (when (--all? (eq (lsp--workspace-status it) 'initialized)
   9046                 lsp--buffer-workspaces)
   9047     (spinner-stop)))
   9048 
   9049 (defun lsp--open-in-workspace (workspace)
   9050   "Open in existing WORKSPACE."
   9051   (if (eq 'initialized (lsp--workspace-status workspace))
   9052       ;; when workspace is initialized just call document did open.
   9053       (progn
   9054         (with-lsp-workspace workspace
   9055           (when-let ((before-document-open-fn (-> workspace
   9056                                                   lsp--workspace-client
   9057                                                   lsp--client-before-file-open-fn)))
   9058             (funcall before-document-open-fn workspace))
   9059           (lsp--text-document-did-open))
   9060         (lsp--spinner-stop))
   9061     ;; when it is not initialized
   9062     (lsp--spinner-start)
   9063     (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace))))
   9064 
   9065 (defun lsp--find-workspace (session client project-root)
   9066   "Find server connection created with CLIENT in SESSION for PROJECT-ROOT."
   9067   (when-let ((workspace (->> session
   9068                              (lsp-session-folder->servers)
   9069                              (gethash project-root)
   9070                              (--first (eql (-> it lsp--workspace-client lsp--client-server-id)
   9071                                            (lsp--client-server-id client))))))
   9072     (lsp--open-in-workspace workspace)
   9073     workspace))
   9074 
   9075 (defun lsp--read-char (prompt &optional options)
   9076   "Wrapper for `read-char-from-minibuffer' if Emacs +27.
   9077 Fallback to `read-key' otherwise.
   9078 PROMPT is the message and OPTIONS the available options."
   9079   (if (fboundp 'read-char-from-minibuffer)
   9080       (read-char-from-minibuffer prompt options)
   9081     (read-key prompt)))
   9082 
   9083 (defun lsp--find-root-interactively (session)
   9084   "Find project interactively.
   9085 Returns nil if the project should not be added to the current SESSION."
   9086   (condition-case nil
   9087       (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory))
   9088              (action (lsp--read-char
   9089                       (format
   9090                        "%s is not part of any project.
   9091 
   9092 %s ==> Import project root %s
   9093 %s ==> Import project by selecting root directory interactively
   9094 %s ==> Import project at current directory %s
   9095 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist
   9096 %s ==> Do not ask again for the current project by selecting ignore path interactively
   9097 %s ==> Do nothing: ask again when opening other files from the current project
   9098 
   9099 Select action: "
   9100                        (propertize (buffer-name) 'face 'bold)
   9101                        (propertize "i" 'face 'success)
   9102                        (propertize project-root-suggestion 'face 'bold)
   9103                        (propertize "I" 'face 'success)
   9104                        (propertize "." 'face 'success)
   9105                        (propertize default-directory 'face 'bold)
   9106                        (propertize "d" 'face 'warning)
   9107                        (propertize project-root-suggestion 'face 'bold)
   9108                        (propertize "D" 'face 'warning)
   9109                        (propertize "n" 'face 'warning))
   9110                       '(?i ?\r ?I ?. ?d ?D ?n))))
   9111         (cl-case action
   9112           (?i project-root-suggestion)
   9113           (?\r project-root-suggestion)
   9114           (?I (read-directory-name "Select workspace folder to add: "
   9115                                    (or project-root-suggestion default-directory)
   9116                                    nil
   9117                                    t))
   9118           (?. default-directory)
   9119           (?d (push project-root-suggestion (lsp-session-folders-blocklist session))
   9120               (lsp--persist-session session)
   9121               nil)
   9122           (?D (push (read-directory-name "Select folder to blocklist: "
   9123                                          (or project-root-suggestion default-directory)
   9124                                          nil
   9125                                          t)
   9126                     (lsp-session-folders-blocklist session))
   9127               (lsp--persist-session session)
   9128               nil)
   9129           (t nil)))
   9130     (quit)))
   9131 
   9132 (declare-function tramp-file-name-host "ext:tramp" (file) t)
   9133 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault))
   9134 
   9135 (defun lsp--files-same-host (f1 f2)
   9136   "Predicate on whether or not two files are on the same host."
   9137   (or (not (or (file-remote-p f1) (file-remote-p f2)))
   9138       (and (file-remote-p f1)
   9139            (file-remote-p f2)
   9140            (progn (require 'tramp)
   9141                   (equal (tramp-file-name-host (tramp-dissect-file-name f1))
   9142                          (tramp-file-name-host (tramp-dissect-file-name f2)))))))
   9143 
   9144 (defun lsp-find-session-folder (session file-name)
   9145   "Look in the current SESSION for folder containing FILE-NAME."
   9146   (let ((file-name-canonical (lsp-f-canonical file-name)))
   9147     (->> session
   9148          (lsp-session-folders)
   9149          (--filter (and (lsp--files-same-host it file-name-canonical)
   9150                         (or (lsp-f-same? it file-name-canonical)
   9151                             (and (f-dir? it)
   9152                                  (lsp-f-ancestor-of? it file-name-canonical)))))
   9153          (--max-by (> (length it)
   9154                       (length other))))))
   9155 
   9156 (defun lsp-find-workspace (server-id &optional file-name)
   9157   "Find workspace for SERVER-ID for FILE-NAME."
   9158   (-when-let* ((session (lsp-session))
   9159                (folder->servers (lsp-session-folder->servers session))
   9160                (workspaces (if file-name
   9161                                (gethash (lsp-find-session-folder session file-name) folder->servers)
   9162                              (lsp--session-workspaces session))))
   9163 
   9164     (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces)))
   9165 
   9166 (defun lsp--calculate-root (session file-name)
   9167   "Calculate project root for FILE-NAME in SESSION."
   9168   (and
   9169    (->> session
   9170         (lsp-session-folders-blocklist)
   9171         (--first (and (lsp--files-same-host it file-name)
   9172                       (lsp-f-ancestor-of? it file-name)
   9173                       (prog1 t
   9174                         (lsp--info "File %s is in blocklisted directory %s" file-name it))))
   9175         not)
   9176    (or
   9177     (when lsp-auto-guess-root
   9178       (lsp--suggest-project-root))
   9179     (unless lsp-guess-root-without-session
   9180       (lsp-find-session-folder session file-name))
   9181     (unless lsp-auto-guess-root
   9182       (when-let ((root-folder (lsp--find-root-interactively session)))
   9183         (if (or (not (f-equal? root-folder (expand-file-name "~/")))
   9184                 (yes-or-no-p
   9185                  (concat
   9186                   (propertize "[WARNING] " 'face 'warning)
   9187                   "You are trying to import your home folder as project root. This may cause performance issue because some language servers (python, lua, etc) will try to scan all files under project root. To avoid that you may:
   9188 
   9189 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/').
   9190 2. If your file is under `~/' then create a subfolder and move that file in this folder.
   9191 
   9192 Type `No' to go back to project selection.
   9193 Type `Yes' to confirm `HOME' as project root.
   9194 Type `C-g' to cancel project import process and stop `lsp'")))
   9195             root-folder
   9196           (lsp--calculate-root session file-name)))))))
   9197 
   9198 (defun lsp--try-open-in-library-workspace ()
   9199   "Try opening current file as library file in any of the active workspace.
   9200 The library folders are defined by each client for each of the active workspace."
   9201   (when-let ((workspace (->> (lsp-session)
   9202                              (lsp--session-workspaces)
   9203                              ;; Sort the last active workspaces first as they are more likely to be
   9204                              ;; the correct ones, especially when jumping to a definition.
   9205                              (-sort (lambda (a _b)
   9206                                       (-contains? lsp--last-active-workspaces a)))
   9207                              (--first
   9208                               (and (-> it lsp--workspace-client lsp--supports-buffer?)
   9209                                    (when-let ((library-folders-fn
   9210                                                (-> it lsp--workspace-client lsp--client-library-folders-fn)))
   9211                                      (-first (lambda (library-folder)
   9212                                                (lsp-f-ancestor-of? library-folder (buffer-file-name)))
   9213                                              (funcall library-folders-fn it))))))))
   9214     (lsp--open-in-workspace workspace)
   9215     (view-mode t)
   9216     (lsp--info "Opening read-only library file %s." (buffer-file-name))
   9217     (list workspace)))
   9218 
   9219 (defun lsp--persist-session (session)
   9220   "Persist SESSION to `lsp-session-file'."
   9221   (lsp--persist lsp-session-file (make-lsp-session
   9222                                   :folders (lsp-session-folders session)
   9223                                   :folders-blocklist (lsp-session-folders-blocklist session)
   9224                                   :server-id->folders (lsp-session-server-id->folders session))))
   9225 
   9226 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder)
   9227   "Try create opening file as a project file.
   9228 When IGNORE-MULTI-FOLDER is t the lsp mode will start new
   9229 language server even if there is language server which can handle
   9230 current language. When IGNORE-MULTI-FOLDER is nil current file
   9231 will be opened in multi folder language server if there is
   9232 such."
   9233   (-let ((session (lsp-session)))
   9234     (-if-let (clients (if ask-for-client
   9235                           (list (lsp--completing-read "Select server to start: "
   9236                                                       (ht-values lsp-clients)
   9237                                                       (-compose 'symbol-name 'lsp--client-server-id) nil t))
   9238                         (lsp--find-clients)))
   9239         (-if-let (project-root (-some-> session
   9240                                  (lsp--calculate-root (buffer-file-name))
   9241                                  (lsp-f-canonical)))
   9242             (progn
   9243               ;; update project roots if needed and persist the lsp session
   9244               (unless (-contains? (lsp-session-folders session) project-root)
   9245                 (cl-pushnew project-root (lsp-session-folders session))
   9246                 (lsp--persist-session session))
   9247               (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder))
   9248           (lsp--warn "%s not in project or it is blocklisted." (buffer-name))
   9249           nil)
   9250       (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode)
   9251       nil)))
   9252 
   9253 (defun lsp-shutdown-workspace ()
   9254   "Shutdown language server."
   9255   (interactive)
   9256   (--when-let (pcase (lsp-workspaces)
   9257                 (`nil (user-error "There are no active servers in the current buffer"))
   9258                 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?"
   9259                                                        (lsp--workspace-print workspace)))
   9260                                  workspace))
   9261                 (workspaces (lsp--completing-read "Select server: "
   9262                                                   workspaces
   9263                                                   'lsp--workspace-print nil t)))
   9264     (lsp-workspace-shutdown it)))
   9265 
   9266 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1")
   9267 
   9268 (defcustom lsp-auto-select-workspace t
   9269   "Shutdown or restart a single workspace.
   9270 If set and the current buffer has only a single workspace
   9271 associated with it, `lsp-shutdown-workspace' and
   9272 `lsp-restart-workspace' will act on it without asking."
   9273   :type 'boolean
   9274   :group 'lsp-mode)
   9275 
   9276 (defun lsp--read-workspace ()
   9277   "Ask the user to select a workspace.
   9278 Errors if there are none."
   9279   (pcase (lsp-workspaces)
   9280     (`nil (error "No workspaces associated with the current buffer"))
   9281     ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace)
   9282     (workspaces (lsp--completing-read "Select workspace: " workspaces
   9283                                       #'lsp--workspace-print nil t))))
   9284 
   9285 (defun lsp-workspace-shutdown (workspace)
   9286   "Shut the workspace WORKSPACE and the language server associated with it"
   9287   (interactive (list (lsp--read-workspace)))
   9288   (lsp--warn "Stopping %s" (lsp--workspace-print workspace))
   9289   (with-lsp-workspace workspace (lsp--shutdown-workspace)))
   9290 
   9291 (defun lsp-disconnect ()
   9292   "Disconnect the buffer from the language server."
   9293   (interactive)
   9294   (lsp--text-document-did-close t)
   9295   (lsp-managed-mode -1)
   9296   (lsp-mode -1)
   9297   (setq lsp--buffer-workspaces nil)
   9298   (lsp--info "Disconnected"))
   9299 
   9300 (defun lsp-restart-workspace ()
   9301   (interactive)
   9302   (--when-let (pcase (lsp-workspaces)
   9303                 (`nil (user-error "There are no active servers in the current buffer"))
   9304                 (`(,workspace) workspace)
   9305                 (workspaces (lsp--completing-read "Select server: "
   9306                                                   workspaces
   9307                                                   'lsp--workspace-print nil t)))
   9308     (lsp-workspace-restart it)))
   9309 
   9310 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1")
   9311 
   9312 (defun lsp-workspace-restart (workspace)
   9313   "Restart the workspace WORKSPACE and the language server associated with it"
   9314   (interactive (list (lsp--read-workspace)))
   9315   (lsp--warn "Restarting %s" (lsp--workspace-print workspace))
   9316   (with-lsp-workspace workspace (lsp--shutdown-workspace t)))
   9317 
   9318 ;;;###autoload
   9319 (defun lsp (&optional arg)
   9320   "Entry point for the server startup.
   9321 When ARG is t the lsp mode will start new language server even if
   9322 there is language server which can handle current language. When
   9323 ARG is nil current file will be opened in multi folder language
   9324 server if there is such. When `lsp' is called with prefix
   9325 argument ask the user to select which language server to start."
   9326   (interactive "P")
   9327 
   9328   (lsp--require-packages)
   9329 
   9330   (when (buffer-file-name)
   9331     (let (clients
   9332           (matching-clients (lsp--filter-clients
   9333                              (-andfn #'lsp--supports-buffer?
   9334                                      #'lsp--server-binary-present?))))
   9335       (cond
   9336        (matching-clients
   9337         (when (setq lsp--buffer-workspaces
   9338                     (or (and
   9339                          ;; Don't open as library file if file is part of a project.
   9340                          (not (lsp-find-session-folder (lsp-session) (buffer-file-name)))
   9341                          (lsp--try-open-in-library-workspace))
   9342                         (lsp--try-project-root-workspaces (equal arg '(4))
   9343                                                           (and arg (not (equal arg 1))))))
   9344           (lsp-mode 1)
   9345           (when lsp-auto-configure (lsp--auto-configure))
   9346           (setq lsp-buffer-uri (lsp--buffer-uri))
   9347           (lsp--info "Connected to %s."
   9348                      (apply 'concat (--map (format "[%s %s]"
   9349                                                    (lsp--workspace-print it)
   9350                                                    (lsp--workspace-root it))
   9351                                            lsp--buffer-workspaces)))))
   9352        ;; look for servers which are currently being downloaded.
   9353        ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9354                                                    #'lsp--client-download-in-progress?)))
   9355         (lsp--info "There are language server(%s) installation in progress.
   9356 The server(s) will be started in the buffer when it has finished."
   9357                    (-map #'lsp--client-server-id clients))
   9358         (seq-do (lambda (client)
   9359                   (cl-pushnew (current-buffer) (lsp--client-buffers client)))
   9360                 clients))
   9361        ;; look for servers to install
   9362        ((setq clients (lsp--filter-clients
   9363                        (-andfn #'lsp--supports-buffer?
   9364                                (-const lsp-enable-suggest-server-download)
   9365                                #'lsp--client-download-server-fn
   9366                                (-not #'lsp--client-download-in-progress?))))
   9367         (let ((client (lsp--completing-read
   9368                        (concat "Unable to find installed server supporting this file. "
   9369                                "The following servers could be installed automatically: ")
   9370                        clients
   9371                        (-compose #'symbol-name #'lsp--client-server-id)
   9372                        nil
   9373                        t)))
   9374           (cl-pushnew (current-buffer) (lsp--client-buffers client))
   9375           (lsp--install-server-internal client)))
   9376        ;; ignore other warnings
   9377        ((not lsp-warn-no-matched-clients)
   9378         nil)
   9379        ;; automatic installation disabled
   9380        ((setq clients (unless matching-clients
   9381                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9382                                                      #'lsp--client-download-server-fn
   9383                                                      (-not (-const lsp-enable-suggest-server-download))
   9384                                                      (-not #'lsp--server-binary-present?)))))
   9385         (lsp--warn "The following servers support current file but automatic download is disabled: %s
   9386 \(If you have already installed the server check *lsp-log*)."
   9387                    (mapconcat (lambda (client)
   9388                                 (symbol-name (lsp--client-server-id client)))
   9389                               clients
   9390                               " ")))
   9391        ;; no clients present
   9392        ((setq clients (unless matching-clients
   9393                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9394                                                      (-not #'lsp--server-binary-present?)))))
   9395         (lsp--warn "The following servers support current file but do not have automatic installation: %s
   9396 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages.
   9397 \(If you have already installed the server check *lsp-log*)."
   9398                    (mapconcat (lambda (client)
   9399                                 (symbol-name (lsp--client-server-id client)))
   9400                               clients
   9401                               " ")))
   9402        ;; no matches
   9403        ((-> #'lsp--supports-buffer? lsp--filter-clients not)
   9404         (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'.
   9405 This issue might be caused by:
   9406 1. The language you are trying to use does not have built-in support in `lsp-mode'. You must install the required support manually. Examples of this are `lsp-java' or `lsp-metals'.
   9407 2. The language server that you expect to run is not configured to run for major mode `%s'. You may check that by checking the `:major-modes' that are passed to `lsp-register-client'.
   9408 3. `lsp-mode' doesn't have any integration for the language behind `%s'. Refer to https://emacs-lsp.github.io/lsp-mode/page/languages and https://langserver.org/ .
   9409 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/.
   9410 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients').
   9411 You can customize `lsp-warn-no-matched-clients' to disable this message."
   9412                     major-mode major-mode major-mode))))))
   9413 
   9414 (defun lsp--buffer-visible-p ()
   9415   "Return non nil if current buffer is visible."
   9416   (or (buffer-modified-p) (get-buffer-window nil t)))
   9417 
   9418 (defun lsp--init-if-visible ()
   9419   "Run `lsp' for the current buffer if the buffer is visible.
   9420 Returns non nil if `lsp' was run for the buffer."
   9421   (when (lsp--buffer-visible-p)
   9422     (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t)
   9423     (lsp)
   9424     t))
   9425 
   9426 ;;;###autoload
   9427 (defun lsp-deferred ()
   9428   "Entry point that defers server startup until buffer is visible.
   9429 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'.
   9430 This avoids overloading the server with many files when starting Emacs."
   9431   ;; Workspace may not be initialized yet. Use a buffer local variable to
   9432   ;; remember that we deferred loading of this buffer.
   9433   (setq lsp--buffer-deferred t)
   9434   (let ((buffer (current-buffer)))
   9435     ;; Avoid false positives as desktop-mode restores buffers by deferring
   9436     ;; visibility check until the stack clears.
   9437     (run-with-idle-timer 0 nil (lambda ()
   9438                                  (when (buffer-live-p buffer)
   9439                                    (with-current-buffer buffer
   9440                                      (unless (lsp--init-if-visible)
   9441                                        (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t))))))))
   9442 
   9443 
   9444 
   9445 (defvar lsp-file-truename-cache (ht))
   9446 
   9447 (defmacro lsp-with-cached-filetrue-name (&rest body)
   9448   "Executes BODY caching the `file-truename' calls."
   9449   `(let ((old-fn (symbol-function 'file-truename)))
   9450      (unwind-protect
   9451          (progn
   9452            (fset 'file-truename
   9453                  (lambda (file-name &optional counter prev-dirs)
   9454                    (or (gethash file-name lsp-file-truename-cache)
   9455                        (puthash file-name (apply old-fn (list file-name counter prev-dirs))
   9456                                 lsp-file-truename-cache))))
   9457            ,@body)
   9458        (fset 'file-truename old-fn))))
   9459 
   9460 
   9461 (defun lsp-virtual-buffer-call (key &rest args)
   9462   (when lsp--virtual-buffer
   9463     (when-let ((fn (plist-get lsp--virtual-buffer key)))
   9464       (apply fn args))))
   9465 
   9466 (defun lsp-translate-column (column)
   9467   "Translate COLUMN taking into account virtual buffers."
   9468   (or (lsp-virtual-buffer-call :real->virtual-char column)
   9469       column))
   9470 
   9471 (defun lsp-translate-line (line)
   9472   "Translate LINE taking into account virtual buffers."
   9473   (or (lsp-virtual-buffer-call :real->virtual-line line)
   9474       line))
   9475 
   9476 
   9477 ;; lsp internal validation.
   9478 
   9479 (defmacro lsp--doctor (&rest checks)
   9480   `(-let [buf (current-buffer)]
   9481      (with-current-buffer (get-buffer-create "*lsp-performance*")
   9482        (with-help-window (current-buffer)
   9483          ,@(-map (-lambda ((msg form))
   9484                    `(insert (format "%s: %s\n" ,msg
   9485                                     (let ((res (with-current-buffer buf
   9486                                                  ,form)))
   9487                                       (cond
   9488                                        ((eq res :optional) (propertize "OPTIONAL" 'face 'warning))
   9489                                        (res (propertize "OK" 'face 'success))
   9490                                        (t (propertize "ERROR" 'face 'error)))))))
   9491                  (-partition 2 checks))))))
   9492 
   9493 (define-obsolete-function-alias 'lsp-diagnose
   9494   'lsp-doctor "lsp-mode 8.0.0")
   9495 
   9496 (defun lsp-doctor ()
   9497   "Validate performance settings."
   9498   (interactive)
   9499   (lsp--doctor
   9500    "Checking for Native JSON support" (functionp 'json-serialize)
   9501    "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max)
   9502    "Check `read-process-output-max' default has been changed from 4k"
   9503    (and (boundp 'read-process-output-max)
   9504         (> read-process-output-max 4096))
   9505    "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)"
   9506    (condition-case _err
   9507        (progn (lsp--make-message (list "a" "b"))
   9508               nil)
   9509      (error t))
   9510    "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000)
   9511    "Using `plist' for deserialized objects? (refer to https://emacs-lsp.github.io/lsp-mode/page/performance/#use-plists-for-deserialization)" (or lsp-use-plists :optional)
   9512    "Using emacs 28+ with native compilation?"
   9513    (or (and (fboundp 'native-comp-available-p)
   9514             (native-comp-available-p))
   9515        :optional)))
   9516 
   9517 (declare-function package-version-join "ext:package")
   9518 (declare-function package-desc-version "ext:package")
   9519 (declare-function package--alist "ext:package")
   9520 
   9521 (defun lsp-version ()
   9522   "Return string describing current version of `lsp-mode'."
   9523   (interactive)
   9524   (unless (featurep 'package)
   9525     (require 'package))
   9526   (let ((ver (format "lsp-mode %s, Emacs %s, %s"
   9527                      (package-version-join
   9528                       (package-desc-version
   9529                        (car (alist-get 'lsp-mode (package--alist)))))
   9530                      emacs-version
   9531                      system-type)))
   9532     (if (called-interactively-p 'interactive)
   9533         (lsp--info "%s" ver)
   9534       ver)))
   9535 
   9536 
   9537 
   9538 ;; org-mode/virtual-buffer
   9539 
   9540 (declare-function org-babel-get-src-block-info "ext:ob-core")
   9541 (declare-function org-do-remove-indentation "ext:org-macs")
   9542 (declare-function org-src-get-lang-mode "ext:org-src")
   9543 (declare-function org-element-context "ext:org-element")
   9544 
   9545 (defun lsp--virtual-buffer-update-position ()
   9546   (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range))
   9547                                      (funcall in-range))
   9548                                    lsp--virtual-buffer-connections))
   9549       (unless (equal virtual-buffer lsp--virtual-buffer)
   9550         (lsp-org))
   9551     (when lsp-managed-mode
   9552       (lsp-managed-mode -1)
   9553       (lsp-mode -1)
   9554       (setq lsp--buffer-workspaces nil)
   9555       (setq lsp--virtual-buffer nil)
   9556       (setq lsp-buffer-uri nil)
   9557 
   9558       ;; force refresh of diagnostics
   9559       (run-hooks 'lsp-after-diagnostics-hook))))
   9560 
   9561 (defun lsp-virtual-buffer-on-change (start end length)
   9562   "Adjust on change event to be executed against the proper language server."
   9563   (let ((max-point (max end
   9564                         (or (plist-get lsp--before-change-vals :end) 0)
   9565                         (+ start length))))
   9566     (when-let ((virtual-buffer (-first (lambda (vb)
   9567                                          (let ((lsp--virtual-buffer vb))
   9568                                            (and (lsp-virtual-buffer-call :in-range start)
   9569                                                 (lsp-virtual-buffer-call :in-range max-point))))
   9570                                        lsp--virtual-buffer-connections)))
   9571       (lsp-with-current-buffer virtual-buffer
   9572         (lsp-on-change start end length
   9573                        (lambda (&rest _)
   9574                          (list :range (lsp--range (list :character 0 :line 0)
   9575                                                   lsp--virtual-buffer-point-max)
   9576                                :text (lsp--buffer-content))))))))
   9577 
   9578 (defun lsp-virtual-buffer-before-change (start _end)
   9579   (when-let ((virtual-buffer (-first (lambda (vb)
   9580                                        (lsp-with-current-buffer vb
   9581                                          (lsp-virtual-buffer-call :in-range start)))
   9582                                      lsp--virtual-buffer-connections)))
   9583     (lsp-with-current-buffer virtual-buffer
   9584       (setq lsp--virtual-buffer-point-max
   9585             (lsp--point-to-position (lsp-virtual-buffer-call :last-point))))))
   9586 
   9587 (defun lsp-patch-on-change-event ()
   9588   (remove-hook 'after-change-functions #'lsp-on-change t)
   9589   (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t)
   9590   (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t))
   9591 
   9592 (defun lsp-kill-virtual-buffers ()
   9593   (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections))
   9594 
   9595 (defun lsp--move-point-in-indentation (point indentation)
   9596   (save-excursion
   9597     (goto-char point)
   9598     (if (<= point (+ (line-beginning-position) indentation))
   9599         (line-beginning-position)
   9600       point)))
   9601 
   9602 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck")
   9603 (declare-function flycheck-add-mode "ext:flycheck")
   9604 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics")
   9605 
   9606 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn)
   9607 
   9608 (defun lsp-flycheck-add-mode (mode)
   9609   "Register flycheck support for MODE."
   9610   (lsp-diagnostics-lsp-checker-if-needed)
   9611   (unless (flycheck-checker-supports-major-mode-p 'lsp mode)
   9612     (flycheck-add-mode 'lsp mode)))
   9613 
   9614 (defun lsp-progress-spinner-type ()
   9615   "Retrieve the spinner type value, if value is not a symbol of `spinner-types
   9616 defaults to `progress-bar."
   9617   (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar))
   9618 
   9619 (defun lsp-org ()
   9620   (interactive)
   9621   (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range))
   9622                                                               (funcall in-range))
   9623                                                             lsp--virtual-buffer-connections))
   9624       (unless (equal lsp--virtual-buffer virtual-buffer)
   9625         (setq lsp--buffer-workspaces workspaces)
   9626         (setq lsp--virtual-buffer virtual-buffer)
   9627         (setq lsp-buffer-uri nil)
   9628         (lsp-mode 1)
   9629         (lsp-managed-mode 1)
   9630         (lsp-patch-on-change-event))
   9631 
   9632     (save-excursion
   9633       (-let* (virtual-buffer
   9634               (wcb (lambda (f)
   9635                      (with-current-buffer (plist-get virtual-buffer :buffer)
   9636                        (-let* (((&plist :major-mode :buffer-file-name
   9637                                         :goto-buffer :workspaces) virtual-buffer)
   9638                                (lsp--virtual-buffer virtual-buffer)
   9639                                (lsp--buffer-workspaces workspaces))
   9640                          (save-excursion
   9641                            (funcall goto-buffer)
   9642                            (funcall f))))))
   9643               ((&plist :begin :end :post-blank :language) (cl-second (org-element-context)))
   9644               ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light)))
   9645 
   9646               (file-name (if file-name
   9647                              (f-expand file-name)
   9648                            (user-error "You should specify file name in the src block header.")))
   9649               (begin-marker (progn
   9650                               (goto-char begin)
   9651                               (forward-line)
   9652                               (set-marker (make-marker) (point))))
   9653               (end-marker (progn
   9654                             (goto-char end)
   9655                             (forward-line (1- (- post-blank)))
   9656                             (set-marker (make-marker) (1+ (point)))))
   9657               (buf (current-buffer))
   9658               (src-block (buffer-substring-no-properties begin-marker
   9659                                                          (1- end-marker)))
   9660               (indentation (with-temp-buffer
   9661                              (insert src-block)
   9662 
   9663                              (goto-char (point-min))
   9664                              (let ((indentation (current-indentation)))
   9665                                (plist-put lsp--virtual-buffer :indentation indentation)
   9666                                (org-do-remove-indentation)
   9667                                (goto-char (point-min))
   9668                                (- indentation (current-indentation))))))
   9669         (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t)
   9670 
   9671         (when (fboundp 'flycheck-add-mode)
   9672           (lsp-flycheck-add-mode 'org-mode))
   9673 
   9674         (setq lsp--virtual-buffer
   9675               (list
   9676                :in-range (lambda (&optional point)
   9677                            (<= begin-marker (or point (point)) (1- end-marker)))
   9678                :goto-buffer (lambda () (goto-char begin-marker))
   9679                :buffer-string
   9680                (lambda ()
   9681                  (let ((src-block (buffer-substring-no-properties
   9682                                    begin-marker
   9683                                    (1- end-marker))))
   9684                    (with-temp-buffer
   9685                      (insert src-block)
   9686 
   9687                      (goto-char (point-min))
   9688                      (while (not (eobp))
   9689                        (delete-region (point) (if (> (+ (point) indentation) (line-end-position))
   9690                                                   (line-end-position)
   9691                                                 (+ (point) indentation)))
   9692                        (forward-line))
   9693                      (buffer-substring-no-properties (point-min)
   9694                                                      (point-max)))))
   9695                :buffer buf
   9696                :begin begin-marker
   9697                :end end-marker
   9698                :indentation indentation
   9699                :last-point (lambda () (1- end-marker))
   9700                :cur-position (lambda ()
   9701                                (lsp-save-restriction-and-excursion
   9702                                  (list :line (- (lsp--cur-line)
   9703                                                 (lsp--cur-line begin-marker))
   9704                                        :character (let ((character (- (point)
   9705                                                                       (line-beginning-position)
   9706                                                                       indentation)))
   9707                                                     (if (< character 0)
   9708                                                         0
   9709                                                       character)))))
   9710                :line/character->point (-lambda (line character)
   9711                                         (-let [inhibit-field-text-motion t]
   9712                                           (+ indentation
   9713                                              (lsp-save-restriction-and-excursion
   9714                                                (goto-char begin-marker)
   9715                                                (forward-line line)
   9716                                                (-let [line-end (line-end-position)]
   9717                                                  (if (> character (- line-end (point)))
   9718                                                      line-end
   9719                                                    (forward-char character)
   9720                                                    (point)))))))
   9721                :major-mode (org-src-get-lang-mode language)
   9722                :buffer-file-name file-name
   9723                :buffer-uri (lsp--path-to-uri file-name)
   9724                :with-current-buffer wcb
   9725                :buffer-live? (lambda (_) (buffer-live-p buf))
   9726                :buffer-name (lambda (_)
   9727                               (propertize (format "%s(%s:%s)%s"
   9728                                                   (buffer-name buf)
   9729                                                   begin-marker
   9730                                                   end-marker
   9731                                                   language)
   9732                                           'face 'italic))
   9733                :real->virtual-line (lambda (line)
   9734                                      (+ line (line-number-at-pos begin-marker) -1))
   9735                :real->virtual-char (lambda (char) (+ char indentation))
   9736                :cleanup (lambda ()
   9737                           (set-marker begin-marker nil)
   9738                           (set-marker end-marker nil))))
   9739         (setf virtual-buffer lsp--virtual-buffer)
   9740         (puthash file-name virtual-buffer lsp--virtual-buffer-mappings)
   9741         (push virtual-buffer lsp--virtual-buffer-connections)
   9742 
   9743         ;; TODO: tangle only connected sections
   9744         (add-hook 'after-save-hook 'org-babel-tangle nil t)
   9745         (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t)
   9746         (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t)
   9747 
   9748         (setq lsp--buffer-workspaces
   9749               (lsp-with-current-buffer virtual-buffer
   9750                 (lsp)
   9751                 (plist-put virtual-buffer :workspaces (lsp-workspaces))
   9752                 (lsp-workspaces)))))))
   9753 
   9754 (defun lsp-virtual-buffer-disconnect (virtual-buffer)
   9755   (interactive (list (or
   9756                       lsp--virtual-buffer
   9757                       (when lsp--virtual-buffer-connections
   9758                         (lsp--completing-read "Select virtual buffer to disconnect: "
   9759                                               lsp--virtual-buffer-connections
   9760                                               (-lambda ((&plist :buffer-file-name))
   9761                                                 buffer-file-name))))))
   9762   (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer)
   9763       (progn
   9764         (lsp-with-current-buffer virtual-buffer
   9765           (lsp--text-document-did-close))
   9766         (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections))
   9767         (when (eq virtual-buffer lsp--virtual-buffer)
   9768           (setf lsp--virtual-buffer nil))
   9769         (when cleanup (funcall cleanup))
   9770         (remhash file-name lsp--virtual-buffer-mappings)
   9771 
   9772         (lsp--virtual-buffer-update-position)
   9773         (lsp--info "Disconnected from buffer %s" file-name))
   9774     (lsp--error "Nothing to disconnect from?")))
   9775 
   9776 
   9777 ;; inlay hints
   9778 
   9779 (defface lsp-inlay-hint-face
   9780   '((t :inherit font-lock-comment-face))
   9781   "The face to use for the JavaScript inlays."
   9782   :group 'lsp-mode
   9783   :package-version '(lsp-mode . "9.0.0"))
   9784 
   9785 (defface lsp-inlay-hint-type-face
   9786   '((t :inherit lsp-inlay-hint-face))
   9787   "Face for inlay type hints (e.g. inferred variable types)."
   9788   :group 'lsp-mode
   9789   :package-version '(lsp-mode . "9.0.0"))
   9790 
   9791 (defcustom lsp-inlay-hint-type-format "%s"
   9792   "Format string for variable inlays (part of the inlay face)."
   9793   :type '(string :tag "String")
   9794   :group 'lsp-mode
   9795   :package-version '(lsp-mode . "9.0.0"))
   9796 
   9797 (defface lsp-inlay-hint-parameter-face
   9798   '((t :inherit lsp-inlay-hint-face))
   9799   "Face for inlay parameter hints (e.g. function parameter names at
   9800 call-site)."
   9801   :group 'lsp-mode
   9802   :package-version '(lsp-mode . "9.0.0"))
   9803 
   9804 (defcustom lsp-inlay-hint-param-format "%s"
   9805   "Format string for parameter inlays (part of the inlay face)."
   9806   :type '(string :tag "String")
   9807   :group 'lsp-mode
   9808   :package-version '(lsp-mode . "9.0.0"))
   9809 
   9810 (defcustom lsp-update-inlay-hints-on-scroll t
   9811   "If non-nil update inlay hints immediately when scrolling or
   9812 modifying window sizes."
   9813   :type 'boolean
   9814   :package-version '(lsp-mode . "9.0.0"))
   9815 
   9816 (defun lsp--format-inlay (text kind)
   9817   (cond
   9818    ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text))
   9819    ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text))
   9820    (t text)))
   9821 
   9822 (defun lsp--face-for-inlay (kind)
   9823   (cond
   9824    ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face)
   9825    ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face)
   9826    (t 'lsp-inlay-hint-face)))
   9827 
   9828 (defun lsp--update-inlay-hints-scroll-function (window start)
   9829   (lsp-update-inlay-hints start (window-end window t)))
   9830 
   9831 (defun lsp--update-inlay-hints ()
   9832   (lsp-update-inlay-hints (window-start) (window-end nil t)))
   9833 
   9834 (defun lsp--label-from-inlay-hints-response (label)
   9835   "Returns a string label built from an array of
   9836 InlayHintLabelParts or the argument itself if it's already a
   9837 string."
   9838   (cl-typecase label
   9839     (string label)
   9840     (vector
   9841      (string-join (mapcar (lambda (part)
   9842                             (-let (((&InlayHintLabelPart :value) part))
   9843                               value))
   9844                           label)))))
   9845 
   9846 (defun lsp-update-inlay-hints (start end)
   9847   (lsp-request-async
   9848    "textDocument/inlayHint"
   9849    (lsp-make-inlay-hints-params
   9850     :text-document (lsp--text-document-identifier)
   9851     :range (lsp-make-range :start
   9852                            (lsp-point-to-position start)
   9853                            :end
   9854                            (lsp-point-to-position end)))
   9855    (lambda (res)
   9856      (lsp--remove-overlays 'lsp-inlay-hint)
   9857      (dolist (hint res)
   9858        (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint)
   9859                (kind (or kind? lsp/inlay-hint-kind-type-hint))
   9860                (label (lsp--label-from-inlay-hints-response label))
   9861                (pos (lsp--position-to-point position))
   9862                (overlay (make-overlay pos pos nil 'front-advance 'end-advance)))
   9863          (when (stringp label)
   9864            (overlay-put overlay 'lsp-inlay-hint t)
   9865            (overlay-put overlay 'before-string
   9866                         (format "%s%s%s"
   9867                                 (if padding-left? " " "")
   9868                                 (propertize (lsp--format-inlay label kind)
   9869                                             'font-lock-face (lsp--face-for-inlay kind))
   9870                                 (if padding-right? " " "")))))))
   9871    :mode 'tick))
   9872 
   9873 (define-minor-mode lsp-inlay-hints-mode
   9874   "Mode for displaying inlay hints."
   9875   :lighter nil
   9876   (cond
   9877    ((and lsp-inlay-hints-mode lsp--buffer-workspaces)
   9878     (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t)
   9879     (when lsp-update-inlay-hints-on-scroll
   9880       (add-to-list (make-local-variable 'window-scroll-functions)
   9881                    #'lsp--update-inlay-hints-scroll-function)))
   9882    (t
   9883     (lsp--remove-overlays 'lsp-inlay-hint)
   9884     (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t)
   9885     (setf window-scroll-functions
   9886           (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions)))))
   9887 
   9888 
   9889 
   9890 ;;;###autoload
   9891 (defun lsp-start-plain ()
   9892   "Start `lsp-mode' using minimal configuration using the latest `melpa' version
   9893 of the packages.
   9894 
   9895 In case the major-mode that you are using for "
   9896   (interactive)
   9897   (let ((start-plain (make-temp-file "plain" nil ".el")))
   9898     (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el"
   9899                    start-plain t)
   9900     (async-shell-command
   9901      (format "%s -q -l %s %s"
   9902              (expand-file-name invocation-name invocation-directory)
   9903              start-plain
   9904              (or (buffer-file-name) ""))
   9905      (generate-new-buffer " *lsp-start-plain*"))))
   9906 
   9907 
   9908 
   9909 (provide 'lsp-mode)
   9910 ;;; lsp-mode.el ends here