config

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

lsp-mode.el (436272B)


      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: 20241123.739
      9 ;; Package-Revision: ae4907082b42
     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 (define-obsolete-variable-alias
    552   'lsp-references-exclude-definition
    553   'lsp-references-exclude-declaration
    554   "9.0.1")
    555 
    556 (defcustom lsp-references-exclude-declaration nil
    557   "If non-nil, exclude declarations when finding references."
    558   :type 'boolean
    559   :group 'lsp-mode)
    560 
    561 (defcustom lsp-enable-indentation t
    562   "Indent regions using the file formatting functionality provided by the
    563 language server."
    564   :type 'boolean
    565   :group 'lsp-mode)
    566 
    567 (defcustom lsp-enable-on-type-formatting t
    568   "Enable `textDocument/onTypeFormatting' integration."
    569   :type 'boolean
    570   :group 'lsp-mode)
    571 
    572 (defcustom lsp-enable-text-document-color t
    573   "Enable `textDocument/documentColor' integration."
    574   :type 'boolean
    575   :group 'lsp-mode)
    576 
    577 (defcustom lsp-before-save-edits t
    578   "If non-nil, `lsp-mode' will apply edits suggested by the language server
    579 before saving a document."
    580   :type 'boolean
    581   :group 'lsp-mode)
    582 
    583 (defcustom lsp-after-apply-edits-hook nil
    584   "Hooks to run when text edit is applied.
    585 It contains the operation source."
    586   :type 'hook
    587   :group 'lsp-mode
    588   :package-version '(lsp-mode . "8.0.0"))
    589 
    590 (defcustom lsp-apply-edits-after-file-operations t
    591   "Whether to apply edits returned by server after file operations if any.
    592 Applicable only if server supports workspace.fileOperations for operations:
    593 `workspace/willRenameFiles', `workspace/willCreateFiles' and
    594 `workspace/willDeleteFiles'."
    595   :group 'lsp-mode
    596   :type 'boolean)
    597 
    598 (defcustom lsp-modeline-code-actions-enable t
    599   "Whether to show code actions on modeline."
    600   :type 'boolean
    601   :group 'lsp-modeline)
    602 
    603 (defcustom lsp-modeline-diagnostics-enable t
    604   "Whether to show diagnostics on modeline."
    605   :type 'boolean
    606   :group 'lsp-modeline)
    607 
    608 (defcustom lsp-modeline-workspace-status-enable t
    609   "Whether to show workspace status on modeline."
    610   :type 'boolean
    611   :group 'lsp-modeline
    612   :package-version '(lsp-mode . "8.0.0"))
    613 
    614 (defcustom lsp-headerline-breadcrumb-enable t
    615   "Whether to enable breadcrumb on headerline."
    616   :type 'boolean
    617   :group 'lsp-headerline)
    618 
    619 (defcustom lsp-configure-hook nil
    620   "Hooks to run when `lsp-configure-buffer' is called."
    621   :type 'hook
    622   :group 'lsp-mode)
    623 
    624 (defcustom lsp-unconfigure-hook nil
    625   "Hooks to run when `lsp-unconfig-buffer' is called."
    626   :type 'hook
    627   :group 'lsp-mode)
    628 
    629 (defcustom lsp-after-diagnostics-hook nil
    630   "Hooks to run after diagnostics are received.
    631 Note: it runs only if the receiving buffer is open. Use
    632 `lsp-diagnostics-updated-hook'if you want to be notified when
    633 diagnostics have changed."
    634   :type 'hook
    635   :group 'lsp-mode)
    636 
    637 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook
    638   'lsp-diagnostics-updated-hook "lsp-mode 6.4")
    639 
    640 (defcustom lsp-diagnostics-updated-hook nil
    641   "Hooks to run after diagnostics are received."
    642   :type 'hook
    643   :group 'lsp-mode)
    644 
    645 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook
    646   'lsp-workspace-folders-changed-functions "lsp-mode 6.3")
    647 
    648 (defcustom lsp-workspace-folders-changed-functions nil
    649   "Hooks to run after the folders has changed.
    650 The hook will receive two parameters list of added and removed folders."
    651   :type 'hook
    652   :group 'lsp-mode)
    653 
    654 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0")
    655 
    656 (defcustom lsp-before-apply-edits-hook nil
    657   "Hooks to run before applying edits."
    658   :type 'hook
    659   :group 'lsp-mode)
    660 
    661 (defgroup lsp-imenu nil
    662   "LSP Imenu."
    663   :group 'lsp-mode
    664   :tag "LSP Imenu")
    665 
    666 (defcustom lsp-imenu-show-container-name t
    667   "Display the symbol's container name in an imenu entry."
    668   :type 'boolean
    669   :group 'lsp-imenu)
    670 
    671 (defcustom lsp-imenu-container-name-separator "/"
    672   "Separator string to use to separate the container name from the symbol while
    673 displaying imenu entries."
    674   :type 'string
    675   :group 'lsp-imenu)
    676 
    677 (defcustom lsp-imenu-sort-methods '(kind name)
    678   "How to sort the imenu items.
    679 
    680 The value is a list of `kind' `name' or `position'.  Priorities
    681 are determined by the index of the element."
    682   :type '(repeat (choice (const name)
    683                          (const position)
    684                          (const kind)))
    685   :group 'lsp-imenu)
    686 
    687 (defcustom lsp-imenu-index-symbol-kinds nil
    688   "Which symbol kinds to show in imenu."
    689   :type '(repeat (choice (const :tag "Miscellaneous" nil)
    690                          (const :tag "File" File)
    691                          (const :tag "Module" Module)
    692                          (const :tag "Namespace" Namespace)
    693                          (const :tag "Package" Package)
    694                          (const :tag "Class" Class)
    695                          (const :tag "Method" Method)
    696                          (const :tag "Property" Property)
    697                          (const :tag "Field" Field)
    698                          (const :tag "Constructor" Constructor)
    699                          (const :tag "Enum" Enum)
    700                          (const :tag "Interface" Interface)
    701                          (const :tag "Function" Function)
    702                          (const :tag "Variable" Variable)
    703                          (const :tag "Constant" Constant)
    704                          (const :tag "String" String)
    705                          (const :tag "Number" Number)
    706                          (const :tag "Boolean" Boolean)
    707                          (const :tag "Array" Array)
    708                          (const :tag "Object" Object)
    709                          (const :tag "Key" Key)
    710                          (const :tag "Null" Null)
    711                          (const :tag "Enum Member" EnumMember)
    712                          (const :tag "Struct" Struct)
    713                          (const :tag "Event" Event)
    714                          (const :tag "Operator" Operator)
    715                          (const :tag "Type Parameter" TypeParameter)))
    716   :group 'lsp-imenu)
    717 
    718 ;; vibhavp: Should we use a lower value (5)?
    719 (defcustom lsp-response-timeout 10
    720   "Number of seconds to wait for a response from the language server before
    721 timing out. Nil if no timeout."
    722   :type '(choice
    723           (number :tag "Seconds")
    724           (const :tag "No timeout" nil))
    725   :group 'lsp-mode)
    726 
    727 (defcustom lsp-tcp-connection-timeout 2
    728   "The timeout for tcp connection in seconds."
    729   :type 'number
    730   :group 'lsp-mode
    731   :package-version '(lsp-mode . "6.2"))
    732 
    733 (defconst lsp--imenu-compare-function-alist
    734   (list (cons 'name #'lsp--imenu-compare-name)
    735         (cons 'kind #'lsp--imenu-compare-kind)
    736         (cons 'position #'lsp--imenu-compare-line-col))
    737   "An alist of (METHOD . FUNCTION).
    738 METHOD is one of the symbols accepted by
    739 `lsp-imenu-sort-methods'.
    740 
    741 FUNCTION takes two hash tables representing DocumentSymbol.  It
    742 returns a negative number, 0, or a positive number indicating
    743 whether the first parameter is less than, equal to, or greater
    744 than the second parameter.")
    745 
    746 (defcustom lsp-diagnostic-clean-after-change nil
    747   "When non-nil, clean the diagnostics on change.
    748 
    749 Note that when that setting is nil, `lsp-mode' will show stale
    750 diagnostics until server publishes the new set of diagnostics"
    751   :type 'boolean
    752   :group 'lsp-diagnostics
    753   :package-version '(lsp-mode . "7.0.1"))
    754 
    755 (defcustom lsp-server-trace nil
    756   "Request tracing on the server side.
    757 The actual trace output at each level depends on the language server in use.
    758 Changes take effect only when a new session is started."
    759   :type '(choice (const :tag "Disabled" "off")
    760                  (const :tag "Messages only" "messages")
    761                  (const :tag "Verbose" "verbose")
    762                  (const :tag "Default (disabled)" nil))
    763   :group 'lsp-mode
    764   :package-version '(lsp-mode . "6.1"))
    765 
    766 (defcustom lsp-auto-touch-files t
    767   "If non-nil ensure the files exist before sending
    768 `textDocument/didOpen' notification."
    769   :type 'boolean
    770   :group 'lsp-mode
    771   :package-version '(lsp-mode . "9.0.0"))
    772 
    773 (defvar lsp-language-id-configuration
    774   '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake")
    775     ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile")
    776     ("\\.astro$" . "astro")
    777     ("\\.cs\\'" . "csharp")
    778     ("\\.css$" . "css")
    779     ("\\.cypher$" . "cypher")
    780     ("Earthfile" . "earthfile")
    781     ("\\.ebuild$" . "shellscript")
    782     ("\\.go\\'" . "go")
    783     ("\\.html$" . "html")
    784     ("\\.hx$" . "haxe")
    785     ("\\.hy$" . "hy")
    786     ("\\.java\\'" . "java")
    787     ("\\.jq$"  . "jq")
    788     ("\\.js$" . "javascript")
    789     ("\\.json$" . "json")
    790     ("\\.jsonc$" . "jsonc")
    791     ("\\.jsonnet$" . "jsonnet")
    792     ("\\.jsx$" . "javascriptreact")
    793     ("\\.lua$" . "lua")
    794     ("\\.fnl$" . "fennel")
    795     ("\\.mdx\\'" . "mdx")
    796     ("\\.nu$" . "nushell")
    797     ("\\.php$" . "php")
    798     ("\\.ps[dm]?1\\'" . "powershell")
    799     ("\\.rs\\'" . "rust")
    800     ("\\.spec\\'" . "rpm-spec")
    801     ("\\.sql$" . "sql")
    802     ("\\.svelte$" . "svelte")
    803     ("\\.toml\\'" . "toml")
    804     ("\\.ts$" . "typescript")
    805     ("\\.tsp$" . "typespec")
    806     ("\\.tsx$" . "typescriptreact")
    807     ("\\.ttcn3$" . "ttcn3")
    808     ("\\.vue$" . "vue")
    809     ("\\.xml$" . "xml")
    810     ("\\ya?ml$" . "yaml")
    811     ("^PKGBUILD$" . "shellscript")
    812     ("^go\\.mod\\'" . "go.mod")
    813     ("^settings\\.json$" . "jsonc")
    814     ("^yang\\.settings$" . "jsonc")
    815     ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson")
    816     (ada-mode . "ada")
    817     (ada-ts-mode . "ada")
    818     (gpr-mode . "gpr")
    819     (gpr-ts-mode . "gpr")
    820     (awk-mode . "awk")
    821     (awk-ts-mode . "awk")
    822     (nxml-mode . "xml")
    823     (sql-mode . "sql")
    824     (vimrc-mode . "vim")
    825     (vimscript-ts-mode . "vim")
    826     (sh-mode . "shellscript")
    827     (bash-ts-mode . "shellscript")
    828     (ebuild-mode . "shellscript")
    829     (pkgbuild-mode . "shellscript")
    830     (envrc-file-mode . "shellscript")
    831     (scala-mode . "scala")
    832     (scala-ts-mode . "scala")
    833     (julia-mode . "julia")
    834     (julia-ts-mode . "julia")
    835     (clojure-mode . "clojure")
    836     (clojurec-mode . "clojure")
    837     (clojurescript-mode . "clojurescript")
    838     (clojure-ts-mode . "clojure")
    839     (clojure-ts-clojurec-mode . "clojure")
    840     (clojure-ts-clojurescript-mode . "clojurescript")
    841     (java-mode . "java")
    842     (java-ts-mode . "java")
    843     (jdee-mode . "java")
    844     (groovy-mode . "groovy")
    845     (nextflow-mode . "nextflow")
    846     (python-mode . "python")
    847     (python-ts-mode . "python")
    848     (cython-mode . "python")
    849     ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo")
    850     (lsp--render-markdown . "markdown")
    851     (move-mode . "move")
    852     (rust-mode . "rust")
    853     (rust-ts-mode . "rust")
    854     (rustic-mode . "rust")
    855     (kotlin-mode . "kotlin")
    856     (kotlin-ts-mode . "kotlin")
    857     (css-mode . "css")
    858     (css-ts-mode . "css")
    859     (less-mode . "less")
    860     (less-css-mode . "less")
    861     (lua-mode . "lua")
    862     (lua-ts-mode . "lua")
    863     (sass-mode . "sass")
    864     (ssass-mode . "sass")
    865     (scss-mode . "scss")
    866     (scad-mode . "openscad")
    867     (xml-mode . "xml")
    868     (c-mode . "c")
    869     (c-ts-mode . "c")
    870     (c++-mode . "cpp")
    871     (c++-ts-mode . "cpp")
    872     (cuda-mode . "cuda")
    873     (objc-mode . "objective-c")
    874     (html-mode . "html")
    875     (html-ts-mode . "html")
    876     (sgml-mode . "html")
    877     (mhtml-mode . "html")
    878     (mint-mode . "mint")
    879     (go-dot-mod-mode . "go.mod")
    880     (go-mod-ts-mode . "go.mod")
    881     (go-mode . "go")
    882     (go-ts-mode . "go")
    883     (graphql-mode . "graphql")
    884     (haskell-mode . "haskell")
    885     (haskell-ts-mode . "haskell")
    886     (hack-mode . "hack")
    887     (php-mode . "php")
    888     (php-ts-mode . "php")
    889     (powershell-mode . "powershell")
    890     (powershell-mode . "PowerShell")
    891     (powershell-ts-mode . "powershell")
    892     (json-mode . "json")
    893     (json-ts-mode . "json")
    894     (jsonc-mode . "jsonc")
    895     (rjsx-mode . "javascript")
    896     (js2-mode . "javascript")
    897     (js-mode . "javascript")
    898     (js-ts-mode . "javascript")
    899     (typescript-mode . "typescript")
    900     (typescript-ts-mode . "typescript")
    901     (typespec-mode . "typespec")
    902     (tsx-ts-mode . "typescriptreact")
    903     (svelte-mode . "svelte")
    904     (fsharp-mode . "fsharp")
    905     (reason-mode . "reason")
    906     (caml-mode . "ocaml")
    907     (tuareg-mode . "ocaml")
    908     (futhark-mode . "futhark")
    909     (swift-mode . "swift")
    910     (elixir-mode . "elixir")
    911     (elixir-ts-mode . "elixir")
    912     (heex-ts-mode . "elixir")
    913     (conf-javaprop-mode . "spring-boot-properties")
    914     (yaml-mode . "yaml")
    915     (yaml-ts-mode . "yaml")
    916     (ruby-mode . "ruby")
    917     (enh-ruby-mode . "ruby")
    918     (ruby-ts-mode . "ruby")
    919     (feature-mode . "cucumber")
    920     (fortran-mode . "fortran")
    921     (f90-mode . "fortran")
    922     (elm-mode . "elm")
    923     (dart-mode . "dart")
    924     (erlang-mode . "erlang")
    925     (dockerfile-mode . "dockerfile")
    926     (dockerfile-ts-mode . "dockerfile")
    927     (csharp-mode . "csharp")
    928     (csharp-tree-sitter-mode . "csharp")
    929     (csharp-ts-mode . "csharp")
    930     (plain-tex-mode . "plaintex")
    931     (context-mode . "context")
    932     (cypher-mode . "cypher")
    933     (latex-mode . "latex")
    934     (LaTeX-mode . "latex")
    935     (v-mode . "v")
    936     (vhdl-mode . "vhdl")
    937     (vhdl-ts-mode . "vhdl")
    938     (verilog-mode . "verilog")
    939     (terraform-mode . "terraform")
    940     (ess-julia-mode . "julia")
    941     (ess-r-mode . "r")
    942     (crystal-mode . "crystal")
    943     (nim-mode . "nim")
    944     (dhall-mode . "dhall")
    945     (cmake-mode . "cmake")
    946     (cmake-ts-mode . "cmake")
    947     (purescript-mode . "purescript")
    948     (gdscript-mode . "gdscript")
    949     (gdscript-ts-mode . "gdscript")
    950     (perl-mode . "perl")
    951     (cperl-mode . "perl")
    952     (robot-mode . "robot")
    953     (racket-mode . "racket")
    954     (nix-mode . "nix")
    955     (nix-ts-mode . "nix")
    956     (prolog-mode . "prolog")
    957     (vala-mode . "vala")
    958     (actionscript-mode . "actionscript")
    959     (d-mode . "d")
    960     (zig-mode . "zig")
    961     (zig-ts-mode . "zig")
    962     (text-mode . "plaintext")
    963     (markdown-mode . "markdown")
    964     (gfm-mode . "markdown")
    965     (beancount-mode . "beancount")
    966     (conf-toml-mode . "toml")
    967     (toml-ts-mode . "toml")
    968     (org-mode . "org")
    969     (org-journal-mode . "org")
    970     (nginx-mode . "nginx")
    971     (magik-mode . "magik")
    972     (magik-ts-mode . "magik")
    973     (idris-mode . "idris")
    974     (idris2-mode . "idris2")
    975     (gleam-mode . "gleam")
    976     (gleam-ts-mode . "gleam")
    977     (graphviz-dot-mode . "dot")
    978     (tiltfile-mode . "tiltfile")
    979     (solidity-mode . "solidity")
    980     (bibtex-mode . "bibtex")
    981     (rst-mode . "restructuredtext")
    982     (glsl-mode . "glsl")
    983     (shader-mode . "shaderlab")
    984     (wgsl-mode . "wgsl")
    985     (jq-mode . "jq")
    986     (jq-ts-mode . "jq")
    987     (protobuf-mode . "protobuf")
    988     (nushell-mode . "nushell")
    989     (nushell-ts-mode . "nushell")
    990     (meson-mode . "meson")
    991     (yang-mode . "yang"))
    992   "Language id configuration.")
    993 
    994 (defvar lsp--last-active-workspaces nil
    995   "Keep track of last active workspace.
    996 We want to try the last workspace first when jumping into a library
    997 directory")
    998 
    999 (defvar lsp-method-requirements
   1000   '(("textDocument/callHierarchy" :capability :callHierarchyProvider)
   1001     ("textDocument/codeAction" :capability :codeActionProvider)
   1002     ("codeAction/resolve"
   1003      :check-command (lambda (workspace)
   1004                       (with-lsp-workspace workspace
   1005                         (lsp:code-action-options-resolve-provider?
   1006                          (lsp--capability-for-method "textDocument/codeAction")))))
   1007     ("textDocument/codeLens" :capability :codeLensProvider)
   1008     ("textDocument/completion" :capability :completionProvider)
   1009     ("completionItem/resolve"
   1010      :check-command (lambda (wk)
   1011                       (with-lsp-workspace wk
   1012                         (lsp:completion-options-resolve-provider?
   1013                          (lsp--capability-for-method "textDocument/completion")))))
   1014     ("textDocument/declaration" :capability :declarationProvider)
   1015     ("textDocument/definition" :capability :definitionProvider)
   1016     ("textDocument/documentColor" :capability :colorProvider)
   1017     ("textDocument/documentLink" :capability :documentLinkProvider)
   1018     ("textDocument/inlayHint" :capability :inlayHintProvider)
   1019     ("textDocument/documentHighlight" :capability :documentHighlightProvider)
   1020     ("textDocument/documentSymbol" :capability :documentSymbolProvider)
   1021     ("textDocument/foldingRange" :capability :foldingRangeProvider)
   1022     ("textDocument/formatting" :capability :documentFormattingProvider)
   1023     ("textDocument/hover" :capability :hoverProvider)
   1024     ("textDocument/implementation" :capability :implementationProvider)
   1025     ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider)
   1026     ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider)
   1027     ("textDocument/prepareRename"
   1028      :check-command (lambda (workspace)
   1029                       (with-lsp-workspace workspace
   1030                         (lsp:rename-options-prepare-provider?
   1031                          (lsp--capability-for-method "textDocument/rename")))))
   1032     ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider)
   1033     ("textDocument/references" :capability :referencesProvider)
   1034     ("textDocument/rename" :capability :renameProvider)
   1035     ("textDocument/selectionRange" :capability :selectionRangeProvider)
   1036     ("textDocument/semanticTokens" :capability :semanticTokensProvider)
   1037     ("textDocument/semanticTokensFull"
   1038      :check-command (lambda (workspace)
   1039                       (with-lsp-workspace workspace
   1040                         (lsp-get (lsp--capability :semanticTokensProvider) :full))))
   1041     ("textDocument/semanticTokensFull/Delta"
   1042      :check-command (lambda (workspace)
   1043                       (with-lsp-workspace workspace
   1044                         (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full)))
   1045                           (and (not (booleanp capFull)) (lsp-get capFull :delta))))))
   1046     ("textDocument/semanticTokensRangeProvider"
   1047      :check-command (lambda (workspace)
   1048                       (with-lsp-workspace workspace
   1049                         (lsp-get (lsp--capability :semanticTokensProvider) :range))))
   1050     ("textDocument/signatureHelp" :capability :signatureHelpProvider)
   1051     ("textDocument/typeDefinition" :capability :typeDefinitionProvider)
   1052     ("textDocument/typeHierarchy" :capability :typeHierarchyProvider)
   1053     ("textDocument/diagnostic" :capability :diagnosticProvider)
   1054     ("workspace/executeCommand" :capability :executeCommandProvider)
   1055     ("workspace/symbol" :capability :workspaceSymbolProvider))
   1056 
   1057   "Map methods to requirements.
   1058 It is used by request-sending functions to determine which server
   1059 must be used for handling a particular message.")
   1060 
   1061 (defconst lsp--file-change-type
   1062   `((created . 1)
   1063     (changed . 2)
   1064     (deleted . 3)))
   1065 
   1066 (defconst lsp--watch-kind
   1067   `((create . 1)
   1068     (change . 2)
   1069     (delete . 4)))
   1070 
   1071 (defvar lsp-window-body-width 40
   1072   "Window body width when rendering doc.")
   1073 
   1074 (defface lsp-face-highlight-textual
   1075   '((t :inherit highlight))
   1076   "Face used for textual occurrences of symbols."
   1077   :group 'lsp-mode)
   1078 
   1079 (defface lsp-face-highlight-read
   1080   '((t :inherit highlight :underline t))
   1081   "Face used for highlighting symbols being read."
   1082   :group 'lsp-mode)
   1083 
   1084 (defface lsp-face-highlight-write
   1085   '((t :inherit highlight :weight bold))
   1086   "Face used for highlighting symbols being written to."
   1087   :group 'lsp-mode)
   1088 
   1089 (define-obsolete-variable-alias 'lsp-lens-auto-enable
   1090   'lsp-lens-enable "lsp-mode 7.0.1")
   1091 
   1092 (defcustom lsp-lens-enable t
   1093   "Auto enable lenses if server supports."
   1094   :group 'lsp-lens
   1095   :type 'boolean
   1096   :package-version '(lsp-mode . "6.3"))
   1097 
   1098 (defcustom lsp-symbol-highlighting-skip-current nil
   1099   "If non-nil skip current symbol when setting symbol highlights."
   1100   :group 'lsp-mode
   1101   :type 'boolean)
   1102 
   1103 (defcustom lsp-file-watch-threshold 1000
   1104   "Show warning if the files to watch are more than.
   1105 Set to nil to disable the warning."
   1106   :type 'number
   1107   :group 'lsp-mode)
   1108 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i))))
   1109 
   1110 (defvar lsp-custom-markup-modes
   1111   '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic"))
   1112   "Mode to uses with markdown code blocks.
   1113 They are added to `markdown-code-lang-modes'")
   1114 
   1115 (defcustom lsp-signature-render-documentation t
   1116   "Display signature documentation in `eldoc'."
   1117   :type 'boolean
   1118   :group 'lsp-mode
   1119   :package-version '(lsp-mode . "6.2"))
   1120 
   1121 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request)
   1122   "Auto activate signature conditions."
   1123   :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char)
   1124                          (const :tag "After selected completion." :after-completion)
   1125                          (const :tag "When the server has sent show signature help." :on-server-request)))
   1126   :group 'lsp-mode
   1127   :package-version '(lsp-mode . "6.2"))
   1128 
   1129 (defcustom lsp-signature-doc-lines 20
   1130   "If number, limit the number of lines to show in the docs."
   1131   :type 'number
   1132   :group 'lsp-mode
   1133   :package-version '(lsp-mode . "6.3"))
   1134 
   1135 (defcustom lsp-signature-function 'lsp-lv-message
   1136   "The function used for displaying signature info.
   1137 It will be called with one param - the signature info. When
   1138 called with nil the signature info must be cleared."
   1139   :type 'function
   1140   :group 'lsp-mode
   1141   :package-version '(lsp-mode . "6.3"))
   1142 
   1143 (defcustom lsp-keymap-prefix "s-l"
   1144   "LSP-mode keymap prefix."
   1145   :group 'lsp-mode
   1146   :type 'string
   1147   :package-version '(lsp-mode . "6.3"))
   1148 
   1149 (defvar-local lsp--buffer-workspaces ()
   1150   "List of the buffer workspaces.")
   1151 
   1152 (defvar-local lsp--buffer-deferred nil
   1153   "Whether buffer was loaded via `lsp-deferred'.")
   1154 
   1155 (defvar lsp--session nil
   1156   "Contain the `lsp-session' for the current Emacs instance.")
   1157 
   1158 (defvar lsp--tcp-port 10000)
   1159 
   1160 (defvar lsp--client-packages-required nil
   1161   "If nil, `lsp-client-packages' are yet to be required.")
   1162 
   1163 (defvar lsp--tcp-server-port 0
   1164   "The server socket which is opened when using `lsp-tcp-server' (a server
   1165 socket is opened in Emacs and the language server connects to it).  The
   1166 default value of 0 ensures that a random high port is used. Set it to a positive
   1167 integer to use a specific port.")
   1168 
   1169 (defvar lsp--tcp-server-wait-seconds 10
   1170   "Wait this amount of time for the client to connect to our server socket
   1171 when using `lsp-tcp-server'.")
   1172 
   1173 (defvar-local lsp--document-symbols nil
   1174   "The latest document symbols.")
   1175 
   1176 (defvar-local lsp--document-selection-range-cache nil
   1177   "The document selection cache.")
   1178 
   1179 (defvar-local lsp--document-symbols-request-async nil
   1180   "If non-nil, request document symbols asynchronously.")
   1181 
   1182 (defvar-local lsp--document-symbols-tick -1
   1183   "The value of `buffer-chars-modified-tick' when document
   1184   symbols were last retrieved.")
   1185 
   1186 (defvar-local lsp--have-document-highlights nil
   1187   "Set to `t' on symbol highlighting, cleared on
   1188 `lsp--cleanup-highlights-if-needed'. Checking a separately
   1189 defined flag is substantially faster than unconditionally
   1190 calling `remove-overlays'.")
   1191 
   1192 ;; Buffer local variable for storing number of lines.
   1193 (defvar lsp--log-lines)
   1194 
   1195 (defvar-local lsp--eldoc-saved-message nil)
   1196 
   1197 (defvar lsp--on-change-timer nil)
   1198 (defvar lsp--on-idle-timer nil)
   1199 
   1200 (defvar-local lsp--signature-last nil)
   1201 (defvar-local lsp--signature-last-index nil)
   1202 (defvar lsp--signature-last-buffer nil)
   1203 
   1204 (defvar-local lsp--virtual-buffer-point-max nil)
   1205 
   1206 (cl-defmethod lsp-execute-command (_server _command _arguments)
   1207   "Ask SERVER to execute COMMAND with ARGUMENTS.")
   1208 
   1209 (defun lsp-elt (sequence n)
   1210   "Return Nth element of SEQUENCE or nil if N is out of range."
   1211   (cond
   1212    ((listp sequence) (elt sequence n))
   1213    ((arrayp sequence)
   1214     (and (> (length sequence) n) (aref sequence n)))
   1215    (t (and (> (length sequence) n) (elt sequence n)))))
   1216 
   1217 ;; define seq-first and seq-rest for older emacs
   1218 (defun lsp-seq-first (sequence)
   1219   "Return the first element of SEQUENCE."
   1220   (lsp-elt sequence 0))
   1221 
   1222 (defun lsp-seq-rest (sequence)
   1223   "Return a sequence of the elements of SEQUENCE except the first one."
   1224   (seq-drop sequence 1))
   1225 
   1226 ;;;###autoload
   1227 (defun lsp--string-listp (sequence)
   1228   "Return t if all elements of SEQUENCE are strings, else nil."
   1229   (not (seq-find (lambda (x) (not (stringp x))) sequence)))
   1230 
   1231 (defun lsp--string-vector-p (candidate)
   1232   "Returns true if CANDIDATE is a vector data structure and
   1233 every element of it is of type string, else nil."
   1234   (and
   1235    (vectorp candidate)
   1236    (seq-every-p #'stringp candidate)))
   1237 
   1238 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0")
   1239 
   1240 (defun lsp--editable-vector-match (widget value)
   1241   "Function for `lsp-editable-vector' :match."
   1242   ;; Value must be a list or a vector and all the members must match the type.
   1243   (and (or (listp value) (vectorp value))
   1244        (length (cdr (lsp--editable-vector-match-inline widget value)))))
   1245 
   1246 (defun lsp--editable-vector-match-inline (widget value)
   1247   "Value for `lsp-editable-vector' :match-inline."
   1248   (let ((type (nth 0 (widget-get widget :args)))
   1249         (ok t)
   1250         found)
   1251     (while (and value ok)
   1252       (let ((answer (widget-match-inline type value)))
   1253         (if answer
   1254             (let ((head (if (vectorp answer) (aref answer 0) (car answer)))
   1255                   (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer))))
   1256               (setq found (append found head)
   1257                     value tail))
   1258           (setq ok nil))))
   1259     (cons found value)))
   1260 
   1261 (defun lsp--editable-vector-value-to-external (_widget internal-value)
   1262   "Convert the internal list value to a vector."
   1263   (if (listp internal-value)
   1264       (apply 'vector internal-value)
   1265     internal-value))
   1266 
   1267 (defun lsp--editable-vector-value-to-internal (_widget external-value)
   1268   "Convert the external vector value to a list."
   1269   (if (vectorp external-value)
   1270       (append external-value nil)
   1271     external-value))
   1272 
   1273 (define-widget 'lsp--editable-vector 'editable-list
   1274   "A subclass of `editable-list' that accepts and returns a
   1275 vector instead of a list."
   1276   :value-to-external 'lsp--editable-vector-value-to-external
   1277   :value-to-internal 'lsp--editable-vector-value-to-internal
   1278   :match 'lsp--editable-vector-match
   1279   :match-inline 'lsp--editable-vector-match-inline)
   1280 
   1281 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector
   1282   "A variable length homogeneous vector."
   1283   :tag "Repeat"
   1284   :format "%{%t%}:\n%v%i\n")
   1285 
   1286 (define-widget 'lsp-string-vector 'lazy
   1287   "A vector of zero or more elements, every element of which is a string.
   1288 Appropriate for any language-specific `defcustom' that needs to
   1289 serialize as a JSON array of strings.
   1290 
   1291 Deprecated. Use `lsp-repeatable-vector' instead. "
   1292   :offset 4
   1293   :tag "Vector"
   1294   :type '(lsp-repeatable-vector string))
   1295 
   1296 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0")
   1297 
   1298 (defvar lsp--show-message t
   1299   "If non-nil, show debug message from `lsp-mode'.")
   1300 
   1301 (defun lsp--message  (format &rest args)
   1302   "Wrapper for `message'
   1303 
   1304 We `inhibit-message' the message when the cursor is in the
   1305 minibuffer and when emacs version is before emacs 27 due to the
   1306 fact that we often use `lsp--info', `lsp--warn' and `lsp--error'
   1307 in async context and the call to these function is removing the
   1308 minibuffer prompt. The issue with async messages is already fixed
   1309 in emacs 27.
   1310 
   1311 See #2049"
   1312   (when lsp--show-message
   1313     (let ((inhibit-message (or inhibit-message
   1314                                (and (minibufferp)
   1315                                     (version< emacs-version "27.0")))))
   1316       (apply #'message format args))))
   1317 
   1318 (defun lsp--info (format &rest args)
   1319   "Display lsp info message with FORMAT with ARGS."
   1320   (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args)))
   1321 
   1322 (defun lsp--warn (format &rest args)
   1323   "Display lsp warn message with FORMAT with ARGS."
   1324   (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args)))
   1325 
   1326 (defun lsp--error (format &rest args)
   1327   "Display lsp error message with FORMAT with ARGS."
   1328   (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args)))
   1329 
   1330 (defun lsp-log (format &rest args)
   1331   "Log message to the ’*lsp-log*’ buffer.
   1332 
   1333 FORMAT and ARGS i the same as for `message'."
   1334   (when lsp-log-max
   1335     (let ((log-buffer (get-buffer "*lsp-log*"))
   1336           (inhibit-read-only t))
   1337       (unless log-buffer
   1338         (setq log-buffer (get-buffer-create "*lsp-log*"))
   1339         (with-current-buffer log-buffer
   1340           (buffer-disable-undo)
   1341           (view-mode 1)
   1342           (set (make-local-variable 'lsp--log-lines) 0)))
   1343       (with-current-buffer log-buffer
   1344         (save-excursion
   1345           (let* ((message (apply 'format format args))
   1346                  ;; Count newlines in message.
   1347                  (newlines (1+ (cl-loop with start = 0
   1348                                         for count from 0
   1349                                         while (string-match "\n" message start)
   1350                                         do (setq start (match-end 0))
   1351                                         finally return count))))
   1352             (goto-char (point-max))
   1353 
   1354             ;; in case the buffer is not empty insert before last \n to preserve
   1355             ;; the point position(in case it is in the end)
   1356             (if (eq (point) (point-min))
   1357                 (progn
   1358                   (insert "\n")
   1359                   (backward-char))
   1360               (backward-char)
   1361               (insert "\n"))
   1362             (insert message)
   1363 
   1364             (setq lsp--log-lines (+ lsp--log-lines newlines))
   1365 
   1366             (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max))
   1367               (let ((to-delete (- lsp--log-lines lsp-log-max)))
   1368                 (goto-char (point-min))
   1369                 (forward-line to-delete)
   1370                 (delete-region (point-min) (point))
   1371                 (setq lsp--log-lines lsp-log-max)))))))))
   1372 
   1373 (defalias 'lsp-message 'lsp-log)
   1374 
   1375 (defalias 'lsp-ht 'ht)
   1376 
   1377 (defalias 'lsp-file-local-name 'file-local-name)
   1378 
   1379 (defun lsp-f-canonical (file-name)
   1380   "Return the canonical FILE-NAME, without a trailing slash."
   1381   (directory-file-name (expand-file-name file-name)))
   1382 
   1383 (defalias 'lsp-canonical-file-name 'lsp-f-canonical)
   1384 
   1385 (defun lsp-f-same? (path-a path-b)
   1386   "Return t if PATH-A and PATH-B are references to the same file.
   1387 Symlinks are not followed."
   1388   (when (and (f-exists? path-a)
   1389              (f-exists? path-b))
   1390     (equal
   1391      (lsp-f-canonical (directory-file-name (f-expand path-a)))
   1392      (lsp-f-canonical (directory-file-name (f-expand path-b))))))
   1393 
   1394 (defun lsp-f-parent (path)
   1395   "Return the parent directory to PATH.
   1396 Symlinks are not followed."
   1397   (let ((parent (file-name-directory
   1398                  (directory-file-name (f-expand path default-directory)))))
   1399     (unless (lsp-f-same? path parent)
   1400       (if (f-relative? path)
   1401           (f-relative parent)
   1402         (directory-file-name parent)))))
   1403 
   1404 (defun lsp-f-ancestor-of? (path-a path-b)
   1405   "Return t if PATH-A is an ancestor of PATH-B.
   1406 Symlinks are not followed."
   1407   (unless (lsp-f-same? path-a path-b)
   1408     (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator))
   1409                (lsp-f-canonical path-b))))
   1410 
   1411 (defun lsp--merge-results (results method)
   1412   "Merge RESULTS by filtering the empty hash-tables and merging
   1413 the lists according to METHOD."
   1414   (pcase (--map (if (vectorp it)
   1415                     (append it nil) it)
   1416                 (-filter #'identity results))
   1417     (`() ())
   1418     ;; only one result - simply return it
   1419     (`(,fst) fst)
   1420     ;; multiple results merge it based on strategy
   1421     (results
   1422      (pcase method
   1423        ("textDocument/hover" (pcase (seq-filter
   1424                                      (-compose #'not #'lsp-empty?)
   1425                                      results)
   1426                                (`(,hover) hover)
   1427                                (hovers (lsp-make-hover
   1428                                         :contents
   1429                                         (-mapcat
   1430                                          (-lambda ((&Hover :contents))
   1431                                            (if (and (sequencep contents)
   1432                                                     (not (stringp contents)))
   1433                                                (append contents ())
   1434                                              (list contents)))
   1435                                          hovers)))))
   1436        ("textDocument/completion"
   1437         (lsp-make-completion-list
   1438          :is-incomplete (seq-some
   1439                          #'lsp:completion-list-is-incomplete
   1440                          results)
   1441          :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it)
   1442                                                     (lsp:completion-list-items it)
   1443                                                   it)
   1444                                                 nil))
   1445                            results)))
   1446        ("completionItem/resolve"
   1447         (let ((item (cl-first results)))
   1448           (when-let* ((details (seq-filter #'identity
   1449                                           (seq-map #'lsp:completion-item-detail? results))))
   1450             (lsp:set-completion-item-detail?
   1451              item
   1452              (string-join details " ")))
   1453           (when-let* ((docs (seq-filter #'identity
   1454                                        (seq-map #'lsp:completion-item-documentation? results))))
   1455             (lsp:set-completion-item-documentation?
   1456              item
   1457              (lsp-make-markup-content
   1458               :kind (or (seq-some (lambda (it)
   1459                                     (when (equal (lsp:markup-content-kind it)
   1460                                                  lsp/markup-kind-markdown)
   1461                                       lsp/markup-kind-markdown))
   1462                                   docs)
   1463                         lsp/markup-kind-plain-text)
   1464               :value (string-join (seq-map (lambda (doc)
   1465                                              (or (lsp:markup-content-value doc)
   1466                                                  (and (stringp doc) doc)))
   1467                                            docs)
   1468                                   "\n"))))
   1469           (when-let* ((edits (seq-filter #'identity
   1470                                         (seq-map #'lsp:completion-item-additional-text-edits? results))))
   1471             (lsp:set-completion-item-additional-text-edits?
   1472              item
   1473              (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits)))
   1474           item))
   1475        (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results))))))
   1476 
   1477 (defun lsp--spinner-start ()
   1478   "Start spinner indication."
   1479   (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error)))
   1480 
   1481 (defun lsp--propertize (str type)
   1482   "Propertize STR as per TYPE."
   1483   (propertize str 'face (alist-get type lsp--message-type-face)))
   1484 
   1485 (defun lsp-workspaces ()
   1486   "Return the lsp workspaces associated with the current project."
   1487   (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces))
   1488 
   1489 (defun lsp--completing-read (prompt collection transform-fn &optional predicate
   1490                                     require-match initial-input
   1491                                     hist def inherit-input-method)
   1492   "Wrap `completing-read' to provide transformation function and disable sort.
   1493 
   1494 TRANSFORM-FN will be used to transform each of the items before displaying.
   1495 
   1496 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF
   1497 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes."
   1498   (let* ((col (--map (cons (funcall transform-fn it) it) collection))
   1499          (completion (completing-read prompt
   1500                                       (lambda (string pred action)
   1501                                         (if (eq action 'metadata)
   1502                                             `(metadata (display-sort-function . identity))
   1503                                           (complete-with-action action col string pred)))
   1504                                       predicate require-match initial-input hist
   1505                                       def inherit-input-method)))
   1506     (cdr (assoc completion col))))
   1507 
   1508 (defconst lsp--system-arch (lambda ()
   1509                              (setq lsp--system-arch
   1510                                    (pcase system-type
   1511                                      ('windows-nt
   1512                                       (pcase system-configuration
   1513                                         ((rx bol "x86_64-") 'x64)
   1514                                         (_ 'x86)))
   1515                                      ('darwin
   1516                                       (pcase system-configuration
   1517                                         ((rx "aarch64-") 'arm64)
   1518                                         (_ 'x64)))
   1519                                      ('gnu/linux
   1520                                        (pcase system-configuration
   1521                                          ((rx bol "aarch64-") 'arm64)
   1522                                          ((rx bol "x86_64") 'x64)
   1523                                          ((rx bol (| "i386" "i886")) 'x32)))
   1524                                      (_
   1525                                       (pcase system-configuration
   1526                                         ((rx bol "x86_64") 'x64)
   1527                                         ((rx bol (| "i386" "i886")) 'x32))))))
   1528   "Return the system architecture of `Emacs'.
   1529 Special values:
   1530   `x64'       64bit
   1531   `x32'       32bit
   1532   `arm64'     ARM 64bit")
   1533 
   1534 (defmacro lsp-with-current-buffer (buffer-id &rest body)
   1535   (declare (indent 1) (debug t))
   1536   `(if-let* ((wcb (plist-get ,buffer-id :with-current-buffer)))
   1537        (with-lsp-workspaces (plist-get ,buffer-id :workspaces)
   1538          (funcall wcb (lambda () ,@body)))
   1539      (with-current-buffer ,buffer-id
   1540        ,@body)))
   1541 
   1542 (defvar lsp--throw-on-input nil
   1543   "Make `lsp-*-while-no-input' throws `input' on interrupted.")
   1544 
   1545 (defmacro lsp--catch (tag bodyform &rest handlers)
   1546   "Catch TAG thrown in BODYFORM.
   1547 The return value from TAG will be handled in HANDLERS by `pcase'."
   1548   (declare (debug (form form &rest (pcase-PAT body))) (indent 2))
   1549   (let ((re-sym (make-symbol "re")))
   1550     `(let ((,re-sym (catch ,tag ,bodyform)))
   1551        (pcase ,re-sym
   1552          ,@handlers))))
   1553 
   1554 (defmacro lsp--while-no-input (&rest body)
   1555   "Wrap BODY in `while-no-input' and respecting `non-essential'.
   1556 If `lsp--throw-on-input' is set, will throw if input is pending, else
   1557 return value of `body' or nil if interrupted."
   1558   (declare (debug t) (indent 0))
   1559   `(if non-essential
   1560        (let ((res (while-no-input ,@body)))
   1561          (cond
   1562           ((and lsp--throw-on-input (equal res t))
   1563            (throw 'input :interrupted))
   1564           ((booleanp res) nil)
   1565           (t res)))
   1566      ,@body))
   1567 
   1568 ;; A ‘lsp--client’ object describes the client-side behavior of a language
   1569 ;; server.  It is used to start individual server processes, each of which is
   1570 ;; represented by a ‘lsp--workspace’ object.  Client objects are normally
   1571 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’.  Each
   1572 ;; workspace refers to exactly one client, but there can be multiple workspaces
   1573 ;; for a single client.
   1574 (cl-defstruct lsp--client
   1575   ;; ‘language-id’ is a function that receives a buffer as a single argument
   1576   ;; and should return the language identifier for that buffer.  See
   1577   ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem
   1578   ;; for a list of language identifiers.  Also consult the documentation for
   1579   ;; the language server represented by this client to find out what language
   1580   ;; identifiers it supports or expects.
   1581   (language-id nil)
   1582 
   1583   ;; ‘add-on?’ when set to t the server will be started no matter whether there
   1584   ;; is another server handling the same mode.
   1585   (add-on? nil)
   1586   ;; ‘new-connection’ is a function that should start a language server process
   1587   ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS).
   1588   ;; COMMAND-PROCESS must be a process object representing the server process
   1589   ;; just started.  COMMUNICATION-PROCESS must be a process (including pipe and
   1590   ;; network processes) that ‘lsp-mode’ uses to communicate with the language
   1591   ;; server using the language server protocol.  COMMAND-PROCESS and
   1592   ;; COMMUNICATION-PROCESS may be the same process; in that case
   1593   ;; ‘new-connection’ may also return that process as a single
   1594   ;; object. ‘new-connection’ is called with two arguments, FILTER and
   1595   ;; SENTINEL.  FILTER should be used as process filter for
   1596   ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for
   1597   ;; COMMAND-PROCESS.
   1598   (new-connection nil)
   1599 
   1600   ;; ‘ignore-regexps’ is a list of regexps.  When a data packet from the
   1601   ;; language server matches any of these regexps, it will be ignored.  This is
   1602   ;; intended for dealing with language servers that output non-protocol data.
   1603   (ignore-regexps nil)
   1604 
   1605   ;; ‘ignore-messages’ is a list of regexps.  When a message from the language
   1606   ;; server matches any of these regexps, it will be ignored.  This is useful
   1607   ;; for filtering out unwanted messages; such as servers that send nonstandard
   1608   ;; message types, or extraneous log messages.
   1609   (ignore-messages nil)
   1610 
   1611   ;; ‘notification-handlers’ is a hash table mapping notification method names
   1612   ;; (strings) to functions handling the respective notifications.  Upon
   1613   ;; receiving a notification, ‘lsp-mode’ will call the associated handler
   1614   ;; function passing two arguments, the ‘lsp--workspace’ object and the
   1615   ;; deserialized notification parameters.
   1616   (notification-handlers (make-hash-table :test 'equal))
   1617 
   1618   ;; ‘request-handlers’ is a hash table mapping request method names
   1619   ;; (strings) to functions handling the respective notifications.  Upon
   1620   ;; receiving a request, ‘lsp-mode’ will call the associated handler function
   1621   ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized
   1622   ;; request parameters.
   1623   (request-handlers (make-hash-table :test 'equal))
   1624 
   1625   ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request
   1626   ;; identifiers for pending asynchronous requests to functions handling the
   1627   ;; respective responses.  Upon receiving a response from the language server,
   1628   ;; ‘lsp-mode’ will call the associated response handler function with a
   1629   ;; single argument, the deserialized response parameters.
   1630   (response-handlers (make-hash-table :test 'eql))
   1631 
   1632   ;; ‘prefix-function’ is called for getting the prefix for completion.
   1633   ;; The function takes no parameter and returns a cons (start . end) representing
   1634   ;; the start and end bounds of the prefix. If it's not set, the client uses a
   1635   ;; default prefix function."
   1636   (prefix-function nil)
   1637 
   1638   ;; Contains mapping of scheme to the function that is going to be used to load
   1639   ;; the file.
   1640   (uri-handlers (make-hash-table :test #'equal))
   1641 
   1642   ;; ‘action-handlers’ is a hash table mapping action to a handler function. It
   1643   ;; can be used in `lsp-execute-code-action' to determine whether the action
   1644   ;; current client is interested in executing the action instead of sending it
   1645   ;; to the server.
   1646   (action-handlers (make-hash-table :test 'equal))
   1647 
   1648   ;; `action-filter' can be set to a function that modifies any incoming
   1649   ;; `CodeAction' in place before it is executed. The return value is ignored.
   1650   ;; This can be used to patch up broken code action requests before they are
   1651   ;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an
   1652   ;; example of a function that can be useful here.
   1653   (action-filter nil)
   1654 
   1655   ;; major modes supported by the client.
   1656   major-modes
   1657   ;; Function that will be called to decide if this language client
   1658   ;; should manage a particular buffer. The function will be passed
   1659   ;; the file name and major mode to inform the decision. Setting
   1660   ;; `activation-fn' will override `major-modes', if
   1661   ;; present.
   1662   activation-fn
   1663   ;; Break the tie when major-mode is supported by multiple clients.
   1664   (priority 0)
   1665   ;; Unique identifier for representing the client object.
   1666   server-id
   1667   ;; defines whether the client supports multi root workspaces.
   1668   multi-root
   1669   ;; Initialization options or a function that returns initialization options.
   1670   initialization-options
   1671   ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or
   1672   ;; completely replace, the faces used for semantic highlighting on a
   1673   ;; client-by-client basis.
   1674   ;;
   1675   ;; It recognizes four members, all of which are optional: `:types’ and
   1676   ;; `:modifiers’, respectively, should be face definition lists akin to
   1677   ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be
   1678   ;; merged with the default face definition list.
   1679   ;;
   1680   ;; Alternatively, if the plist members `:discard-default-types’ or
   1681   ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers'
   1682   ;; face definitions will be replaced entirely by their respective overrides.
   1683   ;;
   1684   ;; For example, setting `:semantic-tokens-faces-overrides' to
   1685   ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from
   1686   ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'.
   1687   ;;
   1688   ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))'
   1689   ;; will also remap "macro", but on top of that associate the fictional token type
   1690   ;; "not-quite-a-macro" with the face named `some-face'.
   1691   ;;
   1692   ;; `(:types (("macro" . font-lock-keyword-face))
   1693   ;;   :modifiers (("declaration" . lsp-face-semhl-interface))
   1694   ;;   :discard-default-types t
   1695   ;;   :discard-default-modifiers t)'
   1696   ;; will discard all default face definitions, hence leaving the client with
   1697   ;; only one token type "macro", mapped to `font-lock-keyword-face', and one
   1698   ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'.
   1699   semantic-tokens-faces-overrides
   1700   ;; Provides support for registering LSP Server specific capabilities.
   1701   custom-capabilities
   1702   ;; Function which returns the folders that are considered to be not projects but library files.
   1703   ;; The function accepts one parameter currently active workspace.
   1704   ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225.
   1705   library-folders-fn
   1706   ;; function which will be called when opening file in the workspace to perform
   1707   ;; client specific initialization. The function accepts one parameter
   1708   ;; currently active workspace.
   1709   before-file-open-fn
   1710   ;; Function which will be called right after a workspace has been initialized.
   1711   initialized-fn
   1712   ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP.
   1713   (remote? nil)
   1714 
   1715   ;; ‘completion-in-comments?’ t if the client supports completion in comments.
   1716   (completion-in-comments? nil)
   1717 
   1718   ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client.
   1719   (path->uri-fn nil)
   1720 
   1721   ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client.
   1722   (uri->path-fn nil)
   1723   ;; Function that returns an environment structure that will be used
   1724   ;; to set some environment variables when starting the language
   1725   ;; server process. These environment variables enable some
   1726   ;; additional features in the language server. The environment
   1727   ;; structure is an alist of the form (KEY . VALUE), where KEY is a
   1728   ;; string (regularly in all caps), and VALUE may be a string, a
   1729   ;; boolean, or a sequence of strings.
   1730   environment-fn
   1731 
   1732   ;; ‘after-open-fn’ workspace after open specific hooks.
   1733   (after-open-fn nil)
   1734 
   1735   ;; ‘async-request-handlers’ is a hash table mapping request method names
   1736   ;; (strings) to functions handling the respective requests that may take
   1737   ;; time to finish.  Upon receiving a request, ‘lsp-mode’ will call the
   1738   ;; associated handler function passing three arguments, the ‘lsp--workspace’
   1739   ;; object, the deserialized request parameters and the callback which accept
   1740   ;; result as its parameter.
   1741   (async-request-handlers (make-hash-table :test 'equal))
   1742   download-server-fn
   1743   download-in-progress?
   1744   buffers
   1745   synchronize-sections)
   1746 
   1747 (defun lsp-clients-executable-find (find-command &rest args)
   1748   "Finds an executable by invoking a search command.
   1749 
   1750 FIND-COMMAND is the executable finder that searches for the
   1751 actual language server executable. ARGS is a list of arguments to
   1752 give to FIND-COMMAND to find the language server.  Returns the
   1753 output of FIND-COMMAND if it exits successfully, nil otherwise.
   1754 
   1755 Typical uses include finding an executable by invoking `find' in
   1756 a project, finding LLVM commands on macOS with `xcrun', or
   1757 looking up project-specific language servers for projects written
   1758 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv'
   1759 etc."
   1760   (when-let* ((find-command-path (executable-find find-command))
   1761               (executable-path
   1762                (with-temp-buffer
   1763                  (when (zerop (apply 'call-process find-command-path nil t nil args))
   1764                    (buffer-substring-no-properties (point-min) (point-max))))))
   1765     (string-trim executable-path)))
   1766 
   1767 (defvar lsp--already-widened nil)
   1768 
   1769 (defmacro lsp-save-restriction-and-excursion (&rest form)
   1770   (declare (indent 0) (debug t))
   1771   `(if lsp--already-widened
   1772        (save-excursion ,@form)
   1773      (-let [lsp--already-widened t]
   1774        (save-restriction
   1775          (widen)
   1776          (save-excursion ,@form)))))
   1777 
   1778 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number
   1779 (defun lsp--line-character-to-point (line character)
   1780   "Return the point for character CHARACTER on line LINE."
   1781   (or (lsp-virtual-buffer-call :line/character->point line character)
   1782       (let ((inhibit-field-text-motion t))
   1783         (lsp-save-restriction-and-excursion
   1784           (goto-char (point-min))
   1785           (forward-line line)
   1786           ;; server may send character position beyond the current line and we
   1787           ;; should fallback to line end.
   1788           (-let [line-end (line-end-position)]
   1789             (if (> character (- line-end (point)))
   1790                 line-end
   1791               (forward-char character)
   1792               (point)))))))
   1793 
   1794 (lsp-defun lsp--position-to-point ((&Position :line :character))
   1795   "Convert `Position' object in PARAMS to a point."
   1796   (lsp--line-character-to-point line character))
   1797 
   1798 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end))
   1799   (cons start end))
   1800 
   1801 (lsp-defun lsp--range-text ((&RangeToPoint :start :end))
   1802   (buffer-substring start end))
   1803 
   1804 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end)))
   1805   (cond
   1806    ((and
   1807      (region-active-p)
   1808      (<= start (region-beginning) end)
   1809      (<= start (region-end) end)
   1810      (or (not (= start (region-beginning)))
   1811          (not (= end (region-end)))))
   1812     (cons start end))
   1813    ((and (<= start (point) end)
   1814          (not (region-active-p)))
   1815     (cons start end))
   1816    (parent? (lsp--find-wrapping-range parent?))))
   1817 
   1818 (defun lsp--get-selection-range ()
   1819   (or
   1820    (-when-let ((cache . cache-tick) lsp--document-selection-range-cache)
   1821      (when (= cache-tick (buffer-modified-tick)) cache))
   1822    (let ((response (cl-first
   1823                     (lsp-request
   1824                      "textDocument/selectionRange"
   1825                      (list :textDocument (lsp--text-document-identifier)
   1826                            :positions (vector (lsp--cur-position)))))))
   1827      (setq lsp--document-selection-range-cache
   1828            (cons response (buffer-modified-tick)))
   1829      response)))
   1830 
   1831 (defun lsp-extend-selection ()
   1832   "Extend selection."
   1833   (interactive)
   1834   (unless (lsp-feature? "textDocument/selectionRange")
   1835     (signal 'lsp-capability-not-supported (list "selectionRangeProvider")))
   1836   (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range)))
   1837     (goto-char start)
   1838     (set-mark (point))
   1839     (goto-char end)
   1840     (exchange-point-and-mark)))
   1841 
   1842 (defun lsp-warn (message &rest args)
   1843   "Display a warning message made from (`format-message' MESSAGE ARGS...).
   1844 This is equivalent to `display-warning', using `lsp-mode' as the type and
   1845 `:warning' as the level."
   1846   (display-warning 'lsp-mode (apply #'format-message message args)))
   1847 
   1848 (defun lsp--get-uri-handler (scheme)
   1849   "Get uri handler for SCHEME in the current workspace."
   1850   (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it)))
   1851           (or (lsp-workspaces) (lsp--session-workspaces (lsp-session)))))
   1852 
   1853 (defun lsp--fix-path-casing (path)
   1854   "On windows, downcases path because the windows file system is
   1855 case-insensitive.
   1856 
   1857 On other systems, returns path without change."
   1858   (if (eq system-type 'windows-nt) (downcase path) path))
   1859 
   1860 (defun lsp--uri-to-path (uri)
   1861   "Convert URI to a file path."
   1862   (if-let* ((fn (->> (lsp-workspaces)
   1863                     (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client))
   1864                     (cl-first))))
   1865       (funcall fn uri)
   1866     (lsp--uri-to-path-1 uri)))
   1867 
   1868 (defun lsp-remap-path-if-needed (file-name)
   1869   (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings))
   1870       (propertize (buffer-local-value 'buffer-file-name buffer)
   1871                   'lsp-virtual-buffer virtual-buffer)
   1872     file-name))
   1873 
   1874 (defun lsp--uri-to-path-1 (uri)
   1875   "Convert URI to a file path."
   1876   (let* ((url (url-generic-parse-url (url-unhex-string uri)))
   1877          (type (url-type url))
   1878          (target (url-target url))
   1879          (file
   1880           (concat (decode-coding-string (url-filename url)
   1881                                         (or locale-coding-system 'utf-8))
   1882                   (when (and target
   1883                              (not (s-match
   1884                                    (rx "#" (group (1+ num)) (or "," "#")
   1885                                        (group (1+ num))
   1886                                        string-end)
   1887                                    uri)))
   1888                     (concat "#" target))))
   1889          (file-name (if (and type (not (string= type "file")))
   1890                         (if-let* ((handler (lsp--get-uri-handler type)))
   1891                             (funcall handler uri)
   1892                           uri)
   1893                       ;; `url-generic-parse-url' is buggy on windows:
   1894                       ;; https://github.com/emacs-lsp/lsp-mode/pull/265
   1895                       (or (and (eq system-type 'windows-nt)
   1896                                (eq (elt file 0) ?\/)
   1897                                (substring file 1))
   1898                           file))))
   1899     (->> file-name
   1900          (concat (-some #'lsp--workspace-host-root (lsp-workspaces)))
   1901          (lsp-remap-path-if-needed))))
   1902 
   1903 (defun lsp--buffer-uri ()
   1904   "Return URI of the current buffer."
   1905   (or lsp-buffer-uri
   1906       (plist-get lsp--virtual-buffer :buffer-uri)
   1907       (lsp--path-to-uri
   1908        (or (buffer-file-name) (buffer-file-name (buffer-base-buffer))))))
   1909 
   1910 (defun lsp-register-client-capabilities (&rest _args)
   1911   "Implemented only to make `company-lsp' happy.
   1912 DELETE when `lsp-mode.el' is deleted.")
   1913 
   1914 (defconst lsp--url-path-allowed-chars
   1915   (url--allowed-chars (append '(?/) url-unreserved-chars))
   1916   "`url-unreserved-chars' with additional delim ?/.
   1917 This set of allowed chars is enough for hexifying local file paths.")
   1918 
   1919 (defun lsp--path-to-uri-1 (path)
   1920   (concat lsp--uri-file-prefix
   1921           (--> path
   1922             (expand-file-name it)
   1923             (or (file-remote-p it 'localname t) it)
   1924             (url-hexify-string it lsp--url-path-allowed-chars))))
   1925 
   1926 (defun lsp--path-to-uri (path)
   1927   "Convert PATH to a uri."
   1928   (if-let* ((uri-fn (->> (lsp-workspaces)
   1929                         (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client))
   1930                         (cl-first))))
   1931       (funcall uri-fn path)
   1932     (lsp--path-to-uri-1 path)))
   1933 
   1934 (defun lsp--string-match-any (regex-list str)
   1935   "Return the first regex, if any, within REGEX-LIST matching STR."
   1936   (--first (string-match it str) regex-list))
   1937 
   1938 (cl-defstruct lsp-watch
   1939   (descriptors (make-hash-table :test 'equal))
   1940   root-directory)
   1941 
   1942 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories)
   1943   (let ((file-name (cl-third event))
   1944         (event-type (cl-second event)))
   1945     (cond
   1946      ((and (file-directory-p file-name)
   1947            (equal 'created event-type)
   1948            (not (lsp--string-match-any ignored-directories file-name)))
   1949 
   1950       (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch)
   1951 
   1952       ;; process the files that are already present in
   1953       ;; the directory.
   1954       (->> (directory-files-recursively file-name ".*" t)
   1955            (seq-do (lambda (f)
   1956                      (unless (file-directory-p f)
   1957                        (funcall callback (list nil 'created f)))))))
   1958      ((and (memq event-type '(created deleted changed))
   1959            (not (file-directory-p file-name))
   1960            (not (lsp--string-match-any ignored-files file-name)))
   1961       (funcall callback event))
   1962      ((and (memq event-type '(renamed))
   1963            (not (file-directory-p file-name))
   1964            (not (lsp--string-match-any ignored-files file-name)))
   1965       (funcall callback `(,(cl-first event) deleted ,(cl-third event)))
   1966       (funcall callback `(,(cl-first event) created ,(cl-fourth event)))))))
   1967 
   1968 (defun lsp--ask-about-watching-big-repo (number-of-directories dir)
   1969   "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR.
   1970 This is useful when there is a lot of files in a repository, as
   1971 that may slow Emacs down. Returns t if the user wants to watch
   1972 the entire repository, nil otherwise."
   1973   (prog1
   1974       (yes-or-no-p
   1975        (format
   1976         "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down.
   1977 Do you want to watch all files in %s? "
   1978         dir
   1979         number-of-directories
   1980         dir))
   1981     (lsp--info
   1982      (concat "You can configure this warning with the `lsp-enable-file-watchers' "
   1983              "and `lsp-file-watch-threshold' variables"))))
   1984 
   1985 
   1986 (defun lsp--path-is-watchable-directory (path dir ignored-directories)
   1987   "Figure out whether PATH (inside of DIR) is meant to have a file watcher set.
   1988 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't
   1989 want to watch."
   1990   (let
   1991       ((full-path (f-join dir path)))
   1992     (and (file-accessible-directory-p full-path)
   1993          (not (equal path "."))
   1994          (not (equal path ".."))
   1995          (not (lsp--string-match-any ignored-directories full-path)))))
   1996 
   1997 
   1998 (defun lsp--all-watchable-directories (dir ignored-directories &optional visited)
   1999   "Traverse DIR recursively returning a list of paths that should have watchers.
   2000 IGNORED-DIRECTORIES will be used for exclusions.
   2001 VISITED is used to track already-visited directories to avoid infinite loops."
   2002   (let* ((dir (if (f-symlink? dir)
   2003                   (file-truename dir)
   2004                 dir))
   2005          ;; Initialize visited directories if not provided
   2006          (visited (or visited (make-hash-table :test 'equal))))
   2007     (if (gethash dir visited)
   2008         ;; If the directory has already been visited, skip it
   2009         nil
   2010       ;; Mark the current directory as visited
   2011       (puthash dir t visited)
   2012       (apply #'nconc
   2013              ;; the directory itself is assumed to be part of the set
   2014              (list dir)
   2015              ;; collect all subdirectories that are watchable
   2016              (-map
   2017               (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories visited))
   2018               ;; but only look at subdirectories that are watchable
   2019               (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories))
   2020                        (directory-files dir)))))))
   2021 
   2022 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?)
   2023   "Create recursive file notification watch in DIR.
   2024 CALLBACK will be called when there are changes in any of
   2025 the monitored files. WATCHES is a hash table directory->file
   2026 notification handle which contains all of the watch that
   2027 already have been created. Watches will not be created for
   2028 any directory that matches any regex in IGNORED-DIRECTORIES.
   2029 Watches will not be created for any file that matches any
   2030 regex in IGNORED-FILES."
   2031   (let* ((dir (if (f-symlink? dir)
   2032                   (file-truename dir)
   2033                 dir))
   2034          (watch (or watch (make-lsp-watch :root-directory dir)))
   2035          (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories)))
   2036     (lsp-log "Creating watchers for following %s folders:\n  %s"
   2037              (length dirs-to-watch)
   2038              (s-join "\n  " dirs-to-watch))
   2039     (when (or
   2040            (not warn-big-repo?)
   2041            (not lsp-file-watch-threshold)
   2042            (let ((number-of-directories (length dirs-to-watch)))
   2043              (or
   2044               (< number-of-directories lsp-file-watch-threshold)
   2045               (condition-case nil
   2046                   (lsp--ask-about-watching-big-repo number-of-directories dir)
   2047                 (quit)))))
   2048       (dolist (current-dir dirs-to-watch)
   2049         (condition-case err
   2050             (progn
   2051               (puthash
   2052                current-dir
   2053                (file-notify-add-watch current-dir
   2054                                       '(change)
   2055                                       (lambda (event)
   2056                                         (lsp--folder-watch-callback event callback watch ignored-files ignored-directories)))
   2057                (lsp-watch-descriptors watch)))
   2058           (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))
   2059           (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err))))))
   2060     watch))
   2061 
   2062 (defun lsp-kill-watch (watch)
   2063   "Delete WATCH."
   2064   (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch))
   2065   (ht-clear! (lsp-watch-descriptors watch)))
   2066 
   2067 (defun lsp-json-bool (val)
   2068   "Convert VAL to JSON boolean."
   2069   (if val t :json-false))
   2070 
   2071 (defmacro with-lsp-workspace (workspace &rest body)
   2072   "Helper macro for invoking BODY in WORKSPACE context."
   2073   (declare (debug (form body))
   2074            (indent 1))
   2075   `(let ((lsp--cur-workspace ,workspace)) ,@body))
   2076 
   2077 (defmacro with-lsp-workspaces (workspaces &rest body)
   2078   "Helper macro for invoking BODY against multiple WORKSPACES."
   2079   (declare (debug (form body))
   2080            (indent 1))
   2081   `(let ((lsp--buffer-workspaces ,workspaces)) ,@body))
   2082 
   2083 
   2084 
   2085 (defmacro lsp-consistency-check (package)
   2086   `(defconst ,(intern (concat (symbol-name package)
   2087                               "-plist-value-when-compiled"))
   2088      (eval-when-compile lsp-use-plists)))
   2089 
   2090 
   2091 ;; loading code-workspace files
   2092 
   2093 ;;;###autoload
   2094 (defun lsp-load-vscode-workspace (file)
   2095   "Load vscode workspace from FILE"
   2096   (interactive "fSelect file to import: ")
   2097   (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session)))
   2098 
   2099   (let ((dir (f-dirname file)))
   2100     (->> file
   2101          (json-read-file)
   2102          (alist-get 'folders)
   2103          (-map (-lambda ((&alist 'path))
   2104                  (lsp-workspace-folders-add (expand-file-name path dir)))))))
   2105 
   2106 ;;;###autoload
   2107 (defun lsp-save-vscode-workspace (file)
   2108   "Save vscode workspace to FILE"
   2109   (interactive "FSelect file to save to: ")
   2110 
   2111   (let ((json-encoding-pretty-print t))
   2112     (f-write-text (json-encode
   2113                    `((folders . ,(->> (lsp-session)
   2114                                       (lsp-session-folders)
   2115                                       (--map `((path . ,it)))))))
   2116                   'utf-8
   2117                   file)))
   2118 
   2119 
   2120 (defmacro lsp-foreach-workspace (&rest body)
   2121   "Execute BODY for each of the current workspaces."
   2122   (declare (debug (form body)))
   2123   `(--map (with-lsp-workspace it ,@body) (lsp-workspaces)))
   2124 
   2125 (defmacro when-lsp-workspace (workspace &rest body)
   2126   "Helper macro for invoking BODY in WORKSPACE context if present."
   2127   (declare (debug (form body))
   2128            (indent 1))
   2129   `(when-let* ((lsp--cur-workspace ,workspace)) ,@body))
   2130 
   2131 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items))
   2132   (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read))
   2133             (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label))
   2134                                  items))
   2135             (result (funcall-interactively
   2136                      selectfunc
   2137                      (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels))
   2138             (choices (if (listp result)
   2139                          (if (equal result '("*"))
   2140                              itemLabels
   2141                            result)
   2142                        (list result))))
   2143       (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data))
   2144                                                  (if (member label choices)
   2145                                                      (lsp-make-quick-pick-item :label label :picked t :user-data user-data)
   2146                                                    nil))
   2147                                                items)))))
   2148 
   2149 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?))
   2150   (read-string (format "%s: " prompt) (or value? "")))
   2151 
   2152 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type))
   2153   "Send the server's messages to log.
   2154 PARAMS - the data sent from _WORKSPACE."
   2155   (funcall (cl-case type
   2156              (1 'lsp--error)
   2157              (2 'lsp--warn)
   2158              (t 'lsp--info))
   2159            "%s"
   2160            message))
   2161 
   2162 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type))
   2163   "Send the server's messages to log.
   2164 PARAMS - the data sent from WORKSPACE."
   2165   (ignore
   2166    (let ((client (lsp--workspace-client workspace)))
   2167      (when (or (not client)
   2168                (cl-notany (-rpartial #'string-match-p message)
   2169                           (lsp--client-ignore-messages client)))
   2170        (lsp-log "%s" (lsp--propertize message type))))))
   2171 
   2172 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?))
   2173   "Display a message request to user sending the user selection back to server."
   2174   (let* ((message (lsp--propertize message type))
   2175          (choices (seq-map #'lsp:message-action-item-title actions?)))
   2176     (if choices
   2177         (completing-read (concat message " ") (seq-into choices 'list) nil t)
   2178       (lsp-log message))))
   2179 
   2180 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?))
   2181   "Show document URI in a buffer and go to SELECTION if any."
   2182   (let ((path (lsp--uri-to-path uri)))
   2183     (when (f-exists? path)
   2184       (with-current-buffer (find-file path)
   2185         (when selection?
   2186           (goto-char (lsp--position-to-point (lsp:range-start selection?))))
   2187         t))))
   2188 
   2189 (defcustom lsp-progress-prefix "⌛ "
   2190   "Progress prefix."
   2191   :group 'lsp-mode
   2192   :type 'string
   2193   :package-version '(lsp-mode . "8.0.0"))
   2194 
   2195 (defcustom lsp-progress-function #'lsp-on-progress-modeline
   2196   "Function for handling the progress notifications."
   2197   :group 'lsp-mode
   2198   :type '(choice
   2199           (const :tag "Use modeline" lsp-on-progress-modeline)
   2200           (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')"
   2201                  lsp-on-progress-legacy)
   2202           (const :tag "Ignore" ignore)
   2203           (function :tag "Other function"))
   2204   :package-version '(lsp-mode . "8.0.0"))
   2205 
   2206 (defcustom lsp-request-while-no-input-may-block nil
   2207   "Have `lsp-request-while-no-input` block unless `non-essential` is t."
   2208   :group 'lsp-mode
   2209   :type 'boolean)
   2210 
   2211 (defun lsp--progress-status ()
   2212   "Returns the status of the progress for the current workspaces."
   2213   (-let ((progress-status
   2214           (s-join
   2215            "|"
   2216            (-keep
   2217             (lambda (workspace)
   2218               (let ((tokens (lsp--workspace-work-done-tokens workspace)))
   2219                 (unless (ht-empty? tokens)
   2220                   (mapconcat
   2221                    (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?))
   2222                      (concat (if percentage?
   2223                                  (if (numberp percentage?)
   2224                                      (format "%.0f%%%% " percentage?)
   2225                                    (format "%s%%%% " percentage?))
   2226                                "")
   2227                              (or message? title)))
   2228                    (ht-values tokens)
   2229                    "|"))))
   2230             (lsp-workspaces)))))
   2231     (unless (s-blank? progress-status)
   2232       (concat lsp-progress-prefix progress-status " "))))
   2233 
   2234 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value
   2235                                                                 (value &as &WorkDoneProgress :kind)))
   2236   "PARAMS contains the progress data.
   2237 WORKSPACE is the workspace that contains the progress token."
   2238   (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status))))
   2239   (pcase kind
   2240     ("begin" (lsp-workspace-set-work-done-token token value workspace))
   2241     ("report" (lsp-workspace-set-work-done-token token value workspace))
   2242     ("end" (lsp-workspace-rem-work-done-token token workspace)))
   2243   (force-mode-line-update))
   2244 
   2245 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value
   2246                                                               (value &as &WorkDoneProgress :kind)))
   2247   "PARAMS contains the progress data.
   2248 WORKSPACE is the workspace that contains the progress token."
   2249   (pcase kind
   2250     ("begin"
   2251      (-let* (((&WorkDoneProgressBegin :title :percentage?) value)
   2252              (reporter
   2253               (if lsp-progress-via-spinner
   2254                   (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types))
   2255                          ;; Set message as a tooltip for the spinner strings
   2256                          (propertized-strings
   2257                           (seq-map (lambda (string) (propertize string 'help-echo title))
   2258                                    spinner-strings))
   2259                          (spinner-type (vconcat propertized-strings)))
   2260                     ;; The progress relates to the server as a whole,
   2261                     ;; display it on all buffers.
   2262                     (mapcar (lambda (buffer)
   2263                               (lsp-with-current-buffer buffer
   2264                                 (spinner-start spinner-type))
   2265                               buffer)
   2266                             (lsp--workspace-buffers workspace)))
   2267                 (if percentage?
   2268                     (make-progress-reporter title 0 100 percentage?)
   2269                   ;; No percentage, just progress
   2270                   (make-progress-reporter title nil nil)))))
   2271        (lsp-workspace-set-work-done-token token reporter workspace)))
   2272     ("report"
   2273      (when-let* ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2274        (unless lsp-progress-via-spinner
   2275          (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value)))))
   2276 
   2277     ("end"
   2278      (when-let* ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2279        (if lsp-progress-via-spinner
   2280            (mapc (lambda (buffer)
   2281                    (when (lsp-buffer-live-p buffer)
   2282                      (lsp-with-current-buffer buffer
   2283                        (spinner-stop))))
   2284                  reporter)
   2285          (progress-reporter-done reporter))
   2286        (lsp-workspace-rem-work-done-token token workspace)))))
   2287 
   2288 
   2289 ;; diagnostics
   2290 
   2291 (defvar lsp-diagnostic-filter nil
   2292   "A a function which will be called with
   2293   `&PublishDiagnosticsParams' and `workspace' which can be used
   2294   to filter out the diagnostics. The function should return
   2295   `&PublishDiagnosticsParams'.
   2296 
   2297 Common usecase are:
   2298 1. Filter the diagnostics for a particular language server.
   2299 2. Filter out the diagnostics under specific level.")
   2300 
   2301 (defvar lsp-diagnostic-stats (ht))
   2302 
   2303 (defun lsp-diagnostics (&optional current-workspace?)
   2304   "Return the diagnostics from all workspaces."
   2305   (or (pcase (if current-workspace?
   2306                  (lsp-workspaces)
   2307                (lsp--session-workspaces (lsp-session)))
   2308         (`() ())
   2309         (`(,workspace) (lsp--workspace-diagnostics workspace))
   2310         (`,workspaces (let ((result (make-hash-table :test 'equal)))
   2311                         (mapc (lambda (workspace)
   2312                                 (->> workspace
   2313                                      (lsp--workspace-diagnostics)
   2314                                      (maphash (lambda (file-name diagnostics)
   2315                                                 (puthash file-name
   2316                                                          (append (gethash file-name result) diagnostics)
   2317                                                          result)))))
   2318                               workspaces)
   2319                         result)))
   2320       (ht)))
   2321 
   2322 (defun lsp-diagnostics-stats-for (path)
   2323   "Get diagnostics statistics for PATH.
   2324 The result format is vector [_ errors warnings infos hints] or nil."
   2325   (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats))
   2326 
   2327 (defun lsp-diagnostics--request-pull-diagnostics (workspace)
   2328   "Request new diagnostics for the current file within WORKSPACE.
   2329 This is only executed if the server supports pull diagnostics."
   2330   (when (lsp-feature? "textDocument/diagnostic")
   2331     (let ((path (lsp--fix-path-casing (buffer-file-name))))
   2332       (lsp-request-async "textDocument/diagnostic"
   2333                          (list :textDocument (lsp--text-document-identifier))
   2334                          (-lambda ((&DocumentDiagnosticReport :kind :items?))
   2335                            (lsp-diagnostics--apply-pull-diagnostics workspace path kind items?))
   2336                          :mode 'tick))))
   2337 
   2338 (defun lsp-diagnostics--update-path (path new-stats)
   2339   (let ((new-stats (copy-sequence new-stats))
   2340         (path (lsp--fix-path-casing (directory-file-name path))))
   2341     (if-let* ((old-data (gethash path lsp-diagnostic-stats)))
   2342         (dotimes (idx 5)
   2343           (cl-callf + (aref old-data idx)
   2344             (aref new-stats idx)))
   2345       (puthash path new-stats lsp-diagnostic-stats))))
   2346 
   2347 (defun lsp-diagnostics--convert-and-update-path-stats (workspace path diagnostics)
   2348   (let ((path (lsp--fix-path-casing path))
   2349         (new-stats (make-vector 5 0)))
   2350     (mapc (-lambda ((&Diagnostic :severity?))
   2351             (cl-incf (aref new-stats (or severity? 1))))
   2352           diagnostics)
   2353     (when-let* ((old-diags (gethash path (lsp--workspace-diagnostics workspace))))
   2354       (mapc (-lambda ((&Diagnostic :severity?))
   2355               (cl-decf (aref new-stats (or severity? 1))))
   2356             old-diags))
   2357     (lsp-diagnostics--update-path path new-stats)
   2358     (while (not (string= path (setf path (file-name-directory
   2359                                           (directory-file-name path)))))
   2360       (lsp-diagnostics--update-path path new-stats))))
   2361 
   2362 (lsp-defun lsp--on-diagnostics-update-stats (workspace
   2363                                              (&PublishDiagnosticsParams :uri :diagnostics))
   2364   (lsp-diagnostics--convert-and-update-path-stats workspace (lsp--uri-to-path uri) diagnostics))
   2365 
   2366 (defun lsp-diagnostics--apply-pull-diagnostics (workspace path kind diagnostics?)
   2367   "Update WORKSPACE diagnostics at PATH with DIAGNOSTICS?.
   2368 Depends on KIND being a \\='full\\=' update."
   2369   (cond
   2370    ((equal kind "full")
   2371     ;; TODO support `lsp-diagnostic-filter'
   2372     ;; (the params types differ from the published diagnostics response)
   2373     (lsp-diagnostics--convert-and-update-path-stats workspace path diagnostics?)
   2374     (-let* ((lsp--virtual-buffer-mappings (ht))
   2375             (workspace-diagnostics (lsp--workspace-diagnostics workspace)))
   2376       (if (seq-empty-p diagnostics?)
   2377           (remhash path workspace-diagnostics)
   2378         (puthash path (append diagnostics? nil) workspace-diagnostics))
   2379       (run-hooks 'lsp-diagnostics-updated-hook)))
   2380     ((equal kind "unchanged") t)
   2381     (t (lsp--error "Unknown pull diagnostic result kind '%s'" kind))))
   2382 
   2383 (defun lsp--on-diagnostics (workspace params)
   2384   "Callback for textDocument/publishDiagnostics.
   2385 interface PublishDiagnosticsParams {
   2386     uri: string;
   2387     diagnostics: Diagnostic[];
   2388 }
   2389 PARAMS contains the diagnostics data.
   2390 WORKSPACE is the workspace that contains the diagnostics."
   2391   (when lsp-diagnostic-filter
   2392     (setf params (funcall lsp-diagnostic-filter params workspace)))
   2393 
   2394   (lsp--on-diagnostics-update-stats workspace params)
   2395 
   2396   (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params)
   2397           (lsp--virtual-buffer-mappings (ht))
   2398           (file (lsp--fix-path-casing (lsp--uri-to-path uri)))
   2399           (workspace-diagnostics (lsp--workspace-diagnostics workspace)))
   2400 
   2401     (if (seq-empty-p diagnostics)
   2402         (remhash file workspace-diagnostics)
   2403       (puthash file (append diagnostics nil) workspace-diagnostics))
   2404 
   2405     (run-hooks 'lsp-diagnostics-updated-hook)))
   2406 
   2407 (defun lsp-diagnostics--workspace-cleanup (workspace)
   2408   (->> workspace
   2409        (lsp--workspace-diagnostics)
   2410        (maphash (lambda (key _)
   2411                   (lsp--on-diagnostics-update-stats
   2412                    workspace
   2413                    (lsp-make-publish-diagnostics-params
   2414                     :uri (lsp--path-to-uri key)
   2415                     :diagnostics [])))))
   2416   (clrhash (lsp--workspace-diagnostics workspace)))
   2417 
   2418 
   2419 
   2420 ;; textDocument/foldingRange support
   2421 
   2422 (cl-defstruct lsp--folding-range beg end kind children)
   2423 
   2424 (defvar-local lsp--cached-folding-ranges nil)
   2425 (defvar-local lsp--cached-nested-folding-ranges nil)
   2426 
   2427 (defun lsp--folding-range-width (range)
   2428   (- (lsp--folding-range-end range)
   2429      (lsp--folding-range-beg range)))
   2430 
   2431 (defun lsp--get-folding-ranges ()
   2432   "Get the folding ranges for the current buffer."
   2433   (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges))
   2434     (let* ((ranges (lsp-request "textDocument/foldingRange"
   2435                                 `(:textDocument ,(lsp--text-document-identifier))))
   2436            (sorted-line-col-pairs (->> ranges
   2437                                        (cl-mapcan (-lambda ((&FoldingRange :start-line
   2438                                                                            :start-character?
   2439                                                                            :end-line
   2440                                                                            :end-character?))
   2441                                                     (list (cons start-line start-character?)
   2442                                                           (cons end-line end-character?))))
   2443                                        (-sort #'lsp--line-col-comparator)))
   2444            (line-col-to-point-map (lsp--convert-line-col-to-points-batch
   2445                                    sorted-line-col-pairs)))
   2446       (setq lsp--cached-folding-ranges
   2447             (cons (buffer-chars-modified-tick)
   2448                   (--> ranges
   2449                     (seq-map (-lambda ((range &as
   2450                                               &FoldingRange :start-line
   2451                                               :start-character?
   2452                                               :end-line
   2453                                               :end-character?
   2454                                               :kind?))
   2455                                (make-lsp--folding-range
   2456                                 :beg (ht-get line-col-to-point-map
   2457                                              (cons start-line start-character?))
   2458                                 :end (ht-get line-col-to-point-map
   2459                                              (cons end-line end-character?))
   2460                                 :kind kind?))
   2461                              it)
   2462                     (seq-filter (lambda (folding-range)
   2463                                   (< (lsp--folding-range-beg folding-range)
   2464                                      (lsp--folding-range-end folding-range)))
   2465                                 it)
   2466                     (seq-into it 'list)
   2467                     (delete-dups it))))))
   2468   (cdr lsp--cached-folding-ranges))
   2469 
   2470 (defun lsp--get-nested-folding-ranges ()
   2471   "Get a list of nested folding ranges for the current buffer."
   2472   (-let [(tick . _) lsp--cached-folding-ranges]
   2473     (if (and (eq tick (buffer-chars-modified-tick))
   2474              lsp--cached-nested-folding-ranges)
   2475         lsp--cached-nested-folding-ranges
   2476       (setq lsp--cached-nested-folding-ranges
   2477             (lsp--folding-range-build-trees (lsp--get-folding-ranges))))))
   2478 
   2479 (defun lsp--folding-range-build-trees (ranges)
   2480   (setq ranges (seq-sort #'lsp--range-before-p ranges))
   2481   (let* ((dummy-node (make-lsp--folding-range
   2482                       :beg most-negative-fixnum
   2483                       :end most-positive-fixnum))
   2484          (stack (list dummy-node)))
   2485     (dolist (range ranges)
   2486       (while (not (lsp--range-inside-p range (car stack)))
   2487         (pop stack))
   2488       (push range (lsp--folding-range-children (car stack)))
   2489       (push range stack))
   2490     (lsp--folding-range-children dummy-node)))
   2491 
   2492 (defun lsp--range-inside-p (r1 r2)
   2493   "Return non-nil if folding range R1 lies inside R2"
   2494   (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2))
   2495        (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2))))
   2496 
   2497 (defun lsp--range-before-p (r1 r2)
   2498   "Return non-nil if folding range R1 ends before R2"
   2499   ;; Ensure r1 comes before r2
   2500   (or (< (lsp--folding-range-beg r1)
   2501          (lsp--folding-range-beg r2))
   2502       ;; If beg(r1) == beg(r2) make sure r2 ends first
   2503       (and (= (lsp--folding-range-beg r1)
   2504               (lsp--folding-range-beg r2))
   2505            (< (lsp--folding-range-end r2)
   2506               (lsp--folding-range-end r1)))))
   2507 
   2508 (defun lsp--point-inside-range-p (point range)
   2509   "Return non-nil if POINT lies inside folding range RANGE."
   2510   (and (>= point (lsp--folding-range-beg range))
   2511        (<= point (lsp--folding-range-end range))))
   2512 
   2513 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point)))
   2514   "Return the innermost folding range POINT lies in."
   2515   (seq-reduce (lambda (innermost-range curr-range)
   2516                 (if (and (lsp--point-inside-range-p point curr-range)
   2517                          (or (null innermost-range)
   2518                              (lsp--range-inside-p curr-range innermost-range)))
   2519                     curr-range
   2520                   innermost-range))
   2521               (lsp--get-folding-ranges)
   2522               nil))
   2523 
   2524 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point)))
   2525   "Return the outermost folding range POINT lies in."
   2526   (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range)
   2527                      (let ((curr-width (lsp--folding-range-width curr-range)))
   2528                        (if (and (lsp--point-inside-range-p point curr-range)
   2529                                 (or (null best-pair)
   2530                                     (> curr-width outermost-width)))
   2531                            (cons curr-width curr-range)
   2532                          best-pair)))
   2533                    (lsp--get-folding-ranges)
   2534                    nil)))
   2535 
   2536 (defun lsp--folding-range-at-point-bounds ()
   2537   (when (and lsp-enable-folding
   2538              (lsp-feature? "textDocument/foldingRange"))
   2539     (if-let* ((range (lsp--get-current-innermost-folding-range)))
   2540         (cons (lsp--folding-range-beg range)
   2541               (lsp--folding-range-end range)))))
   2542 (put 'lsp--folding-range 'bounds-of-thing-at-point
   2543      #'lsp--folding-range-at-point-bounds)
   2544 
   2545 (defun lsp--get-nearest-folding-range (&optional backward)
   2546   (let ((point (point))
   2547         (found nil))
   2548     (while (not
   2549             (or found
   2550                 (if backward
   2551                     (<= point (point-min))
   2552                   (>= point (point-max)))))
   2553       (if backward (cl-decf point) (cl-incf point))
   2554       (setq found (lsp--get-current-innermost-folding-range point)))
   2555     found))
   2556 
   2557 (defun lsp--folding-range-at-point-forward-op (n)
   2558   (when (and lsp-enable-folding
   2559              (not (zerop n))
   2560              (lsp-feature? "textDocument/foldingRange"))
   2561     (cl-block break
   2562       (dotimes (_ (abs n))
   2563         (if-let* ((range (lsp--get-nearest-folding-range (< n 0))))
   2564             (goto-char (if (< n 0)
   2565                            (lsp--folding-range-beg range)
   2566                          (lsp--folding-range-end range)))
   2567           (cl-return-from break))))))
   2568 (put 'lsp--folding-range 'forward-op
   2569      #'lsp--folding-range-at-point-forward-op)
   2570 
   2571 (defun lsp--folding-range-at-point-beginning-op ()
   2572   (goto-char (car (lsp--folding-range-at-point-bounds))))
   2573 (put 'lsp--folding-range 'beginning-op
   2574      #'lsp--folding-range-at-point-beginning-op)
   2575 
   2576 (defun lsp--folding-range-at-point-end-op ()
   2577   (goto-char (cdr (lsp--folding-range-at-point-bounds))))
   2578 (put 'lsp--folding-range 'end-op
   2579      #'lsp--folding-range-at-point-end-op)
   2580 
   2581 (defun lsp--range-at-point-bounds ()
   2582   (or (lsp--folding-range-at-point-bounds)
   2583       (when-let* ((range (and
   2584                          (lsp-feature? "textDocument/hover")
   2585                          (->> (lsp--text-document-position-params)
   2586                               (lsp-request "textDocument/hover")
   2587                               (lsp:hover-range?)))))
   2588         (lsp--range-to-region range))))
   2589 
   2590 ;; A more general purpose "thing", useful for applications like focus.el
   2591 (put 'lsp--range 'bounds-of-thing-at-point
   2592      #'lsp--range-at-point-bounds)
   2593 
   2594 (defun lsp--log-io-p (method)
   2595   "Return non nil if should log for METHOD."
   2596   (and lsp-log-io
   2597        (or (not lsp-log-io-allowlist-methods)
   2598            (member method lsp-log-io-allowlist-methods))))
   2599 
   2600 
   2601 ;; toggles
   2602 
   2603 (defun lsp-toggle-trace-io ()
   2604   "Toggle client-server protocol logging."
   2605   (interactive)
   2606   (setq lsp-log-io (not lsp-log-io))
   2607   (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled")))
   2608 
   2609 (defun lsp-toggle-signature-auto-activate ()
   2610   "Toggle signature auto activate."
   2611   (interactive)
   2612   (setq lsp-signature-auto-activate
   2613         (unless lsp-signature-auto-activate '(:on-trigger-char)))
   2614   (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled"))
   2615   (lsp--update-signature-help-hook))
   2616 
   2617 (defun lsp-toggle-on-type-formatting ()
   2618   "Toggle on type formatting."
   2619   (interactive)
   2620   (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting))
   2621   (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled"))
   2622   (lsp--update-on-type-formatting-hook))
   2623 
   2624 (defun lsp-toggle-symbol-highlight ()
   2625   "Toggle symbol highlighting."
   2626   (interactive)
   2627   (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting))
   2628 
   2629   (cond
   2630    ((and lsp-enable-symbol-highlighting
   2631          (lsp-feature? "textDocument/documentHighlight"))
   2632     (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)
   2633     (lsp--info "Symbol highlighting enabled in current buffer."))
   2634    ((not lsp-enable-symbol-highlighting)
   2635     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   2636     (lsp--remove-overlays 'lsp-highlight)
   2637     (lsp--info "Symbol highlighting disabled in current buffer."))))
   2638 
   2639 
   2640 ;; keybindings
   2641 (defvar lsp--binding-descriptions nil
   2642   "List of key binding/short description pair.")
   2643 
   2644 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings)
   2645   "In KEYMAP, define key sequence KEY as DEF conditionally.
   2646 This is like `define-key', except the definition disappears
   2647 whenever COND evaluates to nil.
   2648 DESC is the short-description for the binding.
   2649 BINDINGS is a list of (key def desc cond)."
   2650   (declare (indent defun)
   2651            (debug (form form form form form &rest sexp)))
   2652   (->> (cl-list* key def desc cond bindings)
   2653        (-partition 4)
   2654        (-mapcat (-lambda ((key def desc cond))
   2655                   `((define-key ,keymap ,key
   2656                       '(menu-item
   2657                         ,(format "maybe-%s" def)
   2658                         ,def
   2659                         :filter
   2660                         (lambda (item)
   2661                           (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer)
   2662                                                            lsp--describe-buffer)
   2663                                                          (current-buffer))
   2664                                   ,cond)
   2665                             item))))
   2666                     (when (stringp ,key)
   2667                       (setq lsp--binding-descriptions
   2668                             (append lsp--binding-descriptions '(,key ,desc)))))))
   2669        macroexp-progn))
   2670 
   2671 (defvar lsp--describe-buffer nil)
   2672 
   2673 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus)
   2674   (let ((lsp--describe-buffer buffer))
   2675     (funcall fn buffer prefix menus)))
   2676 
   2677 (advice-add 'describe-buffer-bindings
   2678             :around
   2679             #'lsp-describe-buffer-bindings-advice)
   2680 
   2681 (defun lsp--prepend-prefix (mappings)
   2682   (->> mappings
   2683        (-partition 2)
   2684        (-mapcat (-lambda ((key description))
   2685                   (list (concat lsp-keymap-prefix " " key)
   2686                         description)))))
   2687 
   2688 (defvar lsp-command-map
   2689   (-doto (make-sparse-keymap)
   2690     (lsp-define-conditional-key
   2691       ;; workspaces
   2692       "wD" lsp-disconnect "disconnect" (lsp-workspaces)
   2693       "wd" lsp-describe-session "describe session" t
   2694       "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces)
   2695       "wr" lsp-workspace-restart "restart server" (lsp-workspaces)
   2696       "ws" lsp "start server" t
   2697 
   2698       ;; formatting
   2699       "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting")
   2700                                                  (lsp-feature? "textDocument/formatting"))
   2701       "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting")
   2702 
   2703       ;; folders
   2704       "Fa" lsp-workspace-folders-add "add folder" t
   2705       "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t
   2706       "Fr" lsp-workspace-folders-remove "remove folder" t
   2707 
   2708       ;; toggles
   2709       "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature?
   2710                                                                         "textDocument/publishDiagnostics")
   2711       "TL" lsp-toggle-trace-io "toggle log io" t
   2712       "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline)
   2713       "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs)
   2714       "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature?
   2715                                                                           "textDocument/codeAction")
   2716       "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature?
   2717                                                                "textDocument/documentSymbol")
   2718       "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc)
   2719       "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature?
   2720                                                                       "textDocument/onTypeFormatting")
   2721       "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight")
   2722       "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens")
   2723       "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp")
   2724 
   2725       ;; goto
   2726       "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol")
   2727       "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration")
   2728       "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list)
   2729       "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition")
   2730       "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls")
   2731                                                              (fboundp 'lsp-treemacs-call-hierarchy))
   2732       "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation")
   2733       "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references")
   2734       "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition")
   2735 
   2736       ;; help
   2737       "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc)
   2738                                                   (lsp-feature? "textDocument/hover"))
   2739       "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover")
   2740       "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp")
   2741 
   2742       ;; refactoring
   2743       "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction")
   2744       "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename")
   2745 
   2746       ;; actions
   2747       "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction")
   2748       "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight")
   2749       "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy))
   2750 
   2751       ;; peeks
   2752       "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition")
   2753                                                                 (fboundp 'lsp-ui-peek-find-definitions))
   2754       "Gi" lsp-ui-peek-find-implementation "peek implementations" (and
   2755                                                                    (fboundp 'lsp-ui-peek-find-implementation)
   2756                                                                    (lsp-feature? "textDocument/implementation"))
   2757       "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references)
   2758                                                               (lsp-feature? "textDocument/references"))
   2759       "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp
   2760                                                                            'lsp-ui-peek-find-workspace-symbol)
   2761                                                                           (lsp-feature? "workspace/symbol")))))
   2762 
   2763 
   2764 ;; which-key integration
   2765 
   2766 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key")
   2767 (declare-function which-key-add-key-based-replacements "ext:which-key")
   2768 
   2769 (defun lsp-enable-which-key-integration (&optional all-modes)
   2770   "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current
   2771 active `major-mode', or for all major modes when ALL-MODES is t."
   2772   (cl-flet ((which-key-fn (if all-modes
   2773                               'which-key-add-key-based-replacements
   2774                             (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode))))
   2775     (apply
   2776      #'which-key-fn
   2777      (lsp--prepend-prefix
   2778       (cl-list*
   2779        ""    "lsp"
   2780        "w"   "workspaces"
   2781        "F"   "folders"
   2782        "="   "formatting"
   2783        "T"   "toggle"
   2784        "g"   "goto"
   2785        "h"   "help"
   2786        "r"   "refactor"
   2787        "a"   "code actions"
   2788        "G"   "peek"
   2789        lsp--binding-descriptions)))))
   2790 
   2791 
   2792 ;; Globbing syntax
   2793 
   2794 ;; We port VSCode's glob-to-regexp code
   2795 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts)
   2796 ;; since the LSP globbing syntax seems to be the same as that of
   2797 ;; VSCode.
   2798 
   2799 (defconst lsp-globstar "**"
   2800   "Globstar pattern.")
   2801 
   2802 (defconst lsp-glob-split ?/
   2803   "The character by which we split path components in a glob
   2804 pattern.")
   2805 
   2806 (defconst lsp-path-regexp "[/\\\\]"
   2807   "Forward or backslash to be used as a path separator in
   2808 computed regexps.")
   2809 
   2810 (defconst lsp-non-path-regexp "[^/\\\\]"
   2811   "A regexp matching anything other than a slash.")
   2812 
   2813 (defconst lsp-globstar-regexp
   2814   (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?"
   2815           lsp-path-regexp
   2816           lsp-non-path-regexp lsp-path-regexp
   2817           lsp-path-regexp lsp-non-path-regexp)
   2818   "Globstar in regexp form.")
   2819 
   2820 (defun lsp-split-glob-pattern (pattern split-char)
   2821   "Split PATTERN at SPLIT-CHAR while respecting braces and brackets."
   2822   (when pattern
   2823     (let ((segments nil)
   2824           (in-braces nil)
   2825           (in-brackets nil)
   2826           (current-segment ""))
   2827       (dolist (char (string-to-list pattern))
   2828         (cl-block 'exit-point
   2829           (if (eq char split-char)
   2830               (when (and (null in-braces)
   2831                          (null in-brackets))
   2832                 (push current-segment segments)
   2833                 (setq current-segment "")
   2834                 (cl-return-from 'exit-point))
   2835             (pcase char
   2836               (?{
   2837                (setq in-braces t))
   2838               (?}
   2839                (setq in-braces nil))
   2840               (?\[
   2841                (setq in-brackets t))
   2842               (?\]
   2843                (setq in-brackets nil))))
   2844           (setq current-segment (concat current-segment
   2845                                         (char-to-string char)))))
   2846       (unless (string-empty-p current-segment)
   2847         (push current-segment segments))
   2848       (nreverse segments))))
   2849 
   2850 (defun lsp--glob-to-regexp (pattern)
   2851   "Helper function to convert a PATTERN from LSP's glob syntax to
   2852 an Elisp regexp."
   2853   (if (string-empty-p pattern)
   2854       ""
   2855     (let ((current-regexp "")
   2856           (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split)))
   2857       (if (-all? (lambda (segment) (eq segment lsp-globstar))
   2858                  glob-segments)
   2859           ".*"
   2860         (let ((prev-segment-was-globstar nil))
   2861           (seq-do-indexed
   2862            (lambda (segment index)
   2863              (if (string-equal segment lsp-globstar)
   2864                  (unless prev-segment-was-globstar
   2865                    (setq current-regexp (concat current-regexp
   2866                                                 lsp-globstar-regexp))
   2867                    (setq prev-segment-was-globstar t))
   2868                (let ((in-braces nil)
   2869                      (brace-val "")
   2870                      (in-brackets nil)
   2871                      (bracket-val ""))
   2872                  (dolist (char (string-to-list segment))
   2873                    (cond
   2874                     ((and (not (char-equal char ?\}))
   2875                           in-braces)
   2876                      (setq brace-val (concat brace-val
   2877                                              (char-to-string char))))
   2878                     ((and in-brackets
   2879                           (or (not (char-equal char ?\]))
   2880                               (string-empty-p bracket-val)))
   2881                      (let ((curr (cond
   2882                                   ((char-equal char ?-)
   2883                                    "-")
   2884                                   ;; NOTE: ?\^ and ?^ are different characters
   2885                                   ((and (memq char '(?^ ?!))
   2886                                         (string-empty-p bracket-val))
   2887                                    "^")
   2888                                   ((char-equal char lsp-glob-split)
   2889                                    "")
   2890                                   (t
   2891                                    (regexp-quote (char-to-string char))))))
   2892                        (setq bracket-val (concat bracket-val curr))))
   2893                     (t
   2894                      (cl-case char
   2895                        (?{
   2896                         (setq in-braces t))
   2897                        (?\[
   2898                         (setq in-brackets t))
   2899                        (?}
   2900                         (let* ((choices (lsp-split-glob-pattern brace-val ?\,))
   2901                                (brace-regexp (concat "\\(?:"
   2902                                                      (mapconcat #'lsp--glob-to-regexp choices "\\|")
   2903                                                      "\\)")))
   2904                           (setq current-regexp (concat current-regexp
   2905                                                        brace-regexp))
   2906                           (setq in-braces nil)
   2907                           (setq brace-val "")))
   2908                        (?\]
   2909                         (setq current-regexp
   2910                               (concat current-regexp
   2911                                       "[" bracket-val "]"))
   2912                         (setq in-brackets nil)
   2913                         (setq bracket-val ""))
   2914                        (??
   2915                         (setq current-regexp
   2916                               (concat current-regexp
   2917                                       lsp-non-path-regexp)))
   2918                        (?*
   2919                         (setq current-regexp
   2920                               (concat current-regexp
   2921                                       lsp-non-path-regexp "*?")))
   2922                        (t
   2923                         (setq current-regexp
   2924                               (concat current-regexp
   2925                                       (regexp-quote (char-to-string char)))))))))
   2926                  (when (and (< index (1- (length glob-segments)))
   2927                             (or (not (string-equal (nth (1+ index) glob-segments)
   2928                                                    lsp-globstar))
   2929                                 (< (+ index 2)
   2930                                    (length glob-segments))))
   2931                    (setq current-regexp
   2932                          (concat current-regexp
   2933                                  lsp-path-regexp)))
   2934                  (setq prev-segment-was-globstar nil))))
   2935            glob-segments)
   2936           current-regexp)))))
   2937 
   2938 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365
   2939 (defun lsp-glob-unbrace-at-top-level (glob-pattern)
   2940   "If GLOB-PATTERN does not start with a brace, return a singleton list
   2941 containing GLOB-PATTERN.
   2942 
   2943 If GLOB-PATTERN does start with a brace, return a list of the
   2944 comma-separated globs within the top-level braces."
   2945   (if (not (string-prefix-p "{" glob-pattern))
   2946       (list glob-pattern)
   2947     (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,)))
   2948 
   2949 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern)
   2950   "Convert GLOB-PATTERN to a regexp wrapped with the beginning-
   2951 and end-of-string meta-characters."
   2952   (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'"))
   2953 
   2954 (defun lsp-glob-to-regexps (glob-pattern)
   2955   "Convert a GLOB-PATTERN to a list of Elisp regexps."
   2956   (when-let*
   2957       ((glob-pattern (cond ((hash-table-p glob-pattern)
   2958                             (ht-get glob-pattern "pattern"))
   2959                            ((stringp glob-pattern) glob-pattern)
   2960                            (t (error "Unknown glob-pattern type: %s" glob-pattern))))
   2961        (trimmed-pattern (string-trim glob-pattern))
   2962        (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern)))
   2963     (seq-map #'lsp-glob-convert-to-wrapped-regexp
   2964              top-level-unbraced-patterns)))
   2965 
   2966 
   2967 
   2968 (defvar lsp-mode-menu)
   2969 
   2970 (defun lsp-mouse-click (event)
   2971   (interactive "e")
   2972   (let* ((ec (event-start event))
   2973          (choice (x-popup-menu event lsp-mode-menu))
   2974          (action (lookup-key lsp-mode-menu (apply 'vector choice))))
   2975 
   2976     (select-window (posn-window ec))
   2977 
   2978     (unless (and (region-active-p) (eq action 'lsp-execute-code-action))
   2979       (goto-char (posn-point ec)))
   2980     (run-with-idle-timer
   2981      0.001 nil
   2982      (lambda ()
   2983        (cl-labels ((check (value) (not (null value))))
   2984          (when choice
   2985            (call-interactively action)))))))
   2986 
   2987 (defvar lsp-mode-map
   2988   (let ((map (make-sparse-keymap)))
   2989     (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse)
   2990     (define-key map (kbd "C-<mouse-1>") #'ignore)
   2991     (define-key map (kbd "<mouse-3>") #'lsp-mouse-click)
   2992     (define-key map (kbd "C-S-SPC") #'lsp-signature-activate)
   2993     (when lsp-keymap-prefix
   2994       (define-key map (kbd lsp-keymap-prefix) lsp-command-map))
   2995     map)
   2996   "Keymap for `lsp-mode'.")
   2997 
   2998 (define-minor-mode lsp-mode "Mode for LSP interaction."
   2999   :keymap lsp-mode-map
   3000   :lighter
   3001   (" LSP["
   3002    (lsp--buffer-workspaces
   3003     (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "]["))
   3004     (:propertize "Disconnected" face warning))
   3005    "]")
   3006   :group 'lsp-mode
   3007   (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred))
   3008     ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp'
   3009     (lsp)))
   3010 
   3011 (defvar lsp-mode-menu
   3012   (easy-menu-create-menu
   3013    nil
   3014    `(["Go to definition" lsp-find-definition
   3015       :active (lsp-feature? "textDocument/definition")]
   3016      ["Find references" lsp-find-references
   3017       :active (lsp-feature? "textDocument/references")]
   3018      ["Find implementations" lsp-find-implementation
   3019       :active (lsp-feature? "textDocument/implementation")]
   3020      ["Find declarations" lsp-find-declaration
   3021       :active (lsp-feature? "textDocument/declaration")]
   3022      ["Go to type declaration" lsp-find-type-definition
   3023       :active (lsp-feature? "textDocument/typeDefinition")]
   3024      "--"
   3025      ["Describe" lsp-describe-thing-at-point]
   3026      ["Code action" lsp-execute-code-action]
   3027      ["Format" lsp-format-buffer]
   3028      ["Highlight references" lsp-document-highlight]
   3029      ["Type Hierarchy" lsp-java-type-hierarchy
   3030       :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")]
   3031      ["Type Hierarchy" lsp-treemacs-type-hierarchy
   3032       :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy"))
   3033                     (functionp 'lsp-treemacs-type-hierarchy)
   3034                     (lsp-feature? "textDocument/typeHierarchy"))]
   3035      ["Call Hierarchy" lsp-treemacs-call-hierarchy
   3036       :visible (and (functionp 'lsp-treemacs-call-hierarchy)
   3037                     (lsp-feature? "textDocument/callHierarchy"))]
   3038      ["Rename" lsp-rename
   3039       :active (lsp-feature? "textDocument/rename")]
   3040      "--"
   3041      ("Session"
   3042       ["View logs" lsp-workspace-show-log]
   3043       ["Describe" lsp-describe-session]
   3044       ["Shutdown" lsp-shutdown-workspace]
   3045       ["Restart" lsp-restart-workspace])
   3046      ("Workspace Folders"
   3047       ["Add" lsp-workspace-folders-add]
   3048       ["Remove" lsp-workspace-folders-remove]
   3049       ["Open" lsp-workspace-folders-open])
   3050      ("Toggle features"
   3051       ["Lenses" lsp-lens-mode]
   3052       ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode]
   3053       ["Modeline code actions" lsp-modeline-code-actions-mode]
   3054       ["Modeline diagnostics" lsp-modeline-diagnostics-mode])
   3055      "---"
   3056      ("Debug"
   3057       :active (bound-and-true-p dap-ui-mode)
   3058       :filter ,(lambda (_)
   3059                  (and (boundp 'dap-ui-menu-items)
   3060                       (nthcdr 3 dap-ui-menu-items))))))
   3061   "Menu for lsp-mode.")
   3062 
   3063 (defalias 'make-lsp-client 'make-lsp--client)
   3064 
   3065 (cl-defstruct lsp--registered-capability
   3066   (id "")
   3067   (method " ")
   3068   (options nil))
   3069 
   3070 ;; A ‘lsp--workspace’ object represents exactly one language server process.
   3071 (cl-defstruct lsp--workspace
   3072   ;; the `ewoc' object for displaying I/O to and from the server
   3073   (ewoc nil)
   3074 
   3075   ;; ‘server-capabilities’ is a hash table of the language server capabilities.
   3076   ;; It is the hash table representation of a LSP ServerCapabilities structure;
   3077   ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize.
   3078   (server-capabilities nil)
   3079 
   3080   ;; ‘registered-server-capabilities’ is a list of hash tables that represent
   3081   ;; dynamically-registered Registration objects.  See
   3082   ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability.
   3083   (registered-server-capabilities nil)
   3084 
   3085   ;; ‘root’ is a directory name or a directory file name for the workspace
   3086   ;; root.  ‘lsp-mode’ passes this directory to the ‘initialize’ method of the
   3087   ;; language server; see
   3088   ;; https://microsoft.github.io/language-server-protocol/specification#initialize.
   3089   (root nil)
   3090 
   3091   ;; ‘client’ is the ‘lsp--client’ object associated with this workspace.
   3092   (client nil)
   3093 
   3094   ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It
   3095   ;; used to derive the file path in `lsp--uri-to-path' when using tramp
   3096   ;; connection.
   3097   (host-root nil)
   3098 
   3099   ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or
   3100   ;; a network connection.  ‘lsp-mode’ communicates with ‘proc’ using the
   3101   ;; language server protocol.  ‘proc’ corresponds to the COMMUNICATION-PROCESS
   3102   ;; element of the return value of the client’s ‘get-root’ field, which see.
   3103   (proc nil)
   3104 
   3105   ;; ‘proc’ is a process object; it must represent a regular process, not a
   3106   ;; pipe or network process.  It represents the actual server process that
   3107   ;; corresponds to this workspace.  ‘cmd-proc’ corresponds to the
   3108   ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’
   3109   ;; field, which see.
   3110   (cmd-proc nil)
   3111 
   3112   ;; ‘buffers’ is a list of buffers associated with this workspace.
   3113   (buffers nil)
   3114 
   3115   ;; if semantic tokens is enabled, `semantic-tokens-faces' contains
   3116   ;; one face (or nil) for each token type supported by the language server.
   3117   (semantic-tokens-faces nil)
   3118 
   3119   ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces'
   3120   ;; contains one face (or nil) for each modifier type supported by the language
   3121   ;; server
   3122   (semantic-tokens-modifier-faces nil)
   3123 
   3124   ;; Extra client capabilities provided by third-party packages using
   3125   ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME
   3126   ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name,
   3127   ;; and CAPS is either a plist of the client capabilities, or a function that
   3128   ;; takes no argument and returns a plist of the client capabilities or nil.
   3129   (extra-client-capabilities nil)
   3130 
   3131   ;; Workspace status
   3132   (status nil)
   3133 
   3134   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3135   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3136   (metadata (make-hash-table :test 'equal))
   3137 
   3138   ;; contains all the file notification watches that have been created for the
   3139   ;; current workspace in format filePath->file notification handle.
   3140   (watches (make-hash-table :test 'equal))
   3141 
   3142   ;; list of workspace folders
   3143   (workspace-folders nil)
   3144 
   3145   ;; ‘last-id’ the last request id for the current workspace.
   3146   (last-id 0)
   3147 
   3148   ;; ‘status-string’ allows extensions to specify custom status string based on
   3149   ;; the Language Server specific messages.
   3150   (status-string nil)
   3151 
   3152   ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it
   3153   ;; was stopped).
   3154   shutdown-action
   3155 
   3156   ;; ‘diagnostics’ a hashmap with workspace diagnostics.
   3157   (diagnostics (make-hash-table :test 'equal))
   3158 
   3159   ;; contains all the workDone progress tokens that have been created
   3160   ;; for the current workspace.
   3161   (work-done-tokens (make-hash-table :test 'equal)))
   3162 
   3163 
   3164 (cl-defstruct lsp-session
   3165   ;; contains the folders that are part of the current session
   3166   folders
   3167   ;; contains the folders that must not be imported in the current workspace.
   3168   folders-blocklist
   3169   ;; contains the list of folders that must be imported in a project in case of
   3170   ;; multi root LSP server.
   3171   (server-id->folders (make-hash-table :test 'equal))
   3172   ;; folder to list of the servers that are associated with the folder.
   3173   (folder->servers (make-hash-table :test 'equal))
   3174   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3175   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3176   (metadata (make-hash-table :test 'equal)))
   3177 
   3178 (defun lsp-workspace-status (status-string &optional workspace)
   3179   "Set current workspace status to STATUS-STRING.
   3180 If WORKSPACE is not specified defaults to lsp--cur-workspace."
   3181   (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string))))
   3182     (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string)))
   3183 
   3184 (defun lsp-session-set-metadata (key value &optional _workspace)
   3185   "Associate KEY with VALUE in the WORKSPACE metadata.
   3186 If WORKSPACE is not provided current workspace will be used."
   3187   (puthash key value (lsp-session-metadata (lsp-session))))
   3188 
   3189 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata)
   3190 
   3191 (defun lsp-session-get-metadata (key &optional _workspace)
   3192   "Lookup KEY in WORKSPACE metadata.
   3193 If WORKSPACE is not provided current workspace will be used."
   3194   (gethash key (lsp-session-metadata (lsp-session))))
   3195 
   3196 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata)
   3197 
   3198 (defun lsp-workspace-set-work-done-token (token value workspace)
   3199   "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens."
   3200   (puthash token value (lsp--workspace-work-done-tokens workspace)))
   3201 
   3202 (defun lsp-workspace-get-work-done-token (token workspace)
   3203   "Lookup TOKEN in the WORKSPACE work-done-tokens."
   3204   (gethash token (lsp--workspace-work-done-tokens workspace)))
   3205 
   3206 (defun lsp-workspace-rem-work-done-token (token workspace)
   3207   "Remove TOKEN from the WORKSPACE work-done-tokens."
   3208   (remhash token (lsp--workspace-work-done-tokens workspace)))
   3209 
   3210 
   3211 (defun lsp--make-notification (method &optional params)
   3212   "Create notification body for method METHOD and parameters PARAMS."
   3213   (list :jsonrpc "2.0" :method method :params params))
   3214 
   3215 (defalias 'lsp--make-request 'lsp--make-notification)
   3216 (defalias 'lsp-make-request 'lsp--make-notification)
   3217 
   3218 (defun lsp--make-response (id result)
   3219   "Create response for REQUEST with RESULT."
   3220   `(:jsonrpc "2.0" :id ,id :result ,result))
   3221 
   3222 (defun lsp-make-notification (method &optional params)
   3223   "Create notification body for method METHOD and parameters PARAMS."
   3224   (lsp--make-notification method params))
   3225 
   3226 (defmacro lsp--json-serialize (params)
   3227   (if (progn
   3228         (require 'json)
   3229         (fboundp 'json-serialize))
   3230       `(json-serialize ,params
   3231                        :null-object nil
   3232                        :false-object :json-false)
   3233     `(let ((json-false :json-false))
   3234        (json-encode ,params))))
   3235 
   3236 (defun lsp--make-message (params)
   3237   "Create a LSP message from PARAMS, after encoding it to a JSON string."
   3238   (let ((body (lsp--json-serialize params)))
   3239     (concat "Content-Length: "
   3240             (number-to-string (1+ (string-bytes body)))
   3241             "\r\n\r\n"
   3242             body
   3243             "\n")))
   3244 
   3245 (cl-defstruct lsp--log-entry timestamp process-time type method id body)
   3246 
   3247 (defun lsp--make-log-entry (method id body type &optional process-time)
   3248   "Create an outgoing log object from BODY with method METHOD and id ID.
   3249 If ID is non-nil, then the body is assumed to be a notification.
   3250 TYPE can either be `incoming' or `outgoing'"
   3251   (cl-assert (memq type '(incoming-req outgoing-req incoming-notif
   3252                                        outgoing-notif incoming-resp
   3253                                        outgoing-resp)))
   3254   (make-lsp--log-entry
   3255    :timestamp (format-time-string "%I:%M:%S %p")
   3256    :process-time process-time
   3257    :method method
   3258    :id id
   3259    :type type
   3260    :body body))
   3261 
   3262 (defun lsp--log-font-lock-json (body)
   3263   "Font lock JSON BODY."
   3264   (with-temp-buffer
   3265     (insert body)
   3266     ;; We set the temp buffer file-name extension to .json and call `set-auto-mode'
   3267     ;; so the users configured json mode is used which could be
   3268     ;; `json-mode', `json-ts-mode', `jsonian-mode', etc.
   3269     (let ((buffer-file-name "lsp-log.json"))
   3270       (delay-mode-hooks
   3271         (set-auto-mode)
   3272         (if (fboundp 'font-lock-ensure)
   3273             (font-lock-ensure)
   3274           (with-no-warnings
   3275             (font-lock-fontify-buffer)))))
   3276     (buffer-string)))
   3277 
   3278 (defun lsp--log-entry-pp (entry)
   3279   (cl-assert (lsp--log-entry-p entry))
   3280   (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time
   3281                           body)
   3282                entry)
   3283               (json-false :json-false)
   3284               (json-encoding-pretty-print t)
   3285               (str nil))
   3286     (setq str
   3287           (concat (format "[Trace - %s] " timestamp)
   3288                   (pcase type
   3289                     ('incoming-req (format "Received request '%s - (%s)." method id))
   3290                     ('outgoing-req (format "Sending request '%s - (%s)'." method id))
   3291 
   3292                     ('incoming-notif (format "Received notification '%s'." method))
   3293                     ('outgoing-notif (format "Sending notification '%s'." method))
   3294 
   3295                     ('incoming-resp (format "Received response '%s - (%s)' in %dms."
   3296                                             method id process-time))
   3297                     ('outgoing-resp
   3298                      (format
   3299                       "Sending response '%s - (%s)'. Processing request took %dms"
   3300                       method id process-time)))
   3301                   "\n"
   3302                   (if (memq type '(incoming-resp ougoing-resp))
   3303                       "Result: "
   3304                     "Params: ")
   3305                   (lsp--log-font-lock-json (json-encode body))
   3306                   "\n\n\n"))
   3307     (setq str (propertize str 'mouse-face 'highlight 'read-only t))
   3308     (insert str)))
   3309 
   3310 (defvar-local lsp--log-io-ewoc nil)
   3311 
   3312 (defun lsp--get-create-io-ewoc (workspace)
   3313   (if (and (lsp--workspace-ewoc workspace)
   3314            (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace))))
   3315       (lsp--workspace-ewoc workspace)
   3316     (with-current-buffer (lsp--get-log-buffer-create workspace)
   3317       (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode))
   3318       (setq-local window-point-insertion-type t)
   3319       (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t))
   3320       (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc))
   3321     (lsp--workspace-ewoc workspace)))
   3322 
   3323 (defun lsp--ewoc-count (ewoc)
   3324   (let* ((count 0)
   3325          (count-fn (lambda (_) (setq count (1+ count)))))
   3326     (ewoc-map count-fn ewoc)
   3327     count))
   3328 
   3329 (defun lsp--log-entry-new (entry workspace)
   3330   (let* ((ewoc (lsp--get-create-io-ewoc workspace))
   3331          (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc)))
   3332          (node (if (or (eq lsp-io-messages-max t)
   3333                        (>= lsp-io-messages-max count))
   3334                    nil
   3335                  (ewoc-nth ewoc (1- lsp-io-messages-max))))
   3336          (prev nil)
   3337          (inhibit-read-only t))
   3338     (while node
   3339       (setq prev (ewoc-prev ewoc node))
   3340       (ewoc-delete ewoc node)
   3341       (setq node prev))
   3342     (ewoc-enter-last ewoc entry)))
   3343 
   3344 (defun lsp--send-notification (body)
   3345   "Send BODY as a notification to the language server."
   3346   (lsp-foreach-workspace
   3347    (when (lsp--log-io-p (plist-get body :method))
   3348      (lsp--log-entry-new (lsp--make-log-entry
   3349                           (plist-get body :method)
   3350                           nil (plist-get body :params) 'outgoing-notif)
   3351                          lsp--cur-workspace))
   3352    (lsp--send-no-wait body
   3353                       (lsp--workspace-proc lsp--cur-workspace))))
   3354 
   3355 (defalias 'lsp-send-notification 'lsp--send-notification)
   3356 
   3357 (defun lsp-notify (method params)
   3358   "Send notification METHOD with PARAMS."
   3359   (lsp--send-notification (lsp--make-notification method params)))
   3360 
   3361 (defun lsp--cur-workspace-check ()
   3362   "Check whether buffer lsp workspace(s) are set."
   3363   (cl-assert (lsp-workspaces) nil
   3364              "No language server(s) is associated with this buffer."))
   3365 
   3366 (defun lsp--send-request (body &optional no-wait no-merge)
   3367   "Send BODY as a request to the language server, get the response.
   3368 If NO-WAIT is non-nil, don't synchronously wait for a response.
   3369 If NO-MERGE is non-nil, don't merge the results but return an
   3370 alist mapping workspace->result."
   3371   (lsp-request (plist-get body :method)
   3372                (plist-get body :params)
   3373                :no-wait no-wait
   3374                :no-merge no-merge))
   3375 
   3376 (defalias 'lsp-send-request 'lsp--send-request
   3377   "Send BODY as a request to the language server and return the response
   3378 synchronously.
   3379 \n(fn BODY)")
   3380 
   3381 (cl-defun lsp-request (method params &key no-wait no-merge)
   3382   "Send request METHOD with PARAMS.
   3383 If NO-MERGE is non-nil, don't merge the results but return alist
   3384 workspace->result.
   3385 If NO-WAIT is non-nil send the request as notification."
   3386   (if no-wait
   3387       (lsp-notify method params)
   3388     (let* ((send-time (float-time))
   3389            ;; max time by which we must get a response
   3390            (expected-time
   3391             (and
   3392              lsp-response-timeout
   3393              (+ send-time lsp-response-timeout)))
   3394            resp-result resp-error done?)
   3395       (unwind-protect
   3396           (progn
   3397             (lsp-request-async method params
   3398                                (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3399                                :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3400                                :no-merge no-merge
   3401                                :mode 'detached
   3402                                :cancel-token :sync-request)
   3403             (while (not (or resp-error resp-result))
   3404               (if (functionp 'json-rpc-connection)
   3405                   (catch 'lsp-done (sit-for 0.01))
   3406                 (catch 'lsp-done
   3407                   (accept-process-output
   3408                    nil
   3409                    (if expected-time (- expected-time send-time) 1))))
   3410               (setq send-time (float-time))
   3411               (when (and expected-time (< expected-time send-time))
   3412                 (error "Timeout while waiting for response.  Method: %s" method)))
   3413             (setq done? t)
   3414             (cond
   3415              ((eq resp-result :finished) nil)
   3416              (resp-result resp-result)
   3417              ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3418              ((lsp-json-error? (cl-first resp-error))
   3419               (error (lsp:json-error-message (cl-first resp-error))))))
   3420         (unless done?
   3421           (lsp-cancel-request-by-token :sync-request))))))
   3422 
   3423 (cl-defun lsp-request-while-no-input (method params)
   3424   "Send request METHOD with PARAMS and waits until there is no input.
   3425 Return same value as `lsp--while-no-input' and respecting `non-essential'."
   3426   (if (or non-essential (not lsp-request-while-no-input-may-block))
   3427       (let* ((send-time (float-time))
   3428              ;; max time by which we must get a response
   3429              (expected-time
   3430               (and
   3431                lsp-response-timeout
   3432                (+ send-time lsp-response-timeout)))
   3433              resp-result resp-error done?)
   3434         (unwind-protect
   3435             (progn
   3436               (lsp-request-async method params
   3437                                  (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3438                                  :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3439                                  :mode 'detached
   3440                                  :cancel-token :sync-request)
   3441               (while (not (or resp-error resp-result (input-pending-p)))
   3442                 (catch 'lsp-done
   3443                   (sit-for
   3444                    (if expected-time (- expected-time send-time) 1)))
   3445                 (setq send-time (float-time))
   3446                 (when (and expected-time (< expected-time send-time))
   3447                   (error "Timeout while waiting for response.  Method: %s" method)))
   3448               (setq done? (or resp-error resp-result))
   3449               (cond
   3450                ((eq resp-result :finished) nil)
   3451                (resp-result resp-result)
   3452                ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3453                ((lsp-json-error? (cl-first resp-error))
   3454                 (error (lsp:json-error-message (cl-first resp-error))))))
   3455           (unless done?
   3456             (lsp-cancel-request-by-token :sync-request))
   3457           (when (and (input-pending-p) lsp--throw-on-input)
   3458             (throw 'input :interrupted))))
   3459     (lsp-request method params)))
   3460 
   3461 (defvar lsp--cancelable-requests (ht))
   3462 
   3463 (cl-defun lsp-request-async (method params callback
   3464                                     &key mode error-handler cancel-handler no-merge cancel-token)
   3465   "Send METHOD with PARAMS as a request to the language server.
   3466 Call CALLBACK with the response received from the server
   3467 asynchronously.
   3468 MODE determines when the callback will be called depending on the
   3469 condition of the original buffer.  It could be:
   3470 - `detached' which means that the callback will be executed no
   3471 matter what has happened to the buffer.
   3472 - `alive' - the callback will be executed only if the buffer from
   3473 which the call was executed is still alive.
   3474 - `current' the callback will be executed only if the original buffer
   3475 is still selected.
   3476 - `tick' - the callback will be executed only if the buffer was not modified.
   3477 - `unchanged' - the callback will be executed only if the buffer hasn't
   3478 changed and if the buffer is not modified.
   3479 
   3480 ERROR-HANDLER will be called in case the request has failed.
   3481 CANCEL-HANDLER will be called in case the request is being canceled.
   3482 If NO-MERGE is non-nil, don't merge the results but return alist
   3483 workspace->result.
   3484 CANCEL-TOKEN is the token that can be used to cancel request."
   3485   (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params)
   3486                            callback mode error-handler cancel-handler no-merge cancel-token))
   3487 
   3488 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback)
   3489   (lambda (&rest _)
   3490     (unless (and (equal 'post-command-hook hook)
   3491                  (equal (current-buffer) buf))
   3492       (lsp--request-cleanup-hooks id)
   3493       (with-lsp-workspaces workspaces
   3494         (lsp--cancel-request id)
   3495         (when cancel-callback (funcall cancel-callback)))
   3496       (lsp-log "Cancelling %s(%s) in hook %s" method id hook))))
   3497 
   3498 (defun lsp--create-async-callback
   3499     (callback method no-merge workspaces)
   3500   "Create async handler expecting COUNT results, merge them and call CALLBACK.
   3501 MODE determines when the callback will be called depending on the
   3502 condition of the original buffer. METHOD is the invoked method.
   3503 If NO-MERGE is non-nil, don't merge the results but return alist
   3504 workspace->result. ID is the request id."
   3505   (let (results errors)
   3506     (lambda (result)
   3507       (push (cons lsp--cur-workspace result)
   3508             (if (eq result :error) errors results))
   3509       (when (and (not (eq (length errors) (length workspaces)))
   3510                  (eq (+ (length errors) (length results)) (length workspaces)))
   3511         (funcall callback
   3512                  (if no-merge
   3513                      results
   3514                    (lsp--merge-results (-map #'cl-rest results) method)))))))
   3515 
   3516 (defcustom lsp-default-create-error-handler-fn nil
   3517   "Default error handler customization.
   3518 Handler should give METHOD as argument and return function of one argument
   3519 ERROR."
   3520   :type 'function
   3521   :group 'lsp-mode
   3522   :package-version '(lsp-mode . "9.0.0"))
   3523 
   3524 (defun lsp--create-default-error-handler (method)
   3525   "Default error handler.
   3526 METHOD is the executed method."
   3527   (if lsp-default-create-error-handler-fn
   3528       (funcall lsp-default-create-error-handler-fn method)
   3529     (lambda (error)
   3530       (lsp--warn "%s" (or (lsp--error-string error)
   3531                           (format "%s Request has failed" method))))))
   3532 
   3533 (defvar lsp--request-cleanup-hooks (ht))
   3534 
   3535 (defun lsp--request-cleanup-hooks (request-id)
   3536   (when-let* ((cleanup-function (gethash request-id lsp--request-cleanup-hooks)))
   3537     (funcall cleanup-function)
   3538     (remhash request-id lsp--request-cleanup-hooks)))
   3539 
   3540 (defun lsp-cancel-request-by-token (cancel-token)
   3541   "Cancel request using CANCEL-TOKEN."
   3542   (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests))
   3543     (with-lsp-workspaces workspaces
   3544       (lsp--cancel-request request-id))
   3545     (remhash cancel-token lsp--cancelable-requests)
   3546     (lsp--request-cleanup-hooks request-id)))
   3547 
   3548 (defun lsp--send-request-async (body callback
   3549                                      &optional mode error-callback cancel-callback
   3550                                      no-merge cancel-token)
   3551   "Send BODY as a request to the language server.
   3552 Call CALLBACK with the response received from the server
   3553 asynchronously.
   3554 MODE determines when the callback will be called depending on the
   3555 condition of the original buffer.  It could be:
   3556 - `detached' which means that the callback will be executed no
   3557 matter what has happened to the buffer.
   3558 - `alive' - the callback will be executed only if the buffer from
   3559 which the call was executed is still alive.
   3560 - `current' the callback will be executed only if the original buffer
   3561 is still selected.
   3562 - `tick' - the callback will be executed only if the buffer was not modified.
   3563 - `unchanged' - the callback will be executed only if the buffer hasn't
   3564 changed and if the buffer is not modified.
   3565 
   3566 ERROR-CALLBACK will be called in case the request has failed.
   3567 CANCEL-CALLBACK will be called in case the request is being canceled.
   3568 If NO-MERGE is non-nil, don't merge the results but return alist
   3569 workspace->result.
   3570 CANCEL-TOKEN is the token that can be used to cancel request."
   3571   (when cancel-token
   3572     (lsp-cancel-request-by-token cancel-token))
   3573 
   3574   (if-let* ((target-workspaces (lsp--find-workspaces-for body)))
   3575       (let* ((start-time (current-time))
   3576              (method (plist-get body :method))
   3577              (id (cl-incf lsp-last-id))
   3578              (buf (current-buffer))
   3579              (cancel-callback (when cancel-callback
   3580                                 (pcase mode
   3581                                   ((or 'alive 'tick 'unchanged)
   3582                                    (lambda ()
   3583                                      (with-current-buffer buf
   3584                                        (funcall cancel-callback))))
   3585                                   (_ cancel-callback))))
   3586              ;; calculate what are the (hook . local) pairs which will cancel
   3587              ;; the request
   3588              (hooks (pcase mode
   3589                       ('alive     '((kill-buffer-hook . t)))
   3590                       ('tick      '((kill-buffer-hook . t) (after-change-functions . t)))
   3591                       ('unchanged '((after-change-functions . t) (post-command-hook . nil)))
   3592                       ('current   '((post-command-hook . nil)))))
   3593              ;; note: lambdas in emacs can be compared but we should make sure
   3594              ;; that all of the captured arguments are the same - in our case
   3595              ;; `lsp--create-request-cancel' will return the same lambda when
   3596              ;; called with the same params.
   3597              (cleanup-hooks
   3598               (lambda () (mapc
   3599                           (-lambda ((hook . local))
   3600                             (if local
   3601                                 (when (buffer-live-p buf)
   3602                                   (with-current-buffer buf
   3603                                     (remove-hook hook
   3604                                                  (lsp--create-request-cancel
   3605                                                   id target-workspaces hook buf method cancel-callback)
   3606                                                  t)))
   3607                               (remove-hook hook (lsp--create-request-cancel
   3608                                                  id target-workspaces hook buf method cancel-callback))))
   3609                           hooks)
   3610                 (remhash cancel-token lsp--cancelable-requests)))
   3611              (callback (pcase mode
   3612                          ((or 'alive 'tick 'unchanged) (lambda (&rest args)
   3613                                                          (with-current-buffer buf
   3614                                                            (apply callback args))))
   3615                          (_ callback)))
   3616              (callback (lsp--create-async-callback callback
   3617                                                    method
   3618                                                    no-merge
   3619                                                    target-workspaces))
   3620              (callback (lambda (result)
   3621                          (lsp--request-cleanup-hooks id)
   3622                          (funcall callback result)))
   3623              (error-callback (lsp--create-async-callback
   3624                               (or error-callback
   3625                                   (lsp--create-default-error-handler method))
   3626                               method
   3627                               nil
   3628                               target-workspaces))
   3629              (error-callback (lambda (error)
   3630                                (funcall callback :error)
   3631                                (lsp--request-cleanup-hooks id)
   3632                                (funcall error-callback error)))
   3633              (body (plist-put body :id id)))
   3634 
   3635         ;; cancel request in any of the hooks
   3636         (mapc (-lambda ((hook . local))
   3637                 (add-hook hook
   3638                           (lsp--create-request-cancel
   3639                            id target-workspaces hook buf method cancel-callback)
   3640                           nil local))
   3641               hooks)
   3642         (puthash id cleanup-hooks lsp--request-cleanup-hooks)
   3643 
   3644         (setq lsp--last-active-workspaces target-workspaces)
   3645 
   3646         (when cancel-token
   3647           (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests))
   3648 
   3649         (seq-doseq (workspace target-workspaces)
   3650           (when (lsp--log-io-p method)
   3651             (lsp--log-entry-new (lsp--make-log-entry method id
   3652                                                      (plist-get body :params)
   3653                                                      'outgoing-req)
   3654                                 workspace))
   3655           (puthash id
   3656                    (list callback error-callback method start-time (current-time))
   3657                    (-> workspace
   3658                        (lsp--workspace-client)
   3659                        (lsp--client-response-handlers)))
   3660           (lsp--send-no-wait body (lsp--workspace-proc workspace)))
   3661         body)
   3662     (error "The connected server(s) does not support method %s.
   3663 To find out what capabilities support your server use `M-x lsp-describe-session'
   3664 and expand the capabilities section"
   3665            (plist-get body :method))))
   3666 
   3667 ;; deprecated, use lsp-request-async.
   3668 (defalias 'lsp-send-request-async 'lsp--send-request-async)
   3669 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1")
   3670 
   3671 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any
   3672 ;; pending language servers.
   3673 (add-hook 'kill-emacs-hook #'lsp--global-teardown)
   3674 
   3675 (defun lsp--global-teardown ()
   3676   "Unload working workspaces."
   3677   (lsp-foreach-workspace (lsp--shutdown-workspace)))
   3678 
   3679 (defun lsp--shutdown-workspace (&optional restart)
   3680   "Shut down the language server process for ‘lsp--cur-workspace’."
   3681   (with-demoted-errors "LSP error: %S"
   3682     (let ((lsp-response-timeout 0.5))
   3683       (condition-case err
   3684           (lsp-request "shutdown" nil)
   3685         (error (lsp--error "%s" err))))
   3686     (lsp-notify "exit" nil))
   3687   (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown))
   3688   (lsp--uninitialize-workspace))
   3689 
   3690 (defcustom lsp-inlay-hint-enable nil
   3691   "If non-nil it will enable inlay hints."
   3692   :type 'boolean
   3693   :group 'lsp-mode
   3694   :package-version '(lsp-mode . "9.0.0"))
   3695 
   3696 (defun lsp--uninitialize-workspace ()
   3697   "Cleanup buffer state.
   3698 When a workspace is shut down, by request or from just
   3699 disappearing, unset all the variables related to it."
   3700   (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace]
   3701     (lsp-process-kill cmd-proc)
   3702     (mapc (lambda (buf)
   3703             (when (lsp-buffer-live-p buf)
   3704               (lsp-with-current-buffer buf
   3705                                        (lsp-managed-mode -1))))
   3706           buffers)
   3707     (lsp-diagnostics--workspace-cleanup lsp--cur-workspace)))
   3708 
   3709 (defun lsp--client-capabilities (&optional custom-capabilities)
   3710   "Return the client capabilities appending CUSTOM-CAPABILITIES."
   3711   (append
   3712    `((general . ((positionEncodings . ["utf-32", "utf-16"])))
   3713      (workspace . ((workspaceEdit . ((documentChanges . t)
   3714                                      (resourceOperations . ["create" "rename" "delete"])))
   3715                    (applyEdit . t)
   3716                    (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))))
   3717                    (executeCommand . ((dynamicRegistration . :json-false)))
   3718                    ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t)))))
   3719                    (workspaceFolders . t)
   3720                    (configuration . t)
   3721                    ,@(when lsp-semantic-tokens-enable
   3722                        `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests)
   3723                                                                         lsp-semantic-tokens-honor-refresh-requests)
   3724                                                                    :json-false))))))
   3725                    ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t)))))
   3726                    ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false)))))
   3727                    (diagnostics . ((refreshSupport . :json-false)))
   3728                    (fileOperations . ((didCreate . :json-false)
   3729                                       (willCreate . :json-false)
   3730                                       (didRename . t)
   3731                                       (willRename . t)
   3732                                       (didDelete . :json-false)
   3733                                       (willDelete . :json-false)))))
   3734      (textDocument . ((declaration . ((dynamicRegistration . t)
   3735                                       (linkSupport . t)))
   3736                       (definition . ((dynamicRegistration . t)
   3737                                      (linkSupport . t)))
   3738                       (references . ((dynamicRegistration . t)))
   3739                       (implementation . ((dynamicRegistration . t)
   3740                                          (linkSupport . t)))
   3741                       (typeDefinition . ((dynamicRegistration . t)
   3742                                          (linkSupport . t)))
   3743                       (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t)))
   3744                       (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))
   3745                                          (hierarchicalDocumentSymbolSupport . t)))
   3746                       (formatting . ((dynamicRegistration . t)))
   3747                       (rangeFormatting . ((dynamicRegistration . t)))
   3748                       (onTypeFormatting . ((dynamicRegistration . t)))
   3749                       ,@(when (and lsp-semantic-tokens-enable
   3750                                    (functionp 'lsp--semantic-tokens-capabilities))
   3751                           (lsp--semantic-tokens-capabilities))
   3752                       (rename . ((dynamicRegistration . t) (prepareSupport . t)))
   3753                       (codeAction . ((dynamicRegistration . t)
   3754                                      (isPreferredSupport . t)
   3755                                      (codeActionLiteralSupport . ((codeActionKind . ((valueSet . [""
   3756                                                                                                   "quickfix"
   3757                                                                                                   "refactor"
   3758                                                                                                   "refactor.extract"
   3759                                                                                                   "refactor.inline"
   3760                                                                                                   "refactor.rewrite"
   3761                                                                                                   "source"
   3762                                                                                                   "source.organizeImports"])))))
   3763                                      (resolveSupport . ((properties . ["edit" "command"])))
   3764                                      (dataSupport . t)))
   3765                       (completion . ((completionItem . ((snippetSupport . ,(cond
   3766                                                                             ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode)))
   3767                                                                              (lsp--warn (concat
   3768                                                                                          "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. "
   3769                                                                                          "You must either install yasnippet, or disable snippet support."))
   3770                                                                              :json-false)
   3771                                                                             (lsp-enable-snippet t)
   3772                                                                             (t :json-false)))
   3773                                                         (documentationFormat . ["markdown" "plaintext"])
   3774                                                         ;; Remove this after jdtls support resolveSupport
   3775                                                         (resolveAdditionalTextEditsSupport . t)
   3776                                                         (insertReplaceSupport . t)
   3777                                                         (deprecatedSupport . t)
   3778                                                         (resolveSupport
   3779                                                          . ((properties . ["documentation"
   3780                                                                            "detail"
   3781                                                                            "additionalTextEdits"
   3782                                                                            "command"
   3783                                                                            "insertTextFormat"
   3784                                                                            "insertTextMode"])))
   3785                                                         (insertTextModeSupport . ((valueSet . [1 2])))))
   3786                                      (contextSupport . t)
   3787                                      (dynamicRegistration . t)))
   3788                       (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t)))))
   3789                                         (dynamicRegistration . t)))
   3790                       (documentLink . ((dynamicRegistration . t)
   3791                                        (tooltipSupport . t)))
   3792                       (hover . ((contentFormat . ["markdown" "plaintext"])
   3793                                 (dynamicRegistration . t)))
   3794                       ,@(when lsp-enable-folding
   3795                           `((foldingRange . ((dynamicRegistration . t)
   3796                                              ,@(when lsp-folding-range-limit
   3797                                                  `((rangeLimit . ,lsp-folding-range-limit)))
   3798                                              ,@(when lsp-folding-line-folding-only
   3799                                                  `((lineFoldingOnly . t)))))))
   3800                       (selectionRange . ((dynamicRegistration . t)))
   3801                       (callHierarchy . ((dynamicRegistration . :json-false)))
   3802                       (typeHierarchy . ((dynamicRegistration . t)))
   3803                       (publishDiagnostics . ((relatedInformation . t)
   3804                                              (tagSupport . ((valueSet . [1 2])))
   3805                                              (versionSupport . t)))
   3806                       (diagnostic . ((dynamicRegistration . :json-false)
   3807                                      (relatedDocumentSupport . :json-false)))
   3808                       (linkedEditingRange . ((dynamicRegistration . t)))))
   3809      (window . ((workDoneProgress . t)
   3810                 (showDocument . ((support . t))))))
   3811    custom-capabilities))
   3812 
   3813 (defun lsp-find-roots-for-workspace (workspace session)
   3814   "Get all roots for the WORKSPACE."
   3815   (-filter #'identity (ht-map (lambda (folder workspaces)
   3816                                 (when (-contains? workspaces workspace)
   3817                                   folder))
   3818                               (lsp-session-folder->servers session))))
   3819 
   3820 (defun lsp-session-watches (&optional session)
   3821   "Get watches created for SESSION."
   3822   (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session))))
   3823       (-let [res (make-hash-table :test 'equal)]
   3824         (puthash "__watches" res (lsp-session-metadata (or session (lsp-session))))
   3825         res)))
   3826 
   3827 (defun lsp--file-process-event (session root-folder event)
   3828   "Process file event."
   3829   (let* ((changed-file (cl-third event))
   3830          (rel-changed-file (f-relative changed-file root-folder))
   3831          (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type))
   3832          (bit-position (1- event-numeric-kind))
   3833          (watch-bit (ash 1 bit-position)))
   3834     (->>
   3835      session
   3836      lsp-session-folder->servers
   3837      (gethash root-folder)
   3838      (seq-do (lambda (workspace)
   3839                (when (->>
   3840                       workspace
   3841                       lsp--workspace-registered-server-capabilities
   3842                       (-any?
   3843                        (lambda (capability)
   3844                          (and
   3845                           (equal (lsp--registered-capability-method capability)
   3846                                  "workspace/didChangeWatchedFiles")
   3847                           (->>
   3848                            capability
   3849                            lsp--registered-capability-options
   3850                            (lsp:did-change-watched-files-registration-options-watchers)
   3851                            (seq-find
   3852                             (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp))
   3853                               (when (or (null kind?)
   3854                                         (> (logand kind? watch-bit) 0))
   3855                                 (-let [regexes (or cached-regexp
   3856                                                    (let ((regexp (lsp-glob-to-regexps glob-pattern)))
   3857                                                      (lsp-put fs-watcher :_cachedRegexp regexp)
   3858                                                      regexp))]
   3859                                   (-any? (lambda (re)
   3860                                            (or (string-match re changed-file)
   3861                                                (string-match re rel-changed-file)))
   3862                                          regexes))))))))))
   3863                  (with-lsp-workspace workspace
   3864                    (lsp-notify
   3865                     "workspace/didChangeWatchedFiles"
   3866                     `((changes . [((type . ,event-numeric-kind)
   3867                                    (uri . ,(lsp--path-to-uri changed-file)))]))))))))))
   3868 
   3869 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?))
   3870   "Register capability REG."
   3871   (when (and lsp-enable-file-watchers
   3872              (equal method "workspace/didChangeWatchedFiles"))
   3873     (-let* ((created-watches (lsp-session-watches (lsp-session)))
   3874             (root-folders (cl-set-difference
   3875                            (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session))
   3876                            (ht-keys created-watches))))
   3877       ;; create watch for each root folder without such
   3878       (dolist (folder root-folders)
   3879         (let* ((watch (make-lsp-watch :root-directory folder))
   3880                (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder))
   3881                (ignored-files-regex-list (car ignored-things))
   3882                (ignored-directories-regex-list (cadr ignored-things)))
   3883           (puthash folder watch created-watches)
   3884           (lsp-watch-root-folder (file-truename folder)
   3885                                  (-partial #'lsp--file-process-event (lsp-session) folder)
   3886                                  ignored-files-regex-list
   3887                                  ignored-directories-regex-list
   3888                                  watch
   3889                                  t)))))
   3890 
   3891   (push
   3892    (make-lsp--registered-capability :id id :method method :options register-options?)
   3893    (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3894 
   3895 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body)
   3896   "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to
   3897 access dir-local variables."
   3898   (declare (indent 1) (debug t))
   3899   `(with-temp-buffer
   3900      ;; Set the buffer's name to something under the root so that we can hack the local variables
   3901      ;; This file doesn't need to exist and will not be created due to this.
   3902      (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root)))
   3903      (hack-local-variables)
   3904      (prog1 ,@body
   3905        (setq-local buffer-file-name nil))))
   3906 
   3907 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root)
   3908   "Return a list of the form
   3909 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given
   3910 WORKSPACE-ROOT."
   3911   ;; The intent of this function is to provide per-root workspace-level customization of the
   3912   ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables.
   3913   (lsp--with-workspace-temp-buffer workspace-root
   3914     (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories))))
   3915 
   3916 
   3917 (defun lsp--cleanup-hanging-watches ()
   3918   "Cleanup watches in case there are no more workspaces that are interested
   3919 in that particular folder."
   3920   (let* ((session (lsp-session))
   3921          (watches (lsp-session-watches session)))
   3922     (dolist (watched-folder (ht-keys watches))
   3923       (when (-none? (lambda (workspace)
   3924                       (with-lsp-workspace workspace
   3925                         (lsp--registered-capability "workspace/didChangeWatchedFiles")))
   3926                     (gethash watched-folder (lsp-session-folder->servers (lsp-session))))
   3927         (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder)
   3928         (lsp-kill-watch (gethash watched-folder watches))
   3929         (remhash watched-folder watches)))))
   3930 
   3931 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method))
   3932   "Unregister capability UNREG."
   3933   (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace)
   3934         (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id))
   3935                     (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3936   (when (equal method "workspace/didChangeWatchedFiles")
   3937     (lsp--cleanup-hanging-watches)))
   3938 
   3939 (defun lsp--server-capabilities ()
   3940   "Return the capabilities of the language server associated with the buffer."
   3941   (->> (lsp-workspaces)
   3942        (-keep #'lsp--workspace-server-capabilities)
   3943        (apply #'lsp-merge)))
   3944 
   3945 (defun lsp--send-open-close-p ()
   3946   "Return whether open and close notifications should be sent to the server."
   3947   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3948     (or (memq sync '(1 2))
   3949         (lsp:text-document-sync-options-open-close? sync))))
   3950 
   3951 (defun lsp--send-will-save-p ()
   3952   "Return whether willSave notifications should be sent to the server."
   3953   (-> (lsp--server-capabilities)
   3954       (lsp:server-capabilities-text-document-sync?)
   3955       (lsp:text-document-sync-options-will-save?)))
   3956 
   3957 (defun lsp--send-will-save-wait-until-p ()
   3958   "Return whether willSaveWaitUntil notifications should be sent to the server."
   3959   (-> (lsp--server-capabilities)
   3960       (lsp:server-capabilities-text-document-sync?)
   3961       (lsp:text-document-sync-options-will-save-wait-until?)))
   3962 
   3963 (defun lsp--send-did-save-p ()
   3964   "Return whether didSave notifications should be sent to the server."
   3965   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3966     (or (memq sync '(1 2))
   3967         (lsp:text-document-sync-options-save? sync))))
   3968 
   3969 (defun lsp--save-include-text-p ()
   3970   "Return whether save notifications should include the text document's contents."
   3971   (->> (lsp--server-capabilities)
   3972        (lsp:server-capabilities-text-document-sync?)
   3973        (lsp:text-document-sync-options-save?)
   3974        (lsp:text-document-save-registration-options-include-text?)))
   3975 
   3976 (defun lsp--send-will-rename-files-p (path)
   3977   "Return whether willRenameFiles request should be sent to the server.
   3978 If any filters, checks if it applies for PATH."
   3979   (let* ((will-rename (-> (lsp--server-capabilities)
   3980                           (lsp:server-capabilities-workspace?)
   3981                           (lsp:workspace-server-capabilities-file-operations?)
   3982                           (lsp:workspace-file-operations-will-rename?)))
   3983          (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list)))
   3984     (and will-rename
   3985          (or (seq-empty-p filters)
   3986              (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob)))
   3987                       (-let [regexes (lsp-glob-to-regexps glob)]
   3988                         (and (or (not scheme?)
   3989                                  (string-prefix-p scheme? (lsp--path-to-uri path)))
   3990                              (-any? (lambda (re)
   3991                                       (string-match re path))
   3992                                     regexes))))
   3993                     filters)))))
   3994 
   3995 (defun lsp--send-did-rename-files-p ()
   3996   "Return whether didRenameFiles notification should be sent to the server."
   3997   (-> (lsp--server-capabilities)
   3998       (lsp:server-capabilities-workspace?)
   3999       (lsp:workspace-server-capabilities-file-operations?)
   4000       (lsp:workspace-file-operations-did-rename?)))
   4001 
   4002 (declare-function project-roots "ext:project" (project) t)
   4003 (declare-function project-root "ext:project" (project) t)
   4004 
   4005 (defun lsp--suggest-project-root ()
   4006   "Get project root."
   4007   (or
   4008    (when (fboundp 'projectile-project-root)
   4009      (condition-case nil
   4010          (projectile-project-root)
   4011        (error nil)))
   4012    (when (fboundp 'project-current)
   4013      (when-let* ((project (project-current)))
   4014        (if (fboundp 'project-root)
   4015            (project-root project)
   4016          (car (with-no-warnings
   4017                 (project-roots project))))))
   4018    default-directory))
   4019 
   4020 (defun lsp--read-from-file (file)
   4021   "Read FILE content."
   4022   (when (file-exists-p file)
   4023     (cl-first (read-from-string (f-read-text file 'utf-8)))))
   4024 
   4025 (defun lsp--persist (file-name to-persist)
   4026   "Persist TO-PERSIST in FILE-NAME.
   4027 
   4028 This function creates the parent directories if they don't exist
   4029 yet."
   4030   (let ((print-length nil)
   4031         (print-level nil))
   4032     ;; Create all parent directories:
   4033     (make-directory (f-parent file-name) t)
   4034     (f-write-text (prin1-to-string to-persist) 'utf-8 file-name)))
   4035 
   4036 (defun lsp-workspace-folders-add (project-root)
   4037   "Add PROJECT-ROOT to the list of workspace folders."
   4038   (interactive
   4039    (list (read-directory-name "Select folder to add: "
   4040                               (or (lsp--suggest-project-root) default-directory) nil t)))
   4041   (cl-pushnew (lsp-f-canonical project-root)
   4042               (lsp-session-folders (lsp-session)) :test 'equal)
   4043   (lsp--persist-session (lsp-session))
   4044 
   4045   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil))
   4046 
   4047 (defun lsp-workspace-folders-remove (project-root)
   4048   "Remove PROJECT-ROOT from the list of workspace folders."
   4049   (interactive (list (completing-read "Select folder to remove: "
   4050                                       (lsp-session-folders (lsp-session))
   4051                                       nil t nil nil
   4052                                       (lsp-find-session-folder (lsp-session) default-directory))))
   4053 
   4054   (setq project-root (lsp-f-canonical project-root))
   4055 
   4056   ;; send remove folder to each multiroot workspace associated with the folder
   4057   (dolist (wks (->> (lsp-session)
   4058                     (lsp-session-folder->servers)
   4059                     (gethash project-root)
   4060                     (--filter (lsp--client-multi-root (lsp--workspace-client it)))))
   4061     (with-lsp-workspace wks
   4062       (lsp-notify "workspace/didChangeWorkspaceFolders"
   4063                   (lsp-make-did-change-workspace-folders-params
   4064                    :event (lsp-make-workspace-folders-change-event
   4065                            :removed (vector (lsp-make-workspace-folder
   4066                                              :uri (lsp--path-to-uri project-root)
   4067                                              :name (f-filename project-root)))
   4068                            :added [])))))
   4069 
   4070   ;; turn off servers in the removed directory
   4071   (let* ((session (lsp-session))
   4072          (folder->servers (lsp-session-folder->servers session))
   4073          (server-id->folders (lsp-session-server-id->folders session))
   4074          (workspaces (gethash project-root folder->servers)))
   4075 
   4076     (remhash project-root folder->servers)
   4077 
   4078     ;; turn off the servers without root folders
   4079     (dolist (workspace workspaces)
   4080       (when (--none? (-contains? it workspace) (ht-values folder->servers))
   4081         (lsp--info "Shutdown %s since folder %s is removed..."
   4082                    (lsp--workspace-print workspace) project-root)
   4083         (with-lsp-workspace workspace (lsp--shutdown-workspace))))
   4084 
   4085     (setf (lsp-session-folders session)
   4086           (-remove-item project-root (lsp-session-folders session)))
   4087 
   4088     (ht-aeach (puthash key
   4089                        (-remove-item project-root value)
   4090                        server-id->folders)
   4091               server-id->folders)
   4092     (lsp--persist-session (lsp-session)))
   4093 
   4094   (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root)))
   4095 
   4096 (defun lsp-workspace-blocklist-remove (project-root)
   4097   "Remove PROJECT-ROOT from the workspace blocklist."
   4098   (interactive (list (completing-read "Select folder to remove:"
   4099                                       (lsp-session-folders-blocklist (lsp-session))
   4100                                       nil t)))
   4101   (setf (lsp-session-folders-blocklist (lsp-session))
   4102         (delete project-root
   4103                 (lsp-session-folders-blocklist (lsp-session))))
   4104   (lsp--persist-session (lsp-session)))
   4105 
   4106 (define-obsolete-function-alias 'lsp-workspace-folders-switch
   4107   'lsp-workspace-folders-open "lsp-mode 6.1")
   4108 
   4109 (defun lsp-workspace-folders-open (project-root)
   4110   "Open the directory located at PROJECT-ROOT"
   4111   (interactive (list (completing-read "Open folder: "
   4112                                       (lsp-session-folders (lsp-session))
   4113                                       nil t)))
   4114   (find-file project-root))
   4115 
   4116 (defun lsp--maybe-enable-signature-help (trigger-characters)
   4117   (let ((ch last-command-event))
   4118     (when (cl-find ch trigger-characters :key #'string-to-char)
   4119       (lsp-signature-activate))))
   4120 
   4121 (defun lsp--on-type-formatting-handler-create ()
   4122   (when-let* ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" )))
   4123     (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character?
   4124                                              :first-trigger-character) provider]
   4125       (lambda ()
   4126         (lsp--on-type-formatting first-trigger-character
   4127                                  more-trigger-character?)))))
   4128 
   4129 (defun lsp--update-on-type-formatting-hook (&optional cleanup?)
   4130   (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create)))
   4131     (cond
   4132      ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?))
   4133       (add-hook 'post-self-insert-hook on-type-formatting-handler nil t))
   4134      ((or cleanup?
   4135           (not lsp-enable-on-type-formatting))
   4136       (remove-hook 'post-self-insert-hook on-type-formatting-handler t)))))
   4137 
   4138 (defun lsp--signature-help-handler-create ()
   4139   (-when-let ((&SignatureHelpOptions? :trigger-characters?)
   4140               (lsp--capability-for-method "textDocument/signatureHelp"))
   4141     (lambda ()
   4142       (lsp--maybe-enable-signature-help trigger-characters?))))
   4143 
   4144 (defun lsp--update-signature-help-hook (&optional cleanup?)
   4145   (let ((signature-help-handler (lsp--signature-help-handler-create)))
   4146     (cond
   4147      ((and (or (equal lsp-signature-auto-activate t)
   4148                (memq :on-trigger-char lsp-signature-auto-activate))
   4149            signature-help-handler
   4150            (not cleanup?))
   4151       (add-hook 'post-self-insert-hook signature-help-handler nil t))
   4152 
   4153      ((or cleanup?
   4154           (not (or (equal lsp-signature-auto-activate t)
   4155                    (memq :on-trigger-char lsp-signature-auto-activate))))
   4156       (remove-hook 'post-self-insert-hook signature-help-handler t)))))
   4157 
   4158 (defun lsp--after-set-visited-file-name ()
   4159   (lsp-disconnect)
   4160   (lsp))
   4161 
   4162 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27
   4163 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099
   4164 (defvar eldoc-documentation-default) ; CI
   4165 (when (< emacs-major-version 28)
   4166   (unless (boundp 'eldoc-documentation-functions)
   4167     (load "eldoc" nil 'nomessage))
   4168   (when (memq (default-value 'eldoc-documentation-function) '(nil ignore))
   4169     ;; actually `eldoc-documentation-strategy', but CI was failing
   4170     (setq-default eldoc-documentation-function 'eldoc-documentation-default)))
   4171 
   4172 (define-minor-mode lsp-managed-mode
   4173   "Mode for source buffers managed by lsp-mode."
   4174   :lighter nil
   4175   (cond
   4176    (lsp-managed-mode
   4177     (when (lsp-feature? "textDocument/hover")
   4178       (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t)
   4179       (eldoc-mode 1))
   4180 
   4181     (add-hook 'after-change-functions #'lsp-on-change nil t)
   4182     (add-hook 'after-revert-hook #'lsp-on-revert nil t)
   4183     (add-hook 'after-save-hook #'lsp-on-save nil t)
   4184     (add-hook 'auto-save-hook #'lsp--on-auto-save nil t)
   4185     (add-hook 'before-change-functions #'lsp-before-change nil t)
   4186     (add-hook 'before-save-hook #'lsp--before-save nil t)
   4187     (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t)
   4188     (add-hook 'post-command-hook #'lsp--post-command nil t)
   4189 
   4190     (lsp--update-on-type-formatting-hook)
   4191     (lsp--update-signature-help-hook)
   4192 
   4193     (when lsp-enable-xref
   4194       (add-hook 'xref-backend-functions #'lsp--xref-backend nil t))
   4195 
   4196     (lsp-configure-buffer)
   4197 
   4198     ;; make sure we turn off lsp-mode in case major mode changes, because major
   4199     ;; mode change will wipe the buffer locals.
   4200     (add-hook 'change-major-mode-hook #'lsp-disconnect nil t)
   4201     (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t)
   4202 
   4203     (let ((buffer (lsp-current-buffer)))
   4204       (run-with-idle-timer
   4205        0.0 nil
   4206        (lambda ()
   4207          (when (lsp-buffer-live-p buffer)
   4208            (lsp-with-current-buffer buffer
   4209              (lsp--on-change-debounce buffer)
   4210              (lsp--on-idle buffer)))))))
   4211    (t
   4212     (lsp-unconfig-buffer)
   4213 
   4214     (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t)
   4215     (remove-hook 'post-command-hook #'lsp--post-command t)
   4216     (remove-hook 'after-change-functions #'lsp-on-change t)
   4217     (remove-hook 'after-revert-hook #'lsp-on-revert t)
   4218     (remove-hook 'after-save-hook #'lsp-on-save t)
   4219     (remove-hook 'auto-save-hook #'lsp--on-auto-save t)
   4220     (remove-hook 'before-change-functions #'lsp-before-change t)
   4221     (remove-hook 'before-save-hook #'lsp--before-save t)
   4222     (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t)
   4223 
   4224     (lsp--update-on-type-formatting-hook :cleanup)
   4225     (lsp--update-signature-help-hook :cleanup)
   4226 
   4227     (when lsp--on-idle-timer
   4228       (cancel-timer lsp--on-idle-timer)
   4229       (setq lsp--on-idle-timer nil))
   4230 
   4231     (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4232     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4233 
   4234     (lsp--remove-overlays 'lsp-highlight)
   4235     (lsp--remove-overlays 'lsp-links)
   4236 
   4237     (remove-hook 'xref-backend-functions #'lsp--xref-backend t)
   4238     (remove-hook 'change-major-mode-hook #'lsp-disconnect t)
   4239     (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t)
   4240     (setq-local lsp-buffer-uri nil))))
   4241 
   4242 (defun lsp-configure-buffer ()
   4243   "Configure LSP features for current buffer."
   4244   ;; make sure the core is running in the context of all available workspaces
   4245   ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context
   4246   (let ((lsp--buffer-workspaces (cond
   4247                                  (lsp--buffer-workspaces)
   4248                                  (lsp--cur-workspace (list lsp--cur-workspace))))
   4249         lsp--cur-workspace)
   4250     (when lsp-auto-configure
   4251       (lsp--auto-configure)
   4252 
   4253       (when (and lsp-enable-text-document-color
   4254                  (lsp-feature? "textDocument/documentColor"))
   4255         (add-hook 'lsp-on-change-hook #'lsp--document-color nil t))
   4256 
   4257       (when (and lsp-enable-imenu
   4258                  (lsp-feature? "textDocument/documentSymbol"))
   4259         (lsp-enable-imenu))
   4260 
   4261       (when (and lsp-enable-indentation
   4262                  (lsp-feature? "textDocument/rangeFormatting"))
   4263         (add-function :override (local 'indent-region-function) #'lsp-format-region))
   4264 
   4265       (when (and lsp-enable-symbol-highlighting
   4266                  (lsp-feature? "textDocument/documentHighlight"))
   4267         (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t))
   4268 
   4269       (when (and lsp-enable-links
   4270                  (lsp-feature? "textDocument/documentLink"))
   4271         (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t))
   4272 
   4273       (when (and lsp-inlay-hint-enable
   4274                  (lsp-feature? "textDocument/inlayHint"))
   4275         (lsp-inlay-hints-mode))
   4276 
   4277       (when (and lsp-enable-dap-auto-configure
   4278                  (functionp 'dap-mode))
   4279         (dap-auto-configure-mode 1)))
   4280     (run-hooks 'lsp-configure-hook)))
   4281 
   4282 (defun lsp-unconfig-buffer ()
   4283   "Unconfigure LSP features for buffer."
   4284   (lsp--remove-overlays 'lsp-color)
   4285 
   4286   (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function)
   4287     (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   4288     (setq-local imenu-menubar-modified-tick 0)
   4289     (setq-local imenu--index-alist nil)
   4290     (imenu--cleanup))
   4291 
   4292   (remove-function (local 'indent-region-function) #'lsp-format-region)
   4293 
   4294   (remove-hook 'lsp-on-change-hook #'lsp--document-color t)
   4295   (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4296   (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4297 
   4298   (when (and lsp-enable-dap-auto-configure
   4299              (functionp 'dap-mode))
   4300     (dap-auto-configure-mode -1))
   4301 
   4302   (run-hooks 'lsp-unconfigure-hook))
   4303 
   4304 (defun lsp--buffer-content ()
   4305   (lsp-save-restriction-and-excursion
   4306     (or (lsp-virtual-buffer-call :buffer-string)
   4307         (buffer-substring-no-properties (point-min)
   4308                                         (point-max)))))
   4309 
   4310 (defun lsp--text-document-did-open ()
   4311   "`document/didOpen' event."
   4312   (run-hooks 'lsp-before-open-hook)
   4313   (when (and lsp-auto-touch-files
   4314              (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri)))))
   4315     (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri))
   4316     (save-buffer))
   4317 
   4318   (setq lsp--cur-version (or lsp--cur-version 0))
   4319   (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   4320   (lsp-notify
   4321    "textDocument/didOpen"
   4322    (list :textDocument
   4323          (list :uri (lsp--buffer-uri)
   4324                :languageId (lsp-buffer-language)
   4325                :version lsp--cur-version
   4326                :text (lsp--buffer-content))))
   4327 
   4328   (lsp-managed-mode 1)
   4329 
   4330   (lsp-diagnostics--request-pull-diagnostics lsp--cur-workspace)
   4331 
   4332   (run-hooks 'lsp-after-open-hook)
   4333   (when-let* ((client (-some-> lsp--cur-workspace (lsp--workspace-client))))
   4334     (-some-> (lsp--client-after-open-fn client)
   4335       (funcall))
   4336     (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client))
   4337       (intern-soft)
   4338       (run-hooks))))
   4339 
   4340 (defun lsp--text-document-identifier ()
   4341   "Make TextDocumentIdentifier."
   4342   (list :uri (lsp--buffer-uri)))
   4343 
   4344 (defun lsp--versioned-text-document-identifier ()
   4345   "Make VersionedTextDocumentIdentifier."
   4346   (plist-put (lsp--text-document-identifier) :version lsp--cur-version))
   4347 
   4348 (defun lsp--cur-line (&optional point)
   4349   (1- (line-number-at-pos point)))
   4350 
   4351 (defun lsp--cur-position ()
   4352   "Make a Position object for the current point."
   4353   (or (lsp-virtual-buffer-call :cur-position)
   4354       (lsp-save-restriction-and-excursion
   4355         (list :line (lsp--cur-line)
   4356               :character (- (point) (line-beginning-position))))))
   4357 
   4358 (defun lsp--point-to-position (point)
   4359   "Convert POINT to Position."
   4360   (lsp-save-restriction-and-excursion
   4361     (goto-char point)
   4362     (lsp--cur-position)))
   4363 
   4364 (defun lsp--range (start end)
   4365   "Make Range body from START and END."
   4366   ;; make sure start and end are Position objects
   4367   (list :start start :end end))
   4368 
   4369 (defun lsp--region-to-range (start end)
   4370   "Make Range object for the current region."
   4371   (lsp--range (lsp--point-to-position start)
   4372               (lsp--point-to-position end)))
   4373 
   4374 (defun lsp--region-or-line ()
   4375   "The active region or the current line."
   4376   (if (use-region-p)
   4377       (lsp--region-to-range (region-beginning) (region-end))
   4378     (lsp--region-to-range (line-beginning-position) (line-end-position))))
   4379 
   4380 (defun lsp--check-document-changes-version (document-changes)
   4381   "Verify that DOCUMENT-CHANGES have the proper version."
   4382   (unless (seq-every-p
   4383            (-lambda ((&TextDocumentEdit :text-document))
   4384              (or
   4385               (not text-document)
   4386               (let* ((filename (-> text-document
   4387                                    lsp:versioned-text-document-identifier-uri
   4388                                    lsp--uri-to-path))
   4389                      (version (lsp:versioned-text-document-identifier-version? text-document)))
   4390                 (with-current-buffer (find-file-noselect filename)
   4391                   (or (null version) (zerop version) (= -1 version)
   4392                       (equal version lsp--cur-version))))))
   4393            document-changes)
   4394     (error "Document changes cannot be applied due to different document version")))
   4395 
   4396 (defun lsp--apply-workspace-edit (workspace-edit &optional operation)
   4397   "Apply the WorkspaceEdit object WORKSPACE-EDIT.
   4398 OPERATION is symbol representing the source of this text edit."
   4399   (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit))
   4400     (if-let* ((document-changes (seq-reverse document-changes?)))
   4401         (progn
   4402           (lsp--check-document-changes-version document-changes)
   4403           (->> document-changes
   4404                (seq-filter (-lambda ((&CreateFile :kind)) (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 (or (not kind) (equal kind "edit"))
   4409                                   (not (equal kind "create")))))
   4410                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4411           (->> document-changes
   4412                (seq-filter (-lambda ((&CreateFile :kind))
   4413                              (and (not (or (not kind) (equal kind "edit")))
   4414                                   (not (equal kind "create")))))
   4415                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))))
   4416       (lsp-map
   4417        (lambda (uri text-edits)
   4418          (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect)
   4419            (lsp--apply-text-edits text-edits operation)))
   4420        changes?))))
   4421 
   4422 (defmacro lsp-with-filename (file &rest body)
   4423   "Execute BODY with FILE as a context.
   4424 Need to handle the case when FILE indicates virtual buffer."
   4425   (declare (indent 1) (debug t))
   4426   `(if-let* ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file)))
   4427        (lsp-with-current-buffer lsp--virtual-buffer
   4428          ,@body)
   4429      ,@body))
   4430 
   4431 (defun lsp--apply-text-document-edit (edit &optional operation)
   4432   "Apply the TextDocumentEdit object EDIT.
   4433 OPERATION is symbol representing the source of this text edit.
   4434 If the file is not being visited by any buffer, it is opened with
   4435 `find-file-noselect'.
   4436 Because lsp-mode does not store previous document versions, the edit is only
   4437 applied if the version of the textDocument matches the version of the
   4438 corresponding file.
   4439 
   4440 interface TextDocumentEdit {
   4441   textDocument: VersionedTextDocumentIdentifier;
   4442   edits: TextEdit[];
   4443 }"
   4444   (pcase (lsp:edit-kind edit)
   4445     ("create" (-let* (((&CreateFile :uri :options?) edit)
   4446                       (file-name (lsp--uri-to-path uri)))
   4447                 (mkdir (f-dirname file-name) t)
   4448                 (f-touch file-name)
   4449                 (when (lsp:create-file-options-overwrite? options?)
   4450                   (f-write-text "" nil file-name))
   4451                 (find-file-noselect file-name)))
   4452     ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit))
   4453                 (f-delete (lsp--uri-to-path uri) recursive?)))
   4454     ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit)
   4455                       (old-file-name (lsp--uri-to-path old-uri))
   4456                       (new-file-name (lsp--uri-to-path new-uri))
   4457                       (buf (find-buffer-visiting old-file-name)))
   4458                 (when buf
   4459                   (lsp-with-current-buffer buf
   4460                     (save-buffer)
   4461                     (lsp--text-document-did-close)))
   4462                 (mkdir (f-dirname new-file-name) t)
   4463                 (rename-file old-file-name new-file-name overwrite?)
   4464                 (when buf
   4465                   (lsp-with-current-buffer buf
   4466                     (set-buffer-modified-p nil)
   4467                     (setq lsp-buffer-uri nil)
   4468                     (set-visited-file-name new-file-name)
   4469                     (lsp)))))
   4470     (_ (let ((file-name (->> edit
   4471                              (lsp:text-document-edit-text-document)
   4472                              (lsp:versioned-text-document-identifier-uri)
   4473                              (lsp--uri-to-path))))
   4474          (lsp-with-current-buffer (find-buffer-visiting file-name)
   4475            (lsp-with-filename file-name
   4476              (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation)))))))
   4477 
   4478 (lsp-defun lsp--position-compare ((&Position :line left-line
   4479                                              :character left-character)
   4480                                   (&Position :line right-line
   4481                                              :character right-character))
   4482   "Return t if position LEFT is greater than RIGHT."
   4483   (if (= left-line right-line)
   4484       (> left-character right-character)
   4485     (> left-line right-line)))
   4486 
   4487 (lsp-defun lsp-point-in-range? (position (&Range :start :end))
   4488   "Returns if POINT is in RANGE."
   4489   (not (or (lsp--position-compare start position)
   4490            (lsp--position-compare position end))))
   4491 
   4492 (lsp-defun lsp--position-equal ((&Position :line left-line
   4493                                            :character left-character)
   4494                                 (&Position :line right-line
   4495                                            :character right-character))
   4496   "Return whether LEFT and RIGHT positions are equal."
   4497   (and (= left-line right-line)
   4498        (= left-character right-character)))
   4499 
   4500 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end))
   4501                                           (&TextEdit :range (&Range :start right-start :end right-end)))
   4502   (if (lsp--position-equal left-start right-start)
   4503       (lsp--position-compare left-end right-end)
   4504     (lsp--position-compare left-start right-start)))
   4505 
   4506 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text))
   4507   "Apply the edits described in the TextEdit object in TEXT-EDIT."
   4508   (setq new-text (s-replace "\r" "" (or new-text "")))
   4509   (lsp:set-text-edit-new-text edit new-text)
   4510   (goto-char start)
   4511   (delete-region start end)
   4512   (insert new-text))
   4513 
   4514 ;; WORKAROUND: typescript-language might send -1 when applying code actions.
   4515 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582
   4516 (lsp-defun lsp--fix-point ((point &as &Position :character :line))
   4517   (-doto point
   4518     (lsp:set-position-line (max 0 line))
   4519     (lsp:set-position-character (max 0 character))))
   4520 
   4521 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as
   4522                                                                &TextEdit
   4523                                                                :range (&Range :start :end)
   4524                                                                :new-text))
   4525   "Apply the edits described in the TextEdit object in TEXT-EDIT.
   4526 The method uses `replace-buffer-contents'."
   4527   (setq new-text (s-replace "\r" "" (or new-text "")))
   4528   (lsp:set-text-edit-new-text edit new-text)
   4529   (-let* ((source (current-buffer))
   4530           ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start)
   4531                                                              :end (lsp--fix-point end)))))
   4532     (with-temp-buffer
   4533       (insert new-text)
   4534       (let ((temp (current-buffer)))
   4535         (with-current-buffer source
   4536           (save-excursion
   4537             (save-restriction
   4538               (narrow-to-region beg end)
   4539 
   4540               ;; On emacs versions < 26.2,
   4541               ;; `replace-buffer-contents' is buggy - it calls
   4542               ;; change functions with invalid arguments - so we
   4543               ;; manually call the change functions here.
   4544               ;;
   4545               ;; See emacs bugs #32237, #32278:
   4546               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237
   4547               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278
   4548               (let ((inhibit-modification-hooks t)
   4549                     (length (- end beg)))
   4550                 (run-hook-with-args 'before-change-functions
   4551                                     beg end)
   4552                 (replace-buffer-contents temp)
   4553                 (run-hook-with-args 'after-change-functions
   4554                                     beg (+ beg (length new-text))
   4555                                     length)))))))))
   4556 
   4557 (defun lsp--to-yasnippet-snippet (snippet)
   4558   "Convert LSP SNIPPET to yasnippet snippet."
   4559   ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it.
   4560   (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`")))
   4561                             (rx "\\" (backref 1))
   4562                             snippet
   4563                             nil nil 1))
   4564 
   4565 (defvar-local lsp-enable-relative-indentation nil
   4566   "Enable relative indentation when insert texts, snippets ...
   4567 from language server.")
   4568 
   4569 (defun lsp--expand-snippet (snippet &optional start end expand-env)
   4570   "Wrapper of `yas-expand-snippet' with all of it arguments.
   4571 The snippet will be convert to LSP style and indent according to
   4572 LSP server result."
   4573   (require 'yasnippet nil t)
   4574   (let* ((inhibit-field-text-motion t)
   4575          (yas-wrap-around-region nil)
   4576          (yas-indent-line 'none)
   4577          (yas-also-auto-indent-first-line nil))
   4578     (yas-expand-snippet
   4579      (lsp--to-yasnippet-snippet snippet)
   4580      start end expand-env)))
   4581 
   4582 (defun lsp--indent-lines (start end &optional insert-text-mode?)
   4583   "Indent from START to END based on INSERT-TEXT-MODE? value.
   4584 - When INSERT-TEXT-MODE? is provided
   4585   - if it's `lsp/insert-text-mode-as-it', do no editor indentation.
   4586   - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading
   4587     whitespaces to match the line where text is inserted.
   4588 - When it's not provided, using `indent-line-function' for each line."
   4589   (save-excursion
   4590     (goto-char end)
   4591     (let* ((end-line (line-number-at-pos))
   4592            (offset (save-excursion
   4593                      (goto-char start)
   4594                      (current-indentation)))
   4595            (indent-line-function
   4596             (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it)
   4597                    #'ignore)
   4598                   ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation)
   4599                        lsp-enable-relative-indentation
   4600                        ;; Indenting snippets is extremely slow in `org-mode' buffers
   4601                        ;; since it has to calculate indentation based on SRC block
   4602                        ;; position.  Thus we use relative indentation as default.
   4603                        (derived-mode-p 'org-mode))
   4604                    (lambda () (save-excursion
   4605                                 (beginning-of-line)
   4606                                 (indent-to-column offset))))
   4607                   (t indent-line-function))))
   4608       (goto-char start)
   4609       (forward-line)
   4610       (while (and (not (eobp))
   4611                   (<= (line-number-at-pos) end-line))
   4612         (funcall indent-line-function)
   4613         (forward-line)))))
   4614 
   4615 (defun lsp--apply-text-edits (edits &optional operation)
   4616   "Apply the EDITS described in the TextEdit[] object.
   4617 OPERATION is symbol representing the source of this text edit."
   4618   (unless (seq-empty-p edits)
   4619     (atomic-change-group
   4620       (run-hooks 'lsp-before-apply-edits-hook)
   4621       (let* ((change-group (prepare-change-group))
   4622              (howmany (length edits))
   4623              (message (format "Applying %s edits to `%s' ..." howmany (current-buffer)))
   4624              (_ (lsp--info message))
   4625              (reporter (make-progress-reporter message 0 howmany))
   4626              (done 0)
   4627              (apply-edit (if (not lsp--virtual-buffer)
   4628                              #'lsp--apply-text-edit-replace-buffer-contents
   4629                            #'lsp--apply-text-edit)))
   4630         (unwind-protect
   4631             (->> edits
   4632                  ;; We sort text edits so as to apply edits that modify latter
   4633                  ;; parts of the document first. Furthermore, because the LSP
   4634                  ;; spec dictates that: "If multiple inserts have the same
   4635                  ;; position, the order in the array defines which edit to
   4636                  ;; apply first."  We reverse the initial list and sort stably
   4637                  ;; to make sure the order among edits with the same position
   4638                  ;; is preserved.
   4639                  (nreverse)
   4640                  (seq-sort #'lsp--text-edit-sort-predicate)
   4641                  (mapc (lambda (edit)
   4642                          (progress-reporter-update reporter (cl-incf done))
   4643                          (funcall apply-edit edit)
   4644                          (when (lsp:snippet-text-edit-insert-text-format? edit)
   4645                            (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start)
   4646                                                          :insert-text-format? :new-text) edit)
   4647                              (when (eq insert-text-format? lsp/insert-text-format-snippet)
   4648                                ;; No `save-excursion' needed since expand snippet will change point anyway
   4649                                (goto-char (+ start (length new-text)))
   4650                                (lsp--indent-lines start (point))
   4651                                (lsp--expand-snippet new-text start (point)))))
   4652                          (run-hook-with-args 'lsp-after-apply-edits-hook operation))))
   4653           (undo-amalgamate-change-group change-group)
   4654           (progress-reporter-done reporter))))))
   4655 
   4656 (defun lsp--create-apply-text-edits-handlers ()
   4657   "Create (handler cleanup-fn) for applying text edits in async request.
   4658 Only works when mode is `tick or `alive."
   4659   (let* (first-edited
   4660          (func (lambda (start &rest _)
   4661                  (setq first-edited (if first-edited
   4662                                         (min start first-edited)
   4663                                       start)))))
   4664     (add-hook 'before-change-functions func nil t)
   4665     (list
   4666      (lambda (edits)
   4667        (if (and first-edited
   4668                 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end)))
   4669                             ;; Text edit region is overlapped
   4670                             (> end first-edited))
   4671                           edits))
   4672            (lsp--warn "TextEdits will not be applied since document has been modified before of them.")
   4673          (lsp--apply-text-edits edits 'completion-cleanup)))
   4674      (lambda ()
   4675        (remove-hook 'before-change-functions func t)))))
   4676 
   4677 (defun lsp--capability (cap &optional capabilities)
   4678   "Get the value of capability CAP.  If CAPABILITIES is non-nil, use them instead."
   4679   (when (stringp cap)
   4680     (setq cap (intern (concat ":" cap))))
   4681 
   4682   (lsp-get (or capabilities
   4683                (lsp--server-capabilities))
   4684            cap))
   4685 
   4686 (defun lsp--registered-capability (method)
   4687   "Check whether there is workspace providing METHOD."
   4688   (->> (lsp-workspaces)
   4689        (--keep (seq-find (lambda (reg)
   4690                            (equal (lsp--registered-capability-method reg) method))
   4691                          (lsp--workspace-registered-server-capabilities it)))
   4692        cl-first))
   4693 
   4694 (defun lsp--capability-for-method (method)
   4695   "Get the value of capability for METHOD."
   4696   (-let* ((reqs (cdr (assoc method lsp-method-requirements)))
   4697           ((&plist :capability) reqs))
   4698     (or (and capability (lsp--capability capability))
   4699         (-some-> (lsp--registered-capability method)
   4700           (lsp--registered-capability-options)))))
   4701 
   4702 (defvar-local lsp--before-change-vals nil
   4703   "Store the positions from the `lsp-before-change' function call, for
   4704 validation and use in the `lsp-on-change' function.")
   4705 
   4706 (defun lsp--text-document-content-change-event (start end length)
   4707   "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH."
   4708   ;; So (47 54 0) means add    7 chars starting at pos 47
   4709   ;; must become
   4710   ;;   {"range":{"start":{"line":5,"character":6}
   4711   ;;             ,"end" :{"line":5,"character":6}}
   4712   ;;             ,"rangeLength":0
   4713   ;;             ,"text":"\nbb = 5"}
   4714   ;;
   4715   ;; And (47 47 7) means delete 7 chars starting at pos 47
   4716   ;; must become
   4717   ;;   {"range":{"start":{"line":6,"character":0}
   4718   ;;            ,"end"  :{"line":7,"character":0}}
   4719   ;;            ,"rangeLength":7
   4720   ;;            ,"text":""}
   4721   ;;
   4722   ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with
   4723   ;; 13 chars. So it must become
   4724   ;;   {"range":{"start":{"line":5,"character":8}
   4725   ;;             ,"end" :{"line":5,"character":11}}
   4726   ;;             ,"rangeLength":3
   4727   ;;             ,"text":"new-chars-xxx"}
   4728   ;;
   4729 
   4730   ;; Adding text:
   4731   ;;   lsp-before-change:(start,end)=(33,33)
   4732   ;;   lsp-on-change:(start,end,length)=(33,34,0)
   4733   ;;
   4734   ;; Changing text:
   4735   ;;   lsp-before-change:(start,end)=(208,211)
   4736   ;;   lsp-on-change:(start,end,length)=(208,221,3)
   4737   ;;
   4738   ;; Deleting text:
   4739   ;;   lsp-before-change:(start,end)=(19,27)
   4740   ;;   lsp-on-change:(start,end,length)=(19,19,8)
   4741   (if (zerop length)
   4742       ;; Adding something only, work from start only
   4743       `( :range ,(lsp--range
   4744                   (lsp--point-to-position start)
   4745                   (lsp--point-to-position start))
   4746          :rangeLength 0
   4747          :text ,(buffer-substring-no-properties start end))
   4748 
   4749     (if (eq start end)
   4750         ;; Deleting something only
   4751         (if (lsp--bracketed-change-p start length)
   4752             ;; The before-change value is bracketed, use it
   4753             `( :range ,(lsp--range
   4754                         (lsp--point-to-position start)
   4755                         (plist-get lsp--before-change-vals :end-pos))
   4756                :rangeLength ,length
   4757                :text "")
   4758           ;; If the change is not bracketed, send a full change event instead.
   4759           (lsp--full-change-event))
   4760 
   4761       ;; Deleting some things, adding others
   4762       (if (lsp--bracketed-change-p start length)
   4763           ;; The before-change value is valid, use it
   4764           `( :range ,(lsp--range
   4765                       (lsp--point-to-position start)
   4766                       (plist-get lsp--before-change-vals :end-pos))
   4767              :rangeLength ,length
   4768              :text ,(buffer-substring-no-properties start end))
   4769         (lsp--full-change-event)))))
   4770 
   4771 (defun lsp--bracketed-change-p (start length)
   4772   "If the before and after positions are the same, and the length
   4773 is the size of the start range, we are probably good."
   4774   (-let [(&plist :end before-end :start before-start) lsp--before-change-vals]
   4775     (and (eq start before-start)
   4776          (eq length (- before-end before-start)))))
   4777 
   4778 (defun lsp--full-change-event ()
   4779   `(:text ,(lsp--buffer-content)))
   4780 
   4781 (defun lsp-before-change (start end)
   4782   "Executed before a file is changed.
   4783 Added to `before-change-functions'."
   4784   ;; Note:
   4785   ;;
   4786   ;; This variable holds a list of functions to call when Emacs is about to
   4787   ;; modify a buffer. Each function gets two arguments, the beginning and end of
   4788   ;; the region that is about to change, represented as integers. The buffer
   4789   ;; that is about to change is always the current buffer when the function is
   4790   ;; called.
   4791   ;;
   4792   ;; WARNING:
   4793   ;;
   4794   ;; Do not expect the before-change hooks and the after-change hooks be called
   4795   ;; in balanced pairs around each buffer change. Also don't expect the
   4796   ;; before-change hooks to be called for every chunk of text Emacs is about to
   4797   ;; delete. These hooks are provided on the assumption that Lisp programs will
   4798   ;; use either before- or the after-change hooks, but not both, and the
   4799   ;; boundaries of the region where the changes happen might include more than
   4800   ;; just the actual changed text, or even lump together several changes done
   4801   ;; piecemeal.
   4802   (save-match-data
   4803     (lsp-save-restriction-and-excursion
   4804       (setq lsp--before-change-vals
   4805             (list :start start
   4806                   :end end
   4807                   :end-pos (lsp--point-to-position end))))))
   4808 
   4809 (defun lsp--flush-delayed-changes ()
   4810   (let ((inhibit-quit t))
   4811     (when lsp--delay-timer
   4812       (cancel-timer lsp--delay-timer))
   4813     (mapc (-lambda ((workspace buffer document change))
   4814             (with-current-buffer buffer
   4815               (with-lsp-workspace workspace
   4816                 (lsp-notify "textDocument/didChange"
   4817                             (list :textDocument document
   4818                                   :contentChanges (vector change))))))
   4819           (prog1 (nreverse lsp--delayed-requests)
   4820             (setq lsp--delayed-requests nil)))))
   4821 
   4822 (defun lsp--workspace-sync-method (workspace)
   4823   (let ((sync (-> workspace
   4824                   (lsp--workspace-server-capabilities)
   4825                   (lsp:server-capabilities-text-document-sync?))))
   4826     (if (lsp-text-document-sync-options? sync)
   4827         (lsp:text-document-sync-options-change? sync)
   4828       sync)))
   4829 
   4830 (defun lsp-on-change (start end length &optional content-change-event-fn)
   4831   "Executed when a file is changed.
   4832 Added to `after-change-functions'."
   4833   ;; Note:
   4834   ;;
   4835   ;; Each function receives three arguments: the beginning and end of the region
   4836   ;; just changed, and the length of the text that existed before the change.
   4837   ;; All three arguments are integers. The buffer that has been changed is
   4838   ;; always the current buffer when the function is called.
   4839   ;;
   4840   ;; The length of the old text is the difference between the buffer positions
   4841   ;; before and after that text as it was before the change. As for the
   4842   ;; changed text, its length is simply the difference between the first two
   4843   ;; arguments.
   4844   ;;
   4845   ;; So (47 54 0) means add    7 chars starting at pos 47
   4846   ;; So (47 47 7) means delete 7 chars starting at pos 47
   4847   (save-match-data
   4848     (let ((inhibit-quit t)
   4849           ;; make sure that `lsp-on-change' is called in multi-workspace context
   4850           ;; see #2901
   4851           lsp--cur-workspace)
   4852       ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done
   4853       ;; by auto-revert-mode) will cause this handler to get called with a nil
   4854       ;; buffer-file-name. We need the buffer-file-name to send notifications;
   4855       ;; so we skip handling revert-buffer-caused changes and instead handle
   4856       ;; reverts separately in lsp-on-revert
   4857       (when (not revert-buffer-in-progress-p)
   4858         (cl-incf lsp--cur-version)
   4859         (mapc
   4860          (lambda (workspace)
   4861            (pcase (or lsp-document-sync-method
   4862                       (lsp--workspace-sync-method workspace))
   4863              (1
   4864               (if lsp-debounce-full-sync-notifications
   4865                   (setq lsp--delayed-requests
   4866                         (->> lsp--delayed-requests
   4867                              (-remove (-lambda ((_ buffer))
   4868                                         (equal (current-buffer) buffer)))
   4869                              (cons (list workspace
   4870                                          (current-buffer)
   4871                                          (lsp--versioned-text-document-identifier)
   4872                                          (lsp--full-change-event)))))
   4873                 (with-lsp-workspace workspace
   4874                   (lsp-notify "textDocument/didChange"
   4875                               (list :contentChanges (vector (lsp--full-change-event))
   4876                                     :textDocument (lsp--versioned-text-document-identifier)))
   4877                   (lsp-diagnostics--request-pull-diagnostics workspace))))
   4878              (2
   4879               (with-lsp-workspace workspace
   4880                 (lsp-notify
   4881                  "textDocument/didChange"
   4882                  (list :textDocument (lsp--versioned-text-document-identifier)
   4883                        :contentChanges (vector
   4884                                         (if content-change-event-fn
   4885                                             (funcall content-change-event-fn start end length)
   4886                                           (lsp--text-document-content-change-event
   4887                                            start end length)))))
   4888                 (lsp-diagnostics--request-pull-diagnostics workspace)))))
   4889          (lsp-workspaces))
   4890         (when lsp--delay-timer (cancel-timer lsp--delay-timer))
   4891         (setq lsp--delay-timer (run-with-idle-timer
   4892                                 lsp-debounce-full-sync-notifications-interval
   4893                                 nil
   4894                                 #'lsp--flush-delayed-changes))
   4895         ;; force cleanup overlays after each change
   4896         (lsp--remove-overlays 'lsp-highlight)
   4897         (lsp--after-change (current-buffer))))))
   4898 
   4899 
   4900 
   4901 ;; facilities for on change hooks. We do not want to make lsp calls on each
   4902 ;; change event so we add debounce to avoid flooding the server with events.
   4903 ;; Additionally, we want to have a mechanism for stopping the server calls in
   4904 ;; particular cases like, e. g. when performing completion.
   4905 
   4906 (defvar lsp-inhibit-lsp-hooks nil
   4907   "Flag to control.")
   4908 
   4909 (defcustom lsp-on-change-hook nil
   4910   "Hooks to run when buffer has changed."
   4911   :type 'hook
   4912   :group 'lsp-mode)
   4913 
   4914 (defcustom lsp-idle-delay 0.500
   4915   "Debounce interval for `after-change-functions'."
   4916   :type 'number
   4917   :group 'lsp-mode)
   4918 
   4919 (defcustom lsp-on-idle-hook nil
   4920   "Hooks to run after `lsp-idle-delay'."
   4921   :type 'hook
   4922   :group 'lsp-mode)
   4923 
   4924 (defun lsp--idle-reschedule (buffer)
   4925   (when lsp--on-idle-timer
   4926     (cancel-timer lsp--on-idle-timer))
   4927 
   4928   (setq lsp--on-idle-timer (run-with-idle-timer
   4929                             lsp-idle-delay
   4930                             nil
   4931                             #'lsp--on-idle
   4932                             buffer)))
   4933 
   4934 (defun lsp--post-command ()
   4935   (lsp--cleanup-highlights-if-needed)
   4936   (lsp--idle-reschedule (current-buffer)))
   4937 
   4938 (defun lsp--on-idle (buffer)
   4939   "Start post command loop."
   4940   (when (and (buffer-live-p buffer)
   4941              (equal buffer (current-buffer))
   4942              (not lsp-inhibit-lsp-hooks)
   4943              lsp-managed-mode)
   4944     (run-hooks 'lsp-on-idle-hook)))
   4945 
   4946 (defun lsp--on-change-debounce (buffer)
   4947   (when (and (buffer-live-p buffer)
   4948              (equal buffer (current-buffer))
   4949              (not lsp-inhibit-lsp-hooks)
   4950              lsp-managed-mode)
   4951     (run-hooks 'lsp-on-change-hook)))
   4952 
   4953 (defun lsp--after-change (buffer)
   4954   "Called after most textDocument/didChange events."
   4955   (setq lsp--signature-last-index nil
   4956         lsp--signature-last nil)
   4957 
   4958   ;; cleanup diagnostics
   4959   (when lsp-diagnostic-clean-after-change
   4960     (dolist (workspace (lsp-workspaces))
   4961       (-let [diagnostics (lsp--workspace-diagnostics workspace)]
   4962         (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics))))
   4963 
   4964   (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled)
   4965     (lsp--semantic-tokens-refresh-if-enabled buffer))
   4966   (when lsp--on-change-timer
   4967     (cancel-timer lsp--on-change-timer))
   4968   (setq lsp--on-change-timer (run-with-idle-timer
   4969                               lsp-idle-delay
   4970                               nil
   4971                               #'lsp--on-change-debounce
   4972                               buffer))
   4973   (lsp--idle-reschedule buffer))
   4974 
   4975 
   4976 (defcustom lsp-trim-trailing-whitespace t
   4977   "Trim trailing whitespace on a line."
   4978   :group 'lsp-mode
   4979   :type 'boolean)
   4980 
   4981 (defcustom lsp-insert-final-newline t
   4982   "Insert a newline character at the end of the file if one does not exist."
   4983   :group 'lsp-mode
   4984   :type 'boolean)
   4985 
   4986 (defcustom lsp-trim-final-newlines t
   4987   "Trim all newlines after the final newline at the end of the file."
   4988   :group 'lsp-mode
   4989   :type 'boolean)
   4990 
   4991 
   4992 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters)
   4993   "Self insert handling.
   4994 Applies on type formatting."
   4995   (let ((ch last-command-event))
   4996     (when (or (eq (string-to-char first-trigger-characters) ch)
   4997               (cl-find ch more-trigger-characters :key #'string-to-char))
   4998       (lsp-request-async "textDocument/onTypeFormatting"
   4999                          (lsp-make-document-on-type-formatting-params
   5000                           :text-document (lsp--text-document-identifier)
   5001                           :options (lsp-make-formatting-options
   5002                                     :tab-size (symbol-value (lsp--get-indent-width major-mode))
   5003                                     :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   5004                                     :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   5005                                     :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   5006                                     :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))
   5007                           :ch (char-to-string ch)
   5008                           :position (lsp--cur-position))
   5009                          (lambda (data) (lsp--apply-text-edits data 'format))
   5010                          :mode 'tick))))
   5011 
   5012 
   5013 ;; links
   5014 (defun lsp--document-links ()
   5015   (when (lsp-feature? "textDocument/documentLink")
   5016     (lsp-request-async
   5017      "textDocument/documentLink"
   5018      `(:textDocument ,(lsp--text-document-identifier))
   5019      (lambda (links)
   5020        (lsp--remove-overlays 'lsp-link)
   5021        (seq-do
   5022         (-lambda ((link &as &DocumentLink :range (&Range :start :end)))
   5023           (-doto (make-button (lsp--position-to-point start)
   5024                               (lsp--position-to-point end)
   5025                               'action (lsp--document-link-keymap link)
   5026                               'keymap (let ((map (make-sparse-keymap)))
   5027                                         (define-key map [M-return] 'push-button)
   5028                                         (define-key map [mouse-2] 'push-button)
   5029                                         map)
   5030                               'help-echo "mouse-2, M-RET: Visit this link")
   5031             (overlay-put 'lsp-link t)))
   5032         links))
   5033      :mode 'unchanged)))
   5034 
   5035 (defun lsp--document-link-handle-target (url)
   5036   (let* ((parsed-url (url-generic-parse-url (url-unhex-string url)))
   5037          (type (url-type parsed-url)))
   5038     (pcase type
   5039       ("file"
   5040        (xref-push-marker-stack)
   5041        (find-file (lsp--uri-to-path url))
   5042        (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url))
   5043          (goto-char (lsp--position-to-point
   5044                      (lsp-make-position :character (1- (string-to-number column))
   5045                                         :line (1- (string-to-number line)))))))
   5046       ((or "http" "https") (browse-url url))
   5047       (type (if-let* ((handler (lsp--get-uri-handler type)))
   5048                 (funcall handler url)
   5049               (signal 'lsp-file-scheme-not-supported (list url)))))))
   5050 
   5051 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?))
   5052   (if target?
   5053       (lambda (_)
   5054         (interactive)
   5055         (lsp--document-link-handle-target target?))
   5056     (lambda (_)
   5057       (interactive)
   5058       (when (lsp:document-link-registration-options-resolve-provider?
   5059              (lsp--capability-for-method "textDocument/documentLink"))
   5060         (lsp-request-async
   5061          "documentLink/resolve"
   5062          link
   5063          (-lambda ((&DocumentLink :target?))
   5064            (lsp--document-link-handle-target target?)))))))
   5065 
   5066 
   5067 
   5068 (defcustom lsp-warn-no-matched-clients t
   5069   "Whether to show messages when there are no supported clients."
   5070   :group 'lsp-mode
   5071   :type 'boolean)
   5072 
   5073 (defun lsp-buffer-language--configured-id ()
   5074   "Return nil when not registered."
   5075   (->> lsp-language-id-configuration
   5076        (-first
   5077         (-lambda ((mode-or-pattern . language))
   5078           (cond
   5079            ((and (stringp mode-or-pattern)
   5080                  (s-matches? mode-or-pattern (buffer-file-name)))
   5081             language)
   5082            ((eq mode-or-pattern major-mode) language))))
   5083        cl-rest))
   5084 
   5085 (defvar-local lsp--buffer-language nil
   5086   "Locally cached returned value of `lsp-buffer-language'.")
   5087 
   5088 (defun lsp-buffer-language ()
   5089   "Get language corresponding current buffer."
   5090   (or lsp--buffer-language
   5091       (let* ((configured-language (lsp-buffer-language--configured-id)))
   5092         (setq lsp--buffer-language
   5093               (or configured-language
   5094                   ;; ensure non-nil
   5095                   (string-remove-suffix "-mode" (symbol-name major-mode))))
   5096         (when (and lsp-warn-no-matched-clients
   5097                    (null configured-language))
   5098           (lsp-warn "Unable to calculate the languageId for buffer `%s'. \
   5099 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s"
   5100                     (buffer-name)
   5101                     major-mode))
   5102         lsp--buffer-language)))
   5103 
   5104 (defun lsp-activate-on (&rest languages)
   5105   "Returns language activation function.
   5106 The function will return t when the `lsp-buffer-language' returns
   5107 one of the LANGUAGES."
   5108   (lambda (_file-name _mode)
   5109     (-contains? languages (lsp-buffer-language))))
   5110 
   5111 (defun lsp-workspace-root (&optional path)
   5112   "Find the workspace root for the current file or PATH."
   5113   (-when-let* ((file-name (or path (buffer-file-name)))
   5114                (file-name (lsp-f-canonical file-name)))
   5115     (->> (lsp-session)
   5116          (lsp-session-folders)
   5117          (--filter (and (lsp--files-same-host it file-name)
   5118                         (or (lsp-f-ancestor-of? it file-name)
   5119                             (equal it file-name))))
   5120          (--max-by (> (length it) (length other))))))
   5121 
   5122 (defun lsp-on-revert ()
   5123   "Executed when a file is reverted.
   5124 Added to `after-revert-hook'."
   5125   (let ((n (buffer-size))
   5126         (revert-buffer-in-progress-p nil))
   5127     (lsp-on-change 0 n n)))
   5128 
   5129 (defun lsp--text-document-did-close (&optional keep-workspace-alive)
   5130   "Executed when the file is closed, added to `kill-buffer-hook'.
   5131 
   5132 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace
   5133 if it's closing the last buffer in the workspace."
   5134   (lsp-foreach-workspace
   5135    (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   5136    (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S"
   5137      (lsp-notify "textDocument/didClose"
   5138                  `(:textDocument ,(lsp--text-document-identifier))))
   5139    (when (and (not lsp-keep-workspace-alive)
   5140               (not keep-workspace-alive)
   5141               (not (lsp--workspace-buffers lsp--cur-workspace)))
   5142      (lsp--shutdown-workspace))))
   5143 
   5144 (defun lsp--will-save-text-document-params (reason)
   5145   (list :textDocument (lsp--text-document-identifier)
   5146         :reason reason))
   5147 
   5148 (defun lsp--before-save ()
   5149   "Before save handler."
   5150   (with-demoted-errors "Error in ‘lsp--before-save’: %S"
   5151     (let ((params (lsp--will-save-text-document-params 1)))
   5152       (when (lsp--send-will-save-p)
   5153         (lsp-notify "textDocument/willSave" params))
   5154       (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits)
   5155         (let ((lsp-response-timeout 0.1))
   5156           (condition-case nil
   5157               (lsp--apply-text-edits
   5158                (lsp-request "textDocument/willSaveWaitUntil"
   5159                             params)
   5160                'before-save)
   5161             (error)))))))
   5162 
   5163 (defun lsp--on-auto-save ()
   5164   "Handler for auto-save."
   5165   (when (lsp--send-will-save-p)
   5166     (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S"
   5167       (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2)))))
   5168 
   5169 (defun lsp--text-document-did-save ()
   5170   "Executed when the file is closed, added to `after-save-hook''."
   5171   (when (lsp--send-did-save-p)
   5172     (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’"
   5173       (lsp-notify "textDocument/didSave"
   5174                   `( :textDocument ,(lsp--versioned-text-document-identifier)
   5175                      ,@(when (lsp--save-include-text-p)
   5176                          (list :text (lsp--buffer-content))))))))
   5177 
   5178 (defun lsp--text-document-position-params (&optional identifier position)
   5179   "Make TextDocumentPositionParams for the current point in the current document.
   5180 If IDENTIFIER and POSITION are non-nil, they will be used as the document
   5181 identifier and the position respectively."
   5182   (list :textDocument (or identifier (lsp--text-document-identifier))
   5183         :position (or position (lsp--cur-position))))
   5184 
   5185 (defun lsp--get-buffer-diagnostics ()
   5186   "Return buffer diagnostics."
   5187   (gethash (or
   5188             (plist-get lsp--virtual-buffer :buffer-file-name)
   5189             (lsp--fix-path-casing (buffer-file-name)))
   5190            (lsp-diagnostics t)))
   5191 
   5192 (defun lsp-cur-line-diagnostics ()
   5193   "Return any diagnostics that apply to the current line."
   5194   (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)]
   5195     (cl-coerce (-filter
   5196                 (-lambda ((&Diagnostic :range (&Range :start (&Position :line))))
   5197                   (and (>= line start) (<= line end)))
   5198                 (lsp--get-buffer-diagnostics))
   5199                'vector)))
   5200 
   5201 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end)
   5202                                   (right &as &Range :start right-start :end right-end))
   5203   (or (lsp-point-in-range? right-start left)
   5204       (lsp-point-in-range? right-end left)
   5205       (lsp-point-in-range? left-start right)
   5206       (lsp-point-in-range? left-end right)))
   5207 
   5208 (defun lsp-make-position-1 (position)
   5209   (lsp-make-position :line (plist-get position :line)
   5210                      :character (plist-get position :character)))
   5211 
   5212 (defun lsp-cur-possition-diagnostics ()
   5213   "Return any diagnostics that apply to the current line."
   5214   (-let* ((start (if (use-region-p) (region-beginning) (point)))
   5215           (end (if (use-region-p) (region-end) (point)))
   5216           (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start))
   5217                                          :end (lsp-make-position-1 (lsp-point-to-position end)))))
   5218     (->> (lsp--get-buffer-diagnostics)
   5219          (-filter
   5220           (-lambda ((&Diagnostic :range))
   5221             (lsp-range-overlapping? range current-range)))
   5222          (apply 'vector))))
   5223 
   5224 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics)
   5225 
   5226 (defun lsp--extract-line-from-buffer (pos)
   5227   "Return the line pointed to by POS (a Position object) in the current buffer."
   5228   (let* ((point (lsp--position-to-point pos))
   5229          (inhibit-field-text-motion t))
   5230     (save-excursion
   5231       (goto-char point)
   5232       (buffer-substring (line-beginning-position) (line-end-position)))))
   5233 
   5234 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line)
   5235                                                  :end (end &as &Position :character end-char)))
   5236   "Return a xref-item from a RANGE in FILENAME."
   5237   (let* ((line (lsp--extract-line-from-buffer start))
   5238          (len (length line)))
   5239     (add-face-text-property (max (min start-char len) 0)
   5240                             (max (min end-char len) 0)
   5241                             'xref-match t line)
   5242     ;; LINE is nil when FILENAME is not being current visited by any buffer.
   5243     (xref-make-match (or line filename)
   5244                      (xref-make-file-location
   5245                       filename
   5246                       (lsp-translate-line (1+ start-line))
   5247                       (lsp-translate-column start-char))
   5248                      (- end-char start-char))))
   5249 
   5250 (defun lsp--location-uri (loc)
   5251   (if (lsp-location? loc)
   5252       (lsp:location-uri loc)
   5253     (lsp:location-link-target-uri loc)))
   5254 
   5255 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start)))
   5256   "Go to location."
   5257   (let ((path (lsp--uri-to-path uri)))
   5258     (if (f-exists? path)
   5259         (with-current-buffer (find-file path)
   5260           (goto-char (lsp--position-to-point start)))
   5261       (error "There is no file %s" path))))
   5262 
   5263 (defun lsp--location-range (loc)
   5264   (if (lsp-location? loc)
   5265       (lsp:location-range loc)
   5266     (lsp:location-link-target-selection-range loc)))
   5267 
   5268 (defun lsp--locations-to-xref-items (locations)
   5269   "Return a list of `xref-item' given LOCATIONS, which can be of
   5270 type Location, LocationLink, Location[] or LocationLink[]."
   5271   (setq locations
   5272         (pcase locations
   5273           ((seq (or (lsp-interface Location)
   5274                     (lsp-interface LocationLink)))
   5275            (append locations nil))
   5276           ((or (lsp-interface Location)
   5277                (lsp-interface LocationLink))
   5278            (list locations))))
   5279 
   5280   (cl-labels ((get-xrefs-in-file
   5281                (file-locs)
   5282                (-let [(filename . matches) file-locs]
   5283                  (condition-case err
   5284                      (let ((visiting (find-buffer-visiting filename))
   5285                            (fn (lambda (loc)
   5286                                  (lsp-with-filename filename
   5287                                    (lsp--xref-make-item filename
   5288                                                         (lsp--location-range loc))))))
   5289                        (if visiting
   5290                            (with-current-buffer visiting
   5291                              (seq-map fn matches))
   5292                          (when (file-readable-p filename)
   5293                            (with-temp-buffer
   5294                              (insert-file-contents-literally filename)
   5295                              (seq-map fn matches)))))
   5296                    (error (lsp-warn "Failed to process xref entry for filename '%s': %s"
   5297                                     filename (error-message-string err)))
   5298                    (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s"
   5299                                          filename (error-message-string err)))))))
   5300 
   5301     (->> locations
   5302          (seq-sort #'lsp--location-before-p)
   5303          (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri))
   5304          (seq-map #'get-xrefs-in-file)
   5305          (apply #'nconc))))
   5306 
   5307 (defun lsp--location-before-p (left right)
   5308   "Sort first by file, then by line, then by column."
   5309   (let ((left-uri (lsp--location-uri left))
   5310         (right-uri (lsp--location-uri right)))
   5311     (if (not (string= left-uri right-uri))
   5312         (string< left-uri right-uri)
   5313       (-let (((&Range :start left-start) (lsp--location-range left))
   5314              ((&Range :start right-start) (lsp--location-range right)))
   5315         (lsp--position-compare right-start left-start)))))
   5316 
   5317 (defun lsp--make-reference-params (&optional td-position exclude-declaration)
   5318   "Make a ReferenceParam object.
   5319 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead.
   5320 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations."
   5321   (let ((json-false :json-false))
   5322     (plist-put (or td-position (lsp--text-document-position-params))
   5323                :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration))))))
   5324 
   5325 (defun lsp--cancel-request (id)
   5326   "Cancel request with ID in all workspaces."
   5327   (lsp-foreach-workspace
   5328    (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id))
   5329    (lsp-notify "$/cancelRequest" `(:id ,id))))
   5330 
   5331 (defvar-local lsp--hover-saved-bounds nil)
   5332 
   5333 (defun lsp-eldoc-function (cb &rest _ignored)
   5334   "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')."
   5335   (if (and lsp--hover-saved-bounds
   5336            (lsp--point-in-bounds-p lsp--hover-saved-bounds))
   5337       lsp--eldoc-saved-message
   5338     (setq lsp--hover-saved-bounds nil
   5339           lsp--eldoc-saved-message nil)
   5340     (if (looking-at-p "[[:space:]\n]")
   5341         (setq lsp--eldoc-saved-message nil) ; And returns nil.
   5342       (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover"))
   5343         (lsp-request-async
   5344          "textDocument/hover"
   5345          (lsp--text-document-position-params)
   5346          (-lambda ((hover &as &Hover? :range? :contents))
   5347            (setq lsp--hover-saved-bounds (when range?
   5348                                            (lsp--range-to-region range?)))
   5349            (funcall cb (setq lsp--eldoc-saved-message
   5350                              (when contents
   5351                                (lsp--render-on-hover-content
   5352                                 contents
   5353                                 lsp-eldoc-render-all)))))
   5354          :error-handler #'ignore
   5355          :mode 'tick
   5356          :cancel-token :eldoc-hover)))))
   5357 
   5358 (defun lsp--point-on-highlight? ()
   5359   (-some? (lambda (overlay)
   5360             (overlay-get overlay 'lsp-highlight))
   5361           (overlays-at (point))))
   5362 
   5363 (defun lsp--cleanup-highlights-if-needed ()
   5364   (when (and lsp-enable-symbol-highlighting
   5365              lsp--have-document-highlights
   5366              (not (lsp--point-on-highlight?)))
   5367     (lsp--remove-overlays 'lsp-highlight)
   5368     (setq lsp--have-document-highlights nil)
   5369     (lsp-cancel-request-by-token :highlights)))
   5370 
   5371 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil
   5372   "The bounds of the symbol from which `lsp--document-highlight'
   5373   most recently requested highlights.")
   5374 
   5375 (defun lsp--document-highlight ()
   5376   (when (lsp-feature? "textDocument/documentHighlight")
   5377     (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol)))
   5378       (unless (or (looking-at-p "[[:space:]\n]")
   5379                   (not lsp-enable-symbol-highlighting)
   5380                   (and lsp--have-document-highlights
   5381                        curr-sym-bounds
   5382                        (equal curr-sym-bounds
   5383                               lsp--symbol-bounds-of-last-highlight-invocation)))
   5384         (setq lsp--symbol-bounds-of-last-highlight-invocation
   5385               curr-sym-bounds)
   5386         (lsp-request-async "textDocument/documentHighlight"
   5387                            (lsp--text-document-position-params)
   5388                            #'lsp--document-highlight-callback
   5389                            :mode 'tick
   5390                            :cancel-token :highlights)))))
   5391 
   5392 (defun lsp--help-open-link (&rest _)
   5393   "Open markdown link at point via mouse or keyboard."
   5394   (interactive "P")
   5395   (let ((buffer-list-update-hook nil))
   5396     (-let [(buffer point) (if-let* ((valid (and (listp last-input-event)
   5397                                                 (eq (car last-input-event) 'mouse-2)))
   5398                                     (event (cadr last-input-event))
   5399                                     (win (posn-window event))
   5400                                     (buffer (window-buffer win)))
   5401                               `(,buffer ,(posn-point event))
   5402                             `(,(current-buffer) ,(point)))]
   5403       (with-current-buffer buffer
   5404         (when-let* ((face (get-text-property point 'face))
   5405                     (url (or (and (eq face 'markdown-link-face)
   5406                                   (get-text-property point 'help-echo))
   5407                              (and (memq face '(markdown-url-face markdown-plain-url-face))
   5408                                   (nth 3 (markdown-link-at-pos point))))))
   5409           (lsp--document-link-handle-target url))))))
   5410 
   5411 (defvar lsp-help-mode-map
   5412   (-doto (make-sparse-keymap)
   5413     (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link))
   5414   "Keymap for `lsp-help-mode'.")
   5415 
   5416 (define-derived-mode lsp-help-mode help-mode "LspHelp"
   5417   "Major mode for displaying lsp help.")
   5418 
   5419 (defun lsp-describe-thing-at-point ()
   5420   "Display the type signature and documentation of the thing at point."
   5421   (interactive)
   5422   (let ((contents (-some->> (lsp--text-document-position-params)
   5423                     (lsp--make-request "textDocument/hover")
   5424                     (lsp--send-request)
   5425                     (lsp:hover-contents))))
   5426     (if (and contents (not (equal contents "")))
   5427         (let ((lsp-help-buf-name "*lsp-help*"))
   5428           (with-current-buffer (get-buffer-create lsp-help-buf-name)
   5429             (delay-mode-hooks
   5430               (lsp-help-mode)
   5431               (with-help-window lsp-help-buf-name
   5432                 (insert
   5433 		 (mapconcat 'string-trim-right
   5434 			    (split-string (lsp--render-on-hover-content contents t) "\n")
   5435 			    "\n"))))
   5436             (run-mode-hooks)))
   5437       (lsp--info "No content at point."))))
   5438 
   5439 (defun lsp--point-in-bounds-p (bounds)
   5440   "Return whether the current point is within BOUNDS."
   5441   (and (<= (car bounds) (point)) (< (point) (cdr bounds))))
   5442 
   5443 (defun lsp-get-renderer (language)
   5444   "Get renderer for LANGUAGE."
   5445   (lambda (str)
   5446     (lsp--render-string str language)))
   5447 
   5448 (defun lsp--setup-markdown (mode)
   5449   "Setup the ‘markdown-mode’ in the frame.
   5450 MODE is the mode used in the parent frame."
   5451   (make-local-variable 'markdown-code-lang-modes)
   5452   (dolist (mark (alist-get mode lsp-custom-markup-modes))
   5453     (add-to-list 'markdown-code-lang-modes (cons mark mode)))
   5454   (setq-local markdown-fontify-code-blocks-natively t)
   5455   (setq-local markdown-fontify-code-block-default-mode mode)
   5456   (setq-local markdown-hide-markup t)
   5457 
   5458   ;; Render some common HTML entities.
   5459   ;; This should really happen in markdown-mode instead,
   5460   ;; but it doesn't, so we do it here for now.
   5461   (setq prettify-symbols-alist
   5462         (cl-loop for i from 0 to 255
   5463                  collect (cons (format "&#x%02X;" i) i)))
   5464   (push '("&lt;" . ?<) prettify-symbols-alist)
   5465   (push '("&gt;" . ?>) prettify-symbols-alist)
   5466   (push '("&amp;" . ?&) prettify-symbols-alist)
   5467   (push '("&nbsp;" . ? ) prettify-symbols-alist)
   5468   (setq prettify-symbols-compose-predicate
   5469         (lambda (_start _end _match) t))
   5470   (prettify-symbols-mode 1))
   5471 
   5472 (defvar lsp-help-link-keymap
   5473   (let ((map (make-sparse-keymap)))
   5474     (define-key map [mouse-2] #'lsp--help-open-link)
   5475     (define-key map "\r" #'lsp--help-open-link)
   5476     map)
   5477   "Keymap active on links in *lsp-help* mode.")
   5478 
   5479 (defun lsp--fix-markdown-links ()
   5480   (let ((inhibit-read-only t)
   5481         (inhibit-modification-hooks t)
   5482         (prop))
   5483     (save-restriction
   5484       (goto-char (point-min))
   5485       (while (setq prop (markdown-find-next-prop 'face))
   5486         (let ((end (or (next-single-property-change (car prop) 'face)
   5487                        (point-max))))
   5488           (when (memq (get-text-property (car prop) 'face)
   5489                       '(markdown-link-face
   5490                         markdown-url-face
   5491                         markdown-plain-url-face))
   5492             (add-text-properties (car prop) end
   5493                                  (list 'button t
   5494                                        'category 'lsp-help-link
   5495                                        'follow-link t
   5496                                        'keymap lsp-help-link-keymap)))
   5497           (goto-char end))))))
   5498 
   5499 (defun lsp--buffer-string-visible ()
   5500   "Return visible buffer string.
   5501 Stolen from `org-copy-visible'."
   5502   (let ((temp (generate-new-buffer " *temp*"))
   5503         (beg (point-min))
   5504         (end (point-max)))
   5505     (while (/= beg end)
   5506       (when (get-char-property beg 'invisible)
   5507         (setq beg (next-single-char-property-change beg 'invisible nil end)))
   5508       (let* ((next (next-single-char-property-change beg 'invisible nil end))
   5509              (substring (buffer-substring beg next)))
   5510         (with-current-buffer temp (insert substring))
   5511         ;; (setq result (concat result substring))
   5512         (setq beg next)))
   5513     (setq deactivate-mark t)
   5514     (prog1 (with-current-buffer temp
   5515              (s-chop-suffix "\n" (buffer-string)))
   5516       (kill-buffer temp))))
   5517 
   5518 (defvar lsp-buffer-major-mode nil
   5519   "Holds the major mode when fontification function is running.
   5520 See #2588")
   5521 
   5522 (defvar view-inhibit-help-message)
   5523 
   5524 (defun lsp--render-markdown ()
   5525   "Render markdown."
   5526 
   5527   (let ((markdown-enable-math nil))
   5528     (goto-char (point-min))
   5529     (while (re-search-forward
   5530             (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/"
   5531                                      "{" "}" "[" "]" "(" ")"
   5532                                      "#" "+" "-" "." "!" "|"))))
   5533             nil t)
   5534       (replace-match (rx (backref 1))))
   5535 
   5536     ;; markdown-mode v2.3 does not yet provide gfm-view-mode
   5537     (if (fboundp 'gfm-view-mode)
   5538         (let ((view-inhibit-help-message t))
   5539           (gfm-view-mode))
   5540       (gfm-mode))
   5541 
   5542     (lsp--setup-markdown lsp-buffer-major-mode)))
   5543 
   5544 (defvar lsp--display-inline-image-alist
   5545   '((lsp--render-markdown
   5546      (:regexp
   5547       "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)"
   5548       :sexp
   5549       (create-image
   5550        (base64-decode-string
   5551         (buffer-substring-no-properties (match-beginning 1) (match-end 1)))
   5552        nil t))))
   5553   "Replaced string regexp and function returning image.
   5554 Each element should have the form (MODE . (PROPERTY-LIST...)).
   5555 MODE (car) is function which is defined in `lsp-language-id-configuration'.
   5556 Cdr should be list of PROPERTY-LIST.
   5557 
   5558 Each PROPERTY-LIST should have properties:
   5559 :regexp  Regexp which determines what string is relpaced to image.
   5560          You should also get information of image, by parenthesis constructs.
   5561          By default, all matched string is replaced to image, but you can
   5562          change index of replaced string by keyword :replaced-index.
   5563 
   5564 :sexp    Return image when evaluated. You can use information of regexp
   5565          by using (match-beggining N), (match-end N) or (match-substring N).
   5566 
   5567 In addition, each can have property:
   5568 :replaced-index  Determine index which is used to replace regexp to image.
   5569                  The value means first argument of `match-beginning' and
   5570                  `match-end'. If omitted, interpreted as index 0.")
   5571 
   5572 (defcustom lsp-display-inline-image t
   5573   "Showing inline image or not."
   5574   :group 'lsp-mode
   5575   :type 'boolean)
   5576 
   5577 (defcustom lsp-enable-suggest-server-download t
   5578   "When non-nil enable server downloading suggestions."
   5579   :group 'lsp-mode
   5580   :type 'boolean
   5581   :package-version '(lsp-mode . "9.0.0"))
   5582 
   5583 (defcustom lsp-auto-register-remote-clients t
   5584   "When non-nil register remote when registering the local one."
   5585   :group 'lsp-mode
   5586   :type 'boolean
   5587   :package-version '(lsp-mode . "9.0.0"))
   5588 
   5589 (defun lsp--display-inline-image (mode)
   5590   "Add image property if available."
   5591   (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist))))
   5592     (when (and (display-images-p) lsp-display-inline-image)
   5593       (cl-loop
   5594        for plist in plist-list
   5595        with regexp with replaced-index
   5596        do
   5597        (setq regexp (plist-get plist :regexp))
   5598        (setq replaced-index (or (plist-get plist :replaced-index) 0))
   5599 
   5600        (font-lock-remove-keywords nil (list regexp replaced-index))
   5601        (let ((inhibit-read-only t))
   5602          (save-excursion
   5603            (goto-char (point-min))
   5604            (while (re-search-forward regexp nil t)
   5605              (set-text-properties
   5606               (match-beginning replaced-index) (match-end replaced-index)
   5607               nil)
   5608              (add-text-properties
   5609               (match-beginning replaced-index) (match-end replaced-index)
   5610               `(display ,(eval (plist-get plist :sexp)))))))))))
   5611 
   5612 (defun lsp--fontlock-with-mode (str mode)
   5613   "Fontlock STR with MODE."
   5614   (let ((lsp-buffer-major-mode major-mode))
   5615     (with-temp-buffer
   5616       (with-demoted-errors "Error during doc rendering: %s"
   5617         (insert str)
   5618         (delay-mode-hooks (funcall mode))
   5619         (cl-flet ((window-body-width () lsp-window-body-width))
   5620           ;; This can go wrong in some cases, and the fontification would
   5621           ;; not work as expected.
   5622           ;;
   5623           ;; See #2984
   5624           (ignore-errors (font-lock-ensure))
   5625           (lsp--display-inline-image mode)
   5626           (when (eq mode 'lsp--render-markdown)
   5627             (lsp--fix-markdown-links))))
   5628       (lsp--buffer-string-visible))))
   5629 
   5630 (defun lsp--render-string (str language)
   5631   "Render STR using `major-mode' corresponding to LANGUAGE.
   5632 When language is nil render as markup if `markdown-mode' is loaded."
   5633   (setq str (s-replace "\r" "" (or str "")))
   5634   (if-let* ((modes (-keep (-lambda ((mode . lang))
   5635                             (when (and (equal lang language) (functionp mode))
   5636                               mode))
   5637                           lsp-language-id-configuration))
   5638             (mode (car (or (member major-mode modes) modes))))
   5639       (lsp--fontlock-with-mode str mode)
   5640     str))
   5641 
   5642 (defun lsp--render-element (content)
   5643   "Render CONTENT element."
   5644   (let ((inhibit-message t))
   5645     (or
   5646      (pcase content
   5647        ((lsp-interface MarkedString :value :language)
   5648         (lsp--render-string value language))
   5649        ((lsp-interface MarkupContent :value :kind)
   5650         (lsp--render-string value kind))
   5651        ;; plain string
   5652        ((pred stringp) (lsp--render-string content "markdown"))
   5653        ((pred null) "")
   5654        (_ (error "Failed to handle %s" content)))
   5655      "")))
   5656 
   5657 (defun lsp--create-unique-string-fn ()
   5658   (let (elements)
   5659     (lambda (element)
   5660       (let ((count (cl-count element elements :test #'string=)))
   5661         (prog1 (if (zerop count)
   5662                    element
   5663                  (format "%s (%s)" element count))
   5664           (push element elements))))))
   5665 
   5666 (defun lsp--select-action (actions)
   5667   "Select an action to execute from ACTIONS."
   5668   (cond
   5669    ((seq-empty-p actions) (signal 'lsp-no-code-actions nil))
   5670    ((and (eq (seq-length actions) 1) lsp-auto-execute-action)
   5671     (lsp-seq-first actions))
   5672    (t (let ((completion-ignore-case t))
   5673         (lsp--completing-read "Select code action: "
   5674                               (seq-into actions 'list)
   5675                               (-compose (lsp--create-unique-string-fn)
   5676                                         #'lsp:code-action-title)
   5677                               nil t)))))
   5678 
   5679 (defun lsp--workspace-server-id (workspace)
   5680   "Return the server ID of WORKSPACE."
   5681   (-> workspace lsp--workspace-client lsp--client-server-id))
   5682 
   5683 (defun lsp--handle-rendered-for-echo-area (contents)
   5684   "Return a single line from RENDERED, appropriate for display in the echo area."
   5685   (pcase (lsp-workspaces)
   5686     (`(,workspace)
   5687      (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace)))
   5688     ;; For projects with multiple active workspaces we also default to
   5689     ;; render the first line.
   5690     (_ (lsp-clients-extract-signature-on-hover contents nil))))
   5691 
   5692 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id)
   5693   "Extract a representative line from CONTENTS, to show in the echo area."
   5694   (car (s-lines (s-trim (lsp--render-element contents)))))
   5695 
   5696 (defun lsp--render-on-hover-content (contents render-all)
   5697   "Render the content received from `document/onHover' request.
   5698 CONTENTS  - MarkedString | MarkedString[] | MarkupContent
   5699 RENDER-ALL - nil if only the signature should be rendered."
   5700   (cond
   5701    ((lsp-markup-content? contents)
   5702     ;; MarkupContent.
   5703     ;; It tends to be long and is not suitable to display fully in the echo area.
   5704     ;; Just display the first line which is typically the signature.
   5705     (if render-all
   5706         (lsp--render-element contents)
   5707       (lsp--handle-rendered-for-echo-area contents)))
   5708    ((and (stringp contents) (not (string-match-p "\n" contents)))
   5709     ;; If the contents is a single string containing a single line,
   5710     ;; render it always.
   5711     (lsp--render-element contents))
   5712    (t
   5713     ;; MarkedString -> MarkedString[]
   5714     (when (or (lsp-marked-string? contents) (stringp contents))
   5715       (setq contents (list contents)))
   5716     ;; Consider the signature consisting of the elements who have a renderable
   5717     ;; "language" property. When render-all is nil, ignore other elements.
   5718     (string-join
   5719      (seq-map
   5720       #'lsp--render-element
   5721       (if render-all
   5722           contents
   5723         ;; Only render contents that have an available renderer.
   5724         (seq-take
   5725          (seq-filter
   5726           (-andfn #'lsp-marked-string?
   5727                   (-compose #'lsp-get-renderer #'lsp:marked-string-language))
   5728           contents)
   5729          1)))
   5730      (if (bound-and-true-p page-break-lines-mode)
   5731          "\n\n"
   5732        "\n")))))
   5733 
   5734 
   5735 
   5736 (defvar lsp-signature-mode-map
   5737   (-doto (make-sparse-keymap)
   5738     (define-key (kbd "M-n") #'lsp-signature-next)
   5739     (define-key (kbd "M-p") #'lsp-signature-previous)
   5740     (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs)
   5741     (define-key (kbd "C-c C-k") #'lsp-signature-stop)
   5742     (define-key (kbd "C-g") #'lsp-signature-stop))
   5743   "Keymap for `lsp-signature-mode'.")
   5744 
   5745 (define-minor-mode lsp-signature-mode
   5746   "Mode used to show signature popup."
   5747   :keymap lsp-signature-mode-map
   5748   :lighter ""
   5749   :group 'lsp-mode)
   5750 
   5751 (defun lsp-signature-stop ()
   5752   "Stop showing current signature help."
   5753   (interactive)
   5754   (lsp-cancel-request-by-token :signature)
   5755   (remove-hook 'post-command-hook #'lsp-signature)
   5756   (funcall lsp-signature-function nil)
   5757   (lsp-signature-mode -1))
   5758 
   5759 (declare-function page-break-lines--update-display-tables "ext:page-break-lines")
   5760 
   5761 (defun lsp--setup-page-break-mode-if-present ()
   5762   "Enable `page-break-lines-mode' in current buffer."
   5763   (when (fboundp 'page-break-lines-mode)
   5764     (page-break-lines-mode)
   5765     ;; force page-break-lines-mode to update the display tables.
   5766     (page-break-lines--update-display-tables)))
   5767 
   5768 (defun lsp-lv-message (message)
   5769   (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)
   5770   (if message
   5771       (progn
   5772         (setq lsp--signature-last-buffer (current-buffer))
   5773         (let ((lv-force-update t))
   5774           (lv-message "%s" message)))
   5775     (lv-delete-window)
   5776     (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)))
   5777 
   5778 (declare-function posframe-show "ext:posframe")
   5779 (declare-function posframe-hide "ext:posframe")
   5780 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe")
   5781 
   5782 (defface lsp-signature-posframe
   5783   '((t :inherit tooltip))
   5784   "Background and foreground for `lsp-signature-posframe'."
   5785   :group 'lsp-mode)
   5786 
   5787 (defvar lsp-signature-posframe-params
   5788   (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward
   5789         :height 10
   5790         :width 60
   5791         :border-width 1
   5792         :min-width 60)
   5793   "Params for signature and `posframe-show'.")
   5794 
   5795 (defun lsp-signature-posframe (str)
   5796   "Use posframe to show the STR signatureHelp string."
   5797   (if str
   5798       (apply #'posframe-show
   5799              (with-current-buffer (get-buffer-create " *lsp-signature*")
   5800                (erase-buffer)
   5801                (insert str)
   5802                (visual-line-mode 1)
   5803                (lsp--setup-page-break-mode-if-present)
   5804                (current-buffer))
   5805              (append
   5806               lsp-signature-posframe-params
   5807               (list :position (point)
   5808                     :background-color (face-attribute 'lsp-signature-posframe :background nil t)
   5809                     :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t)
   5810                     :border-color (face-attribute (if (facep 'child-frame-border)
   5811                                                       'child-frame-border
   5812                                                     'internal-border)
   5813                                                   :background nil t))))
   5814     (posframe-hide " *lsp-signature*")))
   5815 
   5816 (defun lsp--handle-signature-update (signature)
   5817   (let ((message
   5818          (if (lsp-signature-help? signature)
   5819              (lsp--signature->message signature)
   5820            (mapconcat #'lsp--signature->message signature "\n"))))
   5821     (if (s-present? message)
   5822         (funcall lsp-signature-function message)
   5823       (lsp-signature-stop))))
   5824 
   5825 (defun lsp-signature-activate ()
   5826   "Activate signature help.
   5827 It will show up only if current point has signature help."
   5828   (interactive)
   5829   (setq lsp--signature-last nil
   5830         lsp--signature-last-index nil
   5831         lsp--signature-last-buffer (current-buffer))
   5832   (add-hook 'post-command-hook #'lsp-signature)
   5833   (lsp-signature-mode t))
   5834 
   5835 (defcustom lsp-signature-cycle t
   5836   "Whether `lsp-signature-next' and prev should cycle."
   5837   :type 'boolean
   5838   :group 'lsp-mode)
   5839 
   5840 (defun lsp-signature-next ()
   5841   "Show next signature."
   5842   (interactive)
   5843   (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last))))
   5844     (when (and lsp--signature-last-index
   5845                lsp--signature-last
   5846                (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs)))
   5847       (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs))
   5848       (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))))
   5849 
   5850 (defun lsp-signature-previous ()
   5851   "Next signature."
   5852   (interactive)
   5853   (when (and lsp--signature-last-index
   5854              lsp--signature-last
   5855              (or lsp-signature-cycle (not (zerop lsp--signature-last-index))))
   5856     (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index)
   5857                                             (length (lsp:signature-help-signatures lsp--signature-last))
   5858                                           lsp--signature-last-index)))
   5859     (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))
   5860 
   5861 (defun lsp-signature-toggle-full-docs ()
   5862   "Toggle full/partial signature documentation."
   5863   (interactive)
   5864   (let ((all? (not (numberp lsp-signature-doc-lines))))
   5865     (setq lsp-signature-doc-lines (if all?
   5866                                       (or (car-safe lsp-signature-doc-lines)
   5867                                           20)
   5868                                     (list lsp-signature-doc-lines))))
   5869   (lsp-signature-activate))
   5870 
   5871 (defface lsp-signature-highlight-function-argument
   5872   '((t :inherit eldoc-highlight-function-argument))
   5873   "The face to use to highlight function arguments in signatures."
   5874   :group 'lsp-mode)
   5875 
   5876 (defun lsp--signature->message (signature-help)
   5877   "Generate eldoc message from SIGNATURE-HELP response."
   5878   (setq lsp--signature-last signature-help)
   5879 
   5880   (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help))))
   5881     (-let* (((&SignatureHelp :active-signature?
   5882                              :active-parameter?
   5883                              :signatures) signature-help)
   5884             (active-signature? (or lsp--signature-last-index active-signature? 0))
   5885             (_ (setq lsp--signature-last-index active-signature?))
   5886             ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?))
   5887             (prefix (if (= (length signatures) 1)
   5888                         ""
   5889                       (concat (propertize (format " %s/%s"
   5890                                                   (1+ active-signature?)
   5891                                                   (length signatures))
   5892                                           'face 'success)
   5893                               " ")))
   5894             (method-docs (when
   5895                              (and lsp-signature-render-documentation
   5896                                   (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines)))
   5897                            (let ((docs (lsp--render-element
   5898                                         (lsp:parameter-information-documentation? signature))))
   5899                              (when (s-present? docs)
   5900                                (concat
   5901                                 "\n"
   5902                                 (if (fboundp 'page-break-lines-mode)
   5903                                     "\n"
   5904                                   "")
   5905                                 (if (and (numberp lsp-signature-doc-lines)
   5906                                          (> (length (s-lines docs)) lsp-signature-doc-lines))
   5907                                     (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs)))
   5908                                             (propertize "\nTruncated..." 'face 'highlight))
   5909                                   docs)))))))
   5910       (when (and active-parameter? (not (seq-empty-p parameters?)))
   5911         (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?)))
   5912                               (seq-elt parameters? active-parameter?)))
   5913                      (selected-param-label (let ((label (lsp:parameter-information-label param)))
   5914                                              (if (stringp label) label (append label nil))))
   5915                      (start (if (stringp selected-param-label)
   5916                                 (s-index-of selected-param-label label)
   5917                               (cl-first selected-param-label)))
   5918                      (end (if (stringp selected-param-label)
   5919                               (+ start (length selected-param-label))
   5920                             (cl-second selected-param-label))))
   5921           (add-face-text-property start end 'lsp-signature-highlight-function-argument nil label)))
   5922       (concat prefix label method-docs))))
   5923 
   5924 (defun lsp-signature ()
   5925   "Display signature info (based on `textDocument/signatureHelp')"
   5926   (if (and lsp--signature-last-buffer
   5927            (not (equal (current-buffer) lsp--signature-last-buffer)))
   5928       (lsp-signature-stop)
   5929     (lsp-request-async "textDocument/signatureHelp"
   5930                        (lsp--text-document-position-params)
   5931                        #'lsp--handle-signature-update
   5932                        :cancel-token :signature)))
   5933 
   5934 
   5935 (defcustom lsp-overlay-document-color-char "■"
   5936   "Display the char represent the document color in overlay"
   5937   :type 'string
   5938   :group 'lsp-mode)
   5939 
   5940 ;; color presentation
   5941 (defun lsp--color-create-interactive-command (color range)
   5942   (lambda ()
   5943     (interactive)
   5944     (-let [(&ColorPresentation? :text-edit?
   5945                                 :additional-text-edits?)
   5946            (lsp--completing-read
   5947             "Select color presentation: "
   5948             (lsp-request
   5949              "textDocument/colorPresentation"
   5950              `( :textDocument ,(lsp--text-document-identifier)
   5951                 :color ,color
   5952                 :range ,range))
   5953             #'lsp:color-presentation-label
   5954             nil
   5955             t)]
   5956       (when text-edit?
   5957         (lsp--apply-text-edit text-edit?))
   5958       (when additional-text-edits?
   5959         (lsp--apply-text-edits additional-text-edits? 'color-presentation)))))
   5960 
   5961 (defun lsp--number->color (number)
   5962   (let ((result (format "%x"
   5963                         (round (* (or number 0) 255.0)))))
   5964     (if (= 1 (length result))
   5965         (concat "0" result)
   5966       result)))
   5967 
   5968 (defun lsp--document-color ()
   5969   "Document color handler."
   5970   (when (lsp-feature? "textDocument/documentColor")
   5971     (lsp-request-async
   5972      "textDocument/documentColor"
   5973      `(:textDocument ,(lsp--text-document-identifier))
   5974      (lambda (result)
   5975        (lsp--remove-overlays 'lsp-color)
   5976        (seq-do
   5977         (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue)
   5978                                      :range))
   5979           (-let* (((beg . end) (lsp--range-to-region range))
   5980                   (overlay (make-overlay beg end))
   5981                   (command (lsp--color-create-interactive-command color range)))
   5982             (overlay-put overlay 'lsp-color t)
   5983             (overlay-put overlay 'evaporate t)
   5984             (overlay-put overlay
   5985                          'before-string
   5986                          (propertize
   5987                           lsp-overlay-document-color-char
   5988                           'face `((:foreground ,(format
   5989                                                  "#%s%s%s"
   5990                                                  (lsp--number->color red)
   5991                                                  (lsp--number->color green)
   5992                                                  (lsp--number->color blue))))
   5993                           'action command
   5994                           'mouse-face 'lsp-lens-mouse-face
   5995                           'local-map (-doto (make-sparse-keymap)
   5996                                        (define-key [mouse-1] command))))))
   5997         result))
   5998      :mode 'unchanged
   5999      :cancel-token :document-color-token)))
   6000 
   6001 
   6002 
   6003 (defun lsp--action-trigger-parameter-hints (_command)
   6004   "Handler for editor.action.triggerParameterHints."
   6005   (when (member :on-server-request lsp-signature-auto-activate)
   6006     (lsp-signature-activate)))
   6007 
   6008 (defun lsp--action-trigger-suggest (_command)
   6009   "Handler for editor.action.triggerSuggest."
   6010   (cond
   6011    ((and (bound-and-true-p company-mode)
   6012          (fboundp 'company-auto-begin)
   6013          (fboundp 'company-post-command))
   6014     (run-at-time 0 nil
   6015                  (lambda ()
   6016                    (let ((this-command 'company-idle-begin)
   6017                          (company-minimum-prefix-length 0))
   6018                      (company-auto-begin)
   6019                      (company-post-command)))))
   6020    (t
   6021     (completion-at-point))))
   6022 
   6023 (defconst lsp--default-action-handlers
   6024   (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints)
   6025       ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest))
   6026   "Default action handlers.")
   6027 
   6028 (defun lsp--find-action-handler (command)
   6029   "Find action handler for particular COMMAND."
   6030   (or
   6031    (--some (-some->> it
   6032              (lsp--workspace-client)
   6033              (lsp--client-action-handlers)
   6034              (gethash command))
   6035            (lsp-workspaces))
   6036    (gethash command lsp--default-action-handlers)))
   6037 
   6038 (defun lsp--text-document-code-action-params (&optional kind)
   6039   "Code action params."
   6040   (list :textDocument (lsp--text-document-identifier)
   6041         :range (if (use-region-p)
   6042                    (lsp--region-to-range (region-beginning) (region-end))
   6043                  (lsp--region-to-range (point) (point)))
   6044         :context `( :diagnostics ,(lsp-cur-possition-diagnostics)
   6045                     ,@(when kind (list :only (vector kind))))))
   6046 
   6047 (defun lsp-code-actions-at-point (&optional kind)
   6048   "Retrieve the code actions for the active region or the current line.
   6049 It will filter by KIND if non nil."
   6050   (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind)))
   6051 
   6052 (defun lsp-execute-code-action-by-kind (command-kind)
   6053   "Execute code action by COMMAND-KIND."
   6054   (if-let* ((action (->> (lsp-get-or-calculate-code-actions command-kind)
   6055                         (-filter (-lambda ((&CodeAction :kind?))
   6056                                    (and kind? (s-prefix? command-kind kind?))))
   6057                         lsp--select-action)))
   6058       (lsp-execute-code-action action)
   6059     (signal 'lsp-no-code-actions '(command-kind))))
   6060 
   6061 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point)
   6062 
   6063 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?))
   6064   "Parse and execute a code ACTION represented as a Command LSP type."
   6065   (let ((server-id (->> (lsp-workspaces)
   6066                         (cl-first)
   6067                         (or lsp--cur-workspace)
   6068                         (lsp--workspace-client)
   6069                         (lsp--client-server-id))))
   6070     (condition-case nil
   6071         (with-no-warnings
   6072           (lsp-execute-command server-id (intern command) arguments?))
   6073       (cl-no-applicable-method
   6074        (if-let* ((action-handler (lsp--find-action-handler command)))
   6075            (funcall action-handler action)
   6076          (lsp-send-execute-command command arguments?))))))
   6077 
   6078 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?))
   6079   "Execute code action ACTION. For example, when text under the
   6080 caret has a suggestion to apply a fix from an lsp-server, calling
   6081 this function will do so.
   6082 If ACTION is not set it will be selected from `lsp-code-actions-at-point'.
   6083 Request codeAction/resolve for more info if server supports."
   6084   (interactive (list (lsp--select-action (lsp-code-actions-at-point))))
   6085   (if (and (lsp-feature? "codeAction/resolve")
   6086            (not command?)
   6087            (not edit?))
   6088       (lsp--execute-code-action (lsp-request "codeAction/resolve" action))
   6089     (lsp--execute-code-action action)))
   6090 
   6091 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?))
   6092   "Execute code action ACTION."
   6093   (when edit?
   6094     (lsp--apply-workspace-edit edit? 'code-action))
   6095 
   6096   (cond
   6097    ((stringp command?) (lsp--execute-command action))
   6098    ((lsp-command? command?) (progn
   6099                               (when-let* ((action-filter (->> (lsp-workspaces)
   6100                                                              (cl-first)
   6101                                                              (or lsp--cur-workspace)
   6102                                                              (lsp--workspace-client)
   6103                                                              (lsp--client-action-filter))))
   6104                                 (funcall action-filter command?))
   6105                               (lsp--execute-command command?)))))
   6106 
   6107 (lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments)
   6108   "Patch incorrect boolean argument values in the provided `CodeAction' command
   6109 in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values
   6110 in this list can be either symbols or lists of symbols that
   6111 represent paths to boolean arguments in code actions:
   6112 
   6113 > (lsp-fix-code-action-booleans command `(:foo :bar (:some :nested :boolean)))
   6114 
   6115 When there are available code actions, the server sends
   6116 `lsp-mode' a list of possible command names and arguments as
   6117 JSON. `lsp-mode' parses all boolean false values as `nil'. As a
   6118 result code action arguments containing falsy values don't
   6119 roundtrip correctly because `lsp-mode' will end up sending null
   6120 values back to the client. This list makes it possible to
   6121 selectively transform `nil' values back into `:json-false'."
   6122   (seq-doseq (path boolean-action-arguments)
   6123     (seq-doseq (args arguments?)
   6124       (lsp--fix-nested-boolean args (if (listp path) path (list path))))))
   6125 
   6126 (defun lsp--fix-nested-boolean (structure path)
   6127   "Traverse STRUCTURE using the paths from the PATH list, changing the value to
   6128 `:json-false' if it was `nil'. PATH should be a list containing
   6129 one or more symbols, and STRUCTURE should be compatible with
   6130 `lsp-member?', `lsp-get', and `lsp-put'."
   6131   (let ((key (car path))
   6132         (rest (cdr path)))
   6133     (if (null rest)
   6134         ;; `lsp-put' returns `nil' both when the key doesn't exist and when the
   6135         ;; value is `nil', so we need to explicitly check its presence here
   6136         (when (and (lsp-member? structure key) (not (lsp-get structure key)))
   6137           (lsp-put structure key :json-false))
   6138       ;; If `key' does not exist, then we'll silently ignore it
   6139       (when-let* ((child (lsp-get structure key)))
   6140         (lsp--fix-nested-boolean child rest)))))
   6141 
   6142 (defvar lsp--formatting-indent-alist
   6143   ;; Taken from `dtrt-indent-mode'
   6144   '(
   6145     (ada-mode                   . ada-indent)                       ; Ada
   6146     (ada-ts-mode                . ada-ts-mode-indent-offset)
   6147     (c++-mode                   . c-basic-offset)                   ; C++
   6148     (c++-ts-mode                . c-ts-mode-indent-offset)
   6149     (c-mode                     . c-basic-offset)                   ; C
   6150     (c-ts-mode                  . c-ts-mode-indent-offset)
   6151     (cperl-mode                 . cperl-indent-level)               ; Perl
   6152     (crystal-mode               . crystal-indent-level)             ; Crystal (Ruby)
   6153     (csharp-mode                . c-basic-offset)                   ; C#
   6154     (csharp-tree-sitter-mode    . csharp-tree-sitter-indent-offset) ; C#
   6155     (csharp-ts-mode             . csharp-ts-mode-indent-offset)     ; C# (tree-sitter, Emacs29)
   6156     (css-mode                   . css-indent-offset)                ; CSS
   6157     (d-mode                     . c-basic-offset)                   ; D
   6158     (enh-ruby-mode              . enh-ruby-indent-level)            ; Ruby
   6159     (erlang-mode                . erlang-indent-level)              ; Erlang
   6160     (ess-mode                   . ess-indent-offset)                ; ESS (R)
   6161     (go-ts-mode                 . go-ts-mode-indent-offset)
   6162     (gpr-mode                   . gpr-indent-offset)                ; GNAT Project
   6163     (gpr-ts-mode                . gpr-ts-mode-indent-offset)
   6164     (hack-mode                  . hack-indent-offset)               ; Hack
   6165     (java-mode                  . c-basic-offset)                   ; Java
   6166     (java-ts-mode               . java-ts-mode-indent-offset)
   6167     (jde-mode                   . c-basic-offset)                   ; Java (JDE)
   6168     (js-mode                    . js-indent-level)                  ; JavaScript
   6169     (js-ts-mode                 . js-indent-level)
   6170     (js2-mode                   . js2-basic-offset)                 ; JavaScript-IDE
   6171     (js3-mode                   . js3-indent-level)                 ; JavaScript-IDE
   6172     (json-mode                  . js-indent-level)                  ; JSON
   6173     (json-ts-mode               . json-ts-mode-indent-offset)
   6174     (lua-mode                   . lua-indent-level)                 ; Lua
   6175     (lua-ts-mode                . lua-ts-indent-offset)
   6176     (nxml-mode                  . nxml-child-indent)                ; XML
   6177     (objc-mode                  . c-basic-offset)                   ; Objective C
   6178     (pascal-mode                . pascal-indent-level)              ; Pascal
   6179     (perl-mode                  . perl-indent-level)                ; Perl
   6180     (php-mode                   . c-basic-offset)                   ; PHP
   6181     (php-ts-mode                . php-ts-mode-indent-offset)        ; PHP
   6182     (powershell-mode            . powershell-indent)                ; PowerShell
   6183     (powershell-ts-mode         . powershell-ts-mode-indent-offset) ; PowerShell
   6184     (raku-mode                  . raku-indent-offset)               ; Perl6/Raku
   6185     (ruby-mode                  . ruby-indent-level)                ; Ruby
   6186     (rust-mode                  . rust-indent-offset)               ; Rust
   6187     (rust-ts-mode               . rust-ts-mode-indent-offset)
   6188     (rustic-mode                . rustic-indent-offset)             ; Rust
   6189     (scala-mode                 . scala-indent:step)                ; Scala
   6190     (sgml-mode                  . sgml-basic-offset)                ; SGML
   6191     (sh-mode                    . sh-basic-offset)                  ; Shell Script
   6192     (toml-ts-mode               . toml-ts-mode-indent-offset)
   6193     (typescript-mode            . typescript-indent-level)          ; Typescript
   6194     (typescript-ts-mode         . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29)
   6195     (yaml-mode                  . yaml-indent-offset)               ; YAML
   6196     (yang-mode                  . c-basic-offset)                   ; YANG (yang-mode)
   6197 
   6198     (default                    . standard-indent))                 ; default fallback
   6199   "A mapping from `major-mode' to its indent variable.")
   6200 
   6201 (defun lsp--get-indent-width (mode)
   6202   "Get indentation offset for MODE."
   6203   (or (alist-get mode lsp--formatting-indent-alist)
   6204       (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default))))
   6205 
   6206 (defun lsp--make-document-formatting-params ()
   6207   "Create document formatting params."
   6208   (lsp-make-document-formatting-params
   6209    :text-document (lsp--text-document-identifier)
   6210    :options (lsp-make-formatting-options
   6211              :tab-size (symbol-value (lsp--get-indent-width major-mode))
   6212              :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   6213              :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   6214              :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   6215              :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))))
   6216 
   6217 (defun lsp-format-buffer ()
   6218   "Ask the server to format this document."
   6219   (interactive "*")
   6220   (cond ((lsp-feature? "textDocument/formatting")
   6221          (let ((edits (lsp-request "textDocument/formatting"
   6222                                    (lsp--make-document-formatting-params))))
   6223            (if (seq-empty-p edits)
   6224                (lsp--info "No formatting changes provided")
   6225              (lsp--apply-text-edits edits 'format))))
   6226         ((lsp-feature? "textDocument/rangeFormatting")
   6227          (save-restriction
   6228            (widen)
   6229            (lsp-format-region (point-min) (point-max))))
   6230         (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider")))))
   6231 
   6232 (defun lsp-format-region (s e)
   6233   "Ask the server to format the region, or if none is selected, the current line."
   6234   (interactive "r")
   6235   (let ((edits (lsp-request
   6236                 "textDocument/rangeFormatting"
   6237                 (lsp--make-document-range-formatting-params s e))))
   6238     (if (seq-empty-p edits)
   6239         (lsp--info "No formatting changes provided")
   6240       (lsp--apply-text-edits edits 'format))))
   6241 
   6242 (defmacro lsp-make-interactive-code-action (func-name code-action-kind)
   6243   "Define an interactive function FUNC-NAME that attempts to
   6244 execute a CODE-ACTION-KIND action."
   6245   `(defun ,(intern (concat "lsp-" (symbol-name func-name))) ()
   6246      ,(format "Perform the %s code action, if available." code-action-kind)
   6247      (interactive)
   6248      ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to
   6249      ;; auto-execute here: the user has specified exactly what they want.
   6250      (let ((lsp-auto-execute-action t))
   6251        (condition-case nil
   6252            (lsp-execute-code-action-by-kind ,code-action-kind)
   6253          (lsp-no-code-actions
   6254           (when (called-interactively-p 'any)
   6255             (lsp--info ,(format "%s action not available" code-action-kind))))))))
   6256 
   6257 (lsp-make-interactive-code-action organize-imports "source.organizeImports")
   6258 
   6259 (defun lsp--make-document-range-formatting-params (start end)
   6260   "Make DocumentRangeFormattingParams for selected region."
   6261   (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params)
   6262                                                   (lsp--region-to-range start end)))
   6263 
   6264 (defconst lsp--highlight-kind-face
   6265   '((1 . lsp-face-highlight-textual)
   6266     (2 . lsp-face-highlight-read)
   6267     (3 . lsp-face-highlight-write)))
   6268 
   6269 (defun lsp--remove-overlays (name)
   6270   (save-restriction
   6271     (widen)
   6272     (remove-overlays (point-min) (point-max) name t)))
   6273 
   6274 (defun lsp-document-highlight ()
   6275   "Highlight all relevant references to the symbol under point."
   6276   (interactive)
   6277   (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights
   6278   (setq lsp--have-document-highlights nil
   6279         lsp--symbol-bounds-of-last-highlight-invocation nil)
   6280   (let ((lsp-enable-symbol-highlighting t))
   6281     (lsp--document-highlight)))
   6282 
   6283 (defun lsp--document-highlight-callback (highlights)
   6284   "Create a callback to process the reply of a
   6285 `textDocument/documentHighlight' message for the buffer BUF.
   6286 A reference is highlighted only if it is visible in a window."
   6287   (lsp--remove-overlays 'lsp-highlight)
   6288 
   6289   (let* ((wins-visible-pos (-map (lambda (win)
   6290                                    (cons (1- (line-number-at-pos (window-start win) t))
   6291                                          (1+ (line-number-at-pos (window-end win) t))))
   6292                                  (get-buffer-window-list nil nil 'visible))))
   6293     (setq lsp--have-document-highlights t)
   6294     (-map
   6295      (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line)
   6296                                                   :end (end &as &Position :line end-line))
   6297                                    :kind?))
   6298        (-map
   6299         (-lambda ((start-window . end-window))
   6300           ;; Make the overlay only if the reference is visible
   6301           (when (and (> (1+ start-line) start-window)
   6302                      (< (1+ end-line) end-window))
   6303             (let ((start-point (lsp--position-to-point start))
   6304                   (end-point (lsp--position-to-point end)))
   6305               (when (not (and lsp-symbol-highlighting-skip-current
   6306                               (<= start-point (point) end-point)))
   6307                 (-doto (make-overlay start-point end-point)
   6308                   (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face)))
   6309                   (overlay-put 'lsp-highlight t))))))
   6310         wins-visible-pos))
   6311      highlights)))
   6312 
   6313 (defcustom lsp-symbol-kinds
   6314   '((1 . "File")
   6315     (2 . "Module")
   6316     (3 . "Namespace")
   6317     (4 . "Package")
   6318     (5 . "Class")
   6319     (6 . "Method")
   6320     (7 . "Property")
   6321     (8 . "Field")
   6322     (9 . "Constructor")
   6323     (10 . "Enum")
   6324     (11 . "Interface")
   6325     (12 . "Function")
   6326     (13 . "Variable")
   6327     (14 . "Constant")
   6328     (15 . "String")
   6329     (16 . "Number")
   6330     (17 . "Boolean")
   6331     (18 . "Array")
   6332     (19 . "Object")
   6333     (20 . "Key")
   6334     (21 . "Null")
   6335     (22 . "Enum Member")
   6336     (23 . "Struct")
   6337     (24 . "Event")
   6338     (25 . "Operator")
   6339     (26 . "Type Parameter"))
   6340   "Alist mapping SymbolKinds to human-readable strings.
   6341 Various Symbol objects in the LSP protocol have an integral type,
   6342 specifying what they are. This alist maps such type integrals to
   6343 readable representations of them. See
   6344 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/',
   6345 namespace SymbolKind."
   6346   :group 'lsp-mode
   6347   :type '(alist :key-type integer :value-type string))
   6348 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds)
   6349 
   6350 (lsp-defun lsp--symbol-information-to-xref
   6351   ((&SymbolInformation :kind :name
   6352                        :location (&Location :uri :range (&Range :start
   6353                                                                 (&Position :line :character)))))
   6354   "Return a `xref-item' from SYMBOL information."
   6355   (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name)
   6356              (xref-make-file-location (lsp--uri-to-path uri)
   6357                                       line
   6358                                       character)))
   6359 
   6360 (defun lsp--get-document-symbols ()
   6361   "Get document symbols.
   6362 
   6363 If the buffer has not been modified since symbols were last
   6364 retrieved, simply return the latest result.
   6365 
   6366 Else, if the request was initiated by Imenu updating its menu-bar
   6367 entry, perform it asynchronously; i.e., give Imenu the latest
   6368 result and then force a refresh when a new one is available.
   6369 
   6370 Else (e.g., due to interactive use of `imenu' or `xref'),
   6371 perform the request synchronously."
   6372   (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick)
   6373       lsp--document-symbols
   6374     (let ((method "textDocument/documentSymbol")
   6375           (params `(:textDocument ,(lsp--text-document-identifier)))
   6376           (tick (buffer-chars-modified-tick)))
   6377       (if (not lsp--document-symbols-request-async)
   6378           (prog1
   6379               (setq lsp--document-symbols (lsp-request method params))
   6380             (setq lsp--document-symbols-tick tick))
   6381         (lsp-request-async method params
   6382                            (lambda (document-symbols)
   6383                              (setq lsp--document-symbols document-symbols
   6384                                    lsp--document-symbols-tick tick)
   6385                              (lsp--imenu-refresh))
   6386                            :mode 'alive
   6387                            :cancel-token :document-symbols)
   6388         lsp--document-symbols))))
   6389 
   6390 (advice-add 'imenu-update-menubar :around
   6391             (lambda (oldfun &rest r)
   6392               (let ((lsp--document-symbols-request-async t))
   6393                 (apply oldfun r))))
   6394 
   6395 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position)
   6396   "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION."
   6397   (-let (((symbol &as &DocumentSymbol? :children?)
   6398           (seq-find (-lambda ((&DocumentSymbol :range))
   6399                       (lsp-point-in-range? current-position range))
   6400                     document-symbols)))
   6401     (if children?
   6402         (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position))
   6403       (when symbol
   6404         (list symbol)))))
   6405 
   6406 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?))
   6407   "Convert a SymbolInformation to a DocumentInformation"
   6408   (lsp-make-document-symbol :name name
   6409                             :kind kind
   6410                             :range (lsp:location-range location)
   6411                             :children? nil
   6412                             :deprecated? deprecated?
   6413                             :selection-range (lsp:location-range location)
   6414                             :detail? container-name?))
   6415 
   6416 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position)
   6417   "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION."
   6418   (--> symbols-informations
   6419     (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range)))
   6420              (when (lsp-point-in-range? current-position range)
   6421                (lsp--symbol-information->document-symbol symbol)))
   6422            it)
   6423     (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position))
   6424                        (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position)))
   6425                (and (lsp--position-compare b-start-position a-start-position)
   6426                     (lsp--position-compare a-end-position b-end-position))))))
   6427 
   6428 (defun lsp--symbols->document-symbols-hierarchy (symbols)
   6429   "Convert SYMBOLS to symbols-hierarchy."
   6430   (when-let* ((first-symbol (lsp-seq-first symbols)))
   6431     (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line)
   6432                                            :character (plist-get (lsp--cur-position) :character))))
   6433       (if (lsp-symbol-information? first-symbol)
   6434           (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position)
   6435         (lsp--document-symbols->document-symbols-hierarchy symbols cur-position)))))
   6436 
   6437 (defun lsp--xref-backend () 'xref-lsp)
   6438 
   6439 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp)))
   6440   (propertize (or (thing-at-point 'symbol) "")
   6441               'identifier-at-point t))
   6442 
   6443 (defun lsp--xref-elements-index (symbols path)
   6444   (-mapcat
   6445    (-lambda (sym)
   6446      (pcase-exhaustive sym
   6447        ((lsp-interface DocumentSymbol :name :children? :selection-range (lsp-interface Range :start))
   6448         (cons (cons (concat path name)
   6449                     (lsp--position-to-point start))
   6450               (lsp--xref-elements-index children? (concat path name " / "))))
   6451        ((lsp-interface SymbolInformation :name :location (lsp-interface Location :range (lsp-interface Range :start)))
   6452         (list (cons (concat path name)
   6453                     (lsp--position-to-point start))))))
   6454    symbols))
   6455 
   6456 (defvar-local lsp--symbols-cache nil)
   6457 
   6458 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp)))
   6459   (if (lsp--find-workspaces-for "textDocument/documentSymbol")
   6460       (progn
   6461         (setq lsp--symbols-cache (lsp--xref-elements-index
   6462                                   (lsp--get-document-symbols) nil))
   6463         lsp--symbols-cache)
   6464     (list (propertize (or (thing-at-point 'symbol) "")
   6465                       'identifier-at-point t))))
   6466 
   6467 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier)
   6468   (save-excursion
   6469     (unless (get-text-property 0 'identifier-at-point identifier)
   6470       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6471                               (user-error "Unable to find symbol %s in current document" identifier)))))
   6472     (lsp--locations-to-xref-items (lsp-request "textDocument/definition"
   6473                                                (lsp--text-document-position-params)))))
   6474 
   6475 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier)
   6476   (save-excursion
   6477     (unless (get-text-property 0 'identifier-at-point identifier)
   6478       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6479                               (user-error "Unable to find symbol %s" identifier)))))
   6480     (lsp--locations-to-xref-items (lsp-request "textDocument/references"
   6481                                                (lsp--make-reference-params nil lsp-references-exclude-declaration)))))
   6482 
   6483 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern)
   6484   (seq-map #'lsp--symbol-information-to-xref
   6485            (lsp-request "workspace/symbol" `(:query ,pattern))))
   6486 
   6487 (defcustom lsp-rename-use-prepare t
   6488   "Whether `lsp-rename' should do a prepareRename first.
   6489 For some language servers, textDocument/prepareRename might be
   6490 too slow, in which case this variable may be set to nil.
   6491 `lsp-rename' will then use `thing-at-point' `symbol' to determine
   6492 the symbol to rename at point."
   6493   :group 'lsp-mode
   6494   :type 'boolean)
   6495 
   6496 (defun lsp--get-symbol-to-rename ()
   6497   "Get a symbol to rename and placeholder at point.
   6498 Returns a cons ((START . END) . PLACEHOLDER?), and nil if
   6499 renaming is generally supported but cannot be done at point.
   6500 START and END are the bounds of the identifiers being renamed,
   6501 while PLACEHOLDER?, is either nil or a string suggested by the
   6502 language server as the initial input of a new-name prompt."
   6503   (unless (lsp-feature? "textDocument/rename")
   6504     (error "The connected server(s) doesn't support renaming"))
   6505   (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename"))
   6506       (when-let* ((response
   6507                   (lsp-request "textDocument/prepareRename"
   6508                                (lsp--text-document-position-params))))
   6509         (let* ((bounds (lsp--range-to-region
   6510                         (if (lsp-range? response)
   6511                             response
   6512                           (lsp:prepare-rename-result-range response))))
   6513                (placeholder
   6514                 (and (not (lsp-range? response))
   6515                      (lsp:prepare-rename-result-placeholder response))))
   6516           (cons bounds placeholder)))
   6517     (when-let* ((bounds (bounds-of-thing-at-point 'symbol)))
   6518       (cons bounds nil))))
   6519 
   6520 (defface lsp-face-rename '((t :underline t))
   6521   "Face used to highlight the identifier being renamed.
   6522 Renaming can be done using `lsp-rename'."
   6523   :group 'lsp-mode)
   6524 
   6525 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face))
   6526   "Face used to display the rename placeholder in.
   6527 When calling `lsp-rename' interactively, this will be the face of
   6528 the new name."
   6529   :group 'lsp-mode)
   6530 
   6531 (defvar lsp-rename-history '()
   6532   "History for `lsp--read-rename'.")
   6533 
   6534 (defun lsp--read-rename (at-point)
   6535   "Read a new name for a `lsp-rename' at `point' from the user.
   6536 AT-POINT shall be a structure as returned by
   6537 `lsp--get-symbol-to-rename'.
   6538 
   6539 Returns a string, which should be the new name for the identifier
   6540 at point. If renaming cannot be done at point (as determined from
   6541 AT-POINT), throw a `user-error'.
   6542 
   6543 This function is for use in `lsp-rename' only, and shall not be
   6544 relied upon."
   6545   (unless at-point
   6546     (user-error "`lsp-rename' is invalid here"))
   6547   (-let* ((((start . end) . placeholder?) at-point)
   6548           ;; Do the `buffer-substring' first to not include `lsp-face-rename'
   6549           (rename-me (buffer-substring start end))
   6550           (placeholder (or placeholder? rename-me))
   6551           (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face))
   6552 
   6553           overlay)
   6554     ;; We need unwind protect, as the user might cancel here, causing the
   6555     ;; overlay to linger.
   6556     (unwind-protect
   6557         (progn
   6558           (setq overlay (make-overlay start end))
   6559           (overlay-put overlay 'face 'lsp-face-rename)
   6560 
   6561           (read-string (format "Rename %s to: " rename-me) placeholder
   6562                        'lsp-rename-history))
   6563       (and overlay (delete-overlay overlay)))))
   6564 
   6565 (defun lsp-rename (newname)
   6566   "Rename the symbol (and all references to it) under point to NEWNAME."
   6567   (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename))))
   6568   (when-let* ((edits (lsp-request "textDocument/rename"
   6569                                  `( :textDocument ,(lsp--text-document-identifier)
   6570                                     :position ,(lsp--cur-position)
   6571                                     :newName ,newname))))
   6572     (lsp--apply-workspace-edit edits 'rename)))
   6573 
   6574 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?)
   6575   "Advice around function `rename-file'.
   6576 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?.
   6577 
   6578 This advice sends workspace/willRenameFiles before renaming file
   6579 to check if server wants to apply any workspaceEdits after renamed."
   6580   (if (and lsp-apply-edits-after-file-operations
   6581            (lsp--send-will-rename-files-p old-name))
   6582       (let ((params (lsp-make-rename-files-params
   6583                      :files (vector (lsp-make-file-rename
   6584                                      :oldUri (lsp--path-to-uri old-name)
   6585                                      :newUri (lsp--path-to-uri new-name))))))
   6586         (when-let* ((edits (lsp-request "workspace/willRenameFiles" params)))
   6587           (lsp--apply-workspace-edit edits 'rename-file)
   6588           (funcall old-func old-name new-name ok-if-already-exists?)
   6589           (when (lsp--send-did-rename-files-p)
   6590             (lsp-notify "workspace/didRenameFiles" params))))
   6591     (funcall old-func old-name new-name ok-if-already-exists?)))
   6592 
   6593 (advice-add 'rename-file :around #'lsp--on-rename-file)
   6594 
   6595 (defcustom lsp-xref-force-references nil
   6596   "If non-nil threat everything as references(e. g. jump if only one item.)"
   6597   :group 'lsp-mode
   6598   :type 'boolean)
   6599 
   6600 (defun lsp-show-xrefs (xrefs display-action references?)
   6601   (unless (region-active-p) (push-mark nil t))
   6602   (if (boundp 'xref-show-definitions-function)
   6603       (with-no-warnings
   6604         (xref-push-marker-stack)
   6605         (funcall (if (and references? (not lsp-xref-force-references))
   6606                      xref-show-xrefs-function
   6607                    xref-show-definitions-function)
   6608                  (-const xrefs)
   6609                  `((window . ,(selected-window))
   6610                    (display-action . ,display-action)
   6611                    ,(if (and references? (not lsp-xref-force-references))
   6612                         `(auto-jump . ,xref-auto-jump-to-first-xref)
   6613                       `(auto-jump . ,xref-auto-jump-to-first-definition)))))
   6614     (xref--show-xrefs xrefs display-action)))
   6615 
   6616 (cl-defmethod seq-empty-p ((ht hash-table))
   6617   "Function `seq-empty-p' for hash-table."
   6618   (hash-table-empty-p ht))
   6619 
   6620 (cl-defun lsp-find-locations (method &optional extra &key display-action references?)
   6621   "Send request named METHOD and get cross references of the symbol under point.
   6622 EXTRA is a plist of extra parameters.
   6623 REFERENCES? t when METHOD returns references."
   6624   (let ((loc (lsp-request method
   6625                           (append (lsp--text-document-position-params) extra))))
   6626     (if (seq-empty-p loc)
   6627         (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) ""))
   6628       (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?))))
   6629 
   6630 (cl-defun lsp-find-declaration (&key display-action)
   6631   "Find declarations of the symbol under point."
   6632   (interactive)
   6633   (lsp-find-locations "textDocument/declaration" nil :display-action display-action))
   6634 
   6635 (cl-defun lsp-find-definition (&key display-action)
   6636   "Find definitions of the symbol under point."
   6637   (interactive)
   6638   (lsp-find-locations "textDocument/definition" nil :display-action display-action))
   6639 
   6640 (defun lsp-find-definition-mouse (click)
   6641   "Click to start `lsp-find-definition' at clicked point."
   6642   (interactive "e")
   6643   (let* ((ec (event-start click))
   6644          (p1 (posn-point ec))
   6645          (w1 (posn-window ec)))
   6646     (select-window w1)
   6647     (goto-char p1)
   6648     (lsp-find-definition)))
   6649 
   6650 (cl-defun lsp-find-implementation (&key display-action)
   6651   "Find implementations of the symbol under point."
   6652   (interactive)
   6653   (lsp-find-locations "textDocument/implementation"
   6654                       nil
   6655                       :display-action display-action
   6656                       :references? t))
   6657 
   6658 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action)
   6659   "Find references of the symbol under point."
   6660   (interactive "P")
   6661   (lsp-find-locations "textDocument/references"
   6662                       (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-declaration)))))
   6663                       :display-action display-action
   6664                       :references? t))
   6665 
   6666 (cl-defun lsp-find-type-definition (&key display-action)
   6667   "Find type definitions of the symbol under point."
   6668   (interactive)
   6669   (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action))
   6670 
   6671 (defalias 'lsp-find-custom #'lsp-find-locations)
   6672 (defalias 'lsp-goto-implementation #'lsp-find-implementation)
   6673 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition)
   6674 
   6675 (with-eval-after-load 'evil
   6676   (evil-set-command-property 'lsp-find-definition :jump t)
   6677   (evil-set-command-property 'lsp-find-implementation :jump t)
   6678   (evil-set-command-property 'lsp-find-references :jump t)
   6679   (evil-set-command-property 'lsp-find-type-definition :jump t))
   6680 
   6681 (defun lsp--workspace-method-supported? (check-command method capability workspace)
   6682   (with-lsp-workspace workspace
   6683     (if check-command
   6684         (funcall check-command workspace)
   6685       (or
   6686        (when capability (lsp--capability capability))
   6687        (lsp--registered-capability method)
   6688        (and (not capability) (not check-command))))))
   6689 
   6690 (defun lsp-disable-method-for-server (method server-id)
   6691   "Disable METHOD for SERVER-ID."
   6692   (cl-callf
   6693       (lambda (reqs)
   6694         (-let (((&plist :check-command :capability) reqs))
   6695           (list :check-command
   6696                 (lambda (workspace)
   6697                   (unless (-> workspace
   6698                               lsp--workspace-client
   6699                               lsp--client-server-id
   6700                               (eq server-id))
   6701                     (lsp--workspace-method-supported? check-command
   6702                                                       method
   6703                                                       capability
   6704                                                       workspace))))))
   6705       (alist-get method lsp-method-requirements nil nil 'string=)))
   6706 
   6707 (defun lsp--find-workspaces-for (msg-or-method)
   6708   "Find all workspaces in the current project that can handle MSG."
   6709   (let ((method (if (stringp msg-or-method)
   6710                     msg-or-method
   6711                   (plist-get msg-or-method :method))))
   6712     (-if-let (reqs (cdr (assoc method lsp-method-requirements)))
   6713         (-let (((&plist :capability :check-command) reqs))
   6714           (-filter
   6715            (-partial #'lsp--workspace-method-supported?
   6716                      check-command method capability)
   6717            (lsp-workspaces)))
   6718       (lsp-workspaces))))
   6719 
   6720 (defun lsp-can-execute-command? (command-name)
   6721   "Returns non-nil if current language server(s) can execute COMMAND-NAME.
   6722 The command is executed via `workspace/executeCommand'"
   6723   (cl-position
   6724    command-name
   6725    (lsp:execute-command-options-commands
   6726     (lsp:server-capabilities-execute-command-provider?
   6727      (lsp--server-capabilities)))
   6728    :test #'equal))
   6729 
   6730 (defalias 'lsp-feature? 'lsp--find-workspaces-for)
   6731 
   6732 (cl-defmethod lsp-execute-command (_server _command _arguments)
   6733   "Dispatch COMMAND execution."
   6734   (signal 'cl-no-applicable-method nil))
   6735 
   6736 (defun lsp-workspace-command-execute (command &optional args)
   6737   "Execute workspace COMMAND with ARGS."
   6738   (condition-case-unless-debug err
   6739       (let ((params (if args
   6740                         (list :command command :arguments args)
   6741                       (list :command command))))
   6742         (lsp-request "workspace/executeCommand" params))
   6743     (error
   6744      (error "`workspace/executeCommand' with `%s' failed.\n\n%S"
   6745             command err))))
   6746 
   6747 (defun lsp-send-execute-command (command &optional args)
   6748   "Create and send a `workspace/executeCommand' message having command COMMAND
   6749 and optional ARGS."
   6750   (lsp-workspace-command-execute command args))
   6751 
   6752 (defalias 'lsp-point-to-position #'lsp--point-to-position)
   6753 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier)
   6754 (defalias 'lsp--send-execute-command #'lsp-send-execute-command)
   6755 (defalias 'lsp-on-open #'lsp--text-document-did-open)
   6756 (defalias 'lsp-on-save #'lsp--text-document-did-save)
   6757 
   6758 (defun lsp--set-configuration (settings)
   6759   "Set the SETTINGS for the lsp server."
   6760   (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings)))
   6761 
   6762 (defun lsp-current-buffer ()
   6763   (or lsp--virtual-buffer
   6764       (current-buffer)))
   6765 
   6766 (defun lsp-buffer-live-p (buffer-id)
   6767   (if-let* ((buffer-live (plist-get buffer-id :buffer-live?)))
   6768       (funcall buffer-live buffer-id)
   6769     (buffer-live-p buffer-id)))
   6770 
   6771 (defun lsp--on-set-visited-file-name (old-func &rest args)
   6772   "Advice around function `set-visited-file-name'.
   6773 
   6774 This advice sends textDocument/didClose for the old file and
   6775 textDocument/didOpen for the new file."
   6776   (when lsp--cur-workspace
   6777     (lsp--text-document-did-close t))
   6778   (prog1 (apply old-func args)
   6779     (when lsp--cur-workspace
   6780       (lsp--text-document-did-open))))
   6781 
   6782 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name)
   6783 
   6784 (defcustom lsp-flush-delayed-changes-before-next-message t
   6785   "If non-nil send the document changes update before sending other messages.
   6786 
   6787 If nil, and `lsp-debounce-full-sync-notifications' is non-nil,
   6788  change notifications will be throttled by
   6789  `lsp-debounce-full-sync-notifications-interval' regardless of
   6790  other messages."
   6791   :group 'lsp-mode
   6792   :type 'boolean)
   6793 
   6794 (defvar lsp--not-flushing-delayed-changes t)
   6795 
   6796 (defun lsp--send-no-wait (message proc)
   6797   "Send MESSAGE to PROC without waiting for further output."
   6798 
   6799   (when (and lsp--not-flushing-delayed-changes
   6800              lsp-flush-delayed-changes-before-next-message)
   6801     (let ((lsp--not-flushing-delayed-changes nil))
   6802       (lsp--flush-delayed-changes)))
   6803   (lsp-process-send proc message))
   6804 
   6805 (define-error 'lsp-parse-error
   6806   "Error parsing message from language server" 'lsp-error)
   6807 (define-error 'lsp-unknown-message-type
   6808   "Unknown message type" '(lsp-error lsp-parse-error))
   6809 (define-error 'lsp-unknown-json-rpc-version
   6810   "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error))
   6811 (define-error 'lsp-no-content-length
   6812   "Content-Length header missing in message" '(lsp-error lsp-parse-error))
   6813 (define-error 'lsp-invalid-header-name
   6814   "Invalid header name" '(lsp-error lsp-parse-error))
   6815 
   6816 ;;  id  method
   6817 ;;   x    x     request
   6818 ;;   x    .     response
   6819 ;;   .    x     notification
   6820 (defun lsp--get-message-type (json-data)
   6821   "Get the message type from JSON-DATA."
   6822   (if (lsp:json-message-id? json-data)
   6823       (if (lsp:json-message-error? json-data)
   6824           'response-error
   6825         (if (lsp:json-message-method? json-data)
   6826             'request
   6827           'response))
   6828     'notification))
   6829 
   6830 (defconst lsp--default-notification-handlers
   6831   (ht ("window/showMessage" #'lsp--window-show-message)
   6832       ("window/logMessage" #'lsp--window-log-message)
   6833       ("window/showInputBox" #'lsp--window-show-input-box)
   6834       ("window/showQuickPick" #'lsp--window-show-quick-pick)
   6835       ("textDocument/publishDiagnostics" #'lsp--on-diagnostics)
   6836       ("textDocument/diagnosticsEnd" #'ignore)
   6837       ("textDocument/diagnosticsBegin" #'ignore)
   6838       ("telemetry/event" #'ignore)
   6839       ("$/progress" (lambda (workspace params)
   6840                       (funcall lsp-progress-function workspace params)))))
   6841 
   6842 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method))
   6843   "Call the appropriate handler for NOTIFICATION."
   6844   (-let ((client (lsp--workspace-client workspace)))
   6845     (when (lsp--log-io-p method)
   6846       (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif)
   6847                           lsp--cur-workspace))
   6848     (if-let* ((handler (or (gethash method (lsp--client-notification-handlers client))
   6849                           (gethash method lsp--default-notification-handlers))))
   6850         (funcall handler workspace params)
   6851       (when (and method (not (string-prefix-p "$" method)))
   6852         (lsp-warn "Unknown notification: %s" method)))))
   6853 
   6854 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items))
   6855   "Get section configuration.
   6856 PARAMS are the `workspace/configuration' request params"
   6857   (->> items
   6858        (-map (-lambda ((&ConfigurationItem :section?))
   6859                (-let* ((path-parts (split-string section? "\\."))
   6860                        (path-without-last (s-join "." (-slice path-parts 0 -1)))
   6861                        (path-parts-len (length path-parts)))
   6862                  (cond
   6863                   ((<= path-parts-len 1)
   6864                    (ht-get (lsp-configuration-section section?)
   6865                            (car-safe path-parts)
   6866                            (ht-create)))
   6867                   ((> path-parts-len 1)
   6868                    (when-let* ((section (lsp-configuration-section path-without-last))
   6869                               (keys path-parts))
   6870                      (while (and keys section)
   6871                        (setf section (ht-get section (pop keys))))
   6872                      section))))))
   6873        (apply #'vector)))
   6874 
   6875 (defun lsp--ms-since (timestamp)
   6876   "Integer number of milliseconds since TIMESTAMP.  Fractions discarded."
   6877   (floor (* 1000 (float-time (time-since timestamp)))))
   6878 
   6879 (defun lsp--send-request-response (workspace recv-time request response)
   6880   "Send the RESPONSE for REQUEST in WORKSPACE and log if needed."
   6881   (-let* (((&JSONResponse :params :method :id) request)
   6882           (process (lsp--workspace-proc workspace))
   6883           (response (lsp--make-response id response))
   6884           (req-entry (and lsp-log-io
   6885                           (lsp--make-log-entry method id params 'incoming-req)))
   6886           (resp-entry (and lsp-log-io
   6887                            (lsp--make-log-entry method id response 'outgoing-resp
   6888                                                 (lsp--ms-since recv-time)))))
   6889     ;; Send response to the server.
   6890     (when (lsp--log-io-p method)
   6891       (lsp--log-entry-new req-entry workspace)
   6892       (lsp--log-entry-new resp-entry workspace))
   6893     (lsp--send-no-wait response process)))
   6894 
   6895 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method))
   6896   "Call the appropriate handler for REQUEST, and send the return value to the
   6897 server. WORKSPACE is the active workspace."
   6898   (-let* ((recv-time (current-time))
   6899           (client (lsp--workspace-client workspace))
   6900           (buffers (lsp--workspace-buffers workspace))
   6901           handler
   6902           (response (cond
   6903                      ((setq handler (gethash method (lsp--client-request-handlers client) nil))
   6904                       (funcall handler workspace params))
   6905                      ((setq handler (gethash method (lsp--client-async-request-handlers client) nil))
   6906                       (funcall handler workspace params
   6907                                (-partial #'lsp--send-request-response
   6908                                          workspace recv-time request))
   6909                       'delay-response)
   6910                      ((equal method "client/registerCapability")
   6911                       (mapc #'lsp--server-register-capability
   6912                             (lsp:registration-params-registrations params))
   6913                       (mapc (lambda (buf)
   6914                               (when (lsp-buffer-live-p buf)
   6915                                 (lsp-with-current-buffer buf
   6916                                   (lsp-unconfig-buffer)
   6917                                   (lsp-configure-buffer))))
   6918                             buffers)
   6919                       nil)
   6920                      ((equal method "window/showMessageRequest")
   6921                       (let ((choice (lsp--window-log-message-request params)))
   6922                         `(:title ,choice)))
   6923                      ((equal method "window/showDocument")
   6924                       (let ((success? (lsp--window-show-document params)))
   6925                         (lsp-make-show-document-result :success (or success?
   6926                                                                     :json-false))))
   6927                      ((equal method "client/unregisterCapability")
   6928                       (mapc #'lsp--server-unregister-capability
   6929                             (lsp:unregistration-params-unregisterations params))
   6930                       (mapc (lambda (buf)
   6931                               (when (lsp-buffer-live-p buf)
   6932                                 (lsp-with-current-buffer buf
   6933                                   (lsp-unconfig-buffer)
   6934                                   (lsp-configure-buffer))))
   6935                             buffers)
   6936                       nil)
   6937                      ((equal method "workspace/applyEdit")
   6938                       (list :applied (condition-case err
   6939                                          (prog1 t
   6940                                            (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested))
   6941                                        (error
   6942                                         (lsp--error "Failed to apply edits with message %s"
   6943                                                     (error-message-string err))
   6944                                         :json-false))))
   6945                      ((equal method "workspace/configuration")
   6946                       (with-lsp-workspace workspace
   6947                         (if-let* ((buf (car buffers)))
   6948                             (lsp-with-current-buffer buf
   6949                               (lsp--build-workspace-configuration-response params))
   6950                           (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace)
   6951                             (lsp--build-workspace-configuration-response params)))))
   6952                      ((equal method "workspace/workspaceFolders")
   6953                       (let ((folders (or (-> workspace
   6954                                              (lsp--workspace-client)
   6955                                              (lsp--client-server-id)
   6956                                              (gethash (lsp-session-server-id->folders (lsp-session))))
   6957                                          (lsp-session-folders (lsp-session)))))
   6958                         (->> folders
   6959                              (-distinct)
   6960                              (-map (lambda (folder)
   6961                                      (list :uri (lsp--path-to-uri folder))))
   6962                              (apply #'vector))))
   6963                      ((equal method "window/workDoneProgress/create")
   6964                       nil ;; no specific reply, no processing required
   6965                       )
   6966                      ((equal method "workspace/semanticTokens/refresh")
   6967                       (when (and lsp-semantic-tokens-enable
   6968                                  (fboundp 'lsp--semantic-tokens-on-refresh))
   6969                         (lsp--semantic-tokens-on-refresh workspace))
   6970                       nil)
   6971                      ((equal method "workspace/codeLens/refresh")
   6972                       (when (and lsp-lens-enable
   6973                                  (fboundp 'lsp--lens-on-refresh))
   6974                         (lsp--lens-on-refresh workspace))
   6975                       nil)
   6976                      ((equal method "workspace/diagnostic/refresh")
   6977                       nil)
   6978                      (t (lsp-warn "Unknown request method: %s" method) nil))))
   6979     ;; Send response to the server.
   6980     (unless (eq response 'delay-response)
   6981       (lsp--send-request-response workspace recv-time request response))))
   6982 
   6983 (lsp-defun lsp--error-string ((&JSONError :message :code))
   6984   "Format ERR as a user friendly string."
   6985   (format "Error from the Language Server: %s (%s)"
   6986           message
   6987           (or (car (alist-get code lsp--errors)) "Unknown error")))
   6988 
   6989 (defun lsp--get-body-length (headers)
   6990   (let ((content-length (cdr (assoc "Content-Length" headers))))
   6991     (if content-length
   6992         (string-to-number content-length)
   6993 
   6994       ;; This usually means either the server or our parser is
   6995       ;; screwed up with a previous Content-Length
   6996       (error "No Content-Length header"))))
   6997 
   6998 (defun lsp--parse-header (s)
   6999   "Parse string S as a LSP (KEY . VAL) header."
   7000   (let ((pos (string-match "\:" s))
   7001         key val)
   7002     (unless pos
   7003       (signal 'lsp-invalid-header-name (list s)))
   7004     (setq key (substring s 0 pos)
   7005           val (s-trim-left (substring s (+ 1 pos))))
   7006     (when (equal key "Content-Length")
   7007       (cl-assert (cl-loop for c across val
   7008                           when (or (> c ?9) (< c ?0)) return nil
   7009                           finally return t)
   7010                  nil (format "Invalid Content-Length value: %s" val)))
   7011     (cons key val)))
   7012 
   7013 (defmacro lsp--read-json (str)
   7014   "Read json string STR."
   7015   (if (progn
   7016         (require 'json)
   7017         (fboundp 'json-parse-string))
   7018       `(json-parse-string ,str
   7019                           :object-type (if lsp-use-plists
   7020                                            'plist
   7021                                          'hash-table)
   7022                           :null-object nil
   7023                           :false-object nil)
   7024     `(let ((json-array-type 'vector)
   7025            (json-object-type (if lsp-use-plists
   7026                                  'plist
   7027                                'hash-table))
   7028            (json-false nil))
   7029        (json-read-from-string ,str))))
   7030 
   7031 (defmacro lsp-json-read-buffer ()
   7032   "Read json from the current buffer."
   7033   (if (progn
   7034         (require 'json)
   7035         (fboundp 'json-parse-buffer))
   7036       `(json-parse-buffer :object-type (if lsp-use-plists
   7037                                            'plist
   7038                                          'hash-table)
   7039                           :null-object nil
   7040                           :false-object nil)
   7041     `(let ((json-array-type 'vector)
   7042            (json-object-type (if lsp-use-plists
   7043                                  'plist
   7044                                'hash-table))
   7045            (json-false nil))
   7046        (json-read))))
   7047 
   7048 (defun lsp--read-json-file (file-path)
   7049   "Read json file."
   7050   (-> file-path
   7051     (f-read-text)
   7052     (lsp--read-json)))
   7053 
   7054 (defun lsp--parser-on-message (json-data workspace)
   7055   "Called when the parser P read a complete MSG from the server."
   7056   (with-demoted-errors "Error processing message %S."
   7057     (with-lsp-workspace workspace
   7058       (let* ((client (lsp--workspace-client workspace))
   7059              (id (--when-let (lsp:json-response-id json-data)
   7060                    (if (stringp it) (string-to-number it) it)))
   7061              (data (lsp:json-response-result json-data)))
   7062         (pcase (lsp--get-message-type json-data)
   7063           ('response
   7064            (cl-assert id)
   7065            (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))]
   7066              (when (lsp--log-io-p method)
   7067                (lsp--log-entry-new
   7068                 (lsp--make-log-entry method id data 'incoming-resp
   7069                                      (lsp--ms-since before-send))
   7070                 workspace))
   7071              (when callback
   7072                (remhash id (lsp--client-response-handlers client))
   7073                (funcall callback (lsp:json-response-result json-data)))))
   7074           ('response-error
   7075            (cl-assert id)
   7076            (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))]
   7077              (when (lsp--log-io-p method)
   7078                (lsp--log-entry-new
   7079                 (lsp--make-log-entry method id (lsp:json-response-error-error json-data)
   7080                                      'incoming-resp (lsp--ms-since before-send))
   7081                 workspace))
   7082              (when callback
   7083                (remhash id (lsp--client-response-handlers client))
   7084                (funcall callback (lsp:json-response-error-error json-data)))))
   7085           ('notification
   7086            (lsp--on-notification workspace json-data))
   7087           ('request (lsp--on-request workspace json-data)))))))
   7088 
   7089 (defun lsp--create-filter-function (workspace)
   7090   "Make filter for the workspace."
   7091   (let ((body-received 0)
   7092         leftovers body-length body chunk)
   7093     (lambda (_proc input)
   7094       (setf chunk (if (s-blank? leftovers)
   7095                       (encode-coding-string input 'utf-8-unix t)
   7096                     (concat leftovers (encode-coding-string input 'utf-8-unix t))))
   7097 
   7098       (let (messages)
   7099         (while (not (s-blank? chunk))
   7100           (if (not body-length)
   7101               ;; Read headers
   7102               (if-let* ((body-sep-pos (string-match-p "\r\n\r\n" chunk)))
   7103                   ;; We've got all the headers, handle them all at once:
   7104                   (setf body-length (lsp--get-body-length
   7105                                      (mapcar #'lsp--parse-header
   7106                                              (split-string
   7107                                               (substring-no-properties chunk
   7108                                                                        (or (string-match-p "Content-Length" chunk)
   7109                                                                            (error "Unable to find Content-Length header."))
   7110                                                                        body-sep-pos)
   7111                                               "\r\n")))
   7112                         body-received 0
   7113                         leftovers nil
   7114                         chunk (substring-no-properties chunk (+ body-sep-pos 4)))
   7115 
   7116                 ;; Haven't found the end of the headers yet. Save everything
   7117                 ;; for when the next chunk arrives and await further input.
   7118                 (setf leftovers chunk
   7119                       chunk nil))
   7120             (let* ((chunk-length (string-bytes chunk))
   7121                    (left-to-receive (- body-length body-received))
   7122                    (this-body (if (< left-to-receive chunk-length)
   7123                                   (prog1 (substring-no-properties chunk 0 left-to-receive)
   7124                                     (setf chunk (substring-no-properties chunk left-to-receive)))
   7125                                 (prog1 chunk
   7126                                   (setf chunk nil))))
   7127                    (body-bytes (string-bytes this-body)))
   7128               (push this-body body)
   7129               (setf body-received (+ body-received body-bytes))
   7130               (when (>= chunk-length left-to-receive)
   7131                 (condition-case err
   7132                     (with-temp-buffer
   7133                       (apply #'insert
   7134                              (nreverse
   7135                               (prog1 body
   7136                                 (setf leftovers nil
   7137                                       body-length nil
   7138                                       body-received nil
   7139                                       body nil))))
   7140                       (decode-coding-region (point-min)
   7141                                             (point-max)
   7142                                             'utf-8)
   7143                       (goto-char (point-min))
   7144                       (push (lsp-json-read-buffer) messages))
   7145 
   7146                   (error
   7147                    (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s"
   7148                              (concat leftovers input)
   7149                              err)))))))
   7150         (mapc (lambda (msg)
   7151                 (lsp--parser-on-message msg workspace))
   7152               (nreverse messages))))))
   7153 
   7154 (defvar-local lsp--line-col-to-point-hash-table nil
   7155   "Hash table with keys (line . col) and values that are either point positions
   7156 or markers.")
   7157 
   7158 (defcustom lsp-imenu-detailed-outline t
   7159   "Whether `lsp-imenu' should include signatures.
   7160 This will be ignored if the server doesn't provide the necessary
   7161 information, for example if it doesn't support DocumentSymbols."
   7162   :group 'lsp-imenu
   7163   :type 'boolean)
   7164 
   7165 (defcustom lsp-imenu-hide-parent-details t
   7166   "Whether `lsp-imenu' should hide signatures of parent nodes."
   7167   :group 'lsp-imenu
   7168   :type 'boolean)
   7169 
   7170 (defface lsp-details-face '((t :height 0.8 :inherit shadow))
   7171   "Used to display additional information throughout `lsp'.
   7172 Things like line numbers, signatures, ... are considered
   7173 additional information. Often, additional faces are defined that
   7174 inherit from this face by default, like `lsp-signature-face', and
   7175 they may be customized for finer control."
   7176   :group 'lsp-mode)
   7177 
   7178 (defface lsp-signature-face '((t :inherit lsp-details-face))
   7179   "Used to display signatures in `imenu', ...."
   7180   :group 'lsp-mode)
   7181 
   7182 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?)
   7183                               show-detail?)
   7184   "Render INPUT0, an `&DocumentSymbol', to a string.
   7185 If SHOW-DETAIL? is set, make use of its `:detail?' field (often
   7186 the signature)."
   7187   (let ((detail (and show-detail? (s-present? detail?)
   7188                      (propertize (concat " " (s-trim-left detail?))
   7189                                  'face 'lsp-signature-face)))
   7190         (name (if deprecated?
   7191                   (propertize name 'face 'lsp-face-semhl-deprecated) name)))
   7192     (concat name detail)))
   7193 
   7194 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?)
   7195                                           separator)
   7196   "Render a piece of SymbolInformation.
   7197 Handle :deprecated?. If SEPARATOR is non-nil, the
   7198 symbol's (optional) parent, SEPARATOR and the symbol itself are
   7199 concatenated."
   7200   (when (and separator container-name? (not (string-empty-p container-name?)))
   7201     (setq name (concat name separator container-name?)))
   7202   (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name))
   7203 
   7204 (defun lsp--symbol-to-imenu-elem (sym)
   7205   "Convert SYM to imenu element.
   7206 
   7207 SYM is a SymbolInformation message.
   7208 
   7209 Return a cons cell (full-name . start-point)."
   7210   (let ((start-point (ht-get lsp--line-col-to-point-hash-table
   7211                              (lsp--get-line-and-col sym))))
   7212     (cons (lsp-render-symbol-information
   7213            sym (and lsp-imenu-show-container-name
   7214                     lsp-imenu-container-name-separator))
   7215           start-point)))
   7216 
   7217 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?))
   7218   "Convert SYM to hierarchical imenu elements.
   7219 
   7220 SYM is a DocumentSymbol message.
   7221 
   7222 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if
   7223 SYM doesn't have any children. Otherwise return a cons cell with
   7224 an alist
   7225 
   7226   (\"symbol-name\" . ((\"(symbol-kind)\" . start-point)
   7227                     cons-cells-from-children))"
   7228   (let ((filtered-children (lsp--imenu-filter-symbols children?))
   7229         (signature (lsp-render-symbol sym lsp-imenu-detailed-outline)))
   7230     (if (seq-empty-p filtered-children)
   7231         (cons signature
   7232               (ht-get lsp--line-col-to-point-hash-table
   7233                       (lsp--get-line-and-col sym)))
   7234       (cons signature
   7235             (lsp--imenu-create-hierarchical-index filtered-children)))))
   7236 
   7237 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind))
   7238   "Determine if SYM is for the current document and is to be shown."
   7239   ;; It's a SymbolInformation or DocumentSymbol, which is always in the
   7240   ;; current buffer file.
   7241   (and lsp-imenu-index-symbol-kinds
   7242        (numberp kind)
   7243        (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup))
   7244                                kind
   7245                              0)))
   7246          (not (memql (aref lsp/symbol-kind-lookup clamped-kind)
   7247                      lsp-imenu-index-symbol-kinds)))))
   7248 
   7249 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind))
   7250   "The string name of the kind of SYM."
   7251   (alist-get kind lsp-symbol-kinds "Other"))
   7252 
   7253 (defun lsp--get-line-and-col (sym)
   7254   "Obtain the line and column corresponding to SYM."
   7255   (-let* ((location (lsp:symbol-information-location sym))
   7256           (name-range (or (and location (lsp:location-range location))
   7257                           (lsp:document-symbol-selection-range sym)))
   7258           ((&Range :start (&Position :line :character)) name-range))
   7259     (cons line character)))
   7260 
   7261 (defun lsp--collect-lines-and-cols (symbols)
   7262   "Return a sorted list ((line . col) ...) of the locations of SYMBOLS."
   7263   (let ((stack (mapcar 'identity symbols))
   7264         line-col-list)
   7265     (while stack
   7266       (let ((sym (pop stack)))
   7267         (push (lsp--get-line-and-col sym) line-col-list)
   7268         (unless (seq-empty-p (lsp:document-symbol-children? sym))
   7269           (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack)))))
   7270     (-sort #'lsp--line-col-comparator line-col-list)))
   7271 
   7272 (defun lsp--convert-line-col-to-points-batch (line-col-list)
   7273   "Convert a sorted list of positions from line-column
   7274 representation to point representation."
   7275   (let ((line-col-to-point-map (ht-create))
   7276         (inhibit-field-text-motion t)
   7277         (curr-line 0))
   7278     (lsp-save-restriction-and-excursion
   7279       (goto-char (point-min))
   7280       (cl-loop for (line . col) in line-col-list do
   7281                (forward-line (- line curr-line))
   7282                (setq curr-line line)
   7283                (let ((line-end (line-end-position)))
   7284                  (if (or (not col) (> col (- line-end (point))))
   7285                      (goto-char line-end)
   7286                    (forward-char col)))
   7287                (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers
   7288                                                                   (point-marker)
   7289                                                                 (point)))))
   7290     line-col-to-point-map))
   7291 
   7292 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2))
   7293   (or (< l1 l2)
   7294       (and (= l1 l2)
   7295            (cond ((and c1 c2)
   7296                   (< c1 c2))
   7297                  (c1 t)))))
   7298 
   7299 (defun lsp-imenu-create-uncategorized-index (symbols)
   7300   "Create imenu index from document SYMBOLS.
   7301 This function, unlike `lsp-imenu-create-categorized-index', does
   7302 not categorize by type, but instead returns an `imenu' index
   7303 corresponding to the symbol hierarchy returned by the server
   7304 directly."
   7305   (let* ((lsp--line-col-to-point-hash-table (-> symbols
   7306                                                 lsp--collect-lines-and-cols
   7307                                                 lsp--convert-line-col-to-points-batch)))
   7308     (if (lsp--imenu-hierarchical-p symbols)
   7309         (lsp--imenu-create-hierarchical-index symbols)
   7310       (lsp--imenu-create-non-hierarchical-index symbols))))
   7311 
   7312 (defcustom lsp-imenu-symbol-kinds
   7313   '((1 . "Files")
   7314     (2 . "Modules")
   7315     (3 . "Namespaces")
   7316     (4 . "Packages")
   7317     (5 . "Classes")
   7318     (6 . "Methods")
   7319     (7 . "Properties")
   7320     (8 . "Fields")
   7321     (9 . "Constructors")
   7322     (10 . "Enums")
   7323     (11 . "Interfaces")
   7324     (12 . "Functions")
   7325     (13 . "Variables")
   7326     (14 . "Constants")
   7327     (15 . "Strings")
   7328     (16 . "Numbers")
   7329     (17 . "Booleans")
   7330     (18 . "Arrays")
   7331     (19 . "Objects")
   7332     (20 . "Keys")
   7333     (21 . "Nulls")
   7334     (22 . "Enum Members")
   7335     (23 . "Structs")
   7336     (24 . "Events")
   7337     (25 . "Operators")
   7338     (26 . "Type Parameters"))
   7339   "`lsp-symbol-kinds', but only used by `imenu'.
   7340 A new variable is needed, as it is `imenu' convention to use
   7341 pluralized categories, which `lsp-symbol-kinds' doesn't. If the
   7342 non-pluralized names are preferred, this can be set to
   7343 `lsp-symbol-kinds'."
   7344   :type '(alist :key-type integer :value-type string))
   7345 
   7346 (defun lsp--imenu-kind->name (kind)
   7347   (alist-get kind lsp-imenu-symbol-kinds "?"))
   7348 
   7349 (defun lsp-imenu-create-top-level-categorized-index (symbols)
   7350   "Create an `imenu' index categorizing SYMBOLS by type.
   7351 Only root symbols are categorized.
   7352 
   7353 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS
   7354 shall be a list of DocumentSymbols or SymbolInformation."
   7355   (mapcan
   7356    (-lambda ((type . symbols))
   7357      (let ((cat (lsp--imenu-kind->name type))
   7358            (symbols (lsp-imenu-create-uncategorized-index symbols)))
   7359        ;; If there is no :kind (this is being defensive), or we couldn't look it
   7360        ;; up, just display the symbols inline, without categories.
   7361        (if cat (list (cons cat symbols)) symbols)))
   7362    (sort (seq-group-by #'lsp:document-symbol-kind symbols)
   7363          (-lambda ((kinda) (kindb)) (< kinda kindb)))))
   7364 
   7365 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start)))
   7366   "Convert an `&DocumentSymbol' to an `imenu' entry."
   7367   (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start))
   7368 
   7369 (defun lsp--imenu-create-categorized-index-1 (symbols)
   7370   "Returns an `imenu' index from SYMBOLS categorized by type.
   7371 The result looks like this: ((\"Variables\" . (...)))."
   7372   (->>
   7373    symbols
   7374    (mapcan
   7375     (-lambda ((sym &as &DocumentSymbol :kind :children?))
   7376       (if (seq-empty-p children?)
   7377           (list (list kind (lsp--symbol->imenu sym)))
   7378         (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline
   7379                                                   (not lsp-imenu-hide-parent-details)))))
   7380           (cons
   7381            (list kind (lsp--symbol->imenu sym))
   7382            (mapcar (-lambda ((type .  imenu-items))
   7383                      (list type (cons parent (mapcan #'cdr imenu-items))))
   7384                    (-group-by #'car (lsp--imenu-create-categorized-index-1 children?))))))))
   7385    (-group-by #'car)
   7386    (mapcar
   7387     (-lambda ((kind . syms))
   7388       (cons kind (mapcan #'cdr syms))))))
   7389 
   7390 (defun lsp--imenu-create-categorized-index (symbols)
   7391   (let ((syms (lsp--imenu-create-categorized-index-1 symbols)))
   7392     (dolist (sym syms)
   7393       (setcar sym (lsp--imenu-kind->name (car sym))))
   7394     syms))
   7395 
   7396 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start))))
   7397   (cons (lsp-render-symbol-information sym nil) start))
   7398 
   7399 (defun lsp--imenu-create-categorized-index-flat (symbols)
   7400   "Create a kind-categorized index for SymbolInformation."
   7401   (mapcar (-lambda ((kind . syms))
   7402             (cons (lsp--imenu-kind->name kind)
   7403                   (mapcan (-lambda ((parent . children))
   7404                             (let ((children (mapcar #'lsp--symbol-information->imenu children)))
   7405                               (if parent (list (cons parent children)) children)))
   7406                           (-group-by #'lsp:symbol-information-container-name? syms))))
   7407           (seq-group-by #'lsp:symbol-information-kind symbols)))
   7408 
   7409 (defun lsp-imenu-create-categorized-index (symbols)
   7410   (if (lsp--imenu-hierarchical-p symbols)
   7411       (lsp--imenu-create-categorized-index symbols)
   7412     (lsp--imenu-create-categorized-index-flat symbols)))
   7413 
   7414 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index
   7415   "Function that should create an `imenu' index.
   7416 It will be called with a list of SymbolInformation or
   7417 DocumentSymbols, whose first level is already filtered. It shall
   7418 then return an appropriate `imenu' index (see
   7419 `imenu-create-index-function').
   7420 
   7421 Note that this interface is not stable, and subject to change any
   7422 time."
   7423   :group 'lsp-imenu
   7424   :type '(radio
   7425           (const :tag "Categorize by type"
   7426                  lsp-imenu-create-categorized-index)
   7427           (const :tag "Categorize root symbols by type"
   7428                  lsp-imenu-create-top-level-categorized-index)
   7429           (const :tag "Uncategorized, inline entries"
   7430                  lsp-imenu-create-uncategorized-index)
   7431           (function :tag "Custom function")))
   7432 
   7433 (defun lsp--imenu-create-index ()
   7434   "Create an `imenu' index based on the language server.
   7435 Respects `lsp-imenu-index-function'."
   7436   (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols))))
   7437     (funcall lsp-imenu-index-function symbols)))
   7438 
   7439 (defun lsp--imenu-filter-symbols (symbols)
   7440   "Filter out unsupported symbols from SYMBOLS."
   7441   (seq-remove #'lsp--symbol-ignore symbols))
   7442 
   7443 (defun lsp--imenu-hierarchical-p (symbols)
   7444   "Determine whether any element in SYMBOLS has children."
   7445   (seq-some #'lsp-document-symbol? symbols))
   7446 
   7447 (defun lsp--imenu-create-non-hierarchical-index (symbols)
   7448   "Create imenu index for non-hierarchical SYMBOLS.
   7449 
   7450 SYMBOLS are a list of DocumentSymbol messages.
   7451 
   7452 Return a nested alist keyed by symbol names. e.g.
   7453 
   7454    ((\"SomeClass\" (\"(Class)\" . 10)
   7455                  (\"someField (Field)\" . 20)
   7456                  (\"someFunction (Function)\" . 25)
   7457                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7458                                   (\"someSubField (Field)\" . 35))
   7459     (\"someFunction (Function)\" . 40))"
   7460   (seq-map (lambda (nested-alist)
   7461              (cons (car nested-alist)
   7462                    (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist))))
   7463            (seq-group-by #'lsp--get-symbol-type symbols)))
   7464 
   7465 (defun lsp--imenu-create-hierarchical-index (symbols)
   7466   "Create imenu index for hierarchical SYMBOLS.
   7467 
   7468 SYMBOLS are a list of DocumentSymbol messages.
   7469 
   7470 Return a nested alist keyed by symbol names. e.g.
   7471 
   7472    ((\"SomeClass\" (\"(Class)\" . 10)
   7473                  (\"someField (Field)\" . 20)
   7474                  (\"someFunction (Function)\" . 25)
   7475                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7476                                   (\"someSubField (Field)\" . 35))
   7477     (\"someFunction (Function)\" . 40))"
   7478   (seq-map #'lsp--symbol-to-hierarchical-imenu-elem
   7479            (seq-sort #'lsp--imenu-symbol-lessp symbols)))
   7480 
   7481 (defun lsp--imenu-symbol-lessp (sym1 sym2)
   7482   (let* ((compare-results (mapcar (lambda (method)
   7483                                     (funcall (alist-get method lsp--imenu-compare-function-alist)
   7484                                              sym1 sym2))
   7485                                   lsp-imenu-sort-methods))
   7486          (result (seq-find (lambda (result)
   7487                              (not (= result 0)))
   7488                            compare-results
   7489                            0)))
   7490     (and (numberp result) (< result 0))))
   7491 
   7492 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left)
   7493                                     (&SymbolInformation :kind right))
   7494   "Compare SYM1 and SYM2 by kind."
   7495   (- left right))
   7496 
   7497 (defun lsp--imenu-compare-line-col (sym1 sym2)
   7498   (if (lsp--line-col-comparator
   7499        (lsp--get-line-and-col sym1)
   7500        (lsp--get-line-and-col sym2))
   7501       -1
   7502     1))
   7503 
   7504 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1)
   7505                                     (&SymbolInformation :name name2))
   7506   "Compare SYM1 and SYM2 by name."
   7507   (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2))))
   7508     (if (numberp result) result 0)))
   7509 
   7510 (defun lsp--imenu-refresh ()
   7511   "Force Imenu to refresh itself."
   7512   (imenu--menubar-select imenu--rescan-item))
   7513 
   7514 (defun lsp-enable-imenu ()
   7515   "Use lsp-imenu for the current buffer."
   7516   (imenu--cleanup)
   7517   (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   7518   (setq-local imenu-menubar-modified-tick -1)
   7519   (setq-local imenu--index-alist nil)
   7520   (when menu-bar-mode
   7521     (lsp--imenu-refresh)))
   7522 
   7523 (defun lsp-resolve-final-command (command &optional test?)
   7524   "Resolve final function COMMAND."
   7525   (let* ((command (lsp-resolve-value command))
   7526          (command (cl-etypecase command
   7527                     (list
   7528                      (cl-assert (seq-every-p (apply-partially #'stringp) command) nil
   7529                                 "Invalid command list")
   7530                      command)
   7531                     (string (list command)))))
   7532     (if (and (file-remote-p default-directory) (not test?))
   7533         (list shell-file-name "-c"
   7534               (string-join (cons "stty raw > /dev/null;"
   7535                                  (mapcar #'shell-quote-argument command))
   7536                            " "))
   7537       command)))
   7538 
   7539 (defun lsp-server-present? (final-command)
   7540   "Check whether FINAL-COMMAND is present."
   7541   (let ((binary-found? (executable-find (cl-first final-command) t)))
   7542     (if binary-found?
   7543         (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command))
   7544       (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command)))
   7545     binary-found?))
   7546 
   7547 (defun lsp--value-to-string (value)
   7548   "Convert VALUE to a string that can be set as value in an environment
   7549 variable."
   7550   (cond
   7551    ((stringp value) value)
   7552    ((booleanp value) (if value
   7553                          "1"
   7554                        "0"))
   7555    ((and (sequencep value)
   7556          (seq-every-p #'stringp value)) (string-join value ":"))
   7557    (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables"))))
   7558 
   7559 (defun lsp--compute-process-environment (environment-fn)
   7560   "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'.
   7561 Ignore non-boolean keys whose value is nil."
   7562   (let ((environment (if environment-fn
   7563                          (funcall environment-fn)
   7564                        nil)))
   7565     (-flatten (cons (cl-loop for (key . value) in environment
   7566                              if (or (eval value)
   7567                                     (eq (get value 'custom-type) 'boolean))
   7568                              collect (concat key "=" (lsp--value-to-string
   7569                                                       (eval value))))
   7570                     process-environment))))
   7571 
   7572 (defun lsp--default-directory-for-connection (&optional path)
   7573   "Return path to be used for the working directory of a LSP process.
   7574 
   7575 If `lsp-use-workspace-root-for-server-default-directory' is
   7576 non-nil, uses `lsp-workspace-root' to find the directory
   7577 corresponding to PATH, else returns `default-directory'."
   7578   (if lsp-use-workspace-root-for-server-default-directory
   7579       (lsp-workspace-root path)
   7580     default-directory))
   7581 
   7582 (defun lsp--fix-remote-cmd (program)
   7583   "Helper for `lsp-stdio-connection'.
   7584 Originally coppied from eglot."
   7585 
   7586   (if (file-remote-p default-directory)
   7587       (list shell-file-name "-c"
   7588             (string-join (cons "stty raw > /dev/null;"
   7589                                (mapcar #'shell-quote-argument program))
   7590                          " "))
   7591     program))
   7592 
   7593 (defvar tramp-use-ssh-controlmaster-options)
   7594 (defvar tramp-ssh-controlmaster-options)
   7595 
   7596 (defun lsp-stdio-connection (command &optional test-command)
   7597   "Returns a connection property list using COMMAND.
   7598 COMMAND can be: A string, denoting the command to launch the
   7599 language server. A list of strings, denoting an executable with
   7600 its command line arguments. A function, that either returns a
   7601 string or a list of strings. In all cases, the launched language
   7602 server should send and receive messages on standard I/O.
   7603 TEST-COMMAND is a function with no arguments which returns
   7604 whether the command is present or not. When not specified
   7605 `lsp-mode' will check whether the first element of the list
   7606 returned by COMMAND is available via `executable-find'"
   7607   (cl-check-type command (or string
   7608                              function
   7609                              (and list
   7610                                   (satisfies (lambda (l)
   7611                                                (seq-every-p (lambda (el)
   7612                                                               (stringp el))
   7613                                                             l))))))
   7614   (list :connect (lambda (filter sentinel name environment-fn workspace)
   7615                    (if (and (functionp 'json-rpc-connection)
   7616                             (not (file-remote-p default-directory)))
   7617                        (lsp-json-rpc-connection workspace (lsp-resolve-final-command command))
   7618                      (let ((final-command (lsp-resolve-final-command command))
   7619                            (process-name (generate-new-buffer-name name))
   7620                            (process-environment
   7621                             (lsp--compute-process-environment environment-fn)))
   7622                        (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name)))
   7623                               (default-directory (lsp--default-directory-for-connection))
   7624                               (tramp-use-ssh-controlmaster-options 'suppress)
   7625                               (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none")
   7626                               (proc (make-process
   7627                                      :name process-name
   7628                                      :connection-type 'pipe
   7629                                      :buffer (format "*%s*" process-name)
   7630                                      :coding 'no-conversion
   7631                                      :command final-command
   7632                                      :filter filter
   7633                                      :sentinel sentinel
   7634                                      :stderr stderr-buf
   7635                                      :noquery t
   7636                                      :file-handler t)))
   7637                          (set-process-query-on-exit-flag proc nil)
   7638                          (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil)
   7639                          (with-current-buffer (get-buffer stderr-buf)
   7640                            ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc.
   7641                            (special-mode))
   7642                          (cons proc proc)))))
   7643         :test? (or
   7644                 test-command
   7645                 (lambda ()
   7646                   (lsp-server-present? (lsp-resolve-final-command command t))))))
   7647 
   7648 (defun lsp--open-network-stream (host port name)
   7649   "Open network stream to HOST:PORT.
   7650   NAME will be passed to `open-network-stream'.
   7651   RETRY-COUNT is the number of the retries.
   7652   SLEEP-INTERVAL is the sleep interval between each retry."
   7653   (let* ((retries 0)
   7654          (sleep-interval 0.01)
   7655          (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval))
   7656          connection)
   7657     (while (and (not connection) (< retries number-of-retries))
   7658       (condition-case err
   7659           (setq connection (open-network-stream name nil host port
   7660                                                 :type 'plain
   7661                                                 :coding 'no-conversion))
   7662         (file-error
   7663          (let ((inhibit-message t))
   7664            (lsp--warn "Failed to connect to %s:%s with error message %s"
   7665                       host
   7666                       port
   7667                       (error-message-string err))
   7668            (sleep-for sleep-interval)
   7669            (cl-incf retries)))))
   7670     (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port))))
   7671 
   7672 (defun lsp--port-available (host port)
   7673   "Return non-nil if HOST and PORT are available."
   7674   (condition-case _err
   7675       (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain))
   7676     (file-error t)))
   7677 
   7678 (defun lsp--find-available-port (host starting-port)
   7679   "Find available port on HOST starting from STARTING-PORT."
   7680   (let ((port starting-port))
   7681     (while (not (lsp--port-available host port))
   7682       (cl-incf port))
   7683     port))
   7684 
   7685 (defun lsp-tcp-connection (command-fn)
   7686   "Returns a connection property list similar to `lsp-stdio-connection'.
   7687 COMMAND-FN can only be a function that takes a single argument, a
   7688 port number. It should return a command for launches a language server
   7689 process listening for TCP connections on the provided port."
   7690   (cl-check-type command-fn function)
   7691   (list
   7692    :connect (lambda (filter sentinel name environment-fn _workspace)
   7693               (let* ((host "localhost")
   7694                      (port (lsp--find-available-port host (cl-incf lsp--tcp-port)))
   7695                      (command (funcall command-fn port))
   7696                      (final-command (if (consp command) command (list command)))
   7697                      (_ (unless (lsp-server-present? final-command)
   7698                           (user-error (format "Couldn't find executable %s" (cl-first final-command)))))
   7699                      (process-environment
   7700                       (lsp--compute-process-environment environment-fn))
   7701                      (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion
   7702                                          :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t))
   7703                      (tcp-proc (lsp--open-network-stream host port (concat name "::tcp"))))
   7704 
   7705                 ;; TODO: Same :noquery issue (see above)
   7706                 (set-process-query-on-exit-flag proc nil)
   7707                 (set-process-query-on-exit-flag tcp-proc nil)
   7708                 (set-process-filter tcp-proc filter)
   7709                 (cons tcp-proc proc)))
   7710    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7711 
   7712 (defalias 'lsp-tcp-server 'lsp-tcp-server-command)
   7713 
   7714 (defun lsp-tcp-server-command (command-fn)
   7715   "Create tcp server connection.
   7716 In this mode Emacs is TCP server and the language server connects
   7717 to it. COMMAND is function with one parameter(the port) and it
   7718 should return the command to start the LS server."
   7719   (cl-check-type command-fn function)
   7720   (list
   7721    :connect (lambda (filter sentinel name environment-fn _workspace)
   7722               (let* (tcp-client-connection
   7723                      (tcp-server (make-network-process :name (format "*tcp-server-%s*" name)
   7724                                                        :buffer (format "*tcp-server-%s*" name)
   7725                                                        :family 'ipv4
   7726                                                        :service lsp--tcp-server-port
   7727                                                        :sentinel (lambda (proc _string)
   7728                                                                    (lsp-log "Language server %s is connected." name)
   7729                                                                    (setf tcp-client-connection proc))
   7730                                                        :server 't))
   7731                      (port (process-contact tcp-server :service))
   7732                      (final-command (funcall command-fn port))
   7733                      (process-environment
   7734                       (lsp--compute-process-environment environment-fn))
   7735                      (cmd-proc (make-process :name name
   7736                                              :connection-type 'pipe
   7737                                              :coding 'no-conversion
   7738                                              :command final-command
   7739                                              :stderr (format "*tcp-server-%s*::stderr" name)
   7740                                              :noquery t)))
   7741                 (let ((retries 0))
   7742                   ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds)
   7743                   (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds)))
   7744                     (lsp--info "Waiting for connection for %s, retries: %s" name retries)
   7745                     (sit-for 0.500)
   7746                     (cl-incf retries)))
   7747 
   7748                 (unless tcp-client-connection
   7749                   (condition-case nil (delete-process tcp-server) (error))
   7750                   (condition-case nil (delete-process cmd-proc) (error))
   7751                   (error "Failed to create connection to %s on port %s" name port))
   7752                 (lsp--info "Successfully connected to %s" name)
   7753 
   7754                 (set-process-query-on-exit-flag cmd-proc nil)
   7755                 (set-process-query-on-exit-flag tcp-client-connection nil)
   7756                 (set-process-query-on-exit-flag tcp-server nil)
   7757 
   7758                 (set-process-filter tcp-client-connection filter)
   7759                 (set-process-sentinel tcp-client-connection sentinel)
   7760                 (cons tcp-client-connection cmd-proc)))
   7761    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7762 
   7763 (defalias 'lsp-tramp-connection 'lsp-stdio-connection)
   7764 
   7765 (defun lsp--auto-configure ()
   7766   "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed."
   7767   (when (functionp 'lsp-ui-mode)
   7768     (lsp-ui-mode))
   7769 
   7770   (if lsp-headerline-breadcrumb-enable
   7771       (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)
   7772     (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode))
   7773   (if lsp-modeline-code-actions-enable
   7774       (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)
   7775     (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode))
   7776   (if lsp-modeline-diagnostics-enable
   7777       (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)
   7778     (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode))
   7779   (if lsp-modeline-workspace-status-enable
   7780       (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)
   7781     (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode))
   7782   (if lsp-lens-enable
   7783       (add-hook 'lsp-configure-hook 'lsp-lens--enable)
   7784     (remove-hook 'lsp-configure-hook 'lsp-lens--enable))
   7785   (if lsp-semantic-tokens-enable
   7786       (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)
   7787     (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable))
   7788 
   7789   ;; yas-snippet config
   7790   (setq-local yas-inhibit-overlay-modification-protection t))
   7791 
   7792 (defun lsp--restart-if-needed (workspace)
   7793   "Handler restart for WORKSPACE."
   7794   (when (or (eq lsp-restart 'auto-restart)
   7795             (eq (lsp--workspace-shutdown-action workspace) 'restart)
   7796             (and (eq lsp-restart 'interactive)
   7797                  (let ((query (format
   7798                                "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?"
   7799                                (lsp--workspace-print workspace))))
   7800                    (y-or-n-p query))))
   7801     (--each (lsp--workspace-buffers workspace)
   7802       (when (lsp-buffer-live-p it)
   7803         (lsp-with-current-buffer it
   7804           (if lsp--buffer-deferred
   7805               (lsp-deferred)
   7806             (lsp--info "Restarting LSP in buffer %s" (buffer-name))
   7807             (lsp)))))))
   7808 
   7809 (defun lsp--update-key (table key fn)
   7810   "Apply FN on value corresponding to KEY in TABLE."
   7811   (let ((existing-value (gethash key table)))
   7812     (if-let* ((new-value (funcall fn existing-value)))
   7813         (puthash key new-value table)
   7814       (remhash key table))))
   7815 
   7816 (defun lsp--process-sentinel (workspace process exit-str)
   7817   "Create the sentinel for WORKSPACE."
   7818   (unless (process-live-p process)
   7819     (lsp--handle-process-exit workspace exit-str)))
   7820 
   7821 (defun lsp--handle-process-exit (workspace exit-str)
   7822   (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session)))
   7823          (proc (lsp--workspace-proc workspace)))
   7824     (lsp--warn "%s has exited (%s)"
   7825                (lsp-process-name proc)
   7826                (string-trim-right (or exit-str "")))
   7827     (with-lsp-workspace workspace
   7828       ;; Clean workspace related data in each of the buffers
   7829       ;; in the workspace.
   7830       (--each (lsp--workspace-buffers workspace)
   7831         (when (lsp-buffer-live-p it)
   7832           (lsp-with-current-buffer it
   7833             (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces))
   7834             (lsp--uninitialize-workspace)
   7835             (lsp--spinner-stop)
   7836             (lsp--remove-overlays 'lsp-highlight))))
   7837 
   7838       ;; Cleanup session from references to the closed workspace.
   7839       (--each (hash-table-keys folder->workspaces)
   7840         (lsp--update-key folder->workspaces it (apply-partially 'delete workspace)))
   7841 
   7842       (lsp-process-cleanup proc))
   7843 
   7844     (run-hook-with-args 'lsp-after-uninitialized-functions workspace)
   7845 
   7846     (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown)
   7847         (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace))
   7848       (lsp--restart-if-needed workspace))
   7849     (lsp--cleanup-hanging-watches)))
   7850 
   7851 (defun lsp-workspace-folders (workspace)
   7852   "Return all folders associated with WORKSPACE."
   7853   (let (result)
   7854     (->> (lsp-session)
   7855          (lsp-session-folder->servers)
   7856          (maphash (lambda (folder workspaces)
   7857                     (when (-contains? workspaces workspace)
   7858                       (push folder result)))))
   7859     result))
   7860 
   7861 (defun lsp--start-workspace (session client-template root &optional initialization-options)
   7862   "Create new workspace for CLIENT-TEMPLATE with project root ROOT.
   7863 INITIALIZATION-OPTIONS are passed to initialize function.
   7864 SESSION is the active session."
   7865   (lsp--spinner-start)
   7866   (-let* ((default-directory root)
   7867           (client (copy-lsp--client client-template))
   7868           (workspace (make-lsp--workspace
   7869                       :root root
   7870                       :client client
   7871                       :status 'starting
   7872                       :buffers (list (lsp-current-buffer))
   7873                       :host-root (file-remote-p root)))
   7874           ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities
   7875                      'multi-root 'initialized-fn) client)
   7876           ((proc . cmd-proc) (funcall
   7877                               (or (plist-get new-connection :connect)
   7878                                   (user-error "Client %s is configured incorrectly" client))
   7879                               (lsp--create-filter-function workspace)
   7880                               (apply-partially #'lsp--process-sentinel workspace)
   7881                               (format "%s" server-id)
   7882                               environment-fn
   7883                               workspace))
   7884           (workspace-folders (gethash server-id (lsp-session-server-id->folders session))))
   7885     (setf (lsp--workspace-proc workspace) proc
   7886           (lsp--workspace-cmd-proc workspace) cmd-proc)
   7887 
   7888     ;; update (lsp-session-folder->servers) depending on whether we are starting
   7889     ;; multi/single folder workspace
   7890     (mapc (lambda (project-root)
   7891             (->> session
   7892                  (lsp-session-folder->servers)
   7893                  (gethash project-root)
   7894                  (cl-pushnew workspace)))
   7895           (or workspace-folders (list root)))
   7896 
   7897     (with-lsp-workspace workspace
   7898       (run-hooks 'lsp-before-initialize-hook)
   7899       (lsp-request-async
   7900        "initialize"
   7901        (append
   7902         (list :processId (unless (file-remote-p (buffer-file-name))
   7903                            (emacs-pid))
   7904               :rootPath (lsp-file-local-name (expand-file-name root))
   7905               :clientInfo (list :name "emacs"
   7906                                 :version (emacs-version))
   7907               :rootUri (lsp--path-to-uri root)
   7908               :capabilities (lsp--client-capabilities custom-capabilities)
   7909               :initializationOptions initialization-options
   7910               :workDoneToken "1")
   7911         (when lsp-server-trace
   7912           (list :trace lsp-server-trace))
   7913         (when multi-root
   7914           (->> workspace-folders
   7915                (-distinct)
   7916                (-map (lambda (folder)
   7917                        (list :uri (lsp--path-to-uri folder)
   7918                              :name (f-filename folder))))
   7919                (apply 'vector)
   7920                (list :workspaceFolders))))
   7921        (-lambda ((&InitializeResult :capabilities))
   7922          ;; we know that Rust Analyzer will send {} which will be parsed as null
   7923          ;; when using plists
   7924          (when (equal 'rust-analyzer server-id)
   7925            (-> capabilities
   7926                (lsp:server-capabilities-text-document-sync?)
   7927                (lsp:set-text-document-sync-options-save? t)))
   7928 
   7929          (setf (lsp--workspace-server-capabilities workspace) capabilities
   7930                (lsp--workspace-status workspace) 'initialized)
   7931 
   7932          (with-lsp-workspace workspace
   7933            (lsp-notify "initialized" lsp--empty-ht))
   7934 
   7935          (when initialized-fn (funcall initialized-fn workspace))
   7936 
   7937          (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace))
   7938          (->> workspace
   7939               (lsp--workspace-buffers)
   7940               (mapc (lambda (buffer)
   7941                       (lsp-with-current-buffer buffer
   7942                         (lsp--open-in-workspace workspace)))))
   7943 
   7944          (with-lsp-workspace workspace
   7945            (run-hooks 'lsp-after-initialize-hook))
   7946          (lsp--info "%s initialized successfully in folders: %s"
   7947                     (lsp--workspace-print workspace)
   7948                     (lsp-workspace-folders workspace)))
   7949        :mode 'detached))
   7950     workspace))
   7951 
   7952 (defun lsp--load-default-session ()
   7953   "Load default session."
   7954   (setq lsp--session (or (condition-case err
   7955                              (lsp--read-from-file lsp-session-file)
   7956                            (error (lsp--error "Failed to parse the session %s, starting with clean one."
   7957                                               (error-message-string err))
   7958                                   nil))
   7959                          (make-lsp-session))))
   7960 
   7961 (defun lsp-session ()
   7962   "Get the session associated with the current buffer."
   7963   (or lsp--session (setq lsp--session (lsp--load-default-session))))
   7964 
   7965 (defun lsp--client-disabled-p (buffer-major-mode client)
   7966   (seq-some
   7967    (lambda (entry)
   7968      (pcase entry
   7969        ((pred symbolp) (eq entry client))
   7970        (`(,mode . ,client-or-list)
   7971         (and (eq mode buffer-major-mode)
   7972              (if (listp client-or-list)
   7973                  (memq client client-or-list)
   7974                (eq client client-or-list))))))
   7975    lsp-disabled-clients))
   7976 
   7977 
   7978 ;; download server
   7979 
   7980 (defcustom lsp-server-install-dir (expand-file-name
   7981                                    (locate-user-emacs-file (f-join ".cache" "lsp")))
   7982   "Directory in which the servers will be installed."
   7983   :risky t
   7984   :type 'directory
   7985   :package-version '(lsp-mode . "6.3")
   7986   :group 'lsp-mode)
   7987 
   7988 (defcustom lsp-verify-signature t
   7989   "Whether to check GPG signatures of downloaded files."
   7990   :type 'boolean
   7991   :package-version '(lsp-mode . "8.0.0")
   7992   :group 'lsp-mode)
   7993 
   7994 (defvar lsp--dependencies (ht))
   7995 
   7996 (defun lsp-dependency (name &rest definitions)
   7997   "Used to specify a language server DEPENDENCY, the server
   7998 executable or other required file path. Typically, the
   7999 DEPENDENCY is found by locating it on the system path using
   8000 `executable-find'.
   8001 
   8002 You can explicitly call lsp-dependency in your environment to
   8003 specify the absolute path to the DEPENDENCY. For example, the
   8004 typescript-language-server requires both the server and the
   8005 typescript compiler. If you have installed them in a team shared
   8006 read-only location, you can instruct lsp-mode to use them via
   8007 
   8008  (eval-after-load `lsp-mode
   8009    `(progn
   8010       (require lsp-javascript)
   8011       (lsp-dependency typescript-language-server (:system ,tls-exe))
   8012       (lsp-dependency typescript (:system ,ts-js))))
   8013 
   8014 where tls-exe is the absolute path to the typescript-language-server
   8015 executable and ts-js is the absolute path to the typescript compiler
   8016 JavaScript file, tsserver.js (the *.js is required for Windows)."
   8017   (ht-set lsp--dependencies name definitions))
   8018 
   8019 (defun lsp--server-binary-present? (client)
   8020   (unless (equal (lsp--client-server-id client) 'lsp-pwsh)
   8021     (condition-case ()
   8022         (-some-> client lsp--client-new-connection (plist-get :test?) funcall)
   8023       (error nil)
   8024       (args-out-of-range nil))))
   8025 
   8026 (define-minor-mode lsp-installation-buffer-mode
   8027   "Mode used in *lsp-installation* buffers.
   8028 It can be used to set-up keybindings, etc. Disabling this mode
   8029 detaches the installation buffer from commands like
   8030 `lsp-select-installation-buffer'."
   8031   :init-value nil
   8032   :lighter nil)
   8033 
   8034 (defface lsp-installation-finished-buffer-face '((t :foreground "orange"))
   8035   "Face used for finished installation buffers.
   8036 Used in `lsp-select-installation-buffer'."
   8037   :group 'lsp-mode)
   8038 
   8039 (defface lsp-installation-buffer-face '((t :foreground "green"))
   8040   "Face used for installation buffers still in progress.
   8041 Used in `lsp-select-installation-buffer'."
   8042   :group 'lsp-mode)
   8043 
   8044 (defun lsp--installation-buffer? (buf)
   8045   "Check whether BUF is an `lsp-async-start-process' buffer."
   8046   (buffer-local-value 'lsp-installation-buffer-mode buf))
   8047 
   8048 (defun lsp-select-installation-buffer (&optional show-finished)
   8049   "Interactively choose an installation buffer.
   8050 If SHOW-FINISHED is set, leftover (finished) installation buffers
   8051 are still shown."
   8052   (interactive "P")
   8053   (let ((bufs (--filter (and (lsp--installation-buffer? it)
   8054                              (or show-finished (get-buffer-process it)))
   8055                         (buffer-list))))
   8056     (pcase bufs
   8057       (`nil (user-error "No installation buffers"))
   8058       (`(,buf) (pop-to-buffer buf))
   8059       (bufs (pop-to-buffer (completing-read "Select installation buffer: "
   8060                                             (--map (propertize (buffer-name it) 'face
   8061                                                                (if (get-buffer-process it)
   8062                                                                    'lsp-installation-buffer-face
   8063                                                                  'lsp-installation-finished-buffer-face))
   8064                                                    bufs)))))))
   8065 
   8066 (defun lsp-cleanup-installation-buffers ()
   8067   "Delete finished *lsp-installation* buffers."
   8068   (interactive)
   8069   (dolist (buf (buffer-list))
   8070     (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf)))
   8071       (kill-buffer buf))))
   8072 
   8073 (defun lsp--download-status ()
   8074   (-some--> #'lsp--client-download-in-progress?
   8075     (lsp--filter-clients it)
   8076     (-map (-compose #'symbol-name #'lsp--client-server-id) it)
   8077     (format "%s" it)
   8078     (propertize it 'face 'success)
   8079     (format " Installing following servers: %s" it)
   8080     (propertize it
   8081                 'local-map (make-mode-line-mouse-map
   8082                             'mouse-1 #'lsp-select-installation-buffer)
   8083                 'mouse-face 'highlight)))
   8084 
   8085 (defun lsp--install-server-internal (client &optional update?)
   8086   (unless (lsp--client-download-server-fn client)
   8087     (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation."
   8088                 (lsp--client-server-id client)))
   8089 
   8090   (setf (lsp--client-download-in-progress? client) t)
   8091   (add-to-list 'global-mode-string '(t (:eval (lsp--download-status))))
   8092   (cl-flet ((done
   8093              (success? &optional error-message)
   8094              ;; run with idle timer to make sure the lsp command is executed in
   8095              ;; the main thread, see #2739.
   8096              (run-with-timer
   8097               0.0
   8098               nil
   8099               (lambda ()
   8100                 (-let [(&lsp-cln 'server-id 'buffers) client]
   8101                   (setf (lsp--client-download-in-progress? client) nil
   8102                         (lsp--client-buffers client) nil)
   8103                   (if success?
   8104                       (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id
   8105                                  (length buffers))
   8106                     (lsp--error "Server %s install process failed with the following error message: %s.
   8107 Check `*lsp-install*' and `*lsp-log*' buffer."
   8108                                 server-id
   8109                                 error-message))
   8110                   (seq-do
   8111                    (lambda (buffer)
   8112                      (when (lsp-buffer-live-p buffer)
   8113                        (lsp-with-current-buffer buffer
   8114                          (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   8115                                     global-mode-string)
   8116                          (when success? (lsp)))))
   8117                    buffers)
   8118                   (unless (lsp--filter-clients #'lsp--client-download-in-progress?)
   8119                     (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   8120                                global-mode-string)))))))
   8121     (lsp--info "Download %s started." (lsp--client-server-id client))
   8122     (condition-case err
   8123         (funcall
   8124          (lsp--client-download-server-fn client)
   8125          client
   8126          (lambda () (done t))
   8127          (lambda (msg) (done nil msg))
   8128          update?)
   8129       (error
   8130        (done nil (error-message-string err))))))
   8131 
   8132 (defun lsp--require-packages ()
   8133   "Load `lsp-client-packages' if needed."
   8134   (when (and lsp-auto-configure (not lsp--client-packages-required))
   8135     (seq-do (lambda (package)
   8136               ;; loading client is slow and `lsp' can be called repeatedly
   8137               (unless (featurep package)
   8138                 (require package nil t)))
   8139             lsp-client-packages)
   8140     (setq lsp--client-packages-required t)))
   8141 
   8142 ;;;###autoload
   8143 (defun lsp-install-server (update? &optional server-id)
   8144   "Interactively install or re-install server.
   8145 When prefix UPDATE? is t force installation even if the server is present."
   8146   (interactive "P")
   8147   (lsp--require-packages)
   8148   (let* ((chosen-client (or (gethash server-id lsp-clients)
   8149                             (lsp--completing-read
   8150                              "Select server to install/re-install: "
   8151                              (or (->> lsp-clients
   8152                                       (ht-values)
   8153                                       (-filter (-andfn
   8154                                                 (-not #'lsp--client-download-in-progress?)
   8155                                                 #'lsp--client-download-server-fn)))
   8156                                  (user-error "There are no servers with automatic installation"))
   8157                              (lambda (client)
   8158                                (let ((server-name (-> client lsp--client-server-id symbol-name)))
   8159                                  (if (lsp--server-binary-present? client)
   8160                                      (concat server-name " (Already installed)")
   8161                                    server-name)))
   8162                              nil
   8163                              t)))
   8164          (update? (or update?
   8165                       (and (not (lsp--client-download-in-progress? chosen-client))
   8166                            (lsp--server-binary-present? chosen-client)))))
   8167     (lsp--install-server-internal chosen-client update?)))
   8168 
   8169 ;;;###autoload
   8170 (defun lsp-uninstall-server (dir)
   8171   "Delete a LSP server from `lsp-server-install-dir'."
   8172   (interactive
   8173    (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir))))
   8174   (unless (file-directory-p dir)
   8175     (user-error "Couldn't find %s directory" dir))
   8176   (delete-directory dir 'recursive)
   8177   (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir))))
   8178 
   8179 ;;;###autoload
   8180 (defun lsp-uninstall-servers ()
   8181   "Uninstall all installed servers."
   8182   (interactive)
   8183   (let* ((dir lsp-server-install-dir)
   8184          (servers (ignore-errors
   8185                     (directory-files dir t
   8186                                      directory-files-no-dot-files-regexp))))
   8187     (if (or (not (file-directory-p dir)) (zerop (length servers)))
   8188         (user-error "No servers to uninstall")
   8189       (when (yes-or-no-p
   8190              (format "Servers to uninstall: %d (%s), proceed? "
   8191                      (length servers)
   8192                      (mapconcat (lambda (server)
   8193                                   (file-name-nondirectory (directory-file-name server)))
   8194                                 servers " ")))
   8195         (mapc #'lsp-uninstall-server servers)
   8196         (message "All servers uninstalled")))))
   8197 
   8198 ;;;###autoload
   8199 (defun lsp-update-server (&optional server-id)
   8200   "Interactively update (reinstall) a server."
   8201   (interactive)
   8202   (lsp--require-packages)
   8203   (let ((chosen-client (or (gethash server-id lsp-clients)
   8204                            (lsp--completing-read
   8205                             "Select server to update (if not on the list, probably you need to `lsp-install-server`): "
   8206                             (or (->> lsp-clients
   8207                                      (ht-values)
   8208                                      (-filter (-andfn
   8209                                                (-not #'lsp--client-download-in-progress?)
   8210                                                #'lsp--client-download-server-fn
   8211                                                #'lsp--server-binary-present?)))
   8212                                 (user-error "There are no servers to update"))
   8213                             (lambda (client)
   8214                               (-> client lsp--client-server-id symbol-name))
   8215                             nil
   8216                             t))))
   8217     (lsp--install-server-internal chosen-client t)))
   8218 
   8219 ;;;###autoload
   8220 (defun lsp-update-servers ()
   8221   "Update (reinstall) all installed servers."
   8222   (interactive)
   8223   (lsp--require-packages)
   8224   (mapc (lambda (client) (lsp--install-server-internal client t))
   8225         (-filter (-andfn
   8226                   (-not #'lsp--client-download-in-progress?)
   8227                   #'lsp--client-download-server-fn
   8228                   #'lsp--server-binary-present?) (hash-table-values lsp-clients))))
   8229 
   8230 ;;;###autoload
   8231 (defun lsp-ensure-server (server-id)
   8232   "Ensure server SERVER-ID"
   8233   (lsp--require-packages)
   8234   (if-let* ((client (gethash server-id lsp-clients)))
   8235       (unless (lsp--server-binary-present? client)
   8236         (lsp--info "Server `%s' is not preset, installing..." server-id)
   8237         (lsp-install-server nil server-id))
   8238     (warn "Unable to find server registration with id %s" server-id)))
   8239 
   8240 (defun lsp-async-start-process (callback error-callback &rest command)
   8241   "Start async process COMMAND with CALLBACK and ERROR-CALLBACK."
   8242   (let ((name (cl-first command)))
   8243     (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd)
   8244                                                                                          (not (null cmd)))
   8245                                                                                        command)
   8246                                                        " ") t
   8247                                             (lambda (&rest _)
   8248                                               (generate-new-buffer-name (format "*lsp-install: %s*" name))))
   8249       (lsp-installation-buffer-mode +1)
   8250       (view-mode +1)
   8251       (add-hook
   8252        'compilation-finish-functions
   8253        (lambda (_buf status)
   8254          (if (string= "finished\n" status)
   8255              (condition-case err
   8256                  (funcall callback)
   8257                (error
   8258                 (funcall error-callback (error-message-string err))))
   8259            (funcall error-callback (s-trim-right status))))
   8260        nil t))))
   8261 
   8262 (defun lsp-resolve-value (value)
   8263   "Resolve VALUE's value.
   8264 If it is function - call it.
   8265 If it is a variable - return it's value
   8266 Otherwise returns value itself."
   8267   (cond
   8268    ((functionp value) (funcall value))
   8269    ((and (symbolp value) (boundp value)) (symbol-value value))
   8270    (value)))
   8271 
   8272 (defvar lsp-deps-providers
   8273   (list :npm (list :path #'lsp--npm-dependency-path
   8274                    :install #'lsp--npm-dependency-install)
   8275         :cargo (list :path #'lsp--cargo-dependency-path
   8276                      :install #'lsp--cargo-dependency-install)
   8277         :system (list :path #'lsp--system-path)
   8278         :download (list :path #'lsp-download-path
   8279                         :install #'lsp-download-install)))
   8280 
   8281 (defun lsp--system-path (path)
   8282   "If PATH is absolute and exists return it as is. Otherwise,
   8283 return the absolute path to the executable defined by PATH or
   8284 nil."
   8285   ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the
   8286   ;; typescript-language-server. When lsp invokes the server, lsp needs to
   8287   ;; supply the path to the typescript compiler, tsserver.js, as an argument. To
   8288   ;; make code platform independent, one must pass the absolute path to the
   8289   ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript
   8290   ;; child process spawn command that is invoked by the
   8291   ;; typescript-language-server). This is why we check for existence and not
   8292   ;; that the path is executable.
   8293   (let ((path (lsp-resolve-value path)))
   8294     (cond
   8295      ((and (f-absolute? path)
   8296            (f-exists? path))
   8297       path)
   8298      ((executable-find path t) path))))
   8299 
   8300 (defun lsp-package-path (dependency)
   8301   "Path to the DEPENDENCY each of the registered providers."
   8302   (let (path)
   8303     (--first (-let [(provider . rest) it]
   8304               (setq path (-some-> lsp-deps-providers
   8305                            (plist-get provider)
   8306                            (plist-get :path)
   8307                            (apply rest))))
   8308              (gethash dependency lsp--dependencies))
   8309     path))
   8310 
   8311 (defun lsp-package-ensure (dependency callback error-callback)
   8312   "Asynchronously ensure a package."
   8313   (or (-first (-lambda ((provider . rest))
   8314                 (-some-> lsp-deps-providers
   8315                   (plist-get provider)
   8316                   (plist-get :install)
   8317                   (apply (cl-list* callback error-callback rest))))
   8318               (gethash dependency lsp--dependencies))
   8319       (funcall error-callback (format "Unable to find a way to install %s" dependency))))
   8320 
   8321 
   8322 ;; npm handling
   8323 
   8324 ;; https://docs.npmjs.com/files/folders#executables
   8325 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys)
   8326   "Return npm dependency PATH for PACKAGE."
   8327   (let ((path (executable-find
   8328                (f-join lsp-server-install-dir "npm" package
   8329                        (cond ((eq system-type 'windows-nt) "")
   8330                              (t "bin"))
   8331                        path)
   8332                t)))
   8333     (unless (and path (f-exists? path))
   8334       (error "The package %s is not installed.  Unable to find %s" package path))
   8335     path))
   8336 
   8337 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys)
   8338   (if-let* ((npm-binary (executable-find "npm")))
   8339       (progn
   8340         ;; Explicitly `make-directory' to work around NPM bug in
   8341         ;; versions 7.0.0 through 7.4.1. See
   8342         ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for
   8343         ;; discussion.
   8344         (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents)
   8345         (lsp-async-start-process (lambda ()
   8346                                    (if (string-empty-p
   8347                                         (string-trim (shell-command-to-string
   8348                                                       (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " "))))
   8349                                        (funcall callback)
   8350                                      (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json")))))
   8351                                            (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx
   8352                                        (when (f-dir-p default-directory)
   8353                                          (lsp-async-start-process callback
   8354                                                                   error-callback
   8355                                                                   (executable-find "npx")
   8356                                                                   "npm-install-peers")))))
   8357                                  error-callback
   8358                                  npm-binary
   8359                                  "-g"
   8360                                  "--prefix"
   8361                                  (f-join lsp-server-install-dir "npm" package)
   8362                                  "install"
   8363                                  package))
   8364     (lsp-log "Unable to install %s via `npm' because it is not present" package)
   8365     nil))
   8366 
   8367 
   8368 ;; Cargo dependency handling
   8369 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys)
   8370   (let ((path (executable-find
   8371                (f-join lsp-server-install-dir
   8372                        "cargo"
   8373                        package
   8374                        "bin"
   8375                        path)
   8376                t)))
   8377     (unless (and path (f-exists? path))
   8378       (error "The package %s is not installed.  Unable to find %s" package path))
   8379     path))
   8380 
   8381 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys)
   8382   (if-let* ((cargo-binary (executable-find "cargo")))
   8383       (lsp-async-start-process
   8384        callback
   8385        error-callback
   8386        cargo-binary
   8387        "install"
   8388        package
   8389        (when git
   8390          "--git")
   8391        git
   8392        "--root"
   8393        (f-join lsp-server-install-dir "cargo" package))
   8394     (lsp-log "Unable to install %s via `cargo' because it is not present" package)
   8395     nil))
   8396 
   8397 
   8398 
   8399 ;; Download URL handling
   8400 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys)
   8401   (let* ((url (lsp-resolve-value url))
   8402          (store-path (lsp-resolve-value store-path))
   8403          ;; (decompress (lsp-resolve-value decompress))
   8404          (download-path
   8405           (pcase decompress
   8406             (:gzip (concat store-path ".gz"))
   8407             (:zip (concat store-path ".zip"))
   8408             (:targz (concat store-path ".tar.gz"))
   8409             (`nil store-path)
   8410             (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'")))))
   8411     (make-thread
   8412      (lambda ()
   8413        (condition-case err
   8414            (progn
   8415              (when (f-exists? download-path)
   8416                (f-delete download-path))
   8417              (when (f-exists? store-path)
   8418                (f-delete store-path))
   8419              (lsp--info "Starting to download %s to %s..." url download-path)
   8420              (mkdir (f-parent download-path) t)
   8421              (url-copy-file url download-path)
   8422              (lsp--info "Finished downloading %s..." download-path)
   8423              (when (and lsp-verify-signature asc-url pgp-key)
   8424                (if (executable-find epg-gpg-program)
   8425                    (let ((asc-download-path (concat download-path ".asc"))
   8426                          (context (epg-make-context))
   8427                          (fingerprint)
   8428                          (signature))
   8429                      (when (f-exists? asc-download-path)
   8430                        (f-delete asc-download-path))
   8431                      (lsp--info "Starting to download %s to %s..." asc-url asc-download-path)
   8432                      (url-copy-file asc-url asc-download-path)
   8433                      (lsp--info "Finished downloading %s..." asc-download-path)
   8434                      (epg-import-keys-from-string context pgp-key)
   8435                      (setq fingerprint (epg-import-status-fingerprint
   8436                                         (car
   8437                                          (epg-import-result-imports
   8438                                           (epg-context-result-for context 'import)))))
   8439                      (lsp--info "Verifying signature %s..." asc-download-path)
   8440                      (epg-verify-file context asc-download-path download-path)
   8441                      (setq signature (car (epg-context-result-for context 'verify)))
   8442                      (unless (and
   8443                               (eq (epg-signature-status signature) 'good)
   8444                               (equal (epg-signature-fingerprint signature) fingerprint))
   8445                        (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature))))
   8446                  (lsp--warn "GPG is not installed, skipping the signature check.")))
   8447              (when decompress
   8448                (lsp--info "Decompressing %s..." download-path)
   8449                (pcase decompress
   8450                  (:gzip
   8451                   (lsp-gunzip download-path))
   8452                  (:zip (lsp-unzip download-path (f-parent store-path)))
   8453                  (:targz (lsp-tar-gz-decompress download-path (f-parent store-path))))
   8454                (lsp--info "Decompressed %s..." store-path))
   8455              (funcall callback))
   8456          (error (funcall error-callback err)))))))
   8457 
   8458 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys)
   8459   "Download URL and store it into STORE-PATH.
   8460 
   8461 SET-EXECUTABLE? when non-nil change the executable flags of
   8462 STORE-PATH to make it executable. BINARY-PATH can be specified
   8463 when the binary to start does not match the name of the
   8464 archive (e.g. when the archive has multiple files)"
   8465   (let ((store-path (or (lsp-resolve-value binary-path)
   8466                         (lsp-resolve-value store-path))))
   8467     (cond
   8468      ((executable-find store-path) store-path)
   8469      ((and set-executable? (f-exists? store-path))
   8470       (set-file-modes store-path #o0700)
   8471       store-path)
   8472      ((f-exists? store-path) store-path))))
   8473 
   8474 (defun lsp--find-latest-gh-release-url (url regex)
   8475   "Fetch the latest version in the releases given by URL by using REGEX."
   8476   (let ((url-request-method "GET"))
   8477     (with-current-buffer (url-retrieve-synchronously url)
   8478       (goto-char (point-min))
   8479       (re-search-forward "\n\n" nil 'noerror)
   8480       (delete-region (point-min) (point))
   8481       (let* ((json-result (lsp-json-read-buffer)))
   8482         (message "Latest version found: %s" (lsp-get json-result :tag_name))
   8483         (--> json-result
   8484              (lsp-get it :assets)
   8485              (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it)
   8486              (lsp-get it :browser_download_url))))))
   8487 
   8488 ;; unzip
   8489 
   8490 (defconst lsp-ext-pwsh-script "pwsh -noprofile -noninteractive \
   8491 -nologo -ex bypass -c Expand-Archive -Path '%s' -DestinationPath '%s'"
   8492   "Pwsh script to unzip file.")
   8493 
   8494 (defconst lsp-ext-powershell-script "powershell -noprofile -noninteractive \
   8495 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'"
   8496   "Powershell script to unzip file.")
   8497 
   8498 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'"
   8499   "Unzip script to unzip file.")
   8500 
   8501 (defcustom lsp-unzip-script (lambda ()
   8502                               (cond ((and (eq system-type 'windows-nt)
   8503                                           (executable-find "pwsh"))
   8504                                      lsp-ext-pwsh-script)
   8505                                     ((and (eq system-type 'windows-nt)
   8506                                           (executable-find "powershell"))
   8507                                      lsp-ext-powershell-script)
   8508                                     ((executable-find "unzip") lsp-ext-unzip-script)
   8509                                     ((executable-find "pwsh") lsp-ext-pwsh-script)
   8510                                     (t nil)))
   8511   "The script to unzip."
   8512   :group 'lsp-mode
   8513   :type 'string
   8514   :package-version '(lsp-mode . "8.0.0"))
   8515 
   8516 (defun lsp-unzip (zip-file dest)
   8517   "Unzip ZIP-FILE to DEST."
   8518   (unless lsp-unzip-script
   8519     (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'"))
   8520   (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest)))
   8521 
   8522 ;; gunzip
   8523 
   8524 (defconst lsp-ext-gunzip-script "gzip -d %1$s"
   8525   "Script to decompress a gzippped file with gzip.")
   8526 
   8527 (defcustom lsp-gunzip-script (lambda ()
   8528                                (cond ((executable-find "gzip") lsp-ext-gunzip-script)
   8529                                      (t nil)))
   8530   "The script to decompress a gzipped file.
   8531 Should be a format string with one argument for the file to be decompressed
   8532 in place."
   8533   :group 'lsp-mode
   8534   :type 'string
   8535   :package-version '(lsp-mode . "8.0.0"))
   8536 
   8537 (defun lsp-gunzip (gz-file)
   8538   "Decompress GZ-FILE in place."
   8539   (unless lsp-gunzip-script
   8540     (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file))
   8541   (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file)))
   8542 
   8543 ;; tar.gz decompression
   8544 
   8545 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'"
   8546   "Script to decompress a .tar.gz file.")
   8547 
   8548 (defcustom lsp-tar-script (lambda ()
   8549                             (cond ((executable-find "tar") lsp-ext-tar-script)
   8550                                   (t nil)))
   8551   "The script to decompress a .tar.gz file.
   8552 Should be a format string with one argument for the file to be decompressed
   8553 in place."
   8554   :group 'lsp-mode
   8555   :type 'string)
   8556 
   8557 (defun lsp-tar-gz-decompress (targz-file dest)
   8558   "Decompress TARGZ-FILE in DEST."
   8559   (unless lsp-tar-script
   8560     (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file))
   8561   (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest)))
   8562 
   8563 
   8564 ;; VSCode marketplace
   8565 
   8566 (defcustom lsp-vscode-ext-url
   8567   "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s"
   8568   "Vscode extension template url."
   8569   :group 'lsp-mode
   8570   :type 'string
   8571   :package-version '(lsp-mode . "8.0.0"))
   8572 
   8573 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform)
   8574   "Return the URL to vscode extension.
   8575 PUBLISHER is the extension publisher.
   8576 NAME is the name of the extension.
   8577 VERSION is the version of the extension.
   8578 TARGETPLATFORM is the targetPlatform of the extension."
   8579   (format lsp-vscode-ext-url publisher name version (or targetPlatform "")))
   8580 
   8581 
   8582 
   8583 ;; Queueing prompts
   8584 
   8585 (defvar lsp--question-queue nil
   8586   "List of questions yet to be asked by `lsp-ask-question'.")
   8587 
   8588 (defun lsp-ask-question (question options callback)
   8589   "Prompt the user to answer the QUESTION with one of the OPTIONS from the
   8590 minibuffer. Once the user selects an option, the CALLBACK function will be
   8591 called, passing the selected option to it.
   8592 
   8593 If the user is currently being shown a question, the question will be stored in
   8594 `lsp--question-queue', and will be asked once the user has answered the current
   8595 question."
   8596   (add-to-list 'lsp--question-queue `(("question" . ,question)
   8597                                       ("options" . ,options)
   8598                                       ("callback" . ,callback)) t)
   8599   (when (eq (length lsp--question-queue) 1)
   8600     (lsp--process-question-queue)))
   8601 
   8602 (defun lsp--process-question-queue ()
   8603   "Take the first question from `lsp--question-queue', process it, then process
   8604 the next question until the queue is empty."
   8605   (-let* (((&alist "question" "options" "callback") (car lsp--question-queue))
   8606           (answer (completing-read question options nil t)))
   8607     (pop lsp--question-queue)
   8608     (funcall callback answer)
   8609     (when lsp--question-queue
   8610       (lsp--process-question-queue))))
   8611 
   8612 (defun lsp--supports-buffer? (client)
   8613   (and
   8614    ;; both file and client remote or both local
   8615    (eq (---truthy? (file-remote-p (buffer-file-name)))
   8616        (---truthy? (lsp--client-remote? client)))
   8617 
   8618    ;; activation function or major-mode match.
   8619    (if-let* ((activation-fn (lsp--client-activation-fn client)))
   8620        (funcall activation-fn (buffer-file-name) major-mode)
   8621      (-contains? (lsp--client-major-modes client) major-mode))
   8622 
   8623    ;; check whether it is enabled if `lsp-enabled-clients' is not null
   8624    (or (null lsp-enabled-clients)
   8625        (or (member (lsp--client-server-id client) lsp-enabled-clients)
   8626            (ignore (lsp--info "Client %s is not in lsp-enabled-clients"
   8627                               (lsp--client-server-id client)))))
   8628 
   8629    ;; check whether it is not disabled.
   8630    (not (lsp--client-disabled-p major-mode (lsp--client-server-id client)))))
   8631 
   8632 (defun lsp--filter-clients (pred)
   8633   (->> lsp-clients hash-table-values (-filter pred)))
   8634 
   8635 (defun lsp--find-clients ()
   8636   "Find clients which can handle current buffer."
   8637   (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   8638                                                             #'lsp--server-binary-present?)))
   8639     (lsp-log "Found the following clients for %s: %s"
   8640              (buffer-file-name)
   8641              (s-join ", "
   8642                      (-map (lambda (client)
   8643                              (format "(server-id %s, priority %s)"
   8644                                      (lsp--client-server-id client)
   8645                                      (lsp--client-priority client)))
   8646                            matching-clients)))
   8647     (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients))
   8648             (selected-clients (if-let* ((main-client (and main-clients
   8649                                                          (--max-by (> (lsp--client-priority it)
   8650                                                                       (lsp--client-priority other))
   8651                                                                    main-clients))))
   8652                                   (cons main-client add-on-clients)
   8653                                 add-on-clients)))
   8654       (lsp-log "The following clients were selected based on priority: %s"
   8655                (s-join ", "
   8656                        (-map (lambda (client)
   8657                                (format "(server-id %s, priority %s)"
   8658                                        (lsp--client-server-id client)
   8659                                        (lsp--client-priority client)))
   8660                              selected-clients)))
   8661       selected-clients)))
   8662 
   8663 (defun lsp-workspace-remove-all-folders()
   8664   "Delete all lsp tracked folders."
   8665   (interactive)
   8666   (--each (lsp-session-folders (lsp-session))
   8667     (lsp-workspace-folders-remove it)))
   8668 
   8669 (defun lsp-register-client (client)
   8670   "Registers LSP client CLIENT."
   8671   (let ((client-id (lsp--client-server-id client)))
   8672     (puthash client-id client lsp-clients)
   8673     (setplist (intern (format "lsp-%s-after-open-hook" client-id))
   8674               `( standard-value (nil) custom-type hook
   8675                  custom-package-version (lsp-mode . "7.0.1")
   8676                  variable-documentation ,(format "Hooks to run after `%s' server is run." client-id)
   8677                  custom-requests nil)))
   8678   (when (and lsp-auto-register-remote-clients
   8679              (not (lsp--client-remote? client)))
   8680     (let ((remote-client (copy-lsp--client client)))
   8681       (setf (lsp--client-remote? remote-client) t
   8682             (lsp--client-server-id remote-client) (intern
   8683                                                    (format "%s-tramp"
   8684                                                            (lsp--client-server-id client)))
   8685             ;; disable automatic download
   8686             (lsp--client-download-server-fn remote-client) nil)
   8687       (lsp-register-client remote-client))))
   8688 
   8689 (defun lsp--create-initialization-options (_session client)
   8690   "Create initialization-options from SESSION and CLIENT.
   8691 Add workspace folders depending on server being multiroot and
   8692 session workspace folder configuration for the server."
   8693   (let* ((initialization-options-or-fn (lsp--client-initialization-options client)))
   8694     (if (functionp initialization-options-or-fn)
   8695         (funcall initialization-options-or-fn)
   8696       initialization-options-or-fn)))
   8697 
   8698 (defvar lsp-client-settings (make-hash-table :test 'equal)
   8699   "For internal use, any external users please use
   8700   `lsp-register-custom-settings' function instead")
   8701 
   8702 (defun lsp-register-custom-settings (props)
   8703   "Register PROPS.
   8704 PROPS is list of triple (path value boolean?) where PATH is the path to the
   8705 property; VALUE can be a literal value, symbol to be evaluated, or either a
   8706 function or lambda function to be called without arguments; BOOLEAN? is an
   8707 optional flag that should be non-nil for boolean settings, when it is nil the
   8708 property will be ignored if the VALUE is nil.
   8709 
   8710 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))'
   8711 \(note the double parentheses)"
   8712   (mapc
   8713    (-lambda ((path . rest))
   8714      (puthash path rest lsp-client-settings))
   8715    props))
   8716 
   8717 (defun lsp-region-text (region)
   8718   "Get the text for REGION in current buffer."
   8719   (-let (((start . end) (lsp--range-to-region region)))
   8720     (buffer-substring-no-properties start end)))
   8721 
   8722 (defun lsp-ht-set (tbl paths value)
   8723   "Set nested hash table value.
   8724 TBL - a hash table, PATHS is the path to the nested VALUE."
   8725   (pcase paths
   8726     (`(,path) (ht-set! tbl path value))
   8727     (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl)
   8728                                            (let ((temp-tbl (ht)))
   8729                                              (ht-set! tbl path temp-tbl)
   8730                                              temp-tbl))))
   8731                        (lsp-ht-set nested-tbl rst value)))))
   8732 
   8733 ;; sections
   8734 
   8735 (defalias 'defcustom-lsp 'lsp-defcustom)
   8736 
   8737 (defmacro lsp-defcustom (symbol standard doc &rest args)
   8738   "Defines `lsp-mode' server property."
   8739   (declare (doc-string 3) (debug (name body))
   8740            (indent defun))
   8741   (let ((path (plist-get args :lsp-path))
   8742         (setter (intern (concat (symbol-name symbol) "--set"))))
   8743     (cl-remf args :lsp-path)
   8744     `(progn
   8745        (lsp-register-custom-settings
   8746         (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type))))))
   8747 
   8748        (defcustom ,symbol ,standard ,doc ,@args)
   8749 
   8750        ;; Use a variable watcher instead of registering a `defcustom'
   8751        ;; setter since `hack-local-variables' is not aware of custom
   8752        ;; setters and won't invoke them.
   8753 
   8754        (defun ,setter (sym val op _where)
   8755          (when (eq op 'set)
   8756            (lsp--set-custom-property sym val ,path)))
   8757 
   8758        (add-variable-watcher ',symbol #',setter))))
   8759 
   8760 (defun lsp--set-custom-property (sym val path)
   8761   (set sym val)
   8762   (let ((section (cl-first (s-split "\\." path))))
   8763     (mapc (lambda (workspace)
   8764             (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace))
   8765                               section)
   8766               (with-lsp-workspace workspace
   8767                 (lsp--set-configuration (lsp-configuration-section section)))))
   8768           (lsp--session-workspaces (lsp-session)))))
   8769 
   8770 (defun lsp-configuration-section (section)
   8771   "Get settings for SECTION."
   8772   (let ((ret (ht-create)))
   8773     (maphash (-lambda (path (variable boolean?))
   8774                (when (s-matches? (concat (regexp-quote section) "\\..*") path)
   8775                  (let* ((symbol-value (-> variable
   8776                                           lsp-resolve-value
   8777                                           lsp-resolve-value))
   8778                         (value (if (and boolean? (not symbol-value))
   8779                                    :json-false
   8780                                  symbol-value)))
   8781                    (when (or boolean? value)
   8782                      (lsp-ht-set ret (s-split "\\." path) value)))))
   8783              lsp-client-settings)
   8784     ret))
   8785 
   8786 
   8787 (defun lsp--start-connection (session client project-root)
   8788   "Initiates connection created from CLIENT for PROJECT-ROOT.
   8789 SESSION is the active session."
   8790   (when (lsp--client-multi-root client)
   8791     (cl-pushnew project-root (gethash (lsp--client-server-id client)
   8792                                       (lsp-session-server-id->folders session))))
   8793   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)
   8794 
   8795   (unwind-protect
   8796       (lsp--start-workspace session client project-root (lsp--create-initialization-options session client))
   8797     (lsp--spinner-stop)))
   8798 
   8799 ;; lsp-log-io-mode
   8800 
   8801 (defvar lsp-log-io-mode-map
   8802   (let ((map (make-sparse-keymap)))
   8803     (define-key map (kbd "M-n") #'lsp-log-io-next)
   8804     (define-key map (kbd "M-p") #'lsp-log-io-prev)
   8805     (define-key map (kbd "k") #'lsp--erase-log-buffer)
   8806     (define-key map (kbd "K") #'lsp--erase-session-log-buffers)
   8807     map)
   8808   "Keymap for lsp log buffer mode.")
   8809 
   8810 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo"
   8811   "Special mode for viewing IO logs.")
   8812 
   8813 (defun lsp-workspace-show-log (workspace)
   8814   "Display the log buffer of WORKSPACE."
   8815   (interactive
   8816    (list (if lsp-log-io
   8817              (if (eq (length (lsp-workspaces)) 1)
   8818                  (cl-first (lsp-workspaces))
   8819                (lsp--completing-read "Workspace: " (lsp-workspaces)
   8820                                      #'lsp--workspace-print nil t))
   8821            (user-error "IO logging is disabled"))))
   8822   (pop-to-buffer (lsp--get-log-buffer-create workspace)))
   8823 
   8824 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log)
   8825 
   8826 (defun lsp--get-log-buffer-create (workspace)
   8827   "Return the lsp log buffer of WORKSPACE, creating a new one if needed."
   8828   (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8829          (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id)))
   8830     (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid))))
   8831 
   8832 (defun lsp--erase-log-buffer (&optional all)
   8833   "Delete contents of current lsp log buffer.
   8834 When ALL is t, erase all log buffers of the running session."
   8835   (interactive)
   8836   (let* ((workspaces (lsp--session-workspaces (lsp-session)))
   8837          (current-log-buffer (current-buffer)))
   8838     (dolist (w workspaces)
   8839       (let ((b (lsp--get-log-buffer-create w)))
   8840         (when (or all (eq b current-log-buffer))
   8841           (with-current-buffer b
   8842             (let ((inhibit-read-only t))
   8843               (erase-buffer))))))))
   8844 
   8845 (defun lsp--erase-session-log-buffers ()
   8846   "Erase log buffers of the running session."
   8847   (interactive)
   8848   (lsp--erase-log-buffer t))
   8849 
   8850 (defun lsp-log-io-next (arg)
   8851   "Move to next log entry."
   8852   (interactive "P")
   8853   (ewoc-goto-next lsp--log-io-ewoc (or arg 1)))
   8854 
   8855 (defun lsp-log-io-prev (arg)
   8856   "Move to previous log entry."
   8857   (interactive "P")
   8858   (ewoc-goto-prev lsp--log-io-ewoc (or arg 1)))
   8859 
   8860 
   8861 
   8862 (cl-defmethod lsp-process-id ((process process))
   8863   (process-id process))
   8864 
   8865 (cl-defmethod lsp-process-name ((process process)) (process-name process))
   8866 
   8867 (cl-defmethod lsp-process-status ((process process)) (process-status process))
   8868 
   8869 (cl-defmethod lsp-process-kill ((process process))
   8870   (when (process-live-p process)
   8871     (kill-process process)))
   8872 
   8873 (cl-defmethod lsp-process-send ((process process) message)
   8874   (condition-case err
   8875       (process-send-string process (lsp--make-message message))
   8876     (error (lsp--error "Sending to process failed with the following error: %s"
   8877                        (error-message-string err)))))
   8878 
   8879 (cl-defmethod lsp-process-cleanup (process)
   8880   ;; Kill standard error buffer only if the process exited normally.
   8881   ;; Leave it intact otherwise for debugging purposes.
   8882   (let ((buffer (-> process process-name get-buffer)))
   8883     (when (and (eq (process-status process) 'exit)
   8884                (zerop (process-exit-status process))
   8885                (buffer-live-p buffer))
   8886       (kill-buffer buffer))))
   8887 
   8888 
   8889 ;; native JSONRPC
   8890 
   8891 (declare-function json-rpc "ext:json")
   8892 (declare-function json-rpc-connection "ext:json")
   8893 (declare-function json-rpc-send "ext:json")
   8894 (declare-function json-rpc-shutdown "ext:json")
   8895 (declare-function json-rpc-stderr "ext:json")
   8896 (declare-function json-rpc-pid "ext:json")
   8897 
   8898 (defvar lsp-json-rpc-thread nil)
   8899 (defvar lsp-json-rpc-queue nil)
   8900 (defvar lsp-json-rpc-done nil)
   8901 (defvar lsp-json-rpc-mutex (make-mutex))
   8902 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex))
   8903 
   8904 (defun lsp-json-rpc-process-queue ()
   8905   (while (not lsp-json-rpc-done)
   8906     (while lsp-json-rpc-queue
   8907       (-let (((proc . message) (pop lsp-json-rpc-queue)))
   8908         (json-rpc-send
   8909          proc message
   8910          :null-object nil
   8911          :false-object :json-false)))
   8912     (with-mutex lsp-json-rpc-mutex
   8913       (condition-wait lsp-json-rpc-condition))))
   8914 
   8915 (cl-defmethod lsp-process-id (process) (json-rpc-pid process))
   8916 
   8917 (cl-defmethod lsp-process-name (_process) "TBD")
   8918 
   8919 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process))
   8920 
   8921 (cl-defmethod lsp-process-send (proc message)
   8922   (unless lsp-json-rpc-thread
   8923     (with-current-buffer (get-buffer-create " *json-rpc*")
   8924       (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*"))))
   8925 
   8926   (with-mutex lsp-json-rpc-mutex
   8927     (setq lsp-json-rpc-queue (append lsp-json-rpc-queue
   8928                                      (list (cons proc message))))
   8929     (condition-notify lsp-json-rpc-condition)))
   8930 
   8931 (cl-defmethod lsp-process-cleanup (_proc))
   8932 
   8933 (defun lsp-json-rpc-connection (workspace command)
   8934   (let ((con (apply #'json-rpc-connection command))
   8935         (object-type (if lsp-use-plists 'plist 'hash-table)))
   8936     (with-current-buffer (get-buffer-create " *json-rpc*")
   8937       (make-thread
   8938        (lambda ()
   8939          (json-rpc
   8940           con
   8941           (lambda (result err done)
   8942             (run-with-timer
   8943              0.0
   8944              nil
   8945              (lambda ()
   8946                (cond
   8947                 (result (lsp--parser-on-message result workspace))
   8948                 (err (warn "Json parsing failed with the following error: %s" err))
   8949                 (done (lsp--handle-process-exit workspace ""))))))
   8950           :object-type object-type
   8951           :null-object nil
   8952           :false-object nil))
   8953        "*json-rpc-connection*"))
   8954     (cons con con)))
   8955 
   8956 (defun lsp-json-rpc-stderr ()
   8957   (interactive)
   8958   (--when-let (pcase (lsp-workspaces)
   8959                 (`nil (user-error "There are no active servers in the current buffer"))
   8960                 (`(,workspace) workspace)
   8961                 (workspaces (lsp--completing-read "Select server: "
   8962                                                   workspaces
   8963                                                   'lsp--workspace-print nil t)))
   8964     (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it)))
   8965           (buffer (format "*stderr-%s*" (lsp--workspace-print it)) ))
   8966       (with-current-buffer (get-buffer-create buffer)
   8967         (with-help-window buffer
   8968           (insert content))))))
   8969 
   8970 
   8971 (defun lsp--workspace-print (workspace)
   8972   "Visual representation WORKSPACE."
   8973   (let* ((proc (lsp--workspace-cmd-proc workspace))
   8974          (status (lsp--workspace-status workspace))
   8975          (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8976          (pid (lsp-process-id proc)))
   8977 
   8978     (if (eq 'initialized status)
   8979         (format "%s:%s" server-id pid)
   8980       (format "%s:%s/%s" server-id pid status))))
   8981 
   8982 (defun lsp--map-tree-widget (m)
   8983   "Build `tree-widget' from a hash-table or plist M."
   8984   (when (lsp-structure-p m)
   8985     (let (nodes)
   8986       (lsp-map (lambda (k v)
   8987                  (push `(tree-widget
   8988                          :tag ,(if (lsp-structure-p v)
   8989                                    (format "%s:" k)
   8990                                  (format "%s: %s" k
   8991                                          (propertize (format "%s" v)
   8992                                                      'face
   8993                                                      'font-lock-string-face)))
   8994                          :open t
   8995                          ,@(lsp--map-tree-widget v))
   8996                        nodes))
   8997                m)
   8998       nodes)))
   8999 
   9000 (defun lsp-buffer-name (buffer-id)
   9001   (if-let* ((buffer-name (plist-get buffer-id :buffer-name)))
   9002       (funcall buffer-name buffer-id)
   9003     (buffer-name buffer-id)))
   9004 
   9005 (defun lsp--render-workspace (workspace)
   9006   "Tree node representation of WORKSPACE."
   9007   `(tree-widget :tag ,(lsp--workspace-print workspace)
   9008                 :open t
   9009                 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face)
   9010                              :open t
   9011                              ,@(->> workspace
   9012                                     (lsp--workspace-buffers)
   9013                                     (--map `(tree-widget
   9014                                              :tag ,(when (lsp-buffer-live-p it)
   9015                                                      (let ((buffer-name (lsp-buffer-name it)))
   9016                                                        (if (lsp-with-current-buffer it buffer-read-only)
   9017                                                            (propertize buffer-name 'face 'font-lock-constant-face)
   9018                                                          buffer-name)))))))
   9019                 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face)
   9020                              ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget))))
   9021 
   9022 (define-derived-mode lsp-browser-mode special-mode "LspBrowser"
   9023   "Define mode for displaying lsp sessions."
   9024   (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t)))))
   9025 
   9026 (defun lsp-describe-session ()
   9027   "Describes current `lsp-session'."
   9028   (interactive)
   9029   (let ((session (lsp-session))
   9030         (buf (get-buffer-create "*lsp session*"))
   9031         (root (lsp-workspace-root)))
   9032     (with-current-buffer buf
   9033       (lsp-browser-mode)
   9034       (let ((inhibit-read-only t))
   9035         (erase-buffer)
   9036         (--each (lsp-session-folders session)
   9037           (widget-create
   9038            `(tree-widget
   9039              :tag ,(propertize it 'face 'font-lock-keyword-face)
   9040              :open t
   9041              ,@(->> session
   9042                     (lsp-session-folder->servers)
   9043                     (gethash it)
   9044                     (-map 'lsp--render-workspace)))))))
   9045     (pop-to-buffer buf)
   9046     (goto-char (point-min))
   9047     (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag)
   9048              until (or (and root (string= tag root)) (eobp))
   9049              do (goto-char (next-overlay-change (point))))))
   9050 
   9051 (defun lsp--session-workspaces (session)
   9052   "Get all workspaces that are part of the SESSION."
   9053   (-> session lsp-session-folder->servers hash-table-values -flatten -uniq))
   9054 
   9055 (defun lsp--find-multiroot-workspace (session client project-root)
   9056   "Look for a multiroot connection in SESSION created from CLIENT for
   9057 PROJECT-ROOT and BUFFER-MAJOR-MODE."
   9058   (when (lsp--client-multi-root client)
   9059     (-when-let (multi-root-workspace (->> session
   9060                                           (lsp--session-workspaces)
   9061                                           (--first (eq (-> it lsp--workspace-client lsp--client-server-id)
   9062                                                        (lsp--client-server-id client)))))
   9063       (with-lsp-workspace multi-root-workspace
   9064         (lsp-notify "workspace/didChangeWorkspaceFolders"
   9065                     (lsp-make-did-change-workspace-folders-params
   9066                      :event (lsp-make-workspace-folders-change-event
   9067                              :added (vector (lsp-make-workspace-folder
   9068                                              :uri (lsp--path-to-uri project-root)
   9069                                              :name (f-filename project-root)))
   9070                              :removed []))))
   9071 
   9072       (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace))
   9073       (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root))
   9074 
   9075       (lsp--persist-session session)
   9076 
   9077       (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace))
   9078       (lsp--open-in-workspace multi-root-workspace)
   9079 
   9080       multi-root-workspace)))
   9081 
   9082 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder)
   9083   "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT.
   9084 IGNORE-MULTI-FOLDER to ignore multi folder server."
   9085   (-map (lambda (client)
   9086           (or
   9087            (lsp--find-workspace session client project-root)
   9088            (unless ignore-multi-folder
   9089              (lsp--find-multiroot-workspace session client project-root))
   9090            (lsp--start-connection session client project-root)))
   9091         clients))
   9092 
   9093 (defun lsp--spinner-stop ()
   9094   "Stop the spinner in case all of the workspaces are started."
   9095   (when (--all? (eq (lsp--workspace-status it) 'initialized)
   9096                 lsp--buffer-workspaces)
   9097     (spinner-stop)))
   9098 
   9099 (defun lsp--open-in-workspace (workspace)
   9100   "Open in existing WORKSPACE."
   9101   (if (eq 'initialized (lsp--workspace-status workspace))
   9102       ;; when workspace is initialized just call document did open.
   9103       (progn
   9104         (with-lsp-workspace workspace
   9105           (when-let* ((before-document-open-fn (-> workspace
   9106                                                   lsp--workspace-client
   9107                                                   lsp--client-before-file-open-fn)))
   9108             (funcall before-document-open-fn workspace))
   9109           (lsp--text-document-did-open))
   9110         (lsp--spinner-stop))
   9111     ;; when it is not initialized
   9112     (lsp--spinner-start)
   9113     (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace))))
   9114 
   9115 (defun lsp--find-workspace (session client project-root)
   9116   "Find server connection created with CLIENT in SESSION for PROJECT-ROOT."
   9117   (when-let* ((workspace (->> session
   9118                              (lsp-session-folder->servers)
   9119                              (gethash project-root)
   9120                              (--first (eql (-> it lsp--workspace-client lsp--client-server-id)
   9121                                            (lsp--client-server-id client))))))
   9122     (lsp--open-in-workspace workspace)
   9123     workspace))
   9124 
   9125 (defun lsp--read-char (prompt &optional options)
   9126   "Wrapper for `read-char-from-minibuffer' if Emacs +27.
   9127 Fallback to `read-key' otherwise.
   9128 PROMPT is the message and OPTIONS the available options."
   9129   (if (fboundp 'read-char-from-minibuffer)
   9130       (read-char-from-minibuffer prompt options)
   9131     (read-key prompt)))
   9132 
   9133 (defun lsp--find-root-interactively (session)
   9134   "Find project interactively.
   9135 Returns nil if the project should not be added to the current SESSION."
   9136   (condition-case nil
   9137       (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory))
   9138              (action (lsp--read-char
   9139                       (format
   9140                        "%s is not part of any project.
   9141 
   9142 %s ==> Import project root %s
   9143 %s ==> Import project by selecting root directory interactively
   9144 %s ==> Import project at current directory %s
   9145 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist
   9146 %s ==> Do not ask again for the current project by selecting ignore path interactively
   9147 %s ==> Do nothing: ask again when opening other files from the current project
   9148 
   9149 Select action: "
   9150                        (propertize (buffer-name) 'face 'bold)
   9151                        (propertize "i" 'face 'success)
   9152                        (propertize project-root-suggestion 'face 'bold)
   9153                        (propertize "I" 'face 'success)
   9154                        (propertize "." 'face 'success)
   9155                        (propertize default-directory 'face 'bold)
   9156                        (propertize "d" 'face 'warning)
   9157                        (propertize project-root-suggestion 'face 'bold)
   9158                        (propertize "D" 'face 'warning)
   9159                        (propertize "n" 'face 'warning))
   9160                       '(?i ?\r ?I ?. ?d ?D ?n))))
   9161         (cl-case action
   9162           (?i project-root-suggestion)
   9163           (?\r project-root-suggestion)
   9164           (?I (read-directory-name "Select workspace folder to add: "
   9165                                    (or project-root-suggestion default-directory)
   9166                                    nil
   9167                                    t))
   9168           (?. default-directory)
   9169           (?d (push project-root-suggestion (lsp-session-folders-blocklist session))
   9170               (lsp--persist-session session)
   9171               nil)
   9172           (?D (push (read-directory-name "Select folder to blocklist: "
   9173                                          (or project-root-suggestion default-directory)
   9174                                          nil
   9175                                          t)
   9176                     (lsp-session-folders-blocklist session))
   9177               (lsp--persist-session session)
   9178               nil)
   9179           (t nil)))
   9180     (quit)))
   9181 
   9182 (declare-function tramp-file-name-host "ext:tramp" (file) t)
   9183 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault))
   9184 
   9185 (defun lsp--files-same-host (f1 f2)
   9186   "Predicate on whether or not two files are on the same host."
   9187   (or (not (or (file-remote-p f1) (file-remote-p f2)))
   9188       (and (file-remote-p f1)
   9189            (file-remote-p f2)
   9190            (progn (require 'tramp)
   9191                   (equal (tramp-file-name-host (tramp-dissect-file-name f1))
   9192                          (tramp-file-name-host (tramp-dissect-file-name f2)))))))
   9193 
   9194 (defun lsp-find-session-folder (session file-name)
   9195   "Look in the current SESSION for folder containing FILE-NAME."
   9196   (let ((file-name-canonical (lsp-f-canonical file-name)))
   9197     (->> session
   9198          (lsp-session-folders)
   9199          (--filter (and (lsp--files-same-host it file-name-canonical)
   9200                         (or (lsp-f-same? it file-name-canonical)
   9201                             (and (f-dir? it)
   9202                                  (lsp-f-ancestor-of? it file-name-canonical)))))
   9203          (--max-by (> (length it)
   9204                       (length other))))))
   9205 
   9206 (defun lsp-find-workspace (server-id &optional file-name)
   9207   "Find workspace for SERVER-ID for FILE-NAME."
   9208   (-when-let* ((session (lsp-session))
   9209                (folder->servers (lsp-session-folder->servers session))
   9210                (workspaces (if file-name
   9211                                (gethash (lsp-find-session-folder session file-name) folder->servers)
   9212                              (lsp--session-workspaces session))))
   9213 
   9214     (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces)))
   9215 
   9216 (defun lsp--calculate-root (session file-name)
   9217   "Calculate project root for FILE-NAME in SESSION."
   9218   (and
   9219    (->> session
   9220         (lsp-session-folders-blocklist)
   9221         (--first (and (lsp--files-same-host it file-name)
   9222                       (lsp-f-ancestor-of? it file-name)
   9223                       (prog1 t
   9224                         (lsp--info "File %s is in blocklisted directory %s" file-name it))))
   9225         not)
   9226    (or
   9227     (when lsp-auto-guess-root
   9228       (lsp--suggest-project-root))
   9229     (unless lsp-guess-root-without-session
   9230       (lsp-find-session-folder session file-name))
   9231     (unless lsp-auto-guess-root
   9232       (when-let* ((root-folder (lsp--find-root-interactively session)))
   9233         (if (or (not (f-equal? root-folder (expand-file-name "~/")))
   9234                 (yes-or-no-p
   9235                  (concat
   9236                   (propertize "[WARNING] " 'face 'warning)
   9237                   "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:
   9238 
   9239 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/').
   9240 2. If your file is under `~/' then create a subfolder and move that file in this folder.
   9241 
   9242 Type `No' to go back to project selection.
   9243 Type `Yes' to confirm `HOME' as project root.
   9244 Type `C-g' to cancel project import process and stop `lsp'")))
   9245             root-folder
   9246           (lsp--calculate-root session file-name)))))))
   9247 
   9248 (defun lsp--try-open-in-library-workspace ()
   9249   "Try opening current file as library file in any of the active workspace.
   9250 The library folders are defined by each client for each of the active workspace."
   9251   (when-let* ((workspace (->> (lsp-session)
   9252                              (lsp--session-workspaces)
   9253                              ;; Sort the last active workspaces first as they are more likely to be
   9254                              ;; the correct ones, especially when jumping to a definition.
   9255                              (-sort (lambda (a _b)
   9256                                       (-contains? lsp--last-active-workspaces a)))
   9257                              (--first
   9258                               (and (-> it lsp--workspace-client lsp--supports-buffer?)
   9259                                    (when-let* ((library-folders-fn
   9260                                                (-> it lsp--workspace-client lsp--client-library-folders-fn)))
   9261                                      (-first (lambda (library-folder)
   9262                                                (lsp-f-ancestor-of? library-folder (buffer-file-name)))
   9263                                              (funcall library-folders-fn it))))))))
   9264     (lsp--open-in-workspace workspace)
   9265     (view-mode t)
   9266     (lsp--info "Opening read-only library file %s." (buffer-file-name))
   9267     (list workspace)))
   9268 
   9269 (defun lsp--persist-session (session)
   9270   "Persist SESSION to `lsp-session-file'."
   9271   (lsp--persist lsp-session-file (make-lsp-session
   9272                                   :folders (lsp-session-folders session)
   9273                                   :folders-blocklist (lsp-session-folders-blocklist session)
   9274                                   :server-id->folders (lsp-session-server-id->folders session))))
   9275 
   9276 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder)
   9277   "Try create opening file as a project file.
   9278 When IGNORE-MULTI-FOLDER is t the lsp mode will start new
   9279 language server even if there is language server which can handle
   9280 current language. When IGNORE-MULTI-FOLDER is nil current file
   9281 will be opened in multi folder language server if there is
   9282 such."
   9283   (-let ((session (lsp-session)))
   9284     (-if-let (clients (if ask-for-client
   9285                           (list (lsp--completing-read "Select server to start: "
   9286                                                       (ht-values lsp-clients)
   9287                                                       (-compose 'symbol-name 'lsp--client-server-id) nil t))
   9288                         (lsp--find-clients)))
   9289         (-if-let (project-root (-some-> session
   9290                                  (lsp--calculate-root (buffer-file-name))
   9291                                  (lsp-f-canonical)))
   9292             (progn
   9293               ;; update project roots if needed and persist the lsp session
   9294               (unless (-contains? (lsp-session-folders session) project-root)
   9295                 (cl-pushnew project-root (lsp-session-folders session))
   9296                 (lsp--persist-session session))
   9297               (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder))
   9298           (lsp--warn "%s not in project or it is blocklisted." (buffer-name))
   9299           nil)
   9300       (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode)
   9301       nil)))
   9302 
   9303 (defun lsp-shutdown-workspace ()
   9304   "Shutdown language server."
   9305   (interactive)
   9306   (--when-let (pcase (lsp-workspaces)
   9307                 (`nil (user-error "There are no active servers in the current buffer"))
   9308                 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?"
   9309                                                        (lsp--workspace-print workspace)))
   9310                                  workspace))
   9311                 (workspaces (lsp--completing-read "Select server: "
   9312                                                   workspaces
   9313                                                   'lsp--workspace-print nil t)))
   9314     (lsp-workspace-shutdown it)))
   9315 
   9316 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1")
   9317 
   9318 (defcustom lsp-auto-select-workspace t
   9319   "Shutdown or restart a single workspace.
   9320 If set and the current buffer has only a single workspace
   9321 associated with it, `lsp-shutdown-workspace' and
   9322 `lsp-restart-workspace' will act on it without asking."
   9323   :type 'boolean
   9324   :group 'lsp-mode)
   9325 
   9326 (defun lsp--read-workspace ()
   9327   "Ask the user to select a workspace.
   9328 Errors if there are none."
   9329   (pcase (lsp-workspaces)
   9330     (`nil (error "No workspaces associated with the current buffer"))
   9331     ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace)
   9332     (workspaces (lsp--completing-read "Select workspace: " workspaces
   9333                                       #'lsp--workspace-print nil t))))
   9334 
   9335 (defun lsp-workspace-shutdown (workspace)
   9336   "Shut the workspace WORKSPACE and the language server associated with it"
   9337   (interactive (list (lsp--read-workspace)))
   9338   (lsp--warn "Stopping %s" (lsp--workspace-print workspace))
   9339   (with-lsp-workspace workspace (lsp--shutdown-workspace)))
   9340 
   9341 (defun lsp-disconnect ()
   9342   "Disconnect the buffer from the language server."
   9343   (interactive)
   9344   (lsp--text-document-did-close t)
   9345   (lsp-managed-mode -1)
   9346   (lsp-mode -1)
   9347   (setq lsp--buffer-workspaces nil)
   9348   (lsp--info "Disconnected"))
   9349 
   9350 (defun lsp-restart-workspace ()
   9351   (interactive)
   9352   (--when-let (pcase (lsp-workspaces)
   9353                 (`nil (user-error "There are no active servers in the current buffer"))
   9354                 (`(,workspace) workspace)
   9355                 (workspaces (lsp--completing-read "Select server: "
   9356                                                   workspaces
   9357                                                   'lsp--workspace-print nil t)))
   9358     (lsp-workspace-restart it)))
   9359 
   9360 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1")
   9361 
   9362 (defun lsp-workspace-restart (workspace)
   9363   "Restart the workspace WORKSPACE and the language server associated with it"
   9364   (interactive (list (lsp--read-workspace)))
   9365   (lsp--warn "Restarting %s" (lsp--workspace-print workspace))
   9366   (with-lsp-workspace workspace (lsp--shutdown-workspace t)))
   9367 
   9368 ;;;###autoload
   9369 (defun lsp (&optional arg)
   9370   "Entry point for the server startup.
   9371 When ARG is t the lsp mode will start new language server even if
   9372 there is language server which can handle current language. When
   9373 ARG is nil current file will be opened in multi folder language
   9374 server if there is such. When `lsp' is called with prefix
   9375 argument ask the user to select which language server to start."
   9376   (interactive "P")
   9377 
   9378   (lsp--require-packages)
   9379 
   9380   (when (buffer-file-name)
   9381     (let (clients
   9382           (matching-clients (lsp--filter-clients
   9383                              (-andfn #'lsp--supports-buffer?
   9384                                      #'lsp--server-binary-present?))))
   9385       (cond
   9386        (matching-clients
   9387         (when (setq lsp--buffer-workspaces
   9388                     (or (and
   9389                          ;; Don't open as library file if file is part of a project.
   9390                          (not (lsp-find-session-folder (lsp-session) (buffer-file-name)))
   9391                          (lsp--try-open-in-library-workspace))
   9392                         (lsp--try-project-root-workspaces (equal arg '(4))
   9393                                                           (and arg (not (equal arg 1))))))
   9394           (lsp-mode 1)
   9395           (when lsp-auto-configure (lsp--auto-configure))
   9396           (setq lsp-buffer-uri (lsp--buffer-uri))
   9397           (lsp--info "Connected to %s."
   9398                      (apply 'concat (--map (format "[%s %s]"
   9399                                                    (lsp--workspace-print it)
   9400                                                    (lsp--workspace-root it))
   9401                                            lsp--buffer-workspaces)))))
   9402        ;; look for servers which are currently being downloaded.
   9403        ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9404                                                    #'lsp--client-download-in-progress?)))
   9405         (lsp--info "There are language server(%s) installation in progress.
   9406 The server(s) will be started in the buffer when it has finished."
   9407                    (-map #'lsp--client-server-id clients))
   9408         (seq-do (lambda (client)
   9409                   (cl-pushnew (current-buffer) (lsp--client-buffers client)))
   9410                 clients))
   9411        ;; look for servers to install
   9412        ((setq clients (lsp--filter-clients
   9413                        (-andfn #'lsp--supports-buffer?
   9414                                (-const lsp-enable-suggest-server-download)
   9415                                #'lsp--client-download-server-fn
   9416                                (-not #'lsp--client-download-in-progress?))))
   9417         (let ((client (lsp--completing-read
   9418                        (concat "Unable to find installed server supporting this file. "
   9419                                "The following servers could be installed automatically: ")
   9420                        clients
   9421                        (-compose #'symbol-name #'lsp--client-server-id)
   9422                        nil
   9423                        t)))
   9424           (cl-pushnew (current-buffer) (lsp--client-buffers client))
   9425           (lsp--install-server-internal client)))
   9426        ;; ignore other warnings
   9427        ((not lsp-warn-no-matched-clients)
   9428         nil)
   9429        ;; automatic installation disabled
   9430        ((setq clients (unless matching-clients
   9431                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9432                                                      #'lsp--client-download-server-fn
   9433                                                      (-not (-const lsp-enable-suggest-server-download))
   9434                                                      (-not #'lsp--server-binary-present?)))))
   9435         (lsp--warn "The following servers support current file but automatic download is disabled: %s
   9436 \(If you have already installed the server check *lsp-log*)."
   9437                    (mapconcat (lambda (client)
   9438                                 (symbol-name (lsp--client-server-id client)))
   9439                               clients
   9440                               " ")))
   9441        ;; no clients present
   9442        ((setq clients (unless matching-clients
   9443                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9444                                                      (-not #'lsp--server-binary-present?)))))
   9445         (lsp--warn "The following servers support current file but do not have automatic installation: %s
   9446 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages.
   9447 \(If you have already installed the server check *lsp-log*)."
   9448                    (mapconcat (lambda (client)
   9449                                 (symbol-name (lsp--client-server-id client)))
   9450                               clients
   9451                               " ")))
   9452        ;; no matches
   9453        ((-> #'lsp--supports-buffer? lsp--filter-clients not)
   9454         (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'.
   9455 This issue might be caused by:
   9456 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'.
   9457 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'.
   9458 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/ .
   9459 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/.
   9460 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients').
   9461 You can customize `lsp-warn-no-matched-clients' to disable this message."
   9462                     major-mode major-mode major-mode))))))
   9463 
   9464 (defun lsp--buffer-visible-p ()
   9465   "Return non nil if current buffer is visible."
   9466   (or (buffer-modified-p) (get-buffer-window nil t)))
   9467 
   9468 (defun lsp--init-if-visible ()
   9469   "Run `lsp' for the current buffer if the buffer is visible.
   9470 Returns non nil if `lsp' was run for the buffer."
   9471   (when (lsp--buffer-visible-p)
   9472     (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t)
   9473     (lsp)
   9474     t))
   9475 
   9476 ;;;###autoload
   9477 (defun lsp-deferred ()
   9478   "Entry point that defers server startup until buffer is visible.
   9479 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'.
   9480 This avoids overloading the server with many files when starting Emacs."
   9481   ;; Workspace may not be initialized yet. Use a buffer local variable to
   9482   ;; remember that we deferred loading of this buffer.
   9483   (setq lsp--buffer-deferred t)
   9484   (let ((buffer (current-buffer)))
   9485     ;; Avoid false positives as desktop-mode restores buffers by deferring
   9486     ;; visibility check until the stack clears.
   9487     (run-with-idle-timer 0 nil (lambda ()
   9488                                  (when (buffer-live-p buffer)
   9489                                    (with-current-buffer buffer
   9490                                      (unless (lsp--init-if-visible)
   9491                                        (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t))))))))
   9492 
   9493 
   9494 
   9495 (defvar lsp-file-truename-cache (ht))
   9496 
   9497 (defmacro lsp-with-cached-filetrue-name (&rest body)
   9498   "Executes BODY caching the `file-truename' calls."
   9499   `(let ((old-fn (symbol-function 'file-truename)))
   9500      (unwind-protect
   9501          (progn
   9502            (fset 'file-truename
   9503                  (lambda (file-name &optional counter prev-dirs)
   9504                    (or (gethash file-name lsp-file-truename-cache)
   9505                        (puthash file-name (apply old-fn (list file-name counter prev-dirs))
   9506                                 lsp-file-truename-cache))))
   9507            ,@body)
   9508        (fset 'file-truename old-fn))))
   9509 
   9510 
   9511 (defun lsp-virtual-buffer-call (key &rest args)
   9512   (when lsp--virtual-buffer
   9513     (when-let* ((fn (plist-get lsp--virtual-buffer key)))
   9514       (apply fn args))))
   9515 
   9516 (defun lsp-translate-column (column)
   9517   "Translate COLUMN taking into account virtual buffers."
   9518   (or (lsp-virtual-buffer-call :real->virtual-char column)
   9519       column))
   9520 
   9521 (defun lsp-translate-line (line)
   9522   "Translate LINE taking into account virtual buffers."
   9523   (or (lsp-virtual-buffer-call :real->virtual-line line)
   9524       line))
   9525 
   9526 
   9527 ;; lsp internal validation.
   9528 
   9529 (defmacro lsp--doctor (&rest checks)
   9530   `(-let [buf (current-buffer)]
   9531      (with-current-buffer (get-buffer-create "*lsp-performance*")
   9532        (with-help-window (current-buffer)
   9533          ,@(-map (-lambda ((msg form))
   9534                    `(insert (format "%s: %s\n" ,msg
   9535                                     (let ((res (with-current-buffer buf
   9536                                                  ,form)))
   9537                                       (cond
   9538                                        ((eq res :optional) (propertize "OPTIONAL" 'face 'warning))
   9539                                        (res (propertize "OK" 'face 'success))
   9540                                        (t (propertize "ERROR" 'face 'error)))))))
   9541                  (-partition 2 checks))))))
   9542 
   9543 (define-obsolete-function-alias 'lsp-diagnose
   9544   'lsp-doctor "lsp-mode 8.0.0")
   9545 
   9546 (defun lsp-doctor ()
   9547   "Validate performance settings."
   9548   (interactive)
   9549   (lsp--doctor
   9550    "Checking for Native JSON support" (functionp 'json-serialize)
   9551    "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max)
   9552    "Check `read-process-output-max' default has been changed from 4k"
   9553    (and (boundp 'read-process-output-max)
   9554         (> read-process-output-max 4096))
   9555    "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)"
   9556    (condition-case _err
   9557        (progn (lsp--make-message (list "a" "b"))
   9558               nil)
   9559      (error t))
   9560    "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000)
   9561    "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)
   9562    "Using emacs 28+ with native compilation?"
   9563    (or (and (fboundp 'native-comp-available-p)
   9564             (native-comp-available-p))
   9565        :optional)))
   9566 
   9567 (declare-function package-version-join "ext:package")
   9568 (declare-function package-desc-version "ext:package")
   9569 (declare-function package--alist "ext:package")
   9570 
   9571 (defun lsp-version ()
   9572   "Return string describing current version of `lsp-mode'."
   9573   (interactive)
   9574   (unless (featurep 'package)
   9575     (require 'package))
   9576   (let ((ver (format "lsp-mode %s, Emacs %s, %s"
   9577                      (package-version-join
   9578                       (package-desc-version
   9579                        (car (alist-get 'lsp-mode (package--alist)))))
   9580                      emacs-version
   9581                      system-type)))
   9582     (if (called-interactively-p 'interactive)
   9583         (lsp--info "%s" ver)
   9584       ver)))
   9585 
   9586 
   9587 
   9588 ;; org-mode/virtual-buffer
   9589 
   9590 (declare-function org-babel-get-src-block-info "ext:ob-core")
   9591 (declare-function org-do-remove-indentation "ext:org-macs")
   9592 (declare-function org-src-get-lang-mode "ext:org-src")
   9593 (declare-function org-element-context "ext:org-element")
   9594 
   9595 (defun lsp--virtual-buffer-update-position ()
   9596   (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range))
   9597                                      (funcall in-range))
   9598                                    lsp--virtual-buffer-connections))
   9599       (unless (equal virtual-buffer lsp--virtual-buffer)
   9600         (lsp-org))
   9601     (when lsp-managed-mode
   9602       (lsp-managed-mode -1)
   9603       (lsp-mode -1)
   9604       (setq lsp--buffer-workspaces nil)
   9605       (setq lsp--virtual-buffer nil)
   9606       (setq lsp-buffer-uri nil)
   9607 
   9608       ;; force refresh of diagnostics
   9609       (run-hooks 'lsp-after-diagnostics-hook))))
   9610 
   9611 (defun lsp-virtual-buffer-on-change (start end length)
   9612   "Adjust on change event to be executed against the proper language server."
   9613   (let ((max-point (max end
   9614                         (or (plist-get lsp--before-change-vals :end) 0)
   9615                         (+ start length))))
   9616     (when-let* ((virtual-buffer (-first (lambda (vb)
   9617                                          (let ((lsp--virtual-buffer vb))
   9618                                            (and (lsp-virtual-buffer-call :in-range start)
   9619                                                 (lsp-virtual-buffer-call :in-range max-point))))
   9620                                        lsp--virtual-buffer-connections)))
   9621       (lsp-with-current-buffer virtual-buffer
   9622         (lsp-on-change start end length
   9623                        (lambda (&rest _)
   9624                          (list :range (lsp--range (list :character 0 :line 0)
   9625                                                   lsp--virtual-buffer-point-max)
   9626                                :text (lsp--buffer-content))))))))
   9627 
   9628 (defun lsp-virtual-buffer-before-change (start _end)
   9629   (when-let* ((virtual-buffer (-first (lambda (vb)
   9630                                        (lsp-with-current-buffer vb
   9631                                          (lsp-virtual-buffer-call :in-range start)))
   9632                                      lsp--virtual-buffer-connections)))
   9633     (lsp-with-current-buffer virtual-buffer
   9634       (setq lsp--virtual-buffer-point-max
   9635             (lsp--point-to-position (lsp-virtual-buffer-call :last-point))))))
   9636 
   9637 (defun lsp-patch-on-change-event ()
   9638   (remove-hook 'after-change-functions #'lsp-on-change t)
   9639   (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t)
   9640   (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t))
   9641 
   9642 (defun lsp-kill-virtual-buffers ()
   9643   (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections))
   9644 
   9645 (defun lsp--move-point-in-indentation (point indentation)
   9646   (save-excursion
   9647     (goto-char point)
   9648     (if (<= point (+ (line-beginning-position) indentation))
   9649         (line-beginning-position)
   9650       point)))
   9651 
   9652 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck")
   9653 (declare-function flycheck-add-mode "ext:flycheck")
   9654 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics")
   9655 
   9656 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn)
   9657 
   9658 (defun lsp-flycheck-add-mode (mode)
   9659   "Register flycheck support for MODE."
   9660   (lsp-diagnostics-lsp-checker-if-needed)
   9661   (unless (flycheck-checker-supports-major-mode-p 'lsp mode)
   9662     (flycheck-add-mode 'lsp mode)))
   9663 
   9664 (defun lsp-progress-spinner-type ()
   9665   "Retrieve the spinner type value, if value is not a symbol of `spinner-types
   9666 defaults to `progress-bar."
   9667   (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar))
   9668 
   9669 (defun lsp-org ()
   9670   (interactive)
   9671   (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range))
   9672                                                               (funcall in-range))
   9673                                                             lsp--virtual-buffer-connections))
   9674       (unless (equal lsp--virtual-buffer virtual-buffer)
   9675         (setq lsp--buffer-workspaces workspaces)
   9676         (setq lsp--virtual-buffer virtual-buffer)
   9677         (setq lsp-buffer-uri nil)
   9678         (lsp-mode 1)
   9679         (lsp-managed-mode 1)
   9680         (lsp-patch-on-change-event))
   9681 
   9682     (save-excursion
   9683       (-let* (virtual-buffer
   9684               (wcb (lambda (f)
   9685                      (with-current-buffer (plist-get virtual-buffer :buffer)
   9686                        (-let* (((&plist :major-mode :buffer-file-name
   9687                                         :goto-buffer :workspaces) virtual-buffer)
   9688                                (lsp--virtual-buffer virtual-buffer)
   9689                                (lsp--buffer-workspaces workspaces))
   9690                          (save-excursion
   9691                            (funcall goto-buffer)
   9692                            (funcall f))))))
   9693               ((&plist :begin :end :post-blank :language) (cl-second (org-element-context)))
   9694               ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light)))
   9695 
   9696               (file-name (if file-name
   9697                              (f-expand file-name)
   9698                            (user-error "You should specify file name in the src block header.")))
   9699               (begin-marker (progn
   9700                               (goto-char begin)
   9701                               (forward-line)
   9702                               (set-marker (make-marker) (point))))
   9703               (end-marker (progn
   9704                             (goto-char end)
   9705                             (forward-line (1- (- post-blank)))
   9706                             (set-marker (make-marker) (1+ (point)))))
   9707               (buf (current-buffer))
   9708               (src-block (buffer-substring-no-properties begin-marker
   9709                                                          (1- end-marker)))
   9710               (indentation (with-temp-buffer
   9711                              (insert src-block)
   9712 
   9713                              (goto-char (point-min))
   9714                              (let ((indentation (current-indentation)))
   9715                                (plist-put lsp--virtual-buffer :indentation indentation)
   9716                                (org-do-remove-indentation)
   9717                                (goto-char (point-min))
   9718                                (- indentation (current-indentation))))))
   9719         (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t)
   9720 
   9721         (when (fboundp 'flycheck-add-mode)
   9722           (lsp-flycheck-add-mode 'org-mode))
   9723 
   9724         (setq lsp--virtual-buffer
   9725               (list
   9726                :in-range (lambda (&optional point)
   9727                            (<= begin-marker (or point (point)) (1- end-marker)))
   9728                :goto-buffer (lambda () (goto-char begin-marker))
   9729                :buffer-string
   9730                (lambda ()
   9731                  (let ((src-block (buffer-substring-no-properties
   9732                                    begin-marker
   9733                                    (1- end-marker))))
   9734                    (with-temp-buffer
   9735                      (insert src-block)
   9736 
   9737                      (goto-char (point-min))
   9738                      (while (not (eobp))
   9739                        (delete-region (point) (if (> (+ (point) indentation) (line-end-position))
   9740                                                   (line-end-position)
   9741                                                 (+ (point) indentation)))
   9742                        (forward-line))
   9743                      (buffer-substring-no-properties (point-min)
   9744                                                      (point-max)))))
   9745                :buffer buf
   9746                :begin begin-marker
   9747                :end end-marker
   9748                :indentation indentation
   9749                :last-point (lambda () (1- end-marker))
   9750                :cur-position (lambda ()
   9751                                (lsp-save-restriction-and-excursion
   9752                                  (list :line (- (lsp--cur-line)
   9753                                                 (lsp--cur-line begin-marker))
   9754                                        :character (let ((character (- (point)
   9755                                                                       (line-beginning-position)
   9756                                                                       indentation)))
   9757                                                     (if (< character 0)
   9758                                                         0
   9759                                                       character)))))
   9760                :line/character->point (-lambda (line character)
   9761                                         (-let [inhibit-field-text-motion t]
   9762                                           (+ indentation
   9763                                              (lsp-save-restriction-and-excursion
   9764                                                (goto-char begin-marker)
   9765                                                (forward-line line)
   9766                                                (-let [line-end (line-end-position)]
   9767                                                  (if (> character (- line-end (point)))
   9768                                                      line-end
   9769                                                    (forward-char character)
   9770                                                    (point)))))))
   9771                :major-mode (org-src-get-lang-mode language)
   9772                :buffer-file-name file-name
   9773                :buffer-uri (lsp--path-to-uri file-name)
   9774                :with-current-buffer wcb
   9775                :buffer-live? (lambda (_) (buffer-live-p buf))
   9776                :buffer-name (lambda (_)
   9777                               (propertize (format "%s(%s:%s)%s"
   9778                                                   (buffer-name buf)
   9779                                                   begin-marker
   9780                                                   end-marker
   9781                                                   language)
   9782                                           'face 'italic))
   9783                :real->virtual-line (lambda (line)
   9784                                      (+ line (line-number-at-pos begin-marker) -1))
   9785                :real->virtual-char (lambda (char) (+ char indentation))
   9786                :cleanup (lambda ()
   9787                           (set-marker begin-marker nil)
   9788                           (set-marker end-marker nil))))
   9789         (setf virtual-buffer lsp--virtual-buffer)
   9790         (puthash file-name virtual-buffer lsp--virtual-buffer-mappings)
   9791         (push virtual-buffer lsp--virtual-buffer-connections)
   9792 
   9793         ;; TODO: tangle only connected sections
   9794         (add-hook 'after-save-hook 'org-babel-tangle nil t)
   9795         (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t)
   9796         (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t)
   9797 
   9798         (setq lsp--buffer-workspaces
   9799               (lsp-with-current-buffer virtual-buffer
   9800                 (lsp)
   9801                 (plist-put virtual-buffer :workspaces (lsp-workspaces))
   9802                 (lsp-workspaces)))))))
   9803 
   9804 (defun lsp-virtual-buffer-disconnect (virtual-buffer)
   9805   (interactive (list (or
   9806                       lsp--virtual-buffer
   9807                       (when lsp--virtual-buffer-connections
   9808                         (lsp--completing-read "Select virtual buffer to disconnect: "
   9809                                               lsp--virtual-buffer-connections
   9810                                               (-lambda ((&plist :buffer-file-name))
   9811                                                 buffer-file-name))))))
   9812   (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer)
   9813       (progn
   9814         (lsp-with-current-buffer virtual-buffer
   9815           (lsp--text-document-did-close))
   9816         (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections))
   9817         (when (eq virtual-buffer lsp--virtual-buffer)
   9818           (setf lsp--virtual-buffer nil))
   9819         (when cleanup (funcall cleanup))
   9820         (remhash file-name lsp--virtual-buffer-mappings)
   9821 
   9822         (lsp--virtual-buffer-update-position)
   9823         (lsp--info "Disconnected from buffer %s" file-name))
   9824     (lsp--error "Nothing to disconnect from?")))
   9825 
   9826 
   9827 ;; inlay hints
   9828 
   9829 (defface lsp-inlay-hint-face
   9830   '((t :inherit font-lock-comment-face))
   9831   "The face to use for the JavaScript inlays."
   9832   :group 'lsp-mode
   9833   :package-version '(lsp-mode . "9.0.0"))
   9834 
   9835 (defface lsp-inlay-hint-type-face
   9836   '((t :inherit lsp-inlay-hint-face))
   9837   "Face for inlay type hints (e.g. inferred variable types)."
   9838   :group 'lsp-mode
   9839   :package-version '(lsp-mode . "9.0.0"))
   9840 
   9841 (defcustom lsp-inlay-hint-type-format "%s"
   9842   "Format string for variable inlays (part of the inlay face)."
   9843   :type '(string :tag "String")
   9844   :group 'lsp-mode
   9845   :package-version '(lsp-mode . "9.0.0"))
   9846 
   9847 (defface lsp-inlay-hint-parameter-face
   9848   '((t :inherit lsp-inlay-hint-face))
   9849   "Face for inlay parameter hints (e.g. function parameter names at
   9850 call-site)."
   9851   :group 'lsp-mode
   9852   :package-version '(lsp-mode . "9.0.0"))
   9853 
   9854 (defcustom lsp-inlay-hint-param-format "%s"
   9855   "Format string for parameter inlays (part of the inlay face)."
   9856   :type '(string :tag "String")
   9857   :group 'lsp-mode
   9858   :package-version '(lsp-mode . "9.0.0"))
   9859 
   9860 (defcustom lsp-update-inlay-hints-on-scroll t
   9861   "If non-nil update inlay hints immediately when scrolling or
   9862 modifying window sizes."
   9863   :type 'boolean
   9864   :package-version '(lsp-mode . "9.0.0"))
   9865 
   9866 (defun lsp--format-inlay (text kind)
   9867   (cond
   9868    ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text))
   9869    ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text))
   9870    (t text)))
   9871 
   9872 (defun lsp--face-for-inlay (kind)
   9873   (cond
   9874    ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face)
   9875    ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face)
   9876    (t 'lsp-inlay-hint-face)))
   9877 
   9878 (defun lsp--update-inlay-hints-scroll-function (window start)
   9879   (lsp-update-inlay-hints start (window-end window t)))
   9880 
   9881 (defun lsp--update-inlay-hints ()
   9882   (lsp-update-inlay-hints (window-start) (window-end nil t)))
   9883 
   9884 (defun lsp--label-from-inlay-hints-response (label)
   9885   "Returns a string label built from an array of
   9886 InlayHintLabelParts or the argument itself if it's already a
   9887 string."
   9888   (cl-typecase label
   9889     (string label)
   9890     (vector
   9891      (string-join (mapcar (lambda (part)
   9892                             (-let (((&InlayHintLabelPart :value) part))
   9893                               value))
   9894                           label)))))
   9895 
   9896 (defun lsp-update-inlay-hints (start end)
   9897   (lsp-request-async
   9898    "textDocument/inlayHint"
   9899    (lsp-make-inlay-hints-params
   9900     :text-document (lsp--text-document-identifier)
   9901     :range (lsp-make-range :start
   9902                            (lsp-point-to-position start)
   9903                            :end
   9904                            (lsp-point-to-position end)))
   9905    (lambda (res)
   9906      (lsp--remove-overlays 'lsp-inlay-hint)
   9907      (dolist (hint res)
   9908        (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint)
   9909                (kind (or kind? lsp/inlay-hint-kind-type-hint))
   9910                (label (lsp--label-from-inlay-hints-response label))
   9911                (pos (lsp--position-to-point position))
   9912                (overlay (make-overlay pos pos nil 'front-advance 'end-advance)))
   9913          (when (stringp label)
   9914            (overlay-put overlay 'lsp-inlay-hint t)
   9915            (overlay-put overlay 'before-string
   9916                         (format "%s%s%s"
   9917                                 (if padding-left? " " "")
   9918                                 (propertize (lsp--format-inlay label kind)
   9919                                             'font-lock-face (lsp--face-for-inlay kind))
   9920                                 (if padding-right? " " "")))))))
   9921    :mode 'tick))
   9922 
   9923 (define-minor-mode lsp-inlay-hints-mode
   9924   "Mode for displaying inlay hints."
   9925   :lighter nil
   9926   (cond
   9927    ((and lsp-inlay-hints-mode lsp--buffer-workspaces)
   9928     (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t)
   9929     (when lsp-update-inlay-hints-on-scroll
   9930       (add-to-list (make-local-variable 'window-scroll-functions)
   9931                    #'lsp--update-inlay-hints-scroll-function)))
   9932    (t
   9933     (lsp--remove-overlays 'lsp-inlay-hint)
   9934     (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t)
   9935     (setf window-scroll-functions
   9936           (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions)))))
   9937 
   9938 
   9939 
   9940 ;;;###autoload
   9941 (defun lsp-start-plain ()
   9942   "Start `lsp-mode' using minimal configuration using the latest `melpa' version
   9943 of the packages.
   9944 
   9945 In case the major-mode that you are using for "
   9946   (interactive)
   9947   (let ((start-plain (make-temp-file "plain" nil ".el")))
   9948     (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el"
   9949                    start-plain t)
   9950     (start-process "lsp-start-plain"
   9951                    (generate-new-buffer " *lsp-start-plain*")
   9952                    (expand-file-name invocation-name invocation-directory)
   9953                     "-q" "-l" start-plain (or (buffer-file-name) ""))))
   9954 
   9955 
   9956 
   9957 (provide 'lsp-mode)
   9958 ;;; lsp-mode.el ends here