config

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

lsp-mode.el (436096B)


      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 ;; Package-Version: 20241113.743
      9 ;; Package-Revision: c41769e32c8d
     10 
     11 ;; URL: https://github.com/emacs-lsp/lsp-mode
     12 ;; This program is free software; you can redistribute it and/or modify
     13 ;; it under the terms of the GNU General Public License as published by
     14 ;; the Free Software Foundation, either version 3 of the License, or
     15 ;; (at your option) any later version.
     16 
     17 ;; This program is distributed in the hope that it will be useful,
     18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     20 ;; GNU General Public License for more details.
     21 
     22 ;; You should have received a copy of the GNU General Public License
     23 ;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
     24 
     25 ;;; Commentary:
     26 
     27 ;; Emacs client/library for the Language Server Protocol
     28 
     29 ;;; Code:
     30 
     31 (require 'cl-generic)
     32 (require 'cl-lib)
     33 (require 'compile)
     34 (require 'dash)
     35 (require 'epg)
     36 (require 'ewoc)
     37 (require 'f)
     38 (require 'filenotify)
     39 (require 'files)
     40 (require 'ht)
     41 (require 'imenu)
     42 (require 'inline)
     43 (require 'json)
     44 (require 'lv)
     45 (require 'markdown-mode)
     46 (require 'network-stream)
     47 (require 'pcase)
     48 (require 'rx)
     49 (require 's)
     50 (require 'seq)
     51 (require 'spinner)
     52 (require 'subr-x)
     53 (require 'tree-widget)
     54 (require 'url-parse)
     55 (require 'url-util)
     56 (require 'widget)
     57 (require 'xref)
     58 (require 'minibuffer)
     59 (require 'help-mode)
     60 (require 'lsp-protocol)
     61 
     62 (defgroup lsp-mode nil
     63   "Language Server Protocol client."
     64   :group 'tools
     65   :tag "Language Server (lsp-mode)")
     66 
     67 (declare-function evil-set-command-property "ext:evil-common")
     68 (declare-function projectile-project-root "ext:projectile")
     69 (declare-function yas-expand-snippet "ext:yasnippet")
     70 (declare-function dap-mode "ext:dap-mode")
     71 (declare-function dap-auto-configure-mode "ext:dap-mode")
     72 
     73 (defvar yas-inhibit-overlay-modification-protection)
     74 (defvar yas-indent-line)
     75 (defvar yas-wrap-around-region)
     76 (defvar yas-also-auto-indent-first-line)
     77 (defvar dap-auto-configure-mode)
     78 (defvar dap-ui-menu-items)
     79 (defvar company-minimum-prefix-length)
     80 
     81 (defconst lsp--message-type-face
     82   `((1 . ,compilation-error-face)
     83     (2 . ,compilation-warning-face)
     84     (3 . ,compilation-message-face)
     85     (4 . ,compilation-info-face)))
     86 
     87 (defconst lsp--errors
     88   '((-32700 "Parse Error")
     89     (-32600 "Invalid Request")
     90     (-32601 "Method not Found")
     91     (-32602 "Invalid Parameters")
     92     (-32603 "Internal Error")
     93     (-32099 "Server Start Error")
     94     (-32000 "Server End Error")
     95     (-32002 "Server Not Initialized")
     96     (-32001 "Unknown Error Code")
     97     (-32800 "Request Cancelled"))
     98   "Alist of error codes to user friendly strings.")
     99 
    100 (defconst lsp--empty-ht (make-hash-table))
    101 
    102 (eval-and-compile
    103   (defun dash-expand:&lsp-wks (key source)
    104     `(,(intern-soft (format "lsp--workspace-%s" (eval key))) ,source))
    105 
    106   (defun dash-expand:&lsp-cln (key source)
    107     `(,(intern-soft (format "lsp--client-%s" (eval key))) ,source)))
    108 
    109 (define-obsolete-variable-alias 'lsp-print-io 'lsp-log-io "lsp-mode 6.1")
    110 
    111 (defcustom lsp-log-io nil
    112   "If non-nil, log all messages from the language server to a *lsp-log* buffer."
    113   :group 'lsp-mode
    114   :type 'boolean)
    115 
    116 (defcustom lsp-log-io-allowlist-methods '()
    117   "The methods to filter before print to lsp-log-io."
    118   :group 'lsp-mode
    119   :type '(repeat string)
    120   :package-version '(lsp-mode . "9.0.0"))
    121 
    122 (defcustom lsp-log-max message-log-max
    123   "Maximum number of lines to keep in the log buffer.
    124 If nil, disable message logging.  If t, log messages but don’t truncate
    125 the buffer when it becomes large."
    126   :group 'lsp-mode
    127   :type '(choice (const :tag "Disable" nil)
    128                  (integer :tag "lines")
    129                  (const :tag "Unlimited" t))
    130   :package-version '(lsp-mode . "6.1"))
    131 
    132 (defcustom lsp-io-messages-max t
    133   "Maximum number of messages that can be locked in a `lsp-io' buffer."
    134   :group 'lsp-mode
    135   :type '(choice (const :tag "Unlimited" t)
    136                  (integer :tag "Messages"))
    137   :package-version '(lsp-mode . "6.1"))
    138 
    139 (defcustom lsp-keep-workspace-alive t
    140   "If non nil keep workspace alive when the last workspace buffer is closed."
    141   :group 'lsp-mode
    142   :type 'boolean)
    143 
    144 (defcustom lsp-enable-snippet t
    145   "Enable/disable snippet completion support."
    146   :group 'lsp-completion
    147   :type 'boolean)
    148 
    149 (defcustom lsp-enable-folding t
    150   "Enable/disable code folding support."
    151   :group 'lsp-mode
    152   :type 'boolean
    153   :package-version '(lsp-mode . "6.1"))
    154 
    155 (define-obsolete-variable-alias 'lsp-enable-semantic-highlighting 'lsp-semantic-tokens-enable "lsp-mode 8.0.0")
    156 
    157 (defcustom lsp-semantic-tokens-enable nil
    158   "Enable/disable support for semantic tokens.
    159 As defined by the Language Server Protocol 3.16."
    160   :group 'lsp-semantic-tokens
    161   :type 'boolean)
    162 
    163 (defcustom lsp-folding-range-limit nil
    164   "The maximum number of folding ranges to receive from the language server."
    165   :group 'lsp-mode
    166   :type '(choice (const :tag "No limit." nil)
    167                  (integer :tag "Number of lines."))
    168   :package-version '(lsp-mode . "6.1"))
    169 
    170 (defcustom lsp-folding-line-folding-only nil
    171   "If non-nil, only fold complete lines."
    172   :group 'lsp-mode
    173   :type 'boolean
    174   :package-version '(lsp-mode . "6.1"))
    175 
    176 (defcustom lsp-client-packages
    177   '( ccls lsp-actionscript lsp-ada lsp-angular lsp-ansible lsp-asm lsp-astro
    178      lsp-autotools lsp-awk lsp-bash lsp-beancount lsp-bufls lsp-clangd
    179      lsp-clojure lsp-cmake lsp-cobol lsp-credo lsp-crystal lsp-csharp lsp-css
    180      lsp-cucumber lsp-cypher lsp-d lsp-dart lsp-dhall lsp-docker lsp-dockerfile
    181      lsp-earthly lsp-elixir lsp-elm lsp-emmet lsp-erlang lsp-eslint lsp-fortran lsp-futhark
    182      lsp-fsharp lsp-gdscript lsp-gleam lsp-glsl lsp-go lsp-golangci-lint lsp-grammarly
    183      lsp-graphql lsp-groovy lsp-hack lsp-haskell lsp-haxe lsp-idris lsp-java
    184      lsp-javascript lsp-jq lsp-json lsp-kotlin lsp-latex lsp-lisp lsp-ltex
    185      lsp-lua lsp-fennel lsp-magik lsp-markdown lsp-marksman lsp-mdx lsp-meson lsp-metals lsp-mint
    186      lsp-mojo lsp-move lsp-mssql lsp-nextflow lsp-nginx lsp-nim lsp-nix lsp-nushell lsp-ocaml
    187      lsp-openscad lsp-pascal lsp-perl lsp-perlnavigator lsp-php lsp-pls
    188      lsp-purescript lsp-pwsh lsp-pyls lsp-pylsp lsp-pyright lsp-python-ms
    189      lsp-qml lsp-r lsp-racket lsp-remark lsp-rf lsp-roslyn lsp-rubocop lsp-ruby-lsp
    190      lsp-ruby-syntax-tree lsp-ruff lsp-rust lsp-semgrep lsp-shader
    191      lsp-solargraph lsp-solidity lsp-sonarlint lsp-sorbet lsp-sourcekit
    192      lsp-sql lsp-sqls lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform
    193      lsp-tex lsp-tilt lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-typespec lsp-v
    194      lsp-vala lsp-verilog lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl
    195      lsp-xml lsp-yaml lsp-yang lsp-zig)
    196   "List of the clients to be automatically required."
    197   :group 'lsp-mode
    198   :type '(repeat symbol))
    199 
    200 (defcustom lsp-progress-via-spinner t
    201   "If non-nil, display LSP $/progress reports via a spinner in the modeline."
    202   :group 'lsp-mode
    203   :type 'boolean)
    204 
    205 (defcustom lsp-progress-spinner-type 'progress-bar
    206   "Holds the type of spinner to be used in the mode-line.
    207 Takes a value accepted by `spinner-start'."
    208   :group 'lsp-mode
    209   :type `(choice :tag "Choose a spinner by name"
    210                  ,@(mapcar (lambda (c) (list 'const (car c)))
    211                            spinner-types)))
    212 
    213 (defvar-local lsp-use-workspace-root-for-server-default-directory nil
    214   "Use `lsp-workspace-root' for `default-directory' when starting LSP process.")
    215 
    216 (defvar-local lsp--cur-workspace nil)
    217 
    218 (defvar-local lsp--cur-version 0)
    219 (defvar-local lsp--virtual-buffer-connections nil)
    220 (defvar-local lsp--virtual-buffer nil)
    221 (defvar lsp--virtual-buffer-mappings (ht))
    222 
    223 (defvar lsp--uri-file-prefix (pcase system-type
    224                                (`windows-nt "file:///")
    225                                (_ "file://"))
    226   "Prefix for a file-uri.")
    227 
    228 (defvar-local lsp-buffer-uri nil
    229   "If set, return it instead of calculating it using `buffer-file-name'.")
    230 
    231 (define-error 'lsp-error "Unknown lsp-mode error")
    232 (define-error 'lsp-empty-response-error
    233   "Empty response from the language server" 'lsp-error)
    234 (define-error 'lsp-timed-out-error
    235   "Timed out while waiting for a response from the language server" 'lsp-error)
    236 (define-error 'lsp-capability-not-supported
    237   "Capability not supported by the language server" 'lsp-error)
    238 (define-error 'lsp-file-scheme-not-supported
    239   "Unsupported file scheme" 'lsp-error)
    240 (define-error 'lsp-client-already-exists-error
    241   "A client with this server-id already exists" 'lsp-error)
    242 (define-error 'lsp-no-code-actions
    243   "No code actions" 'lsp-error)
    244 
    245 (defcustom lsp-auto-guess-root nil
    246   "Automatically guess the project root using projectile/project.
    247 Do *not* use this setting unless you are familiar with `lsp-mode'
    248 internals and you are sure that all of your projects are
    249 following `projectile'/`project.el' conventions."
    250   :group 'lsp-mode
    251   :type 'boolean)
    252 
    253 (defcustom lsp-guess-root-without-session nil
    254   "Ignore the session file when calculating the project root.
    255 You almost always want to set lsp-auto-guess-root too.
    256 Do *not* use this setting unless you are familiar with `lsp-mode'
    257 internals and you are sure that all of your projects are
    258 following `projectile'/`project.el' conventions."
    259   :group 'lsp-mode
    260   :type 'boolean)
    261 
    262 (defcustom lsp-restart 'interactive
    263   "Defines how server-exited events must be handled."
    264   :group 'lsp-mode
    265   :type '(choice (const interactive)
    266                  (const auto-restart)
    267                  (const ignore)))
    268 
    269 (defcustom lsp-session-file (expand-file-name (locate-user-emacs-file ".lsp-session-v1"))
    270   "File where session information is stored."
    271   :group 'lsp-mode
    272   :type 'file)
    273 
    274 (defcustom lsp-auto-configure t
    275   "Auto configure `lsp-mode' main features.
    276 When set to t `lsp-mode' will auto-configure completion,
    277 code-actions, breadcrumb, `flycheck', `flymake', `imenu', symbol highlighting,
    278 lenses, links, and so on.
    279 
    280 For finer granularity you may use `lsp-enable-*' properties."
    281   :group 'lsp-mode
    282   :type 'boolean
    283   :package-version '(lsp-mode . "6.1"))
    284 
    285 (defcustom lsp-disabled-clients nil
    286   "A list of disabled/blocklisted clients.
    287 Each entry in the list can be either:
    288 a symbol, the server-id for the LSP client, or
    289 a cons pair (MAJOR-MODE . CLIENTS), where MAJOR-MODE is the major-mode,
    290 and CLIENTS is either a client or a list of clients.
    291 
    292 This option can also be used as a file- or directory-local variable to
    293 disable a language server for individual files or directories/projects
    294 respectively."
    295   :group 'lsp-mode
    296   :type '(repeat (symbol))
    297   :safe 'listp
    298   :package-version '(lsp-mode . "6.1"))
    299 
    300 (defvar lsp-clients (make-hash-table :test 'eql)
    301   "Hash table server-id -> client.
    302 It contains all of the clients that are currently registered.")
    303 
    304 (defvar lsp-enabled-clients nil
    305   "List of clients allowed to be used for projects.
    306 When nil, all registered clients are considered candidates.")
    307 
    308 (defvar lsp-last-id 0
    309   "Last request id.")
    310 
    311 (defcustom lsp-before-initialize-hook nil
    312   "List of functions to be called before a Language Server has been initialized
    313 for a new workspace."
    314   :type 'hook
    315   :group 'lsp-mode)
    316 
    317 (defcustom lsp-after-initialize-hook nil
    318   "List of functions to be called after a Language Server has been initialized
    319 for a new workspace."
    320   :type 'hook
    321   :group 'lsp-mode)
    322 
    323 (defcustom lsp-before-open-hook nil
    324   "List of functions to be called before a new file with LSP support is opened."
    325   :type 'hook
    326   :group 'lsp-mode)
    327 
    328 (defcustom lsp-after-open-hook nil
    329   "List of functions to be called after a new file with LSP support is opened."
    330   :type 'hook
    331   :group 'lsp-mode)
    332 
    333 (defcustom lsp-enable-file-watchers t
    334   "If non-nil lsp-mode will watch the files in the workspace if
    335 the server has requested that."
    336   :type 'boolean
    337   :group 'lsp-mode
    338   :package-version '(lsp-mode . "6.1"))
    339 ;;;###autoload(put 'lsp-enable-file-watchers 'safe-local-variable #'booleanp)
    340 
    341 (define-obsolete-variable-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "8.0.0")
    342 
    343 (defcustom lsp-file-watch-ignored-directories
    344   '(; SCM tools
    345     "[/\\\\]\\.git\\'"
    346     "[/\\\\]\\.github\\'"
    347     "[/\\\\]\\.gitlab\\'"
    348     "[/\\\\]\\.circleci\\'"
    349     "[/\\\\]\\.hg\\'"
    350     "[/\\\\]\\.bzr\\'"
    351     "[/\\\\]_darcs\\'"
    352     "[/\\\\]\\.svn\\'"
    353     "[/\\\\]_FOSSIL_\\'"
    354     ;; IDE or build tools
    355     "[/\\\\]\\.idea\\'"
    356     "[/\\\\]\\.ensime_cache\\'"
    357     "[/\\\\]\\.eunit\\'"
    358     "[/\\\\]node_modules"
    359     "[/\\\\]\\.yarn\\'"
    360     "[/\\\\]\\.fslckout\\'"
    361     "[/\\\\]\\.tox\\'"
    362     "[/\\\\]\\.nox\\'"
    363     "[/\\\\]dist\\'"
    364     "[/\\\\]dist-newstyle\\'"
    365     "[/\\\\]\\.stack-work\\'"
    366     "[/\\\\]\\.bloop\\'"
    367     "[/\\\\]\\.metals\\'"
    368     "[/\\\\]target\\'"
    369     "[/\\\\]\\.ccls-cache\\'"
    370     "[/\\\\]\\.vs\\'"
    371     "[/\\\\]\\.vscode\\'"
    372     "[/\\\\]\\.venv\\'"
    373     "[/\\\\]\\.mypy_cache\\'"
    374     "[/\\\\]\\.pytest_cache\\'"
    375     ;; Swift Package Manager
    376     "[/\\\\]\\.build\\'"
    377     ;; Python
    378     "[/\\\\]__pycache__\\'"
    379     "[/\\\\]site-packages\\'"
    380     "[/\\\\].pyenv\\'"
    381     ;; Autotools output
    382     "[/\\\\]\\.deps\\'"
    383     "[/\\\\]build-aux\\'"
    384     "[/\\\\]autom4te.cache\\'"
    385     "[/\\\\]\\.reference\\'"
    386     ;; Bazel
    387     "[/\\\\]bazel-[^/\\\\]+\\'"
    388     ;; CSharp
    389     "[/\\\\]\\.cache[/\\\\]lsp-csharp\\'"
    390     "[/\\\\]\\.meta\\'"
    391     "[/\\\\]\\.nuget\\'"
    392     ;; Unity
    393     "[/\\\\]Library\\'"
    394     ;; Clojure
    395     "[/\\\\]\\.lsp\\'"
    396     "[/\\\\]\\.clj-kondo\\'"
    397     "[/\\\\]\\.shadow-cljs\\'"
    398     "[/\\\\]\\.babel_cache\\'"
    399     "[/\\\\]\\.cpcache\\'"
    400     "[/\\\\]\\checkouts\\'"
    401     ;; Gradle
    402     "[/\\\\]\\.gradle\\'"
    403     ;; Maven
    404     "[/\\\\]\\.m2\\'"
    405     ;; .Net Core build-output
    406     "[/\\\\]bin/Debug\\'"
    407     "[/\\\\]obj\\'"
    408     ;; OCaml and Dune
    409     "[/\\\\]_opam\\'"
    410     "[/\\\\]_build\\'"
    411     ;; Elixir
    412     "[/\\\\]\\.elixir_ls\\'"
    413     ;; Elixir Credo
    414     "[/\\\\]\\.elixir-tools\\'"
    415     ;; terraform and terragrunt
    416     "[/\\\\]\\.terraform\\'"
    417     "[/\\\\]\\.terragrunt-cache\\'"
    418     ;; nix-direnv
    419     "[/\\\\]\\result"
    420     "[/\\\\]\\result-bin"
    421     "[/\\\\]\\.direnv\\'")
    422   "List of regexps matching directory paths which won't be monitored when
    423 creating file watches. Customization of this variable is only honored at
    424 the global level or at a root of an lsp workspace."
    425   :group 'lsp-mode
    426   :type '(repeat string)
    427   :package-version '(lsp-mode . "8.0.0"))
    428 
    429 (define-obsolete-function-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "7.0.1")
    430 
    431 (defun lsp-file-watch-ignored-directories ()
    432   lsp-file-watch-ignored-directories)
    433 
    434 ;; Allow lsp-file-watch-ignored-directories as a file or directory-local variable
    435 ;;;###autoload(put 'lsp-file-watch-ignored-directories 'safe-local-variable 'lsp--string-listp)
    436 
    437 (defcustom lsp-file-watch-ignored-files
    438   '(
    439     ;; Flycheck tempfiles
    440     "[/\\\\]flycheck_[^/\\\\]+\\'"
    441     ;; lockfiles
    442     "[/\\\\]\\.#[^/\\\\]+\\'"
    443     ;; backup files
    444     "[/\\\\][^/\\\\]+~\\'" )
    445   "List of regexps matching files for which change events will
    446 not be sent to the server.
    447 
    448 This setting has no impact on whether a file-watch is created for
    449 a directory; it merely prevents notifications pertaining to
    450 matched files from being sent to the server.  To prevent a
    451 file-watch from being created for a directory, customize
    452 `lsp-file-watch-ignored-directories'
    453 
    454 Customization of this variable is only honored at the global
    455 level or at a root of an lsp workspace."
    456   :group 'lsp-mode
    457   :type '(repeat string)
    458   :package-version '(lsp-mode . "8.0.0"))
    459 
    460 ;; Allow lsp-file-watch-ignored-files as a file or directory-local variable
    461 ;;;###autoload(put 'lsp-file-watch-ignored-files 'safe-local-variable 'lsp--string-listp)
    462 
    463 (defcustom lsp-after-uninitialized-functions nil
    464   "List of functions to be called after a Language Server has been uninitialized."
    465   :type 'hook
    466   :group 'lsp-mode
    467   :package-version '(lsp-mode . "6.3"))
    468 
    469 (defconst lsp--sync-full 1)
    470 (defconst lsp--sync-incremental 2)
    471 
    472 (defcustom lsp-debounce-full-sync-notifications t
    473   "If non-nil debounce full sync events.
    474 This flag affects only servers which do not support incremental updates."
    475   :type 'boolean
    476   :group 'lsp-mode
    477   :package-version '(lsp-mode . "6.1"))
    478 
    479 (defcustom lsp-debounce-full-sync-notifications-interval 1.0
    480   "Time to wait before sending full sync synchronization after buffer modification."
    481   :type 'float
    482   :group 'lsp-mode
    483   :package-version '(lsp-mode . "6.1"))
    484 
    485 (defvar lsp--stderr-index 0)
    486 
    487 (defvar lsp--delayed-requests nil)
    488 (defvar lsp--delay-timer nil)
    489 
    490 (defcustom lsp-document-sync-method nil
    491   "How to sync the document with the language server."
    492   :type '(choice (const :tag "Documents are synced by always sending the full content of the document." lsp--sync-full)
    493                  (const :tag "Documents are synced by always sending incremental changes to the document." lsp--sync-incremental)
    494                  (const :tag "Use the method recommended by the language server." nil))
    495   :group 'lsp-mode)
    496 
    497 (defcustom lsp-auto-execute-action t
    498   "Auto-execute single action."
    499   :type 'boolean
    500   :group 'lsp-mode)
    501 
    502 (defcustom lsp-enable-links t
    503   "If non-nil, all references to links in a file will be made clickable, if
    504 supported by the language server."
    505   :type 'boolean
    506   :group 'lsp-mode
    507   :package-version '(lsp-mode . "6.1"))
    508 
    509 (defcustom lsp-enable-imenu t
    510   "If non-nil, automatically enable `imenu' integration when server provides
    511 `textDocument/documentSymbol'."
    512   :type 'boolean
    513   :group 'lsp-mode
    514   :package-version '(lsp-mode . "6.2"))
    515 
    516 (defcustom lsp-enable-dap-auto-configure t
    517   "If non-nil, enable `dap-auto-configure-mode`."
    518   :type 'boolean
    519   :group 'lsp-mode
    520   :package-version '(lsp-mode . "7.0"))
    521 
    522 (defcustom lsp-eldoc-enable-hover t
    523   "If non-nil, `eldoc' will display hover info when it is present."
    524   :type 'boolean
    525   :group 'lsp-mode)
    526 
    527 (defcustom lsp-eldoc-render-all nil
    528   "Display all of the info returned by document/onHover.
    529 If this is set to nil, `eldoc' will show only the symbol information."
    530   :type 'boolean
    531   :group 'lsp-mode)
    532 
    533 (define-obsolete-variable-alias 'lsp-enable-completion-at-point
    534   'lsp-completion-enable "lsp-mode 7.0.1")
    535 
    536 (defcustom lsp-completion-enable t
    537   "Enable `completion-at-point' integration."
    538   :type 'boolean
    539   :group 'lsp-completion)
    540 
    541 (defcustom lsp-enable-symbol-highlighting t
    542   "Highlight references of the symbol at point."
    543   :type 'boolean
    544   :group 'lsp-mode)
    545 
    546 (defcustom lsp-enable-xref t
    547   "Enable xref integration."
    548   :type 'boolean
    549   :group 'lsp-mode)
    550 
    551 (defcustom lsp-references-exclude-definition nil
    552   "If non-nil, exclude declarations when finding references."
    553   :type 'boolean
    554   :group 'lsp-mode)
    555 
    556 (defcustom lsp-enable-indentation t
    557   "Indent regions using the file formatting functionality provided by the
    558 language server."
    559   :type 'boolean
    560   :group 'lsp-mode)
    561 
    562 (defcustom lsp-enable-on-type-formatting t
    563   "Enable `textDocument/onTypeFormatting' integration."
    564   :type 'boolean
    565   :group 'lsp-mode)
    566 
    567 (defcustom lsp-enable-text-document-color t
    568   "Enable `textDocument/documentColor' integration."
    569   :type 'boolean
    570   :group 'lsp-mode)
    571 
    572 (defcustom lsp-before-save-edits t
    573   "If non-nil, `lsp-mode' will apply edits suggested by the language server
    574 before saving a document."
    575   :type 'boolean
    576   :group 'lsp-mode)
    577 
    578 (defcustom lsp-after-apply-edits-hook nil
    579   "Hooks to run when text edit is applied.
    580 It contains the operation source."
    581   :type 'hook
    582   :group 'lsp-mode
    583   :package-version '(lsp-mode . "8.0.0"))
    584 
    585 (defcustom lsp-apply-edits-after-file-operations t
    586   "Whether to apply edits returned by server after file operations if any.
    587 Applicable only if server supports workspace.fileOperations for operations:
    588 `workspace/willRenameFiles', `workspace/willCreateFiles' and
    589 `workspace/willDeleteFiles'."
    590   :group 'lsp-mode
    591   :type 'boolean)
    592 
    593 (defcustom lsp-modeline-code-actions-enable t
    594   "Whether to show code actions on modeline."
    595   :type 'boolean
    596   :group 'lsp-modeline)
    597 
    598 (defcustom lsp-modeline-diagnostics-enable t
    599   "Whether to show diagnostics on modeline."
    600   :type 'boolean
    601   :group 'lsp-modeline)
    602 
    603 (defcustom lsp-modeline-workspace-status-enable t
    604   "Whether to show workspace status on modeline."
    605   :type 'boolean
    606   :group 'lsp-modeline
    607   :package-version '(lsp-mode . "8.0.0"))
    608 
    609 (defcustom lsp-headerline-breadcrumb-enable t
    610   "Whether to enable breadcrumb on headerline."
    611   :type 'boolean
    612   :group 'lsp-headerline)
    613 
    614 (defcustom lsp-configure-hook nil
    615   "Hooks to run when `lsp-configure-buffer' is called."
    616   :type 'hook
    617   :group 'lsp-mode)
    618 
    619 (defcustom lsp-unconfigure-hook nil
    620   "Hooks to run when `lsp-unconfig-buffer' is called."
    621   :type 'hook
    622   :group 'lsp-mode)
    623 
    624 (defcustom lsp-after-diagnostics-hook nil
    625   "Hooks to run after diagnostics are received.
    626 Note: it runs only if the receiving buffer is open. Use
    627 `lsp-diagnostics-updated-hook'if you want to be notified when
    628 diagnostics have changed."
    629   :type 'hook
    630   :group 'lsp-mode)
    631 
    632 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook
    633   'lsp-diagnostics-updated-hook "lsp-mode 6.4")
    634 
    635 (defcustom lsp-diagnostics-updated-hook nil
    636   "Hooks to run after diagnostics are received."
    637   :type 'hook
    638   :group 'lsp-mode)
    639 
    640 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook
    641   'lsp-workspace-folders-changed-functions "lsp-mode 6.3")
    642 
    643 (defcustom lsp-workspace-folders-changed-functions nil
    644   "Hooks to run after the folders has changed.
    645 The hook will receive two parameters list of added and removed folders."
    646   :type 'hook
    647   :group 'lsp-mode)
    648 
    649 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0")
    650 
    651 (defcustom lsp-before-apply-edits-hook nil
    652   "Hooks to run before applying edits."
    653   :type 'hook
    654   :group 'lsp-mode)
    655 
    656 (defgroup lsp-imenu nil
    657   "LSP Imenu."
    658   :group 'lsp-mode
    659   :tag "LSP Imenu")
    660 
    661 (defcustom lsp-imenu-show-container-name t
    662   "Display the symbol's container name in an imenu entry."
    663   :type 'boolean
    664   :group 'lsp-imenu)
    665 
    666 (defcustom lsp-imenu-container-name-separator "/"
    667   "Separator string to use to separate the container name from the symbol while
    668 displaying imenu entries."
    669   :type 'string
    670   :group 'lsp-imenu)
    671 
    672 (defcustom lsp-imenu-sort-methods '(kind name)
    673   "How to sort the imenu items.
    674 
    675 The value is a list of `kind' `name' or `position'.  Priorities
    676 are determined by the index of the element."
    677   :type '(repeat (choice (const name)
    678                          (const position)
    679                          (const kind)))
    680   :group 'lsp-imenu)
    681 
    682 (defcustom lsp-imenu-index-symbol-kinds nil
    683   "Which symbol kinds to show in imenu."
    684   :type '(repeat (choice (const :tag "Miscellaneous" nil)
    685                          (const :tag "File" File)
    686                          (const :tag "Module" Module)
    687                          (const :tag "Namespace" Namespace)
    688                          (const :tag "Package" Package)
    689                          (const :tag "Class" Class)
    690                          (const :tag "Method" Method)
    691                          (const :tag "Property" Property)
    692                          (const :tag "Field" Field)
    693                          (const :tag "Constructor" Constructor)
    694                          (const :tag "Enum" Enum)
    695                          (const :tag "Interface" Interface)
    696                          (const :tag "Function" Function)
    697                          (const :tag "Variable" Variable)
    698                          (const :tag "Constant" Constant)
    699                          (const :tag "String" String)
    700                          (const :tag "Number" Number)
    701                          (const :tag "Boolean" Boolean)
    702                          (const :tag "Array" Array)
    703                          (const :tag "Object" Object)
    704                          (const :tag "Key" Key)
    705                          (const :tag "Null" Null)
    706                          (const :tag "Enum Member" EnumMember)
    707                          (const :tag "Struct" Struct)
    708                          (const :tag "Event" Event)
    709                          (const :tag "Operator" Operator)
    710                          (const :tag "Type Parameter" TypeParameter)))
    711   :group 'lsp-imenu)
    712 
    713 ;; vibhavp: Should we use a lower value (5)?
    714 (defcustom lsp-response-timeout 10
    715   "Number of seconds to wait for a response from the language server before
    716 timing out. Nil if no timeout."
    717   :type '(choice
    718           (number :tag "Seconds")
    719           (const :tag "No timeout" nil))
    720   :group 'lsp-mode)
    721 
    722 (defcustom lsp-tcp-connection-timeout 2
    723   "The timeout for tcp connection in seconds."
    724   :type 'number
    725   :group 'lsp-mode
    726   :package-version '(lsp-mode . "6.2"))
    727 
    728 (defconst lsp--imenu-compare-function-alist
    729   (list (cons 'name #'lsp--imenu-compare-name)
    730         (cons 'kind #'lsp--imenu-compare-kind)
    731         (cons 'position #'lsp--imenu-compare-line-col))
    732   "An alist of (METHOD . FUNCTION).
    733 METHOD is one of the symbols accepted by
    734 `lsp-imenu-sort-methods'.
    735 
    736 FUNCTION takes two hash tables representing DocumentSymbol.  It
    737 returns a negative number, 0, or a positive number indicating
    738 whether the first parameter is less than, equal to, or greater
    739 than the second parameter.")
    740 
    741 (defcustom lsp-diagnostic-clean-after-change nil
    742   "When non-nil, clean the diagnostics on change.
    743 
    744 Note that when that setting is nil, `lsp-mode' will show stale
    745 diagnostics until server publishes the new set of diagnostics"
    746   :type 'boolean
    747   :group 'lsp-diagnostics
    748   :package-version '(lsp-mode . "7.0.1"))
    749 
    750 (defcustom lsp-server-trace nil
    751   "Request tracing on the server side.
    752 The actual trace output at each level depends on the language server in use.
    753 Changes take effect only when a new session is started."
    754   :type '(choice (const :tag "Disabled" "off")
    755                  (const :tag "Messages only" "messages")
    756                  (const :tag "Verbose" "verbose")
    757                  (const :tag "Default (disabled)" nil))
    758   :group 'lsp-mode
    759   :package-version '(lsp-mode . "6.1"))
    760 
    761 (defcustom lsp-auto-touch-files t
    762   "If non-nil ensure the files exist before sending
    763 `textDocument/didOpen' notification."
    764   :type 'boolean
    765   :group 'lsp-mode
    766   :package-version '(lsp-mode . "9.0.0"))
    767 
    768 (defvar lsp-language-id-configuration
    769   '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake")
    770     ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile")
    771     ("\\.astro$" . "astro")
    772     ("\\.cs\\'" . "csharp")
    773     ("\\.css$" . "css")
    774     ("\\.cypher$" . "cypher")
    775     ("Earthfile" . "earthfile")
    776     ("\\.ebuild$" . "shellscript")
    777     ("\\.go\\'" . "go")
    778     ("\\.html$" . "html")
    779     ("\\.hx$" . "haxe")
    780     ("\\.hy$" . "hy")
    781     ("\\.java\\'" . "java")
    782     ("\\.jq$"  . "jq")
    783     ("\\.js$" . "javascript")
    784     ("\\.json$" . "json")
    785     ("\\.jsonc$" . "jsonc")
    786     ("\\.jsonnet$" . "jsonnet")
    787     ("\\.jsx$" . "javascriptreact")
    788     ("\\.lua$" . "lua")
    789     ("\\.fnl$" . "fennel")
    790     ("\\.mdx\\'" . "mdx")
    791     ("\\.nu$" . "nushell")
    792     ("\\.php$" . "php")
    793     ("\\.ps[dm]?1\\'" . "powershell")
    794     ("\\.rs\\'" . "rust")
    795     ("\\.spec\\'" . "rpm-spec")
    796     ("\\.sql$" . "sql")
    797     ("\\.svelte$" . "svelte")
    798     ("\\.toml\\'" . "toml")
    799     ("\\.ts$" . "typescript")
    800     ("\\.tsp$" . "typespec")
    801     ("\\.tsx$" . "typescriptreact")
    802     ("\\.ttcn3$" . "ttcn3")
    803     ("\\.vue$" . "vue")
    804     ("\\.xml$" . "xml")
    805     ("\\ya?ml$" . "yaml")
    806     ("^PKGBUILD$" . "shellscript")
    807     ("^go\\.mod\\'" . "go.mod")
    808     ("^settings\\.json$" . "jsonc")
    809     ("^yang\\.settings$" . "jsonc")
    810     ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson")
    811     (ada-mode . "ada")
    812     (ada-ts-mode . "ada")
    813     (gpr-mode . "gpr")
    814     (gpr-ts-mode . "gpr")
    815     (awk-mode . "awk")
    816     (awk-ts-mode . "awk")
    817     (nxml-mode . "xml")
    818     (sql-mode . "sql")
    819     (vimrc-mode . "vim")
    820     (vimscript-ts-mode . "vim")
    821     (sh-mode . "shellscript")
    822     (bash-ts-mode . "shellscript")
    823     (ebuild-mode . "shellscript")
    824     (pkgbuild-mode . "shellscript")
    825     (envrc-file-mode . "shellscript")
    826     (scala-mode . "scala")
    827     (scala-ts-mode . "scala")
    828     (julia-mode . "julia")
    829     (julia-ts-mode . "julia")
    830     (clojure-mode . "clojure")
    831     (clojurec-mode . "clojure")
    832     (clojurescript-mode . "clojurescript")
    833     (clojure-ts-mode . "clojure")
    834     (clojure-ts-clojurec-mode . "clojure")
    835     (clojure-ts-clojurescript-mode . "clojurescript")
    836     (java-mode . "java")
    837     (java-ts-mode . "java")
    838     (jdee-mode . "java")
    839     (groovy-mode . "groovy")
    840     (nextflow-mode . "nextflow")
    841     (python-mode . "python")
    842     (python-ts-mode . "python")
    843     (cython-mode . "python")
    844     ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo")
    845     (lsp--render-markdown . "markdown")
    846     (move-mode . "move")
    847     (rust-mode . "rust")
    848     (rust-ts-mode . "rust")
    849     (rustic-mode . "rust")
    850     (kotlin-mode . "kotlin")
    851     (kotlin-ts-mode . "kotlin")
    852     (css-mode . "css")
    853     (css-ts-mode . "css")
    854     (less-mode . "less")
    855     (less-css-mode . "less")
    856     (lua-mode . "lua")
    857     (lua-ts-mode . "lua")
    858     (sass-mode . "sass")
    859     (ssass-mode . "sass")
    860     (scss-mode . "scss")
    861     (scad-mode . "openscad")
    862     (xml-mode . "xml")
    863     (c-mode . "c")
    864     (c-ts-mode . "c")
    865     (c++-mode . "cpp")
    866     (c++-ts-mode . "cpp")
    867     (cuda-mode . "cuda")
    868     (objc-mode . "objective-c")
    869     (html-mode . "html")
    870     (html-ts-mode . "html")
    871     (sgml-mode . "html")
    872     (mhtml-mode . "html")
    873     (mint-mode . "mint")
    874     (go-dot-mod-mode . "go.mod")
    875     (go-mod-ts-mode . "go.mod")
    876     (go-mode . "go")
    877     (go-ts-mode . "go")
    878     (graphql-mode . "graphql")
    879     (haskell-mode . "haskell")
    880     (haskell-ts-mode . "haskell")
    881     (hack-mode . "hack")
    882     (php-mode . "php")
    883     (php-ts-mode . "php")
    884     (powershell-mode . "powershell")
    885     (powershell-mode . "PowerShell")
    886     (powershell-ts-mode . "powershell")
    887     (json-mode . "json")
    888     (json-ts-mode . "json")
    889     (jsonc-mode . "jsonc")
    890     (rjsx-mode . "javascript")
    891     (js2-mode . "javascript")
    892     (js-mode . "javascript")
    893     (js-ts-mode . "javascript")
    894     (typescript-mode . "typescript")
    895     (typescript-ts-mode . "typescript")
    896     (typespec-mode . "typespec")
    897     (tsx-ts-mode . "typescriptreact")
    898     (svelte-mode . "svelte")
    899     (fsharp-mode . "fsharp")
    900     (reason-mode . "reason")
    901     (caml-mode . "ocaml")
    902     (tuareg-mode . "ocaml")
    903     (futhark-mode . "futhark")
    904     (swift-mode . "swift")
    905     (elixir-mode . "elixir")
    906     (elixir-ts-mode . "elixir")
    907     (heex-ts-mode . "elixir")
    908     (conf-javaprop-mode . "spring-boot-properties")
    909     (yaml-mode . "yaml")
    910     (yaml-ts-mode . "yaml")
    911     (ruby-mode . "ruby")
    912     (enh-ruby-mode . "ruby")
    913     (ruby-ts-mode . "ruby")
    914     (feature-mode . "cucumber")
    915     (fortran-mode . "fortran")
    916     (f90-mode . "fortran")
    917     (elm-mode . "elm")
    918     (dart-mode . "dart")
    919     (erlang-mode . "erlang")
    920     (dockerfile-mode . "dockerfile")
    921     (dockerfile-ts-mode . "dockerfile")
    922     (csharp-mode . "csharp")
    923     (csharp-tree-sitter-mode . "csharp")
    924     (csharp-ts-mode . "csharp")
    925     (plain-tex-mode . "plaintex")
    926     (context-mode . "context")
    927     (cypher-mode . "cypher")
    928     (latex-mode . "latex")
    929     (LaTeX-mode . "latex")
    930     (v-mode . "v")
    931     (vhdl-mode . "vhdl")
    932     (vhdl-ts-mode . "vhdl")
    933     (verilog-mode . "verilog")
    934     (terraform-mode . "terraform")
    935     (ess-julia-mode . "julia")
    936     (ess-r-mode . "r")
    937     (crystal-mode . "crystal")
    938     (nim-mode . "nim")
    939     (dhall-mode . "dhall")
    940     (cmake-mode . "cmake")
    941     (cmake-ts-mode . "cmake")
    942     (purescript-mode . "purescript")
    943     (gdscript-mode . "gdscript")
    944     (gdscript-ts-mode . "gdscript")
    945     (perl-mode . "perl")
    946     (cperl-mode . "perl")
    947     (robot-mode . "robot")
    948     (racket-mode . "racket")
    949     (nix-mode . "nix")
    950     (nix-ts-mode . "nix")
    951     (prolog-mode . "prolog")
    952     (vala-mode . "vala")
    953     (actionscript-mode . "actionscript")
    954     (d-mode . "d")
    955     (zig-mode . "zig")
    956     (zig-ts-mode . "zig")
    957     (text-mode . "plaintext")
    958     (markdown-mode . "markdown")
    959     (gfm-mode . "markdown")
    960     (beancount-mode . "beancount")
    961     (conf-toml-mode . "toml")
    962     (toml-ts-mode . "toml")
    963     (org-mode . "org")
    964     (org-journal-mode . "org")
    965     (nginx-mode . "nginx")
    966     (magik-mode . "magik")
    967     (magik-ts-mode . "magik")
    968     (idris-mode . "idris")
    969     (idris2-mode . "idris2")
    970     (gleam-mode . "gleam")
    971     (gleam-ts-mode . "gleam")
    972     (graphviz-dot-mode . "dot")
    973     (tiltfile-mode . "tiltfile")
    974     (solidity-mode . "solidity")
    975     (bibtex-mode . "bibtex")
    976     (rst-mode . "restructuredtext")
    977     (glsl-mode . "glsl")
    978     (shader-mode . "shaderlab")
    979     (wgsl-mode . "wgsl")
    980     (jq-mode . "jq")
    981     (jq-ts-mode . "jq")
    982     (protobuf-mode . "protobuf")
    983     (nushell-mode . "nushell")
    984     (nushell-ts-mode . "nushell")
    985     (meson-mode . "meson")
    986     (yang-mode . "yang"))
    987   "Language id configuration.")
    988 
    989 (defvar lsp--last-active-workspaces nil
    990   "Keep track of last active workspace.
    991 We want to try the last workspace first when jumping into a library
    992 directory")
    993 
    994 (defvar lsp-method-requirements
    995   '(("textDocument/callHierarchy" :capability :callHierarchyProvider)
    996     ("textDocument/codeAction" :capability :codeActionProvider)
    997     ("codeAction/resolve"
    998      :check-command (lambda (workspace)
    999                       (with-lsp-workspace workspace
   1000                         (lsp:code-action-options-resolve-provider?
   1001                          (lsp--capability-for-method "textDocument/codeAction")))))
   1002     ("textDocument/codeLens" :capability :codeLensProvider)
   1003     ("textDocument/completion" :capability :completionProvider)
   1004     ("completionItem/resolve"
   1005      :check-command (lambda (wk)
   1006                       (with-lsp-workspace wk
   1007                         (lsp:completion-options-resolve-provider?
   1008                          (lsp--capability-for-method "textDocument/completion")))))
   1009     ("textDocument/declaration" :capability :declarationProvider)
   1010     ("textDocument/definition" :capability :definitionProvider)
   1011     ("textDocument/documentColor" :capability :colorProvider)
   1012     ("textDocument/documentLink" :capability :documentLinkProvider)
   1013     ("textDocument/inlayHint" :capability :inlayHintProvider)
   1014     ("textDocument/documentHighlight" :capability :documentHighlightProvider)
   1015     ("textDocument/documentSymbol" :capability :documentSymbolProvider)
   1016     ("textDocument/foldingRange" :capability :foldingRangeProvider)
   1017     ("textDocument/formatting" :capability :documentFormattingProvider)
   1018     ("textDocument/hover" :capability :hoverProvider)
   1019     ("textDocument/implementation" :capability :implementationProvider)
   1020     ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider)
   1021     ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider)
   1022     ("textDocument/prepareRename"
   1023      :check-command (lambda (workspace)
   1024                       (with-lsp-workspace workspace
   1025                         (lsp:rename-options-prepare-provider?
   1026                          (lsp--capability-for-method "textDocument/rename")))))
   1027     ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider)
   1028     ("textDocument/references" :capability :referencesProvider)
   1029     ("textDocument/rename" :capability :renameProvider)
   1030     ("textDocument/selectionRange" :capability :selectionRangeProvider)
   1031     ("textDocument/semanticTokens" :capability :semanticTokensProvider)
   1032     ("textDocument/semanticTokensFull"
   1033      :check-command (lambda (workspace)
   1034                       (with-lsp-workspace workspace
   1035                         (lsp-get (lsp--capability :semanticTokensProvider) :full))))
   1036     ("textDocument/semanticTokensFull/Delta"
   1037      :check-command (lambda (workspace)
   1038                       (with-lsp-workspace workspace
   1039                         (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full)))
   1040                           (and (not (booleanp capFull)) (lsp-get capFull :delta))))))
   1041     ("textDocument/semanticTokensRangeProvider"
   1042      :check-command (lambda (workspace)
   1043                       (with-lsp-workspace workspace
   1044                         (lsp-get (lsp--capability :semanticTokensProvider) :range))))
   1045     ("textDocument/signatureHelp" :capability :signatureHelpProvider)
   1046     ("textDocument/typeDefinition" :capability :typeDefinitionProvider)
   1047     ("textDocument/typeHierarchy" :capability :typeHierarchyProvider)
   1048     ("textDocument/diagnostic" :capability :diagnosticProvider)
   1049     ("workspace/executeCommand" :capability :executeCommandProvider)
   1050     ("workspace/symbol" :capability :workspaceSymbolProvider))
   1051 
   1052   "Map methods to requirements.
   1053 It is used by request-sending functions to determine which server
   1054 must be used for handling a particular message.")
   1055 
   1056 (defconst lsp--file-change-type
   1057   `((created . 1)
   1058     (changed . 2)
   1059     (deleted . 3)))
   1060 
   1061 (defconst lsp--watch-kind
   1062   `((create . 1)
   1063     (change . 2)
   1064     (delete . 4)))
   1065 
   1066 (defvar lsp-window-body-width 40
   1067   "Window body width when rendering doc.")
   1068 
   1069 (defface lsp-face-highlight-textual
   1070   '((t :inherit highlight))
   1071   "Face used for textual occurrences of symbols."
   1072   :group 'lsp-mode)
   1073 
   1074 (defface lsp-face-highlight-read
   1075   '((t :inherit highlight :underline t))
   1076   "Face used for highlighting symbols being read."
   1077   :group 'lsp-mode)
   1078 
   1079 (defface lsp-face-highlight-write
   1080   '((t :inherit highlight :weight bold))
   1081   "Face used for highlighting symbols being written to."
   1082   :group 'lsp-mode)
   1083 
   1084 (define-obsolete-variable-alias 'lsp-lens-auto-enable
   1085   'lsp-lens-enable "lsp-mode 7.0.1")
   1086 
   1087 (defcustom lsp-lens-enable t
   1088   "Auto enable lenses if server supports."
   1089   :group 'lsp-lens
   1090   :type 'boolean
   1091   :package-version '(lsp-mode . "6.3"))
   1092 
   1093 (defcustom lsp-symbol-highlighting-skip-current nil
   1094   "If non-nil skip current symbol when setting symbol highlights."
   1095   :group 'lsp-mode
   1096   :type 'boolean)
   1097 
   1098 (defcustom lsp-file-watch-threshold 1000
   1099   "Show warning if the files to watch are more than.
   1100 Set to nil to disable the warning."
   1101   :type 'number
   1102   :group 'lsp-mode)
   1103 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i))))
   1104 
   1105 (defvar lsp-custom-markup-modes
   1106   '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic"))
   1107   "Mode to uses with markdown code blocks.
   1108 They are added to `markdown-code-lang-modes'")
   1109 
   1110 (defcustom lsp-signature-render-documentation t
   1111   "Display signature documentation in `eldoc'."
   1112   :type 'boolean
   1113   :group 'lsp-mode
   1114   :package-version '(lsp-mode . "6.2"))
   1115 
   1116 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request)
   1117   "Auto activate signature conditions."
   1118   :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char)
   1119                          (const :tag "After selected completion." :after-completion)
   1120                          (const :tag "When the server has sent show signature help." :on-server-request)))
   1121   :group 'lsp-mode
   1122   :package-version '(lsp-mode . "6.2"))
   1123 
   1124 (defcustom lsp-signature-doc-lines 20
   1125   "If number, limit the number of lines to show in the docs."
   1126   :type 'number
   1127   :group 'lsp-mode
   1128   :package-version '(lsp-mode . "6.3"))
   1129 
   1130 (defcustom lsp-signature-function 'lsp-lv-message
   1131   "The function used for displaying signature info.
   1132 It will be called with one param - the signature info. When
   1133 called with nil the signature info must be cleared."
   1134   :type 'function
   1135   :group 'lsp-mode
   1136   :package-version '(lsp-mode . "6.3"))
   1137 
   1138 (defcustom lsp-keymap-prefix "s-l"
   1139   "LSP-mode keymap prefix."
   1140   :group 'lsp-mode
   1141   :type 'string
   1142   :package-version '(lsp-mode . "6.3"))
   1143 
   1144 (defvar-local lsp--buffer-workspaces ()
   1145   "List of the buffer workspaces.")
   1146 
   1147 (defvar-local lsp--buffer-deferred nil
   1148   "Whether buffer was loaded via `lsp-deferred'.")
   1149 
   1150 (defvar lsp--session nil
   1151   "Contain the `lsp-session' for the current Emacs instance.")
   1152 
   1153 (defvar lsp--tcp-port 10000)
   1154 
   1155 (defvar lsp--client-packages-required nil
   1156   "If nil, `lsp-client-packages' are yet to be required.")
   1157 
   1158 (defvar lsp--tcp-server-port 0
   1159   "The server socket which is opened when using `lsp-tcp-server' (a server
   1160 socket is opened in Emacs and the language server connects to it).  The
   1161 default value of 0 ensures that a random high port is used. Set it to a positive
   1162 integer to use a specific port.")
   1163 
   1164 (defvar lsp--tcp-server-wait-seconds 10
   1165   "Wait this amount of time for the client to connect to our server socket
   1166 when using `lsp-tcp-server'.")
   1167 
   1168 (defvar-local lsp--document-symbols nil
   1169   "The latest document symbols.")
   1170 
   1171 (defvar-local lsp--document-selection-range-cache nil
   1172   "The document selection cache.")
   1173 
   1174 (defvar-local lsp--document-symbols-request-async nil
   1175   "If non-nil, request document symbols asynchronously.")
   1176 
   1177 (defvar-local lsp--document-symbols-tick -1
   1178   "The value of `buffer-chars-modified-tick' when document
   1179   symbols were last retrieved.")
   1180 
   1181 (defvar-local lsp--have-document-highlights nil
   1182   "Set to `t' on symbol highlighting, cleared on
   1183 `lsp--cleanup-highlights-if-needed'. Checking a separately
   1184 defined flag is substantially faster than unconditionally
   1185 calling `remove-overlays'.")
   1186 
   1187 ;; Buffer local variable for storing number of lines.
   1188 (defvar lsp--log-lines)
   1189 
   1190 (defvar-local lsp--eldoc-saved-message nil)
   1191 
   1192 (defvar lsp--on-change-timer nil)
   1193 (defvar lsp--on-idle-timer nil)
   1194 
   1195 (defvar-local lsp--signature-last nil)
   1196 (defvar-local lsp--signature-last-index nil)
   1197 (defvar lsp--signature-last-buffer nil)
   1198 
   1199 (defvar-local lsp--virtual-buffer-point-max nil)
   1200 
   1201 (cl-defmethod lsp-execute-command (_server _command _arguments)
   1202   "Ask SERVER to execute COMMAND with ARGUMENTS.")
   1203 
   1204 (defun lsp-elt (sequence n)
   1205   "Return Nth element of SEQUENCE or nil if N is out of range."
   1206   (cond
   1207    ((listp sequence) (elt sequence n))
   1208    ((arrayp sequence)
   1209     (and (> (length sequence) n) (aref sequence n)))
   1210    (t (and (> (length sequence) n) (elt sequence n)))))
   1211 
   1212 ;; define seq-first and seq-rest for older emacs
   1213 (defun lsp-seq-first (sequence)
   1214   "Return the first element of SEQUENCE."
   1215   (lsp-elt sequence 0))
   1216 
   1217 (defun lsp-seq-rest (sequence)
   1218   "Return a sequence of the elements of SEQUENCE except the first one."
   1219   (seq-drop sequence 1))
   1220 
   1221 ;;;###autoload
   1222 (defun lsp--string-listp (sequence)
   1223   "Return t if all elements of SEQUENCE are strings, else nil."
   1224   (not (seq-find (lambda (x) (not (stringp x))) sequence)))
   1225 
   1226 (defun lsp--string-vector-p (candidate)
   1227   "Returns true if CANDIDATE is a vector data structure and
   1228 every element of it is of type string, else nil."
   1229   (and
   1230    (vectorp candidate)
   1231    (seq-every-p #'stringp candidate)))
   1232 
   1233 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0")
   1234 
   1235 (defun lsp--editable-vector-match (widget value)
   1236   "Function for `lsp-editable-vector' :match."
   1237   ;; Value must be a list or a vector and all the members must match the type.
   1238   (and (or (listp value) (vectorp value))
   1239        (length (cdr (lsp--editable-vector-match-inline widget value)))))
   1240 
   1241 (defun lsp--editable-vector-match-inline (widget value)
   1242   "Value for `lsp-editable-vector' :match-inline."
   1243   (let ((type (nth 0 (widget-get widget :args)))
   1244         (ok t)
   1245         found)
   1246     (while (and value ok)
   1247       (let ((answer (widget-match-inline type value)))
   1248         (if answer
   1249             (let ((head (if (vectorp answer) (aref answer 0) (car answer)))
   1250                   (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer))))
   1251               (setq found (append found head)
   1252                     value tail))
   1253           (setq ok nil))))
   1254     (cons found value)))
   1255 
   1256 (defun lsp--editable-vector-value-to-external (_widget internal-value)
   1257   "Convert the internal list value to a vector."
   1258   (if (listp internal-value)
   1259       (apply 'vector internal-value)
   1260     internal-value))
   1261 
   1262 (defun lsp--editable-vector-value-to-internal (_widget external-value)
   1263   "Convert the external vector value to a list."
   1264   (if (vectorp external-value)
   1265       (append external-value nil)
   1266     external-value))
   1267 
   1268 (define-widget 'lsp--editable-vector 'editable-list
   1269   "A subclass of `editable-list' that accepts and returns a
   1270 vector instead of a list."
   1271   :value-to-external 'lsp--editable-vector-value-to-external
   1272   :value-to-internal 'lsp--editable-vector-value-to-internal
   1273   :match 'lsp--editable-vector-match
   1274   :match-inline 'lsp--editable-vector-match-inline)
   1275 
   1276 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector
   1277   "A variable length homogeneous vector."
   1278   :tag "Repeat"
   1279   :format "%{%t%}:\n%v%i\n")
   1280 
   1281 (define-widget 'lsp-string-vector 'lazy
   1282   "A vector of zero or more elements, every element of which is a string.
   1283 Appropriate for any language-specific `defcustom' that needs to
   1284 serialize as a JSON array of strings.
   1285 
   1286 Deprecated. Use `lsp-repeatable-vector' instead. "
   1287   :offset 4
   1288   :tag "Vector"
   1289   :type '(lsp-repeatable-vector string))
   1290 
   1291 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0")
   1292 
   1293 (defvar lsp--show-message t
   1294   "If non-nil, show debug message from `lsp-mode'.")
   1295 
   1296 (defun lsp--message  (format &rest args)
   1297   "Wrapper for `message'
   1298 
   1299 We `inhibit-message' the message when the cursor is in the
   1300 minibuffer and when emacs version is before emacs 27 due to the
   1301 fact that we often use `lsp--info', `lsp--warn' and `lsp--error'
   1302 in async context and the call to these function is removing the
   1303 minibuffer prompt. The issue with async messages is already fixed
   1304 in emacs 27.
   1305 
   1306 See #2049"
   1307   (when lsp--show-message
   1308     (let ((inhibit-message (or inhibit-message
   1309                                (and (minibufferp)
   1310                                     (version< emacs-version "27.0")))))
   1311       (apply #'message format args))))
   1312 
   1313 (defun lsp--info (format &rest args)
   1314   "Display lsp info message with FORMAT with ARGS."
   1315   (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args)))
   1316 
   1317 (defun lsp--warn (format &rest args)
   1318   "Display lsp warn message with FORMAT with ARGS."
   1319   (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args)))
   1320 
   1321 (defun lsp--error (format &rest args)
   1322   "Display lsp error message with FORMAT with ARGS."
   1323   (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args)))
   1324 
   1325 (defun lsp-log (format &rest args)
   1326   "Log message to the ’*lsp-log*’ buffer.
   1327 
   1328 FORMAT and ARGS i the same as for `message'."
   1329   (when lsp-log-max
   1330     (let ((log-buffer (get-buffer "*lsp-log*"))
   1331           (inhibit-read-only t))
   1332       (unless log-buffer
   1333         (setq log-buffer (get-buffer-create "*lsp-log*"))
   1334         (with-current-buffer log-buffer
   1335           (buffer-disable-undo)
   1336           (view-mode 1)
   1337           (set (make-local-variable 'lsp--log-lines) 0)))
   1338       (with-current-buffer log-buffer
   1339         (save-excursion
   1340           (let* ((message (apply 'format format args))
   1341                  ;; Count newlines in message.
   1342                  (newlines (1+ (cl-loop with start = 0
   1343                                         for count from 0
   1344                                         while (string-match "\n" message start)
   1345                                         do (setq start (match-end 0))
   1346                                         finally return count))))
   1347             (goto-char (point-max))
   1348 
   1349             ;; in case the buffer is not empty insert before last \n to preserve
   1350             ;; the point position(in case it is in the end)
   1351             (if (eq (point) (point-min))
   1352                 (progn
   1353                   (insert "\n")
   1354                   (backward-char))
   1355               (backward-char)
   1356               (insert "\n"))
   1357             (insert message)
   1358 
   1359             (setq lsp--log-lines (+ lsp--log-lines newlines))
   1360 
   1361             (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max))
   1362               (let ((to-delete (- lsp--log-lines lsp-log-max)))
   1363                 (goto-char (point-min))
   1364                 (forward-line to-delete)
   1365                 (delete-region (point-min) (point))
   1366                 (setq lsp--log-lines lsp-log-max)))))))))
   1367 
   1368 (defalias 'lsp-message 'lsp-log)
   1369 
   1370 (defalias 'lsp-ht 'ht)
   1371 
   1372 (defalias 'lsp-file-local-name 'file-local-name)
   1373 
   1374 (defun lsp-f-canonical (file-name)
   1375   "Return the canonical FILE-NAME, without a trailing slash."
   1376   (directory-file-name (expand-file-name file-name)))
   1377 
   1378 (defalias 'lsp-canonical-file-name 'lsp-f-canonical)
   1379 
   1380 (defun lsp-f-same? (path-a path-b)
   1381   "Return t if PATH-A and PATH-B are references to the same file.
   1382 Symlinks are not followed."
   1383   (when (and (f-exists? path-a)
   1384              (f-exists? path-b))
   1385     (equal
   1386      (lsp-f-canonical (directory-file-name (f-expand path-a)))
   1387      (lsp-f-canonical (directory-file-name (f-expand path-b))))))
   1388 
   1389 (defun lsp-f-parent (path)
   1390   "Return the parent directory to PATH.
   1391 Symlinks are not followed."
   1392   (let ((parent (file-name-directory
   1393                  (directory-file-name (f-expand path default-directory)))))
   1394     (unless (lsp-f-same? path parent)
   1395       (if (f-relative? path)
   1396           (f-relative parent)
   1397         (directory-file-name parent)))))
   1398 
   1399 (defun lsp-f-ancestor-of? (path-a path-b)
   1400   "Return t if PATH-A is an ancestor of PATH-B.
   1401 Symlinks are not followed."
   1402   (unless (lsp-f-same? path-a path-b)
   1403     (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator))
   1404                (lsp-f-canonical path-b))))
   1405 
   1406 (defun lsp--merge-results (results method)
   1407   "Merge RESULTS by filtering the empty hash-tables and merging
   1408 the lists according to METHOD."
   1409   (pcase (--map (if (vectorp it)
   1410                     (append it nil) it)
   1411                 (-filter #'identity results))
   1412     (`() ())
   1413     ;; only one result - simply return it
   1414     (`(,fst) fst)
   1415     ;; multiple results merge it based on strategy
   1416     (results
   1417      (pcase method
   1418        ("textDocument/hover" (pcase (seq-filter
   1419                                      (-compose #'not #'lsp-empty?)
   1420                                      results)
   1421                                (`(,hover) hover)
   1422                                (hovers (lsp-make-hover
   1423                                         :contents
   1424                                         (-mapcat
   1425                                          (-lambda ((&Hover :contents))
   1426                                            (if (and (sequencep contents)
   1427                                                     (not (stringp contents)))
   1428                                                (append contents ())
   1429                                              (list contents)))
   1430                                          hovers)))))
   1431        ("textDocument/completion"
   1432         (lsp-make-completion-list
   1433          :is-incomplete (seq-some
   1434                          #'lsp:completion-list-is-incomplete
   1435                          results)
   1436          :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it)
   1437                                                     (lsp:completion-list-items it)
   1438                                                   it)
   1439                                                 nil))
   1440                            results)))
   1441        ("completionItem/resolve"
   1442         (let ((item (cl-first results)))
   1443           (when-let ((details (seq-filter #'identity
   1444                                           (seq-map #'lsp:completion-item-detail? results))))
   1445             (lsp:set-completion-item-detail?
   1446              item
   1447              (string-join details " ")))
   1448           (when-let ((docs (seq-filter #'identity
   1449                                        (seq-map #'lsp:completion-item-documentation? results))))
   1450             (lsp:set-completion-item-documentation?
   1451              item
   1452              (lsp-make-markup-content
   1453               :kind (or (seq-some (lambda (it)
   1454                                     (when (equal (lsp:markup-content-kind it)
   1455                                                  lsp/markup-kind-markdown)
   1456                                       lsp/markup-kind-markdown))
   1457                                   docs)
   1458                         lsp/markup-kind-plain-text)
   1459               :value (string-join (seq-map (lambda (doc)
   1460                                              (or (lsp:markup-content-value doc)
   1461                                                  (and (stringp doc) doc)))
   1462                                            docs)
   1463                                   "\n"))))
   1464           (when-let ((edits (seq-filter #'identity
   1465                                         (seq-map #'lsp:completion-item-additional-text-edits? results))))
   1466             (lsp:set-completion-item-additional-text-edits?
   1467              item
   1468              (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits)))
   1469           item))
   1470        (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results))))))
   1471 
   1472 (defun lsp--spinner-start ()
   1473   "Start spinner indication."
   1474   (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error)))
   1475 
   1476 (defun lsp--propertize (str type)
   1477   "Propertize STR as per TYPE."
   1478   (propertize str 'face (alist-get type lsp--message-type-face)))
   1479 
   1480 (defun lsp-workspaces ()
   1481   "Return the lsp workspaces associated with the current project."
   1482   (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces))
   1483 
   1484 (defun lsp--completing-read (prompt collection transform-fn &optional predicate
   1485                                     require-match initial-input
   1486                                     hist def inherit-input-method)
   1487   "Wrap `completing-read' to provide transformation function and disable sort.
   1488 
   1489 TRANSFORM-FN will be used to transform each of the items before displaying.
   1490 
   1491 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF
   1492 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes."
   1493   (let* ((col (--map (cons (funcall transform-fn it) it) collection))
   1494          (completion (completing-read prompt
   1495                                       (lambda (string pred action)
   1496                                         (if (eq action 'metadata)
   1497                                             `(metadata (display-sort-function . identity))
   1498                                           (complete-with-action action col string pred)))
   1499                                       predicate require-match initial-input hist
   1500                                       def inherit-input-method)))
   1501     (cdr (assoc completion col))))
   1502 
   1503 (defconst lsp--system-arch (lambda ()
   1504                              (setq lsp--system-arch
   1505                                    (pcase system-type
   1506                                      ('windows-nt
   1507                                       (pcase system-configuration
   1508                                         ((rx bol "x86_64-") 'x64)
   1509                                         (_ 'x86)))
   1510                                      ('darwin
   1511                                       (pcase system-configuration
   1512                                         ((rx "aarch64-") 'arm64)
   1513                                         (_ 'x64)))
   1514                                      ('gnu/linux
   1515                                        (pcase system-configuration
   1516                                          ((rx bol "aarch64-") 'arm64)
   1517                                          ((rx bol "x86_64") 'x64)
   1518                                          ((rx bol (| "i386" "i886")) 'x32)))
   1519                                      (_
   1520                                       (pcase system-configuration
   1521                                         ((rx bol "x86_64") 'x64)
   1522                                         ((rx bol (| "i386" "i886")) 'x32))))))
   1523   "Return the system architecture of `Emacs'.
   1524 Special values:
   1525   `x64'       64bit
   1526   `x32'       32bit
   1527   `arm64'     ARM 64bit")
   1528 
   1529 (defmacro lsp-with-current-buffer (buffer-id &rest body)
   1530   (declare (indent 1) (debug t))
   1531   `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer)))
   1532        (with-lsp-workspaces (plist-get ,buffer-id :workspaces)
   1533          (funcall wcb (lambda () ,@body)))
   1534      (with-current-buffer ,buffer-id
   1535        ,@body)))
   1536 
   1537 (defvar lsp--throw-on-input nil
   1538   "Make `lsp-*-while-no-input' throws `input' on interrupted.")
   1539 
   1540 (defmacro lsp--catch (tag bodyform &rest handlers)
   1541   "Catch TAG thrown in BODYFORM.
   1542 The return value from TAG will be handled in HANDLERS by `pcase'."
   1543   (declare (debug (form form &rest (pcase-PAT body))) (indent 2))
   1544   (let ((re-sym (make-symbol "re")))
   1545     `(let ((,re-sym (catch ,tag ,bodyform)))
   1546        (pcase ,re-sym
   1547          ,@handlers))))
   1548 
   1549 (defmacro lsp--while-no-input (&rest body)
   1550   "Wrap BODY in `while-no-input' and respecting `non-essential'.
   1551 If `lsp--throw-on-input' is set, will throw if input is pending, else
   1552 return value of `body' or nil if interrupted."
   1553   (declare (debug t) (indent 0))
   1554   `(if non-essential
   1555        (let ((res (while-no-input ,@body)))
   1556          (cond
   1557           ((and lsp--throw-on-input (equal res t))
   1558            (throw 'input :interrupted))
   1559           ((booleanp res) nil)
   1560           (t res)))
   1561      ,@body))
   1562 
   1563 ;; A ‘lsp--client’ object describes the client-side behavior of a language
   1564 ;; server.  It is used to start individual server processes, each of which is
   1565 ;; represented by a ‘lsp--workspace’ object.  Client objects are normally
   1566 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’.  Each
   1567 ;; workspace refers to exactly one client, but there can be multiple workspaces
   1568 ;; for a single client.
   1569 (cl-defstruct lsp--client
   1570   ;; ‘language-id’ is a function that receives a buffer as a single argument
   1571   ;; and should return the language identifier for that buffer.  See
   1572   ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem
   1573   ;; for a list of language identifiers.  Also consult the documentation for
   1574   ;; the language server represented by this client to find out what language
   1575   ;; identifiers it supports or expects.
   1576   (language-id nil)
   1577 
   1578   ;; ‘add-on?’ when set to t the server will be started no matter whether there
   1579   ;; is another server handling the same mode.
   1580   (add-on? nil)
   1581   ;; ‘new-connection’ is a function that should start a language server process
   1582   ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS).
   1583   ;; COMMAND-PROCESS must be a process object representing the server process
   1584   ;; just started.  COMMUNICATION-PROCESS must be a process (including pipe and
   1585   ;; network processes) that ‘lsp-mode’ uses to communicate with the language
   1586   ;; server using the language server protocol.  COMMAND-PROCESS and
   1587   ;; COMMUNICATION-PROCESS may be the same process; in that case
   1588   ;; ‘new-connection’ may also return that process as a single
   1589   ;; object. ‘new-connection’ is called with two arguments, FILTER and
   1590   ;; SENTINEL.  FILTER should be used as process filter for
   1591   ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for
   1592   ;; COMMAND-PROCESS.
   1593   (new-connection nil)
   1594 
   1595   ;; ‘ignore-regexps’ is a list of regexps.  When a data packet from the
   1596   ;; language server matches any of these regexps, it will be ignored.  This is
   1597   ;; intended for dealing with language servers that output non-protocol data.
   1598   (ignore-regexps nil)
   1599 
   1600   ;; ‘ignore-messages’ is a list of regexps.  When a message from the language
   1601   ;; server matches any of these regexps, it will be ignored.  This is useful
   1602   ;; for filtering out unwanted messages; such as servers that send nonstandard
   1603   ;; message types, or extraneous log messages.
   1604   (ignore-messages nil)
   1605 
   1606   ;; ‘notification-handlers’ is a hash table mapping notification method names
   1607   ;; (strings) to functions handling the respective notifications.  Upon
   1608   ;; receiving a notification, ‘lsp-mode’ will call the associated handler
   1609   ;; function passing two arguments, the ‘lsp--workspace’ object and the
   1610   ;; deserialized notification parameters.
   1611   (notification-handlers (make-hash-table :test 'equal))
   1612 
   1613   ;; ‘request-handlers’ is a hash table mapping request method names
   1614   ;; (strings) to functions handling the respective notifications.  Upon
   1615   ;; receiving a request, ‘lsp-mode’ will call the associated handler function
   1616   ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized
   1617   ;; request parameters.
   1618   (request-handlers (make-hash-table :test 'equal))
   1619 
   1620   ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request
   1621   ;; identifiers for pending asynchronous requests to functions handling the
   1622   ;; respective responses.  Upon receiving a response from the language server,
   1623   ;; ‘lsp-mode’ will call the associated response handler function with a
   1624   ;; single argument, the deserialized response parameters.
   1625   (response-handlers (make-hash-table :test 'eql))
   1626 
   1627   ;; ‘prefix-function’ is called for getting the prefix for completion.
   1628   ;; The function takes no parameter and returns a cons (start . end) representing
   1629   ;; the start and end bounds of the prefix. If it's not set, the client uses a
   1630   ;; default prefix function."
   1631   (prefix-function nil)
   1632 
   1633   ;; Contains mapping of scheme to the function that is going to be used to load
   1634   ;; the file.
   1635   (uri-handlers (make-hash-table :test #'equal))
   1636 
   1637   ;; ‘action-handlers’ is a hash table mapping action to a handler function. It
   1638   ;; can be used in `lsp-execute-code-action' to determine whether the action
   1639   ;; current client is interested in executing the action instead of sending it
   1640   ;; to the server.
   1641   (action-handlers (make-hash-table :test 'equal))
   1642 
   1643   ;; `action-filter' can be set to a function that modifies any incoming
   1644   ;; `CodeAction' in place before it is executed. The return value is ignored.
   1645   ;; This can be used to patch up broken code action requests before they are
   1646   ;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an
   1647   ;; example of a function that can be useful here.
   1648   (action-filter nil)
   1649 
   1650   ;; major modes supported by the client.
   1651   major-modes
   1652   ;; Function that will be called to decide if this language client
   1653   ;; should manage a particular buffer. The function will be passed
   1654   ;; the file name and major mode to inform the decision. Setting
   1655   ;; `activation-fn' will override `major-modes', if
   1656   ;; present.
   1657   activation-fn
   1658   ;; Break the tie when major-mode is supported by multiple clients.
   1659   (priority 0)
   1660   ;; Unique identifier for representing the client object.
   1661   server-id
   1662   ;; defines whether the client supports multi root workspaces.
   1663   multi-root
   1664   ;; Initialization options or a function that returns initialization options.
   1665   initialization-options
   1666   ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or
   1667   ;; completely replace, the faces used for semantic highlighting on a
   1668   ;; client-by-client basis.
   1669   ;;
   1670   ;; It recognizes four members, all of which are optional: `:types’ and
   1671   ;; `:modifiers’, respectively, should be face definition lists akin to
   1672   ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be
   1673   ;; merged with the default face definition list.
   1674   ;;
   1675   ;; Alternatively, if the plist members `:discard-default-types’ or
   1676   ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers'
   1677   ;; face definitions will be replaced entirely by their respective overrides.
   1678   ;;
   1679   ;; For example, setting `:semantic-tokens-faces-overrides' to
   1680   ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from
   1681   ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'.
   1682   ;;
   1683   ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))'
   1684   ;; will also remap "macro", but on top of that associate the fictional token type
   1685   ;; "not-quite-a-macro" with the face named `some-face'.
   1686   ;;
   1687   ;; `(:types (("macro" . font-lock-keyword-face))
   1688   ;;   :modifiers (("declaration" . lsp-face-semhl-interface))
   1689   ;;   :discard-default-types t
   1690   ;;   :discard-default-modifiers t)'
   1691   ;; will discard all default face definitions, hence leaving the client with
   1692   ;; only one token type "macro", mapped to `font-lock-keyword-face', and one
   1693   ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'.
   1694   semantic-tokens-faces-overrides
   1695   ;; Provides support for registering LSP Server specific capabilities.
   1696   custom-capabilities
   1697   ;; Function which returns the folders that are considered to be not projects but library files.
   1698   ;; The function accepts one parameter currently active workspace.
   1699   ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225.
   1700   library-folders-fn
   1701   ;; function which will be called when opening file in the workspace to perform
   1702   ;; client specific initialization. The function accepts one parameter
   1703   ;; currently active workspace.
   1704   before-file-open-fn
   1705   ;; Function which will be called right after a workspace has been initialized.
   1706   initialized-fn
   1707   ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP.
   1708   (remote? nil)
   1709 
   1710   ;; ‘completion-in-comments?’ t if the client supports completion in comments.
   1711   (completion-in-comments? nil)
   1712 
   1713   ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client.
   1714   (path->uri-fn nil)
   1715 
   1716   ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client.
   1717   (uri->path-fn nil)
   1718   ;; Function that returns an environment structure that will be used
   1719   ;; to set some environment variables when starting the language
   1720   ;; server process. These environment variables enable some
   1721   ;; additional features in the language server. The environment
   1722   ;; structure is an alist of the form (KEY . VALUE), where KEY is a
   1723   ;; string (regularly in all caps), and VALUE may be a string, a
   1724   ;; boolean, or a sequence of strings.
   1725   environment-fn
   1726 
   1727   ;; ‘after-open-fn’ workspace after open specific hooks.
   1728   (after-open-fn nil)
   1729 
   1730   ;; ‘async-request-handlers’ is a hash table mapping request method names
   1731   ;; (strings) to functions handling the respective requests that may take
   1732   ;; time to finish.  Upon receiving a request, ‘lsp-mode’ will call the
   1733   ;; associated handler function passing three arguments, the ‘lsp--workspace’
   1734   ;; object, the deserialized request parameters and the callback which accept
   1735   ;; result as its parameter.
   1736   (async-request-handlers (make-hash-table :test 'equal))
   1737   download-server-fn
   1738   download-in-progress?
   1739   buffers
   1740   synchronize-sections)
   1741 
   1742 (defun lsp-clients-executable-find (find-command &rest args)
   1743   "Finds an executable by invoking a search command.
   1744 
   1745 FIND-COMMAND is the executable finder that searches for the
   1746 actual language server executable. ARGS is a list of arguments to
   1747 give to FIND-COMMAND to find the language server.  Returns the
   1748 output of FIND-COMMAND if it exits successfully, nil otherwise.
   1749 
   1750 Typical uses include finding an executable by invoking `find' in
   1751 a project, finding LLVM commands on macOS with `xcrun', or
   1752 looking up project-specific language servers for projects written
   1753 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv'
   1754 etc."
   1755   (when-let* ((find-command-path (executable-find find-command))
   1756               (executable-path
   1757                (with-temp-buffer
   1758                  (when (zerop (apply 'call-process find-command-path nil t nil args))
   1759                    (buffer-substring-no-properties (point-min) (point-max))))))
   1760     (string-trim executable-path)))
   1761 
   1762 (defvar lsp--already-widened nil)
   1763 
   1764 (defmacro lsp-save-restriction-and-excursion (&rest form)
   1765   (declare (indent 0) (debug t))
   1766   `(if lsp--already-widened
   1767        (save-excursion ,@form)
   1768      (-let [lsp--already-widened t]
   1769        (save-restriction
   1770          (widen)
   1771          (save-excursion ,@form)))))
   1772 
   1773 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number
   1774 (defun lsp--line-character-to-point (line character)
   1775   "Return the point for character CHARACTER on line LINE."
   1776   (or (lsp-virtual-buffer-call :line/character->point line character)
   1777       (let ((inhibit-field-text-motion t))
   1778         (lsp-save-restriction-and-excursion
   1779           (goto-char (point-min))
   1780           (forward-line line)
   1781           ;; server may send character position beyond the current line and we
   1782           ;; should fallback to line end.
   1783           (-let [line-end (line-end-position)]
   1784             (if (> character (- line-end (point)))
   1785                 line-end
   1786               (forward-char character)
   1787               (point)))))))
   1788 
   1789 (lsp-defun lsp--position-to-point ((&Position :line :character))
   1790   "Convert `Position' object in PARAMS to a point."
   1791   (lsp--line-character-to-point line character))
   1792 
   1793 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end))
   1794   (cons start end))
   1795 
   1796 (lsp-defun lsp--range-text ((&RangeToPoint :start :end))
   1797   (buffer-substring start end))
   1798 
   1799 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end)))
   1800   (cond
   1801    ((and
   1802      (region-active-p)
   1803      (<= start (region-beginning) end)
   1804      (<= start (region-end) end)
   1805      (or (not (= start (region-beginning)))
   1806          (not (= end (region-end)))))
   1807     (cons start end))
   1808    ((and (<= start (point) end)
   1809          (not (region-active-p)))
   1810     (cons start end))
   1811    (parent? (lsp--find-wrapping-range parent?))))
   1812 
   1813 (defun lsp--get-selection-range ()
   1814   (or
   1815    (-when-let ((cache . cache-tick) lsp--document-selection-range-cache)
   1816      (when (= cache-tick (buffer-modified-tick)) cache))
   1817    (let ((response (cl-first
   1818                     (lsp-request
   1819                      "textDocument/selectionRange"
   1820                      (list :textDocument (lsp--text-document-identifier)
   1821                            :positions (vector (lsp--cur-position)))))))
   1822      (setq lsp--document-selection-range-cache
   1823            (cons response (buffer-modified-tick)))
   1824      response)))
   1825 
   1826 (defun lsp-extend-selection ()
   1827   "Extend selection."
   1828   (interactive)
   1829   (unless (lsp-feature? "textDocument/selectionRange")
   1830     (signal 'lsp-capability-not-supported (list "selectionRangeProvider")))
   1831   (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range)))
   1832     (goto-char start)
   1833     (set-mark (point))
   1834     (goto-char end)
   1835     (exchange-point-and-mark)))
   1836 
   1837 (defun lsp-warn (message &rest args)
   1838   "Display a warning message made from (`format-message' MESSAGE ARGS...).
   1839 This is equivalent to `display-warning', using `lsp-mode' as the type and
   1840 `:warning' as the level."
   1841   (display-warning 'lsp-mode (apply #'format-message message args)))
   1842 
   1843 (defun lsp--get-uri-handler (scheme)
   1844   "Get uri handler for SCHEME in the current workspace."
   1845   (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it)))
   1846           (or (lsp-workspaces) (lsp--session-workspaces (lsp-session)))))
   1847 
   1848 (defun lsp--fix-path-casing (path)
   1849   "On windows, downcases path because the windows file system is
   1850 case-insensitive.
   1851 
   1852 On other systems, returns path without change."
   1853   (if (eq system-type 'windows-nt) (downcase path) path))
   1854 
   1855 (defun lsp--uri-to-path (uri)
   1856   "Convert URI to a file path."
   1857   (if-let ((fn (->> (lsp-workspaces)
   1858                     (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client))
   1859                     (cl-first))))
   1860       (funcall fn uri)
   1861     (lsp--uri-to-path-1 uri)))
   1862 
   1863 (defun lsp-remap-path-if-needed (file-name)
   1864   (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings))
   1865       (propertize (buffer-local-value 'buffer-file-name buffer)
   1866                   'lsp-virtual-buffer virtual-buffer)
   1867     file-name))
   1868 
   1869 (defun lsp--uri-to-path-1 (uri)
   1870   "Convert URI to a file path."
   1871   (let* ((url (url-generic-parse-url (url-unhex-string uri)))
   1872          (type (url-type url))
   1873          (target (url-target url))
   1874          (file
   1875           (concat (decode-coding-string (url-filename url)
   1876                                         (or locale-coding-system 'utf-8))
   1877                   (when (and target
   1878                              (not (s-match
   1879                                    (rx "#" (group (1+ num)) (or "," "#")
   1880                                        (group (1+ num))
   1881                                        string-end)
   1882                                    uri)))
   1883                     (concat "#" target))))
   1884          (file-name (if (and type (not (string= type "file")))
   1885                         (if-let ((handler (lsp--get-uri-handler type)))
   1886                             (funcall handler uri)
   1887                           uri)
   1888                       ;; `url-generic-parse-url' is buggy on windows:
   1889                       ;; https://github.com/emacs-lsp/lsp-mode/pull/265
   1890                       (or (and (eq system-type 'windows-nt)
   1891                                (eq (elt file 0) ?\/)
   1892                                (substring file 1))
   1893                           file))))
   1894     (->> file-name
   1895          (concat (-some #'lsp--workspace-host-root (lsp-workspaces)))
   1896          (lsp-remap-path-if-needed))))
   1897 
   1898 (defun lsp--buffer-uri ()
   1899   "Return URI of the current buffer."
   1900   (or lsp-buffer-uri
   1901       (plist-get lsp--virtual-buffer :buffer-uri)
   1902       (lsp--path-to-uri
   1903        (or (buffer-file-name) (buffer-file-name (buffer-base-buffer))))))
   1904 
   1905 (defun lsp-register-client-capabilities (&rest _args)
   1906   "Implemented only to make `company-lsp' happy.
   1907 DELETE when `lsp-mode.el' is deleted.")
   1908 
   1909 (defconst lsp--url-path-allowed-chars
   1910   (url--allowed-chars (append '(?/) url-unreserved-chars))
   1911   "`url-unreserved-chars' with additional delim ?/.
   1912 This set of allowed chars is enough for hexifying local file paths.")
   1913 
   1914 (defun lsp--path-to-uri-1 (path)
   1915   (concat lsp--uri-file-prefix
   1916           (--> path
   1917             (expand-file-name it)
   1918             (or (file-remote-p it 'localname t) it)
   1919             (url-hexify-string it lsp--url-path-allowed-chars))))
   1920 
   1921 (defun lsp--path-to-uri (path)
   1922   "Convert PATH to a uri."
   1923   (if-let ((uri-fn (->> (lsp-workspaces)
   1924                         (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client))
   1925                         (cl-first))))
   1926       (funcall uri-fn path)
   1927     (lsp--path-to-uri-1 path)))
   1928 
   1929 (defun lsp--string-match-any (regex-list str)
   1930   "Return the first regex, if any, within REGEX-LIST matching STR."
   1931   (--first (string-match it str) regex-list))
   1932 
   1933 (cl-defstruct lsp-watch
   1934   (descriptors (make-hash-table :test 'equal))
   1935   root-directory)
   1936 
   1937 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories)
   1938   (let ((file-name (cl-third event))
   1939         (event-type (cl-second event)))
   1940     (cond
   1941      ((and (file-directory-p file-name)
   1942            (equal 'created event-type)
   1943            (not (lsp--string-match-any ignored-directories file-name)))
   1944 
   1945       (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch)
   1946 
   1947       ;; process the files that are already present in
   1948       ;; the directory.
   1949       (->> (directory-files-recursively file-name ".*" t)
   1950            (seq-do (lambda (f)
   1951                      (unless (file-directory-p f)
   1952                        (funcall callback (list nil 'created f)))))))
   1953      ((and (memq event-type '(created deleted changed))
   1954            (not (file-directory-p file-name))
   1955            (not (lsp--string-match-any ignored-files file-name)))
   1956       (funcall callback event))
   1957      ((and (memq event-type '(renamed))
   1958            (not (file-directory-p file-name))
   1959            (not (lsp--string-match-any ignored-files file-name)))
   1960       (funcall callback `(,(cl-first event) deleted ,(cl-third event)))
   1961       (funcall callback `(,(cl-first event) created ,(cl-fourth event)))))))
   1962 
   1963 (defun lsp--ask-about-watching-big-repo (number-of-directories dir)
   1964   "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR.
   1965 This is useful when there is a lot of files in a repository, as
   1966 that may slow Emacs down. Returns t if the user wants to watch
   1967 the entire repository, nil otherwise."
   1968   (prog1
   1969       (yes-or-no-p
   1970        (format
   1971         "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down.
   1972 Do you want to watch all files in %s? "
   1973         dir
   1974         number-of-directories
   1975         dir))
   1976     (lsp--info
   1977      (concat "You can configure this warning with the `lsp-enable-file-watchers' "
   1978              "and `lsp-file-watch-threshold' variables"))))
   1979 
   1980 
   1981 (defun lsp--path-is-watchable-directory (path dir ignored-directories)
   1982   "Figure out whether PATH (inside of DIR) is meant to have a file watcher set.
   1983 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't
   1984 want to watch."
   1985   (let
   1986       ((full-path (f-join dir path)))
   1987     (and (file-accessible-directory-p full-path)
   1988          (not (equal path "."))
   1989          (not (equal path ".."))
   1990          (not (lsp--string-match-any ignored-directories full-path)))))
   1991 
   1992 
   1993 (defun lsp--all-watchable-directories (dir ignored-directories &optional visited)
   1994   "Traverse DIR recursively returning a list of paths that should have watchers.
   1995 IGNORED-DIRECTORIES will be used for exclusions.
   1996 VISITED is used to track already-visited directories to avoid infinite loops."
   1997   (let* ((dir (if (f-symlink? dir)
   1998                   (file-truename dir)
   1999                 dir))
   2000          ;; Initialize visited directories if not provided
   2001          (visited (or visited (make-hash-table :test 'equal))))
   2002     (if (gethash dir visited)
   2003         ;; If the directory has already been visited, skip it
   2004         nil
   2005       ;; Mark the current directory as visited
   2006       (puthash dir t visited)
   2007       (apply #'nconc
   2008              ;; the directory itself is assumed to be part of the set
   2009              (list dir)
   2010              ;; collect all subdirectories that are watchable
   2011              (-map
   2012               (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories visited))
   2013               ;; but only look at subdirectories that are watchable
   2014               (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories))
   2015                        (directory-files dir)))))))
   2016 
   2017 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?)
   2018   "Create recursive file notification watch in DIR.
   2019 CALLBACK will be called when there are changes in any of
   2020 the monitored files. WATCHES is a hash table directory->file
   2021 notification handle which contains all of the watch that
   2022 already have been created. Watches will not be created for
   2023 any directory that matches any regex in IGNORED-DIRECTORIES.
   2024 Watches will not be created for any file that matches any
   2025 regex in IGNORED-FILES."
   2026   (let* ((dir (if (f-symlink? dir)
   2027                   (file-truename dir)
   2028                 dir))
   2029          (watch (or watch (make-lsp-watch :root-directory dir)))
   2030          (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories)))
   2031     (lsp-log "Creating watchers for following %s folders:\n  %s"
   2032              (length dirs-to-watch)
   2033              (s-join "\n  " dirs-to-watch))
   2034     (when (or
   2035            (not warn-big-repo?)
   2036            (not lsp-file-watch-threshold)
   2037            (let ((number-of-directories (length dirs-to-watch)))
   2038              (or
   2039               (< number-of-directories lsp-file-watch-threshold)
   2040               (condition-case nil
   2041                   (lsp--ask-about-watching-big-repo number-of-directories dir)
   2042                 (quit)))))
   2043       (dolist (current-dir dirs-to-watch)
   2044         (condition-case err
   2045             (progn
   2046               (puthash
   2047                current-dir
   2048                (file-notify-add-watch current-dir
   2049                                       '(change)
   2050                                       (lambda (event)
   2051                                         (lsp--folder-watch-callback event callback watch ignored-files ignored-directories)))
   2052                (lsp-watch-descriptors watch)))
   2053           (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))
   2054           (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err))))))
   2055     watch))
   2056 
   2057 (defun lsp-kill-watch (watch)
   2058   "Delete WATCH."
   2059   (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch))
   2060   (ht-clear! (lsp-watch-descriptors watch)))
   2061 
   2062 (defun lsp-json-bool (val)
   2063   "Convert VAL to JSON boolean."
   2064   (if val t :json-false))
   2065 
   2066 (defmacro with-lsp-workspace (workspace &rest body)
   2067   "Helper macro for invoking BODY in WORKSPACE context."
   2068   (declare (debug (form body))
   2069            (indent 1))
   2070   `(let ((lsp--cur-workspace ,workspace)) ,@body))
   2071 
   2072 (defmacro with-lsp-workspaces (workspaces &rest body)
   2073   "Helper macro for invoking BODY against multiple WORKSPACES."
   2074   (declare (debug (form body))
   2075            (indent 1))
   2076   `(let ((lsp--buffer-workspaces ,workspaces)) ,@body))
   2077 
   2078 
   2079 
   2080 (defmacro lsp-consistency-check (package)
   2081   `(defconst ,(intern (concat (symbol-name package)
   2082                               "-plist-value-when-compiled"))
   2083      (eval-when-compile lsp-use-plists)))
   2084 
   2085 
   2086 ;; loading code-workspace files
   2087 
   2088 ;;;###autoload
   2089 (defun lsp-load-vscode-workspace (file)
   2090   "Load vscode workspace from FILE"
   2091   (interactive "fSelect file to import: ")
   2092   (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session)))
   2093 
   2094   (let ((dir (f-dirname file)))
   2095     (->> file
   2096          (json-read-file)
   2097          (alist-get 'folders)
   2098          (-map (-lambda ((&alist 'path))
   2099                  (lsp-workspace-folders-add (expand-file-name path dir)))))))
   2100 
   2101 ;;;###autoload
   2102 (defun lsp-save-vscode-workspace (file)
   2103   "Save vscode workspace to FILE"
   2104   (interactive "FSelect file to save to: ")
   2105 
   2106   (let ((json-encoding-pretty-print t))
   2107     (f-write-text (json-encode
   2108                    `((folders . ,(->> (lsp-session)
   2109                                       (lsp-session-folders)
   2110                                       (--map `((path . ,it)))))))
   2111                   'utf-8
   2112                   file)))
   2113 
   2114 
   2115 (defmacro lsp-foreach-workspace (&rest body)
   2116   "Execute BODY for each of the current workspaces."
   2117   (declare (debug (form body)))
   2118   `(--map (with-lsp-workspace it ,@body) (lsp-workspaces)))
   2119 
   2120 (defmacro when-lsp-workspace (workspace &rest body)
   2121   "Helper macro for invoking BODY in WORKSPACE context if present."
   2122   (declare (debug (form body))
   2123            (indent 1))
   2124   `(when-let ((lsp--cur-workspace ,workspace)) ,@body))
   2125 
   2126 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items))
   2127   (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read))
   2128             (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label))
   2129                                  items))
   2130             (result (funcall-interactively
   2131                      selectfunc
   2132                      (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels))
   2133             (choices (if (listp result)
   2134                          (if (equal result '("*"))
   2135                              itemLabels
   2136                            result)
   2137                        (list result))))
   2138       (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data))
   2139                                                  (if (member label choices)
   2140                                                      (lsp-make-quick-pick-item :label label :picked t :user-data user-data)
   2141                                                    nil))
   2142                                                items)))))
   2143 
   2144 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?))
   2145   (read-string (format "%s: " prompt) (or value? "")))
   2146 
   2147 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type))
   2148   "Send the server's messages to log.
   2149 PARAMS - the data sent from _WORKSPACE."
   2150   (funcall (cl-case type
   2151              (1 'lsp--error)
   2152              (2 'lsp--warn)
   2153              (t 'lsp--info))
   2154            "%s"
   2155            message))
   2156 
   2157 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type))
   2158   "Send the server's messages to log.
   2159 PARAMS - the data sent from WORKSPACE."
   2160   (ignore
   2161    (let ((client (lsp--workspace-client workspace)))
   2162      (when (or (not client)
   2163                (cl-notany (-rpartial #'string-match-p message)
   2164                           (lsp--client-ignore-messages client)))
   2165        (lsp-log "%s" (lsp--propertize message type))))))
   2166 
   2167 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?))
   2168   "Display a message request to user sending the user selection back to server."
   2169   (let* ((message (lsp--propertize message type))
   2170          (choices (seq-map #'lsp:message-action-item-title actions?)))
   2171     (if choices
   2172         (completing-read (concat message " ") (seq-into choices 'list) nil t)
   2173       (lsp-log message))))
   2174 
   2175 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?))
   2176   "Show document URI in a buffer and go to SELECTION if any."
   2177   (let ((path (lsp--uri-to-path uri)))
   2178     (when (f-exists? path)
   2179       (with-current-buffer (find-file path)
   2180         (when selection?
   2181           (goto-char (lsp--position-to-point (lsp:range-start selection?))))
   2182         t))))
   2183 
   2184 (defcustom lsp-progress-prefix "⌛ "
   2185   "Progress prefix."
   2186   :group 'lsp-mode
   2187   :type 'string
   2188   :package-version '(lsp-mode . "8.0.0"))
   2189 
   2190 (defcustom lsp-progress-function #'lsp-on-progress-modeline
   2191   "Function for handling the progress notifications."
   2192   :group 'lsp-mode
   2193   :type '(choice
   2194           (const :tag "Use modeline" lsp-on-progress-modeline)
   2195           (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')"
   2196                  lsp-on-progress-legacy)
   2197           (const :tag "Ignore" ignore)
   2198           (function :tag "Other function"))
   2199   :package-version '(lsp-mode . "8.0.0"))
   2200 
   2201 (defcustom lsp-request-while-no-input-may-block nil
   2202   "Have `lsp-request-while-no-input` block unless `non-essential` is t."
   2203   :group 'lsp-mode
   2204   :type 'boolean)
   2205 
   2206 (defun lsp--progress-status ()
   2207   "Returns the status of the progress for the current workspaces."
   2208   (-let ((progress-status
   2209           (s-join
   2210            "|"
   2211            (-keep
   2212             (lambda (workspace)
   2213               (let ((tokens (lsp--workspace-work-done-tokens workspace)))
   2214                 (unless (ht-empty? tokens)
   2215                   (mapconcat
   2216                    (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?))
   2217                      (concat (if percentage?
   2218                                  (if (numberp percentage?)
   2219                                      (format "%.0f%%%% " percentage?)
   2220                                    (format "%s%%%% " percentage?))
   2221                                "")
   2222                              (or message? title)))
   2223                    (ht-values tokens)
   2224                    "|"))))
   2225             (lsp-workspaces)))))
   2226     (unless (s-blank? progress-status)
   2227       (concat lsp-progress-prefix progress-status " "))))
   2228 
   2229 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value
   2230                                                                 (value &as &WorkDoneProgress :kind)))
   2231   "PARAMS contains the progress data.
   2232 WORKSPACE is the workspace that contains the progress token."
   2233   (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status))))
   2234   (pcase kind
   2235     ("begin" (lsp-workspace-set-work-done-token token value workspace))
   2236     ("report" (lsp-workspace-set-work-done-token token value workspace))
   2237     ("end" (lsp-workspace-rem-work-done-token token workspace)))
   2238   (force-mode-line-update))
   2239 
   2240 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value
   2241                                                               (value &as &WorkDoneProgress :kind)))
   2242   "PARAMS contains the progress data.
   2243 WORKSPACE is the workspace that contains the progress token."
   2244   (pcase kind
   2245     ("begin"
   2246      (-let* (((&WorkDoneProgressBegin :title :percentage?) value)
   2247              (reporter
   2248               (if lsp-progress-via-spinner
   2249                   (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types))
   2250                          ;; Set message as a tooltip for the spinner strings
   2251                          (propertized-strings
   2252                           (seq-map (lambda (string) (propertize string 'help-echo title))
   2253                                    spinner-strings))
   2254                          (spinner-type (vconcat propertized-strings)))
   2255                     ;; The progress relates to the server as a whole,
   2256                     ;; display it on all buffers.
   2257                     (mapcar (lambda (buffer)
   2258                               (lsp-with-current-buffer buffer
   2259                                 (spinner-start spinner-type))
   2260                               buffer)
   2261                             (lsp--workspace-buffers workspace)))
   2262                 (if percentage?
   2263                     (make-progress-reporter title 0 100 percentage?)
   2264                   ;; No percentage, just progress
   2265                   (make-progress-reporter title nil nil)))))
   2266        (lsp-workspace-set-work-done-token token reporter workspace)))
   2267     ("report"
   2268      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2269        (unless lsp-progress-via-spinner
   2270          (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value)))))
   2271 
   2272     ("end"
   2273      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2274        (if lsp-progress-via-spinner
   2275            (mapc (lambda (buffer)
   2276                    (when (lsp-buffer-live-p buffer)
   2277                      (lsp-with-current-buffer buffer
   2278                        (spinner-stop))))
   2279                  reporter)
   2280          (progress-reporter-done reporter))
   2281        (lsp-workspace-rem-work-done-token token workspace)))))
   2282 
   2283 
   2284 ;; diagnostics
   2285 
   2286 (defvar lsp-diagnostic-filter nil
   2287   "A a function which will be called with
   2288   `&PublishDiagnosticsParams' and `workspace' which can be used
   2289   to filter out the diagnostics. The function should return
   2290   `&PublishDiagnosticsParams'.
   2291 
   2292 Common usecase are:
   2293 1. Filter the diagnostics for a particular language server.
   2294 2. Filter out the diagnostics under specific level.")
   2295 
   2296 (defvar lsp-diagnostic-stats (ht))
   2297 
   2298 (defun lsp-diagnostics (&optional current-workspace?)
   2299   "Return the diagnostics from all workspaces."
   2300   (or (pcase (if current-workspace?
   2301                  (lsp-workspaces)
   2302                (lsp--session-workspaces (lsp-session)))
   2303         (`() ())
   2304         (`(,workspace) (lsp--workspace-diagnostics workspace))
   2305         (`,workspaces (let ((result (make-hash-table :test 'equal)))
   2306                         (mapc (lambda (workspace)
   2307                                 (->> workspace
   2308                                      (lsp--workspace-diagnostics)
   2309                                      (maphash (lambda (file-name diagnostics)
   2310                                                 (puthash file-name
   2311                                                          (append (gethash file-name result) diagnostics)
   2312                                                          result)))))
   2313                               workspaces)
   2314                         result)))
   2315       (ht)))
   2316 
   2317 (defun lsp-diagnostics-stats-for (path)
   2318   "Get diagnostics statistics for PATH.
   2319 The result format is vector [_ errors warnings infos hints] or nil."
   2320   (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats))
   2321 
   2322 (defun lsp-diagnostics--request-pull-diagnostics (workspace)
   2323   "Request new diagnostics for the current file within WORKSPACE.
   2324 This is only executed if the server supports pull diagnostics."
   2325   (when (lsp-feature? "textDocument/diagnostic")
   2326     (let ((path (lsp--fix-path-casing (buffer-file-name))))
   2327       (lsp-request-async "textDocument/diagnostic"
   2328                          (list :textDocument (lsp--text-document-identifier))
   2329                          (-lambda ((&DocumentDiagnosticReport :kind :items?))
   2330                            (lsp-diagnostics--apply-pull-diagnostics workspace path kind items?))
   2331                          :mode 'tick))))
   2332 
   2333 (defun lsp-diagnostics--update-path (path new-stats)
   2334   (let ((new-stats (copy-sequence new-stats))
   2335         (path (lsp--fix-path-casing (directory-file-name path))))
   2336     (if-let ((old-data (gethash path lsp-diagnostic-stats)))
   2337         (dotimes (idx 5)
   2338           (cl-callf + (aref old-data idx)
   2339             (aref new-stats idx)))
   2340       (puthash path new-stats lsp-diagnostic-stats))))
   2341 
   2342 (defun lsp-diagnostics--convert-and-update-path-stats (workspace path diagnostics)
   2343   (let ((path (lsp--fix-path-casing path))
   2344         (new-stats (make-vector 5 0)))
   2345     (mapc (-lambda ((&Diagnostic :severity?))
   2346             (cl-incf (aref new-stats (or severity? 1))))
   2347           diagnostics)
   2348     (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace))))
   2349       (mapc (-lambda ((&Diagnostic :severity?))
   2350               (cl-decf (aref new-stats (or severity? 1))))
   2351             old-diags))
   2352     (lsp-diagnostics--update-path path new-stats)
   2353     (while (not (string= path (setf path (file-name-directory
   2354                                           (directory-file-name path)))))
   2355       (lsp-diagnostics--update-path path new-stats))))
   2356 
   2357 (lsp-defun lsp--on-diagnostics-update-stats (workspace
   2358                                              (&PublishDiagnosticsParams :uri :diagnostics))
   2359   (lsp-diagnostics--convert-and-update-path-stats workspace (lsp--uri-to-path uri) diagnostics))
   2360 
   2361 (defun lsp-diagnostics--apply-pull-diagnostics (workspace path kind diagnostics?)
   2362   "Update WORKSPACE diagnostics at PATH with DIAGNOSTICS?.
   2363 Depends on KIND being a \\='full\\=' update."
   2364   (cond
   2365    ((equal kind "full")
   2366     ;; TODO support `lsp-diagnostic-filter'
   2367     ;; (the params types differ from the published diagnostics response)
   2368     (lsp-diagnostics--convert-and-update-path-stats workspace path diagnostics?)
   2369     (-let* ((lsp--virtual-buffer-mappings (ht))
   2370             (workspace-diagnostics (lsp--workspace-diagnostics workspace)))
   2371       (if (seq-empty-p diagnostics?)
   2372           (remhash path workspace-diagnostics)
   2373         (puthash path (append diagnostics? nil) workspace-diagnostics))
   2374       (run-hooks 'lsp-diagnostics-updated-hook)))
   2375     ((equal kind "unchanged") t)
   2376     (t (lsp--error "Unknown pull diagnostic result kind '%s'" kind))))
   2377 
   2378 (defun lsp--on-diagnostics (workspace params)
   2379   "Callback for textDocument/publishDiagnostics.
   2380 interface PublishDiagnosticsParams {
   2381     uri: string;
   2382     diagnostics: Diagnostic[];
   2383 }
   2384 PARAMS contains the diagnostics data.
   2385 WORKSPACE is the workspace that contains the diagnostics."
   2386   (when lsp-diagnostic-filter
   2387     (setf params (funcall lsp-diagnostic-filter params workspace)))
   2388 
   2389   (lsp--on-diagnostics-update-stats workspace params)
   2390 
   2391   (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params)
   2392           (lsp--virtual-buffer-mappings (ht))
   2393           (file (lsp--fix-path-casing (lsp--uri-to-path uri)))
   2394           (workspace-diagnostics (lsp--workspace-diagnostics workspace)))
   2395 
   2396     (if (seq-empty-p diagnostics)
   2397         (remhash file workspace-diagnostics)
   2398       (puthash file (append diagnostics nil) workspace-diagnostics))
   2399 
   2400     (run-hooks 'lsp-diagnostics-updated-hook)))
   2401 
   2402 (defun lsp-diagnostics--workspace-cleanup (workspace)
   2403   (->> workspace
   2404        (lsp--workspace-diagnostics)
   2405        (maphash (lambda (key _)
   2406                   (lsp--on-diagnostics-update-stats
   2407                    workspace
   2408                    (lsp-make-publish-diagnostics-params
   2409                     :uri (lsp--path-to-uri key)
   2410                     :diagnostics [])))))
   2411   (clrhash (lsp--workspace-diagnostics workspace)))
   2412 
   2413 
   2414 
   2415 ;; textDocument/foldingRange support
   2416 
   2417 (cl-defstruct lsp--folding-range beg end kind children)
   2418 
   2419 (defvar-local lsp--cached-folding-ranges nil)
   2420 (defvar-local lsp--cached-nested-folding-ranges nil)
   2421 
   2422 (defun lsp--folding-range-width (range)
   2423   (- (lsp--folding-range-end range)
   2424      (lsp--folding-range-beg range)))
   2425 
   2426 (defun lsp--get-folding-ranges ()
   2427   "Get the folding ranges for the current buffer."
   2428   (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges))
   2429     (let* ((ranges (lsp-request "textDocument/foldingRange"
   2430                                 `(:textDocument ,(lsp--text-document-identifier))))
   2431            (sorted-line-col-pairs (->> ranges
   2432                                        (cl-mapcan (-lambda ((&FoldingRange :start-line
   2433                                                                            :start-character?
   2434                                                                            :end-line
   2435                                                                            :end-character?))
   2436                                                     (list (cons start-line start-character?)
   2437                                                           (cons end-line end-character?))))
   2438                                        (-sort #'lsp--line-col-comparator)))
   2439            (line-col-to-point-map (lsp--convert-line-col-to-points-batch
   2440                                    sorted-line-col-pairs)))
   2441       (setq lsp--cached-folding-ranges
   2442             (cons (buffer-chars-modified-tick)
   2443                   (--> ranges
   2444                     (seq-map (-lambda ((range &as
   2445                                               &FoldingRange :start-line
   2446                                               :start-character?
   2447                                               :end-line
   2448                                               :end-character?
   2449                                               :kind?))
   2450                                (make-lsp--folding-range
   2451                                 :beg (ht-get line-col-to-point-map
   2452                                              (cons start-line start-character?))
   2453                                 :end (ht-get line-col-to-point-map
   2454                                              (cons end-line end-character?))
   2455                                 :kind kind?))
   2456                              it)
   2457                     (seq-filter (lambda (folding-range)
   2458                                   (< (lsp--folding-range-beg folding-range)
   2459                                      (lsp--folding-range-end folding-range)))
   2460                                 it)
   2461                     (seq-into it 'list)
   2462                     (delete-dups it))))))
   2463   (cdr lsp--cached-folding-ranges))
   2464 
   2465 (defun lsp--get-nested-folding-ranges ()
   2466   "Get a list of nested folding ranges for the current buffer."
   2467   (-let [(tick . _) lsp--cached-folding-ranges]
   2468     (if (and (eq tick (buffer-chars-modified-tick))
   2469              lsp--cached-nested-folding-ranges)
   2470         lsp--cached-nested-folding-ranges
   2471       (setq lsp--cached-nested-folding-ranges
   2472             (lsp--folding-range-build-trees (lsp--get-folding-ranges))))))
   2473 
   2474 (defun lsp--folding-range-build-trees (ranges)
   2475   (setq ranges (seq-sort #'lsp--range-before-p ranges))
   2476   (let* ((dummy-node (make-lsp--folding-range
   2477                       :beg most-negative-fixnum
   2478                       :end most-positive-fixnum))
   2479          (stack (list dummy-node)))
   2480     (dolist (range ranges)
   2481       (while (not (lsp--range-inside-p range (car stack)))
   2482         (pop stack))
   2483       (push range (lsp--folding-range-children (car stack)))
   2484       (push range stack))
   2485     (lsp--folding-range-children dummy-node)))
   2486 
   2487 (defun lsp--range-inside-p (r1 r2)
   2488   "Return non-nil if folding range R1 lies inside R2"
   2489   (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2))
   2490        (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2))))
   2491 
   2492 (defun lsp--range-before-p (r1 r2)
   2493   "Return non-nil if folding range R1 ends before R2"
   2494   ;; Ensure r1 comes before r2
   2495   (or (< (lsp--folding-range-beg r1)
   2496          (lsp--folding-range-beg r2))
   2497       ;; If beg(r1) == beg(r2) make sure r2 ends first
   2498       (and (= (lsp--folding-range-beg r1)
   2499               (lsp--folding-range-beg r2))
   2500            (< (lsp--folding-range-end r2)
   2501               (lsp--folding-range-end r1)))))
   2502 
   2503 (defun lsp--point-inside-range-p (point range)
   2504   "Return non-nil if POINT lies inside folding range RANGE."
   2505   (and (>= point (lsp--folding-range-beg range))
   2506        (<= point (lsp--folding-range-end range))))
   2507 
   2508 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point)))
   2509   "Return the innermost folding range POINT lies in."
   2510   (seq-reduce (lambda (innermost-range curr-range)
   2511                 (if (and (lsp--point-inside-range-p point curr-range)
   2512                          (or (null innermost-range)
   2513                              (lsp--range-inside-p curr-range innermost-range)))
   2514                     curr-range
   2515                   innermost-range))
   2516               (lsp--get-folding-ranges)
   2517               nil))
   2518 
   2519 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point)))
   2520   "Return the outermost folding range POINT lies in."
   2521   (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range)
   2522                      (let ((curr-width (lsp--folding-range-width curr-range)))
   2523                        (if (and (lsp--point-inside-range-p point curr-range)
   2524                                 (or (null best-pair)
   2525                                     (> curr-width outermost-width)))
   2526                            (cons curr-width curr-range)
   2527                          best-pair)))
   2528                    (lsp--get-folding-ranges)
   2529                    nil)))
   2530 
   2531 (defun lsp--folding-range-at-point-bounds ()
   2532   (when (and lsp-enable-folding
   2533              (lsp-feature? "textDocument/foldingRange"))
   2534     (if-let ((range (lsp--get-current-innermost-folding-range)))
   2535         (cons (lsp--folding-range-beg range)
   2536               (lsp--folding-range-end range)))))
   2537 (put 'lsp--folding-range 'bounds-of-thing-at-point
   2538      #'lsp--folding-range-at-point-bounds)
   2539 
   2540 (defun lsp--get-nearest-folding-range (&optional backward)
   2541   (let ((point (point))
   2542         (found nil))
   2543     (while (not
   2544             (or found
   2545                 (if backward
   2546                     (<= point (point-min))
   2547                   (>= point (point-max)))))
   2548       (if backward (cl-decf point) (cl-incf point))
   2549       (setq found (lsp--get-current-innermost-folding-range point)))
   2550     found))
   2551 
   2552 (defun lsp--folding-range-at-point-forward-op (n)
   2553   (when (and lsp-enable-folding
   2554              (not (zerop n))
   2555              (lsp-feature? "textDocument/foldingRange"))
   2556     (cl-block break
   2557       (dotimes (_ (abs n))
   2558         (if-let ((range (lsp--get-nearest-folding-range (< n 0))))
   2559             (goto-char (if (< n 0)
   2560                            (lsp--folding-range-beg range)
   2561                          (lsp--folding-range-end range)))
   2562           (cl-return-from break))))))
   2563 (put 'lsp--folding-range 'forward-op
   2564      #'lsp--folding-range-at-point-forward-op)
   2565 
   2566 (defun lsp--folding-range-at-point-beginning-op ()
   2567   (goto-char (car (lsp--folding-range-at-point-bounds))))
   2568 (put 'lsp--folding-range 'beginning-op
   2569      #'lsp--folding-range-at-point-beginning-op)
   2570 
   2571 (defun lsp--folding-range-at-point-end-op ()
   2572   (goto-char (cdr (lsp--folding-range-at-point-bounds))))
   2573 (put 'lsp--folding-range 'end-op
   2574      #'lsp--folding-range-at-point-end-op)
   2575 
   2576 (defun lsp--range-at-point-bounds ()
   2577   (or (lsp--folding-range-at-point-bounds)
   2578       (when-let ((range (and
   2579                          (lsp-feature? "textDocument/hover")
   2580                          (->> (lsp--text-document-position-params)
   2581                               (lsp-request "textDocument/hover")
   2582                               (lsp:hover-range?)))))
   2583         (lsp--range-to-region range))))
   2584 
   2585 ;; A more general purpose "thing", useful for applications like focus.el
   2586 (put 'lsp--range 'bounds-of-thing-at-point
   2587      #'lsp--range-at-point-bounds)
   2588 
   2589 (defun lsp--log-io-p (method)
   2590   "Return non nil if should log for METHOD."
   2591   (and lsp-log-io
   2592        (or (not lsp-log-io-allowlist-methods)
   2593            (member method lsp-log-io-allowlist-methods))))
   2594 
   2595 
   2596 ;; toggles
   2597 
   2598 (defun lsp-toggle-trace-io ()
   2599   "Toggle client-server protocol logging."
   2600   (interactive)
   2601   (setq lsp-log-io (not lsp-log-io))
   2602   (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled")))
   2603 
   2604 (defun lsp-toggle-signature-auto-activate ()
   2605   "Toggle signature auto activate."
   2606   (interactive)
   2607   (setq lsp-signature-auto-activate
   2608         (unless lsp-signature-auto-activate '(:on-trigger-char)))
   2609   (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled"))
   2610   (lsp--update-signature-help-hook))
   2611 
   2612 (defun lsp-toggle-on-type-formatting ()
   2613   "Toggle on type formatting."
   2614   (interactive)
   2615   (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting))
   2616   (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled"))
   2617   (lsp--update-on-type-formatting-hook))
   2618 
   2619 (defun lsp-toggle-symbol-highlight ()
   2620   "Toggle symbol highlighting."
   2621   (interactive)
   2622   (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting))
   2623 
   2624   (cond
   2625    ((and lsp-enable-symbol-highlighting
   2626          (lsp-feature? "textDocument/documentHighlight"))
   2627     (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)
   2628     (lsp--info "Symbol highlighting enabled in current buffer."))
   2629    ((not lsp-enable-symbol-highlighting)
   2630     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   2631     (lsp--remove-overlays 'lsp-highlight)
   2632     (lsp--info "Symbol highlighting disabled in current buffer."))))
   2633 
   2634 
   2635 ;; keybindings
   2636 (defvar lsp--binding-descriptions nil
   2637   "List of key binding/short description pair.")
   2638 
   2639 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings)
   2640   "In KEYMAP, define key sequence KEY as DEF conditionally.
   2641 This is like `define-key', except the definition disappears
   2642 whenever COND evaluates to nil.
   2643 DESC is the short-description for the binding.
   2644 BINDINGS is a list of (key def desc cond)."
   2645   (declare (indent defun)
   2646            (debug (form form form form form &rest sexp)))
   2647   (->> (cl-list* key def desc cond bindings)
   2648        (-partition 4)
   2649        (-mapcat (-lambda ((key def desc cond))
   2650                   `((define-key ,keymap ,key
   2651                       '(menu-item
   2652                         ,(format "maybe-%s" def)
   2653                         ,def
   2654                         :filter
   2655                         (lambda (item)
   2656                           (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer)
   2657                                                            lsp--describe-buffer)
   2658                                                          (current-buffer))
   2659                                   ,cond)
   2660                             item))))
   2661                     (when (stringp ,key)
   2662                       (setq lsp--binding-descriptions
   2663                             (append lsp--binding-descriptions '(,key ,desc)))))))
   2664        macroexp-progn))
   2665 
   2666 (defvar lsp--describe-buffer nil)
   2667 
   2668 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus)
   2669   (let ((lsp--describe-buffer buffer))
   2670     (funcall fn buffer prefix menus)))
   2671 
   2672 (advice-add 'describe-buffer-bindings
   2673             :around
   2674             #'lsp-describe-buffer-bindings-advice)
   2675 
   2676 (defun lsp--prepend-prefix (mappings)
   2677   (->> mappings
   2678        (-partition 2)
   2679        (-mapcat (-lambda ((key description))
   2680                   (list (concat lsp-keymap-prefix " " key)
   2681                         description)))))
   2682 
   2683 (defvar lsp-command-map
   2684   (-doto (make-sparse-keymap)
   2685     (lsp-define-conditional-key
   2686       ;; workspaces
   2687       "wD" lsp-disconnect "disconnect" (lsp-workspaces)
   2688       "wd" lsp-describe-session "describe session" t
   2689       "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces)
   2690       "wr" lsp-workspace-restart "restart server" (lsp-workspaces)
   2691       "ws" lsp "start server" t
   2692 
   2693       ;; formatting
   2694       "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting")
   2695                                                  (lsp-feature? "textDocument/formatting"))
   2696       "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting")
   2697 
   2698       ;; folders
   2699       "Fa" lsp-workspace-folders-add "add folder" t
   2700       "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t
   2701       "Fr" lsp-workspace-folders-remove "remove folder" t
   2702 
   2703       ;; toggles
   2704       "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature?
   2705                                                                         "textDocument/publishDiagnostics")
   2706       "TL" lsp-toggle-trace-io "toggle log io" t
   2707       "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline)
   2708       "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs)
   2709       "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature?
   2710                                                                           "textDocument/codeAction")
   2711       "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature?
   2712                                                                "textDocument/documentSymbol")
   2713       "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc)
   2714       "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature?
   2715                                                                       "textDocument/onTypeFormatting")
   2716       "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight")
   2717       "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens")
   2718       "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp")
   2719 
   2720       ;; goto
   2721       "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol")
   2722       "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration")
   2723       "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list)
   2724       "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition")
   2725       "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls")
   2726                                                              (fboundp 'lsp-treemacs-call-hierarchy))
   2727       "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation")
   2728       "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references")
   2729       "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition")
   2730 
   2731       ;; help
   2732       "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc)
   2733                                                   (lsp-feature? "textDocument/hover"))
   2734       "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover")
   2735       "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp")
   2736 
   2737       ;; refactoring
   2738       "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction")
   2739       "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename")
   2740 
   2741       ;; actions
   2742       "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction")
   2743       "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight")
   2744       "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy))
   2745 
   2746       ;; peeks
   2747       "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition")
   2748                                                                 (fboundp 'lsp-ui-peek-find-definitions))
   2749       "Gi" lsp-ui-peek-find-implementation "peek implementations" (and
   2750                                                                    (fboundp 'lsp-ui-peek-find-implementation)
   2751                                                                    (lsp-feature? "textDocument/implementation"))
   2752       "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references)
   2753                                                               (lsp-feature? "textDocument/references"))
   2754       "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp
   2755                                                                            'lsp-ui-peek-find-workspace-symbol)
   2756                                                                           (lsp-feature? "workspace/symbol")))))
   2757 
   2758 
   2759 ;; which-key integration
   2760 
   2761 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key")
   2762 (declare-function which-key-add-key-based-replacements "ext:which-key")
   2763 
   2764 (defun lsp-enable-which-key-integration (&optional all-modes)
   2765   "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current
   2766 active `major-mode', or for all major modes when ALL-MODES is t."
   2767   (cl-flet ((which-key-fn (if all-modes
   2768                               'which-key-add-key-based-replacements
   2769                             (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode))))
   2770     (apply
   2771      #'which-key-fn
   2772      (lsp--prepend-prefix
   2773       (cl-list*
   2774        ""    "lsp"
   2775        "w"   "workspaces"
   2776        "F"   "folders"
   2777        "="   "formatting"
   2778        "T"   "toggle"
   2779        "g"   "goto"
   2780        "h"   "help"
   2781        "r"   "refactor"
   2782        "a"   "code actions"
   2783        "G"   "peek"
   2784        lsp--binding-descriptions)))))
   2785 
   2786 
   2787 ;; Globbing syntax
   2788 
   2789 ;; We port VSCode's glob-to-regexp code
   2790 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts)
   2791 ;; since the LSP globbing syntax seems to be the same as that of
   2792 ;; VSCode.
   2793 
   2794 (defconst lsp-globstar "**"
   2795   "Globstar pattern.")
   2796 
   2797 (defconst lsp-glob-split ?/
   2798   "The character by which we split path components in a glob
   2799 pattern.")
   2800 
   2801 (defconst lsp-path-regexp "[/\\\\]"
   2802   "Forward or backslash to be used as a path separator in
   2803 computed regexps.")
   2804 
   2805 (defconst lsp-non-path-regexp "[^/\\\\]"
   2806   "A regexp matching anything other than a slash.")
   2807 
   2808 (defconst lsp-globstar-regexp
   2809   (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?"
   2810           lsp-path-regexp
   2811           lsp-non-path-regexp lsp-path-regexp
   2812           lsp-path-regexp lsp-non-path-regexp)
   2813   "Globstar in regexp form.")
   2814 
   2815 (defun lsp-split-glob-pattern (pattern split-char)
   2816   "Split PATTERN at SPLIT-CHAR while respecting braces and brackets."
   2817   (when pattern
   2818     (let ((segments nil)
   2819           (in-braces nil)
   2820           (in-brackets nil)
   2821           (current-segment ""))
   2822       (dolist (char (string-to-list pattern))
   2823         (cl-block 'exit-point
   2824           (if (eq char split-char)
   2825               (when (and (null in-braces)
   2826                          (null in-brackets))
   2827                 (push current-segment segments)
   2828                 (setq current-segment "")
   2829                 (cl-return-from 'exit-point))
   2830             (pcase char
   2831               (?{
   2832                (setq in-braces t))
   2833               (?}
   2834                (setq in-braces nil))
   2835               (?\[
   2836                (setq in-brackets t))
   2837               (?\]
   2838                (setq in-brackets nil))))
   2839           (setq current-segment (concat current-segment
   2840                                         (char-to-string char)))))
   2841       (unless (string-empty-p current-segment)
   2842         (push current-segment segments))
   2843       (nreverse segments))))
   2844 
   2845 (defun lsp--glob-to-regexp (pattern)
   2846   "Helper function to convert a PATTERN from LSP's glob syntax to
   2847 an Elisp regexp."
   2848   (if (string-empty-p pattern)
   2849       ""
   2850     (let ((current-regexp "")
   2851           (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split)))
   2852       (if (-all? (lambda (segment) (eq segment lsp-globstar))
   2853                  glob-segments)
   2854           ".*"
   2855         (let ((prev-segment-was-globstar nil))
   2856           (seq-do-indexed
   2857            (lambda (segment index)
   2858              (if (string-equal segment lsp-globstar)
   2859                  (unless prev-segment-was-globstar
   2860                    (setq current-regexp (concat current-regexp
   2861                                                 lsp-globstar-regexp))
   2862                    (setq prev-segment-was-globstar t))
   2863                (let ((in-braces nil)
   2864                      (brace-val "")
   2865                      (in-brackets nil)
   2866                      (bracket-val ""))
   2867                  (dolist (char (string-to-list segment))
   2868                    (cond
   2869                     ((and (not (char-equal char ?\}))
   2870                           in-braces)
   2871                      (setq brace-val (concat brace-val
   2872                                              (char-to-string char))))
   2873                     ((and in-brackets
   2874                           (or (not (char-equal char ?\]))
   2875                               (string-empty-p bracket-val)))
   2876                      (let ((curr (cond
   2877                                   ((char-equal char ?-)
   2878                                    "-")
   2879                                   ;; NOTE: ?\^ and ?^ are different characters
   2880                                   ((and (memq char '(?^ ?!))
   2881                                         (string-empty-p bracket-val))
   2882                                    "^")
   2883                                   ((char-equal char lsp-glob-split)
   2884                                    "")
   2885                                   (t
   2886                                    (regexp-quote (char-to-string char))))))
   2887                        (setq bracket-val (concat bracket-val curr))))
   2888                     (t
   2889                      (cl-case char
   2890                        (?{
   2891                         (setq in-braces t))
   2892                        (?\[
   2893                         (setq in-brackets t))
   2894                        (?}
   2895                         (let* ((choices (lsp-split-glob-pattern brace-val ?\,))
   2896                                (brace-regexp (concat "\\(?:"
   2897                                                      (mapconcat #'lsp--glob-to-regexp choices "\\|")
   2898                                                      "\\)")))
   2899                           (setq current-regexp (concat current-regexp
   2900                                                        brace-regexp))
   2901                           (setq in-braces nil)
   2902                           (setq brace-val "")))
   2903                        (?\]
   2904                         (setq current-regexp
   2905                               (concat current-regexp
   2906                                       "[" bracket-val "]"))
   2907                         (setq in-brackets nil)
   2908                         (setq bracket-val ""))
   2909                        (??
   2910                         (setq current-regexp
   2911                               (concat current-regexp
   2912                                       lsp-non-path-regexp)))
   2913                        (?*
   2914                         (setq current-regexp
   2915                               (concat current-regexp
   2916                                       lsp-non-path-regexp "*?")))
   2917                        (t
   2918                         (setq current-regexp
   2919                               (concat current-regexp
   2920                                       (regexp-quote (char-to-string char)))))))))
   2921                  (when (and (< index (1- (length glob-segments)))
   2922                             (or (not (string-equal (nth (1+ index) glob-segments)
   2923                                                    lsp-globstar))
   2924                                 (< (+ index 2)
   2925                                    (length glob-segments))))
   2926                    (setq current-regexp
   2927                          (concat current-regexp
   2928                                  lsp-path-regexp)))
   2929                  (setq prev-segment-was-globstar nil))))
   2930            glob-segments)
   2931           current-regexp)))))
   2932 
   2933 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365
   2934 (defun lsp-glob-unbrace-at-top-level (glob-pattern)
   2935   "If GLOB-PATTERN does not start with a brace, return a singleton list
   2936 containing GLOB-PATTERN.
   2937 
   2938 If GLOB-PATTERN does start with a brace, return a list of the
   2939 comma-separated globs within the top-level braces."
   2940   (if (not (string-prefix-p "{" glob-pattern))
   2941       (list glob-pattern)
   2942     (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,)))
   2943 
   2944 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern)
   2945   "Convert GLOB-PATTERN to a regexp wrapped with the beginning-
   2946 and end-of-string meta-characters."
   2947   (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'"))
   2948 
   2949 (defun lsp-glob-to-regexps (glob-pattern)
   2950   "Convert a GLOB-PATTERN to a list of Elisp regexps."
   2951   (when-let*
   2952       ((glob-pattern (cond ((hash-table-p glob-pattern)
   2953                             (ht-get glob-pattern "pattern"))
   2954                            ((stringp glob-pattern) glob-pattern)
   2955                            (t (error "Unknown glob-pattern type: %s" glob-pattern))))
   2956        (trimmed-pattern (string-trim glob-pattern))
   2957        (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern)))
   2958     (seq-map #'lsp-glob-convert-to-wrapped-regexp
   2959              top-level-unbraced-patterns)))
   2960 
   2961 
   2962 
   2963 (defvar lsp-mode-menu)
   2964 
   2965 (defun lsp-mouse-click (event)
   2966   (interactive "e")
   2967   (let* ((ec (event-start event))
   2968          (choice (x-popup-menu event lsp-mode-menu))
   2969          (action (lookup-key lsp-mode-menu (apply 'vector choice))))
   2970 
   2971     (select-window (posn-window ec))
   2972 
   2973     (unless (and (region-active-p) (eq action 'lsp-execute-code-action))
   2974       (goto-char (posn-point ec)))
   2975     (run-with-idle-timer
   2976      0.001 nil
   2977      (lambda ()
   2978        (cl-labels ((check (value) (not (null value))))
   2979          (when choice
   2980            (call-interactively action)))))))
   2981 
   2982 (defvar lsp-mode-map
   2983   (let ((map (make-sparse-keymap)))
   2984     (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse)
   2985     (define-key map (kbd "C-<mouse-1>") #'ignore)
   2986     (define-key map (kbd "<mouse-3>") #'lsp-mouse-click)
   2987     (define-key map (kbd "C-S-SPC") #'lsp-signature-activate)
   2988     (when lsp-keymap-prefix
   2989       (define-key map (kbd lsp-keymap-prefix) lsp-command-map))
   2990     map)
   2991   "Keymap for `lsp-mode'.")
   2992 
   2993 (define-minor-mode lsp-mode "Mode for LSP interaction."
   2994   :keymap lsp-mode-map
   2995   :lighter
   2996   (" LSP["
   2997    (lsp--buffer-workspaces
   2998     (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "]["))
   2999     (:propertize "Disconnected" face warning))
   3000    "]")
   3001   :group 'lsp-mode
   3002   (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred))
   3003     ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp'
   3004     (lsp)))
   3005 
   3006 (defvar lsp-mode-menu
   3007   (easy-menu-create-menu
   3008    nil
   3009    `(["Go to definition" lsp-find-definition
   3010       :active (lsp-feature? "textDocument/definition")]
   3011      ["Find references" lsp-find-references
   3012       :active (lsp-feature? "textDocument/references")]
   3013      ["Find implementations" lsp-find-implementation
   3014       :active (lsp-feature? "textDocument/implementation")]
   3015      ["Find declarations" lsp-find-declaration
   3016       :active (lsp-feature? "textDocument/declaration")]
   3017      ["Go to type declaration" lsp-find-type-definition
   3018       :active (lsp-feature? "textDocument/typeDefinition")]
   3019      "--"
   3020      ["Describe" lsp-describe-thing-at-point]
   3021      ["Code action" lsp-execute-code-action]
   3022      ["Format" lsp-format-buffer]
   3023      ["Highlight references" lsp-document-highlight]
   3024      ["Type Hierarchy" lsp-java-type-hierarchy
   3025       :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")]
   3026      ["Type Hierarchy" lsp-treemacs-type-hierarchy
   3027       :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy"))
   3028                     (functionp 'lsp-treemacs-type-hierarchy)
   3029                     (lsp-feature? "textDocument/typeHierarchy"))]
   3030      ["Call Hierarchy" lsp-treemacs-call-hierarchy
   3031       :visible (and (functionp 'lsp-treemacs-call-hierarchy)
   3032                     (lsp-feature? "textDocument/callHierarchy"))]
   3033      ["Rename" lsp-rename
   3034       :active (lsp-feature? "textDocument/rename")]
   3035      "--"
   3036      ("Session"
   3037       ["View logs" lsp-workspace-show-log]
   3038       ["Describe" lsp-describe-session]
   3039       ["Shutdown" lsp-shutdown-workspace]
   3040       ["Restart" lsp-restart-workspace])
   3041      ("Workspace Folders"
   3042       ["Add" lsp-workspace-folders-add]
   3043       ["Remove" lsp-workspace-folders-remove]
   3044       ["Open" lsp-workspace-folders-open])
   3045      ("Toggle features"
   3046       ["Lenses" lsp-lens-mode]
   3047       ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode]
   3048       ["Modeline code actions" lsp-modeline-code-actions-mode]
   3049       ["Modeline diagnostics" lsp-modeline-diagnostics-mode])
   3050      "---"
   3051      ("Debug"
   3052       :active (bound-and-true-p dap-ui-mode)
   3053       :filter ,(lambda (_)
   3054                  (and (boundp 'dap-ui-menu-items)
   3055                       (nthcdr 3 dap-ui-menu-items))))))
   3056   "Menu for lsp-mode.")
   3057 
   3058 (defalias 'make-lsp-client 'make-lsp--client)
   3059 
   3060 (cl-defstruct lsp--registered-capability
   3061   (id "")
   3062   (method " ")
   3063   (options nil))
   3064 
   3065 ;; A ‘lsp--workspace’ object represents exactly one language server process.
   3066 (cl-defstruct lsp--workspace
   3067   ;; the `ewoc' object for displaying I/O to and from the server
   3068   (ewoc nil)
   3069 
   3070   ;; ‘server-capabilities’ is a hash table of the language server capabilities.
   3071   ;; It is the hash table representation of a LSP ServerCapabilities structure;
   3072   ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize.
   3073   (server-capabilities nil)
   3074 
   3075   ;; ‘registered-server-capabilities’ is a list of hash tables that represent
   3076   ;; dynamically-registered Registration objects.  See
   3077   ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability.
   3078   (registered-server-capabilities nil)
   3079 
   3080   ;; ‘root’ is a directory name or a directory file name for the workspace
   3081   ;; root.  ‘lsp-mode’ passes this directory to the ‘initialize’ method of the
   3082   ;; language server; see
   3083   ;; https://microsoft.github.io/language-server-protocol/specification#initialize.
   3084   (root nil)
   3085 
   3086   ;; ‘client’ is the ‘lsp--client’ object associated with this workspace.
   3087   (client nil)
   3088 
   3089   ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It
   3090   ;; used to derive the file path in `lsp--uri-to-path' when using tramp
   3091   ;; connection.
   3092   (host-root nil)
   3093 
   3094   ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or
   3095   ;; a network connection.  ‘lsp-mode’ communicates with ‘proc’ using the
   3096   ;; language server protocol.  ‘proc’ corresponds to the COMMUNICATION-PROCESS
   3097   ;; element of the return value of the client’s ‘get-root’ field, which see.
   3098   (proc nil)
   3099 
   3100   ;; ‘proc’ is a process object; it must represent a regular process, not a
   3101   ;; pipe or network process.  It represents the actual server process that
   3102   ;; corresponds to this workspace.  ‘cmd-proc’ corresponds to the
   3103   ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’
   3104   ;; field, which see.
   3105   (cmd-proc nil)
   3106 
   3107   ;; ‘buffers’ is a list of buffers associated with this workspace.
   3108   (buffers nil)
   3109 
   3110   ;; if semantic tokens is enabled, `semantic-tokens-faces' contains
   3111   ;; one face (or nil) for each token type supported by the language server.
   3112   (semantic-tokens-faces nil)
   3113 
   3114   ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces'
   3115   ;; contains one face (or nil) for each modifier type supported by the language
   3116   ;; server
   3117   (semantic-tokens-modifier-faces nil)
   3118 
   3119   ;; Extra client capabilities provided by third-party packages using
   3120   ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME
   3121   ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name,
   3122   ;; and CAPS is either a plist of the client capabilities, or a function that
   3123   ;; takes no argument and returns a plist of the client capabilities or nil.
   3124   (extra-client-capabilities nil)
   3125 
   3126   ;; Workspace status
   3127   (status nil)
   3128 
   3129   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3130   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3131   (metadata (make-hash-table :test 'equal))
   3132 
   3133   ;; contains all the file notification watches that have been created for the
   3134   ;; current workspace in format filePath->file notification handle.
   3135   (watches (make-hash-table :test 'equal))
   3136 
   3137   ;; list of workspace folders
   3138   (workspace-folders nil)
   3139 
   3140   ;; ‘last-id’ the last request id for the current workspace.
   3141   (last-id 0)
   3142 
   3143   ;; ‘status-string’ allows extensions to specify custom status string based on
   3144   ;; the Language Server specific messages.
   3145   (status-string nil)
   3146 
   3147   ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it
   3148   ;; was stopped).
   3149   shutdown-action
   3150 
   3151   ;; ‘diagnostics’ a hashmap with workspace diagnostics.
   3152   (diagnostics (make-hash-table :test 'equal))
   3153 
   3154   ;; contains all the workDone progress tokens that have been created
   3155   ;; for the current workspace.
   3156   (work-done-tokens (make-hash-table :test 'equal)))
   3157 
   3158 
   3159 (cl-defstruct lsp-session
   3160   ;; contains the folders that are part of the current session
   3161   folders
   3162   ;; contains the folders that must not be imported in the current workspace.
   3163   folders-blocklist
   3164   ;; contains the list of folders that must be imported in a project in case of
   3165   ;; multi root LSP server.
   3166   (server-id->folders (make-hash-table :test 'equal))
   3167   ;; folder to list of the servers that are associated with the folder.
   3168   (folder->servers (make-hash-table :test 'equal))
   3169   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3170   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3171   (metadata (make-hash-table :test 'equal)))
   3172 
   3173 (defun lsp-workspace-status (status-string &optional workspace)
   3174   "Set current workspace status to STATUS-STRING.
   3175 If WORKSPACE is not specified defaults to lsp--cur-workspace."
   3176   (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string))))
   3177     (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string)))
   3178 
   3179 (defun lsp-session-set-metadata (key value &optional _workspace)
   3180   "Associate KEY with VALUE in the WORKSPACE metadata.
   3181 If WORKSPACE is not provided current workspace will be used."
   3182   (puthash key value (lsp-session-metadata (lsp-session))))
   3183 
   3184 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata)
   3185 
   3186 (defun lsp-session-get-metadata (key &optional _workspace)
   3187   "Lookup KEY in WORKSPACE metadata.
   3188 If WORKSPACE is not provided current workspace will be used."
   3189   (gethash key (lsp-session-metadata (lsp-session))))
   3190 
   3191 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata)
   3192 
   3193 (defun lsp-workspace-set-work-done-token (token value workspace)
   3194   "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens."
   3195   (puthash token value (lsp--workspace-work-done-tokens workspace)))
   3196 
   3197 (defun lsp-workspace-get-work-done-token (token workspace)
   3198   "Lookup TOKEN in the WORKSPACE work-done-tokens."
   3199   (gethash token (lsp--workspace-work-done-tokens workspace)))
   3200 
   3201 (defun lsp-workspace-rem-work-done-token (token workspace)
   3202   "Remove TOKEN from the WORKSPACE work-done-tokens."
   3203   (remhash token (lsp--workspace-work-done-tokens workspace)))
   3204 
   3205 
   3206 (defun lsp--make-notification (method &optional params)
   3207   "Create notification body for method METHOD and parameters PARAMS."
   3208   (list :jsonrpc "2.0" :method method :params params))
   3209 
   3210 (defalias 'lsp--make-request 'lsp--make-notification)
   3211 (defalias 'lsp-make-request 'lsp--make-notification)
   3212 
   3213 (defun lsp--make-response (id result)
   3214   "Create response for REQUEST with RESULT."
   3215   `(:jsonrpc "2.0" :id ,id :result ,result))
   3216 
   3217 (defun lsp-make-notification (method &optional params)
   3218   "Create notification body for method METHOD and parameters PARAMS."
   3219   (lsp--make-notification method params))
   3220 
   3221 (defmacro lsp--json-serialize (params)
   3222   (if (progn
   3223         (require 'json)
   3224         (fboundp 'json-serialize))
   3225       `(json-serialize ,params
   3226                        :null-object nil
   3227                        :false-object :json-false)
   3228     `(let ((json-false :json-false))
   3229        (json-encode ,params))))
   3230 
   3231 (defun lsp--make-message (params)
   3232   "Create a LSP message from PARAMS, after encoding it to a JSON string."
   3233   (let ((body (lsp--json-serialize params)))
   3234     (concat "Content-Length: "
   3235             (number-to-string (1+ (string-bytes body)))
   3236             "\r\n\r\n"
   3237             body
   3238             "\n")))
   3239 
   3240 (cl-defstruct lsp--log-entry timestamp process-time type method id body)
   3241 
   3242 (defun lsp--make-log-entry (method id body type &optional process-time)
   3243   "Create an outgoing log object from BODY with method METHOD and id ID.
   3244 If ID is non-nil, then the body is assumed to be a notification.
   3245 TYPE can either be `incoming' or `outgoing'"
   3246   (cl-assert (memq type '(incoming-req outgoing-req incoming-notif
   3247                                        outgoing-notif incoming-resp
   3248                                        outgoing-resp)))
   3249   (make-lsp--log-entry
   3250    :timestamp (format-time-string "%I:%M:%S %p")
   3251    :process-time process-time
   3252    :method method
   3253    :id id
   3254    :type type
   3255    :body body))
   3256 
   3257 (defun lsp--log-font-lock-json (body)
   3258   "Font lock JSON BODY."
   3259   (with-temp-buffer
   3260     (insert body)
   3261     ;; We set the temp buffer file-name extension to .json and call `set-auto-mode'
   3262     ;; so the users configured json mode is used which could be
   3263     ;; `json-mode', `json-ts-mode', `jsonian-mode', etc.
   3264     (let ((buffer-file-name "lsp-log.json"))
   3265       (delay-mode-hooks
   3266         (set-auto-mode)
   3267         (if (fboundp 'font-lock-ensure)
   3268             (font-lock-ensure)
   3269           (with-no-warnings
   3270             (font-lock-fontify-buffer)))))
   3271     (buffer-string)))
   3272 
   3273 (defun lsp--log-entry-pp (entry)
   3274   (cl-assert (lsp--log-entry-p entry))
   3275   (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time
   3276                           body)
   3277                entry)
   3278               (json-false :json-false)
   3279               (json-encoding-pretty-print t)
   3280               (str nil))
   3281     (setq str
   3282           (concat (format "[Trace - %s] " timestamp)
   3283                   (pcase type
   3284                     ('incoming-req (format "Received request '%s - (%s)." method id))
   3285                     ('outgoing-req (format "Sending request '%s - (%s)'." method id))
   3286 
   3287                     ('incoming-notif (format "Received notification '%s'." method))
   3288                     ('outgoing-notif (format "Sending notification '%s'." method))
   3289 
   3290                     ('incoming-resp (format "Received response '%s - (%s)' in %dms."
   3291                                             method id process-time))
   3292                     ('outgoing-resp
   3293                      (format
   3294                       "Sending response '%s - (%s)'. Processing request took %dms"
   3295                       method id process-time)))
   3296                   "\n"
   3297                   (if (memq type '(incoming-resp ougoing-resp))
   3298                       "Result: "
   3299                     "Params: ")
   3300                   (lsp--log-font-lock-json (json-encode body))
   3301                   "\n\n\n"))
   3302     (setq str (propertize str 'mouse-face 'highlight 'read-only t))
   3303     (insert str)))
   3304 
   3305 (defvar-local lsp--log-io-ewoc nil)
   3306 
   3307 (defun lsp--get-create-io-ewoc (workspace)
   3308   (if (and (lsp--workspace-ewoc workspace)
   3309            (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace))))
   3310       (lsp--workspace-ewoc workspace)
   3311     (with-current-buffer (lsp--get-log-buffer-create workspace)
   3312       (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode))
   3313       (setq-local window-point-insertion-type t)
   3314       (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t))
   3315       (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc))
   3316     (lsp--workspace-ewoc workspace)))
   3317 
   3318 (defun lsp--ewoc-count (ewoc)
   3319   (let* ((count 0)
   3320          (count-fn (lambda (_) (setq count (1+ count)))))
   3321     (ewoc-map count-fn ewoc)
   3322     count))
   3323 
   3324 (defun lsp--log-entry-new (entry workspace)
   3325   (let* ((ewoc (lsp--get-create-io-ewoc workspace))
   3326          (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc)))
   3327          (node (if (or (eq lsp-io-messages-max t)
   3328                        (>= lsp-io-messages-max count))
   3329                    nil
   3330                  (ewoc-nth ewoc (1- lsp-io-messages-max))))
   3331          (prev nil)
   3332          (inhibit-read-only t))
   3333     (while node
   3334       (setq prev (ewoc-prev ewoc node))
   3335       (ewoc-delete ewoc node)
   3336       (setq node prev))
   3337     (ewoc-enter-last ewoc entry)))
   3338 
   3339 (defun lsp--send-notification (body)
   3340   "Send BODY as a notification to the language server."
   3341   (lsp-foreach-workspace
   3342    (when (lsp--log-io-p (plist-get body :method))
   3343      (lsp--log-entry-new (lsp--make-log-entry
   3344                           (plist-get body :method)
   3345                           nil (plist-get body :params) 'outgoing-notif)
   3346                          lsp--cur-workspace))
   3347    (lsp--send-no-wait body
   3348                       (lsp--workspace-proc lsp--cur-workspace))))
   3349 
   3350 (defalias 'lsp-send-notification 'lsp--send-notification)
   3351 
   3352 (defun lsp-notify (method params)
   3353   "Send notification METHOD with PARAMS."
   3354   (lsp--send-notification (lsp--make-notification method params)))
   3355 
   3356 (defun lsp--cur-workspace-check ()
   3357   "Check whether buffer lsp workspace(s) are set."
   3358   (cl-assert (lsp-workspaces) nil
   3359              "No language server(s) is associated with this buffer."))
   3360 
   3361 (defun lsp--send-request (body &optional no-wait no-merge)
   3362   "Send BODY as a request to the language server, get the response.
   3363 If NO-WAIT is non-nil, don't synchronously wait for a response.
   3364 If NO-MERGE is non-nil, don't merge the results but return an
   3365 alist mapping workspace->result."
   3366   (lsp-request (plist-get body :method)
   3367                (plist-get body :params)
   3368                :no-wait no-wait
   3369                :no-merge no-merge))
   3370 
   3371 (defalias 'lsp-send-request 'lsp--send-request
   3372   "Send BODY as a request to the language server and return the response
   3373 synchronously.
   3374 \n(fn BODY)")
   3375 
   3376 (cl-defun lsp-request (method params &key no-wait no-merge)
   3377   "Send request METHOD with PARAMS.
   3378 If NO-MERGE is non-nil, don't merge the results but return alist
   3379 workspace->result.
   3380 If NO-WAIT is non-nil send the request as notification."
   3381   (if no-wait
   3382       (lsp-notify method params)
   3383     (let* ((send-time (float-time))
   3384            ;; max time by which we must get a response
   3385            (expected-time
   3386             (and
   3387              lsp-response-timeout
   3388              (+ send-time lsp-response-timeout)))
   3389            resp-result resp-error done?)
   3390       (unwind-protect
   3391           (progn
   3392             (lsp-request-async method params
   3393                                (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3394                                :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3395                                :no-merge no-merge
   3396                                :mode 'detached
   3397                                :cancel-token :sync-request)
   3398             (while (not (or resp-error resp-result))
   3399               (if (functionp 'json-rpc-connection)
   3400                   (catch 'lsp-done (sit-for 0.01))
   3401                 (catch 'lsp-done
   3402                   (accept-process-output
   3403                    nil
   3404                    (if expected-time (- expected-time send-time) 1))))
   3405               (setq send-time (float-time))
   3406               (when (and expected-time (< expected-time send-time))
   3407                 (error "Timeout while waiting for response.  Method: %s" method)))
   3408             (setq done? t)
   3409             (cond
   3410              ((eq resp-result :finished) nil)
   3411              (resp-result resp-result)
   3412              ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3413              ((lsp-json-error? (cl-first resp-error))
   3414               (error (lsp:json-error-message (cl-first resp-error))))))
   3415         (unless done?
   3416           (lsp-cancel-request-by-token :sync-request))))))
   3417 
   3418 (cl-defun lsp-request-while-no-input (method params)
   3419   "Send request METHOD with PARAMS and waits until there is no input.
   3420 Return same value as `lsp--while-no-input' and respecting `non-essential'."
   3421   (if (or non-essential (not lsp-request-while-no-input-may-block))
   3422       (let* ((send-time (float-time))
   3423              ;; max time by which we must get a response
   3424              (expected-time
   3425               (and
   3426                lsp-response-timeout
   3427                (+ send-time lsp-response-timeout)))
   3428              resp-result resp-error done?)
   3429         (unwind-protect
   3430             (progn
   3431               (lsp-request-async method params
   3432                                  (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3433                                  :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3434                                  :mode 'detached
   3435                                  :cancel-token :sync-request)
   3436               (while (not (or resp-error resp-result (input-pending-p)))
   3437                 (catch 'lsp-done
   3438                   (sit-for
   3439                    (if expected-time (- expected-time send-time) 1)))
   3440                 (setq send-time (float-time))
   3441                 (when (and expected-time (< expected-time send-time))
   3442                   (error "Timeout while waiting for response.  Method: %s" method)))
   3443               (setq done? (or resp-error resp-result))
   3444               (cond
   3445                ((eq resp-result :finished) nil)
   3446                (resp-result resp-result)
   3447                ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3448                ((lsp-json-error? (cl-first resp-error))
   3449                 (error (lsp:json-error-message (cl-first resp-error))))))
   3450           (unless done?
   3451             (lsp-cancel-request-by-token :sync-request))
   3452           (when (and (input-pending-p) lsp--throw-on-input)
   3453             (throw 'input :interrupted))))
   3454     (lsp-request method params)))
   3455 
   3456 (defvar lsp--cancelable-requests (ht))
   3457 
   3458 (cl-defun lsp-request-async (method params callback
   3459                                     &key mode error-handler cancel-handler no-merge cancel-token)
   3460   "Send METHOD with PARAMS as a request to the language server.
   3461 Call CALLBACK with the response received from the server
   3462 asynchronously.
   3463 MODE determines when the callback will be called depending on the
   3464 condition of the original buffer.  It could be:
   3465 - `detached' which means that the callback will be executed no
   3466 matter what has happened to the buffer.
   3467 - `alive' - the callback will be executed only if the buffer from
   3468 which the call was executed is still alive.
   3469 - `current' the callback will be executed only if the original buffer
   3470 is still selected.
   3471 - `tick' - the callback will be executed only if the buffer was not modified.
   3472 - `unchanged' - the callback will be executed only if the buffer hasn't
   3473 changed and if the buffer is not modified.
   3474 
   3475 ERROR-HANDLER will be called in case the request has failed.
   3476 CANCEL-HANDLER will be called in case the request is being canceled.
   3477 If NO-MERGE is non-nil, don't merge the results but return alist
   3478 workspace->result.
   3479 CANCEL-TOKEN is the token that can be used to cancel request."
   3480   (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params)
   3481                            callback mode error-handler cancel-handler no-merge cancel-token))
   3482 
   3483 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback)
   3484   (lambda (&rest _)
   3485     (unless (and (equal 'post-command-hook hook)
   3486                  (equal (current-buffer) buf))
   3487       (lsp--request-cleanup-hooks id)
   3488       (with-lsp-workspaces workspaces
   3489         (lsp--cancel-request id)
   3490         (when cancel-callback (funcall cancel-callback)))
   3491       (lsp-log "Cancelling %s(%s) in hook %s" method id hook))))
   3492 
   3493 (defun lsp--create-async-callback
   3494     (callback method no-merge workspaces)
   3495   "Create async handler expecting COUNT results, merge them and call CALLBACK.
   3496 MODE determines when the callback will be called depending on the
   3497 condition of the original buffer. METHOD is the invoked method.
   3498 If NO-MERGE is non-nil, don't merge the results but return alist
   3499 workspace->result. ID is the request id."
   3500   (let (results errors)
   3501     (lambda (result)
   3502       (push (cons lsp--cur-workspace result)
   3503             (if (eq result :error) errors results))
   3504       (when (and (not (eq (length errors) (length workspaces)))
   3505                  (eq (+ (length errors) (length results)) (length workspaces)))
   3506         (funcall callback
   3507                  (if no-merge
   3508                      results
   3509                    (lsp--merge-results (-map #'cl-rest results) method)))))))
   3510 
   3511 (defcustom lsp-default-create-error-handler-fn nil
   3512   "Default error handler customization.
   3513 Handler should give METHOD as argument and return function of one argument
   3514 ERROR."
   3515   :type 'function
   3516   :group 'lsp-mode
   3517   :package-version '(lsp-mode . "9.0.0"))
   3518 
   3519 (defun lsp--create-default-error-handler (method)
   3520   "Default error handler.
   3521 METHOD is the executed method."
   3522   (if lsp-default-create-error-handler-fn
   3523       (funcall lsp-default-create-error-handler-fn method)
   3524     (lambda (error)
   3525       (lsp--warn "%s" (or (lsp--error-string error)
   3526                           (format "%s Request has failed" method))))))
   3527 
   3528 (defvar lsp--request-cleanup-hooks (ht))
   3529 
   3530 (defun lsp--request-cleanup-hooks (request-id)
   3531   (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks)))
   3532     (funcall cleanup-function)
   3533     (remhash request-id lsp--request-cleanup-hooks)))
   3534 
   3535 (defun lsp-cancel-request-by-token (cancel-token)
   3536   "Cancel request using CANCEL-TOKEN."
   3537   (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests))
   3538     (with-lsp-workspaces workspaces
   3539       (lsp--cancel-request request-id))
   3540     (remhash cancel-token lsp--cancelable-requests)
   3541     (lsp--request-cleanup-hooks request-id)))
   3542 
   3543 (defun lsp--send-request-async (body callback
   3544                                      &optional mode error-callback cancel-callback
   3545                                      no-merge cancel-token)
   3546   "Send BODY as a request to the language server.
   3547 Call CALLBACK with the response received from the server
   3548 asynchronously.
   3549 MODE determines when the callback will be called depending on the
   3550 condition of the original buffer.  It could be:
   3551 - `detached' which means that the callback will be executed no
   3552 matter what has happened to the buffer.
   3553 - `alive' - the callback will be executed only if the buffer from
   3554 which the call was executed is still alive.
   3555 - `current' the callback will be executed only if the original buffer
   3556 is still selected.
   3557 - `tick' - the callback will be executed only if the buffer was not modified.
   3558 - `unchanged' - the callback will be executed only if the buffer hasn't
   3559 changed and if the buffer is not modified.
   3560 
   3561 ERROR-CALLBACK will be called in case the request has failed.
   3562 CANCEL-CALLBACK will be called in case the request is being canceled.
   3563 If NO-MERGE is non-nil, don't merge the results but return alist
   3564 workspace->result.
   3565 CANCEL-TOKEN is the token that can be used to cancel request."
   3566   (when cancel-token
   3567     (lsp-cancel-request-by-token cancel-token))
   3568 
   3569   (if-let ((target-workspaces (lsp--find-workspaces-for body)))
   3570       (let* ((start-time (current-time))
   3571              (method (plist-get body :method))
   3572              (id (cl-incf lsp-last-id))
   3573              (buf (current-buffer))
   3574              (cancel-callback (when cancel-callback
   3575                                 (pcase mode
   3576                                   ((or 'alive 'tick 'unchanged)
   3577                                    (lambda ()
   3578                                      (with-current-buffer buf
   3579                                        (funcall cancel-callback))))
   3580                                   (_ cancel-callback))))
   3581              ;; calculate what are the (hook . local) pairs which will cancel
   3582              ;; the request
   3583              (hooks (pcase mode
   3584                       ('alive     '((kill-buffer-hook . t)))
   3585                       ('tick      '((kill-buffer-hook . t) (after-change-functions . t)))
   3586                       ('unchanged '((after-change-functions . t) (post-command-hook . nil)))
   3587                       ('current   '((post-command-hook . nil)))))
   3588              ;; note: lambdas in emacs can be compared but we should make sure
   3589              ;; that all of the captured arguments are the same - in our case
   3590              ;; `lsp--create-request-cancel' will return the same lambda when
   3591              ;; called with the same params.
   3592              (cleanup-hooks
   3593               (lambda () (mapc
   3594                           (-lambda ((hook . local))
   3595                             (if local
   3596                                 (when (buffer-live-p buf)
   3597                                   (with-current-buffer buf
   3598                                     (remove-hook hook
   3599                                                  (lsp--create-request-cancel
   3600                                                   id target-workspaces hook buf method cancel-callback)
   3601                                                  t)))
   3602                               (remove-hook hook (lsp--create-request-cancel
   3603                                                  id target-workspaces hook buf method cancel-callback))))
   3604                           hooks)
   3605                 (remhash cancel-token lsp--cancelable-requests)))
   3606              (callback (pcase mode
   3607                          ((or 'alive 'tick 'unchanged) (lambda (&rest args)
   3608                                                          (with-current-buffer buf
   3609                                                            (apply callback args))))
   3610                          (_ callback)))
   3611              (callback (lsp--create-async-callback callback
   3612                                                    method
   3613                                                    no-merge
   3614                                                    target-workspaces))
   3615              (callback (lambda (result)
   3616                          (lsp--request-cleanup-hooks id)
   3617                          (funcall callback result)))
   3618              (error-callback (lsp--create-async-callback
   3619                               (or error-callback
   3620                                   (lsp--create-default-error-handler method))
   3621                               method
   3622                               nil
   3623                               target-workspaces))
   3624              (error-callback (lambda (error)
   3625                                (funcall callback :error)
   3626                                (lsp--request-cleanup-hooks id)
   3627                                (funcall error-callback error)))
   3628              (body (plist-put body :id id)))
   3629 
   3630         ;; cancel request in any of the hooks
   3631         (mapc (-lambda ((hook . local))
   3632                 (add-hook hook
   3633                           (lsp--create-request-cancel
   3634                            id target-workspaces hook buf method cancel-callback)
   3635                           nil local))
   3636               hooks)
   3637         (puthash id cleanup-hooks lsp--request-cleanup-hooks)
   3638 
   3639         (setq lsp--last-active-workspaces target-workspaces)
   3640 
   3641         (when cancel-token
   3642           (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests))
   3643 
   3644         (seq-doseq (workspace target-workspaces)
   3645           (when (lsp--log-io-p method)
   3646             (lsp--log-entry-new (lsp--make-log-entry method id
   3647                                                      (plist-get body :params)
   3648                                                      'outgoing-req)
   3649                                 workspace))
   3650           (puthash id
   3651                    (list callback error-callback method start-time (current-time))
   3652                    (-> workspace
   3653                        (lsp--workspace-client)
   3654                        (lsp--client-response-handlers)))
   3655           (lsp--send-no-wait body (lsp--workspace-proc workspace)))
   3656         body)
   3657     (error "The connected server(s) does not support method %s.
   3658 To find out what capabilities support your server use `M-x lsp-describe-session'
   3659 and expand the capabilities section"
   3660            (plist-get body :method))))
   3661 
   3662 ;; deprecated, use lsp-request-async.
   3663 (defalias 'lsp-send-request-async 'lsp--send-request-async)
   3664 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1")
   3665 
   3666 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any
   3667 ;; pending language servers.
   3668 (add-hook 'kill-emacs-hook #'lsp--global-teardown)
   3669 
   3670 (defun lsp--global-teardown ()
   3671   "Unload working workspaces."
   3672   (lsp-foreach-workspace (lsp--shutdown-workspace)))
   3673 
   3674 (defun lsp--shutdown-workspace (&optional restart)
   3675   "Shut down the language server process for ‘lsp--cur-workspace’."
   3676   (with-demoted-errors "LSP error: %S"
   3677     (let ((lsp-response-timeout 0.5))
   3678       (condition-case err
   3679           (lsp-request "shutdown" nil)
   3680         (error (lsp--error "%s" err))))
   3681     (lsp-notify "exit" nil))
   3682   (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown))
   3683   (lsp--uninitialize-workspace))
   3684 
   3685 (defcustom lsp-inlay-hint-enable nil
   3686   "If non-nil it will enable inlay hints."
   3687   :type 'boolean
   3688   :group 'lsp-mode
   3689   :package-version '(lsp-mode . "9.0.0"))
   3690 
   3691 (defun lsp--uninitialize-workspace ()
   3692   "Cleanup buffer state.
   3693 When a workspace is shut down, by request or from just
   3694 disappearing, unset all the variables related to it."
   3695   (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace]
   3696     (lsp-process-kill cmd-proc)
   3697     (mapc (lambda (buf)
   3698             (when (lsp-buffer-live-p buf)
   3699               (lsp-with-current-buffer buf
   3700                                        (lsp-managed-mode -1))))
   3701           buffers)
   3702     (lsp-diagnostics--workspace-cleanup lsp--cur-workspace)))
   3703 
   3704 (defun lsp--client-capabilities (&optional custom-capabilities)
   3705   "Return the client capabilities appending CUSTOM-CAPABILITIES."
   3706   (append
   3707    `((general . ((positionEncodings . ["utf-32", "utf-16"])))
   3708      (workspace . ((workspaceEdit . ((documentChanges . t)
   3709                                      (resourceOperations . ["create" "rename" "delete"])))
   3710                    (applyEdit . t)
   3711                    (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))))
   3712                    (executeCommand . ((dynamicRegistration . :json-false)))
   3713                    ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t)))))
   3714                    (workspaceFolders . t)
   3715                    (configuration . t)
   3716                    ,@(when lsp-semantic-tokens-enable
   3717                        `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests)
   3718                                                                         lsp-semantic-tokens-honor-refresh-requests)
   3719                                                                    :json-false))))))
   3720                    ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t)))))
   3721                    ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false)))))
   3722                    (diagnostics . ((refreshSupport . :json-false)))
   3723                    (fileOperations . ((didCreate . :json-false)
   3724                                       (willCreate . :json-false)
   3725                                       (didRename . t)
   3726                                       (willRename . t)
   3727                                       (didDelete . :json-false)
   3728                                       (willDelete . :json-false)))))
   3729      (textDocument . ((declaration . ((dynamicRegistration . t)
   3730                                       (linkSupport . t)))
   3731                       (definition . ((dynamicRegistration . t)
   3732                                      (linkSupport . t)))
   3733                       (references . ((dynamicRegistration . t)))
   3734                       (implementation . ((dynamicRegistration . t)
   3735                                          (linkSupport . t)))
   3736                       (typeDefinition . ((dynamicRegistration . t)
   3737                                          (linkSupport . t)))
   3738                       (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t)))
   3739                       (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))
   3740                                          (hierarchicalDocumentSymbolSupport . t)))
   3741                       (formatting . ((dynamicRegistration . t)))
   3742                       (rangeFormatting . ((dynamicRegistration . t)))
   3743                       (onTypeFormatting . ((dynamicRegistration . t)))
   3744                       ,@(when (and lsp-semantic-tokens-enable
   3745                                    (functionp 'lsp--semantic-tokens-capabilities))
   3746                           (lsp--semantic-tokens-capabilities))
   3747                       (rename . ((dynamicRegistration . t) (prepareSupport . t)))
   3748                       (codeAction . ((dynamicRegistration . t)
   3749                                      (isPreferredSupport . t)
   3750                                      (codeActionLiteralSupport . ((codeActionKind . ((valueSet . [""
   3751                                                                                                   "quickfix"
   3752                                                                                                   "refactor"
   3753                                                                                                   "refactor.extract"
   3754                                                                                                   "refactor.inline"
   3755                                                                                                   "refactor.rewrite"
   3756                                                                                                   "source"
   3757                                                                                                   "source.organizeImports"])))))
   3758                                      (resolveSupport . ((properties . ["edit" "command"])))
   3759                                      (dataSupport . t)))
   3760                       (completion . ((completionItem . ((snippetSupport . ,(cond
   3761                                                                             ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode)))
   3762                                                                              (lsp--warn (concat
   3763                                                                                          "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. "
   3764                                                                                          "You must either install yasnippet, or disable snippet support."))
   3765                                                                              :json-false)
   3766                                                                             (lsp-enable-snippet t)
   3767                                                                             (t :json-false)))
   3768                                                         (documentationFormat . ["markdown" "plaintext"])
   3769                                                         ;; Remove this after jdtls support resolveSupport
   3770                                                         (resolveAdditionalTextEditsSupport . t)
   3771                                                         (insertReplaceSupport . t)
   3772                                                         (deprecatedSupport . t)
   3773                                                         (resolveSupport
   3774                                                          . ((properties . ["documentation"
   3775                                                                            "detail"
   3776                                                                            "additionalTextEdits"
   3777                                                                            "command"
   3778                                                                            "insertTextFormat"
   3779                                                                            "insertTextMode"])))
   3780                                                         (insertTextModeSupport . ((valueSet . [1 2])))))
   3781                                      (contextSupport . t)
   3782                                      (dynamicRegistration . t)))
   3783                       (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t)))))
   3784                                         (dynamicRegistration . t)))
   3785                       (documentLink . ((dynamicRegistration . t)
   3786                                        (tooltipSupport . t)))
   3787                       (hover . ((contentFormat . ["markdown" "plaintext"])
   3788                                 (dynamicRegistration . t)))
   3789                       ,@(when lsp-enable-folding
   3790                           `((foldingRange . ((dynamicRegistration . t)
   3791                                              ,@(when lsp-folding-range-limit
   3792                                                  `((rangeLimit . ,lsp-folding-range-limit)))
   3793                                              ,@(when lsp-folding-line-folding-only
   3794                                                  `((lineFoldingOnly . t)))))))
   3795                       (selectionRange . ((dynamicRegistration . t)))
   3796                       (callHierarchy . ((dynamicRegistration . :json-false)))
   3797                       (typeHierarchy . ((dynamicRegistration . t)))
   3798                       (publishDiagnostics . ((relatedInformation . t)
   3799                                              (tagSupport . ((valueSet . [1 2])))
   3800                                              (versionSupport . t)))
   3801                       (diagnostic . ((dynamicRegistration . :json-false)
   3802                                      (relatedDocumentSupport . :json-false)))
   3803                       (linkedEditingRange . ((dynamicRegistration . t)))))
   3804      (window . ((workDoneProgress . t)
   3805                 (showDocument . ((support . t))))))
   3806    custom-capabilities))
   3807 
   3808 (defun lsp-find-roots-for-workspace (workspace session)
   3809   "Get all roots for the WORKSPACE."
   3810   (-filter #'identity (ht-map (lambda (folder workspaces)
   3811                                 (when (-contains? workspaces workspace)
   3812                                   folder))
   3813                               (lsp-session-folder->servers session))))
   3814 
   3815 (defun lsp-session-watches (&optional session)
   3816   "Get watches created for SESSION."
   3817   (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session))))
   3818       (-let [res (make-hash-table :test 'equal)]
   3819         (puthash "__watches" res (lsp-session-metadata (or session (lsp-session))))
   3820         res)))
   3821 
   3822 (defun lsp--file-process-event (session root-folder event)
   3823   "Process file event."
   3824   (let* ((changed-file (cl-third event))
   3825          (rel-changed-file (f-relative changed-file root-folder))
   3826          (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type))
   3827          (bit-position (1- event-numeric-kind))
   3828          (watch-bit (ash 1 bit-position)))
   3829     (->>
   3830      session
   3831      lsp-session-folder->servers
   3832      (gethash root-folder)
   3833      (seq-do (lambda (workspace)
   3834                (when (->>
   3835                       workspace
   3836                       lsp--workspace-registered-server-capabilities
   3837                       (-any?
   3838                        (lambda (capability)
   3839                          (and
   3840                           (equal (lsp--registered-capability-method capability)
   3841                                  "workspace/didChangeWatchedFiles")
   3842                           (->>
   3843                            capability
   3844                            lsp--registered-capability-options
   3845                            (lsp:did-change-watched-files-registration-options-watchers)
   3846                            (seq-find
   3847                             (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp))
   3848                               (when (or (null kind?)
   3849                                         (> (logand kind? watch-bit) 0))
   3850                                 (-let [regexes (or cached-regexp
   3851                                                    (let ((regexp (lsp-glob-to-regexps glob-pattern)))
   3852                                                      (lsp-put fs-watcher :_cachedRegexp regexp)
   3853                                                      regexp))]
   3854                                   (-any? (lambda (re)
   3855                                            (or (string-match re changed-file)
   3856                                                (string-match re rel-changed-file)))
   3857                                          regexes))))))))))
   3858                  (with-lsp-workspace workspace
   3859                    (lsp-notify
   3860                     "workspace/didChangeWatchedFiles"
   3861                     `((changes . [((type . ,event-numeric-kind)
   3862                                    (uri . ,(lsp--path-to-uri changed-file)))]))))))))))
   3863 
   3864 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?))
   3865   "Register capability REG."
   3866   (when (and lsp-enable-file-watchers
   3867              (equal method "workspace/didChangeWatchedFiles"))
   3868     (-let* ((created-watches (lsp-session-watches (lsp-session)))
   3869             (root-folders (cl-set-difference
   3870                            (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session))
   3871                            (ht-keys created-watches))))
   3872       ;; create watch for each root folder without such
   3873       (dolist (folder root-folders)
   3874         (let* ((watch (make-lsp-watch :root-directory folder))
   3875                (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder))
   3876                (ignored-files-regex-list (car ignored-things))
   3877                (ignored-directories-regex-list (cadr ignored-things)))
   3878           (puthash folder watch created-watches)
   3879           (lsp-watch-root-folder (file-truename folder)
   3880                                  (-partial #'lsp--file-process-event (lsp-session) folder)
   3881                                  ignored-files-regex-list
   3882                                  ignored-directories-regex-list
   3883                                  watch
   3884                                  t)))))
   3885 
   3886   (push
   3887    (make-lsp--registered-capability :id id :method method :options register-options?)
   3888    (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3889 
   3890 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body)
   3891   "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to
   3892 access dir-local variables."
   3893   (declare (indent 1) (debug t))
   3894   `(with-temp-buffer
   3895      ;; Set the buffer's name to something under the root so that we can hack the local variables
   3896      ;; This file doesn't need to exist and will not be created due to this.
   3897      (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root)))
   3898      (hack-local-variables)
   3899      (prog1 ,@body
   3900        (setq-local buffer-file-name nil))))
   3901 
   3902 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root)
   3903   "Return a list of the form
   3904 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given
   3905 WORKSPACE-ROOT."
   3906   ;; The intent of this function is to provide per-root workspace-level customization of the
   3907   ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables.
   3908   (lsp--with-workspace-temp-buffer workspace-root
   3909     (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories))))
   3910 
   3911 
   3912 (defun lsp--cleanup-hanging-watches ()
   3913   "Cleanup watches in case there are no more workspaces that are interested
   3914 in that particular folder."
   3915   (let* ((session (lsp-session))
   3916          (watches (lsp-session-watches session)))
   3917     (dolist (watched-folder (ht-keys watches))
   3918       (when (-none? (lambda (workspace)
   3919                       (with-lsp-workspace workspace
   3920                         (lsp--registered-capability "workspace/didChangeWatchedFiles")))
   3921                     (gethash watched-folder (lsp-session-folder->servers (lsp-session))))
   3922         (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder)
   3923         (lsp-kill-watch (gethash watched-folder watches))
   3924         (remhash watched-folder watches)))))
   3925 
   3926 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method))
   3927   "Unregister capability UNREG."
   3928   (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace)
   3929         (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id))
   3930                     (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3931   (when (equal method "workspace/didChangeWatchedFiles")
   3932     (lsp--cleanup-hanging-watches)))
   3933 
   3934 (defun lsp--server-capabilities ()
   3935   "Return the capabilities of the language server associated with the buffer."
   3936   (->> (lsp-workspaces)
   3937        (-keep #'lsp--workspace-server-capabilities)
   3938        (apply #'lsp-merge)))
   3939 
   3940 (defun lsp--send-open-close-p ()
   3941   "Return whether open and close notifications should be sent to the server."
   3942   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3943     (or (memq sync '(1 2))
   3944         (lsp:text-document-sync-options-open-close? sync))))
   3945 
   3946 (defun lsp--send-will-save-p ()
   3947   "Return whether willSave notifications should be sent to the server."
   3948   (-> (lsp--server-capabilities)
   3949       (lsp:server-capabilities-text-document-sync?)
   3950       (lsp:text-document-sync-options-will-save?)))
   3951 
   3952 (defun lsp--send-will-save-wait-until-p ()
   3953   "Return whether willSaveWaitUntil notifications should be sent to the server."
   3954   (-> (lsp--server-capabilities)
   3955       (lsp:server-capabilities-text-document-sync?)
   3956       (lsp:text-document-sync-options-will-save-wait-until?)))
   3957 
   3958 (defun lsp--send-did-save-p ()
   3959   "Return whether didSave notifications should be sent to the server."
   3960   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3961     (or (memq sync '(1 2))
   3962         (lsp:text-document-sync-options-save? sync))))
   3963 
   3964 (defun lsp--save-include-text-p ()
   3965   "Return whether save notifications should include the text document's contents."
   3966   (->> (lsp--server-capabilities)
   3967        (lsp:server-capabilities-text-document-sync?)
   3968        (lsp:text-document-sync-options-save?)
   3969        (lsp:text-document-save-registration-options-include-text?)))
   3970 
   3971 (defun lsp--send-will-rename-files-p (path)
   3972   "Return whether willRenameFiles request should be sent to the server.
   3973 If any filters, checks if it applies for PATH."
   3974   (let* ((will-rename (-> (lsp--server-capabilities)
   3975                           (lsp:server-capabilities-workspace?)
   3976                           (lsp:workspace-server-capabilities-file-operations?)
   3977                           (lsp:workspace-file-operations-will-rename?)))
   3978          (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list)))
   3979     (and will-rename
   3980          (or (seq-empty-p filters)
   3981              (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob)))
   3982                       (-let [regexes (lsp-glob-to-regexps glob)]
   3983                         (and (or (not scheme?)
   3984                                  (string-prefix-p scheme? (lsp--path-to-uri path)))
   3985                              (-any? (lambda (re)
   3986                                       (string-match re path))
   3987                                     regexes))))
   3988                     filters)))))
   3989 
   3990 (defun lsp--send-did-rename-files-p ()
   3991   "Return whether didRenameFiles notification should be sent to the server."
   3992   (-> (lsp--server-capabilities)
   3993       (lsp:server-capabilities-workspace?)
   3994       (lsp:workspace-server-capabilities-file-operations?)
   3995       (lsp:workspace-file-operations-did-rename?)))
   3996 
   3997 (declare-function project-roots "ext:project" (project) t)
   3998 (declare-function project-root "ext:project" (project) t)
   3999 
   4000 (defun lsp--suggest-project-root ()
   4001   "Get project root."
   4002   (or
   4003    (when (fboundp 'projectile-project-root)
   4004      (condition-case nil
   4005          (projectile-project-root)
   4006        (error nil)))
   4007    (when (fboundp 'project-current)
   4008      (when-let ((project (project-current)))
   4009        (if (fboundp 'project-root)
   4010            (project-root project)
   4011          (car (with-no-warnings
   4012                 (project-roots project))))))
   4013    default-directory))
   4014 
   4015 (defun lsp--read-from-file (file)
   4016   "Read FILE content."
   4017   (when (file-exists-p file)
   4018     (cl-first (read-from-string (f-read-text file 'utf-8)))))
   4019 
   4020 (defun lsp--persist (file-name to-persist)
   4021   "Persist TO-PERSIST in FILE-NAME.
   4022 
   4023 This function creates the parent directories if they don't exist
   4024 yet."
   4025   (let ((print-length nil)
   4026         (print-level nil))
   4027     ;; Create all parent directories:
   4028     (make-directory (f-parent file-name) t)
   4029     (f-write-text (prin1-to-string to-persist) 'utf-8 file-name)))
   4030 
   4031 (defun lsp-workspace-folders-add (project-root)
   4032   "Add PROJECT-ROOT to the list of workspace folders."
   4033   (interactive
   4034    (list (read-directory-name "Select folder to add: "
   4035                               (or (lsp--suggest-project-root) default-directory) nil t)))
   4036   (cl-pushnew (lsp-f-canonical project-root)
   4037               (lsp-session-folders (lsp-session)) :test 'equal)
   4038   (lsp--persist-session (lsp-session))
   4039 
   4040   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil))
   4041 
   4042 (defun lsp-workspace-folders-remove (project-root)
   4043   "Remove PROJECT-ROOT from the list of workspace folders."
   4044   (interactive (list (completing-read "Select folder to remove: "
   4045                                       (lsp-session-folders (lsp-session))
   4046                                       nil t nil nil
   4047                                       (lsp-find-session-folder (lsp-session) default-directory))))
   4048 
   4049   (setq project-root (lsp-f-canonical project-root))
   4050 
   4051   ;; send remove folder to each multiroot workspace associated with the folder
   4052   (dolist (wks (->> (lsp-session)
   4053                     (lsp-session-folder->servers)
   4054                     (gethash project-root)
   4055                     (--filter (lsp--client-multi-root (lsp--workspace-client it)))))
   4056     (with-lsp-workspace wks
   4057       (lsp-notify "workspace/didChangeWorkspaceFolders"
   4058                   (lsp-make-did-change-workspace-folders-params
   4059                    :event (lsp-make-workspace-folders-change-event
   4060                            :removed (vector (lsp-make-workspace-folder
   4061                                              :uri (lsp--path-to-uri project-root)
   4062                                              :name (f-filename project-root)))
   4063                            :added [])))))
   4064 
   4065   ;; turn off servers in the removed directory
   4066   (let* ((session (lsp-session))
   4067          (folder->servers (lsp-session-folder->servers session))
   4068          (server-id->folders (lsp-session-server-id->folders session))
   4069          (workspaces (gethash project-root folder->servers)))
   4070 
   4071     (remhash project-root folder->servers)
   4072 
   4073     ;; turn off the servers without root folders
   4074     (dolist (workspace workspaces)
   4075       (when (--none? (-contains? it workspace) (ht-values folder->servers))
   4076         (lsp--info "Shutdown %s since folder %s is removed..."
   4077                    (lsp--workspace-print workspace) project-root)
   4078         (with-lsp-workspace workspace (lsp--shutdown-workspace))))
   4079 
   4080     (setf (lsp-session-folders session)
   4081           (-remove-item project-root (lsp-session-folders session)))
   4082 
   4083     (ht-aeach (puthash key
   4084                        (-remove-item project-root value)
   4085                        server-id->folders)
   4086               server-id->folders)
   4087     (lsp--persist-session (lsp-session)))
   4088 
   4089   (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root)))
   4090 
   4091 (defun lsp-workspace-blocklist-remove (project-root)
   4092   "Remove PROJECT-ROOT from the workspace blocklist."
   4093   (interactive (list (completing-read "Select folder to remove:"
   4094                                       (lsp-session-folders-blocklist (lsp-session))
   4095                                       nil t)))
   4096   (setf (lsp-session-folders-blocklist (lsp-session))
   4097         (delete project-root
   4098                 (lsp-session-folders-blocklist (lsp-session))))
   4099   (lsp--persist-session (lsp-session)))
   4100 
   4101 (define-obsolete-function-alias 'lsp-workspace-folders-switch
   4102   'lsp-workspace-folders-open "lsp-mode 6.1")
   4103 
   4104 (defun lsp-workspace-folders-open (project-root)
   4105   "Open the directory located at PROJECT-ROOT"
   4106   (interactive (list (completing-read "Open folder: "
   4107                                       (lsp-session-folders (lsp-session))
   4108                                       nil t)))
   4109   (find-file project-root))
   4110 
   4111 (defun lsp--maybe-enable-signature-help (trigger-characters)
   4112   (let ((ch last-command-event))
   4113     (when (cl-find ch trigger-characters :key #'string-to-char)
   4114       (lsp-signature-activate))))
   4115 
   4116 (defun lsp--on-type-formatting-handler-create ()
   4117   (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" )))
   4118     (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character?
   4119                                              :first-trigger-character) provider]
   4120       (lambda ()
   4121         (lsp--on-type-formatting first-trigger-character
   4122                                  more-trigger-character?)))))
   4123 
   4124 (defun lsp--update-on-type-formatting-hook (&optional cleanup?)
   4125   (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create)))
   4126     (cond
   4127      ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?))
   4128       (add-hook 'post-self-insert-hook on-type-formatting-handler nil t))
   4129      ((or cleanup?
   4130           (not lsp-enable-on-type-formatting))
   4131       (remove-hook 'post-self-insert-hook on-type-formatting-handler t)))))
   4132 
   4133 (defun lsp--signature-help-handler-create ()
   4134   (-when-let ((&SignatureHelpOptions? :trigger-characters?)
   4135               (lsp--capability-for-method "textDocument/signatureHelp"))
   4136     (lambda ()
   4137       (lsp--maybe-enable-signature-help trigger-characters?))))
   4138 
   4139 (defun lsp--update-signature-help-hook (&optional cleanup?)
   4140   (let ((signature-help-handler (lsp--signature-help-handler-create)))
   4141     (cond
   4142      ((and (or (equal lsp-signature-auto-activate t)
   4143                (memq :on-trigger-char lsp-signature-auto-activate))
   4144            signature-help-handler
   4145            (not cleanup?))
   4146       (add-hook 'post-self-insert-hook signature-help-handler nil t))
   4147 
   4148      ((or cleanup?
   4149           (not (or (equal lsp-signature-auto-activate t)
   4150                    (memq :on-trigger-char lsp-signature-auto-activate))))
   4151       (remove-hook 'post-self-insert-hook signature-help-handler t)))))
   4152 
   4153 (defun lsp--after-set-visited-file-name ()
   4154   (lsp-disconnect)
   4155   (lsp))
   4156 
   4157 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27
   4158 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099
   4159 (defvar eldoc-documentation-default) ; CI
   4160 (when (< emacs-major-version 28)
   4161   (unless (boundp 'eldoc-documentation-functions)
   4162     (load "eldoc" nil 'nomessage))
   4163   (when (memq (default-value 'eldoc-documentation-function) '(nil ignore))
   4164     ;; actually `eldoc-documentation-strategy', but CI was failing
   4165     (setq-default eldoc-documentation-function 'eldoc-documentation-default)))
   4166 
   4167 (define-minor-mode lsp-managed-mode
   4168   "Mode for source buffers managed by lsp-mode."
   4169   :lighter nil
   4170   (cond
   4171    (lsp-managed-mode
   4172     (when (lsp-feature? "textDocument/hover")
   4173       (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t)
   4174       (eldoc-mode 1))
   4175 
   4176     (add-hook 'after-change-functions #'lsp-on-change nil t)
   4177     (add-hook 'after-revert-hook #'lsp-on-revert nil t)
   4178     (add-hook 'after-save-hook #'lsp-on-save nil t)
   4179     (add-hook 'auto-save-hook #'lsp--on-auto-save nil t)
   4180     (add-hook 'before-change-functions #'lsp-before-change nil t)
   4181     (add-hook 'before-save-hook #'lsp--before-save nil t)
   4182     (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t)
   4183     (add-hook 'post-command-hook #'lsp--post-command nil t)
   4184 
   4185     (lsp--update-on-type-formatting-hook)
   4186     (lsp--update-signature-help-hook)
   4187 
   4188     (when lsp-enable-xref
   4189       (add-hook 'xref-backend-functions #'lsp--xref-backend nil t))
   4190 
   4191     (lsp-configure-buffer)
   4192 
   4193     ;; make sure we turn off lsp-mode in case major mode changes, because major
   4194     ;; mode change will wipe the buffer locals.
   4195     (add-hook 'change-major-mode-hook #'lsp-disconnect nil t)
   4196     (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t)
   4197 
   4198     (let ((buffer (lsp-current-buffer)))
   4199       (run-with-idle-timer
   4200        0.0 nil
   4201        (lambda ()
   4202          (when (lsp-buffer-live-p buffer)
   4203            (lsp-with-current-buffer buffer
   4204              (lsp--on-change-debounce buffer)
   4205              (lsp--on-idle buffer)))))))
   4206    (t
   4207     (lsp-unconfig-buffer)
   4208 
   4209     (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t)
   4210     (remove-hook 'post-command-hook #'lsp--post-command t)
   4211     (remove-hook 'after-change-functions #'lsp-on-change t)
   4212     (remove-hook 'after-revert-hook #'lsp-on-revert t)
   4213     (remove-hook 'after-save-hook #'lsp-on-save t)
   4214     (remove-hook 'auto-save-hook #'lsp--on-auto-save t)
   4215     (remove-hook 'before-change-functions #'lsp-before-change t)
   4216     (remove-hook 'before-save-hook #'lsp--before-save t)
   4217     (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t)
   4218 
   4219     (lsp--update-on-type-formatting-hook :cleanup)
   4220     (lsp--update-signature-help-hook :cleanup)
   4221 
   4222     (when lsp--on-idle-timer
   4223       (cancel-timer lsp--on-idle-timer)
   4224       (setq lsp--on-idle-timer nil))
   4225 
   4226     (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4227     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4228 
   4229     (lsp--remove-overlays 'lsp-highlight)
   4230     (lsp--remove-overlays 'lsp-links)
   4231 
   4232     (remove-hook 'xref-backend-functions #'lsp--xref-backend t)
   4233     (remove-hook 'change-major-mode-hook #'lsp-disconnect t)
   4234     (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t)
   4235     (setq-local lsp-buffer-uri nil))))
   4236 
   4237 (defun lsp-configure-buffer ()
   4238   "Configure LSP features for current buffer."
   4239   ;; make sure the core is running in the context of all available workspaces
   4240   ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context
   4241   (let ((lsp--buffer-workspaces (cond
   4242                                  (lsp--buffer-workspaces)
   4243                                  (lsp--cur-workspace (list lsp--cur-workspace))))
   4244         lsp--cur-workspace)
   4245     (when lsp-auto-configure
   4246       (lsp--auto-configure)
   4247 
   4248       (when (and lsp-enable-text-document-color
   4249                  (lsp-feature? "textDocument/documentColor"))
   4250         (add-hook 'lsp-on-change-hook #'lsp--document-color nil t))
   4251 
   4252       (when (and lsp-enable-imenu
   4253                  (lsp-feature? "textDocument/documentSymbol"))
   4254         (lsp-enable-imenu))
   4255 
   4256       (when (and lsp-enable-indentation
   4257                  (lsp-feature? "textDocument/rangeFormatting"))
   4258         (add-function :override (local 'indent-region-function) #'lsp-format-region))
   4259 
   4260       (when (and lsp-enable-symbol-highlighting
   4261                  (lsp-feature? "textDocument/documentHighlight"))
   4262         (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t))
   4263 
   4264       (when (and lsp-enable-links
   4265                  (lsp-feature? "textDocument/documentLink"))
   4266         (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t))
   4267 
   4268       (when (and lsp-inlay-hint-enable
   4269                  (lsp-feature? "textDocument/inlayHint"))
   4270         (lsp-inlay-hints-mode))
   4271 
   4272       (when (and lsp-enable-dap-auto-configure
   4273                  (functionp 'dap-mode))
   4274         (dap-auto-configure-mode 1)))
   4275     (run-hooks 'lsp-configure-hook)))
   4276 
   4277 (defun lsp-unconfig-buffer ()
   4278   "Unconfigure LSP features for buffer."
   4279   (lsp--remove-overlays 'lsp-color)
   4280 
   4281   (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function)
   4282     (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   4283     (setq-local imenu-menubar-modified-tick 0)
   4284     (setq-local imenu--index-alist nil)
   4285     (imenu--cleanup))
   4286 
   4287   (remove-function (local 'indent-region-function) #'lsp-format-region)
   4288 
   4289   (remove-hook 'lsp-on-change-hook #'lsp--document-color t)
   4290   (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4291   (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4292 
   4293   (when (and lsp-enable-dap-auto-configure
   4294              (functionp 'dap-mode))
   4295     (dap-auto-configure-mode -1))
   4296 
   4297   (run-hooks 'lsp-unconfigure-hook))
   4298 
   4299 (defun lsp--buffer-content ()
   4300   (lsp-save-restriction-and-excursion
   4301     (or (lsp-virtual-buffer-call :buffer-string)
   4302         (buffer-substring-no-properties (point-min)
   4303                                         (point-max)))))
   4304 
   4305 (defun lsp--text-document-did-open ()
   4306   "`document/didOpen' event."
   4307   (run-hooks 'lsp-before-open-hook)
   4308   (when (and lsp-auto-touch-files
   4309              (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri)))))
   4310     (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri))
   4311     (save-buffer))
   4312 
   4313   (setq lsp--cur-version (or lsp--cur-version 0))
   4314   (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   4315   (lsp-notify
   4316    "textDocument/didOpen"
   4317    (list :textDocument
   4318          (list :uri (lsp--buffer-uri)
   4319                :languageId (lsp-buffer-language)
   4320                :version lsp--cur-version
   4321                :text (lsp--buffer-content))))
   4322 
   4323   (lsp-managed-mode 1)
   4324 
   4325   (lsp-diagnostics--request-pull-diagnostics lsp--cur-workspace)
   4326 
   4327   (run-hooks 'lsp-after-open-hook)
   4328   (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client))))
   4329     (-some-> (lsp--client-after-open-fn client)
   4330       (funcall))
   4331     (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client))
   4332       (intern-soft)
   4333       (run-hooks))))
   4334 
   4335 (defun lsp--text-document-identifier ()
   4336   "Make TextDocumentIdentifier."
   4337   (list :uri (lsp--buffer-uri)))
   4338 
   4339 (defun lsp--versioned-text-document-identifier ()
   4340   "Make VersionedTextDocumentIdentifier."
   4341   (plist-put (lsp--text-document-identifier) :version lsp--cur-version))
   4342 
   4343 (defun lsp--cur-line (&optional point)
   4344   (1- (line-number-at-pos point)))
   4345 
   4346 (defun lsp--cur-position ()
   4347   "Make a Position object for the current point."
   4348   (or (lsp-virtual-buffer-call :cur-position)
   4349       (lsp-save-restriction-and-excursion
   4350         (list :line (lsp--cur-line)
   4351               :character (- (point) (line-beginning-position))))))
   4352 
   4353 (defun lsp--point-to-position (point)
   4354   "Convert POINT to Position."
   4355   (lsp-save-restriction-and-excursion
   4356     (goto-char point)
   4357     (lsp--cur-position)))
   4358 
   4359 (defun lsp--range (start end)
   4360   "Make Range body from START and END."
   4361   ;; make sure start and end are Position objects
   4362   (list :start start :end end))
   4363 
   4364 (defun lsp--region-to-range (start end)
   4365   "Make Range object for the current region."
   4366   (lsp--range (lsp--point-to-position start)
   4367               (lsp--point-to-position end)))
   4368 
   4369 (defun lsp--region-or-line ()
   4370   "The active region or the current line."
   4371   (if (use-region-p)
   4372       (lsp--region-to-range (region-beginning) (region-end))
   4373     (lsp--region-to-range (line-beginning-position) (line-end-position))))
   4374 
   4375 (defun lsp--check-document-changes-version (document-changes)
   4376   "Verify that DOCUMENT-CHANGES have the proper version."
   4377   (unless (seq-every-p
   4378            (-lambda ((&TextDocumentEdit :text-document))
   4379              (or
   4380               (not text-document)
   4381               (let* ((filename (-> text-document
   4382                                    lsp:versioned-text-document-identifier-uri
   4383                                    lsp--uri-to-path))
   4384                      (version (lsp:versioned-text-document-identifier-version? text-document)))
   4385                 (with-current-buffer (find-file-noselect filename)
   4386                   (or (null version) (zerop version) (= -1 version)
   4387                       (equal version lsp--cur-version))))))
   4388            document-changes)
   4389     (error "Document changes cannot be applied due to different document version")))
   4390 
   4391 (defun lsp--apply-workspace-edit (workspace-edit &optional operation)
   4392   "Apply the WorkspaceEdit object WORKSPACE-EDIT.
   4393 OPERATION is symbol representing the source of this text edit."
   4394   (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit))
   4395     (if-let ((document-changes (seq-reverse document-changes?)))
   4396         (progn
   4397           (lsp--check-document-changes-version document-changes)
   4398           (->> document-changes
   4399                (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create")))
   4400                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4401           (->> document-changes
   4402                (seq-filter (-lambda ((&CreateFile :kind))
   4403                              (and (or (not kind) (equal kind "edit"))
   4404                                   (not (equal kind "create")))))
   4405                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4406           (->> document-changes
   4407                (seq-filter (-lambda ((&CreateFile :kind))
   4408                              (and (not (or (not kind) (equal kind "edit")))
   4409                                   (not (equal kind "create")))))
   4410                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))))
   4411       (lsp-map
   4412        (lambda (uri text-edits)
   4413          (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect)
   4414            (lsp--apply-text-edits text-edits operation)))
   4415        changes?))))
   4416 
   4417 (defmacro lsp-with-filename (file &rest body)
   4418   "Execute BODY with FILE as a context.
   4419 Need to handle the case when FILE indicates virtual buffer."
   4420   (declare (indent 1) (debug t))
   4421   `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file)))
   4422        (lsp-with-current-buffer lsp--virtual-buffer
   4423          ,@body)
   4424      ,@body))
   4425 
   4426 (defun lsp--apply-text-document-edit (edit &optional operation)
   4427   "Apply the TextDocumentEdit object EDIT.
   4428 OPERATION is symbol representing the source of this text edit.
   4429 If the file is not being visited by any buffer, it is opened with
   4430 `find-file-noselect'.
   4431 Because lsp-mode does not store previous document versions, the edit is only
   4432 applied if the version of the textDocument matches the version of the
   4433 corresponding file.
   4434 
   4435 interface TextDocumentEdit {
   4436   textDocument: VersionedTextDocumentIdentifier;
   4437   edits: TextEdit[];
   4438 }"
   4439   (pcase (lsp:edit-kind edit)
   4440     ("create" (-let* (((&CreateFile :uri :options?) edit)
   4441                       (file-name (lsp--uri-to-path uri)))
   4442                 (mkdir (f-dirname file-name) t)
   4443                 (f-touch file-name)
   4444                 (when (lsp:create-file-options-overwrite? options?)
   4445                   (f-write-text "" nil file-name))
   4446                 (find-file-noselect file-name)))
   4447     ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit))
   4448                 (f-delete (lsp--uri-to-path uri) recursive?)))
   4449     ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit)
   4450                       (old-file-name (lsp--uri-to-path old-uri))
   4451                       (new-file-name (lsp--uri-to-path new-uri))
   4452                       (buf (find-buffer-visiting old-file-name)))
   4453                 (when buf
   4454                   (lsp-with-current-buffer buf
   4455                     (save-buffer)
   4456                     (lsp--text-document-did-close)))
   4457                 (mkdir (f-dirname new-file-name) t)
   4458                 (rename-file old-file-name new-file-name overwrite?)
   4459                 (when buf
   4460                   (lsp-with-current-buffer buf
   4461                     (set-buffer-modified-p nil)
   4462                     (setq lsp-buffer-uri nil)
   4463                     (set-visited-file-name new-file-name)
   4464                     (lsp)))))
   4465     (_ (let ((file-name (->> edit
   4466                              (lsp:text-document-edit-text-document)
   4467                              (lsp:versioned-text-document-identifier-uri)
   4468                              (lsp--uri-to-path))))
   4469          (lsp-with-current-buffer (find-buffer-visiting file-name)
   4470            (lsp-with-filename file-name
   4471              (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation)))))))
   4472 
   4473 (lsp-defun lsp--position-compare ((&Position :line left-line
   4474                                              :character left-character)
   4475                                   (&Position :line right-line
   4476                                              :character right-character))
   4477   "Return t if position LEFT is greater than RIGHT."
   4478   (if (= left-line right-line)
   4479       (> left-character right-character)
   4480     (> left-line right-line)))
   4481 
   4482 (lsp-defun lsp-point-in-range? (position (&Range :start :end))
   4483   "Returns if POINT is in RANGE."
   4484   (not (or (lsp--position-compare start position)
   4485            (lsp--position-compare position end))))
   4486 
   4487 (lsp-defun lsp--position-equal ((&Position :line left-line
   4488                                            :character left-character)
   4489                                 (&Position :line right-line
   4490                                            :character right-character))
   4491   "Return whether LEFT and RIGHT positions are equal."
   4492   (and (= left-line right-line)
   4493        (= left-character right-character)))
   4494 
   4495 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end))
   4496                                           (&TextEdit :range (&Range :start right-start :end right-end)))
   4497   (if (lsp--position-equal left-start right-start)
   4498       (lsp--position-compare left-end right-end)
   4499     (lsp--position-compare left-start right-start)))
   4500 
   4501 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text))
   4502   "Apply the edits described in the TextEdit object in TEXT-EDIT."
   4503   (setq new-text (s-replace "\r" "" (or new-text "")))
   4504   (lsp:set-text-edit-new-text edit new-text)
   4505   (goto-char start)
   4506   (delete-region start end)
   4507   (insert new-text))
   4508 
   4509 ;; WORKAROUND: typescript-language might send -1 when applying code actions.
   4510 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582
   4511 (lsp-defun lsp--fix-point ((point &as &Position :character :line))
   4512   (-doto point
   4513     (lsp:set-position-line (max 0 line))
   4514     (lsp:set-position-character (max 0 character))))
   4515 
   4516 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as
   4517                                                                &TextEdit
   4518                                                                :range (&Range :start :end)
   4519                                                                :new-text))
   4520   "Apply the edits described in the TextEdit object in TEXT-EDIT.
   4521 The method uses `replace-buffer-contents'."
   4522   (setq new-text (s-replace "\r" "" (or new-text "")))
   4523   (lsp:set-text-edit-new-text edit new-text)
   4524   (-let* ((source (current-buffer))
   4525           ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start)
   4526                                                              :end (lsp--fix-point end)))))
   4527     (with-temp-buffer
   4528       (insert new-text)
   4529       (let ((temp (current-buffer)))
   4530         (with-current-buffer source
   4531           (save-excursion
   4532             (save-restriction
   4533               (narrow-to-region beg end)
   4534 
   4535               ;; On emacs versions < 26.2,
   4536               ;; `replace-buffer-contents' is buggy - it calls
   4537               ;; change functions with invalid arguments - so we
   4538               ;; manually call the change functions here.
   4539               ;;
   4540               ;; See emacs bugs #32237, #32278:
   4541               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237
   4542               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278
   4543               (let ((inhibit-modification-hooks t)
   4544                     (length (- end beg)))
   4545                 (run-hook-with-args 'before-change-functions
   4546                                     beg end)
   4547                 (replace-buffer-contents temp)
   4548                 (run-hook-with-args 'after-change-functions
   4549                                     beg (+ beg (length new-text))
   4550                                     length)))))))))
   4551 
   4552 (defun lsp--to-yasnippet-snippet (snippet)
   4553   "Convert LSP SNIPPET to yasnippet snippet."
   4554   ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it.
   4555   (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`")))
   4556                             (rx "\\" (backref 1))
   4557                             snippet
   4558                             nil nil 1))
   4559 
   4560 (defvar-local lsp-enable-relative-indentation nil
   4561   "Enable relative indentation when insert texts, snippets ...
   4562 from language server.")
   4563 
   4564 (defun lsp--expand-snippet (snippet &optional start end expand-env)
   4565   "Wrapper of `yas-expand-snippet' with all of it arguments.
   4566 The snippet will be convert to LSP style and indent according to
   4567 LSP server result."
   4568   (require 'yasnippet nil t)
   4569   (let* ((inhibit-field-text-motion t)
   4570          (yas-wrap-around-region nil)
   4571          (yas-indent-line 'none)
   4572          (yas-also-auto-indent-first-line nil))
   4573     (yas-expand-snippet
   4574      (lsp--to-yasnippet-snippet snippet)
   4575      start end expand-env)))
   4576 
   4577 (defun lsp--indent-lines (start end &optional insert-text-mode?)
   4578   "Indent from START to END based on INSERT-TEXT-MODE? value.
   4579 - When INSERT-TEXT-MODE? is provided
   4580   - if it's `lsp/insert-text-mode-as-it', do no editor indentation.
   4581   - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading
   4582     whitespaces to match the line where text is inserted.
   4583 - When it's not provided, using `indent-line-function' for each line."
   4584   (save-excursion
   4585     (goto-char end)
   4586     (let* ((end-line (line-number-at-pos))
   4587            (offset (save-excursion
   4588                      (goto-char start)
   4589                      (current-indentation)))
   4590            (indent-line-function
   4591             (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it)
   4592                    #'ignore)
   4593                   ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation)
   4594                        lsp-enable-relative-indentation
   4595                        ;; Indenting snippets is extremely slow in `org-mode' buffers
   4596                        ;; since it has to calculate indentation based on SRC block
   4597                        ;; position.  Thus we use relative indentation as default.
   4598                        (derived-mode-p 'org-mode))
   4599                    (lambda () (save-excursion
   4600                                 (beginning-of-line)
   4601                                 (indent-to-column offset))))
   4602                   (t indent-line-function))))
   4603       (goto-char start)
   4604       (forward-line)
   4605       (while (and (not (eobp))
   4606                   (<= (line-number-at-pos) end-line))
   4607         (funcall indent-line-function)
   4608         (forward-line)))))
   4609 
   4610 (defun lsp--apply-text-edits (edits &optional operation)
   4611   "Apply the EDITS described in the TextEdit[] object.
   4612 OPERATION is symbol representing the source of this text edit."
   4613   (unless (seq-empty-p edits)
   4614     (atomic-change-group
   4615       (run-hooks 'lsp-before-apply-edits-hook)
   4616       (let* ((change-group (prepare-change-group))
   4617              (howmany (length edits))
   4618              (message (format "Applying %s edits to `%s' ..." howmany (current-buffer)))
   4619              (_ (lsp--info message))
   4620              (reporter (make-progress-reporter message 0 howmany))
   4621              (done 0)
   4622              (apply-edit (if (not lsp--virtual-buffer)
   4623                              #'lsp--apply-text-edit-replace-buffer-contents
   4624                            #'lsp--apply-text-edit)))
   4625         (unwind-protect
   4626             (->> edits
   4627                  ;; We sort text edits so as to apply edits that modify latter
   4628                  ;; parts of the document first. Furthermore, because the LSP
   4629                  ;; spec dictates that: "If multiple inserts have the same
   4630                  ;; position, the order in the array defines which edit to
   4631                  ;; apply first."  We reverse the initial list and sort stably
   4632                  ;; to make sure the order among edits with the same position
   4633                  ;; is preserved.
   4634                  (nreverse)
   4635                  (seq-sort #'lsp--text-edit-sort-predicate)
   4636                  (mapc (lambda (edit)
   4637                          (progress-reporter-update reporter (cl-incf done))
   4638                          (funcall apply-edit edit)
   4639                          (when (lsp:snippet-text-edit-insert-text-format? edit)
   4640                            (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start)
   4641                                                          :insert-text-format? :new-text) edit)
   4642                              (when (eq insert-text-format? lsp/insert-text-format-snippet)
   4643                                ;; No `save-excursion' needed since expand snippet will change point anyway
   4644                                (goto-char (+ start (length new-text)))
   4645                                (lsp--indent-lines start (point))
   4646                                (lsp--expand-snippet new-text start (point)))))
   4647                          (run-hook-with-args 'lsp-after-apply-edits-hook operation))))
   4648           (undo-amalgamate-change-group change-group)
   4649           (progress-reporter-done reporter))))))
   4650 
   4651 (defun lsp--create-apply-text-edits-handlers ()
   4652   "Create (handler cleanup-fn) for applying text edits in async request.
   4653 Only works when mode is `tick or `alive."
   4654   (let* (first-edited
   4655          (func (lambda (start &rest _)
   4656                  (setq first-edited (if first-edited
   4657                                         (min start first-edited)
   4658                                       start)))))
   4659     (add-hook 'before-change-functions func nil t)
   4660     (list
   4661      (lambda (edits)
   4662        (if (and first-edited
   4663                 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end)))
   4664                             ;; Text edit region is overlapped
   4665                             (> end first-edited))
   4666                           edits))
   4667            (lsp--warn "TextEdits will not be applied since document has been modified before of them.")
   4668          (lsp--apply-text-edits edits 'completion-cleanup)))
   4669      (lambda ()
   4670        (remove-hook 'before-change-functions func t)))))
   4671 
   4672 (defun lsp--capability (cap &optional capabilities)
   4673   "Get the value of capability CAP.  If CAPABILITIES is non-nil, use them instead."
   4674   (when (stringp cap)
   4675     (setq cap (intern (concat ":" cap))))
   4676 
   4677   (lsp-get (or capabilities
   4678                (lsp--server-capabilities))
   4679            cap))
   4680 
   4681 (defun lsp--registered-capability (method)
   4682   "Check whether there is workspace providing METHOD."
   4683   (->> (lsp-workspaces)
   4684        (--keep (seq-find (lambda (reg)
   4685                            (equal (lsp--registered-capability-method reg) method))
   4686                          (lsp--workspace-registered-server-capabilities it)))
   4687        cl-first))
   4688 
   4689 (defun lsp--capability-for-method (method)
   4690   "Get the value of capability for METHOD."
   4691   (-let* ((reqs (cdr (assoc method lsp-method-requirements)))
   4692           ((&plist :capability) reqs))
   4693     (or (and capability (lsp--capability capability))
   4694         (-some-> (lsp--registered-capability method)
   4695           (lsp--registered-capability-options)))))
   4696 
   4697 (defvar-local lsp--before-change-vals nil
   4698   "Store the positions from the `lsp-before-change' function call, for
   4699 validation and use in the `lsp-on-change' function.")
   4700 
   4701 (defun lsp--text-document-content-change-event (start end length)
   4702   "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH."
   4703   ;; So (47 54 0) means add    7 chars starting at pos 47
   4704   ;; must become
   4705   ;;   {"range":{"start":{"line":5,"character":6}
   4706   ;;             ,"end" :{"line":5,"character":6}}
   4707   ;;             ,"rangeLength":0
   4708   ;;             ,"text":"\nbb = 5"}
   4709   ;;
   4710   ;; And (47 47 7) means delete 7 chars starting at pos 47
   4711   ;; must become
   4712   ;;   {"range":{"start":{"line":6,"character":0}
   4713   ;;            ,"end"  :{"line":7,"character":0}}
   4714   ;;            ,"rangeLength":7
   4715   ;;            ,"text":""}
   4716   ;;
   4717   ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with
   4718   ;; 13 chars. So it must become
   4719   ;;   {"range":{"start":{"line":5,"character":8}
   4720   ;;             ,"end" :{"line":5,"character":11}}
   4721   ;;             ,"rangeLength":3
   4722   ;;             ,"text":"new-chars-xxx"}
   4723   ;;
   4724 
   4725   ;; Adding text:
   4726   ;;   lsp-before-change:(start,end)=(33,33)
   4727   ;;   lsp-on-change:(start,end,length)=(33,34,0)
   4728   ;;
   4729   ;; Changing text:
   4730   ;;   lsp-before-change:(start,end)=(208,211)
   4731   ;;   lsp-on-change:(start,end,length)=(208,221,3)
   4732   ;;
   4733   ;; Deleting text:
   4734   ;;   lsp-before-change:(start,end)=(19,27)
   4735   ;;   lsp-on-change:(start,end,length)=(19,19,8)
   4736   (if (zerop length)
   4737       ;; Adding something only, work from start only
   4738       `( :range ,(lsp--range
   4739                   (lsp--point-to-position start)
   4740                   (lsp--point-to-position start))
   4741          :rangeLength 0
   4742          :text ,(buffer-substring-no-properties start end))
   4743 
   4744     (if (eq start end)
   4745         ;; Deleting something only
   4746         (if (lsp--bracketed-change-p start length)
   4747             ;; The before-change value is bracketed, use it
   4748             `( :range ,(lsp--range
   4749                         (lsp--point-to-position start)
   4750                         (plist-get lsp--before-change-vals :end-pos))
   4751                :rangeLength ,length
   4752                :text "")
   4753           ;; If the change is not bracketed, send a full change event instead.
   4754           (lsp--full-change-event))
   4755 
   4756       ;; Deleting some things, adding others
   4757       (if (lsp--bracketed-change-p start length)
   4758           ;; The before-change value is valid, use it
   4759           `( :range ,(lsp--range
   4760                       (lsp--point-to-position start)
   4761                       (plist-get lsp--before-change-vals :end-pos))
   4762              :rangeLength ,length
   4763              :text ,(buffer-substring-no-properties start end))
   4764         (lsp--full-change-event)))))
   4765 
   4766 (defun lsp--bracketed-change-p (start length)
   4767   "If the before and after positions are the same, and the length
   4768 is the size of the start range, we are probably good."
   4769   (-let [(&plist :end before-end :start before-start) lsp--before-change-vals]
   4770     (and (eq start before-start)
   4771          (eq length (- before-end before-start)))))
   4772 
   4773 (defun lsp--full-change-event ()
   4774   `(:text ,(lsp--buffer-content)))
   4775 
   4776 (defun lsp-before-change (start end)
   4777   "Executed before a file is changed.
   4778 Added to `before-change-functions'."
   4779   ;; Note:
   4780   ;;
   4781   ;; This variable holds a list of functions to call when Emacs is about to
   4782   ;; modify a buffer. Each function gets two arguments, the beginning and end of
   4783   ;; the region that is about to change, represented as integers. The buffer
   4784   ;; that is about to change is always the current buffer when the function is
   4785   ;; called.
   4786   ;;
   4787   ;; WARNING:
   4788   ;;
   4789   ;; Do not expect the before-change hooks and the after-change hooks be called
   4790   ;; in balanced pairs around each buffer change. Also don't expect the
   4791   ;; before-change hooks to be called for every chunk of text Emacs is about to
   4792   ;; delete. These hooks are provided on the assumption that Lisp programs will
   4793   ;; use either before- or the after-change hooks, but not both, and the
   4794   ;; boundaries of the region where the changes happen might include more than
   4795   ;; just the actual changed text, or even lump together several changes done
   4796   ;; piecemeal.
   4797   (save-match-data
   4798     (lsp-save-restriction-and-excursion
   4799       (setq lsp--before-change-vals
   4800             (list :start start
   4801                   :end end
   4802                   :end-pos (lsp--point-to-position end))))))
   4803 
   4804 (defun lsp--flush-delayed-changes ()
   4805   (let ((inhibit-quit t))
   4806     (when lsp--delay-timer
   4807       (cancel-timer lsp--delay-timer))
   4808     (mapc (-lambda ((workspace buffer document change))
   4809             (with-current-buffer buffer
   4810               (with-lsp-workspace workspace
   4811                 (lsp-notify "textDocument/didChange"
   4812                             (list :textDocument document
   4813                                   :contentChanges (vector change))))))
   4814           (prog1 (nreverse lsp--delayed-requests)
   4815             (setq lsp--delayed-requests nil)))))
   4816 
   4817 (defun lsp--workspace-sync-method (workspace)
   4818   (let ((sync (-> workspace
   4819                   (lsp--workspace-server-capabilities)
   4820                   (lsp:server-capabilities-text-document-sync?))))
   4821     (if (lsp-text-document-sync-options? sync)
   4822         (lsp:text-document-sync-options-change? sync)
   4823       sync)))
   4824 
   4825 (defun lsp-on-change (start end length &optional content-change-event-fn)
   4826   "Executed when a file is changed.
   4827 Added to `after-change-functions'."
   4828   ;; Note:
   4829   ;;
   4830   ;; Each function receives three arguments: the beginning and end of the region
   4831   ;; just changed, and the length of the text that existed before the change.
   4832   ;; All three arguments are integers. The buffer that has been changed is
   4833   ;; always the current buffer when the function is called.
   4834   ;;
   4835   ;; The length of the old text is the difference between the buffer positions
   4836   ;; before and after that text as it was before the change. As for the
   4837   ;; changed text, its length is simply the difference between the first two
   4838   ;; arguments.
   4839   ;;
   4840   ;; So (47 54 0) means add    7 chars starting at pos 47
   4841   ;; So (47 47 7) means delete 7 chars starting at pos 47
   4842   (save-match-data
   4843     (let ((inhibit-quit t)
   4844           ;; make sure that `lsp-on-change' is called in multi-workspace context
   4845           ;; see #2901
   4846           lsp--cur-workspace)
   4847       ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done
   4848       ;; by auto-revert-mode) will cause this handler to get called with a nil
   4849       ;; buffer-file-name. We need the buffer-file-name to send notifications;
   4850       ;; so we skip handling revert-buffer-caused changes and instead handle
   4851       ;; reverts separately in lsp-on-revert
   4852       (when (not revert-buffer-in-progress-p)
   4853         (cl-incf lsp--cur-version)
   4854         (mapc
   4855          (lambda (workspace)
   4856            (pcase (or lsp-document-sync-method
   4857                       (lsp--workspace-sync-method workspace))
   4858              (1
   4859               (if lsp-debounce-full-sync-notifications
   4860                   (setq lsp--delayed-requests
   4861                         (->> lsp--delayed-requests
   4862                              (-remove (-lambda ((_ buffer))
   4863                                         (equal (current-buffer) buffer)))
   4864                              (cons (list workspace
   4865                                          (current-buffer)
   4866                                          (lsp--versioned-text-document-identifier)
   4867                                          (lsp--full-change-event)))))
   4868                 (with-lsp-workspace workspace
   4869                   (lsp-notify "textDocument/didChange"
   4870                               (list :contentChanges (vector (lsp--full-change-event))
   4871                                     :textDocument (lsp--versioned-text-document-identifier)))
   4872                   (lsp-diagnostics--request-pull-diagnostics workspace))))
   4873              (2
   4874               (with-lsp-workspace workspace
   4875                 (lsp-notify
   4876                  "textDocument/didChange"
   4877                  (list :textDocument (lsp--versioned-text-document-identifier)
   4878                        :contentChanges (vector
   4879                                         (if content-change-event-fn
   4880                                             (funcall content-change-event-fn start end length)
   4881                                           (lsp--text-document-content-change-event
   4882                                            start end length)))))
   4883                 (lsp-diagnostics--request-pull-diagnostics workspace)))))
   4884          (lsp-workspaces))
   4885         (when lsp--delay-timer (cancel-timer lsp--delay-timer))
   4886         (setq lsp--delay-timer (run-with-idle-timer
   4887                                 lsp-debounce-full-sync-notifications-interval
   4888                                 nil
   4889                                 #'lsp--flush-delayed-changes))
   4890         ;; force cleanup overlays after each change
   4891         (lsp--remove-overlays 'lsp-highlight)
   4892         (lsp--after-change (current-buffer))))))
   4893 
   4894 
   4895 
   4896 ;; facilities for on change hooks. We do not want to make lsp calls on each
   4897 ;; change event so we add debounce to avoid flooding the server with events.
   4898 ;; Additionally, we want to have a mechanism for stopping the server calls in
   4899 ;; particular cases like, e. g. when performing completion.
   4900 
   4901 (defvar lsp-inhibit-lsp-hooks nil
   4902   "Flag to control.")
   4903 
   4904 (defcustom lsp-on-change-hook nil
   4905   "Hooks to run when buffer has changed."
   4906   :type 'hook
   4907   :group 'lsp-mode)
   4908 
   4909 (defcustom lsp-idle-delay 0.500
   4910   "Debounce interval for `after-change-functions'."
   4911   :type 'number
   4912   :group 'lsp-mode)
   4913 
   4914 (defcustom lsp-on-idle-hook nil
   4915   "Hooks to run after `lsp-idle-delay'."
   4916   :type 'hook
   4917   :group 'lsp-mode)
   4918 
   4919 (defun lsp--idle-reschedule (buffer)
   4920   (when lsp--on-idle-timer
   4921     (cancel-timer lsp--on-idle-timer))
   4922 
   4923   (setq lsp--on-idle-timer (run-with-idle-timer
   4924                             lsp-idle-delay
   4925                             nil
   4926                             #'lsp--on-idle
   4927                             buffer)))
   4928 
   4929 (defun lsp--post-command ()
   4930   (lsp--cleanup-highlights-if-needed)
   4931   (lsp--idle-reschedule (current-buffer)))
   4932 
   4933 (defun lsp--on-idle (buffer)
   4934   "Start post command loop."
   4935   (when (and (buffer-live-p buffer)
   4936              (equal buffer (current-buffer))
   4937              (not lsp-inhibit-lsp-hooks)
   4938              lsp-managed-mode)
   4939     (run-hooks 'lsp-on-idle-hook)))
   4940 
   4941 (defun lsp--on-change-debounce (buffer)
   4942   (when (and (buffer-live-p buffer)
   4943              (equal buffer (current-buffer))
   4944              (not lsp-inhibit-lsp-hooks)
   4945              lsp-managed-mode)
   4946     (run-hooks 'lsp-on-change-hook)))
   4947 
   4948 (defun lsp--after-change (buffer)
   4949   "Called after most textDocument/didChange events."
   4950   (setq lsp--signature-last-index nil
   4951         lsp--signature-last nil)
   4952 
   4953   ;; cleanup diagnostics
   4954   (when lsp-diagnostic-clean-after-change
   4955     (dolist (workspace (lsp-workspaces))
   4956       (-let [diagnostics (lsp--workspace-diagnostics workspace)]
   4957         (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics))))
   4958 
   4959   (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled)
   4960     (lsp--semantic-tokens-refresh-if-enabled buffer))
   4961   (when lsp--on-change-timer
   4962     (cancel-timer lsp--on-change-timer))
   4963   (setq lsp--on-change-timer (run-with-idle-timer
   4964                               lsp-idle-delay
   4965                               nil
   4966                               #'lsp--on-change-debounce
   4967                               buffer))
   4968   (lsp--idle-reschedule buffer))
   4969 
   4970 
   4971 (defcustom lsp-trim-trailing-whitespace t
   4972   "Trim trailing whitespace on a line."
   4973   :group 'lsp-mode
   4974   :type 'boolean)
   4975 
   4976 (defcustom lsp-insert-final-newline t
   4977   "Insert a newline character at the end of the file if one does not exist."
   4978   :group 'lsp-mode
   4979   :type 'boolean)
   4980 
   4981 (defcustom lsp-trim-final-newlines t
   4982   "Trim all newlines after the final newline at the end of the file."
   4983   :group 'lsp-mode
   4984   :type 'boolean)
   4985 
   4986 
   4987 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters)
   4988   "Self insert handling.
   4989 Applies on type formatting."
   4990   (let ((ch last-command-event))
   4991     (when (or (eq (string-to-char first-trigger-characters) ch)
   4992               (cl-find ch more-trigger-characters :key #'string-to-char))
   4993       (lsp-request-async "textDocument/onTypeFormatting"
   4994                          (lsp-make-document-on-type-formatting-params
   4995                           :text-document (lsp--text-document-identifier)
   4996                           :options (lsp-make-formatting-options
   4997                                     :tab-size (symbol-value (lsp--get-indent-width major-mode))
   4998                                     :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   4999                                     :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   5000                                     :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   5001                                     :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))
   5002                           :ch (char-to-string ch)
   5003                           :position (lsp--cur-position))
   5004                          (lambda (data) (lsp--apply-text-edits data 'format))
   5005                          :mode 'tick))))
   5006 
   5007 
   5008 ;; links
   5009 (defun lsp--document-links ()
   5010   (when (lsp-feature? "textDocument/documentLink")
   5011     (lsp-request-async
   5012      "textDocument/documentLink"
   5013      `(:textDocument ,(lsp--text-document-identifier))
   5014      (lambda (links)
   5015        (lsp--remove-overlays 'lsp-link)
   5016        (seq-do
   5017         (-lambda ((link &as &DocumentLink :range (&Range :start :end)))
   5018           (-doto (make-button (lsp--position-to-point start)
   5019                               (lsp--position-to-point end)
   5020                               'action (lsp--document-link-keymap link)
   5021                               'keymap (let ((map (make-sparse-keymap)))
   5022                                         (define-key map [M-return] 'push-button)
   5023                                         (define-key map [mouse-2] 'push-button)
   5024                                         map)
   5025                               'help-echo "mouse-2, M-RET: Visit this link")
   5026             (overlay-put 'lsp-link t)))
   5027         links))
   5028      :mode 'unchanged)))
   5029 
   5030 (defun lsp--document-link-handle-target (url)
   5031   (let* ((parsed-url (url-generic-parse-url (url-unhex-string url)))
   5032          (type (url-type parsed-url)))
   5033     (pcase type
   5034       ("file"
   5035        (xref-push-marker-stack)
   5036        (find-file (lsp--uri-to-path url))
   5037        (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url))
   5038          (goto-char (lsp--position-to-point
   5039                      (lsp-make-position :character (1- (string-to-number column))
   5040                                         :line (1- (string-to-number line)))))))
   5041       ((or "http" "https") (browse-url url))
   5042       (type (if-let ((handler (lsp--get-uri-handler type)))
   5043                 (funcall handler url)
   5044               (signal 'lsp-file-scheme-not-supported (list url)))))))
   5045 
   5046 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?))
   5047   (if target?
   5048       (lambda (_)
   5049         (interactive)
   5050         (lsp--document-link-handle-target target?))
   5051     (lambda (_)
   5052       (interactive)
   5053       (when (lsp:document-link-registration-options-resolve-provider?
   5054              (lsp--capability-for-method "textDocument/documentLink"))
   5055         (lsp-request-async
   5056          "documentLink/resolve"
   5057          link
   5058          (-lambda ((&DocumentLink :target?))
   5059            (lsp--document-link-handle-target target?)))))))
   5060 
   5061 
   5062 
   5063 (defcustom lsp-warn-no-matched-clients t
   5064   "Whether to show messages when there are no supported clients."
   5065   :group 'lsp-mode
   5066   :type 'boolean)
   5067 
   5068 (defun lsp-buffer-language--configured-id ()
   5069   "Return nil when not registered."
   5070   (->> lsp-language-id-configuration
   5071        (-first
   5072         (-lambda ((mode-or-pattern . language))
   5073           (cond
   5074            ((and (stringp mode-or-pattern)
   5075                  (s-matches? mode-or-pattern (buffer-file-name)))
   5076             language)
   5077            ((eq mode-or-pattern major-mode) language))))
   5078        cl-rest))
   5079 
   5080 (defvar-local lsp--buffer-language nil
   5081   "Locally cached returned value of `lsp-buffer-language'.")
   5082 
   5083 (defun lsp-buffer-language ()
   5084   "Get language corresponding current buffer."
   5085   (or lsp--buffer-language
   5086       (let* ((configured-language (lsp-buffer-language--configured-id)))
   5087         (setq lsp--buffer-language
   5088               (or configured-language
   5089                   ;; ensure non-nil
   5090                   (string-remove-suffix "-mode" (symbol-name major-mode))))
   5091         (when (and lsp-warn-no-matched-clients
   5092                    (null configured-language))
   5093           (lsp-warn "Unable to calculate the languageId for buffer `%s'. \
   5094 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s"
   5095                     (buffer-name)
   5096                     major-mode))
   5097         lsp--buffer-language)))
   5098 
   5099 (defun lsp-activate-on (&rest languages)
   5100   "Returns language activation function.
   5101 The function will return t when the `lsp-buffer-language' returns
   5102 one of the LANGUAGES."
   5103   (lambda (_file-name _mode)
   5104     (-contains? languages (lsp-buffer-language))))
   5105 
   5106 (defun lsp-workspace-root (&optional path)
   5107   "Find the workspace root for the current file or PATH."
   5108   (-when-let* ((file-name (or path (buffer-file-name)))
   5109                (file-name (lsp-f-canonical file-name)))
   5110     (->> (lsp-session)
   5111          (lsp-session-folders)
   5112          (--filter (and (lsp--files-same-host it file-name)
   5113                         (or (lsp-f-ancestor-of? it file-name)
   5114                             (equal it file-name))))
   5115          (--max-by (> (length it) (length other))))))
   5116 
   5117 (defun lsp-on-revert ()
   5118   "Executed when a file is reverted.
   5119 Added to `after-revert-hook'."
   5120   (let ((n (buffer-size))
   5121         (revert-buffer-in-progress-p nil))
   5122     (lsp-on-change 0 n n)))
   5123 
   5124 (defun lsp--text-document-did-close (&optional keep-workspace-alive)
   5125   "Executed when the file is closed, added to `kill-buffer-hook'.
   5126 
   5127 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace
   5128 if it's closing the last buffer in the workspace."
   5129   (lsp-foreach-workspace
   5130    (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   5131    (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S"
   5132      (lsp-notify "textDocument/didClose"
   5133                  `(:textDocument ,(lsp--text-document-identifier))))
   5134    (when (and (not lsp-keep-workspace-alive)
   5135               (not keep-workspace-alive)
   5136               (not (lsp--workspace-buffers lsp--cur-workspace)))
   5137      (lsp--shutdown-workspace))))
   5138 
   5139 (defun lsp--will-save-text-document-params (reason)
   5140   (list :textDocument (lsp--text-document-identifier)
   5141         :reason reason))
   5142 
   5143 (defun lsp--before-save ()
   5144   "Before save handler."
   5145   (with-demoted-errors "Error in ‘lsp--before-save’: %S"
   5146     (let ((params (lsp--will-save-text-document-params 1)))
   5147       (when (lsp--send-will-save-p)
   5148         (lsp-notify "textDocument/willSave" params))
   5149       (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits)
   5150         (let ((lsp-response-timeout 0.1))
   5151           (condition-case nil
   5152               (lsp--apply-text-edits
   5153                (lsp-request "textDocument/willSaveWaitUntil"
   5154                             params)
   5155                'before-save)
   5156             (error)))))))
   5157 
   5158 (defun lsp--on-auto-save ()
   5159   "Handler for auto-save."
   5160   (when (lsp--send-will-save-p)
   5161     (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S"
   5162       (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2)))))
   5163 
   5164 (defun lsp--text-document-did-save ()
   5165   "Executed when the file is closed, added to `after-save-hook''."
   5166   (when (lsp--send-did-save-p)
   5167     (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’"
   5168       (lsp-notify "textDocument/didSave"
   5169                   `( :textDocument ,(lsp--versioned-text-document-identifier)
   5170                      ,@(when (lsp--save-include-text-p)
   5171                          (list :text (lsp--buffer-content))))))))
   5172 
   5173 (defun lsp--text-document-position-params (&optional identifier position)
   5174   "Make TextDocumentPositionParams for the current point in the current document.
   5175 If IDENTIFIER and POSITION are non-nil, they will be used as the document
   5176 identifier and the position respectively."
   5177   (list :textDocument (or identifier (lsp--text-document-identifier))
   5178         :position (or position (lsp--cur-position))))
   5179 
   5180 (defun lsp--get-buffer-diagnostics ()
   5181   "Return buffer diagnostics."
   5182   (gethash (or
   5183             (plist-get lsp--virtual-buffer :buffer-file-name)
   5184             (lsp--fix-path-casing (buffer-file-name)))
   5185            (lsp-diagnostics t)))
   5186 
   5187 (defun lsp-cur-line-diagnostics ()
   5188   "Return any diagnostics that apply to the current line."
   5189   (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)]
   5190     (cl-coerce (-filter
   5191                 (-lambda ((&Diagnostic :range (&Range :start (&Position :line))))
   5192                   (and (>= line start) (<= line end)))
   5193                 (lsp--get-buffer-diagnostics))
   5194                'vector)))
   5195 
   5196 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end)
   5197                                   (right &as &Range :start right-start :end right-end))
   5198   (or (lsp-point-in-range? right-start left)
   5199       (lsp-point-in-range? right-end left)
   5200       (lsp-point-in-range? left-start right)
   5201       (lsp-point-in-range? left-end right)))
   5202 
   5203 (defun lsp-make-position-1 (position)
   5204   (lsp-make-position :line (plist-get position :line)
   5205                      :character (plist-get position :character)))
   5206 
   5207 (defun lsp-cur-possition-diagnostics ()
   5208   "Return any diagnostics that apply to the current line."
   5209   (-let* ((start (if (use-region-p) (region-beginning) (point)))
   5210           (end (if (use-region-p) (region-end) (point)))
   5211           (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start))
   5212                                          :end (lsp-make-position-1 (lsp-point-to-position end)))))
   5213     (->> (lsp--get-buffer-diagnostics)
   5214          (-filter
   5215           (-lambda ((&Diagnostic :range))
   5216             (lsp-range-overlapping? range current-range)))
   5217          (apply 'vector))))
   5218 
   5219 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics)
   5220 
   5221 (defun lsp--extract-line-from-buffer (pos)
   5222   "Return the line pointed to by POS (a Position object) in the current buffer."
   5223   (let* ((point (lsp--position-to-point pos))
   5224          (inhibit-field-text-motion t))
   5225     (save-excursion
   5226       (goto-char point)
   5227       (buffer-substring (line-beginning-position) (line-end-position)))))
   5228 
   5229 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line)
   5230                                                  :end (end &as &Position :character end-char)))
   5231   "Return a xref-item from a RANGE in FILENAME."
   5232   (let* ((line (lsp--extract-line-from-buffer start))
   5233          (len (length line)))
   5234     (add-face-text-property (max (min start-char len) 0)
   5235                             (max (min end-char len) 0)
   5236                             'xref-match t line)
   5237     ;; LINE is nil when FILENAME is not being current visited by any buffer.
   5238     (xref-make-match (or line filename)
   5239                      (xref-make-file-location
   5240                       filename
   5241                       (lsp-translate-line (1+ start-line))
   5242                       (lsp-translate-column start-char))
   5243                      (- end-char start-char))))
   5244 
   5245 (defun lsp--location-uri (loc)
   5246   (if (lsp-location? loc)
   5247       (lsp:location-uri loc)
   5248     (lsp:location-link-target-uri loc)))
   5249 
   5250 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start)))
   5251   "Go to location."
   5252   (let ((path (lsp--uri-to-path uri)))
   5253     (if (f-exists? path)
   5254         (with-current-buffer (find-file path)
   5255           (goto-char (lsp--position-to-point start)))
   5256       (error "There is no file %s" path))))
   5257 
   5258 (defun lsp--location-range (loc)
   5259   (if (lsp-location? loc)
   5260       (lsp:location-range loc)
   5261     (lsp:location-link-target-selection-range loc)))
   5262 
   5263 (defun lsp--locations-to-xref-items (locations)
   5264   "Return a list of `xref-item' given LOCATIONS, which can be of
   5265 type Location, LocationLink, Location[] or LocationLink[]."
   5266   (setq locations
   5267         (pcase locations
   5268           ((seq (or (lsp-interface Location)
   5269                     (lsp-interface LocationLink)))
   5270            (append locations nil))
   5271           ((or (lsp-interface Location)
   5272                (lsp-interface LocationLink))
   5273            (list locations))))
   5274 
   5275   (cl-labels ((get-xrefs-in-file
   5276                (file-locs)
   5277                (-let [(filename . matches) file-locs]
   5278                  (condition-case err
   5279                      (let ((visiting (find-buffer-visiting filename))
   5280                            (fn (lambda (loc)
   5281                                  (lsp-with-filename filename
   5282                                    (lsp--xref-make-item filename
   5283                                                         (lsp--location-range loc))))))
   5284                        (if visiting
   5285                            (with-current-buffer visiting
   5286                              (seq-map fn matches))
   5287                          (when (file-readable-p filename)
   5288                            (with-temp-buffer
   5289                              (insert-file-contents-literally filename)
   5290                              (seq-map fn matches)))))
   5291                    (error (lsp-warn "Failed to process xref entry for filename '%s': %s"
   5292                                     filename (error-message-string err)))
   5293                    (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s"
   5294                                          filename (error-message-string err)))))))
   5295 
   5296     (->> locations
   5297          (seq-sort #'lsp--location-before-p)
   5298          (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri))
   5299          (seq-map #'get-xrefs-in-file)
   5300          (apply #'nconc))))
   5301 
   5302 (defun lsp--location-before-p (left right)
   5303   "Sort first by file, then by line, then by column."
   5304   (let ((left-uri (lsp--location-uri left))
   5305         (right-uri (lsp--location-uri right)))
   5306     (if (not (string= left-uri right-uri))
   5307         (string< left-uri right-uri)
   5308       (-let (((&Range :start left-start) (lsp--location-range left))
   5309              ((&Range :start right-start) (lsp--location-range right)))
   5310         (lsp--position-compare right-start left-start)))))
   5311 
   5312 (defun lsp--make-reference-params (&optional td-position exclude-declaration)
   5313   "Make a ReferenceParam object.
   5314 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead.
   5315 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations."
   5316   (let ((json-false :json-false))
   5317     (plist-put (or td-position (lsp--text-document-position-params))
   5318                :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration))))))
   5319 
   5320 (defun lsp--cancel-request (id)
   5321   "Cancel request with ID in all workspaces."
   5322   (lsp-foreach-workspace
   5323    (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id))
   5324    (lsp-notify "$/cancelRequest" `(:id ,id))))
   5325 
   5326 (defvar-local lsp--hover-saved-bounds nil)
   5327 
   5328 (defun lsp-eldoc-function (cb &rest _ignored)
   5329   "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')."
   5330   (if (and lsp--hover-saved-bounds
   5331            (lsp--point-in-bounds-p lsp--hover-saved-bounds))
   5332       lsp--eldoc-saved-message
   5333     (setq lsp--hover-saved-bounds nil
   5334           lsp--eldoc-saved-message nil)
   5335     (if (looking-at-p "[[:space:]\n]")
   5336         (setq lsp--eldoc-saved-message nil) ; And returns nil.
   5337       (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover"))
   5338         (lsp-request-async
   5339          "textDocument/hover"
   5340          (lsp--text-document-position-params)
   5341          (-lambda ((hover &as &Hover? :range? :contents))
   5342            (setq lsp--hover-saved-bounds (when range?
   5343                                            (lsp--range-to-region range?)))
   5344            (funcall cb (setq lsp--eldoc-saved-message
   5345                              (when contents
   5346                                (lsp--render-on-hover-content
   5347                                 contents
   5348                                 lsp-eldoc-render-all)))))
   5349          :error-handler #'ignore
   5350          :mode 'tick
   5351          :cancel-token :eldoc-hover)))))
   5352 
   5353 (defun lsp--point-on-highlight? ()
   5354   (-some? (lambda (overlay)
   5355             (overlay-get overlay 'lsp-highlight))
   5356           (overlays-at (point))))
   5357 
   5358 (defun lsp--cleanup-highlights-if-needed ()
   5359   (when (and lsp-enable-symbol-highlighting
   5360              lsp--have-document-highlights
   5361              (not (lsp--point-on-highlight?)))
   5362     (lsp--remove-overlays 'lsp-highlight)
   5363     (setq lsp--have-document-highlights nil)
   5364     (lsp-cancel-request-by-token :highlights)))
   5365 
   5366 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil
   5367   "The bounds of the symbol from which `lsp--document-highlight'
   5368   most recently requested highlights.")
   5369 
   5370 (defun lsp--document-highlight ()
   5371   (when (lsp-feature? "textDocument/documentHighlight")
   5372     (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol)))
   5373       (unless (or (looking-at-p "[[:space:]\n]")
   5374                   (not lsp-enable-symbol-highlighting)
   5375                   (and lsp--have-document-highlights
   5376                        curr-sym-bounds
   5377                        (equal curr-sym-bounds
   5378                               lsp--symbol-bounds-of-last-highlight-invocation)))
   5379         (setq lsp--symbol-bounds-of-last-highlight-invocation
   5380               curr-sym-bounds)
   5381         (lsp-request-async "textDocument/documentHighlight"
   5382                            (lsp--text-document-position-params)
   5383                            #'lsp--document-highlight-callback
   5384                            :mode 'tick
   5385                            :cancel-token :highlights)))))
   5386 
   5387 (defun lsp--help-open-link (&rest _)
   5388   "Open markdown link at point via mouse or keyboard."
   5389   (interactive "P")
   5390   (let ((buffer-list-update-hook nil))
   5391     (-let [(buffer point) (if-let* ((valid (and (listp last-input-event)
   5392                                                 (eq (car last-input-event) 'mouse-2)))
   5393                                     (event (cadr last-input-event))
   5394                                     (win (posn-window event))
   5395                                     (buffer (window-buffer win)))
   5396                               `(,buffer ,(posn-point event))
   5397                             `(,(current-buffer) ,(point)))]
   5398       (with-current-buffer buffer
   5399         (when-let* ((face (get-text-property point 'face))
   5400                     (url (or (and (eq face 'markdown-link-face)
   5401                                   (get-text-property point 'help-echo))
   5402                              (and (memq face '(markdown-url-face markdown-plain-url-face))
   5403                                   (nth 3 (markdown-link-at-pos point))))))
   5404           (lsp--document-link-handle-target url))))))
   5405 
   5406 (defvar lsp-help-mode-map
   5407   (-doto (make-sparse-keymap)
   5408     (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link))
   5409   "Keymap for `lsp-help-mode'.")
   5410 
   5411 (define-derived-mode lsp-help-mode help-mode "LspHelp"
   5412   "Major mode for displaying lsp help.")
   5413 
   5414 (defun lsp-describe-thing-at-point ()
   5415   "Display the type signature and documentation of the thing at point."
   5416   (interactive)
   5417   (let ((contents (-some->> (lsp--text-document-position-params)
   5418                     (lsp--make-request "textDocument/hover")
   5419                     (lsp--send-request)
   5420                     (lsp:hover-contents))))
   5421     (if (and contents (not (equal contents "")))
   5422         (let ((lsp-help-buf-name "*lsp-help*"))
   5423           (with-current-buffer (get-buffer-create lsp-help-buf-name)
   5424             (delay-mode-hooks
   5425               (lsp-help-mode)
   5426               (with-help-window lsp-help-buf-name
   5427                 (insert
   5428 		 (mapconcat 'string-trim-right
   5429 			    (split-string (lsp--render-on-hover-content contents t) "\n")
   5430 			    "\n"))))
   5431             (run-mode-hooks)))
   5432       (lsp--info "No content at point."))))
   5433 
   5434 (defun lsp--point-in-bounds-p (bounds)
   5435   "Return whether the current point is within BOUNDS."
   5436   (and (<= (car bounds) (point)) (< (point) (cdr bounds))))
   5437 
   5438 (defun lsp-get-renderer (language)
   5439   "Get renderer for LANGUAGE."
   5440   (lambda (str)
   5441     (lsp--render-string str language)))
   5442 
   5443 (defun lsp--setup-markdown (mode)
   5444   "Setup the ‘markdown-mode’ in the frame.
   5445 MODE is the mode used in the parent frame."
   5446   (make-local-variable 'markdown-code-lang-modes)
   5447   (dolist (mark (alist-get mode lsp-custom-markup-modes))
   5448     (add-to-list 'markdown-code-lang-modes (cons mark mode)))
   5449   (setq-local markdown-fontify-code-blocks-natively t)
   5450   (setq-local markdown-fontify-code-block-default-mode mode)
   5451   (setq-local markdown-hide-markup t)
   5452 
   5453   ;; Render some common HTML entities.
   5454   ;; This should really happen in markdown-mode instead,
   5455   ;; but it doesn't, so we do it here for now.
   5456   (setq prettify-symbols-alist
   5457         (cl-loop for i from 0 to 255
   5458                  collect (cons (format "&#x%02X;" i) i)))
   5459   (push '("&lt;" . ?<) prettify-symbols-alist)
   5460   (push '("&gt;" . ?>) prettify-symbols-alist)
   5461   (push '("&amp;" . ?&) prettify-symbols-alist)
   5462   (push '("&nbsp;" . ? ) prettify-symbols-alist)
   5463   (setq prettify-symbols-compose-predicate
   5464         (lambda (_start _end _match) t))
   5465   (prettify-symbols-mode 1))
   5466 
   5467 (defvar lsp-help-link-keymap
   5468   (let ((map (make-sparse-keymap)))
   5469     (define-key map [mouse-2] #'lsp--help-open-link)
   5470     (define-key map "\r" #'lsp--help-open-link)
   5471     map)
   5472   "Keymap active on links in *lsp-help* mode.")
   5473 
   5474 (defun lsp--fix-markdown-links ()
   5475   (let ((inhibit-read-only t)
   5476         (inhibit-modification-hooks t)
   5477         (prop))
   5478     (save-restriction
   5479       (goto-char (point-min))
   5480       (while (setq prop (markdown-find-next-prop 'face))
   5481         (let ((end (or (next-single-property-change (car prop) 'face)
   5482                        (point-max))))
   5483           (when (memq (get-text-property (car prop) 'face)
   5484                       '(markdown-link-face
   5485                         markdown-url-face
   5486                         markdown-plain-url-face))
   5487             (add-text-properties (car prop) end
   5488                                  (list 'button t
   5489                                        'category 'lsp-help-link
   5490                                        'follow-link t
   5491                                        'keymap lsp-help-link-keymap)))
   5492           (goto-char end))))))
   5493 
   5494 (defun lsp--buffer-string-visible ()
   5495   "Return visible buffer string.
   5496 Stolen from `org-copy-visible'."
   5497   (let ((temp (generate-new-buffer " *temp*"))
   5498         (beg (point-min))
   5499         (end (point-max)))
   5500     (while (/= beg end)
   5501       (when (get-char-property beg 'invisible)
   5502         (setq beg (next-single-char-property-change beg 'invisible nil end)))
   5503       (let* ((next (next-single-char-property-change beg 'invisible nil end))
   5504              (substring (buffer-substring beg next)))
   5505         (with-current-buffer temp (insert substring))
   5506         ;; (setq result (concat result substring))
   5507         (setq beg next)))
   5508     (setq deactivate-mark t)
   5509     (prog1 (with-current-buffer temp
   5510              (s-chop-suffix "\n" (buffer-string)))
   5511       (kill-buffer temp))))
   5512 
   5513 (defvar lsp-buffer-major-mode nil
   5514   "Holds the major mode when fontification function is running.
   5515 See #2588")
   5516 
   5517 (defvar view-inhibit-help-message)
   5518 
   5519 (defun lsp--render-markdown ()
   5520   "Render markdown."
   5521 
   5522   (let ((markdown-enable-math nil))
   5523     (goto-char (point-min))
   5524     (while (re-search-forward
   5525             (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/"
   5526                                      "{" "}" "[" "]" "(" ")"
   5527                                      "#" "+" "-" "." "!" "|"))))
   5528             nil t)
   5529       (replace-match (rx (backref 1))))
   5530 
   5531     ;; markdown-mode v2.3 does not yet provide gfm-view-mode
   5532     (if (fboundp 'gfm-view-mode)
   5533         (let ((view-inhibit-help-message t))
   5534           (gfm-view-mode))
   5535       (gfm-mode))
   5536 
   5537     (lsp--setup-markdown lsp-buffer-major-mode)))
   5538 
   5539 (defvar lsp--display-inline-image-alist
   5540   '((lsp--render-markdown
   5541      (:regexp
   5542       "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)"
   5543       :sexp
   5544       (create-image
   5545        (base64-decode-string
   5546         (buffer-substring-no-properties (match-beginning 1) (match-end 1)))
   5547        nil t))))
   5548   "Replaced string regexp and function returning image.
   5549 Each element should have the form (MODE . (PROPERTY-LIST...)).
   5550 MODE (car) is function which is defined in `lsp-language-id-configuration'.
   5551 Cdr should be list of PROPERTY-LIST.
   5552 
   5553 Each PROPERTY-LIST should have properties:
   5554 :regexp  Regexp which determines what string is relpaced to image.
   5555          You should also get information of image, by parenthesis constructs.
   5556          By default, all matched string is replaced to image, but you can
   5557          change index of replaced string by keyword :replaced-index.
   5558 
   5559 :sexp    Return image when evaluated. You can use information of regexp
   5560          by using (match-beggining N), (match-end N) or (match-substring N).
   5561 
   5562 In addition, each can have property:
   5563 :replaced-index  Determine index which is used to replace regexp to image.
   5564                  The value means first argument of `match-beginning' and
   5565                  `match-end'. If omitted, interpreted as index 0.")
   5566 
   5567 (defcustom lsp-display-inline-image t
   5568   "Showing inline image or not."
   5569   :group 'lsp-mode
   5570   :type 'boolean)
   5571 
   5572 (defcustom lsp-enable-suggest-server-download t
   5573   "When non-nil enable server downloading suggestions."
   5574   :group 'lsp-mode
   5575   :type 'boolean
   5576   :package-version '(lsp-mode . "9.0.0"))
   5577 
   5578 (defcustom lsp-auto-register-remote-clients t
   5579   "When non-nil register remote when registering the local one."
   5580   :group 'lsp-mode
   5581   :type 'boolean
   5582   :package-version '(lsp-mode . "9.0.0"))
   5583 
   5584 (defun lsp--display-inline-image (mode)
   5585   "Add image property if available."
   5586   (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist))))
   5587     (when (and (display-images-p) lsp-display-inline-image)
   5588       (cl-loop
   5589        for plist in plist-list
   5590        with regexp with replaced-index
   5591        do
   5592        (setq regexp (plist-get plist :regexp))
   5593        (setq replaced-index (or (plist-get plist :replaced-index) 0))
   5594 
   5595        (font-lock-remove-keywords nil (list regexp replaced-index))
   5596        (let ((inhibit-read-only t))
   5597          (save-excursion
   5598            (goto-char (point-min))
   5599            (while (re-search-forward regexp nil t)
   5600              (set-text-properties
   5601               (match-beginning replaced-index) (match-end replaced-index)
   5602               nil)
   5603              (add-text-properties
   5604               (match-beginning replaced-index) (match-end replaced-index)
   5605               `(display ,(eval (plist-get plist :sexp)))))))))))
   5606 
   5607 (defun lsp--fontlock-with-mode (str mode)
   5608   "Fontlock STR with MODE."
   5609   (let ((lsp-buffer-major-mode major-mode))
   5610     (with-temp-buffer
   5611       (with-demoted-errors "Error during doc rendering: %s"
   5612         (insert str)
   5613         (delay-mode-hooks (funcall mode))
   5614         (cl-flet ((window-body-width () lsp-window-body-width))
   5615           ;; This can go wrong in some cases, and the fontification would
   5616           ;; not work as expected.
   5617           ;;
   5618           ;; See #2984
   5619           (ignore-errors (font-lock-ensure))
   5620           (lsp--display-inline-image mode)
   5621           (when (eq mode 'lsp--render-markdown)
   5622             (lsp--fix-markdown-links))))
   5623       (lsp--buffer-string-visible))))
   5624 
   5625 (defun lsp--render-string (str language)
   5626   "Render STR using `major-mode' corresponding to LANGUAGE.
   5627 When language is nil render as markup if `markdown-mode' is loaded."
   5628   (setq str (s-replace "\r" "" (or str "")))
   5629   (if-let* ((modes (-keep (-lambda ((mode . lang))
   5630                             (when (and (equal lang language) (functionp mode))
   5631                               mode))
   5632                           lsp-language-id-configuration))
   5633             (mode (car (or (member major-mode modes) modes))))
   5634       (lsp--fontlock-with-mode str mode)
   5635     str))
   5636 
   5637 (defun lsp--render-element (content)
   5638   "Render CONTENT element."
   5639   (let ((inhibit-message t))
   5640     (or
   5641      (pcase content
   5642        ((lsp-interface MarkedString :value :language)
   5643         (lsp--render-string value language))
   5644        ((lsp-interface MarkupContent :value :kind)
   5645         (lsp--render-string value kind))
   5646        ;; plain string
   5647        ((pred stringp) (lsp--render-string content "markdown"))
   5648        ((pred null) "")
   5649        (_ (error "Failed to handle %s" content)))
   5650      "")))
   5651 
   5652 (defun lsp--create-unique-string-fn ()
   5653   (let (elements)
   5654     (lambda (element)
   5655       (let ((count (cl-count element elements :test #'string=)))
   5656         (prog1 (if (zerop count)
   5657                    element
   5658                  (format "%s (%s)" element count))
   5659           (push element elements))))))
   5660 
   5661 (defun lsp--select-action (actions)
   5662   "Select an action to execute from ACTIONS."
   5663   (cond
   5664    ((seq-empty-p actions) (signal 'lsp-no-code-actions nil))
   5665    ((and (eq (seq-length actions) 1) lsp-auto-execute-action)
   5666     (lsp-seq-first actions))
   5667    (t (let ((completion-ignore-case t))
   5668         (lsp--completing-read "Select code action: "
   5669                               (seq-into actions 'list)
   5670                               (-compose (lsp--create-unique-string-fn)
   5671                                         #'lsp:code-action-title)
   5672                               nil t)))))
   5673 
   5674 (defun lsp--workspace-server-id (workspace)
   5675   "Return the server ID of WORKSPACE."
   5676   (-> workspace lsp--workspace-client lsp--client-server-id))
   5677 
   5678 (defun lsp--handle-rendered-for-echo-area (contents)
   5679   "Return a single line from RENDERED, appropriate for display in the echo area."
   5680   (pcase (lsp-workspaces)
   5681     (`(,workspace)
   5682      (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace)))
   5683     ;; For projects with multiple active workspaces we also default to
   5684     ;; render the first line.
   5685     (_ (lsp-clients-extract-signature-on-hover contents nil))))
   5686 
   5687 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id)
   5688   "Extract a representative line from CONTENTS, to show in the echo area."
   5689   (car (s-lines (s-trim (lsp--render-element contents)))))
   5690 
   5691 (defun lsp--render-on-hover-content (contents render-all)
   5692   "Render the content received from `document/onHover' request.
   5693 CONTENTS  - MarkedString | MarkedString[] | MarkupContent
   5694 RENDER-ALL - nil if only the signature should be rendered."
   5695   (cond
   5696    ((lsp-markup-content? contents)
   5697     ;; MarkupContent.
   5698     ;; It tends to be long and is not suitable to display fully in the echo area.
   5699     ;; Just display the first line which is typically the signature.
   5700     (if render-all
   5701         (lsp--render-element contents)
   5702       (lsp--handle-rendered-for-echo-area contents)))
   5703    ((and (stringp contents) (not (string-match-p "\n" contents)))
   5704     ;; If the contents is a single string containing a single line,
   5705     ;; render it always.
   5706     (lsp--render-element contents))
   5707    (t
   5708     ;; MarkedString -> MarkedString[]
   5709     (when (or (lsp-marked-string? contents) (stringp contents))
   5710       (setq contents (list contents)))
   5711     ;; Consider the signature consisting of the elements who have a renderable
   5712     ;; "language" property. When render-all is nil, ignore other elements.
   5713     (string-join
   5714      (seq-map
   5715       #'lsp--render-element
   5716       (if render-all
   5717           contents
   5718         ;; Only render contents that have an available renderer.
   5719         (seq-take
   5720          (seq-filter
   5721           (-andfn #'lsp-marked-string?
   5722                   (-compose #'lsp-get-renderer #'lsp:marked-string-language))
   5723           contents)
   5724          1)))
   5725      (if (bound-and-true-p page-break-lines-mode)
   5726          "\n\n"
   5727        "\n")))))
   5728 
   5729 
   5730 
   5731 (defvar lsp-signature-mode-map
   5732   (-doto (make-sparse-keymap)
   5733     (define-key (kbd "M-n") #'lsp-signature-next)
   5734     (define-key (kbd "M-p") #'lsp-signature-previous)
   5735     (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs)
   5736     (define-key (kbd "C-c C-k") #'lsp-signature-stop)
   5737     (define-key (kbd "C-g") #'lsp-signature-stop))
   5738   "Keymap for `lsp-signature-mode'.")
   5739 
   5740 (define-minor-mode lsp-signature-mode
   5741   "Mode used to show signature popup."
   5742   :keymap lsp-signature-mode-map
   5743   :lighter ""
   5744   :group 'lsp-mode)
   5745 
   5746 (defun lsp-signature-stop ()
   5747   "Stop showing current signature help."
   5748   (interactive)
   5749   (lsp-cancel-request-by-token :signature)
   5750   (remove-hook 'post-command-hook #'lsp-signature)
   5751   (funcall lsp-signature-function nil)
   5752   (lsp-signature-mode -1))
   5753 
   5754 (declare-function page-break-lines--update-display-tables "ext:page-break-lines")
   5755 
   5756 (defun lsp--setup-page-break-mode-if-present ()
   5757   "Enable `page-break-lines-mode' in current buffer."
   5758   (when (fboundp 'page-break-lines-mode)
   5759     (page-break-lines-mode)
   5760     ;; force page-break-lines-mode to update the display tables.
   5761     (page-break-lines--update-display-tables)))
   5762 
   5763 (defun lsp-lv-message (message)
   5764   (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)
   5765   (if message
   5766       (progn
   5767         (setq lsp--signature-last-buffer (current-buffer))
   5768         (let ((lv-force-update t))
   5769           (lv-message "%s" message)))
   5770     (lv-delete-window)
   5771     (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)))
   5772 
   5773 (declare-function posframe-show "ext:posframe")
   5774 (declare-function posframe-hide "ext:posframe")
   5775 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe")
   5776 
   5777 (defface lsp-signature-posframe
   5778   '((t :inherit tooltip))
   5779   "Background and foreground for `lsp-signature-posframe'."
   5780   :group 'lsp-mode)
   5781 
   5782 (defvar lsp-signature-posframe-params
   5783   (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward
   5784         :height 10
   5785         :width 60
   5786         :border-width 1
   5787         :min-width 60)
   5788   "Params for signature and `posframe-show'.")
   5789 
   5790 (defun lsp-signature-posframe (str)
   5791   "Use posframe to show the STR signatureHelp string."
   5792   (if str
   5793       (apply #'posframe-show
   5794              (with-current-buffer (get-buffer-create " *lsp-signature*")
   5795                (erase-buffer)
   5796                (insert str)
   5797                (visual-line-mode 1)
   5798                (lsp--setup-page-break-mode-if-present)
   5799                (current-buffer))
   5800              (append
   5801               lsp-signature-posframe-params
   5802               (list :position (point)
   5803                     :background-color (face-attribute 'lsp-signature-posframe :background nil t)
   5804                     :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t)
   5805                     :border-color (face-attribute (if (facep 'child-frame-border)
   5806                                                       'child-frame-border
   5807                                                     'internal-border)
   5808                                                   :background nil t))))
   5809     (posframe-hide " *lsp-signature*")))
   5810 
   5811 (defun lsp--handle-signature-update (signature)
   5812   (let ((message
   5813          (if (lsp-signature-help? signature)
   5814              (lsp--signature->message signature)
   5815            (mapconcat #'lsp--signature->message signature "\n"))))
   5816     (if (s-present? message)
   5817         (funcall lsp-signature-function message)
   5818       (lsp-signature-stop))))
   5819 
   5820 (defun lsp-signature-activate ()
   5821   "Activate signature help.
   5822 It will show up only if current point has signature help."
   5823   (interactive)
   5824   (setq lsp--signature-last nil
   5825         lsp--signature-last-index nil
   5826         lsp--signature-last-buffer (current-buffer))
   5827   (add-hook 'post-command-hook #'lsp-signature)
   5828   (lsp-signature-mode t))
   5829 
   5830 (defcustom lsp-signature-cycle t
   5831   "Whether `lsp-signature-next' and prev should cycle."
   5832   :type 'boolean
   5833   :group 'lsp-mode)
   5834 
   5835 (defun lsp-signature-next ()
   5836   "Show next signature."
   5837   (interactive)
   5838   (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last))))
   5839     (when (and lsp--signature-last-index
   5840                lsp--signature-last
   5841                (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs)))
   5842       (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs))
   5843       (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))))
   5844 
   5845 (defun lsp-signature-previous ()
   5846   "Next signature."
   5847   (interactive)
   5848   (when (and lsp--signature-last-index
   5849              lsp--signature-last
   5850              (or lsp-signature-cycle (not (zerop lsp--signature-last-index))))
   5851     (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index)
   5852                                             (length (lsp:signature-help-signatures lsp--signature-last))
   5853                                           lsp--signature-last-index)))
   5854     (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))
   5855 
   5856 (defun lsp-signature-toggle-full-docs ()
   5857   "Toggle full/partial signature documentation."
   5858   (interactive)
   5859   (let ((all? (not (numberp lsp-signature-doc-lines))))
   5860     (setq lsp-signature-doc-lines (if all?
   5861                                       (or (car-safe lsp-signature-doc-lines)
   5862                                           20)
   5863                                     (list lsp-signature-doc-lines))))
   5864   (lsp-signature-activate))
   5865 
   5866 (defface lsp-signature-highlight-function-argument
   5867   '((t :inherit eldoc-highlight-function-argument))
   5868   "The face to use to highlight function arguments in signatures."
   5869   :group 'lsp-mode)
   5870 
   5871 (defun lsp--signature->message (signature-help)
   5872   "Generate eldoc message from SIGNATURE-HELP response."
   5873   (setq lsp--signature-last signature-help)
   5874 
   5875   (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help))))
   5876     (-let* (((&SignatureHelp :active-signature?
   5877                              :active-parameter?
   5878                              :signatures) signature-help)
   5879             (active-signature? (or lsp--signature-last-index active-signature? 0))
   5880             (_ (setq lsp--signature-last-index active-signature?))
   5881             ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?))
   5882             (prefix (if (= (length signatures) 1)
   5883                         ""
   5884                       (concat (propertize (format " %s/%s"
   5885                                                   (1+ active-signature?)
   5886                                                   (length signatures))
   5887                                           'face 'success)
   5888                               " ")))
   5889             (method-docs (when
   5890                              (and lsp-signature-render-documentation
   5891                                   (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines)))
   5892                            (let ((docs (lsp--render-element
   5893                                         (lsp:parameter-information-documentation? signature))))
   5894                              (when (s-present? docs)
   5895                                (concat
   5896                                 "\n"
   5897                                 (if (fboundp 'page-break-lines-mode)
   5898                                     "\n"
   5899                                   "")
   5900                                 (if (and (numberp lsp-signature-doc-lines)
   5901                                          (> (length (s-lines docs)) lsp-signature-doc-lines))
   5902                                     (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs)))
   5903                                             (propertize "\nTruncated..." 'face 'highlight))
   5904                                   docs)))))))
   5905       (when (and active-parameter? (not (seq-empty-p parameters?)))
   5906         (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?)))
   5907                               (seq-elt parameters? active-parameter?)))
   5908                      (selected-param-label (let ((label (lsp:parameter-information-label param)))
   5909                                              (if (stringp label) label (append label nil))))
   5910                      (start (if (stringp selected-param-label)
   5911                                 (s-index-of selected-param-label label)
   5912                               (cl-first selected-param-label)))
   5913                      (end (if (stringp selected-param-label)
   5914                               (+ start (length selected-param-label))
   5915                             (cl-second selected-param-label))))
   5916           (add-face-text-property start end 'lsp-signature-highlight-function-argument nil label)))
   5917       (concat prefix label method-docs))))
   5918 
   5919 (defun lsp-signature ()
   5920   "Display signature info (based on `textDocument/signatureHelp')"
   5921   (if (and lsp--signature-last-buffer
   5922            (not (equal (current-buffer) lsp--signature-last-buffer)))
   5923       (lsp-signature-stop)
   5924     (lsp-request-async "textDocument/signatureHelp"
   5925                        (lsp--text-document-position-params)
   5926                        #'lsp--handle-signature-update
   5927                        :cancel-token :signature)))
   5928 
   5929 
   5930 (defcustom lsp-overlay-document-color-char "■"
   5931   "Display the char represent the document color in overlay"
   5932   :type 'string
   5933   :group 'lsp-mode)
   5934 
   5935 ;; color presentation
   5936 (defun lsp--color-create-interactive-command (color range)
   5937   (lambda ()
   5938     (interactive)
   5939     (-let [(&ColorPresentation? :text-edit?
   5940                                 :additional-text-edits?)
   5941            (lsp--completing-read
   5942             "Select color presentation: "
   5943             (lsp-request
   5944              "textDocument/colorPresentation"
   5945              `( :textDocument ,(lsp--text-document-identifier)
   5946                 :color ,color
   5947                 :range ,range))
   5948             #'lsp:color-presentation-label
   5949             nil
   5950             t)]
   5951       (when text-edit?
   5952         (lsp--apply-text-edit text-edit?))
   5953       (when additional-text-edits?
   5954         (lsp--apply-text-edits additional-text-edits? 'color-presentation)))))
   5955 
   5956 (defun lsp--number->color (number)
   5957   (let ((result (format "%x"
   5958                         (round (* (or number 0) 255.0)))))
   5959     (if (= 1 (length result))
   5960         (concat "0" result)
   5961       result)))
   5962 
   5963 (defun lsp--document-color ()
   5964   "Document color handler."
   5965   (when (lsp-feature? "textDocument/documentColor")
   5966     (lsp-request-async
   5967      "textDocument/documentColor"
   5968      `(:textDocument ,(lsp--text-document-identifier))
   5969      (lambda (result)
   5970        (lsp--remove-overlays 'lsp-color)
   5971        (seq-do
   5972         (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue)
   5973                                      :range))
   5974           (-let* (((beg . end) (lsp--range-to-region range))
   5975                   (overlay (make-overlay beg end))
   5976                   (command (lsp--color-create-interactive-command color range)))
   5977             (overlay-put overlay 'lsp-color t)
   5978             (overlay-put overlay 'evaporate t)
   5979             (overlay-put overlay
   5980                          'before-string
   5981                          (propertize
   5982                           lsp-overlay-document-color-char
   5983                           'face `((:foreground ,(format
   5984                                                  "#%s%s%s"
   5985                                                  (lsp--number->color red)
   5986                                                  (lsp--number->color green)
   5987                                                  (lsp--number->color blue))))
   5988                           'action command
   5989                           'mouse-face 'lsp-lens-mouse-face
   5990                           'local-map (-doto (make-sparse-keymap)
   5991                                        (define-key [mouse-1] command))))))
   5992         result))
   5993      :mode 'unchanged
   5994      :cancel-token :document-color-token)))
   5995 
   5996 
   5997 
   5998 (defun lsp--action-trigger-parameter-hints (_command)
   5999   "Handler for editor.action.triggerParameterHints."
   6000   (when (member :on-server-request lsp-signature-auto-activate)
   6001     (lsp-signature-activate)))
   6002 
   6003 (defun lsp--action-trigger-suggest (_command)
   6004   "Handler for editor.action.triggerSuggest."
   6005   (cond
   6006    ((and (bound-and-true-p company-mode)
   6007          (fboundp 'company-auto-begin)
   6008          (fboundp 'company-post-command))
   6009     (run-at-time 0 nil
   6010                  (lambda ()
   6011                    (let ((this-command 'company-idle-begin)
   6012                          (company-minimum-prefix-length 0))
   6013                      (company-auto-begin)
   6014                      (company-post-command)))))
   6015    (t
   6016     (completion-at-point))))
   6017 
   6018 (defconst lsp--default-action-handlers
   6019   (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints)
   6020       ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest))
   6021   "Default action handlers.")
   6022 
   6023 (defun lsp--find-action-handler (command)
   6024   "Find action handler for particular COMMAND."
   6025   (or
   6026    (--some (-some->> it
   6027              (lsp--workspace-client)
   6028              (lsp--client-action-handlers)
   6029              (gethash command))
   6030            (lsp-workspaces))
   6031    (gethash command lsp--default-action-handlers)))
   6032 
   6033 (defun lsp--text-document-code-action-params (&optional kind)
   6034   "Code action params."
   6035   (list :textDocument (lsp--text-document-identifier)
   6036         :range (if (use-region-p)
   6037                    (lsp--region-to-range (region-beginning) (region-end))
   6038                  (lsp--region-to-range (point) (point)))
   6039         :context `( :diagnostics ,(lsp-cur-possition-diagnostics)
   6040                     ,@(when kind (list :only (vector kind))))))
   6041 
   6042 (defun lsp-code-actions-at-point (&optional kind)
   6043   "Retrieve the code actions for the active region or the current line.
   6044 It will filter by KIND if non nil."
   6045   (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind)))
   6046 
   6047 (defun lsp-execute-code-action-by-kind (command-kind)
   6048   "Execute code action by COMMAND-KIND."
   6049   (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind)
   6050                         (-filter (-lambda ((&CodeAction :kind?))
   6051                                    (and kind? (s-prefix? command-kind kind?))))
   6052                         lsp--select-action)))
   6053       (lsp-execute-code-action action)
   6054     (signal 'lsp-no-code-actions '(command-kind))))
   6055 
   6056 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point)
   6057 
   6058 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?))
   6059   "Parse and execute a code ACTION represented as a Command LSP type."
   6060   (let ((server-id (->> (lsp-workspaces)
   6061                         (cl-first)
   6062                         (or lsp--cur-workspace)
   6063                         (lsp--workspace-client)
   6064                         (lsp--client-server-id))))
   6065     (condition-case nil
   6066         (with-no-warnings
   6067           (lsp-execute-command server-id (intern command) arguments?))
   6068       (cl-no-applicable-method
   6069        (if-let ((action-handler (lsp--find-action-handler command)))
   6070            (funcall action-handler action)
   6071          (lsp-send-execute-command command arguments?))))))
   6072 
   6073 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?))
   6074   "Execute code action ACTION. For example, when text under the
   6075 caret has a suggestion to apply a fix from an lsp-server, calling
   6076 this function will do so.
   6077 If ACTION is not set it will be selected from `lsp-code-actions-at-point'.
   6078 Request codeAction/resolve for more info if server supports."
   6079   (interactive (list (lsp--select-action (lsp-code-actions-at-point))))
   6080   (if (and (lsp-feature? "codeAction/resolve")
   6081            (not command?)
   6082            (not edit?))
   6083       (lsp--execute-code-action (lsp-request "codeAction/resolve" action))
   6084     (lsp--execute-code-action action)))
   6085 
   6086 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?))
   6087   "Execute code action ACTION."
   6088   (when edit?
   6089     (lsp--apply-workspace-edit edit? 'code-action))
   6090 
   6091   (cond
   6092    ((stringp command?) (lsp--execute-command action))
   6093    ((lsp-command? command?) (progn
   6094                               (when-let ((action-filter (->> (lsp-workspaces)
   6095                                                              (cl-first)
   6096                                                              (or lsp--cur-workspace)
   6097                                                              (lsp--workspace-client)
   6098                                                              (lsp--client-action-filter))))
   6099                                 (funcall action-filter command?))
   6100                               (lsp--execute-command command?)))))
   6101 
   6102 (lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments)
   6103   "Patch incorrect boolean argument values in the provided `CodeAction' command
   6104 in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values
   6105 in this list can be either symbols or lists of symbols that
   6106 represent paths to boolean arguments in code actions:
   6107 
   6108 > (lsp-fix-code-action-booleans command `(:foo :bar (:some :nested :boolean)))
   6109 
   6110 When there are available code actions, the server sends
   6111 `lsp-mode' a list of possible command names and arguments as
   6112 JSON. `lsp-mode' parses all boolean false values as `nil'. As a
   6113 result code action arguments containing falsy values don't
   6114 roundtrip correctly because `lsp-mode' will end up sending null
   6115 values back to the client. This list makes it possible to
   6116 selectively transform `nil' values back into `:json-false'."
   6117   (seq-doseq (path boolean-action-arguments)
   6118     (seq-doseq (args arguments?)
   6119       (lsp--fix-nested-boolean args (if (listp path) path (list path))))))
   6120 
   6121 (defun lsp--fix-nested-boolean (structure path)
   6122   "Traverse STRUCTURE using the paths from the PATH list, changing the value to
   6123 `:json-false' if it was `nil'. PATH should be a list containing
   6124 one or more symbols, and STRUCTURE should be compatible with
   6125 `lsp-member?', `lsp-get', and `lsp-put'."
   6126   (let ((key (car path))
   6127         (rest (cdr path)))
   6128     (if (null rest)
   6129         ;; `lsp-put' returns `nil' both when the key doesn't exist and when the
   6130         ;; value is `nil', so we need to explicitly check its presence here
   6131         (when (and (lsp-member? structure key) (not (lsp-get structure key)))
   6132           (lsp-put structure key :json-false))
   6133       ;; If `key' does not exist, then we'll silently ignore it
   6134       (when-let ((child (lsp-get structure key)))
   6135         (lsp--fix-nested-boolean child rest)))))
   6136 
   6137 (defvar lsp--formatting-indent-alist
   6138   ;; Taken from `dtrt-indent-mode'
   6139   '(
   6140     (ada-mode                   . ada-indent)                       ; Ada
   6141     (ada-ts-mode                . ada-ts-mode-indent-offset)
   6142     (c++-mode                   . c-basic-offset)                   ; C++
   6143     (c++-ts-mode                . c-ts-mode-indent-offset)
   6144     (c-mode                     . c-basic-offset)                   ; C
   6145     (c-ts-mode                  . c-ts-mode-indent-offset)
   6146     (cperl-mode                 . cperl-indent-level)               ; Perl
   6147     (crystal-mode               . crystal-indent-level)             ; Crystal (Ruby)
   6148     (csharp-mode                . c-basic-offset)                   ; C#
   6149     (csharp-tree-sitter-mode    . csharp-tree-sitter-indent-offset) ; C#
   6150     (csharp-ts-mode             . csharp-ts-mode-indent-offset)     ; C# (tree-sitter, Emacs29)
   6151     (css-mode                   . css-indent-offset)                ; CSS
   6152     (d-mode                     . c-basic-offset)                   ; D
   6153     (enh-ruby-mode              . enh-ruby-indent-level)            ; Ruby
   6154     (erlang-mode                . erlang-indent-level)              ; Erlang
   6155     (ess-mode                   . ess-indent-offset)                ; ESS (R)
   6156     (go-ts-mode                 . go-ts-mode-indent-offset)
   6157     (gpr-mode                   . gpr-indent-offset)                ; GNAT Project
   6158     (gpr-ts-mode                . gpr-ts-mode-indent-offset)
   6159     (hack-mode                  . hack-indent-offset)               ; Hack
   6160     (java-mode                  . c-basic-offset)                   ; Java
   6161     (java-ts-mode               . java-ts-mode-indent-offset)
   6162     (jde-mode                   . c-basic-offset)                   ; Java (JDE)
   6163     (js-mode                    . js-indent-level)                  ; JavaScript
   6164     (js-ts-mode                 . js-indent-level)
   6165     (js2-mode                   . js2-basic-offset)                 ; JavaScript-IDE
   6166     (js3-mode                   . js3-indent-level)                 ; JavaScript-IDE
   6167     (json-mode                  . js-indent-level)                  ; JSON
   6168     (json-ts-mode               . json-ts-mode-indent-offset)
   6169     (lua-mode                   . lua-indent-level)                 ; Lua
   6170     (lua-ts-mode                . lua-ts-indent-offset)
   6171     (nxml-mode                  . nxml-child-indent)                ; XML
   6172     (objc-mode                  . c-basic-offset)                   ; Objective C
   6173     (pascal-mode                . pascal-indent-level)              ; Pascal
   6174     (perl-mode                  . perl-indent-level)                ; Perl
   6175     (php-mode                   . c-basic-offset)                   ; PHP
   6176     (php-ts-mode                . php-ts-mode-indent-offset)        ; PHP
   6177     (powershell-mode            . powershell-indent)                ; PowerShell
   6178     (powershell-ts-mode         . powershell-ts-mode-indent-offset) ; PowerShell
   6179     (raku-mode                  . raku-indent-offset)               ; Perl6/Raku
   6180     (ruby-mode                  . ruby-indent-level)                ; Ruby
   6181     (rust-mode                  . rust-indent-offset)               ; Rust
   6182     (rust-ts-mode               . rust-ts-mode-indent-offset)
   6183     (rustic-mode                . rustic-indent-offset)             ; Rust
   6184     (scala-mode                 . scala-indent:step)                ; Scala
   6185     (sgml-mode                  . sgml-basic-offset)                ; SGML
   6186     (sh-mode                    . sh-basic-offset)                  ; Shell Script
   6187     (toml-ts-mode               . toml-ts-mode-indent-offset)
   6188     (typescript-mode            . typescript-indent-level)          ; Typescript
   6189     (typescript-ts-mode         . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29)
   6190     (yaml-mode                  . yaml-indent-offset)               ; YAML
   6191     (yang-mode                  . c-basic-offset)                   ; YANG (yang-mode)
   6192 
   6193     (default                    . standard-indent))                 ; default fallback
   6194   "A mapping from `major-mode' to its indent variable.")
   6195 
   6196 (defun lsp--get-indent-width (mode)
   6197   "Get indentation offset for MODE."
   6198   (or (alist-get mode lsp--formatting-indent-alist)
   6199       (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default))))
   6200 
   6201 (defun lsp--make-document-formatting-params ()
   6202   "Create document formatting params."
   6203   (lsp-make-document-formatting-params
   6204    :text-document (lsp--text-document-identifier)
   6205    :options (lsp-make-formatting-options
   6206              :tab-size (symbol-value (lsp--get-indent-width major-mode))
   6207              :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   6208              :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   6209              :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   6210              :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))))
   6211 
   6212 (defun lsp-format-buffer ()
   6213   "Ask the server to format this document."
   6214   (interactive "*")
   6215   (cond ((lsp-feature? "textDocument/formatting")
   6216          (let ((edits (lsp-request "textDocument/formatting"
   6217                                    (lsp--make-document-formatting-params))))
   6218            (if (seq-empty-p edits)
   6219                (lsp--info "No formatting changes provided")
   6220              (lsp--apply-text-edits edits 'format))))
   6221         ((lsp-feature? "textDocument/rangeFormatting")
   6222          (save-restriction
   6223            (widen)
   6224            (lsp-format-region (point-min) (point-max))))
   6225         (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider")))))
   6226 
   6227 (defun lsp-format-region (s e)
   6228   "Ask the server to format the region, or if none is selected, the current line."
   6229   (interactive "r")
   6230   (let ((edits (lsp-request
   6231                 "textDocument/rangeFormatting"
   6232                 (lsp--make-document-range-formatting-params s e))))
   6233     (if (seq-empty-p edits)
   6234         (lsp--info "No formatting changes provided")
   6235       (lsp--apply-text-edits edits 'format))))
   6236 
   6237 (defmacro lsp-make-interactive-code-action (func-name code-action-kind)
   6238   "Define an interactive function FUNC-NAME that attempts to
   6239 execute a CODE-ACTION-KIND action."
   6240   `(defun ,(intern (concat "lsp-" (symbol-name func-name))) ()
   6241      ,(format "Perform the %s code action, if available." code-action-kind)
   6242      (interactive)
   6243      ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to
   6244      ;; auto-execute here: the user has specified exactly what they want.
   6245      (let ((lsp-auto-execute-action t))
   6246        (condition-case nil
   6247            (lsp-execute-code-action-by-kind ,code-action-kind)
   6248          (lsp-no-code-actions
   6249           (when (called-interactively-p 'any)
   6250             (lsp--info ,(format "%s action not available" code-action-kind))))))))
   6251 
   6252 (lsp-make-interactive-code-action organize-imports "source.organizeImports")
   6253 
   6254 (defun lsp--make-document-range-formatting-params (start end)
   6255   "Make DocumentRangeFormattingParams for selected region."
   6256   (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params)
   6257                                                   (lsp--region-to-range start end)))
   6258 
   6259 (defconst lsp--highlight-kind-face
   6260   '((1 . lsp-face-highlight-textual)
   6261     (2 . lsp-face-highlight-read)
   6262     (3 . lsp-face-highlight-write)))
   6263 
   6264 (defun lsp--remove-overlays (name)
   6265   (save-restriction
   6266     (widen)
   6267     (remove-overlays (point-min) (point-max) name t)))
   6268 
   6269 (defun lsp-document-highlight ()
   6270   "Highlight all relevant references to the symbol under point."
   6271   (interactive)
   6272   (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights
   6273   (setq lsp--have-document-highlights nil
   6274         lsp--symbol-bounds-of-last-highlight-invocation nil)
   6275   (let ((lsp-enable-symbol-highlighting t))
   6276     (lsp--document-highlight)))
   6277 
   6278 (defun lsp--document-highlight-callback (highlights)
   6279   "Create a callback to process the reply of a
   6280 `textDocument/documentHighlight' message for the buffer BUF.
   6281 A reference is highlighted only if it is visible in a window."
   6282   (lsp--remove-overlays 'lsp-highlight)
   6283 
   6284   (let* ((wins-visible-pos (-map (lambda (win)
   6285                                    (cons (1- (line-number-at-pos (window-start win) t))
   6286                                          (1+ (line-number-at-pos (window-end win) t))))
   6287                                  (get-buffer-window-list nil nil 'visible))))
   6288     (setq lsp--have-document-highlights t)
   6289     (-map
   6290      (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line)
   6291                                                   :end (end &as &Position :line end-line))
   6292                                    :kind?))
   6293        (-map
   6294         (-lambda ((start-window . end-window))
   6295           ;; Make the overlay only if the reference is visible
   6296           (when (and (> (1+ start-line) start-window)
   6297                      (< (1+ end-line) end-window))
   6298             (let ((start-point (lsp--position-to-point start))
   6299                   (end-point (lsp--position-to-point end)))
   6300               (when (not (and lsp-symbol-highlighting-skip-current
   6301                               (<= start-point (point) end-point)))
   6302                 (-doto (make-overlay start-point end-point)
   6303                   (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face)))
   6304                   (overlay-put 'lsp-highlight t))))))
   6305         wins-visible-pos))
   6306      highlights)))
   6307 
   6308 (defcustom lsp-symbol-kinds
   6309   '((1 . "File")
   6310     (2 . "Module")
   6311     (3 . "Namespace")
   6312     (4 . "Package")
   6313     (5 . "Class")
   6314     (6 . "Method")
   6315     (7 . "Property")
   6316     (8 . "Field")
   6317     (9 . "Constructor")
   6318     (10 . "Enum")
   6319     (11 . "Interface")
   6320     (12 . "Function")
   6321     (13 . "Variable")
   6322     (14 . "Constant")
   6323     (15 . "String")
   6324     (16 . "Number")
   6325     (17 . "Boolean")
   6326     (18 . "Array")
   6327     (19 . "Object")
   6328     (20 . "Key")
   6329     (21 . "Null")
   6330     (22 . "Enum Member")
   6331     (23 . "Struct")
   6332     (24 . "Event")
   6333     (25 . "Operator")
   6334     (26 . "Type Parameter"))
   6335   "Alist mapping SymbolKinds to human-readable strings.
   6336 Various Symbol objects in the LSP protocol have an integral type,
   6337 specifying what they are. This alist maps such type integrals to
   6338 readable representations of them. See
   6339 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/',
   6340 namespace SymbolKind."
   6341   :group 'lsp-mode
   6342   :type '(alist :key-type integer :value-type string))
   6343 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds)
   6344 
   6345 (lsp-defun lsp--symbol-information-to-xref
   6346   ((&SymbolInformation :kind :name
   6347                        :location (&Location :uri :range (&Range :start
   6348                                                                 (&Position :line :character)))))
   6349   "Return a `xref-item' from SYMBOL information."
   6350   (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name)
   6351              (xref-make-file-location (lsp--uri-to-path uri)
   6352                                       line
   6353                                       character)))
   6354 
   6355 (defun lsp--get-document-symbols ()
   6356   "Get document symbols.
   6357 
   6358 If the buffer has not been modified since symbols were last
   6359 retrieved, simply return the latest result.
   6360 
   6361 Else, if the request was initiated by Imenu updating its menu-bar
   6362 entry, perform it asynchronously; i.e., give Imenu the latest
   6363 result and then force a refresh when a new one is available.
   6364 
   6365 Else (e.g., due to interactive use of `imenu' or `xref'),
   6366 perform the request synchronously."
   6367   (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick)
   6368       lsp--document-symbols
   6369     (let ((method "textDocument/documentSymbol")
   6370           (params `(:textDocument ,(lsp--text-document-identifier)))
   6371           (tick (buffer-chars-modified-tick)))
   6372       (if (not lsp--document-symbols-request-async)
   6373           (prog1
   6374               (setq lsp--document-symbols (lsp-request method params))
   6375             (setq lsp--document-symbols-tick tick))
   6376         (lsp-request-async method params
   6377                            (lambda (document-symbols)
   6378                              (setq lsp--document-symbols document-symbols
   6379                                    lsp--document-symbols-tick tick)
   6380                              (lsp--imenu-refresh))
   6381                            :mode 'alive
   6382                            :cancel-token :document-symbols)
   6383         lsp--document-symbols))))
   6384 
   6385 (advice-add 'imenu-update-menubar :around
   6386             (lambda (oldfun &rest r)
   6387               (let ((lsp--document-symbols-request-async t))
   6388                 (apply oldfun r))))
   6389 
   6390 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position)
   6391   "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION."
   6392   (-let (((symbol &as &DocumentSymbol? :children?)
   6393           (seq-find (-lambda ((&DocumentSymbol :range))
   6394                       (lsp-point-in-range? current-position range))
   6395                     document-symbols)))
   6396     (if children?
   6397         (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position))
   6398       (when symbol
   6399         (list symbol)))))
   6400 
   6401 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?))
   6402   "Convert a SymbolInformation to a DocumentInformation"
   6403   (lsp-make-document-symbol :name name
   6404                             :kind kind
   6405                             :range (lsp:location-range location)
   6406                             :children? nil
   6407                             :deprecated? deprecated?
   6408                             :selection-range (lsp:location-range location)
   6409                             :detail? container-name?))
   6410 
   6411 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position)
   6412   "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION."
   6413   (--> symbols-informations
   6414     (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range)))
   6415              (when (lsp-point-in-range? current-position range)
   6416                (lsp--symbol-information->document-symbol symbol)))
   6417            it)
   6418     (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position))
   6419                        (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position)))
   6420                (and (lsp--position-compare b-start-position a-start-position)
   6421                     (lsp--position-compare a-end-position b-end-position))))))
   6422 
   6423 (defun lsp--symbols->document-symbols-hierarchy (symbols)
   6424   "Convert SYMBOLS to symbols-hierarchy."
   6425   (when-let ((first-symbol (lsp-seq-first symbols)))
   6426     (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line)
   6427                                            :character (plist-get (lsp--cur-position) :character))))
   6428       (if (lsp-symbol-information? first-symbol)
   6429           (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position)
   6430         (lsp--document-symbols->document-symbols-hierarchy symbols cur-position)))))
   6431 
   6432 (defun lsp--xref-backend () 'xref-lsp)
   6433 
   6434 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp)))
   6435   (propertize (or (thing-at-point 'symbol) "")
   6436               'identifier-at-point t))
   6437 
   6438 (defun lsp--xref-elements-index (symbols path)
   6439   (-mapcat
   6440    (-lambda (sym)
   6441      (pcase-exhaustive sym
   6442        ((lsp-interface DocumentSymbol :name :children? :selection-range (lsp-interface Range :start))
   6443         (cons (cons (concat path name)
   6444                     (lsp--position-to-point start))
   6445               (lsp--xref-elements-index children? (concat path name " / "))))
   6446        ((lsp-interface SymbolInformation :name :location (lsp-interface Location :range (lsp-interface Range :start)))
   6447         (list (cons (concat path name)
   6448                     (lsp--position-to-point start))))))
   6449    symbols))
   6450 
   6451 (defvar-local lsp--symbols-cache nil)
   6452 
   6453 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp)))
   6454   (if (lsp--find-workspaces-for "textDocument/documentSymbol")
   6455       (progn
   6456         (setq lsp--symbols-cache (lsp--xref-elements-index
   6457                                   (lsp--get-document-symbols) nil))
   6458         lsp--symbols-cache)
   6459     (list (propertize (or (thing-at-point 'symbol) "")
   6460                       'identifier-at-point t))))
   6461 
   6462 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier)
   6463   (save-excursion
   6464     (unless (get-text-property 0 'identifier-at-point identifier)
   6465       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6466                               (user-error "Unable to find symbol %s in current document" identifier)))))
   6467     (lsp--locations-to-xref-items (lsp-request "textDocument/definition"
   6468                                                (lsp--text-document-position-params)))))
   6469 
   6470 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier)
   6471   (save-excursion
   6472     (unless (get-text-property 0 'identifier-at-point identifier)
   6473       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6474                               (user-error "Unable to find symbol %s" identifier)))))
   6475     (lsp--locations-to-xref-items (lsp-request "textDocument/references"
   6476                                                (lsp--make-reference-params nil lsp-references-exclude-definition)))))
   6477 
   6478 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern)
   6479   (seq-map #'lsp--symbol-information-to-xref
   6480            (lsp-request "workspace/symbol" `(:query ,pattern))))
   6481 
   6482 (defcustom lsp-rename-use-prepare t
   6483   "Whether `lsp-rename' should do a prepareRename first.
   6484 For some language servers, textDocument/prepareRename might be
   6485 too slow, in which case this variable may be set to nil.
   6486 `lsp-rename' will then use `thing-at-point' `symbol' to determine
   6487 the symbol to rename at point."
   6488   :group 'lsp-mode
   6489   :type 'boolean)
   6490 
   6491 (defun lsp--get-symbol-to-rename ()
   6492   "Get a symbol to rename and placeholder at point.
   6493 Returns a cons ((START . END) . PLACEHOLDER?), and nil if
   6494 renaming is generally supported but cannot be done at point.
   6495 START and END are the bounds of the identifiers being renamed,
   6496 while PLACEHOLDER?, is either nil or a string suggested by the
   6497 language server as the initial input of a new-name prompt."
   6498   (unless (lsp-feature? "textDocument/rename")
   6499     (error "The connected server(s) doesn't support renaming"))
   6500   (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename"))
   6501       (when-let ((response
   6502                   (lsp-request "textDocument/prepareRename"
   6503                                (lsp--text-document-position-params))))
   6504         (let* ((bounds (lsp--range-to-region
   6505                         (if (lsp-range? response)
   6506                             response
   6507                           (lsp:prepare-rename-result-range response))))
   6508                (placeholder
   6509                 (and (not (lsp-range? response))
   6510                      (lsp:prepare-rename-result-placeholder response))))
   6511           (cons bounds placeholder)))
   6512     (when-let ((bounds (bounds-of-thing-at-point 'symbol)))
   6513       (cons bounds nil))))
   6514 
   6515 (defface lsp-face-rename '((t :underline t))
   6516   "Face used to highlight the identifier being renamed.
   6517 Renaming can be done using `lsp-rename'."
   6518   :group 'lsp-mode)
   6519 
   6520 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face))
   6521   "Face used to display the rename placeholder in.
   6522 When calling `lsp-rename' interactively, this will be the face of
   6523 the new name."
   6524   :group 'lsp-mode)
   6525 
   6526 (defvar lsp-rename-history '()
   6527   "History for `lsp--read-rename'.")
   6528 
   6529 (defun lsp--read-rename (at-point)
   6530   "Read a new name for a `lsp-rename' at `point' from the user.
   6531 AT-POINT shall be a structure as returned by
   6532 `lsp--get-symbol-to-rename'.
   6533 
   6534 Returns a string, which should be the new name for the identifier
   6535 at point. If renaming cannot be done at point (as determined from
   6536 AT-POINT), throw a `user-error'.
   6537 
   6538 This function is for use in `lsp-rename' only, and shall not be
   6539 relied upon."
   6540   (unless at-point
   6541     (user-error "`lsp-rename' is invalid here"))
   6542   (-let* ((((start . end) . placeholder?) at-point)
   6543           ;; Do the `buffer-substring' first to not include `lsp-face-rename'
   6544           (rename-me (buffer-substring start end))
   6545           (placeholder (or placeholder? rename-me))
   6546           (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face))
   6547 
   6548           overlay)
   6549     ;; We need unwind protect, as the user might cancel here, causing the
   6550     ;; overlay to linger.
   6551     (unwind-protect
   6552         (progn
   6553           (setq overlay (make-overlay start end))
   6554           (overlay-put overlay 'face 'lsp-face-rename)
   6555 
   6556           (read-string (format "Rename %s to: " rename-me) placeholder
   6557                        'lsp-rename-history))
   6558       (and overlay (delete-overlay overlay)))))
   6559 
   6560 (defun lsp-rename (newname)
   6561   "Rename the symbol (and all references to it) under point to NEWNAME."
   6562   (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename))))
   6563   (when-let ((edits (lsp-request "textDocument/rename"
   6564                                  `( :textDocument ,(lsp--text-document-identifier)
   6565                                     :position ,(lsp--cur-position)
   6566                                     :newName ,newname))))
   6567     (lsp--apply-workspace-edit edits 'rename)))
   6568 
   6569 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?)
   6570   "Advice around function `rename-file'.
   6571 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?.
   6572 
   6573 This advice sends workspace/willRenameFiles before renaming file
   6574 to check if server wants to apply any workspaceEdits after renamed."
   6575   (if (and lsp-apply-edits-after-file-operations
   6576            (lsp--send-will-rename-files-p old-name))
   6577       (let ((params (lsp-make-rename-files-params
   6578                      :files (vector (lsp-make-file-rename
   6579                                      :oldUri (lsp--path-to-uri old-name)
   6580                                      :newUri (lsp--path-to-uri new-name))))))
   6581         (when-let ((edits (lsp-request "workspace/willRenameFiles" params)))
   6582           (lsp--apply-workspace-edit edits 'rename-file)
   6583           (funcall old-func old-name new-name ok-if-already-exists?)
   6584           (when (lsp--send-did-rename-files-p)
   6585             (lsp-notify "workspace/didRenameFiles" params))))
   6586     (funcall old-func old-name new-name ok-if-already-exists?)))
   6587 
   6588 (advice-add 'rename-file :around #'lsp--on-rename-file)
   6589 
   6590 (defcustom lsp-xref-force-references nil
   6591   "If non-nil threat everything as references(e. g. jump if only one item.)"
   6592   :group 'lsp-mode
   6593   :type 'boolean)
   6594 
   6595 (defun lsp-show-xrefs (xrefs display-action references?)
   6596   (unless (region-active-p) (push-mark nil t))
   6597   (if (boundp 'xref-show-definitions-function)
   6598       (with-no-warnings
   6599         (xref-push-marker-stack)
   6600         (funcall (if (and references? (not lsp-xref-force-references))
   6601                      xref-show-xrefs-function
   6602                    xref-show-definitions-function)
   6603                  (-const xrefs)
   6604                  `((window . ,(selected-window))
   6605                    (display-action . ,display-action)
   6606                    ,(if (and references? (not lsp-xref-force-references))
   6607                         `(auto-jump . ,xref-auto-jump-to-first-xref)
   6608                       `(auto-jump . ,xref-auto-jump-to-first-definition)))))
   6609     (xref--show-xrefs xrefs display-action)))
   6610 
   6611 (cl-defmethod seq-empty-p ((ht hash-table))
   6612   "Function `seq-empty-p' for hash-table."
   6613   (hash-table-empty-p ht))
   6614 
   6615 (cl-defun lsp-find-locations (method &optional extra &key display-action references?)
   6616   "Send request named METHOD and get cross references of the symbol under point.
   6617 EXTRA is a plist of extra parameters.
   6618 REFERENCES? t when METHOD returns references."
   6619   (let ((loc (lsp-request method
   6620                           (append (lsp--text-document-position-params) extra))))
   6621     (if (seq-empty-p loc)
   6622         (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) ""))
   6623       (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?))))
   6624 
   6625 (cl-defun lsp-find-declaration (&key display-action)
   6626   "Find declarations of the symbol under point."
   6627   (interactive)
   6628   (lsp-find-locations "textDocument/declaration" nil :display-action display-action))
   6629 
   6630 (cl-defun lsp-find-definition (&key display-action)
   6631   "Find definitions of the symbol under point."
   6632   (interactive)
   6633   (lsp-find-locations "textDocument/definition" nil :display-action display-action))
   6634 
   6635 (defun lsp-find-definition-mouse (click)
   6636   "Click to start `lsp-find-definition' at clicked point."
   6637   (interactive "e")
   6638   (let* ((ec (event-start click))
   6639          (p1 (posn-point ec))
   6640          (w1 (posn-window ec)))
   6641     (select-window w1)
   6642     (goto-char p1)
   6643     (lsp-find-definition)))
   6644 
   6645 (cl-defun lsp-find-implementation (&key display-action)
   6646   "Find implementations of the symbol under point."
   6647   (interactive)
   6648   (lsp-find-locations "textDocument/implementation"
   6649                       nil
   6650                       :display-action display-action
   6651                       :references? t))
   6652 
   6653 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action)
   6654   "Find references of the symbol under point."
   6655   (interactive "P")
   6656   (lsp-find-locations "textDocument/references"
   6657                       (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition)))))
   6658                       :display-action display-action
   6659                       :references? t))
   6660 
   6661 (cl-defun lsp-find-type-definition (&key display-action)
   6662   "Find type definitions of the symbol under point."
   6663   (interactive)
   6664   (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action))
   6665 
   6666 (defalias 'lsp-find-custom #'lsp-find-locations)
   6667 (defalias 'lsp-goto-implementation #'lsp-find-implementation)
   6668 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition)
   6669 
   6670 (with-eval-after-load 'evil
   6671   (evil-set-command-property 'lsp-find-definition :jump t)
   6672   (evil-set-command-property 'lsp-find-implementation :jump t)
   6673   (evil-set-command-property 'lsp-find-references :jump t)
   6674   (evil-set-command-property 'lsp-find-type-definition :jump t))
   6675 
   6676 (defun lsp--workspace-method-supported? (check-command method capability workspace)
   6677   (with-lsp-workspace workspace
   6678     (if check-command
   6679         (funcall check-command workspace)
   6680       (or
   6681        (when capability (lsp--capability capability))
   6682        (lsp--registered-capability method)
   6683        (and (not capability) (not check-command))))))
   6684 
   6685 (defun lsp-disable-method-for-server (method server-id)
   6686   "Disable METHOD for SERVER-ID."
   6687   (cl-callf
   6688       (lambda (reqs)
   6689         (-let (((&plist :check-command :capability) reqs))
   6690           (list :check-command
   6691                 (lambda (workspace)
   6692                   (unless (-> workspace
   6693                               lsp--workspace-client
   6694                               lsp--client-server-id
   6695                               (eq server-id))
   6696                     (lsp--workspace-method-supported? check-command
   6697                                                       method
   6698                                                       capability
   6699                                                       workspace))))))
   6700       (alist-get method lsp-method-requirements nil nil 'string=)))
   6701 
   6702 (defun lsp--find-workspaces-for (msg-or-method)
   6703   "Find all workspaces in the current project that can handle MSG."
   6704   (let ((method (if (stringp msg-or-method)
   6705                     msg-or-method
   6706                   (plist-get msg-or-method :method))))
   6707     (-if-let (reqs (cdr (assoc method lsp-method-requirements)))
   6708         (-let (((&plist :capability :check-command) reqs))
   6709           (-filter
   6710            (-partial #'lsp--workspace-method-supported?
   6711                      check-command method capability)
   6712            (lsp-workspaces)))
   6713       (lsp-workspaces))))
   6714 
   6715 (defun lsp-can-execute-command? (command-name)
   6716   "Returns non-nil if current language server(s) can execute COMMAND-NAME.
   6717 The command is executed via `workspace/executeCommand'"
   6718   (cl-position
   6719    command-name
   6720    (lsp:execute-command-options-commands
   6721     (lsp:server-capabilities-execute-command-provider?
   6722      (lsp--server-capabilities)))
   6723    :test #'equal))
   6724 
   6725 (defalias 'lsp-feature? 'lsp--find-workspaces-for)
   6726 
   6727 (cl-defmethod lsp-execute-command (_server _command _arguments)
   6728   "Dispatch COMMAND execution."
   6729   (signal 'cl-no-applicable-method nil))
   6730 
   6731 (defun lsp-workspace-command-execute (command &optional args)
   6732   "Execute workspace COMMAND with ARGS."
   6733   (condition-case-unless-debug err
   6734       (let ((params (if args
   6735                         (list :command command :arguments args)
   6736                       (list :command command))))
   6737         (lsp-request "workspace/executeCommand" params))
   6738     (error
   6739      (error "`workspace/executeCommand' with `%s' failed.\n\n%S"
   6740             command err))))
   6741 
   6742 (defun lsp-send-execute-command (command &optional args)
   6743   "Create and send a `workspace/executeCommand' message having command COMMAND
   6744 and optional ARGS."
   6745   (lsp-workspace-command-execute command args))
   6746 
   6747 (defalias 'lsp-point-to-position #'lsp--point-to-position)
   6748 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier)
   6749 (defalias 'lsp--send-execute-command #'lsp-send-execute-command)
   6750 (defalias 'lsp-on-open #'lsp--text-document-did-open)
   6751 (defalias 'lsp-on-save #'lsp--text-document-did-save)
   6752 
   6753 (defun lsp--set-configuration (settings)
   6754   "Set the SETTINGS for the lsp server."
   6755   (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings)))
   6756 
   6757 (defun lsp-current-buffer ()
   6758   (or lsp--virtual-buffer
   6759       (current-buffer)))
   6760 
   6761 (defun lsp-buffer-live-p (buffer-id)
   6762   (if-let ((buffer-live (plist-get buffer-id :buffer-live?)))
   6763       (funcall buffer-live buffer-id)
   6764     (buffer-live-p buffer-id)))
   6765 
   6766 (defun lsp--on-set-visited-file-name (old-func &rest args)
   6767   "Advice around function `set-visited-file-name'.
   6768 
   6769 This advice sends textDocument/didClose for the old file and
   6770 textDocument/didOpen for the new file."
   6771   (when lsp--cur-workspace
   6772     (lsp--text-document-did-close t))
   6773   (prog1 (apply old-func args)
   6774     (when lsp--cur-workspace
   6775       (lsp--text-document-did-open))))
   6776 
   6777 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name)
   6778 
   6779 (defcustom lsp-flush-delayed-changes-before-next-message t
   6780   "If non-nil send the document changes update before sending other messages.
   6781 
   6782 If nil, and `lsp-debounce-full-sync-notifications' is non-nil,
   6783  change notifications will be throttled by
   6784  `lsp-debounce-full-sync-notifications-interval' regardless of
   6785  other messages."
   6786   :group 'lsp-mode
   6787   :type 'boolean)
   6788 
   6789 (defvar lsp--not-flushing-delayed-changes t)
   6790 
   6791 (defun lsp--send-no-wait (message proc)
   6792   "Send MESSAGE to PROC without waiting for further output."
   6793 
   6794   (when (and lsp--not-flushing-delayed-changes
   6795              lsp-flush-delayed-changes-before-next-message)
   6796     (let ((lsp--not-flushing-delayed-changes nil))
   6797       (lsp--flush-delayed-changes)))
   6798   (lsp-process-send proc message))
   6799 
   6800 (define-error 'lsp-parse-error
   6801   "Error parsing message from language server" 'lsp-error)
   6802 (define-error 'lsp-unknown-message-type
   6803   "Unknown message type" '(lsp-error lsp-parse-error))
   6804 (define-error 'lsp-unknown-json-rpc-version
   6805   "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error))
   6806 (define-error 'lsp-no-content-length
   6807   "Content-Length header missing in message" '(lsp-error lsp-parse-error))
   6808 (define-error 'lsp-invalid-header-name
   6809   "Invalid header name" '(lsp-error lsp-parse-error))
   6810 
   6811 ;;  id  method
   6812 ;;   x    x     request
   6813 ;;   x    .     response
   6814 ;;   .    x     notification
   6815 (defun lsp--get-message-type (json-data)
   6816   "Get the message type from JSON-DATA."
   6817   (if (lsp:json-message-id? json-data)
   6818       (if (lsp:json-message-error? json-data)
   6819           'response-error
   6820         (if (lsp:json-message-method? json-data)
   6821             'request
   6822           'response))
   6823     'notification))
   6824 
   6825 (defconst lsp--default-notification-handlers
   6826   (ht ("window/showMessage" #'lsp--window-show-message)
   6827       ("window/logMessage" #'lsp--window-log-message)
   6828       ("window/showInputBox" #'lsp--window-show-input-box)
   6829       ("window/showQuickPick" #'lsp--window-show-quick-pick)
   6830       ("textDocument/publishDiagnostics" #'lsp--on-diagnostics)
   6831       ("textDocument/diagnosticsEnd" #'ignore)
   6832       ("textDocument/diagnosticsBegin" #'ignore)
   6833       ("telemetry/event" #'ignore)
   6834       ("$/progress" (lambda (workspace params)
   6835                       (funcall lsp-progress-function workspace params)))))
   6836 
   6837 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method))
   6838   "Call the appropriate handler for NOTIFICATION."
   6839   (-let ((client (lsp--workspace-client workspace)))
   6840     (when (lsp--log-io-p method)
   6841       (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif)
   6842                           lsp--cur-workspace))
   6843     (if-let ((handler (or (gethash method (lsp--client-notification-handlers client))
   6844                           (gethash method lsp--default-notification-handlers))))
   6845         (funcall handler workspace params)
   6846       (when (and method (not (string-prefix-p "$" method)))
   6847         (lsp-warn "Unknown notification: %s" method)))))
   6848 
   6849 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items))
   6850   "Get section configuration.
   6851 PARAMS are the `workspace/configuration' request params"
   6852   (->> items
   6853        (-map (-lambda ((&ConfigurationItem :section?))
   6854                (-let* ((path-parts (split-string section? "\\."))
   6855                        (path-without-last (s-join "." (-slice path-parts 0 -1)))
   6856                        (path-parts-len (length path-parts)))
   6857                  (cond
   6858                   ((<= path-parts-len 1)
   6859                    (ht-get (lsp-configuration-section section?)
   6860                            (car-safe path-parts)
   6861                            (ht-create)))
   6862                   ((> path-parts-len 1)
   6863                    (when-let ((section (lsp-configuration-section path-without-last))
   6864                               (keys path-parts))
   6865                      (while (and keys section)
   6866                        (setf section (ht-get section (pop keys))))
   6867                      section))))))
   6868        (apply #'vector)))
   6869 
   6870 (defun lsp--ms-since (timestamp)
   6871   "Integer number of milliseconds since TIMESTAMP.  Fractions discarded."
   6872   (floor (* 1000 (float-time (time-since timestamp)))))
   6873 
   6874 (defun lsp--send-request-response (workspace recv-time request response)
   6875   "Send the RESPONSE for REQUEST in WORKSPACE and log if needed."
   6876   (-let* (((&JSONResponse :params :method :id) request)
   6877           (process (lsp--workspace-proc workspace))
   6878           (response (lsp--make-response id response))
   6879           (req-entry (and lsp-log-io
   6880                           (lsp--make-log-entry method id params 'incoming-req)))
   6881           (resp-entry (and lsp-log-io
   6882                            (lsp--make-log-entry method id response 'outgoing-resp
   6883                                                 (lsp--ms-since recv-time)))))
   6884     ;; Send response to the server.
   6885     (when (lsp--log-io-p method)
   6886       (lsp--log-entry-new req-entry workspace)
   6887       (lsp--log-entry-new resp-entry workspace))
   6888     (lsp--send-no-wait response process)))
   6889 
   6890 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method))
   6891   "Call the appropriate handler for REQUEST, and send the return value to the
   6892 server. WORKSPACE is the active workspace."
   6893   (-let* ((recv-time (current-time))
   6894           (client (lsp--workspace-client workspace))
   6895           (buffers (lsp--workspace-buffers workspace))
   6896           handler
   6897           (response (cond
   6898                      ((setq handler (gethash method (lsp--client-request-handlers client) nil))
   6899                       (funcall handler workspace params))
   6900                      ((setq handler (gethash method (lsp--client-async-request-handlers client) nil))
   6901                       (funcall handler workspace params
   6902                                (-partial #'lsp--send-request-response
   6903                                          workspace recv-time request))
   6904                       'delay-response)
   6905                      ((equal method "client/registerCapability")
   6906                       (mapc #'lsp--server-register-capability
   6907                             (lsp:registration-params-registrations params))
   6908                       (mapc (lambda (buf)
   6909                               (when (lsp-buffer-live-p buf)
   6910                                 (lsp-with-current-buffer buf
   6911                                   (lsp-unconfig-buffer)
   6912                                   (lsp-configure-buffer))))
   6913                             buffers)
   6914                       nil)
   6915                      ((equal method "window/showMessageRequest")
   6916                       (let ((choice (lsp--window-log-message-request params)))
   6917                         `(:title ,choice)))
   6918                      ((equal method "window/showDocument")
   6919                       (let ((success? (lsp--window-show-document params)))
   6920                         (lsp-make-show-document-result :success (or success?
   6921                                                                     :json-false))))
   6922                      ((equal method "client/unregisterCapability")
   6923                       (mapc #'lsp--server-unregister-capability
   6924                             (lsp:unregistration-params-unregisterations params))
   6925                       (mapc (lambda (buf)
   6926                               (when (lsp-buffer-live-p buf)
   6927                                 (lsp-with-current-buffer buf
   6928                                   (lsp-unconfig-buffer)
   6929                                   (lsp-configure-buffer))))
   6930                             buffers)
   6931                       nil)
   6932                      ((equal method "workspace/applyEdit")
   6933                       (list :applied (condition-case err
   6934                                          (prog1 t
   6935                                            (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested))
   6936                                        (error
   6937                                         (lsp--error "Failed to apply edits with message %s"
   6938                                                     (error-message-string err))
   6939                                         :json-false))))
   6940                      ((equal method "workspace/configuration")
   6941                       (with-lsp-workspace workspace
   6942                         (if-let ((buf (car buffers)))
   6943                             (lsp-with-current-buffer buf
   6944                               (lsp--build-workspace-configuration-response params))
   6945                           (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace)
   6946                             (lsp--build-workspace-configuration-response params)))))
   6947                      ((equal method "workspace/workspaceFolders")
   6948                       (let ((folders (or (-> workspace
   6949                                              (lsp--workspace-client)
   6950                                              (lsp--client-server-id)
   6951                                              (gethash (lsp-session-server-id->folders (lsp-session))))
   6952                                          (lsp-session-folders (lsp-session)))))
   6953                         (->> folders
   6954                              (-distinct)
   6955                              (-map (lambda (folder)
   6956                                      (list :uri (lsp--path-to-uri folder))))
   6957                              (apply #'vector))))
   6958                      ((equal method "window/workDoneProgress/create")
   6959                       nil ;; no specific reply, no processing required
   6960                       )
   6961                      ((equal method "workspace/semanticTokens/refresh")
   6962                       (when (and lsp-semantic-tokens-enable
   6963                                  (fboundp 'lsp--semantic-tokens-on-refresh))
   6964                         (lsp--semantic-tokens-on-refresh workspace))
   6965                       nil)
   6966                      ((equal method "workspace/codeLens/refresh")
   6967                       (when (and lsp-lens-enable
   6968                                  (fboundp 'lsp--lens-on-refresh))
   6969                         (lsp--lens-on-refresh workspace))
   6970                       nil)
   6971                      ((equal method "workspace/diagnostic/refresh")
   6972                       nil)
   6973                      (t (lsp-warn "Unknown request method: %s" method) nil))))
   6974     ;; Send response to the server.
   6975     (unless (eq response 'delay-response)
   6976       (lsp--send-request-response workspace recv-time request response))))
   6977 
   6978 (lsp-defun lsp--error-string ((&JSONError :message :code))
   6979   "Format ERR as a user friendly string."
   6980   (format "Error from the Language Server: %s (%s)"
   6981           message
   6982           (or (car (alist-get code lsp--errors)) "Unknown error")))
   6983 
   6984 (defun lsp--get-body-length (headers)
   6985   (let ((content-length (cdr (assoc "Content-Length" headers))))
   6986     (if content-length
   6987         (string-to-number content-length)
   6988 
   6989       ;; This usually means either the server or our parser is
   6990       ;; screwed up with a previous Content-Length
   6991       (error "No Content-Length header"))))
   6992 
   6993 (defun lsp--parse-header (s)
   6994   "Parse string S as a LSP (KEY . VAL) header."
   6995   (let ((pos (string-match "\:" s))
   6996         key val)
   6997     (unless pos
   6998       (signal 'lsp-invalid-header-name (list s)))
   6999     (setq key (substring s 0 pos)
   7000           val (s-trim-left (substring s (+ 1 pos))))
   7001     (when (equal key "Content-Length")
   7002       (cl-assert (cl-loop for c across val
   7003                           when (or (> c ?9) (< c ?0)) return nil
   7004                           finally return t)
   7005                  nil (format "Invalid Content-Length value: %s" val)))
   7006     (cons key val)))
   7007 
   7008 (defmacro lsp--read-json (str)
   7009   "Read json string STR."
   7010   (if (progn
   7011         (require 'json)
   7012         (fboundp 'json-parse-string))
   7013       `(json-parse-string ,str
   7014                           :object-type (if lsp-use-plists
   7015                                            'plist
   7016                                          'hash-table)
   7017                           :null-object nil
   7018                           :false-object nil)
   7019     `(let ((json-array-type 'vector)
   7020            (json-object-type (if lsp-use-plists
   7021                                  'plist
   7022                                'hash-table))
   7023            (json-false nil))
   7024        (json-read-from-string ,str))))
   7025 
   7026 (defmacro lsp-json-read-buffer ()
   7027   "Read json from the current buffer."
   7028   (if (progn
   7029         (require 'json)
   7030         (fboundp 'json-parse-buffer))
   7031       `(json-parse-buffer :object-type (if lsp-use-plists
   7032                                            'plist
   7033                                          'hash-table)
   7034                           :null-object nil
   7035                           :false-object nil)
   7036     `(let ((json-array-type 'vector)
   7037            (json-object-type (if lsp-use-plists
   7038                                  'plist
   7039                                'hash-table))
   7040            (json-false nil))
   7041        (json-read))))
   7042 
   7043 (defun lsp--read-json-file (file-path)
   7044   "Read json file."
   7045   (-> file-path
   7046     (f-read-text)
   7047     (lsp--read-json)))
   7048 
   7049 (defun lsp--parser-on-message (json-data workspace)
   7050   "Called when the parser P read a complete MSG from the server."
   7051   (with-demoted-errors "Error processing message %S."
   7052     (with-lsp-workspace workspace
   7053       (let* ((client (lsp--workspace-client workspace))
   7054              (id (--when-let (lsp:json-response-id json-data)
   7055                    (if (stringp it) (string-to-number it) it)))
   7056              (data (lsp:json-response-result json-data)))
   7057         (pcase (lsp--get-message-type json-data)
   7058           ('response
   7059            (cl-assert id)
   7060            (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))]
   7061              (when (lsp--log-io-p method)
   7062                (lsp--log-entry-new
   7063                 (lsp--make-log-entry method id data 'incoming-resp
   7064                                      (lsp--ms-since before-send))
   7065                 workspace))
   7066              (when callback
   7067                (remhash id (lsp--client-response-handlers client))
   7068                (funcall callback (lsp:json-response-result json-data)))))
   7069           ('response-error
   7070            (cl-assert id)
   7071            (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))]
   7072              (when (lsp--log-io-p method)
   7073                (lsp--log-entry-new
   7074                 (lsp--make-log-entry method id (lsp:json-response-error-error json-data)
   7075                                      'incoming-resp (lsp--ms-since before-send))
   7076                 workspace))
   7077              (when callback
   7078                (remhash id (lsp--client-response-handlers client))
   7079                (funcall callback (lsp:json-response-error-error json-data)))))
   7080           ('notification
   7081            (lsp--on-notification workspace json-data))
   7082           ('request (lsp--on-request workspace json-data)))))))
   7083 
   7084 (defun lsp--create-filter-function (workspace)
   7085   "Make filter for the workspace."
   7086   (let ((body-received 0)
   7087         leftovers body-length body chunk)
   7088     (lambda (_proc input)
   7089       (setf chunk (if (s-blank? leftovers)
   7090                       (encode-coding-string input 'utf-8-unix t)
   7091                     (concat leftovers (encode-coding-string input 'utf-8-unix t))))
   7092 
   7093       (let (messages)
   7094         (while (not (s-blank? chunk))
   7095           (if (not body-length)
   7096               ;; Read headers
   7097               (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk)))
   7098                   ;; We've got all the headers, handle them all at once:
   7099                   (setf body-length (lsp--get-body-length
   7100                                      (mapcar #'lsp--parse-header
   7101                                              (split-string
   7102                                               (substring-no-properties chunk
   7103                                                                        (or (string-match-p "Content-Length" chunk)
   7104                                                                            (error "Unable to find Content-Length header."))
   7105                                                                        body-sep-pos)
   7106                                               "\r\n")))
   7107                         body-received 0
   7108                         leftovers nil
   7109                         chunk (substring-no-properties chunk (+ body-sep-pos 4)))
   7110 
   7111                 ;; Haven't found the end of the headers yet. Save everything
   7112                 ;; for when the next chunk arrives and await further input.
   7113                 (setf leftovers chunk
   7114                       chunk nil))
   7115             (let* ((chunk-length (string-bytes chunk))
   7116                    (left-to-receive (- body-length body-received))
   7117                    (this-body (if (< left-to-receive chunk-length)
   7118                                   (prog1 (substring-no-properties chunk 0 left-to-receive)
   7119                                     (setf chunk (substring-no-properties chunk left-to-receive)))
   7120                                 (prog1 chunk
   7121                                   (setf chunk nil))))
   7122                    (body-bytes (string-bytes this-body)))
   7123               (push this-body body)
   7124               (setf body-received (+ body-received body-bytes))
   7125               (when (>= chunk-length left-to-receive)
   7126                 (condition-case err
   7127                     (with-temp-buffer
   7128                       (apply #'insert
   7129                              (nreverse
   7130                               (prog1 body
   7131                                 (setf leftovers nil
   7132                                       body-length nil
   7133                                       body-received nil
   7134                                       body nil))))
   7135                       (decode-coding-region (point-min)
   7136                                             (point-max)
   7137                                             'utf-8)
   7138                       (goto-char (point-min))
   7139                       (push (lsp-json-read-buffer) messages))
   7140 
   7141                   (error
   7142                    (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s"
   7143                              (concat leftovers input)
   7144                              err)))))))
   7145         (mapc (lambda (msg)
   7146                 (lsp--parser-on-message msg workspace))
   7147               (nreverse messages))))))
   7148 
   7149 (defvar-local lsp--line-col-to-point-hash-table nil
   7150   "Hash table with keys (line . col) and values that are either point positions
   7151 or markers.")
   7152 
   7153 (defcustom lsp-imenu-detailed-outline t
   7154   "Whether `lsp-imenu' should include signatures.
   7155 This will be ignored if the server doesn't provide the necessary
   7156 information, for example if it doesn't support DocumentSymbols."
   7157   :group 'lsp-imenu
   7158   :type 'boolean)
   7159 
   7160 (defcustom lsp-imenu-hide-parent-details t
   7161   "Whether `lsp-imenu' should hide signatures of parent nodes."
   7162   :group 'lsp-imenu
   7163   :type 'boolean)
   7164 
   7165 (defface lsp-details-face '((t :height 0.8 :inherit shadow))
   7166   "Used to display additional information throughout `lsp'.
   7167 Things like line numbers, signatures, ... are considered
   7168 additional information. Often, additional faces are defined that
   7169 inherit from this face by default, like `lsp-signature-face', and
   7170 they may be customized for finer control."
   7171   :group 'lsp-mode)
   7172 
   7173 (defface lsp-signature-face '((t :inherit lsp-details-face))
   7174   "Used to display signatures in `imenu', ...."
   7175   :group 'lsp-mode)
   7176 
   7177 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?)
   7178                               show-detail?)
   7179   "Render INPUT0, an `&DocumentSymbol', to a string.
   7180 If SHOW-DETAIL? is set, make use of its `:detail?' field (often
   7181 the signature)."
   7182   (let ((detail (and show-detail? (s-present? detail?)
   7183                      (propertize (concat " " (s-trim-left detail?))
   7184                                  'face 'lsp-signature-face)))
   7185         (name (if deprecated?
   7186                   (propertize name 'face 'lsp-face-semhl-deprecated) name)))
   7187     (concat name detail)))
   7188 
   7189 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?)
   7190                                           separator)
   7191   "Render a piece of SymbolInformation.
   7192 Handle :deprecated?. If SEPARATOR is non-nil, the
   7193 symbol's (optional) parent, SEPARATOR and the symbol itself are
   7194 concatenated."
   7195   (when (and separator container-name? (not (string-empty-p container-name?)))
   7196     (setq name (concat name separator container-name?)))
   7197   (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name))
   7198 
   7199 (defun lsp--symbol-to-imenu-elem (sym)
   7200   "Convert SYM to imenu element.
   7201 
   7202 SYM is a SymbolInformation message.
   7203 
   7204 Return a cons cell (full-name . start-point)."
   7205   (let ((start-point (ht-get lsp--line-col-to-point-hash-table
   7206                              (lsp--get-line-and-col sym))))
   7207     (cons (lsp-render-symbol-information
   7208            sym (and lsp-imenu-show-container-name
   7209                     lsp-imenu-container-name-separator))
   7210           start-point)))
   7211 
   7212 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?))
   7213   "Convert SYM to hierarchical imenu elements.
   7214 
   7215 SYM is a DocumentSymbol message.
   7216 
   7217 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if
   7218 SYM doesn't have any children. Otherwise return a cons cell with
   7219 an alist
   7220 
   7221   (\"symbol-name\" . ((\"(symbol-kind)\" . start-point)
   7222                     cons-cells-from-children))"
   7223   (let ((filtered-children (lsp--imenu-filter-symbols children?))
   7224         (signature (lsp-render-symbol sym lsp-imenu-detailed-outline)))
   7225     (if (seq-empty-p filtered-children)
   7226         (cons signature
   7227               (ht-get lsp--line-col-to-point-hash-table
   7228                       (lsp--get-line-and-col sym)))
   7229       (cons signature
   7230             (lsp--imenu-create-hierarchical-index filtered-children)))))
   7231 
   7232 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind))
   7233   "Determine if SYM is for the current document and is to be shown."
   7234   ;; It's a SymbolInformation or DocumentSymbol, which is always in the
   7235   ;; current buffer file.
   7236   (and lsp-imenu-index-symbol-kinds
   7237        (numberp kind)
   7238        (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup))
   7239                                kind
   7240                              0)))
   7241          (not (memql (aref lsp/symbol-kind-lookup clamped-kind)
   7242                      lsp-imenu-index-symbol-kinds)))))
   7243 
   7244 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind))
   7245   "The string name of the kind of SYM."
   7246   (alist-get kind lsp-symbol-kinds "Other"))
   7247 
   7248 (defun lsp--get-line-and-col (sym)
   7249   "Obtain the line and column corresponding to SYM."
   7250   (-let* ((location (lsp:symbol-information-location sym))
   7251           (name-range (or (and location (lsp:location-range location))
   7252                           (lsp:document-symbol-selection-range sym)))
   7253           ((&Range :start (&Position :line :character)) name-range))
   7254     (cons line character)))
   7255 
   7256 (defun lsp--collect-lines-and-cols (symbols)
   7257   "Return a sorted list ((line . col) ...) of the locations of SYMBOLS."
   7258   (let ((stack (mapcar 'identity symbols))
   7259         line-col-list)
   7260     (while stack
   7261       (let ((sym (pop stack)))
   7262         (push (lsp--get-line-and-col sym) line-col-list)
   7263         (unless (seq-empty-p (lsp:document-symbol-children? sym))
   7264           (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack)))))
   7265     (-sort #'lsp--line-col-comparator line-col-list)))
   7266 
   7267 (defun lsp--convert-line-col-to-points-batch (line-col-list)
   7268   "Convert a sorted list of positions from line-column
   7269 representation to point representation."
   7270   (let ((line-col-to-point-map (ht-create))
   7271         (inhibit-field-text-motion t)
   7272         (curr-line 0))
   7273     (lsp-save-restriction-and-excursion
   7274       (goto-char (point-min))
   7275       (cl-loop for (line . col) in line-col-list do
   7276                (forward-line (- line curr-line))
   7277                (setq curr-line line)
   7278                (let ((line-end (line-end-position)))
   7279                  (if (or (not col) (> col (- line-end (point))))
   7280                      (goto-char line-end)
   7281                    (forward-char col)))
   7282                (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers
   7283                                                                   (point-marker)
   7284                                                                 (point)))))
   7285     line-col-to-point-map))
   7286 
   7287 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2))
   7288   (or (< l1 l2)
   7289       (and (= l1 l2)
   7290            (cond ((and c1 c2)
   7291                   (< c1 c2))
   7292                  (c1 t)))))
   7293 
   7294 (defun lsp-imenu-create-uncategorized-index (symbols)
   7295   "Create imenu index from document SYMBOLS.
   7296 This function, unlike `lsp-imenu-create-categorized-index', does
   7297 not categorize by type, but instead returns an `imenu' index
   7298 corresponding to the symbol hierarchy returned by the server
   7299 directly."
   7300   (let* ((lsp--line-col-to-point-hash-table (-> symbols
   7301                                                 lsp--collect-lines-and-cols
   7302                                                 lsp--convert-line-col-to-points-batch)))
   7303     (if (lsp--imenu-hierarchical-p symbols)
   7304         (lsp--imenu-create-hierarchical-index symbols)
   7305       (lsp--imenu-create-non-hierarchical-index symbols))))
   7306 
   7307 (defcustom lsp-imenu-symbol-kinds
   7308   '((1 . "Files")
   7309     (2 . "Modules")
   7310     (3 . "Namespaces")
   7311     (4 . "Packages")
   7312     (5 . "Classes")
   7313     (6 . "Methods")
   7314     (7 . "Properties")
   7315     (8 . "Fields")
   7316     (9 . "Constructors")
   7317     (10 . "Enums")
   7318     (11 . "Interfaces")
   7319     (12 . "Functions")
   7320     (13 . "Variables")
   7321     (14 . "Constants")
   7322     (15 . "Strings")
   7323     (16 . "Numbers")
   7324     (17 . "Booleans")
   7325     (18 . "Arrays")
   7326     (19 . "Objects")
   7327     (20 . "Keys")
   7328     (21 . "Nulls")
   7329     (22 . "Enum Members")
   7330     (23 . "Structs")
   7331     (24 . "Events")
   7332     (25 . "Operators")
   7333     (26 . "Type Parameters"))
   7334   "`lsp-symbol-kinds', but only used by `imenu'.
   7335 A new variable is needed, as it is `imenu' convention to use
   7336 pluralized categories, which `lsp-symbol-kinds' doesn't. If the
   7337 non-pluralized names are preferred, this can be set to
   7338 `lsp-symbol-kinds'."
   7339   :type '(alist :key-type integer :value-type string))
   7340 
   7341 (defun lsp--imenu-kind->name (kind)
   7342   (alist-get kind lsp-imenu-symbol-kinds "?"))
   7343 
   7344 (defun lsp-imenu-create-top-level-categorized-index (symbols)
   7345   "Create an `imenu' index categorizing SYMBOLS by type.
   7346 Only root symbols are categorized.
   7347 
   7348 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS
   7349 shall be a list of DocumentSymbols or SymbolInformation."
   7350   (mapcan
   7351    (-lambda ((type . symbols))
   7352      (let ((cat (lsp--imenu-kind->name type))
   7353            (symbols (lsp-imenu-create-uncategorized-index symbols)))
   7354        ;; If there is no :kind (this is being defensive), or we couldn't look it
   7355        ;; up, just display the symbols inline, without categories.
   7356        (if cat (list (cons cat symbols)) symbols)))
   7357    (sort (seq-group-by #'lsp:document-symbol-kind symbols)
   7358          (-lambda ((kinda) (kindb)) (< kinda kindb)))))
   7359 
   7360 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start)))
   7361   "Convert an `&DocumentSymbol' to an `imenu' entry."
   7362   (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start))
   7363 
   7364 (defun lsp--imenu-create-categorized-index-1 (symbols)
   7365   "Returns an `imenu' index from SYMBOLS categorized by type.
   7366 The result looks like this: ((\"Variables\" . (...)))."
   7367   (->>
   7368    symbols
   7369    (mapcan
   7370     (-lambda ((sym &as &DocumentSymbol :kind :children?))
   7371       (if (seq-empty-p children?)
   7372           (list (list kind (lsp--symbol->imenu sym)))
   7373         (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline
   7374                                                   (not lsp-imenu-hide-parent-details)))))
   7375           (cons
   7376            (list kind (lsp--symbol->imenu sym))
   7377            (mapcar (-lambda ((type .  imenu-items))
   7378                      (list type (cons parent (mapcan #'cdr imenu-items))))
   7379                    (-group-by #'car (lsp--imenu-create-categorized-index-1 children?))))))))
   7380    (-group-by #'car)
   7381    (mapcar
   7382     (-lambda ((kind . syms))
   7383       (cons kind (mapcan #'cdr syms))))))
   7384 
   7385 (defun lsp--imenu-create-categorized-index (symbols)
   7386   (let ((syms (lsp--imenu-create-categorized-index-1 symbols)))
   7387     (dolist (sym syms)
   7388       (setcar sym (lsp--imenu-kind->name (car sym))))
   7389     syms))
   7390 
   7391 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start))))
   7392   (cons (lsp-render-symbol-information sym nil) start))
   7393 
   7394 (defun lsp--imenu-create-categorized-index-flat (symbols)
   7395   "Create a kind-categorized index for SymbolInformation."
   7396   (mapcar (-lambda ((kind . syms))
   7397             (cons (lsp--imenu-kind->name kind)
   7398                   (mapcan (-lambda ((parent . children))
   7399                             (let ((children (mapcar #'lsp--symbol-information->imenu children)))
   7400                               (if parent (list (cons parent children)) children)))
   7401                           (-group-by #'lsp:symbol-information-container-name? syms))))
   7402           (seq-group-by #'lsp:symbol-information-kind symbols)))
   7403 
   7404 (defun lsp-imenu-create-categorized-index (symbols)
   7405   (if (lsp--imenu-hierarchical-p symbols)
   7406       (lsp--imenu-create-categorized-index symbols)
   7407     (lsp--imenu-create-categorized-index-flat symbols)))
   7408 
   7409 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index
   7410   "Function that should create an `imenu' index.
   7411 It will be called with a list of SymbolInformation or
   7412 DocumentSymbols, whose first level is already filtered. It shall
   7413 then return an appropriate `imenu' index (see
   7414 `imenu-create-index-function').
   7415 
   7416 Note that this interface is not stable, and subject to change any
   7417 time."
   7418   :group 'lsp-imenu
   7419   :type '(radio
   7420           (const :tag "Categorize by type"
   7421                  lsp-imenu-create-categorized-index)
   7422           (const :tag "Categorize root symbols by type"
   7423                  lsp-imenu-create-top-level-categorized-index)
   7424           (const :tag "Uncategorized, inline entries"
   7425                  lsp-imenu-create-uncategorized-index)
   7426           (function :tag "Custom function")))
   7427 
   7428 (defun lsp--imenu-create-index ()
   7429   "Create an `imenu' index based on the language server.
   7430 Respects `lsp-imenu-index-function'."
   7431   (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols))))
   7432     (funcall lsp-imenu-index-function symbols)))
   7433 
   7434 (defun lsp--imenu-filter-symbols (symbols)
   7435   "Filter out unsupported symbols from SYMBOLS."
   7436   (seq-remove #'lsp--symbol-ignore symbols))
   7437 
   7438 (defun lsp--imenu-hierarchical-p (symbols)
   7439   "Determine whether any element in SYMBOLS has children."
   7440   (seq-some #'lsp-document-symbol? symbols))
   7441 
   7442 (defun lsp--imenu-create-non-hierarchical-index (symbols)
   7443   "Create imenu index for non-hierarchical SYMBOLS.
   7444 
   7445 SYMBOLS are a list of DocumentSymbol messages.
   7446 
   7447 Return a nested alist keyed by symbol names. e.g.
   7448 
   7449    ((\"SomeClass\" (\"(Class)\" . 10)
   7450                  (\"someField (Field)\" . 20)
   7451                  (\"someFunction (Function)\" . 25)
   7452                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7453                                   (\"someSubField (Field)\" . 35))
   7454     (\"someFunction (Function)\" . 40))"
   7455   (seq-map (lambda (nested-alist)
   7456              (cons (car nested-alist)
   7457                    (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist))))
   7458            (seq-group-by #'lsp--get-symbol-type symbols)))
   7459 
   7460 (defun lsp--imenu-create-hierarchical-index (symbols)
   7461   "Create imenu index for hierarchical SYMBOLS.
   7462 
   7463 SYMBOLS are a list of DocumentSymbol messages.
   7464 
   7465 Return a nested alist keyed by symbol names. e.g.
   7466 
   7467    ((\"SomeClass\" (\"(Class)\" . 10)
   7468                  (\"someField (Field)\" . 20)
   7469                  (\"someFunction (Function)\" . 25)
   7470                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7471                                   (\"someSubField (Field)\" . 35))
   7472     (\"someFunction (Function)\" . 40))"
   7473   (seq-map #'lsp--symbol-to-hierarchical-imenu-elem
   7474            (seq-sort #'lsp--imenu-symbol-lessp symbols)))
   7475 
   7476 (defun lsp--imenu-symbol-lessp (sym1 sym2)
   7477   (let* ((compare-results (mapcar (lambda (method)
   7478                                     (funcall (alist-get method lsp--imenu-compare-function-alist)
   7479                                              sym1 sym2))
   7480                                   lsp-imenu-sort-methods))
   7481          (result (seq-find (lambda (result)
   7482                              (not (= result 0)))
   7483                            compare-results
   7484                            0)))
   7485     (and (numberp result) (< result 0))))
   7486 
   7487 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left)
   7488                                     (&SymbolInformation :kind right))
   7489   "Compare SYM1 and SYM2 by kind."
   7490   (- left right))
   7491 
   7492 (defun lsp--imenu-compare-line-col (sym1 sym2)
   7493   (if (lsp--line-col-comparator
   7494        (lsp--get-line-and-col sym1)
   7495        (lsp--get-line-and-col sym2))
   7496       -1
   7497     1))
   7498 
   7499 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1)
   7500                                     (&SymbolInformation :name name2))
   7501   "Compare SYM1 and SYM2 by name."
   7502   (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2))))
   7503     (if (numberp result) result 0)))
   7504 
   7505 (defun lsp--imenu-refresh ()
   7506   "Force Imenu to refresh itself."
   7507   (imenu--menubar-select imenu--rescan-item))
   7508 
   7509 (defun lsp-enable-imenu ()
   7510   "Use lsp-imenu for the current buffer."
   7511   (imenu--cleanup)
   7512   (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   7513   (setq-local imenu-menubar-modified-tick -1)
   7514   (setq-local imenu--index-alist nil)
   7515   (when menu-bar-mode
   7516     (lsp--imenu-refresh)))
   7517 
   7518 (defun lsp-resolve-final-command (command &optional test?)
   7519   "Resolve final function COMMAND."
   7520   (let* ((command (lsp-resolve-value command))
   7521          (command (cl-etypecase command
   7522                     (list
   7523                      (cl-assert (seq-every-p (apply-partially #'stringp) command) nil
   7524                                 "Invalid command list")
   7525                      command)
   7526                     (string (list command)))))
   7527     (if (and (file-remote-p default-directory) (not test?))
   7528         (list shell-file-name "-c"
   7529               (string-join (cons "stty raw > /dev/null;"
   7530                                  (mapcar #'shell-quote-argument command))
   7531                            " "))
   7532       command)))
   7533 
   7534 (defun lsp-server-present? (final-command)
   7535   "Check whether FINAL-COMMAND is present."
   7536   (let ((binary-found? (executable-find (cl-first final-command) t)))
   7537     (if binary-found?
   7538         (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command))
   7539       (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command)))
   7540     binary-found?))
   7541 
   7542 (defun lsp--value-to-string (value)
   7543   "Convert VALUE to a string that can be set as value in an environment
   7544 variable."
   7545   (cond
   7546    ((stringp value) value)
   7547    ((booleanp value) (if value
   7548                          "1"
   7549                        "0"))
   7550    ((and (sequencep value)
   7551          (seq-every-p #'stringp value)) (string-join value ":"))
   7552    (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables"))))
   7553 
   7554 (defun lsp--compute-process-environment (environment-fn)
   7555   "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'.
   7556 Ignore non-boolean keys whose value is nil."
   7557   (let ((environment (if environment-fn
   7558                          (funcall environment-fn)
   7559                        nil)))
   7560     (-flatten (cons (cl-loop for (key . value) in environment
   7561                              if (or (eval value)
   7562                                     (eq (get value 'custom-type) 'boolean))
   7563                              collect (concat key "=" (lsp--value-to-string
   7564                                                       (eval value))))
   7565                     process-environment))))
   7566 
   7567 (defun lsp--default-directory-for-connection (&optional path)
   7568   "Return path to be used for the working directory of a LSP process.
   7569 
   7570 If `lsp-use-workspace-root-for-server-default-directory' is
   7571 non-nil, uses `lsp-workspace-root' to find the directory
   7572 corresponding to PATH, else returns `default-directory'."
   7573   (if lsp-use-workspace-root-for-server-default-directory
   7574       (lsp-workspace-root path)
   7575     default-directory))
   7576 
   7577 (defun lsp--fix-remote-cmd (program)
   7578   "Helper for `lsp-stdio-connection'.
   7579 Originally coppied from eglot."
   7580 
   7581   (if (file-remote-p default-directory)
   7582       (list shell-file-name "-c"
   7583             (string-join (cons "stty raw > /dev/null;"
   7584                                (mapcar #'shell-quote-argument program))
   7585                          " "))
   7586     program))
   7587 
   7588 (defvar tramp-use-ssh-controlmaster-options)
   7589 (defvar tramp-ssh-controlmaster-options)
   7590 
   7591 (defun lsp-stdio-connection (command &optional test-command)
   7592   "Returns a connection property list using COMMAND.
   7593 COMMAND can be: A string, denoting the command to launch the
   7594 language server. A list of strings, denoting an executable with
   7595 its command line arguments. A function, that either returns a
   7596 string or a list of strings. In all cases, the launched language
   7597 server should send and receive messages on standard I/O.
   7598 TEST-COMMAND is a function with no arguments which returns
   7599 whether the command is present or not. When not specified
   7600 `lsp-mode' will check whether the first element of the list
   7601 returned by COMMAND is available via `executable-find'"
   7602   (cl-check-type command (or string
   7603                              function
   7604                              (and list
   7605                                   (satisfies (lambda (l)
   7606                                                (seq-every-p (lambda (el)
   7607                                                               (stringp el))
   7608                                                             l))))))
   7609   (list :connect (lambda (filter sentinel name environment-fn workspace)
   7610                    (if (and (functionp 'json-rpc-connection)
   7611                             (not (file-remote-p default-directory)))
   7612                        (lsp-json-rpc-connection workspace (lsp-resolve-final-command command))
   7613                      (let ((final-command (lsp-resolve-final-command command))
   7614                            (process-name (generate-new-buffer-name name))
   7615                            (process-environment
   7616                             (lsp--compute-process-environment environment-fn)))
   7617                        (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name)))
   7618                               (default-directory (lsp--default-directory-for-connection))
   7619                               (tramp-use-ssh-controlmaster-options 'suppress)
   7620                               (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none")
   7621                               (proc (make-process
   7622                                      :name process-name
   7623                                      :connection-type 'pipe
   7624                                      :buffer (format "*%s*" process-name)
   7625                                      :coding 'no-conversion
   7626                                      :command final-command
   7627                                      :filter filter
   7628                                      :sentinel sentinel
   7629                                      :stderr stderr-buf
   7630                                      :noquery t
   7631                                      :file-handler t)))
   7632                          (set-process-query-on-exit-flag proc nil)
   7633                          (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil)
   7634                          (with-current-buffer (get-buffer stderr-buf)
   7635                            ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc.
   7636                            (special-mode))
   7637                          (cons proc proc)))))
   7638         :test? (or
   7639                 test-command
   7640                 (lambda ()
   7641                   (lsp-server-present? (lsp-resolve-final-command command t))))))
   7642 
   7643 (defun lsp--open-network-stream (host port name)
   7644   "Open network stream to HOST:PORT.
   7645   NAME will be passed to `open-network-stream'.
   7646   RETRY-COUNT is the number of the retries.
   7647   SLEEP-INTERVAL is the sleep interval between each retry."
   7648   (let* ((retries 0)
   7649          (sleep-interval 0.01)
   7650          (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval))
   7651          connection)
   7652     (while (and (not connection) (< retries number-of-retries))
   7653       (condition-case err
   7654           (setq connection (open-network-stream name nil host port
   7655                                                 :type 'plain
   7656                                                 :coding 'no-conversion))
   7657         (file-error
   7658          (let ((inhibit-message t))
   7659            (lsp--warn "Failed to connect to %s:%s with error message %s"
   7660                       host
   7661                       port
   7662                       (error-message-string err))
   7663            (sleep-for sleep-interval)
   7664            (cl-incf retries)))))
   7665     (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port))))
   7666 
   7667 (defun lsp--port-available (host port)
   7668   "Return non-nil if HOST and PORT are available."
   7669   (condition-case _err
   7670       (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain))
   7671     (file-error t)))
   7672 
   7673 (defun lsp--find-available-port (host starting-port)
   7674   "Find available port on HOST starting from STARTING-PORT."
   7675   (let ((port starting-port))
   7676     (while (not (lsp--port-available host port))
   7677       (cl-incf port))
   7678     port))
   7679 
   7680 (defun lsp-tcp-connection (command-fn)
   7681   "Returns a connection property list similar to `lsp-stdio-connection'.
   7682 COMMAND-FN can only be a function that takes a single argument, a
   7683 port number. It should return a command for launches a language server
   7684 process listening for TCP connections on the provided port."
   7685   (cl-check-type command-fn function)
   7686   (list
   7687    :connect (lambda (filter sentinel name environment-fn _workspace)
   7688               (let* ((host "localhost")
   7689                      (port (lsp--find-available-port host (cl-incf lsp--tcp-port)))
   7690                      (command (funcall command-fn port))
   7691                      (final-command (if (consp command) command (list command)))
   7692                      (_ (unless (lsp-server-present? final-command)
   7693                           (user-error (format "Couldn't find executable %s" (cl-first final-command)))))
   7694                      (process-environment
   7695                       (lsp--compute-process-environment environment-fn))
   7696                      (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion
   7697                                          :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t))
   7698                      (tcp-proc (lsp--open-network-stream host port (concat name "::tcp"))))
   7699 
   7700                 ;; TODO: Same :noquery issue (see above)
   7701                 (set-process-query-on-exit-flag proc nil)
   7702                 (set-process-query-on-exit-flag tcp-proc nil)
   7703                 (set-process-filter tcp-proc filter)
   7704                 (cons tcp-proc proc)))
   7705    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7706 
   7707 (defalias 'lsp-tcp-server 'lsp-tcp-server-command)
   7708 
   7709 (defun lsp-tcp-server-command (command-fn)
   7710   "Create tcp server connection.
   7711 In this mode Emacs is TCP server and the language server connects
   7712 to it. COMMAND is function with one parameter(the port) and it
   7713 should return the command to start the LS server."
   7714   (cl-check-type command-fn function)
   7715   (list
   7716    :connect (lambda (filter sentinel name environment-fn _workspace)
   7717               (let* (tcp-client-connection
   7718                      (tcp-server (make-network-process :name (format "*tcp-server-%s*" name)
   7719                                                        :buffer (format "*tcp-server-%s*" name)
   7720                                                        :family 'ipv4
   7721                                                        :service lsp--tcp-server-port
   7722                                                        :sentinel (lambda (proc _string)
   7723                                                                    (lsp-log "Language server %s is connected." name)
   7724                                                                    (setf tcp-client-connection proc))
   7725                                                        :server 't))
   7726                      (port (process-contact tcp-server :service))
   7727                      (final-command (funcall command-fn port))
   7728                      (process-environment
   7729                       (lsp--compute-process-environment environment-fn))
   7730                      (cmd-proc (make-process :name name
   7731                                              :connection-type 'pipe
   7732                                              :coding 'no-conversion
   7733                                              :command final-command
   7734                                              :stderr (format "*tcp-server-%s*::stderr" name)
   7735                                              :noquery t)))
   7736                 (let ((retries 0))
   7737                   ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds)
   7738                   (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds)))
   7739                     (lsp--info "Waiting for connection for %s, retries: %s" name retries)
   7740                     (sit-for 0.500)
   7741                     (cl-incf retries)))
   7742 
   7743                 (unless tcp-client-connection
   7744                   (condition-case nil (delete-process tcp-server) (error))
   7745                   (condition-case nil (delete-process cmd-proc) (error))
   7746                   (error "Failed to create connection to %s on port %s" name port))
   7747                 (lsp--info "Successfully connected to %s" name)
   7748 
   7749                 (set-process-query-on-exit-flag cmd-proc nil)
   7750                 (set-process-query-on-exit-flag tcp-client-connection nil)
   7751                 (set-process-query-on-exit-flag tcp-server nil)
   7752 
   7753                 (set-process-filter tcp-client-connection filter)
   7754                 (set-process-sentinel tcp-client-connection sentinel)
   7755                 (cons tcp-client-connection cmd-proc)))
   7756    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7757 
   7758 (defalias 'lsp-tramp-connection 'lsp-stdio-connection)
   7759 
   7760 (defun lsp--auto-configure ()
   7761   "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed."
   7762   (when (functionp 'lsp-ui-mode)
   7763     (lsp-ui-mode))
   7764 
   7765   (if lsp-headerline-breadcrumb-enable
   7766       (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)
   7767     (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode))
   7768   (if lsp-modeline-code-actions-enable
   7769       (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)
   7770     (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode))
   7771   (if lsp-modeline-diagnostics-enable
   7772       (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)
   7773     (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode))
   7774   (if lsp-modeline-workspace-status-enable
   7775       (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)
   7776     (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode))
   7777   (if lsp-lens-enable
   7778       (add-hook 'lsp-configure-hook 'lsp-lens--enable)
   7779     (remove-hook 'lsp-configure-hook 'lsp-lens--enable))
   7780   (if lsp-semantic-tokens-enable
   7781       (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)
   7782     (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable))
   7783 
   7784   ;; yas-snippet config
   7785   (setq-local yas-inhibit-overlay-modification-protection t))
   7786 
   7787 (defun lsp--restart-if-needed (workspace)
   7788   "Handler restart for WORKSPACE."
   7789   (when (or (eq lsp-restart 'auto-restart)
   7790             (eq (lsp--workspace-shutdown-action workspace) 'restart)
   7791             (and (eq lsp-restart 'interactive)
   7792                  (let ((query (format
   7793                                "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?"
   7794                                (lsp--workspace-print workspace))))
   7795                    (y-or-n-p query))))
   7796     (--each (lsp--workspace-buffers workspace)
   7797       (when (lsp-buffer-live-p it)
   7798         (lsp-with-current-buffer it
   7799           (if lsp--buffer-deferred
   7800               (lsp-deferred)
   7801             (lsp--info "Restarting LSP in buffer %s" (buffer-name))
   7802             (lsp)))))))
   7803 
   7804 (defun lsp--update-key (table key fn)
   7805   "Apply FN on value corresponding to KEY in TABLE."
   7806   (let ((existing-value (gethash key table)))
   7807     (if-let ((new-value (funcall fn existing-value)))
   7808         (puthash key new-value table)
   7809       (remhash key table))))
   7810 
   7811 (defun lsp--process-sentinel (workspace process exit-str)
   7812   "Create the sentinel for WORKSPACE."
   7813   (unless (process-live-p process)
   7814     (lsp--handle-process-exit workspace exit-str)))
   7815 
   7816 (defun lsp--handle-process-exit (workspace exit-str)
   7817   (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session)))
   7818          (proc (lsp--workspace-proc workspace)))
   7819     (lsp--warn "%s has exited (%s)"
   7820                (lsp-process-name proc)
   7821                (string-trim-right (or exit-str "")))
   7822     (with-lsp-workspace workspace
   7823       ;; Clean workspace related data in each of the buffers
   7824       ;; in the workspace.
   7825       (--each (lsp--workspace-buffers workspace)
   7826         (when (lsp-buffer-live-p it)
   7827           (lsp-with-current-buffer it
   7828             (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces))
   7829             (lsp--uninitialize-workspace)
   7830             (lsp--spinner-stop)
   7831             (lsp--remove-overlays 'lsp-highlight))))
   7832 
   7833       ;; Cleanup session from references to the closed workspace.
   7834       (--each (hash-table-keys folder->workspaces)
   7835         (lsp--update-key folder->workspaces it (apply-partially 'delete workspace)))
   7836 
   7837       (lsp-process-cleanup proc))
   7838 
   7839     (run-hook-with-args 'lsp-after-uninitialized-functions workspace)
   7840 
   7841     (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown)
   7842         (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace))
   7843       (lsp--restart-if-needed workspace))
   7844     (lsp--cleanup-hanging-watches)))
   7845 
   7846 (defun lsp-workspace-folders (workspace)
   7847   "Return all folders associated with WORKSPACE."
   7848   (let (result)
   7849     (->> (lsp-session)
   7850          (lsp-session-folder->servers)
   7851          (maphash (lambda (folder workspaces)
   7852                     (when (-contains? workspaces workspace)
   7853                       (push folder result)))))
   7854     result))
   7855 
   7856 (defun lsp--start-workspace (session client-template root &optional initialization-options)
   7857   "Create new workspace for CLIENT-TEMPLATE with project root ROOT.
   7858 INITIALIZATION-OPTIONS are passed to initialize function.
   7859 SESSION is the active session."
   7860   (lsp--spinner-start)
   7861   (-let* ((default-directory root)
   7862           (client (copy-lsp--client client-template))
   7863           (workspace (make-lsp--workspace
   7864                       :root root
   7865                       :client client
   7866                       :status 'starting
   7867                       :buffers (list (lsp-current-buffer))
   7868                       :host-root (file-remote-p root)))
   7869           ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities
   7870                      'multi-root 'initialized-fn) client)
   7871           ((proc . cmd-proc) (funcall
   7872                               (or (plist-get new-connection :connect)
   7873                                   (user-error "Client %s is configured incorrectly" client))
   7874                               (lsp--create-filter-function workspace)
   7875                               (apply-partially #'lsp--process-sentinel workspace)
   7876                               (format "%s" server-id)
   7877                               environment-fn
   7878                               workspace))
   7879           (workspace-folders (gethash server-id (lsp-session-server-id->folders session))))
   7880     (setf (lsp--workspace-proc workspace) proc
   7881           (lsp--workspace-cmd-proc workspace) cmd-proc)
   7882 
   7883     ;; update (lsp-session-folder->servers) depending on whether we are starting
   7884     ;; multi/single folder workspace
   7885     (mapc (lambda (project-root)
   7886             (->> session
   7887                  (lsp-session-folder->servers)
   7888                  (gethash project-root)
   7889                  (cl-pushnew workspace)))
   7890           (or workspace-folders (list root)))
   7891 
   7892     (with-lsp-workspace workspace
   7893       (run-hooks 'lsp-before-initialize-hook)
   7894       (lsp-request-async
   7895        "initialize"
   7896        (append
   7897         (list :processId (unless (file-remote-p (buffer-file-name))
   7898                            (emacs-pid))
   7899               :rootPath (lsp-file-local-name (expand-file-name root))
   7900               :clientInfo (list :name "emacs"
   7901                                 :version (emacs-version))
   7902               :rootUri (lsp--path-to-uri root)
   7903               :capabilities (lsp--client-capabilities custom-capabilities)
   7904               :initializationOptions initialization-options
   7905               :workDoneToken "1")
   7906         (when lsp-server-trace
   7907           (list :trace lsp-server-trace))
   7908         (when multi-root
   7909           (->> workspace-folders
   7910                (-distinct)
   7911                (-map (lambda (folder)
   7912                        (list :uri (lsp--path-to-uri folder)
   7913                              :name (f-filename folder))))
   7914                (apply 'vector)
   7915                (list :workspaceFolders))))
   7916        (-lambda ((&InitializeResult :capabilities))
   7917          ;; we know that Rust Analyzer will send {} which will be parsed as null
   7918          ;; when using plists
   7919          (when (equal 'rust-analyzer server-id)
   7920            (-> capabilities
   7921                (lsp:server-capabilities-text-document-sync?)
   7922                (lsp:set-text-document-sync-options-save? t)))
   7923 
   7924          (setf (lsp--workspace-server-capabilities workspace) capabilities
   7925                (lsp--workspace-status workspace) 'initialized)
   7926 
   7927          (with-lsp-workspace workspace
   7928            (lsp-notify "initialized" lsp--empty-ht))
   7929 
   7930          (when initialized-fn (funcall initialized-fn workspace))
   7931 
   7932          (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace))
   7933          (->> workspace
   7934               (lsp--workspace-buffers)
   7935               (mapc (lambda (buffer)
   7936                       (lsp-with-current-buffer buffer
   7937                         (lsp--open-in-workspace workspace)))))
   7938 
   7939          (with-lsp-workspace workspace
   7940            (run-hooks 'lsp-after-initialize-hook))
   7941          (lsp--info "%s initialized successfully in folders: %s"
   7942                     (lsp--workspace-print workspace)
   7943                     (lsp-workspace-folders workspace)))
   7944        :mode 'detached))
   7945     workspace))
   7946 
   7947 (defun lsp--load-default-session ()
   7948   "Load default session."
   7949   (setq lsp--session (or (condition-case err
   7950                              (lsp--read-from-file lsp-session-file)
   7951                            (error (lsp--error "Failed to parse the session %s, starting with clean one."
   7952                                               (error-message-string err))
   7953                                   nil))
   7954                          (make-lsp-session))))
   7955 
   7956 (defun lsp-session ()
   7957   "Get the session associated with the current buffer."
   7958   (or lsp--session (setq lsp--session (lsp--load-default-session))))
   7959 
   7960 (defun lsp--client-disabled-p (buffer-major-mode client)
   7961   (seq-some
   7962    (lambda (entry)
   7963      (pcase entry
   7964        ((pred symbolp) (eq entry client))
   7965        (`(,mode . ,client-or-list)
   7966         (and (eq mode buffer-major-mode)
   7967              (if (listp client-or-list)
   7968                  (memq client client-or-list)
   7969                (eq client client-or-list))))))
   7970    lsp-disabled-clients))
   7971 
   7972 
   7973 ;; download server
   7974 
   7975 (defcustom lsp-server-install-dir (expand-file-name
   7976                                    (locate-user-emacs-file (f-join ".cache" "lsp")))
   7977   "Directory in which the servers will be installed."
   7978   :risky t
   7979   :type 'directory
   7980   :package-version '(lsp-mode . "6.3")
   7981   :group 'lsp-mode)
   7982 
   7983 (defcustom lsp-verify-signature t
   7984   "Whether to check GPG signatures of downloaded files."
   7985   :type 'boolean
   7986   :package-version '(lsp-mode . "8.0.0")
   7987   :group 'lsp-mode)
   7988 
   7989 (defvar lsp--dependencies (ht))
   7990 
   7991 (defun lsp-dependency (name &rest definitions)
   7992   "Used to specify a language server DEPENDENCY, the server
   7993 executable or other required file path. Typically, the
   7994 DEPENDENCY is found by locating it on the system path using
   7995 `executable-find'.
   7996 
   7997 You can explicitly call lsp-dependency in your environment to
   7998 specify the absolute path to the DEPENDENCY. For example, the
   7999 typescript-language-server requires both the server and the
   8000 typescript compiler. If you have installed them in a team shared
   8001 read-only location, you can instruct lsp-mode to use them via
   8002 
   8003  (eval-after-load `lsp-mode
   8004    `(progn
   8005       (require lsp-javascript)
   8006       (lsp-dependency typescript-language-server (:system ,tls-exe))
   8007       (lsp-dependency typescript (:system ,ts-js))))
   8008 
   8009 where tls-exe is the absolute path to the typescript-language-server
   8010 executable and ts-js is the absolute path to the typescript compiler
   8011 JavaScript file, tsserver.js (the *.js is required for Windows)."
   8012   (ht-set lsp--dependencies name definitions))
   8013 
   8014 (defun lsp--server-binary-present? (client)
   8015   (unless (equal (lsp--client-server-id client) 'lsp-pwsh)
   8016     (condition-case ()
   8017         (-some-> client lsp--client-new-connection (plist-get :test?) funcall)
   8018       (error nil)
   8019       (args-out-of-range nil))))
   8020 
   8021 (define-minor-mode lsp-installation-buffer-mode
   8022   "Mode used in *lsp-installation* buffers.
   8023 It can be used to set-up keybindings, etc. Disabling this mode
   8024 detaches the installation buffer from commands like
   8025 `lsp-select-installation-buffer'."
   8026   :init-value nil
   8027   :lighter nil)
   8028 
   8029 (defface lsp-installation-finished-buffer-face '((t :foreground "orange"))
   8030   "Face used for finished installation buffers.
   8031 Used in `lsp-select-installation-buffer'."
   8032   :group 'lsp-mode)
   8033 
   8034 (defface lsp-installation-buffer-face '((t :foreground "green"))
   8035   "Face used for installation buffers still in progress.
   8036 Used in `lsp-select-installation-buffer'."
   8037   :group 'lsp-mode)
   8038 
   8039 (defun lsp--installation-buffer? (buf)
   8040   "Check whether BUF is an `lsp-async-start-process' buffer."
   8041   (buffer-local-value 'lsp-installation-buffer-mode buf))
   8042 
   8043 (defun lsp-select-installation-buffer (&optional show-finished)
   8044   "Interactively choose an installation buffer.
   8045 If SHOW-FINISHED is set, leftover (finished) installation buffers
   8046 are still shown."
   8047   (interactive "P")
   8048   (let ((bufs (--filter (and (lsp--installation-buffer? it)
   8049                              (or show-finished (get-buffer-process it)))
   8050                         (buffer-list))))
   8051     (pcase bufs
   8052       (`nil (user-error "No installation buffers"))
   8053       (`(,buf) (pop-to-buffer buf))
   8054       (bufs (pop-to-buffer (completing-read "Select installation buffer: "
   8055                                             (--map (propertize (buffer-name it) 'face
   8056                                                                (if (get-buffer-process it)
   8057                                                                    'lsp-installation-buffer-face
   8058                                                                  'lsp-installation-finished-buffer-face))
   8059                                                    bufs)))))))
   8060 
   8061 (defun lsp-cleanup-installation-buffers ()
   8062   "Delete finished *lsp-installation* buffers."
   8063   (interactive)
   8064   (dolist (buf (buffer-list))
   8065     (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf)))
   8066       (kill-buffer buf))))
   8067 
   8068 (defun lsp--download-status ()
   8069   (-some--> #'lsp--client-download-in-progress?
   8070     (lsp--filter-clients it)
   8071     (-map (-compose #'symbol-name #'lsp--client-server-id) it)
   8072     (format "%s" it)
   8073     (propertize it 'face 'success)
   8074     (format " Installing following servers: %s" it)
   8075     (propertize it
   8076                 'local-map (make-mode-line-mouse-map
   8077                             'mouse-1 #'lsp-select-installation-buffer)
   8078                 'mouse-face 'highlight)))
   8079 
   8080 (defun lsp--install-server-internal (client &optional update?)
   8081   (unless (lsp--client-download-server-fn client)
   8082     (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation."
   8083                 (lsp--client-server-id client)))
   8084 
   8085   (setf (lsp--client-download-in-progress? client) t)
   8086   (add-to-list 'global-mode-string '(t (:eval (lsp--download-status))))
   8087   (cl-flet ((done
   8088              (success? &optional error-message)
   8089              ;; run with idle timer to make sure the lsp command is executed in
   8090              ;; the main thread, see #2739.
   8091              (run-with-timer
   8092               0.0
   8093               nil
   8094               (lambda ()
   8095                 (-let [(&lsp-cln 'server-id 'buffers) client]
   8096                   (setf (lsp--client-download-in-progress? client) nil
   8097                         (lsp--client-buffers client) nil)
   8098                   (if success?
   8099                       (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id
   8100                                  (length buffers))
   8101                     (lsp--error "Server %s install process failed with the following error message: %s.
   8102 Check `*lsp-install*' and `*lsp-log*' buffer."
   8103                                 server-id
   8104                                 error-message))
   8105                   (seq-do
   8106                    (lambda (buffer)
   8107                      (when (lsp-buffer-live-p buffer)
   8108                        (lsp-with-current-buffer buffer
   8109                          (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   8110                                     global-mode-string)
   8111                          (when success? (lsp)))))
   8112                    buffers)
   8113                   (unless (lsp--filter-clients #'lsp--client-download-in-progress?)
   8114                     (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   8115                                global-mode-string)))))))
   8116     (lsp--info "Download %s started." (lsp--client-server-id client))
   8117     (condition-case err
   8118         (funcall
   8119          (lsp--client-download-server-fn client)
   8120          client
   8121          (lambda () (done t))
   8122          (lambda (msg) (done nil msg))
   8123          update?)
   8124       (error
   8125        (done nil (error-message-string err))))))
   8126 
   8127 (defun lsp--require-packages ()
   8128   "Load `lsp-client-packages' if needed."
   8129   (when (and lsp-auto-configure (not lsp--client-packages-required))
   8130     (seq-do (lambda (package)
   8131               ;; loading client is slow and `lsp' can be called repeatedly
   8132               (unless (featurep package)
   8133                 (require package nil t)))
   8134             lsp-client-packages)
   8135     (setq lsp--client-packages-required t)))
   8136 
   8137 ;;;###autoload
   8138 (defun lsp-install-server (update? &optional server-id)
   8139   "Interactively install or re-install server.
   8140 When prefix UPDATE? is t force installation even if the server is present."
   8141   (interactive "P")
   8142   (lsp--require-packages)
   8143   (let* ((chosen-client (or (gethash server-id lsp-clients)
   8144                             (lsp--completing-read
   8145                              "Select server to install/re-install: "
   8146                              (or (->> lsp-clients
   8147                                       (ht-values)
   8148                                       (-filter (-andfn
   8149                                                 (-not #'lsp--client-download-in-progress?)
   8150                                                 #'lsp--client-download-server-fn)))
   8151                                  (user-error "There are no servers with automatic installation"))
   8152                              (lambda (client)
   8153                                (let ((server-name (-> client lsp--client-server-id symbol-name)))
   8154                                  (if (lsp--server-binary-present? client)
   8155                                      (concat server-name " (Already installed)")
   8156                                    server-name)))
   8157                              nil
   8158                              t)))
   8159          (update? (or update?
   8160                       (and (not (lsp--client-download-in-progress? chosen-client))
   8161                            (lsp--server-binary-present? chosen-client)))))
   8162     (lsp--install-server-internal chosen-client update?)))
   8163 
   8164 ;;;###autoload
   8165 (defun lsp-uninstall-server (dir)
   8166   "Delete a LSP server from `lsp-server-install-dir'."
   8167   (interactive
   8168    (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir))))
   8169   (unless (file-directory-p dir)
   8170     (user-error "Couldn't find %s directory" dir))
   8171   (delete-directory dir 'recursive)
   8172   (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir))))
   8173 
   8174 ;;;###autoload
   8175 (defun lsp-uninstall-servers ()
   8176   "Uninstall all installed servers."
   8177   (interactive)
   8178   (let* ((dir lsp-server-install-dir)
   8179          (servers (ignore-errors
   8180                     (directory-files dir t
   8181                                      directory-files-no-dot-files-regexp))))
   8182     (if (or (not (file-directory-p dir)) (zerop (length servers)))
   8183         (user-error "No servers to uninstall")
   8184       (when (yes-or-no-p
   8185              (format "Servers to uninstall: %d (%s), proceed? "
   8186                      (length servers)
   8187                      (mapconcat (lambda (server)
   8188                                   (file-name-nondirectory (directory-file-name server)))
   8189                                 servers " ")))
   8190         (mapc #'lsp-uninstall-server servers)
   8191         (message "All servers uninstalled")))))
   8192 
   8193 ;;;###autoload
   8194 (defun lsp-update-server (&optional server-id)
   8195   "Interactively update (reinstall) a server."
   8196   (interactive)
   8197   (lsp--require-packages)
   8198   (let ((chosen-client (or (gethash server-id lsp-clients)
   8199                            (lsp--completing-read
   8200                             "Select server to update (if not on the list, probably you need to `lsp-install-server`): "
   8201                             (or (->> lsp-clients
   8202                                      (ht-values)
   8203                                      (-filter (-andfn
   8204                                                (-not #'lsp--client-download-in-progress?)
   8205                                                #'lsp--client-download-server-fn
   8206                                                #'lsp--server-binary-present?)))
   8207                                 (user-error "There are no servers to update"))
   8208                             (lambda (client)
   8209                               (-> client lsp--client-server-id symbol-name))
   8210                             nil
   8211                             t))))
   8212     (lsp--install-server-internal chosen-client t)))
   8213 
   8214 ;;;###autoload
   8215 (defun lsp-update-servers ()
   8216   "Update (reinstall) all installed servers."
   8217   (interactive)
   8218   (lsp--require-packages)
   8219   (mapc (lambda (client) (lsp--install-server-internal client t))
   8220         (-filter (-andfn
   8221                   (-not #'lsp--client-download-in-progress?)
   8222                   #'lsp--client-download-server-fn
   8223                   #'lsp--server-binary-present?) (hash-table-values lsp-clients))))
   8224 
   8225 ;;;###autoload
   8226 (defun lsp-ensure-server (server-id)
   8227   "Ensure server SERVER-ID"
   8228   (lsp--require-packages)
   8229   (if-let ((client (gethash server-id lsp-clients)))
   8230       (unless (lsp--server-binary-present? client)
   8231         (lsp--info "Server `%s' is not preset, installing..." server-id)
   8232         (lsp-install-server nil server-id))
   8233     (warn "Unable to find server registration with id %s" server-id)))
   8234 
   8235 (defun lsp-async-start-process (callback error-callback &rest command)
   8236   "Start async process COMMAND with CALLBACK and ERROR-CALLBACK."
   8237   (let ((name (cl-first command)))
   8238     (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd)
   8239                                                                                          (not (null cmd)))
   8240                                                                                        command)
   8241                                                        " ") t
   8242                                             (lambda (&rest _)
   8243                                               (generate-new-buffer-name (format "*lsp-install: %s*" name))))
   8244       (lsp-installation-buffer-mode +1)
   8245       (view-mode +1)
   8246       (add-hook
   8247        'compilation-finish-functions
   8248        (lambda (_buf status)
   8249          (if (string= "finished\n" status)
   8250              (condition-case err
   8251                  (funcall callback)
   8252                (error
   8253                 (funcall error-callback (error-message-string err))))
   8254            (funcall error-callback (s-trim-right status))))
   8255        nil t))))
   8256 
   8257 (defun lsp-resolve-value (value)
   8258   "Resolve VALUE's value.
   8259 If it is function - call it.
   8260 If it is a variable - return it's value
   8261 Otherwise returns value itself."
   8262   (cond
   8263    ((functionp value) (funcall value))
   8264    ((and (symbolp value) (boundp value)) (symbol-value value))
   8265    (value)))
   8266 
   8267 (defvar lsp-deps-providers
   8268   (list :npm (list :path #'lsp--npm-dependency-path
   8269                    :install #'lsp--npm-dependency-install)
   8270         :cargo (list :path #'lsp--cargo-dependency-path
   8271                      :install #'lsp--cargo-dependency-install)
   8272         :system (list :path #'lsp--system-path)
   8273         :download (list :path #'lsp-download-path
   8274                         :install #'lsp-download-install)))
   8275 
   8276 (defun lsp--system-path (path)
   8277   "If PATH is absolute and exists return it as is. Otherwise,
   8278 return the absolute path to the executable defined by PATH or
   8279 nil."
   8280   ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the
   8281   ;; typescript-language-server. When lsp invokes the server, lsp needs to
   8282   ;; supply the path to the typescript compiler, tsserver.js, as an argument. To
   8283   ;; make code platform independent, one must pass the absolute path to the
   8284   ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript
   8285   ;; child process spawn command that is invoked by the
   8286   ;; typescript-language-server). This is why we check for existence and not
   8287   ;; that the path is executable.
   8288   (let ((path (lsp-resolve-value path)))
   8289     (cond
   8290      ((and (f-absolute? path)
   8291            (f-exists? path))
   8292       path)
   8293      ((executable-find path t) path))))
   8294 
   8295 (defun lsp-package-path (dependency)
   8296   "Path to the DEPENDENCY each of the registered providers."
   8297   (let (path)
   8298     (-first (-lambda ((provider . rest))
   8299               (setq path (-some-> lsp-deps-providers
   8300                            (plist-get provider)
   8301                            (plist-get :path)
   8302                            (apply rest))))
   8303             (gethash dependency lsp--dependencies))
   8304     path))
   8305 
   8306 (defun lsp-package-ensure (dependency callback error-callback)
   8307   "Asynchronously ensure a package."
   8308   (or (-first (-lambda ((provider . rest))
   8309                 (-some-> lsp-deps-providers
   8310                   (plist-get provider)
   8311                   (plist-get :install)
   8312                   (apply (cl-list* callback error-callback rest))))
   8313               (gethash dependency lsp--dependencies))
   8314       (funcall error-callback (format "Unable to find a way to install %s" dependency))))
   8315 
   8316 
   8317 ;; npm handling
   8318 
   8319 ;; https://docs.npmjs.com/files/folders#executables
   8320 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys)
   8321   "Return npm dependency PATH for PACKAGE."
   8322   (let ((path (executable-find
   8323                (f-join lsp-server-install-dir "npm" package
   8324                        (cond ((eq system-type 'windows-nt) "")
   8325                              (t "bin"))
   8326                        path)
   8327                t)))
   8328     (unless (and path (f-exists? path))
   8329       (error "The package %s is not installed.  Unable to find %s" package path))
   8330     path))
   8331 
   8332 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys)
   8333   (if-let ((npm-binary (executable-find "npm")))
   8334       (progn
   8335         ;; Explicitly `make-directory' to work around NPM bug in
   8336         ;; versions 7.0.0 through 7.4.1. See
   8337         ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for
   8338         ;; discussion.
   8339         (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents)
   8340         (lsp-async-start-process (lambda ()
   8341                                    (if (string-empty-p
   8342                                         (string-trim (shell-command-to-string
   8343                                                       (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " "))))
   8344                                        (funcall callback)
   8345                                      (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json")))))
   8346                                            (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx
   8347                                        (when (f-dir-p default-directory)
   8348                                          (lsp-async-start-process callback
   8349                                                                   error-callback
   8350                                                                   (executable-find "npx")
   8351                                                                   "npm-install-peers")))))
   8352                                  error-callback
   8353                                  npm-binary
   8354                                  "-g"
   8355                                  "--prefix"
   8356                                  (f-join lsp-server-install-dir "npm" package)
   8357                                  "install"
   8358                                  package))
   8359     (lsp-log "Unable to install %s via `npm' because it is not present" package)
   8360     nil))
   8361 
   8362 
   8363 ;; Cargo dependency handling
   8364 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys)
   8365   (let ((path (executable-find
   8366                (f-join lsp-server-install-dir
   8367                        "cargo"
   8368                        package
   8369                        "bin"
   8370                        path)
   8371                t)))
   8372     (unless (and path (f-exists? path))
   8373       (error "The package %s is not installed.  Unable to find %s" package path))
   8374     path))
   8375 
   8376 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys)
   8377   (if-let ((cargo-binary (executable-find "cargo")))
   8378       (lsp-async-start-process
   8379        callback
   8380        error-callback
   8381        cargo-binary
   8382        "install"
   8383        package
   8384        (when git
   8385          "--git")
   8386        git
   8387        "--root"
   8388        (f-join lsp-server-install-dir "cargo" package))
   8389     (lsp-log "Unable to install %s via `cargo' because it is not present" package)
   8390     nil))
   8391 
   8392 
   8393 
   8394 ;; Download URL handling
   8395 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys)
   8396   (let* ((url (lsp-resolve-value url))
   8397          (store-path (lsp-resolve-value store-path))
   8398          ;; (decompress (lsp-resolve-value decompress))
   8399          (download-path
   8400           (pcase decompress
   8401             (:gzip (concat store-path ".gz"))
   8402             (:zip (concat store-path ".zip"))
   8403             (:targz (concat store-path ".tar.gz"))
   8404             (`nil store-path)
   8405             (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'")))))
   8406     (make-thread
   8407      (lambda ()
   8408        (condition-case err
   8409            (progn
   8410              (when (f-exists? download-path)
   8411                (f-delete download-path))
   8412              (when (f-exists? store-path)
   8413                (f-delete store-path))
   8414              (lsp--info "Starting to download %s to %s..." url download-path)
   8415              (mkdir (f-parent download-path) t)
   8416              (url-copy-file url download-path)
   8417              (lsp--info "Finished downloading %s..." download-path)
   8418              (when (and lsp-verify-signature asc-url pgp-key)
   8419                (if (executable-find epg-gpg-program)
   8420                    (let ((asc-download-path (concat download-path ".asc"))
   8421                          (context (epg-make-context))
   8422                          (fingerprint)
   8423                          (signature))
   8424                      (when (f-exists? asc-download-path)
   8425                        (f-delete asc-download-path))
   8426                      (lsp--info "Starting to download %s to %s..." asc-url asc-download-path)
   8427                      (url-copy-file asc-url asc-download-path)
   8428                      (lsp--info "Finished downloading %s..." asc-download-path)
   8429                      (epg-import-keys-from-string context pgp-key)
   8430                      (setq fingerprint (epg-import-status-fingerprint
   8431                                         (car
   8432                                          (epg-import-result-imports
   8433                                           (epg-context-result-for context 'import)))))
   8434                      (lsp--info "Verifying signature %s..." asc-download-path)
   8435                      (epg-verify-file context asc-download-path download-path)
   8436                      (setq signature (car (epg-context-result-for context 'verify)))
   8437                      (unless (and
   8438                               (eq (epg-signature-status signature) 'good)
   8439                               (equal (epg-signature-fingerprint signature) fingerprint))
   8440                        (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature))))
   8441                  (lsp--warn "GPG is not installed, skipping the signature check.")))
   8442              (when decompress
   8443                (lsp--info "Decompressing %s..." download-path)
   8444                (pcase decompress
   8445                  (:gzip
   8446                   (lsp-gunzip download-path))
   8447                  (:zip (lsp-unzip download-path (f-parent store-path)))
   8448                  (:targz (lsp-tar-gz-decompress download-path (f-parent store-path))))
   8449                (lsp--info "Decompressed %s..." store-path))
   8450              (funcall callback))
   8451          (error (funcall error-callback err)))))))
   8452 
   8453 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys)
   8454   "Download URL and store it into STORE-PATH.
   8455 
   8456 SET-EXECUTABLE? when non-nil change the executable flags of
   8457 STORE-PATH to make it executable. BINARY-PATH can be specified
   8458 when the binary to start does not match the name of the
   8459 archive (e.g. when the archive has multiple files)"
   8460   (let ((store-path (or (lsp-resolve-value binary-path)
   8461                         (lsp-resolve-value store-path))))
   8462     (cond
   8463      ((executable-find store-path) store-path)
   8464      ((and set-executable? (f-exists? store-path))
   8465       (set-file-modes store-path #o0700)
   8466       store-path)
   8467      ((f-exists? store-path) store-path))))
   8468 
   8469 (defun lsp--find-latest-gh-release-url (url regex)
   8470   "Fetch the latest version in the releases given by URL by using REGEX."
   8471   (let ((url-request-method "GET"))
   8472     (with-current-buffer (url-retrieve-synchronously url)
   8473       (goto-char (point-min))
   8474       (re-search-forward "\n\n" nil 'noerror)
   8475       (delete-region (point-min) (point))
   8476       (let* ((json-result (lsp-json-read-buffer)))
   8477         (message "Latest version found: %s" (lsp-get json-result :tag_name))
   8478         (--> json-result
   8479              (lsp-get it :assets)
   8480              (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it)
   8481              (lsp-get it :browser_download_url))))))
   8482 
   8483 ;; unzip
   8484 
   8485 (defconst lsp-ext-pwsh-script "pwsh -noprofile -noninteractive \
   8486 -nologo -ex bypass -c Expand-Archive -Path '%s' -DestinationPath '%s'"
   8487   "Pwsh script to unzip file.")
   8488 
   8489 (defconst lsp-ext-powershell-script "powershell -noprofile -noninteractive \
   8490 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'"
   8491   "Powershell script to unzip file.")
   8492 
   8493 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'"
   8494   "Unzip script to unzip file.")
   8495 
   8496 (defcustom lsp-unzip-script (lambda ()
   8497                               (cond ((and (eq system-type 'windows-nt)
   8498                                           (executable-find "pwsh"))
   8499                                      lsp-ext-pwsh-script)
   8500                                     ((and (eq system-type 'windows-nt)
   8501                                           (executable-find "powershell"))
   8502                                      lsp-ext-powershell-script)
   8503                                     ((executable-find "unzip") lsp-ext-unzip-script)
   8504                                     ((executable-find "pwsh") lsp-ext-pwsh-script)
   8505                                     (t nil)))
   8506   "The script to unzip."
   8507   :group 'lsp-mode
   8508   :type 'string
   8509   :package-version '(lsp-mode . "8.0.0"))
   8510 
   8511 (defun lsp-unzip (zip-file dest)
   8512   "Unzip ZIP-FILE to DEST."
   8513   (unless lsp-unzip-script
   8514     (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'"))
   8515   (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest)))
   8516 
   8517 ;; gunzip
   8518 
   8519 (defconst lsp-ext-gunzip-script "gzip -d %1$s"
   8520   "Script to decompress a gzippped file with gzip.")
   8521 
   8522 (defcustom lsp-gunzip-script (lambda ()
   8523                                (cond ((executable-find "gzip") lsp-ext-gunzip-script)
   8524                                      (t nil)))
   8525   "The script to decompress a gzipped file.
   8526 Should be a format string with one argument for the file to be decompressed
   8527 in place."
   8528   :group 'lsp-mode
   8529   :type 'string
   8530   :package-version '(lsp-mode . "8.0.0"))
   8531 
   8532 (defun lsp-gunzip (gz-file)
   8533   "Decompress GZ-FILE in place."
   8534   (unless lsp-gunzip-script
   8535     (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file))
   8536   (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file)))
   8537 
   8538 ;; tar.gz decompression
   8539 
   8540 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'"
   8541   "Script to decompress a .tar.gz file.")
   8542 
   8543 (defcustom lsp-tar-script (lambda ()
   8544                             (cond ((executable-find "tar") lsp-ext-tar-script)
   8545                                   (t nil)))
   8546   "The script to decompress a .tar.gz file.
   8547 Should be a format string with one argument for the file to be decompressed
   8548 in place."
   8549   :group 'lsp-mode
   8550   :type 'string)
   8551 
   8552 (defun lsp-tar-gz-decompress (targz-file dest)
   8553   "Decompress TARGZ-FILE in DEST."
   8554   (unless lsp-tar-script
   8555     (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file))
   8556   (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest)))
   8557 
   8558 
   8559 ;; VSCode marketplace
   8560 
   8561 (defcustom lsp-vscode-ext-url
   8562   "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s"
   8563   "Vscode extension template url."
   8564   :group 'lsp-mode
   8565   :type 'string
   8566   :package-version '(lsp-mode . "8.0.0"))
   8567 
   8568 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform)
   8569   "Return the URL to vscode extension.
   8570 PUBLISHER is the extension publisher.
   8571 NAME is the name of the extension.
   8572 VERSION is the version of the extension.
   8573 TARGETPLATFORM is the targetPlatform of the extension."
   8574   (format lsp-vscode-ext-url publisher name version (or targetPlatform "")))
   8575 
   8576 
   8577 
   8578 ;; Queueing prompts
   8579 
   8580 (defvar lsp--question-queue nil
   8581   "List of questions yet to be asked by `lsp-ask-question'.")
   8582 
   8583 (defun lsp-ask-question (question options callback)
   8584   "Prompt the user to answer the QUESTION with one of the OPTIONS from the
   8585 minibuffer. Once the user selects an option, the CALLBACK function will be
   8586 called, passing the selected option to it.
   8587 
   8588 If the user is currently being shown a question, the question will be stored in
   8589 `lsp--question-queue', and will be asked once the user has answered the current
   8590 question."
   8591   (add-to-list 'lsp--question-queue `(("question" . ,question)
   8592                                       ("options" . ,options)
   8593                                       ("callback" . ,callback)) t)
   8594   (when (eq (length lsp--question-queue) 1)
   8595     (lsp--process-question-queue)))
   8596 
   8597 (defun lsp--process-question-queue ()
   8598   "Take the first question from `lsp--question-queue', process it, then process
   8599 the next question until the queue is empty."
   8600   (-let* (((&alist "question" "options" "callback") (car lsp--question-queue))
   8601           (answer (completing-read question options nil t)))
   8602     (pop lsp--question-queue)
   8603     (funcall callback answer)
   8604     (when lsp--question-queue
   8605       (lsp--process-question-queue))))
   8606 
   8607 (defun lsp--supports-buffer? (client)
   8608   (and
   8609    ;; both file and client remote or both local
   8610    (eq (---truthy? (file-remote-p (buffer-file-name)))
   8611        (---truthy? (lsp--client-remote? client)))
   8612 
   8613    ;; activation function or major-mode match.
   8614    (if-let ((activation-fn (lsp--client-activation-fn client)))
   8615        (funcall activation-fn (buffer-file-name) major-mode)
   8616      (-contains? (lsp--client-major-modes client) major-mode))
   8617 
   8618    ;; check whether it is enabled if `lsp-enabled-clients' is not null
   8619    (or (null lsp-enabled-clients)
   8620        (or (member (lsp--client-server-id client) lsp-enabled-clients)
   8621            (ignore (lsp--info "Client %s is not in lsp-enabled-clients"
   8622                               (lsp--client-server-id client)))))
   8623 
   8624    ;; check whether it is not disabled.
   8625    (not (lsp--client-disabled-p major-mode (lsp--client-server-id client)))))
   8626 
   8627 (defun lsp--filter-clients (pred)
   8628   (->> lsp-clients hash-table-values (-filter pred)))
   8629 
   8630 (defun lsp--find-clients ()
   8631   "Find clients which can handle current buffer."
   8632   (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   8633                                                             #'lsp--server-binary-present?)))
   8634     (lsp-log "Found the following clients for %s: %s"
   8635              (buffer-file-name)
   8636              (s-join ", "
   8637                      (-map (lambda (client)
   8638                              (format "(server-id %s, priority %s)"
   8639                                      (lsp--client-server-id client)
   8640                                      (lsp--client-priority client)))
   8641                            matching-clients)))
   8642     (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients))
   8643             (selected-clients (if-let ((main-client (and main-clients
   8644                                                          (--max-by (> (lsp--client-priority it)
   8645                                                                       (lsp--client-priority other))
   8646                                                                    main-clients))))
   8647                                   (cons main-client add-on-clients)
   8648                                 add-on-clients)))
   8649       (lsp-log "The following clients were selected based on priority: %s"
   8650                (s-join ", "
   8651                        (-map (lambda (client)
   8652                                (format "(server-id %s, priority %s)"
   8653                                        (lsp--client-server-id client)
   8654                                        (lsp--client-priority client)))
   8655                              selected-clients)))
   8656       selected-clients)))
   8657 
   8658 (defun lsp-workspace-remove-all-folders()
   8659   "Delete all lsp tracked folders."
   8660   (interactive)
   8661   (--each (lsp-session-folders (lsp-session))
   8662     (lsp-workspace-folders-remove it)))
   8663 
   8664 (defun lsp-register-client (client)
   8665   "Registers LSP client CLIENT."
   8666   (let ((client-id (lsp--client-server-id client)))
   8667     (puthash client-id client lsp-clients)
   8668     (setplist (intern (format "lsp-%s-after-open-hook" client-id))
   8669               `( standard-value (nil) custom-type hook
   8670                  custom-package-version (lsp-mode . "7.0.1")
   8671                  variable-documentation ,(format "Hooks to run after `%s' server is run." client-id)
   8672                  custom-requests nil)))
   8673   (when (and lsp-auto-register-remote-clients
   8674              (not (lsp--client-remote? client)))
   8675     (let ((remote-client (copy-lsp--client client)))
   8676       (setf (lsp--client-remote? remote-client) t
   8677             (lsp--client-server-id remote-client) (intern
   8678                                                    (format "%s-tramp"
   8679                                                            (lsp--client-server-id client)))
   8680             ;; disable automatic download
   8681             (lsp--client-download-server-fn remote-client) nil)
   8682       (lsp-register-client remote-client))))
   8683 
   8684 (defun lsp--create-initialization-options (_session client)
   8685   "Create initialization-options from SESSION and CLIENT.
   8686 Add workspace folders depending on server being multiroot and
   8687 session workspace folder configuration for the server."
   8688   (let* ((initialization-options-or-fn (lsp--client-initialization-options client)))
   8689     (if (functionp initialization-options-or-fn)
   8690         (funcall initialization-options-or-fn)
   8691       initialization-options-or-fn)))
   8692 
   8693 (defvar lsp-client-settings (make-hash-table :test 'equal)
   8694   "For internal use, any external users please use
   8695   `lsp-register-custom-settings' function instead")
   8696 
   8697 (defun lsp-register-custom-settings (props)
   8698   "Register PROPS.
   8699 PROPS is list of triple (path value boolean?) where PATH is the path to the
   8700 property; VALUE can be a literal value, symbol to be evaluated, or either a
   8701 function or lambda function to be called without arguments; BOOLEAN? is an
   8702 optional flag that should be non-nil for boolean settings, when it is nil the
   8703 property will be ignored if the VALUE is nil.
   8704 
   8705 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))'
   8706 \(note the double parentheses)"
   8707   (mapc
   8708    (-lambda ((path . rest))
   8709      (puthash path rest lsp-client-settings))
   8710    props))
   8711 
   8712 (defun lsp-region-text (region)
   8713   "Get the text for REGION in current buffer."
   8714   (-let (((start . end) (lsp--range-to-region region)))
   8715     (buffer-substring-no-properties start end)))
   8716 
   8717 (defun lsp-ht-set (tbl paths value)
   8718   "Set nested hash table value.
   8719 TBL - a hash table, PATHS is the path to the nested VALUE."
   8720   (pcase paths
   8721     (`(,path) (ht-set! tbl path value))
   8722     (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl)
   8723                                            (let ((temp-tbl (ht)))
   8724                                              (ht-set! tbl path temp-tbl)
   8725                                              temp-tbl))))
   8726                        (lsp-ht-set nested-tbl rst value)))))
   8727 
   8728 ;; sections
   8729 
   8730 (defalias 'defcustom-lsp 'lsp-defcustom)
   8731 
   8732 (defmacro lsp-defcustom (symbol standard doc &rest args)
   8733   "Defines `lsp-mode' server property."
   8734   (declare (doc-string 3) (debug (name body))
   8735            (indent defun))
   8736   (let ((path (plist-get args :lsp-path))
   8737         (setter (intern (concat (symbol-name symbol) "--set"))))
   8738     (cl-remf args :lsp-path)
   8739     `(progn
   8740        (lsp-register-custom-settings
   8741         (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type))))))
   8742 
   8743        (defcustom ,symbol ,standard ,doc ,@args)
   8744 
   8745        ;; Use a variable watcher instead of registering a `defcustom'
   8746        ;; setter since `hack-local-variables' is not aware of custom
   8747        ;; setters and won't invoke them.
   8748 
   8749        (defun ,setter (sym val op _where)
   8750          (when (eq op 'set)
   8751            (lsp--set-custom-property sym val ,path)))
   8752 
   8753        (add-variable-watcher ',symbol #',setter))))
   8754 
   8755 (defun lsp--set-custom-property (sym val path)
   8756   (set sym val)
   8757   (let ((section (cl-first (s-split "\\." path))))
   8758     (mapc (lambda (workspace)
   8759             (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace))
   8760                               section)
   8761               (with-lsp-workspace workspace
   8762                 (lsp--set-configuration (lsp-configuration-section section)))))
   8763           (lsp--session-workspaces (lsp-session)))))
   8764 
   8765 (defun lsp-configuration-section (section)
   8766   "Get settings for SECTION."
   8767   (let ((ret (ht-create)))
   8768     (maphash (-lambda (path (variable boolean?))
   8769                (when (s-matches? (concat (regexp-quote section) "\\..*") path)
   8770                  (let* ((symbol-value (-> variable
   8771                                           lsp-resolve-value
   8772                                           lsp-resolve-value))
   8773                         (value (if (and boolean? (not symbol-value))
   8774                                    :json-false
   8775                                  symbol-value)))
   8776                    (when (or boolean? value)
   8777                      (lsp-ht-set ret (s-split "\\." path) value)))))
   8778              lsp-client-settings)
   8779     ret))
   8780 
   8781 
   8782 (defun lsp--start-connection (session client project-root)
   8783   "Initiates connection created from CLIENT for PROJECT-ROOT.
   8784 SESSION is the active session."
   8785   (when (lsp--client-multi-root client)
   8786     (cl-pushnew project-root (gethash (lsp--client-server-id client)
   8787                                       (lsp-session-server-id->folders session))))
   8788   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)
   8789 
   8790   (unwind-protect
   8791       (lsp--start-workspace session client project-root (lsp--create-initialization-options session client))
   8792     (lsp--spinner-stop)))
   8793 
   8794 ;; lsp-log-io-mode
   8795 
   8796 (defvar lsp-log-io-mode-map
   8797   (let ((map (make-sparse-keymap)))
   8798     (define-key map (kbd "M-n") #'lsp-log-io-next)
   8799     (define-key map (kbd "M-p") #'lsp-log-io-prev)
   8800     (define-key map (kbd "k") #'lsp--erase-log-buffer)
   8801     (define-key map (kbd "K") #'lsp--erase-session-log-buffers)
   8802     map)
   8803   "Keymap for lsp log buffer mode.")
   8804 
   8805 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo"
   8806   "Special mode for viewing IO logs.")
   8807 
   8808 (defun lsp-workspace-show-log (workspace)
   8809   "Display the log buffer of WORKSPACE."
   8810   (interactive
   8811    (list (if lsp-log-io
   8812              (if (eq (length (lsp-workspaces)) 1)
   8813                  (cl-first (lsp-workspaces))
   8814                (lsp--completing-read "Workspace: " (lsp-workspaces)
   8815                                      #'lsp--workspace-print nil t))
   8816            (user-error "IO logging is disabled"))))
   8817   (pop-to-buffer (lsp--get-log-buffer-create workspace)))
   8818 
   8819 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log)
   8820 
   8821 (defun lsp--get-log-buffer-create (workspace)
   8822   "Return the lsp log buffer of WORKSPACE, creating a new one if needed."
   8823   (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8824          (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id)))
   8825     (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid))))
   8826 
   8827 (defun lsp--erase-log-buffer (&optional all)
   8828   "Delete contents of current lsp log buffer.
   8829 When ALL is t, erase all log buffers of the running session."
   8830   (interactive)
   8831   (let* ((workspaces (lsp--session-workspaces (lsp-session)))
   8832          (current-log-buffer (current-buffer)))
   8833     (dolist (w workspaces)
   8834       (let ((b (lsp--get-log-buffer-create w)))
   8835         (when (or all (eq b current-log-buffer))
   8836           (with-current-buffer b
   8837             (let ((inhibit-read-only t))
   8838               (erase-buffer))))))))
   8839 
   8840 (defun lsp--erase-session-log-buffers ()
   8841   "Erase log buffers of the running session."
   8842   (interactive)
   8843   (lsp--erase-log-buffer t))
   8844 
   8845 (defun lsp-log-io-next (arg)
   8846   "Move to next log entry."
   8847   (interactive "P")
   8848   (ewoc-goto-next lsp--log-io-ewoc (or arg 1)))
   8849 
   8850 (defun lsp-log-io-prev (arg)
   8851   "Move to previous log entry."
   8852   (interactive "P")
   8853   (ewoc-goto-prev lsp--log-io-ewoc (or arg 1)))
   8854 
   8855 
   8856 
   8857 (cl-defmethod lsp-process-id ((process process))
   8858   (process-id process))
   8859 
   8860 (cl-defmethod lsp-process-name ((process process)) (process-name process))
   8861 
   8862 (cl-defmethod lsp-process-status ((process process)) (process-status process))
   8863 
   8864 (cl-defmethod lsp-process-kill ((process process))
   8865   (when (process-live-p process)
   8866     (kill-process process)))
   8867 
   8868 (cl-defmethod lsp-process-send ((process process) message)
   8869   (condition-case err
   8870       (process-send-string process (lsp--make-message message))
   8871     (error (lsp--error "Sending to process failed with the following error: %s"
   8872                        (error-message-string err)))))
   8873 
   8874 (cl-defmethod lsp-process-cleanup (process)
   8875   ;; Kill standard error buffer only if the process exited normally.
   8876   ;; Leave it intact otherwise for debugging purposes.
   8877   (let ((buffer (-> process process-name get-buffer)))
   8878     (when (and (eq (process-status process) 'exit)
   8879                (zerop (process-exit-status process))
   8880                (buffer-live-p buffer))
   8881       (kill-buffer buffer))))
   8882 
   8883 
   8884 ;; native JSONRPC
   8885 
   8886 (declare-function json-rpc "ext:json")
   8887 (declare-function json-rpc-connection "ext:json")
   8888 (declare-function json-rpc-send "ext:json")
   8889 (declare-function json-rpc-shutdown "ext:json")
   8890 (declare-function json-rpc-stderr "ext:json")
   8891 (declare-function json-rpc-pid "ext:json")
   8892 
   8893 (defvar lsp-json-rpc-thread nil)
   8894 (defvar lsp-json-rpc-queue nil)
   8895 (defvar lsp-json-rpc-done nil)
   8896 (defvar lsp-json-rpc-mutex (make-mutex))
   8897 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex))
   8898 
   8899 (defun lsp-json-rpc-process-queue ()
   8900   (while (not lsp-json-rpc-done)
   8901     (while lsp-json-rpc-queue
   8902       (-let (((proc . message) (pop lsp-json-rpc-queue)))
   8903         (json-rpc-send
   8904          proc message
   8905          :null-object nil
   8906          :false-object :json-false)))
   8907     (with-mutex lsp-json-rpc-mutex
   8908       (condition-wait lsp-json-rpc-condition))))
   8909 
   8910 (cl-defmethod lsp-process-id (process) (json-rpc-pid process))
   8911 
   8912 (cl-defmethod lsp-process-name (_process) "TBD")
   8913 
   8914 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process))
   8915 
   8916 (cl-defmethod lsp-process-send (proc message)
   8917   (unless lsp-json-rpc-thread
   8918     (with-current-buffer (get-buffer-create " *json-rpc*")
   8919       (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*"))))
   8920 
   8921   (with-mutex lsp-json-rpc-mutex
   8922     (setq lsp-json-rpc-queue (append lsp-json-rpc-queue
   8923                                      (list (cons proc message))))
   8924     (condition-notify lsp-json-rpc-condition)))
   8925 
   8926 (cl-defmethod lsp-process-cleanup (_proc))
   8927 
   8928 (defun lsp-json-rpc-connection (workspace command)
   8929   (let ((con (apply #'json-rpc-connection command))
   8930         (object-type (if lsp-use-plists 'plist 'hash-table)))
   8931     (with-current-buffer (get-buffer-create " *json-rpc*")
   8932       (make-thread
   8933        (lambda ()
   8934          (json-rpc
   8935           con
   8936           (lambda (result err done)
   8937             (run-with-timer
   8938              0.0
   8939              nil
   8940              (lambda ()
   8941                (cond
   8942                 (result (lsp--parser-on-message result workspace))
   8943                 (err (warn "Json parsing failed with the following error: %s" err))
   8944                 (done (lsp--handle-process-exit workspace ""))))))
   8945           :object-type object-type
   8946           :null-object nil
   8947           :false-object nil))
   8948        "*json-rpc-connection*"))
   8949     (cons con con)))
   8950 
   8951 (defun lsp-json-rpc-stderr ()
   8952   (interactive)
   8953   (--when-let (pcase (lsp-workspaces)
   8954                 (`nil (user-error "There are no active servers in the current buffer"))
   8955                 (`(,workspace) workspace)
   8956                 (workspaces (lsp--completing-read "Select server: "
   8957                                                   workspaces
   8958                                                   'lsp--workspace-print nil t)))
   8959     (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it)))
   8960           (buffer (format "*stderr-%s*" (lsp--workspace-print it)) ))
   8961       (with-current-buffer (get-buffer-create buffer)
   8962         (with-help-window buffer
   8963           (insert content))))))
   8964 
   8965 
   8966 (defun lsp--workspace-print (workspace)
   8967   "Visual representation WORKSPACE."
   8968   (let* ((proc (lsp--workspace-cmd-proc workspace))
   8969          (status (lsp--workspace-status workspace))
   8970          (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8971          (pid (lsp-process-id proc)))
   8972 
   8973     (if (eq 'initialized status)
   8974         (format "%s:%s" server-id pid)
   8975       (format "%s:%s/%s" server-id pid status))))
   8976 
   8977 (defun lsp--map-tree-widget (m)
   8978   "Build `tree-widget' from a hash-table or plist M."
   8979   (when (lsp-structure-p m)
   8980     (let (nodes)
   8981       (lsp-map (lambda (k v)
   8982                  (push `(tree-widget
   8983                          :tag ,(if (lsp-structure-p v)
   8984                                    (format "%s:" k)
   8985                                  (format "%s: %s" k
   8986                                          (propertize (format "%s" v)
   8987                                                      'face
   8988                                                      'font-lock-string-face)))
   8989                          :open t
   8990                          ,@(lsp--map-tree-widget v))
   8991                        nodes))
   8992                m)
   8993       nodes)))
   8994 
   8995 (defun lsp-buffer-name (buffer-id)
   8996   (if-let ((buffer-name (plist-get buffer-id :buffer-name)))
   8997       (funcall buffer-name buffer-id)
   8998     (buffer-name buffer-id)))
   8999 
   9000 (defun lsp--render-workspace (workspace)
   9001   "Tree node representation of WORKSPACE."
   9002   `(tree-widget :tag ,(lsp--workspace-print workspace)
   9003                 :open t
   9004                 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face)
   9005                              :open t
   9006                              ,@(->> workspace
   9007                                     (lsp--workspace-buffers)
   9008                                     (--map `(tree-widget
   9009                                              :tag ,(when (lsp-buffer-live-p it)
   9010                                                      (let ((buffer-name (lsp-buffer-name it)))
   9011                                                        (if (lsp-with-current-buffer it buffer-read-only)
   9012                                                            (propertize buffer-name 'face 'font-lock-constant-face)
   9013                                                          buffer-name)))))))
   9014                 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face)
   9015                              ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget))))
   9016 
   9017 (define-derived-mode lsp-browser-mode special-mode "LspBrowser"
   9018   "Define mode for displaying lsp sessions."
   9019   (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t)))))
   9020 
   9021 (defun lsp-describe-session ()
   9022   "Describes current `lsp-session'."
   9023   (interactive)
   9024   (let ((session (lsp-session))
   9025         (buf (get-buffer-create "*lsp session*"))
   9026         (root (lsp-workspace-root)))
   9027     (with-current-buffer buf
   9028       (lsp-browser-mode)
   9029       (let ((inhibit-read-only t))
   9030         (erase-buffer)
   9031         (--each (lsp-session-folders session)
   9032           (widget-create
   9033            `(tree-widget
   9034              :tag ,(propertize it 'face 'font-lock-keyword-face)
   9035              :open t
   9036              ,@(->> session
   9037                     (lsp-session-folder->servers)
   9038                     (gethash it)
   9039                     (-map 'lsp--render-workspace)))))))
   9040     (pop-to-buffer buf)
   9041     (goto-char (point-min))
   9042     (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag)
   9043              until (or (and root (string= tag root)) (eobp))
   9044              do (goto-char (next-overlay-change (point))))))
   9045 
   9046 (defun lsp--session-workspaces (session)
   9047   "Get all workspaces that are part of the SESSION."
   9048   (-> session lsp-session-folder->servers hash-table-values -flatten -uniq))
   9049 
   9050 (defun lsp--find-multiroot-workspace (session client project-root)
   9051   "Look for a multiroot connection in SESSION created from CLIENT for
   9052 PROJECT-ROOT and BUFFER-MAJOR-MODE."
   9053   (when (lsp--client-multi-root client)
   9054     (-when-let (multi-root-workspace (->> session
   9055                                           (lsp--session-workspaces)
   9056                                           (--first (eq (-> it lsp--workspace-client lsp--client-server-id)
   9057                                                        (lsp--client-server-id client)))))
   9058       (with-lsp-workspace multi-root-workspace
   9059         (lsp-notify "workspace/didChangeWorkspaceFolders"
   9060                     (lsp-make-did-change-workspace-folders-params
   9061                      :event (lsp-make-workspace-folders-change-event
   9062                              :added (vector (lsp-make-workspace-folder
   9063                                              :uri (lsp--path-to-uri project-root)
   9064                                              :name (f-filename project-root)))
   9065                              :removed []))))
   9066 
   9067       (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace))
   9068       (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root))
   9069 
   9070       (lsp--persist-session session)
   9071 
   9072       (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace))
   9073       (lsp--open-in-workspace multi-root-workspace)
   9074 
   9075       multi-root-workspace)))
   9076 
   9077 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder)
   9078   "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT.
   9079 IGNORE-MULTI-FOLDER to ignore multi folder server."
   9080   (-map (lambda (client)
   9081           (or
   9082            (lsp--find-workspace session client project-root)
   9083            (unless ignore-multi-folder
   9084              (lsp--find-multiroot-workspace session client project-root))
   9085            (lsp--start-connection session client project-root)))
   9086         clients))
   9087 
   9088 (defun lsp--spinner-stop ()
   9089   "Stop the spinner in case all of the workspaces are started."
   9090   (when (--all? (eq (lsp--workspace-status it) 'initialized)
   9091                 lsp--buffer-workspaces)
   9092     (spinner-stop)))
   9093 
   9094 (defun lsp--open-in-workspace (workspace)
   9095   "Open in existing WORKSPACE."
   9096   (if (eq 'initialized (lsp--workspace-status workspace))
   9097       ;; when workspace is initialized just call document did open.
   9098       (progn
   9099         (with-lsp-workspace workspace
   9100           (when-let ((before-document-open-fn (-> workspace
   9101                                                   lsp--workspace-client
   9102                                                   lsp--client-before-file-open-fn)))
   9103             (funcall before-document-open-fn workspace))
   9104           (lsp--text-document-did-open))
   9105         (lsp--spinner-stop))
   9106     ;; when it is not initialized
   9107     (lsp--spinner-start)
   9108     (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace))))
   9109 
   9110 (defun lsp--find-workspace (session client project-root)
   9111   "Find server connection created with CLIENT in SESSION for PROJECT-ROOT."
   9112   (when-let ((workspace (->> session
   9113                              (lsp-session-folder->servers)
   9114                              (gethash project-root)
   9115                              (--first (eql (-> it lsp--workspace-client lsp--client-server-id)
   9116                                            (lsp--client-server-id client))))))
   9117     (lsp--open-in-workspace workspace)
   9118     workspace))
   9119 
   9120 (defun lsp--read-char (prompt &optional options)
   9121   "Wrapper for `read-char-from-minibuffer' if Emacs +27.
   9122 Fallback to `read-key' otherwise.
   9123 PROMPT is the message and OPTIONS the available options."
   9124   (if (fboundp 'read-char-from-minibuffer)
   9125       (read-char-from-minibuffer prompt options)
   9126     (read-key prompt)))
   9127 
   9128 (defun lsp--find-root-interactively (session)
   9129   "Find project interactively.
   9130 Returns nil if the project should not be added to the current SESSION."
   9131   (condition-case nil
   9132       (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory))
   9133              (action (lsp--read-char
   9134                       (format
   9135                        "%s is not part of any project.
   9136 
   9137 %s ==> Import project root %s
   9138 %s ==> Import project by selecting root directory interactively
   9139 %s ==> Import project at current directory %s
   9140 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist
   9141 %s ==> Do not ask again for the current project by selecting ignore path interactively
   9142 %s ==> Do nothing: ask again when opening other files from the current project
   9143 
   9144 Select action: "
   9145                        (propertize (buffer-name) 'face 'bold)
   9146                        (propertize "i" 'face 'success)
   9147                        (propertize project-root-suggestion 'face 'bold)
   9148                        (propertize "I" 'face 'success)
   9149                        (propertize "." 'face 'success)
   9150                        (propertize default-directory 'face 'bold)
   9151                        (propertize "d" 'face 'warning)
   9152                        (propertize project-root-suggestion 'face 'bold)
   9153                        (propertize "D" 'face 'warning)
   9154                        (propertize "n" 'face 'warning))
   9155                       '(?i ?\r ?I ?. ?d ?D ?n))))
   9156         (cl-case action
   9157           (?i project-root-suggestion)
   9158           (?\r project-root-suggestion)
   9159           (?I (read-directory-name "Select workspace folder to add: "
   9160                                    (or project-root-suggestion default-directory)
   9161                                    nil
   9162                                    t))
   9163           (?. default-directory)
   9164           (?d (push project-root-suggestion (lsp-session-folders-blocklist session))
   9165               (lsp--persist-session session)
   9166               nil)
   9167           (?D (push (read-directory-name "Select folder to blocklist: "
   9168                                          (or project-root-suggestion default-directory)
   9169                                          nil
   9170                                          t)
   9171                     (lsp-session-folders-blocklist session))
   9172               (lsp--persist-session session)
   9173               nil)
   9174           (t nil)))
   9175     (quit)))
   9176 
   9177 (declare-function tramp-file-name-host "ext:tramp" (file) t)
   9178 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault))
   9179 
   9180 (defun lsp--files-same-host (f1 f2)
   9181   "Predicate on whether or not two files are on the same host."
   9182   (or (not (or (file-remote-p f1) (file-remote-p f2)))
   9183       (and (file-remote-p f1)
   9184            (file-remote-p f2)
   9185            (progn (require 'tramp)
   9186                   (equal (tramp-file-name-host (tramp-dissect-file-name f1))
   9187                          (tramp-file-name-host (tramp-dissect-file-name f2)))))))
   9188 
   9189 (defun lsp-find-session-folder (session file-name)
   9190   "Look in the current SESSION for folder containing FILE-NAME."
   9191   (let ((file-name-canonical (lsp-f-canonical file-name)))
   9192     (->> session
   9193          (lsp-session-folders)
   9194          (--filter (and (lsp--files-same-host it file-name-canonical)
   9195                         (or (lsp-f-same? it file-name-canonical)
   9196                             (and (f-dir? it)
   9197                                  (lsp-f-ancestor-of? it file-name-canonical)))))
   9198          (--max-by (> (length it)
   9199                       (length other))))))
   9200 
   9201 (defun lsp-find-workspace (server-id &optional file-name)
   9202   "Find workspace for SERVER-ID for FILE-NAME."
   9203   (-when-let* ((session (lsp-session))
   9204                (folder->servers (lsp-session-folder->servers session))
   9205                (workspaces (if file-name
   9206                                (gethash (lsp-find-session-folder session file-name) folder->servers)
   9207                              (lsp--session-workspaces session))))
   9208 
   9209     (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces)))
   9210 
   9211 (defun lsp--calculate-root (session file-name)
   9212   "Calculate project root for FILE-NAME in SESSION."
   9213   (and
   9214    (->> session
   9215         (lsp-session-folders-blocklist)
   9216         (--first (and (lsp--files-same-host it file-name)
   9217                       (lsp-f-ancestor-of? it file-name)
   9218                       (prog1 t
   9219                         (lsp--info "File %s is in blocklisted directory %s" file-name it))))
   9220         not)
   9221    (or
   9222     (when lsp-auto-guess-root
   9223       (lsp--suggest-project-root))
   9224     (unless lsp-guess-root-without-session
   9225       (lsp-find-session-folder session file-name))
   9226     (unless lsp-auto-guess-root
   9227       (when-let ((root-folder (lsp--find-root-interactively session)))
   9228         (if (or (not (f-equal? root-folder (expand-file-name "~/")))
   9229                 (yes-or-no-p
   9230                  (concat
   9231                   (propertize "[WARNING] " 'face 'warning)
   9232                   "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:
   9233 
   9234 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/').
   9235 2. If your file is under `~/' then create a subfolder and move that file in this folder.
   9236 
   9237 Type `No' to go back to project selection.
   9238 Type `Yes' to confirm `HOME' as project root.
   9239 Type `C-g' to cancel project import process and stop `lsp'")))
   9240             root-folder
   9241           (lsp--calculate-root session file-name)))))))
   9242 
   9243 (defun lsp--try-open-in-library-workspace ()
   9244   "Try opening current file as library file in any of the active workspace.
   9245 The library folders are defined by each client for each of the active workspace."
   9246   (when-let ((workspace (->> (lsp-session)
   9247                              (lsp--session-workspaces)
   9248                              ;; Sort the last active workspaces first as they are more likely to be
   9249                              ;; the correct ones, especially when jumping to a definition.
   9250                              (-sort (lambda (a _b)
   9251                                       (-contains? lsp--last-active-workspaces a)))
   9252                              (--first
   9253                               (and (-> it lsp--workspace-client lsp--supports-buffer?)
   9254                                    (when-let ((library-folders-fn
   9255                                                (-> it lsp--workspace-client lsp--client-library-folders-fn)))
   9256                                      (-first (lambda (library-folder)
   9257                                                (lsp-f-ancestor-of? library-folder (buffer-file-name)))
   9258                                              (funcall library-folders-fn it))))))))
   9259     (lsp--open-in-workspace workspace)
   9260     (view-mode t)
   9261     (lsp--info "Opening read-only library file %s." (buffer-file-name))
   9262     (list workspace)))
   9263 
   9264 (defun lsp--persist-session (session)
   9265   "Persist SESSION to `lsp-session-file'."
   9266   (lsp--persist lsp-session-file (make-lsp-session
   9267                                   :folders (lsp-session-folders session)
   9268                                   :folders-blocklist (lsp-session-folders-blocklist session)
   9269                                   :server-id->folders (lsp-session-server-id->folders session))))
   9270 
   9271 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder)
   9272   "Try create opening file as a project file.
   9273 When IGNORE-MULTI-FOLDER is t the lsp mode will start new
   9274 language server even if there is language server which can handle
   9275 current language. When IGNORE-MULTI-FOLDER is nil current file
   9276 will be opened in multi folder language server if there is
   9277 such."
   9278   (-let ((session (lsp-session)))
   9279     (-if-let (clients (if ask-for-client
   9280                           (list (lsp--completing-read "Select server to start: "
   9281                                                       (ht-values lsp-clients)
   9282                                                       (-compose 'symbol-name 'lsp--client-server-id) nil t))
   9283                         (lsp--find-clients)))
   9284         (-if-let (project-root (-some-> session
   9285                                  (lsp--calculate-root (buffer-file-name))
   9286                                  (lsp-f-canonical)))
   9287             (progn
   9288               ;; update project roots if needed and persist the lsp session
   9289               (unless (-contains? (lsp-session-folders session) project-root)
   9290                 (cl-pushnew project-root (lsp-session-folders session))
   9291                 (lsp--persist-session session))
   9292               (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder))
   9293           (lsp--warn "%s not in project or it is blocklisted." (buffer-name))
   9294           nil)
   9295       (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode)
   9296       nil)))
   9297 
   9298 (defun lsp-shutdown-workspace ()
   9299   "Shutdown language server."
   9300   (interactive)
   9301   (--when-let (pcase (lsp-workspaces)
   9302                 (`nil (user-error "There are no active servers in the current buffer"))
   9303                 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?"
   9304                                                        (lsp--workspace-print workspace)))
   9305                                  workspace))
   9306                 (workspaces (lsp--completing-read "Select server: "
   9307                                                   workspaces
   9308                                                   'lsp--workspace-print nil t)))
   9309     (lsp-workspace-shutdown it)))
   9310 
   9311 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1")
   9312 
   9313 (defcustom lsp-auto-select-workspace t
   9314   "Shutdown or restart a single workspace.
   9315 If set and the current buffer has only a single workspace
   9316 associated with it, `lsp-shutdown-workspace' and
   9317 `lsp-restart-workspace' will act on it without asking."
   9318   :type 'boolean
   9319   :group 'lsp-mode)
   9320 
   9321 (defun lsp--read-workspace ()
   9322   "Ask the user to select a workspace.
   9323 Errors if there are none."
   9324   (pcase (lsp-workspaces)
   9325     (`nil (error "No workspaces associated with the current buffer"))
   9326     ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace)
   9327     (workspaces (lsp--completing-read "Select workspace: " workspaces
   9328                                       #'lsp--workspace-print nil t))))
   9329 
   9330 (defun lsp-workspace-shutdown (workspace)
   9331   "Shut the workspace WORKSPACE and the language server associated with it"
   9332   (interactive (list (lsp--read-workspace)))
   9333   (lsp--warn "Stopping %s" (lsp--workspace-print workspace))
   9334   (with-lsp-workspace workspace (lsp--shutdown-workspace)))
   9335 
   9336 (defun lsp-disconnect ()
   9337   "Disconnect the buffer from the language server."
   9338   (interactive)
   9339   (lsp--text-document-did-close t)
   9340   (lsp-managed-mode -1)
   9341   (lsp-mode -1)
   9342   (setq lsp--buffer-workspaces nil)
   9343   (lsp--info "Disconnected"))
   9344 
   9345 (defun lsp-restart-workspace ()
   9346   (interactive)
   9347   (--when-let (pcase (lsp-workspaces)
   9348                 (`nil (user-error "There are no active servers in the current buffer"))
   9349                 (`(,workspace) workspace)
   9350                 (workspaces (lsp--completing-read "Select server: "
   9351                                                   workspaces
   9352                                                   'lsp--workspace-print nil t)))
   9353     (lsp-workspace-restart it)))
   9354 
   9355 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1")
   9356 
   9357 (defun lsp-workspace-restart (workspace)
   9358   "Restart the workspace WORKSPACE and the language server associated with it"
   9359   (interactive (list (lsp--read-workspace)))
   9360   (lsp--warn "Restarting %s" (lsp--workspace-print workspace))
   9361   (with-lsp-workspace workspace (lsp--shutdown-workspace t)))
   9362 
   9363 ;;;###autoload
   9364 (defun lsp (&optional arg)
   9365   "Entry point for the server startup.
   9366 When ARG is t the lsp mode will start new language server even if
   9367 there is language server which can handle current language. When
   9368 ARG is nil current file will be opened in multi folder language
   9369 server if there is such. When `lsp' is called with prefix
   9370 argument ask the user to select which language server to start."
   9371   (interactive "P")
   9372 
   9373   (lsp--require-packages)
   9374 
   9375   (when (buffer-file-name)
   9376     (let (clients
   9377           (matching-clients (lsp--filter-clients
   9378                              (-andfn #'lsp--supports-buffer?
   9379                                      #'lsp--server-binary-present?))))
   9380       (cond
   9381        (matching-clients
   9382         (when (setq lsp--buffer-workspaces
   9383                     (or (and
   9384                          ;; Don't open as library file if file is part of a project.
   9385                          (not (lsp-find-session-folder (lsp-session) (buffer-file-name)))
   9386                          (lsp--try-open-in-library-workspace))
   9387                         (lsp--try-project-root-workspaces (equal arg '(4))
   9388                                                           (and arg (not (equal arg 1))))))
   9389           (lsp-mode 1)
   9390           (when lsp-auto-configure (lsp--auto-configure))
   9391           (setq lsp-buffer-uri (lsp--buffer-uri))
   9392           (lsp--info "Connected to %s."
   9393                      (apply 'concat (--map (format "[%s %s]"
   9394                                                    (lsp--workspace-print it)
   9395                                                    (lsp--workspace-root it))
   9396                                            lsp--buffer-workspaces)))))
   9397        ;; look for servers which are currently being downloaded.
   9398        ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9399                                                    #'lsp--client-download-in-progress?)))
   9400         (lsp--info "There are language server(%s) installation in progress.
   9401 The server(s) will be started in the buffer when it has finished."
   9402                    (-map #'lsp--client-server-id clients))
   9403         (seq-do (lambda (client)
   9404                   (cl-pushnew (current-buffer) (lsp--client-buffers client)))
   9405                 clients))
   9406        ;; look for servers to install
   9407        ((setq clients (lsp--filter-clients
   9408                        (-andfn #'lsp--supports-buffer?
   9409                                (-const lsp-enable-suggest-server-download)
   9410                                #'lsp--client-download-server-fn
   9411                                (-not #'lsp--client-download-in-progress?))))
   9412         (let ((client (lsp--completing-read
   9413                        (concat "Unable to find installed server supporting this file. "
   9414                                "The following servers could be installed automatically: ")
   9415                        clients
   9416                        (-compose #'symbol-name #'lsp--client-server-id)
   9417                        nil
   9418                        t)))
   9419           (cl-pushnew (current-buffer) (lsp--client-buffers client))
   9420           (lsp--install-server-internal client)))
   9421        ;; ignore other warnings
   9422        ((not lsp-warn-no-matched-clients)
   9423         nil)
   9424        ;; automatic installation disabled
   9425        ((setq clients (unless matching-clients
   9426                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9427                                                      #'lsp--client-download-server-fn
   9428                                                      (-not (-const lsp-enable-suggest-server-download))
   9429                                                      (-not #'lsp--server-binary-present?)))))
   9430         (lsp--warn "The following servers support current file but automatic download is disabled: %s
   9431 \(If you have already installed the server check *lsp-log*)."
   9432                    (mapconcat (lambda (client)
   9433                                 (symbol-name (lsp--client-server-id client)))
   9434                               clients
   9435                               " ")))
   9436        ;; no clients present
   9437        ((setq clients (unless matching-clients
   9438                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9439                                                      (-not #'lsp--server-binary-present?)))))
   9440         (lsp--warn "The following servers support current file but do not have automatic installation: %s
   9441 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages.
   9442 \(If you have already installed the server check *lsp-log*)."
   9443                    (mapconcat (lambda (client)
   9444                                 (symbol-name (lsp--client-server-id client)))
   9445                               clients
   9446                               " ")))
   9447        ;; no matches
   9448        ((-> #'lsp--supports-buffer? lsp--filter-clients not)
   9449         (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'.
   9450 This issue might be caused by:
   9451 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'.
   9452 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'.
   9453 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/ .
   9454 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/.
   9455 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients').
   9456 You can customize `lsp-warn-no-matched-clients' to disable this message."
   9457                     major-mode major-mode major-mode))))))
   9458 
   9459 (defun lsp--buffer-visible-p ()
   9460   "Return non nil if current buffer is visible."
   9461   (or (buffer-modified-p) (get-buffer-window nil t)))
   9462 
   9463 (defun lsp--init-if-visible ()
   9464   "Run `lsp' for the current buffer if the buffer is visible.
   9465 Returns non nil if `lsp' was run for the buffer."
   9466   (when (lsp--buffer-visible-p)
   9467     (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t)
   9468     (lsp)
   9469     t))
   9470 
   9471 ;;;###autoload
   9472 (defun lsp-deferred ()
   9473   "Entry point that defers server startup until buffer is visible.
   9474 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'.
   9475 This avoids overloading the server with many files when starting Emacs."
   9476   ;; Workspace may not be initialized yet. Use a buffer local variable to
   9477   ;; remember that we deferred loading of this buffer.
   9478   (setq lsp--buffer-deferred t)
   9479   (let ((buffer (current-buffer)))
   9480     ;; Avoid false positives as desktop-mode restores buffers by deferring
   9481     ;; visibility check until the stack clears.
   9482     (run-with-idle-timer 0 nil (lambda ()
   9483                                  (when (buffer-live-p buffer)
   9484                                    (with-current-buffer buffer
   9485                                      (unless (lsp--init-if-visible)
   9486                                        (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t))))))))
   9487 
   9488 
   9489 
   9490 (defvar lsp-file-truename-cache (ht))
   9491 
   9492 (defmacro lsp-with-cached-filetrue-name (&rest body)
   9493   "Executes BODY caching the `file-truename' calls."
   9494   `(let ((old-fn (symbol-function 'file-truename)))
   9495      (unwind-protect
   9496          (progn
   9497            (fset 'file-truename
   9498                  (lambda (file-name &optional counter prev-dirs)
   9499                    (or (gethash file-name lsp-file-truename-cache)
   9500                        (puthash file-name (apply old-fn (list file-name counter prev-dirs))
   9501                                 lsp-file-truename-cache))))
   9502            ,@body)
   9503        (fset 'file-truename old-fn))))
   9504 
   9505 
   9506 (defun lsp-virtual-buffer-call (key &rest args)
   9507   (when lsp--virtual-buffer
   9508     (when-let ((fn (plist-get lsp--virtual-buffer key)))
   9509       (apply fn args))))
   9510 
   9511 (defun lsp-translate-column (column)
   9512   "Translate COLUMN taking into account virtual buffers."
   9513   (or (lsp-virtual-buffer-call :real->virtual-char column)
   9514       column))
   9515 
   9516 (defun lsp-translate-line (line)
   9517   "Translate LINE taking into account virtual buffers."
   9518   (or (lsp-virtual-buffer-call :real->virtual-line line)
   9519       line))
   9520 
   9521 
   9522 ;; lsp internal validation.
   9523 
   9524 (defmacro lsp--doctor (&rest checks)
   9525   `(-let [buf (current-buffer)]
   9526      (with-current-buffer (get-buffer-create "*lsp-performance*")
   9527        (with-help-window (current-buffer)
   9528          ,@(-map (-lambda ((msg form))
   9529                    `(insert (format "%s: %s\n" ,msg
   9530                                     (let ((res (with-current-buffer buf
   9531                                                  ,form)))
   9532                                       (cond
   9533                                        ((eq res :optional) (propertize "OPTIONAL" 'face 'warning))
   9534                                        (res (propertize "OK" 'face 'success))
   9535                                        (t (propertize "ERROR" 'face 'error)))))))
   9536                  (-partition 2 checks))))))
   9537 
   9538 (define-obsolete-function-alias 'lsp-diagnose
   9539   'lsp-doctor "lsp-mode 8.0.0")
   9540 
   9541 (defun lsp-doctor ()
   9542   "Validate performance settings."
   9543   (interactive)
   9544   (lsp--doctor
   9545    "Checking for Native JSON support" (functionp 'json-serialize)
   9546    "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max)
   9547    "Check `read-process-output-max' default has been changed from 4k"
   9548    (and (boundp 'read-process-output-max)
   9549         (> read-process-output-max 4096))
   9550    "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)"
   9551    (condition-case _err
   9552        (progn (lsp--make-message (list "a" "b"))
   9553               nil)
   9554      (error t))
   9555    "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000)
   9556    "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)
   9557    "Using emacs 28+ with native compilation?"
   9558    (or (and (fboundp 'native-comp-available-p)
   9559             (native-comp-available-p))
   9560        :optional)))
   9561 
   9562 (declare-function package-version-join "ext:package")
   9563 (declare-function package-desc-version "ext:package")
   9564 (declare-function package--alist "ext:package")
   9565 
   9566 (defun lsp-version ()
   9567   "Return string describing current version of `lsp-mode'."
   9568   (interactive)
   9569   (unless (featurep 'package)
   9570     (require 'package))
   9571   (let ((ver (format "lsp-mode %s, Emacs %s, %s"
   9572                      (package-version-join
   9573                       (package-desc-version
   9574                        (car (alist-get 'lsp-mode (package--alist)))))
   9575                      emacs-version
   9576                      system-type)))
   9577     (if (called-interactively-p 'interactive)
   9578         (lsp--info "%s" ver)
   9579       ver)))
   9580 
   9581 
   9582 
   9583 ;; org-mode/virtual-buffer
   9584 
   9585 (declare-function org-babel-get-src-block-info "ext:ob-core")
   9586 (declare-function org-do-remove-indentation "ext:org-macs")
   9587 (declare-function org-src-get-lang-mode "ext:org-src")
   9588 (declare-function org-element-context "ext:org-element")
   9589 
   9590 (defun lsp--virtual-buffer-update-position ()
   9591   (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range))
   9592                                      (funcall in-range))
   9593                                    lsp--virtual-buffer-connections))
   9594       (unless (equal virtual-buffer lsp--virtual-buffer)
   9595         (lsp-org))
   9596     (when lsp-managed-mode
   9597       (lsp-managed-mode -1)
   9598       (lsp-mode -1)
   9599       (setq lsp--buffer-workspaces nil)
   9600       (setq lsp--virtual-buffer nil)
   9601       (setq lsp-buffer-uri nil)
   9602 
   9603       ;; force refresh of diagnostics
   9604       (run-hooks 'lsp-after-diagnostics-hook))))
   9605 
   9606 (defun lsp-virtual-buffer-on-change (start end length)
   9607   "Adjust on change event to be executed against the proper language server."
   9608   (let ((max-point (max end
   9609                         (or (plist-get lsp--before-change-vals :end) 0)
   9610                         (+ start length))))
   9611     (when-let ((virtual-buffer (-first (lambda (vb)
   9612                                          (let ((lsp--virtual-buffer vb))
   9613                                            (and (lsp-virtual-buffer-call :in-range start)
   9614                                                 (lsp-virtual-buffer-call :in-range max-point))))
   9615                                        lsp--virtual-buffer-connections)))
   9616       (lsp-with-current-buffer virtual-buffer
   9617         (lsp-on-change start end length
   9618                        (lambda (&rest _)
   9619                          (list :range (lsp--range (list :character 0 :line 0)
   9620                                                   lsp--virtual-buffer-point-max)
   9621                                :text (lsp--buffer-content))))))))
   9622 
   9623 (defun lsp-virtual-buffer-before-change (start _end)
   9624   (when-let ((virtual-buffer (-first (lambda (vb)
   9625                                        (lsp-with-current-buffer vb
   9626                                          (lsp-virtual-buffer-call :in-range start)))
   9627                                      lsp--virtual-buffer-connections)))
   9628     (lsp-with-current-buffer virtual-buffer
   9629       (setq lsp--virtual-buffer-point-max
   9630             (lsp--point-to-position (lsp-virtual-buffer-call :last-point))))))
   9631 
   9632 (defun lsp-patch-on-change-event ()
   9633   (remove-hook 'after-change-functions #'lsp-on-change t)
   9634   (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t)
   9635   (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t))
   9636 
   9637 (defun lsp-kill-virtual-buffers ()
   9638   (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections))
   9639 
   9640 (defun lsp--move-point-in-indentation (point indentation)
   9641   (save-excursion
   9642     (goto-char point)
   9643     (if (<= point (+ (line-beginning-position) indentation))
   9644         (line-beginning-position)
   9645       point)))
   9646 
   9647 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck")
   9648 (declare-function flycheck-add-mode "ext:flycheck")
   9649 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics")
   9650 
   9651 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn)
   9652 
   9653 (defun lsp-flycheck-add-mode (mode)
   9654   "Register flycheck support for MODE."
   9655   (lsp-diagnostics-lsp-checker-if-needed)
   9656   (unless (flycheck-checker-supports-major-mode-p 'lsp mode)
   9657     (flycheck-add-mode 'lsp mode)))
   9658 
   9659 (defun lsp-progress-spinner-type ()
   9660   "Retrieve the spinner type value, if value is not a symbol of `spinner-types
   9661 defaults to `progress-bar."
   9662   (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar))
   9663 
   9664 (defun lsp-org ()
   9665   (interactive)
   9666   (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range))
   9667                                                               (funcall in-range))
   9668                                                             lsp--virtual-buffer-connections))
   9669       (unless (equal lsp--virtual-buffer virtual-buffer)
   9670         (setq lsp--buffer-workspaces workspaces)
   9671         (setq lsp--virtual-buffer virtual-buffer)
   9672         (setq lsp-buffer-uri nil)
   9673         (lsp-mode 1)
   9674         (lsp-managed-mode 1)
   9675         (lsp-patch-on-change-event))
   9676 
   9677     (save-excursion
   9678       (-let* (virtual-buffer
   9679               (wcb (lambda (f)
   9680                      (with-current-buffer (plist-get virtual-buffer :buffer)
   9681                        (-let* (((&plist :major-mode :buffer-file-name
   9682                                         :goto-buffer :workspaces) virtual-buffer)
   9683                                (lsp--virtual-buffer virtual-buffer)
   9684                                (lsp--buffer-workspaces workspaces))
   9685                          (save-excursion
   9686                            (funcall goto-buffer)
   9687                            (funcall f))))))
   9688               ((&plist :begin :end :post-blank :language) (cl-second (org-element-context)))
   9689               ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light)))
   9690 
   9691               (file-name (if file-name
   9692                              (f-expand file-name)
   9693                            (user-error "You should specify file name in the src block header.")))
   9694               (begin-marker (progn
   9695                               (goto-char begin)
   9696                               (forward-line)
   9697                               (set-marker (make-marker) (point))))
   9698               (end-marker (progn
   9699                             (goto-char end)
   9700                             (forward-line (1- (- post-blank)))
   9701                             (set-marker (make-marker) (1+ (point)))))
   9702               (buf (current-buffer))
   9703               (src-block (buffer-substring-no-properties begin-marker
   9704                                                          (1- end-marker)))
   9705               (indentation (with-temp-buffer
   9706                              (insert src-block)
   9707 
   9708                              (goto-char (point-min))
   9709                              (let ((indentation (current-indentation)))
   9710                                (plist-put lsp--virtual-buffer :indentation indentation)
   9711                                (org-do-remove-indentation)
   9712                                (goto-char (point-min))
   9713                                (- indentation (current-indentation))))))
   9714         (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t)
   9715 
   9716         (when (fboundp 'flycheck-add-mode)
   9717           (lsp-flycheck-add-mode 'org-mode))
   9718 
   9719         (setq lsp--virtual-buffer
   9720               (list
   9721                :in-range (lambda (&optional point)
   9722                            (<= begin-marker (or point (point)) (1- end-marker)))
   9723                :goto-buffer (lambda () (goto-char begin-marker))
   9724                :buffer-string
   9725                (lambda ()
   9726                  (let ((src-block (buffer-substring-no-properties
   9727                                    begin-marker
   9728                                    (1- end-marker))))
   9729                    (with-temp-buffer
   9730                      (insert src-block)
   9731 
   9732                      (goto-char (point-min))
   9733                      (while (not (eobp))
   9734                        (delete-region (point) (if (> (+ (point) indentation) (line-end-position))
   9735                                                   (line-end-position)
   9736                                                 (+ (point) indentation)))
   9737                        (forward-line))
   9738                      (buffer-substring-no-properties (point-min)
   9739                                                      (point-max)))))
   9740                :buffer buf
   9741                :begin begin-marker
   9742                :end end-marker
   9743                :indentation indentation
   9744                :last-point (lambda () (1- end-marker))
   9745                :cur-position (lambda ()
   9746                                (lsp-save-restriction-and-excursion
   9747                                  (list :line (- (lsp--cur-line)
   9748                                                 (lsp--cur-line begin-marker))
   9749                                        :character (let ((character (- (point)
   9750                                                                       (line-beginning-position)
   9751                                                                       indentation)))
   9752                                                     (if (< character 0)
   9753                                                         0
   9754                                                       character)))))
   9755                :line/character->point (-lambda (line character)
   9756                                         (-let [inhibit-field-text-motion t]
   9757                                           (+ indentation
   9758                                              (lsp-save-restriction-and-excursion
   9759                                                (goto-char begin-marker)
   9760                                                (forward-line line)
   9761                                                (-let [line-end (line-end-position)]
   9762                                                  (if (> character (- line-end (point)))
   9763                                                      line-end
   9764                                                    (forward-char character)
   9765                                                    (point)))))))
   9766                :major-mode (org-src-get-lang-mode language)
   9767                :buffer-file-name file-name
   9768                :buffer-uri (lsp--path-to-uri file-name)
   9769                :with-current-buffer wcb
   9770                :buffer-live? (lambda (_) (buffer-live-p buf))
   9771                :buffer-name (lambda (_)
   9772                               (propertize (format "%s(%s:%s)%s"
   9773                                                   (buffer-name buf)
   9774                                                   begin-marker
   9775                                                   end-marker
   9776                                                   language)
   9777                                           'face 'italic))
   9778                :real->virtual-line (lambda (line)
   9779                                      (+ line (line-number-at-pos begin-marker) -1))
   9780                :real->virtual-char (lambda (char) (+ char indentation))
   9781                :cleanup (lambda ()
   9782                           (set-marker begin-marker nil)
   9783                           (set-marker end-marker nil))))
   9784         (setf virtual-buffer lsp--virtual-buffer)
   9785         (puthash file-name virtual-buffer lsp--virtual-buffer-mappings)
   9786         (push virtual-buffer lsp--virtual-buffer-connections)
   9787 
   9788         ;; TODO: tangle only connected sections
   9789         (add-hook 'after-save-hook 'org-babel-tangle nil t)
   9790         (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t)
   9791         (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t)
   9792 
   9793         (setq lsp--buffer-workspaces
   9794               (lsp-with-current-buffer virtual-buffer
   9795                 (lsp)
   9796                 (plist-put virtual-buffer :workspaces (lsp-workspaces))
   9797                 (lsp-workspaces)))))))
   9798 
   9799 (defun lsp-virtual-buffer-disconnect (virtual-buffer)
   9800   (interactive (list (or
   9801                       lsp--virtual-buffer
   9802                       (when lsp--virtual-buffer-connections
   9803                         (lsp--completing-read "Select virtual buffer to disconnect: "
   9804                                               lsp--virtual-buffer-connections
   9805                                               (-lambda ((&plist :buffer-file-name))
   9806                                                 buffer-file-name))))))
   9807   (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer)
   9808       (progn
   9809         (lsp-with-current-buffer virtual-buffer
   9810           (lsp--text-document-did-close))
   9811         (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections))
   9812         (when (eq virtual-buffer lsp--virtual-buffer)
   9813           (setf lsp--virtual-buffer nil))
   9814         (when cleanup (funcall cleanup))
   9815         (remhash file-name lsp--virtual-buffer-mappings)
   9816 
   9817         (lsp--virtual-buffer-update-position)
   9818         (lsp--info "Disconnected from buffer %s" file-name))
   9819     (lsp--error "Nothing to disconnect from?")))
   9820 
   9821 
   9822 ;; inlay hints
   9823 
   9824 (defface lsp-inlay-hint-face
   9825   '((t :inherit font-lock-comment-face))
   9826   "The face to use for the JavaScript inlays."
   9827   :group 'lsp-mode
   9828   :package-version '(lsp-mode . "9.0.0"))
   9829 
   9830 (defface lsp-inlay-hint-type-face
   9831   '((t :inherit lsp-inlay-hint-face))
   9832   "Face for inlay type hints (e.g. inferred variable types)."
   9833   :group 'lsp-mode
   9834   :package-version '(lsp-mode . "9.0.0"))
   9835 
   9836 (defcustom lsp-inlay-hint-type-format "%s"
   9837   "Format string for variable inlays (part of the inlay face)."
   9838   :type '(string :tag "String")
   9839   :group 'lsp-mode
   9840   :package-version '(lsp-mode . "9.0.0"))
   9841 
   9842 (defface lsp-inlay-hint-parameter-face
   9843   '((t :inherit lsp-inlay-hint-face))
   9844   "Face for inlay parameter hints (e.g. function parameter names at
   9845 call-site)."
   9846   :group 'lsp-mode
   9847   :package-version '(lsp-mode . "9.0.0"))
   9848 
   9849 (defcustom lsp-inlay-hint-param-format "%s"
   9850   "Format string for parameter inlays (part of the inlay face)."
   9851   :type '(string :tag "String")
   9852   :group 'lsp-mode
   9853   :package-version '(lsp-mode . "9.0.0"))
   9854 
   9855 (defcustom lsp-update-inlay-hints-on-scroll t
   9856   "If non-nil update inlay hints immediately when scrolling or
   9857 modifying window sizes."
   9858   :type 'boolean
   9859   :package-version '(lsp-mode . "9.0.0"))
   9860 
   9861 (defun lsp--format-inlay (text kind)
   9862   (cond
   9863    ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text))
   9864    ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text))
   9865    (t text)))
   9866 
   9867 (defun lsp--face-for-inlay (kind)
   9868   (cond
   9869    ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face)
   9870    ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face)
   9871    (t 'lsp-inlay-hint-face)))
   9872 
   9873 (defun lsp--update-inlay-hints-scroll-function (window start)
   9874   (lsp-update-inlay-hints start (window-end window t)))
   9875 
   9876 (defun lsp--update-inlay-hints ()
   9877   (lsp-update-inlay-hints (window-start) (window-end nil t)))
   9878 
   9879 (defun lsp--label-from-inlay-hints-response (label)
   9880   "Returns a string label built from an array of
   9881 InlayHintLabelParts or the argument itself if it's already a
   9882 string."
   9883   (cl-typecase label
   9884     (string label)
   9885     (vector
   9886      (string-join (mapcar (lambda (part)
   9887                             (-let (((&InlayHintLabelPart :value) part))
   9888                               value))
   9889                           label)))))
   9890 
   9891 (defun lsp-update-inlay-hints (start end)
   9892   (lsp-request-async
   9893    "textDocument/inlayHint"
   9894    (lsp-make-inlay-hints-params
   9895     :text-document (lsp--text-document-identifier)
   9896     :range (lsp-make-range :start
   9897                            (lsp-point-to-position start)
   9898                            :end
   9899                            (lsp-point-to-position end)))
   9900    (lambda (res)
   9901      (lsp--remove-overlays 'lsp-inlay-hint)
   9902      (dolist (hint res)
   9903        (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint)
   9904                (kind (or kind? lsp/inlay-hint-kind-type-hint))
   9905                (label (lsp--label-from-inlay-hints-response label))
   9906                (pos (lsp--position-to-point position))
   9907                (overlay (make-overlay pos pos nil 'front-advance 'end-advance)))
   9908          (when (stringp label)
   9909            (overlay-put overlay 'lsp-inlay-hint t)
   9910            (overlay-put overlay 'before-string
   9911                         (format "%s%s%s"
   9912                                 (if padding-left? " " "")
   9913                                 (propertize (lsp--format-inlay label kind)
   9914                                             'font-lock-face (lsp--face-for-inlay kind))
   9915                                 (if padding-right? " " "")))))))
   9916    :mode 'tick))
   9917 
   9918 (define-minor-mode lsp-inlay-hints-mode
   9919   "Mode for displaying inlay hints."
   9920   :lighter nil
   9921   (cond
   9922    ((and lsp-inlay-hints-mode lsp--buffer-workspaces)
   9923     (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t)
   9924     (when lsp-update-inlay-hints-on-scroll
   9925       (add-to-list (make-local-variable 'window-scroll-functions)
   9926                    #'lsp--update-inlay-hints-scroll-function)))
   9927    (t
   9928     (lsp--remove-overlays 'lsp-inlay-hint)
   9929     (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t)
   9930     (setf window-scroll-functions
   9931           (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions)))))
   9932 
   9933 
   9934 
   9935 ;;;###autoload
   9936 (defun lsp-start-plain ()
   9937   "Start `lsp-mode' using minimal configuration using the latest `melpa' version
   9938 of the packages.
   9939 
   9940 In case the major-mode that you are using for "
   9941   (interactive)
   9942   (let ((start-plain (make-temp-file "plain" nil ".el")))
   9943     (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el"
   9944                    start-plain t)
   9945     (start-process "lsp-start-plain"
   9946                    (generate-new-buffer " *lsp-start-plain*")
   9947                    (expand-file-name invocation-name invocation-directory)
   9948                     "-q" "-l" start-plain (or (buffer-file-name) ""))))
   9949 
   9950 
   9951 
   9952 (provide 'lsp-mode)
   9953 ;;; lsp-mode.el ends here