config

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

lsp-mode.el (427902B)


      1 ;;; lsp-mode.el --- LSP mode                              -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2020-2024 emacs-lsp maintainers
      4 
      5 ;; Author: Vibhav Pant, Fangrui Song, Ivan Yonchovski
      6 ;; Keywords: languages
      7 ;; Package-Requires: ((emacs "27.1") (dash "2.18.0") (f "0.20.0") (ht "2.3") (spinner "1.7.3") (markdown-mode "2.3") (lv "0") (eldoc "1.11"))
      8 ;; Version: 9.0.1
      9 
     10 ;; URL: https://github.com/emacs-lsp/lsp-mode
     11 ;; This program is free software; you can redistribute it and/or modify
     12 ;; it under the terms of the GNU General Public License as published by
     13 ;; the Free Software Foundation, either version 3 of the License, or
     14 ;; (at your option) any later version.
     15 
     16 ;; This program is distributed in the hope that it will be useful,
     17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     19 ;; GNU General Public License for more details.
     20 
     21 ;; You should have received a copy of the GNU General Public License
     22 ;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
     23 
     24 ;;; Commentary:
     25 
     26 ;; Emacs client/library for the Language Server Protocol
     27 
     28 ;;; Code:
     29 
     30 (require 'cl-generic)
     31 (require 'cl-lib)
     32 (require 'compile)
     33 (require 'dash)
     34 (require 'epg)
     35 (require 'ewoc)
     36 (require 'f)
     37 (require 'filenotify)
     38 (require 'files)
     39 (require 'ht)
     40 (require 'imenu)
     41 (require 'inline)
     42 (require 'json)
     43 (require 'lv)
     44 (require 'markdown-mode)
     45 (require 'network-stream)
     46 (require 'pcase)
     47 (require 'rx)
     48 (require 's)
     49 (require 'seq)
     50 (require 'spinner)
     51 (require 'subr-x)
     52 (require 'tree-widget)
     53 (require 'url-parse)
     54 (require 'url-util)
     55 (require 'widget)
     56 (require 'xref)
     57 (require 'minibuffer)
     58 (require 'help-mode)
     59 (require 'lsp-protocol)
     60 
     61 (defgroup lsp-mode nil
     62   "Language Server Protocol client."
     63   :group 'tools
     64   :tag "Language Server (lsp-mode)")
     65 
     66 (declare-function evil-set-command-property "ext:evil-common")
     67 (declare-function projectile-project-root "ext:projectile")
     68 (declare-function yas-expand-snippet "ext:yasnippet")
     69 (declare-function dap-mode "ext:dap-mode")
     70 (declare-function dap-auto-configure-mode "ext:dap-mode")
     71 
     72 (defvar yas-inhibit-overlay-modification-protection)
     73 (defvar yas-indent-line)
     74 (defvar yas-wrap-around-region)
     75 (defvar yas-also-auto-indent-first-line)
     76 (defvar dap-auto-configure-mode)
     77 (defvar dap-ui-menu-items)
     78 (defvar company-minimum-prefix-length)
     79 
     80 (defconst lsp--message-type-face
     81   `((1 . ,compilation-error-face)
     82     (2 . ,compilation-warning-face)
     83     (3 . ,compilation-message-face)
     84     (4 . ,compilation-info-face)))
     85 
     86 (defconst lsp--errors
     87   '((-32700 "Parse Error")
     88     (-32600 "Invalid Request")
     89     (-32601 "Method not Found")
     90     (-32602 "Invalid Parameters")
     91     (-32603 "Internal Error")
     92     (-32099 "Server Start Error")
     93     (-32000 "Server End Error")
     94     (-32002 "Server Not Initialized")
     95     (-32001 "Unknown Error Code")
     96     (-32800 "Request Cancelled"))
     97   "Alist of error codes to user friendly strings.")
     98 
     99 (defconst lsp--empty-ht (make-hash-table))
    100 
    101 (eval-and-compile
    102   (defun dash-expand:&lsp-wks (key source)
    103     `(,(intern-soft (format "lsp--workspace-%s" (eval key))) ,source))
    104 
    105   (defun dash-expand:&lsp-cln (key source)
    106     `(,(intern-soft (format "lsp--client-%s" (eval key))) ,source)))
    107 
    108 (define-obsolete-variable-alias 'lsp-print-io 'lsp-log-io "lsp-mode 6.1")
    109 
    110 (defcustom lsp-log-io nil
    111   "If non-nil, log all messages from the language server to a *lsp-log* buffer."
    112   :group 'lsp-mode
    113   :type 'boolean)
    114 
    115 (defcustom lsp-log-io-allowlist-methods '()
    116   "The methods to filter before print to lsp-log-io."
    117   :group 'lsp-mode
    118   :type '(repeat string)
    119   :package-version '(lsp-mode . "9.0.0"))
    120 
    121 (defcustom lsp-log-max message-log-max
    122   "Maximum number of lines to keep in the log buffer.
    123 If nil, disable message logging.  If t, log messages but don’t truncate
    124 the buffer when it becomes large."
    125   :group 'lsp-mode
    126   :type '(choice (const :tag "Disable" nil)
    127                  (integer :tag "lines")
    128                  (const :tag "Unlimited" t))
    129   :package-version '(lsp-mode . "6.1"))
    130 
    131 (defcustom lsp-io-messages-max t
    132   "Maximum number of messages that can be locked in a `lsp-io' buffer."
    133   :group 'lsp-mode
    134   :type '(choice (const :tag "Unlimited" t)
    135                  (integer :tag "Messages"))
    136   :package-version '(lsp-mode . "6.1"))
    137 
    138 (defcustom lsp-keep-workspace-alive t
    139   "If non nil keep workspace alive when the last workspace buffer is closed."
    140   :group 'lsp-mode
    141   :type 'boolean)
    142 
    143 (defcustom lsp-enable-snippet t
    144   "Enable/disable snippet completion support."
    145   :group 'lsp-completion
    146   :type 'boolean)
    147 
    148 (defcustom lsp-enable-folding t
    149   "Enable/disable code folding support."
    150   :group 'lsp-mode
    151   :type 'boolean
    152   :package-version '(lsp-mode . "6.1"))
    153 
    154 (define-obsolete-variable-alias 'lsp-enable-semantic-highlighting 'lsp-semantic-tokens-enable "lsp-mode 8.0.0")
    155 
    156 (defcustom lsp-semantic-tokens-enable nil
    157   "Enable/disable support for semantic tokens.
    158 As defined by the Language Server Protocol 3.16."
    159   :group 'lsp-semantic-tokens
    160   :type 'boolean)
    161 
    162 (defcustom lsp-folding-range-limit nil
    163   "The maximum number of folding ranges to receive from the language server."
    164   :group 'lsp-mode
    165   :type '(choice (const :tag "No limit." nil)
    166                  (integer :tag "Number of lines."))
    167   :package-version '(lsp-mode . "6.1"))
    168 
    169 (defcustom lsp-folding-line-folding-only nil
    170   "If non-nil, only fold complete lines."
    171   :group 'lsp-mode
    172   :type 'boolean
    173   :package-version '(lsp-mode . "6.1"))
    174 
    175 (defcustom lsp-client-packages
    176   '( ccls lsp-actionscript lsp-ada lsp-angular lsp-ansible lsp-asm lsp-astro
    177      lsp-autotools lsp-awk lsp-bash lsp-beancount lsp-bufls lsp-clangd
    178      lsp-clojure lsp-cmake lsp-cobol lsp-credo lsp-crystal lsp-csharp lsp-css
    179      lsp-cucumber lsp-cypher lsp-d lsp-dart lsp-dhall lsp-docker lsp-dockerfile
    180      lsp-elixir lsp-elm lsp-emmet lsp-erlang lsp-eslint lsp-fortran lsp-fsharp
    181      lsp-gdscript lsp-gleam lsp-glsl lsp-go lsp-golangci-lint lsp-grammarly
    182      lsp-graphql lsp-groovy lsp-hack lsp-haskell lsp-haxe lsp-idris lsp-java
    183      lsp-javascript lsp-jq lsp-json lsp-kotlin lsp-latex lsp-lisp lsp-ltex
    184      lsp-lua lsp-magik lsp-markdown lsp-marksman lsp-mdx lsp-meson lsp-metals lsp-mint
    185      lsp-mojo lsp-move lsp-mssql lsp-nginx lsp-nim lsp-nix lsp-nushell lsp-ocaml
    186      lsp-openscad lsp-pascal lsp-perl lsp-perlnavigator lsp-php lsp-pls
    187      lsp-purescript lsp-pwsh lsp-pyls lsp-pylsp lsp-pyright lsp-python-ms
    188      lsp-qml lsp-r lsp-racket lsp-remark lsp-rf lsp-rubocop lsp-ruby-lsp
    189      lsp-ruby-syntax-tree lsp-ruff-lsp lsp-rust lsp-semgrep lsp-shader
    190      lsp-solargraph lsp-solidity lsp-sonarlint lsp-sorbet lsp-sourcekit
    191      lsp-sql lsp-sqls lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform
    192      lsp-tex lsp-tilt lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-v
    193      lsp-vala lsp-verilog lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl
    194      lsp-xml lsp-yaml lsp-yang lsp-zig)
    195   "List of the clients to be automatically required."
    196   :group 'lsp-mode
    197   :type '(repeat symbol))
    198 
    199 (defcustom lsp-progress-via-spinner t
    200   "If non-nil, display LSP $/progress reports via a spinner in the modeline."
    201   :group 'lsp-mode
    202   :type 'boolean)
    203 
    204 (defcustom lsp-progress-spinner-type 'progress-bar
    205   "Holds the type of spinner to be used in the mode-line.
    206 Takes a value accepted by `spinner-start'."
    207   :group 'lsp-mode
    208   :type `(choice :tag "Choose a spinner by name"
    209                  ,@(mapcar (lambda (c) (list 'const (car c)))
    210                            spinner-types)))
    211 
    212 (defvar-local lsp-use-workspace-root-for-server-default-directory nil
    213   "Use `lsp-workspace-root' for `default-directory' when starting LSP process.")
    214 
    215 (defvar-local lsp--cur-workspace nil)
    216 
    217 (defvar-local lsp--cur-version 0)
    218 (defvar-local lsp--virtual-buffer-connections nil)
    219 (defvar-local lsp--virtual-buffer nil)
    220 (defvar lsp--virtual-buffer-mappings (ht))
    221 
    222 (defvar lsp--uri-file-prefix (pcase system-type
    223                                (`windows-nt "file:///")
    224                                (_ "file://"))
    225   "Prefix for a file-uri.")
    226 
    227 (defvar-local lsp-buffer-uri nil
    228   "If set, return it instead of calculating it using `buffer-file-name'.")
    229 
    230 (define-error 'lsp-error "Unknown lsp-mode error")
    231 (define-error 'lsp-empty-response-error
    232   "Empty response from the language server" 'lsp-error)
    233 (define-error 'lsp-timed-out-error
    234   "Timed out while waiting for a response from the language server" 'lsp-error)
    235 (define-error 'lsp-capability-not-supported
    236   "Capability not supported by the language server" 'lsp-error)
    237 (define-error 'lsp-file-scheme-not-supported
    238   "Unsupported file scheme" 'lsp-error)
    239 (define-error 'lsp-client-already-exists-error
    240   "A client with this server-id already exists" 'lsp-error)
    241 (define-error 'lsp-no-code-actions
    242   "No code actions" 'lsp-error)
    243 
    244 (defcustom lsp-auto-guess-root nil
    245   "Automatically guess the project root using projectile/project.
    246 Do *not* use this setting unless you are familiar with `lsp-mode'
    247 internals and you are sure that all of your projects are
    248 following `projectile'/`project.el' conventions."
    249   :group 'lsp-mode
    250   :type 'boolean)
    251 
    252 (defcustom lsp-guess-root-without-session nil
    253   "Ignore the session file when calculating the project root.
    254 You almost always want to set lsp-auto-guess-root too.
    255 Do *not* use this setting unless you are familiar with `lsp-mode'
    256 internals and you are sure that all of your projects are
    257 following `projectile'/`project.el' conventions."
    258   :group 'lsp-mode
    259   :type 'boolean)
    260 
    261 (defcustom lsp-restart 'interactive
    262   "Defines how server-exited events must be handled."
    263   :group 'lsp-mode
    264   :type '(choice (const interactive)
    265                  (const auto-restart)
    266                  (const ignore)))
    267 
    268 (defcustom lsp-session-file (expand-file-name (locate-user-emacs-file ".lsp-session-v1"))
    269   "File where session information is stored."
    270   :group 'lsp-mode
    271   :type 'file)
    272 
    273 (defcustom lsp-auto-configure t
    274   "Auto configure `lsp-mode' main features.
    275 When set to t `lsp-mode' will auto-configure completion,
    276 code-actions, breadcrumb, `flycheck', `flymake', `imenu', symbol highlighting,
    277 lenses, links, and so on.
    278 
    279 For finer granularity you may use `lsp-enable-*' properties."
    280   :group 'lsp-mode
    281   :type 'boolean
    282   :package-version '(lsp-mode . "6.1"))
    283 
    284 (defcustom lsp-disabled-clients nil
    285   "A list of disabled/blocklisted clients.
    286 Each entry in the list can be either:
    287 a symbol, the server-id for the LSP client, or
    288 a cons pair (MAJOR-MODE . CLIENTS), where MAJOR-MODE is the major-mode,
    289 and CLIENTS is either a client or a list of clients.
    290 
    291 This option can also be used as a file- or directory-local variable to
    292 disable a language server for individual files or directories/projects
    293 respectively."
    294   :group 'lsp-mode
    295   :type '(repeat (symbol))
    296   :safe 'listp
    297   :package-version '(lsp-mode . "6.1"))
    298 
    299 (defvar lsp-clients (make-hash-table :test 'eql)
    300   "Hash table server-id -> client.
    301 It contains all of the clients that are currently registered.")
    302 
    303 (defvar lsp-enabled-clients nil
    304   "List of clients allowed to be used for projects.
    305 When nil, all registered clients are considered candidates.")
    306 
    307 (defvar lsp-last-id 0
    308   "Last request id.")
    309 
    310 (defcustom lsp-before-initialize-hook nil
    311   "List of functions to be called before a Language Server has been initialized
    312 for a new workspace."
    313   :type 'hook
    314   :group 'lsp-mode)
    315 
    316 (defcustom lsp-after-initialize-hook nil
    317   "List of functions to be called after a Language Server has been initialized
    318 for a new workspace."
    319   :type 'hook
    320   :group 'lsp-mode)
    321 
    322 (defcustom lsp-before-open-hook nil
    323   "List of functions to be called before a new file with LSP support is opened."
    324   :type 'hook
    325   :group 'lsp-mode)
    326 
    327 (defcustom lsp-after-open-hook nil
    328   "List of functions to be called after a new file with LSP support is opened."
    329   :type 'hook
    330   :group 'lsp-mode)
    331 
    332 (defcustom lsp-enable-file-watchers t
    333   "If non-nil lsp-mode will watch the files in the workspace if
    334 the server has requested that."
    335   :type 'boolean
    336   :group 'lsp-mode
    337   :package-version '(lsp-mode . "6.1"))
    338 ;;;###autoload(put 'lsp-enable-file-watchers 'safe-local-variable #'booleanp)
    339 
    340 (define-obsolete-variable-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "8.0.0")
    341 
    342 (defcustom lsp-file-watch-ignored-directories
    343   '(; SCM tools
    344     "[/\\\\]\\.git\\'"
    345     "[/\\\\]\\.github\\'"
    346     "[/\\\\]\\.gitlab\\'"
    347     "[/\\\\]\\.circleci\\'"
    348     "[/\\\\]\\.hg\\'"
    349     "[/\\\\]\\.bzr\\'"
    350     "[/\\\\]_darcs\\'"
    351     "[/\\\\]\\.svn\\'"
    352     "[/\\\\]_FOSSIL_\\'"
    353     ;; IDE or build tools
    354     "[/\\\\]\\.idea\\'"
    355     "[/\\\\]\\.ensime_cache\\'"
    356     "[/\\\\]\\.eunit\\'"
    357     "[/\\\\]node_modules"
    358     "[/\\\\]\\.yarn\\'"
    359     "[/\\\\]\\.fslckout\\'"
    360     "[/\\\\]\\.tox\\'"
    361     "[/\\\\]\\.nox\\'"
    362     "[/\\\\]dist\\'"
    363     "[/\\\\]dist-newstyle\\'"
    364     "[/\\\\]\\.stack-work\\'"
    365     "[/\\\\]\\.bloop\\'"
    366     "[/\\\\]\\.metals\\'"
    367     "[/\\\\]target\\'"
    368     "[/\\\\]\\.ccls-cache\\'"
    369     "[/\\\\]\\.vs\\'"
    370     "[/\\\\]\\.vscode\\'"
    371     "[/\\\\]\\.venv\\'"
    372     "[/\\\\]\\.mypy_cache\\'"
    373     "[/\\\\]\\.pytest_cache\\'"
    374     ;; Swift Package Manager
    375     "[/\\\\]\\.build\\'"
    376     ;; Python
    377     "[/\\\\]__pycache__\\'"
    378     ;; Autotools output
    379     "[/\\\\]\\.deps\\'"
    380     "[/\\\\]build-aux\\'"
    381     "[/\\\\]autom4te.cache\\'"
    382     "[/\\\\]\\.reference\\'"
    383     ;; Bazel
    384     "[/\\\\]bazel-[^/\\\\]+\\'"
    385     ;; CSharp
    386     "[/\\\\]\\.cache[/\\\\]lsp-csharp\\'"
    387     "[/\\\\]\\.meta\\'"
    388     "[/\\\\]\\.nuget\\'"
    389     ;; Unity
    390     "[/\\\\]Library\\'"
    391     ;; Clojure
    392     "[/\\\\]\\.lsp\\'"
    393     "[/\\\\]\\.clj-kondo\\'"
    394     "[/\\\\]\\.shadow-cljs\\'"
    395     "[/\\\\]\\.babel_cache\\'"
    396     "[/\\\\]\\.cpcache\\'"
    397     "[/\\\\]\\checkouts\\'"
    398     ;; Gradle
    399     "[/\\\\]\\.gradle\\'"
    400     ;; Maven
    401     "[/\\\\]\\.m2\\'"
    402     ;; .Net Core build-output
    403     "[/\\\\]bin/Debug\\'"
    404     "[/\\\\]obj\\'"
    405     ;; OCaml and Dune
    406     "[/\\\\]_opam\\'"
    407     "[/\\\\]_build\\'"
    408     ;; Elixir
    409     "[/\\\\]\\.elixir_ls\\'"
    410     ;; Elixir Credo
    411     "[/\\\\]\\.elixir-tools\\'"
    412     ;; terraform and terragrunt
    413     "[/\\\\]\\.terraform\\'"
    414     "[/\\\\]\\.terragrunt-cache\\'"
    415     ;; nix-direnv
    416     "[/\\\\]\\result"
    417     "[/\\\\]\\result-bin"
    418     "[/\\\\]\\.direnv\\'")
    419   "List of regexps matching directory paths which won't be monitored when
    420 creating file watches. Customization of this variable is only honored at
    421 the global level or at a root of an lsp workspace."
    422   :group 'lsp-mode
    423   :type '(repeat string)
    424   :package-version '(lsp-mode . "8.0.0"))
    425 
    426 (define-obsolete-function-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "7.0.1")
    427 
    428 (defun lsp-file-watch-ignored-directories ()
    429   lsp-file-watch-ignored-directories)
    430 
    431 ;; Allow lsp-file-watch-ignored-directories as a file or directory-local variable
    432 ;;;###autoload(put 'lsp-file-watch-ignored-directories 'safe-local-variable 'lsp--string-listp)
    433 
    434 (defcustom lsp-file-watch-ignored-files
    435   '(
    436     ;; Flycheck tempfiles
    437     "[/\\\\]flycheck_[^/\\\\]+\\'"
    438     ;; lockfiles
    439     "[/\\\\]\\.#[^/\\\\]+\\'"
    440     ;; backup files
    441     "[/\\\\][^/\\\\]+~\\'" )
    442   "List of regexps matching files for which change events will
    443 not be sent to the server.
    444 
    445 This setting has no impact on whether a file-watch is created for
    446 a directory; it merely prevents notifications pertaining to
    447 matched files from being sent to the server.  To prevent a
    448 file-watch from being created for a directory, customize
    449 `lsp-file-watch-ignored-directories'
    450 
    451 Customization of this variable is only honored at the global
    452 level or at a root of an lsp workspace."
    453   :group 'lsp-mode
    454   :type '(repeat string)
    455   :package-version '(lsp-mode . "8.0.0"))
    456 
    457 ;; Allow lsp-file-watch-ignored-files as a file or directory-local variable
    458 ;;;###autoload(put 'lsp-file-watch-ignored-files 'safe-local-variable 'lsp--string-listp)
    459 
    460 (defcustom lsp-after-uninitialized-functions nil
    461   "List of functions to be called after a Language Server has been uninitialized."
    462   :type 'hook
    463   :group 'lsp-mode
    464   :package-version '(lsp-mode . "6.3"))
    465 
    466 (defconst lsp--sync-full 1)
    467 (defconst lsp--sync-incremental 2)
    468 
    469 (defcustom lsp-debounce-full-sync-notifications t
    470   "If non-nil debounce full sync events.
    471 This flag affects only servers which do not support incremental updates."
    472   :type 'boolean
    473   :group 'lsp-mode
    474   :package-version '(lsp-mode . "6.1"))
    475 
    476 (defcustom lsp-debounce-full-sync-notifications-interval 1.0
    477   "Time to wait before sending full sync synchronization after buffer modification."
    478   :type 'float
    479   :group 'lsp-mode
    480   :package-version '(lsp-mode . "6.1"))
    481 
    482 (defvar lsp--stderr-index 0)
    483 
    484 (defvar lsp--delayed-requests nil)
    485 (defvar lsp--delay-timer nil)
    486 
    487 (defcustom lsp-document-sync-method nil
    488   "How to sync the document with the language server."
    489   :type '(choice (const :tag "Documents are synced by always sending the full content of the document." lsp--sync-full)
    490                  (const :tag "Documents are synced by always sending incremental changes to the document." lsp--sync-incremental)
    491                  (const :tag "Use the method recommended by the language server." nil))
    492   :group 'lsp-mode)
    493 
    494 (defcustom lsp-auto-execute-action t
    495   "Auto-execute single action."
    496   :type 'boolean
    497   :group 'lsp-mode)
    498 
    499 (defcustom lsp-enable-links t
    500   "If non-nil, all references to links in a file will be made clickable, if
    501 supported by the language server."
    502   :type 'boolean
    503   :group 'lsp-mode
    504   :package-version '(lsp-mode . "6.1"))
    505 
    506 (defcustom lsp-enable-imenu t
    507   "If non-nil, automatically enable `imenu' integration when server provides
    508 `textDocument/documentSymbol'."
    509   :type 'boolean
    510   :group 'lsp-mode
    511   :package-version '(lsp-mode . "6.2"))
    512 
    513 (defcustom lsp-enable-dap-auto-configure t
    514   "If non-nil, enable `dap-auto-configure-mode`."
    515   :type 'boolean
    516   :group 'lsp-mode
    517   :package-version '(lsp-mode . "7.0"))
    518 
    519 (defcustom lsp-eldoc-enable-hover t
    520   "If non-nil, `eldoc' will display hover info when it is present."
    521   :type 'boolean
    522   :group 'lsp-mode)
    523 
    524 (defcustom lsp-eldoc-render-all nil
    525   "Display all of the info returned by document/onHover.
    526 If this is set to nil, `eldoc' will show only the symbol information."
    527   :type 'boolean
    528   :group 'lsp-mode)
    529 
    530 (define-obsolete-variable-alias 'lsp-enable-completion-at-point
    531   'lsp-completion-enable "lsp-mode 7.0.1")
    532 
    533 (defcustom lsp-completion-enable t
    534   "Enable `completion-at-point' integration."
    535   :type 'boolean
    536   :group 'lsp-completion)
    537 
    538 (defcustom lsp-enable-symbol-highlighting t
    539   "Highlight references of the symbol at point."
    540   :type 'boolean
    541   :group 'lsp-mode)
    542 
    543 (defcustom lsp-enable-xref t
    544   "Enable xref integration."
    545   :type 'boolean
    546   :group 'lsp-mode)
    547 
    548 (defcustom lsp-references-exclude-definition nil
    549   "If non-nil, exclude declarations when finding references."
    550   :type 'boolean
    551   :group 'lsp-mode)
    552 
    553 (defcustom lsp-enable-indentation t
    554   "Indent regions using the file formatting functionality provided by the
    555 language server."
    556   :type 'boolean
    557   :group 'lsp-mode)
    558 
    559 (defcustom lsp-enable-on-type-formatting t
    560   "Enable `textDocument/onTypeFormatting' integration."
    561   :type 'boolean
    562   :group 'lsp-mode)
    563 
    564 (defcustom lsp-enable-text-document-color t
    565   "Enable `textDocument/documentColor' integration."
    566   :type 'boolean
    567   :group 'lsp-mode)
    568 
    569 (defcustom lsp-before-save-edits t
    570   "If non-nil, `lsp-mode' will apply edits suggested by the language server
    571 before saving a document."
    572   :type 'boolean
    573   :group 'lsp-mode)
    574 
    575 (defcustom lsp-after-apply-edits-hook nil
    576   "Hooks to run when text edit is applied.
    577 It contains the operation source."
    578   :type 'hook
    579   :group 'lsp-mode
    580   :package-version '(lsp-mode . "8.0.0"))
    581 
    582 (defcustom lsp-apply-edits-after-file-operations t
    583   "Whether to apply edits returned by server after file operations if any.
    584 Applicable only if server supports workspace.fileOperations for operations:
    585 `workspace/willRenameFiles', `workspace/willCreateFiles' and
    586 `workspace/willDeleteFiles'."
    587   :group 'lsp-mode
    588   :type 'boolean)
    589 
    590 (defcustom lsp-modeline-code-actions-enable t
    591   "Whether to show code actions on modeline."
    592   :type 'boolean
    593   :group 'lsp-modeline)
    594 
    595 (defcustom lsp-modeline-diagnostics-enable t
    596   "Whether to show diagnostics on modeline."
    597   :type 'boolean
    598   :group 'lsp-modeline)
    599 
    600 (defcustom lsp-modeline-workspace-status-enable t
    601   "Whether to show workspace status on modeline."
    602   :type 'boolean
    603   :group 'lsp-modeline
    604   :package-version '(lsp-mode . "8.0.0"))
    605 
    606 (defcustom lsp-headerline-breadcrumb-enable t
    607   "Whether to enable breadcrumb on headerline."
    608   :type 'boolean
    609   :group 'lsp-headerline)
    610 
    611 (defcustom lsp-configure-hook nil
    612   "Hooks to run when `lsp-configure-buffer' is called."
    613   :type 'hook
    614   :group 'lsp-mode)
    615 
    616 (defcustom lsp-unconfigure-hook nil
    617   "Hooks to run when `lsp-unconfig-buffer' is called."
    618   :type 'hook
    619   :group 'lsp-mode)
    620 
    621 (defcustom lsp-after-diagnostics-hook nil
    622   "Hooks to run after diagnostics are received.
    623 Note: it runs only if the receiving buffer is open. Use
    624 `lsp-diagnostics-updated-hook'if you want to be notified when
    625 diagnostics have changed."
    626   :type 'hook
    627   :group 'lsp-mode)
    628 
    629 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook
    630   'lsp-diagnostics-updated-hook "lsp-mode 6.4")
    631 
    632 (defcustom lsp-diagnostics-updated-hook nil
    633   "Hooks to run after diagnostics are received."
    634   :type 'hook
    635   :group 'lsp-mode)
    636 
    637 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook
    638   'lsp-workspace-folders-changed-functions "lsp-mode 6.3")
    639 
    640 (defcustom lsp-workspace-folders-changed-functions nil
    641   "Hooks to run after the folders has changed.
    642 The hook will receive two parameters list of added and removed folders."
    643   :type 'hook
    644   :group 'lsp-mode)
    645 
    646 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0")
    647 
    648 (defcustom lsp-before-apply-edits-hook nil
    649   "Hooks to run before applying edits."
    650   :type 'hook
    651   :group 'lsp-mode)
    652 
    653 (defgroup lsp-imenu nil
    654   "LSP Imenu."
    655   :group 'lsp-mode
    656   :tag "LSP Imenu")
    657 
    658 (defcustom lsp-imenu-show-container-name t
    659   "Display the symbol's container name in an imenu entry."
    660   :type 'boolean
    661   :group 'lsp-imenu)
    662 
    663 (defcustom lsp-imenu-container-name-separator "/"
    664   "Separator string to use to separate the container name from the symbol while
    665 displaying imenu entries."
    666   :type 'string
    667   :group 'lsp-imenu)
    668 
    669 (defcustom lsp-imenu-sort-methods '(kind name)
    670   "How to sort the imenu items.
    671 
    672 The value is a list of `kind' `name' or `position'.  Priorities
    673 are determined by the index of the element."
    674   :type '(repeat (choice (const name)
    675                          (const position)
    676                          (const kind)))
    677   :group 'lsp-imenu)
    678 
    679 (defcustom lsp-imenu-index-symbol-kinds nil
    680   "Which symbol kinds to show in imenu."
    681   :type '(repeat (choice (const :tag "Miscellaneous" nil)
    682                          (const :tag "File" File)
    683                          (const :tag "Module" Module)
    684                          (const :tag "Namespace" Namespace)
    685                          (const :tag "Package" Package)
    686                          (const :tag "Class" Class)
    687                          (const :tag "Method" Method)
    688                          (const :tag "Property" Property)
    689                          (const :tag "Field" Field)
    690                          (const :tag "Constructor" Constructor)
    691                          (const :tag "Enum" Enum)
    692                          (const :tag "Interface" Interface)
    693                          (const :tag "Function" Function)
    694                          (const :tag "Variable" Variable)
    695                          (const :tag "Constant" Constant)
    696                          (const :tag "String" String)
    697                          (const :tag "Number" Number)
    698                          (const :tag "Boolean" Boolean)
    699                          (const :tag "Array" Array)
    700                          (const :tag "Object" Object)
    701                          (const :tag "Key" Key)
    702                          (const :tag "Null" Null)
    703                          (const :tag "Enum Member" EnumMember)
    704                          (const :tag "Struct" Struct)
    705                          (const :tag "Event" Event)
    706                          (const :tag "Operator" Operator)
    707                          (const :tag "Type Parameter" TypeParameter)))
    708   :group 'lsp-imenu)
    709 
    710 ;; vibhavp: Should we use a lower value (5)?
    711 (defcustom lsp-response-timeout 10
    712   "Number of seconds to wait for a response from the language server before
    713 timing out. Nil if no timeout."
    714   :type '(choice
    715           (number :tag "Seconds")
    716           (const :tag "No timeout" nil))
    717   :group 'lsp-mode)
    718 
    719 (defcustom lsp-tcp-connection-timeout 2
    720   "The timeout for tcp connection in seconds."
    721   :type 'number
    722   :group 'lsp-mode
    723   :package-version '(lsp-mode . "6.2"))
    724 
    725 (defconst lsp--imenu-compare-function-alist
    726   (list (cons 'name #'lsp--imenu-compare-name)
    727         (cons 'kind #'lsp--imenu-compare-kind)
    728         (cons 'position #'lsp--imenu-compare-line-col))
    729   "An alist of (METHOD . FUNCTION).
    730 METHOD is one of the symbols accepted by
    731 `lsp-imenu-sort-methods'.
    732 
    733 FUNCTION takes two hash tables representing DocumentSymbol.  It
    734 returns a negative number, 0, or a positive number indicating
    735 whether the first parameter is less than, equal to, or greater
    736 than the second parameter.")
    737 
    738 (defcustom lsp-diagnostic-clean-after-change nil
    739   "When non-nil, clean the diagnostics on change.
    740 
    741 Note that when that setting is nil, `lsp-mode' will show stale
    742 diagnostics until server publishes the new set of diagnostics"
    743   :type 'boolean
    744   :group 'lsp-diagnostics
    745   :package-version '(lsp-mode . "7.0.1"))
    746 
    747 (defcustom lsp-server-trace nil
    748   "Request tracing on the server side.
    749 The actual trace output at each level depends on the language server in use.
    750 Changes take effect only when a new session is started."
    751   :type '(choice (const :tag "Disabled" "off")
    752                  (const :tag "Messages only" "messages")
    753                  (const :tag "Verbose" "verbose")
    754                  (const :tag "Default (disabled)" nil))
    755   :group 'lsp-mode
    756   :package-version '(lsp-mode . "6.1"))
    757 
    758 (defcustom lsp-auto-touch-files t
    759   "If non-nil ensure the files exist before sending
    760 `textDocument/didOpen' notification."
    761   :type 'boolean
    762   :group 'lsp-mode
    763   :package-version '(lsp-mode . "9.0.0"))
    764 
    765 (defvar lsp-language-id-configuration
    766   '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake")
    767     ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile")
    768     ("\\.astro$" . "astro")
    769     ("\\.cs\\'" . "csharp")
    770     ("\\.css$" . "css")
    771     ("\\.cypher$" . "cypher")
    772     ("\\.ebuild$" . "shellscript")
    773     ("\\.go\\'" . "go")
    774     ("\\.html$" . "html")
    775     ("\\.hx$" . "haxe")
    776     ("\\.hy$" . "hy")
    777     ("\\.java\\'" . "java")
    778     ("\\.jq$"  . "jq")
    779     ("\\.js$" . "javascript")
    780     ("\\.json$" . "json")
    781     ("\\.jsonc$" . "jsonc")
    782     ("\\.jsonnet$" . "jsonnet")
    783     ("\\.jsx$" . "javascriptreact")
    784     ("\\.lua$" . "lua")
    785     ("\\.mdx\\'" . "mdx")
    786     ("\\.nu$" . "nushell")
    787     ("\\.php$" . "php")
    788     ("\\.ps[dm]?1\\'" . "powershell")
    789     ("\\.rs\\'" . "rust")
    790     ("\\.spec\\'" . "rpm-spec")
    791     ("\\.sql$" . "sql")
    792     ("\\.svelte$" . "svelte")
    793     ("\\.toml\\'" . "toml")
    794     ("\\.ts$" . "typescript")
    795     ("\\.tsx$" . "typescriptreact")
    796     ("\\.ttcn3$" . "ttcn3")
    797     ("\\.vue$" . "vue")
    798     ("\\.xml$" . "xml")
    799     ("\\ya?ml$" . "yaml")
    800     ("^PKGBUILD$" . "shellscript")
    801     ("^go\\.mod\\'" . "go.mod")
    802     ("^settings\\.json$" . "jsonc")
    803     ("^yang\\.settings$" . "jsonc")
    804     ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson")
    805     (ada-mode . "ada")
    806     (ada-ts-mode . "ada")
    807     (gpr-mode . "gpr")
    808     (gpr-ts-mode . "gpr")
    809     (awk-mode . "awk")
    810     (awk-ts-mode . "awk")
    811     (nxml-mode . "xml")
    812     (sql-mode . "sql")
    813     (vimrc-mode . "vim")
    814     (vimscript-ts-mode . "vim")
    815     (sh-mode . "shellscript")
    816     (bash-ts-mode . "shellscript")
    817     (ebuild-mode . "shellscript")
    818     (pkgbuild-mode . "shellscript")
    819     (envrc-file-mode . "shellscript")
    820     (scala-mode . "scala")
    821     (scala-ts-mode . "scala")
    822     (julia-mode . "julia")
    823     (julia-ts-mode . "julia")
    824     (clojure-mode . "clojure")
    825     (clojurec-mode . "clojure")
    826     (clojurescript-mode . "clojurescript")
    827     (clojure-ts-mode . "clojure")
    828     (clojure-ts-clojurec-mode . "clojure")
    829     (clojure-ts-clojurescript-mode . "clojurescript")
    830     (java-mode . "java")
    831     (java-ts-mode . "java")
    832     (jdee-mode . "java")
    833     (groovy-mode . "groovy")
    834     (python-mode . "python")
    835     (python-ts-mode . "python")
    836     (cython-mode . "python")
    837     ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo")
    838     (lsp--render-markdown . "markdown")
    839     (move-mode . "move")
    840     (rust-mode . "rust")
    841     (rust-ts-mode . "rust")
    842     (rustic-mode . "rust")
    843     (kotlin-mode . "kotlin")
    844     (kotlin-ts-mode . "kotlin")
    845     (css-mode . "css")
    846     (css-ts-mode . "css")
    847     (less-mode . "less")
    848     (less-css-mode . "less")
    849     (lua-mode . "lua")
    850     (lua-ts-mode . "lua")
    851     (sass-mode . "sass")
    852     (ssass-mode . "sass")
    853     (scss-mode . "scss")
    854     (scad-mode . "openscad")
    855     (xml-mode . "xml")
    856     (c-mode . "c")
    857     (c-ts-mode . "c")
    858     (c++-mode . "cpp")
    859     (c++-ts-mode . "cpp")
    860     (cuda-mode . "cuda")
    861     (objc-mode . "objective-c")
    862     (html-mode . "html")
    863     (html-ts-mode . "html")
    864     (sgml-mode . "html")
    865     (mhtml-mode . "html")
    866     (mint-mode . "mint")
    867     (go-dot-mod-mode . "go.mod")
    868     (go-mod-ts-mode . "go.mod")
    869     (go-mode . "go")
    870     (go-ts-mode . "go")
    871     (graphql-mode . "graphql")
    872     (haskell-mode . "haskell")
    873     (hack-mode . "hack")
    874     (php-mode . "php")
    875     (php-ts-mode . "php")
    876     (powershell-mode . "powershell")
    877     (powershell-mode . "PowerShell")
    878     (powershell-ts-mode . "powershell")
    879     (json-mode . "json")
    880     (json-ts-mode . "json")
    881     (jsonc-mode . "jsonc")
    882     (rjsx-mode . "javascript")
    883     (js2-mode . "javascript")
    884     (js-mode . "javascript")
    885     (js-ts-mode . "javascript")
    886     (typescript-mode . "typescript")
    887     (typescript-ts-mode . "typescript")
    888     (tsx-ts-mode . "typescriptreact")
    889     (svelte-mode . "svelte")
    890     (fsharp-mode . "fsharp")
    891     (reason-mode . "reason")
    892     (caml-mode . "ocaml")
    893     (tuareg-mode . "ocaml")
    894     (swift-mode . "swift")
    895     (elixir-mode . "elixir")
    896     (elixir-ts-mode . "elixir")
    897     (heex-ts-mode . "elixir")
    898     (conf-javaprop-mode . "spring-boot-properties")
    899     (yaml-mode . "yaml")
    900     (yaml-ts-mode . "yaml")
    901     (ruby-mode . "ruby")
    902     (enh-ruby-mode . "ruby")
    903     (ruby-ts-mode . "ruby")
    904     (fortran-mode . "fortran")
    905     (f90-mode . "fortran")
    906     (elm-mode . "elm")
    907     (dart-mode . "dart")
    908     (erlang-mode . "erlang")
    909     (dockerfile-mode . "dockerfile")
    910     (dockerfile-ts-mode . "dockerfile")
    911     (csharp-mode . "csharp")
    912     (csharp-tree-sitter-mode . "csharp")
    913     (csharp-ts-mode . "csharp")
    914     (plain-tex-mode . "plaintex")
    915     (context-mode . "context")
    916     (cypher-mode . "cypher")
    917     (latex-mode . "latex")
    918     (LaTeX-mode . "latex")
    919     (v-mode . "v")
    920     (vhdl-mode . "vhdl")
    921     (vhdl-ts-mode . "vhdl")
    922     (verilog-mode . "verilog")
    923     (terraform-mode . "terraform")
    924     (ess-julia-mode . "julia")
    925     (ess-r-mode . "r")
    926     (crystal-mode . "crystal")
    927     (nim-mode . "nim")
    928     (dhall-mode . "dhall")
    929     (cmake-mode . "cmake")
    930     (cmake-ts-mode . "cmake")
    931     (purescript-mode . "purescript")
    932     (gdscript-mode . "gdscript")
    933     (gdscript-ts-mode . "gdscript")
    934     (perl-mode . "perl")
    935     (cperl-mode . "perl")
    936     (robot-mode . "robot")
    937     (racket-mode . "racket")
    938     (nix-mode . "nix")
    939     (nix-ts-mode . "Nix")
    940     (prolog-mode . "prolog")
    941     (vala-mode . "vala")
    942     (actionscript-mode . "actionscript")
    943     (d-mode . "d")
    944     (zig-mode . "zig")
    945     (text-mode . "plaintext")
    946     (markdown-mode . "markdown")
    947     (gfm-mode . "markdown")
    948     (beancount-mode . "beancount")
    949     (conf-toml-mode . "toml")
    950     (toml-ts-mode . "toml")
    951     (org-mode . "org")
    952     (org-journal-mode . "org")
    953     (nginx-mode . "nginx")
    954     (magik-mode . "magik")
    955     (magik-ts-mode . "magik")
    956     (idris-mode . "idris")
    957     (idris2-mode . "idris2")
    958     (gleam-mode . "gleam")
    959     (graphviz-dot-mode . "dot")
    960     (tiltfile-mode . "tiltfile")
    961     (solidity-mode . "solidity")
    962     (bibtex-mode . "bibtex")
    963     (rst-mode . "restructuredtext")
    964     (glsl-mode . "glsl")
    965     (shader-mode . "shaderlab")
    966     (wgsl-mode . "wgsl")
    967     (jq-mode . "jq")
    968     (jq-ts-mode . "jq")
    969     (protobuf-mode . "protobuf")
    970     (nushell-mode . "nushell")
    971     (nushell-ts-mode . "nushell")
    972     (meson-mode . "meson")
    973     (yang-mode . "yang"))
    974   "Language id configuration.")
    975 
    976 (defvar lsp--last-active-workspaces nil
    977   "Keep track of last active workspace.
    978 We want to try the last workspace first when jumping into a library
    979 directory")
    980 
    981 (defvar lsp-method-requirements
    982   '(("textDocument/callHierarchy" :capability :callHierarchyProvider)
    983     ("textDocument/codeAction" :capability :codeActionProvider)
    984     ("codeAction/resolve"
    985      :check-command (lambda (workspace)
    986                       (with-lsp-workspace workspace
    987                         (lsp:code-action-options-resolve-provider?
    988                          (lsp--capability-for-method "textDocument/codeAction")))))
    989     ("textDocument/codeLens" :capability :codeLensProvider)
    990     ("textDocument/completion" :capability :completionProvider)
    991     ("completionItem/resolve"
    992      :check-command (lambda (wk)
    993                       (with-lsp-workspace wk
    994                         (lsp:completion-options-resolve-provider?
    995                          (lsp--capability-for-method "textDocument/completion")))))
    996     ("textDocument/declaration" :capability :declarationProvider)
    997     ("textDocument/definition" :capability :definitionProvider)
    998     ("textDocument/documentColor" :capability :colorProvider)
    999     ("textDocument/documentLink" :capability :documentLinkProvider)
   1000     ("textDocument/inlayHint" :capability :inlayHintProvider)
   1001     ("textDocument/documentHighlight" :capability :documentHighlightProvider)
   1002     ("textDocument/documentSymbol" :capability :documentSymbolProvider)
   1003     ("textDocument/foldingRange" :capability :foldingRangeProvider)
   1004     ("textDocument/formatting" :capability :documentFormattingProvider)
   1005     ("textDocument/hover" :capability :hoverProvider)
   1006     ("textDocument/implementation" :capability :implementationProvider)
   1007     ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider)
   1008     ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider)
   1009     ("textDocument/prepareRename"
   1010      :check-command (lambda (workspace)
   1011                       (with-lsp-workspace workspace
   1012                         (lsp:rename-options-prepare-provider?
   1013                          (lsp--capability-for-method "textDocument/rename")))))
   1014     ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider)
   1015     ("textDocument/references" :capability :referencesProvider)
   1016     ("textDocument/rename" :capability :renameProvider)
   1017     ("textDocument/selectionRange" :capability :selectionRangeProvider)
   1018     ("textDocument/semanticTokens" :capability :semanticTokensProvider)
   1019     ("textDocument/semanticTokensFull"
   1020      :check-command (lambda (workspace)
   1021                       (with-lsp-workspace workspace
   1022                         (lsp-get (lsp--capability :semanticTokensProvider) :full))))
   1023     ("textDocument/semanticTokensFull/Delta"
   1024      :check-command (lambda (workspace)
   1025                       (with-lsp-workspace workspace
   1026                         (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full)))
   1027                           (and (not (booleanp capFull)) (lsp-get capFull :delta))))))
   1028     ("textDocument/semanticTokensRangeProvider"
   1029      :check-command (lambda (workspace)
   1030                       (with-lsp-workspace workspace
   1031                         (lsp-get (lsp--capability :semanticTokensProvider) :range))))
   1032     ("textDocument/signatureHelp" :capability :signatureHelpProvider)
   1033     ("textDocument/typeDefinition" :capability :typeDefinitionProvider)
   1034     ("textDocument/typeHierarchy" :capability :typeHierarchyProvider)
   1035     ("workspace/executeCommand" :capability :executeCommandProvider)
   1036     ("workspace/symbol" :capability :workspaceSymbolProvider))
   1037 
   1038   "Map methods to requirements.
   1039 It is used by request-sending functions to determine which server
   1040 must be used for handling a particular message.")
   1041 
   1042 (defconst lsp--file-change-type
   1043   `((created . 1)
   1044     (changed . 2)
   1045     (deleted . 3)))
   1046 
   1047 (defconst lsp--watch-kind
   1048   `((create . 1)
   1049     (change . 2)
   1050     (delete . 4)))
   1051 
   1052 (defvar lsp-window-body-width 40
   1053   "Window body width when rendering doc.")
   1054 
   1055 (defface lsp-face-highlight-textual
   1056   '((t :inherit highlight))
   1057   "Face used for textual occurrences of symbols."
   1058   :group 'lsp-mode)
   1059 
   1060 (defface lsp-face-highlight-read
   1061   '((t :inherit highlight :underline t))
   1062   "Face used for highlighting symbols being read."
   1063   :group 'lsp-mode)
   1064 
   1065 (defface lsp-face-highlight-write
   1066   '((t :inherit highlight :weight bold))
   1067   "Face used for highlighting symbols being written to."
   1068   :group 'lsp-mode)
   1069 
   1070 (define-obsolete-variable-alias 'lsp-lens-auto-enable
   1071   'lsp-lens-enable "lsp-mode 7.0.1")
   1072 
   1073 (defcustom lsp-lens-enable t
   1074   "Auto enable lenses if server supports."
   1075   :group 'lsp-lens
   1076   :type 'boolean
   1077   :package-version '(lsp-mode . "6.3"))
   1078 
   1079 (defcustom lsp-symbol-highlighting-skip-current nil
   1080   "If non-nil skip current symbol when setting symbol highlights."
   1081   :group 'lsp-mode
   1082   :type 'boolean)
   1083 
   1084 (defcustom lsp-file-watch-threshold 1000
   1085   "Show warning if the files to watch are more than.
   1086 Set to nil to disable the warning."
   1087   :type 'number
   1088   :group 'lsp-mode)
   1089 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i))))
   1090 
   1091 (defvar lsp-custom-markup-modes
   1092   '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic"))
   1093   "Mode to uses with markdown code blocks.
   1094 They are added to `markdown-code-lang-modes'")
   1095 
   1096 (defcustom lsp-signature-render-documentation t
   1097   "Display signature documentation in `eldoc'."
   1098   :type 'boolean
   1099   :group 'lsp-mode
   1100   :package-version '(lsp-mode . "6.2"))
   1101 
   1102 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request)
   1103   "Auto activate signature conditions."
   1104   :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char)
   1105                          (const :tag "After selected completion." :after-completion)
   1106                          (const :tag "When the server has sent show signature help." :on-server-request)))
   1107   :group 'lsp-mode
   1108   :package-version '(lsp-mode . "6.2"))
   1109 
   1110 (defcustom lsp-signature-doc-lines 20
   1111   "If number, limit the number of lines to show in the docs."
   1112   :type 'number
   1113   :group 'lsp-mode
   1114   :package-version '(lsp-mode . "6.3"))
   1115 
   1116 (defcustom lsp-signature-function 'lsp-lv-message
   1117   "The function used for displaying signature info.
   1118 It will be called with one param - the signature info. When
   1119 called with nil the signature info must be cleared."
   1120   :type 'function
   1121   :group 'lsp-mode
   1122   :package-version '(lsp-mode . "6.3"))
   1123 
   1124 (defcustom lsp-keymap-prefix "s-l"
   1125   "LSP-mode keymap prefix."
   1126   :group 'lsp-mode
   1127   :type 'string
   1128   :package-version '(lsp-mode . "6.3"))
   1129 
   1130 (defvar-local lsp--buffer-workspaces ()
   1131   "List of the buffer workspaces.")
   1132 
   1133 (defvar-local lsp--buffer-deferred nil
   1134   "Whether buffer was loaded via `lsp-deferred'.")
   1135 
   1136 (defvar lsp--session nil
   1137   "Contain the `lsp-session' for the current Emacs instance.")
   1138 
   1139 (defvar lsp--tcp-port 10000)
   1140 
   1141 (defvar lsp--client-packages-required nil
   1142   "If nil, `lsp-client-packages' are yet to be required.")
   1143 
   1144 (defvar lsp--tcp-server-port 0
   1145   "The server socket which is opened when using `lsp-tcp-server' (a server
   1146 socket is opened in Emacs and the language server connects to it).  The
   1147 default value of 0 ensures that a random high port is used. Set it to a positive
   1148 integer to use a specific port.")
   1149 
   1150 (defvar lsp--tcp-server-wait-seconds 10
   1151   "Wait this amount of time for the client to connect to our server socket
   1152 when using `lsp-tcp-server'.")
   1153 
   1154 (defvar-local lsp--document-symbols nil
   1155   "The latest document symbols.")
   1156 
   1157 (defvar-local lsp--document-selection-range-cache nil
   1158   "The document selection cache.")
   1159 
   1160 (defvar-local lsp--document-symbols-request-async nil
   1161   "If non-nil, request document symbols asynchronously.")
   1162 
   1163 (defvar-local lsp--document-symbols-tick -1
   1164   "The value of `buffer-chars-modified-tick' when document
   1165   symbols were last retrieved.")
   1166 
   1167 (defvar-local lsp--have-document-highlights nil
   1168   "Set to `t' on symbol highlighting, cleared on
   1169 `lsp--cleanup-highlights-if-needed'. Checking a separately
   1170 defined flag is substantially faster than unconditionally
   1171 calling `remove-overlays'.")
   1172 
   1173 ;; Buffer local variable for storing number of lines.
   1174 (defvar lsp--log-lines)
   1175 
   1176 (defvar-local lsp--eldoc-saved-message nil)
   1177 
   1178 (defvar lsp--on-change-timer nil)
   1179 (defvar lsp--on-idle-timer nil)
   1180 
   1181 (defvar-local lsp--signature-last nil)
   1182 (defvar-local lsp--signature-last-index nil)
   1183 (defvar lsp--signature-last-buffer nil)
   1184 
   1185 (defvar-local lsp--virtual-buffer-point-max nil)
   1186 
   1187 (cl-defmethod lsp-execute-command (_server _command _arguments)
   1188   "Ask SERVER to execute COMMAND with ARGUMENTS.")
   1189 
   1190 (defun lsp-elt (sequence n)
   1191   "Return Nth element of SEQUENCE or nil if N is out of range."
   1192   (cond
   1193    ((listp sequence) (elt sequence n))
   1194    ((arrayp sequence)
   1195     (and (> (length sequence) n) (aref sequence n)))
   1196    (t (and (> (length sequence) n) (elt sequence n)))))
   1197 
   1198 ;; define seq-first and seq-rest for older emacs
   1199 (defun lsp-seq-first (sequence)
   1200   "Return the first element of SEQUENCE."
   1201   (lsp-elt sequence 0))
   1202 
   1203 (defun lsp-seq-rest (sequence)
   1204   "Return a sequence of the elements of SEQUENCE except the first one."
   1205   (seq-drop sequence 1))
   1206 
   1207 ;;;###autoload
   1208 (defun lsp--string-listp (sequence)
   1209   "Return t if all elements of SEQUENCE are strings, else nil."
   1210   (not (seq-find (lambda (x) (not (stringp x))) sequence)))
   1211 
   1212 (defun lsp--string-vector-p (candidate)
   1213   "Returns true if CANDIDATE is a vector data structure and
   1214 every element of it is of type string, else nil."
   1215   (and
   1216    (vectorp candidate)
   1217    (seq-every-p #'stringp candidate)))
   1218 
   1219 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0")
   1220 
   1221 (defun lsp--editable-vector-match (widget value)
   1222   "Function for `lsp-editable-vector' :match."
   1223   ;; Value must be a list or a vector and all the members must match the type.
   1224   (and (or (listp value) (vectorp value))
   1225        (length (cdr (lsp--editable-vector-match-inline widget value)))))
   1226 
   1227 (defun lsp--editable-vector-match-inline (widget value)
   1228   "Value for `lsp-editable-vector' :match-inline."
   1229   (let ((type (nth 0 (widget-get widget :args)))
   1230         (ok t)
   1231         found)
   1232     (while (and value ok)
   1233       (let ((answer (widget-match-inline type value)))
   1234         (if answer
   1235             (let ((head (if (vectorp answer) (aref answer 0) (car answer)))
   1236                   (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer))))
   1237               (setq found (append found head)
   1238                     value tail))
   1239           (setq ok nil))))
   1240     (cons found value)))
   1241 
   1242 (defun lsp--editable-vector-value-to-external (_widget internal-value)
   1243   "Convert the internal list value to a vector."
   1244   (if (listp internal-value)
   1245       (apply 'vector internal-value)
   1246     internal-value))
   1247 
   1248 (defun lsp--editable-vector-value-to-internal (_widget external-value)
   1249   "Convert the external vector value to a list."
   1250   (if (vectorp external-value)
   1251       (append external-value nil)
   1252     external-value))
   1253 
   1254 (define-widget 'lsp--editable-vector 'editable-list
   1255   "A subclass of `editable-list' that accepts and returns a
   1256 vector instead of a list."
   1257   :value-to-external 'lsp--editable-vector-value-to-external
   1258   :value-to-internal 'lsp--editable-vector-value-to-internal
   1259   :match 'lsp--editable-vector-match
   1260   :match-inline 'lsp--editable-vector-match-inline)
   1261 
   1262 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector
   1263   "A variable length homogeneous vector."
   1264   :tag "Repeat"
   1265   :format "%{%t%}:\n%v%i\n")
   1266 
   1267 (define-widget 'lsp-string-vector 'lazy
   1268   "A vector of zero or more elements, every element of which is a string.
   1269 Appropriate for any language-specific `defcustom' that needs to
   1270 serialize as a JSON array of strings.
   1271 
   1272 Deprecated. Use `lsp-repeatable-vector' instead. "
   1273   :offset 4
   1274   :tag "Vector"
   1275   :type '(lsp-repeatable-vector string))
   1276 
   1277 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0")
   1278 
   1279 (defvar lsp--show-message t
   1280   "If non-nil, show debug message from `lsp-mode'.")
   1281 
   1282 (defun lsp--message  (format &rest args)
   1283   "Wrapper for `message'
   1284 
   1285 We `inhibit-message' the message when the cursor is in the
   1286 minibuffer and when emacs version is before emacs 27 due to the
   1287 fact that we often use `lsp--info', `lsp--warn' and `lsp--error'
   1288 in async context and the call to these function is removing the
   1289 minibuffer prompt. The issue with async messages is already fixed
   1290 in emacs 27.
   1291 
   1292 See #2049"
   1293   (when lsp--show-message
   1294     (let ((inhibit-message (or inhibit-message
   1295                                (and (minibufferp)
   1296                                     (version< emacs-version "27.0")))))
   1297       (apply #'message format args))))
   1298 
   1299 (defun lsp--info (format &rest args)
   1300   "Display lsp info message with FORMAT with ARGS."
   1301   (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args)))
   1302 
   1303 (defun lsp--warn (format &rest args)
   1304   "Display lsp warn message with FORMAT with ARGS."
   1305   (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args)))
   1306 
   1307 (defun lsp--error (format &rest args)
   1308   "Display lsp error message with FORMAT with ARGS."
   1309   (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args)))
   1310 
   1311 (defun lsp-log (format &rest args)
   1312   "Log message to the ’*lsp-log*’ buffer.
   1313 
   1314 FORMAT and ARGS i the same as for `message'."
   1315   (when lsp-log-max
   1316     (let ((log-buffer (get-buffer "*lsp-log*"))
   1317           (inhibit-read-only t))
   1318       (unless log-buffer
   1319         (setq log-buffer (get-buffer-create "*lsp-log*"))
   1320         (with-current-buffer log-buffer
   1321           (buffer-disable-undo)
   1322           (view-mode 1)
   1323           (set (make-local-variable 'lsp--log-lines) 0)))
   1324       (with-current-buffer log-buffer
   1325         (save-excursion
   1326           (let* ((message (apply 'format format args))
   1327                  ;; Count newlines in message.
   1328                  (newlines (1+ (cl-loop with start = 0
   1329                                         for count from 0
   1330                                         while (string-match "\n" message start)
   1331                                         do (setq start (match-end 0))
   1332                                         finally return count))))
   1333             (goto-char (point-max))
   1334 
   1335             ;; in case the buffer is not empty insert before last \n to preserve
   1336             ;; the point position(in case it is in the end)
   1337             (if (eq (point) (point-min))
   1338                 (progn
   1339                   (insert "\n")
   1340                   (backward-char))
   1341               (backward-char)
   1342               (insert "\n"))
   1343             (insert message)
   1344 
   1345             (setq lsp--log-lines (+ lsp--log-lines newlines))
   1346 
   1347             (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max))
   1348               (let ((to-delete (- lsp--log-lines lsp-log-max)))
   1349                 (goto-char (point-min))
   1350                 (forward-line to-delete)
   1351                 (delete-region (point-min) (point))
   1352                 (setq lsp--log-lines lsp-log-max)))))))))
   1353 
   1354 (defalias 'lsp-message 'lsp-log)
   1355 
   1356 (defalias 'lsp-ht 'ht)
   1357 
   1358 (defalias 'lsp-file-local-name 'file-local-name)
   1359 
   1360 (defun lsp-f-canonical (file-name)
   1361   "Return the canonical FILE-NAME, without a trailing slash."
   1362   (directory-file-name (expand-file-name file-name)))
   1363 
   1364 (defalias 'lsp-canonical-file-name 'lsp-f-canonical)
   1365 
   1366 (defun lsp-f-same? (path-a path-b)
   1367   "Return t if PATH-A and PATH-B are references to the same file.
   1368 Symlinks are not followed."
   1369   (when (and (f-exists? path-a)
   1370              (f-exists? path-b))
   1371     (equal
   1372      (lsp-f-canonical (directory-file-name (f-expand path-a)))
   1373      (lsp-f-canonical (directory-file-name (f-expand path-b))))))
   1374 
   1375 (defun lsp-f-parent (path)
   1376   "Return the parent directory to PATH.
   1377 Symlinks are not followed."
   1378   (let ((parent (file-name-directory
   1379                  (directory-file-name (f-expand path default-directory)))))
   1380     (unless (lsp-f-same? path parent)
   1381       (if (f-relative? path)
   1382           (f-relative parent)
   1383         (directory-file-name parent)))))
   1384 
   1385 (defun lsp-f-ancestor-of? (path-a path-b)
   1386   "Return t if PATH-A is an ancestor of PATH-B.
   1387 Symlinks are not followed."
   1388   (unless (lsp-f-same? path-a path-b)
   1389     (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator))
   1390                (lsp-f-canonical path-b))))
   1391 
   1392 (defun lsp--merge-results (results method)
   1393   "Merge RESULTS by filtering the empty hash-tables and merging
   1394 the lists according to METHOD."
   1395   (pcase (--map (if (vectorp it)
   1396                     (append it nil) it)
   1397                 (-filter #'identity results))
   1398     (`() ())
   1399     ;; only one result - simply return it
   1400     (`(,fst) fst)
   1401     ;; multiple results merge it based on strategy
   1402     (results
   1403      (pcase method
   1404        ("textDocument/hover" (pcase (seq-filter
   1405                                      (-compose #'not #'lsp-empty?)
   1406                                      results)
   1407                                (`(,hover) hover)
   1408                                (hovers (lsp-make-hover
   1409                                         :contents
   1410                                         (-mapcat
   1411                                          (-lambda ((&Hover :contents))
   1412                                            (if (and (sequencep contents)
   1413                                                     (not (stringp contents)))
   1414                                                (append contents ())
   1415                                              (list contents)))
   1416                                          hovers)))))
   1417        ("textDocument/completion"
   1418         (lsp-make-completion-list
   1419          :is-incomplete (seq-some
   1420                          #'lsp:completion-list-is-incomplete
   1421                          results)
   1422          :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it)
   1423                                                     (lsp:completion-list-items it)
   1424                                                   it)
   1425                                                 nil))
   1426                            results)))
   1427        ("completionItem/resolve"
   1428         (let ((item (cl-first results)))
   1429           (when-let ((details (seq-filter #'identity
   1430                                           (seq-map #'lsp:completion-item-detail? results))))
   1431             (lsp:set-completion-item-detail?
   1432              item
   1433              (string-join details " ")))
   1434           (when-let ((docs (seq-filter #'identity
   1435                                        (seq-map #'lsp:completion-item-documentation? results))))
   1436             (lsp:set-completion-item-documentation?
   1437              item
   1438              (lsp-make-markup-content
   1439               :kind (or (seq-some (lambda (it)
   1440                                     (when (equal (lsp:markup-content-kind it)
   1441                                                  lsp/markup-kind-markdown)
   1442                                       lsp/markup-kind-markdown))
   1443                                   docs)
   1444                         lsp/markup-kind-plain-text)
   1445               :value (string-join (seq-map (lambda (doc)
   1446                                              (or (lsp:markup-content-value doc)
   1447                                                  (and (stringp doc) doc)))
   1448                                            docs)
   1449                                   "\n"))))
   1450           (when-let ((edits (seq-filter #'identity
   1451                                         (seq-map #'lsp:completion-item-additional-text-edits? results))))
   1452             (lsp:set-completion-item-additional-text-edits?
   1453              item
   1454              (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits)))
   1455           item))
   1456        (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results))))))
   1457 
   1458 (defun lsp--spinner-start ()
   1459   "Start spinner indication."
   1460   (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error)))
   1461 
   1462 (defun lsp--propertize (str type)
   1463   "Propertize STR as per TYPE."
   1464   (propertize str 'face (alist-get type lsp--message-type-face)))
   1465 
   1466 (defun lsp-workspaces ()
   1467   "Return the lsp workspaces associated with the current project."
   1468   (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces))
   1469 
   1470 (defun lsp--completing-read (prompt collection transform-fn &optional predicate
   1471                                     require-match initial-input
   1472                                     hist def inherit-input-method)
   1473   "Wrap `completing-read' to provide transformation function and disable sort.
   1474 
   1475 TRANSFORM-FN will be used to transform each of the items before displaying.
   1476 
   1477 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF
   1478 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes."
   1479   (let* ((col (--map (cons (funcall transform-fn it) it) collection))
   1480          (completion (completing-read prompt
   1481                                       (lambda (string pred action)
   1482                                         (if (eq action 'metadata)
   1483                                             `(metadata (display-sort-function . identity))
   1484                                           (complete-with-action action col string pred)))
   1485                                       predicate require-match initial-input hist
   1486                                       def inherit-input-method)))
   1487     (cdr (assoc completion col))))
   1488 
   1489 (defconst lsp--system-arch (lambda ()
   1490                              (setq lsp--system-arch
   1491                                    (pcase system-type
   1492                                      ('windows-nt
   1493                                       (pcase system-configuration
   1494                                         ((rx bol "x86_64-") 'x64)
   1495                                         (_ 'x86)))
   1496                                      ('darwin
   1497                                       (pcase system-configuration
   1498                                         ((rx "aarch64-") 'arm64)
   1499                                         (_ 'x64)))
   1500                                      ('gnu/linux
   1501                                        (pcase system-configuration
   1502                                          ((rx bol "x86_64") 'x64)
   1503                                          ((rx bol (| "i386" "i886")) 'x32)))
   1504                                      (_
   1505                                       (pcase system-configuration
   1506                                         ((rx bol "x86_64") 'x64)
   1507                                         ((rx bol (| "i386" "i886")) 'x32))))))
   1508   "Return the system architecture of `Emacs'.
   1509 Special values:
   1510   `x64'       64bit
   1511   `x32'       32bit
   1512   `arm64'     ARM 64bit")
   1513 
   1514 (defmacro lsp-with-current-buffer (buffer-id &rest body)
   1515   (declare (indent 1) (debug t))
   1516   `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer)))
   1517        (with-lsp-workspaces (plist-get ,buffer-id :workspaces)
   1518          (funcall wcb (lambda () ,@body)))
   1519      (with-current-buffer ,buffer-id
   1520        ,@body)))
   1521 
   1522 (defvar lsp--throw-on-input nil
   1523   "Make `lsp-*-while-no-input' throws `input' on interrupted.")
   1524 
   1525 (defmacro lsp--catch (tag bodyform &rest handlers)
   1526   "Catch TAG thrown in BODYFORM.
   1527 The return value from TAG will be handled in HANDLERS by `pcase'."
   1528   (declare (debug (form form &rest (pcase-PAT body))) (indent 2))
   1529   (let ((re-sym (make-symbol "re")))
   1530     `(let ((,re-sym (catch ,tag ,bodyform)))
   1531        (pcase ,re-sym
   1532          ,@handlers))))
   1533 
   1534 (defmacro lsp--while-no-input (&rest body)
   1535   "Wrap BODY in `while-no-input' and respecting `non-essential'.
   1536 If `lsp--throw-on-input' is set, will throw if input is pending, else
   1537 return value of `body' or nil if interrupted."
   1538   (declare (debug t) (indent 0))
   1539   `(if non-essential
   1540        (let ((res (while-no-input ,@body)))
   1541          (cond
   1542           ((and lsp--throw-on-input (equal res t))
   1543            (throw 'input :interrupted))
   1544           ((booleanp res) nil)
   1545           (t res)))
   1546      ,@body))
   1547 
   1548 ;; A ‘lsp--client’ object describes the client-side behavior of a language
   1549 ;; server.  It is used to start individual server processes, each of which is
   1550 ;; represented by a ‘lsp--workspace’ object.  Client objects are normally
   1551 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’.  Each
   1552 ;; workspace refers to exactly one client, but there can be multiple workspaces
   1553 ;; for a single client.
   1554 (cl-defstruct lsp--client
   1555   ;; ‘language-id’ is a function that receives a buffer as a single argument
   1556   ;; and should return the language identifier for that buffer.  See
   1557   ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem
   1558   ;; for a list of language identifiers.  Also consult the documentation for
   1559   ;; the language server represented by this client to find out what language
   1560   ;; identifiers it supports or expects.
   1561   (language-id nil)
   1562 
   1563   ;; ‘add-on?’ when set to t the server will be started no matter whether there
   1564   ;; is another server handling the same mode.
   1565   (add-on? nil)
   1566   ;; ‘new-connection’ is a function that should start a language server process
   1567   ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS).
   1568   ;; COMMAND-PROCESS must be a process object representing the server process
   1569   ;; just started.  COMMUNICATION-PROCESS must be a process (including pipe and
   1570   ;; network processes) that ‘lsp-mode’ uses to communicate with the language
   1571   ;; server using the language server protocol.  COMMAND-PROCESS and
   1572   ;; COMMUNICATION-PROCESS may be the same process; in that case
   1573   ;; ‘new-connection’ may also return that process as a single
   1574   ;; object. ‘new-connection’ is called with two arguments, FILTER and
   1575   ;; SENTINEL.  FILTER should be used as process filter for
   1576   ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for
   1577   ;; COMMAND-PROCESS.
   1578   (new-connection nil)
   1579 
   1580   ;; ‘ignore-regexps’ is a list of regexps.  When a data packet from the
   1581   ;; language server matches any of these regexps, it will be ignored.  This is
   1582   ;; intended for dealing with language servers that output non-protocol data.
   1583   (ignore-regexps nil)
   1584 
   1585   ;; ‘ignore-messages’ is a list of regexps.  When a message from the language
   1586   ;; server matches any of these regexps, it will be ignored.  This is useful
   1587   ;; for filtering out unwanted messages; such as servers that send nonstandard
   1588   ;; message types, or extraneous log messages.
   1589   (ignore-messages nil)
   1590 
   1591   ;; ‘notification-handlers’ is a hash table mapping notification method names
   1592   ;; (strings) to functions handling the respective notifications.  Upon
   1593   ;; receiving a notification, ‘lsp-mode’ will call the associated handler
   1594   ;; function passing two arguments, the ‘lsp--workspace’ object and the
   1595   ;; deserialized notification parameters.
   1596   (notification-handlers (make-hash-table :test 'equal))
   1597 
   1598   ;; ‘request-handlers’ is a hash table mapping request method names
   1599   ;; (strings) to functions handling the respective notifications.  Upon
   1600   ;; receiving a request, ‘lsp-mode’ will call the associated handler function
   1601   ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized
   1602   ;; request parameters.
   1603   (request-handlers (make-hash-table :test 'equal))
   1604 
   1605   ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request
   1606   ;; identifiers for pending asynchronous requests to functions handling the
   1607   ;; respective responses.  Upon receiving a response from the language server,
   1608   ;; ‘lsp-mode’ will call the associated response handler function with a
   1609   ;; single argument, the deserialized response parameters.
   1610   (response-handlers (make-hash-table :test 'eql))
   1611 
   1612   ;; ‘prefix-function’ is called for getting the prefix for completion.
   1613   ;; The function takes no parameter and returns a cons (start . end) representing
   1614   ;; the start and end bounds of the prefix. If it's not set, the client uses a
   1615   ;; default prefix function."
   1616   (prefix-function nil)
   1617 
   1618   ;; Contains mapping of scheme to the function that is going to be used to load
   1619   ;; the file.
   1620   (uri-handlers (make-hash-table :test #'equal))
   1621 
   1622   ;; ‘action-handlers’ is a hash table mapping action to a handler function. It
   1623   ;; can be used in `lsp-execute-code-action' to determine whether the action
   1624   ;; current client is interested in executing the action instead of sending it
   1625   ;; to the server.
   1626   (action-handlers (make-hash-table :test 'equal))
   1627 
   1628   ;; major modes supported by the client.
   1629   major-modes
   1630   ;; Function that will be called to decide if this language client
   1631   ;; should manage a particular buffer. The function will be passed
   1632   ;; the file name and major mode to inform the decision. Setting
   1633   ;; `activation-fn' will override `major-modes', if
   1634   ;; present.
   1635   activation-fn
   1636   ;; Break the tie when major-mode is supported by multiple clients.
   1637   (priority 0)
   1638   ;; Unique identifier for representing the client object.
   1639   server-id
   1640   ;; defines whether the client supports multi root workspaces.
   1641   multi-root
   1642   ;; Initialization options or a function that returns initialization options.
   1643   initialization-options
   1644   ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or
   1645   ;; completely replace, the faces used for semantic highlighting on a
   1646   ;; client-by-client basis.
   1647   ;;
   1648   ;; It recognizes four members, all of which are optional: `:types’ and
   1649   ;; `:modifiers’, respectively, should be face definition lists akin to
   1650   ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be
   1651   ;; merged with the default face definition list.
   1652   ;;
   1653   ;; Alternatively, if the plist members `:discard-default-types’ or
   1654   ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers'
   1655   ;; face definitions will be replaced entirely by their respective overrides.
   1656   ;;
   1657   ;; For example, setting `:semantic-tokens-faces-overrides' to
   1658   ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from
   1659   ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'.
   1660   ;;
   1661   ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))'
   1662   ;; will also remap "macro", but on top of that associate the fictional token type
   1663   ;; "not-quite-a-macro" with the face named `some-face'.
   1664   ;;
   1665   ;; `(:types (("macro" . font-lock-keyword-face))
   1666   ;;   :modifiers (("declaration" . lsp-face-semhl-interface))
   1667   ;;   :discard-default-types t
   1668   ;;   :discard-default-modifiers t)'
   1669   ;; will discard all default face definitions, hence leaving the client with
   1670   ;; only one token type "macro", mapped to `font-lock-keyword-face', and one
   1671   ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'.
   1672   semantic-tokens-faces-overrides
   1673   ;; Provides support for registering LSP Server specific capabilities.
   1674   custom-capabilities
   1675   ;; Function which returns the folders that are considered to be not projects but library files.
   1676   ;; The function accepts one parameter currently active workspace.
   1677   ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225.
   1678   library-folders-fn
   1679   ;; function which will be called when opening file in the workspace to perform
   1680   ;; client specific initialization. The function accepts one parameter
   1681   ;; currently active workspace.
   1682   before-file-open-fn
   1683   ;; Function which will be called right after a workspace has been initialized.
   1684   initialized-fn
   1685   ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP.
   1686   (remote? nil)
   1687 
   1688   ;; ‘completion-in-comments?’ t if the client supports completion in comments.
   1689   (completion-in-comments? nil)
   1690 
   1691   ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client.
   1692   (path->uri-fn nil)
   1693 
   1694   ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client.
   1695   (uri->path-fn nil)
   1696   ;; Function that returns an environment structure that will be used
   1697   ;; to set some environment variables when starting the language
   1698   ;; server process. These environment variables enable some
   1699   ;; additional features in the language server. The environment
   1700   ;; structure is an alist of the form (KEY . VALUE), where KEY is a
   1701   ;; string (regularly in all caps), and VALUE may be a string, a
   1702   ;; boolean, or a sequence of strings.
   1703   environment-fn
   1704 
   1705   ;; ‘after-open-fn’ workspace after open specific hooks.
   1706   (after-open-fn nil)
   1707 
   1708   ;; ‘async-request-handlers’ is a hash table mapping request method names
   1709   ;; (strings) to functions handling the respective requests that may take
   1710   ;; time to finish.  Upon receiving a request, ‘lsp-mode’ will call the
   1711   ;; associated handler function passing three arguments, the ‘lsp--workspace’
   1712   ;; object, the deserialized request parameters and the callback which accept
   1713   ;; result as its parameter.
   1714   (async-request-handlers (make-hash-table :test 'equal))
   1715   download-server-fn
   1716   download-in-progress?
   1717   buffers
   1718   synchronize-sections)
   1719 
   1720 (defun lsp-clients-executable-find (find-command &rest args)
   1721   "Finds an executable by invoking a search command.
   1722 
   1723 FIND-COMMAND is the executable finder that searches for the
   1724 actual language server executable. ARGS is a list of arguments to
   1725 give to FIND-COMMAND to find the language server.  Returns the
   1726 output of FIND-COMMAND if it exits successfully, nil otherwise.
   1727 
   1728 Typical uses include finding an executable by invoking `find' in
   1729 a project, finding LLVM commands on macOS with `xcrun', or
   1730 looking up project-specific language servers for projects written
   1731 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv'
   1732 etc."
   1733   (when-let* ((find-command-path (executable-find find-command))
   1734               (executable-path
   1735                (with-temp-buffer
   1736                  (when (zerop (apply 'call-process find-command-path nil t nil args))
   1737                    (buffer-substring-no-properties (point-min) (point-max))))))
   1738     (string-trim executable-path)))
   1739 
   1740 (defvar lsp--already-widened nil)
   1741 
   1742 (defmacro lsp-save-restriction-and-excursion (&rest form)
   1743   (declare (indent 0) (debug t))
   1744   `(if lsp--already-widened
   1745        (save-excursion ,@form)
   1746      (-let [lsp--already-widened t]
   1747        (save-restriction
   1748          (widen)
   1749          (save-excursion ,@form)))))
   1750 
   1751 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number
   1752 (defun lsp--line-character-to-point (line character)
   1753   "Return the point for character CHARACTER on line LINE."
   1754   (or (lsp-virtual-buffer-call :line/character->point line character)
   1755       (let ((inhibit-field-text-motion t))
   1756         (lsp-save-restriction-and-excursion
   1757           (goto-char (point-min))
   1758           (forward-line line)
   1759           ;; server may send character position beyond the current line and we
   1760           ;; should fallback to line end.
   1761           (-let [line-end (line-end-position)]
   1762             (if (> character (- line-end (point)))
   1763                 line-end
   1764               (forward-char character)
   1765               (point)))))))
   1766 
   1767 (lsp-defun lsp--position-to-point ((&Position :line :character))
   1768   "Convert `Position' object in PARAMS to a point."
   1769   (lsp--line-character-to-point line character))
   1770 
   1771 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end))
   1772   (cons start end))
   1773 
   1774 (lsp-defun lsp--range-text ((&RangeToPoint :start :end))
   1775   (buffer-substring start end))
   1776 
   1777 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end)))
   1778   (cond
   1779    ((and
   1780      (region-active-p)
   1781      (<= start (region-beginning) end)
   1782      (<= start (region-end) end)
   1783      (or (not (= start (region-beginning)))
   1784          (not (= end (region-end)))))
   1785     (cons start end))
   1786    ((and (<= start (point) end)
   1787          (not (region-active-p)))
   1788     (cons start end))
   1789    (parent? (lsp--find-wrapping-range parent?))))
   1790 
   1791 (defun lsp--get-selection-range ()
   1792   (or
   1793    (-when-let ((cache . cache-tick) lsp--document-selection-range-cache)
   1794      (when (= cache-tick (buffer-modified-tick)) cache))
   1795    (let ((response (cl-first
   1796                     (lsp-request
   1797                      "textDocument/selectionRange"
   1798                      (list :textDocument (lsp--text-document-identifier)
   1799                            :positions (vector (lsp--cur-position)))))))
   1800      (setq lsp--document-selection-range-cache
   1801            (cons response (buffer-modified-tick)))
   1802      response)))
   1803 
   1804 (defun lsp-extend-selection ()
   1805   "Extend selection."
   1806   (interactive)
   1807   (unless (lsp-feature? "textDocument/selectionRange")
   1808     (signal 'lsp-capability-not-supported (list "selectionRangeProvider")))
   1809   (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range)))
   1810     (goto-char start)
   1811     (set-mark (point))
   1812     (goto-char end)
   1813     (exchange-point-and-mark)))
   1814 
   1815 (defun lsp-warn (message &rest args)
   1816   "Display a warning message made from (`format-message' MESSAGE ARGS...).
   1817 This is equivalent to `display-warning', using `lsp-mode' as the type and
   1818 `:warning' as the level."
   1819   (display-warning 'lsp-mode (apply #'format-message message args)))
   1820 
   1821 (defun lsp--get-uri-handler (scheme)
   1822   "Get uri handler for SCHEME in the current workspace."
   1823   (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it)))
   1824           (or (lsp-workspaces) (lsp--session-workspaces (lsp-session)))))
   1825 
   1826 (defun lsp--fix-path-casing (path)
   1827   "On windows, downcases path because the windows file system is
   1828 case-insensitive.
   1829 
   1830 On other systems, returns path without change."
   1831   (if (eq system-type 'windows-nt) (downcase path) path))
   1832 
   1833 (defun lsp--uri-to-path (uri)
   1834   "Convert URI to a file path."
   1835   (if-let ((fn (->> (lsp-workspaces)
   1836                     (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client))
   1837                     (cl-first))))
   1838       (funcall fn uri)
   1839     (lsp--uri-to-path-1 uri)))
   1840 
   1841 (defun lsp-remap-path-if-needed (file-name)
   1842   (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings))
   1843       (propertize (buffer-local-value 'buffer-file-name buffer)
   1844                   'lsp-virtual-buffer virtual-buffer)
   1845     file-name))
   1846 
   1847 (defun lsp--uri-to-path-1 (uri)
   1848   "Convert URI to a file path."
   1849   (let* ((url (url-generic-parse-url (url-unhex-string uri)))
   1850          (type (url-type url))
   1851          (target (url-target url))
   1852          (file
   1853           (concat (decode-coding-string (url-filename url)
   1854                                         (or locale-coding-system 'utf-8))
   1855                   (when (and target
   1856                              (not (s-match
   1857                                    (rx "#" (group (1+ num)) (or "," "#")
   1858                                        (group (1+ num))
   1859                                        string-end)
   1860                                    uri)))
   1861                     (concat "#" target))))
   1862          (file-name (if (and type (not (string= type "file")))
   1863                         (if-let ((handler (lsp--get-uri-handler type)))
   1864                             (funcall handler uri)
   1865                           uri)
   1866                       ;; `url-generic-parse-url' is buggy on windows:
   1867                       ;; https://github.com/emacs-lsp/lsp-mode/pull/265
   1868                       (or (and (eq system-type 'windows-nt)
   1869                                (eq (elt file 0) ?\/)
   1870                                (substring file 1))
   1871                           file))))
   1872     (->> file-name
   1873          (concat (-some #'lsp--workspace-host-root (lsp-workspaces)))
   1874          (lsp-remap-path-if-needed))))
   1875 
   1876 (defun lsp--buffer-uri ()
   1877   "Return URI of the current buffer."
   1878   (or lsp-buffer-uri
   1879       (plist-get lsp--virtual-buffer :buffer-uri)
   1880       (lsp--path-to-uri
   1881        (or (buffer-file-name) (buffer-file-name (buffer-base-buffer))))))
   1882 
   1883 (defun lsp-register-client-capabilities (&rest _args)
   1884   "Implemented only to make `company-lsp' happy.
   1885 DELETE when `lsp-mode.el' is deleted.")
   1886 
   1887 (defconst lsp--url-path-allowed-chars
   1888   (url--allowed-chars (append '(?/) url-unreserved-chars))
   1889   "`url-unreserved-chars' with additional delim ?/.
   1890 This set of allowed chars is enough for hexifying local file paths.")
   1891 
   1892 (defun lsp--path-to-uri-1 (path)
   1893   (concat lsp--uri-file-prefix
   1894           (--> path
   1895             (expand-file-name it)
   1896             (or (file-remote-p it 'localname t) it)
   1897             (url-hexify-string it lsp--url-path-allowed-chars))))
   1898 
   1899 (defun lsp--path-to-uri (path)
   1900   "Convert PATH to a uri."
   1901   (if-let ((uri-fn (->> (lsp-workspaces)
   1902                         (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client))
   1903                         (cl-first))))
   1904       (funcall uri-fn path)
   1905     (lsp--path-to-uri-1 path)))
   1906 
   1907 (defun lsp--string-match-any (regex-list str)
   1908   "Return the first regex, if any, within REGEX-LIST matching STR."
   1909   (--first (string-match it str) regex-list))
   1910 
   1911 (cl-defstruct lsp-watch
   1912   (descriptors (make-hash-table :test 'equal))
   1913   root-directory)
   1914 
   1915 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories)
   1916   (let ((file-name (cl-third event))
   1917         (event-type (cl-second event)))
   1918     (cond
   1919      ((and (file-directory-p file-name)
   1920            (equal 'created event-type)
   1921            (not (lsp--string-match-any ignored-directories file-name)))
   1922 
   1923       (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch)
   1924 
   1925       ;; process the files that are already present in
   1926       ;; the directory.
   1927       (->> (directory-files-recursively file-name ".*" t)
   1928            (seq-do (lambda (f)
   1929                      (unless (file-directory-p f)
   1930                        (funcall callback (list nil 'created f)))))))
   1931      ((and (memq event-type '(created deleted changed))
   1932            (not (file-directory-p file-name))
   1933            (not (lsp--string-match-any ignored-files file-name)))
   1934       (funcall callback event))
   1935      ((and (memq event-type '(renamed))
   1936            (not (file-directory-p file-name))
   1937            (not (lsp--string-match-any ignored-files file-name)))
   1938       (funcall callback `(,(cl-first event) deleted ,(cl-third event)))
   1939       (funcall callback `(,(cl-first event) created ,(cl-fourth event)))))))
   1940 
   1941 (defun lsp--ask-about-watching-big-repo (number-of-directories dir)
   1942   "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR.
   1943 This is useful when there is a lot of files in a repository, as
   1944 that may slow Emacs down. Returns t if the user wants to watch
   1945 the entire repository, nil otherwise."
   1946   (prog1
   1947       (yes-or-no-p
   1948        (format
   1949         "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down.
   1950 Do you want to watch all files in %s? "
   1951         dir
   1952         number-of-directories
   1953         dir))
   1954     (lsp--info
   1955      (concat "You can configure this warning with the `lsp-enable-file-watchers' "
   1956              "and `lsp-file-watch-threshold' variables"))))
   1957 
   1958 
   1959 (defun lsp--path-is-watchable-directory (path dir ignored-directories)
   1960   "Figure out whether PATH (inside of DIR) is meant to have a file watcher set.
   1961 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't
   1962 want to watch."
   1963   (let
   1964       ((full-path (f-join dir path)))
   1965     (and (file-accessible-directory-p full-path)
   1966          (not (equal path "."))
   1967          (not (equal path ".."))
   1968          (not (lsp--string-match-any ignored-directories full-path)))))
   1969 
   1970 
   1971 (defun lsp--all-watchable-directories (dir ignored-directories)
   1972   "Traverse DIR recursively returning a list of paths that should have watchers.
   1973 IGNORED-DIRECTORIES will be used for exclusions"
   1974   (let* ((dir (if (f-symlink? dir)
   1975                   (file-truename dir)
   1976                 dir)))
   1977     (apply #'nconc
   1978            ;; the directory itself is assumed to be part of the set
   1979            (list dir)
   1980            ;; collect all subdirectories that are watchable
   1981            (-map
   1982             (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories))
   1983             ;; but only look at subdirectories that are watchable
   1984             (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories))
   1985                      (directory-files dir))))))
   1986 
   1987 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?)
   1988   "Create recursive file notification watch in DIR.
   1989 CALLBACK will be called when there are changes in any of
   1990 the monitored files. WATCHES is a hash table directory->file
   1991 notification handle which contains all of the watch that
   1992 already have been created. Watches will not be created for
   1993 any directory that matches any regex in IGNORED-DIRECTORIES.
   1994 Watches will not be created for any file that matches any
   1995 regex in IGNORED-FILES."
   1996   (let* ((dir (if (f-symlink? dir)
   1997                   (file-truename dir)
   1998                 dir))
   1999          (watch (or watch (make-lsp-watch :root-directory dir)))
   2000          (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories)))
   2001     (lsp-log "Creating watchers for following %s folders:\n  %s"
   2002              (length dirs-to-watch)
   2003              (s-join "\n  " dirs-to-watch))
   2004     (when (or
   2005            (not warn-big-repo?)
   2006            (not lsp-file-watch-threshold)
   2007            (let ((number-of-directories (length dirs-to-watch)))
   2008              (or
   2009               (< number-of-directories lsp-file-watch-threshold)
   2010               (condition-case nil
   2011                   (lsp--ask-about-watching-big-repo number-of-directories dir)
   2012                 (quit)))))
   2013       (dolist (current-dir dirs-to-watch)
   2014         (condition-case err
   2015             (progn
   2016               (puthash
   2017                current-dir
   2018                (file-notify-add-watch current-dir
   2019                                       '(change)
   2020                                       (lambda (event)
   2021                                         (lsp--folder-watch-callback event callback watch ignored-files ignored-directories)))
   2022                (lsp-watch-descriptors watch)))
   2023           (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))
   2024           (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err))))))
   2025     watch))
   2026 
   2027 (defun lsp-kill-watch (watch)
   2028   "Delete WATCH."
   2029   (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch))
   2030   (ht-clear! (lsp-watch-descriptors watch)))
   2031 
   2032 (defun lsp-json-bool (val)
   2033   "Convert VAL to JSON boolean."
   2034   (if val t :json-false))
   2035 
   2036 (defmacro with-lsp-workspace (workspace &rest body)
   2037   "Helper macro for invoking BODY in WORKSPACE context."
   2038   (declare (debug (form body))
   2039            (indent 1))
   2040   `(let ((lsp--cur-workspace ,workspace)) ,@body))
   2041 
   2042 (defmacro with-lsp-workspaces (workspaces &rest body)
   2043   "Helper macro for invoking BODY against multiple WORKSPACES."
   2044   (declare (debug (form body))
   2045            (indent 1))
   2046   `(let ((lsp--buffer-workspaces ,workspaces)) ,@body))
   2047 
   2048 
   2049 
   2050 (defmacro lsp-consistency-check (package)
   2051   `(defconst ,(intern (concat (symbol-name package)
   2052                               "-plist-value-when-compiled"))
   2053      (eval-when-compile lsp-use-plists)))
   2054 
   2055 
   2056 ;; loading code-workspace files
   2057 
   2058 ;;;###autoload
   2059 (defun lsp-load-vscode-workspace (file)
   2060   "Load vscode workspace from FILE"
   2061   (interactive "fSelect file to import: ")
   2062   (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session)))
   2063 
   2064   (let ((dir (f-dirname file)))
   2065     (->> file
   2066          (json-read-file)
   2067          (alist-get 'folders)
   2068          (-map (-lambda ((&alist 'path))
   2069                  (lsp-workspace-folders-add (expand-file-name path dir)))))))
   2070 
   2071 ;;;###autoload
   2072 (defun lsp-save-vscode-workspace (file)
   2073   "Save vscode workspace to FILE"
   2074   (interactive "FSelect file to save to: ")
   2075 
   2076   (let ((json-encoding-pretty-print t))
   2077     (f-write-text (json-encode
   2078                    `((folders . ,(->> (lsp-session)
   2079                                       (lsp-session-folders)
   2080                                       (--map `((path . ,it)))))))
   2081                   'utf-8
   2082                   file)))
   2083 
   2084 
   2085 (defmacro lsp-foreach-workspace (&rest body)
   2086   "Execute BODY for each of the current workspaces."
   2087   (declare (debug (form body)))
   2088   `(--map (with-lsp-workspace it ,@body) (lsp-workspaces)))
   2089 
   2090 (defmacro when-lsp-workspace (workspace &rest body)
   2091   "Helper macro for invoking BODY in WORKSPACE context if present."
   2092   (declare (debug (form body))
   2093            (indent 1))
   2094   `(when-let ((lsp--cur-workspace ,workspace)) ,@body))
   2095 
   2096 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items))
   2097   (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read))
   2098             (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label))
   2099                                  items))
   2100             (result (funcall-interactively
   2101                      selectfunc
   2102                      (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels))
   2103             (choices (if (listp result)
   2104                          (if (equal result '("*"))
   2105                              itemLabels
   2106                            result)
   2107                        (list result))))
   2108       (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data))
   2109                                                  (if (member label choices)
   2110                                                      (lsp-make-quick-pick-item :label label :picked t :user-data user-data)
   2111                                                    nil))
   2112                                                items)))))
   2113 
   2114 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?))
   2115   (read-string (format "%s: " prompt) (or value? "")))
   2116 
   2117 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type))
   2118   "Send the server's messages to log.
   2119 PARAMS - the data sent from _WORKSPACE."
   2120   (funcall (cl-case type
   2121              (1 'lsp--error)
   2122              (2 'lsp--warn)
   2123              (t 'lsp--info))
   2124            "%s"
   2125            message))
   2126 
   2127 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type))
   2128   "Send the server's messages to log.
   2129 PARAMS - the data sent from WORKSPACE."
   2130   (ignore
   2131    (let ((client (lsp--workspace-client workspace)))
   2132      (when (or (not client)
   2133                (cl-notany (-rpartial #'string-match-p message)
   2134                           (lsp--client-ignore-messages client)))
   2135        (lsp-log "%s" (lsp--propertize message type))))))
   2136 
   2137 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?))
   2138   "Display a message request to user sending the user selection back to server."
   2139   (let* ((message (lsp--propertize message type))
   2140          (choices (seq-map #'lsp:message-action-item-title actions?)))
   2141     (if choices
   2142         (completing-read (concat message " ") (seq-into choices 'list) nil t)
   2143       (lsp-log message))))
   2144 
   2145 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?))
   2146   "Show document URI in a buffer and go to SELECTION if any."
   2147   (let ((path (lsp--uri-to-path uri)))
   2148     (when (f-exists? path)
   2149       (with-current-buffer (find-file path)
   2150         (when selection?
   2151           (goto-char (lsp--position-to-point (lsp:range-start selection?))))
   2152         t))))
   2153 
   2154 (defcustom lsp-progress-prefix " ⌛ "
   2155   "Progress prefix."
   2156   :group 'lsp-mode
   2157   :type 'string
   2158   :package-version '(lsp-mode . "8.0.0"))
   2159 
   2160 (defcustom lsp-progress-function #'lsp-on-progress-modeline
   2161   "Function for handling the progress notifications."
   2162   :group 'lsp-mode
   2163   :type '(choice
   2164           (const :tag "Use modeline" lsp-on-progress-modeline)
   2165           (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')"
   2166                  lsp-on-progress-legacy)
   2167           (const :tag "Ignore" ignore)
   2168           (function :tag "Other function"))
   2169   :package-version '(lsp-mode . "8.0.0"))
   2170 
   2171 (defcustom lsp-request-while-no-input-may-block nil
   2172   "Have `lsp-request-while-no-input` block unless `non-essential` is t."
   2173   :group 'lsp-mode
   2174   :type 'boolean)
   2175 
   2176 (defun lsp--progress-status ()
   2177   "Returns the status of the progress for the current workspaces."
   2178   (-let ((progress-status
   2179           (s-join
   2180            "|"
   2181            (-keep
   2182             (lambda (workspace)
   2183               (let ((tokens (lsp--workspace-work-done-tokens workspace)))
   2184                 (unless (ht-empty? tokens)
   2185                   (mapconcat
   2186                    (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?))
   2187                      (concat (if percentage?
   2188                                  (if (numberp percentage?)
   2189                                      (format "%.0f%%%% " percentage?)
   2190                                    (format "%s%%%% " percentage?))
   2191                                "")
   2192                              (or message? title)))
   2193                    (ht-values tokens)
   2194                    "|"))))
   2195             (lsp-workspaces)))))
   2196     (unless (s-blank? progress-status)
   2197       (concat lsp-progress-prefix progress-status))))
   2198 
   2199 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value
   2200                                                                 (value &as &WorkDoneProgress :kind)))
   2201   "PARAMS contains the progress data.
   2202 WORKSPACE is the workspace that contains the progress token."
   2203   (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status))))
   2204   (pcase kind
   2205     ("begin" (lsp-workspace-set-work-done-token token value workspace))
   2206     ("report" (lsp-workspace-set-work-done-token token value workspace))
   2207     ("end" (lsp-workspace-rem-work-done-token token workspace)))
   2208   (force-mode-line-update))
   2209 
   2210 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value
   2211                                                               (value &as &WorkDoneProgress :kind)))
   2212   "PARAMS contains the progress data.
   2213 WORKSPACE is the workspace that contains the progress token."
   2214   (pcase kind
   2215     ("begin"
   2216      (-let* (((&WorkDoneProgressBegin :title :percentage?) value)
   2217              (reporter
   2218               (if lsp-progress-via-spinner
   2219                   (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types))
   2220                          ;; Set message as a tooltip for the spinner strings
   2221                          (propertized-strings
   2222                           (seq-map (lambda (string) (propertize string 'help-echo title))
   2223                                    spinner-strings))
   2224                          (spinner-type (vconcat propertized-strings)))
   2225                     ;; The progress relates to the server as a whole,
   2226                     ;; display it on all buffers.
   2227                     (mapcar (lambda (buffer)
   2228                               (lsp-with-current-buffer buffer
   2229                                 (spinner-start spinner-type))
   2230                               buffer)
   2231                             (lsp--workspace-buffers workspace)))
   2232                 (if percentage?
   2233                     (make-progress-reporter title 0 100 percentage?)
   2234                   ;; No percentage, just progress
   2235                   (make-progress-reporter title nil nil)))))
   2236        (lsp-workspace-set-work-done-token token reporter workspace)))
   2237     ("report"
   2238      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2239        (unless lsp-progress-via-spinner
   2240          (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value)))))
   2241 
   2242     ("end"
   2243      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2244        (if lsp-progress-via-spinner
   2245            (mapc (lambda (buffer)
   2246                    (when (lsp-buffer-live-p buffer)
   2247                      (lsp-with-current-buffer buffer
   2248                        (spinner-stop))))
   2249                  reporter)
   2250          (progress-reporter-done reporter))
   2251        (lsp-workspace-rem-work-done-token token workspace)))))
   2252 
   2253 
   2254 ;; diagnostics
   2255 
   2256 (defvar lsp-diagnostic-filter nil
   2257   "A a function which will be called with
   2258   `&PublishDiagnosticsParams' and `workspace' which can be used
   2259   to filter out the diagnostics. The function should return
   2260   `&PublishDiagnosticsParams'.
   2261 
   2262 Common usecase are:
   2263 1. Filter the diagnostics for a particular language server.
   2264 2. Filter out the diagnostics under specific level.")
   2265 
   2266 (defvar lsp-diagnostic-stats (ht))
   2267 
   2268 (defun lsp-diagnostics (&optional current-workspace?)
   2269   "Return the diagnostics from all workspaces."
   2270   (or (pcase (if current-workspace?
   2271                  (lsp-workspaces)
   2272                (lsp--session-workspaces (lsp-session)))
   2273         (`() ())
   2274         (`(,workspace) (lsp--workspace-diagnostics workspace))
   2275         (`,workspaces (let ((result (make-hash-table :test 'equal)))
   2276                         (mapc (lambda (workspace)
   2277                                 (->> workspace
   2278                                      (lsp--workspace-diagnostics)
   2279                                      (maphash (lambda (file-name diagnostics)
   2280                                                 (puthash file-name
   2281                                                          (append (gethash file-name result) diagnostics)
   2282                                                          result)))))
   2283                               workspaces)
   2284                         result)))
   2285       (ht)))
   2286 
   2287 (defun lsp-diagnostics-stats-for (path)
   2288   "Get diagnostics statistics for PATH.
   2289 The result format is vector [_ errors warnings infos hints] or nil."
   2290   (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats))
   2291 
   2292 (defun lsp-diagnostics--update-path (path new-stats)
   2293   (let ((new-stats (copy-sequence new-stats))
   2294         (path (lsp--fix-path-casing (directory-file-name path))))
   2295     (if-let ((old-data (gethash path lsp-diagnostic-stats)))
   2296         (dotimes (idx 5)
   2297           (cl-callf + (aref old-data idx)
   2298             (aref new-stats idx)))
   2299       (puthash path new-stats lsp-diagnostic-stats))))
   2300 
   2301 (lsp-defun lsp--on-diagnostics-update-stats (workspace
   2302                                              (&PublishDiagnosticsParams :uri :diagnostics))
   2303   (let ((path (lsp--fix-path-casing (lsp--uri-to-path uri)))
   2304         (new-stats (make-vector 5 0)))
   2305     (mapc (-lambda ((&Diagnostic :severity?))
   2306             (cl-incf (aref new-stats (or severity? 1))))
   2307           diagnostics)
   2308     (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace))))
   2309       (mapc (-lambda ((&Diagnostic :severity?))
   2310               (cl-decf (aref new-stats (or severity? 1))))
   2311             old-diags))
   2312     (lsp-diagnostics--update-path path new-stats)
   2313     (while (not (string= path (setf path (file-name-directory
   2314                                           (directory-file-name path)))))
   2315       (lsp-diagnostics--update-path path new-stats))))
   2316 
   2317 (defun lsp--on-diagnostics (workspace params)
   2318   "Callback for textDocument/publishDiagnostics.
   2319 interface PublishDiagnosticsParams {
   2320     uri: string;
   2321     diagnostics: Diagnostic[];
   2322 }
   2323 PARAMS contains the diagnostics data.
   2324 WORKSPACE is the workspace that contains the diagnostics."
   2325   (when lsp-diagnostic-filter
   2326     (setf params (funcall lsp-diagnostic-filter params workspace)))
   2327 
   2328   (lsp--on-diagnostics-update-stats workspace params)
   2329 
   2330   (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params)
   2331           (lsp--virtual-buffer-mappings (ht))
   2332           (file (lsp--fix-path-casing (lsp--uri-to-path uri)))
   2333           (workspace-diagnostics (lsp--workspace-diagnostics workspace)))
   2334 
   2335     (if (seq-empty-p diagnostics)
   2336         (remhash file workspace-diagnostics)
   2337       (puthash file (append diagnostics nil) workspace-diagnostics))
   2338 
   2339     (run-hooks 'lsp-diagnostics-updated-hook)))
   2340 
   2341 (defun lsp-diagnostics--workspace-cleanup (workspace)
   2342   (->> workspace
   2343        (lsp--workspace-diagnostics)
   2344        (maphash (lambda (key _)
   2345                   (lsp--on-diagnostics-update-stats
   2346                    workspace
   2347                    (lsp-make-publish-diagnostics-params
   2348                     :uri (lsp--path-to-uri key)
   2349                     :diagnostics [])))))
   2350   (clrhash (lsp--workspace-diagnostics workspace)))
   2351 
   2352 
   2353 
   2354 ;; textDocument/foldingRange support
   2355 
   2356 (cl-defstruct lsp--folding-range beg end kind children)
   2357 
   2358 (defvar-local lsp--cached-folding-ranges nil)
   2359 (defvar-local lsp--cached-nested-folding-ranges nil)
   2360 
   2361 (defun lsp--folding-range-width (range)
   2362   (- (lsp--folding-range-end range)
   2363      (lsp--folding-range-beg range)))
   2364 
   2365 (defun lsp--get-folding-ranges ()
   2366   "Get the folding ranges for the current buffer."
   2367   (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges))
   2368     (let* ((ranges (lsp-request "textDocument/foldingRange"
   2369                                 `(:textDocument ,(lsp--text-document-identifier))))
   2370            (sorted-line-col-pairs (->> ranges
   2371                                        (cl-mapcan (-lambda ((&FoldingRange :start-line
   2372                                                                            :start-character?
   2373                                                                            :end-line
   2374                                                                            :end-character?))
   2375                                                     (list (cons start-line start-character?)
   2376                                                           (cons end-line end-character?))))
   2377                                        (-sort #'lsp--line-col-comparator)))
   2378            (line-col-to-point-map (lsp--convert-line-col-to-points-batch
   2379                                    sorted-line-col-pairs)))
   2380       (setq lsp--cached-folding-ranges
   2381             (cons (buffer-chars-modified-tick)
   2382                   (--> ranges
   2383                     (seq-map (-lambda ((range &as
   2384                                               &FoldingRange :start-line
   2385                                               :start-character?
   2386                                               :end-line
   2387                                               :end-character?
   2388                                               :kind?))
   2389                                (make-lsp--folding-range
   2390                                 :beg (ht-get line-col-to-point-map
   2391                                              (cons start-line start-character?))
   2392                                 :end (ht-get line-col-to-point-map
   2393                                              (cons end-line end-character?))
   2394                                 :kind kind?))
   2395                              it)
   2396                     (seq-filter (lambda (folding-range)
   2397                                   (< (lsp--folding-range-beg folding-range)
   2398                                      (lsp--folding-range-end folding-range)))
   2399                                 it)
   2400                     (seq-into it 'list)
   2401                     (delete-dups it))))))
   2402   (cdr lsp--cached-folding-ranges))
   2403 
   2404 (defun lsp--get-nested-folding-ranges ()
   2405   "Get a list of nested folding ranges for the current buffer."
   2406   (-let [(tick . _) lsp--cached-folding-ranges]
   2407     (if (and (eq tick (buffer-chars-modified-tick))
   2408              lsp--cached-nested-folding-ranges)
   2409         lsp--cached-nested-folding-ranges
   2410       (setq lsp--cached-nested-folding-ranges
   2411             (lsp--folding-range-build-trees (lsp--get-folding-ranges))))))
   2412 
   2413 (defun lsp--folding-range-build-trees (ranges)
   2414   (setq ranges (seq-sort #'lsp--range-before-p ranges))
   2415   (let* ((dummy-node (make-lsp--folding-range
   2416                       :beg most-negative-fixnum
   2417                       :end most-positive-fixnum))
   2418          (stack (list dummy-node)))
   2419     (dolist (range ranges)
   2420       (while (not (lsp--range-inside-p range (car stack)))
   2421         (pop stack))
   2422       (push range (lsp--folding-range-children (car stack)))
   2423       (push range stack))
   2424     (lsp--folding-range-children dummy-node)))
   2425 
   2426 (defun lsp--range-inside-p (r1 r2)
   2427   "Return non-nil if folding range R1 lies inside R2"
   2428   (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2))
   2429        (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2))))
   2430 
   2431 (defun lsp--range-before-p (r1 r2)
   2432   "Return non-nil if folding range R1 ends before R2"
   2433   ;; Ensure r1 comes before r2
   2434   (or (< (lsp--folding-range-beg r1)
   2435          (lsp--folding-range-beg r2))
   2436       ;; If beg(r1) == beg(r2) make sure r2 ends first
   2437       (and (= (lsp--folding-range-beg r1)
   2438               (lsp--folding-range-beg r2))
   2439            (< (lsp--folding-range-end r2)
   2440               (lsp--folding-range-end r1)))))
   2441 
   2442 (defun lsp--point-inside-range-p (point range)
   2443   "Return non-nil if POINT lies inside folding range RANGE."
   2444   (and (>= point (lsp--folding-range-beg range))
   2445        (<= point (lsp--folding-range-end range))))
   2446 
   2447 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point)))
   2448   "Return the innermost folding range POINT lies in."
   2449   (seq-reduce (lambda (innermost-range curr-range)
   2450                 (if (and (lsp--point-inside-range-p point curr-range)
   2451                          (or (null innermost-range)
   2452                              (lsp--range-inside-p curr-range innermost-range)))
   2453                     curr-range
   2454                   innermost-range))
   2455               (lsp--get-folding-ranges)
   2456               nil))
   2457 
   2458 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point)))
   2459   "Return the outermost folding range POINT lies in."
   2460   (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range)
   2461                      (let ((curr-width (lsp--folding-range-width curr-range)))
   2462                        (if (and (lsp--point-inside-range-p point curr-range)
   2463                                 (or (null best-pair)
   2464                                     (> curr-width outermost-width)))
   2465                            (cons curr-width curr-range)
   2466                          best-pair)))
   2467                    (lsp--get-folding-ranges)
   2468                    nil)))
   2469 
   2470 (defun lsp--folding-range-at-point-bounds ()
   2471   (when (and lsp-enable-folding
   2472              (lsp-feature? "textDocument/foldingRange"))
   2473     (if-let ((range (lsp--get-current-innermost-folding-range)))
   2474         (cons (lsp--folding-range-beg range)
   2475               (lsp--folding-range-end range)))))
   2476 (put 'lsp--folding-range 'bounds-of-thing-at-point
   2477      #'lsp--folding-range-at-point-bounds)
   2478 
   2479 (defun lsp--get-nearest-folding-range (&optional backward)
   2480   (let ((point (point))
   2481         (found nil))
   2482     (while (not
   2483             (or found
   2484                 (if backward
   2485                     (<= point (point-min))
   2486                   (>= point (point-max)))))
   2487       (if backward (cl-decf point) (cl-incf point))
   2488       (setq found (lsp--get-current-innermost-folding-range point)))
   2489     found))
   2490 
   2491 (defun lsp--folding-range-at-point-forward-op (n)
   2492   (when (and lsp-enable-folding
   2493              (not (zerop n))
   2494              (lsp-feature? "textDocument/foldingRange"))
   2495     (cl-block break
   2496       (dotimes (_ (abs n))
   2497         (if-let ((range (lsp--get-nearest-folding-range (< n 0))))
   2498             (goto-char (if (< n 0)
   2499                            (lsp--folding-range-beg range)
   2500                          (lsp--folding-range-end range)))
   2501           (cl-return-from break))))))
   2502 (put 'lsp--folding-range 'forward-op
   2503      #'lsp--folding-range-at-point-forward-op)
   2504 
   2505 (defun lsp--folding-range-at-point-beginning-op ()
   2506   (goto-char (car (lsp--folding-range-at-point-bounds))))
   2507 (put 'lsp--folding-range 'beginning-op
   2508      #'lsp--folding-range-at-point-beginning-op)
   2509 
   2510 (defun lsp--folding-range-at-point-end-op ()
   2511   (goto-char (cdr (lsp--folding-range-at-point-bounds))))
   2512 (put 'lsp--folding-range 'end-op
   2513      #'lsp--folding-range-at-point-end-op)
   2514 
   2515 (defun lsp--range-at-point-bounds ()
   2516   (or (lsp--folding-range-at-point-bounds)
   2517       (when-let ((range (and
   2518                          (lsp-feature? "textDocument/hover")
   2519                          (->> (lsp--text-document-position-params)
   2520                               (lsp-request "textDocument/hover")
   2521                               (lsp:hover-range?)))))
   2522         (lsp--range-to-region range))))
   2523 
   2524 ;; A more general purpose "thing", useful for applications like focus.el
   2525 (put 'lsp--range 'bounds-of-thing-at-point
   2526      #'lsp--range-at-point-bounds)
   2527 
   2528 (defun lsp--log-io-p (method)
   2529   "Return non nil if should log for METHOD."
   2530   (and lsp-log-io
   2531        (or (not lsp-log-io-allowlist-methods)
   2532            (member method lsp-log-io-allowlist-methods))))
   2533 
   2534 
   2535 ;; toggles
   2536 
   2537 (defun lsp-toggle-trace-io ()
   2538   "Toggle client-server protocol logging."
   2539   (interactive)
   2540   (setq lsp-log-io (not lsp-log-io))
   2541   (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled")))
   2542 
   2543 (defun lsp-toggle-signature-auto-activate ()
   2544   "Toggle signature auto activate."
   2545   (interactive)
   2546   (setq lsp-signature-auto-activate
   2547         (unless lsp-signature-auto-activate '(:on-trigger-char)))
   2548   (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled"))
   2549   (lsp--update-signature-help-hook))
   2550 
   2551 (defun lsp-toggle-on-type-formatting ()
   2552   "Toggle on type formatting."
   2553   (interactive)
   2554   (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting))
   2555   (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled"))
   2556   (lsp--update-on-type-formatting-hook))
   2557 
   2558 (defun lsp-toggle-symbol-highlight ()
   2559   "Toggle symbol highlighting."
   2560   (interactive)
   2561   (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting))
   2562 
   2563   (cond
   2564    ((and lsp-enable-symbol-highlighting
   2565          (lsp-feature? "textDocument/documentHighlight"))
   2566     (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)
   2567     (lsp--info "Symbol highlighting enabled in current buffer."))
   2568    ((not lsp-enable-symbol-highlighting)
   2569     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   2570     (lsp--remove-overlays 'lsp-highlight)
   2571     (lsp--info "Symbol highlighting disabled in current buffer."))))
   2572 
   2573 
   2574 ;; keybindings
   2575 (defvar lsp--binding-descriptions nil
   2576   "List of key binding/short description pair.")
   2577 
   2578 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings)
   2579   "In KEYMAP, define key sequence KEY as DEF conditionally.
   2580 This is like `define-key', except the definition disappears
   2581 whenever COND evaluates to nil.
   2582 DESC is the short-description for the binding.
   2583 BINDINGS is a list of (key def desc cond)."
   2584   (declare (indent defun)
   2585            (debug (form form form form form &rest sexp)))
   2586   (->> (cl-list* key def desc cond bindings)
   2587        (-partition 4)
   2588        (-mapcat (-lambda ((key def desc cond))
   2589                   `((define-key ,keymap ,key
   2590                       '(menu-item
   2591                         ,(format "maybe-%s" def)
   2592                         ,def
   2593                         :filter
   2594                         (lambda (item)
   2595                           (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer)
   2596                                                            lsp--describe-buffer)
   2597                                                          (current-buffer))
   2598                                   ,cond)
   2599                             item))))
   2600                     (when (stringp ,key)
   2601                       (setq lsp--binding-descriptions
   2602                             (append lsp--binding-descriptions '(,key ,desc)))))))
   2603        macroexp-progn))
   2604 
   2605 (defvar lsp--describe-buffer nil)
   2606 
   2607 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus)
   2608   (let ((lsp--describe-buffer buffer))
   2609     (funcall fn buffer prefix menus)))
   2610 
   2611 (advice-add 'describe-buffer-bindings
   2612             :around
   2613             #'lsp-describe-buffer-bindings-advice)
   2614 
   2615 (defun lsp--prepend-prefix (mappings)
   2616   (->> mappings
   2617        (-partition 2)
   2618        (-mapcat (-lambda ((key description))
   2619                   (list (concat lsp-keymap-prefix " " key)
   2620                         description)))))
   2621 
   2622 (defvar lsp-command-map
   2623   (-doto (make-sparse-keymap)
   2624     (lsp-define-conditional-key
   2625       ;; workspaces
   2626       "wD" lsp-disconnect "disconnect" (lsp-workspaces)
   2627       "wd" lsp-describe-session "describe session" t
   2628       "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces)
   2629       "wr" lsp-workspace-restart "restart server" (lsp-workspaces)
   2630       "ws" lsp "start server" t
   2631 
   2632       ;; formatting
   2633       "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting")
   2634                                                  (lsp-feature? "textDocument/formatting"))
   2635       "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting")
   2636 
   2637       ;; folders
   2638       "Fa" lsp-workspace-folders-add "add folder" t
   2639       "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t
   2640       "Fr" lsp-workspace-folders-remove "remove folder" t
   2641 
   2642       ;; toggles
   2643       "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature?
   2644                                                                         "textDocument/publishDiagnostics")
   2645       "TL" lsp-toggle-trace-io "toggle log io" t
   2646       "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline)
   2647       "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs)
   2648       "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature?
   2649                                                                           "textDocument/codeAction")
   2650       "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature?
   2651                                                                "textDocument/documentSymbol")
   2652       "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc)
   2653       "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature?
   2654                                                                       "textDocument/onTypeFormatting")
   2655       "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight")
   2656       "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens")
   2657       "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp")
   2658 
   2659       ;; goto
   2660       "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol")
   2661       "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration")
   2662       "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list)
   2663       "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition")
   2664       "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls")
   2665                                                              (fboundp 'lsp-treemacs-call-hierarchy))
   2666       "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation")
   2667       "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references")
   2668       "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition")
   2669 
   2670       ;; help
   2671       "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc)
   2672                                                   (lsp-feature? "textDocument/hover"))
   2673       "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover")
   2674       "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp")
   2675 
   2676       ;; refactoring
   2677       "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction")
   2678       "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename")
   2679 
   2680       ;; actions
   2681       "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction")
   2682       "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight")
   2683       "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy))
   2684 
   2685       ;; peeks
   2686       "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition")
   2687                                                                 (fboundp 'lsp-ui-peek-find-definitions))
   2688       "Gi" lsp-ui-peek-find-implementation "peek implementations" (and
   2689                                                                    (fboundp 'lsp-ui-peek-find-implementation)
   2690                                                                    (lsp-feature? "textDocument/implementation"))
   2691       "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references)
   2692                                                               (lsp-feature? "textDocument/references"))
   2693       "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp
   2694                                                                            'lsp-ui-peek-find-workspace-symbol)
   2695                                                                           (lsp-feature? "workspace/symbol")))))
   2696 
   2697 
   2698 ;; which-key integration
   2699 
   2700 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key")
   2701 (declare-function which-key-add-key-based-replacements "ext:which-key")
   2702 
   2703 (defun lsp-enable-which-key-integration (&optional all-modes)
   2704   "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current
   2705 active `major-mode', or for all major modes when ALL-MODES is t."
   2706   (cl-flet ((which-key-fn (if all-modes
   2707                               'which-key-add-key-based-replacements
   2708                             (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode))))
   2709     (apply
   2710      #'which-key-fn
   2711      (lsp--prepend-prefix
   2712       (cl-list*
   2713        ""    "lsp"
   2714        "w"   "workspaces"
   2715        "F"   "folders"
   2716        "="   "formatting"
   2717        "T"   "toggle"
   2718        "g"   "goto"
   2719        "h"   "help"
   2720        "r"   "refactor"
   2721        "a"   "code actions"
   2722        "G"   "peek"
   2723        lsp--binding-descriptions)))))
   2724 
   2725 
   2726 ;; Globbing syntax
   2727 
   2728 ;; We port VSCode's glob-to-regexp code
   2729 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts)
   2730 ;; since the LSP globbing syntax seems to be the same as that of
   2731 ;; VSCode.
   2732 
   2733 (defconst lsp-globstar "**"
   2734   "Globstar pattern.")
   2735 
   2736 (defconst lsp-glob-split ?/
   2737   "The character by which we split path components in a glob
   2738 pattern.")
   2739 
   2740 (defconst lsp-path-regexp "[/\\\\]"
   2741   "Forward or backslash to be used as a path separator in
   2742 computed regexps.")
   2743 
   2744 (defconst lsp-non-path-regexp "[^/\\\\]"
   2745   "A regexp matching anything other than a slash.")
   2746 
   2747 (defconst lsp-globstar-regexp
   2748   (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?"
   2749           lsp-path-regexp
   2750           lsp-non-path-regexp lsp-path-regexp
   2751           lsp-path-regexp lsp-non-path-regexp)
   2752   "Globstar in regexp form.")
   2753 
   2754 (defun lsp-split-glob-pattern (pattern split-char)
   2755   "Split PATTERN at SPLIT-CHAR while respecting braces and brackets."
   2756   (when pattern
   2757     (let ((segments nil)
   2758           (in-braces nil)
   2759           (in-brackets nil)
   2760           (current-segment ""))
   2761       (dolist (char (string-to-list pattern))
   2762         (cl-block 'exit-point
   2763           (if (eq char split-char)
   2764               (when (and (null in-braces)
   2765                          (null in-brackets))
   2766                 (push current-segment segments)
   2767                 (setq current-segment "")
   2768                 (cl-return-from 'exit-point))
   2769             (pcase char
   2770               (?{
   2771                (setq in-braces t))
   2772               (?}
   2773                (setq in-braces nil))
   2774               (?\[
   2775                (setq in-brackets t))
   2776               (?\]
   2777                (setq in-brackets nil))))
   2778           (setq current-segment (concat current-segment
   2779                                         (char-to-string char)))))
   2780       (unless (string-empty-p current-segment)
   2781         (push current-segment segments))
   2782       (nreverse segments))))
   2783 
   2784 (defun lsp--glob-to-regexp (pattern)
   2785   "Helper function to convert a PATTERN from LSP's glob syntax to
   2786 an Elisp regexp."
   2787   (if (string-empty-p pattern)
   2788       ""
   2789     (let ((current-regexp "")
   2790           (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split)))
   2791       (if (-all? (lambda (segment) (eq segment lsp-globstar))
   2792                  glob-segments)
   2793           ".*"
   2794         (let ((prev-segment-was-globstar nil))
   2795           (seq-do-indexed
   2796            (lambda (segment index)
   2797              (if (string-equal segment lsp-globstar)
   2798                  (unless prev-segment-was-globstar
   2799                    (setq current-regexp (concat current-regexp
   2800                                                 lsp-globstar-regexp))
   2801                    (setq prev-segment-was-globstar t))
   2802                (let ((in-braces nil)
   2803                      (brace-val "")
   2804                      (in-brackets nil)
   2805                      (bracket-val ""))
   2806                  (dolist (char (string-to-list segment))
   2807                    (cond
   2808                     ((and (not (char-equal char ?\}))
   2809                           in-braces)
   2810                      (setq brace-val (concat brace-val
   2811                                              (char-to-string char))))
   2812                     ((and in-brackets
   2813                           (or (not (char-equal char ?\]))
   2814                               (string-empty-p bracket-val)))
   2815                      (let ((curr (cond
   2816                                   ((char-equal char ?-)
   2817                                    "-")
   2818                                   ;; NOTE: ?\^ and ?^ are different characters
   2819                                   ((and (memq char '(?^ ?!))
   2820                                         (string-empty-p bracket-val))
   2821                                    "^")
   2822                                   ((char-equal char lsp-glob-split)
   2823                                    "")
   2824                                   (t
   2825                                    (regexp-quote (char-to-string char))))))
   2826                        (setq bracket-val (concat bracket-val curr))))
   2827                     (t
   2828                      (cl-case char
   2829                        (?{
   2830                         (setq in-braces t))
   2831                        (?\[
   2832                         (setq in-brackets t))
   2833                        (?}
   2834                         (let* ((choices (lsp-split-glob-pattern brace-val ?\,))
   2835                                (brace-regexp (concat "\\(?:"
   2836                                                      (mapconcat #'lsp--glob-to-regexp choices "\\|")
   2837                                                      "\\)")))
   2838                           (setq current-regexp (concat current-regexp
   2839                                                        brace-regexp))
   2840                           (setq in-braces nil)
   2841                           (setq brace-val "")))
   2842                        (?\]
   2843                         (setq current-regexp
   2844                               (concat current-regexp
   2845                                       "[" bracket-val "]"))
   2846                         (setq in-brackets nil)
   2847                         (setq bracket-val ""))
   2848                        (??
   2849                         (setq current-regexp
   2850                               (concat current-regexp
   2851                                       lsp-non-path-regexp)))
   2852                        (?*
   2853                         (setq current-regexp
   2854                               (concat current-regexp
   2855                                       lsp-non-path-regexp "*?")))
   2856                        (t
   2857                         (setq current-regexp
   2858                               (concat current-regexp
   2859                                       (regexp-quote (char-to-string char)))))))))
   2860                  (when (and (< index (1- (length glob-segments)))
   2861                             (or (not (string-equal (nth (1+ index) glob-segments)
   2862                                                    lsp-globstar))
   2863                                 (< (+ index 2)
   2864                                    (length glob-segments))))
   2865                    (setq current-regexp
   2866                          (concat current-regexp
   2867                                  lsp-path-regexp)))
   2868                  (setq prev-segment-was-globstar nil))))
   2869            glob-segments)
   2870           current-regexp)))))
   2871 
   2872 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365
   2873 (defun lsp-glob-unbrace-at-top-level (glob-pattern)
   2874   "If GLOB-PATTERN does not start with a brace, return a singleton list
   2875 containing GLOB-PATTERN.
   2876 
   2877 If GLOB-PATTERN does start with a brace, return a list of the
   2878 comma-separated globs within the top-level braces."
   2879   (if (not (string-prefix-p "{" glob-pattern))
   2880       (list glob-pattern)
   2881     (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,)))
   2882 
   2883 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern)
   2884   "Convert GLOB-PATTERN to a regexp wrapped with the beginning-
   2885 and end-of-string meta-characters."
   2886   (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'"))
   2887 
   2888 (defun lsp-glob-to-regexps (glob-pattern)
   2889   "Convert a GLOB-PATTERN to a list of Elisp regexps."
   2890   (when-let*
   2891       ((glob-pattern (cond ((hash-table-p glob-pattern)
   2892                             (ht-get glob-pattern "pattern"))
   2893                            ((stringp glob-pattern) glob-pattern)
   2894                            (t (error "Unknown glob-pattern type: %s" glob-pattern))))
   2895        (trimmed-pattern (string-trim glob-pattern))
   2896        (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern)))
   2897     (seq-map #'lsp-glob-convert-to-wrapped-regexp
   2898              top-level-unbraced-patterns)))
   2899 
   2900 
   2901 
   2902 (defvar lsp-mode-menu)
   2903 
   2904 (defun lsp-mouse-click (event)
   2905   (interactive "e")
   2906   (let* ((ec (event-start event))
   2907          (choice (x-popup-menu event lsp-mode-menu))
   2908          (action (lookup-key lsp-mode-menu (apply 'vector choice))))
   2909 
   2910     (select-window (posn-window ec))
   2911 
   2912     (unless (and (region-active-p) (eq action 'lsp-execute-code-action))
   2913       (goto-char (posn-point ec)))
   2914     (run-with-idle-timer
   2915      0.001 nil
   2916      (lambda ()
   2917        (cl-labels ((check (value) (not (null value))))
   2918          (when choice
   2919            (call-interactively action)))))))
   2920 
   2921 (defvar lsp-mode-map
   2922   (let ((map (make-sparse-keymap)))
   2923     (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse)
   2924     (define-key map (kbd "C-<mouse-1>") #'ignore)
   2925     (define-key map (kbd "<mouse-3>") #'lsp-mouse-click)
   2926     (define-key map (kbd "C-S-SPC") #'lsp-signature-activate)
   2927     (when lsp-keymap-prefix
   2928       (define-key map (kbd lsp-keymap-prefix) lsp-command-map))
   2929     map)
   2930   "Keymap for `lsp-mode'.")
   2931 
   2932 (define-minor-mode lsp-mode "Mode for LSP interaction."
   2933   :keymap lsp-mode-map
   2934   :lighter
   2935   (" LSP["
   2936    (lsp--buffer-workspaces
   2937     (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "]["))
   2938     (:propertize "Disconnected" face warning))
   2939    "]")
   2940   :group 'lsp-mode
   2941   (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred))
   2942     ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp'
   2943     (lsp)))
   2944 
   2945 (defvar lsp-mode-menu
   2946   (easy-menu-create-menu
   2947    nil
   2948    `(["Go to definition" lsp-find-definition
   2949       :active (lsp-feature? "textDocument/definition")]
   2950      ["Find references" lsp-find-references
   2951       :active (lsp-feature? "textDocument/references")]
   2952      ["Find implementations" lsp-find-implementation
   2953       :active (lsp-feature? "textDocument/implementation")]
   2954      ["Find declarations" lsp-find-declaration
   2955       :active (lsp-feature? "textDocument/declaration")]
   2956      ["Go to type declaration" lsp-find-type-definition
   2957       :active (lsp-feature? "textDocument/typeDefinition")]
   2958      "--"
   2959      ["Describe" lsp-describe-thing-at-point]
   2960      ["Code action" lsp-execute-code-action]
   2961      ["Format" lsp-format-buffer]
   2962      ["Highlight references" lsp-document-highlight]
   2963      ["Type Hierarchy" lsp-java-type-hierarchy
   2964       :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")]
   2965      ["Type Hierarchy" lsp-treemacs-type-hierarchy
   2966       :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy"))
   2967                     (functionp 'lsp-treemacs-type-hierarchy)
   2968                     (lsp-feature? "textDocument/typeHierarchy"))]
   2969      ["Call Hierarchy" lsp-treemacs-call-hierarchy
   2970       :visible (and (functionp 'lsp-treemacs-call-hierarchy)
   2971                     (lsp-feature? "textDocument/callHierarchy"))]
   2972      ["Rename" lsp-rename
   2973       :active (lsp-feature? "textDocument/rename")]
   2974      "--"
   2975      ("Session"
   2976       ["View logs" lsp-workspace-show-log]
   2977       ["Describe" lsp-describe-session]
   2978       ["Shutdown" lsp-shutdown-workspace]
   2979       ["Restart" lsp-restart-workspace])
   2980      ("Workspace Folders"
   2981       ["Add" lsp-workspace-folders-add]
   2982       ["Remove" lsp-workspace-folders-remove]
   2983       ["Open" lsp-workspace-folders-open])
   2984      ("Toggle features"
   2985       ["Lenses" lsp-lens-mode]
   2986       ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode]
   2987       ["Modeline code actions" lsp-modeline-code-actions-mode]
   2988       ["Modeline diagnostics" lsp-modeline-diagnostics-mode])
   2989      "---"
   2990      ("Debug"
   2991       :active (bound-and-true-p dap-ui-mode)
   2992       :filter ,(lambda (_)
   2993                  (and (boundp 'dap-ui-menu-items)
   2994                       (nthcdr 3 dap-ui-menu-items))))))
   2995   "Menu for lsp-mode.")
   2996 
   2997 (defalias 'make-lsp-client 'make-lsp--client)
   2998 
   2999 (cl-defstruct lsp--registered-capability
   3000   (id "")
   3001   (method " ")
   3002   (options nil))
   3003 
   3004 ;; A ‘lsp--workspace’ object represents exactly one language server process.
   3005 (cl-defstruct lsp--workspace
   3006   ;; the `ewoc' object for displaying I/O to and from the server
   3007   (ewoc nil)
   3008 
   3009   ;; ‘server-capabilities’ is a hash table of the language server capabilities.
   3010   ;; It is the hash table representation of a LSP ServerCapabilities structure;
   3011   ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize.
   3012   (server-capabilities nil)
   3013 
   3014   ;; ‘registered-server-capabilities’ is a list of hash tables that represent
   3015   ;; dynamically-registered Registration objects.  See
   3016   ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability.
   3017   (registered-server-capabilities nil)
   3018 
   3019   ;; ‘root’ is a directory name or a directory file name for the workspace
   3020   ;; root.  ‘lsp-mode’ passes this directory to the ‘initialize’ method of the
   3021   ;; language server; see
   3022   ;; https://microsoft.github.io/language-server-protocol/specification#initialize.
   3023   (root nil)
   3024 
   3025   ;; ‘client’ is the ‘lsp--client’ object associated with this workspace.
   3026   (client nil)
   3027 
   3028   ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It
   3029   ;; used to derive the file path in `lsp--uri-to-path' when using tramp
   3030   ;; connection.
   3031   (host-root nil)
   3032 
   3033   ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or
   3034   ;; a network connection.  ‘lsp-mode’ communicates with ‘proc’ using the
   3035   ;; language server protocol.  ‘proc’ corresponds to the COMMUNICATION-PROCESS
   3036   ;; element of the return value of the client’s ‘get-root’ field, which see.
   3037   (proc nil)
   3038 
   3039   ;; ‘proc’ is a process object; it must represent a regular process, not a
   3040   ;; pipe or network process.  It represents the actual server process that
   3041   ;; corresponds to this workspace.  ‘cmd-proc’ corresponds to the
   3042   ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’
   3043   ;; field, which see.
   3044   (cmd-proc nil)
   3045 
   3046   ;; ‘buffers’ is a list of buffers associated with this workspace.
   3047   (buffers nil)
   3048 
   3049   ;; if semantic tokens is enabled, `semantic-tokens-faces' contains
   3050   ;; one face (or nil) for each token type supported by the language server.
   3051   (semantic-tokens-faces nil)
   3052 
   3053   ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces'
   3054   ;; contains one face (or nil) for each modifier type supported by the language
   3055   ;; server
   3056   (semantic-tokens-modifier-faces nil)
   3057 
   3058   ;; Extra client capabilities provided by third-party packages using
   3059   ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME
   3060   ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name,
   3061   ;; and CAPS is either a plist of the client capabilities, or a function that
   3062   ;; takes no argument and returns a plist of the client capabilities or nil.
   3063   (extra-client-capabilities nil)
   3064 
   3065   ;; Workspace status
   3066   (status nil)
   3067 
   3068   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3069   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3070   (metadata (make-hash-table :test 'equal))
   3071 
   3072   ;; contains all the file notification watches that have been created for the
   3073   ;; current workspace in format filePath->file notification handle.
   3074   (watches (make-hash-table :test 'equal))
   3075 
   3076   ;; list of workspace folders
   3077   (workspace-folders nil)
   3078 
   3079   ;; ‘last-id’ the last request id for the current workspace.
   3080   (last-id 0)
   3081 
   3082   ;; ‘status-string’ allows extensions to specify custom status string based on
   3083   ;; the Language Server specific messages.
   3084   (status-string nil)
   3085 
   3086   ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it
   3087   ;; was stopped).
   3088   shutdown-action
   3089 
   3090   ;; ‘diagnostics’ a hashmap with workspace diagnostics.
   3091   (diagnostics (make-hash-table :test 'equal))
   3092 
   3093   ;; contains all the workDone progress tokens that have been created
   3094   ;; for the current workspace.
   3095   (work-done-tokens (make-hash-table :test 'equal)))
   3096 
   3097 
   3098 (cl-defstruct lsp-session
   3099   ;; contains the folders that are part of the current session
   3100   folders
   3101   ;; contains the folders that must not be imported in the current workspace.
   3102   folders-blocklist
   3103   ;; contains the list of folders that must be imported in a project in case of
   3104   ;; multi root LSP server.
   3105   (server-id->folders (make-hash-table :test 'equal))
   3106   ;; folder to list of the servers that are associated with the folder.
   3107   (folder->servers (make-hash-table :test 'equal))
   3108   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3109   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3110   (metadata (make-hash-table :test 'equal)))
   3111 
   3112 (defun lsp-workspace-status (status-string &optional workspace)
   3113   "Set current workspace status to STATUS-STRING.
   3114 If WORKSPACE is not specified defaults to lsp--cur-workspace."
   3115   (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string))))
   3116     (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string)))
   3117 
   3118 (defun lsp-session-set-metadata (key value &optional _workspace)
   3119   "Associate KEY with VALUE in the WORKSPACE metadata.
   3120 If WORKSPACE is not provided current workspace will be used."
   3121   (puthash key value (lsp-session-metadata (lsp-session))))
   3122 
   3123 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata)
   3124 
   3125 (defun lsp-session-get-metadata (key &optional _workspace)
   3126   "Lookup KEY in WORKSPACE metadata.
   3127 If WORKSPACE is not provided current workspace will be used."
   3128   (gethash key (lsp-session-metadata (lsp-session))))
   3129 
   3130 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata)
   3131 
   3132 (defun lsp-workspace-set-work-done-token (token value workspace)
   3133   "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens."
   3134   (puthash token value (lsp--workspace-work-done-tokens workspace)))
   3135 
   3136 (defun lsp-workspace-get-work-done-token (token workspace)
   3137   "Lookup TOKEN in the WORKSPACE work-done-tokens."
   3138   (gethash token (lsp--workspace-work-done-tokens workspace)))
   3139 
   3140 (defun lsp-workspace-rem-work-done-token (token workspace)
   3141   "Remove TOKEN from the WORKSPACE work-done-tokens."
   3142   (remhash token (lsp--workspace-work-done-tokens workspace)))
   3143 
   3144 
   3145 (defun lsp--make-notification (method &optional params)
   3146   "Create notification body for method METHOD and parameters PARAMS."
   3147   (list :jsonrpc "2.0" :method method :params params))
   3148 
   3149 (defalias 'lsp--make-request 'lsp--make-notification)
   3150 (defalias 'lsp-make-request 'lsp--make-notification)
   3151 
   3152 (defun lsp--make-response (id result)
   3153   "Create response for REQUEST with RESULT."
   3154   `(:jsonrpc "2.0" :id ,id :result ,result))
   3155 
   3156 (defun lsp-make-notification (method &optional params)
   3157   "Create notification body for method METHOD and parameters PARAMS."
   3158   (lsp--make-notification method params))
   3159 
   3160 (defmacro lsp--json-serialize (params)
   3161   (if (progn
   3162         (require 'json)
   3163         (fboundp 'json-serialize))
   3164       `(json-serialize ,params
   3165                        :null-object nil
   3166                        :false-object :json-false)
   3167     `(let ((json-false :json-false))
   3168        (json-encode ,params))))
   3169 
   3170 (defun lsp--make-message (params)
   3171   "Create a LSP message from PARAMS, after encoding it to a JSON string."
   3172   (let ((body (lsp--json-serialize params)))
   3173     (concat "Content-Length: "
   3174             (number-to-string (1+ (string-bytes body)))
   3175             "\r\n\r\n"
   3176             body
   3177             "\n")))
   3178 
   3179 (cl-defstruct lsp--log-entry timestamp process-time type method id body)
   3180 
   3181 (defun lsp--make-log-entry (method id body type &optional process-time)
   3182   "Create an outgoing log object from BODY with method METHOD and id ID.
   3183 If ID is non-nil, then the body is assumed to be a notification.
   3184 TYPE can either be `incoming' or `outgoing'"
   3185   (cl-assert (memq type '(incoming-req outgoing-req incoming-notif
   3186                                        outgoing-notif incoming-resp
   3187                                        outgoing-resp)))
   3188   (make-lsp--log-entry
   3189    :timestamp (format-time-string "%I:%M:%S %p")
   3190    :process-time process-time
   3191    :method method
   3192    :id id
   3193    :type type
   3194    :body body))
   3195 
   3196 (defun lsp--log-font-lock-json (body)
   3197   "Font lock JSON BODY."
   3198   (with-temp-buffer
   3199     (insert body)
   3200     ;; We set the temp buffer file-name extension to .json and call `set-auto-mode'
   3201     ;; so the users configured json mode is used which could be
   3202     ;; `json-mode', `json-ts-mode', `jsonian-mode', etc.
   3203     (let ((buffer-file-name "lsp-log.json"))
   3204       (delay-mode-hooks
   3205         (set-auto-mode)
   3206         (if (fboundp 'font-lock-ensure)
   3207             (font-lock-ensure)
   3208           (with-no-warnings
   3209             (font-lock-fontify-buffer)))))
   3210     (buffer-string)))
   3211 
   3212 (defun lsp--log-entry-pp (entry)
   3213   (cl-assert (lsp--log-entry-p entry))
   3214   (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time
   3215                           body)
   3216                entry)
   3217               (json-false :json-false)
   3218               (json-encoding-pretty-print t)
   3219               (str nil))
   3220     (setq str
   3221           (concat (format "[Trace - %s] " timestamp)
   3222                   (pcase type
   3223                     ('incoming-req (format "Received request '%s - (%s)." method id))
   3224                     ('outgoing-req (format "Sending request '%s - (%s)'." method id))
   3225 
   3226                     ('incoming-notif (format "Received notification '%s'." method))
   3227                     ('outgoing-notif (format "Sending notification '%s'." method))
   3228 
   3229                     ('incoming-resp (format "Received response '%s - (%s)' in %dms."
   3230                                             method id process-time))
   3231                     ('outgoing-resp
   3232                      (format
   3233                       "Sending response '%s - (%s)'. Processing request took %dms"
   3234                       method id process-time)))
   3235                   "\n"
   3236                   (if (memq type '(incoming-resp ougoing-resp))
   3237                       "Result: "
   3238                     "Params: ")
   3239                   (lsp--log-font-lock-json (json-encode body))
   3240                   "\n\n\n"))
   3241     (setq str (propertize str 'mouse-face 'highlight 'read-only t))
   3242     (insert str)))
   3243 
   3244 (defvar-local lsp--log-io-ewoc nil)
   3245 
   3246 (defun lsp--get-create-io-ewoc (workspace)
   3247   (if (and (lsp--workspace-ewoc workspace)
   3248            (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace))))
   3249       (lsp--workspace-ewoc workspace)
   3250     (with-current-buffer (lsp--get-log-buffer-create workspace)
   3251       (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode))
   3252       (setq-local window-point-insertion-type t)
   3253       (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t))
   3254       (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc))
   3255     (lsp--workspace-ewoc workspace)))
   3256 
   3257 (defun lsp--ewoc-count (ewoc)
   3258   (let* ((count 0)
   3259          (count-fn (lambda (_) (setq count (1+ count)))))
   3260     (ewoc-map count-fn ewoc)
   3261     count))
   3262 
   3263 (defun lsp--log-entry-new (entry workspace)
   3264   (let* ((ewoc (lsp--get-create-io-ewoc workspace))
   3265          (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc)))
   3266          (node (if (or (eq lsp-io-messages-max t)
   3267                        (>= lsp-io-messages-max count))
   3268                    nil
   3269                  (ewoc-nth ewoc (1- lsp-io-messages-max))))
   3270          (prev nil)
   3271          (inhibit-read-only t))
   3272     (while node
   3273       (setq prev (ewoc-prev ewoc node))
   3274       (ewoc-delete ewoc node)
   3275       (setq node prev))
   3276     (ewoc-enter-last ewoc entry)))
   3277 
   3278 (defun lsp--send-notification (body)
   3279   "Send BODY as a notification to the language server."
   3280   (lsp-foreach-workspace
   3281    (when (lsp--log-io-p (plist-get body :method))
   3282      (lsp--log-entry-new (lsp--make-log-entry
   3283                           (plist-get body :method)
   3284                           nil (plist-get body :params) 'outgoing-notif)
   3285                          lsp--cur-workspace))
   3286    (lsp--send-no-wait body
   3287                       (lsp--workspace-proc lsp--cur-workspace))))
   3288 
   3289 (defalias 'lsp-send-notification 'lsp--send-notification)
   3290 
   3291 (defun lsp-notify (method params)
   3292   "Send notification METHOD with PARAMS."
   3293   (lsp--send-notification (lsp--make-notification method params)))
   3294 
   3295 (defun lsp--cur-workspace-check ()
   3296   "Check whether buffer lsp workspace(s) are set."
   3297   (cl-assert (lsp-workspaces) nil
   3298              "No language server(s) is associated with this buffer."))
   3299 
   3300 (defun lsp--send-request (body &optional no-wait no-merge)
   3301   "Send BODY as a request to the language server, get the response.
   3302 If NO-WAIT is non-nil, don't synchronously wait for a response.
   3303 If NO-MERGE is non-nil, don't merge the results but return an
   3304 alist mapping workspace->result."
   3305   (lsp-request (plist-get body :method)
   3306                (plist-get body :params)
   3307                :no-wait no-wait
   3308                :no-merge no-merge))
   3309 
   3310 (defalias 'lsp-send-request 'lsp--send-request
   3311   "Send BODY as a request to the language server and return the response
   3312 synchronously.
   3313 \n(fn BODY)")
   3314 
   3315 (cl-defun lsp-request (method params &key no-wait no-merge)
   3316   "Send request METHOD with PARAMS.
   3317 If NO-MERGE is non-nil, don't merge the results but return alist
   3318 workspace->result.
   3319 If NO-WAIT is non-nil send the request as notification."
   3320   (if no-wait
   3321       (lsp-notify method params)
   3322     (let* ((send-time (float-time))
   3323            ;; max time by which we must get a response
   3324            (expected-time
   3325             (and
   3326              lsp-response-timeout
   3327              (+ send-time lsp-response-timeout)))
   3328            resp-result resp-error done?)
   3329       (unwind-protect
   3330           (progn
   3331             (lsp-request-async method params
   3332                                (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3333                                :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3334                                :no-merge no-merge
   3335                                :mode 'detached
   3336                                :cancel-token :sync-request)
   3337             (while (not (or resp-error resp-result))
   3338               (if (functionp 'json-rpc-connection)
   3339                   (catch 'lsp-done (sit-for 0.01))
   3340                 (catch 'lsp-done
   3341                   (accept-process-output
   3342                    nil
   3343                    (if expected-time (- expected-time send-time) 1))))
   3344               (setq send-time (float-time))
   3345               (when (and expected-time (< expected-time send-time))
   3346                 (error "Timeout while waiting for response.  Method: %s" method)))
   3347             (setq done? t)
   3348             (cond
   3349              ((eq resp-result :finished) nil)
   3350              (resp-result resp-result)
   3351              ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3352              ((lsp-json-error? (cl-first resp-error))
   3353               (error (lsp:json-error-message (cl-first resp-error))))))
   3354         (unless done?
   3355           (lsp-cancel-request-by-token :sync-request))))))
   3356 
   3357 (cl-defun lsp-request-while-no-input (method params)
   3358   "Send request METHOD with PARAMS and waits until there is no input.
   3359 Return same value as `lsp--while-no-input' and respecting `non-essential'."
   3360   (if (or non-essential (not lsp-request-while-no-input-may-block))
   3361       (let* ((send-time (float-time))
   3362              ;; max time by which we must get a response
   3363              (expected-time
   3364               (and
   3365                lsp-response-timeout
   3366                (+ send-time lsp-response-timeout)))
   3367              resp-result resp-error done?)
   3368         (unwind-protect
   3369             (progn
   3370               (lsp-request-async method params
   3371                                  (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3372                                  :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3373                                  :mode 'detached
   3374                                  :cancel-token :sync-request)
   3375               (while (not (or resp-error resp-result (input-pending-p)))
   3376                 (catch 'lsp-done
   3377                   (sit-for
   3378                    (if expected-time (- expected-time send-time) 1)))
   3379                 (setq send-time (float-time))
   3380                 (when (and expected-time (< expected-time send-time))
   3381                   (error "Timeout while waiting for response.  Method: %s" method)))
   3382               (setq done? (or resp-error resp-result))
   3383               (cond
   3384                ((eq resp-result :finished) nil)
   3385                (resp-result resp-result)
   3386                ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3387                ((lsp-json-error? (cl-first resp-error))
   3388                 (error (lsp:json-error-message (cl-first resp-error))))))
   3389           (unless done?
   3390             (lsp-cancel-request-by-token :sync-request))
   3391           (when (and (input-pending-p) lsp--throw-on-input)
   3392             (throw 'input :interrupted))))
   3393     (lsp-request method params)))
   3394 
   3395 (defvar lsp--cancelable-requests (ht))
   3396 
   3397 (cl-defun lsp-request-async (method params callback
   3398                                     &key mode error-handler cancel-handler no-merge cancel-token)
   3399   "Send METHOD with PARAMS as a request to the language server.
   3400 Call CALLBACK with the response received from the server
   3401 asynchronously.
   3402 MODE determines when the callback will be called depending on the
   3403 condition of the original buffer.  It could be:
   3404 - `detached' which means that the callback will be executed no
   3405 matter what has happened to the buffer.
   3406 - `alive' - the callback will be executed only if the buffer from
   3407 which the call was executed is still alive.
   3408 - `current' the callback will be executed only if the original buffer
   3409 is still selected.
   3410 - `tick' - the callback will be executed only if the buffer was not modified.
   3411 - `unchanged' - the callback will be executed only if the buffer hasn't
   3412 changed and if the buffer is not modified.
   3413 
   3414 ERROR-HANDLER will be called in case the request has failed.
   3415 CANCEL-HANDLER will be called in case the request is being canceled.
   3416 If NO-MERGE is non-nil, don't merge the results but return alist
   3417 workspace->result.
   3418 CANCEL-TOKEN is the token that can be used to cancel request."
   3419   (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params)
   3420                            callback mode error-handler cancel-handler no-merge cancel-token))
   3421 
   3422 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback)
   3423   (lambda (&rest _)
   3424     (unless (and (equal 'post-command-hook hook)
   3425                  (equal (current-buffer) buf))
   3426       (lsp--request-cleanup-hooks id)
   3427       (with-lsp-workspaces workspaces
   3428         (lsp--cancel-request id)
   3429         (when cancel-callback (funcall cancel-callback)))
   3430       (lsp-log "Cancelling %s(%s) in hook %s" method id hook))))
   3431 
   3432 (defun lsp--create-async-callback
   3433     (callback method no-merge workspaces)
   3434   "Create async handler expecting COUNT results, merge them and call CALLBACK.
   3435 MODE determines when the callback will be called depending on the
   3436 condition of the original buffer. METHOD is the invoked method.
   3437 If NO-MERGE is non-nil, don't merge the results but return alist
   3438 workspace->result. ID is the request id."
   3439   (let (results errors)
   3440     (lambda (result)
   3441       (push (cons lsp--cur-workspace result)
   3442             (if (eq result :error) errors results))
   3443       (when (and (not (eq (length errors) (length workspaces)))
   3444                  (eq (+ (length errors) (length results)) (length workspaces)))
   3445         (funcall callback
   3446                  (if no-merge
   3447                      results
   3448                    (lsp--merge-results (-map #'cl-rest results) method)))))))
   3449 
   3450 (defcustom lsp-default-create-error-handler-fn nil
   3451   "Default error handler customization.
   3452 Handler should give METHOD as argument and return function of one argument
   3453 ERROR."
   3454   :type 'function
   3455   :group 'lsp-mode
   3456   :package-version '(lsp-mode . "9.0.0"))
   3457 
   3458 (defun lsp--create-default-error-handler (method)
   3459   "Default error handler.
   3460 METHOD is the executed method."
   3461   (if lsp-default-create-error-handler-fn
   3462       (funcall lsp-default-create-error-handler-fn method)
   3463     (lambda (error)
   3464       (lsp--warn "%s" (or (lsp--error-string error)
   3465                           (format "%s Request has failed" method))))))
   3466 
   3467 (defvar lsp--request-cleanup-hooks (ht))
   3468 
   3469 (defun lsp--request-cleanup-hooks (request-id)
   3470   (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks)))
   3471     (funcall cleanup-function)
   3472     (remhash request-id lsp--request-cleanup-hooks)))
   3473 
   3474 (defun lsp-cancel-request-by-token (cancel-token)
   3475   "Cancel request using CANCEL-TOKEN."
   3476   (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests))
   3477     (with-lsp-workspaces workspaces
   3478       (lsp--cancel-request request-id))
   3479     (remhash cancel-token lsp--cancelable-requests)
   3480     (lsp--request-cleanup-hooks request-id)))
   3481 
   3482 (defun lsp--send-request-async (body callback
   3483                                      &optional mode error-callback cancel-callback
   3484                                      no-merge cancel-token)
   3485   "Send BODY as a request to the language server.
   3486 Call CALLBACK with the response received from the server
   3487 asynchronously.
   3488 MODE determines when the callback will be called depending on the
   3489 condition of the original buffer.  It could be:
   3490 - `detached' which means that the callback will be executed no
   3491 matter what has happened to the buffer.
   3492 - `alive' - the callback will be executed only if the buffer from
   3493 which the call was executed is still alive.
   3494 - `current' the callback will be executed only if the original buffer
   3495 is still selected.
   3496 - `tick' - the callback will be executed only if the buffer was not modified.
   3497 - `unchanged' - the callback will be executed only if the buffer hasn't
   3498 changed and if the buffer is not modified.
   3499 
   3500 ERROR-CALLBACK will be called in case the request has failed.
   3501 CANCEL-CALLBACK will be called in case the request is being canceled.
   3502 If NO-MERGE is non-nil, don't merge the results but return alist
   3503 workspace->result.
   3504 CANCEL-TOKEN is the token that can be used to cancel request."
   3505   (when cancel-token
   3506     (lsp-cancel-request-by-token cancel-token))
   3507 
   3508   (if-let ((target-workspaces (lsp--find-workspaces-for body)))
   3509       (let* ((start-time (current-time))
   3510              (method (plist-get body :method))
   3511              (id (cl-incf lsp-last-id))
   3512              (buf (current-buffer))
   3513              (cancel-callback (when cancel-callback
   3514                                 (pcase mode
   3515                                   ((or 'alive 'tick 'unchanged)
   3516                                    (lambda ()
   3517                                      (with-current-buffer buf
   3518                                        (funcall cancel-callback))))
   3519                                   (_ cancel-callback))))
   3520              ;; calculate what are the (hook . local) pairs which will cancel
   3521              ;; the request
   3522              (hooks (pcase mode
   3523                       ('alive     '((kill-buffer-hook . t)))
   3524                       ('tick      '((kill-buffer-hook . t) (after-change-functions . t)))
   3525                       ('unchanged '((after-change-functions . t) (post-command-hook . nil)))
   3526                       ('current   '((post-command-hook . nil)))))
   3527              ;; note: lambdas in emacs can be compared but we should make sure
   3528              ;; that all of the captured arguments are the same - in our case
   3529              ;; `lsp--create-request-cancel' will return the same lambda when
   3530              ;; called with the same params.
   3531              (cleanup-hooks
   3532               (lambda () (mapc
   3533                           (-lambda ((hook . local))
   3534                             (if local
   3535                                 (when (buffer-live-p buf)
   3536                                   (with-current-buffer buf
   3537                                     (remove-hook hook
   3538                                                  (lsp--create-request-cancel
   3539                                                   id target-workspaces hook buf method cancel-callback)
   3540                                                  t)))
   3541                               (remove-hook hook (lsp--create-request-cancel
   3542                                                  id target-workspaces hook buf method cancel-callback))))
   3543                           hooks)
   3544                 (remhash cancel-token lsp--cancelable-requests)))
   3545              (callback (pcase mode
   3546                          ((or 'alive 'tick 'unchanged) (lambda (&rest args)
   3547                                                          (with-current-buffer buf
   3548                                                            (apply callback args))))
   3549                          (_ callback)))
   3550              (callback (lsp--create-async-callback callback
   3551                                                    method
   3552                                                    no-merge
   3553                                                    target-workspaces))
   3554              (callback (lambda (result)
   3555                          (lsp--request-cleanup-hooks id)
   3556                          (funcall callback result)))
   3557              (error-callback (lsp--create-async-callback
   3558                               (or error-callback
   3559                                   (lsp--create-default-error-handler method))
   3560                               method
   3561                               nil
   3562                               target-workspaces))
   3563              (error-callback (lambda (error)
   3564                                (funcall callback :error)
   3565                                (lsp--request-cleanup-hooks id)
   3566                                (funcall error-callback error)))
   3567              (body (plist-put body :id id)))
   3568 
   3569         ;; cancel request in any of the hooks
   3570         (mapc (-lambda ((hook . local))
   3571                 (add-hook hook
   3572                           (lsp--create-request-cancel
   3573                            id target-workspaces hook buf method cancel-callback)
   3574                           nil local))
   3575               hooks)
   3576         (puthash id cleanup-hooks lsp--request-cleanup-hooks)
   3577 
   3578         (setq lsp--last-active-workspaces target-workspaces)
   3579 
   3580         (when cancel-token
   3581           (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests))
   3582 
   3583         (seq-doseq (workspace target-workspaces)
   3584           (when (lsp--log-io-p method)
   3585             (lsp--log-entry-new (lsp--make-log-entry method id
   3586                                                      (plist-get body :params)
   3587                                                      'outgoing-req)
   3588                                 workspace))
   3589           (puthash id
   3590                    (list callback error-callback method start-time (current-time))
   3591                    (-> workspace
   3592                        (lsp--workspace-client)
   3593                        (lsp--client-response-handlers)))
   3594           (lsp--send-no-wait body (lsp--workspace-proc workspace)))
   3595         body)
   3596     (error "The connected server(s) does not support method %s.
   3597 To find out what capabilities support your server use `M-x lsp-describe-session'
   3598 and expand the capabilities section"
   3599            (plist-get body :method))))
   3600 
   3601 ;; deprecated, use lsp-request-async.
   3602 (defalias 'lsp-send-request-async 'lsp--send-request-async)
   3603 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1")
   3604 
   3605 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any
   3606 ;; pending language servers.
   3607 (add-hook 'kill-emacs-hook #'lsp--global-teardown)
   3608 
   3609 (defun lsp--global-teardown ()
   3610   "Unload working workspaces."
   3611   (lsp-foreach-workspace (lsp--shutdown-workspace)))
   3612 
   3613 (defun lsp--shutdown-workspace (&optional restart)
   3614   "Shut down the language server process for ‘lsp--cur-workspace’."
   3615   (with-demoted-errors "LSP error: %S"
   3616     (let ((lsp-response-timeout 0.5))
   3617       (condition-case err
   3618           (lsp-request "shutdown" nil)
   3619         (error (lsp--error "%s" err))))
   3620     (lsp-notify "exit" nil))
   3621   (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown))
   3622   (lsp--uninitialize-workspace))
   3623 
   3624 (defcustom lsp-inlay-hint-enable nil
   3625   "If non-nil it will enable inlay hints."
   3626   :type 'boolean
   3627   :group 'lsp-mode
   3628   :package-version '(lsp-mode . "9.0.0"))
   3629 
   3630 (defun lsp--uninitialize-workspace ()
   3631   "Cleanup buffer state.
   3632 When a workspace is shut down, by request or from just
   3633 disappearing, unset all the variables related to it."
   3634   (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace]
   3635     (lsp-process-kill cmd-proc)
   3636     (mapc (lambda (buf)
   3637             (when (lsp-buffer-live-p buf)
   3638               (lsp-with-current-buffer buf
   3639                                        (lsp-managed-mode -1))))
   3640           buffers)
   3641     (lsp-diagnostics--workspace-cleanup lsp--cur-workspace)))
   3642 
   3643 (defun lsp--client-capabilities (&optional custom-capabilities)
   3644   "Return the client capabilities appending CUSTOM-CAPABILITIES."
   3645   (append
   3646    `((general . ((positionEncodings . ["utf-32", "utf-16"])))
   3647      (workspace . ((workspaceEdit . ((documentChanges . t)
   3648                                      (resourceOperations . ["create" "rename" "delete"])))
   3649                    (applyEdit . t)
   3650                    (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))))
   3651                    (executeCommand . ((dynamicRegistration . :json-false)))
   3652                    ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t)))))
   3653                    (workspaceFolders . t)
   3654                    (configuration . t)
   3655                    ,@(when lsp-semantic-tokens-enable
   3656                        `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests)
   3657                                                                         lsp-semantic-tokens-honor-refresh-requests)
   3658                                                                    :json-false))))))
   3659                    ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t)))))
   3660                    ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false)))))
   3661                    (fileOperations . ((didCreate . :json-false)
   3662                                       (willCreate . :json-false)
   3663                                       (didRename . t)
   3664                                       (willRename . t)
   3665                                       (didDelete . :json-false)
   3666                                       (willDelete . :json-false)))))
   3667      (textDocument . ((declaration . ((dynamicRegistration . t)
   3668                                       (linkSupport . t)))
   3669                       (definition . ((dynamicRegistration . t)
   3670                                      (linkSupport . t)))
   3671                       (references . ((dynamicRegistration . t)))
   3672                       (implementation . ((dynamicRegistration . t)
   3673                                          (linkSupport . t)))
   3674                       (typeDefinition . ((dynamicRegistration . t)
   3675                                          (linkSupport . t)))
   3676                       (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t)))
   3677                       (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))
   3678                                          (hierarchicalDocumentSymbolSupport . t)))
   3679                       (formatting . ((dynamicRegistration . t)))
   3680                       (rangeFormatting . ((dynamicRegistration . t)))
   3681                       (onTypeFormatting . ((dynamicRegistration . t)))
   3682                       ,@(when (and lsp-semantic-tokens-enable
   3683                                    (functionp 'lsp--semantic-tokens-capabilities))
   3684                           (lsp--semantic-tokens-capabilities))
   3685                       (rename . ((dynamicRegistration . t) (prepareSupport . t)))
   3686                       (codeAction . ((dynamicRegistration . t)
   3687                                      (isPreferredSupport . t)
   3688                                      (codeActionLiteralSupport . ((codeActionKind . ((valueSet . [""
   3689                                                                                                   "quickfix"
   3690                                                                                                   "refactor"
   3691                                                                                                   "refactor.extract"
   3692                                                                                                   "refactor.inline"
   3693                                                                                                   "refactor.rewrite"
   3694                                                                                                   "source"
   3695                                                                                                   "source.organizeImports"])))))
   3696                                      (resolveSupport . ((properties . ["edit" "command"])))
   3697                                      (dataSupport . t)))
   3698                       (completion . ((completionItem . ((snippetSupport . ,(cond
   3699                                                                             ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode)))
   3700                                                                              (lsp--warn (concat
   3701                                                                                          "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. "
   3702                                                                                          "You must either install yasnippet, or disable snippet support."))
   3703                                                                              :json-false)
   3704                                                                             (lsp-enable-snippet t)
   3705                                                                             (t :json-false)))
   3706                                                         (documentationFormat . ["markdown" "plaintext"])
   3707                                                         ;; Remove this after jdtls support resolveSupport
   3708                                                         (resolveAdditionalTextEditsSupport . t)
   3709                                                         (insertReplaceSupport . t)
   3710                                                         (deprecatedSupport . t)
   3711                                                         (resolveSupport
   3712                                                          . ((properties . ["documentation"
   3713                                                                            "detail"
   3714                                                                            "additionalTextEdits"
   3715                                                                            "command"])))
   3716                                                         (insertTextModeSupport . ((valueSet . [1 2])))))
   3717                                      (contextSupport . t)
   3718                                      (dynamicRegistration . t)))
   3719                       (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t)))))
   3720                                         (dynamicRegistration . t)))
   3721                       (documentLink . ((dynamicRegistration . t)
   3722                                        (tooltipSupport . t)))
   3723                       (hover . ((contentFormat . ["markdown" "plaintext"])
   3724                                 (dynamicRegistration . t)))
   3725                       ,@(when lsp-enable-folding
   3726                           `((foldingRange . ((dynamicRegistration . t)
   3727                                              ,@(when lsp-folding-range-limit
   3728                                                  `((rangeLimit . ,lsp-folding-range-limit)))
   3729                                              ,@(when lsp-folding-line-folding-only
   3730                                                  `((lineFoldingOnly . t)))))))
   3731                       (selectionRange . ((dynamicRegistration . t)))
   3732                       (callHierarchy . ((dynamicRegistration . :json-false)))
   3733                       (typeHierarchy . ((dynamicRegistration . t)))
   3734                       (publishDiagnostics . ((relatedInformation . t)
   3735                                              (tagSupport . ((valueSet . [1 2])))
   3736                                              (versionSupport . t)))
   3737                       (linkedEditingRange . ((dynamicRegistration . t)))))
   3738      (window . ((workDoneProgress . t)
   3739                 (showDocument . ((support . t))))))
   3740    custom-capabilities))
   3741 
   3742 (defun lsp-find-roots-for-workspace (workspace session)
   3743   "Get all roots for the WORKSPACE."
   3744   (-filter #'identity (ht-map (lambda (folder workspaces)
   3745                                 (when (-contains? workspaces workspace)
   3746                                   folder))
   3747                               (lsp-session-folder->servers session))))
   3748 
   3749 (defun lsp-session-watches (&optional session)
   3750   "Get watches created for SESSION."
   3751   (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session))))
   3752       (-let [res (make-hash-table :test 'equal)]
   3753         (puthash "__watches" res (lsp-session-metadata (or session (lsp-session))))
   3754         res)))
   3755 
   3756 (defun lsp--file-process-event (session root-folder event)
   3757   "Process file event."
   3758   (let* ((changed-file (cl-third event))
   3759          (rel-changed-file (f-relative changed-file root-folder))
   3760          (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type))
   3761          (bit-position (1- event-numeric-kind))
   3762          (watch-bit (ash 1 bit-position)))
   3763     (->>
   3764      session
   3765      lsp-session-folder->servers
   3766      (gethash root-folder)
   3767      (seq-do (lambda (workspace)
   3768                (when (->>
   3769                       workspace
   3770                       lsp--workspace-registered-server-capabilities
   3771                       (-any?
   3772                        (lambda (capability)
   3773                          (and
   3774                           (equal (lsp--registered-capability-method capability)
   3775                                  "workspace/didChangeWatchedFiles")
   3776                           (->>
   3777                            capability
   3778                            lsp--registered-capability-options
   3779                            (lsp:did-change-watched-files-registration-options-watchers)
   3780                            (seq-find
   3781                             (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp))
   3782                               (when (or (null kind?)
   3783                                         (> (logand kind? watch-bit) 0))
   3784                                 (-let [regexes (or cached-regexp
   3785                                                    (let ((regexp (lsp-glob-to-regexps glob-pattern)))
   3786                                                      (lsp-put fs-watcher :_cachedRegexp regexp)
   3787                                                      regexp))]
   3788                                   (-any? (lambda (re)
   3789                                            (or (string-match re changed-file)
   3790                                                (string-match re rel-changed-file)))
   3791                                          regexes))))))))))
   3792                  (with-lsp-workspace workspace
   3793                    (lsp-notify
   3794                     "workspace/didChangeWatchedFiles"
   3795                     `((changes . [((type . ,event-numeric-kind)
   3796                                    (uri . ,(lsp--path-to-uri changed-file)))]))))))))))
   3797 
   3798 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?))
   3799   "Register capability REG."
   3800   (when (and lsp-enable-file-watchers
   3801              (equal method "workspace/didChangeWatchedFiles"))
   3802     (-let* ((created-watches (lsp-session-watches (lsp-session)))
   3803             (root-folders (cl-set-difference
   3804                            (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session))
   3805                            (ht-keys created-watches))))
   3806       ;; create watch for each root folder without such
   3807       (dolist (folder root-folders)
   3808         (let* ((watch (make-lsp-watch :root-directory folder))
   3809                (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder))
   3810                (ignored-files-regex-list (car ignored-things))
   3811                (ignored-directories-regex-list (cadr ignored-things)))
   3812           (puthash folder watch created-watches)
   3813           (lsp-watch-root-folder (file-truename folder)
   3814                                  (-partial #'lsp--file-process-event (lsp-session) folder)
   3815                                  ignored-files-regex-list
   3816                                  ignored-directories-regex-list
   3817                                  watch
   3818                                  t)))))
   3819 
   3820   (push
   3821    (make-lsp--registered-capability :id id :method method :options register-options?)
   3822    (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3823 
   3824 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body)
   3825   "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to
   3826 access dir-local variables."
   3827   (declare (indent 1) (debug t))
   3828   `(with-temp-buffer
   3829      ;; Set the buffer's name to something under the root so that we can hack the local variables
   3830      ;; This file doesn't need to exist and will not be created due to this.
   3831      (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root)))
   3832      (hack-local-variables)
   3833      (prog1 ,@body
   3834        (setq-local buffer-file-name nil))))
   3835 
   3836 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root)
   3837   "Return a list of the form
   3838 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given
   3839 WORKSPACE-ROOT."
   3840   ;; The intent of this function is to provide per-root workspace-level customization of the
   3841   ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables.
   3842   (lsp--with-workspace-temp-buffer workspace-root
   3843     (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories))))
   3844 
   3845 
   3846 (defun lsp--cleanup-hanging-watches ()
   3847   "Cleanup watches in case there are no more workspaces that are interested
   3848 in that particular folder."
   3849   (let* ((session (lsp-session))
   3850          (watches (lsp-session-watches session)))
   3851     (dolist (watched-folder (ht-keys watches))
   3852       (when (-none? (lambda (workspace)
   3853                       (with-lsp-workspace workspace
   3854                         (lsp--registered-capability "workspace/didChangeWatchedFiles")))
   3855                     (gethash watched-folder (lsp-session-folder->servers (lsp-session))))
   3856         (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder)
   3857         (lsp-kill-watch (gethash watched-folder watches))
   3858         (remhash watched-folder watches)))))
   3859 
   3860 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method))
   3861   "Unregister capability UNREG."
   3862   (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace)
   3863         (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id))
   3864                     (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3865   (when (equal method "workspace/didChangeWatchedFiles")
   3866     (lsp--cleanup-hanging-watches)))
   3867 
   3868 (defun lsp--server-capabilities ()
   3869   "Return the capabilities of the language server associated with the buffer."
   3870   (->> (lsp-workspaces)
   3871        (-keep #'lsp--workspace-server-capabilities)
   3872        (apply #'lsp-merge)))
   3873 
   3874 (defun lsp--send-open-close-p ()
   3875   "Return whether open and close notifications should be sent to the server."
   3876   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3877     (or (memq sync '(1 2))
   3878         (lsp:text-document-sync-options-open-close? sync))))
   3879 
   3880 (defun lsp--send-will-save-p ()
   3881   "Return whether willSave notifications should be sent to the server."
   3882   (-> (lsp--server-capabilities)
   3883       (lsp:server-capabilities-text-document-sync?)
   3884       (lsp:text-document-sync-options-will-save?)))
   3885 
   3886 (defun lsp--send-will-save-wait-until-p ()
   3887   "Return whether willSaveWaitUntil notifications should be sent to the server."
   3888   (-> (lsp--server-capabilities)
   3889       (lsp:server-capabilities-text-document-sync?)
   3890       (lsp:text-document-sync-options-will-save-wait-until?)))
   3891 
   3892 (defun lsp--send-did-save-p ()
   3893   "Return whether didSave notifications should be sent to the server."
   3894   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3895     (or (memq sync '(1 2))
   3896         (lsp:text-document-sync-options-save? sync))))
   3897 
   3898 (defun lsp--save-include-text-p ()
   3899   "Return whether save notifications should include the text document's contents."
   3900   (->> (lsp--server-capabilities)
   3901        (lsp:server-capabilities-text-document-sync?)
   3902        (lsp:text-document-sync-options-save?)
   3903        (lsp:text-document-save-registration-options-include-text?)))
   3904 
   3905 (defun lsp--send-will-rename-files-p (path)
   3906   "Return whether willRenameFiles request should be sent to the server.
   3907 If any filters, checks if it applies for PATH."
   3908   (let* ((will-rename (-> (lsp--server-capabilities)
   3909                           (lsp:server-capabilities-workspace?)
   3910                           (lsp:workspace-server-capabilities-file-operations?)
   3911                           (lsp:workspace-file-operations-will-rename?)))
   3912          (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list)))
   3913     (and will-rename
   3914          (or (seq-empty-p filters)
   3915              (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob)))
   3916                       (-let [regexes (lsp-glob-to-regexps glob)]
   3917                         (and (or (not scheme?)
   3918                                  (string-prefix-p scheme? (lsp--path-to-uri path)))
   3919                              (-any? (lambda (re)
   3920                                       (string-match re path))
   3921                                     regexes))))
   3922                     filters)))))
   3923 
   3924 (defun lsp--send-did-rename-files-p ()
   3925   "Return whether didRenameFiles notification should be sent to the server."
   3926   (-> (lsp--server-capabilities)
   3927       (lsp:server-capabilities-workspace?)
   3928       (lsp:workspace-server-capabilities-file-operations?)
   3929       (lsp:workspace-file-operations-did-rename?)))
   3930 
   3931 (declare-function project-roots "ext:project" (project) t)
   3932 (declare-function project-root "ext:project" (project) t)
   3933 
   3934 (defun lsp--suggest-project-root ()
   3935   "Get project root."
   3936   (or
   3937    (when (fboundp 'projectile-project-root)
   3938      (condition-case nil
   3939          (projectile-project-root)
   3940        (error nil)))
   3941    (when (fboundp 'project-current)
   3942      (when-let ((project (project-current)))
   3943        (if (fboundp 'project-root)
   3944            (project-root project)
   3945          (car (with-no-warnings
   3946                 (project-roots project))))))
   3947    default-directory))
   3948 
   3949 (defun lsp--read-from-file (file)
   3950   "Read FILE content."
   3951   (when (file-exists-p file)
   3952     (cl-first (read-from-string (f-read-text file 'utf-8)))))
   3953 
   3954 (defun lsp--persist (file-name to-persist)
   3955   "Persist TO-PERSIST in FILE-NAME.
   3956 
   3957 This function creates the parent directories if they don't exist
   3958 yet."
   3959   (let ((print-length nil)
   3960         (print-level nil))
   3961     ;; Create all parent directories:
   3962     (make-directory (f-parent file-name) t)
   3963     (f-write-text (prin1-to-string to-persist) 'utf-8 file-name)))
   3964 
   3965 (defun lsp-workspace-folders-add (project-root)
   3966   "Add PROJECT-ROOT to the list of workspace folders."
   3967   (interactive
   3968    (list (read-directory-name "Select folder to add: "
   3969                               (or (lsp--suggest-project-root) default-directory) nil t)))
   3970   (cl-pushnew (lsp-f-canonical project-root)
   3971               (lsp-session-folders (lsp-session)) :test 'equal)
   3972   (lsp--persist-session (lsp-session))
   3973 
   3974   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil))
   3975 
   3976 (defun lsp-workspace-folders-remove (project-root)
   3977   "Remove PROJECT-ROOT from the list of workspace folders."
   3978   (interactive (list (completing-read "Select folder to remove: "
   3979                                       (lsp-session-folders (lsp-session))
   3980                                       nil t nil nil
   3981                                       (lsp-find-session-folder (lsp-session) default-directory))))
   3982 
   3983   (setq project-root (lsp-f-canonical project-root))
   3984 
   3985   ;; send remove folder to each multiroot workspace associated with the folder
   3986   (dolist (wks (->> (lsp-session)
   3987                     (lsp-session-folder->servers)
   3988                     (gethash project-root)
   3989                     (--filter (lsp--client-multi-root (lsp--workspace-client it)))))
   3990     (with-lsp-workspace wks
   3991       (lsp-notify "workspace/didChangeWorkspaceFolders"
   3992                   (lsp-make-did-change-workspace-folders-params
   3993                    :event (lsp-make-workspace-folders-change-event
   3994                            :removed (vector (lsp-make-workspace-folder
   3995                                              :uri (lsp--path-to-uri project-root)
   3996                                              :name (f-filename project-root)))
   3997                            :added [])))))
   3998 
   3999   ;; turn off servers in the removed directory
   4000   (let* ((session (lsp-session))
   4001          (folder->servers (lsp-session-folder->servers session))
   4002          (server-id->folders (lsp-session-server-id->folders session))
   4003          (workspaces (gethash project-root folder->servers)))
   4004 
   4005     (remhash project-root folder->servers)
   4006 
   4007     ;; turn off the servers without root folders
   4008     (dolist (workspace workspaces)
   4009       (when (--none? (-contains? it workspace) (ht-values folder->servers))
   4010         (lsp--info "Shutdown %s since folder %s is removed..."
   4011                    (lsp--workspace-print workspace) project-root)
   4012         (with-lsp-workspace workspace (lsp--shutdown-workspace))))
   4013 
   4014     (setf (lsp-session-folders session)
   4015           (-remove-item project-root (lsp-session-folders session)))
   4016 
   4017     (ht-aeach (puthash key
   4018                        (-remove-item project-root value)
   4019                        server-id->folders)
   4020               server-id->folders)
   4021     (lsp--persist-session (lsp-session)))
   4022 
   4023   (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root)))
   4024 
   4025 (defun lsp-workspace-blocklist-remove (project-root)
   4026   "Remove PROJECT-ROOT from the workspace blocklist."
   4027   (interactive (list (completing-read "Select folder to remove:"
   4028                                       (lsp-session-folders-blocklist (lsp-session))
   4029                                       nil t)))
   4030   (setf (lsp-session-folders-blocklist (lsp-session))
   4031         (delete project-root
   4032                 (lsp-session-folders-blocklist (lsp-session))))
   4033   (lsp--persist-session (lsp-session)))
   4034 
   4035 (define-obsolete-function-alias 'lsp-workspace-folders-switch
   4036   'lsp-workspace-folders-open "lsp-mode 6.1")
   4037 
   4038 (defun lsp-workspace-folders-open (project-root)
   4039   "Open the directory located at PROJECT-ROOT"
   4040   (interactive (list (completing-read "Open folder: "
   4041                                       (lsp-session-folders (lsp-session))
   4042                                       nil t)))
   4043   (find-file project-root))
   4044 
   4045 (defun lsp--maybe-enable-signature-help (trigger-characters)
   4046   (let ((ch last-command-event))
   4047     (when (cl-find ch trigger-characters :key #'string-to-char)
   4048       (lsp-signature-activate))))
   4049 
   4050 (defun lsp--on-type-formatting-handler-create ()
   4051   (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" )))
   4052     (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character?
   4053                                              :first-trigger-character) provider]
   4054       (lambda ()
   4055         (lsp--on-type-formatting first-trigger-character
   4056                                  more-trigger-character?)))))
   4057 
   4058 (defun lsp--update-on-type-formatting-hook (&optional cleanup?)
   4059   (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create)))
   4060     (cond
   4061      ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?))
   4062       (add-hook 'post-self-insert-hook on-type-formatting-handler nil t))
   4063      ((or cleanup?
   4064           (not lsp-enable-on-type-formatting))
   4065       (remove-hook 'post-self-insert-hook on-type-formatting-handler t)))))
   4066 
   4067 (defun lsp--signature-help-handler-create ()
   4068   (-when-let ((&SignatureHelpOptions? :trigger-characters?)
   4069               (lsp--capability-for-method "textDocument/signatureHelp"))
   4070     (lambda ()
   4071       (lsp--maybe-enable-signature-help trigger-characters?))))
   4072 
   4073 (defun lsp--update-signature-help-hook (&optional cleanup?)
   4074   (let ((signature-help-handler (lsp--signature-help-handler-create)))
   4075     (cond
   4076      ((and (or (equal lsp-signature-auto-activate t)
   4077                (memq :on-trigger-char lsp-signature-auto-activate))
   4078            signature-help-handler)
   4079       (add-hook 'post-self-insert-hook signature-help-handler nil t))
   4080 
   4081      ((or cleanup?
   4082           (not (or (equal lsp-signature-auto-activate t)
   4083                    (memq :on-trigger-char lsp-signature-auto-activate))))
   4084       (remove-hook 'post-self-insert-hook signature-help-handler t)))))
   4085 
   4086 (defun lsp--after-set-visited-file-name ()
   4087   (lsp-disconnect)
   4088   (lsp))
   4089 
   4090 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27
   4091 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099
   4092 (defvar eldoc-documentation-default) ; CI
   4093 (when (< emacs-major-version 28)
   4094   (unless (boundp 'eldoc-documentation-functions)
   4095     (load "eldoc" nil 'nomessage))
   4096   (when (memq (default-value 'eldoc-documentation-function) '(nil ignore))
   4097     ;; actually `eldoc-documentation-strategy', but CI was failing
   4098     (setq-default eldoc-documentation-function 'eldoc-documentation-default)))
   4099 
   4100 (define-minor-mode lsp-managed-mode
   4101   "Mode for source buffers managed by lsp-mode."
   4102   :lighter nil
   4103   (cond
   4104    (lsp-managed-mode
   4105     (when (lsp-feature? "textDocument/hover")
   4106       (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t)
   4107       (eldoc-mode 1))
   4108 
   4109     (add-hook 'after-change-functions #'lsp-on-change nil t)
   4110     (add-hook 'after-revert-hook #'lsp-on-revert nil t)
   4111     (add-hook 'after-save-hook #'lsp-on-save nil t)
   4112     (add-hook 'auto-save-hook #'lsp--on-auto-save nil t)
   4113     (add-hook 'before-change-functions #'lsp-before-change nil t)
   4114     (add-hook 'before-save-hook #'lsp--before-save nil t)
   4115     (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t)
   4116     (add-hook 'post-command-hook #'lsp--post-command nil t)
   4117 
   4118     (lsp--update-on-type-formatting-hook)
   4119     (lsp--update-signature-help-hook)
   4120 
   4121     (when lsp-enable-xref
   4122       (add-hook 'xref-backend-functions #'lsp--xref-backend nil t))
   4123 
   4124     (lsp-configure-buffer)
   4125 
   4126     ;; make sure we turn off lsp-mode in case major mode changes, because major
   4127     ;; mode change will wipe the buffer locals.
   4128     (add-hook 'change-major-mode-hook #'lsp-disconnect nil t)
   4129     (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t)
   4130 
   4131     (let ((buffer (lsp-current-buffer)))
   4132       (run-with-idle-timer
   4133        0.0 nil
   4134        (lambda ()
   4135          (when (lsp-buffer-live-p buffer)
   4136            (lsp-with-current-buffer buffer
   4137              (lsp--on-change-debounce buffer)
   4138              (lsp--on-idle buffer)))))))
   4139    (t
   4140     (lsp-unconfig-buffer)
   4141 
   4142     (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t)
   4143     (remove-hook 'post-command-hook #'lsp--post-command t)
   4144     (remove-hook 'after-change-functions #'lsp-on-change t)
   4145     (remove-hook 'after-revert-hook #'lsp-on-revert t)
   4146     (remove-hook 'after-save-hook #'lsp-on-save t)
   4147     (remove-hook 'auto-save-hook #'lsp--on-auto-save t)
   4148     (remove-hook 'before-change-functions #'lsp-before-change t)
   4149     (remove-hook 'before-save-hook #'lsp--before-save t)
   4150     (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t)
   4151 
   4152     (lsp--update-on-type-formatting-hook :cleanup)
   4153     (lsp--update-signature-help-hook :cleanup)
   4154 
   4155     (when lsp--on-idle-timer
   4156       (cancel-timer lsp--on-idle-timer)
   4157       (setq lsp--on-idle-timer nil))
   4158 
   4159     (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4160     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4161 
   4162     (lsp--remove-overlays 'lsp-highlight)
   4163     (lsp--remove-overlays 'lsp-links)
   4164 
   4165     (remove-hook 'xref-backend-functions #'lsp--xref-backend t)
   4166     (remove-hook 'change-major-mode-hook #'lsp-disconnect t)
   4167     (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t)
   4168     (setq-local lsp-buffer-uri nil))))
   4169 
   4170 (defun lsp-configure-buffer ()
   4171   "Configure LSP features for current buffer."
   4172   ;; make sure the core is running in the context of all available workspaces
   4173   ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context
   4174   (let ((lsp--buffer-workspaces (cond
   4175                                  (lsp--buffer-workspaces)
   4176                                  (lsp--cur-workspace (list lsp--cur-workspace))))
   4177         lsp--cur-workspace)
   4178     (when lsp-auto-configure
   4179       (lsp--auto-configure)
   4180 
   4181       (when (and lsp-enable-text-document-color
   4182                  (lsp-feature? "textDocument/documentColor"))
   4183         (add-hook 'lsp-on-change-hook #'lsp--document-color nil t))
   4184 
   4185       (when (and lsp-enable-imenu
   4186                  (lsp-feature? "textDocument/documentSymbol"))
   4187         (lsp-enable-imenu))
   4188 
   4189       (when (and lsp-enable-indentation
   4190                  (lsp-feature? "textDocument/rangeFormatting"))
   4191         (add-function :override (local 'indent-region-function) #'lsp-format-region))
   4192 
   4193       (when (and lsp-enable-symbol-highlighting
   4194                  (lsp-feature? "textDocument/documentHighlight"))
   4195         (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t))
   4196 
   4197       (when (and lsp-enable-links
   4198                  (lsp-feature? "textDocument/documentLink"))
   4199         (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t))
   4200 
   4201       (when (and lsp-inlay-hint-enable
   4202                  (lsp-feature? "textDocument/inlayHint"))
   4203         (lsp-inlay-hints-mode))
   4204 
   4205       (when (and lsp-enable-dap-auto-configure
   4206                  (functionp 'dap-mode))
   4207         (dap-auto-configure-mode 1)))
   4208     (run-hooks 'lsp-configure-hook)))
   4209 
   4210 (defun lsp-unconfig-buffer ()
   4211   "Unconfigure LSP features for buffer."
   4212   (lsp--remove-overlays 'lsp-color)
   4213 
   4214   (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function)
   4215     (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   4216     (setq-local imenu-menubar-modified-tick 0)
   4217     (setq-local imenu--index-alist nil)
   4218     (imenu--cleanup))
   4219 
   4220   (remove-function (local 'indent-region-function) #'lsp-format-region)
   4221 
   4222   (remove-hook 'lsp-on-change-hook #'lsp--document-color t)
   4223   (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4224   (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4225 
   4226   (when (and lsp-enable-dap-auto-configure
   4227              (functionp 'dap-mode))
   4228     (dap-auto-configure-mode -1))
   4229 
   4230   (run-hooks 'lsp-unconfigure-hook))
   4231 
   4232 (defun lsp--buffer-content ()
   4233   (lsp-save-restriction-and-excursion
   4234     (or (lsp-virtual-buffer-call :buffer-string)
   4235         (buffer-substring-no-properties (point-min)
   4236                                         (point-max)))))
   4237 
   4238 (defun lsp--text-document-did-open ()
   4239   "`document/didOpen' event."
   4240   (run-hooks 'lsp-before-open-hook)
   4241   (when (and lsp-auto-touch-files
   4242              (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri)))))
   4243     (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri))
   4244     (save-buffer))
   4245 
   4246   (setq lsp--cur-version (or lsp--cur-version 0))
   4247   (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   4248   (lsp-notify
   4249    "textDocument/didOpen"
   4250    (list :textDocument
   4251          (list :uri (lsp--buffer-uri)
   4252                :languageId (lsp-buffer-language)
   4253                :version lsp--cur-version
   4254                :text (lsp--buffer-content))))
   4255 
   4256   (lsp-managed-mode 1)
   4257 
   4258   (run-hooks 'lsp-after-open-hook)
   4259   (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client))))
   4260     (-some-> (lsp--client-after-open-fn client)
   4261       (funcall))
   4262     (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client))
   4263       (intern-soft)
   4264       (run-hooks))))
   4265 
   4266 (defun lsp--text-document-identifier ()
   4267   "Make TextDocumentIdentifier."
   4268   (list :uri (lsp--buffer-uri)))
   4269 
   4270 (defun lsp--versioned-text-document-identifier ()
   4271   "Make VersionedTextDocumentIdentifier."
   4272   (plist-put (lsp--text-document-identifier) :version lsp--cur-version))
   4273 
   4274 (defun lsp--cur-line (&optional point)
   4275   (1- (line-number-at-pos point)))
   4276 
   4277 (defun lsp--cur-position ()
   4278   "Make a Position object for the current point."
   4279   (or (lsp-virtual-buffer-call :cur-position)
   4280       (lsp-save-restriction-and-excursion
   4281         (list :line (lsp--cur-line)
   4282               :character (- (point) (line-beginning-position))))))
   4283 
   4284 (defun lsp--point-to-position (point)
   4285   "Convert POINT to Position."
   4286   (lsp-save-restriction-and-excursion
   4287     (goto-char point)
   4288     (lsp--cur-position)))
   4289 
   4290 (defun lsp--range (start end)
   4291   "Make Range body from START and END."
   4292   ;; make sure start and end are Position objects
   4293   (list :start start :end end))
   4294 
   4295 (defun lsp--region-to-range (start end)
   4296   "Make Range object for the current region."
   4297   (lsp--range (lsp--point-to-position start)
   4298               (lsp--point-to-position end)))
   4299 
   4300 (defun lsp--region-or-line ()
   4301   "The active region or the current line."
   4302   (if (use-region-p)
   4303       (lsp--region-to-range (region-beginning) (region-end))
   4304     (lsp--region-to-range (line-beginning-position) (line-end-position))))
   4305 
   4306 (defun lsp--check-document-changes-version (document-changes)
   4307   "Verify that DOCUMENT-CHANGES have the proper version."
   4308   (unless (seq-every-p
   4309            (-lambda ((&TextDocumentEdit :text-document))
   4310              (or
   4311               (not text-document)
   4312               (let* ((filename (-> text-document
   4313                                    lsp:versioned-text-document-identifier-uri
   4314                                    lsp--uri-to-path))
   4315                      (version (lsp:versioned-text-document-identifier-version? text-document)))
   4316                 (with-current-buffer (find-file-noselect filename)
   4317                   (or (null version) (zerop version) (= -1 version)
   4318                       (equal version lsp--cur-version))))))
   4319            document-changes)
   4320     (error "Document changes cannot be applied due to different document version")))
   4321 
   4322 (defun lsp--apply-workspace-edit (workspace-edit &optional operation)
   4323   "Apply the WorkspaceEdit object WORKSPACE-EDIT.
   4324 OPERATION is symbol representing the source of this text edit."
   4325   (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit))
   4326     (if-let ((document-changes (seq-reverse document-changes?)))
   4327         (progn
   4328           (lsp--check-document-changes-version document-changes)
   4329           (->> document-changes
   4330                (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create")))
   4331                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4332           (->> document-changes
   4333                (seq-filter (-lambda ((&CreateFile :kind))
   4334                              (and (or (not kind) (equal kind "edit"))
   4335                                   (not (equal kind "create")))))
   4336                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4337           (->> document-changes
   4338                (seq-filter (-lambda ((&CreateFile :kind))
   4339                              (and (not (or (not kind) (equal kind "edit")))
   4340                                   (not (equal kind "create")))))
   4341                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))))
   4342       (lsp-map
   4343        (lambda (uri text-edits)
   4344          (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect)
   4345            (lsp--apply-text-edits text-edits operation)))
   4346        changes?))))
   4347 
   4348 (defmacro lsp-with-filename (file &rest body)
   4349   "Execute BODY with FILE as a context.
   4350 Need to handle the case when FILE indicates virtual buffer."
   4351   (declare (indent 1) (debug t))
   4352   `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file)))
   4353        (lsp-with-current-buffer lsp--virtual-buffer
   4354          ,@body)
   4355      ,@body))
   4356 
   4357 (defun lsp--apply-text-document-edit (edit &optional operation)
   4358   "Apply the TextDocumentEdit object EDIT.
   4359 OPERATION is symbol representing the source of this text edit.
   4360 If the file is not being visited by any buffer, it is opened with
   4361 `find-file-noselect'.
   4362 Because lsp-mode does not store previous document versions, the edit is only
   4363 applied if the version of the textDocument matches the version of the
   4364 corresponding file.
   4365 
   4366 interface TextDocumentEdit {
   4367   textDocument: VersionedTextDocumentIdentifier;
   4368   edits: TextEdit[];
   4369 }"
   4370   (pcase (lsp:edit-kind edit)
   4371     ("create" (-let* (((&CreateFile :uri :options?) edit)
   4372                       (file-name (lsp--uri-to-path uri)))
   4373                 (mkdir (f-dirname file-name) t)
   4374                 (f-touch file-name)
   4375                 (when (lsp:create-file-options-overwrite? options?)
   4376                   (f-write-text "" nil file-name))
   4377                 (find-file-noselect file-name)))
   4378     ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit))
   4379                 (f-delete (lsp--uri-to-path uri) recursive?)))
   4380     ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit)
   4381                       (old-file-name (lsp--uri-to-path old-uri))
   4382                       (new-file-name (lsp--uri-to-path new-uri))
   4383                       (buf (find-buffer-visiting old-file-name)))
   4384                 (when buf
   4385                   (lsp-with-current-buffer buf
   4386                     (save-buffer)
   4387                     (lsp--text-document-did-close)))
   4388                 (mkdir (f-dirname new-file-name) t)
   4389                 (rename-file old-file-name new-file-name overwrite?)
   4390                 (when buf
   4391                   (lsp-with-current-buffer buf
   4392                     (set-buffer-modified-p nil)
   4393                     (setq lsp-buffer-uri nil)
   4394                     (set-visited-file-name new-file-name)
   4395                     (lsp)))))
   4396     (_ (let ((file-name (->> edit
   4397                              (lsp:text-document-edit-text-document)
   4398                              (lsp:versioned-text-document-identifier-uri)
   4399                              (lsp--uri-to-path))))
   4400          (lsp-with-current-buffer (find-buffer-visiting file-name)
   4401            (lsp-with-filename file-name
   4402              (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation)))))))
   4403 
   4404 (lsp-defun lsp--position-compare ((&Position :line left-line
   4405                                              :character left-character)
   4406                                   (&Position :line right-line
   4407                                              :character right-character))
   4408   "Return t if position LEFT is greater than RIGHT."
   4409   (if (= left-line right-line)
   4410       (> left-character right-character)
   4411     (> left-line right-line)))
   4412 
   4413 (lsp-defun lsp-point-in-range? (position (&Range :start :end))
   4414   "Returns if POINT is in RANGE."
   4415   (not (or (lsp--position-compare start position)
   4416            (lsp--position-compare position end))))
   4417 
   4418 (lsp-defun lsp--position-equal ((&Position :line left-line
   4419                                            :character left-character)
   4420                                 (&Position :line right-line
   4421                                            :character right-character))
   4422   "Return whether LEFT and RIGHT positions are equal."
   4423   (and (= left-line right-line)
   4424        (= left-character right-character)))
   4425 
   4426 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end))
   4427                                           (&TextEdit :range (&Range :start right-start :end right-end)))
   4428   (if (lsp--position-equal left-start right-start)
   4429       (lsp--position-compare left-end right-end)
   4430     (lsp--position-compare left-start right-start)))
   4431 
   4432 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text))
   4433   "Apply the edits described in the TextEdit object in TEXT-EDIT."
   4434   (setq new-text (s-replace "\r" "" (or new-text "")))
   4435   (lsp:set-text-edit-new-text edit new-text)
   4436   (goto-char start)
   4437   (delete-region start end)
   4438   (insert new-text))
   4439 
   4440 ;; WORKAROUND: typescript-language might send -1 when applying code actions.
   4441 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582
   4442 (lsp-defun lsp--fix-point ((point &as &Position :character :line))
   4443   (-doto point
   4444     (lsp:set-position-line (max 0 line))
   4445     (lsp:set-position-character (max 0 character))))
   4446 
   4447 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as
   4448                                                                &TextEdit
   4449                                                                :range (&Range :start :end)
   4450                                                                :new-text))
   4451   "Apply the edits described in the TextEdit object in TEXT-EDIT.
   4452 The method uses `replace-buffer-contents'."
   4453   (setq new-text (s-replace "\r" "" (or new-text "")))
   4454   (lsp:set-text-edit-new-text edit new-text)
   4455   (-let* ((source (current-buffer))
   4456           ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start)
   4457                                                              :end (lsp--fix-point end)))))
   4458     (with-temp-buffer
   4459       (insert new-text)
   4460       (let ((temp (current-buffer)))
   4461         (with-current-buffer source
   4462           (save-excursion
   4463             (save-restriction
   4464               (narrow-to-region beg end)
   4465 
   4466               ;; On emacs versions < 26.2,
   4467               ;; `replace-buffer-contents' is buggy - it calls
   4468               ;; change functions with invalid arguments - so we
   4469               ;; manually call the change functions here.
   4470               ;;
   4471               ;; See emacs bugs #32237, #32278:
   4472               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237
   4473               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278
   4474               (let ((inhibit-modification-hooks t)
   4475                     (length (- end beg)))
   4476                 (run-hook-with-args 'before-change-functions
   4477                                     beg end)
   4478                 (replace-buffer-contents temp)
   4479                 (run-hook-with-args 'after-change-functions
   4480                                     beg (+ beg (length new-text))
   4481                                     length)))))))))
   4482 
   4483 (defun lsp--to-yasnippet-snippet (snippet)
   4484   "Convert LSP SNIPPET to yasnippet snippet."
   4485   ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it.
   4486   (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`")))
   4487                             (rx "\\" (backref 1))
   4488                             snippet
   4489                             nil nil 1))
   4490 
   4491 (defvar-local lsp-enable-relative-indentation nil
   4492   "Enable relative indentation when insert texts, snippets ...
   4493 from language server.")
   4494 
   4495 (defun lsp--expand-snippet (snippet &optional start end expand-env)
   4496   "Wrapper of `yas-expand-snippet' with all of it arguments.
   4497 The snippet will be convert to LSP style and indent according to
   4498 LSP server result."
   4499   (require 'yasnippet nil t)
   4500   (let* ((inhibit-field-text-motion t)
   4501          (yas-wrap-around-region nil)
   4502          (yas-indent-line 'none)
   4503          (yas-also-auto-indent-first-line nil))
   4504     (yas-expand-snippet
   4505      (lsp--to-yasnippet-snippet snippet)
   4506      start end expand-env)))
   4507 
   4508 (defun lsp--indent-lines (start end &optional insert-text-mode?)
   4509   "Indent from START to END based on INSERT-TEXT-MODE? value.
   4510 - When INSERT-TEXT-MODE? is provided
   4511   - if it's `lsp/insert-text-mode-as-it', do no editor indentation.
   4512   - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading
   4513     whitespaces to match the line where text is inserted.
   4514 - When it's not provided, using `indent-line-function' for each line."
   4515   (save-excursion
   4516     (goto-char end)
   4517     (let* ((end-line (line-number-at-pos))
   4518            (offset (save-excursion
   4519                      (goto-char start)
   4520                      (current-indentation)))
   4521            (indent-line-function
   4522             (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it)
   4523                    #'ignore)
   4524                   ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation)
   4525                        lsp-enable-relative-indentation
   4526                        ;; Indenting snippets is extremely slow in `org-mode' buffers
   4527                        ;; since it has to calculate indentation based on SRC block
   4528                        ;; position.  Thus we use relative indentation as default.
   4529                        (derived-mode-p 'org-mode))
   4530                    (lambda () (save-excursion
   4531                                 (beginning-of-line)
   4532                                 (indent-to-column offset))))
   4533                   (t indent-line-function))))
   4534       (goto-char start)
   4535       (forward-line)
   4536       (while (and (not (eobp))
   4537                   (<= (line-number-at-pos) end-line))
   4538         (funcall indent-line-function)
   4539         (forward-line)))))
   4540 
   4541 (defun lsp--apply-text-edits (edits &optional operation)
   4542   "Apply the EDITS described in the TextEdit[] object.
   4543 OPERATION is symbol representing the source of this text edit."
   4544   (unless (seq-empty-p edits)
   4545     (atomic-change-group
   4546       (run-hooks 'lsp-before-apply-edits-hook)
   4547       (let* ((change-group (prepare-change-group))
   4548              (howmany (length edits))
   4549              (message (format "Applying %s edits to `%s' ..." howmany (current-buffer)))
   4550              (_ (lsp--info message))
   4551              (reporter (make-progress-reporter message 0 howmany))
   4552              (done 0)
   4553              (apply-edit (if (not lsp--virtual-buffer)
   4554                              #'lsp--apply-text-edit-replace-buffer-contents
   4555                            #'lsp--apply-text-edit)))
   4556         (unwind-protect
   4557             (->> edits
   4558                  ;; We sort text edits so as to apply edits that modify latter
   4559                  ;; parts of the document first. Furthermore, because the LSP
   4560                  ;; spec dictates that: "If multiple inserts have the same
   4561                  ;; position, the order in the array defines which edit to
   4562                  ;; apply first."  We reverse the initial list and sort stably
   4563                  ;; to make sure the order among edits with the same position
   4564                  ;; is preserved.
   4565                  (nreverse)
   4566                  (seq-sort #'lsp--text-edit-sort-predicate)
   4567                  (mapc (lambda (edit)
   4568                          (progress-reporter-update reporter (cl-incf done))
   4569                          (funcall apply-edit edit)
   4570                          (when (lsp:snippet-text-edit-insert-text-format? edit)
   4571                            (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start)
   4572                                                          :insert-text-format? :new-text) edit)
   4573                              (when (eq insert-text-format? lsp/insert-text-format-snippet)
   4574                                ;; No `save-excursion' needed since expand snippet will change point anyway
   4575                                (goto-char (+ start (length new-text)))
   4576                                (lsp--indent-lines start (point))
   4577                                (lsp--expand-snippet new-text start (point)))))
   4578                          (run-hook-with-args 'lsp-after-apply-edits-hook operation))))
   4579           (undo-amalgamate-change-group change-group)
   4580           (progress-reporter-done reporter))))))
   4581 
   4582 (defun lsp--create-apply-text-edits-handlers ()
   4583   "Create (handler cleanup-fn) for applying text edits in async request.
   4584 Only works when mode is `tick or `alive."
   4585   (let* (first-edited
   4586          (func (lambda (start &rest _)
   4587                  (setq first-edited (if first-edited
   4588                                         (min start first-edited)
   4589                                       start)))))
   4590     (add-hook 'before-change-functions func nil t)
   4591     (list
   4592      (lambda (edits)
   4593        (if (and first-edited
   4594                 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end)))
   4595                             ;; Text edit region is overlapped
   4596                             (> end first-edited))
   4597                           edits))
   4598            (lsp--warn "TextEdits will not be applied since document has been modified before of them.")
   4599          (lsp--apply-text-edits edits 'completion-cleanup)))
   4600      (lambda ()
   4601        (remove-hook 'before-change-functions func t)))))
   4602 
   4603 (defun lsp--capability (cap &optional capabilities)
   4604   "Get the value of capability CAP.  If CAPABILITIES is non-nil, use them instead."
   4605   (when (stringp cap)
   4606     (setq cap (intern (concat ":" cap))))
   4607 
   4608   (lsp-get (or capabilities
   4609                (lsp--server-capabilities))
   4610            cap))
   4611 
   4612 (defun lsp--registered-capability (method)
   4613   "Check whether there is workspace providing METHOD."
   4614   (->> (lsp-workspaces)
   4615        (--keep (seq-find (lambda (reg)
   4616                            (equal (lsp--registered-capability-method reg) method))
   4617                          (lsp--workspace-registered-server-capabilities it)))
   4618        cl-first))
   4619 
   4620 (defun lsp--capability-for-method (method)
   4621   "Get the value of capability for METHOD."
   4622   (-let* ((reqs (cdr (assoc method lsp-method-requirements)))
   4623           ((&plist :capability) reqs))
   4624     (or (and capability (lsp--capability capability))
   4625         (-some-> (lsp--registered-capability method)
   4626           (lsp--registered-capability-options)))))
   4627 
   4628 (defvar-local lsp--before-change-vals nil
   4629   "Store the positions from the `lsp-before-change' function call, for
   4630 validation and use in the `lsp-on-change' function.")
   4631 
   4632 (defun lsp--text-document-content-change-event (start end length)
   4633   "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH."
   4634   ;; So (47 54 0) means add    7 chars starting at pos 47
   4635   ;; must become
   4636   ;;   {"range":{"start":{"line":5,"character":6}
   4637   ;;             ,"end" :{"line":5,"character":6}}
   4638   ;;             ,"rangeLength":0
   4639   ;;             ,"text":"\nbb = 5"}
   4640   ;;
   4641   ;; And (47 47 7) means delete 7 chars starting at pos 47
   4642   ;; must become
   4643   ;;   {"range":{"start":{"line":6,"character":0}
   4644   ;;            ,"end"  :{"line":7,"character":0}}
   4645   ;;            ,"rangeLength":7
   4646   ;;            ,"text":""}
   4647   ;;
   4648   ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with
   4649   ;; 13 chars. So it must become
   4650   ;;   {"range":{"start":{"line":5,"character":8}
   4651   ;;             ,"end" :{"line":5,"character":11}}
   4652   ;;             ,"rangeLength":3
   4653   ;;             ,"text":"new-chars-xxx"}
   4654   ;;
   4655 
   4656   ;; Adding text:
   4657   ;;   lsp-before-change:(start,end)=(33,33)
   4658   ;;   lsp-on-change:(start,end,length)=(33,34,0)
   4659   ;;
   4660   ;; Changing text:
   4661   ;;   lsp-before-change:(start,end)=(208,211)
   4662   ;;   lsp-on-change:(start,end,length)=(208,221,3)
   4663   ;;
   4664   ;; Deleting text:
   4665   ;;   lsp-before-change:(start,end)=(19,27)
   4666   ;;   lsp-on-change:(start,end,length)=(19,19,8)
   4667   (if (zerop length)
   4668       ;; Adding something only, work from start only
   4669       `( :range ,(lsp--range
   4670                   (lsp--point-to-position start)
   4671                   (lsp--point-to-position start))
   4672          :rangeLength 0
   4673          :text ,(buffer-substring-no-properties start end))
   4674 
   4675     (if (eq start end)
   4676         ;; Deleting something only
   4677         (if (lsp--bracketed-change-p start length)
   4678             ;; The before-change value is bracketed, use it
   4679             `( :range ,(lsp--range
   4680                         (lsp--point-to-position start)
   4681                         (plist-get lsp--before-change-vals :end-pos))
   4682                :rangeLength ,length
   4683                :text "")
   4684           ;; If the change is not bracketed, send a full change event instead.
   4685           (lsp--full-change-event))
   4686 
   4687       ;; Deleting some things, adding others
   4688       (if (lsp--bracketed-change-p start length)
   4689           ;; The before-change value is valid, use it
   4690           `( :range ,(lsp--range
   4691                       (lsp--point-to-position start)
   4692                       (plist-get lsp--before-change-vals :end-pos))
   4693              :rangeLength ,length
   4694              :text ,(buffer-substring-no-properties start end))
   4695         (lsp--full-change-event)))))
   4696 
   4697 (defun lsp--bracketed-change-p (start length)
   4698   "If the before and after positions are the same, and the length
   4699 is the size of the start range, we are probably good."
   4700   (-let [(&plist :end before-end :start before-start) lsp--before-change-vals]
   4701     (and (eq start before-start)
   4702          (eq length (- before-end before-start)))))
   4703 
   4704 (defun lsp--full-change-event ()
   4705   `(:text ,(lsp--buffer-content)))
   4706 
   4707 (defun lsp-before-change (start end)
   4708   "Executed before a file is changed.
   4709 Added to `before-change-functions'."
   4710   ;; Note:
   4711   ;;
   4712   ;; This variable holds a list of functions to call when Emacs is about to
   4713   ;; modify a buffer. Each function gets two arguments, the beginning and end of
   4714   ;; the region that is about to change, represented as integers. The buffer
   4715   ;; that is about to change is always the current buffer when the function is
   4716   ;; called.
   4717   ;;
   4718   ;; WARNING:
   4719   ;;
   4720   ;; Do not expect the before-change hooks and the after-change hooks be called
   4721   ;; in balanced pairs around each buffer change. Also don't expect the
   4722   ;; before-change hooks to be called for every chunk of text Emacs is about to
   4723   ;; delete. These hooks are provided on the assumption that Lisp programs will
   4724   ;; use either before- or the after-change hooks, but not both, and the
   4725   ;; boundaries of the region where the changes happen might include more than
   4726   ;; just the actual changed text, or even lump together several changes done
   4727   ;; piecemeal.
   4728   (save-match-data
   4729     (lsp-save-restriction-and-excursion
   4730       (setq lsp--before-change-vals
   4731             (list :start start
   4732                   :end end
   4733                   :end-pos (lsp--point-to-position end))))))
   4734 
   4735 (defun lsp--flush-delayed-changes ()
   4736   (let ((inhibit-quit t))
   4737     (when lsp--delay-timer
   4738       (cancel-timer lsp--delay-timer))
   4739     (mapc (-lambda ((workspace buffer document change))
   4740             (with-current-buffer buffer
   4741               (with-lsp-workspace workspace
   4742                 (lsp-notify "textDocument/didChange"
   4743                             (list :textDocument document
   4744                                   :contentChanges (vector change))))))
   4745           (prog1 (nreverse lsp--delayed-requests)
   4746             (setq lsp--delayed-requests nil)))))
   4747 
   4748 (defun lsp--workspace-sync-method (workspace)
   4749   (let ((sync (-> workspace
   4750                   (lsp--workspace-server-capabilities)
   4751                   (lsp:server-capabilities-text-document-sync?))))
   4752     (if (lsp-text-document-sync-options? sync)
   4753         (lsp:text-document-sync-options-change? sync)
   4754       sync)))
   4755 
   4756 (defun lsp-on-change (start end length &optional content-change-event-fn)
   4757   "Executed when a file is changed.
   4758 Added to `after-change-functions'."
   4759   ;; Note:
   4760   ;;
   4761   ;; Each function receives three arguments: the beginning and end of the region
   4762   ;; just changed, and the length of the text that existed before the change.
   4763   ;; All three arguments are integers. The buffer that has been changed is
   4764   ;; always the current buffer when the function is called.
   4765   ;;
   4766   ;; The length of the old text is the difference between the buffer positions
   4767   ;; before and after that text as it was before the change. As for the
   4768   ;; changed text, its length is simply the difference between the first two
   4769   ;; arguments.
   4770   ;;
   4771   ;; So (47 54 0) means add    7 chars starting at pos 47
   4772   ;; So (47 47 7) means delete 7 chars starting at pos 47
   4773   (save-match-data
   4774     (let ((inhibit-quit t)
   4775           ;; make sure that `lsp-on-change' is called in multi-workspace context
   4776           ;; see #2901
   4777           lsp--cur-workspace)
   4778       ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done
   4779       ;; by auto-revert-mode) will cause this handler to get called with a nil
   4780       ;; buffer-file-name. We need the buffer-file-name to send notifications;
   4781       ;; so we skip handling revert-buffer-caused changes and instead handle
   4782       ;; reverts separately in lsp-on-revert
   4783       (when (not revert-buffer-in-progress-p)
   4784         (cl-incf lsp--cur-version)
   4785         (mapc
   4786          (lambda (workspace)
   4787            (pcase (or lsp-document-sync-method
   4788                       (lsp--workspace-sync-method workspace))
   4789              (1
   4790               (if lsp-debounce-full-sync-notifications
   4791                   (setq lsp--delayed-requests
   4792                         (->> lsp--delayed-requests
   4793                              (-remove (-lambda ((_ buffer))
   4794                                         (equal (current-buffer) buffer)))
   4795                              (cons (list workspace
   4796                                          (current-buffer)
   4797                                          (lsp--versioned-text-document-identifier)
   4798                                          (lsp--full-change-event)))))
   4799                 (with-lsp-workspace workspace
   4800                   (lsp-notify "textDocument/didChange"
   4801                               (list :contentChanges (vector (lsp--full-change-event))
   4802                                     :textDocument (lsp--versioned-text-document-identifier))))))
   4803              (2
   4804               (with-lsp-workspace workspace
   4805                 (lsp-notify
   4806                  "textDocument/didChange"
   4807                  (list :textDocument (lsp--versioned-text-document-identifier)
   4808                        :contentChanges (vector
   4809                                         (if content-change-event-fn
   4810                                             (funcall content-change-event-fn start end length)
   4811                                           (lsp--text-document-content-change-event
   4812                                            start end length)))))))))
   4813          (lsp-workspaces))
   4814         (when lsp--delay-timer (cancel-timer lsp--delay-timer))
   4815         (setq lsp--delay-timer (run-with-idle-timer
   4816                                 lsp-debounce-full-sync-notifications-interval
   4817                                 nil
   4818                                 #'lsp--flush-delayed-changes))
   4819         ;; force cleanup overlays after each change
   4820         (lsp--remove-overlays 'lsp-highlight)
   4821         (lsp--after-change (current-buffer))
   4822         (setq lsp--signature-last-index nil
   4823               lsp--signature-last nil)
   4824         ;; cleanup diagnostics
   4825         (when lsp-diagnostic-clean-after-change
   4826           (lsp-foreach-workspace
   4827            (-let [diagnostics (lsp--workspace-diagnostics lsp--cur-workspace)]
   4828              (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics))))))))
   4829 
   4830 
   4831 
   4832 ;; facilities for on change hooks. We do not want to make lsp calls on each
   4833 ;; change event so we add debounce to avoid flooding the server with events.
   4834 ;; Additionally, we want to have a mechanism for stopping the server calls in
   4835 ;; particular cases like, e. g. when performing completion.
   4836 
   4837 (defvar lsp-inhibit-lsp-hooks nil
   4838   "Flag to control.")
   4839 
   4840 (defcustom lsp-on-change-hook nil
   4841   "Hooks to run when buffer has changed."
   4842   :type 'hook
   4843   :group 'lsp-mode)
   4844 
   4845 (defcustom lsp-idle-delay 0.500
   4846   "Debounce interval for `after-change-functions'."
   4847   :type 'number
   4848   :group 'lsp-mode)
   4849 
   4850 (defcustom lsp-on-idle-hook nil
   4851   "Hooks to run after `lsp-idle-delay'."
   4852   :type 'hook
   4853   :group 'lsp-mode)
   4854 
   4855 (defun lsp--idle-reschedule (buffer)
   4856   (when lsp--on-idle-timer
   4857     (cancel-timer lsp--on-idle-timer))
   4858 
   4859   (setq lsp--on-idle-timer (run-with-idle-timer
   4860                             lsp-idle-delay
   4861                             nil
   4862                             #'lsp--on-idle
   4863                             buffer)))
   4864 
   4865 (defun lsp--post-command ()
   4866   (lsp--cleanup-highlights-if-needed)
   4867   (lsp--idle-reschedule (current-buffer)))
   4868 
   4869 (defun lsp--on-idle (buffer)
   4870   "Start post command loop."
   4871   (when (and (buffer-live-p buffer)
   4872              (equal buffer (current-buffer))
   4873              (not lsp-inhibit-lsp-hooks)
   4874              lsp-managed-mode)
   4875     (run-hooks 'lsp-on-idle-hook)))
   4876 
   4877 (defun lsp--on-change-debounce (buffer)
   4878   (when (and (buffer-live-p buffer)
   4879              (equal buffer (current-buffer))
   4880              (not lsp-inhibit-lsp-hooks)
   4881              lsp-managed-mode)
   4882     (run-hooks 'lsp-on-change-hook)))
   4883 
   4884 (defun lsp--after-change (buffer)
   4885   (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled)
   4886     (lsp--semantic-tokens-refresh-if-enabled buffer))
   4887   (when lsp--on-change-timer
   4888     (cancel-timer lsp--on-change-timer))
   4889   (setq lsp--on-change-timer (run-with-idle-timer
   4890                               lsp-idle-delay
   4891                               nil
   4892                               #'lsp--on-change-debounce
   4893                               buffer))
   4894   (lsp--idle-reschedule buffer))
   4895 
   4896 
   4897 (defcustom lsp-trim-trailing-whitespace t
   4898   "Trim trailing whitespace on a line."
   4899   :group 'lsp-mode
   4900   :type 'boolean)
   4901 
   4902 (defcustom lsp-insert-final-newline t
   4903   "Insert a newline character at the end of the file if one does not exist."
   4904   :group 'lsp-mode
   4905   :type 'boolean)
   4906 
   4907 (defcustom lsp-trim-final-newlines t
   4908   "Trim all newlines after the final newline at the end of the file."
   4909   :group 'lsp-mode
   4910   :type 'boolean)
   4911 
   4912 
   4913 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters)
   4914   "Self insert handling.
   4915 Applies on type formatting."
   4916   (let ((ch last-command-event))
   4917     (when (or (eq (string-to-char first-trigger-characters) ch)
   4918               (cl-find ch more-trigger-characters :key #'string-to-char))
   4919       (lsp-request-async "textDocument/onTypeFormatting"
   4920                          (lsp-make-document-on-type-formatting-params
   4921                           :text-document (lsp--text-document-identifier)
   4922                           :options (lsp-make-formatting-options
   4923                                     :tab-size (symbol-value (lsp--get-indent-width major-mode))
   4924                                     :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   4925                                     :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   4926                                     :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   4927                                     :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))
   4928                           :ch (char-to-string ch)
   4929                           :position (lsp--cur-position))
   4930                          (lambda (data) (lsp--apply-text-edits data 'format))
   4931                          :mode 'tick))))
   4932 
   4933 
   4934 ;; links
   4935 (defun lsp--document-links ()
   4936   (when (lsp-feature? "textDocument/documentLink")
   4937     (lsp-request-async
   4938      "textDocument/documentLink"
   4939      `(:textDocument ,(lsp--text-document-identifier))
   4940      (lambda (links)
   4941        (lsp--remove-overlays 'lsp-link)
   4942        (seq-do
   4943         (-lambda ((link &as &DocumentLink :range (&Range :start :end)))
   4944           (-doto (make-button (lsp--position-to-point start)
   4945                               (lsp--position-to-point end)
   4946                               'action (lsp--document-link-keymap link)
   4947                               'keymap (let ((map (make-sparse-keymap)))
   4948                                         (define-key map [M-return] 'push-button)
   4949                                         (define-key map [mouse-2] 'push-button)
   4950                                         map)
   4951                               'help-echo "mouse-2, M-RET: Visit this link")
   4952             (overlay-put 'lsp-link t)))
   4953         links))
   4954      :mode 'unchanged)))
   4955 
   4956 (defun lsp--document-link-handle-target (url)
   4957   (let* ((parsed-url (url-generic-parse-url (url-unhex-string url)))
   4958          (type (url-type parsed-url)))
   4959     (pcase type
   4960       ("file"
   4961        (xref-push-marker-stack)
   4962        (find-file (lsp--uri-to-path url))
   4963        (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url))
   4964          (goto-char (lsp--position-to-point
   4965                      (lsp-make-position :character (1- (string-to-number column))
   4966                                         :line (1- (string-to-number line)))))))
   4967       ((or "http" "https") (browse-url url))
   4968       (type (if-let ((handler (lsp--get-uri-handler type)))
   4969                 (funcall handler url)
   4970               (signal 'lsp-file-scheme-not-supported (list url)))))))
   4971 
   4972 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?))
   4973   (if target?
   4974       (lambda (_)
   4975         (interactive)
   4976         (lsp--document-link-handle-target target?))
   4977     (lambda (_)
   4978       (interactive)
   4979       (when (lsp:document-link-registration-options-resolve-provider?
   4980              (lsp--capability-for-method "textDocument/documentLink"))
   4981         (lsp-request-async
   4982          "documentLink/resolve"
   4983          link
   4984          (-lambda ((&DocumentLink :target?))
   4985            (lsp--document-link-handle-target target?)))))))
   4986 
   4987 
   4988 
   4989 (defcustom lsp-warn-no-matched-clients t
   4990   "Whether to show messages when there are no supported clients."
   4991   :group 'lsp-mode
   4992   :type 'boolean)
   4993 
   4994 (defun lsp-buffer-language--configured-id ()
   4995   "Return nil when not registered."
   4996   (->> lsp-language-id-configuration
   4997        (-first
   4998         (-lambda ((mode-or-pattern . language))
   4999           (cond
   5000            ((and (stringp mode-or-pattern)
   5001                  (s-matches? mode-or-pattern (buffer-file-name)))
   5002             language)
   5003            ((eq mode-or-pattern major-mode) language))))
   5004        cl-rest))
   5005 
   5006 (defvar-local lsp--buffer-language nil
   5007   "Locally cached returned value of `lsp-buffer-language'.")
   5008 
   5009 (defun lsp-buffer-language ()
   5010   "Get language corresponding current buffer."
   5011   (or lsp--buffer-language
   5012       (let* ((configured-language (lsp-buffer-language--configured-id)))
   5013         (setq lsp--buffer-language
   5014               (or configured-language
   5015                   ;; ensure non-nil
   5016                   (string-remove-suffix "-mode" (symbol-name major-mode))))
   5017         (when (and lsp-warn-no-matched-clients
   5018                    (null configured-language))
   5019           (lsp-warn "Unable to calculate the languageId for buffer `%s'. \
   5020 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s"
   5021                     (buffer-name)
   5022                     major-mode))
   5023         lsp--buffer-language)))
   5024 
   5025 (defun lsp-activate-on (&rest languages)
   5026   "Returns language activation function.
   5027 The function will return t when the `lsp-buffer-language' returns
   5028 one of the LANGUAGES."
   5029   (lambda (_file-name _mode)
   5030     (-contains? languages (lsp-buffer-language))))
   5031 
   5032 (defun lsp-workspace-root (&optional path)
   5033   "Find the workspace root for the current file or PATH."
   5034   (-when-let* ((file-name (or path (buffer-file-name)))
   5035                (file-name (lsp-f-canonical file-name)))
   5036     (->> (lsp-session)
   5037          (lsp-session-folders)
   5038          (--filter (and (lsp--files-same-host it file-name)
   5039                         (or (lsp-f-ancestor-of? it file-name)
   5040                             (equal it file-name))))
   5041          (--max-by (> (length it) (length other))))))
   5042 
   5043 (defun lsp-on-revert ()
   5044   "Executed when a file is reverted.
   5045 Added to `after-revert-hook'."
   5046   (let ((n (buffer-size))
   5047         (revert-buffer-in-progress-p nil))
   5048     (lsp-on-change 0 n n)))
   5049 
   5050 (defun lsp--text-document-did-close (&optional keep-workspace-alive)
   5051   "Executed when the file is closed, added to `kill-buffer-hook'.
   5052 
   5053 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace
   5054 if it's closing the last buffer in the workspace."
   5055   (lsp-foreach-workspace
   5056    (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   5057    (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S"
   5058      (lsp-notify "textDocument/didClose"
   5059                  `(:textDocument ,(lsp--text-document-identifier))))
   5060    (when (and (not lsp-keep-workspace-alive)
   5061               (not keep-workspace-alive)
   5062               (not (lsp--workspace-buffers lsp--cur-workspace)))
   5063      (lsp--shutdown-workspace))))
   5064 
   5065 (defun lsp--will-save-text-document-params (reason)
   5066   (list :textDocument (lsp--text-document-identifier)
   5067         :reason reason))
   5068 
   5069 (defun lsp--before-save ()
   5070   "Before save handler."
   5071   (with-demoted-errors "Error in ‘lsp--before-save’: %S"
   5072     (let ((params (lsp--will-save-text-document-params 1)))
   5073       (when (lsp--send-will-save-p)
   5074         (lsp-notify "textDocument/willSave" params))
   5075       (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits)
   5076         (let ((lsp-response-timeout 0.1))
   5077           (condition-case nil
   5078               (lsp--apply-text-edits
   5079                (lsp-request "textDocument/willSaveWaitUntil"
   5080                             params)
   5081                'before-save)
   5082             (error)))))))
   5083 
   5084 (defun lsp--on-auto-save ()
   5085   "Handler for auto-save."
   5086   (when (lsp--send-will-save-p)
   5087     (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S"
   5088       (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2)))))
   5089 
   5090 (defun lsp--text-document-did-save ()
   5091   "Executed when the file is closed, added to `after-save-hook''."
   5092   (when (lsp--send-did-save-p)
   5093     (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’"
   5094       (lsp-notify "textDocument/didSave"
   5095                   `( :textDocument ,(lsp--versioned-text-document-identifier)
   5096                      ,@(when (lsp--save-include-text-p)
   5097                          (list :text (lsp--buffer-content))))))))
   5098 
   5099 (defun lsp--text-document-position-params (&optional identifier position)
   5100   "Make TextDocumentPositionParams for the current point in the current document.
   5101 If IDENTIFIER and POSITION are non-nil, they will be used as the document
   5102 identifier and the position respectively."
   5103   (list :textDocument (or identifier (lsp--text-document-identifier))
   5104         :position (or position (lsp--cur-position))))
   5105 
   5106 (defun lsp--get-buffer-diagnostics ()
   5107   "Return buffer diagnostics."
   5108   (gethash (or
   5109             (plist-get lsp--virtual-buffer :buffer-file-name)
   5110             (lsp--fix-path-casing (buffer-file-name)))
   5111            (lsp-diagnostics t)))
   5112 
   5113 (defun lsp-cur-line-diagnostics ()
   5114   "Return any diagnostics that apply to the current line."
   5115   (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)]
   5116     (cl-coerce (-filter
   5117                 (-lambda ((&Diagnostic :range (&Range :start (&Position :line))))
   5118                   (and (>= line start) (<= line end)))
   5119                 (lsp--get-buffer-diagnostics))
   5120                'vector)))
   5121 
   5122 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end)
   5123                                   (right &as &Range :start right-start :end right-end))
   5124   (or (lsp-point-in-range? right-start left)
   5125       (lsp-point-in-range? right-end left)
   5126       (lsp-point-in-range? left-start right)
   5127       (lsp-point-in-range? left-end right)))
   5128 
   5129 (defun lsp-make-position-1 (position)
   5130   (lsp-make-position :line (plist-get position :line)
   5131                      :character (plist-get position :character)))
   5132 
   5133 (defun lsp-cur-possition-diagnostics ()
   5134   "Return any diagnostics that apply to the current line."
   5135   (-let* ((start (if (use-region-p) (region-beginning) (point)))
   5136           (end (if (use-region-p) (region-end) (point)))
   5137           (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start))
   5138                                          :end (lsp-make-position-1 (lsp-point-to-position end)))))
   5139     (->> (lsp--get-buffer-diagnostics)
   5140          (-filter
   5141           (-lambda ((&Diagnostic :range))
   5142             (lsp-range-overlapping? range current-range)))
   5143          (apply 'vector))))
   5144 
   5145 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics)
   5146 
   5147 (defun lsp--extract-line-from-buffer (pos)
   5148   "Return the line pointed to by POS (a Position object) in the current buffer."
   5149   (let* ((point (lsp--position-to-point pos))
   5150          (inhibit-field-text-motion t))
   5151     (save-excursion
   5152       (goto-char point)
   5153       (buffer-substring (line-beginning-position) (line-end-position)))))
   5154 
   5155 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line)
   5156                                                  :end (end &as &Position :character end-char)))
   5157   "Return a xref-item from a RANGE in FILENAME."
   5158   (let* ((line (lsp--extract-line-from-buffer start))
   5159          (len (length line)))
   5160     (add-face-text-property (max (min start-char len) 0)
   5161                             (max (min end-char len) 0)
   5162                             'xref-match t line)
   5163     ;; LINE is nil when FILENAME is not being current visited by any buffer.
   5164     (xref-make-match (or line filename)
   5165                      (xref-make-file-location
   5166                       filename
   5167                       (lsp-translate-line (1+ start-line))
   5168                       (lsp-translate-column start-char))
   5169                      (- end-char start-char))))
   5170 
   5171 (defun lsp--location-uri (loc)
   5172   (if (lsp-location? loc)
   5173       (lsp:location-uri loc)
   5174     (lsp:location-link-target-uri loc)))
   5175 
   5176 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start)))
   5177   "Go to location."
   5178   (let ((path (lsp--uri-to-path uri)))
   5179     (if (f-exists? path)
   5180         (with-current-buffer (find-file path)
   5181           (goto-char (lsp--position-to-point start)))
   5182       (error "There is no file %s" path))))
   5183 
   5184 (defun lsp--location-range (loc)
   5185   (if (lsp-location? loc)
   5186       (lsp:location-range loc)
   5187     (lsp:location-link-target-selection-range loc)))
   5188 
   5189 (defun lsp--locations-to-xref-items (locations)
   5190   "Return a list of `xref-item' given LOCATIONS, which can be of
   5191 type Location, LocationLink, Location[] or LocationLink[]."
   5192   (setq locations
   5193         (pcase locations
   5194           ((seq (or (Location)
   5195                     (LocationLink)))
   5196            (append locations nil))
   5197           ((or (Location)
   5198                (LocationLink))
   5199            (list locations))))
   5200 
   5201   (cl-labels ((get-xrefs-in-file
   5202                (file-locs)
   5203                (-let [(filename . matches) file-locs]
   5204                  (condition-case err
   5205                      (let ((visiting (find-buffer-visiting filename))
   5206                            (fn (lambda (loc)
   5207                                  (lsp-with-filename filename
   5208                                    (lsp--xref-make-item filename
   5209                                                         (lsp--location-range loc))))))
   5210                        (if visiting
   5211                            (with-current-buffer visiting
   5212                              (seq-map fn matches))
   5213                          (when (file-readable-p filename)
   5214                            (with-temp-buffer
   5215                              (insert-file-contents-literally filename)
   5216                              (seq-map fn matches)))))
   5217                    (error (lsp-warn "Failed to process xref entry for filename '%s': %s"
   5218                                     filename (error-message-string err)))
   5219                    (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s"
   5220                                          filename (error-message-string err)))))))
   5221 
   5222     (->> locations
   5223          (seq-sort #'lsp--location-before-p)
   5224          (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri))
   5225          (seq-map #'get-xrefs-in-file)
   5226          (apply #'nconc))))
   5227 
   5228 (defun lsp--location-before-p (left right)
   5229   "Sort first by file, then by line, then by column."
   5230   (let ((left-uri (lsp--location-uri left))
   5231         (right-uri (lsp--location-uri right)))
   5232     (if (not (string= left-uri right-uri))
   5233         (string< left-uri right-uri)
   5234       (-let (((&Range :start left-start) (lsp--location-range left))
   5235              ((&Range :start right-start) (lsp--location-range right)))
   5236         (lsp--position-compare right-start left-start)))))
   5237 
   5238 (defun lsp--make-reference-params (&optional td-position exclude-declaration)
   5239   "Make a ReferenceParam object.
   5240 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead.
   5241 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations."
   5242   (let ((json-false :json-false))
   5243     (plist-put (or td-position (lsp--text-document-position-params))
   5244                :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration))))))
   5245 
   5246 (defun lsp--cancel-request (id)
   5247   "Cancel request with ID in all workspaces."
   5248   (lsp-foreach-workspace
   5249    (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id))
   5250    (lsp-notify "$/cancelRequest" `(:id ,id))))
   5251 
   5252 (defvar-local lsp--hover-saved-bounds nil)
   5253 
   5254 (defun lsp-eldoc-function (cb &rest _ignored)
   5255   "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')."
   5256   (if (and lsp--hover-saved-bounds
   5257            (lsp--point-in-bounds-p lsp--hover-saved-bounds))
   5258       lsp--eldoc-saved-message
   5259     (setq lsp--hover-saved-bounds nil
   5260           lsp--eldoc-saved-message nil)
   5261     (if (looking-at-p "[[:space:]\n]")
   5262         (setq lsp--eldoc-saved-message nil) ; And returns nil.
   5263       (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover"))
   5264         (lsp-request-async
   5265          "textDocument/hover"
   5266          (lsp--text-document-position-params)
   5267          (-lambda ((hover &as &Hover? :range? :contents))
   5268            (setq lsp--hover-saved-bounds (when range?
   5269                                            (lsp--range-to-region range?)))
   5270            (funcall cb (setq lsp--eldoc-saved-message
   5271                              (when contents
   5272                                (lsp--render-on-hover-content
   5273                                 contents
   5274                                 lsp-eldoc-render-all)))))
   5275          :error-handler #'ignore
   5276          :mode 'tick
   5277          :cancel-token :eldoc-hover)))))
   5278 
   5279 (defun lsp--point-on-highlight? ()
   5280   (-some? (lambda (overlay)
   5281             (overlay-get overlay 'lsp-highlight))
   5282           (overlays-at (point))))
   5283 
   5284 (defun lsp--cleanup-highlights-if-needed ()
   5285   (when (and lsp-enable-symbol-highlighting
   5286              lsp--have-document-highlights
   5287              (not (lsp--point-on-highlight?)))
   5288     (lsp--remove-overlays 'lsp-highlight)
   5289     (setq lsp--have-document-highlights nil)
   5290     (lsp-cancel-request-by-token :highlights)))
   5291 
   5292 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil
   5293   "The bounds of the symbol from which `lsp--document-highlight'
   5294   most recently requested highlights.")
   5295 
   5296 (defun lsp--document-highlight ()
   5297   (when (lsp-feature? "textDocument/documentHighlight")
   5298     (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol)))
   5299       (unless (or (looking-at-p "[[:space:]\n]")
   5300                   (not lsp-enable-symbol-highlighting)
   5301                   (and lsp--have-document-highlights
   5302                        curr-sym-bounds
   5303                        (equal curr-sym-bounds
   5304                               lsp--symbol-bounds-of-last-highlight-invocation)))
   5305         (setq lsp--symbol-bounds-of-last-highlight-invocation
   5306               curr-sym-bounds)
   5307         (lsp-request-async "textDocument/documentHighlight"
   5308                            (lsp--text-document-position-params)
   5309                            #'lsp--document-highlight-callback
   5310                            :mode 'tick
   5311                            :cancel-token :highlights)))))
   5312 
   5313 (defun lsp--help-open-link (&rest _)
   5314   "Open markdown link at point via mouse or keyboard."
   5315   (interactive "P")
   5316   (let ((buffer-list-update-hook nil))
   5317     (-let [(buffer point) (if-let* ((valid (and (listp last-input-event)
   5318                                                 (eq (car last-input-event) 'mouse-2)))
   5319                                     (event (cadr last-input-event))
   5320                                     (win (posn-window event))
   5321                                     (buffer (window-buffer win)))
   5322                               `(,buffer ,(posn-point event))
   5323                             `(,(current-buffer) ,(point)))]
   5324       (with-current-buffer buffer
   5325         (when-let* ((face (get-text-property point 'face))
   5326                     (url (or (and (eq face 'markdown-link-face)
   5327                                   (get-text-property point 'help-echo))
   5328                              (and (memq face '(markdown-url-face markdown-plain-url-face))
   5329                                   (nth 3 (markdown-link-at-pos point))))))
   5330           (lsp--document-link-handle-target url))))))
   5331 
   5332 (defvar lsp-help-mode-map
   5333   (-doto (make-sparse-keymap)
   5334     (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link))
   5335   "Keymap for `lsp-help-mode'.")
   5336 
   5337 (define-derived-mode lsp-help-mode help-mode "LspHelp"
   5338   "Major mode for displaying lsp help.")
   5339 
   5340 (defun lsp-describe-thing-at-point ()
   5341   "Display the type signature and documentation of the thing at point."
   5342   (interactive)
   5343   (let ((contents (-some->> (lsp--text-document-position-params)
   5344                     (lsp--make-request "textDocument/hover")
   5345                     (lsp--send-request)
   5346                     (lsp:hover-contents))))
   5347     (if (and contents (not (equal contents "")))
   5348         (let ((lsp-help-buf-name "*lsp-help*"))
   5349           (with-current-buffer (get-buffer-create lsp-help-buf-name)
   5350             (delay-mode-hooks
   5351               (lsp-help-mode)
   5352               (with-help-window lsp-help-buf-name
   5353                 (insert (string-trim-right (lsp--render-on-hover-content contents t)))))
   5354             (run-mode-hooks)))
   5355       (lsp--info "No content at point."))))
   5356 
   5357 (defun lsp--point-in-bounds-p (bounds)
   5358   "Return whether the current point is within BOUNDS."
   5359   (and (<= (car bounds) (point)) (< (point) (cdr bounds))))
   5360 
   5361 (defun lsp-get-renderer (language)
   5362   "Get renderer for LANGUAGE."
   5363   (lambda (str)
   5364     (lsp--render-string str language)))
   5365 
   5366 (defun lsp--setup-markdown (mode)
   5367   "Setup the ‘markdown-mode’ in the frame.
   5368 MODE is the mode used in the parent frame."
   5369   (make-local-variable 'markdown-code-lang-modes)
   5370   (dolist (mark (alist-get mode lsp-custom-markup-modes))
   5371     (add-to-list 'markdown-code-lang-modes (cons mark mode)))
   5372   (setq-local markdown-fontify-code-blocks-natively t)
   5373   (setq-local markdown-fontify-code-block-default-mode mode)
   5374   (setq-local markdown-hide-markup t)
   5375 
   5376   ;; Render some common HTML entities.
   5377   ;; This should really happen in markdown-mode instead,
   5378   ;; but it doesn't, so we do it here for now.
   5379   (setq prettify-symbols-alist
   5380         (cl-loop for i from 0 to 255
   5381                  collect (cons (format "&#x%02X;" i) i)))
   5382   (push '("&lt;" . ?<) prettify-symbols-alist)
   5383   (push '("&gt;" . ?>) prettify-symbols-alist)
   5384   (push '("&amp;" . ?&) prettify-symbols-alist)
   5385   (push '("&nbsp;" . ? ) prettify-symbols-alist)
   5386   (setq prettify-symbols-compose-predicate
   5387         (lambda (_start _end _match) t))
   5388   (prettify-symbols-mode 1))
   5389 
   5390 (defvar lsp-help-link-keymap
   5391   (let ((map (make-sparse-keymap)))
   5392     (define-key map [mouse-2] #'lsp--help-open-link)
   5393     (define-key map "\r" #'lsp--help-open-link)
   5394     map)
   5395   "Keymap active on links in *lsp-help* mode.")
   5396 
   5397 (defun lsp--fix-markdown-links ()
   5398   (let ((inhibit-read-only t)
   5399         (inhibit-modification-hooks t)
   5400         (prop))
   5401     (save-restriction
   5402       (goto-char (point-min))
   5403       (while (setq prop (markdown-find-next-prop 'face))
   5404         (let ((end (or (next-single-property-change (car prop) 'face)
   5405                        (point-max))))
   5406           (when (memq (get-text-property (car prop) 'face)
   5407                       '(markdown-link-face
   5408                         markdown-url-face
   5409                         markdown-plain-url-face))
   5410             (add-text-properties (car prop) end
   5411                                  (list 'button t
   5412                                        'category 'lsp-help-link
   5413                                        'follow-link t
   5414                                        'keymap lsp-help-link-keymap)))
   5415           (goto-char end))))))
   5416 
   5417 (defun lsp--buffer-string-visible ()
   5418   "Return visible buffer string.
   5419 Stolen from `org-copy-visible'."
   5420   (let ((temp (generate-new-buffer " *temp*"))
   5421         (beg (point-min))
   5422         (end (point-max)))
   5423     (while (/= beg end)
   5424       (when (get-char-property beg 'invisible)
   5425         (setq beg (next-single-char-property-change beg 'invisible nil end)))
   5426       (let* ((next (next-single-char-property-change beg 'invisible nil end))
   5427              (substring (buffer-substring beg next)))
   5428         (with-current-buffer temp (insert substring))
   5429         ;; (setq result (concat result substring))
   5430         (setq beg next)))
   5431     (setq deactivate-mark t)
   5432     (prog1 (with-current-buffer temp
   5433              (s-chop-suffix "\n" (buffer-string)))
   5434       (kill-buffer temp))))
   5435 
   5436 (defvar lsp-buffer-major-mode nil
   5437   "Holds the major mode when fontification function is running.
   5438 See #2588")
   5439 
   5440 (defvar view-inhibit-help-message)
   5441 
   5442 (defun lsp--render-markdown ()
   5443   "Render markdown."
   5444 
   5445   (let ((markdown-enable-math nil))
   5446     (goto-char (point-min))
   5447     (while (re-search-forward
   5448             (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/"
   5449                                      "{" "}" "[" "]" "(" ")"
   5450                                      "#" "+" "-" "." "!" "|"))))
   5451             nil t)
   5452       (replace-match (rx (backref 1))))
   5453 
   5454     ;; markdown-mode v2.3 does not yet provide gfm-view-mode
   5455     (if (fboundp 'gfm-view-mode)
   5456         (let ((view-inhibit-help-message t))
   5457           (gfm-view-mode))
   5458       (gfm-mode))
   5459 
   5460     (lsp--setup-markdown lsp-buffer-major-mode)))
   5461 
   5462 (defvar lsp--display-inline-image-alist
   5463   '((lsp--render-markdown
   5464      (:regexp
   5465       "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)"
   5466       :sexp
   5467       (create-image
   5468        (base64-decode-string
   5469         (buffer-substring-no-properties (match-beginning 1) (match-end 1)))
   5470        nil t))))
   5471   "Replaced string regexp and function returning image.
   5472 Each element should have the form (MODE . (PROPERTY-LIST...)).
   5473 MODE (car) is function which is defined in `lsp-language-id-configuration'.
   5474 Cdr should be list of PROPERTY-LIST.
   5475 
   5476 Each PROPERTY-LIST should have properties:
   5477 :regexp  Regexp which determines what string is relpaced to image.
   5478          You should also get information of image, by parenthesis constructs.
   5479          By default, all matched string is replaced to image, but you can
   5480          change index of replaced string by keyword :replaced-index.
   5481 
   5482 :sexp    Return image when evaluated. You can use information of regexp
   5483          by using (match-beggining N), (match-end N) or (match-substring N).
   5484 
   5485 In addition, each can have property:
   5486 :replaced-index  Determine index which is used to replace regexp to image.
   5487                  The value means first argument of `match-beginning' and
   5488                  `match-end'. If omitted, interpreted as index 0.")
   5489 
   5490 (defcustom lsp-display-inline-image t
   5491   "Showing inline image or not."
   5492   :group 'lsp-mode
   5493   :type 'boolean)
   5494 
   5495 (defcustom lsp-enable-suggest-server-download t
   5496   "When non-nil enable server downloading suggestions."
   5497   :group 'lsp-mode
   5498   :type 'boolean
   5499   :package-version '(lsp-mode . "9.0.0"))
   5500 
   5501 (defcustom lsp-auto-register-remote-clients t
   5502   "When non-nil register remote when registering the local one."
   5503   :group 'lsp-mode
   5504   :type 'boolean
   5505   :package-version '(lsp-mode . "9.0.0"))
   5506 
   5507 (defun lsp--display-inline-image (mode)
   5508   "Add image property if available."
   5509   (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist))))
   5510     (when (and (display-images-p) lsp-display-inline-image)
   5511       (cl-loop
   5512        for plist in plist-list
   5513        with regexp with replaced-index
   5514        do
   5515        (setq regexp (plist-get plist :regexp))
   5516        (setq replaced-index (or (plist-get plist :replaced-index) 0))
   5517 
   5518        (font-lock-remove-keywords nil (list regexp replaced-index))
   5519        (let ((inhibit-read-only t))
   5520          (save-excursion
   5521            (goto-char (point-min))
   5522            (while (re-search-forward regexp nil t)
   5523              (set-text-properties
   5524               (match-beginning replaced-index) (match-end replaced-index)
   5525               nil)
   5526              (add-text-properties
   5527               (match-beginning replaced-index) (match-end replaced-index)
   5528               `(display ,(eval (plist-get plist :sexp)))))))))))
   5529 
   5530 (defun lsp--fontlock-with-mode (str mode)
   5531   "Fontlock STR with MODE."
   5532   (let ((lsp-buffer-major-mode major-mode))
   5533     (with-temp-buffer
   5534       (with-demoted-errors "Error during doc rendering: %s"
   5535         (insert str)
   5536         (delay-mode-hooks (funcall mode))
   5537         (cl-flet ((window-body-width () lsp-window-body-width))
   5538           ;; This can go wrong in some cases, and the fontification would
   5539           ;; not work as expected.
   5540           ;;
   5541           ;; See #2984
   5542           (ignore-errors (font-lock-ensure))
   5543           (lsp--display-inline-image mode)
   5544           (when (eq mode 'lsp--render-markdown)
   5545             (lsp--fix-markdown-links))))
   5546       (lsp--buffer-string-visible))))
   5547 
   5548 (defun lsp--render-string (str language)
   5549   "Render STR using `major-mode' corresponding to LANGUAGE.
   5550 When language is nil render as markup if `markdown-mode' is loaded."
   5551   (setq str (s-replace "\r" "" (or str "")))
   5552   (if-let* ((modes (-keep (-lambda ((mode . lang))
   5553                             (when (and (equal lang language) (functionp mode))
   5554                               mode))
   5555                           lsp-language-id-configuration))
   5556             (mode (car (or (member major-mode modes) modes))))
   5557       (lsp--fontlock-with-mode str mode)
   5558     str))
   5559 
   5560 (defun lsp--render-element (content)
   5561   "Render CONTENT element."
   5562   (let ((inhibit-message t))
   5563     (or
   5564      (pcase content
   5565        ((MarkedString :value :language)
   5566         (lsp--render-string value language))
   5567        ((MarkupContent :value :kind)
   5568         (lsp--render-string value kind))
   5569        ;; plain string
   5570        ((pred stringp) (lsp--render-string content "markdown"))
   5571        ((pred null) "")
   5572        (_ (error "Failed to handle %s" content)))
   5573      "")))
   5574 
   5575 (defun lsp--create-unique-string-fn ()
   5576   (let (elements)
   5577     (lambda (element)
   5578       (let ((count (cl-count element elements :test #'string=)))
   5579         (prog1 (if (zerop count)
   5580                    element
   5581                  (format "%s (%s)" element count))
   5582           (push element elements))))))
   5583 
   5584 (defun lsp--select-action (actions)
   5585   "Select an action to execute from ACTIONS."
   5586   (cond
   5587    ((seq-empty-p actions) (signal 'lsp-no-code-actions nil))
   5588    ((and (eq (seq-length actions) 1) lsp-auto-execute-action)
   5589     (lsp-seq-first actions))
   5590    (t (let ((completion-ignore-case t))
   5591         (lsp--completing-read "Select code action: "
   5592                               (seq-into actions 'list)
   5593                               (-compose (lsp--create-unique-string-fn)
   5594                                         #'lsp:code-action-title)
   5595                               nil t)))))
   5596 
   5597 (defun lsp--workspace-server-id (workspace)
   5598   "Return the server ID of WORKSPACE."
   5599   (-> workspace lsp--workspace-client lsp--client-server-id))
   5600 
   5601 (defun lsp--handle-rendered-for-echo-area (contents)
   5602   "Return a single line from RENDERED, appropriate for display in the echo area."
   5603   (pcase (lsp-workspaces)
   5604     (`(,workspace)
   5605      (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace)))
   5606     ;; For projects with multiple active workspaces we also default to
   5607     ;; render the first line.
   5608     (_ (lsp-clients-extract-signature-on-hover contents nil))))
   5609 
   5610 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id)
   5611   "Extract a representative line from CONTENTS, to show in the echo area."
   5612   (car (s-lines (s-trim (lsp--render-element contents)))))
   5613 
   5614 (defun lsp--render-on-hover-content (contents render-all)
   5615   "Render the content received from `document/onHover' request.
   5616 CONTENTS  - MarkedString | MarkedString[] | MarkupContent
   5617 RENDER-ALL - nil if only the signature should be rendered."
   5618   (cond
   5619    ((lsp-markup-content? contents)
   5620     ;; MarkupContent.
   5621     ;; It tends to be long and is not suitable to display fully in the echo area.
   5622     ;; Just display the first line which is typically the signature.
   5623     (if render-all
   5624         (lsp--render-element contents)
   5625       (lsp--handle-rendered-for-echo-area contents)))
   5626    ((and (stringp contents) (not (string-match-p "\n" contents)))
   5627     ;; If the contents is a single string containing a single line,
   5628     ;; render it always.
   5629     (lsp--render-element contents))
   5630    (t
   5631     ;; MarkedString -> MarkedString[]
   5632     (when (or (lsp-marked-string? contents) (stringp contents))
   5633       (setq contents (list contents)))
   5634     ;; Consider the signature consisting of the elements who have a renderable
   5635     ;; "language" property. When render-all is nil, ignore other elements.
   5636     (string-join
   5637      (seq-map
   5638       #'lsp--render-element
   5639       (if render-all
   5640           contents
   5641         ;; Only render contents that have an available renderer.
   5642         (seq-take
   5643          (seq-filter
   5644           (-andfn #'lsp-marked-string?
   5645                   (-compose #'lsp-get-renderer #'lsp:marked-string-language))
   5646           contents)
   5647          1)))
   5648      (if (bound-and-true-p page-break-lines-mode)
   5649          "\n\n"
   5650        "\n")))))
   5651 
   5652 
   5653 
   5654 (defvar lsp-signature-mode-map
   5655   (-doto (make-sparse-keymap)
   5656     (define-key (kbd "M-n") #'lsp-signature-next)
   5657     (define-key (kbd "M-p") #'lsp-signature-previous)
   5658     (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs)
   5659     (define-key (kbd "C-c C-k") #'lsp-signature-stop)
   5660     (define-key (kbd "C-g") #'lsp-signature-stop))
   5661   "Keymap for `lsp-signature-mode'.")
   5662 
   5663 (define-minor-mode lsp-signature-mode
   5664   "Mode used to show signature popup."
   5665   :keymap lsp-signature-mode-map
   5666   :lighter ""
   5667   :group 'lsp-mode)
   5668 
   5669 (defun lsp-signature-stop ()
   5670   "Stop showing current signature help."
   5671   (interactive)
   5672   (lsp-cancel-request-by-token :signature)
   5673   (remove-hook 'post-command-hook #'lsp-signature)
   5674   (funcall lsp-signature-function nil)
   5675   (lsp-signature-mode -1))
   5676 
   5677 (declare-function page-break-lines--update-display-tables "ext:page-break-lines")
   5678 
   5679 (defun lsp--setup-page-break-mode-if-present ()
   5680   "Enable `page-break-lines-mode' in current buffer."
   5681   (when (fboundp 'page-break-lines-mode)
   5682     (page-break-lines-mode)
   5683     ;; force page-break-lines-mode to update the display tables.
   5684     (page-break-lines--update-display-tables)))
   5685 
   5686 (defun lsp-lv-message (message)
   5687   (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)
   5688   (if message
   5689       (progn
   5690         (setq lsp--signature-last-buffer (current-buffer))
   5691         (let ((lv-force-update t))
   5692           (lv-message "%s" message)))
   5693     (lv-delete-window)
   5694     (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)))
   5695 
   5696 (declare-function posframe-show "ext:posframe")
   5697 (declare-function posframe-hide "ext:posframe")
   5698 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe")
   5699 
   5700 (defface lsp-signature-posframe
   5701   '((t :inherit tooltip))
   5702   "Background and foreground for `lsp-signature-posframe'."
   5703   :group 'lsp-mode)
   5704 
   5705 (defvar lsp-signature-posframe-params
   5706   (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward
   5707         :height 10
   5708         :width 60
   5709         :border-width 1
   5710         :min-width 60)
   5711   "Params for signature and `posframe-show'.")
   5712 
   5713 (defun lsp-signature-posframe (str)
   5714   "Use posframe to show the STR signatureHelp string."
   5715   (if str
   5716       (apply #'posframe-show
   5717              (with-current-buffer (get-buffer-create " *lsp-signature*")
   5718                (erase-buffer)
   5719                (insert str)
   5720                (visual-line-mode 1)
   5721                (lsp--setup-page-break-mode-if-present)
   5722                (current-buffer))
   5723              (append
   5724               lsp-signature-posframe-params
   5725               (list :position (point)
   5726                     :background-color (face-attribute 'lsp-signature-posframe :background nil t)
   5727                     :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t)
   5728                     :border-color (face-attribute 'font-lock-comment-face :foreground nil t))))
   5729     (posframe-hide " *lsp-signature*")))
   5730 
   5731 (defun lsp--handle-signature-update (signature)
   5732   (let ((message
   5733          (if (lsp-signature-help? signature)
   5734              (lsp--signature->message signature)
   5735            (mapconcat #'lsp--signature->message signature "\n"))))
   5736     (if (s-present? message)
   5737         (funcall lsp-signature-function message)
   5738       (lsp-signature-stop))))
   5739 
   5740 (defun lsp-signature-activate ()
   5741   "Activate signature help.
   5742 It will show up only if current point has signature help."
   5743   (interactive)
   5744   (setq lsp--signature-last nil
   5745         lsp--signature-last-index nil
   5746         lsp--signature-last-buffer (current-buffer))
   5747   (add-hook 'post-command-hook #'lsp-signature)
   5748   (lsp-signature-mode t))
   5749 
   5750 (defcustom lsp-signature-cycle t
   5751   "Whether `lsp-signature-next' and prev should cycle."
   5752   :type 'boolean
   5753   :group 'lsp-mode)
   5754 
   5755 (defun lsp-signature-next ()
   5756   "Show next signature."
   5757   (interactive)
   5758   (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last))))
   5759     (when (and lsp--signature-last-index
   5760                lsp--signature-last
   5761                (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs)))
   5762       (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs))
   5763       (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))))
   5764 
   5765 (defun lsp-signature-previous ()
   5766   "Next signature."
   5767   (interactive)
   5768   (when (and lsp--signature-last-index
   5769              lsp--signature-last
   5770              (or lsp-signature-cycle (not (zerop lsp--signature-last-index))))
   5771     (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index)
   5772                                             (length (lsp:signature-help-signatures lsp--signature-last))
   5773                                           lsp--signature-last-index)))
   5774     (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))
   5775 
   5776 (defun lsp-signature-toggle-full-docs ()
   5777   "Toggle full/partial signature documentation."
   5778   (interactive)
   5779   (let ((all? (not (numberp lsp-signature-doc-lines))))
   5780     (setq lsp-signature-doc-lines (if all?
   5781                                       (or (car-safe lsp-signature-doc-lines)
   5782                                           20)
   5783                                     (list lsp-signature-doc-lines))))
   5784   (lsp-signature-activate))
   5785 
   5786 (defun lsp--signature->message (signature-help)
   5787   "Generate eldoc message from SIGNATURE-HELP response."
   5788   (setq lsp--signature-last signature-help)
   5789 
   5790   (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help))))
   5791     (-let* (((&SignatureHelp :active-signature?
   5792                              :active-parameter?
   5793                              :signatures) signature-help)
   5794             (active-signature? (or lsp--signature-last-index active-signature? 0))
   5795             (_ (setq lsp--signature-last-index active-signature?))
   5796             ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?))
   5797             (prefix (if (= (length signatures) 1)
   5798                         ""
   5799                       (concat (propertize (format " %s/%s"
   5800                                                   (1+ active-signature?)
   5801                                                   (length signatures))
   5802                                           'face 'success)
   5803                               " ")))
   5804             (method-docs (when
   5805                              (and lsp-signature-render-documentation
   5806                                   (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines)))
   5807                            (let ((docs (lsp--render-element
   5808                                         (lsp:parameter-information-documentation? signature))))
   5809                              (when (s-present? docs)
   5810                                (concat
   5811                                 "\n"
   5812                                 (if (fboundp 'page-break-lines-mode)
   5813                                     "\n"
   5814                                   "")
   5815                                 (if (and (numberp lsp-signature-doc-lines)
   5816                                          (> (length (s-lines docs)) lsp-signature-doc-lines))
   5817                                     (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs)))
   5818                                             (propertize "\nTruncated..." 'face 'highlight))
   5819                                   docs)))))))
   5820       (when (and active-parameter? (not (seq-empty-p parameters?)))
   5821         (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?)))
   5822                               (seq-elt parameters? active-parameter?)))
   5823                      (selected-param-label (let ((label (lsp:parameter-information-label param)))
   5824                                              (if (stringp label) label (append label nil))))
   5825                      (start (if (stringp selected-param-label)
   5826                                 (s-index-of selected-param-label label)
   5827                               (cl-first selected-param-label)))
   5828                      (end (if (stringp selected-param-label)
   5829                               (+ start (length selected-param-label))
   5830                             (cl-second selected-param-label))))
   5831           (add-face-text-property start end 'eldoc-highlight-function-argument nil label)))
   5832       (concat prefix label method-docs))))
   5833 
   5834 (defun lsp-signature ()
   5835   "Display signature info (based on `textDocument/signatureHelp')"
   5836   (if (and lsp--signature-last-buffer
   5837            (not (equal (current-buffer) lsp--signature-last-buffer)))
   5838       (lsp-signature-stop)
   5839     (lsp-request-async "textDocument/signatureHelp"
   5840                        (lsp--text-document-position-params)
   5841                        #'lsp--handle-signature-update
   5842                        :cancel-token :signature)))
   5843 
   5844 
   5845 (defcustom lsp-overlay-document-color-char "■"
   5846   "Display the char represent the document color in overlay"
   5847   :type 'string
   5848   :group 'lsp-mode)
   5849 
   5850 ;; color presentation
   5851 (defun lsp--color-create-interactive-command (color range)
   5852   (lambda ()
   5853     (interactive)
   5854     (-let [(&ColorPresentation? :text-edit?
   5855                                 :additional-text-edits?)
   5856            (lsp--completing-read
   5857             "Select color presentation: "
   5858             (lsp-request
   5859              "textDocument/colorPresentation"
   5860              `( :textDocument ,(lsp--text-document-identifier)
   5861                 :color ,color
   5862                 :range ,range))
   5863             #'lsp:color-presentation-label
   5864             nil
   5865             t)]
   5866       (when text-edit?
   5867         (lsp--apply-text-edit text-edit?))
   5868       (when additional-text-edits?
   5869         (lsp--apply-text-edits additional-text-edits? 'color-presentation)))))
   5870 
   5871 (defun lsp--number->color (number)
   5872   (let ((result (format "%x"
   5873                         (round (* (or number 0) 255.0)))))
   5874     (if (= 1 (length result))
   5875         (concat "0" result)
   5876       result)))
   5877 
   5878 (defun lsp--document-color ()
   5879   "Document color handler."
   5880   (when (lsp-feature? "textDocument/documentColor")
   5881     (lsp-request-async
   5882      "textDocument/documentColor"
   5883      `(:textDocument ,(lsp--text-document-identifier))
   5884      (lambda (result)
   5885        (lsp--remove-overlays 'lsp-color)
   5886        (seq-do
   5887         (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue)
   5888                                      :range))
   5889           (-let* (((beg . end) (lsp--range-to-region range))
   5890                   (overlay (make-overlay beg end))
   5891                   (command (lsp--color-create-interactive-command color range)))
   5892             (overlay-put overlay 'lsp-color t)
   5893             (overlay-put overlay 'evaporate t)
   5894             (overlay-put overlay
   5895                          'before-string
   5896                          (propertize
   5897                           lsp-overlay-document-color-char
   5898                           'face `((:foreground ,(format
   5899                                                  "#%s%s%s"
   5900                                                  (lsp--number->color red)
   5901                                                  (lsp--number->color green)
   5902                                                  (lsp--number->color blue))))
   5903                           'action command
   5904                           'mouse-face 'lsp-lens-mouse-face
   5905                           'local-map (-doto (make-sparse-keymap)
   5906                                        (define-key [mouse-1] command))))))
   5907         result))
   5908      :mode 'unchanged
   5909      :cancel-token :document-color-token)))
   5910 
   5911 
   5912 
   5913 (defun lsp--action-trigger-parameter-hints (_command)
   5914   "Handler for editor.action.triggerParameterHints."
   5915   (when (member :on-server-request lsp-signature-auto-activate)
   5916     (lsp-signature-activate)))
   5917 
   5918 (defun lsp--action-trigger-suggest (_command)
   5919   "Handler for editor.action.triggerSuggest."
   5920   (cond
   5921    ((and (bound-and-true-p company-mode)
   5922          (fboundp 'company-auto-begin)
   5923          (fboundp 'company-post-command))
   5924     (run-at-time 0 nil
   5925                  (lambda ()
   5926                    (let ((this-command 'company-idle-begin)
   5927                          (company-minimum-prefix-length 0))
   5928                      (company-auto-begin)
   5929                      (company-post-command)))))
   5930    (t
   5931     (completion-at-point))))
   5932 
   5933 (defconst lsp--default-action-handlers
   5934   (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints)
   5935       ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest))
   5936   "Default action handlers.")
   5937 
   5938 (defun lsp--find-action-handler (command)
   5939   "Find action handler for particular COMMAND."
   5940   (or
   5941    (--some (-some->> it
   5942              (lsp--workspace-client)
   5943              (lsp--client-action-handlers)
   5944              (gethash command))
   5945            (lsp-workspaces))
   5946    (gethash command lsp--default-action-handlers)))
   5947 
   5948 (defun lsp--text-document-code-action-params (&optional kind)
   5949   "Code action params."
   5950   (list :textDocument (lsp--text-document-identifier)
   5951         :range (if (use-region-p)
   5952                    (lsp--region-to-range (region-beginning) (region-end))
   5953                  (lsp--region-to-range (point) (point)))
   5954         :context `( :diagnostics ,(lsp-cur-possition-diagnostics)
   5955                     ,@(when kind (list :only (vector kind))))))
   5956 
   5957 (defun lsp-code-actions-at-point (&optional kind)
   5958   "Retrieve the code actions for the active region or the current line.
   5959 It will filter by KIND if non nil."
   5960   (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind)))
   5961 
   5962 (defun lsp-execute-code-action-by-kind (command-kind)
   5963   "Execute code action by COMMAND-KIND."
   5964   (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind)
   5965                         (-filter (-lambda ((&CodeAction :kind?))
   5966                                    (and kind? (s-prefix? command-kind kind?))))
   5967                         lsp--select-action)))
   5968       (lsp-execute-code-action action)
   5969     (signal 'lsp-no-code-actions '(command-kind))))
   5970 
   5971 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point)
   5972 
   5973 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?))
   5974   "Parse and execute a code ACTION represented as a Command LSP type."
   5975   (let ((server-id (->> (lsp-workspaces)
   5976                         (cl-first)
   5977                         (or lsp--cur-workspace)
   5978                         (lsp--workspace-client)
   5979                         (lsp--client-server-id))))
   5980     (condition-case nil
   5981         (with-no-warnings
   5982           (lsp-execute-command server-id (intern command) arguments?))
   5983       (cl-no-applicable-method
   5984        (if-let ((action-handler (lsp--find-action-handler command)))
   5985            (funcall action-handler action)
   5986          (lsp-send-execute-command command arguments?))))))
   5987 
   5988 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?))
   5989   "Execute code action ACTION. For example, when text under the
   5990 caret has a suggestion to apply a fix from an lsp-server, calling
   5991 this function will do so.
   5992 If ACTION is not set it will be selected from `lsp-code-actions-at-point'.
   5993 Request codeAction/resolve for more info if server supports."
   5994   (interactive (list (lsp--select-action (lsp-code-actions-at-point))))
   5995   (if (and (lsp-feature? "codeAction/resolve")
   5996            (not command?)
   5997            (not edit?))
   5998       (lsp--execute-code-action (lsp-request "codeAction/resolve" action))
   5999     (lsp--execute-code-action action)))
   6000 
   6001 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?))
   6002   "Execute code action ACTION."
   6003   (when edit?
   6004     (lsp--apply-workspace-edit edit? 'code-action))
   6005 
   6006   (cond
   6007    ((stringp command?) (lsp--execute-command action))
   6008    ((lsp-command? command?) (lsp--execute-command command?))))
   6009 
   6010 (defvar lsp--formatting-indent-alist
   6011   ;; Taken from `dtrt-indent-mode'
   6012   '(
   6013     (ada-mode                   . ada-indent)                       ; Ada
   6014     (ada-ts-mode                . ada-ts-mode-indent-offset)
   6015     (c++-mode                   . c-basic-offset)                   ; C++
   6016     (c++-ts-mode                . c-ts-mode-indent-offset)
   6017     (c-mode                     . c-basic-offset)                   ; C
   6018     (c-ts-mode                  . c-ts-mode-indent-offset)
   6019     (cperl-mode                 . cperl-indent-level)               ; Perl
   6020     (crystal-mode               . crystal-indent-level)             ; Crystal (Ruby)
   6021     (csharp-mode                . c-basic-offset)                   ; C#
   6022     (csharp-tree-sitter-mode    . csharp-tree-sitter-indent-offset) ; C#
   6023     (csharp-ts-mode             . csharp-ts-mode-indent-offset)     ; C# (tree-sitter, Emacs29)
   6024     (css-mode                   . css-indent-offset)                ; CSS
   6025     (d-mode                     . c-basic-offset)                   ; D
   6026     (enh-ruby-mode              . enh-ruby-indent-level)            ; Ruby
   6027     (erlang-mode                . erlang-indent-level)              ; Erlang
   6028     (ess-mode                   . ess-indent-offset)                ; ESS (R)
   6029     (go-ts-mode                 . go-ts-mode-indent-offset)
   6030     (gpr-mode                   . gpr-indent-offset)                ; GNAT Project
   6031     (gpr-ts-mode                . gpr-ts-mode-indent-offset)
   6032     (hack-mode                  . hack-indent-offset)               ; Hack
   6033     (java-mode                  . c-basic-offset)                   ; Java
   6034     (java-ts-mode               . java-ts-mode-indent-offset)
   6035     (jde-mode                   . c-basic-offset)                   ; Java (JDE)
   6036     (js-mode                    . js-indent-level)                  ; JavaScript
   6037     (js-ts-mode                 . js-indent-level)
   6038     (js2-mode                   . js2-basic-offset)                 ; JavaScript-IDE
   6039     (js3-mode                   . js3-indent-level)                 ; JavaScript-IDE
   6040     (json-mode                  . js-indent-level)                  ; JSON
   6041     (json-ts-mode               . json-ts-mode-indent-offset)
   6042     (lua-mode                   . lua-indent-level)                 ; Lua
   6043     (lua-ts-mode                . lua-ts-indent-offset)
   6044     (nxml-mode                  . nxml-child-indent)                ; XML
   6045     (objc-mode                  . c-basic-offset)                   ; Objective C
   6046     (pascal-mode                . pascal-indent-level)              ; Pascal
   6047     (perl-mode                  . perl-indent-level)                ; Perl
   6048     (php-mode                   . c-basic-offset)                   ; PHP
   6049     (php-ts-mode                . php-ts-mode-indent-offset)        ; PHP
   6050     (powershell-mode            . powershell-indent)                ; PowerShell
   6051     (powershell-ts-mode         . powershell-ts-mode-indent-offset) ; PowerShell
   6052     (raku-mode                  . raku-indent-offset)               ; Perl6/Raku
   6053     (ruby-mode                  . ruby-indent-level)                ; Ruby
   6054     (rust-mode                  . rust-indent-offset)               ; Rust
   6055     (rust-ts-mode               . rust-ts-mode-indent-offset)
   6056     (rustic-mode                . rustic-indent-offset)             ; Rust
   6057     (scala-mode                 . scala-indent:step)                ; Scala
   6058     (sgml-mode                  . sgml-basic-offset)                ; SGML
   6059     (sh-mode                    . sh-basic-offset)                  ; Shell Script
   6060     (toml-ts-mode               . toml-ts-mode-indent-offset)
   6061     (typescript-mode            . typescript-indent-level)          ; Typescript
   6062     (typescript-ts-mode         . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29)
   6063     (yaml-mode                  . yaml-indent-offset)               ; YAML
   6064     (yang-mode                  . c-basic-offset)                   ; YANG (yang-mode)
   6065 
   6066     (default                    . standard-indent))                 ; default fallback
   6067   "A mapping from `major-mode' to its indent variable.")
   6068 
   6069 (defun lsp--get-indent-width (mode)
   6070   "Get indentation offset for MODE."
   6071   (or (alist-get mode lsp--formatting-indent-alist)
   6072       (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default))))
   6073 
   6074 (defun lsp--make-document-formatting-params ()
   6075   "Create document formatting params."
   6076   (lsp-make-document-formatting-params
   6077    :text-document (lsp--text-document-identifier)
   6078    :options (lsp-make-formatting-options
   6079              :tab-size (symbol-value (lsp--get-indent-width major-mode))
   6080              :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   6081              :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   6082              :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   6083              :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))))
   6084 
   6085 (defun lsp-format-buffer ()
   6086   "Ask the server to format this document."
   6087   (interactive "*")
   6088   (cond ((lsp-feature? "textDocument/formatting")
   6089          (let ((edits (lsp-request "textDocument/formatting"
   6090                                    (lsp--make-document-formatting-params))))
   6091            (if (seq-empty-p edits)
   6092                (lsp--info "No formatting changes provided")
   6093              (lsp--apply-text-edits edits 'format))))
   6094         ((lsp-feature? "textDocument/rangeFormatting")
   6095          (save-restriction
   6096            (widen)
   6097            (lsp-format-region (point-min) (point-max))))
   6098         (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider")))))
   6099 
   6100 (defun lsp-format-region (s e)
   6101   "Ask the server to format the region, or if none is selected, the current line."
   6102   (interactive "r")
   6103   (let ((edits (lsp-request
   6104                 "textDocument/rangeFormatting"
   6105                 (lsp--make-document-range-formatting-params s e))))
   6106     (if (seq-empty-p edits)
   6107         (lsp--info "No formatting changes provided")
   6108       (lsp--apply-text-edits edits 'format))))
   6109 
   6110 (defmacro lsp-make-interactive-code-action (func-name code-action-kind)
   6111   "Define an interactive function FUNC-NAME that attempts to
   6112 execute a CODE-ACTION-KIND action."
   6113   `(defun ,(intern (concat "lsp-" (symbol-name func-name))) ()
   6114      ,(format "Perform the %s code action, if available." code-action-kind)
   6115      (interactive)
   6116      ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to
   6117      ;; auto-execute here: the user has specified exactly what they want.
   6118      (let ((lsp-auto-execute-action t))
   6119        (condition-case nil
   6120            (lsp-execute-code-action-by-kind ,code-action-kind)
   6121          (lsp-no-code-actions
   6122           (when (called-interactively-p 'any)
   6123             (lsp--info ,(format "%s action not available" code-action-kind))))))))
   6124 
   6125 (lsp-make-interactive-code-action organize-imports "source.organizeImports")
   6126 
   6127 (defun lsp--make-document-range-formatting-params (start end)
   6128   "Make DocumentRangeFormattingParams for selected region."
   6129   (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params)
   6130                                                   (lsp--region-to-range start end)))
   6131 
   6132 (defconst lsp--highlight-kind-face
   6133   '((1 . lsp-face-highlight-textual)
   6134     (2 . lsp-face-highlight-read)
   6135     (3 . lsp-face-highlight-write)))
   6136 
   6137 (defun lsp--remove-overlays (name)
   6138   (save-restriction
   6139     (widen)
   6140     (remove-overlays (point-min) (point-max) name t)))
   6141 
   6142 (defun lsp-document-highlight ()
   6143   "Highlight all relevant references to the symbol under point."
   6144   (interactive)
   6145   (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights
   6146   (setq lsp--have-document-highlights nil
   6147         lsp--symbol-bounds-of-last-highlight-invocation nil)
   6148   (let ((lsp-enable-symbol-highlighting t))
   6149     (lsp--document-highlight)))
   6150 
   6151 (defun lsp--document-highlight-callback (highlights)
   6152   "Create a callback to process the reply of a
   6153 `textDocument/documentHighlight' message for the buffer BUF.
   6154 A reference is highlighted only if it is visible in a window."
   6155   (lsp--remove-overlays 'lsp-highlight)
   6156 
   6157   (let* ((wins-visible-pos (-map (lambda (win)
   6158                                    (cons (1- (line-number-at-pos (window-start win) t))
   6159                                          (1+ (line-number-at-pos (window-end win) t))))
   6160                                  (get-buffer-window-list nil nil 'visible))))
   6161     (setq lsp--have-document-highlights t)
   6162     (-map
   6163      (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line)
   6164                                                   :end (end &as &Position :line end-line))
   6165                                    :kind?))
   6166        (-map
   6167         (-lambda ((start-window . end-window))
   6168           ;; Make the overlay only if the reference is visible
   6169           (let ((start-point (lsp--position-to-point start))
   6170                 (end-point (lsp--position-to-point end)))
   6171             (when (and (> (1+ start-line) start-window)
   6172                        (< (1+ end-line) end-window)
   6173                        (not (and lsp-symbol-highlighting-skip-current
   6174                                  (<= start-point (point) end-point))))
   6175               (-doto (make-overlay start-point end-point)
   6176                 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face)))
   6177                 (overlay-put 'lsp-highlight t)))))
   6178         wins-visible-pos))
   6179      highlights)))
   6180 
   6181 (defcustom lsp-symbol-kinds
   6182   '((1 . "File")
   6183     (2 . "Module")
   6184     (3 . "Namespace")
   6185     (4 . "Package")
   6186     (5 . "Class")
   6187     (6 . "Method")
   6188     (7 . "Property")
   6189     (8 . "Field")
   6190     (9 . "Constructor")
   6191     (10 . "Enum")
   6192     (11 . "Interface")
   6193     (12 . "Function")
   6194     (13 . "Variable")
   6195     (14 . "Constant")
   6196     (15 . "String")
   6197     (16 . "Number")
   6198     (17 . "Boolean")
   6199     (18 . "Array")
   6200     (19 . "Object")
   6201     (20 . "Key")
   6202     (21 . "Null")
   6203     (22 . "Enum Member")
   6204     (23 . "Struct")
   6205     (24 . "Event")
   6206     (25 . "Operator")
   6207     (26 . "Type Parameter"))
   6208   "Alist mapping SymbolKinds to human-readable strings.
   6209 Various Symbol objects in the LSP protocol have an integral type,
   6210 specifying what they are. This alist maps such type integrals to
   6211 readable representations of them. See
   6212 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/',
   6213 namespace SymbolKind."
   6214   :group 'lsp-mode
   6215   :type '(alist :key-type integer :value-type string))
   6216 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds)
   6217 
   6218 (lsp-defun lsp--symbol-information-to-xref
   6219   ((&SymbolInformation :kind :name
   6220                        :location (&Location :uri :range (&Range :start
   6221                                                                 (&Position :line :character)))))
   6222   "Return a `xref-item' from SYMBOL information."
   6223   (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name)
   6224              (xref-make-file-location (lsp--uri-to-path uri)
   6225                                       line
   6226                                       character)))
   6227 
   6228 (defun lsp--get-document-symbols ()
   6229   "Get document symbols.
   6230 
   6231 If the buffer has not been modified since symbols were last
   6232 retrieved, simply return the latest result.
   6233 
   6234 Else, if the request was initiated by Imenu updating its menu-bar
   6235 entry, perform it asynchronously; i.e., give Imenu the latest
   6236 result and then force a refresh when a new one is available.
   6237 
   6238 Else (e.g., due to interactive use of `imenu' or `xref'),
   6239 perform the request synchronously."
   6240   (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick)
   6241       lsp--document-symbols
   6242     (let ((method "textDocument/documentSymbol")
   6243           (params `(:textDocument ,(lsp--text-document-identifier)))
   6244           (tick (buffer-chars-modified-tick)))
   6245       (if (not lsp--document-symbols-request-async)
   6246           (prog1
   6247               (setq lsp--document-symbols (lsp-request method params))
   6248             (setq lsp--document-symbols-tick tick))
   6249         (lsp-request-async method params
   6250                            (lambda (document-symbols)
   6251                              (setq lsp--document-symbols document-symbols
   6252                                    lsp--document-symbols-tick tick)
   6253                              (lsp--imenu-refresh))
   6254                            :mode 'alive
   6255                            :cancel-token :document-symbols)
   6256         lsp--document-symbols))))
   6257 
   6258 (advice-add 'imenu-update-menubar :around
   6259             (lambda (oldfun &rest r)
   6260               (let ((lsp--document-symbols-request-async t))
   6261                 (apply oldfun r))))
   6262 
   6263 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position)
   6264   "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION."
   6265   (-let (((symbol &as &DocumentSymbol? :children?)
   6266           (seq-find (-lambda ((&DocumentSymbol :range))
   6267                       (lsp-point-in-range? current-position range))
   6268                     document-symbols)))
   6269     (if children?
   6270         (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position))
   6271       (when symbol
   6272         (list symbol)))))
   6273 
   6274 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?))
   6275   "Convert a SymbolInformation to a DocumentInformation"
   6276   (lsp-make-document-symbol :name name
   6277                             :kind kind
   6278                             :range (lsp:location-range location)
   6279                             :children? nil
   6280                             :deprecated? deprecated?
   6281                             :selection-range (lsp:location-range location)
   6282                             :detail? container-name?))
   6283 
   6284 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position)
   6285   "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION."
   6286   (--> symbols-informations
   6287     (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range)))
   6288              (when (lsp-point-in-range? current-position range)
   6289                (lsp--symbol-information->document-symbol symbol)))
   6290            it)
   6291     (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position))
   6292                        (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position)))
   6293                (and (lsp--position-compare b-start-position a-start-position)
   6294                     (lsp--position-compare a-end-position b-end-position))))))
   6295 
   6296 (defun lsp--symbols->document-symbols-hierarchy (symbols)
   6297   "Convert SYMBOLS to symbols-hierarchy."
   6298   (when-let ((first-symbol (lsp-seq-first symbols)))
   6299     (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line)
   6300                                            :character (plist-get (lsp--cur-position) :character))))
   6301       (if (lsp-symbol-information? first-symbol)
   6302           (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position)
   6303         (lsp--document-symbols->document-symbols-hierarchy symbols cur-position)))))
   6304 
   6305 (defun lsp--xref-backend () 'xref-lsp)
   6306 
   6307 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp)))
   6308   (propertize (or (thing-at-point 'symbol) "")
   6309               'identifier-at-point t))
   6310 
   6311 (defun lsp--xref-elements-index (symbols path)
   6312   (-mapcat
   6313    (-lambda (sym)
   6314      (pcase-exhaustive sym
   6315        ((DocumentSymbol :name :children? :selection-range (Range :start))
   6316         (cons (cons (concat path name)
   6317                     (lsp--position-to-point start))
   6318               (lsp--xref-elements-index children? (concat path name " / "))))
   6319        ((SymbolInformation :name :location (Location :range (Range :start)))
   6320         (list (cons (concat path name)
   6321                     (lsp--position-to-point start))))))
   6322    symbols))
   6323 
   6324 (defvar-local lsp--symbols-cache nil)
   6325 
   6326 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp)))
   6327   (if (lsp--find-workspaces-for "textDocument/documentSymbol")
   6328       (progn
   6329         (setq lsp--symbols-cache (lsp--xref-elements-index
   6330                                   (lsp--get-document-symbols) nil))
   6331         lsp--symbols-cache)
   6332     (list (propertize (or (thing-at-point 'symbol) "")
   6333                       'identifier-at-point t))))
   6334 
   6335 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier)
   6336   (save-excursion
   6337     (unless (get-text-property 0 'identifier-at-point identifier)
   6338       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6339                               (user-error "Unable to find symbol %s in current document" identifier)))))
   6340     (lsp--locations-to-xref-items (lsp-request "textDocument/definition"
   6341                                                (lsp--text-document-position-params)))))
   6342 
   6343 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier)
   6344   (save-excursion
   6345     (unless (get-text-property 0 'identifier-at-point identifier)
   6346       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6347                               (user-error "Unable to find symbol %s" identifier)))))
   6348     (lsp--locations-to-xref-items (lsp-request "textDocument/references"
   6349                                                (lsp--make-reference-params nil lsp-references-exclude-definition)))))
   6350 
   6351 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern)
   6352   (seq-map #'lsp--symbol-information-to-xref
   6353            (lsp-request "workspace/symbol" `(:query ,pattern))))
   6354 
   6355 (defcustom lsp-rename-use-prepare t
   6356   "Whether `lsp-rename' should do a prepareRename first.
   6357 For some language servers, textDocument/prepareRename might be
   6358 too slow, in which case this variable may be set to nil.
   6359 `lsp-rename' will then use `thing-at-point' `symbol' to determine
   6360 the symbol to rename at point."
   6361   :group 'lsp-mode
   6362   :type 'boolean)
   6363 
   6364 (defun lsp--get-symbol-to-rename ()
   6365   "Get a symbol to rename and placeholder at point.
   6366 Returns a cons ((START . END) . PLACEHOLDER?), and nil if
   6367 renaming is generally supported but cannot be done at point.
   6368 START and END are the bounds of the identifiers being renamed,
   6369 while PLACEHOLDER?, is either nil or a string suggested by the
   6370 language server as the initial input of a new-name prompt."
   6371   (unless (lsp-feature? "textDocument/rename")
   6372     (error "The connected server(s) doesn't support renaming"))
   6373   (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename"))
   6374       (when-let ((response
   6375                   (lsp-request "textDocument/prepareRename"
   6376                                (lsp--text-document-position-params))))
   6377         (let* ((bounds (lsp--range-to-region
   6378                         (if (lsp-range? response)
   6379                             response
   6380                           (lsp:prepare-rename-result-range response))))
   6381                (placeholder
   6382                 (and (not (lsp-range? response))
   6383                      (lsp:prepare-rename-result-placeholder response))))
   6384           (cons bounds placeholder)))
   6385     (when-let ((bounds (bounds-of-thing-at-point 'symbol)))
   6386       (cons bounds nil))))
   6387 
   6388 (defface lsp-face-rename '((t :underline t))
   6389   "Face used to highlight the identifier being renamed.
   6390 Renaming can be done using `lsp-rename'."
   6391   :group 'lsp-mode)
   6392 
   6393 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face))
   6394   "Face used to display the rename placeholder in.
   6395 When calling `lsp-rename' interactively, this will be the face of
   6396 the new name."
   6397   :group 'lsp-mode)
   6398 
   6399 (defvar lsp-rename-history '()
   6400   "History for `lsp--read-rename'.")
   6401 
   6402 (defun lsp--read-rename (at-point)
   6403   "Read a new name for a `lsp-rename' at `point' from the user.
   6404 AT-POINT shall be a structure as returned by
   6405 `lsp--get-symbol-to-rename'.
   6406 
   6407 Returns a string, which should be the new name for the identifier
   6408 at point. If renaming cannot be done at point (as determined from
   6409 AT-POINT), throw a `user-error'.
   6410 
   6411 This function is for use in `lsp-rename' only, and shall not be
   6412 relied upon."
   6413   (unless at-point
   6414     (user-error "`lsp-rename' is invalid here"))
   6415   (-let* ((((start . end) . placeholder?) at-point)
   6416           ;; Do the `buffer-substring' first to not include `lsp-face-rename'
   6417           (rename-me (buffer-substring start end))
   6418           (placeholder (or placeholder? rename-me))
   6419           (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face))
   6420 
   6421           overlay)
   6422     ;; We need unwind protect, as the user might cancel here, causing the
   6423     ;; overlay to linger.
   6424     (unwind-protect
   6425         (progn
   6426           (setq overlay (make-overlay start end))
   6427           (overlay-put overlay 'face 'lsp-face-rename)
   6428 
   6429           (read-string (format "Rename %s to: " rename-me) placeholder
   6430                        'lsp-rename-history))
   6431       (and overlay (delete-overlay overlay)))))
   6432 
   6433 (defun lsp-rename (newname)
   6434   "Rename the symbol (and all references to it) under point to NEWNAME."
   6435   (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename))))
   6436   (when-let ((edits (lsp-request "textDocument/rename"
   6437                                  `( :textDocument ,(lsp--text-document-identifier)
   6438                                     :position ,(lsp--cur-position)
   6439                                     :newName ,newname))))
   6440     (lsp--apply-workspace-edit edits 'rename)))
   6441 
   6442 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?)
   6443   "Advice around function `rename-file'.
   6444 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?.
   6445 
   6446 This advice sends workspace/willRenameFiles before renaming file
   6447 to check if server wants to apply any workspaceEdits after renamed."
   6448   (if (and lsp-apply-edits-after-file-operations
   6449            (lsp--send-will-rename-files-p old-name))
   6450       (let ((params (lsp-make-rename-files-params
   6451                      :files (vector (lsp-make-file-rename
   6452                                      :oldUri (lsp--path-to-uri old-name)
   6453                                      :newUri (lsp--path-to-uri new-name))))))
   6454         (when-let ((edits (lsp-request "workspace/willRenameFiles" params)))
   6455           (lsp--apply-workspace-edit edits 'rename-file)
   6456           (funcall old-func old-name new-name ok-if-already-exists?)
   6457           (when (lsp--send-did-rename-files-p)
   6458             (lsp-notify "workspace/didRenameFiles" params))))
   6459     (funcall old-func old-name new-name ok-if-already-exists?)))
   6460 
   6461 (advice-add 'rename-file :around #'lsp--on-rename-file)
   6462 
   6463 (defcustom lsp-xref-force-references nil
   6464   "If non-nil threat everything as references(e. g. jump if only one item.)"
   6465   :group 'lsp-mode
   6466   :type 'boolean)
   6467 
   6468 (defun lsp-show-xrefs (xrefs display-action references?)
   6469   (unless (region-active-p) (push-mark nil t))
   6470   (if (boundp 'xref-show-definitions-function)
   6471       (with-no-warnings
   6472         (xref-push-marker-stack)
   6473         (funcall (if (and references? (not lsp-xref-force-references))
   6474                      xref-show-xrefs-function
   6475                    xref-show-definitions-function)
   6476                  (-const xrefs)
   6477                  `((window . ,(selected-window))
   6478                    (display-action . ,display-action)
   6479                    ,(if (and references? (not lsp-xref-force-references))
   6480                         `(auto-jump . ,xref-auto-jump-to-first-xref)
   6481                       `(auto-jump . ,xref-auto-jump-to-first-definition)))))
   6482     (xref--show-xrefs xrefs display-action)))
   6483 
   6484 (cl-defmethod seq-empty-p ((ht hash-table))
   6485   "Function `seq-empty-p' for hash-table."
   6486   (hash-table-empty-p ht))
   6487 
   6488 (cl-defun lsp-find-locations (method &optional extra &key display-action references?)
   6489   "Send request named METHOD and get cross references of the symbol under point.
   6490 EXTRA is a plist of extra parameters.
   6491 REFERENCES? t when METHOD returns references."
   6492   (let ((loc (lsp-request method
   6493                           (append (lsp--text-document-position-params) extra))))
   6494     (if (seq-empty-p loc)
   6495         (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) ""))
   6496       (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?))))
   6497 
   6498 (cl-defun lsp-find-declaration (&key display-action)
   6499   "Find declarations of the symbol under point."
   6500   (interactive)
   6501   (lsp-find-locations "textDocument/declaration" nil :display-action display-action))
   6502 
   6503 (cl-defun lsp-find-definition (&key display-action)
   6504   "Find definitions of the symbol under point."
   6505   (interactive)
   6506   (lsp-find-locations "textDocument/definition" nil :display-action display-action))
   6507 
   6508 (defun lsp-find-definition-mouse (click)
   6509   "Click to start `lsp-find-definition' at clicked point."
   6510   (interactive "e")
   6511   (let* ((ec (event-start click))
   6512          (p1 (posn-point ec))
   6513          (w1 (posn-window ec)))
   6514     (select-window w1)
   6515     (goto-char p1)
   6516     (lsp-find-definition)))
   6517 
   6518 (cl-defun lsp-find-implementation (&key display-action)
   6519   "Find implementations of the symbol under point."
   6520   (interactive)
   6521   (lsp-find-locations "textDocument/implementation"
   6522                       nil
   6523                       :display-action display-action
   6524                       :references? t))
   6525 
   6526 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action)
   6527   "Find references of the symbol under point."
   6528   (interactive "P")
   6529   (lsp-find-locations "textDocument/references"
   6530                       (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition)))))
   6531                       :display-action display-action
   6532                       :references? t))
   6533 
   6534 (cl-defun lsp-find-type-definition (&key display-action)
   6535   "Find type definitions of the symbol under point."
   6536   (interactive)
   6537   (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action))
   6538 
   6539 (defalias 'lsp-find-custom #'lsp-find-locations)
   6540 (defalias 'lsp-goto-implementation #'lsp-find-implementation)
   6541 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition)
   6542 
   6543 (with-eval-after-load 'evil
   6544   (evil-set-command-property 'lsp-find-definition :jump t)
   6545   (evil-set-command-property 'lsp-find-implementation :jump t)
   6546   (evil-set-command-property 'lsp-find-references :jump t)
   6547   (evil-set-command-property 'lsp-find-type-definition :jump t))
   6548 
   6549 (defun lsp--workspace-method-supported? (check-command method capability workspace)
   6550   (with-lsp-workspace workspace
   6551     (if check-command
   6552         (funcall check-command workspace)
   6553       (or
   6554        (when capability (lsp--capability capability))
   6555        (lsp--registered-capability method)
   6556        (and (not capability) (not check-command))))))
   6557 
   6558 (defun lsp-disable-method-for-server (method server-id)
   6559   "Disable METHOD for SERVER-ID."
   6560   (cl-callf
   6561       (lambda (reqs)
   6562         (-let (((&plist :check-command :capability) reqs))
   6563           (list :check-command
   6564                 (lambda (workspace)
   6565                   (unless (-> workspace
   6566                               lsp--workspace-client
   6567                               lsp--client-server-id
   6568                               (eq server-id))
   6569                     (lsp--workspace-method-supported? check-command
   6570                                                       method
   6571                                                       capability
   6572                                                       workspace))))))
   6573       (alist-get method lsp-method-requirements nil nil 'string=)))
   6574 
   6575 (defun lsp--find-workspaces-for (msg-or-method)
   6576   "Find all workspaces in the current project that can handle MSG."
   6577   (let ((method (if (stringp msg-or-method)
   6578                     msg-or-method
   6579                   (plist-get msg-or-method :method))))
   6580     (-if-let (reqs (cdr (assoc method lsp-method-requirements)))
   6581         (-let (((&plist :capability :check-command) reqs))
   6582           (-filter
   6583            (-partial #'lsp--workspace-method-supported?
   6584                      check-command method capability)
   6585            (lsp-workspaces)))
   6586       (lsp-workspaces))))
   6587 
   6588 (defun lsp-can-execute-command? (command-name)
   6589   "Returns non-nil if current language server(s) can execute COMMAND-NAME.
   6590 The command is executed via `workspace/executeCommand'"
   6591   (cl-position
   6592    command-name
   6593    (lsp:execute-command-options-commands
   6594     (lsp:server-capabilities-execute-command-provider?
   6595      (lsp--server-capabilities)))
   6596    :test #'equal))
   6597 
   6598 (defalias 'lsp-feature? 'lsp--find-workspaces-for)
   6599 
   6600 (cl-defmethod lsp-execute-command (_server _command _arguments)
   6601   "Dispatch COMMAND execution."
   6602   (signal 'cl-no-applicable-method nil))
   6603 
   6604 (defun lsp-workspace-command-execute (command &optional args)
   6605   "Execute workspace COMMAND with ARGS."
   6606   (condition-case-unless-debug err
   6607       (let ((params (if args
   6608                         (list :command command :arguments args)
   6609                       (list :command command))))
   6610         (lsp-request "workspace/executeCommand" params))
   6611     (error
   6612      (error "`workspace/executeCommand' with `%s' failed.\n\n%S"
   6613             command err))))
   6614 
   6615 (defun lsp-send-execute-command (command &optional args)
   6616   "Create and send a `workspace/executeCommand' message having command COMMAND
   6617 and optional ARGS."
   6618   (lsp-workspace-command-execute command args))
   6619 
   6620 (defalias 'lsp-point-to-position #'lsp--point-to-position)
   6621 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier)
   6622 (defalias 'lsp--send-execute-command #'lsp-send-execute-command)
   6623 (defalias 'lsp-on-open #'lsp--text-document-did-open)
   6624 (defalias 'lsp-on-save #'lsp--text-document-did-save)
   6625 
   6626 (defun lsp--set-configuration (settings)
   6627   "Set the SETTINGS for the lsp server."
   6628   (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings)))
   6629 
   6630 (defun lsp-current-buffer ()
   6631   (or lsp--virtual-buffer
   6632       (current-buffer)))
   6633 
   6634 (defun lsp-buffer-live-p (buffer-id)
   6635   (if-let ((buffer-live (plist-get buffer-id :buffer-live?)))
   6636       (funcall buffer-live buffer-id)
   6637     (buffer-live-p buffer-id)))
   6638 
   6639 (defun lsp--on-set-visited-file-name (old-func &rest args)
   6640   "Advice around function `set-visited-file-name'.
   6641 
   6642 This advice sends textDocument/didClose for the old file and
   6643 textDocument/didOpen for the new file."
   6644   (when lsp--cur-workspace
   6645     (lsp--text-document-did-close t))
   6646   (prog1 (apply old-func args)
   6647     (when lsp--cur-workspace
   6648       (lsp--text-document-did-open))))
   6649 
   6650 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name)
   6651 
   6652 (defvar lsp--flushing-delayed-changes nil)
   6653 
   6654 (defun lsp--send-no-wait (message proc)
   6655   "Send MESSAGE to PROC without waiting for further output."
   6656 
   6657   (unless lsp--flushing-delayed-changes
   6658     (let ((lsp--flushing-delayed-changes t))
   6659       (lsp--flush-delayed-changes)))
   6660   (lsp-process-send proc message))
   6661 
   6662 (define-error 'lsp-parse-error
   6663   "Error parsing message from language server" 'lsp-error)
   6664 (define-error 'lsp-unknown-message-type
   6665   "Unknown message type" '(lsp-error lsp-parse-error))
   6666 (define-error 'lsp-unknown-json-rpc-version
   6667   "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error))
   6668 (define-error 'lsp-no-content-length
   6669   "Content-Length header missing in message" '(lsp-error lsp-parse-error))
   6670 (define-error 'lsp-invalid-header-name
   6671   "Invalid header name" '(lsp-error lsp-parse-error))
   6672 
   6673 ;;  id  method
   6674 ;;   x    x     request
   6675 ;;   x    .     response
   6676 ;;   .    x     notification
   6677 (defun lsp--get-message-type (json-data)
   6678   "Get the message type from JSON-DATA."
   6679   (if (lsp:json-message-id? json-data)
   6680       (if (lsp:json-message-error? json-data)
   6681           'response-error
   6682         (if (lsp:json-message-method? json-data)
   6683             'request
   6684           'response))
   6685     'notification))
   6686 
   6687 (defconst lsp--default-notification-handlers
   6688   (ht ("window/showMessage" #'lsp--window-show-message)
   6689       ("window/logMessage" #'lsp--window-log-message)
   6690       ("window/showInputBox" #'lsp--window-show-input-box)
   6691       ("window/showQuickPick" #'lsp--window-show-quick-pick)
   6692       ("textDocument/publishDiagnostics" #'lsp--on-diagnostics)
   6693       ("textDocument/diagnosticsEnd" #'ignore)
   6694       ("textDocument/diagnosticsBegin" #'ignore)
   6695       ("telemetry/event" #'ignore)
   6696       ("$/progress" (lambda (workspace params)
   6697                       (funcall lsp-progress-function workspace params)))))
   6698 
   6699 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method))
   6700   "Call the appropriate handler for NOTIFICATION."
   6701   (-let ((client (lsp--workspace-client workspace)))
   6702     (when (lsp--log-io-p method)
   6703       (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif)
   6704                           lsp--cur-workspace))
   6705     (if-let ((handler (or (gethash method (lsp--client-notification-handlers client))
   6706                           (gethash method lsp--default-notification-handlers))))
   6707         (funcall handler workspace params)
   6708       (when (and method (not (string-prefix-p "$" method)))
   6709         (lsp-warn "Unknown notification: %s" method)))))
   6710 
   6711 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items))
   6712   "Get section configuration.
   6713 PARAMS are the `workspace/configuration' request params"
   6714   (->> items
   6715        (-map (-lambda ((&ConfigurationItem :section?))
   6716                (-let* ((path-parts (split-string section? "\\."))
   6717                        (path-without-last (s-join "." (-slice path-parts 0 -1)))
   6718                        (path-parts-len (length path-parts)))
   6719                  (cond
   6720                   ((<= path-parts-len 1)
   6721                    (ht-get (lsp-configuration-section section?)
   6722                            (car-safe path-parts)
   6723                            (ht-create)))
   6724                   ((> path-parts-len 1)
   6725                    (when-let ((section (lsp-configuration-section path-without-last))
   6726                               (keys path-parts))
   6727                      (while (and keys section)
   6728                        (setf section (ht-get section (pop keys))))
   6729                      section))))))
   6730        (apply #'vector)))
   6731 
   6732 (defun lsp--ms-since (timestamp)
   6733   "Integer number of milliseconds since TIMESTAMP.  Fractions discarded."
   6734   (floor (* 1000 (float-time (time-since timestamp)))))
   6735 
   6736 (defun lsp--send-request-response (workspace recv-time request response)
   6737   "Send the RESPONSE for REQUEST in WORKSPACE and log if needed."
   6738   (-let* (((&JSONResponse :params :method :id) request)
   6739           (process (lsp--workspace-proc workspace))
   6740           (response (lsp--make-response id response))
   6741           (req-entry (and lsp-log-io
   6742                           (lsp--make-log-entry method id params 'incoming-req)))
   6743           (resp-entry (and lsp-log-io
   6744                            (lsp--make-log-entry method id response 'outgoing-resp
   6745                                                 (lsp--ms-since recv-time)))))
   6746     ;; Send response to the server.
   6747     (when (lsp--log-io-p method)
   6748       (lsp--log-entry-new req-entry workspace)
   6749       (lsp--log-entry-new resp-entry workspace))
   6750     (lsp--send-no-wait response process)))
   6751 
   6752 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method))
   6753   "Call the appropriate handler for REQUEST, and send the return value to the
   6754 server. WORKSPACE is the active workspace."
   6755   (-let* ((recv-time (current-time))
   6756           (client (lsp--workspace-client workspace))
   6757           (buffers (lsp--workspace-buffers workspace))
   6758           handler
   6759           (response (cond
   6760                      ((setq handler (gethash method (lsp--client-request-handlers client) nil))
   6761                       (funcall handler workspace params))
   6762                      ((setq handler (gethash method (lsp--client-async-request-handlers client) nil))
   6763                       (funcall handler workspace params
   6764                                (-partial #'lsp--send-request-response
   6765                                          workspace recv-time request))
   6766                       'delay-response)
   6767                      ((equal method "client/registerCapability")
   6768                       (mapc #'lsp--server-register-capability
   6769                             (lsp:registration-params-registrations params))
   6770                       (mapc (lambda (buf)
   6771                               (when (lsp-buffer-live-p buf)
   6772                                 (lsp-with-current-buffer buf
   6773                                   (lsp-unconfig-buffer)
   6774                                   (lsp-configure-buffer))))
   6775                             buffers)
   6776                       nil)
   6777                      ((equal method "window/showMessageRequest")
   6778                       (let ((choice (lsp--window-log-message-request params)))
   6779                         `(:title ,choice)))
   6780                      ((equal method "window/showDocument")
   6781                       (let ((success? (lsp--window-show-document params)))
   6782                         (lsp-make-show-document-result :success (or success?
   6783                                                                     :json-false))))
   6784                      ((equal method "client/unregisterCapability")
   6785                       (mapc #'lsp--server-unregister-capability
   6786                             (lsp:unregistration-params-unregisterations params))
   6787                       (mapc (lambda (buf)
   6788                               (when (lsp-buffer-live-p buf)
   6789                                 (lsp-with-current-buffer buf
   6790                                   (lsp-unconfig-buffer)
   6791                                   (lsp-configure-buffer))))
   6792                             buffers)
   6793                       nil)
   6794                      ((equal method "workspace/applyEdit")
   6795                       (list :applied (condition-case err
   6796                                          (prog1 t
   6797                                            (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested))
   6798                                        (error
   6799                                         (lsp--error "Failed to apply edits with message %s"
   6800                                                     (error-message-string err))
   6801                                         :json-false))))
   6802                      ((equal method "workspace/configuration")
   6803                       (with-lsp-workspace workspace
   6804                         (if-let ((buf (car buffers)))
   6805                             (lsp-with-current-buffer buf
   6806                               (lsp--build-workspace-configuration-response params))
   6807                           (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace)
   6808                             (lsp--build-workspace-configuration-response params)))))
   6809                      ((equal method "workspace/workspaceFolders")
   6810                       (let ((folders (or (-> workspace
   6811                                              (lsp--workspace-client)
   6812                                              (lsp--client-server-id)
   6813                                              (gethash (lsp-session-server-id->folders (lsp-session))))
   6814                                          (lsp-session-folders (lsp-session)))))
   6815                         (->> folders
   6816                              (-distinct)
   6817                              (-map (lambda (folder)
   6818                                      (list :uri (lsp--path-to-uri folder))))
   6819                              (apply #'vector))))
   6820                      ((equal method "window/workDoneProgress/create")
   6821                       nil ;; no specific reply, no processing required
   6822                       )
   6823                      ((equal method "workspace/semanticTokens/refresh")
   6824                       (when (and lsp-semantic-tokens-enable
   6825                                  (fboundp 'lsp--semantic-tokens-on-refresh))
   6826                         (lsp--semantic-tokens-on-refresh workspace))
   6827                       nil)
   6828                      ((equal method "workspace/codeLens/refresh")
   6829                       (when (and lsp-lens-enable
   6830                                  (fboundp 'lsp--lens-on-refresh))
   6831                         (lsp--lens-on-refresh workspace))
   6832                       nil)
   6833                      (t (lsp-warn "Unknown request method: %s" method) nil))))
   6834     ;; Send response to the server.
   6835     (unless (eq response 'delay-response)
   6836       (lsp--send-request-response workspace recv-time request response))))
   6837 
   6838 (lsp-defun lsp--error-string ((&JSONError :message :code))
   6839   "Format ERR as a user friendly string."
   6840   (format "Error from the Language Server: %s (%s)"
   6841           message
   6842           (or (car (alist-get code lsp--errors)) "Unknown error")))
   6843 
   6844 (defun lsp--get-body-length (headers)
   6845   (let ((content-length (cdr (assoc "Content-Length" headers))))
   6846     (if content-length
   6847         (string-to-number content-length)
   6848 
   6849       ;; This usually means either the server or our parser is
   6850       ;; screwed up with a previous Content-Length
   6851       (error "No Content-Length header"))))
   6852 
   6853 (defun lsp--parse-header (s)
   6854   "Parse string S as a LSP (KEY . VAL) header."
   6855   (let ((pos (string-match "\:" s))
   6856         key val)
   6857     (unless pos
   6858       (signal 'lsp-invalid-header-name (list s)))
   6859     (setq key (substring s 0 pos)
   6860           val (s-trim-left (substring s (+ 1 pos))))
   6861     (when (equal key "Content-Length")
   6862       (cl-assert (cl-loop for c across val
   6863                           when (or (> c ?9) (< c ?0)) return nil
   6864                           finally return t)
   6865                  nil (format "Invalid Content-Length value: %s" val)))
   6866     (cons key val)))
   6867 
   6868 (defmacro lsp--read-json (str)
   6869   "Read json string STR."
   6870   (if (progn
   6871         (require 'json)
   6872         (fboundp 'json-parse-string))
   6873       `(json-parse-string ,str
   6874                           :object-type (if lsp-use-plists
   6875                                            'plist
   6876                                          'hash-table)
   6877                           :null-object nil
   6878                           :false-object nil)
   6879     `(let ((json-array-type 'vector)
   6880            (json-object-type (if lsp-use-plists
   6881                                  'plist
   6882                                'hash-table))
   6883            (json-false nil))
   6884        (json-read-from-string ,str))))
   6885 
   6886 (defmacro lsp-json-read-buffer ()
   6887   "Read json from the current buffer."
   6888   (if (progn
   6889         (require 'json)
   6890         (fboundp 'json-parse-buffer))
   6891       `(json-parse-buffer :object-type (if lsp-use-plists
   6892                                            'plist
   6893                                          'hash-table)
   6894                           :null-object nil
   6895                           :false-object nil)
   6896     `(let ((json-array-type 'vector)
   6897            (json-object-type (if lsp-use-plists
   6898                                  'plist
   6899                                'hash-table))
   6900            (json-false nil))
   6901        (json-read))))
   6902 
   6903 (defun lsp--read-json-file (file-path)
   6904   "Read json file."
   6905   (-> file-path
   6906     (f-read-text)
   6907     (lsp--read-json)))
   6908 
   6909 (defun lsp--parser-on-message (json-data workspace)
   6910   "Called when the parser P read a complete MSG from the server."
   6911   (with-demoted-errors "Error processing message %S."
   6912     (with-lsp-workspace workspace
   6913       (let* ((client (lsp--workspace-client workspace))
   6914              (id (--when-let (lsp:json-response-id json-data)
   6915                    (if (stringp it) (string-to-number it) it)))
   6916              (data (lsp:json-response-result json-data)))
   6917         (pcase (lsp--get-message-type json-data)
   6918           ('response
   6919            (cl-assert id)
   6920            (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))]
   6921              (when (lsp--log-io-p method)
   6922                (lsp--log-entry-new
   6923                 (lsp--make-log-entry method id data 'incoming-resp
   6924                                      (lsp--ms-since before-send))
   6925                 workspace))
   6926              (when callback
   6927                (remhash id (lsp--client-response-handlers client))
   6928                (funcall callback (lsp:json-response-result json-data)))))
   6929           ('response-error
   6930            (cl-assert id)
   6931            (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))]
   6932              (when (lsp--log-io-p method)
   6933                (lsp--log-entry-new
   6934                 (lsp--make-log-entry method id (lsp:json-response-error-error json-data)
   6935                                      'incoming-resp (lsp--ms-since before-send))
   6936                 workspace))
   6937              (when callback
   6938                (remhash id (lsp--client-response-handlers client))
   6939                (funcall callback (lsp:json-response-error-error json-data)))))
   6940           ('notification
   6941            (lsp--on-notification workspace json-data))
   6942           ('request (lsp--on-request workspace json-data)))))))
   6943 
   6944 (defun lsp--create-filter-function (workspace)
   6945   "Make filter for the workspace."
   6946   (let ((body-received 0)
   6947         leftovers body-length body chunk)
   6948     (lambda (_proc input)
   6949       (setf chunk (if (s-blank? leftovers)
   6950                       input
   6951                     (concat leftovers input)))
   6952 
   6953       (let (messages)
   6954         (while (not (s-blank? chunk))
   6955           (if (not body-length)
   6956               ;; Read headers
   6957               (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk)))
   6958                   ;; We've got all the headers, handle them all at once:
   6959                   (setf body-length (lsp--get-body-length
   6960                                      (mapcar #'lsp--parse-header
   6961                                              (split-string
   6962                                               (substring-no-properties chunk
   6963                                                                        (or (string-match-p "Content-Length" chunk)
   6964                                                                            (error "Unable to find Content-Length header."))
   6965                                                                        body-sep-pos)
   6966                                               "\r\n")))
   6967                         body-received 0
   6968                         leftovers nil
   6969                         chunk (substring-no-properties chunk (+ body-sep-pos 4)))
   6970 
   6971                 ;; Haven't found the end of the headers yet. Save everything
   6972                 ;; for when the next chunk arrives and await further input.
   6973                 (setf leftovers chunk
   6974                       chunk nil))
   6975             (let* ((chunk-length (string-bytes chunk))
   6976                    (left-to-receive (- body-length body-received))
   6977                    (this-body (if (< left-to-receive chunk-length)
   6978                                   (prog1 (substring-no-properties chunk 0 left-to-receive)
   6979                                     (setf chunk (substring-no-properties chunk left-to-receive)))
   6980                                 (prog1 chunk
   6981                                   (setf chunk nil))))
   6982                    (body-bytes (string-bytes this-body)))
   6983               (push this-body body)
   6984               (setf body-received (+ body-received body-bytes))
   6985               (when (>= chunk-length left-to-receive)
   6986                 (condition-case err
   6987                     (with-temp-buffer
   6988                       (apply #'insert
   6989                              (nreverse
   6990                               (prog1 body
   6991                                 (setf leftovers nil
   6992                                       body-length nil
   6993                                       body-received nil
   6994                                       body nil))))
   6995                       (decode-coding-region (point-min)
   6996                                             (point-max)
   6997                                             'utf-8)
   6998                       (goto-char (point-min))
   6999                       (push (lsp-json-read-buffer) messages))
   7000 
   7001                   (error
   7002                    (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s"
   7003                              (concat leftovers input)
   7004                              err)))))))
   7005         (mapc (lambda (msg)
   7006                 (lsp--parser-on-message msg workspace))
   7007               (nreverse messages))))))
   7008 
   7009 (defvar-local lsp--line-col-to-point-hash-table nil
   7010   "Hash table with keys (line . col) and values that are either point positions
   7011 or markers.")
   7012 
   7013 (defcustom lsp-imenu-detailed-outline t
   7014   "Whether `lsp-imenu' should include signatures.
   7015 This will be ignored if the server doesn't provide the necessary
   7016 information, for example if it doesn't support DocumentSymbols."
   7017   :group 'lsp-imenu
   7018   :type 'boolean)
   7019 
   7020 (defcustom lsp-imenu-hide-parent-details t
   7021   "Whether `lsp-imenu' should hide signatures of parent nodes."
   7022   :group 'lsp-imenu
   7023   :type 'boolean)
   7024 
   7025 (defface lsp-details-face '((t :height 0.8 :inherit shadow))
   7026   "Used to display additional information throughout `lsp'.
   7027 Things like line numbers, signatures, ... are considered
   7028 additional information. Often, additional faces are defined that
   7029 inherit from this face by default, like `lsp-signature-face', and
   7030 they may be customized for finer control."
   7031   :group 'lsp-mode)
   7032 
   7033 (defface lsp-signature-face '((t :inherit lsp-details-face))
   7034   "Used to display signatures in `imenu', ...."
   7035   :group 'lsp-mode)
   7036 
   7037 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?)
   7038                               show-detail?)
   7039   "Render INPUT0, an `&DocumentSymbol', to a string.
   7040 If SHOW-DETAIL? is set, make use of its `:detail?' field (often
   7041 the signature)."
   7042   (let ((detail (and show-detail? (s-present? detail?)
   7043                      (propertize (concat " " (s-trim-left detail?))
   7044                                  'face 'lsp-signature-face)))
   7045         (name (if deprecated?
   7046                   (propertize name 'face 'lsp-face-semhl-deprecated) name)))
   7047     (concat name detail)))
   7048 
   7049 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?)
   7050                                           separator)
   7051   "Render a piece of SymbolInformation.
   7052 Handle :deprecated?. If SEPARATOR is non-nil, the
   7053 symbol's (optional) parent, SEPARATOR and the symbol itself are
   7054 concatenated."
   7055   (when (and separator container-name? (not (string-empty-p container-name?)))
   7056     (setq name (concat name separator container-name?)))
   7057   (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name))
   7058 
   7059 (defun lsp--symbol-to-imenu-elem (sym)
   7060   "Convert SYM to imenu element.
   7061 
   7062 SYM is a SymbolInformation message.
   7063 
   7064 Return a cons cell (full-name . start-point)."
   7065   (let ((start-point (ht-get lsp--line-col-to-point-hash-table
   7066                              (lsp--get-line-and-col sym))))
   7067     (cons (lsp-render-symbol-information
   7068            sym (and lsp-imenu-show-container-name
   7069                     lsp-imenu-container-name-separator))
   7070           start-point)))
   7071 
   7072 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?))
   7073   "Convert SYM to hierarchical imenu elements.
   7074 
   7075 SYM is a DocumentSymbol message.
   7076 
   7077 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if
   7078 SYM doesn't have any children. Otherwise return a cons cell with
   7079 an alist
   7080 
   7081   (\"symbol-name\" . ((\"(symbol-kind)\" . start-point)
   7082                     cons-cells-from-children))"
   7083   (let ((filtered-children (lsp--imenu-filter-symbols children?))
   7084         (signature (lsp-render-symbol sym lsp-imenu-detailed-outline)))
   7085     (if (seq-empty-p filtered-children)
   7086         (cons signature
   7087               (ht-get lsp--line-col-to-point-hash-table
   7088                       (lsp--get-line-and-col sym)))
   7089       (cons signature
   7090             (lsp--imenu-create-hierarchical-index filtered-children)))))
   7091 
   7092 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind))
   7093   "Determine if SYM is for the current document and is to be shown."
   7094   ;; It's a SymbolInformation or DocumentSymbol, which is always in the
   7095   ;; current buffer file.
   7096   (and lsp-imenu-index-symbol-kinds
   7097        (numberp kind)
   7098        (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup))
   7099                                kind
   7100                              0)))
   7101          (not (memql (aref lsp/symbol-kind-lookup clamped-kind)
   7102                      lsp-imenu-index-symbol-kinds)))))
   7103 
   7104 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind))
   7105   "The string name of the kind of SYM."
   7106   (alist-get kind lsp-symbol-kinds "Other"))
   7107 
   7108 (defun lsp--get-line-and-col (sym)
   7109   "Obtain the line and column corresponding to SYM."
   7110   (-let* ((location (lsp:symbol-information-location sym))
   7111           (name-range (or (and location (lsp:location-range location))
   7112                           (lsp:document-symbol-selection-range sym)))
   7113           ((&Range :start (&Position :line :character)) name-range))
   7114     (cons line character)))
   7115 
   7116 (defun lsp--collect-lines-and-cols (symbols)
   7117   "Return a sorted list ((line . col) ...) of the locations of SYMBOLS."
   7118   (let ((stack (mapcar 'identity symbols))
   7119         line-col-list)
   7120     (while stack
   7121       (let ((sym (pop stack)))
   7122         (push (lsp--get-line-and-col sym) line-col-list)
   7123         (unless (seq-empty-p (lsp:document-symbol-children? sym))
   7124           (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack)))))
   7125     (-sort #'lsp--line-col-comparator line-col-list)))
   7126 
   7127 (defun lsp--convert-line-col-to-points-batch (line-col-list)
   7128   "Convert a sorted list of positions from line-column
   7129 representation to point representation."
   7130   (let ((line-col-to-point-map (ht-create))
   7131         (inhibit-field-text-motion t)
   7132         (curr-line 0))
   7133     (lsp-save-restriction-and-excursion
   7134       (goto-char (point-min))
   7135       (cl-loop for (line . col) in line-col-list do
   7136                (forward-line (- line curr-line))
   7137                (setq curr-line line)
   7138                (let ((line-end (line-end-position)))
   7139                  (if (or (not col) (> col (- line-end (point))))
   7140                      (goto-char line-end)
   7141                    (forward-char col)))
   7142                (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers
   7143                                                                   (point-marker)
   7144                                                                 (point)))))
   7145     line-col-to-point-map))
   7146 
   7147 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2))
   7148   (or (< l1 l2)
   7149       (and (= l1 l2)
   7150            (cond ((and c1 c2)
   7151                   (< c1 c2))
   7152                  (c1 t)))))
   7153 
   7154 (defun lsp-imenu-create-uncategorized-index (symbols)
   7155   "Create imenu index from document SYMBOLS.
   7156 This function, unlike `lsp-imenu-create-categorized-index', does
   7157 not categorize by type, but instead returns an `imenu' index
   7158 corresponding to the symbol hierarchy returned by the server
   7159 directly."
   7160   (let* ((lsp--line-col-to-point-hash-table (-> symbols
   7161                                                 lsp--collect-lines-and-cols
   7162                                                 lsp--convert-line-col-to-points-batch)))
   7163     (if (lsp--imenu-hierarchical-p symbols)
   7164         (lsp--imenu-create-hierarchical-index symbols)
   7165       (lsp--imenu-create-non-hierarchical-index symbols))))
   7166 
   7167 (defcustom lsp-imenu-symbol-kinds
   7168   '((1 . "Files")
   7169     (2 . "Modules")
   7170     (3 . "Namespaces")
   7171     (4 . "Packages")
   7172     (5 . "Classes")
   7173     (6 . "Methods")
   7174     (7 . "Properties")
   7175     (8 . "Fields")
   7176     (9 . "Constructors")
   7177     (10 . "Enums")
   7178     (11 . "Interfaces")
   7179     (12 . "Functions")
   7180     (13 . "Variables")
   7181     (14 . "Constants")
   7182     (15 . "Strings")
   7183     (16 . "Numbers")
   7184     (17 . "Booleans")
   7185     (18 . "Arrays")
   7186     (19 . "Objects")
   7187     (20 . "Keys")
   7188     (21 . "Nulls")
   7189     (22 . "Enum Members")
   7190     (23 . "Structs")
   7191     (24 . "Events")
   7192     (25 . "Operators")
   7193     (26 . "Type Parameters"))
   7194   "`lsp-symbol-kinds', but only used by `imenu'.
   7195 A new variable is needed, as it is `imenu' convention to use
   7196 pluralized categories, which `lsp-symbol-kinds' doesn't. If the
   7197 non-pluralized names are preferred, this can be set to
   7198 `lsp-symbol-kinds'."
   7199   :type '(alist :key-type integer :value-type string))
   7200 
   7201 (defun lsp--imenu-kind->name (kind)
   7202   (alist-get kind lsp-imenu-symbol-kinds "?"))
   7203 
   7204 (defun lsp-imenu-create-top-level-categorized-index (symbols)
   7205   "Create an `imenu' index categorizing SYMBOLS by type.
   7206 Only root symbols are categorized.
   7207 
   7208 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS
   7209 shall be a list of DocumentSymbols or SymbolInformation."
   7210   (mapcan
   7211    (-lambda ((type . symbols))
   7212      (let ((cat (lsp--imenu-kind->name type))
   7213            (symbols (lsp-imenu-create-uncategorized-index symbols)))
   7214        ;; If there is no :kind (this is being defensive), or we couldn't look it
   7215        ;; up, just display the symbols inline, without categories.
   7216        (if cat (list (cons cat symbols)) symbols)))
   7217    (sort (seq-group-by #'lsp:document-symbol-kind symbols)
   7218          (-lambda ((kinda) (kindb)) (< kinda kindb)))))
   7219 
   7220 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start)))
   7221   "Convert an `&DocumentSymbol' to an `imenu' entry."
   7222   (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start))
   7223 
   7224 (defun lsp--imenu-create-categorized-index-1 (symbols)
   7225   "Returns an `imenu' index from SYMBOLS categorized by type.
   7226 The result looks like this: ((\"Variables\" . (...)))."
   7227   (->>
   7228    symbols
   7229    (mapcan
   7230     (-lambda ((sym &as &DocumentSymbol :kind :children?))
   7231       (if (seq-empty-p children?)
   7232           (list (list kind (lsp--symbol->imenu sym)))
   7233         (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline
   7234                                                   (not lsp-imenu-hide-parent-details)))))
   7235           (cons
   7236            (list kind (lsp--symbol->imenu sym))
   7237            (mapcar (-lambda ((type .  imenu-items))
   7238                      (list type (cons parent (mapcan #'cdr imenu-items))))
   7239                    (-group-by #'car (lsp--imenu-create-categorized-index-1 children?))))))))
   7240    (-group-by #'car)
   7241    (mapcar
   7242     (-lambda ((kind . syms))
   7243       (cons kind (mapcan #'cdr syms))))))
   7244 
   7245 (defun lsp--imenu-create-categorized-index (symbols)
   7246   (let ((syms (lsp--imenu-create-categorized-index-1 symbols)))
   7247     (dolist (sym syms)
   7248       (setcar sym (lsp--imenu-kind->name (car sym))))
   7249     syms))
   7250 
   7251 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start))))
   7252   (cons (lsp-render-symbol-information sym nil) start))
   7253 
   7254 (defun lsp--imenu-create-categorized-index-flat (symbols)
   7255   "Create a kind-categorized index for SymbolInformation."
   7256   (mapcar (-lambda ((kind . syms))
   7257             (cons (lsp--imenu-kind->name kind)
   7258                   (mapcan (-lambda ((parent . children))
   7259                             (let ((children (mapcar #'lsp--symbol-information->imenu children)))
   7260                               (if parent (list (cons parent children)) children)))
   7261                           (-group-by #'lsp:symbol-information-container-name? syms))))
   7262           (seq-group-by #'lsp:symbol-information-kind symbols)))
   7263 
   7264 (defun lsp-imenu-create-categorized-index (symbols)
   7265   (if (lsp--imenu-hierarchical-p symbols)
   7266       (lsp--imenu-create-categorized-index symbols)
   7267     (lsp--imenu-create-categorized-index-flat symbols)))
   7268 
   7269 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index
   7270   "Function that should create an `imenu' index.
   7271 It will be called with a list of SymbolInformation or
   7272 DocumentSymbols, whose first level is already filtered. It shall
   7273 then return an appropriate `imenu' index (see
   7274 `imenu-create-index-function').
   7275 
   7276 Note that this interface is not stable, and subject to change any
   7277 time."
   7278   :group 'lsp-imenu
   7279   :type '(radio
   7280           (const :tag "Categorize by type"
   7281                  lsp-imenu-create-categorized-index)
   7282           (const :tag "Categorize root symbols by type"
   7283                  lsp-imenu-create-top-level-categorized-index)
   7284           (const :tag "Uncategorized, inline entries"
   7285                  lsp-imenu-create-uncategorized-index)
   7286           (function :tag "Custom function")))
   7287 
   7288 (defun lsp--imenu-create-index ()
   7289   "Create an `imenu' index based on the language server.
   7290 Respects `lsp-imenu-index-function'."
   7291   (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols))))
   7292     (funcall lsp-imenu-index-function symbols)))
   7293 
   7294 (defun lsp--imenu-filter-symbols (symbols)
   7295   "Filter out unsupported symbols from SYMBOLS."
   7296   (seq-remove #'lsp--symbol-ignore symbols))
   7297 
   7298 (defun lsp--imenu-hierarchical-p (symbols)
   7299   "Determine whether any element in SYMBOLS has children."
   7300   (seq-some #'lsp-document-symbol? symbols))
   7301 
   7302 (defun lsp--imenu-create-non-hierarchical-index (symbols)
   7303   "Create imenu index for non-hierarchical SYMBOLS.
   7304 
   7305 SYMBOLS are a list of DocumentSymbol messages.
   7306 
   7307 Return a nested alist keyed by symbol names. e.g.
   7308 
   7309    ((\"SomeClass\" (\"(Class)\" . 10)
   7310                  (\"someField (Field)\" . 20)
   7311                  (\"someFunction (Function)\" . 25)
   7312                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7313                                   (\"someSubField (Field)\" . 35))
   7314     (\"someFunction (Function)\" . 40))"
   7315   (seq-map (lambda (nested-alist)
   7316              (cons (car nested-alist)
   7317                    (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist))))
   7318            (seq-group-by #'lsp--get-symbol-type symbols)))
   7319 
   7320 (defun lsp--imenu-create-hierarchical-index (symbols)
   7321   "Create imenu index for hierarchical SYMBOLS.
   7322 
   7323 SYMBOLS are a list of DocumentSymbol messages.
   7324 
   7325 Return a nested alist keyed by symbol names. e.g.
   7326 
   7327    ((\"SomeClass\" (\"(Class)\" . 10)
   7328                  (\"someField (Field)\" . 20)
   7329                  (\"someFunction (Function)\" . 25)
   7330                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7331                                   (\"someSubField (Field)\" . 35))
   7332     (\"someFunction (Function)\" . 40))"
   7333   (seq-map #'lsp--symbol-to-hierarchical-imenu-elem
   7334            (seq-sort #'lsp--imenu-symbol-lessp symbols)))
   7335 
   7336 (defun lsp--imenu-symbol-lessp (sym1 sym2)
   7337   (let* ((compare-results (mapcar (lambda (method)
   7338                                     (funcall (alist-get method lsp--imenu-compare-function-alist)
   7339                                              sym1 sym2))
   7340                                   lsp-imenu-sort-methods))
   7341          (result (seq-find (lambda (result)
   7342                              (not (= result 0)))
   7343                            compare-results
   7344                            0)))
   7345     (and (numberp result) (< result 0))))
   7346 
   7347 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left)
   7348                                     (&SymbolInformation :kind right))
   7349   "Compare SYM1 and SYM2 by kind."
   7350   (- left right))
   7351 
   7352 (defun lsp--imenu-compare-line-col (sym1 sym2)
   7353   (if (lsp--line-col-comparator
   7354        (lsp--get-line-and-col sym1)
   7355        (lsp--get-line-and-col sym2))
   7356       -1
   7357     1))
   7358 
   7359 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1)
   7360                                     (&SymbolInformation :name name2))
   7361   "Compare SYM1 and SYM2 by name."
   7362   (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2))))
   7363     (if (numberp result) result 0)))
   7364 
   7365 (defun lsp--imenu-refresh ()
   7366   "Force Imenu to refresh itself."
   7367   (imenu--menubar-select imenu--rescan-item))
   7368 
   7369 (defun lsp-enable-imenu ()
   7370   "Use lsp-imenu for the current buffer."
   7371   (imenu--cleanup)
   7372   (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   7373   (setq-local imenu-menubar-modified-tick -1)
   7374   (setq-local imenu--index-alist nil)
   7375   (when menu-bar-mode
   7376     (lsp--imenu-refresh)))
   7377 
   7378 (defun lsp-resolve-final-command (command &optional test?)
   7379   "Resolve final function COMMAND."
   7380   (let* ((command (lsp-resolve-value command))
   7381          (command (cl-etypecase command
   7382                     (list
   7383                      (cl-assert (seq-every-p (apply-partially #'stringp) command) nil
   7384                                 "Invalid command list")
   7385                      command)
   7386                     (string (list command)))))
   7387     (if (and (file-remote-p default-directory) (not test?))
   7388         (list shell-file-name "-c"
   7389               (string-join (cons "stty raw > /dev/null;"
   7390                                  (mapcar #'shell-quote-argument command))
   7391                            " "))
   7392       command)))
   7393 
   7394 (defun lsp-server-present? (final-command)
   7395   "Check whether FINAL-COMMAND is present."
   7396   (let ((binary-found? (executable-find (cl-first final-command) t)))
   7397     (if binary-found?
   7398         (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command))
   7399       (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command)))
   7400     binary-found?))
   7401 
   7402 (defun lsp--value-to-string (value)
   7403   "Convert VALUE to a string that can be set as value in an environment
   7404 variable."
   7405   (cond
   7406    ((stringp value) value)
   7407    ((booleanp value) (if value
   7408                          "1"
   7409                        "0"))
   7410    ((and (sequencep value)
   7411          (seq-every-p #'stringp value)) (string-join value ":"))
   7412    (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables"))))
   7413 
   7414 (defun lsp--compute-process-environment (environment-fn)
   7415   "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'.
   7416 Ignore non-boolean keys whose value is nil."
   7417   (let ((environment (if environment-fn
   7418                          (funcall environment-fn)
   7419                        nil)))
   7420     (-flatten (cons (cl-loop for (key . value) in environment
   7421                              if (or (eval value)
   7422                                     (eq (get value 'custom-type) 'boolean))
   7423                              collect (concat key "=" (lsp--value-to-string
   7424                                                       (eval value))))
   7425                     process-environment))))
   7426 
   7427 (defun lsp--default-directory-for-connection (&optional path)
   7428   "Return path to be used for the working directory of a LSP process.
   7429 
   7430 If `lsp-use-workspace-root-for-server-default-directory' is
   7431 non-nil, uses `lsp-workspace-root' to find the directory
   7432 corresponding to PATH, else returns `default-directory'."
   7433   (if lsp-use-workspace-root-for-server-default-directory
   7434       (lsp-workspace-root path)
   7435     default-directory))
   7436 
   7437 (defun lsp--fix-remote-cmd (program)
   7438   "Helper for `lsp-stdio-connection'.
   7439 Originally coppied from eglot."
   7440 
   7441   (if (file-remote-p default-directory)
   7442       (list shell-file-name "-c"
   7443             (string-join (cons "stty raw > /dev/null;"
   7444                                (mapcar #'shell-quote-argument program))
   7445                          " "))
   7446     program))
   7447 
   7448 (defvar tramp-use-ssh-controlmaster-options)
   7449 (defvar tramp-ssh-controlmaster-options)
   7450 
   7451 (defun lsp-stdio-connection (command &optional test-command)
   7452   "Returns a connection property list using COMMAND.
   7453 COMMAND can be: A string, denoting the command to launch the
   7454 language server. A list of strings, denoting an executable with
   7455 its command line arguments. A function, that either returns a
   7456 string or a list of strings. In all cases, the launched language
   7457 server should send and receive messages on standard I/O.
   7458 TEST-COMMAND is a function with no arguments which returns
   7459 whether the command is present or not. When not specified
   7460 `lsp-mode' will check whether the first element of the list
   7461 returned by COMMAND is available via `executable-find'"
   7462   (cl-check-type command (or string
   7463                              function
   7464                              (and list
   7465                                   (satisfies (lambda (l)
   7466                                                (seq-every-p (lambda (el)
   7467                                                               (stringp el))
   7468                                                             l))))))
   7469   (list :connect (lambda (filter sentinel name environment-fn workspace)
   7470                    (if (and (functionp 'json-rpc-connection)
   7471                             (not (file-remote-p default-directory)))
   7472                        (lsp-json-rpc-connection workspace (lsp-resolve-final-command command))
   7473                      (let ((final-command (lsp-resolve-final-command command))
   7474                            (process-name (generate-new-buffer-name name))
   7475                            (process-environment
   7476                             (lsp--compute-process-environment environment-fn)))
   7477                        (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name)))
   7478                               (default-directory (lsp--default-directory-for-connection))
   7479                               (tramp-use-ssh-controlmaster-options 'suppress)
   7480                               (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none")
   7481                               (proc (make-process
   7482                                      :name process-name
   7483                                      :connection-type 'pipe
   7484                                      :buffer (format "*%s*" process-name)
   7485                                      :coding 'no-conversion
   7486                                      :command final-command
   7487                                      :filter filter
   7488                                      :sentinel sentinel
   7489                                      :stderr stderr-buf
   7490                                      :noquery t
   7491                                      :file-handler t)))
   7492                          (set-process-query-on-exit-flag proc nil)
   7493                          (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil)
   7494                          (with-current-buffer (get-buffer stderr-buf)
   7495                            ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc.
   7496                            (special-mode))
   7497                          (cons proc proc)))))
   7498         :test? (or
   7499                 test-command
   7500                 (lambda ()
   7501                   (lsp-server-present? (lsp-resolve-final-command command t))))))
   7502 
   7503 (defun lsp--open-network-stream (host port name)
   7504   "Open network stream to HOST:PORT.
   7505   NAME will be passed to `open-network-stream'.
   7506   RETRY-COUNT is the number of the retries.
   7507   SLEEP-INTERVAL is the sleep interval between each retry."
   7508   (let* ((retries 0)
   7509          (sleep-interval 0.01)
   7510          (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval))
   7511          connection)
   7512     (while (and (not connection) (< retries number-of-retries))
   7513       (condition-case err
   7514           (setq connection (open-network-stream name nil host port
   7515                                                 :type 'plain
   7516                                                 :coding 'no-conversion))
   7517         (file-error
   7518          (let ((inhibit-message t))
   7519            (lsp--warn "Failed to connect to %s:%s with error message %s"
   7520                       host
   7521                       port
   7522                       (error-message-string err))
   7523            (sleep-for sleep-interval)
   7524            (cl-incf retries)))))
   7525     (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port))))
   7526 
   7527 (defun lsp--port-available (host port)
   7528   "Return non-nil if HOST and PORT are available."
   7529   (condition-case _err
   7530       (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain))
   7531     (file-error t)))
   7532 
   7533 (defun lsp--find-available-port (host starting-port)
   7534   "Find available port on HOST starting from STARTING-PORT."
   7535   (let ((port starting-port))
   7536     (while (not (lsp--port-available host port))
   7537       (cl-incf port))
   7538     port))
   7539 
   7540 (defun lsp-tcp-connection (command-fn)
   7541   "Returns a connection property list similar to `lsp-stdio-connection'.
   7542 COMMAND-FN can only be a function that takes a single argument, a
   7543 port number. It should return a command for launches a language server
   7544 process listening for TCP connections on the provided port."
   7545   (cl-check-type command-fn function)
   7546   (list
   7547    :connect (lambda (filter sentinel name environment-fn _workspace)
   7548               (let* ((host "localhost")
   7549                      (port (lsp--find-available-port host (cl-incf lsp--tcp-port)))
   7550                      (command (funcall command-fn port))
   7551                      (final-command (if (consp command) command (list command)))
   7552                      (_ (unless (lsp-server-present? final-command)
   7553                           (user-error (format "Couldn't find executable %s" (cl-first final-command)))))
   7554                      (process-environment
   7555                       (lsp--compute-process-environment environment-fn))
   7556                      (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion
   7557                                          :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t))
   7558                      (tcp-proc (lsp--open-network-stream host port (concat name "::tcp"))))
   7559 
   7560                 ;; TODO: Same :noquery issue (see above)
   7561                 (set-process-query-on-exit-flag proc nil)
   7562                 (set-process-query-on-exit-flag tcp-proc nil)
   7563                 (set-process-filter tcp-proc filter)
   7564                 (cons tcp-proc proc)))
   7565    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7566 
   7567 (defalias 'lsp-tcp-server 'lsp-tcp-server-command)
   7568 
   7569 (defun lsp-tcp-server-command (command-fn)
   7570   "Create tcp server connection.
   7571 In this mode Emacs is TCP server and the language server connects
   7572 to it. COMMAND is function with one parameter(the port) and it
   7573 should return the command to start the LS server."
   7574   (cl-check-type command-fn function)
   7575   (list
   7576    :connect (lambda (filter sentinel name environment-fn _workspace)
   7577               (let* (tcp-client-connection
   7578                      (tcp-server (make-network-process :name (format "*tcp-server-%s*" name)
   7579                                                        :buffer (format "*tcp-server-%s*" name)
   7580                                                        :family 'ipv4
   7581                                                        :service lsp--tcp-server-port
   7582                                                        :sentinel (lambda (proc _string)
   7583                                                                    (lsp-log "Language server %s is connected." name)
   7584                                                                    (setf tcp-client-connection proc))
   7585                                                        :server 't))
   7586                      (port (process-contact tcp-server :service))
   7587                      (final-command (funcall command-fn port))
   7588                      (process-environment
   7589                       (lsp--compute-process-environment environment-fn))
   7590                      (cmd-proc (make-process :name name
   7591                                              :connection-type 'pipe
   7592                                              :coding 'no-conversion
   7593                                              :command final-command
   7594                                              :stderr (format "*tcp-server-%s*::stderr" name)
   7595                                              :noquery t)))
   7596                 (let ((retries 0))
   7597                   ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds)
   7598                   (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds)))
   7599                     (lsp--info "Waiting for connection for %s, retries: %s" name retries)
   7600                     (sit-for 0.500)
   7601                     (cl-incf retries)))
   7602 
   7603                 (unless tcp-client-connection
   7604                   (condition-case nil (delete-process tcp-server) (error))
   7605                   (condition-case nil (delete-process cmd-proc) (error))
   7606                   (error "Failed to create connection to %s on port %s" name port))
   7607                 (lsp--info "Successfully connected to %s" name)
   7608 
   7609                 (set-process-query-on-exit-flag cmd-proc nil)
   7610                 (set-process-query-on-exit-flag tcp-client-connection nil)
   7611                 (set-process-query-on-exit-flag tcp-server nil)
   7612 
   7613                 (set-process-filter tcp-client-connection filter)
   7614                 (set-process-sentinel tcp-client-connection sentinel)
   7615                 (cons tcp-client-connection cmd-proc)))
   7616    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7617 
   7618 (defalias 'lsp-tramp-connection 'lsp-stdio-connection)
   7619 
   7620 (defun lsp--auto-configure ()
   7621   "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed."
   7622   (when (functionp 'lsp-ui-mode)
   7623     (lsp-ui-mode))
   7624 
   7625   (if lsp-headerline-breadcrumb-enable
   7626       (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)
   7627     (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode))
   7628   (if lsp-modeline-code-actions-enable
   7629       (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)
   7630     (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode))
   7631   (if lsp-modeline-diagnostics-enable
   7632       (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)
   7633     (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode))
   7634   (if lsp-modeline-workspace-status-enable
   7635       (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)
   7636     (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode))
   7637   (if lsp-lens-enable
   7638       (add-hook 'lsp-configure-hook 'lsp-lens--enable)
   7639     (remove-hook 'lsp-configure-hook 'lsp-lens--enable))
   7640   (if lsp-semantic-tokens-enable
   7641       (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)
   7642     (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable))
   7643 
   7644   ;; yas-snippet config
   7645   (setq-local yas-inhibit-overlay-modification-protection t))
   7646 
   7647 (defun lsp--restart-if-needed (workspace)
   7648   "Handler restart for WORKSPACE."
   7649   (when (or (eq lsp-restart 'auto-restart)
   7650             (eq (lsp--workspace-shutdown-action workspace) 'restart)
   7651             (and (eq lsp-restart 'interactive)
   7652                  (let ((query (format
   7653                                "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?"
   7654                                (lsp--workspace-print workspace))))
   7655                    (y-or-n-p query))))
   7656     (--each (lsp--workspace-buffers workspace)
   7657       (when (lsp-buffer-live-p it)
   7658         (lsp-with-current-buffer it
   7659           (if lsp--buffer-deferred
   7660               (lsp-deferred)
   7661             (lsp--info "Restarting LSP in buffer %s" (buffer-name))
   7662             (lsp)))))))
   7663 
   7664 (defun lsp--update-key (table key fn)
   7665   "Apply FN on value corresponding to KEY in TABLE."
   7666   (let ((existing-value (gethash key table)))
   7667     (if-let ((new-value (funcall fn existing-value)))
   7668         (puthash key new-value table)
   7669       (remhash key table))))
   7670 
   7671 (defun lsp--process-sentinel (workspace process exit-str)
   7672   "Create the sentinel for WORKSPACE."
   7673   (unless (process-live-p process)
   7674     (lsp--handle-process-exit workspace exit-str)))
   7675 
   7676 (defun lsp--handle-process-exit (workspace exit-str)
   7677   (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session)))
   7678          (proc (lsp--workspace-proc workspace)))
   7679     (lsp--warn "%s has exited (%s)"
   7680                (lsp-process-name proc)
   7681                (string-trim-right (or exit-str "")))
   7682     (with-lsp-workspace workspace
   7683       ;; Clean workspace related data in each of the buffers
   7684       ;; in the workspace.
   7685       (--each (lsp--workspace-buffers workspace)
   7686         (when (lsp-buffer-live-p it)
   7687           (lsp-with-current-buffer it
   7688             (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces))
   7689             (lsp--uninitialize-workspace)
   7690             (lsp--spinner-stop)
   7691             (lsp--remove-overlays 'lsp-highlight))))
   7692 
   7693       ;; Cleanup session from references to the closed workspace.
   7694       (--each (hash-table-keys folder->workspaces)
   7695         (lsp--update-key folder->workspaces it (apply-partially 'delete workspace)))
   7696 
   7697       (lsp-process-cleanup proc))
   7698 
   7699     (run-hook-with-args 'lsp-after-uninitialized-functions workspace)
   7700 
   7701     (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown)
   7702         (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace))
   7703       (lsp--restart-if-needed workspace))
   7704     (lsp--cleanup-hanging-watches)))
   7705 
   7706 (defun lsp-workspace-folders (workspace)
   7707   "Return all folders associated with WORKSPACE."
   7708   (let (result)
   7709     (->> (lsp-session)
   7710          (lsp-session-folder->servers)
   7711          (maphash (lambda (folder workspaces)
   7712                     (when (-contains? workspaces workspace)
   7713                       (push folder result)))))
   7714     result))
   7715 
   7716 (defun lsp--start-workspace (session client-template root &optional initialization-options)
   7717   "Create new workspace for CLIENT-TEMPLATE with project root ROOT.
   7718 INITIALIZATION-OPTIONS are passed to initialize function.
   7719 SESSION is the active session."
   7720   (lsp--spinner-start)
   7721   (-let* ((default-directory root)
   7722           (client (copy-lsp--client client-template))
   7723           (workspace (make-lsp--workspace
   7724                       :root root
   7725                       :client client
   7726                       :status 'starting
   7727                       :buffers (list (lsp-current-buffer))
   7728                       :host-root (file-remote-p root)))
   7729           ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities
   7730                      'multi-root 'initialized-fn) client)
   7731           ((proc . cmd-proc) (funcall
   7732                               (or (plist-get new-connection :connect)
   7733                                   (user-error "Client %s is configured incorrectly" client))
   7734                               (lsp--create-filter-function workspace)
   7735                               (apply-partially #'lsp--process-sentinel workspace)
   7736                               (format "%s" server-id)
   7737                               environment-fn
   7738                               workspace))
   7739           (workspace-folders (gethash server-id (lsp-session-server-id->folders session))))
   7740     (setf (lsp--workspace-proc workspace) proc
   7741           (lsp--workspace-cmd-proc workspace) cmd-proc)
   7742 
   7743     ;; update (lsp-session-folder->servers) depending on whether we are starting
   7744     ;; multi/single folder workspace
   7745     (mapc (lambda (project-root)
   7746             (->> session
   7747                  (lsp-session-folder->servers)
   7748                  (gethash project-root)
   7749                  (cl-pushnew workspace)))
   7750           (or workspace-folders (list root)))
   7751 
   7752     (with-lsp-workspace workspace
   7753       (run-hooks 'lsp-before-initialize-hook)
   7754       (lsp-request-async
   7755        "initialize"
   7756        (append
   7757         (list :processId (unless (file-remote-p (buffer-file-name))
   7758                            (emacs-pid))
   7759               :rootPath (lsp-file-local-name (expand-file-name root))
   7760               :clientInfo (list :name "emacs"
   7761                                 :version (emacs-version))
   7762               :rootUri (lsp--path-to-uri root)
   7763               :capabilities (lsp--client-capabilities custom-capabilities)
   7764               :initializationOptions initialization-options
   7765               :workDoneToken "1")
   7766         (when lsp-server-trace
   7767           (list :trace lsp-server-trace))
   7768         (when multi-root
   7769           (->> workspace-folders
   7770                (-distinct)
   7771                (-map (lambda (folder)
   7772                        (list :uri (lsp--path-to-uri folder)
   7773                              :name (f-filename folder))))
   7774                (apply 'vector)
   7775                (list :workspaceFolders))))
   7776        (-lambda ((&InitializeResult :capabilities))
   7777          ;; we know that Rust Analyzer will send {} which will be parsed as null
   7778          ;; when using plists
   7779          (when (equal 'rust-analyzer server-id)
   7780            (-> capabilities
   7781                (lsp:server-capabilities-text-document-sync?)
   7782                (lsp:set-text-document-sync-options-save? t)))
   7783 
   7784          (setf (lsp--workspace-server-capabilities workspace) capabilities
   7785                (lsp--workspace-status workspace) 'initialized)
   7786 
   7787          (with-lsp-workspace workspace
   7788            (lsp-notify "initialized" lsp--empty-ht))
   7789 
   7790          (when initialized-fn (funcall initialized-fn workspace))
   7791 
   7792          (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace))
   7793          (->> workspace
   7794               (lsp--workspace-buffers)
   7795               (mapc (lambda (buffer)
   7796                       (lsp-with-current-buffer buffer
   7797                         (lsp--open-in-workspace workspace)))))
   7798 
   7799          (with-lsp-workspace workspace
   7800            (run-hooks 'lsp-after-initialize-hook))
   7801          (lsp--info "%s initialized successfully in folders: %s"
   7802                     (lsp--workspace-print workspace)
   7803                     (lsp-workspace-folders workspace)))
   7804        :mode 'detached))
   7805     workspace))
   7806 
   7807 (defun lsp--load-default-session ()
   7808   "Load default session."
   7809   (setq lsp--session (or (condition-case err
   7810                              (lsp--read-from-file lsp-session-file)
   7811                            (error (lsp--error "Failed to parse the session %s, starting with clean one."
   7812                                               (error-message-string err))
   7813                                   nil))
   7814                          (make-lsp-session))))
   7815 
   7816 (defun lsp-session ()
   7817   "Get the session associated with the current buffer."
   7818   (or lsp--session (setq lsp--session (lsp--load-default-session))))
   7819 
   7820 (defun lsp--client-disabled-p (buffer-major-mode client)
   7821   (seq-some
   7822    (lambda (entry)
   7823      (pcase entry
   7824        ((pred symbolp) (eq entry client))
   7825        (`(,mode . ,client-or-list)
   7826         (and (eq mode buffer-major-mode)
   7827              (if (listp client-or-list)
   7828                  (memq client client-or-list)
   7829                (eq client client-or-list))))))
   7830    lsp-disabled-clients))
   7831 
   7832 
   7833 ;; download server
   7834 
   7835 (defcustom lsp-server-install-dir (expand-file-name
   7836                                    (locate-user-emacs-file (f-join ".cache" "lsp")))
   7837   "Directory in which the servers will be installed."
   7838   :risky t
   7839   :type 'directory
   7840   :package-version '(lsp-mode . "6.3")
   7841   :group 'lsp-mode)
   7842 
   7843 (defcustom lsp-verify-signature t
   7844   "Whether to check GPG signatures of downloaded files."
   7845   :type 'boolean
   7846   :package-version '(lsp-mode . "8.0.0")
   7847   :group 'lsp-mode)
   7848 
   7849 (defvar lsp--dependencies (ht))
   7850 
   7851 (defun lsp-dependency (name &rest definitions)
   7852   "Used to specify a language server DEPENDENCY, the server
   7853 executable or other required file path. Typically, the
   7854 DEPENDENCY is found by locating it on the system path using
   7855 `executable-find'.
   7856 
   7857 You can explicitly call lsp-dependency in your environment to
   7858 specify the absolute path to the DEPENDENCY. For example, the
   7859 typescript-language-server requires both the server and the
   7860 typescript compiler. If you have installed them in a team shared
   7861 read-only location, you can instruct lsp-mode to use them via
   7862 
   7863  (eval-after-load `lsp-mode
   7864    `(progn
   7865       (require lsp-javascript)
   7866       (lsp-dependency typescript-language-server (:system ,tls-exe))
   7867       (lsp-dependency typescript (:system ,ts-js))))
   7868 
   7869 where tls-exe is the absolute path to the typescript-language-server
   7870 executable and ts-js is the absolute path to the typescript compiler
   7871 JavaScript file, tsserver.js (the *.js is required for Windows)."
   7872   (ht-set lsp--dependencies name definitions))
   7873 
   7874 (defun lsp--server-binary-present? (client)
   7875   (unless (equal (lsp--client-server-id client) 'lsp-pwsh)
   7876     (condition-case ()
   7877         (-some-> client lsp--client-new-connection (plist-get :test?) funcall)
   7878       (error nil)
   7879       (args-out-of-range nil))))
   7880 
   7881 (define-minor-mode lsp-installation-buffer-mode
   7882   "Mode used in *lsp-installation* buffers.
   7883 It can be used to set-up keybindings, etc. Disabling this mode
   7884 detaches the installation buffer from commands like
   7885 `lsp-select-installation-buffer'."
   7886   :init-value nil
   7887   :lighter nil)
   7888 
   7889 (defface lsp-installation-finished-buffer-face '((t :foreground "orange"))
   7890   "Face used for finished installation buffers.
   7891 Used in `lsp-select-installation-buffer'."
   7892   :group 'lsp-mode)
   7893 
   7894 (defface lsp-installation-buffer-face '((t :foreground "green"))
   7895   "Face used for installation buffers still in progress.
   7896 Used in `lsp-select-installation-buffer'."
   7897   :group 'lsp-mode)
   7898 
   7899 (defun lsp--installation-buffer? (buf)
   7900   "Check whether BUF is an `lsp-async-start-process' buffer."
   7901   (buffer-local-value 'lsp-installation-buffer-mode buf))
   7902 
   7903 (defun lsp-select-installation-buffer (&optional show-finished)
   7904   "Interactively choose an installation buffer.
   7905 If SHOW-FINISHED is set, leftover (finished) installation buffers
   7906 are still shown."
   7907   (interactive "P")
   7908   (let ((bufs (--filter (and (lsp--installation-buffer? it)
   7909                              (or show-finished (get-buffer-process it)))
   7910                         (buffer-list))))
   7911     (pcase bufs
   7912       (`nil (user-error "No installation buffers"))
   7913       (`(,buf) (pop-to-buffer buf))
   7914       (bufs (pop-to-buffer (completing-read "Select installation buffer: "
   7915                                             (--map (propertize (buffer-name it) 'face
   7916                                                                (if (get-buffer-process it)
   7917                                                                    'lsp-installation-buffer-face
   7918                                                                  'lsp-installation-finished-buffer-face))
   7919                                                    bufs)))))))
   7920 
   7921 (defun lsp-cleanup-installation-buffers ()
   7922   "Delete finished *lsp-installation* buffers."
   7923   (interactive)
   7924   (dolist (buf (buffer-list))
   7925     (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf)))
   7926       (kill-buffer buf))))
   7927 
   7928 (defun lsp--download-status ()
   7929   (-some--> #'lsp--client-download-in-progress?
   7930     (lsp--filter-clients it)
   7931     (-map (-compose #'symbol-name #'lsp--client-server-id) it)
   7932     (format "%s" it)
   7933     (propertize it 'face 'success)
   7934     (format " Installing following servers: %s" it)
   7935     (propertize it
   7936                 'local-map (make-mode-line-mouse-map
   7937                             'mouse-1 #'lsp-select-installation-buffer)
   7938                 'mouse-face 'highlight)))
   7939 
   7940 (defun lsp--install-server-internal (client &optional update?)
   7941   (unless (lsp--client-download-server-fn client)
   7942     (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation."
   7943                 (lsp--client-server-id client)))
   7944 
   7945   (setf (lsp--client-download-in-progress? client) t)
   7946   (add-to-list 'global-mode-string '(t (:eval (lsp--download-status))))
   7947   (cl-flet ((done
   7948              (success? &optional error-message)
   7949              ;; run with idle timer to make sure the lsp command is executed in
   7950              ;; the main thread, see #2739.
   7951              (run-with-timer
   7952               0.0
   7953               nil
   7954               (lambda ()
   7955                 (-let [(&lsp-cln 'server-id 'buffers) client]
   7956                   (setf (lsp--client-download-in-progress? client) nil
   7957                         (lsp--client-buffers client) nil)
   7958                   (if success?
   7959                       (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id
   7960                                  (length buffers))
   7961                     (lsp--error "Server %s install process failed with the following error message: %s.
   7962 Check `*lsp-install*' and `*lsp-log*' buffer."
   7963                                 server-id
   7964                                 error-message))
   7965                   (seq-do
   7966                    (lambda (buffer)
   7967                      (when (lsp-buffer-live-p buffer)
   7968                        (lsp-with-current-buffer buffer
   7969                          (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   7970                                     global-mode-string)
   7971                          (when success? (lsp)))))
   7972                    buffers)
   7973                   (unless (lsp--filter-clients #'lsp--client-download-in-progress?)
   7974                     (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   7975                                global-mode-string)))))))
   7976     (lsp--info "Download %s started." (lsp--client-server-id client))
   7977     (condition-case err
   7978         (funcall
   7979          (lsp--client-download-server-fn client)
   7980          client
   7981          (lambda () (done t))
   7982          (lambda (msg) (done nil msg))
   7983          update?)
   7984       (error
   7985        (done nil (error-message-string err))))))
   7986 
   7987 (defun lsp--require-packages ()
   7988   "Load `lsp-client-packages' if needed."
   7989   (when (and lsp-auto-configure (not lsp--client-packages-required))
   7990     (seq-do (lambda (package)
   7991               ;; loading client is slow and `lsp' can be called repeatedly
   7992               (unless (featurep package)
   7993                 (require package nil t)))
   7994             lsp-client-packages)
   7995     (setq lsp--client-packages-required t)))
   7996 
   7997 ;;;###autoload
   7998 (defun lsp-install-server (update? &optional server-id)
   7999   "Interactively install or re-install server.
   8000 When prefix UPDATE? is t force installation even if the server is present."
   8001   (interactive "P")
   8002   (lsp--require-packages)
   8003   (let* ((chosen-client (or (gethash server-id lsp-clients)
   8004                             (lsp--completing-read
   8005                              "Select server to install/re-install: "
   8006                              (or (->> lsp-clients
   8007                                       (ht-values)
   8008                                       (-filter (-andfn
   8009                                                 (-not #'lsp--client-download-in-progress?)
   8010                                                 #'lsp--client-download-server-fn)))
   8011                                  (user-error "There are no servers with automatic installation"))
   8012                              (lambda (client)
   8013                                (let ((server-name (-> client lsp--client-server-id symbol-name)))
   8014                                  (if (lsp--server-binary-present? client)
   8015                                      (concat server-name " (Already installed)")
   8016                                    server-name)))
   8017                              nil
   8018                              t)))
   8019          (update? (or update?
   8020                       (and (not (lsp--client-download-in-progress? chosen-client))
   8021                            (lsp--server-binary-present? chosen-client)))))
   8022     (lsp--install-server-internal chosen-client update?)))
   8023 
   8024 ;;;###autoload
   8025 (defun lsp-uninstall-server (dir)
   8026   "Delete a LSP server from `lsp-server-install-dir'."
   8027   (interactive
   8028    (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir))))
   8029   (unless (file-directory-p dir)
   8030     (user-error "Couldn't find %s directory" dir))
   8031   (delete-directory dir 'recursive)
   8032   (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir))))
   8033 
   8034 ;;;###autoload
   8035 (defun lsp-uninstall-servers ()
   8036   "Uninstall all installed servers."
   8037   (interactive)
   8038   (let* ((dir lsp-server-install-dir)
   8039          (servers (ignore-errors
   8040                     (directory-files dir t
   8041                                      directory-files-no-dot-files-regexp))))
   8042     (if (or (not (file-directory-p dir)) (zerop (length servers)))
   8043         (user-error "No servers to uninstall")
   8044       (when (yes-or-no-p
   8045              (format "Servers to uninstall: %d (%s), proceed? "
   8046                      (length servers)
   8047                      (mapconcat (lambda (server)
   8048                                   (file-name-nondirectory (directory-file-name server)))
   8049                                 servers " ")))
   8050         (mapc #'lsp-uninstall-server servers)
   8051         (message "All servers uninstalled")))))
   8052 
   8053 ;;;###autoload
   8054 (defun lsp-update-server (&optional server-id)
   8055   "Interactively update (reinstall) a server."
   8056   (interactive)
   8057   (lsp--require-packages)
   8058   (let ((chosen-client (or (gethash server-id lsp-clients)
   8059                            (lsp--completing-read
   8060                             "Select server to update (if not on the list, probably you need to `lsp-install-server`): "
   8061                             (or (->> lsp-clients
   8062                                      (ht-values)
   8063                                      (-filter (-andfn
   8064                                                (-not #'lsp--client-download-in-progress?)
   8065                                                #'lsp--client-download-server-fn
   8066                                                #'lsp--server-binary-present?)))
   8067                                 (user-error "There are no servers to update"))
   8068                             (lambda (client)
   8069                               (-> client lsp--client-server-id symbol-name))
   8070                             nil
   8071                             t))))
   8072     (lsp--install-server-internal chosen-client t)))
   8073 
   8074 ;;;###autoload
   8075 (defun lsp-update-servers ()
   8076   "Update (reinstall) all installed servers."
   8077   (interactive)
   8078   (lsp--require-packages)
   8079   (mapc (lambda (client) (lsp--install-server-internal client t))
   8080         (-filter (-andfn
   8081                   (-not #'lsp--client-download-in-progress?)
   8082                   #'lsp--client-download-server-fn
   8083                   #'lsp--server-binary-present?) (hash-table-values lsp-clients))))
   8084 
   8085 ;;;###autoload
   8086 (defun lsp-ensure-server (server-id)
   8087   "Ensure server SERVER-ID"
   8088   (lsp--require-packages)
   8089   (if-let ((client (gethash server-id lsp-clients)))
   8090       (unless (lsp--server-binary-present? client)
   8091         (lsp--info "Server `%s' is not preset, installing..." server-id)
   8092         (lsp-install-server nil server-id))
   8093     (warn "Unable to find server registration with id %s" server-id)))
   8094 
   8095 (defun lsp-async-start-process (callback error-callback &rest command)
   8096   "Start async process COMMAND with CALLBACK and ERROR-CALLBACK."
   8097   (let ((name (cl-first command)))
   8098     (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd)
   8099                                                                                          (not (null cmd)))
   8100                                                                                        command)
   8101                                                        " ") t
   8102                                             (lambda (&rest _)
   8103                                               (generate-new-buffer-name (format "*lsp-install: %s*" name))))
   8104       (lsp-installation-buffer-mode +1)
   8105       (view-mode +1)
   8106       (add-hook
   8107        'compilation-finish-functions
   8108        (lambda (_buf status)
   8109          (if (string= "finished\n" status)
   8110              (condition-case err
   8111                  (funcall callback)
   8112                (error
   8113                 (funcall error-callback (error-message-string err))))
   8114            (funcall error-callback (s-trim-right status))))
   8115        nil t))))
   8116 
   8117 (defun lsp-resolve-value (value)
   8118   "Resolve VALUE's value.
   8119 If it is function - call it.
   8120 If it is a variable - return it's value
   8121 Otherwise returns value itself."
   8122   (cond
   8123    ((functionp value) (funcall value))
   8124    ((and (symbolp value) (boundp value)) (symbol-value value))
   8125    (value)))
   8126 
   8127 (defvar lsp-deps-providers
   8128   (list :npm (list :path #'lsp--npm-dependency-path
   8129                    :install #'lsp--npm-dependency-install)
   8130         :cargo (list :path #'lsp--cargo-dependency-path
   8131                      :install #'lsp--cargo-dependency-install)
   8132         :system (list :path #'lsp--system-path)
   8133         :download (list :path #'lsp-download-path
   8134                         :install #'lsp-download-install)))
   8135 
   8136 (defun lsp--system-path (path)
   8137   "If PATH is absolute and exists return it as is. Otherwise,
   8138 return the absolute path to the executable defined by PATH or
   8139 nil."
   8140   ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the
   8141   ;; typescript-language-server. When lsp invokes the server, lsp needs to
   8142   ;; supply the path to the typescript compiler, tsserver.js, as an argument. To
   8143   ;; make code platform independent, one must pass the absolute path to the
   8144   ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript
   8145   ;; child process spawn command that is invoked by the
   8146   ;; typescript-language-server). This is why we check for existence and not
   8147   ;; that the path is executable.
   8148   (let ((path (lsp-resolve-value path)))
   8149     (cond
   8150      ((and (f-absolute? path)
   8151            (f-exists? path))
   8152       path)
   8153      ((executable-find path t) path))))
   8154 
   8155 (defun lsp-package-path (dependency)
   8156   "Path to the DEPENDENCY each of the registered providers."
   8157   (let (path)
   8158     (-first (-lambda ((provider . rest))
   8159               (setq path (-some-> lsp-deps-providers
   8160                            (plist-get provider)
   8161                            (plist-get :path)
   8162                            (apply rest))))
   8163             (gethash dependency lsp--dependencies))
   8164     path))
   8165 
   8166 (defun lsp-package-ensure (dependency callback error-callback)
   8167   "Asynchronously ensure a package."
   8168   (or (-first (-lambda ((provider . rest))
   8169                 (-some-> lsp-deps-providers
   8170                   (plist-get provider)
   8171                   (plist-get :install)
   8172                   (apply (cl-list* callback error-callback rest))))
   8173               (gethash dependency lsp--dependencies))
   8174       (funcall error-callback (format "Unable to find a way to install %s" dependency))))
   8175 
   8176 
   8177 ;; npm handling
   8178 
   8179 ;; https://docs.npmjs.com/files/folders#executables
   8180 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys)
   8181   "Return npm dependency PATH for PACKAGE."
   8182   (let ((path (executable-find
   8183                (f-join lsp-server-install-dir "npm" package
   8184                        (cond ((eq system-type 'windows-nt) "")
   8185                              (t "bin"))
   8186                        path)
   8187                t)))
   8188     (unless (and path (f-exists? path))
   8189       (error "The package %s is not installed.  Unable to find %s" package path))
   8190     path))
   8191 
   8192 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys)
   8193   (if-let ((npm-binary (executable-find "npm")))
   8194       (progn
   8195         ;; Explicitly `make-directory' to work around NPM bug in
   8196         ;; versions 7.0.0 through 7.4.1. See
   8197         ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for
   8198         ;; discussion.
   8199         (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents)
   8200         (lsp-async-start-process (lambda ()
   8201                                    (if (string-empty-p
   8202                                         (string-trim (shell-command-to-string
   8203                                                       (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " "))))
   8204                                        (funcall callback)
   8205                                      (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json")))))
   8206                                            (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx
   8207                                        (when (f-dir-p default-directory)
   8208                                          (lsp-async-start-process callback
   8209                                                                   error-callback
   8210                                                                   (executable-find "npx")
   8211                                                                   "npm-install-peers")))))
   8212                                  error-callback
   8213                                  npm-binary
   8214                                  "-g"
   8215                                  "--prefix"
   8216                                  (f-join lsp-server-install-dir "npm" package)
   8217                                  "install"
   8218                                  package))
   8219     (lsp-log "Unable to install %s via `npm' because it is not present" package)
   8220     nil))
   8221 
   8222 
   8223 ;; Cargo dependency handling
   8224 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys)
   8225   (let ((path (executable-find
   8226                (f-join lsp-server-install-dir
   8227                        "cargo"
   8228                        package
   8229                        "bin"
   8230                        path)
   8231                t)))
   8232     (unless (and path (f-exists? path))
   8233       (error "The package %s is not installed.  Unable to find %s" package path))
   8234     path))
   8235 
   8236 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys)
   8237   (if-let ((cargo-binary (executable-find "cargo")))
   8238       (lsp-async-start-process
   8239        callback
   8240        error-callback
   8241        cargo-binary
   8242        "install"
   8243        package
   8244        (when git
   8245          "--git")
   8246        git
   8247        "--root"
   8248        (f-join lsp-server-install-dir "cargo" package))
   8249     (lsp-log "Unable to install %s via `cargo' because it is not present" package)
   8250     nil))
   8251 
   8252 
   8253 
   8254 ;; Download URL handling
   8255 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys)
   8256   (let* ((url (lsp-resolve-value url))
   8257          (store-path (lsp-resolve-value store-path))
   8258          ;; (decompress (lsp-resolve-value decompress))
   8259          (download-path
   8260           (pcase decompress
   8261             (:gzip (concat store-path ".gz"))
   8262             (:zip (concat store-path ".zip"))
   8263             (:targz (concat store-path ".tar.gz"))
   8264             (`nil store-path)
   8265             (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'")))))
   8266     (make-thread
   8267      (lambda ()
   8268        (condition-case err
   8269            (progn
   8270              (when (f-exists? download-path)
   8271                (f-delete download-path))
   8272              (when (f-exists? store-path)
   8273                (f-delete store-path))
   8274              (lsp--info "Starting to download %s to %s..." url download-path)
   8275              (mkdir (f-parent download-path) t)
   8276              (url-copy-file url download-path)
   8277              (lsp--info "Finished downloading %s..." download-path)
   8278              (when (and lsp-verify-signature asc-url pgp-key)
   8279                (if (executable-find epg-gpg-program)
   8280                    (let ((asc-download-path (concat download-path ".asc"))
   8281                          (context (epg-make-context))
   8282                          (fingerprint)
   8283                          (signature))
   8284                      (when (f-exists? asc-download-path)
   8285                        (f-delete asc-download-path))
   8286                      (lsp--info "Starting to download %s to %s..." asc-url asc-download-path)
   8287                      (url-copy-file asc-url asc-download-path)
   8288                      (lsp--info "Finished downloading %s..." asc-download-path)
   8289                      (epg-import-keys-from-string context pgp-key)
   8290                      (setq fingerprint (epg-import-status-fingerprint
   8291                                         (car
   8292                                          (epg-import-result-imports
   8293                                           (epg-context-result-for context 'import)))))
   8294                      (lsp--info "Verifying signature %s..." asc-download-path)
   8295                      (epg-verify-file context asc-download-path download-path)
   8296                      (setq signature (car (epg-context-result-for context 'verify)))
   8297                      (unless (and
   8298                               (eq (epg-signature-status signature) 'good)
   8299                               (equal (epg-signature-fingerprint signature) fingerprint))
   8300                        (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature))))
   8301                  (lsp--warn "GPG is not installed, skipping the signature check.")))
   8302              (when decompress
   8303                (lsp--info "Decompressing %s..." download-path)
   8304                (pcase decompress
   8305                  (:gzip
   8306                   (lsp-gunzip download-path))
   8307                  (:zip (lsp-unzip download-path (f-parent store-path)))
   8308                  (:targz (lsp-tar-gz-decompress download-path (f-parent store-path))))
   8309                (lsp--info "Decompressed %s..." store-path))
   8310              (funcall callback))
   8311          (error (funcall error-callback err)))))))
   8312 
   8313 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys)
   8314   "Download URL and store it into STORE-PATH.
   8315 
   8316 SET-EXECUTABLE? when non-nil change the executable flags of
   8317 STORE-PATH to make it executable. BINARY-PATH can be specified
   8318 when the binary to start does not match the name of the
   8319 archive (e.g. when the archive has multiple files)"
   8320   (let ((store-path (or (lsp-resolve-value binary-path)
   8321                         (lsp-resolve-value store-path))))
   8322     (cond
   8323      ((executable-find store-path) store-path)
   8324      ((and set-executable? (f-exists? store-path))
   8325       (set-file-modes store-path #o0700)
   8326       store-path)
   8327      ((f-exists? store-path) store-path))))
   8328 
   8329 (defun lsp--find-latest-gh-release-url (url regex)
   8330   "Fetch the latest version in the releases given by URL by using REGEX."
   8331   (let ((url-request-method "GET"))
   8332     (with-current-buffer (url-retrieve-synchronously url)
   8333       (goto-char (point-min))
   8334       (re-search-forward "\n\n" nil 'noerror)
   8335       (delete-region (point-min) (point))
   8336       (let* ((json-result (lsp-json-read-buffer)))
   8337         (message "Latest version found: %s" (lsp-get json-result :tag_name))
   8338         (--> json-result
   8339              (lsp-get it :assets)
   8340              (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it)
   8341              (lsp-get it :browser_download_url))))))
   8342 
   8343 ;; unzip
   8344 
   8345 (defconst lsp-ext-pwsh-script "powershell -noprofile -noninteractive \
   8346 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'"
   8347   "Powershell script to unzip file.")
   8348 
   8349 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'"
   8350   "Unzip script to unzip file.")
   8351 
   8352 (defcustom lsp-unzip-script (lambda ()
   8353                               (cond ((executable-find "unzip") lsp-ext-unzip-script)
   8354                                     ((executable-find "powershell") lsp-ext-pwsh-script)
   8355                                     (t nil)))
   8356   "The script to unzip."
   8357   :group 'lsp-mode
   8358   :type 'string
   8359   :package-version '(lsp-mode . "8.0.0"))
   8360 
   8361 (defun lsp-unzip (zip-file dest)
   8362   "Unzip ZIP-FILE to DEST."
   8363   (unless lsp-unzip-script
   8364     (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'"))
   8365   (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest)))
   8366 
   8367 ;; gunzip
   8368 
   8369 (defconst lsp-ext-gunzip-script "gzip -d %1$s"
   8370   "Script to decompress a gzippped file with gzip.")
   8371 
   8372 (defcustom lsp-gunzip-script (lambda ()
   8373                                (cond ((executable-find "gzip") lsp-ext-gunzip-script)
   8374                                      (t nil)))
   8375   "The script to decompress a gzipped file.
   8376 Should be a format string with one argument for the file to be decompressed
   8377 in place."
   8378   :group 'lsp-mode
   8379   :type 'string
   8380   :package-version '(lsp-mode . "8.0.0"))
   8381 
   8382 (defun lsp-gunzip (gz-file)
   8383   "Decompress GZ-FILE in place."
   8384   (unless lsp-gunzip-script
   8385     (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file))
   8386   (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file)))
   8387 
   8388 ;; tar.gz decompression
   8389 
   8390 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'"
   8391   "Script to decompress a .tar.gz file.")
   8392 
   8393 (defcustom lsp-tar-script (lambda ()
   8394                             (cond ((executable-find "tar") lsp-ext-tar-script)
   8395                                   (t nil)))
   8396   "The script to decompress a .tar.gz file.
   8397 Should be a format string with one argument for the file to be decompressed
   8398 in place."
   8399   :group 'lsp-mode
   8400   :type 'string)
   8401 
   8402 (defun lsp-tar-gz-decompress (targz-file dest)
   8403   "Decompress TARGZ-FILE in DEST."
   8404   (unless lsp-tar-script
   8405     (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file))
   8406   (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest)))
   8407 
   8408 
   8409 ;; VSCode marketplace
   8410 
   8411 (defcustom lsp-vscode-ext-url
   8412   "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s"
   8413   "Vscode extension template url."
   8414   :group 'lsp-mode
   8415   :type 'string
   8416   :package-version '(lsp-mode . "8.0.0"))
   8417 
   8418 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform)
   8419   "Return the URL to vscode extension.
   8420 PUBLISHER is the extension publisher.
   8421 NAME is the name of the extension.
   8422 VERSION is the version of the extension.
   8423 TARGETPLATFORM is the targetPlatform of the extension."
   8424   (format lsp-vscode-ext-url publisher name version (or targetPlatform "")))
   8425 
   8426 
   8427 
   8428 ;; Queueing prompts
   8429 
   8430 (defvar lsp--question-queue nil
   8431   "List of questions yet to be asked by `lsp-ask-question'.")
   8432 
   8433 (defun lsp-ask-question (question options callback)
   8434   "Prompt the user to answer the QUESTION with one of the OPTIONS from the
   8435 minibuffer. Once the user selects an option, the CALLBACK function will be
   8436 called, passing the selected option to it.
   8437 
   8438 If the user is currently being shown a question, the question will be stored in
   8439 `lsp--question-queue', and will be asked once the user has answered the current
   8440 question."
   8441   (add-to-list 'lsp--question-queue `(("question" . ,question)
   8442                                       ("options" . ,options)
   8443                                       ("callback" . ,callback)) t)
   8444   (when (eq (length lsp--question-queue) 1)
   8445     (lsp--process-question-queue)))
   8446 
   8447 (defun lsp--process-question-queue ()
   8448   "Take the first question from `lsp--question-queue', process it, then process
   8449 the next question until the queue is empty."
   8450   (-let* (((&alist "question" "options" "callback") (car lsp--question-queue))
   8451           (answer (completing-read question options nil t)))
   8452     (pop lsp--question-queue)
   8453     (funcall callback answer)
   8454     (when lsp--question-queue
   8455       (lsp--process-question-queue))))
   8456 
   8457 (defun lsp--supports-buffer? (client)
   8458   (and
   8459    ;; both file and client remote or both local
   8460    (eq (---truthy? (file-remote-p (buffer-file-name)))
   8461        (---truthy? (lsp--client-remote? client)))
   8462 
   8463    ;; activation function or major-mode match.
   8464    (if-let ((activation-fn (lsp--client-activation-fn client)))
   8465        (funcall activation-fn (buffer-file-name) major-mode)
   8466      (-contains? (lsp--client-major-modes client) major-mode))
   8467 
   8468    ;; check whether it is enabled if `lsp-enabled-clients' is not null
   8469    (or (null lsp-enabled-clients)
   8470        (or (member (lsp--client-server-id client) lsp-enabled-clients)
   8471            (ignore (lsp--info "Client %s is not in lsp-enabled-clients"
   8472                               (lsp--client-server-id client)))))
   8473 
   8474    ;; check whether it is not disabled.
   8475    (not (lsp--client-disabled-p major-mode (lsp--client-server-id client)))))
   8476 
   8477 (defun lsp--filter-clients (pred)
   8478   (->> lsp-clients hash-table-values (-filter pred)))
   8479 
   8480 (defun lsp--find-clients ()
   8481   "Find clients which can handle current buffer."
   8482   (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   8483                                                             #'lsp--server-binary-present?)))
   8484     (lsp-log "Found the following clients for %s: %s"
   8485              (buffer-file-name)
   8486              (s-join ", "
   8487                      (-map (lambda (client)
   8488                              (format "(server-id %s, priority %s)"
   8489                                      (lsp--client-server-id client)
   8490                                      (lsp--client-priority client)))
   8491                            matching-clients)))
   8492     (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients))
   8493             (selected-clients (if-let ((main-client (and main-clients
   8494                                                          (--max-by (> (lsp--client-priority it)
   8495                                                                       (lsp--client-priority other))
   8496                                                                    main-clients))))
   8497                                   (cons main-client add-on-clients)
   8498                                 add-on-clients)))
   8499       (lsp-log "The following clients were selected based on priority: %s"
   8500                (s-join ", "
   8501                        (-map (lambda (client)
   8502                                (format "(server-id %s, priority %s)"
   8503                                        (lsp--client-server-id client)
   8504                                        (lsp--client-priority client)))
   8505                              selected-clients)))
   8506       selected-clients)))
   8507 
   8508 (defun lsp-workspace-remove-all-folders()
   8509   "Delete all lsp tracked folders."
   8510   (interactive)
   8511   (--each (lsp-session-folders (lsp-session))
   8512     (lsp-workspace-folders-remove it)))
   8513 
   8514 (defun lsp-register-client (client)
   8515   "Registers LSP client CLIENT."
   8516   (let ((client-id (lsp--client-server-id client)))
   8517     (puthash client-id client lsp-clients)
   8518     (setplist (intern (format "lsp-%s-after-open-hook" client-id))
   8519               `( standard-value (nil) custom-type hook
   8520                  custom-package-version (lsp-mode . "7.0.1")
   8521                  variable-documentation ,(format "Hooks to run after `%s' server is run." client-id)
   8522                  custom-requests nil)))
   8523   (when (and lsp-auto-register-remote-clients
   8524              (not (lsp--client-remote? client)))
   8525     (let ((remote-client (copy-lsp--client client)))
   8526       (setf (lsp--client-remote? remote-client) t
   8527             (lsp--client-server-id remote-client) (intern
   8528                                                    (format "%s-tramp"
   8529                                                            (lsp--client-server-id client)))
   8530             ;; disable automatic download
   8531             (lsp--client-download-server-fn remote-client) nil)
   8532       (lsp-register-client remote-client))))
   8533 
   8534 (defun lsp--create-initialization-options (_session client)
   8535   "Create initialization-options from SESSION and CLIENT.
   8536 Add workspace folders depending on server being multiroot and
   8537 session workspace folder configuration for the server."
   8538   (let* ((initialization-options-or-fn (lsp--client-initialization-options client)))
   8539     (if (functionp initialization-options-or-fn)
   8540         (funcall initialization-options-or-fn)
   8541       initialization-options-or-fn)))
   8542 
   8543 (defvar lsp-client-settings (make-hash-table :test 'equal)
   8544   "For internal use, any external users please use
   8545   `lsp-register-custom-settings' function instead")
   8546 
   8547 (defun lsp-register-custom-settings (props)
   8548   "Register PROPS.
   8549 PROPS is list of triple (path value boolean?) where PATH is the path to the
   8550 property; VALUE can be a literal value, symbol to be evaluated, or either a
   8551 function or lambda function to be called without arguments; BOOLEAN? is an
   8552 optional flag that should be non-nil for boolean settings, when it is nil the
   8553 property will be ignored if the VALUE is nil.
   8554 
   8555 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))'
   8556 \(note the double parentheses)"
   8557   (mapc
   8558    (-lambda ((path . rest))
   8559      (puthash path rest lsp-client-settings))
   8560    props))
   8561 
   8562 (defun lsp-region-text (region)
   8563   "Get the text for REGION in current buffer."
   8564   (-let (((start . end) (lsp--range-to-region region)))
   8565     (buffer-substring-no-properties start end)))
   8566 
   8567 (defun lsp-ht-set (tbl paths value)
   8568   "Set nested hash table value.
   8569 TBL - a hash table, PATHS is the path to the nested VALUE."
   8570   (pcase paths
   8571     (`(,path) (ht-set! tbl path value))
   8572     (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl)
   8573                                            (let ((temp-tbl (ht)))
   8574                                              (ht-set! tbl path temp-tbl)
   8575                                              temp-tbl))))
   8576                        (lsp-ht-set nested-tbl rst value)))))
   8577 
   8578 ;; sections
   8579 
   8580 (defalias 'defcustom-lsp 'lsp-defcustom)
   8581 
   8582 (defmacro lsp-defcustom (symbol standard doc &rest args)
   8583   "Defines `lsp-mode' server property."
   8584   (declare (doc-string 3) (debug (name body))
   8585            (indent defun))
   8586   (let ((path (plist-get args :lsp-path)))
   8587     (cl-remf args :lsp-path)
   8588     `(progn
   8589        (lsp-register-custom-settings
   8590         (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type))))))
   8591 
   8592        (defcustom ,symbol ,standard ,doc
   8593          :set (lambda (sym val)
   8594                 (lsp--set-custom-property sym val ,path))
   8595          ,@args))))
   8596 
   8597 (defun lsp--set-custom-property (sym val path)
   8598   (set sym val)
   8599   (let ((section (cl-first (s-split "\\." path))))
   8600     (mapc (lambda (workspace)
   8601             (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace))
   8602                               section)
   8603               (with-lsp-workspace workspace
   8604                 (lsp--set-configuration (lsp-configuration-section section)))))
   8605           (lsp--session-workspaces (lsp-session)))))
   8606 
   8607 (defun lsp-configuration-section (section)
   8608   "Get settings for SECTION."
   8609   (let ((ret (ht-create)))
   8610     (maphash (-lambda (path (variable boolean?))
   8611                (when (s-matches? (concat (regexp-quote section) "\\..*") path)
   8612                  (let* ((symbol-value (-> variable
   8613                                           lsp-resolve-value
   8614                                           lsp-resolve-value))
   8615                         (value (if (and boolean? (not symbol-value))
   8616                                    :json-false
   8617                                  symbol-value)))
   8618                    (when (or boolean? value)
   8619                      (lsp-ht-set ret (s-split "\\." path) value)))))
   8620              lsp-client-settings)
   8621     ret))
   8622 
   8623 
   8624 (defun lsp--start-connection (session client project-root)
   8625   "Initiates connection created from CLIENT for PROJECT-ROOT.
   8626 SESSION is the active session."
   8627   (when (lsp--client-multi-root client)
   8628     (cl-pushnew project-root (gethash (lsp--client-server-id client)
   8629                                       (lsp-session-server-id->folders session))))
   8630   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)
   8631 
   8632   (unwind-protect
   8633       (lsp--start-workspace session client project-root (lsp--create-initialization-options session client))
   8634     (lsp--spinner-stop)))
   8635 
   8636 ;; lsp-log-io-mode
   8637 
   8638 (defvar lsp-log-io-mode-map
   8639   (let ((map (make-sparse-keymap)))
   8640     (define-key map (kbd "M-n") #'lsp-log-io-next)
   8641     (define-key map (kbd "M-p") #'lsp-log-io-prev)
   8642     (define-key map (kbd "k") #'lsp--erase-log-buffer)
   8643     (define-key map (kbd "K") #'lsp--erase-session-log-buffers)
   8644     map)
   8645   "Keymap for lsp log buffer mode.")
   8646 
   8647 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo"
   8648   "Special mode for viewing IO logs.")
   8649 
   8650 (defun lsp-workspace-show-log (workspace)
   8651   "Display the log buffer of WORKSPACE."
   8652   (interactive
   8653    (list (if lsp-log-io
   8654              (if (eq (length (lsp-workspaces)) 1)
   8655                  (cl-first (lsp-workspaces))
   8656                (lsp--completing-read "Workspace: " (lsp-workspaces)
   8657                                      #'lsp--workspace-print nil t))
   8658            (user-error "IO logging is disabled"))))
   8659   (pop-to-buffer (lsp--get-log-buffer-create workspace)))
   8660 
   8661 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log)
   8662 
   8663 (defun lsp--get-log-buffer-create (workspace)
   8664   "Return the lsp log buffer of WORKSPACE, creating a new one if needed."
   8665   (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8666          (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id)))
   8667     (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid))))
   8668 
   8669 (defun lsp--erase-log-buffer (&optional all)
   8670   "Delete contents of current lsp log buffer.
   8671 When ALL is t, erase all log buffers of the running session."
   8672   (interactive)
   8673   (let* ((workspaces (lsp--session-workspaces (lsp-session)))
   8674          (current-log-buffer (current-buffer)))
   8675     (dolist (w workspaces)
   8676       (let ((b (lsp--get-log-buffer-create w)))
   8677         (when (or all (eq b current-log-buffer))
   8678           (with-current-buffer b
   8679             (let ((inhibit-read-only t))
   8680               (erase-buffer))))))))
   8681 
   8682 (defun lsp--erase-session-log-buffers ()
   8683   "Erase log buffers of the running session."
   8684   (interactive)
   8685   (lsp--erase-log-buffer t))
   8686 
   8687 (defun lsp-log-io-next (arg)
   8688   "Move to next log entry."
   8689   (interactive "P")
   8690   (ewoc-goto-next lsp--log-io-ewoc (or arg 1)))
   8691 
   8692 (defun lsp-log-io-prev (arg)
   8693   "Move to previous log entry."
   8694   (interactive "P")
   8695   (ewoc-goto-prev lsp--log-io-ewoc (or arg 1)))
   8696 
   8697 
   8698 
   8699 (cl-defmethod lsp-process-id ((process process))
   8700   (process-id process))
   8701 
   8702 (cl-defmethod lsp-process-name ((process process)) (process-name process))
   8703 
   8704 (cl-defmethod lsp-process-status ((process process)) (process-status process))
   8705 
   8706 (cl-defmethod lsp-process-kill ((process process))
   8707   (when (process-live-p process)
   8708     (kill-process process)))
   8709 
   8710 (cl-defmethod lsp-process-send ((process process) message)
   8711   (condition-case err
   8712       (process-send-string process (lsp--make-message message))
   8713     (error (lsp--error "Sending to process failed with the following error: %s"
   8714                        (error-message-string err)))))
   8715 
   8716 (cl-defmethod lsp-process-cleanup (process)
   8717   ;; Kill standard error buffer only if the process exited normally.
   8718   ;; Leave it intact otherwise for debugging purposes.
   8719   (let ((buffer (-> process process-name get-buffer)))
   8720     (when (and (eq (process-status process) 'exit)
   8721                (zerop (process-exit-status process))
   8722                (buffer-live-p buffer))
   8723       (kill-buffer buffer))))
   8724 
   8725 
   8726 ;; native JSONRPC
   8727 
   8728 (declare-function json-rpc "ext:json")
   8729 (declare-function json-rpc-connection "ext:json")
   8730 (declare-function json-rpc-send "ext:json")
   8731 (declare-function json-rpc-shutdown "ext:json")
   8732 (declare-function json-rpc-stderr "ext:json")
   8733 (declare-function json-rpc-pid "ext:json")
   8734 
   8735 (defvar lsp-json-rpc-thread nil)
   8736 (defvar lsp-json-rpc-queue nil)
   8737 (defvar lsp-json-rpc-done nil)
   8738 (defvar lsp-json-rpc-mutex (make-mutex))
   8739 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex))
   8740 
   8741 (defun lsp-json-rpc-process-queue ()
   8742   (while (not lsp-json-rpc-done)
   8743     (while lsp-json-rpc-queue
   8744       (-let (((proc . message) (pop lsp-json-rpc-queue)))
   8745         (json-rpc-send
   8746          proc message
   8747          :null-object nil
   8748          :false-object :json-false)))
   8749     (with-mutex lsp-json-rpc-mutex
   8750       (condition-wait lsp-json-rpc-condition))))
   8751 
   8752 (cl-defmethod lsp-process-id (process) (json-rpc-pid process))
   8753 
   8754 (cl-defmethod lsp-process-name (_process) "TBD")
   8755 
   8756 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process))
   8757 
   8758 (cl-defmethod lsp-process-send (proc message)
   8759   (unless lsp-json-rpc-thread
   8760     (with-current-buffer (get-buffer-create " *json-rpc*")
   8761       (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*"))))
   8762 
   8763   (with-mutex lsp-json-rpc-mutex
   8764     (setq lsp-json-rpc-queue (append lsp-json-rpc-queue
   8765                                      (list (cons proc message))))
   8766     (condition-notify lsp-json-rpc-condition)))
   8767 
   8768 (cl-defmethod lsp-process-cleanup (_proc))
   8769 
   8770 (defun lsp-json-rpc-connection (workspace command)
   8771   (let ((con (apply #'json-rpc-connection command))
   8772         (object-type (if lsp-use-plists 'plist 'hash-table)))
   8773     (with-current-buffer (get-buffer-create " *json-rpc*")
   8774       (make-thread
   8775        (lambda ()
   8776          (json-rpc
   8777           con
   8778           (lambda (result err done)
   8779             (run-with-timer
   8780              0.0
   8781              nil
   8782              (lambda ()
   8783                (cond
   8784                 (result (lsp--parser-on-message result workspace))
   8785                 (err (warn "Json parsing failed with the following error: %s" err))
   8786                 (done (lsp--handle-process-exit workspace ""))))))
   8787           :object-type object-type
   8788           :null-object nil
   8789           :false-object nil))
   8790        "*json-rpc-connection*"))
   8791     (cons con con)))
   8792 
   8793 (defun lsp-json-rpc-stderr ()
   8794   (interactive)
   8795   (--when-let (pcase (lsp-workspaces)
   8796                 (`nil (user-error "There are no active servers in the current buffer"))
   8797                 (`(,workspace) workspace)
   8798                 (workspaces (lsp--completing-read "Select server: "
   8799                                                   workspaces
   8800                                                   'lsp--workspace-print nil t)))
   8801     (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it)))
   8802           (buffer (format "*stderr-%s*" (lsp--workspace-print it)) ))
   8803       (with-current-buffer (get-buffer-create buffer)
   8804         (with-help-window buffer
   8805           (insert content))))))
   8806 
   8807 
   8808 (defun lsp--workspace-print (workspace)
   8809   "Visual representation WORKSPACE."
   8810   (let* ((proc (lsp--workspace-cmd-proc workspace))
   8811          (status (lsp--workspace-status workspace))
   8812          (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8813          (pid (lsp-process-id proc)))
   8814 
   8815     (if (eq 'initialized status)
   8816         (format "%s:%s" server-id pid)
   8817       (format "%s:%s/%s" server-id pid status))))
   8818 
   8819 (defun lsp--map-tree-widget (m)
   8820   "Build `tree-widget' from a hash-table or plist M."
   8821   (when (lsp-structure-p m)
   8822     (let (nodes)
   8823       (lsp-map (lambda (k v)
   8824                  (push `(tree-widget
   8825                          :tag ,(if (lsp-structure-p v)
   8826                                    (format "%s:" k)
   8827                                  (format "%s: %s" k
   8828                                          (propertize (format "%s" v)
   8829                                                      'face
   8830                                                      'font-lock-string-face)))
   8831                          :open t
   8832                          ,@(lsp--map-tree-widget v))
   8833                        nodes))
   8834                m)
   8835       nodes)))
   8836 
   8837 (defun lsp-buffer-name (buffer-id)
   8838   (if-let ((buffer-name (plist-get buffer-id :buffer-name)))
   8839       (funcall buffer-name buffer-id)
   8840     (buffer-name buffer-id)))
   8841 
   8842 (defun lsp--render-workspace (workspace)
   8843   "Tree node representation of WORKSPACE."
   8844   `(tree-widget :tag ,(lsp--workspace-print workspace)
   8845                 :open t
   8846                 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face)
   8847                              :open t
   8848                              ,@(->> workspace
   8849                                     (lsp--workspace-buffers)
   8850                                     (--map `(tree-widget
   8851                                              :tag ,(when (lsp-buffer-live-p it)
   8852                                                      (let ((buffer-name (lsp-buffer-name it)))
   8853                                                        (if (lsp-with-current-buffer it buffer-read-only)
   8854                                                            (propertize buffer-name 'face 'font-lock-constant-face)
   8855                                                          buffer-name)))))))
   8856                 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face)
   8857                              ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget))))
   8858 
   8859 (define-derived-mode lsp-browser-mode special-mode "LspBrowser"
   8860   "Define mode for displaying lsp sessions."
   8861   (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t)))))
   8862 
   8863 (defun lsp-describe-session ()
   8864   "Describes current `lsp-session'."
   8865   (interactive)
   8866   (let ((session (lsp-session))
   8867         (buf (get-buffer-create "*lsp session*"))
   8868         (root (lsp-workspace-root)))
   8869     (with-current-buffer buf
   8870       (lsp-browser-mode)
   8871       (let ((inhibit-read-only t))
   8872         (erase-buffer)
   8873         (--each (lsp-session-folders session)
   8874           (widget-create
   8875            `(tree-widget
   8876              :tag ,(propertize it 'face 'font-lock-keyword-face)
   8877              :open t
   8878              ,@(->> session
   8879                     (lsp-session-folder->servers)
   8880                     (gethash it)
   8881                     (-map 'lsp--render-workspace)))))))
   8882     (pop-to-buffer buf)
   8883     (goto-char (point-min))
   8884     (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag)
   8885              until (or (and root (string= tag root)) (eobp))
   8886              do (goto-char (next-overlay-change (point))))))
   8887 
   8888 (defun lsp--session-workspaces (session)
   8889   "Get all workspaces that are part of the SESSION."
   8890   (-> session lsp-session-folder->servers hash-table-values -flatten -uniq))
   8891 
   8892 (defun lsp--find-multiroot-workspace (session client project-root)
   8893   "Look for a multiroot connection in SESSION created from CLIENT for
   8894 PROJECT-ROOT and BUFFER-MAJOR-MODE."
   8895   (when (lsp--client-multi-root client)
   8896     (-when-let (multi-root-workspace (->> session
   8897                                           (lsp--session-workspaces)
   8898                                           (--first (eq (-> it lsp--workspace-client lsp--client-server-id)
   8899                                                        (lsp--client-server-id client)))))
   8900       (with-lsp-workspace multi-root-workspace
   8901         (lsp-notify "workspace/didChangeWorkspaceFolders"
   8902                     (lsp-make-did-change-workspace-folders-params
   8903                      :event (lsp-make-workspace-folders-change-event
   8904                              :added (vector (lsp-make-workspace-folder
   8905                                              :uri (lsp--path-to-uri project-root)
   8906                                              :name (f-filename project-root)))
   8907                              :removed []))))
   8908 
   8909       (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace))
   8910       (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root))
   8911 
   8912       (lsp--persist-session session)
   8913 
   8914       (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace))
   8915       (lsp--open-in-workspace multi-root-workspace)
   8916 
   8917       multi-root-workspace)))
   8918 
   8919 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder)
   8920   "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT.
   8921 IGNORE-MULTI-FOLDER to ignore multi folder server."
   8922   (-map (lambda (client)
   8923           (or
   8924            (lsp--find-workspace session client project-root)
   8925            (unless ignore-multi-folder
   8926              (lsp--find-multiroot-workspace session client project-root))
   8927            (lsp--start-connection session client project-root)))
   8928         clients))
   8929 
   8930 (defun lsp--spinner-stop ()
   8931   "Stop the spinner in case all of the workspaces are started."
   8932   (when (--all? (eq (lsp--workspace-status it) 'initialized)
   8933                 lsp--buffer-workspaces)
   8934     (spinner-stop)))
   8935 
   8936 (defun lsp--open-in-workspace (workspace)
   8937   "Open in existing WORKSPACE."
   8938   (if (eq 'initialized (lsp--workspace-status workspace))
   8939       ;; when workspace is initialized just call document did open.
   8940       (progn
   8941         (with-lsp-workspace workspace
   8942           (when-let ((before-document-open-fn (-> workspace
   8943                                                   lsp--workspace-client
   8944                                                   lsp--client-before-file-open-fn)))
   8945             (funcall before-document-open-fn workspace))
   8946           (lsp--text-document-did-open))
   8947         (lsp--spinner-stop))
   8948     ;; when it is not initialized
   8949     (lsp--spinner-start)
   8950     (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace))))
   8951 
   8952 (defun lsp--find-workspace (session client project-root)
   8953   "Find server connection created with CLIENT in SESSION for PROJECT-ROOT."
   8954   (when-let ((workspace (->> session
   8955                              (lsp-session-folder->servers)
   8956                              (gethash project-root)
   8957                              (--first (eql (-> it lsp--workspace-client lsp--client-server-id)
   8958                                            (lsp--client-server-id client))))))
   8959     (lsp--open-in-workspace workspace)
   8960     workspace))
   8961 
   8962 (defun lsp--read-char (prompt &optional options)
   8963   "Wrapper for `read-char-from-minibuffer' if Emacs +27.
   8964 Fallback to `read-key' otherwise.
   8965 PROMPT is the message and OPTIONS the available options."
   8966   (if (fboundp 'read-char-from-minibuffer)
   8967       (read-char-from-minibuffer prompt options)
   8968     (read-key prompt)))
   8969 
   8970 (defun lsp--find-root-interactively (session)
   8971   "Find project interactively.
   8972 Returns nil if the project should not be added to the current SESSION."
   8973   (condition-case nil
   8974       (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory))
   8975              (action (lsp--read-char
   8976                       (format
   8977                        "%s is not part of any project.
   8978 
   8979 %s ==> Import project root %s
   8980 %s ==> Import project by selecting root directory interactively
   8981 %s ==> Import project at current directory %s
   8982 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist
   8983 %s ==> Do not ask again for the current project by selecting ignore path interactively
   8984 %s ==> Do nothing: ask again when opening other files from the current project
   8985 
   8986 Select action: "
   8987                        (propertize (buffer-name) 'face 'bold)
   8988                        (propertize "i" 'face 'success)
   8989                        (propertize project-root-suggestion 'face 'bold)
   8990                        (propertize "I" 'face 'success)
   8991                        (propertize "." 'face 'success)
   8992                        (propertize default-directory 'face 'bold)
   8993                        (propertize "d" 'face 'warning)
   8994                        (propertize project-root-suggestion 'face 'bold)
   8995                        (propertize "D" 'face 'warning)
   8996                        (propertize "n" 'face 'warning))
   8997                       '(?i ?\r ?I ?. ?d ?D ?n))))
   8998         (cl-case action
   8999           (?i project-root-suggestion)
   9000           (?\r project-root-suggestion)
   9001           (?I (read-directory-name "Select workspace folder to add: "
   9002                                    (or project-root-suggestion default-directory)
   9003                                    nil
   9004                                    t))
   9005           (?. default-directory)
   9006           (?d (push project-root-suggestion (lsp-session-folders-blocklist session))
   9007               (lsp--persist-session session)
   9008               nil)
   9009           (?D (push (read-directory-name "Select folder to blocklist: "
   9010                                          (or project-root-suggestion default-directory)
   9011                                          nil
   9012                                          t)
   9013                     (lsp-session-folders-blocklist session))
   9014               (lsp--persist-session session)
   9015               nil)
   9016           (t nil)))
   9017     (quit)))
   9018 
   9019 (declare-function tramp-file-name-host "ext:tramp" (file) t)
   9020 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault))
   9021 
   9022 (defun lsp--files-same-host (f1 f2)
   9023   "Predicate on whether or not two files are on the same host."
   9024   (or (not (or (file-remote-p f1) (file-remote-p f2)))
   9025       (and (file-remote-p f1)
   9026            (file-remote-p f2)
   9027            (progn (require 'tramp)
   9028                   (equal (tramp-file-name-host (tramp-dissect-file-name f1))
   9029                          (tramp-file-name-host (tramp-dissect-file-name f2)))))))
   9030 
   9031 (defun lsp-find-session-folder (session file-name)
   9032   "Look in the current SESSION for folder containing FILE-NAME."
   9033   (let ((file-name-canonical (lsp-f-canonical file-name)))
   9034     (->> session
   9035          (lsp-session-folders)
   9036          (--filter (and (lsp--files-same-host it file-name-canonical)
   9037                         (or (lsp-f-same? it file-name-canonical)
   9038                             (and (f-dir? it)
   9039                                  (lsp-f-ancestor-of? it file-name-canonical)))))
   9040          (--max-by (> (length it)
   9041                       (length other))))))
   9042 
   9043 (defun lsp-find-workspace (server-id &optional file-name)
   9044   "Find workspace for SERVER-ID for FILE-NAME."
   9045   (-when-let* ((session (lsp-session))
   9046                (folder->servers (lsp-session-folder->servers session))
   9047                (workspaces (if file-name
   9048                                (gethash (lsp-find-session-folder session file-name) folder->servers)
   9049                              (lsp--session-workspaces session))))
   9050 
   9051     (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces)))
   9052 
   9053 (defun lsp--calculate-root (session file-name)
   9054   "Calculate project root for FILE-NAME in SESSION."
   9055   (and
   9056    (->> session
   9057         (lsp-session-folders-blocklist)
   9058         (--first (and (lsp--files-same-host it file-name)
   9059                       (lsp-f-ancestor-of? it file-name)
   9060                       (prog1 t
   9061                         (lsp--info "File %s is in blocklisted directory %s" file-name it))))
   9062         not)
   9063    (or
   9064     (when lsp-auto-guess-root
   9065       (lsp--suggest-project-root))
   9066     (unless lsp-guess-root-without-session
   9067       (lsp-find-session-folder session file-name))
   9068     (unless lsp-auto-guess-root
   9069       (when-let ((root-folder (lsp--find-root-interactively session)))
   9070         (if (or (not (f-equal? root-folder (expand-file-name "~/")))
   9071                 (yes-or-no-p
   9072                  (concat
   9073                   (propertize "[WARNING] " 'face 'warning)
   9074                   "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:
   9075 
   9076 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/').
   9077 2. If your file is under `~/' then create a subfolder and move that file in this folder.
   9078 
   9079 Type `No' to go back to project selection.
   9080 Type `Yes' to confirm `HOME' as project root.
   9081 Type `C-g' to cancel project import process and stop `lsp'")))
   9082             root-folder
   9083           (lsp--calculate-root session file-name)))))))
   9084 
   9085 (defun lsp--try-open-in-library-workspace ()
   9086   "Try opening current file as library file in any of the active workspace.
   9087 The library folders are defined by each client for each of the active workspace."
   9088   (when-let ((workspace (->> (lsp-session)
   9089                              (lsp--session-workspaces)
   9090                              ;; Sort the last active workspaces first as they are more likely to be
   9091                              ;; the correct ones, especially when jumping to a definition.
   9092                              (-sort (lambda (a _b)
   9093                                       (-contains? lsp--last-active-workspaces a)))
   9094                              (--first
   9095                               (and (-> it lsp--workspace-client lsp--supports-buffer?)
   9096                                    (when-let ((library-folders-fn
   9097                                                (-> it lsp--workspace-client lsp--client-library-folders-fn)))
   9098                                      (-first (lambda (library-folder)
   9099                                                (lsp-f-ancestor-of? library-folder (buffer-file-name)))
   9100                                              (funcall library-folders-fn it))))))))
   9101     (lsp--open-in-workspace workspace)
   9102     (view-mode t)
   9103     (lsp--info "Opening read-only library file %s." (buffer-file-name))
   9104     (list workspace)))
   9105 
   9106 (defun lsp--persist-session (session)
   9107   "Persist SESSION to `lsp-session-file'."
   9108   (lsp--persist lsp-session-file (make-lsp-session
   9109                                   :folders (lsp-session-folders session)
   9110                                   :folders-blocklist (lsp-session-folders-blocklist session)
   9111                                   :server-id->folders (lsp-session-server-id->folders session))))
   9112 
   9113 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder)
   9114   "Try create opening file as a project file.
   9115 When IGNORE-MULTI-FOLDER is t the lsp mode will start new
   9116 language server even if there is language server which can handle
   9117 current language. When IGNORE-MULTI-FOLDER is nil current file
   9118 will be opened in multi folder language server if there is
   9119 such."
   9120   (-let ((session (lsp-session)))
   9121     (-if-let (clients (if ask-for-client
   9122                           (list (lsp--completing-read "Select server to start: "
   9123                                                       (ht-values lsp-clients)
   9124                                                       (-compose 'symbol-name 'lsp--client-server-id) nil t))
   9125                         (lsp--find-clients)))
   9126         (-if-let (project-root (-some-> session
   9127                                  (lsp--calculate-root (buffer-file-name))
   9128                                  (lsp-f-canonical)))
   9129             (progn
   9130               ;; update project roots if needed and persist the lsp session
   9131               (unless (-contains? (lsp-session-folders session) project-root)
   9132                 (cl-pushnew project-root (lsp-session-folders session))
   9133                 (lsp--persist-session session))
   9134               (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder))
   9135           (lsp--warn "%s not in project or it is blocklisted." (buffer-name))
   9136           nil)
   9137       (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode)
   9138       nil)))
   9139 
   9140 (defun lsp-shutdown-workspace ()
   9141   "Shutdown language server."
   9142   (interactive)
   9143   (--when-let (pcase (lsp-workspaces)
   9144                 (`nil (user-error "There are no active servers in the current buffer"))
   9145                 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?"
   9146                                                        (lsp--workspace-print workspace)))
   9147                                  workspace))
   9148                 (workspaces (lsp--completing-read "Select server: "
   9149                                                   workspaces
   9150                                                   'lsp--workspace-print nil t)))
   9151     (lsp-workspace-shutdown it)))
   9152 
   9153 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1")
   9154 
   9155 (defcustom lsp-auto-select-workspace t
   9156   "Shutdown or restart a single workspace.
   9157 If set and the current buffer has only a single workspace
   9158 associated with it, `lsp-shutdown-workspace' and
   9159 `lsp-restart-workspace' will act on it without asking."
   9160   :type 'boolean
   9161   :group 'lsp-mode)
   9162 
   9163 (defun lsp--read-workspace ()
   9164   "Ask the user to select a workspace.
   9165 Errors if there are none."
   9166   (pcase (lsp-workspaces)
   9167     (`nil (error "No workspaces associated with the current buffer"))
   9168     ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace)
   9169     (workspaces (lsp--completing-read "Select workspace: " workspaces
   9170                                       #'lsp--workspace-print nil t))))
   9171 
   9172 (defun lsp-workspace-shutdown (workspace)
   9173   "Shut the workspace WORKSPACE and the language server associated with it"
   9174   (interactive (list (lsp--read-workspace)))
   9175   (lsp--warn "Stopping %s" (lsp--workspace-print workspace))
   9176   (with-lsp-workspace workspace (lsp--shutdown-workspace)))
   9177 
   9178 (defun lsp-disconnect ()
   9179   "Disconnect the buffer from the language server."
   9180   (interactive)
   9181   (lsp--text-document-did-close t)
   9182   (lsp-managed-mode -1)
   9183   (lsp-mode -1)
   9184   (setq lsp--buffer-workspaces nil)
   9185   (lsp--info "Disconnected"))
   9186 
   9187 (defun lsp-restart-workspace ()
   9188   (interactive)
   9189   (--when-let (pcase (lsp-workspaces)
   9190                 (`nil (user-error "There are no active servers in the current buffer"))
   9191                 (`(,workspace) workspace)
   9192                 (workspaces (lsp--completing-read "Select server: "
   9193                                                   workspaces
   9194                                                   'lsp--workspace-print nil t)))
   9195     (lsp-workspace-restart it)))
   9196 
   9197 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1")
   9198 
   9199 (defun lsp-workspace-restart (workspace)
   9200   "Restart the workspace WORKSPACE and the language server associated with it"
   9201   (interactive (list (lsp--read-workspace)))
   9202   (lsp--warn "Restarting %s" (lsp--workspace-print workspace))
   9203   (with-lsp-workspace workspace (lsp--shutdown-workspace t)))
   9204 
   9205 ;;;###autoload
   9206 (defun lsp (&optional arg)
   9207   "Entry point for the server startup.
   9208 When ARG is t the lsp mode will start new language server even if
   9209 there is language server which can handle current language. When
   9210 ARG is nil current file will be opened in multi folder language
   9211 server if there is such. When `lsp' is called with prefix
   9212 argument ask the user to select which language server to start."
   9213   (interactive "P")
   9214 
   9215   (lsp--require-packages)
   9216 
   9217   (when (buffer-file-name)
   9218     (let (clients
   9219           (matching-clients (lsp--filter-clients
   9220                              (-andfn #'lsp--supports-buffer?
   9221                                      #'lsp--server-binary-present?))))
   9222       (cond
   9223        (matching-clients
   9224         (when (setq lsp--buffer-workspaces
   9225                     (or (and
   9226                          ;; Don't open as library file if file is part of a project.
   9227                          (not (lsp-find-session-folder (lsp-session) (buffer-file-name)))
   9228                          (lsp--try-open-in-library-workspace))
   9229                         (lsp--try-project-root-workspaces (equal arg '(4))
   9230                                                           (and arg (not (equal arg 1))))))
   9231           (lsp-mode 1)
   9232           (when lsp-auto-configure (lsp--auto-configure))
   9233           (setq lsp-buffer-uri (lsp--buffer-uri))
   9234           (lsp--info "Connected to %s."
   9235                      (apply 'concat (--map (format "[%s %s]"
   9236                                                    (lsp--workspace-print it)
   9237                                                    (lsp--workspace-root it))
   9238                                            lsp--buffer-workspaces)))))
   9239        ;; look for servers which are currently being downloaded.
   9240        ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9241                                                    #'lsp--client-download-in-progress?)))
   9242         (lsp--info "There are language server(%s) installation in progress.
   9243 The server(s) will be started in the buffer when it has finished."
   9244                    (-map #'lsp--client-server-id clients))
   9245         (seq-do (lambda (client)
   9246                   (cl-pushnew (current-buffer) (lsp--client-buffers client)))
   9247                 clients))
   9248        ;; look for servers to install
   9249        ((setq clients (lsp--filter-clients
   9250                        (-andfn #'lsp--supports-buffer?
   9251                                (-const lsp-enable-suggest-server-download)
   9252                                #'lsp--client-download-server-fn
   9253                                (-not #'lsp--client-download-in-progress?))))
   9254         (let ((client (lsp--completing-read
   9255                        (concat "Unable to find installed server supporting this file. "
   9256                                "The following servers could be installed automatically: ")
   9257                        clients
   9258                        (-compose #'symbol-name #'lsp--client-server-id)
   9259                        nil
   9260                        t)))
   9261           (cl-pushnew (current-buffer) (lsp--client-buffers client))
   9262           (lsp--install-server-internal client)))
   9263        ;; ignore other warnings
   9264        ((not lsp-warn-no-matched-clients)
   9265         nil)
   9266        ;; automatic installation disabled
   9267        ((setq clients (unless matching-clients
   9268                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9269                                                      #'lsp--client-download-server-fn
   9270                                                      (-not (-const lsp-enable-suggest-server-download))
   9271                                                      (-not #'lsp--server-binary-present?)))))
   9272         (lsp--warn "The following servers support current file but automatic download is disabled: %s
   9273 \(If you have already installed the server check *lsp-log*)."
   9274                    (mapconcat (lambda (client)
   9275                                 (symbol-name (lsp--client-server-id client)))
   9276                               clients
   9277                               " ")))
   9278        ;; no clients present
   9279        ((setq clients (unless matching-clients
   9280                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9281                                                      (-not #'lsp--server-binary-present?)))))
   9282         (lsp--warn "The following servers support current file but do not have automatic installation: %s
   9283 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages.
   9284 \(If you have already installed the server check *lsp-log*)."
   9285                    (mapconcat (lambda (client)
   9286                                 (symbol-name (lsp--client-server-id client)))
   9287                               clients
   9288                               " ")))
   9289        ;; no matches
   9290        ((-> #'lsp--supports-buffer? lsp--filter-clients not)
   9291         (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'.
   9292 This issue might be caused by:
   9293 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'.
   9294 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'.
   9295 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/ .
   9296 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/.
   9297 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients').
   9298 You can customize `lsp-warn-no-matched-clients' to disable this message."
   9299                     major-mode major-mode major-mode))))))
   9300 
   9301 (defun lsp--buffer-visible-p ()
   9302   "Return non nil if current buffer is visible."
   9303   (or (buffer-modified-p) (get-buffer-window nil t)))
   9304 
   9305 (defun lsp--init-if-visible ()
   9306   "Run `lsp' for the current buffer if the buffer is visible.
   9307 Returns non nil if `lsp' was run for the buffer."
   9308   (when (lsp--buffer-visible-p)
   9309     (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t)
   9310     (lsp)
   9311     t))
   9312 
   9313 ;;;###autoload
   9314 (defun lsp-deferred ()
   9315   "Entry point that defers server startup until buffer is visible.
   9316 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'.
   9317 This avoids overloading the server with many files when starting Emacs."
   9318   ;; Workspace may not be initialized yet. Use a buffer local variable to
   9319   ;; remember that we deferred loading of this buffer.
   9320   (setq lsp--buffer-deferred t)
   9321   (let ((buffer (current-buffer)))
   9322     ;; Avoid false positives as desktop-mode restores buffers by deferring
   9323     ;; visibility check until the stack clears.
   9324     (run-with-idle-timer 0 nil (lambda ()
   9325                                  (when (buffer-live-p buffer)
   9326                                    (with-current-buffer buffer
   9327                                      (unless (lsp--init-if-visible)
   9328                                        (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t))))))))
   9329 
   9330 
   9331 
   9332 (defvar lsp-file-truename-cache (ht))
   9333 
   9334 (defmacro lsp-with-cached-filetrue-name (&rest body)
   9335   "Executes BODY caching the `file-truename' calls."
   9336   `(let ((old-fn (symbol-function 'file-truename)))
   9337      (unwind-protect
   9338          (progn
   9339            (fset 'file-truename
   9340                  (lambda (file-name &optional counter prev-dirs)
   9341                    (or (gethash file-name lsp-file-truename-cache)
   9342                        (puthash file-name (apply old-fn (list file-name counter prev-dirs))
   9343                                 lsp-file-truename-cache))))
   9344            ,@body)
   9345        (fset 'file-truename old-fn))))
   9346 
   9347 
   9348 (defun lsp-virtual-buffer-call (key &rest args)
   9349   (when lsp--virtual-buffer
   9350     (when-let ((fn (plist-get lsp--virtual-buffer key)))
   9351       (apply fn args))))
   9352 
   9353 (defun lsp-translate-column (column)
   9354   "Translate COLUMN taking into account virtual buffers."
   9355   (or (lsp-virtual-buffer-call :real->virtual-char column)
   9356       column))
   9357 
   9358 (defun lsp-translate-line (line)
   9359   "Translate LINE taking into account virtual buffers."
   9360   (or (lsp-virtual-buffer-call :real->virtual-line line)
   9361       line))
   9362 
   9363 
   9364 ;; lsp internal validation.
   9365 
   9366 (defmacro lsp--doctor (&rest checks)
   9367   `(-let [buf (current-buffer)]
   9368      (with-current-buffer (get-buffer-create "*lsp-performance*")
   9369        (with-help-window (current-buffer)
   9370          ,@(-map (-lambda ((msg form))
   9371                    `(insert (format "%s: %s\n" ,msg
   9372                                     (let ((res (with-current-buffer buf
   9373                                                  ,form)))
   9374                                       (cond
   9375                                        ((eq res :optional) (propertize "OPTIONAL" 'face 'warning))
   9376                                        (res (propertize "OK" 'face 'success))
   9377                                        (t (propertize "ERROR" 'face 'error)))))))
   9378                  (-partition 2 checks))))))
   9379 
   9380 (define-obsolete-function-alias 'lsp-diagnose
   9381   'lsp-doctor "lsp-mode 8.0.0")
   9382 
   9383 (defun lsp-doctor ()
   9384   "Validate performance settings."
   9385   (interactive)
   9386   (lsp--doctor
   9387    "Checking for Native JSON support" (functionp 'json-serialize)
   9388    "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max)
   9389    "Check `read-process-output-max' default has been changed from 4k"
   9390    (and (boundp 'read-process-output-max)
   9391         (> read-process-output-max 4096))
   9392    "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)"
   9393    (condition-case _err
   9394        (progn (lsp--make-message (list "a" "b"))
   9395               nil)
   9396      (error t))
   9397    "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000)
   9398    "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)
   9399    "Using emacs 28+ with native compilation?"
   9400    (or (and (fboundp 'native-comp-available-p)
   9401             (native-comp-available-p))
   9402        :optional)))
   9403 
   9404 (declare-function package-version-join "ext:package")
   9405 (declare-function package-desc-version "ext:package")
   9406 (declare-function package--alist "ext:package")
   9407 
   9408 (defun lsp-version ()
   9409   "Return string describing current version of `lsp-mode'."
   9410   (interactive)
   9411   (unless (featurep 'package)
   9412     (require 'package))
   9413   (let ((ver (format "lsp-mode %s, Emacs %s, %s"
   9414                      (package-version-join
   9415                       (package-desc-version
   9416                        (car (alist-get 'lsp-mode (package--alist)))))
   9417                      emacs-version
   9418                      system-type)))
   9419     (if (called-interactively-p 'interactive)
   9420         (lsp--info "%s" ver)
   9421       ver)))
   9422 
   9423 
   9424 
   9425 ;; org-mode/virtual-buffer
   9426 
   9427 (declare-function org-babel-get-src-block-info "ext:ob-core")
   9428 (declare-function org-do-remove-indentation "ext:org-macs")
   9429 (declare-function org-src-get-lang-mode "ext:org-src")
   9430 (declare-function org-element-context "ext:org-element")
   9431 
   9432 (defun lsp--virtual-buffer-update-position ()
   9433   (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range))
   9434                                      (funcall in-range))
   9435                                    lsp--virtual-buffer-connections))
   9436       (unless (equal virtual-buffer lsp--virtual-buffer)
   9437         (lsp-org))
   9438     (when lsp-managed-mode
   9439       (lsp-managed-mode -1)
   9440       (lsp-mode -1)
   9441       (setq lsp--buffer-workspaces nil)
   9442       (setq lsp--virtual-buffer nil)
   9443       (setq lsp-buffer-uri nil)
   9444 
   9445       ;; force refresh of diagnostics
   9446       (run-hooks 'lsp-after-diagnostics-hook))))
   9447 
   9448 (defun lsp-virtual-buffer-on-change (start end length)
   9449   "Adjust on change event to be executed against the proper language server."
   9450   (let ((max-point (max end
   9451                         (or (plist-get lsp--before-change-vals :end) 0)
   9452                         (+ start length))))
   9453     (when-let ((virtual-buffer (-first (lambda (vb)
   9454                                          (let ((lsp--virtual-buffer vb))
   9455                                            (and (lsp-virtual-buffer-call :in-range start)
   9456                                                 (lsp-virtual-buffer-call :in-range max-point))))
   9457                                        lsp--virtual-buffer-connections)))
   9458       (lsp-with-current-buffer virtual-buffer
   9459         (lsp-on-change start end length
   9460                        (lambda (&rest _)
   9461                          (list :range (lsp--range (list :character 0 :line 0)
   9462                                                   lsp--virtual-buffer-point-max)
   9463                                :text (lsp--buffer-content))))))))
   9464 
   9465 (defun lsp-virtual-buffer-before-change (start _end)
   9466   (when-let ((virtual-buffer (-first (lambda (vb)
   9467                                        (lsp-with-current-buffer vb
   9468                                          (lsp-virtual-buffer-call :in-range start)))
   9469                                      lsp--virtual-buffer-connections)))
   9470     (lsp-with-current-buffer virtual-buffer
   9471       (setq lsp--virtual-buffer-point-max
   9472             (lsp--point-to-position (lsp-virtual-buffer-call :last-point))))))
   9473 
   9474 (defun lsp-patch-on-change-event ()
   9475   (remove-hook 'after-change-functions #'lsp-on-change t)
   9476   (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t)
   9477   (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t))
   9478 
   9479 (defun lsp-kill-virtual-buffers ()
   9480   (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections))
   9481 
   9482 (defun lsp--move-point-in-indentation (point indentation)
   9483   (save-excursion
   9484     (goto-char point)
   9485     (if (<= point (+ (line-beginning-position) indentation))
   9486         (line-beginning-position)
   9487       point)))
   9488 
   9489 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck")
   9490 (declare-function flycheck-add-mode "ext:flycheck")
   9491 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics")
   9492 
   9493 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn)
   9494 
   9495 (defun lsp-flycheck-add-mode (mode)
   9496   "Register flycheck support for MODE."
   9497   (lsp-diagnostics-lsp-checker-if-needed)
   9498   (unless (flycheck-checker-supports-major-mode-p 'lsp mode)
   9499     (flycheck-add-mode 'lsp mode)))
   9500 
   9501 (defun lsp-progress-spinner-type ()
   9502   "Retrieve the spinner type value, if value is not a symbol of `spinner-types
   9503 defaults to `progress-bar."
   9504   (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar))
   9505 
   9506 (defun lsp-org ()
   9507   (interactive)
   9508   (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range))
   9509                                                               (funcall in-range))
   9510                                                             lsp--virtual-buffer-connections))
   9511       (unless (equal lsp--virtual-buffer virtual-buffer)
   9512         (setq lsp--buffer-workspaces workspaces)
   9513         (setq lsp--virtual-buffer virtual-buffer)
   9514         (setq lsp-buffer-uri nil)
   9515         (lsp-mode 1)
   9516         (lsp-managed-mode 1)
   9517         (lsp-patch-on-change-event))
   9518 
   9519     (save-excursion
   9520       (-let* (virtual-buffer
   9521               (wcb (lambda (f)
   9522                      (with-current-buffer (plist-get virtual-buffer :buffer)
   9523                        (-let* (((&plist :major-mode :buffer-file-name
   9524                                         :goto-buffer :workspaces) virtual-buffer)
   9525                                (lsp--virtual-buffer virtual-buffer)
   9526                                (lsp--buffer-workspaces workspaces))
   9527                          (save-excursion
   9528                            (funcall goto-buffer)
   9529                            (funcall f))))))
   9530               ((&plist :begin :end :post-blank :language) (cl-second (org-element-context)))
   9531               ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light)))
   9532 
   9533               (file-name (if file-name
   9534                              (f-expand file-name)
   9535                            (user-error "You should specify file name in the src block header.")))
   9536               (begin-marker (progn
   9537                               (goto-char begin)
   9538                               (forward-line)
   9539                               (set-marker (make-marker) (point))))
   9540               (end-marker (progn
   9541                             (goto-char end)
   9542                             (forward-line (1- (- post-blank)))
   9543                             (set-marker (make-marker) (1+ (point)))))
   9544               (buf (current-buffer))
   9545               (src-block (buffer-substring-no-properties begin-marker
   9546                                                          (1- end-marker)))
   9547               (indentation (with-temp-buffer
   9548                              (insert src-block)
   9549 
   9550                              (goto-char (point-min))
   9551                              (let ((indentation (current-indentation)))
   9552                                (plist-put lsp--virtual-buffer :indentation indentation)
   9553                                (org-do-remove-indentation)
   9554                                (goto-char (point-min))
   9555                                (- indentation (current-indentation))))))
   9556         (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t)
   9557 
   9558         (when (fboundp 'flycheck-add-mode)
   9559           (lsp-flycheck-add-mode 'org-mode))
   9560 
   9561         (setq lsp--virtual-buffer
   9562               (list
   9563                :in-range (lambda (&optional point)
   9564                            (<= begin-marker (or point (point)) (1- end-marker)))
   9565                :goto-buffer (lambda () (goto-char begin-marker))
   9566                :buffer-string
   9567                (lambda ()
   9568                  (let ((src-block (buffer-substring-no-properties
   9569                                    begin-marker
   9570                                    (1- end-marker))))
   9571                    (with-temp-buffer
   9572                      (insert src-block)
   9573 
   9574                      (goto-char (point-min))
   9575                      (while (not (eobp))
   9576                        (delete-region (point) (if (> (+ (point) indentation) (line-end-position))
   9577                                                   (line-end-position)
   9578                                                 (+ (point) indentation)))
   9579                        (forward-line))
   9580                      (buffer-substring-no-properties (point-min)
   9581                                                      (point-max)))))
   9582                :buffer buf
   9583                :begin begin-marker
   9584                :end end-marker
   9585                :indentation indentation
   9586                :last-point (lambda () (1- end-marker))
   9587                :cur-position (lambda ()
   9588                                (lsp-save-restriction-and-excursion
   9589                                  (list :line (- (lsp--cur-line)
   9590                                                 (lsp--cur-line begin-marker))
   9591                                        :character (let ((character (- (point)
   9592                                                                       (line-beginning-position)
   9593                                                                       indentation)))
   9594                                                     (if (< character 0)
   9595                                                         0
   9596                                                       character)))))
   9597                :line/character->point (-lambda (line character)
   9598                                         (-let [inhibit-field-text-motion t]
   9599                                           (+ indentation
   9600                                              (lsp-save-restriction-and-excursion
   9601                                                (goto-char begin-marker)
   9602                                                (forward-line line)
   9603                                                (-let [line-end (line-end-position)]
   9604                                                  (if (> character (- line-end (point)))
   9605                                                      line-end
   9606                                                    (forward-char character)
   9607                                                    (point)))))))
   9608                :major-mode (org-src-get-lang-mode language)
   9609                :buffer-file-name file-name
   9610                :buffer-uri (lsp--path-to-uri file-name)
   9611                :with-current-buffer wcb
   9612                :buffer-live? (lambda (_) (buffer-live-p buf))
   9613                :buffer-name (lambda (_)
   9614                               (propertize (format "%s(%s:%s)%s"
   9615                                                   (buffer-name buf)
   9616                                                   begin-marker
   9617                                                   end-marker
   9618                                                   language)
   9619                                           'face 'italic))
   9620                :real->virtual-line (lambda (line)
   9621                                      (+ line (line-number-at-pos begin-marker) -1))
   9622                :real->virtual-char (lambda (char) (+ char indentation))
   9623                :cleanup (lambda ()
   9624                           (set-marker begin-marker nil)
   9625                           (set-marker end-marker nil))))
   9626         (setf virtual-buffer lsp--virtual-buffer)
   9627         (puthash file-name virtual-buffer lsp--virtual-buffer-mappings)
   9628         (push virtual-buffer lsp--virtual-buffer-connections)
   9629 
   9630         ;; TODO: tangle only connected sections
   9631         (add-hook 'after-save-hook 'org-babel-tangle nil t)
   9632         (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t)
   9633         (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t)
   9634 
   9635         (setq lsp--buffer-workspaces
   9636               (lsp-with-current-buffer virtual-buffer
   9637                 (lsp)
   9638                 (plist-put virtual-buffer :workspaces (lsp-workspaces))
   9639                 (lsp-workspaces)))))))
   9640 
   9641 (defun lsp-virtual-buffer-disconnect (virtual-buffer)
   9642   (interactive (list (or
   9643                       lsp--virtual-buffer
   9644                       (when lsp--virtual-buffer-connections
   9645                         (lsp--completing-read "Select virtual buffer to disconnect: "
   9646                                               lsp--virtual-buffer-connections
   9647                                               (-lambda ((&plist :buffer-file-name))
   9648                                                 buffer-file-name))))))
   9649   (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer)
   9650       (progn
   9651         (lsp-with-current-buffer virtual-buffer
   9652           (lsp--text-document-did-close))
   9653         (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections))
   9654         (when (eq virtual-buffer lsp--virtual-buffer)
   9655           (setf lsp--virtual-buffer nil))
   9656         (when cleanup (funcall cleanup))
   9657         (remhash file-name lsp--virtual-buffer-mappings)
   9658 
   9659         (lsp--virtual-buffer-update-position)
   9660         (lsp--info "Disconnected from buffer %s" file-name))
   9661     (lsp--error "Nothing to disconnect from?")))
   9662 
   9663 
   9664 ;; inlay hints
   9665 
   9666 (defface lsp-inlay-hint-face
   9667   '((t :inherit font-lock-comment-face))
   9668   "The face to use for the JavaScript inlays."
   9669   :group 'lsp-mode
   9670   :package-version '(lsp-mode . "9.0.0"))
   9671 
   9672 (defface lsp-inlay-hint-type-face
   9673   '((t :inherit lsp-inlay-hint-face))
   9674   "Face for inlay type hints (e.g. inferred variable types)."
   9675   :group 'lsp-mode
   9676   :package-version '(lsp-mode . "9.0.0"))
   9677 
   9678 (defcustom lsp-inlay-hint-type-format "%s"
   9679   "Format string for variable inlays (part of the inlay face)."
   9680   :type '(string :tag "String")
   9681   :group 'lsp-mode
   9682   :package-version '(lsp-mode . "9.0.0"))
   9683 
   9684 (defface lsp-inlay-hint-parameter-face
   9685   '((t :inherit lsp-inlay-hint-face))
   9686   "Face for inlay parameter hints (e.g. function parameter names at
   9687 call-site)."
   9688   :group 'lsp-mode
   9689   :package-version '(lsp-mode . "9.0.0"))
   9690 
   9691 (defcustom lsp-inlay-hint-param-format "%s"
   9692   "Format string for parameter inlays (part of the inlay face)."
   9693   :type '(string :tag "String")
   9694   :group 'lsp-mode
   9695   :package-version '(lsp-mode . "9.0.0"))
   9696 
   9697 (defcustom lsp-update-inlay-hints-on-scroll t
   9698   "If non-nil update inlay hints immediately when scrolling or
   9699 modifying window sizes."
   9700   :type 'boolean
   9701   :package-version '(lsp-mode . "9.0.0"))
   9702 
   9703 (defun lsp--format-inlay (text kind)
   9704   (cond
   9705    ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text))
   9706    ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text))
   9707    (t text)))
   9708 
   9709 (defun lsp--face-for-inlay (kind)
   9710   (cond
   9711    ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face)
   9712    ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face)
   9713    (t 'lsp-inlay-hint-face)))
   9714 
   9715 (defun lsp--update-inlay-hints-scroll-function (window start)
   9716   (lsp-update-inlay-hints start (window-end window t)))
   9717 
   9718 (defun lsp--update-inlay-hints ()
   9719   (lsp-update-inlay-hints (window-start) (window-end nil t)))
   9720 
   9721 (defun lsp--label-from-inlay-hints-response (label)
   9722   "Returns a string label built from an array of
   9723 InlayHintLabelParts or the argument itself if it's already a
   9724 string."
   9725   (cl-typecase label
   9726     (string label)
   9727     (vector
   9728      (string-join (mapcar (lambda (part)
   9729                             (-let (((&InlayHintLabelPart :value) part))
   9730                               value))
   9731                           label)))))
   9732 
   9733 (defun lsp-update-inlay-hints (start end)
   9734   (lsp-request-async
   9735    "textDocument/inlayHint"
   9736    (lsp-make-inlay-hints-params
   9737     :text-document (lsp--text-document-identifier)
   9738     :range (lsp-make-range :start
   9739                            (lsp-point-to-position start)
   9740                            :end
   9741                            (lsp-point-to-position end)))
   9742    (lambda (res)
   9743      (lsp--remove-overlays 'lsp-inlay-hint)
   9744      (dolist (hint res)
   9745        (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint)
   9746                (kind (or kind? lsp/inlay-hint-kind-type-hint))
   9747                (label (lsp--label-from-inlay-hints-response label))
   9748                (pos (lsp--position-to-point position))
   9749                (overlay (make-overlay pos pos nil 'front-advance 'end-advance)))
   9750          (when (stringp label)
   9751            (overlay-put overlay 'lsp-inlay-hint t)
   9752            (overlay-put overlay 'before-string
   9753                         (format "%s%s%s"
   9754                                 (if padding-left? " " "")
   9755                                 (propertize (lsp--format-inlay label kind)
   9756                                             'font-lock-face (lsp--face-for-inlay kind))
   9757                                 (if padding-right? " " "")))))))
   9758    :mode 'tick))
   9759 
   9760 (define-minor-mode lsp-inlay-hints-mode
   9761   "Mode for displaying inlay hints."
   9762   :lighter nil
   9763   (cond
   9764    ((and lsp-inlay-hints-mode lsp--buffer-workspaces)
   9765     (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t)
   9766     (when lsp-update-inlay-hints-on-scroll
   9767       (add-to-list (make-local-variable 'window-scroll-functions)
   9768                    #'lsp--update-inlay-hints-scroll-function)))
   9769    (t
   9770     (lsp--remove-overlays 'lsp-inlay-hint)
   9771     (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t)
   9772     (setf window-scroll-functions
   9773           (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions)))))
   9774 
   9775 
   9776 
   9777 ;;;###autoload
   9778 (defun lsp-start-plain ()
   9779   "Start `lsp-mode' using minimal configuration using the latest `melpa' version
   9780 of the packages.
   9781 
   9782 In case the major-mode that you are using for "
   9783   (interactive)
   9784   (let ((start-plain (make-temp-file "plain" nil ".el")))
   9785     (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el"
   9786                    start-plain t)
   9787     (async-shell-command
   9788      (format "%s -q -l %s %s"
   9789              (expand-file-name invocation-name invocation-directory)
   9790              start-plain
   9791              (or (buffer-file-name) ""))
   9792      (generate-new-buffer " *lsp-start-plain*"))))
   9793 
   9794 
   9795 
   9796 (provide 'lsp-mode)
   9797 ;;; lsp-mode.el ends here