config

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

lsp-mode.el (427830B)


      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     (v-mode . "v")
    919     (vhdl-mode . "vhdl")
    920     (vhdl-ts-mode . "vhdl")
    921     (verilog-mode . "verilog")
    922     (terraform-mode . "terraform")
    923     (ess-julia-mode . "julia")
    924     (ess-r-mode . "r")
    925     (crystal-mode . "crystal")
    926     (nim-mode . "nim")
    927     (dhall-mode . "dhall")
    928     (cmake-mode . "cmake")
    929     (cmake-ts-mode . "cmake")
    930     (purescript-mode . "purescript")
    931     (gdscript-mode . "gdscript")
    932     (gdscript-ts-mode . "gdscript")
    933     (perl-mode . "perl")
    934     (cperl-mode . "perl")
    935     (robot-mode . "robot")
    936     (racket-mode . "racket")
    937     (nix-mode . "nix")
    938     (nix-ts-mode . "Nix")
    939     (prolog-mode . "prolog")
    940     (vala-mode . "vala")
    941     (actionscript-mode . "actionscript")
    942     (d-mode . "d")
    943     (zig-mode . "zig")
    944     (text-mode . "plaintext")
    945     (markdown-mode . "markdown")
    946     (gfm-mode . "markdown")
    947     (beancount-mode . "beancount")
    948     (conf-toml-mode . "toml")
    949     (toml-ts-mode . "toml")
    950     (org-mode . "org")
    951     (org-journal-mode . "org")
    952     (nginx-mode . "nginx")
    953     (magik-mode . "magik")
    954     (magik-ts-mode . "magik")
    955     (idris-mode . "idris")
    956     (idris2-mode . "idris2")
    957     (gleam-mode . "gleam")
    958     (graphviz-dot-mode . "dot")
    959     (tiltfile-mode . "tiltfile")
    960     (solidity-mode . "solidity")
    961     (bibtex-mode . "bibtex")
    962     (rst-mode . "restructuredtext")
    963     (glsl-mode . "glsl")
    964     (shader-mode . "shaderlab")
    965     (wgsl-mode . "wgsl")
    966     (jq-mode . "jq")
    967     (jq-ts-mode . "jq")
    968     (protobuf-mode . "protobuf")
    969     (nushell-mode . "nushell")
    970     (nushell-ts-mode . "nushell")
    971     (meson-mode . "meson")
    972     (yang-mode . "yang"))
    973   "Language id configuration.")
    974 
    975 (defvar lsp--last-active-workspaces nil
    976   "Keep track of last active workspace.
    977 We want to try the last workspace first when jumping into a library
    978 directory")
    979 
    980 (defvar lsp-method-requirements
    981   '(("textDocument/callHierarchy" :capability :callHierarchyProvider)
    982     ("textDocument/codeAction" :capability :codeActionProvider)
    983     ("codeAction/resolve"
    984      :check-command (lambda (workspace)
    985                       (with-lsp-workspace workspace
    986                         (lsp:code-action-options-resolve-provider?
    987                          (lsp--capability-for-method "textDocument/codeAction")))))
    988     ("textDocument/codeLens" :capability :codeLensProvider)
    989     ("textDocument/completion" :capability :completionProvider)
    990     ("completionItem/resolve"
    991      :check-command (lambda (wk)
    992                       (with-lsp-workspace wk
    993                         (lsp:completion-options-resolve-provider?
    994                          (lsp--capability-for-method "textDocument/completion")))))
    995     ("textDocument/declaration" :capability :declarationProvider)
    996     ("textDocument/definition" :capability :definitionProvider)
    997     ("textDocument/documentColor" :capability :colorProvider)
    998     ("textDocument/documentLink" :capability :documentLinkProvider)
    999     ("textDocument/inlayHint" :capability :inlayHintProvider)
   1000     ("textDocument/documentHighlight" :capability :documentHighlightProvider)
   1001     ("textDocument/documentSymbol" :capability :documentSymbolProvider)
   1002     ("textDocument/foldingRange" :capability :foldingRangeProvider)
   1003     ("textDocument/formatting" :capability :documentFormattingProvider)
   1004     ("textDocument/hover" :capability :hoverProvider)
   1005     ("textDocument/implementation" :capability :implementationProvider)
   1006     ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider)
   1007     ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider)
   1008     ("textDocument/prepareRename"
   1009      :check-command (lambda (workspace)
   1010                       (with-lsp-workspace workspace
   1011                         (lsp:rename-options-prepare-provider?
   1012                          (lsp--capability-for-method "textDocument/rename")))))
   1013     ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider)
   1014     ("textDocument/references" :capability :referencesProvider)
   1015     ("textDocument/rename" :capability :renameProvider)
   1016     ("textDocument/selectionRange" :capability :selectionRangeProvider)
   1017     ("textDocument/semanticTokens" :capability :semanticTokensProvider)
   1018     ("textDocument/semanticTokensFull"
   1019      :check-command (lambda (workspace)
   1020                       (with-lsp-workspace workspace
   1021                         (lsp-get (lsp--capability :semanticTokensProvider) :full))))
   1022     ("textDocument/semanticTokensFull/Delta"
   1023      :check-command (lambda (workspace)
   1024                       (with-lsp-workspace workspace
   1025                         (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full)))
   1026                           (and (not (booleanp capFull)) (lsp-get capFull :delta))))))
   1027     ("textDocument/semanticTokensRangeProvider"
   1028      :check-command (lambda (workspace)
   1029                       (with-lsp-workspace workspace
   1030                         (lsp-get (lsp--capability :semanticTokensProvider) :range))))
   1031     ("textDocument/signatureHelp" :capability :signatureHelpProvider)
   1032     ("textDocument/typeDefinition" :capability :typeDefinitionProvider)
   1033     ("textDocument/typeHierarchy" :capability :typeHierarchyProvider)
   1034     ("workspace/executeCommand" :capability :executeCommandProvider)
   1035     ("workspace/symbol" :capability :workspaceSymbolProvider))
   1036 
   1037   "Map methods to requirements.
   1038 It is used by request-sending functions to determine which server
   1039 must be used for handling a particular message.")
   1040 
   1041 (defconst lsp--file-change-type
   1042   `((created . 1)
   1043     (changed . 2)
   1044     (deleted . 3)))
   1045 
   1046 (defconst lsp--watch-kind
   1047   `((create . 1)
   1048     (change . 2)
   1049     (delete . 4)))
   1050 
   1051 (defvar lsp-window-body-width 40
   1052   "Window body width when rendering doc.")
   1053 
   1054 (defface lsp-face-highlight-textual
   1055   '((t :inherit highlight))
   1056   "Face used for textual occurrences of symbols."
   1057   :group 'lsp-mode)
   1058 
   1059 (defface lsp-face-highlight-read
   1060   '((t :inherit highlight :underline t))
   1061   "Face used for highlighting symbols being read."
   1062   :group 'lsp-mode)
   1063 
   1064 (defface lsp-face-highlight-write
   1065   '((t :inherit highlight :weight bold))
   1066   "Face used for highlighting symbols being written to."
   1067   :group 'lsp-mode)
   1068 
   1069 (define-obsolete-variable-alias 'lsp-lens-auto-enable
   1070   'lsp-lens-enable "lsp-mode 7.0.1")
   1071 
   1072 (defcustom lsp-lens-enable t
   1073   "Auto enable lenses if server supports."
   1074   :group 'lsp-lens
   1075   :type 'boolean
   1076   :package-version '(lsp-mode . "6.3"))
   1077 
   1078 (defcustom lsp-symbol-highlighting-skip-current nil
   1079   "If non-nil skip current symbol when setting symbol highlights."
   1080   :group 'lsp-mode
   1081   :type 'boolean)
   1082 
   1083 (defcustom lsp-file-watch-threshold 1000
   1084   "Show warning if the files to watch are more than.
   1085 Set to nil to disable the warning."
   1086   :type 'number
   1087   :group 'lsp-mode)
   1088 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i))))
   1089 
   1090 (defvar lsp-custom-markup-modes
   1091   '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic"))
   1092   "Mode to uses with markdown code blocks.
   1093 They are added to `markdown-code-lang-modes'")
   1094 
   1095 (defcustom lsp-signature-render-documentation t
   1096   "Display signature documentation in `eldoc'."
   1097   :type 'boolean
   1098   :group 'lsp-mode
   1099   :package-version '(lsp-mode . "6.2"))
   1100 
   1101 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request)
   1102   "Auto activate signature conditions."
   1103   :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char)
   1104                          (const :tag "After selected completion." :after-completion)
   1105                          (const :tag "When the server has sent show signature help." :on-server-request)))
   1106   :group 'lsp-mode
   1107   :package-version '(lsp-mode . "6.2"))
   1108 
   1109 (defcustom lsp-signature-doc-lines 20
   1110   "If number, limit the number of lines to show in the docs."
   1111   :type 'number
   1112   :group 'lsp-mode
   1113   :package-version '(lsp-mode . "6.3"))
   1114 
   1115 (defcustom lsp-signature-function 'lsp-lv-message
   1116   "The function used for displaying signature info.
   1117 It will be called with one param - the signature info. When
   1118 called with nil the signature info must be cleared."
   1119   :type 'function
   1120   :group 'lsp-mode
   1121   :package-version '(lsp-mode . "6.3"))
   1122 
   1123 (defcustom lsp-keymap-prefix "s-l"
   1124   "LSP-mode keymap prefix."
   1125   :group 'lsp-mode
   1126   :type 'string
   1127   :package-version '(lsp-mode . "6.3"))
   1128 
   1129 (defvar-local lsp--buffer-workspaces ()
   1130   "List of the buffer workspaces.")
   1131 
   1132 (defvar-local lsp--buffer-deferred nil
   1133   "Whether buffer was loaded via `lsp-deferred'.")
   1134 
   1135 (defvar lsp--session nil
   1136   "Contain the `lsp-session' for the current Emacs instance.")
   1137 
   1138 (defvar lsp--tcp-port 10000)
   1139 
   1140 (defvar lsp--client-packages-required nil
   1141   "If nil, `lsp-client-packages' are yet to be required.")
   1142 
   1143 (defvar lsp--tcp-server-port 0
   1144   "The server socket which is opened when using `lsp-tcp-server' (a server
   1145 socket is opened in Emacs and the language server connects to it).  The
   1146 default value of 0 ensures that a random high port is used. Set it to a positive
   1147 integer to use a specific port.")
   1148 
   1149 (defvar lsp--tcp-server-wait-seconds 10
   1150   "Wait this amount of time for the client to connect to our server socket
   1151 when using `lsp-tcp-server'.")
   1152 
   1153 (defvar-local lsp--document-symbols nil
   1154   "The latest document symbols.")
   1155 
   1156 (defvar-local lsp--document-selection-range-cache nil
   1157   "The document selection cache.")
   1158 
   1159 (defvar-local lsp--document-symbols-request-async nil
   1160   "If non-nil, request document symbols asynchronously.")
   1161 
   1162 (defvar-local lsp--document-symbols-tick -1
   1163   "The value of `buffer-chars-modified-tick' when document
   1164   symbols were last retrieved.")
   1165 
   1166 (defvar-local lsp--have-document-highlights nil
   1167   "Set to `t' on symbol highlighting, cleared on
   1168 `lsp--cleanup-highlights-if-needed'. Checking a separately
   1169 defined flag is substantially faster than unconditionally
   1170 calling `remove-overlays'.")
   1171 
   1172 ;; Buffer local variable for storing number of lines.
   1173 (defvar lsp--log-lines)
   1174 
   1175 (defvar-local lsp--eldoc-saved-message nil)
   1176 
   1177 (defvar lsp--on-change-timer nil)
   1178 (defvar lsp--on-idle-timer nil)
   1179 
   1180 (defvar-local lsp--signature-last nil)
   1181 (defvar-local lsp--signature-last-index nil)
   1182 (defvar lsp--signature-last-buffer nil)
   1183 
   1184 (defvar-local lsp--virtual-buffer-point-max nil)
   1185 
   1186 (cl-defmethod lsp-execute-command (_server _command _arguments)
   1187   "Ask SERVER to execute COMMAND with ARGUMENTS.")
   1188 
   1189 (defun lsp-elt (sequence n)
   1190   "Return Nth element of SEQUENCE or nil if N is out of range."
   1191   (cond
   1192    ((listp sequence) (elt sequence n))
   1193    ((arrayp sequence)
   1194     (and (> (length sequence) n) (aref sequence n)))
   1195    (t (and (> (length sequence) n) (elt sequence n)))))
   1196 
   1197 ;; define seq-first and seq-rest for older emacs
   1198 (defun lsp-seq-first (sequence)
   1199   "Return the first element of SEQUENCE."
   1200   (lsp-elt sequence 0))
   1201 
   1202 (defun lsp-seq-rest (sequence)
   1203   "Return a sequence of the elements of SEQUENCE except the first one."
   1204   (seq-drop sequence 1))
   1205 
   1206 ;;;###autoload
   1207 (defun lsp--string-listp (sequence)
   1208   "Return t if all elements of SEQUENCE are strings, else nil."
   1209   (not (seq-find (lambda (x) (not (stringp x))) sequence)))
   1210 
   1211 (defun lsp--string-vector-p (candidate)
   1212   "Returns true if CANDIDATE is a vector data structure and
   1213 every element of it is of type string, else nil."
   1214   (and
   1215    (vectorp candidate)
   1216    (seq-every-p #'stringp candidate)))
   1217 
   1218 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0")
   1219 
   1220 (defun lsp--editable-vector-match (widget value)
   1221   "Function for `lsp-editable-vector' :match."
   1222   ;; Value must be a list or a vector and all the members must match the type.
   1223   (and (or (listp value) (vectorp value))
   1224        (length (cdr (lsp--editable-vector-match-inline widget value)))))
   1225 
   1226 (defun lsp--editable-vector-match-inline (widget value)
   1227   "Value for `lsp-editable-vector' :match-inline."
   1228   (let ((type (nth 0 (widget-get widget :args)))
   1229         (ok t)
   1230         found)
   1231     (while (and value ok)
   1232       (let ((answer (widget-match-inline type value)))
   1233         (if answer
   1234             (let ((head (if (vectorp answer) (aref answer 0) (car answer)))
   1235                   (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer))))
   1236               (setq found (append found head)
   1237                     value tail))
   1238           (setq ok nil))))
   1239     (cons found value)))
   1240 
   1241 (defun lsp--editable-vector-value-to-external (_widget internal-value)
   1242   "Convert the internal list value to a vector."
   1243   (if (listp internal-value)
   1244       (apply 'vector internal-value)
   1245     internal-value))
   1246 
   1247 (defun lsp--editable-vector-value-to-internal (_widget external-value)
   1248   "Convert the external vector value to a list."
   1249   (if (vectorp external-value)
   1250       (append external-value nil)
   1251     external-value))
   1252 
   1253 (define-widget 'lsp--editable-vector 'editable-list
   1254   "A subclass of `editable-list' that accepts and returns a
   1255 vector instead of a list."
   1256   :value-to-external 'lsp--editable-vector-value-to-external
   1257   :value-to-internal 'lsp--editable-vector-value-to-internal
   1258   :match 'lsp--editable-vector-match
   1259   :match-inline 'lsp--editable-vector-match-inline)
   1260 
   1261 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector
   1262   "A variable length homogeneous vector."
   1263   :tag "Repeat"
   1264   :format "%{%t%}:\n%v%i\n")
   1265 
   1266 (define-widget 'lsp-string-vector 'lazy
   1267   "A vector of zero or more elements, every element of which is a string.
   1268 Appropriate for any language-specific `defcustom' that needs to
   1269 serialize as a JSON array of strings.
   1270 
   1271 Deprecated. Use `lsp-repeatable-vector' instead. "
   1272   :offset 4
   1273   :tag "Vector"
   1274   :type '(lsp-repeatable-vector string))
   1275 
   1276 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0")
   1277 
   1278 (defvar lsp--show-message t
   1279   "If non-nil, show debug message from `lsp-mode'.")
   1280 
   1281 (defun lsp--message  (format &rest args)
   1282   "Wrapper for `message'
   1283 
   1284 We `inhibit-message' the message when the cursor is in the
   1285 minibuffer and when emacs version is before emacs 27 due to the
   1286 fact that we often use `lsp--info', `lsp--warn' and `lsp--error'
   1287 in async context and the call to these function is removing the
   1288 minibuffer prompt. The issue with async messages is already fixed
   1289 in emacs 27.
   1290 
   1291 See #2049"
   1292   (when lsp--show-message
   1293     (let ((inhibit-message (or inhibit-message
   1294                                (and (minibufferp)
   1295                                     (version< emacs-version "27.0")))))
   1296       (apply #'message format args))))
   1297 
   1298 (defun lsp--info (format &rest args)
   1299   "Display lsp info message with FORMAT with ARGS."
   1300   (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args)))
   1301 
   1302 (defun lsp--warn (format &rest args)
   1303   "Display lsp warn message with FORMAT with ARGS."
   1304   (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args)))
   1305 
   1306 (defun lsp--error (format &rest args)
   1307   "Display lsp error message with FORMAT with ARGS."
   1308   (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args)))
   1309 
   1310 (defun lsp-log (format &rest args)
   1311   "Log message to the ’*lsp-log*’ buffer.
   1312 
   1313 FORMAT and ARGS i the same as for `message'."
   1314   (when lsp-log-max
   1315     (let ((log-buffer (get-buffer "*lsp-log*"))
   1316           (inhibit-read-only t))
   1317       (unless log-buffer
   1318         (setq log-buffer (get-buffer-create "*lsp-log*"))
   1319         (with-current-buffer log-buffer
   1320           (buffer-disable-undo)
   1321           (view-mode 1)
   1322           (set (make-local-variable 'lsp--log-lines) 0)))
   1323       (with-current-buffer log-buffer
   1324         (save-excursion
   1325           (let* ((message (apply 'format format args))
   1326                  ;; Count newlines in message.
   1327                  (newlines (1+ (cl-loop with start = 0
   1328                                         for count from 0
   1329                                         while (string-match "\n" message start)
   1330                                         do (setq start (match-end 0))
   1331                                         finally return count))))
   1332             (goto-char (point-max))
   1333 
   1334             ;; in case the buffer is not empty insert before last \n to preserve
   1335             ;; the point position(in case it is in the end)
   1336             (if (eq (point) (point-min))
   1337                 (progn
   1338                   (insert "\n")
   1339                   (backward-char))
   1340               (backward-char)
   1341               (insert "\n"))
   1342             (insert message)
   1343 
   1344             (setq lsp--log-lines (+ lsp--log-lines newlines))
   1345 
   1346             (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max))
   1347               (let ((to-delete (- lsp--log-lines lsp-log-max)))
   1348                 (goto-char (point-min))
   1349                 (forward-line to-delete)
   1350                 (delete-region (point-min) (point))
   1351                 (setq lsp--log-lines lsp-log-max)))))))))
   1352 
   1353 (defalias 'lsp-message 'lsp-log)
   1354 
   1355 (defalias 'lsp-ht 'ht)
   1356 
   1357 (defalias 'lsp-file-local-name 'file-local-name)
   1358 
   1359 (defun lsp-f-canonical (file-name)
   1360   "Return the canonical FILE-NAME, without a trailing slash."
   1361   (directory-file-name (expand-file-name file-name)))
   1362 
   1363 (defalias 'lsp-canonical-file-name 'lsp-f-canonical)
   1364 
   1365 (defun lsp-f-same? (path-a path-b)
   1366   "Return t if PATH-A and PATH-B are references to the same file.
   1367 Symlinks are not followed."
   1368   (when (and (f-exists? path-a)
   1369              (f-exists? path-b))
   1370     (equal
   1371      (lsp-f-canonical (directory-file-name (f-expand path-a)))
   1372      (lsp-f-canonical (directory-file-name (f-expand path-b))))))
   1373 
   1374 (defun lsp-f-parent (path)
   1375   "Return the parent directory to PATH.
   1376 Symlinks are not followed."
   1377   (let ((parent (file-name-directory
   1378                  (directory-file-name (f-expand path default-directory)))))
   1379     (unless (lsp-f-same? path parent)
   1380       (if (f-relative? path)
   1381           (f-relative parent)
   1382         (directory-file-name parent)))))
   1383 
   1384 (defun lsp-f-ancestor-of? (path-a path-b)
   1385   "Return t if PATH-A is an ancestor of PATH-B.
   1386 Symlinks are not followed."
   1387   (unless (lsp-f-same? path-a path-b)
   1388     (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator))
   1389                (lsp-f-canonical path-b))))
   1390 
   1391 (defun lsp--merge-results (results method)
   1392   "Merge RESULTS by filtering the empty hash-tables and merging
   1393 the lists according to METHOD."
   1394   (pcase (--map (if (vectorp it)
   1395                     (append it nil) it)
   1396                 (-filter #'identity results))
   1397     (`() ())
   1398     ;; only one result - simply return it
   1399     (`(,fst) fst)
   1400     ;; multiple results merge it based on strategy
   1401     (results
   1402      (pcase method
   1403        ("textDocument/hover" (pcase (seq-filter
   1404                                      (-compose #'not #'lsp-empty?)
   1405                                      results)
   1406                                (`(,hover) hover)
   1407                                (hovers (lsp-make-hover
   1408                                         :contents
   1409                                         (-mapcat
   1410                                          (-lambda ((&Hover :contents))
   1411                                            (if (and (sequencep contents)
   1412                                                     (not (stringp contents)))
   1413                                                (append contents ())
   1414                                              (list contents)))
   1415                                          hovers)))))
   1416        ("textDocument/completion"
   1417         (lsp-make-completion-list
   1418          :is-incomplete (seq-some
   1419                          #'lsp:completion-list-is-incomplete
   1420                          results)
   1421          :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it)
   1422                                                     (lsp:completion-list-items it)
   1423                                                   it)
   1424                                                 nil))
   1425                            results)))
   1426        ("completionItem/resolve"
   1427         (let ((item (cl-first results)))
   1428           (when-let ((details (seq-filter #'identity
   1429                                           (seq-map #'lsp:completion-item-detail? results))))
   1430             (lsp:set-completion-item-detail?
   1431              item
   1432              (string-join details " ")))
   1433           (when-let ((docs (seq-filter #'identity
   1434                                        (seq-map #'lsp:completion-item-documentation? results))))
   1435             (lsp:set-completion-item-documentation?
   1436              item
   1437              (lsp-make-markup-content
   1438               :kind (or (seq-some (lambda (it)
   1439                                     (when (equal (lsp:markup-content-kind it)
   1440                                                  lsp/markup-kind-markdown)
   1441                                       lsp/markup-kind-markdown))
   1442                                   docs)
   1443                         lsp/markup-kind-plain-text)
   1444               :value (string-join (seq-map (lambda (doc)
   1445                                              (or (lsp:markup-content-value doc)
   1446                                                  (and (stringp doc) doc)))
   1447                                            docs)
   1448                                   "\n"))))
   1449           (when-let ((edits (seq-filter #'identity
   1450                                         (seq-map #'lsp:completion-item-additional-text-edits? results))))
   1451             (lsp:set-completion-item-additional-text-edits?
   1452              item
   1453              (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits)))
   1454           item))
   1455        (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results))))))
   1456 
   1457 (defun lsp--spinner-start ()
   1458   "Start spinner indication."
   1459   (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error)))
   1460 
   1461 (defun lsp--propertize (str type)
   1462   "Propertize STR as per TYPE."
   1463   (propertize str 'face (alist-get type lsp--message-type-face)))
   1464 
   1465 (defun lsp-workspaces ()
   1466   "Return the lsp workspaces associated with the current project."
   1467   (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces))
   1468 
   1469 (defun lsp--completing-read (prompt collection transform-fn &optional predicate
   1470                                     require-match initial-input
   1471                                     hist def inherit-input-method)
   1472   "Wrap `completing-read' to provide transformation function and disable sort.
   1473 
   1474 TRANSFORM-FN will be used to transform each of the items before displaying.
   1475 
   1476 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF
   1477 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes."
   1478   (let* ((col (--map (cons (funcall transform-fn it) it) collection))
   1479          (completion (completing-read prompt
   1480                                       (lambda (string pred action)
   1481                                         (if (eq action 'metadata)
   1482                                             `(metadata (display-sort-function . identity))
   1483                                           (complete-with-action action col string pred)))
   1484                                       predicate require-match initial-input hist
   1485                                       def inherit-input-method)))
   1486     (cdr (assoc completion col))))
   1487 
   1488 (defconst lsp--system-arch (lambda ()
   1489                              (setq lsp--system-arch
   1490                                    (pcase system-type
   1491                                      ('windows-nt
   1492                                       (pcase system-configuration
   1493                                         ((rx bol "x86_64-") 'x64)
   1494                                         (_ 'x86)))
   1495                                      ('darwin
   1496                                       (pcase system-configuration
   1497                                         ((rx "aarch64-") 'arm64)
   1498                                         (_ 'x64)))
   1499                                      ('gnu/linux
   1500                                        (pcase system-configuration
   1501                                          ((rx bol "x86_64") 'x64)
   1502                                          ((rx bol (| "i386" "i886")) 'x32)))
   1503                                      (_
   1504                                       (pcase system-configuration
   1505                                         ((rx bol "x86_64") 'x64)
   1506                                         ((rx bol (| "i386" "i886")) 'x32))))))
   1507   "Return the system architecture of `Emacs'.
   1508 Special values:
   1509   `x64'       64bit
   1510   `x32'       32bit
   1511   `arm64'     ARM 64bit")
   1512 
   1513 (defmacro lsp-with-current-buffer (buffer-id &rest body)
   1514   (declare (indent 1) (debug t))
   1515   `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer)))
   1516        (with-lsp-workspaces (plist-get ,buffer-id :workspaces)
   1517          (funcall wcb (lambda () ,@body)))
   1518      (with-current-buffer ,buffer-id
   1519        ,@body)))
   1520 
   1521 (defvar lsp--throw-on-input nil
   1522   "Make `lsp-*-while-no-input' throws `input' on interrupted.")
   1523 
   1524 (defmacro lsp--catch (tag bodyform &rest handlers)
   1525   "Catch TAG thrown in BODYFORM.
   1526 The return value from TAG will be handled in HANDLERS by `pcase'."
   1527   (declare (debug (form form &rest (pcase-PAT body))) (indent 2))
   1528   (let ((re-sym (make-symbol "re")))
   1529     `(let ((,re-sym (catch ,tag ,bodyform)))
   1530        (pcase ,re-sym
   1531          ,@handlers))))
   1532 
   1533 (defmacro lsp--while-no-input (&rest body)
   1534   "Wrap BODY in `while-no-input' and respecting `non-essential'.
   1535 If `lsp--throw-on-input' is set, will throw if input is pending, else
   1536 return value of `body' or nil if interrupted."
   1537   (declare (debug t) (indent 0))
   1538   `(if non-essential
   1539        (let ((res (while-no-input ,@body)))
   1540          (cond
   1541           ((and lsp--throw-on-input (equal res t))
   1542            (throw 'input :interrupted))
   1543           ((booleanp res) nil)
   1544           (t res)))
   1545      ,@body))
   1546 
   1547 ;; A ‘lsp--client’ object describes the client-side behavior of a language
   1548 ;; server.  It is used to start individual server processes, each of which is
   1549 ;; represented by a ‘lsp--workspace’ object.  Client objects are normally
   1550 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’.  Each
   1551 ;; workspace refers to exactly one client, but there can be multiple workspaces
   1552 ;; for a single client.
   1553 (cl-defstruct lsp--client
   1554   ;; ‘language-id’ is a function that receives a buffer as a single argument
   1555   ;; and should return the language identifier for that buffer.  See
   1556   ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem
   1557   ;; for a list of language identifiers.  Also consult the documentation for
   1558   ;; the language server represented by this client to find out what language
   1559   ;; identifiers it supports or expects.
   1560   (language-id nil)
   1561 
   1562   ;; ‘add-on?’ when set to t the server will be started no matter whether there
   1563   ;; is another server handling the same mode.
   1564   (add-on? nil)
   1565   ;; ‘new-connection’ is a function that should start a language server process
   1566   ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS).
   1567   ;; COMMAND-PROCESS must be a process object representing the server process
   1568   ;; just started.  COMMUNICATION-PROCESS must be a process (including pipe and
   1569   ;; network processes) that ‘lsp-mode’ uses to communicate with the language
   1570   ;; server using the language server protocol.  COMMAND-PROCESS and
   1571   ;; COMMUNICATION-PROCESS may be the same process; in that case
   1572   ;; ‘new-connection’ may also return that process as a single
   1573   ;; object. ‘new-connection’ is called with two arguments, FILTER and
   1574   ;; SENTINEL.  FILTER should be used as process filter for
   1575   ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for
   1576   ;; COMMAND-PROCESS.
   1577   (new-connection nil)
   1578 
   1579   ;; ‘ignore-regexps’ is a list of regexps.  When a data packet from the
   1580   ;; language server matches any of these regexps, it will be ignored.  This is
   1581   ;; intended for dealing with language servers that output non-protocol data.
   1582   (ignore-regexps nil)
   1583 
   1584   ;; ‘ignore-messages’ is a list of regexps.  When a message from the language
   1585   ;; server matches any of these regexps, it will be ignored.  This is useful
   1586   ;; for filtering out unwanted messages; such as servers that send nonstandard
   1587   ;; message types, or extraneous log messages.
   1588   (ignore-messages nil)
   1589 
   1590   ;; ‘notification-handlers’ is a hash table mapping notification method names
   1591   ;; (strings) to functions handling the respective notifications.  Upon
   1592   ;; receiving a notification, ‘lsp-mode’ will call the associated handler
   1593   ;; function passing two arguments, the ‘lsp--workspace’ object and the
   1594   ;; deserialized notification parameters.
   1595   (notification-handlers (make-hash-table :test 'equal))
   1596 
   1597   ;; ‘request-handlers’ is a hash table mapping request method names
   1598   ;; (strings) to functions handling the respective notifications.  Upon
   1599   ;; receiving a request, ‘lsp-mode’ will call the associated handler function
   1600   ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized
   1601   ;; request parameters.
   1602   (request-handlers (make-hash-table :test 'equal))
   1603 
   1604   ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request
   1605   ;; identifiers for pending asynchronous requests to functions handling the
   1606   ;; respective responses.  Upon receiving a response from the language server,
   1607   ;; ‘lsp-mode’ will call the associated response handler function with a
   1608   ;; single argument, the deserialized response parameters.
   1609   (response-handlers (make-hash-table :test 'eql))
   1610 
   1611   ;; ‘prefix-function’ is called for getting the prefix for completion.
   1612   ;; The function takes no parameter and returns a cons (start . end) representing
   1613   ;; the start and end bounds of the prefix. If it's not set, the client uses a
   1614   ;; default prefix function."
   1615   (prefix-function nil)
   1616 
   1617   ;; Contains mapping of scheme to the function that is going to be used to load
   1618   ;; the file.
   1619   (uri-handlers (make-hash-table :test #'equal))
   1620 
   1621   ;; ‘action-handlers’ is a hash table mapping action to a handler function. It
   1622   ;; can be used in `lsp-execute-code-action' to determine whether the action
   1623   ;; current client is interested in executing the action instead of sending it
   1624   ;; to the server.
   1625   (action-handlers (make-hash-table :test 'equal))
   1626 
   1627   ;; major modes supported by the client.
   1628   major-modes
   1629   ;; Function that will be called to decide if this language client
   1630   ;; should manage a particular buffer. The function will be passed
   1631   ;; the file name and major mode to inform the decision. Setting
   1632   ;; `activation-fn' will override `major-modes', if
   1633   ;; present.
   1634   activation-fn
   1635   ;; Break the tie when major-mode is supported by multiple clients.
   1636   (priority 0)
   1637   ;; Unique identifier for representing the client object.
   1638   server-id
   1639   ;; defines whether the client supports multi root workspaces.
   1640   multi-root
   1641   ;; Initialization options or a function that returns initialization options.
   1642   initialization-options
   1643   ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or
   1644   ;; completely replace, the faces used for semantic highlighting on a
   1645   ;; client-by-client basis.
   1646   ;;
   1647   ;; It recognizes four members, all of which are optional: `:types’ and
   1648   ;; `:modifiers’, respectively, should be face definition lists akin to
   1649   ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be
   1650   ;; merged with the default face definition list.
   1651   ;;
   1652   ;; Alternatively, if the plist members `:discard-default-types’ or
   1653   ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers'
   1654   ;; face definitions will be replaced entirely by their respective overrides.
   1655   ;;
   1656   ;; For example, setting `:semantic-tokens-faces-overrides' to
   1657   ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from
   1658   ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'.
   1659   ;;
   1660   ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))'
   1661   ;; will also remap "macro", but on top of that associate the fictional token type
   1662   ;; "not-quite-a-macro" with the face named `some-face'.
   1663   ;;
   1664   ;; `(:types (("macro" . font-lock-keyword-face))
   1665   ;;   :modifiers (("declaration" . lsp-face-semhl-interface))
   1666   ;;   :discard-default-types t
   1667   ;;   :discard-default-modifiers t)'
   1668   ;; will discard all default face definitions, hence leaving the client with
   1669   ;; only one token type "macro", mapped to `font-lock-keyword-face', and one
   1670   ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'.
   1671   semantic-tokens-faces-overrides
   1672   ;; Provides support for registering LSP Server specific capabilities.
   1673   custom-capabilities
   1674   ;; Function which returns the folders that are considered to be not projects but library files.
   1675   ;; The function accepts one parameter currently active workspace.
   1676   ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225.
   1677   library-folders-fn
   1678   ;; function which will be called when opening file in the workspace to perform
   1679   ;; client specific initialization. The function accepts one parameter
   1680   ;; currently active workspace.
   1681   before-file-open-fn
   1682   ;; Function which will be called right after a workspace has been initialized.
   1683   initialized-fn
   1684   ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP.
   1685   (remote? nil)
   1686 
   1687   ;; ‘completion-in-comments?’ t if the client supports completion in comments.
   1688   (completion-in-comments? nil)
   1689 
   1690   ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client.
   1691   (path->uri-fn nil)
   1692 
   1693   ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client.
   1694   (uri->path-fn nil)
   1695   ;; Function that returns an environment structure that will be used
   1696   ;; to set some environment variables when starting the language
   1697   ;; server process. These environment variables enable some
   1698   ;; additional features in the language server. The environment
   1699   ;; structure is an alist of the form (KEY . VALUE), where KEY is a
   1700   ;; string (regularly in all caps), and VALUE may be a string, a
   1701   ;; boolean, or a sequence of strings.
   1702   environment-fn
   1703 
   1704   ;; ‘after-open-fn’ workspace after open specific hooks.
   1705   (after-open-fn nil)
   1706 
   1707   ;; ‘async-request-handlers’ is a hash table mapping request method names
   1708   ;; (strings) to functions handling the respective requests that may take
   1709   ;; time to finish.  Upon receiving a request, ‘lsp-mode’ will call the
   1710   ;; associated handler function passing three arguments, the ‘lsp--workspace’
   1711   ;; object, the deserialized request parameters and the callback which accept
   1712   ;; result as its parameter.
   1713   (async-request-handlers (make-hash-table :test 'equal))
   1714   download-server-fn
   1715   download-in-progress?
   1716   buffers
   1717   synchronize-sections)
   1718 
   1719 (defun lsp-clients-executable-find (find-command &rest args)
   1720   "Finds an executable by invoking a search command.
   1721 
   1722 FIND-COMMAND is the executable finder that searches for the
   1723 actual language server executable. ARGS is a list of arguments to
   1724 give to FIND-COMMAND to find the language server.  Returns the
   1725 output of FIND-COMMAND if it exits successfully, nil otherwise.
   1726 
   1727 Typical uses include finding an executable by invoking `find' in
   1728 a project, finding LLVM commands on macOS with `xcrun', or
   1729 looking up project-specific language servers for projects written
   1730 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv'
   1731 etc."
   1732   (when-let* ((find-command-path (executable-find find-command))
   1733               (executable-path
   1734                (with-temp-buffer
   1735                  (when (zerop (apply 'call-process find-command-path nil t nil args))
   1736                    (buffer-substring-no-properties (point-min) (point-max))))))
   1737     (string-trim executable-path)))
   1738 
   1739 (defvar lsp--already-widened nil)
   1740 
   1741 (defmacro lsp-save-restriction-and-excursion (&rest form)
   1742   (declare (indent 0) (debug t))
   1743   `(if lsp--already-widened
   1744        (save-excursion ,@form)
   1745      (-let [lsp--already-widened t]
   1746        (save-restriction
   1747          (widen)
   1748          (save-excursion ,@form)))))
   1749 
   1750 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number
   1751 (defun lsp--line-character-to-point (line character)
   1752   "Return the point for character CHARACTER on line LINE."
   1753   (or (lsp-virtual-buffer-call :line/character->point line character)
   1754       (let ((inhibit-field-text-motion t))
   1755         (lsp-save-restriction-and-excursion
   1756           (goto-char (point-min))
   1757           (forward-line line)
   1758           ;; server may send character position beyond the current line and we
   1759           ;; should fallback to line end.
   1760           (-let [line-end (line-end-position)]
   1761             (if (> character (- line-end (point)))
   1762                 line-end
   1763               (forward-char character)
   1764               (point)))))))
   1765 
   1766 (lsp-defun lsp--position-to-point ((&Position :line :character))
   1767   "Convert `Position' object in PARAMS to a point."
   1768   (lsp--line-character-to-point line character))
   1769 
   1770 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end))
   1771   (cons start end))
   1772 
   1773 (lsp-defun lsp--range-text ((&RangeToPoint :start :end))
   1774   (buffer-substring start end))
   1775 
   1776 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end)))
   1777   (cond
   1778    ((and
   1779      (region-active-p)
   1780      (<= start (region-beginning) end)
   1781      (<= start (region-end) end)
   1782      (or (not (= start (region-beginning)))
   1783          (not (= end (region-end)))))
   1784     (cons start end))
   1785    ((and (<= start (point) end)
   1786          (not (region-active-p)))
   1787     (cons start end))
   1788    (parent? (lsp--find-wrapping-range parent?))))
   1789 
   1790 (defun lsp--get-selection-range ()
   1791   (or
   1792    (-when-let ((cache . cache-tick) lsp--document-selection-range-cache)
   1793      (when (= cache-tick (buffer-modified-tick)) cache))
   1794    (let ((response (cl-first
   1795                     (lsp-request
   1796                      "textDocument/selectionRange"
   1797                      (list :textDocument (lsp--text-document-identifier)
   1798                            :positions (vector (lsp--cur-position)))))))
   1799      (setq lsp--document-selection-range-cache
   1800            (cons response (buffer-modified-tick)))
   1801      response)))
   1802 
   1803 (defun lsp-extend-selection ()
   1804   "Extend selection."
   1805   (interactive)
   1806   (unless (lsp-feature? "textDocument/selectionRange")
   1807     (signal 'lsp-capability-not-supported (list "selectionRangeProvider")))
   1808   (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range)))
   1809     (goto-char start)
   1810     (set-mark (point))
   1811     (goto-char end)
   1812     (exchange-point-and-mark)))
   1813 
   1814 (defun lsp-warn (message &rest args)
   1815   "Display a warning message made from (`format-message' MESSAGE ARGS...).
   1816 This is equivalent to `display-warning', using `lsp-mode' as the type and
   1817 `:warning' as the level."
   1818   (display-warning 'lsp-mode (apply #'format-message message args)))
   1819 
   1820 (defun lsp--get-uri-handler (scheme)
   1821   "Get uri handler for SCHEME in the current workspace."
   1822   (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it)))
   1823           (or (lsp-workspaces) (lsp--session-workspaces (lsp-session)))))
   1824 
   1825 (defun lsp--fix-path-casing (path)
   1826   "On windows, downcases path because the windows file system is
   1827 case-insensitive.
   1828 
   1829 On other systems, returns path without change."
   1830   (if (eq system-type 'windows-nt) (downcase path) path))
   1831 
   1832 (defun lsp--uri-to-path (uri)
   1833   "Convert URI to a file path."
   1834   (if-let ((fn (->> (lsp-workspaces)
   1835                     (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client))
   1836                     (cl-first))))
   1837       (funcall fn uri)
   1838     (lsp--uri-to-path-1 uri)))
   1839 
   1840 (defun lsp-remap-path-if-needed (file-name)
   1841   (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings))
   1842       (propertize (buffer-local-value 'buffer-file-name buffer)
   1843                   'lsp-virtual-buffer virtual-buffer)
   1844     file-name))
   1845 
   1846 (defun lsp--uri-to-path-1 (uri)
   1847   "Convert URI to a file path."
   1848   (let* ((url (url-generic-parse-url (url-unhex-string uri)))
   1849          (type (url-type url))
   1850          (target (url-target url))
   1851          (file
   1852           (concat (decode-coding-string (url-filename url)
   1853                                         (or locale-coding-system 'utf-8))
   1854                   (when (and target
   1855                              (not (s-match
   1856                                    (rx "#" (group (1+ num)) (or "," "#")
   1857                                        (group (1+ num))
   1858                                        string-end)
   1859                                    uri)))
   1860                     (concat "#" target))))
   1861          (file-name (if (and type (not (string= type "file")))
   1862                         (if-let ((handler (lsp--get-uri-handler type)))
   1863                             (funcall handler uri)
   1864                           uri)
   1865                       ;; `url-generic-parse-url' is buggy on windows:
   1866                       ;; https://github.com/emacs-lsp/lsp-mode/pull/265
   1867                       (or (and (eq system-type 'windows-nt)
   1868                                (eq (elt file 0) ?\/)
   1869                                (substring file 1))
   1870                           file))))
   1871     (->> file-name
   1872          (concat (-some #'lsp--workspace-host-root (lsp-workspaces)))
   1873          (lsp-remap-path-if-needed))))
   1874 
   1875 (defun lsp--buffer-uri ()
   1876   "Return URI of the current buffer."
   1877   (or lsp-buffer-uri
   1878       (plist-get lsp--virtual-buffer :buffer-uri)
   1879       (lsp--path-to-uri
   1880        (or (buffer-file-name) (buffer-file-name (buffer-base-buffer))))))
   1881 
   1882 (defun lsp-register-client-capabilities (&rest _args)
   1883   "Implemented only to make `company-lsp' happy.
   1884 DELETE when `lsp-mode.el' is deleted.")
   1885 
   1886 (defconst lsp--url-path-allowed-chars
   1887   (url--allowed-chars (append '(?/) url-unreserved-chars))
   1888   "`url-unreserved-chars' with additional delim ?/.
   1889 This set of allowed chars is enough for hexifying local file paths.")
   1890 
   1891 (defun lsp--path-to-uri-1 (path)
   1892   (concat lsp--uri-file-prefix
   1893           (--> path
   1894             (expand-file-name it)
   1895             (or (file-remote-p it 'localname t) it)
   1896             (url-hexify-string it lsp--url-path-allowed-chars))))
   1897 
   1898 (defun lsp--path-to-uri (path)
   1899   "Convert PATH to a uri."
   1900   (if-let ((uri-fn (->> (lsp-workspaces)
   1901                         (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client))
   1902                         (cl-first))))
   1903       (funcall uri-fn path)
   1904     (lsp--path-to-uri-1 path)))
   1905 
   1906 (defun lsp--string-match-any (regex-list str)
   1907   "Return the first regex, if any, within REGEX-LIST matching STR."
   1908   (--first (string-match it str) regex-list))
   1909 
   1910 (cl-defstruct lsp-watch
   1911   (descriptors (make-hash-table :test 'equal))
   1912   root-directory)
   1913 
   1914 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories)
   1915   (let ((file-name (cl-third event))
   1916         (event-type (cl-second event)))
   1917     (cond
   1918      ((and (file-directory-p file-name)
   1919            (equal 'created event-type)
   1920            (not (lsp--string-match-any ignored-directories file-name)))
   1921 
   1922       (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch)
   1923 
   1924       ;; process the files that are already present in
   1925       ;; the directory.
   1926       (->> (directory-files-recursively file-name ".*" t)
   1927            (seq-do (lambda (f)
   1928                      (unless (file-directory-p f)
   1929                        (funcall callback (list nil 'created f)))))))
   1930      ((and (memq event-type '(created deleted changed))
   1931            (not (file-directory-p file-name))
   1932            (not (lsp--string-match-any ignored-files file-name)))
   1933       (funcall callback event))
   1934      ((and (memq event-type '(renamed))
   1935            (not (file-directory-p file-name))
   1936            (not (lsp--string-match-any ignored-files file-name)))
   1937       (funcall callback `(,(cl-first event) deleted ,(cl-third event)))
   1938       (funcall callback `(,(cl-first event) created ,(cl-fourth event)))))))
   1939 
   1940 (defun lsp--ask-about-watching-big-repo (number-of-directories dir)
   1941   "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR.
   1942 This is useful when there is a lot of files in a repository, as
   1943 that may slow Emacs down. Returns t if the user wants to watch
   1944 the entire repository, nil otherwise."
   1945   (prog1
   1946       (yes-or-no-p
   1947        (format
   1948         "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down.
   1949 Do you want to watch all files in %s? "
   1950         dir
   1951         number-of-directories
   1952         dir))
   1953     (lsp--info
   1954      (concat "You can configure this warning with the `lsp-enable-file-watchers' "
   1955              "and `lsp-file-watch-threshold' variables"))))
   1956 
   1957 
   1958 (defun lsp--path-is-watchable-directory (path dir ignored-directories)
   1959   "Figure out whether PATH (inside of DIR) is meant to have a file watcher set.
   1960 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't
   1961 want to watch."
   1962   (let
   1963       ((full-path (f-join dir path)))
   1964     (and (file-accessible-directory-p full-path)
   1965          (not (equal path "."))
   1966          (not (equal path ".."))
   1967          (not (lsp--string-match-any ignored-directories full-path)))))
   1968 
   1969 
   1970 (defun lsp--all-watchable-directories (dir ignored-directories)
   1971   "Traverse DIR recursively returning a list of paths that should have watchers.
   1972 IGNORED-DIRECTORIES will be used for exclusions"
   1973   (let* ((dir (if (f-symlink? dir)
   1974                   (file-truename dir)
   1975                 dir)))
   1976     (apply #'nconc
   1977            ;; the directory itself is assumed to be part of the set
   1978            (list dir)
   1979            ;; collect all subdirectories that are watchable
   1980            (-map
   1981             (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories))
   1982             ;; but only look at subdirectories that are watchable
   1983             (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories))
   1984                      (directory-files dir))))))
   1985 
   1986 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?)
   1987   "Create recursive file notification watch in DIR.
   1988 CALLBACK will be called when there are changes in any of
   1989 the monitored files. WATCHES is a hash table directory->file
   1990 notification handle which contains all of the watch that
   1991 already have been created. Watches will not be created for
   1992 any directory that matches any regex in IGNORED-DIRECTORIES.
   1993 Watches will not be created for any file that matches any
   1994 regex in IGNORED-FILES."
   1995   (let* ((dir (if (f-symlink? dir)
   1996                   (file-truename dir)
   1997                 dir))
   1998          (watch (or watch (make-lsp-watch :root-directory dir)))
   1999          (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories)))
   2000     (lsp-log "Creating watchers for following %s folders:\n  %s"
   2001              (length dirs-to-watch)
   2002              (s-join "\n  " dirs-to-watch))
   2003     (when (or
   2004            (not warn-big-repo?)
   2005            (not lsp-file-watch-threshold)
   2006            (let ((number-of-directories (length dirs-to-watch)))
   2007              (or
   2008               (< number-of-directories lsp-file-watch-threshold)
   2009               (condition-case nil
   2010                   (lsp--ask-about-watching-big-repo number-of-directories dir)
   2011                 (quit)))))
   2012       (dolist (current-dir dirs-to-watch)
   2013         (condition-case err
   2014             (progn
   2015               (puthash
   2016                current-dir
   2017                (file-notify-add-watch current-dir
   2018                                       '(change)
   2019                                       (lambda (event)
   2020                                         (lsp--folder-watch-callback event callback watch ignored-files ignored-directories)))
   2021                (lsp-watch-descriptors watch)))
   2022           (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))
   2023           (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err))))))
   2024     watch))
   2025 
   2026 (defun lsp-kill-watch (watch)
   2027   "Delete WATCH."
   2028   (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch))
   2029   (ht-clear! (lsp-watch-descriptors watch)))
   2030 
   2031 (defun lsp-json-bool (val)
   2032   "Convert VAL to JSON boolean."
   2033   (if val t :json-false))
   2034 
   2035 (defmacro with-lsp-workspace (workspace &rest body)
   2036   "Helper macro for invoking BODY in WORKSPACE context."
   2037   (declare (debug (form body))
   2038            (indent 1))
   2039   `(let ((lsp--cur-workspace ,workspace)) ,@body))
   2040 
   2041 (defmacro with-lsp-workspaces (workspaces &rest body)
   2042   "Helper macro for invoking BODY against multiple WORKSPACES."
   2043   (declare (debug (form body))
   2044            (indent 1))
   2045   `(let ((lsp--buffer-workspaces ,workspaces)) ,@body))
   2046 
   2047 
   2048 
   2049 (defmacro lsp-consistency-check (package)
   2050   `(defconst ,(intern (concat (symbol-name package)
   2051                               "-plist-value-when-compiled"))
   2052      (eval-when-compile lsp-use-plists)))
   2053 
   2054 
   2055 ;; loading code-workspace files
   2056 
   2057 ;;;###autoload
   2058 (defun lsp-load-vscode-workspace (file)
   2059   "Load vscode workspace from FILE"
   2060   (interactive "fSelect file to import: ")
   2061   (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session)))
   2062 
   2063   (let ((dir (f-dirname file)))
   2064     (->> file
   2065          (json-read-file)
   2066          (alist-get 'folders)
   2067          (-map (-lambda ((&alist 'path))
   2068                  (lsp-workspace-folders-add (expand-file-name path dir)))))))
   2069 
   2070 ;;;###autoload
   2071 (defun lsp-save-vscode-workspace (file)
   2072   "Save vscode workspace to FILE"
   2073   (interactive "FSelect file to save to: ")
   2074 
   2075   (let ((json-encoding-pretty-print t))
   2076     (f-write-text (json-encode
   2077                    `((folders . ,(->> (lsp-session)
   2078                                       (lsp-session-folders)
   2079                                       (--map `((path . ,it)))))))
   2080                   'utf-8
   2081                   file)))
   2082 
   2083 
   2084 (defmacro lsp-foreach-workspace (&rest body)
   2085   "Execute BODY for each of the current workspaces."
   2086   (declare (debug (form body)))
   2087   `(--map (with-lsp-workspace it ,@body) (lsp-workspaces)))
   2088 
   2089 (defmacro when-lsp-workspace (workspace &rest body)
   2090   "Helper macro for invoking BODY in WORKSPACE context if present."
   2091   (declare (debug (form body))
   2092            (indent 1))
   2093   `(when-let ((lsp--cur-workspace ,workspace)) ,@body))
   2094 
   2095 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items))
   2096   (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read))
   2097             (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label))
   2098                                  items))
   2099             (result (funcall-interactively
   2100                      selectfunc
   2101                      (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels))
   2102             (choices (if (listp result)
   2103                          (if (equal result '("*"))
   2104                              itemLabels
   2105                            result)
   2106                        (list result))))
   2107       (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data))
   2108                                                  (if (member label choices)
   2109                                                      (lsp-make-quick-pick-item :label label :picked t :user-data user-data)
   2110                                                    nil))
   2111                                                items)))))
   2112 
   2113 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?))
   2114   (read-string (format "%s: " prompt) (or value? "")))
   2115 
   2116 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type))
   2117   "Send the server's messages to log.
   2118 PARAMS - the data sent from _WORKSPACE."
   2119   (funcall (cl-case type
   2120              (1 'lsp--error)
   2121              (2 'lsp--warn)
   2122              (t 'lsp--info))
   2123            "%s"
   2124            message))
   2125 
   2126 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type))
   2127   "Send the server's messages to log.
   2128 PARAMS - the data sent from WORKSPACE."
   2129   (ignore
   2130    (let ((client (lsp--workspace-client workspace)))
   2131      (when (or (not client)
   2132                (cl-notany (-rpartial #'string-match-p message)
   2133                           (lsp--client-ignore-messages client)))
   2134        (lsp-log "%s" (lsp--propertize message type))))))
   2135 
   2136 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?))
   2137   "Display a message request to user sending the user selection back to server."
   2138   (let* ((message (lsp--propertize message type))
   2139          (choices (seq-map #'lsp:message-action-item-title actions?)))
   2140     (if choices
   2141         (completing-read (concat message " ") (seq-into choices 'list) nil t)
   2142       (lsp-log message))))
   2143 
   2144 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?))
   2145   "Show document URI in a buffer and go to SELECTION if any."
   2146   (let ((path (lsp--uri-to-path uri)))
   2147     (when (f-exists? path)
   2148       (with-current-buffer (find-file path)
   2149         (when selection?
   2150           (goto-char (lsp--position-to-point (lsp:range-start selection?))))
   2151         t))))
   2152 
   2153 (defcustom lsp-progress-prefix " ⌛ "
   2154   "Progress prefix."
   2155   :group 'lsp-mode
   2156   :type 'string
   2157   :package-version '(lsp-mode . "8.0.0"))
   2158 
   2159 (defcustom lsp-progress-function #'lsp-on-progress-modeline
   2160   "Function for handling the progress notifications."
   2161   :group 'lsp-mode
   2162   :type '(choice
   2163           (const :tag "Use modeline" lsp-on-progress-modeline)
   2164           (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')"
   2165                  lsp-on-progress-legacy)
   2166           (const :tag "Ignore" ignore)
   2167           (function :tag "Other function"))
   2168   :package-version '(lsp-mode . "8.0.0"))
   2169 
   2170 (defcustom lsp-request-while-no-input-may-block nil
   2171   "Have `lsp-request-while-no-input` block unless `non-essential` is t."
   2172   :group 'lsp-mode
   2173   :type 'boolean)
   2174 
   2175 (defun lsp--progress-status ()
   2176   "Returns the status of the progress for the current workspaces."
   2177   (-let ((progress-status
   2178           (s-join
   2179            "|"
   2180            (-keep
   2181             (lambda (workspace)
   2182               (let ((tokens (lsp--workspace-work-done-tokens workspace)))
   2183                 (unless (ht-empty? tokens)
   2184                   (mapconcat
   2185                    (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?))
   2186                      (concat (if percentage?
   2187                                  (if (numberp percentage?)
   2188                                      (format "%.0f%%%% " percentage?)
   2189                                    (format "%s%%%% " percentage?))
   2190                                "")
   2191                              (or message? title)))
   2192                    (ht-values tokens)
   2193                    "|"))))
   2194             (lsp-workspaces)))))
   2195     (unless (s-blank? progress-status)
   2196       (concat lsp-progress-prefix progress-status))))
   2197 
   2198 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value
   2199                                                                 (value &as &WorkDoneProgress :kind)))
   2200   "PARAMS contains the progress data.
   2201 WORKSPACE is the workspace that contains the progress token."
   2202   (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status))))
   2203   (pcase kind
   2204     ("begin" (lsp-workspace-set-work-done-token token value workspace))
   2205     ("report" (lsp-workspace-set-work-done-token token value workspace))
   2206     ("end" (lsp-workspace-rem-work-done-token token workspace)))
   2207   (force-mode-line-update))
   2208 
   2209 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value
   2210                                                               (value &as &WorkDoneProgress :kind)))
   2211   "PARAMS contains the progress data.
   2212 WORKSPACE is the workspace that contains the progress token."
   2213   (pcase kind
   2214     ("begin"
   2215      (-let* (((&WorkDoneProgressBegin :title :percentage?) value)
   2216              (reporter
   2217               (if lsp-progress-via-spinner
   2218                   (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types))
   2219                          ;; Set message as a tooltip for the spinner strings
   2220                          (propertized-strings
   2221                           (seq-map (lambda (string) (propertize string 'help-echo title))
   2222                                    spinner-strings))
   2223                          (spinner-type (vconcat propertized-strings)))
   2224                     ;; The progress relates to the server as a whole,
   2225                     ;; display it on all buffers.
   2226                     (mapcar (lambda (buffer)
   2227                               (lsp-with-current-buffer buffer
   2228                                 (spinner-start spinner-type))
   2229                               buffer)
   2230                             (lsp--workspace-buffers workspace)))
   2231                 (if percentage?
   2232                     (make-progress-reporter title 0 100 percentage?)
   2233                   ;; No percentage, just progress
   2234                   (make-progress-reporter title nil nil)))))
   2235        (lsp-workspace-set-work-done-token token reporter workspace)))
   2236     ("report"
   2237      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2238        (unless lsp-progress-via-spinner
   2239          (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value)))))
   2240 
   2241     ("end"
   2242      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2243        (if lsp-progress-via-spinner
   2244            (mapc (lambda (buffer)
   2245                    (when (lsp-buffer-live-p buffer)
   2246                      (lsp-with-current-buffer buffer
   2247                        (spinner-stop))))
   2248                  reporter)
   2249          (progress-reporter-done reporter))
   2250        (lsp-workspace-rem-work-done-token token workspace)))))
   2251 
   2252 
   2253 ;; diagnostics
   2254 
   2255 (defvar lsp-diagnostic-filter nil
   2256   "A a function which will be called with
   2257   `&PublishDiagnosticsParams' and `workspace' which can be used
   2258   to filter out the diagnostics. The function should return
   2259   `&PublishDiagnosticsParams'.
   2260 
   2261 Common usecase are:
   2262 1. Filter the diagnostics for a particular language server.
   2263 2. Filter out the diagnostics under specific level.")
   2264 
   2265 (defvar lsp-diagnostic-stats (ht))
   2266 
   2267 (defun lsp-diagnostics (&optional current-workspace?)
   2268   "Return the diagnostics from all workspaces."
   2269   (or (pcase (if current-workspace?
   2270                  (lsp-workspaces)
   2271                (lsp--session-workspaces (lsp-session)))
   2272         (`() ())
   2273         (`(,workspace) (lsp--workspace-diagnostics workspace))
   2274         (`,workspaces (let ((result (make-hash-table :test 'equal)))
   2275                         (mapc (lambda (workspace)
   2276                                 (->> workspace
   2277                                      (lsp--workspace-diagnostics)
   2278                                      (maphash (lambda (file-name diagnostics)
   2279                                                 (puthash file-name
   2280                                                          (append (gethash file-name result) diagnostics)
   2281                                                          result)))))
   2282                               workspaces)
   2283                         result)))
   2284       (ht)))
   2285 
   2286 (defun lsp-diagnostics-stats-for (path)
   2287   "Get diagnostics statistics for PATH.
   2288 The result format is vector [_ errors warnings infos hints] or nil."
   2289   (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats))
   2290 
   2291 (defun lsp-diagnostics--update-path (path new-stats)
   2292   (let ((new-stats (copy-sequence new-stats))
   2293         (path (lsp--fix-path-casing (directory-file-name path))))
   2294     (if-let ((old-data (gethash path lsp-diagnostic-stats)))
   2295         (dotimes (idx 5)
   2296           (cl-callf + (aref old-data idx)
   2297             (aref new-stats idx)))
   2298       (puthash path new-stats lsp-diagnostic-stats))))
   2299 
   2300 (lsp-defun lsp--on-diagnostics-update-stats (workspace
   2301                                              (&PublishDiagnosticsParams :uri :diagnostics))
   2302   (let ((path (lsp--fix-path-casing (lsp--uri-to-path uri)))
   2303         (new-stats (make-vector 5 0)))
   2304     (mapc (-lambda ((&Diagnostic :severity?))
   2305             (cl-incf (aref new-stats (or severity? 1))))
   2306           diagnostics)
   2307     (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace))))
   2308       (mapc (-lambda ((&Diagnostic :severity?))
   2309               (cl-decf (aref new-stats (or severity? 1))))
   2310             old-diags))
   2311     (lsp-diagnostics--update-path path new-stats)
   2312     (while (not (string= path (setf path (file-name-directory
   2313                                           (directory-file-name path)))))
   2314       (lsp-diagnostics--update-path path new-stats))))
   2315 
   2316 (defun lsp--on-diagnostics (workspace params)
   2317   "Callback for textDocument/publishDiagnostics.
   2318 interface PublishDiagnosticsParams {
   2319     uri: string;
   2320     diagnostics: Diagnostic[];
   2321 }
   2322 PARAMS contains the diagnostics data.
   2323 WORKSPACE is the workspace that contains the diagnostics."
   2324   (when lsp-diagnostic-filter
   2325     (setf params (funcall lsp-diagnostic-filter params workspace)))
   2326 
   2327   (lsp--on-diagnostics-update-stats workspace params)
   2328 
   2329   (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params)
   2330           (lsp--virtual-buffer-mappings (ht))
   2331           (file (lsp--fix-path-casing (lsp--uri-to-path uri)))
   2332           (workspace-diagnostics (lsp--workspace-diagnostics workspace)))
   2333 
   2334     (if (seq-empty-p diagnostics)
   2335         (remhash file workspace-diagnostics)
   2336       (puthash file (append diagnostics nil) workspace-diagnostics))
   2337 
   2338     (run-hooks 'lsp-diagnostics-updated-hook)))
   2339 
   2340 (defun lsp-diagnostics--workspace-cleanup (workspace)
   2341   (->> workspace
   2342        (lsp--workspace-diagnostics)
   2343        (maphash (lambda (key _)
   2344                   (lsp--on-diagnostics-update-stats
   2345                    workspace
   2346                    (lsp-make-publish-diagnostics-params
   2347                     :uri (lsp--path-to-uri key)
   2348                     :diagnostics [])))))
   2349   (clrhash (lsp--workspace-diagnostics workspace)))
   2350 
   2351 
   2352 
   2353 ;; textDocument/foldingRange support
   2354 
   2355 (cl-defstruct lsp--folding-range beg end kind children)
   2356 
   2357 (defvar-local lsp--cached-folding-ranges nil)
   2358 (defvar-local lsp--cached-nested-folding-ranges nil)
   2359 
   2360 (defun lsp--folding-range-width (range)
   2361   (- (lsp--folding-range-end range)
   2362      (lsp--folding-range-beg range)))
   2363 
   2364 (defun lsp--get-folding-ranges ()
   2365   "Get the folding ranges for the current buffer."
   2366   (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges))
   2367     (let* ((ranges (lsp-request "textDocument/foldingRange"
   2368                                 `(:textDocument ,(lsp--text-document-identifier))))
   2369            (sorted-line-col-pairs (->> ranges
   2370                                        (cl-mapcan (-lambda ((&FoldingRange :start-line
   2371                                                                            :start-character?
   2372                                                                            :end-line
   2373                                                                            :end-character?))
   2374                                                     (list (cons start-line start-character?)
   2375                                                           (cons end-line end-character?))))
   2376                                        (-sort #'lsp--line-col-comparator)))
   2377            (line-col-to-point-map (lsp--convert-line-col-to-points-batch
   2378                                    sorted-line-col-pairs)))
   2379       (setq lsp--cached-folding-ranges
   2380             (cons (buffer-chars-modified-tick)
   2381                   (--> ranges
   2382                     (seq-map (-lambda ((range &as
   2383                                               &FoldingRange :start-line
   2384                                               :start-character?
   2385                                               :end-line
   2386                                               :end-character?
   2387                                               :kind?))
   2388                                (make-lsp--folding-range
   2389                                 :beg (ht-get line-col-to-point-map
   2390                                              (cons start-line start-character?))
   2391                                 :end (ht-get line-col-to-point-map
   2392                                              (cons end-line end-character?))
   2393                                 :kind kind?))
   2394                              it)
   2395                     (seq-filter (lambda (folding-range)
   2396                                   (< (lsp--folding-range-beg folding-range)
   2397                                      (lsp--folding-range-end folding-range)))
   2398                                 it)
   2399                     (seq-into it 'list)
   2400                     (delete-dups it))))))
   2401   (cdr lsp--cached-folding-ranges))
   2402 
   2403 (defun lsp--get-nested-folding-ranges ()
   2404   "Get a list of nested folding ranges for the current buffer."
   2405   (-let [(tick . _) lsp--cached-folding-ranges]
   2406     (if (and (eq tick (buffer-chars-modified-tick))
   2407              lsp--cached-nested-folding-ranges)
   2408         lsp--cached-nested-folding-ranges
   2409       (setq lsp--cached-nested-folding-ranges
   2410             (lsp--folding-range-build-trees (lsp--get-folding-ranges))))))
   2411 
   2412 (defun lsp--folding-range-build-trees (ranges)
   2413   (setq ranges (seq-sort #'lsp--range-before-p ranges))
   2414   (let* ((dummy-node (make-lsp--folding-range
   2415                       :beg most-negative-fixnum
   2416                       :end most-positive-fixnum))
   2417          (stack (list dummy-node)))
   2418     (dolist (range ranges)
   2419       (while (not (lsp--range-inside-p range (car stack)))
   2420         (pop stack))
   2421       (push range (lsp--folding-range-children (car stack)))
   2422       (push range stack))
   2423     (lsp--folding-range-children dummy-node)))
   2424 
   2425 (defun lsp--range-inside-p (r1 r2)
   2426   "Return non-nil if folding range R1 lies inside R2"
   2427   (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2))
   2428        (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2))))
   2429 
   2430 (defun lsp--range-before-p (r1 r2)
   2431   "Return non-nil if folding range R1 ends before R2"
   2432   ;; Ensure r1 comes before r2
   2433   (or (< (lsp--folding-range-beg r1)
   2434          (lsp--folding-range-beg r2))
   2435       ;; If beg(r1) == beg(r2) make sure r2 ends first
   2436       (and (= (lsp--folding-range-beg r1)
   2437               (lsp--folding-range-beg r2))
   2438            (< (lsp--folding-range-end r2)
   2439               (lsp--folding-range-end r1)))))
   2440 
   2441 (defun lsp--point-inside-range-p (point range)
   2442   "Return non-nil if POINT lies inside folding range RANGE."
   2443   (and (>= point (lsp--folding-range-beg range))
   2444        (<= point (lsp--folding-range-end range))))
   2445 
   2446 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point)))
   2447   "Return the innermost folding range POINT lies in."
   2448   (seq-reduce (lambda (innermost-range curr-range)
   2449                 (if (and (lsp--point-inside-range-p point curr-range)
   2450                          (or (null innermost-range)
   2451                              (lsp--range-inside-p curr-range innermost-range)))
   2452                     curr-range
   2453                   innermost-range))
   2454               (lsp--get-folding-ranges)
   2455               nil))
   2456 
   2457 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point)))
   2458   "Return the outermost folding range POINT lies in."
   2459   (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range)
   2460                      (let ((curr-width (lsp--folding-range-width curr-range)))
   2461                        (if (and (lsp--point-inside-range-p point curr-range)
   2462                                 (or (null best-pair)
   2463                                     (> curr-width outermost-width)))
   2464                            (cons curr-width curr-range)
   2465                          best-pair)))
   2466                    (lsp--get-folding-ranges)
   2467                    nil)))
   2468 
   2469 (defun lsp--folding-range-at-point-bounds ()
   2470   (when (and lsp-enable-folding
   2471              (lsp-feature? "textDocument/foldingRange"))
   2472     (if-let ((range (lsp--get-current-innermost-folding-range)))
   2473         (cons (lsp--folding-range-beg range)
   2474               (lsp--folding-range-end range)))))
   2475 (put 'lsp--folding-range 'bounds-of-thing-at-point
   2476      #'lsp--folding-range-at-point-bounds)
   2477 
   2478 (defun lsp--get-nearest-folding-range (&optional backward)
   2479   (let ((point (point))
   2480         (found nil))
   2481     (while (not
   2482             (or found
   2483                 (if backward
   2484                     (<= point (point-min))
   2485                   (>= point (point-max)))))
   2486       (if backward (cl-decf point) (cl-incf point))
   2487       (setq found (lsp--get-current-innermost-folding-range point)))
   2488     found))
   2489 
   2490 (defun lsp--folding-range-at-point-forward-op (n)
   2491   (when (and lsp-enable-folding
   2492              (not (zerop n))
   2493              (lsp-feature? "textDocument/foldingRange"))
   2494     (cl-block break
   2495       (dotimes (_ (abs n))
   2496         (if-let ((range (lsp--get-nearest-folding-range (< n 0))))
   2497             (goto-char (if (< n 0)
   2498                            (lsp--folding-range-beg range)
   2499                          (lsp--folding-range-end range)))
   2500           (cl-return-from break))))))
   2501 (put 'lsp--folding-range 'forward-op
   2502      #'lsp--folding-range-at-point-forward-op)
   2503 
   2504 (defun lsp--folding-range-at-point-beginning-op ()
   2505   (goto-char (car (lsp--folding-range-at-point-bounds))))
   2506 (put 'lsp--folding-range 'beginning-op
   2507      #'lsp--folding-range-at-point-beginning-op)
   2508 
   2509 (defun lsp--folding-range-at-point-end-op ()
   2510   (goto-char (cdr (lsp--folding-range-at-point-bounds))))
   2511 (put 'lsp--folding-range 'end-op
   2512      #'lsp--folding-range-at-point-end-op)
   2513 
   2514 (defun lsp--range-at-point-bounds ()
   2515   (or (lsp--folding-range-at-point-bounds)
   2516       (when-let ((range (and
   2517                          (lsp-feature? "textDocument/hover")
   2518                          (->> (lsp--text-document-position-params)
   2519                               (lsp-request "textDocument/hover")
   2520                               (lsp:hover-range?)))))
   2521         (lsp--range-to-region range))))
   2522 
   2523 ;; A more general purpose "thing", useful for applications like focus.el
   2524 (put 'lsp--range 'bounds-of-thing-at-point
   2525      #'lsp--range-at-point-bounds)
   2526 
   2527 (defun lsp--log-io-p (method)
   2528   "Return non nil if should log for METHOD."
   2529   (and lsp-log-io
   2530        (or (not lsp-log-io-allowlist-methods)
   2531            (member method lsp-log-io-allowlist-methods))))
   2532 
   2533 
   2534 ;; toggles
   2535 
   2536 (defun lsp-toggle-trace-io ()
   2537   "Toggle client-server protocol logging."
   2538   (interactive)
   2539   (setq lsp-log-io (not lsp-log-io))
   2540   (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled")))
   2541 
   2542 (defun lsp-toggle-signature-auto-activate ()
   2543   "Toggle signature auto activate."
   2544   (interactive)
   2545   (setq lsp-signature-auto-activate
   2546         (unless lsp-signature-auto-activate '(:on-trigger-char)))
   2547   (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled"))
   2548   (lsp--update-signature-help-hook))
   2549 
   2550 (defun lsp-toggle-on-type-formatting ()
   2551   "Toggle on type formatting."
   2552   (interactive)
   2553   (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting))
   2554   (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled"))
   2555   (lsp--update-on-type-formatting-hook))
   2556 
   2557 (defun lsp-toggle-symbol-highlight ()
   2558   "Toggle symbol highlighting."
   2559   (interactive)
   2560   (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting))
   2561 
   2562   (cond
   2563    ((and lsp-enable-symbol-highlighting
   2564          (lsp-feature? "textDocument/documentHighlight"))
   2565     (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)
   2566     (lsp--info "Symbol highlighting enabled in current buffer."))
   2567    ((not lsp-enable-symbol-highlighting)
   2568     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   2569     (lsp--remove-overlays 'lsp-highlight)
   2570     (lsp--info "Symbol highlighting disabled in current buffer."))))
   2571 
   2572 
   2573 ;; keybindings
   2574 (defvar lsp--binding-descriptions nil
   2575   "List of key binding/short description pair.")
   2576 
   2577 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings)
   2578   "In KEYMAP, define key sequence KEY as DEF conditionally.
   2579 This is like `define-key', except the definition disappears
   2580 whenever COND evaluates to nil.
   2581 DESC is the short-description for the binding.
   2582 BINDINGS is a list of (key def desc cond)."
   2583   (declare (indent defun)
   2584            (debug (form form form form form &rest sexp)))
   2585   (->> (cl-list* key def desc cond bindings)
   2586        (-partition 4)
   2587        (-mapcat (-lambda ((key def desc cond))
   2588                   `((define-key ,keymap ,key
   2589                       '(menu-item
   2590                         ,(format "maybe-%s" def)
   2591                         ,def
   2592                         :filter
   2593                         (lambda (item)
   2594                           (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer)
   2595                                                            lsp--describe-buffer)
   2596                                                          (current-buffer))
   2597                                   ,cond)
   2598                             item))))
   2599                     (when (stringp ,key)
   2600                       (setq lsp--binding-descriptions
   2601                             (append lsp--binding-descriptions '(,key ,desc)))))))
   2602        macroexp-progn))
   2603 
   2604 (defvar lsp--describe-buffer nil)
   2605 
   2606 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus)
   2607   (let ((lsp--describe-buffer buffer))
   2608     (funcall fn buffer prefix menus)))
   2609 
   2610 (advice-add 'describe-buffer-bindings
   2611             :around
   2612             #'lsp-describe-buffer-bindings-advice)
   2613 
   2614 (defun lsp--prepend-prefix (mappings)
   2615   (->> mappings
   2616        (-partition 2)
   2617        (-mapcat (-lambda ((key description))
   2618                   (list (concat lsp-keymap-prefix " " key)
   2619                         description)))))
   2620 
   2621 (defvar lsp-command-map
   2622   (-doto (make-sparse-keymap)
   2623     (lsp-define-conditional-key
   2624       ;; workspaces
   2625       "wD" lsp-disconnect "disconnect" (lsp-workspaces)
   2626       "wd" lsp-describe-session "describe session" t
   2627       "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces)
   2628       "wr" lsp-workspace-restart "restart server" (lsp-workspaces)
   2629       "ws" lsp "start server" t
   2630 
   2631       ;; formatting
   2632       "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting")
   2633                                                  (lsp-feature? "textDocument/formatting"))
   2634       "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting")
   2635 
   2636       ;; folders
   2637       "Fa" lsp-workspace-folders-add "add folder" t
   2638       "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t
   2639       "Fr" lsp-workspace-folders-remove "remove folder" t
   2640 
   2641       ;; toggles
   2642       "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature?
   2643                                                                         "textDocument/publishDiagnostics")
   2644       "TL" lsp-toggle-trace-io "toggle log io" t
   2645       "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline)
   2646       "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs)
   2647       "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature?
   2648                                                                           "textDocument/codeAction")
   2649       "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature?
   2650                                                                "textDocument/documentSymbol")
   2651       "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc)
   2652       "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature?
   2653                                                                       "textDocument/onTypeFormatting")
   2654       "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight")
   2655       "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens")
   2656       "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp")
   2657 
   2658       ;; goto
   2659       "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol")
   2660       "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration")
   2661       "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list)
   2662       "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition")
   2663       "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls")
   2664                                                              (fboundp 'lsp-treemacs-call-hierarchy))
   2665       "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation")
   2666       "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references")
   2667       "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition")
   2668 
   2669       ;; help
   2670       "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc)
   2671                                                   (lsp-feature? "textDocument/hover"))
   2672       "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover")
   2673       "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp")
   2674 
   2675       ;; refactoring
   2676       "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction")
   2677       "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename")
   2678 
   2679       ;; actions
   2680       "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction")
   2681       "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight")
   2682       "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy))
   2683 
   2684       ;; peeks
   2685       "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition")
   2686                                                                 (fboundp 'lsp-ui-peek-find-definitions))
   2687       "Gi" lsp-ui-peek-find-implementation "peek implementations" (and
   2688                                                                    (fboundp 'lsp-ui-peek-find-implementation)
   2689                                                                    (lsp-feature? "textDocument/implementation"))
   2690       "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references)
   2691                                                               (lsp-feature? "textDocument/references"))
   2692       "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp
   2693                                                                            'lsp-ui-peek-find-workspace-symbol)
   2694                                                                           (lsp-feature? "workspace/symbol")))))
   2695 
   2696 
   2697 ;; which-key integration
   2698 
   2699 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key")
   2700 (declare-function which-key-add-key-based-replacements "ext:which-key")
   2701 
   2702 (defun lsp-enable-which-key-integration (&optional all-modes)
   2703   "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current
   2704 active `major-mode', or for all major modes when ALL-MODES is t."
   2705   (cl-flet ((which-key-fn (if all-modes
   2706                               'which-key-add-key-based-replacements
   2707                             (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode))))
   2708     (apply
   2709      #'which-key-fn
   2710      (lsp--prepend-prefix
   2711       (cl-list*
   2712        ""    "lsp"
   2713        "w"   "workspaces"
   2714        "F"   "folders"
   2715        "="   "formatting"
   2716        "T"   "toggle"
   2717        "g"   "goto"
   2718        "h"   "help"
   2719        "r"   "refactor"
   2720        "a"   "code actions"
   2721        "G"   "peek"
   2722        lsp--binding-descriptions)))))
   2723 
   2724 
   2725 ;; Globbing syntax
   2726 
   2727 ;; We port VSCode's glob-to-regexp code
   2728 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts)
   2729 ;; since the LSP globbing syntax seems to be the same as that of
   2730 ;; VSCode.
   2731 
   2732 (defconst lsp-globstar "**"
   2733   "Globstar pattern.")
   2734 
   2735 (defconst lsp-glob-split ?/
   2736   "The character by which we split path components in a glob
   2737 pattern.")
   2738 
   2739 (defconst lsp-path-regexp "[/\\\\]"
   2740   "Forward or backslash to be used as a path separator in
   2741 computed regexps.")
   2742 
   2743 (defconst lsp-non-path-regexp "[^/\\\\]"
   2744   "A regexp matching anything other than a slash.")
   2745 
   2746 (defconst lsp-globstar-regexp
   2747   (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?"
   2748           lsp-path-regexp
   2749           lsp-non-path-regexp lsp-path-regexp
   2750           lsp-path-regexp lsp-non-path-regexp)
   2751   "Globstar in regexp form.")
   2752 
   2753 (defun lsp-split-glob-pattern (pattern split-char)
   2754   "Split PATTERN at SPLIT-CHAR while respecting braces and brackets."
   2755   (when pattern
   2756     (let ((segments nil)
   2757           (in-braces nil)
   2758           (in-brackets nil)
   2759           (current-segment ""))
   2760       (dolist (char (string-to-list pattern))
   2761         (cl-block 'exit-point
   2762           (if (eq char split-char)
   2763               (when (and (null in-braces)
   2764                          (null in-brackets))
   2765                 (push current-segment segments)
   2766                 (setq current-segment "")
   2767                 (cl-return-from 'exit-point))
   2768             (pcase char
   2769               (?{
   2770                (setq in-braces t))
   2771               (?}
   2772                (setq in-braces nil))
   2773               (?\[
   2774                (setq in-brackets t))
   2775               (?\]
   2776                (setq in-brackets nil))))
   2777           (setq current-segment (concat current-segment
   2778                                         (char-to-string char)))))
   2779       (unless (string-empty-p current-segment)
   2780         (push current-segment segments))
   2781       (nreverse segments))))
   2782 
   2783 (defun lsp--glob-to-regexp (pattern)
   2784   "Helper function to convert a PATTERN from LSP's glob syntax to
   2785 an Elisp regexp."
   2786   (if (string-empty-p pattern)
   2787       ""
   2788     (let ((current-regexp "")
   2789           (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split)))
   2790       (if (-all? (lambda (segment) (eq segment lsp-globstar))
   2791                  glob-segments)
   2792           ".*"
   2793         (let ((prev-segment-was-globstar nil))
   2794           (seq-do-indexed
   2795            (lambda (segment index)
   2796              (if (string-equal segment lsp-globstar)
   2797                  (unless prev-segment-was-globstar
   2798                    (setq current-regexp (concat current-regexp
   2799                                                 lsp-globstar-regexp))
   2800                    (setq prev-segment-was-globstar t))
   2801                (let ((in-braces nil)
   2802                      (brace-val "")
   2803                      (in-brackets nil)
   2804                      (bracket-val ""))
   2805                  (dolist (char (string-to-list segment))
   2806                    (cond
   2807                     ((and (not (char-equal char ?\}))
   2808                           in-braces)
   2809                      (setq brace-val (concat brace-val
   2810                                              (char-to-string char))))
   2811                     ((and in-brackets
   2812                           (or (not (char-equal char ?\]))
   2813                               (string-empty-p bracket-val)))
   2814                      (let ((curr (cond
   2815                                   ((char-equal char ?-)
   2816                                    "-")
   2817                                   ;; NOTE: ?\^ and ?^ are different characters
   2818                                   ((and (memq char '(?^ ?!))
   2819                                         (string-empty-p bracket-val))
   2820                                    "^")
   2821                                   ((char-equal char lsp-glob-split)
   2822                                    "")
   2823                                   (t
   2824                                    (regexp-quote (char-to-string char))))))
   2825                        (setq bracket-val (concat bracket-val curr))))
   2826                     (t
   2827                      (cl-case char
   2828                        (?{
   2829                         (setq in-braces t))
   2830                        (?\[
   2831                         (setq in-brackets t))
   2832                        (?}
   2833                         (let* ((choices (lsp-split-glob-pattern brace-val ?\,))
   2834                                (brace-regexp (concat "\\(?:"
   2835                                                      (mapconcat #'lsp--glob-to-regexp choices "\\|")
   2836                                                      "\\)")))
   2837                           (setq current-regexp (concat current-regexp
   2838                                                        brace-regexp))
   2839                           (setq in-braces nil)
   2840                           (setq brace-val "")))
   2841                        (?\]
   2842                         (setq current-regexp
   2843                               (concat current-regexp
   2844                                       "[" bracket-val "]"))
   2845                         (setq in-brackets nil)
   2846                         (setq bracket-val ""))
   2847                        (??
   2848                         (setq current-regexp
   2849                               (concat current-regexp
   2850                                       lsp-non-path-regexp)))
   2851                        (?*
   2852                         (setq current-regexp
   2853                               (concat current-regexp
   2854                                       lsp-non-path-regexp "*?")))
   2855                        (t
   2856                         (setq current-regexp
   2857                               (concat current-regexp
   2858                                       (regexp-quote (char-to-string char)))))))))
   2859                  (when (and (< index (1- (length glob-segments)))
   2860                             (or (not (string-equal (nth (1+ index) glob-segments)
   2861                                                    lsp-globstar))
   2862                                 (< (+ index 2)
   2863                                    (length glob-segments))))
   2864                    (setq current-regexp
   2865                          (concat current-regexp
   2866                                  lsp-path-regexp)))
   2867                  (setq prev-segment-was-globstar nil))))
   2868            glob-segments)
   2869           current-regexp)))))
   2870 
   2871 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365
   2872 (defun lsp-glob-unbrace-at-top-level (glob-pattern)
   2873   "If GLOB-PATTERN does not start with a brace, return a singleton list
   2874 containing GLOB-PATTERN.
   2875 
   2876 If GLOB-PATTERN does start with a brace, return a list of the
   2877 comma-separated globs within the top-level braces."
   2878   (if (not (string-prefix-p "{" glob-pattern))
   2879       (list glob-pattern)
   2880     (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,)))
   2881 
   2882 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern)
   2883   "Convert GLOB-PATTERN to a regexp wrapped with the beginning-
   2884 and end-of-string meta-characters."
   2885   (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'"))
   2886 
   2887 (defun lsp-glob-to-regexps (glob-pattern)
   2888   "Convert a GLOB-PATTERN to a list of Elisp regexps."
   2889   (when-let*
   2890       ((glob-pattern (cond ((hash-table-p glob-pattern)
   2891                             (ht-get glob-pattern "pattern"))
   2892                            ((stringp glob-pattern) glob-pattern)
   2893                            (t (error "Unknown glob-pattern type: %s" glob-pattern))))
   2894        (trimmed-pattern (string-trim glob-pattern))
   2895        (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern)))
   2896     (seq-map #'lsp-glob-convert-to-wrapped-regexp
   2897              top-level-unbraced-patterns)))
   2898 
   2899 
   2900 
   2901 (defvar lsp-mode-menu)
   2902 
   2903 (defun lsp-mouse-click (event)
   2904   (interactive "e")
   2905   (let* ((ec (event-start event))
   2906          (choice (x-popup-menu event lsp-mode-menu))
   2907          (action (lookup-key lsp-mode-menu (apply 'vector choice))))
   2908 
   2909     (select-window (posn-window ec))
   2910 
   2911     (unless (and (region-active-p) (eq action 'lsp-execute-code-action))
   2912       (goto-char (posn-point ec)))
   2913     (run-with-idle-timer
   2914      0.001 nil
   2915      (lambda ()
   2916        (cl-labels ((check (value) (not (null value))))
   2917          (when choice
   2918            (call-interactively action)))))))
   2919 
   2920 (defvar lsp-mode-map
   2921   (let ((map (make-sparse-keymap)))
   2922     (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse)
   2923     (define-key map (kbd "C-<mouse-1>") #'ignore)
   2924     (define-key map (kbd "<mouse-3>") #'lsp-mouse-click)
   2925     (define-key map (kbd "C-S-SPC") #'lsp-signature-activate)
   2926     (when lsp-keymap-prefix
   2927       (define-key map (kbd lsp-keymap-prefix) lsp-command-map))
   2928     map)
   2929   "Keymap for `lsp-mode'.")
   2930 
   2931 (define-minor-mode lsp-mode "Mode for LSP interaction."
   2932   :keymap lsp-mode-map
   2933   :lighter
   2934   (" LSP["
   2935    (lsp--buffer-workspaces
   2936     (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "]["))
   2937     (:propertize "Disconnected" face warning))
   2938    "]")
   2939   :group 'lsp-mode
   2940   (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred))
   2941     ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp'
   2942     (lsp)))
   2943 
   2944 (defvar lsp-mode-menu
   2945   (easy-menu-create-menu
   2946    nil
   2947    `(["Go to definition" lsp-find-definition
   2948       :active (lsp-feature? "textDocument/definition")]
   2949      ["Find references" lsp-find-references
   2950       :active (lsp-feature? "textDocument/references")]
   2951      ["Find implementations" lsp-find-implementation
   2952       :active (lsp-feature? "textDocument/implementation")]
   2953      ["Find declarations" lsp-find-declaration
   2954       :active (lsp-feature? "textDocument/declaration")]
   2955      ["Go to type declaration" lsp-find-type-definition
   2956       :active (lsp-feature? "textDocument/typeDefinition")]
   2957      "--"
   2958      ["Describe" lsp-describe-thing-at-point]
   2959      ["Code action" lsp-execute-code-action]
   2960      ["Format" lsp-format-buffer]
   2961      ["Highlight references" lsp-document-highlight]
   2962      ["Type Hierarchy" lsp-java-type-hierarchy
   2963       :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")]
   2964      ["Type Hierarchy" lsp-treemacs-type-hierarchy
   2965       :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy"))
   2966                     (functionp 'lsp-treemacs-type-hierarchy)
   2967                     (lsp-feature? "textDocument/typeHierarchy"))]
   2968      ["Call Hierarchy" lsp-treemacs-call-hierarchy
   2969       :visible (and (functionp 'lsp-treemacs-call-hierarchy)
   2970                     (lsp-feature? "textDocument/callHierarchy"))]
   2971      ["Rename" lsp-rename
   2972       :active (lsp-feature? "textDocument/rename")]
   2973      "--"
   2974      ("Session"
   2975       ["View logs" lsp-workspace-show-log]
   2976       ["Describe" lsp-describe-session]
   2977       ["Shutdown" lsp-shutdown-workspace]
   2978       ["Restart" lsp-restart-workspace])
   2979      ("Workspace Folders"
   2980       ["Add" lsp-workspace-folders-add]
   2981       ["Remove" lsp-workspace-folders-remove]
   2982       ["Open" lsp-workspace-folders-open])
   2983      ("Toggle features"
   2984       ["Lenses" lsp-lens-mode]
   2985       ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode]
   2986       ["Modeline code actions" lsp-modeline-code-actions-mode]
   2987       ["Modeline diagnostics" lsp-modeline-diagnostics-mode])
   2988      "---"
   2989      ("Debug"
   2990       :active (bound-and-true-p dap-ui-mode)
   2991       :filter ,(lambda (_)
   2992                  (and (boundp 'dap-ui-menu-items)
   2993                       (nthcdr 3 dap-ui-menu-items))))))
   2994   "Menu for lsp-mode.")
   2995 
   2996 (defalias 'make-lsp-client 'make-lsp--client)
   2997 
   2998 (cl-defstruct lsp--registered-capability
   2999   (id "")
   3000   (method " ")
   3001   (options nil))
   3002 
   3003 ;; A ‘lsp--workspace’ object represents exactly one language server process.
   3004 (cl-defstruct lsp--workspace
   3005   ;; the `ewoc' object for displaying I/O to and from the server
   3006   (ewoc nil)
   3007 
   3008   ;; ‘server-capabilities’ is a hash table of the language server capabilities.
   3009   ;; It is the hash table representation of a LSP ServerCapabilities structure;
   3010   ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize.
   3011   (server-capabilities nil)
   3012 
   3013   ;; ‘registered-server-capabilities’ is a list of hash tables that represent
   3014   ;; dynamically-registered Registration objects.  See
   3015   ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability.
   3016   (registered-server-capabilities nil)
   3017 
   3018   ;; ‘root’ is a directory name or a directory file name for the workspace
   3019   ;; root.  ‘lsp-mode’ passes this directory to the ‘initialize’ method of the
   3020   ;; language server; see
   3021   ;; https://microsoft.github.io/language-server-protocol/specification#initialize.
   3022   (root nil)
   3023 
   3024   ;; ‘client’ is the ‘lsp--client’ object associated with this workspace.
   3025   (client nil)
   3026 
   3027   ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It
   3028   ;; used to derive the file path in `lsp--uri-to-path' when using tramp
   3029   ;; connection.
   3030   (host-root nil)
   3031 
   3032   ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or
   3033   ;; a network connection.  ‘lsp-mode’ communicates with ‘proc’ using the
   3034   ;; language server protocol.  ‘proc’ corresponds to the COMMUNICATION-PROCESS
   3035   ;; element of the return value of the client’s ‘get-root’ field, which see.
   3036   (proc nil)
   3037 
   3038   ;; ‘proc’ is a process object; it must represent a regular process, not a
   3039   ;; pipe or network process.  It represents the actual server process that
   3040   ;; corresponds to this workspace.  ‘cmd-proc’ corresponds to the
   3041   ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’
   3042   ;; field, which see.
   3043   (cmd-proc nil)
   3044 
   3045   ;; ‘buffers’ is a list of buffers associated with this workspace.
   3046   (buffers nil)
   3047 
   3048   ;; if semantic tokens is enabled, `semantic-tokens-faces' contains
   3049   ;; one face (or nil) for each token type supported by the language server.
   3050   (semantic-tokens-faces nil)
   3051 
   3052   ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces'
   3053   ;; contains one face (or nil) for each modifier type supported by the language
   3054   ;; server
   3055   (semantic-tokens-modifier-faces nil)
   3056 
   3057   ;; Extra client capabilities provided by third-party packages using
   3058   ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME
   3059   ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name,
   3060   ;; and CAPS is either a plist of the client capabilities, or a function that
   3061   ;; takes no argument and returns a plist of the client capabilities or nil.
   3062   (extra-client-capabilities nil)
   3063 
   3064   ;; Workspace status
   3065   (status nil)
   3066 
   3067   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3068   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3069   (metadata (make-hash-table :test 'equal))
   3070 
   3071   ;; contains all the file notification watches that have been created for the
   3072   ;; current workspace in format filePath->file notification handle.
   3073   (watches (make-hash-table :test 'equal))
   3074 
   3075   ;; list of workspace folders
   3076   (workspace-folders nil)
   3077 
   3078   ;; ‘last-id’ the last request id for the current workspace.
   3079   (last-id 0)
   3080 
   3081   ;; ‘status-string’ allows extensions to specify custom status string based on
   3082   ;; the Language Server specific messages.
   3083   (status-string nil)
   3084 
   3085   ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it
   3086   ;; was stopped).
   3087   shutdown-action
   3088 
   3089   ;; ‘diagnostics’ a hashmap with workspace diagnostics.
   3090   (diagnostics (make-hash-table :test 'equal))
   3091 
   3092   ;; contains all the workDone progress tokens that have been created
   3093   ;; for the current workspace.
   3094   (work-done-tokens (make-hash-table :test 'equal)))
   3095 
   3096 
   3097 (cl-defstruct lsp-session
   3098   ;; contains the folders that are part of the current session
   3099   folders
   3100   ;; contains the folders that must not be imported in the current workspace.
   3101   folders-blocklist
   3102   ;; contains the list of folders that must be imported in a project in case of
   3103   ;; multi root LSP server.
   3104   (server-id->folders (make-hash-table :test 'equal))
   3105   ;; folder to list of the servers that are associated with the folder.
   3106   (folder->servers (make-hash-table :test 'equal))
   3107   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3108   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3109   (metadata (make-hash-table :test 'equal)))
   3110 
   3111 (defun lsp-workspace-status (status-string &optional workspace)
   3112   "Set current workspace status to STATUS-STRING.
   3113 If WORKSPACE is not specified defaults to lsp--cur-workspace."
   3114   (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string))))
   3115     (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string)))
   3116 
   3117 (defun lsp-session-set-metadata (key value &optional _workspace)
   3118   "Associate KEY with VALUE in the WORKSPACE metadata.
   3119 If WORKSPACE is not provided current workspace will be used."
   3120   (puthash key value (lsp-session-metadata (lsp-session))))
   3121 
   3122 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata)
   3123 
   3124 (defun lsp-session-get-metadata (key &optional _workspace)
   3125   "Lookup KEY in WORKSPACE metadata.
   3126 If WORKSPACE is not provided current workspace will be used."
   3127   (gethash key (lsp-session-metadata (lsp-session))))
   3128 
   3129 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata)
   3130 
   3131 (defun lsp-workspace-set-work-done-token (token value workspace)
   3132   "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens."
   3133   (puthash token value (lsp--workspace-work-done-tokens workspace)))
   3134 
   3135 (defun lsp-workspace-get-work-done-token (token workspace)
   3136   "Lookup TOKEN in the WORKSPACE work-done-tokens."
   3137   (gethash token (lsp--workspace-work-done-tokens workspace)))
   3138 
   3139 (defun lsp-workspace-rem-work-done-token (token workspace)
   3140   "Remove TOKEN from the WORKSPACE work-done-tokens."
   3141   (remhash token (lsp--workspace-work-done-tokens workspace)))
   3142 
   3143 
   3144 (defun lsp--make-notification (method &optional params)
   3145   "Create notification body for method METHOD and parameters PARAMS."
   3146   (list :jsonrpc "2.0" :method method :params params))
   3147 
   3148 (defalias 'lsp--make-request 'lsp--make-notification)
   3149 (defalias 'lsp-make-request 'lsp--make-notification)
   3150 
   3151 (defun lsp--make-response (id result)
   3152   "Create response for REQUEST with RESULT."
   3153   `(:jsonrpc "2.0" :id ,id :result ,result))
   3154 
   3155 (defun lsp-make-notification (method &optional params)
   3156   "Create notification body for method METHOD and parameters PARAMS."
   3157   (lsp--make-notification method params))
   3158 
   3159 (defmacro lsp--json-serialize (params)
   3160   (if (progn
   3161         (require 'json)
   3162         (fboundp 'json-serialize))
   3163       `(json-serialize ,params
   3164                        :null-object nil
   3165                        :false-object :json-false)
   3166     `(let ((json-false :json-false))
   3167        (json-encode ,params))))
   3168 
   3169 (defun lsp--make-message (params)
   3170   "Create a LSP message from PARAMS, after encoding it to a JSON string."
   3171   (let ((body (lsp--json-serialize params)))
   3172     (concat "Content-Length: "
   3173             (number-to-string (1+ (string-bytes body)))
   3174             "\r\n\r\n"
   3175             body
   3176             "\n")))
   3177 
   3178 (cl-defstruct lsp--log-entry timestamp process-time type method id body)
   3179 
   3180 (defun lsp--make-log-entry (method id body type &optional process-time)
   3181   "Create an outgoing log object from BODY with method METHOD and id ID.
   3182 If ID is non-nil, then the body is assumed to be a notification.
   3183 TYPE can either be `incoming' or `outgoing'"
   3184   (cl-assert (memq type '(incoming-req outgoing-req incoming-notif
   3185                                        outgoing-notif incoming-resp
   3186                                        outgoing-resp)))
   3187   (make-lsp--log-entry
   3188    :timestamp (format-time-string "%I:%M:%S %p")
   3189    :process-time process-time
   3190    :method method
   3191    :id id
   3192    :type type
   3193    :body body))
   3194 
   3195 (defun lsp--log-font-lock-json (body)
   3196   "Font lock JSON BODY."
   3197   (with-temp-buffer
   3198     (insert body)
   3199     ;; We set the temp buffer file-name extension to .json and call `set-auto-mode'
   3200     ;; so the users configured json mode is used which could be
   3201     ;; `json-mode', `json-ts-mode', `jsonian-mode', etc.
   3202     (let ((buffer-file-name "lsp-log.json"))
   3203       (delay-mode-hooks
   3204         (set-auto-mode)
   3205         (if (fboundp 'font-lock-ensure)
   3206             (font-lock-ensure)
   3207           (with-no-warnings
   3208             (font-lock-fontify-buffer)))))
   3209     (buffer-string)))
   3210 
   3211 (defun lsp--log-entry-pp (entry)
   3212   (cl-assert (lsp--log-entry-p entry))
   3213   (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time
   3214                           body)
   3215                entry)
   3216               (json-false :json-false)
   3217               (json-encoding-pretty-print t)
   3218               (str nil))
   3219     (setq str
   3220           (concat (format "[Trace - %s] " timestamp)
   3221                   (pcase type
   3222                     ('incoming-req (format "Received request '%s - (%s)." method id))
   3223                     ('outgoing-req (format "Sending request '%s - (%s)'." method id))
   3224 
   3225                     ('incoming-notif (format "Received notification '%s'." method))
   3226                     ('outgoing-notif (format "Sending notification '%s'." method))
   3227 
   3228                     ('incoming-resp (format "Received response '%s - (%s)' in %dms."
   3229                                             method id process-time))
   3230                     ('outgoing-resp
   3231                      (format
   3232                       "Sending response '%s - (%s)'. Processing request took %dms"
   3233                       method id process-time)))
   3234                   "\n"
   3235                   (if (memq type '(incoming-resp ougoing-resp))
   3236                       "Result: "
   3237                     "Params: ")
   3238                   (lsp--log-font-lock-json (json-encode body))
   3239                   "\n\n\n"))
   3240     (setq str (propertize str 'mouse-face 'highlight 'read-only t))
   3241     (insert str)))
   3242 
   3243 (defvar-local lsp--log-io-ewoc nil)
   3244 
   3245 (defun lsp--get-create-io-ewoc (workspace)
   3246   (if (and (lsp--workspace-ewoc workspace)
   3247            (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace))))
   3248       (lsp--workspace-ewoc workspace)
   3249     (with-current-buffer (lsp--get-log-buffer-create workspace)
   3250       (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode))
   3251       (setq-local window-point-insertion-type t)
   3252       (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t))
   3253       (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc))
   3254     (lsp--workspace-ewoc workspace)))
   3255 
   3256 (defun lsp--ewoc-count (ewoc)
   3257   (let* ((count 0)
   3258          (count-fn (lambda (_) (setq count (1+ count)))))
   3259     (ewoc-map count-fn ewoc)
   3260     count))
   3261 
   3262 (defun lsp--log-entry-new (entry workspace)
   3263   (let* ((ewoc (lsp--get-create-io-ewoc workspace))
   3264          (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc)))
   3265          (node (if (or (eq lsp-io-messages-max t)
   3266                        (>= lsp-io-messages-max count))
   3267                    nil
   3268                  (ewoc-nth ewoc (1- lsp-io-messages-max))))
   3269          (prev nil)
   3270          (inhibit-read-only t))
   3271     (while node
   3272       (setq prev (ewoc-prev ewoc node))
   3273       (ewoc-delete ewoc node)
   3274       (setq node prev))
   3275     (ewoc-enter-last ewoc entry)))
   3276 
   3277 (defun lsp--send-notification (body)
   3278   "Send BODY as a notification to the language server."
   3279   (lsp-foreach-workspace
   3280    (when (lsp--log-io-p (plist-get body :method))
   3281      (lsp--log-entry-new (lsp--make-log-entry
   3282                           (plist-get body :method)
   3283                           nil (plist-get body :params) 'outgoing-notif)
   3284                          lsp--cur-workspace))
   3285    (lsp--send-no-wait body
   3286                       (lsp--workspace-proc lsp--cur-workspace))))
   3287 
   3288 (defalias 'lsp-send-notification 'lsp--send-notification)
   3289 
   3290 (defun lsp-notify (method params)
   3291   "Send notification METHOD with PARAMS."
   3292   (lsp--send-notification (lsp--make-notification method params)))
   3293 
   3294 (defun lsp--cur-workspace-check ()
   3295   "Check whether buffer lsp workspace(s) are set."
   3296   (cl-assert (lsp-workspaces) nil
   3297              "No language server(s) is associated with this buffer."))
   3298 
   3299 (defun lsp--send-request (body &optional no-wait no-merge)
   3300   "Send BODY as a request to the language server, get the response.
   3301 If NO-WAIT is non-nil, don't synchronously wait for a response.
   3302 If NO-MERGE is non-nil, don't merge the results but return an
   3303 alist mapping workspace->result."
   3304   (lsp-request (plist-get body :method)
   3305                (plist-get body :params)
   3306                :no-wait no-wait
   3307                :no-merge no-merge))
   3308 
   3309 (defalias 'lsp-send-request 'lsp--send-request
   3310   "Send BODY as a request to the language server and return the response
   3311 synchronously.
   3312 \n(fn BODY)")
   3313 
   3314 (cl-defun lsp-request (method params &key no-wait no-merge)
   3315   "Send request METHOD with PARAMS.
   3316 If NO-MERGE is non-nil, don't merge the results but return alist
   3317 workspace->result.
   3318 If NO-WAIT is non-nil send the request as notification."
   3319   (if no-wait
   3320       (lsp-notify method params)
   3321     (let* ((send-time (float-time))
   3322            ;; max time by which we must get a response
   3323            (expected-time
   3324             (and
   3325              lsp-response-timeout
   3326              (+ send-time lsp-response-timeout)))
   3327            resp-result resp-error done?)
   3328       (unwind-protect
   3329           (progn
   3330             (lsp-request-async method params
   3331                                (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3332                                :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3333                                :no-merge no-merge
   3334                                :mode 'detached
   3335                                :cancel-token :sync-request)
   3336             (while (not (or resp-error resp-result))
   3337               (if (functionp 'json-rpc-connection)
   3338                   (catch 'lsp-done (sit-for 0.01))
   3339                 (catch 'lsp-done
   3340                   (accept-process-output
   3341                    nil
   3342                    (if expected-time (- expected-time send-time) 1))))
   3343               (setq send-time (float-time))
   3344               (when (and expected-time (< expected-time send-time))
   3345                 (error "Timeout while waiting for response.  Method: %s" method)))
   3346             (setq done? t)
   3347             (cond
   3348              ((eq resp-result :finished) nil)
   3349              (resp-result resp-result)
   3350              ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3351              ((lsp-json-error? (cl-first resp-error))
   3352               (error (lsp:json-error-message (cl-first resp-error))))))
   3353         (unless done?
   3354           (lsp-cancel-request-by-token :sync-request))))))
   3355 
   3356 (cl-defun lsp-request-while-no-input (method params)
   3357   "Send request METHOD with PARAMS and waits until there is no input.
   3358 Return same value as `lsp--while-no-input' and respecting `non-essential'."
   3359   (if (or non-essential (not lsp-request-while-no-input-may-block))
   3360       (let* ((send-time (float-time))
   3361              ;; max time by which we must get a response
   3362              (expected-time
   3363               (and
   3364                lsp-response-timeout
   3365                (+ send-time lsp-response-timeout)))
   3366              resp-result resp-error done?)
   3367         (unwind-protect
   3368             (progn
   3369               (lsp-request-async method params
   3370                                  (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3371                                  :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3372                                  :mode 'detached
   3373                                  :cancel-token :sync-request)
   3374               (while (not (or resp-error resp-result (input-pending-p)))
   3375                 (catch 'lsp-done
   3376                   (sit-for
   3377                    (if expected-time (- expected-time send-time) 1)))
   3378                 (setq send-time (float-time))
   3379                 (when (and expected-time (< expected-time send-time))
   3380                   (error "Timeout while waiting for response.  Method: %s" method)))
   3381               (setq done? (or resp-error resp-result))
   3382               (cond
   3383                ((eq resp-result :finished) nil)
   3384                (resp-result resp-result)
   3385                ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3386                ((lsp-json-error? (cl-first resp-error))
   3387                 (error (lsp:json-error-message (cl-first resp-error))))))
   3388           (unless done?
   3389             (lsp-cancel-request-by-token :sync-request))
   3390           (when (and (input-pending-p) lsp--throw-on-input)
   3391             (throw 'input :interrupted))))
   3392     (lsp-request method params)))
   3393 
   3394 (defvar lsp--cancelable-requests (ht))
   3395 
   3396 (cl-defun lsp-request-async (method params callback
   3397                                     &key mode error-handler cancel-handler no-merge cancel-token)
   3398   "Send METHOD with PARAMS as a request to the language server.
   3399 Call CALLBACK with the response received from the server
   3400 asynchronously.
   3401 MODE determines when the callback will be called depending on the
   3402 condition of the original buffer.  It could be:
   3403 - `detached' which means that the callback will be executed no
   3404 matter what has happened to the buffer.
   3405 - `alive' - the callback will be executed only if the buffer from
   3406 which the call was executed is still alive.
   3407 - `current' the callback will be executed only if the original buffer
   3408 is still selected.
   3409 - `tick' - the callback will be executed only if the buffer was not modified.
   3410 - `unchanged' - the callback will be executed only if the buffer hasn't
   3411 changed and if the buffer is not modified.
   3412 
   3413 ERROR-HANDLER will be called in case the request has failed.
   3414 CANCEL-HANDLER will be called in case the request is being canceled.
   3415 If NO-MERGE is non-nil, don't merge the results but return alist
   3416 workspace->result.
   3417 CANCEL-TOKEN is the token that can be used to cancel request."
   3418   (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params)
   3419                            callback mode error-handler cancel-handler no-merge cancel-token))
   3420 
   3421 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback)
   3422   (lambda (&rest _)
   3423     (unless (and (equal 'post-command-hook hook)
   3424                  (equal (current-buffer) buf))
   3425       (lsp--request-cleanup-hooks id)
   3426       (with-lsp-workspaces workspaces
   3427         (lsp--cancel-request id)
   3428         (when cancel-callback (funcall cancel-callback)))
   3429       (lsp-log "Cancelling %s(%s) in hook %s" method id hook))))
   3430 
   3431 (defun lsp--create-async-callback
   3432     (callback method no-merge workspaces)
   3433   "Create async handler expecting COUNT results, merge them and call CALLBACK.
   3434 MODE determines when the callback will be called depending on the
   3435 condition of the original buffer. METHOD is the invoked method.
   3436 If NO-MERGE is non-nil, don't merge the results but return alist
   3437 workspace->result. ID is the request id."
   3438   (let (results errors)
   3439     (lambda (result)
   3440       (push (cons lsp--cur-workspace result)
   3441             (if (eq result :error) errors results))
   3442       (when (and (not (eq (length errors) (length workspaces)))
   3443                  (eq (+ (length errors) (length results)) (length workspaces)))
   3444         (funcall callback
   3445                  (if no-merge
   3446                      results
   3447                    (lsp--merge-results (-map #'cl-rest results) method)))))))
   3448 
   3449 (defcustom lsp-default-create-error-handler-fn nil
   3450   "Default error handler customization.
   3451 Handler should give METHOD as argument and return function of one argument
   3452 ERROR."
   3453   :type 'function
   3454   :group 'lsp-mode
   3455   :package-version '(lsp-mode . "9.0.0"))
   3456 
   3457 (defun lsp--create-default-error-handler (method)
   3458   "Default error handler.
   3459 METHOD is the executed method."
   3460   (if lsp-default-create-error-handler-fn
   3461       (funcall lsp-default-create-error-handler-fn method)
   3462     (lambda (error)
   3463       (lsp--warn "%s" (or (lsp--error-string error)
   3464                           (format "%s Request has failed" method))))))
   3465 
   3466 (defvar lsp--request-cleanup-hooks (ht))
   3467 
   3468 (defun lsp--request-cleanup-hooks (request-id)
   3469   (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks)))
   3470     (funcall cleanup-function)
   3471     (remhash request-id lsp--request-cleanup-hooks)))
   3472 
   3473 (defun lsp-cancel-request-by-token (cancel-token)
   3474   "Cancel request using CANCEL-TOKEN."
   3475   (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests))
   3476     (with-lsp-workspaces workspaces
   3477       (lsp--cancel-request request-id))
   3478     (remhash cancel-token lsp--cancelable-requests)
   3479     (lsp--request-cleanup-hooks request-id)))
   3480 
   3481 (defun lsp--send-request-async (body callback
   3482                                      &optional mode error-callback cancel-callback
   3483                                      no-merge cancel-token)
   3484   "Send BODY as a request to the language server.
   3485 Call CALLBACK with the response received from the server
   3486 asynchronously.
   3487 MODE determines when the callback will be called depending on the
   3488 condition of the original buffer.  It could be:
   3489 - `detached' which means that the callback will be executed no
   3490 matter what has happened to the buffer.
   3491 - `alive' - the callback will be executed only if the buffer from
   3492 which the call was executed is still alive.
   3493 - `current' the callback will be executed only if the original buffer
   3494 is still selected.
   3495 - `tick' - the callback will be executed only if the buffer was not modified.
   3496 - `unchanged' - the callback will be executed only if the buffer hasn't
   3497 changed and if the buffer is not modified.
   3498 
   3499 ERROR-CALLBACK will be called in case the request has failed.
   3500 CANCEL-CALLBACK will be called in case the request is being canceled.
   3501 If NO-MERGE is non-nil, don't merge the results but return alist
   3502 workspace->result.
   3503 CANCEL-TOKEN is the token that can be used to cancel request."
   3504   (when cancel-token
   3505     (lsp-cancel-request-by-token cancel-token))
   3506 
   3507   (if-let ((target-workspaces (lsp--find-workspaces-for body)))
   3508       (let* ((start-time (current-time))
   3509              (method (plist-get body :method))
   3510              (id (cl-incf lsp-last-id))
   3511              (buf (current-buffer))
   3512              (cancel-callback (when cancel-callback
   3513                                 (pcase mode
   3514                                   ((or 'alive 'tick 'unchanged)
   3515                                    (lambda ()
   3516                                      (with-current-buffer buf
   3517                                        (funcall cancel-callback))))
   3518                                   (_ cancel-callback))))
   3519              ;; calculate what are the (hook . local) pairs which will cancel
   3520              ;; the request
   3521              (hooks (pcase mode
   3522                       ('alive     '((kill-buffer-hook . t)))
   3523                       ('tick      '((kill-buffer-hook . t) (after-change-functions . t)))
   3524                       ('unchanged '((after-change-functions . t) (post-command-hook . nil)))
   3525                       ('current   '((post-command-hook . nil)))))
   3526              ;; note: lambdas in emacs can be compared but we should make sure
   3527              ;; that all of the captured arguments are the same - in our case
   3528              ;; `lsp--create-request-cancel' will return the same lambda when
   3529              ;; called with the same params.
   3530              (cleanup-hooks
   3531               (lambda () (mapc
   3532                           (-lambda ((hook . local))
   3533                             (if local
   3534                                 (when (buffer-live-p buf)
   3535                                   (with-current-buffer buf
   3536                                     (remove-hook hook
   3537                                                  (lsp--create-request-cancel
   3538                                                   id target-workspaces hook buf method cancel-callback)
   3539                                                  t)))
   3540                               (remove-hook hook (lsp--create-request-cancel
   3541                                                  id target-workspaces hook buf method cancel-callback))))
   3542                           hooks)
   3543                 (remhash cancel-token lsp--cancelable-requests)))
   3544              (callback (pcase mode
   3545                          ((or 'alive 'tick 'unchanged) (lambda (&rest args)
   3546                                                          (with-current-buffer buf
   3547                                                            (apply callback args))))
   3548                          (_ callback)))
   3549              (callback (lsp--create-async-callback callback
   3550                                                    method
   3551                                                    no-merge
   3552                                                    target-workspaces))
   3553              (callback (lambda (result)
   3554                          (lsp--request-cleanup-hooks id)
   3555                          (funcall callback result)))
   3556              (error-callback (lsp--create-async-callback
   3557                               (or error-callback
   3558                                   (lsp--create-default-error-handler method))
   3559                               method
   3560                               nil
   3561                               target-workspaces))
   3562              (error-callback (lambda (error)
   3563                                (funcall callback :error)
   3564                                (lsp--request-cleanup-hooks id)
   3565                                (funcall error-callback error)))
   3566              (body (plist-put body :id id)))
   3567 
   3568         ;; cancel request in any of the hooks
   3569         (mapc (-lambda ((hook . local))
   3570                 (add-hook hook
   3571                           (lsp--create-request-cancel
   3572                            id target-workspaces hook buf method cancel-callback)
   3573                           nil local))
   3574               hooks)
   3575         (puthash id cleanup-hooks lsp--request-cleanup-hooks)
   3576 
   3577         (setq lsp--last-active-workspaces target-workspaces)
   3578 
   3579         (when cancel-token
   3580           (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests))
   3581 
   3582         (seq-doseq (workspace target-workspaces)
   3583           (when (lsp--log-io-p method)
   3584             (lsp--log-entry-new (lsp--make-log-entry method id
   3585                                                      (plist-get body :params)
   3586                                                      'outgoing-req)
   3587                                 workspace))
   3588           (puthash id
   3589                    (list callback error-callback method start-time (current-time))
   3590                    (-> workspace
   3591                        (lsp--workspace-client)
   3592                        (lsp--client-response-handlers)))
   3593           (lsp--send-no-wait body (lsp--workspace-proc workspace)))
   3594         body)
   3595     (error "The connected server(s) does not support method %s.
   3596 To find out what capabilities support your server use `M-x lsp-describe-session'
   3597 and expand the capabilities section"
   3598            (plist-get body :method))))
   3599 
   3600 ;; deprecated, use lsp-request-async.
   3601 (defalias 'lsp-send-request-async 'lsp--send-request-async)
   3602 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1")
   3603 
   3604 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any
   3605 ;; pending language servers.
   3606 (add-hook 'kill-emacs-hook #'lsp--global-teardown)
   3607 
   3608 (defun lsp--global-teardown ()
   3609   "Unload working workspaces."
   3610   (lsp-foreach-workspace (lsp--shutdown-workspace)))
   3611 
   3612 (defun lsp--shutdown-workspace (&optional restart)
   3613   "Shut down the language server process for ‘lsp--cur-workspace’."
   3614   (with-demoted-errors "LSP error: %S"
   3615     (let ((lsp-response-timeout 0.5))
   3616       (condition-case err
   3617           (lsp-request "shutdown" nil)
   3618         (error (lsp--error "%s" err))))
   3619     (lsp-notify "exit" nil))
   3620   (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown))
   3621   (lsp--uninitialize-workspace))
   3622 
   3623 (defcustom lsp-inlay-hint-enable nil
   3624   "If non-nil it will enable inlay hints."
   3625   :type 'boolean
   3626   :group 'lsp-mode
   3627   :package-version '(lsp-mode . "9.0.0"))
   3628 
   3629 (defun lsp--uninitialize-workspace ()
   3630   "Cleanup buffer state.
   3631 When a workspace is shut down, by request or from just
   3632 disappearing, unset all the variables related to it."
   3633   (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace]
   3634     (lsp-process-kill cmd-proc)
   3635     (mapc (lambda (buf)
   3636             (when (lsp-buffer-live-p buf)
   3637               (lsp-with-current-buffer buf
   3638                                        (lsp-managed-mode -1))))
   3639           buffers)
   3640     (lsp-diagnostics--workspace-cleanup lsp--cur-workspace)))
   3641 
   3642 (defun lsp--client-capabilities (&optional custom-capabilities)
   3643   "Return the client capabilities appending CUSTOM-CAPABILITIES."
   3644   (append
   3645    `((general . ((positionEncodings . ["utf-32", "utf-16"])))
   3646      (workspace . ((workspaceEdit . ((documentChanges . t)
   3647                                      (resourceOperations . ["create" "rename" "delete"])))
   3648                    (applyEdit . t)
   3649                    (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))))
   3650                    (executeCommand . ((dynamicRegistration . :json-false)))
   3651                    ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t)))))
   3652                    (workspaceFolders . t)
   3653                    (configuration . t)
   3654                    ,@(when lsp-semantic-tokens-enable
   3655                        `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests)
   3656                                                                         lsp-semantic-tokens-honor-refresh-requests)
   3657                                                                    :json-false))))))
   3658                    ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t)))))
   3659                    ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false)))))
   3660                    (fileOperations . ((didCreate . :json-false)
   3661                                       (willCreate . :json-false)
   3662                                       (didRename . t)
   3663                                       (willRename . t)
   3664                                       (didDelete . :json-false)
   3665                                       (willDelete . :json-false)))))
   3666      (textDocument . ((declaration . ((dynamicRegistration . t)
   3667                                       (linkSupport . t)))
   3668                       (definition . ((dynamicRegistration . t)
   3669                                      (linkSupport . t)))
   3670                       (references . ((dynamicRegistration . t)))
   3671                       (implementation . ((dynamicRegistration . t)
   3672                                          (linkSupport . t)))
   3673                       (typeDefinition . ((dynamicRegistration . t)
   3674                                          (linkSupport . t)))
   3675                       (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t)))
   3676                       (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))
   3677                                          (hierarchicalDocumentSymbolSupport . t)))
   3678                       (formatting . ((dynamicRegistration . t)))
   3679                       (rangeFormatting . ((dynamicRegistration . t)))
   3680                       (onTypeFormatting . ((dynamicRegistration . t)))
   3681                       ,@(when (and lsp-semantic-tokens-enable
   3682                                    (functionp 'lsp--semantic-tokens-capabilities))
   3683                           (lsp--semantic-tokens-capabilities))
   3684                       (rename . ((dynamicRegistration . t) (prepareSupport . t)))
   3685                       (codeAction . ((dynamicRegistration . t)
   3686                                      (isPreferredSupport . t)
   3687                                      (codeActionLiteralSupport . ((codeActionKind . ((valueSet . [""
   3688                                                                                                   "quickfix"
   3689                                                                                                   "refactor"
   3690                                                                                                   "refactor.extract"
   3691                                                                                                   "refactor.inline"
   3692                                                                                                   "refactor.rewrite"
   3693                                                                                                   "source"
   3694                                                                                                   "source.organizeImports"])))))
   3695                                      (resolveSupport . ((properties . ["edit" "command"])))
   3696                                      (dataSupport . t)))
   3697                       (completion . ((completionItem . ((snippetSupport . ,(cond
   3698                                                                             ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode)))
   3699                                                                              (lsp--warn (concat
   3700                                                                                          "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. "
   3701                                                                                          "You must either install yasnippet, or disable snippet support."))
   3702                                                                              :json-false)
   3703                                                                             (lsp-enable-snippet t)
   3704                                                                             (t :json-false)))
   3705                                                         (documentationFormat . ["markdown" "plaintext"])
   3706                                                         ;; Remove this after jdtls support resolveSupport
   3707                                                         (resolveAdditionalTextEditsSupport . t)
   3708                                                         (insertReplaceSupport . t)
   3709                                                         (deprecatedSupport . t)
   3710                                                         (resolveSupport
   3711                                                          . ((properties . ["documentation"
   3712                                                                            "detail"
   3713                                                                            "additionalTextEdits"
   3714                                                                            "command"])))
   3715                                                         (insertTextModeSupport . ((valueSet . [1 2])))))
   3716                                      (contextSupport . t)
   3717                                      (dynamicRegistration . t)))
   3718                       (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t)))))
   3719                                         (dynamicRegistration . t)))
   3720                       (documentLink . ((dynamicRegistration . t)
   3721                                        (tooltipSupport . t)))
   3722                       (hover . ((contentFormat . ["markdown" "plaintext"])
   3723                                 (dynamicRegistration . t)))
   3724                       ,@(when lsp-enable-folding
   3725                           `((foldingRange . ((dynamicRegistration . t)
   3726                                              ,@(when lsp-folding-range-limit
   3727                                                  `((rangeLimit . ,lsp-folding-range-limit)))
   3728                                              ,@(when lsp-folding-line-folding-only
   3729                                                  `((lineFoldingOnly . t)))))))
   3730                       (selectionRange . ((dynamicRegistration . t)))
   3731                       (callHierarchy . ((dynamicRegistration . :json-false)))
   3732                       (typeHierarchy . ((dynamicRegistration . t)))
   3733                       (publishDiagnostics . ((relatedInformation . t)
   3734                                              (tagSupport . ((valueSet . [1 2])))
   3735                                              (versionSupport . t)))
   3736                       (linkedEditingRange . ((dynamicRegistration . t)))))
   3737      (window . ((workDoneProgress . t)
   3738                 (showDocument . ((support . t))))))
   3739    custom-capabilities))
   3740 
   3741 (defun lsp-find-roots-for-workspace (workspace session)
   3742   "Get all roots for the WORKSPACE."
   3743   (-filter #'identity (ht-map (lambda (folder workspaces)
   3744                                 (when (-contains? workspaces workspace)
   3745                                   folder))
   3746                               (lsp-session-folder->servers session))))
   3747 
   3748 (defun lsp-session-watches (&optional session)
   3749   "Get watches created for SESSION."
   3750   (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session))))
   3751       (-let [res (make-hash-table :test 'equal)]
   3752         (puthash "__watches" res (lsp-session-metadata (or session (lsp-session))))
   3753         res)))
   3754 
   3755 (defun lsp--file-process-event (session root-folder event)
   3756   "Process file event."
   3757   (let* ((changed-file (cl-third event))
   3758          (rel-changed-file (f-relative changed-file root-folder))
   3759          (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type))
   3760          (bit-position (1- event-numeric-kind))
   3761          (watch-bit (ash 1 bit-position)))
   3762     (->>
   3763      session
   3764      lsp-session-folder->servers
   3765      (gethash root-folder)
   3766      (seq-do (lambda (workspace)
   3767                (when (->>
   3768                       workspace
   3769                       lsp--workspace-registered-server-capabilities
   3770                       (-any?
   3771                        (lambda (capability)
   3772                          (and
   3773                           (equal (lsp--registered-capability-method capability)
   3774                                  "workspace/didChangeWatchedFiles")
   3775                           (->>
   3776                            capability
   3777                            lsp--registered-capability-options
   3778                            (lsp:did-change-watched-files-registration-options-watchers)
   3779                            (seq-find
   3780                             (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp))
   3781                               (when (or (null kind?)
   3782                                         (> (logand kind? watch-bit) 0))
   3783                                 (-let [regexes (or cached-regexp
   3784                                                    (let ((regexp (lsp-glob-to-regexps glob-pattern)))
   3785                                                      (lsp-put fs-watcher :_cachedRegexp regexp)
   3786                                                      regexp))]
   3787                                   (-any? (lambda (re)
   3788                                            (or (string-match re changed-file)
   3789                                                (string-match re rel-changed-file)))
   3790                                          regexes))))))))))
   3791                  (with-lsp-workspace workspace
   3792                    (lsp-notify
   3793                     "workspace/didChangeWatchedFiles"
   3794                     `((changes . [((type . ,event-numeric-kind)
   3795                                    (uri . ,(lsp--path-to-uri changed-file)))]))))))))))
   3796 
   3797 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?))
   3798   "Register capability REG."
   3799   (when (and lsp-enable-file-watchers
   3800              (equal method "workspace/didChangeWatchedFiles"))
   3801     (-let* ((created-watches (lsp-session-watches (lsp-session)))
   3802             (root-folders (cl-set-difference
   3803                            (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session))
   3804                            (ht-keys created-watches))))
   3805       ;; create watch for each root folder without such
   3806       (dolist (folder root-folders)
   3807         (let* ((watch (make-lsp-watch :root-directory folder))
   3808                (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder))
   3809                (ignored-files-regex-list (car ignored-things))
   3810                (ignored-directories-regex-list (cadr ignored-things)))
   3811           (puthash folder watch created-watches)
   3812           (lsp-watch-root-folder (file-truename folder)
   3813                                  (-partial #'lsp--file-process-event (lsp-session) folder)
   3814                                  ignored-files-regex-list
   3815                                  ignored-directories-regex-list
   3816                                  watch
   3817                                  t)))))
   3818 
   3819   (push
   3820    (make-lsp--registered-capability :id id :method method :options register-options?)
   3821    (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3822 
   3823 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body)
   3824   "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to
   3825 access dir-local variables."
   3826   (declare (indent 1) (debug t))
   3827   `(with-temp-buffer
   3828      ;; Set the buffer's name to something under the root so that we can hack the local variables
   3829      ;; This file doesn't need to exist and will not be created due to this.
   3830      (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root)))
   3831      (hack-local-variables)
   3832      (prog1 ,@body
   3833        (setq-local buffer-file-name nil))))
   3834 
   3835 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root)
   3836   "Return a list of the form
   3837 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given
   3838 WORKSPACE-ROOT."
   3839   ;; The intent of this function is to provide per-root workspace-level customization of the
   3840   ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables.
   3841   (lsp--with-workspace-temp-buffer workspace-root
   3842     (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories))))
   3843 
   3844 
   3845 (defun lsp--cleanup-hanging-watches ()
   3846   "Cleanup watches in case there are no more workspaces that are interested
   3847 in that particular folder."
   3848   (let* ((session (lsp-session))
   3849          (watches (lsp-session-watches session)))
   3850     (dolist (watched-folder (ht-keys watches))
   3851       (when (-none? (lambda (workspace)
   3852                       (with-lsp-workspace workspace
   3853                         (lsp--registered-capability "workspace/didChangeWatchedFiles")))
   3854                     (gethash watched-folder (lsp-session-folder->servers (lsp-session))))
   3855         (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder)
   3856         (lsp-kill-watch (gethash watched-folder watches))
   3857         (remhash watched-folder watches)))))
   3858 
   3859 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method))
   3860   "Unregister capability UNREG."
   3861   (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace)
   3862         (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id))
   3863                     (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3864   (when (equal method "workspace/didChangeWatchedFiles")
   3865     (lsp--cleanup-hanging-watches)))
   3866 
   3867 (defun lsp--server-capabilities ()
   3868   "Return the capabilities of the language server associated with the buffer."
   3869   (->> (lsp-workspaces)
   3870        (-keep #'lsp--workspace-server-capabilities)
   3871        (apply #'lsp-merge)))
   3872 
   3873 (defun lsp--send-open-close-p ()
   3874   "Return whether open and close notifications should be sent to the server."
   3875   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3876     (or (memq sync '(1 2))
   3877         (lsp:text-document-sync-options-open-close? sync))))
   3878 
   3879 (defun lsp--send-will-save-p ()
   3880   "Return whether willSave notifications should be sent to the server."
   3881   (-> (lsp--server-capabilities)
   3882       (lsp:server-capabilities-text-document-sync?)
   3883       (lsp:text-document-sync-options-will-save?)))
   3884 
   3885 (defun lsp--send-will-save-wait-until-p ()
   3886   "Return whether willSaveWaitUntil notifications should be sent to the server."
   3887   (-> (lsp--server-capabilities)
   3888       (lsp:server-capabilities-text-document-sync?)
   3889       (lsp:text-document-sync-options-will-save-wait-until?)))
   3890 
   3891 (defun lsp--send-did-save-p ()
   3892   "Return whether didSave notifications should be sent to the server."
   3893   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3894     (or (memq sync '(1 2))
   3895         (lsp:text-document-sync-options-save? sync))))
   3896 
   3897 (defun lsp--save-include-text-p ()
   3898   "Return whether save notifications should include the text document's contents."
   3899   (->> (lsp--server-capabilities)
   3900        (lsp:server-capabilities-text-document-sync?)
   3901        (lsp:text-document-sync-options-save?)
   3902        (lsp:text-document-save-registration-options-include-text?)))
   3903 
   3904 (defun lsp--send-will-rename-files-p (path)
   3905   "Return whether willRenameFiles request should be sent to the server.
   3906 If any filters, checks if it applies for PATH."
   3907   (let* ((will-rename (-> (lsp--server-capabilities)
   3908                           (lsp:server-capabilities-workspace?)
   3909                           (lsp:workspace-server-capabilities-file-operations?)
   3910                           (lsp:workspace-file-operations-will-rename?)))
   3911          (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list)))
   3912     (and will-rename
   3913          (or (seq-empty-p filters)
   3914              (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob)))
   3915                       (-let [regexes (lsp-glob-to-regexps glob)]
   3916                         (and (or (not scheme?)
   3917                                  (string-prefix-p scheme? (lsp--path-to-uri path)))
   3918                              (-any? (lambda (re)
   3919                                       (string-match re path))
   3920                                     regexes))))
   3921                     filters)))))
   3922 
   3923 (defun lsp--send-did-rename-files-p ()
   3924   "Return whether didRenameFiles notification should be sent to the server."
   3925   (-> (lsp--server-capabilities)
   3926       (lsp:server-capabilities-workspace?)
   3927       (lsp:workspace-server-capabilities-file-operations?)
   3928       (lsp:workspace-file-operations-did-rename?)))
   3929 
   3930 (declare-function project-roots "ext:project" (project) t)
   3931 (declare-function project-root "ext:project" (project) t)
   3932 
   3933 (defun lsp--suggest-project-root ()
   3934   "Get project root."
   3935   (or
   3936    (when (featurep 'projectile) (condition-case nil
   3937                                     (projectile-project-root)
   3938                                   (error nil)))
   3939    (when (featurep 'project)
   3940      (when-let ((project (project-current)))
   3941        (if (fboundp 'project-root)
   3942            (project-root project)
   3943          (car (with-no-warnings
   3944                 (project-roots project))))))
   3945    default-directory))
   3946 
   3947 (defun lsp--read-from-file (file)
   3948   "Read FILE content."
   3949   (when (file-exists-p file)
   3950     (cl-first (read-from-string (f-read-text file 'utf-8)))))
   3951 
   3952 (defun lsp--persist (file-name to-persist)
   3953   "Persist TO-PERSIST in FILE-NAME.
   3954 
   3955 This function creates the parent directories if they don't exist
   3956 yet."
   3957   (let ((print-length nil)
   3958         (print-level nil))
   3959     ;; Create all parent directories:
   3960     (make-directory (f-parent file-name) t)
   3961     (f-write-text (prin1-to-string to-persist) 'utf-8 file-name)))
   3962 
   3963 (defun lsp-workspace-folders-add (project-root)
   3964   "Add PROJECT-ROOT to the list of workspace folders."
   3965   (interactive
   3966    (list (read-directory-name "Select folder to add: "
   3967                               (or (lsp--suggest-project-root) default-directory) nil t)))
   3968   (cl-pushnew (lsp-f-canonical project-root)
   3969               (lsp-session-folders (lsp-session)) :test 'equal)
   3970   (lsp--persist-session (lsp-session))
   3971 
   3972   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil))
   3973 
   3974 (defun lsp-workspace-folders-remove (project-root)
   3975   "Remove PROJECT-ROOT from the list of workspace folders."
   3976   (interactive (list (completing-read "Select folder to remove: "
   3977                                       (lsp-session-folders (lsp-session))
   3978                                       nil t nil nil
   3979                                       (lsp-find-session-folder (lsp-session) default-directory))))
   3980 
   3981   (setq project-root (lsp-f-canonical project-root))
   3982 
   3983   ;; send remove folder to each multiroot workspace associated with the folder
   3984   (dolist (wks (->> (lsp-session)
   3985                     (lsp-session-folder->servers)
   3986                     (gethash project-root)
   3987                     (--filter (lsp--client-multi-root (lsp--workspace-client it)))))
   3988     (with-lsp-workspace wks
   3989       (lsp-notify "workspace/didChangeWorkspaceFolders"
   3990                   (lsp-make-did-change-workspace-folders-params
   3991                    :event (lsp-make-workspace-folders-change-event
   3992                            :removed (vector (lsp-make-workspace-folder
   3993                                              :uri (lsp--path-to-uri project-root)
   3994                                              :name (f-filename project-root)))
   3995                            :added [])))))
   3996 
   3997   ;; turn off servers in the removed directory
   3998   (let* ((session (lsp-session))
   3999          (folder->servers (lsp-session-folder->servers session))
   4000          (server-id->folders (lsp-session-server-id->folders session))
   4001          (workspaces (gethash project-root folder->servers)))
   4002 
   4003     (remhash project-root folder->servers)
   4004 
   4005     ;; turn off the servers without root folders
   4006     (dolist (workspace workspaces)
   4007       (when (--none? (-contains? it workspace) (ht-values folder->servers))
   4008         (lsp--info "Shutdown %s since folder %s is removed..."
   4009                    (lsp--workspace-print workspace) project-root)
   4010         (with-lsp-workspace workspace (lsp--shutdown-workspace))))
   4011 
   4012     (setf (lsp-session-folders session)
   4013           (-remove-item project-root (lsp-session-folders session)))
   4014 
   4015     (ht-aeach (puthash key
   4016                        (-remove-item project-root value)
   4017                        server-id->folders)
   4018               server-id->folders)
   4019     (lsp--persist-session (lsp-session)))
   4020 
   4021   (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root)))
   4022 
   4023 (defun lsp-workspace-blocklist-remove (project-root)
   4024   "Remove PROJECT-ROOT from the workspace blocklist."
   4025   (interactive (list (completing-read "Select folder to remove:"
   4026                                       (lsp-session-folders-blocklist (lsp-session))
   4027                                       nil t)))
   4028   (setf (lsp-session-folders-blocklist (lsp-session))
   4029         (delete project-root
   4030                 (lsp-session-folders-blocklist (lsp-session))))
   4031   (lsp--persist-session (lsp-session)))
   4032 
   4033 (define-obsolete-function-alias 'lsp-workspace-folders-switch
   4034   'lsp-workspace-folders-open "lsp-mode 6.1")
   4035 
   4036 (defun lsp-workspace-folders-open (project-root)
   4037   "Open the directory located at PROJECT-ROOT"
   4038   (interactive (list (completing-read "Open folder: "
   4039                                       (lsp-session-folders (lsp-session))
   4040                                       nil t)))
   4041   (find-file project-root))
   4042 
   4043 (defun lsp--maybe-enable-signature-help (trigger-characters)
   4044   (let ((ch last-command-event))
   4045     (when (cl-find ch trigger-characters :key #'string-to-char)
   4046       (lsp-signature-activate))))
   4047 
   4048 (defun lsp--on-type-formatting-handler-create ()
   4049   (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" )))
   4050     (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character?
   4051                                              :first-trigger-character) provider]
   4052       (lambda ()
   4053         (lsp--on-type-formatting first-trigger-character
   4054                                  more-trigger-character?)))))
   4055 
   4056 (defun lsp--update-on-type-formatting-hook (&optional cleanup?)
   4057   (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create)))
   4058     (cond
   4059      ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?))
   4060       (add-hook 'post-self-insert-hook on-type-formatting-handler nil t))
   4061      ((or cleanup?
   4062           (not lsp-enable-on-type-formatting))
   4063       (remove-hook 'post-self-insert-hook on-type-formatting-handler t)))))
   4064 
   4065 (defun lsp--signature-help-handler-create ()
   4066   (-when-let ((&SignatureHelpOptions? :trigger-characters?)
   4067               (lsp--capability-for-method "textDocument/signatureHelp"))
   4068     (lambda ()
   4069       (lsp--maybe-enable-signature-help trigger-characters?))))
   4070 
   4071 (defun lsp--update-signature-help-hook (&optional cleanup?)
   4072   (let ((signature-help-handler (lsp--signature-help-handler-create)))
   4073     (cond
   4074      ((and (or (equal lsp-signature-auto-activate t)
   4075                (memq :on-trigger-char lsp-signature-auto-activate))
   4076            signature-help-handler)
   4077       (add-hook 'post-self-insert-hook signature-help-handler nil t))
   4078 
   4079      ((or cleanup?
   4080           (not (or (equal lsp-signature-auto-activate t)
   4081                    (memq :on-trigger-char lsp-signature-auto-activate))))
   4082       (remove-hook 'post-self-insert-hook signature-help-handler t)))))
   4083 
   4084 (defun lsp--after-set-visited-file-name ()
   4085   (lsp-disconnect)
   4086   (lsp))
   4087 
   4088 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27
   4089 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099
   4090 (defvar eldoc-documentation-default) ; CI
   4091 (when (< emacs-major-version 28)
   4092   (unless (boundp 'eldoc-documentation-functions)
   4093     (load "eldoc" nil 'nomessage))
   4094   (when (memq (default-value 'eldoc-documentation-function) '(nil ignore))
   4095     ;; actually `eldoc-documentation-strategy', but CI was failing
   4096     (setq-default eldoc-documentation-function 'eldoc-documentation-default)))
   4097 
   4098 (define-minor-mode lsp-managed-mode
   4099   "Mode for source buffers managed by lsp-mode."
   4100   :lighter nil
   4101   (cond
   4102    (lsp-managed-mode
   4103     (when (lsp-feature? "textDocument/hover")
   4104       (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t)
   4105       (eldoc-mode 1))
   4106 
   4107     (add-hook 'after-change-functions #'lsp-on-change nil t)
   4108     (add-hook 'after-revert-hook #'lsp-on-revert nil t)
   4109     (add-hook 'after-save-hook #'lsp-on-save nil t)
   4110     (add-hook 'auto-save-hook #'lsp--on-auto-save nil t)
   4111     (add-hook 'before-change-functions #'lsp-before-change nil t)
   4112     (add-hook 'before-save-hook #'lsp--before-save nil t)
   4113     (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t)
   4114     (add-hook 'post-command-hook #'lsp--post-command nil t)
   4115 
   4116     (lsp--update-on-type-formatting-hook)
   4117     (lsp--update-signature-help-hook)
   4118 
   4119     (when lsp-enable-xref
   4120       (add-hook 'xref-backend-functions #'lsp--xref-backend nil t))
   4121 
   4122     (lsp-configure-buffer)
   4123 
   4124     ;; make sure we turn off lsp-mode in case major mode changes, because major
   4125     ;; mode change will wipe the buffer locals.
   4126     (add-hook 'change-major-mode-hook #'lsp-disconnect nil t)
   4127     (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t)
   4128 
   4129     (let ((buffer (lsp-current-buffer)))
   4130       (run-with-idle-timer
   4131        0.0 nil
   4132        (lambda ()
   4133          (when (lsp-buffer-live-p buffer)
   4134            (lsp-with-current-buffer buffer
   4135              (lsp--on-change-debounce buffer)
   4136              (lsp--on-idle buffer)))))))
   4137    (t
   4138     (lsp-unconfig-buffer)
   4139 
   4140     (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t)
   4141     (remove-hook 'post-command-hook #'lsp--post-command t)
   4142     (remove-hook 'after-change-functions #'lsp-on-change t)
   4143     (remove-hook 'after-revert-hook #'lsp-on-revert t)
   4144     (remove-hook 'after-save-hook #'lsp-on-save t)
   4145     (remove-hook 'auto-save-hook #'lsp--on-auto-save t)
   4146     (remove-hook 'before-change-functions #'lsp-before-change t)
   4147     (remove-hook 'before-save-hook #'lsp--before-save t)
   4148     (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t)
   4149 
   4150     (lsp--update-on-type-formatting-hook :cleanup)
   4151     (lsp--update-signature-help-hook :cleanup)
   4152 
   4153     (when lsp--on-idle-timer
   4154       (cancel-timer lsp--on-idle-timer)
   4155       (setq lsp--on-idle-timer nil))
   4156 
   4157     (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4158     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4159 
   4160     (lsp--remove-overlays 'lsp-highlight)
   4161     (lsp--remove-overlays 'lsp-links)
   4162 
   4163     (remove-hook 'xref-backend-functions #'lsp--xref-backend t)
   4164     (remove-hook 'change-major-mode-hook #'lsp-disconnect t)
   4165     (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t)
   4166     (setq-local lsp-buffer-uri nil))))
   4167 
   4168 (defun lsp-configure-buffer ()
   4169   "Configure LSP features for current buffer."
   4170   ;; make sure the core is running in the context of all available workspaces
   4171   ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context
   4172   (let ((lsp--buffer-workspaces (cond
   4173                                  (lsp--buffer-workspaces)
   4174                                  (lsp--cur-workspace (list lsp--cur-workspace))))
   4175         lsp--cur-workspace)
   4176     (when lsp-auto-configure
   4177       (lsp--auto-configure)
   4178 
   4179       (when (and lsp-enable-text-document-color
   4180                  (lsp-feature? "textDocument/documentColor"))
   4181         (add-hook 'lsp-on-change-hook #'lsp--document-color nil t))
   4182 
   4183       (when (and lsp-enable-imenu
   4184                  (lsp-feature? "textDocument/documentSymbol"))
   4185         (lsp-enable-imenu))
   4186 
   4187       (when (and lsp-enable-indentation
   4188                  (lsp-feature? "textDocument/rangeFormatting"))
   4189         (add-function :override (local 'indent-region-function) #'lsp-format-region))
   4190 
   4191       (when (and lsp-enable-symbol-highlighting
   4192                  (lsp-feature? "textDocument/documentHighlight"))
   4193         (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t))
   4194 
   4195       (when (and lsp-enable-links
   4196                  (lsp-feature? "textDocument/documentLink"))
   4197         (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t))
   4198 
   4199       (when (and lsp-inlay-hint-enable
   4200                  (lsp-feature? "textDocument/inlayHint"))
   4201         (lsp-inlay-hints-mode))
   4202 
   4203       (when (and lsp-enable-dap-auto-configure
   4204                  (functionp 'dap-mode))
   4205         (dap-auto-configure-mode 1)))
   4206     (run-hooks 'lsp-configure-hook)))
   4207 
   4208 (defun lsp-unconfig-buffer ()
   4209   "Unconfigure LSP features for buffer."
   4210   (lsp--remove-overlays 'lsp-color)
   4211 
   4212   (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function)
   4213     (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   4214     (setq-local imenu-menubar-modified-tick 0)
   4215     (setq-local imenu--index-alist nil)
   4216     (imenu--cleanup))
   4217 
   4218   (remove-function (local 'indent-region-function) #'lsp-format-region)
   4219 
   4220   (remove-hook 'lsp-on-change-hook #'lsp--document-color t)
   4221   (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4222   (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4223 
   4224   (when (and lsp-enable-dap-auto-configure
   4225              (functionp 'dap-mode))
   4226     (dap-auto-configure-mode -1))
   4227 
   4228   (run-hooks 'lsp-unconfigure-hook))
   4229 
   4230 (defun lsp--buffer-content ()
   4231   (lsp-save-restriction-and-excursion
   4232     (or (lsp-virtual-buffer-call :buffer-string)
   4233         (buffer-substring-no-properties (point-min)
   4234                                         (point-max)))))
   4235 
   4236 (defun lsp--text-document-did-open ()
   4237   "`document/didOpen' event."
   4238   (run-hooks 'lsp-before-open-hook)
   4239   (when (and lsp-auto-touch-files
   4240              (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri)))))
   4241     (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri))
   4242     (save-buffer))
   4243 
   4244   (setq lsp--cur-version (or lsp--cur-version 0))
   4245   (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   4246   (lsp-notify
   4247    "textDocument/didOpen"
   4248    (list :textDocument
   4249          (list :uri (lsp--buffer-uri)
   4250                :languageId (lsp-buffer-language)
   4251                :version lsp--cur-version
   4252                :text (lsp--buffer-content))))
   4253 
   4254   (lsp-managed-mode 1)
   4255 
   4256   (run-hooks 'lsp-after-open-hook)
   4257   (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client))))
   4258     (-some-> (lsp--client-after-open-fn client)
   4259       (funcall))
   4260     (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client))
   4261       (intern-soft)
   4262       (run-hooks))))
   4263 
   4264 (defun lsp--text-document-identifier ()
   4265   "Make TextDocumentIdentifier."
   4266   (list :uri (lsp--buffer-uri)))
   4267 
   4268 (defun lsp--versioned-text-document-identifier ()
   4269   "Make VersionedTextDocumentIdentifier."
   4270   (plist-put (lsp--text-document-identifier) :version lsp--cur-version))
   4271 
   4272 (defun lsp--cur-line (&optional point)
   4273   (1- (line-number-at-pos point)))
   4274 
   4275 (defun lsp--cur-position ()
   4276   "Make a Position object for the current point."
   4277   (or (lsp-virtual-buffer-call :cur-position)
   4278       (lsp-save-restriction-and-excursion
   4279         (list :line (lsp--cur-line)
   4280               :character (- (point) (line-beginning-position))))))
   4281 
   4282 (defun lsp--point-to-position (point)
   4283   "Convert POINT to Position."
   4284   (lsp-save-restriction-and-excursion
   4285     (goto-char point)
   4286     (lsp--cur-position)))
   4287 
   4288 (defun lsp--range (start end)
   4289   "Make Range body from START and END."
   4290   ;; make sure start and end are Position objects
   4291   (list :start start :end end))
   4292 
   4293 (defun lsp--region-to-range (start end)
   4294   "Make Range object for the current region."
   4295   (lsp--range (lsp--point-to-position start)
   4296               (lsp--point-to-position end)))
   4297 
   4298 (defun lsp--region-or-line ()
   4299   "The active region or the current line."
   4300   (if (use-region-p)
   4301       (lsp--region-to-range (region-beginning) (region-end))
   4302     (lsp--region-to-range (line-beginning-position) (line-end-position))))
   4303 
   4304 (defun lsp--check-document-changes-version (document-changes)
   4305   "Verify that DOCUMENT-CHANGES have the proper version."
   4306   (unless (seq-every-p
   4307            (-lambda ((&TextDocumentEdit :text-document))
   4308              (or
   4309               (not text-document)
   4310               (let* ((filename (-> text-document
   4311                                    lsp:versioned-text-document-identifier-uri
   4312                                    lsp--uri-to-path))
   4313                      (version (lsp:versioned-text-document-identifier-version? text-document)))
   4314                 (with-current-buffer (find-file-noselect filename)
   4315                   (or (null version) (zerop version) (= -1 version)
   4316                       (equal version lsp--cur-version))))))
   4317            document-changes)
   4318     (error "Document changes cannot be applied due to different document version")))
   4319 
   4320 (defun lsp--apply-workspace-edit (workspace-edit &optional operation)
   4321   "Apply the WorkspaceEdit object WORKSPACE-EDIT.
   4322 OPERATION is symbol representing the source of this text edit."
   4323   (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit))
   4324     (if-let ((document-changes (seq-reverse document-changes?)))
   4325         (progn
   4326           (lsp--check-document-changes-version document-changes)
   4327           (->> document-changes
   4328                (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create")))
   4329                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4330           (->> document-changes
   4331                (seq-filter (-lambda ((&CreateFile :kind))
   4332                              (and (or (not kind) (equal kind "edit"))
   4333                                   (not (equal kind "create")))))
   4334                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4335           (->> document-changes
   4336                (seq-filter (-lambda ((&CreateFile :kind))
   4337                              (and (not (or (not kind) (equal kind "edit")))
   4338                                   (not (equal kind "create")))))
   4339                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))))
   4340       (lsp-map
   4341        (lambda (uri text-edits)
   4342          (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect)
   4343            (lsp--apply-text-edits text-edits operation)))
   4344        changes?))))
   4345 
   4346 (defmacro lsp-with-filename (file &rest body)
   4347   "Execute BODY with FILE as a context.
   4348 Need to handle the case when FILE indicates virtual buffer."
   4349   (declare (indent 1) (debug t))
   4350   `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file)))
   4351        (lsp-with-current-buffer lsp--virtual-buffer
   4352          ,@body)
   4353      ,@body))
   4354 
   4355 (defun lsp--apply-text-document-edit (edit &optional operation)
   4356   "Apply the TextDocumentEdit object EDIT.
   4357 OPERATION is symbol representing the source of this text edit.
   4358 If the file is not being visited by any buffer, it is opened with
   4359 `find-file-noselect'.
   4360 Because lsp-mode does not store previous document versions, the edit is only
   4361 applied if the version of the textDocument matches the version of the
   4362 corresponding file.
   4363 
   4364 interface TextDocumentEdit {
   4365   textDocument: VersionedTextDocumentIdentifier;
   4366   edits: TextEdit[];
   4367 }"
   4368   (pcase (lsp:edit-kind edit)
   4369     ("create" (-let* (((&CreateFile :uri :options?) edit)
   4370                       (file-name (lsp--uri-to-path uri)))
   4371                 (mkdir (f-dirname file-name) t)
   4372                 (f-touch file-name)
   4373                 (when (lsp:create-file-options-overwrite? options?)
   4374                   (f-write-text "" nil file-name))
   4375                 (find-file-noselect file-name)))
   4376     ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit))
   4377                 (f-delete (lsp--uri-to-path uri) recursive?)))
   4378     ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit)
   4379                       (old-file-name (lsp--uri-to-path old-uri))
   4380                       (new-file-name (lsp--uri-to-path new-uri))
   4381                       (buf (find-buffer-visiting old-file-name)))
   4382                 (when buf
   4383                   (lsp-with-current-buffer buf
   4384                     (save-buffer)
   4385                     (lsp--text-document-did-close)))
   4386                 (mkdir (f-dirname new-file-name) t)
   4387                 (rename-file old-file-name new-file-name overwrite?)
   4388                 (when buf
   4389                   (lsp-with-current-buffer buf
   4390                     (set-buffer-modified-p nil)
   4391                     (setq lsp-buffer-uri nil)
   4392                     (set-visited-file-name new-file-name)
   4393                     (lsp)))))
   4394     (_ (let ((file-name (->> edit
   4395                              (lsp:text-document-edit-text-document)
   4396                              (lsp:versioned-text-document-identifier-uri)
   4397                              (lsp--uri-to-path))))
   4398          (lsp-with-current-buffer (find-buffer-visiting file-name)
   4399            (lsp-with-filename file-name
   4400              (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation)))))))
   4401 
   4402 (lsp-defun lsp--position-compare ((&Position :line left-line
   4403                                              :character left-character)
   4404                                   (&Position :line right-line
   4405                                              :character right-character))
   4406   "Return t if position LEFT is greater than RIGHT."
   4407   (if (= left-line right-line)
   4408       (> left-character right-character)
   4409     (> left-line right-line)))
   4410 
   4411 (lsp-defun lsp-point-in-range? (position (&Range :start :end))
   4412   "Returns if POINT is in RANGE."
   4413   (not (or (lsp--position-compare start position)
   4414            (lsp--position-compare position end))))
   4415 
   4416 (lsp-defun lsp--position-equal ((&Position :line left-line
   4417                                            :character left-character)
   4418                                 (&Position :line right-line
   4419                                            :character right-character))
   4420   "Return whether LEFT and RIGHT positions are equal."
   4421   (and (= left-line right-line)
   4422        (= left-character right-character)))
   4423 
   4424 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end))
   4425                                           (&TextEdit :range (&Range :start right-start :end right-end)))
   4426   (if (lsp--position-equal left-start right-start)
   4427       (lsp--position-compare left-end right-end)
   4428     (lsp--position-compare left-start right-start)))
   4429 
   4430 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text))
   4431   "Apply the edits described in the TextEdit object in TEXT-EDIT."
   4432   (setq new-text (s-replace "\r" "" (or new-text "")))
   4433   (lsp:set-text-edit-new-text edit new-text)
   4434   (goto-char start)
   4435   (delete-region start end)
   4436   (insert new-text))
   4437 
   4438 ;; WORKAROUND: typescript-language might send -1 when applying code actions.
   4439 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582
   4440 (lsp-defun lsp--fix-point ((point &as &Position :character :line))
   4441   (-doto point
   4442     (lsp:set-position-line (max 0 line))
   4443     (lsp:set-position-character (max 0 character))))
   4444 
   4445 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as
   4446                                                                &TextEdit
   4447                                                                :range (&Range :start :end)
   4448                                                                :new-text))
   4449   "Apply the edits described in the TextEdit object in TEXT-EDIT.
   4450 The method uses `replace-buffer-contents'."
   4451   (setq new-text (s-replace "\r" "" (or new-text "")))
   4452   (lsp:set-text-edit-new-text edit new-text)
   4453   (-let* ((source (current-buffer))
   4454           ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start)
   4455                                                              :end (lsp--fix-point end)))))
   4456     (with-temp-buffer
   4457       (insert new-text)
   4458       (let ((temp (current-buffer)))
   4459         (with-current-buffer source
   4460           (save-excursion
   4461             (save-restriction
   4462               (narrow-to-region beg end)
   4463 
   4464               ;; On emacs versions < 26.2,
   4465               ;; `replace-buffer-contents' is buggy - it calls
   4466               ;; change functions with invalid arguments - so we
   4467               ;; manually call the change functions here.
   4468               ;;
   4469               ;; See emacs bugs #32237, #32278:
   4470               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237
   4471               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278
   4472               (let ((inhibit-modification-hooks t)
   4473                     (length (- end beg)))
   4474                 (run-hook-with-args 'before-change-functions
   4475                                     beg end)
   4476                 (replace-buffer-contents temp)
   4477                 (run-hook-with-args 'after-change-functions
   4478                                     beg (+ beg (length new-text))
   4479                                     length)))))))))
   4480 
   4481 (defun lsp--to-yasnippet-snippet (snippet)
   4482   "Convert LSP SNIPPET to yasnippet snippet."
   4483   ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it.
   4484   (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`")))
   4485                             (rx "\\" (backref 1))
   4486                             snippet
   4487                             nil nil 1))
   4488 
   4489 (defvar-local lsp-enable-relative-indentation nil
   4490   "Enable relative indentation when insert texts, snippets ...
   4491 from language server.")
   4492 
   4493 (defun lsp--expand-snippet (snippet &optional start end expand-env)
   4494   "Wrapper of `yas-expand-snippet' with all of it arguments.
   4495 The snippet will be convert to LSP style and indent according to
   4496 LSP server result."
   4497   (require 'yasnippet nil t)
   4498   (let* ((inhibit-field-text-motion t)
   4499          (yas-wrap-around-region nil)
   4500          (yas-indent-line 'none)
   4501          (yas-also-auto-indent-first-line nil))
   4502     (yas-expand-snippet
   4503      (lsp--to-yasnippet-snippet snippet)
   4504      start end expand-env)))
   4505 
   4506 (defun lsp--indent-lines (start end &optional insert-text-mode?)
   4507   "Indent from START to END based on INSERT-TEXT-MODE? value.
   4508 - When INSERT-TEXT-MODE? is provided
   4509   - if it's `lsp/insert-text-mode-as-it', do no editor indentation.
   4510   - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading
   4511     whitespaces to match the line where text is inserted.
   4512 - When it's not provided, using `indent-line-function' for each line."
   4513   (save-excursion
   4514     (goto-char end)
   4515     (let* ((end-line (line-number-at-pos))
   4516            (offset (save-excursion
   4517                      (goto-char start)
   4518                      (current-indentation)))
   4519            (indent-line-function
   4520             (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it)
   4521                    #'ignore)
   4522                   ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation)
   4523                        lsp-enable-relative-indentation
   4524                        ;; Indenting snippets is extremely slow in `org-mode' buffers
   4525                        ;; since it has to calculate indentation based on SRC block
   4526                        ;; position.  Thus we use relative indentation as default.
   4527                        (derived-mode-p 'org-mode))
   4528                    (lambda () (save-excursion
   4529                                 (beginning-of-line)
   4530                                 (indent-to-column offset))))
   4531                   (t indent-line-function))))
   4532       (goto-char start)
   4533       (forward-line)
   4534       (while (and (not (eobp))
   4535                   (<= (line-number-at-pos) end-line))
   4536         (funcall indent-line-function)
   4537         (forward-line)))))
   4538 
   4539 (defun lsp--apply-text-edits (edits &optional operation)
   4540   "Apply the EDITS described in the TextEdit[] object.
   4541 OPERATION is symbol representing the source of this text edit."
   4542   (unless (seq-empty-p edits)
   4543     (atomic-change-group
   4544       (run-hooks 'lsp-before-apply-edits-hook)
   4545       (let* ((change-group (prepare-change-group))
   4546              (howmany (length edits))
   4547              (message (format "Applying %s edits to `%s' ..." howmany (current-buffer)))
   4548              (_ (lsp--info message))
   4549              (reporter (make-progress-reporter message 0 howmany))
   4550              (done 0)
   4551              (apply-edit (if (not lsp--virtual-buffer)
   4552                              #'lsp--apply-text-edit-replace-buffer-contents
   4553                            #'lsp--apply-text-edit)))
   4554         (unwind-protect
   4555             (->> edits
   4556                  ;; We sort text edits so as to apply edits that modify latter
   4557                  ;; parts of the document first. Furthermore, because the LSP
   4558                  ;; spec dictates that: "If multiple inserts have the same
   4559                  ;; position, the order in the array defines which edit to
   4560                  ;; apply first."  We reverse the initial list and sort stably
   4561                  ;; to make sure the order among edits with the same position
   4562                  ;; is preserved.
   4563                  (nreverse)
   4564                  (seq-sort #'lsp--text-edit-sort-predicate)
   4565                  (mapc (lambda (edit)
   4566                          (progress-reporter-update reporter (cl-incf done))
   4567                          (funcall apply-edit edit)
   4568                          (when (lsp:snippet-text-edit-insert-text-format? edit)
   4569                            (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start)
   4570                                                          :insert-text-format? :new-text) edit)
   4571                              (when (eq insert-text-format? lsp/insert-text-format-snippet)
   4572                                ;; No `save-excursion' needed since expand snippet will change point anyway
   4573                                (goto-char (+ start (length new-text)))
   4574                                (lsp--indent-lines start (point))
   4575                                (lsp--expand-snippet new-text start (point)))))
   4576                          (run-hook-with-args 'lsp-after-apply-edits-hook operation))))
   4577           (undo-amalgamate-change-group change-group)
   4578           (progress-reporter-done reporter))))))
   4579 
   4580 (defun lsp--create-apply-text-edits-handlers ()
   4581   "Create (handler cleanup-fn) for applying text edits in async request.
   4582 Only works when mode is `tick or `alive."
   4583   (let* (first-edited
   4584          (func (lambda (start &rest _)
   4585                  (setq first-edited (if first-edited
   4586                                         (min start first-edited)
   4587                                       start)))))
   4588     (add-hook 'before-change-functions func nil t)
   4589     (list
   4590      (lambda (edits)
   4591        (if (and first-edited
   4592                 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end)))
   4593                             ;; Text edit region is overlapped
   4594                             (> end first-edited))
   4595                           edits))
   4596            (lsp--warn "TextEdits will not be applied since document has been modified before of them.")
   4597          (lsp--apply-text-edits edits 'completion-cleanup)))
   4598      (lambda ()
   4599        (remove-hook 'before-change-functions func t)))))
   4600 
   4601 (defun lsp--capability (cap &optional capabilities)
   4602   "Get the value of capability CAP.  If CAPABILITIES is non-nil, use them instead."
   4603   (when (stringp cap)
   4604     (setq cap (intern (concat ":" cap))))
   4605 
   4606   (lsp-get (or capabilities
   4607                (lsp--server-capabilities))
   4608            cap))
   4609 
   4610 (defun lsp--registered-capability (method)
   4611   "Check whether there is workspace providing METHOD."
   4612   (->> (lsp-workspaces)
   4613        (--keep (seq-find (lambda (reg)
   4614                            (equal (lsp--registered-capability-method reg) method))
   4615                          (lsp--workspace-registered-server-capabilities it)))
   4616        cl-first))
   4617 
   4618 (defun lsp--capability-for-method (method)
   4619   "Get the value of capability for METHOD."
   4620   (-let* ((reqs (cdr (assoc method lsp-method-requirements)))
   4621           ((&plist :capability) reqs))
   4622     (or (and capability (lsp--capability capability))
   4623         (-some-> (lsp--registered-capability method)
   4624           (lsp--registered-capability-options)))))
   4625 
   4626 (defvar-local lsp--before-change-vals nil
   4627   "Store the positions from the `lsp-before-change' function call, for
   4628 validation and use in the `lsp-on-change' function.")
   4629 
   4630 (defun lsp--text-document-content-change-event (start end length)
   4631   "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH."
   4632   ;; So (47 54 0) means add    7 chars starting at pos 47
   4633   ;; must become
   4634   ;;   {"range":{"start":{"line":5,"character":6}
   4635   ;;             ,"end" :{"line":5,"character":6}}
   4636   ;;             ,"rangeLength":0
   4637   ;;             ,"text":"\nbb = 5"}
   4638   ;;
   4639   ;; And (47 47 7) means delete 7 chars starting at pos 47
   4640   ;; must become
   4641   ;;   {"range":{"start":{"line":6,"character":0}
   4642   ;;            ,"end"  :{"line":7,"character":0}}
   4643   ;;            ,"rangeLength":7
   4644   ;;            ,"text":""}
   4645   ;;
   4646   ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with
   4647   ;; 13 chars. So it must become
   4648   ;;   {"range":{"start":{"line":5,"character":8}
   4649   ;;             ,"end" :{"line":5,"character":11}}
   4650   ;;             ,"rangeLength":3
   4651   ;;             ,"text":"new-chars-xxx"}
   4652   ;;
   4653 
   4654   ;; Adding text:
   4655   ;;   lsp-before-change:(start,end)=(33,33)
   4656   ;;   lsp-on-change:(start,end,length)=(33,34,0)
   4657   ;;
   4658   ;; Changing text:
   4659   ;;   lsp-before-change:(start,end)=(208,211)
   4660   ;;   lsp-on-change:(start,end,length)=(208,221,3)
   4661   ;;
   4662   ;; Deleting text:
   4663   ;;   lsp-before-change:(start,end)=(19,27)
   4664   ;;   lsp-on-change:(start,end,length)=(19,19,8)
   4665   (if (zerop length)
   4666       ;; Adding something only, work from start only
   4667       `( :range ,(lsp--range
   4668                   (lsp--point-to-position start)
   4669                   (lsp--point-to-position start))
   4670          :rangeLength 0
   4671          :text ,(buffer-substring-no-properties start end))
   4672 
   4673     (if (eq start end)
   4674         ;; Deleting something only
   4675         (if (lsp--bracketed-change-p start length)
   4676             ;; The before-change value is bracketed, use it
   4677             `( :range ,(lsp--range
   4678                         (lsp--point-to-position start)
   4679                         (plist-get lsp--before-change-vals :end-pos))
   4680                :rangeLength ,length
   4681                :text "")
   4682           ;; If the change is not bracketed, send a full change event instead.
   4683           (lsp--full-change-event))
   4684 
   4685       ;; Deleting some things, adding others
   4686       (if (lsp--bracketed-change-p start length)
   4687           ;; The before-change value is valid, use it
   4688           `( :range ,(lsp--range
   4689                       (lsp--point-to-position start)
   4690                       (plist-get lsp--before-change-vals :end-pos))
   4691              :rangeLength ,length
   4692              :text ,(buffer-substring-no-properties start end))
   4693         (lsp--full-change-event)))))
   4694 
   4695 (defun lsp--bracketed-change-p (start length)
   4696   "If the before and after positions are the same, and the length
   4697 is the size of the start range, we are probably good."
   4698   (-let [(&plist :end before-end :start before-start) lsp--before-change-vals]
   4699     (and (eq start before-start)
   4700          (eq length (- before-end before-start)))))
   4701 
   4702 (defun lsp--full-change-event ()
   4703   `(:text ,(lsp--buffer-content)))
   4704 
   4705 (defun lsp-before-change (start end)
   4706   "Executed before a file is changed.
   4707 Added to `before-change-functions'."
   4708   ;; Note:
   4709   ;;
   4710   ;; This variable holds a list of functions to call when Emacs is about to
   4711   ;; modify a buffer. Each function gets two arguments, the beginning and end of
   4712   ;; the region that is about to change, represented as integers. The buffer
   4713   ;; that is about to change is always the current buffer when the function is
   4714   ;; called.
   4715   ;;
   4716   ;; WARNING:
   4717   ;;
   4718   ;; Do not expect the before-change hooks and the after-change hooks be called
   4719   ;; in balanced pairs around each buffer change. Also don't expect the
   4720   ;; before-change hooks to be called for every chunk of text Emacs is about to
   4721   ;; delete. These hooks are provided on the assumption that Lisp programs will
   4722   ;; use either before- or the after-change hooks, but not both, and the
   4723   ;; boundaries of the region where the changes happen might include more than
   4724   ;; just the actual changed text, or even lump together several changes done
   4725   ;; piecemeal.
   4726   (save-match-data
   4727     (lsp-save-restriction-and-excursion
   4728       (setq lsp--before-change-vals
   4729             (list :start start
   4730                   :end end
   4731                   :end-pos (lsp--point-to-position end))))))
   4732 
   4733 (defun lsp--flush-delayed-changes ()
   4734   (let ((inhibit-quit t))
   4735     (when lsp--delay-timer
   4736       (cancel-timer lsp--delay-timer))
   4737     (mapc (-lambda ((workspace buffer document change))
   4738             (with-current-buffer buffer
   4739               (with-lsp-workspace workspace
   4740                 (lsp-notify "textDocument/didChange"
   4741                             (list :textDocument document
   4742                                   :contentChanges (vector change))))))
   4743           (prog1 (nreverse lsp--delayed-requests)
   4744             (setq lsp--delayed-requests nil)))))
   4745 
   4746 (defun lsp--workspace-sync-method (workspace)
   4747   (let ((sync (-> workspace
   4748                   (lsp--workspace-server-capabilities)
   4749                   (lsp:server-capabilities-text-document-sync?))))
   4750     (if (lsp-text-document-sync-options? sync)
   4751         (lsp:text-document-sync-options-change? sync)
   4752       sync)))
   4753 
   4754 (defun lsp-on-change (start end length &optional content-change-event-fn)
   4755   "Executed when a file is changed.
   4756 Added to `after-change-functions'."
   4757   ;; Note:
   4758   ;;
   4759   ;; Each function receives three arguments: the beginning and end of the region
   4760   ;; just changed, and the length of the text that existed before the change.
   4761   ;; All three arguments are integers. The buffer that has been changed is
   4762   ;; always the current buffer when the function is called.
   4763   ;;
   4764   ;; The length of the old text is the difference between the buffer positions
   4765   ;; before and after that text as it was before the change. As for the
   4766   ;; changed text, its length is simply the difference between the first two
   4767   ;; arguments.
   4768   ;;
   4769   ;; So (47 54 0) means add    7 chars starting at pos 47
   4770   ;; So (47 47 7) means delete 7 chars starting at pos 47
   4771   (save-match-data
   4772     (let ((inhibit-quit t)
   4773           ;; make sure that `lsp-on-change' is called in multi-workspace context
   4774           ;; see #2901
   4775           lsp--cur-workspace)
   4776       ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done
   4777       ;; by auto-revert-mode) will cause this handler to get called with a nil
   4778       ;; buffer-file-name. We need the buffer-file-name to send notifications;
   4779       ;; so we skip handling revert-buffer-caused changes and instead handle
   4780       ;; reverts separately in lsp-on-revert
   4781       (when (not revert-buffer-in-progress-p)
   4782         (cl-incf lsp--cur-version)
   4783         (mapc
   4784          (lambda (workspace)
   4785            (pcase (or lsp-document-sync-method
   4786                       (lsp--workspace-sync-method workspace))
   4787              (1
   4788               (if lsp-debounce-full-sync-notifications
   4789                   (setq lsp--delayed-requests
   4790                         (->> lsp--delayed-requests
   4791                              (-remove (-lambda ((_ buffer))
   4792                                         (equal (current-buffer) buffer)))
   4793                              (cons (list workspace
   4794                                          (current-buffer)
   4795                                          (lsp--versioned-text-document-identifier)
   4796                                          (lsp--full-change-event)))))
   4797                 (with-lsp-workspace workspace
   4798                   (lsp-notify "textDocument/didChange"
   4799                               (list :contentChanges (vector (lsp--full-change-event))
   4800                                     :textDocument (lsp--versioned-text-document-identifier))))))
   4801              (2
   4802               (with-lsp-workspace workspace
   4803                 (lsp-notify
   4804                  "textDocument/didChange"
   4805                  (list :textDocument (lsp--versioned-text-document-identifier)
   4806                        :contentChanges (vector
   4807                                         (if content-change-event-fn
   4808                                             (funcall content-change-event-fn start end length)
   4809                                           (lsp--text-document-content-change-event
   4810                                            start end length)))))))))
   4811          (lsp-workspaces))
   4812         (when lsp--delay-timer (cancel-timer lsp--delay-timer))
   4813         (setq lsp--delay-timer (run-with-idle-timer
   4814                                 lsp-debounce-full-sync-notifications-interval
   4815                                 nil
   4816                                 #'lsp--flush-delayed-changes))
   4817         ;; force cleanup overlays after each change
   4818         (lsp--remove-overlays 'lsp-highlight)
   4819         (lsp--after-change (current-buffer))
   4820         (setq lsp--signature-last-index nil
   4821               lsp--signature-last nil)
   4822         ;; cleanup diagnostics
   4823         (when lsp-diagnostic-clean-after-change
   4824           (lsp-foreach-workspace
   4825            (-let [diagnostics (lsp--workspace-diagnostics lsp--cur-workspace)]
   4826              (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics))))))))
   4827 
   4828 
   4829 
   4830 ;; facilities for on change hooks. We do not want to make lsp calls on each
   4831 ;; change event so we add debounce to avoid flooding the server with events.
   4832 ;; Additionally, we want to have a mechanism for stopping the server calls in
   4833 ;; particular cases like, e. g. when performing completion.
   4834 
   4835 (defvar lsp-inhibit-lsp-hooks nil
   4836   "Flag to control.")
   4837 
   4838 (defcustom lsp-on-change-hook nil
   4839   "Hooks to run when buffer has changed."
   4840   :type 'hook
   4841   :group 'lsp-mode)
   4842 
   4843 (defcustom lsp-idle-delay 0.500
   4844   "Debounce interval for `after-change-functions'."
   4845   :type 'number
   4846   :group 'lsp-mode)
   4847 
   4848 (defcustom lsp-on-idle-hook nil
   4849   "Hooks to run after `lsp-idle-delay'."
   4850   :type 'hook
   4851   :group 'lsp-mode)
   4852 
   4853 (defun lsp--idle-reschedule (buffer)
   4854   (when lsp--on-idle-timer
   4855     (cancel-timer lsp--on-idle-timer))
   4856 
   4857   (setq lsp--on-idle-timer (run-with-idle-timer
   4858                             lsp-idle-delay
   4859                             nil
   4860                             #'lsp--on-idle
   4861                             buffer)))
   4862 
   4863 (defun lsp--post-command ()
   4864   (lsp--cleanup-highlights-if-needed)
   4865   (lsp--idle-reschedule (current-buffer)))
   4866 
   4867 (defun lsp--on-idle (buffer)
   4868   "Start post command loop."
   4869   (when (and (buffer-live-p buffer)
   4870              (equal buffer (current-buffer))
   4871              (not lsp-inhibit-lsp-hooks)
   4872              lsp-managed-mode)
   4873     (run-hooks 'lsp-on-idle-hook)))
   4874 
   4875 (defun lsp--on-change-debounce (buffer)
   4876   (when (and (buffer-live-p buffer)
   4877              (equal buffer (current-buffer))
   4878              (not lsp-inhibit-lsp-hooks)
   4879              lsp-managed-mode)
   4880     (run-hooks 'lsp-on-change-hook)))
   4881 
   4882 (defun lsp--after-change (buffer)
   4883   (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled)
   4884     (lsp--semantic-tokens-refresh-if-enabled buffer))
   4885   (when lsp--on-change-timer
   4886     (cancel-timer lsp--on-change-timer))
   4887   (setq lsp--on-change-timer (run-with-idle-timer
   4888                               lsp-idle-delay
   4889                               nil
   4890                               #'lsp--on-change-debounce
   4891                               buffer))
   4892   (lsp--idle-reschedule buffer))
   4893 
   4894 
   4895 (defcustom lsp-trim-trailing-whitespace t
   4896   "Trim trailing whitespace on a line."
   4897   :group 'lsp-mode
   4898   :type 'boolean)
   4899 
   4900 (defcustom lsp-insert-final-newline t
   4901   "Insert a newline character at the end of the file if one does not exist."
   4902   :group 'lsp-mode
   4903   :type 'boolean)
   4904 
   4905 (defcustom lsp-trim-final-newlines t
   4906   "Trim all newlines after the final newline at the end of the file."
   4907   :group 'lsp-mode
   4908   :type 'boolean)
   4909 
   4910 
   4911 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters)
   4912   "Self insert handling.
   4913 Applies on type formatting."
   4914   (let ((ch last-command-event))
   4915     (when (or (eq (string-to-char first-trigger-characters) ch)
   4916               (cl-find ch more-trigger-characters :key #'string-to-char))
   4917       (lsp-request-async "textDocument/onTypeFormatting"
   4918                          (lsp-make-document-on-type-formatting-params
   4919                           :text-document (lsp--text-document-identifier)
   4920                           :options (lsp-make-formatting-options
   4921                                     :tab-size (symbol-value (lsp--get-indent-width major-mode))
   4922                                     :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   4923                                     :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   4924                                     :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   4925                                     :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))
   4926                           :ch (char-to-string ch)
   4927                           :position (lsp--cur-position))
   4928                          (lambda (data) (lsp--apply-text-edits data 'format))
   4929                          :mode 'tick))))
   4930 
   4931 
   4932 ;; links
   4933 (defun lsp--document-links ()
   4934   (when (lsp-feature? "textDocument/documentLink")
   4935     (lsp-request-async
   4936      "textDocument/documentLink"
   4937      `(:textDocument ,(lsp--text-document-identifier))
   4938      (lambda (links)
   4939        (lsp--remove-overlays 'lsp-link)
   4940        (seq-do
   4941         (-lambda ((link &as &DocumentLink :range (&Range :start :end)))
   4942           (-doto (make-button (lsp--position-to-point start)
   4943                               (lsp--position-to-point end)
   4944                               'action (lsp--document-link-keymap link)
   4945                               'keymap (let ((map (make-sparse-keymap)))
   4946                                         (define-key map [M-return] 'push-button)
   4947                                         (define-key map [mouse-2] 'push-button)
   4948                                         map)
   4949                               'help-echo "mouse-2, M-RET: Visit this link")
   4950             (overlay-put 'lsp-link t)))
   4951         links))
   4952      :mode 'unchanged)))
   4953 
   4954 (defun lsp--document-link-handle-target (url)
   4955   (let* ((parsed-url (url-generic-parse-url (url-unhex-string url)))
   4956          (type (url-type parsed-url)))
   4957     (pcase type
   4958       ("file"
   4959        (xref-push-marker-stack)
   4960        (find-file (lsp--uri-to-path url))
   4961        (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url))
   4962          (goto-char (lsp--position-to-point
   4963                      (lsp-make-position :character (1- (string-to-number column))
   4964                                         :line (1- (string-to-number line)))))))
   4965       ((or "http" "https") (browse-url url))
   4966       (type (if-let ((handler (lsp--get-uri-handler type)))
   4967                 (funcall handler url)
   4968               (signal 'lsp-file-scheme-not-supported (list url)))))))
   4969 
   4970 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?))
   4971   (if target?
   4972       (lambda (_)
   4973         (interactive)
   4974         (lsp--document-link-handle-target target?))
   4975     (lambda (_)
   4976       (interactive)
   4977       (when (lsp:document-link-registration-options-resolve-provider?
   4978              (lsp--capability-for-method "textDocument/documentLink"))
   4979         (lsp-request-async
   4980          "documentLink/resolve"
   4981          link
   4982          (-lambda ((&DocumentLink :target?))
   4983            (lsp--document-link-handle-target target?)))))))
   4984 
   4985 
   4986 
   4987 (defcustom lsp-warn-no-matched-clients t
   4988   "Whether to show messages when there are no supported clients."
   4989   :group 'lsp-mode
   4990   :type 'boolean)
   4991 
   4992 (defun lsp-buffer-language--configured-id ()
   4993   "Return nil when not registered."
   4994   (->> lsp-language-id-configuration
   4995        (-first
   4996         (-lambda ((mode-or-pattern . language))
   4997           (cond
   4998            ((and (stringp mode-or-pattern)
   4999                  (s-matches? mode-or-pattern (buffer-file-name)))
   5000             language)
   5001            ((eq mode-or-pattern major-mode) language))))
   5002        cl-rest))
   5003 
   5004 (defvar-local lsp--buffer-language nil
   5005   "Locally cached returned value of `lsp-buffer-language'.")
   5006 
   5007 (defun lsp-buffer-language ()
   5008   "Get language corresponding current buffer."
   5009   (or lsp--buffer-language
   5010       (let* ((configured-language (lsp-buffer-language--configured-id)))
   5011         (setq lsp--buffer-language
   5012               (or configured-language
   5013                   ;; ensure non-nil
   5014                   (string-remove-suffix "-mode" (symbol-name major-mode))))
   5015         (when (and lsp-warn-no-matched-clients
   5016                    (null configured-language))
   5017           (lsp-warn "Unable to calculate the languageId for buffer `%s'. \
   5018 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s"
   5019                     (buffer-name)
   5020                     major-mode))
   5021         lsp--buffer-language)))
   5022 
   5023 (defun lsp-activate-on (&rest languages)
   5024   "Returns language activation function.
   5025 The function will return t when the `lsp-buffer-language' returns
   5026 one of the LANGUAGES."
   5027   (lambda (_file-name _mode)
   5028     (-contains? languages (lsp-buffer-language))))
   5029 
   5030 (defun lsp-workspace-root (&optional path)
   5031   "Find the workspace root for the current file or PATH."
   5032   (-when-let* ((file-name (or path (buffer-file-name)))
   5033                (file-name (lsp-f-canonical file-name)))
   5034     (->> (lsp-session)
   5035          (lsp-session-folders)
   5036          (--filter (and (lsp--files-same-host it file-name)
   5037                         (or (lsp-f-ancestor-of? it file-name)
   5038                             (equal it file-name))))
   5039          (--max-by (> (length it) (length other))))))
   5040 
   5041 (defun lsp-on-revert ()
   5042   "Executed when a file is reverted.
   5043 Added to `after-revert-hook'."
   5044   (let ((n (buffer-size))
   5045         (revert-buffer-in-progress-p nil))
   5046     (lsp-on-change 0 n n)))
   5047 
   5048 (defun lsp--text-document-did-close (&optional keep-workspace-alive)
   5049   "Executed when the file is closed, added to `kill-buffer-hook'.
   5050 
   5051 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace
   5052 if it's closing the last buffer in the workspace."
   5053   (lsp-foreach-workspace
   5054    (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   5055    (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S"
   5056      (lsp-notify "textDocument/didClose"
   5057                  `(:textDocument ,(lsp--text-document-identifier))))
   5058    (when (and (not lsp-keep-workspace-alive)
   5059               (not keep-workspace-alive)
   5060               (not (lsp--workspace-buffers lsp--cur-workspace)))
   5061      (lsp--shutdown-workspace))))
   5062 
   5063 (defun lsp--will-save-text-document-params (reason)
   5064   (list :textDocument (lsp--text-document-identifier)
   5065         :reason reason))
   5066 
   5067 (defun lsp--before-save ()
   5068   "Before save handler."
   5069   (with-demoted-errors "Error in ‘lsp--before-save’: %S"
   5070     (let ((params (lsp--will-save-text-document-params 1)))
   5071       (when (lsp--send-will-save-p)
   5072         (lsp-notify "textDocument/willSave" params))
   5073       (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits)
   5074         (let ((lsp-response-timeout 0.1))
   5075           (condition-case nil
   5076               (lsp--apply-text-edits
   5077                (lsp-request "textDocument/willSaveWaitUntil"
   5078                             params)
   5079                'before-save)
   5080             (error)))))))
   5081 
   5082 (defun lsp--on-auto-save ()
   5083   "Handler for auto-save."
   5084   (when (lsp--send-will-save-p)
   5085     (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S"
   5086       (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2)))))
   5087 
   5088 (defun lsp--text-document-did-save ()
   5089   "Executed when the file is closed, added to `after-save-hook''."
   5090   (when (lsp--send-did-save-p)
   5091     (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’"
   5092       (lsp-notify "textDocument/didSave"
   5093                   `( :textDocument ,(lsp--versioned-text-document-identifier)
   5094                      ,@(when (lsp--save-include-text-p)
   5095                          (list :text (lsp--buffer-content))))))))
   5096 
   5097 (defun lsp--text-document-position-params (&optional identifier position)
   5098   "Make TextDocumentPositionParams for the current point in the current document.
   5099 If IDENTIFIER and POSITION are non-nil, they will be used as the document
   5100 identifier and the position respectively."
   5101   (list :textDocument (or identifier (lsp--text-document-identifier))
   5102         :position (or position (lsp--cur-position))))
   5103 
   5104 (defun lsp--get-buffer-diagnostics ()
   5105   "Return buffer diagnostics."
   5106   (gethash (or
   5107             (plist-get lsp--virtual-buffer :buffer-file-name)
   5108             (lsp--fix-path-casing (buffer-file-name)))
   5109            (lsp-diagnostics t)))
   5110 
   5111 (defun lsp-cur-line-diagnostics ()
   5112   "Return any diagnostics that apply to the current line."
   5113   (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)]
   5114     (cl-coerce (-filter
   5115                 (-lambda ((&Diagnostic :range (&Range :start (&Position :line))))
   5116                   (and (>= line start) (<= line end)))
   5117                 (lsp--get-buffer-diagnostics))
   5118                'vector)))
   5119 
   5120 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end)
   5121                                   (right &as &Range :start right-start :end right-end))
   5122   (or (lsp-point-in-range? right-start left)
   5123       (lsp-point-in-range? right-end left)
   5124       (lsp-point-in-range? left-start right)
   5125       (lsp-point-in-range? left-end right)))
   5126 
   5127 (defun lsp-make-position-1 (position)
   5128   (lsp-make-position :line (plist-get position :line)
   5129                      :character (plist-get position :character)))
   5130 
   5131 (defun lsp-cur-possition-diagnostics ()
   5132   "Return any diagnostics that apply to the current line."
   5133   (-let* ((start (if (use-region-p) (region-beginning) (point)))
   5134           (end (if (use-region-p) (region-end) (point)))
   5135           (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start))
   5136                                          :end (lsp-make-position-1 (lsp-point-to-position end)))))
   5137     (->> (lsp--get-buffer-diagnostics)
   5138          (-filter
   5139           (-lambda ((&Diagnostic :range))
   5140             (lsp-range-overlapping? range current-range)))
   5141          (apply 'vector))))
   5142 
   5143 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics)
   5144 
   5145 (defun lsp--extract-line-from-buffer (pos)
   5146   "Return the line pointed to by POS (a Position object) in the current buffer."
   5147   (let* ((point (lsp--position-to-point pos))
   5148          (inhibit-field-text-motion t))
   5149     (save-excursion
   5150       (goto-char point)
   5151       (buffer-substring (line-beginning-position) (line-end-position)))))
   5152 
   5153 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line)
   5154                                                  :end (end &as &Position :character end-char)))
   5155   "Return a xref-item from a RANGE in FILENAME."
   5156   (let* ((line (lsp--extract-line-from-buffer start))
   5157          (len (length line)))
   5158     (add-face-text-property (max (min start-char len) 0)
   5159                             (max (min end-char len) 0)
   5160                             'xref-match t line)
   5161     ;; LINE is nil when FILENAME is not being current visited by any buffer.
   5162     (xref-make (or line filename)
   5163                (xref-make-file-location
   5164                 filename
   5165                 (lsp-translate-line (1+ start-line))
   5166                 (lsp-translate-column start-char)))))
   5167 
   5168 (defun lsp--location-uri (loc)
   5169   (if (lsp-location? loc)
   5170       (lsp:location-uri loc)
   5171     (lsp:location-link-target-uri loc)))
   5172 
   5173 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start)))
   5174   "Go to location."
   5175   (let ((path (lsp--uri-to-path uri)))
   5176     (if (f-exists? path)
   5177         (with-current-buffer (find-file path)
   5178           (goto-char (lsp--position-to-point start)))
   5179       (error "There is no file %s" path))))
   5180 
   5181 (defun lsp--location-range (loc)
   5182   (if (lsp-location? loc)
   5183       (lsp:location-range loc)
   5184     (lsp:location-link-target-selection-range loc)))
   5185 
   5186 (defun lsp--locations-to-xref-items (locations)
   5187   "Return a list of `xref-item' given LOCATIONS, which can be of
   5188 type Location, LocationLink, Location[] or LocationLink[]."
   5189   (setq locations
   5190         (pcase locations
   5191           ((seq (or (Location)
   5192                     (LocationLink)))
   5193            (append locations nil))
   5194           ((or (Location)
   5195                (LocationLink))
   5196            (list locations))))
   5197 
   5198   (cl-labels ((get-xrefs-in-file
   5199                (file-locs)
   5200                (-let [(filename . matches) file-locs]
   5201                  (condition-case err
   5202                      (let ((visiting (find-buffer-visiting filename))
   5203                            (fn (lambda (loc)
   5204                                  (lsp-with-filename filename
   5205                                    (lsp--xref-make-item filename
   5206                                                         (lsp--location-range loc))))))
   5207                        (if visiting
   5208                            (with-current-buffer visiting
   5209                              (seq-map fn matches))
   5210                          (when (file-readable-p filename)
   5211                            (with-temp-buffer
   5212                              (insert-file-contents-literally filename)
   5213                              (seq-map fn matches)))))
   5214                    (error (lsp-warn "Failed to process xref entry for filename '%s': %s"
   5215                                     filename (error-message-string err)))
   5216                    (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s"
   5217                                          filename (error-message-string err)))))))
   5218 
   5219     (->> locations
   5220          (seq-sort #'lsp--location-before-p)
   5221          (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri))
   5222          (seq-map #'get-xrefs-in-file)
   5223          (apply #'nconc))))
   5224 
   5225 (defun lsp--location-before-p (left right)
   5226   "Sort first by file, then by line, then by column."
   5227   (let ((left-uri (lsp--location-uri left))
   5228         (right-uri (lsp--location-uri right)))
   5229     (if (not (string= left-uri right-uri))
   5230         (string< left-uri right-uri)
   5231       (-let (((&Range :start left-start) (lsp--location-range left))
   5232              ((&Range :start right-start) (lsp--location-range right)))
   5233         (lsp--position-compare right-start left-start)))))
   5234 
   5235 (defun lsp--make-reference-params (&optional td-position exclude-declaration)
   5236   "Make a ReferenceParam object.
   5237 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead.
   5238 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations."
   5239   (let ((json-false :json-false))
   5240     (plist-put (or td-position (lsp--text-document-position-params))
   5241                :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration))))))
   5242 
   5243 (defun lsp--cancel-request (id)
   5244   "Cancel request with ID in all workspaces."
   5245   (lsp-foreach-workspace
   5246    (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id))
   5247    (lsp-notify "$/cancelRequest" `(:id ,id))))
   5248 
   5249 (defvar-local lsp--hover-saved-bounds nil)
   5250 
   5251 (defun lsp-eldoc-function (cb &rest _ignored)
   5252   "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')."
   5253   (if (and lsp--hover-saved-bounds
   5254            (lsp--point-in-bounds-p lsp--hover-saved-bounds))
   5255       lsp--eldoc-saved-message
   5256     (setq lsp--hover-saved-bounds nil
   5257           lsp--eldoc-saved-message nil)
   5258     (if (looking-at-p "[[:space:]\n]")
   5259         (setq lsp--eldoc-saved-message nil) ; And returns nil.
   5260       (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover"))
   5261         (lsp-request-async
   5262          "textDocument/hover"
   5263          (lsp--text-document-position-params)
   5264          (-lambda ((hover &as &Hover? :range? :contents))
   5265            (setq lsp--hover-saved-bounds (when range?
   5266                                            (lsp--range-to-region range?)))
   5267            (funcall cb (setq lsp--eldoc-saved-message
   5268                              (when contents
   5269                                (lsp--render-on-hover-content
   5270                                 contents
   5271                                 lsp-eldoc-render-all)))))
   5272          :error-handler #'ignore
   5273          :mode 'tick
   5274          :cancel-token :eldoc-hover)))))
   5275 
   5276 (defun lsp--point-on-highlight? ()
   5277   (-some? (lambda (overlay)
   5278             (overlay-get overlay 'lsp-highlight))
   5279           (overlays-at (point))))
   5280 
   5281 (defun lsp--cleanup-highlights-if-needed ()
   5282   (when (and lsp-enable-symbol-highlighting
   5283              lsp--have-document-highlights
   5284              (not (lsp--point-on-highlight?)))
   5285     (lsp--remove-overlays 'lsp-highlight)
   5286     (setq lsp--have-document-highlights nil)
   5287     (lsp-cancel-request-by-token :highlights)))
   5288 
   5289 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil
   5290   "The bounds of the symbol from which `lsp--document-highlight'
   5291   most recently requested highlights.")
   5292 
   5293 (defun lsp--document-highlight ()
   5294   (when (lsp-feature? "textDocument/documentHighlight")
   5295     (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol)))
   5296       (unless (or (looking-at-p "[[:space:]\n]")
   5297                   (not lsp-enable-symbol-highlighting)
   5298                   (and lsp--have-document-highlights
   5299                        curr-sym-bounds
   5300                        (equal curr-sym-bounds
   5301                               lsp--symbol-bounds-of-last-highlight-invocation)))
   5302         (setq lsp--symbol-bounds-of-last-highlight-invocation
   5303               curr-sym-bounds)
   5304         (lsp-request-async "textDocument/documentHighlight"
   5305                            (lsp--text-document-position-params)
   5306                            #'lsp--document-highlight-callback
   5307                            :mode 'tick
   5308                            :cancel-token :highlights)))))
   5309 
   5310 (defun lsp--help-open-link (&rest _)
   5311   "Open markdown link at point via mouse or keyboard."
   5312   (interactive "P")
   5313   (let ((buffer-list-update-hook nil))
   5314     (-let [(buffer point) (if-let* ((valid (and (listp last-input-event)
   5315                                                 (eq (car last-input-event) 'mouse-2)))
   5316                                     (event (cadr last-input-event))
   5317                                     (win (posn-window event))
   5318                                     (buffer (window-buffer win)))
   5319                               `(,buffer ,(posn-point event))
   5320                             `(,(current-buffer) ,(point)))]
   5321       (with-current-buffer buffer
   5322         (when-let* ((face (get-text-property point 'face))
   5323                     (url (or (and (eq face 'markdown-link-face)
   5324                                   (get-text-property point 'help-echo))
   5325                              (and (memq face '(markdown-url-face markdown-plain-url-face))
   5326                                   (nth 3 (markdown-link-at-pos point))))))
   5327           (lsp--document-link-handle-target url))))))
   5328 
   5329 (defvar lsp-help-mode-map
   5330   (-doto (make-sparse-keymap)
   5331     (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link))
   5332   "Keymap for `lsp-help-mode'.")
   5333 
   5334 (define-derived-mode lsp-help-mode help-mode "LspHelp"
   5335   "Major mode for displaying lsp help.")
   5336 
   5337 (defun lsp-describe-thing-at-point ()
   5338   "Display the type signature and documentation of the thing at point."
   5339   (interactive)
   5340   (let ((contents (-some->> (lsp--text-document-position-params)
   5341                     (lsp--make-request "textDocument/hover")
   5342                     (lsp--send-request)
   5343                     (lsp:hover-contents))))
   5344     (if (and contents (not (equal contents "")))
   5345         (let ((lsp-help-buf-name "*lsp-help*"))
   5346           (with-current-buffer (get-buffer-create lsp-help-buf-name)
   5347             (delay-mode-hooks
   5348               (lsp-help-mode)
   5349               (with-help-window lsp-help-buf-name
   5350                 (insert (string-trim-right (lsp--render-on-hover-content contents t)))))
   5351             (run-mode-hooks)))
   5352       (lsp--info "No content at point."))))
   5353 
   5354 (defun lsp--point-in-bounds-p (bounds)
   5355   "Return whether the current point is within BOUNDS."
   5356   (and (<= (car bounds) (point)) (< (point) (cdr bounds))))
   5357 
   5358 (defun lsp-get-renderer (language)
   5359   "Get renderer for LANGUAGE."
   5360   (lambda (str)
   5361     (lsp--render-string str language)))
   5362 
   5363 (defun lsp--setup-markdown (mode)
   5364   "Setup the ‘markdown-mode’ in the frame.
   5365 MODE is the mode used in the parent frame."
   5366   (make-local-variable 'markdown-code-lang-modes)
   5367   (dolist (mark (alist-get mode lsp-custom-markup-modes))
   5368     (add-to-list 'markdown-code-lang-modes (cons mark mode)))
   5369   (setq-local markdown-fontify-code-blocks-natively t)
   5370   (setq-local markdown-fontify-code-block-default-mode mode)
   5371   (setq-local markdown-hide-markup t)
   5372 
   5373   ;; Render some common HTML entities.
   5374   ;; This should really happen in markdown-mode instead,
   5375   ;; but it doesn't, so we do it here for now.
   5376   (setq prettify-symbols-alist
   5377         (cl-loop for i from 0 to 255
   5378                  collect (cons (format "&#x%02X;" i) i)))
   5379   (push '("&lt;" . ?<) prettify-symbols-alist)
   5380   (push '("&gt;" . ?>) prettify-symbols-alist)
   5381   (push '("&amp;" . ?&) prettify-symbols-alist)
   5382   (push '("&nbsp;" . ? ) prettify-symbols-alist)
   5383   (setq prettify-symbols-compose-predicate
   5384         (lambda (_start _end _match) t))
   5385   (prettify-symbols-mode 1))
   5386 
   5387 (defvar lsp-help-link-keymap
   5388   (let ((map (make-sparse-keymap)))
   5389     (define-key map [mouse-2] #'lsp--help-open-link)
   5390     (define-key map "\r" #'lsp--help-open-link)
   5391     map)
   5392   "Keymap active on links in *lsp-help* mode.")
   5393 
   5394 (defun lsp--fix-markdown-links ()
   5395   (let ((inhibit-read-only t)
   5396         (inhibit-modification-hooks t)
   5397         (prop))
   5398     (save-restriction
   5399       (goto-char (point-min))
   5400       (while (setq prop (markdown-find-next-prop 'face))
   5401         (let ((end (or (next-single-property-change (car prop) 'face)
   5402                        (point-max))))
   5403           (when (memq (get-text-property (car prop) 'face)
   5404                       '(markdown-link-face
   5405                         markdown-url-face
   5406                         markdown-plain-url-face))
   5407             (add-text-properties (car prop) end
   5408                                  (list 'button t
   5409                                        'category 'lsp-help-link
   5410                                        'follow-link t
   5411                                        'keymap lsp-help-link-keymap)))
   5412           (goto-char end))))))
   5413 
   5414 (defun lsp--buffer-string-visible ()
   5415   "Return visible buffer string.
   5416 Stolen from `org-copy-visible'."
   5417   (let ((temp (generate-new-buffer " *temp*"))
   5418         (beg (point-min))
   5419         (end (point-max)))
   5420     (while (/= beg end)
   5421       (when (get-char-property beg 'invisible)
   5422         (setq beg (next-single-char-property-change beg 'invisible nil end)))
   5423       (let* ((next (next-single-char-property-change beg 'invisible nil end))
   5424              (substring (buffer-substring beg next)))
   5425         (with-current-buffer temp (insert substring))
   5426         ;; (setq result (concat result substring))
   5427         (setq beg next)))
   5428     (setq deactivate-mark t)
   5429     (prog1 (with-current-buffer temp
   5430              (s-chop-suffix "\n" (buffer-string)))
   5431       (kill-buffer temp))))
   5432 
   5433 (defvar lsp-buffer-major-mode nil
   5434   "Holds the major mode when fontification function is running.
   5435 See #2588")
   5436 
   5437 (defvar view-inhibit-help-message)
   5438 
   5439 (defun lsp--render-markdown ()
   5440   "Render markdown."
   5441 
   5442   (let ((markdown-enable-math nil))
   5443     (goto-char (point-min))
   5444     (while (re-search-forward
   5445             (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/"
   5446                                      "{" "}" "[" "]" "(" ")"
   5447                                      "#" "+" "-" "." "!" "|"))))
   5448             nil t)
   5449       (replace-match (rx (backref 1))))
   5450 
   5451     ;; markdown-mode v2.3 does not yet provide gfm-view-mode
   5452     (if (fboundp 'gfm-view-mode)
   5453         (let ((view-inhibit-help-message t))
   5454           (gfm-view-mode))
   5455       (gfm-mode))
   5456 
   5457     (lsp--setup-markdown lsp-buffer-major-mode)))
   5458 
   5459 (defvar lsp--display-inline-image-alist
   5460   '((lsp--render-markdown
   5461      (:regexp
   5462       "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)"
   5463       :sexp
   5464       (create-image
   5465        (base64-decode-string
   5466         (buffer-substring-no-properties (match-beginning 1) (match-end 1)))
   5467        nil t))))
   5468   "Replaced string regexp and function returning image.
   5469 Each element should have the form (MODE . (PROPERTY-LIST...)).
   5470 MODE (car) is function which is defined in `lsp-language-id-configuration'.
   5471 Cdr should be list of PROPERTY-LIST.
   5472 
   5473 Each PROPERTY-LIST should have properties:
   5474 :regexp  Regexp which determines what string is relpaced to image.
   5475          You should also get information of image, by parenthesis constructs.
   5476          By default, all matched string is replaced to image, but you can
   5477          change index of replaced string by keyword :replaced-index.
   5478 
   5479 :sexp    Return image when evaluated. You can use information of regexp
   5480          by using (match-beggining N), (match-end N) or (match-substring N).
   5481 
   5482 In addition, each can have property:
   5483 :replaced-index  Determine index which is used to replace regexp to image.
   5484                  The value means first argument of `match-beginning' and
   5485                  `match-end'. If omitted, interpreted as index 0.")
   5486 
   5487 (defcustom lsp-display-inline-image t
   5488   "Showing inline image or not."
   5489   :group 'lsp-mode
   5490   :type 'boolean)
   5491 
   5492 (defcustom lsp-enable-suggest-server-download t
   5493   "When non-nil enable server downloading suggestions."
   5494   :group 'lsp-mode
   5495   :type 'boolean
   5496   :package-version '(lsp-mode . "9.0.0"))
   5497 
   5498 (defcustom lsp-auto-register-remote-clients t
   5499   "When non-nil register remote when registering the local one."
   5500   :group 'lsp-mode
   5501   :type 'boolean
   5502   :package-version '(lsp-mode . "9.0.0"))
   5503 
   5504 (defun lsp--display-inline-image (mode)
   5505   "Add image property if available."
   5506   (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist))))
   5507     (when (and (display-images-p) lsp-display-inline-image)
   5508       (cl-loop
   5509        for plist in plist-list
   5510        with regexp with replaced-index
   5511        do
   5512        (setq regexp (plist-get plist :regexp))
   5513        (setq replaced-index (or (plist-get plist :replaced-index) 0))
   5514 
   5515        (font-lock-remove-keywords nil (list regexp replaced-index))
   5516        (let ((inhibit-read-only t))
   5517          (save-excursion
   5518            (goto-char (point-min))
   5519            (while (re-search-forward regexp nil t)
   5520              (set-text-properties
   5521               (match-beginning replaced-index) (match-end replaced-index)
   5522               nil)
   5523              (add-text-properties
   5524               (match-beginning replaced-index) (match-end replaced-index)
   5525               `(display ,(eval (plist-get plist :sexp)))))))))))
   5526 
   5527 (defun lsp--fontlock-with-mode (str mode)
   5528   "Fontlock STR with MODE."
   5529   (let ((lsp-buffer-major-mode major-mode))
   5530     (with-temp-buffer
   5531       (with-demoted-errors "Error during doc rendering: %s"
   5532         (insert str)
   5533         (delay-mode-hooks (funcall mode))
   5534         (cl-flet ((window-body-width () lsp-window-body-width))
   5535           ;; This can go wrong in some cases, and the fontification would
   5536           ;; not work as expected.
   5537           ;;
   5538           ;; See #2984
   5539           (ignore-errors (font-lock-ensure))
   5540           (lsp--display-inline-image mode)
   5541           (when (eq mode 'lsp--render-markdown)
   5542             (lsp--fix-markdown-links))))
   5543       (lsp--buffer-string-visible))))
   5544 
   5545 (defun lsp--render-string (str language)
   5546   "Render STR using `major-mode' corresponding to LANGUAGE.
   5547 When language is nil render as markup if `markdown-mode' is loaded."
   5548   (setq str (s-replace "\r" "" (or str "")))
   5549   (if-let* ((modes (-keep (-lambda ((mode . lang))
   5550                             (when (and (equal lang language) (functionp mode))
   5551                               mode))
   5552                           lsp-language-id-configuration))
   5553             (mode (car (or (member major-mode modes) modes))))
   5554       (lsp--fontlock-with-mode str mode)
   5555     str))
   5556 
   5557 (defun lsp--render-element (content)
   5558   "Render CONTENT element."
   5559   (let ((inhibit-message t))
   5560     (or
   5561      (pcase content
   5562        ((MarkedString :value :language)
   5563         (lsp--render-string value language))
   5564        ((MarkupContent :value :kind)
   5565         (lsp--render-string value kind))
   5566        ;; plain string
   5567        ((pred stringp) (lsp--render-string content "markdown"))
   5568        ((pred null) "")
   5569        (_ (error "Failed to handle %s" content)))
   5570      "")))
   5571 
   5572 (defun lsp--create-unique-string-fn ()
   5573   (let (elements)
   5574     (lambda (element)
   5575       (let ((count (cl-count element elements :test #'string=)))
   5576         (prog1 (if (zerop count)
   5577                    element
   5578                  (format "%s (%s)" element count))
   5579           (push element elements))))))
   5580 
   5581 (defun lsp--select-action (actions)
   5582   "Select an action to execute from ACTIONS."
   5583   (cond
   5584    ((seq-empty-p actions) (signal 'lsp-no-code-actions nil))
   5585    ((and (eq (seq-length actions) 1) lsp-auto-execute-action)
   5586     (lsp-seq-first actions))
   5587    (t (let ((completion-ignore-case t))
   5588         (lsp--completing-read "Select code action: "
   5589                               (seq-into actions 'list)
   5590                               (-compose (lsp--create-unique-string-fn)
   5591                                         #'lsp:code-action-title)
   5592                               nil t)))))
   5593 
   5594 (defun lsp--workspace-server-id (workspace)
   5595   "Return the server ID of WORKSPACE."
   5596   (-> workspace lsp--workspace-client lsp--client-server-id))
   5597 
   5598 (defun lsp--handle-rendered-for-echo-area (contents)
   5599   "Return a single line from RENDERED, appropriate for display in the echo area."
   5600   (pcase (lsp-workspaces)
   5601     (`(,workspace)
   5602      (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace)))
   5603     ;; For projects with multiple active workspaces we also default to
   5604     ;; render the first line.
   5605     (_ (lsp-clients-extract-signature-on-hover contents nil))))
   5606 
   5607 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id)
   5608   "Extract a representative line from CONTENTS, to show in the echo area."
   5609   (car (s-lines (s-trim (lsp--render-element contents)))))
   5610 
   5611 (defun lsp--render-on-hover-content (contents render-all)
   5612   "Render the content received from `document/onHover' request.
   5613 CONTENTS  - MarkedString | MarkedString[] | MarkupContent
   5614 RENDER-ALL - nil if only the signature should be rendered."
   5615   (cond
   5616    ((lsp-markup-content? contents)
   5617     ;; MarkupContent.
   5618     ;; It tends to be long and is not suitable to display fully in the echo area.
   5619     ;; Just display the first line which is typically the signature.
   5620     (if render-all
   5621         (lsp--render-element contents)
   5622       (lsp--handle-rendered-for-echo-area contents)))
   5623    ((and (stringp contents) (not (string-match-p "\n" contents)))
   5624     ;; If the contents is a single string containing a single line,
   5625     ;; render it always.
   5626     (lsp--render-element contents))
   5627    (t
   5628     ;; MarkedString -> MarkedString[]
   5629     (when (or (lsp-marked-string? contents) (stringp contents))
   5630       (setq contents (list contents)))
   5631     ;; Consider the signature consisting of the elements who have a renderable
   5632     ;; "language" property. When render-all is nil, ignore other elements.
   5633     (string-join
   5634      (seq-map
   5635       #'lsp--render-element
   5636       (if render-all
   5637           contents
   5638         ;; Only render contents that have an available renderer.
   5639         (seq-take
   5640          (seq-filter
   5641           (-andfn #'lsp-marked-string?
   5642                   (-compose #'lsp-get-renderer #'lsp:marked-string-language))
   5643           contents)
   5644          1)))
   5645      (if (bound-and-true-p page-break-lines-mode)
   5646          "\n\n"
   5647        "\n")))))
   5648 
   5649 
   5650 
   5651 (defvar lsp-signature-mode-map
   5652   (-doto (make-sparse-keymap)
   5653     (define-key (kbd "M-n") #'lsp-signature-next)
   5654     (define-key (kbd "M-p") #'lsp-signature-previous)
   5655     (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs)
   5656     (define-key (kbd "C-c C-k") #'lsp-signature-stop)
   5657     (define-key (kbd "C-g") #'lsp-signature-stop))
   5658   "Keymap for `lsp-signature-mode'.")
   5659 
   5660 (define-minor-mode lsp-signature-mode
   5661   "Mode used to show signature popup."
   5662   :keymap lsp-signature-mode-map
   5663   :lighter ""
   5664   :group 'lsp-mode)
   5665 
   5666 (defun lsp-signature-stop ()
   5667   "Stop showing current signature help."
   5668   (interactive)
   5669   (lsp-cancel-request-by-token :signature)
   5670   (remove-hook 'post-command-hook #'lsp-signature)
   5671   (funcall lsp-signature-function nil)
   5672   (lsp-signature-mode -1))
   5673 
   5674 (declare-function page-break-lines--update-display-tables "ext:page-break-lines")
   5675 
   5676 (defun lsp--setup-page-break-mode-if-present ()
   5677   "Enable `page-break-lines-mode' in current buffer."
   5678   (when (fboundp 'page-break-lines-mode)
   5679     (page-break-lines-mode)
   5680     ;; force page-break-lines-mode to update the display tables.
   5681     (page-break-lines--update-display-tables)))
   5682 
   5683 (defun lsp-lv-message (message)
   5684   (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)
   5685   (if message
   5686       (progn
   5687         (setq lsp--signature-last-buffer (current-buffer))
   5688         (let ((lv-force-update t))
   5689           (lv-message "%s" message)))
   5690     (lv-delete-window)
   5691     (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)))
   5692 
   5693 (declare-function posframe-show "ext:posframe")
   5694 (declare-function posframe-hide "ext:posframe")
   5695 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe")
   5696 
   5697 (defface lsp-signature-posframe
   5698   '((t :inherit tooltip))
   5699   "Background and foreground for `lsp-signature-posframe'."
   5700   :group 'lsp-mode)
   5701 
   5702 (defvar lsp-signature-posframe-params
   5703   (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward
   5704         :height 10
   5705         :width 60
   5706         :border-width 1
   5707         :min-width 60)
   5708   "Params for signature and `posframe-show'.")
   5709 
   5710 (defun lsp-signature-posframe (str)
   5711   "Use posframe to show the STR signatureHelp string."
   5712   (if str
   5713       (apply #'posframe-show
   5714              (with-current-buffer (get-buffer-create " *lsp-signature*")
   5715                (erase-buffer)
   5716                (insert str)
   5717                (visual-line-mode 1)
   5718                (lsp--setup-page-break-mode-if-present)
   5719                (current-buffer))
   5720              (append
   5721               lsp-signature-posframe-params
   5722               (list :position (point)
   5723                     :background-color (face-attribute 'lsp-signature-posframe :background nil t)
   5724                     :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t)
   5725                     :border-color (face-attribute 'font-lock-comment-face :foreground nil t))))
   5726     (posframe-hide " *lsp-signature*")))
   5727 
   5728 (defun lsp--handle-signature-update (signature)
   5729   (let ((message
   5730          (if (lsp-signature-help? signature)
   5731              (lsp--signature->message signature)
   5732            (mapconcat #'lsp--signature->message signature "\n"))))
   5733     (if (s-present? message)
   5734         (funcall lsp-signature-function message)
   5735       (lsp-signature-stop))))
   5736 
   5737 (defun lsp-signature-activate ()
   5738   "Activate signature help.
   5739 It will show up only if current point has signature help."
   5740   (interactive)
   5741   (setq lsp--signature-last nil
   5742         lsp--signature-last-index nil
   5743         lsp--signature-last-buffer (current-buffer))
   5744   (add-hook 'post-command-hook #'lsp-signature)
   5745   (lsp-signature-mode t))
   5746 
   5747 (defcustom lsp-signature-cycle t
   5748   "Whether `lsp-signature-next' and prev should cycle."
   5749   :type 'boolean
   5750   :group 'lsp-mode)
   5751 
   5752 (defun lsp-signature-next ()
   5753   "Show next signature."
   5754   (interactive)
   5755   (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last))))
   5756     (when (and lsp--signature-last-index
   5757                lsp--signature-last
   5758                (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs)))
   5759       (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs))
   5760       (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))))
   5761 
   5762 (defun lsp-signature-previous ()
   5763   "Next signature."
   5764   (interactive)
   5765   (when (and lsp--signature-last-index
   5766              lsp--signature-last
   5767              (or lsp-signature-cycle (not (zerop lsp--signature-last-index))))
   5768     (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index)
   5769                                             (length (lsp:signature-help-signatures lsp--signature-last))
   5770                                           lsp--signature-last-index)))
   5771     (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))
   5772 
   5773 (defun lsp-signature-toggle-full-docs ()
   5774   "Toggle full/partial signature documentation."
   5775   (interactive)
   5776   (let ((all? (not (numberp lsp-signature-doc-lines))))
   5777     (setq lsp-signature-doc-lines (if all?
   5778                                       (or (car-safe lsp-signature-doc-lines)
   5779                                           20)
   5780                                     (list lsp-signature-doc-lines))))
   5781   (lsp-signature-activate))
   5782 
   5783 (defun lsp--signature->message (signature-help)
   5784   "Generate eldoc message from SIGNATURE-HELP response."
   5785   (setq lsp--signature-last signature-help)
   5786 
   5787   (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help))))
   5788     (-let* (((&SignatureHelp :active-signature?
   5789                              :active-parameter?
   5790                              :signatures) signature-help)
   5791             (active-signature? (or lsp--signature-last-index active-signature? 0))
   5792             (_ (setq lsp--signature-last-index active-signature?))
   5793             ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?))
   5794             (prefix (if (= (length signatures) 1)
   5795                         ""
   5796                       (concat (propertize (format " %s/%s"
   5797                                                   (1+ active-signature?)
   5798                                                   (length signatures))
   5799                                           'face 'success)
   5800                               " ")))
   5801             (method-docs (when
   5802                              (and lsp-signature-render-documentation
   5803                                   (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines)))
   5804                            (let ((docs (lsp--render-element
   5805                                         (lsp:parameter-information-documentation? signature))))
   5806                              (when (s-present? docs)
   5807                                (concat
   5808                                 "\n"
   5809                                 (if (fboundp 'page-break-lines-mode)
   5810                                     "\n"
   5811                                   "")
   5812                                 (if (and (numberp lsp-signature-doc-lines)
   5813                                          (> (length (s-lines docs)) lsp-signature-doc-lines))
   5814                                     (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs)))
   5815                                             (propertize "\nTruncated..." 'face 'highlight))
   5816                                   docs)))))))
   5817       (when (and active-parameter? (not (seq-empty-p parameters?)))
   5818         (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?)))
   5819                               (seq-elt parameters? active-parameter?)))
   5820                      (selected-param-label (let ((label (lsp:parameter-information-label param)))
   5821                                              (if (stringp label) label (append label nil))))
   5822                      (start (if (stringp selected-param-label)
   5823                                 (s-index-of selected-param-label label)
   5824                               (cl-first selected-param-label)))
   5825                      (end (if (stringp selected-param-label)
   5826                               (+ start (length selected-param-label))
   5827                             (cl-second selected-param-label))))
   5828           (add-face-text-property start end 'eldoc-highlight-function-argument nil label)))
   5829       (concat prefix label method-docs))))
   5830 
   5831 (defun lsp-signature ()
   5832   "Display signature info (based on `textDocument/signatureHelp')"
   5833   (if (and lsp--signature-last-buffer
   5834            (not (equal (current-buffer) lsp--signature-last-buffer)))
   5835       (lsp-signature-stop)
   5836     (lsp-request-async "textDocument/signatureHelp"
   5837                        (lsp--text-document-position-params)
   5838                        #'lsp--handle-signature-update
   5839                        :cancel-token :signature)))
   5840 
   5841 
   5842 (defcustom lsp-overlay-document-color-char "■"
   5843   "Display the char represent the document color in overlay"
   5844   :type 'string
   5845   :group 'lsp-mode)
   5846 
   5847 ;; color presentation
   5848 (defun lsp--color-create-interactive-command (color range)
   5849   (lambda ()
   5850     (interactive)
   5851     (-let [(&ColorPresentation? :text-edit?
   5852                                 :additional-text-edits?)
   5853            (lsp--completing-read
   5854             "Select color presentation: "
   5855             (lsp-request
   5856              "textDocument/colorPresentation"
   5857              `( :textDocument ,(lsp--text-document-identifier)
   5858                 :color ,color
   5859                 :range ,range))
   5860             #'lsp:color-presentation-label
   5861             nil
   5862             t)]
   5863       (when text-edit?
   5864         (lsp--apply-text-edit text-edit?))
   5865       (when additional-text-edits?
   5866         (lsp--apply-text-edits additional-text-edits? 'color-presentation)))))
   5867 
   5868 (defun lsp--number->color (number)
   5869   (let ((result (format "%x"
   5870                         (round (* (or number 0) 255.0)))))
   5871     (if (= 1 (length result))
   5872         (concat "0" result)
   5873       result)))
   5874 
   5875 (defun lsp--document-color ()
   5876   "Document color handler."
   5877   (when (lsp-feature? "textDocument/documentColor")
   5878     (lsp-request-async
   5879      "textDocument/documentColor"
   5880      `(:textDocument ,(lsp--text-document-identifier))
   5881      (lambda (result)
   5882        (lsp--remove-overlays 'lsp-color)
   5883        (seq-do
   5884         (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue)
   5885                                      :range))
   5886           (-let* (((beg . end) (lsp--range-to-region range))
   5887                   (overlay (make-overlay beg end))
   5888                   (command (lsp--color-create-interactive-command color range)))
   5889             (overlay-put overlay 'lsp-color t)
   5890             (overlay-put overlay 'evaporate t)
   5891             (overlay-put overlay
   5892                          'before-string
   5893                          (propertize
   5894                           lsp-overlay-document-color-char
   5895                           'face `((:foreground ,(format
   5896                                                  "#%s%s%s"
   5897                                                  (lsp--number->color red)
   5898                                                  (lsp--number->color green)
   5899                                                  (lsp--number->color blue))))
   5900                           'action command
   5901                           'mouse-face 'lsp-lens-mouse-face
   5902                           'local-map (-doto (make-sparse-keymap)
   5903                                        (define-key [mouse-1] command))))))
   5904         result))
   5905      :mode 'unchanged
   5906      :cancel-token :document-color-token)))
   5907 
   5908 
   5909 
   5910 (defun lsp--action-trigger-parameter-hints (_command)
   5911   "Handler for editor.action.triggerParameterHints."
   5912   (when (member :on-server-request lsp-signature-auto-activate)
   5913     (lsp-signature-activate)))
   5914 
   5915 (defun lsp--action-trigger-suggest (_command)
   5916   "Handler for editor.action.triggerSuggest."
   5917   (cond
   5918    ((and (bound-and-true-p company-mode)
   5919          (fboundp 'company-auto-begin)
   5920          (fboundp 'company-post-command))
   5921     (run-at-time 0 nil
   5922                  (lambda ()
   5923                    (let ((this-command 'company-idle-begin)
   5924                          (company-minimum-prefix-length 0))
   5925                      (company-auto-begin)
   5926                      (company-post-command)))))
   5927    (t
   5928     (completion-at-point))))
   5929 
   5930 (defconst lsp--default-action-handlers
   5931   (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints)
   5932       ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest))
   5933   "Default action handlers.")
   5934 
   5935 (defun lsp--find-action-handler (command)
   5936   "Find action handler for particular COMMAND."
   5937   (or
   5938    (--some (-some->> it
   5939              (lsp--workspace-client)
   5940              (lsp--client-action-handlers)
   5941              (gethash command))
   5942            (lsp-workspaces))
   5943    (gethash command lsp--default-action-handlers)))
   5944 
   5945 (defun lsp--text-document-code-action-params (&optional kind)
   5946   "Code action params."
   5947   (list :textDocument (lsp--text-document-identifier)
   5948         :range (if (use-region-p)
   5949                    (lsp--region-to-range (region-beginning) (region-end))
   5950                  (lsp--region-to-range (point) (point)))
   5951         :context `( :diagnostics ,(lsp-cur-possition-diagnostics)
   5952                     ,@(when kind (list :only (vector kind))))))
   5953 
   5954 (defun lsp-code-actions-at-point (&optional kind)
   5955   "Retrieve the code actions for the active region or the current line.
   5956 It will filter by KIND if non nil."
   5957   (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind)))
   5958 
   5959 (defun lsp-execute-code-action-by-kind (command-kind)
   5960   "Execute code action by COMMAND-KIND."
   5961   (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind)
   5962                         (-filter (-lambda ((&CodeAction :kind?))
   5963                                    (and kind? (s-prefix? command-kind kind?))))
   5964                         lsp--select-action)))
   5965       (lsp-execute-code-action action)
   5966     (signal 'lsp-no-code-actions '(command-kind))))
   5967 
   5968 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point)
   5969 
   5970 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?))
   5971   "Parse and execute a code ACTION represented as a Command LSP type."
   5972   (let ((server-id (->> (lsp-workspaces)
   5973                         (cl-first)
   5974                         (or lsp--cur-workspace)
   5975                         (lsp--workspace-client)
   5976                         (lsp--client-server-id))))
   5977     (condition-case nil
   5978         (with-no-warnings
   5979           (lsp-execute-command server-id (intern command) arguments?))
   5980       (cl-no-applicable-method
   5981        (if-let ((action-handler (lsp--find-action-handler command)))
   5982            (funcall action-handler action)
   5983          (lsp-send-execute-command command arguments?))))))
   5984 
   5985 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?))
   5986   "Execute code action ACTION. For example, when text under the
   5987 caret has a suggestion to apply a fix from an lsp-server, calling
   5988 this function will do so.
   5989 If ACTION is not set it will be selected from `lsp-code-actions-at-point'.
   5990 Request codeAction/resolve for more info if server supports."
   5991   (interactive (list (lsp--select-action (lsp-code-actions-at-point))))
   5992   (if (and (lsp-feature? "codeAction/resolve")
   5993            (not command?)
   5994            (not edit?))
   5995       (lsp--execute-code-action (lsp-request "codeAction/resolve" action))
   5996     (lsp--execute-code-action action)))
   5997 
   5998 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?))
   5999   "Execute code action ACTION."
   6000   (when edit?
   6001     (lsp--apply-workspace-edit edit? 'code-action))
   6002 
   6003   (cond
   6004    ((stringp command?) (lsp--execute-command action))
   6005    ((lsp-command? command?) (lsp--execute-command command?))))
   6006 
   6007 (defvar lsp--formatting-indent-alist
   6008   ;; Taken from `dtrt-indent-mode'
   6009   '(
   6010     (ada-mode                   . ada-indent)                       ; Ada
   6011     (ada-ts-mode                . ada-ts-mode-indent-offset)
   6012     (c++-mode                   . c-basic-offset)                   ; C++
   6013     (c++-ts-mode                . c-ts-mode-indent-offset)
   6014     (c-mode                     . c-basic-offset)                   ; C
   6015     (c-ts-mode                  . c-ts-mode-indent-offset)
   6016     (cperl-mode                 . cperl-indent-level)               ; Perl
   6017     (crystal-mode               . crystal-indent-level)             ; Crystal (Ruby)
   6018     (csharp-mode                . c-basic-offset)                   ; C#
   6019     (csharp-tree-sitter-mode    . csharp-tree-sitter-indent-offset) ; C#
   6020     (csharp-ts-mode             . csharp-ts-mode-indent-offset)     ; C# (tree-sitter, Emacs29)
   6021     (css-mode                   . css-indent-offset)                ; CSS
   6022     (d-mode                     . c-basic-offset)                   ; D
   6023     (enh-ruby-mode              . enh-ruby-indent-level)            ; Ruby
   6024     (erlang-mode                . erlang-indent-level)              ; Erlang
   6025     (ess-mode                   . ess-indent-offset)                ; ESS (R)
   6026     (go-ts-mode                 . go-ts-mode-indent-offset)
   6027     (gpr-mode                   . gpr-indent-offset)                ; GNAT Project
   6028     (gpr-ts-mode                . gpr-ts-mode-indent-offset)
   6029     (hack-mode                  . hack-indent-offset)               ; Hack
   6030     (java-mode                  . c-basic-offset)                   ; Java
   6031     (java-ts-mode               . java-ts-mode-indent-offset)
   6032     (jde-mode                   . c-basic-offset)                   ; Java (JDE)
   6033     (js-mode                    . js-indent-level)                  ; JavaScript
   6034     (js-ts-mode                 . js-indent-level)
   6035     (js2-mode                   . js2-basic-offset)                 ; JavaScript-IDE
   6036     (js3-mode                   . js3-indent-level)                 ; JavaScript-IDE
   6037     (json-mode                  . js-indent-level)                  ; JSON
   6038     (json-ts-mode               . json-ts-mode-indent-offset)
   6039     (lua-mode                   . lua-indent-level)                 ; Lua
   6040     (lua-ts-mode                . lua-ts-indent-offset)
   6041     (nxml-mode                  . nxml-child-indent)                ; XML
   6042     (objc-mode                  . c-basic-offset)                   ; Objective C
   6043     (pascal-mode                . pascal-indent-level)              ; Pascal
   6044     (perl-mode                  . perl-indent-level)                ; Perl
   6045     (php-mode                   . c-basic-offset)                   ; PHP
   6046     (php-ts-mode                . php-ts-mode-indent-offset)        ; PHP
   6047     (powershell-mode            . powershell-indent)                ; PowerShell
   6048     (powershell-ts-mode         . powershell-ts-mode-indent-offset) ; PowerShell
   6049     (raku-mode                  . raku-indent-offset)               ; Perl6/Raku
   6050     (ruby-mode                  . ruby-indent-level)                ; Ruby
   6051     (rust-mode                  . rust-indent-offset)               ; Rust
   6052     (rust-ts-mode               . rust-ts-mode-indent-offset)
   6053     (rustic-mode                . rustic-indent-offset)             ; Rust
   6054     (scala-mode                 . scala-indent:step)                ; Scala
   6055     (sgml-mode                  . sgml-basic-offset)                ; SGML
   6056     (sh-mode                    . sh-basic-offset)                  ; Shell Script
   6057     (toml-ts-mode               . toml-ts-mode-indent-offset)
   6058     (typescript-mode            . typescript-indent-level)          ; Typescript
   6059     (typescript-ts-mode         . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29)
   6060     (yaml-mode                  . yaml-indent-offset)               ; YAML
   6061     (yang-mode                  . c-basic-offset)                   ; YANG (yang-mode)
   6062 
   6063     (default                    . standard-indent))                 ; default fallback
   6064   "A mapping from `major-mode' to its indent variable.")
   6065 
   6066 (defun lsp--get-indent-width (mode)
   6067   "Get indentation offset for MODE."
   6068   (or (alist-get mode lsp--formatting-indent-alist)
   6069       (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default))))
   6070 
   6071 (defun lsp--make-document-formatting-params ()
   6072   "Create document formatting params."
   6073   (lsp-make-document-formatting-params
   6074    :text-document (lsp--text-document-identifier)
   6075    :options (lsp-make-formatting-options
   6076              :tab-size (symbol-value (lsp--get-indent-width major-mode))
   6077              :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   6078              :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   6079              :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   6080              :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))))
   6081 
   6082 (defun lsp-format-buffer ()
   6083   "Ask the server to format this document."
   6084   (interactive "*")
   6085   (cond ((lsp-feature? "textDocument/formatting")
   6086          (let ((edits (lsp-request "textDocument/formatting"
   6087                                    (lsp--make-document-formatting-params))))
   6088            (if (seq-empty-p edits)
   6089                (lsp--info "No formatting changes provided")
   6090              (lsp--apply-text-edits edits 'format))))
   6091         ((lsp-feature? "textDocument/rangeFormatting")
   6092          (save-restriction
   6093            (widen)
   6094            (lsp-format-region (point-min) (point-max))))
   6095         (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider")))))
   6096 
   6097 (defun lsp-format-region (s e)
   6098   "Ask the server to format the region, or if none is selected, the current line."
   6099   (interactive "r")
   6100   (let ((edits (lsp-request
   6101                 "textDocument/rangeFormatting"
   6102                 (lsp--make-document-range-formatting-params s e))))
   6103     (if (seq-empty-p edits)
   6104         (lsp--info "No formatting changes provided")
   6105       (lsp--apply-text-edits edits 'format))))
   6106 
   6107 (defmacro lsp-make-interactive-code-action (func-name code-action-kind)
   6108   "Define an interactive function FUNC-NAME that attempts to
   6109 execute a CODE-ACTION-KIND action."
   6110   `(defun ,(intern (concat "lsp-" (symbol-name func-name))) ()
   6111      ,(format "Perform the %s code action, if available." code-action-kind)
   6112      (interactive)
   6113      ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to
   6114      ;; auto-execute here: the user has specified exactly what they want.
   6115      (let ((lsp-auto-execute-action t))
   6116        (condition-case nil
   6117            (lsp-execute-code-action-by-kind ,code-action-kind)
   6118          (lsp-no-code-actions
   6119           (when (called-interactively-p 'any)
   6120             (lsp--info ,(format "%s action not available" code-action-kind))))))))
   6121 
   6122 (lsp-make-interactive-code-action organize-imports "source.organizeImports")
   6123 
   6124 (defun lsp--make-document-range-formatting-params (start end)
   6125   "Make DocumentRangeFormattingParams for selected region."
   6126   (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params)
   6127                                                   (lsp--region-to-range start end)))
   6128 
   6129 (defconst lsp--highlight-kind-face
   6130   '((1 . lsp-face-highlight-textual)
   6131     (2 . lsp-face-highlight-read)
   6132     (3 . lsp-face-highlight-write)))
   6133 
   6134 (defun lsp--remove-overlays (name)
   6135   (save-restriction
   6136     (widen)
   6137     (remove-overlays (point-min) (point-max) name t)))
   6138 
   6139 (defun lsp-document-highlight ()
   6140   "Highlight all relevant references to the symbol under point."
   6141   (interactive)
   6142   (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights
   6143   (setq lsp--have-document-highlights nil
   6144         lsp--symbol-bounds-of-last-highlight-invocation nil)
   6145   (let ((lsp-enable-symbol-highlighting t))
   6146     (lsp--document-highlight)))
   6147 
   6148 (defun lsp--document-highlight-callback (highlights)
   6149   "Create a callback to process the reply of a
   6150 `textDocument/documentHighlight' message for the buffer BUF.
   6151 A reference is highlighted only if it is visible in a window."
   6152   (lsp--remove-overlays 'lsp-highlight)
   6153 
   6154   (let* ((wins-visible-pos (-map (lambda (win)
   6155                                    (cons (1- (line-number-at-pos (window-start win) t))
   6156                                          (1+ (line-number-at-pos (window-end win) t))))
   6157                                  (get-buffer-window-list nil nil 'visible))))
   6158     (setq lsp--have-document-highlights t)
   6159     (-map
   6160      (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line)
   6161                                                   :end (end &as &Position :line end-line))
   6162                                    :kind?))
   6163        (-map
   6164         (-lambda ((start-window . end-window))
   6165           ;; Make the overlay only if the reference is visible
   6166           (let ((start-point (lsp--position-to-point start))
   6167                 (end-point (lsp--position-to-point end)))
   6168             (when (and (> (1+ start-line) start-window)
   6169                        (< (1+ end-line) end-window)
   6170                        (not (and lsp-symbol-highlighting-skip-current
   6171                                  (<= start-point (point) end-point))))
   6172               (-doto (make-overlay start-point end-point)
   6173                 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face)))
   6174                 (overlay-put 'lsp-highlight t)))))
   6175         wins-visible-pos))
   6176      highlights)))
   6177 
   6178 (defcustom lsp-symbol-kinds
   6179   '((1 . "File")
   6180     (2 . "Module")
   6181     (3 . "Namespace")
   6182     (4 . "Package")
   6183     (5 . "Class")
   6184     (6 . "Method")
   6185     (7 . "Property")
   6186     (8 . "Field")
   6187     (9 . "Constructor")
   6188     (10 . "Enum")
   6189     (11 . "Interface")
   6190     (12 . "Function")
   6191     (13 . "Variable")
   6192     (14 . "Constant")
   6193     (15 . "String")
   6194     (16 . "Number")
   6195     (17 . "Boolean")
   6196     (18 . "Array")
   6197     (19 . "Object")
   6198     (20 . "Key")
   6199     (21 . "Null")
   6200     (22 . "Enum Member")
   6201     (23 . "Struct")
   6202     (24 . "Event")
   6203     (25 . "Operator")
   6204     (26 . "Type Parameter"))
   6205   "Alist mapping SymbolKinds to human-readable strings.
   6206 Various Symbol objects in the LSP protocol have an integral type,
   6207 specifying what they are. This alist maps such type integrals to
   6208 readable representations of them. See
   6209 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/',
   6210 namespace SymbolKind."
   6211   :group 'lsp-mode
   6212   :type '(alist :key-type integer :value-type string))
   6213 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds)
   6214 
   6215 (lsp-defun lsp--symbol-information-to-xref
   6216   ((&SymbolInformation :kind :name
   6217                        :location (&Location :uri :range (&Range :start
   6218                                                                 (&Position :line :character)))))
   6219   "Return a `xref-item' from SYMBOL information."
   6220   (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name)
   6221              (xref-make-file-location (lsp--uri-to-path uri)
   6222                                       line
   6223                                       character)))
   6224 
   6225 (defun lsp--get-document-symbols ()
   6226   "Get document symbols.
   6227 
   6228 If the buffer has not been modified since symbols were last
   6229 retrieved, simply return the latest result.
   6230 
   6231 Else, if the request was initiated by Imenu updating its menu-bar
   6232 entry, perform it asynchronously; i.e., give Imenu the latest
   6233 result and then force a refresh when a new one is available.
   6234 
   6235 Else (e.g., due to interactive use of `imenu' or `xref'),
   6236 perform the request synchronously."
   6237   (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick)
   6238       lsp--document-symbols
   6239     (let ((method "textDocument/documentSymbol")
   6240           (params `(:textDocument ,(lsp--text-document-identifier)))
   6241           (tick (buffer-chars-modified-tick)))
   6242       (if (not lsp--document-symbols-request-async)
   6243           (prog1
   6244               (setq lsp--document-symbols (lsp-request method params))
   6245             (setq lsp--document-symbols-tick tick))
   6246         (lsp-request-async method params
   6247                            (lambda (document-symbols)
   6248                              (setq lsp--document-symbols document-symbols
   6249                                    lsp--document-symbols-tick tick)
   6250                              (lsp--imenu-refresh))
   6251                            :mode 'alive
   6252                            :cancel-token :document-symbols)
   6253         lsp--document-symbols))))
   6254 
   6255 (advice-add 'imenu-update-menubar :around
   6256             (lambda (oldfun &rest r)
   6257               (let ((lsp--document-symbols-request-async t))
   6258                 (apply oldfun r))))
   6259 
   6260 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position)
   6261   "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION."
   6262   (-let (((symbol &as &DocumentSymbol? :children?)
   6263           (seq-find (-lambda ((&DocumentSymbol :range))
   6264                       (lsp-point-in-range? current-position range))
   6265                     document-symbols)))
   6266     (if children?
   6267         (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position))
   6268       (when symbol
   6269         (list symbol)))))
   6270 
   6271 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?))
   6272   "Convert a SymbolInformation to a DocumentInformation"
   6273   (lsp-make-document-symbol :name name
   6274                             :kind kind
   6275                             :range (lsp:location-range location)
   6276                             :children? nil
   6277                             :deprecated? deprecated?
   6278                             :selection-range (lsp:location-range location)
   6279                             :detail? container-name?))
   6280 
   6281 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position)
   6282   "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION."
   6283   (--> symbols-informations
   6284     (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range)))
   6285              (when (lsp-point-in-range? current-position range)
   6286                (lsp--symbol-information->document-symbol symbol)))
   6287            it)
   6288     (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position))
   6289                        (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position)))
   6290                (and (lsp--position-compare b-start-position a-start-position)
   6291                     (lsp--position-compare a-end-position b-end-position))))))
   6292 
   6293 (defun lsp--symbols->document-symbols-hierarchy (symbols)
   6294   "Convert SYMBOLS to symbols-hierarchy."
   6295   (when-let ((first-symbol (lsp-seq-first symbols)))
   6296     (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line)
   6297                                            :character (plist-get (lsp--cur-position) :character))))
   6298       (if (lsp-symbol-information? first-symbol)
   6299           (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position)
   6300         (lsp--document-symbols->document-symbols-hierarchy symbols cur-position)))))
   6301 
   6302 (defun lsp--xref-backend () 'xref-lsp)
   6303 
   6304 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp)))
   6305   (propertize (or (thing-at-point 'symbol) "")
   6306               'identifier-at-point t))
   6307 
   6308 (defun lsp--xref-elements-index (symbols path)
   6309   (-mapcat
   6310    (-lambda (sym)
   6311      (pcase-exhaustive sym
   6312        ((DocumentSymbol :name :children? :selection-range (Range :start))
   6313         (cons (cons (concat path name)
   6314                     (lsp--position-to-point start))
   6315               (lsp--xref-elements-index children? (concat path name " / "))))
   6316        ((SymbolInformation :name :location (Location :range (Range :start)))
   6317         (list (cons (concat path name)
   6318                     (lsp--position-to-point start))))))
   6319    symbols))
   6320 
   6321 (defvar-local lsp--symbols-cache nil)
   6322 
   6323 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp)))
   6324   (if (lsp--find-workspaces-for "textDocument/documentSymbol")
   6325       (progn
   6326         (setq lsp--symbols-cache (lsp--xref-elements-index
   6327                                   (lsp--get-document-symbols) nil))
   6328         lsp--symbols-cache)
   6329     (list (propertize (or (thing-at-point 'symbol) "")
   6330                       'identifier-at-point t))))
   6331 
   6332 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier)
   6333   (save-excursion
   6334     (unless (get-text-property 0 'identifier-at-point identifier)
   6335       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6336                               (user-error "Unable to find symbol %s in current document" identifier)))))
   6337     (lsp--locations-to-xref-items (lsp-request "textDocument/definition"
   6338                                                (lsp--text-document-position-params)))))
   6339 
   6340 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier)
   6341   (save-excursion
   6342     (unless (get-text-property 0 'identifier-at-point identifier)
   6343       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6344                               (user-error "Unable to find symbol %s" identifier)))))
   6345     (lsp--locations-to-xref-items (lsp-request "textDocument/references"
   6346                                                (lsp--make-reference-params nil lsp-references-exclude-definition)))))
   6347 
   6348 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern)
   6349   (seq-map #'lsp--symbol-information-to-xref
   6350            (lsp-request "workspace/symbol" `(:query ,pattern))))
   6351 
   6352 (defcustom lsp-rename-use-prepare t
   6353   "Whether `lsp-rename' should do a prepareRename first.
   6354 For some language servers, textDocument/prepareRename might be
   6355 too slow, in which case this variable may be set to nil.
   6356 `lsp-rename' will then use `thing-at-point' `symbol' to determine
   6357 the symbol to rename at point."
   6358   :group 'lsp-mode
   6359   :type 'boolean)
   6360 
   6361 (defun lsp--get-symbol-to-rename ()
   6362   "Get a symbol to rename and placeholder at point.
   6363 Returns a cons ((START . END) . PLACEHOLDER?), and nil if
   6364 renaming is generally supported but cannot be done at point.
   6365 START and END are the bounds of the identifiers being renamed,
   6366 while PLACEHOLDER?, is either nil or a string suggested by the
   6367 language server as the initial input of a new-name prompt."
   6368   (unless (lsp-feature? "textDocument/rename")
   6369     (error "The connected server(s) doesn't support renaming"))
   6370   (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename"))
   6371       (when-let ((response
   6372                   (lsp-request "textDocument/prepareRename"
   6373                                (lsp--text-document-position-params))))
   6374         (let* ((bounds (lsp--range-to-region
   6375                         (if (lsp-range? response)
   6376                             response
   6377                           (lsp:prepare-rename-result-range response))))
   6378                (placeholder
   6379                 (and (not (lsp-range? response))
   6380                      (lsp:prepare-rename-result-placeholder response))))
   6381           (cons bounds placeholder)))
   6382     (when-let ((bounds (bounds-of-thing-at-point 'symbol)))
   6383       (cons bounds nil))))
   6384 
   6385 (defface lsp-face-rename '((t :underline t))
   6386   "Face used to highlight the identifier being renamed.
   6387 Renaming can be done using `lsp-rename'."
   6388   :group 'lsp-mode)
   6389 
   6390 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face))
   6391   "Face used to display the rename placeholder in.
   6392 When calling `lsp-rename' interactively, this will be the face of
   6393 the new name."
   6394   :group 'lsp-mode)
   6395 
   6396 (defvar lsp-rename-history '()
   6397   "History for `lsp--read-rename'.")
   6398 
   6399 (defun lsp--read-rename (at-point)
   6400   "Read a new name for a `lsp-rename' at `point' from the user.
   6401 AT-POINT shall be a structure as returned by
   6402 `lsp--get-symbol-to-rename'.
   6403 
   6404 Returns a string, which should be the new name for the identifier
   6405 at point. If renaming cannot be done at point (as determined from
   6406 AT-POINT), throw a `user-error'.
   6407 
   6408 This function is for use in `lsp-rename' only, and shall not be
   6409 relied upon."
   6410   (unless at-point
   6411     (user-error "`lsp-rename' is invalid here"))
   6412   (-let* ((((start . end) . placeholder?) at-point)
   6413           ;; Do the `buffer-substring' first to not include `lsp-face-rename'
   6414           (rename-me (buffer-substring start end))
   6415           (placeholder (or placeholder? rename-me))
   6416           (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face))
   6417 
   6418           overlay)
   6419     ;; We need unwind protect, as the user might cancel here, causing the
   6420     ;; overlay to linger.
   6421     (unwind-protect
   6422         (progn
   6423           (setq overlay (make-overlay start end))
   6424           (overlay-put overlay 'face 'lsp-face-rename)
   6425 
   6426           (read-string (format "Rename %s to: " rename-me) placeholder
   6427                        'lsp-rename-history))
   6428       (and overlay (delete-overlay overlay)))))
   6429 
   6430 (defun lsp-rename (newname)
   6431   "Rename the symbol (and all references to it) under point to NEWNAME."
   6432   (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename))))
   6433   (when-let ((edits (lsp-request "textDocument/rename"
   6434                                  `( :textDocument ,(lsp--text-document-identifier)
   6435                                     :position ,(lsp--cur-position)
   6436                                     :newName ,newname))))
   6437     (lsp--apply-workspace-edit edits 'rename)))
   6438 
   6439 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?)
   6440   "Advice around function `rename-file'.
   6441 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?.
   6442 
   6443 This advice sends workspace/willRenameFiles before renaming file
   6444 to check if server wants to apply any workspaceEdits after renamed."
   6445   (if (and lsp-apply-edits-after-file-operations
   6446            (lsp--send-will-rename-files-p old-name))
   6447       (let ((params (lsp-make-rename-files-params
   6448                      :files (vector (lsp-make-file-rename
   6449                                      :oldUri (lsp--path-to-uri old-name)
   6450                                      :newUri (lsp--path-to-uri new-name))))))
   6451         (when-let ((edits (lsp-request "workspace/willRenameFiles" params)))
   6452           (lsp--apply-workspace-edit edits 'rename-file)
   6453           (funcall old-func old-name new-name ok-if-already-exists?)
   6454           (when (lsp--send-did-rename-files-p)
   6455             (lsp-notify "workspace/didRenameFiles" params))))
   6456     (funcall old-func old-name new-name ok-if-already-exists?)))
   6457 
   6458 (advice-add 'rename-file :around #'lsp--on-rename-file)
   6459 
   6460 (defcustom lsp-xref-force-references nil
   6461   "If non-nil threat everything as references(e. g. jump if only one item.)"
   6462   :group 'lsp-mode
   6463   :type 'boolean)
   6464 
   6465 (defun lsp-show-xrefs (xrefs display-action references?)
   6466   (unless (region-active-p) (push-mark nil t))
   6467   (if (boundp 'xref-show-definitions-function)
   6468       (with-no-warnings
   6469         (xref-push-marker-stack)
   6470         (funcall (if (and references? (not lsp-xref-force-references))
   6471                      xref-show-xrefs-function
   6472                    xref-show-definitions-function)
   6473                  (-const xrefs)
   6474                  `((window . ,(selected-window))
   6475                    (display-action . ,display-action)
   6476                    ,(if (and references? (not lsp-xref-force-references))
   6477                         `(auto-jump . ,xref-auto-jump-to-first-xref)
   6478                       `(auto-jump . ,xref-auto-jump-to-first-definition)))))
   6479     (xref--show-xrefs xrefs display-action)))
   6480 
   6481 (cl-defmethod seq-empty-p ((ht hash-table))
   6482   "Function `seq-empty-p' for hash-table."
   6483   (hash-table-empty-p ht))
   6484 
   6485 (cl-defun lsp-find-locations (method &optional extra &key display-action references?)
   6486   "Send request named METHOD and get cross references of the symbol under point.
   6487 EXTRA is a plist of extra parameters.
   6488 REFERENCES? t when METHOD returns references."
   6489   (let ((loc (lsp-request method
   6490                           (append (lsp--text-document-position-params) extra))))
   6491     (if (seq-empty-p loc)
   6492         (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) ""))
   6493       (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?))))
   6494 
   6495 (cl-defun lsp-find-declaration (&key display-action)
   6496   "Find declarations of the symbol under point."
   6497   (interactive)
   6498   (lsp-find-locations "textDocument/declaration" nil :display-action display-action))
   6499 
   6500 (cl-defun lsp-find-definition (&key display-action)
   6501   "Find definitions of the symbol under point."
   6502   (interactive)
   6503   (lsp-find-locations "textDocument/definition" nil :display-action display-action))
   6504 
   6505 (defun lsp-find-definition-mouse (click)
   6506   "Click to start `lsp-find-definition' at clicked point."
   6507   (interactive "e")
   6508   (let* ((ec (event-start click))
   6509          (p1 (posn-point ec))
   6510          (w1 (posn-window ec)))
   6511     (select-window w1)
   6512     (goto-char p1)
   6513     (lsp-find-definition)))
   6514 
   6515 (cl-defun lsp-find-implementation (&key display-action)
   6516   "Find implementations of the symbol under point."
   6517   (interactive)
   6518   (lsp-find-locations "textDocument/implementation"
   6519                       nil
   6520                       :display-action display-action
   6521                       :references? t))
   6522 
   6523 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action)
   6524   "Find references of the symbol under point."
   6525   (interactive "P")
   6526   (lsp-find-locations "textDocument/references"
   6527                       (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition)))))
   6528                       :display-action display-action
   6529                       :references? t))
   6530 
   6531 (cl-defun lsp-find-type-definition (&key display-action)
   6532   "Find type definitions of the symbol under point."
   6533   (interactive)
   6534   (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action))
   6535 
   6536 (defalias 'lsp-find-custom #'lsp-find-locations)
   6537 (defalias 'lsp-goto-implementation #'lsp-find-implementation)
   6538 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition)
   6539 
   6540 (with-eval-after-load 'evil
   6541   (evil-set-command-property 'lsp-find-definition :jump t)
   6542   (evil-set-command-property 'lsp-find-implementation :jump t)
   6543   (evil-set-command-property 'lsp-find-references :jump t)
   6544   (evil-set-command-property 'lsp-find-type-definition :jump t))
   6545 
   6546 (defun lsp--workspace-method-supported? (check-command method capability workspace)
   6547   (with-lsp-workspace workspace
   6548     (if check-command
   6549         (funcall check-command workspace)
   6550       (or
   6551        (when capability (lsp--capability capability))
   6552        (lsp--registered-capability method)
   6553        (and (not capability) (not check-command))))))
   6554 
   6555 (defun lsp-disable-method-for-server (method server-id)
   6556   "Disable METHOD for SERVER-ID."
   6557   (cl-callf
   6558       (lambda (reqs)
   6559         (-let (((&plist :check-command :capability) reqs))
   6560           (list :check-command
   6561                 (lambda (workspace)
   6562                   (unless (-> workspace
   6563                               lsp--workspace-client
   6564                               lsp--client-server-id
   6565                               (eq server-id))
   6566                     (lsp--workspace-method-supported? check-command
   6567                                                       method
   6568                                                       capability
   6569                                                       workspace))))))
   6570       (alist-get method lsp-method-requirements nil nil 'string=)))
   6571 
   6572 (defun lsp--find-workspaces-for (msg-or-method)
   6573   "Find all workspaces in the current project that can handle MSG."
   6574   (let ((method (if (stringp msg-or-method)
   6575                     msg-or-method
   6576                   (plist-get msg-or-method :method))))
   6577     (-if-let (reqs (cdr (assoc method lsp-method-requirements)))
   6578         (-let (((&plist :capability :check-command) reqs))
   6579           (-filter
   6580            (-partial #'lsp--workspace-method-supported?
   6581                      check-command method capability)
   6582            (lsp-workspaces)))
   6583       (lsp-workspaces))))
   6584 
   6585 (defun lsp-can-execute-command? (command-name)
   6586   "Returns non-nil if current language server(s) can execute COMMAND-NAME.
   6587 The command is executed via `workspace/executeCommand'"
   6588   (cl-position
   6589    command-name
   6590    (lsp:execute-command-options-commands
   6591     (lsp:server-capabilities-execute-command-provider?
   6592      (lsp--server-capabilities)))
   6593    :test #'equal))
   6594 
   6595 (defalias 'lsp-feature? 'lsp--find-workspaces-for)
   6596 
   6597 (cl-defmethod lsp-execute-command (_server _command _arguments)
   6598   "Dispatch COMMAND execution."
   6599   (signal 'cl-no-applicable-method nil))
   6600 
   6601 (defun lsp-workspace-command-execute (command &optional args)
   6602   "Execute workspace COMMAND with ARGS."
   6603   (condition-case-unless-debug err
   6604       (let ((params (if args
   6605                         (list :command command :arguments args)
   6606                       (list :command command))))
   6607         (lsp-request "workspace/executeCommand" params))
   6608     (error
   6609      (error "`workspace/executeCommand' with `%s' failed.\n\n%S"
   6610             command err))))
   6611 
   6612 (defun lsp-send-execute-command (command &optional args)
   6613   "Create and send a `workspace/executeCommand' message having command COMMAND
   6614 and optional ARGS."
   6615   (lsp-workspace-command-execute command args))
   6616 
   6617 (defalias 'lsp-point-to-position #'lsp--point-to-position)
   6618 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier)
   6619 (defalias 'lsp--send-execute-command #'lsp-send-execute-command)
   6620 (defalias 'lsp-on-open #'lsp--text-document-did-open)
   6621 (defalias 'lsp-on-save #'lsp--text-document-did-save)
   6622 
   6623 (defun lsp--set-configuration (settings)
   6624   "Set the SETTINGS for the lsp server."
   6625   (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings)))
   6626 
   6627 (defun lsp-current-buffer ()
   6628   (or lsp--virtual-buffer
   6629       (current-buffer)))
   6630 
   6631 (defun lsp-buffer-live-p (buffer-id)
   6632   (if-let ((buffer-live (plist-get buffer-id :buffer-live?)))
   6633       (funcall buffer-live buffer-id)
   6634     (buffer-live-p buffer-id)))
   6635 
   6636 (defun lsp--on-set-visited-file-name (old-func &rest args)
   6637   "Advice around function `set-visited-file-name'.
   6638 
   6639 This advice sends textDocument/didClose for the old file and
   6640 textDocument/didOpen for the new file."
   6641   (when lsp--cur-workspace
   6642     (lsp--text-document-did-close t))
   6643   (prog1 (apply old-func args)
   6644     (when lsp--cur-workspace
   6645       (lsp--text-document-did-open))))
   6646 
   6647 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name)
   6648 
   6649 (defvar lsp--flushing-delayed-changes nil)
   6650 
   6651 (defun lsp--send-no-wait (message proc)
   6652   "Send MESSAGE to PROC without waiting for further output."
   6653 
   6654   (unless lsp--flushing-delayed-changes
   6655     (let ((lsp--flushing-delayed-changes t))
   6656       (lsp--flush-delayed-changes)))
   6657   (lsp-process-send proc message))
   6658 
   6659 (define-error 'lsp-parse-error
   6660   "Error parsing message from language server" 'lsp-error)
   6661 (define-error 'lsp-unknown-message-type
   6662   "Unknown message type" '(lsp-error lsp-parse-error))
   6663 (define-error 'lsp-unknown-json-rpc-version
   6664   "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error))
   6665 (define-error 'lsp-no-content-length
   6666   "Content-Length header missing in message" '(lsp-error lsp-parse-error))
   6667 (define-error 'lsp-invalid-header-name
   6668   "Invalid header name" '(lsp-error lsp-parse-error))
   6669 
   6670 ;;  id  method
   6671 ;;   x    x     request
   6672 ;;   x    .     response
   6673 ;;   .    x     notification
   6674 (defun lsp--get-message-type (json-data)
   6675   "Get the message type from JSON-DATA."
   6676   (if (lsp:json-message-id? json-data)
   6677       (if (lsp:json-message-error? json-data)
   6678           'response-error
   6679         (if (lsp:json-message-method? json-data)
   6680             'request
   6681           'response))
   6682     'notification))
   6683 
   6684 (defconst lsp--default-notification-handlers
   6685   (ht ("window/showMessage" #'lsp--window-show-message)
   6686       ("window/logMessage" #'lsp--window-log-message)
   6687       ("window/showInputBox" #'lsp--window-show-input-box)
   6688       ("window/showQuickPick" #'lsp--window-show-quick-pick)
   6689       ("textDocument/publishDiagnostics" #'lsp--on-diagnostics)
   6690       ("textDocument/diagnosticsEnd" #'ignore)
   6691       ("textDocument/diagnosticsBegin" #'ignore)
   6692       ("telemetry/event" #'ignore)
   6693       ("$/progress" (lambda (workspace params)
   6694                       (funcall lsp-progress-function workspace params)))))
   6695 
   6696 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method))
   6697   "Call the appropriate handler for NOTIFICATION."
   6698   (-let ((client (lsp--workspace-client workspace)))
   6699     (when (lsp--log-io-p method)
   6700       (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif)
   6701                           lsp--cur-workspace))
   6702     (if-let ((handler (or (gethash method (lsp--client-notification-handlers client))
   6703                           (gethash method lsp--default-notification-handlers))))
   6704         (funcall handler workspace params)
   6705       (when (and method (not (string-prefix-p "$" method)))
   6706         (lsp-warn "Unknown notification: %s" method)))))
   6707 
   6708 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items))
   6709   "Get section configuration.
   6710 PARAMS are the `workspace/configuration' request params"
   6711   (->> items
   6712        (-map (-lambda ((&ConfigurationItem :section?))
   6713                (-let* ((path-parts (split-string section? "\\."))
   6714                        (path-without-last (s-join "." (-slice path-parts 0 -1)))
   6715                        (path-parts-len (length path-parts)))
   6716                  (cond
   6717                   ((<= path-parts-len 1)
   6718                    (ht-get (lsp-configuration-section section?)
   6719                            (car-safe path-parts)
   6720                            (ht-create)))
   6721                   ((> path-parts-len 1)
   6722                    (when-let ((section (lsp-configuration-section path-without-last))
   6723                               (keys path-parts))
   6724                      (while (and keys section)
   6725                        (setf section (ht-get section (pop keys))))
   6726                      section))))))
   6727        (apply #'vector)))
   6728 
   6729 (defun lsp--ms-since (timestamp)
   6730   "Integer number of milliseconds since TIMESTAMP.  Fractions discarded."
   6731   (floor (* 1000 (float-time (time-since timestamp)))))
   6732 
   6733 (defun lsp--send-request-response (workspace recv-time request response)
   6734   "Send the RESPONSE for REQUEST in WORKSPACE and log if needed."
   6735   (-let* (((&JSONResponse :params :method :id) request)
   6736           (process (lsp--workspace-proc workspace))
   6737           (response (lsp--make-response id response))
   6738           (req-entry (and lsp-log-io
   6739                           (lsp--make-log-entry method id params 'incoming-req)))
   6740           (resp-entry (and lsp-log-io
   6741                            (lsp--make-log-entry method id response 'outgoing-resp
   6742                                                 (lsp--ms-since recv-time)))))
   6743     ;; Send response to the server.
   6744     (when (lsp--log-io-p method)
   6745       (lsp--log-entry-new req-entry workspace)
   6746       (lsp--log-entry-new resp-entry workspace))
   6747     (lsp--send-no-wait response process)))
   6748 
   6749 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method))
   6750   "Call the appropriate handler for REQUEST, and send the return value to the
   6751 server. WORKSPACE is the active workspace."
   6752   (-let* ((recv-time (current-time))
   6753           (client (lsp--workspace-client workspace))
   6754           (buffers (lsp--workspace-buffers workspace))
   6755           handler
   6756           (response (cond
   6757                      ((setq handler (gethash method (lsp--client-request-handlers client) nil))
   6758                       (funcall handler workspace params))
   6759                      ((setq handler (gethash method (lsp--client-async-request-handlers client) nil))
   6760                       (funcall handler workspace params
   6761                                (-partial #'lsp--send-request-response
   6762                                          workspace recv-time request))
   6763                       'delay-response)
   6764                      ((equal method "client/registerCapability")
   6765                       (mapc #'lsp--server-register-capability
   6766                             (lsp:registration-params-registrations params))
   6767                       (mapc (lambda (buf)
   6768                               (when (lsp-buffer-live-p buf)
   6769                                 (lsp-with-current-buffer buf
   6770                                   (lsp-unconfig-buffer)
   6771                                   (lsp-configure-buffer))))
   6772                             buffers)
   6773                       nil)
   6774                      ((equal method "window/showMessageRequest")
   6775                       (let ((choice (lsp--window-log-message-request params)))
   6776                         `(:title ,choice)))
   6777                      ((equal method "window/showDocument")
   6778                       (let ((success? (lsp--window-show-document params)))
   6779                         (lsp-make-show-document-result :success (or success?
   6780                                                                     :json-false))))
   6781                      ((equal method "client/unregisterCapability")
   6782                       (mapc #'lsp--server-unregister-capability
   6783                             (lsp:unregistration-params-unregisterations params))
   6784                       (mapc (lambda (buf)
   6785                               (when (lsp-buffer-live-p buf)
   6786                                 (lsp-with-current-buffer buf
   6787                                   (lsp-unconfig-buffer)
   6788                                   (lsp-configure-buffer))))
   6789                             buffers)
   6790                       nil)
   6791                      ((equal method "workspace/applyEdit")
   6792                       (list :applied (condition-case err
   6793                                          (prog1 t
   6794                                            (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested))
   6795                                        (error
   6796                                         (lsp--error "Failed to apply edits with message %s"
   6797                                                     (error-message-string err))
   6798                                         :json-false))))
   6799                      ((equal method "workspace/configuration")
   6800                       (with-lsp-workspace workspace
   6801                         (if-let ((buf (car buffers)))
   6802                             (lsp-with-current-buffer buf
   6803                               (lsp--build-workspace-configuration-response params))
   6804                           (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace)
   6805                             (lsp--build-workspace-configuration-response params)))))
   6806                      ((equal method "workspace/workspaceFolders")
   6807                       (let ((folders (or (-> workspace
   6808                                              (lsp--workspace-client)
   6809                                              (lsp--client-server-id)
   6810                                              (gethash (lsp-session-server-id->folders (lsp-session))))
   6811                                          (lsp-session-folders (lsp-session)))))
   6812                         (->> folders
   6813                              (-distinct)
   6814                              (-map (lambda (folder)
   6815                                      (list :uri (lsp--path-to-uri folder))))
   6816                              (apply #'vector))))
   6817                      ((equal method "window/workDoneProgress/create")
   6818                       nil ;; no specific reply, no processing required
   6819                       )
   6820                      ((equal method "workspace/semanticTokens/refresh")
   6821                       (when (and lsp-semantic-tokens-enable
   6822                                  (fboundp 'lsp--semantic-tokens-on-refresh))
   6823                         (lsp--semantic-tokens-on-refresh workspace))
   6824                       nil)
   6825                      ((equal method "workspace/codeLens/refresh")
   6826                       (when (and lsp-lens-enable
   6827                                  (fboundp 'lsp--lens-on-refresh))
   6828                         (lsp--lens-on-refresh workspace))
   6829                       nil)
   6830                      (t (lsp-warn "Unknown request method: %s" method) nil))))
   6831     ;; Send response to the server.
   6832     (unless (eq response 'delay-response)
   6833       (lsp--send-request-response workspace recv-time request response))))
   6834 
   6835 (lsp-defun lsp--error-string ((&JSONError :message :code))
   6836   "Format ERR as a user friendly string."
   6837   (format "Error from the Language Server: %s (%s)"
   6838           message
   6839           (or (car (alist-get code lsp--errors)) "Unknown error")))
   6840 
   6841 (defun lsp--get-body-length (headers)
   6842   (let ((content-length (cdr (assoc "Content-Length" headers))))
   6843     (if content-length
   6844         (string-to-number content-length)
   6845 
   6846       ;; This usually means either the server or our parser is
   6847       ;; screwed up with a previous Content-Length
   6848       (error "No Content-Length header"))))
   6849 
   6850 (defun lsp--parse-header (s)
   6851   "Parse string S as a LSP (KEY . VAL) header."
   6852   (let ((pos (string-match "\:" s))
   6853         key val)
   6854     (unless pos
   6855       (signal 'lsp-invalid-header-name (list s)))
   6856     (setq key (substring s 0 pos)
   6857           val (s-trim-left (substring s (+ 1 pos))))
   6858     (when (equal key "Content-Length")
   6859       (cl-assert (cl-loop for c across val
   6860                           when (or (> c ?9) (< c ?0)) return nil
   6861                           finally return t)
   6862                  nil (format "Invalid Content-Length value: %s" val)))
   6863     (cons key val)))
   6864 
   6865 (defmacro lsp--read-json (str)
   6866   "Read json string STR."
   6867   (if (progn
   6868         (require 'json)
   6869         (fboundp 'json-parse-string))
   6870       `(json-parse-string ,str
   6871                           :object-type (if lsp-use-plists
   6872                                            'plist
   6873                                          'hash-table)
   6874                           :null-object nil
   6875                           :false-object nil)
   6876     `(let ((json-array-type 'vector)
   6877            (json-object-type (if lsp-use-plists
   6878                                  'plist
   6879                                'hash-table))
   6880            (json-false nil))
   6881        (json-read-from-string ,str))))
   6882 
   6883 (defmacro lsp-json-read-buffer ()
   6884   "Read json from the current buffer."
   6885   (if (progn
   6886         (require 'json)
   6887         (fboundp 'json-parse-buffer))
   6888       `(json-parse-buffer :object-type (if lsp-use-plists
   6889                                            'plist
   6890                                          'hash-table)
   6891                           :null-object nil
   6892                           :false-object nil)
   6893     `(let ((json-array-type 'vector)
   6894            (json-object-type (if lsp-use-plists
   6895                                  'plist
   6896                                'hash-table))
   6897            (json-false nil))
   6898        (json-read))))
   6899 
   6900 (defun lsp--read-json-file (file-path)
   6901   "Read json file."
   6902   (-> file-path
   6903     (f-read-text)
   6904     (lsp--read-json)))
   6905 
   6906 (defun lsp--parser-on-message (json-data workspace)
   6907   "Called when the parser P read a complete MSG from the server."
   6908   (with-demoted-errors "Error processing message %S."
   6909     (with-lsp-workspace workspace
   6910       (let* ((client (lsp--workspace-client workspace))
   6911              (id (--when-let (lsp:json-response-id json-data)
   6912                    (if (stringp it) (string-to-number it) it)))
   6913              (data (lsp:json-response-result json-data)))
   6914         (pcase (lsp--get-message-type json-data)
   6915           ('response
   6916            (cl-assert id)
   6917            (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))]
   6918              (when (lsp--log-io-p method)
   6919                (lsp--log-entry-new
   6920                 (lsp--make-log-entry method id data 'incoming-resp
   6921                                      (lsp--ms-since before-send))
   6922                 workspace))
   6923              (when callback
   6924                (remhash id (lsp--client-response-handlers client))
   6925                (funcall callback (lsp:json-response-result json-data)))))
   6926           ('response-error
   6927            (cl-assert id)
   6928            (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))]
   6929              (when (lsp--log-io-p method)
   6930                (lsp--log-entry-new
   6931                 (lsp--make-log-entry method id (lsp:json-response-error-error json-data)
   6932                                      'incoming-resp (lsp--ms-since before-send))
   6933                 workspace))
   6934              (when callback
   6935                (remhash id (lsp--client-response-handlers client))
   6936                (funcall callback (lsp:json-response-error-error json-data)))))
   6937           ('notification
   6938            (lsp--on-notification workspace json-data))
   6939           ('request (lsp--on-request workspace json-data)))))))
   6940 
   6941 (defun lsp--create-filter-function (workspace)
   6942   "Make filter for the workspace."
   6943   (let ((body-received 0)
   6944         leftovers body-length body chunk)
   6945     (lambda (_proc input)
   6946       (setf chunk (if (s-blank? leftovers)
   6947                       input
   6948                     (concat leftovers input)))
   6949 
   6950       (let (messages)
   6951         (while (not (s-blank? chunk))
   6952           (if (not body-length)
   6953               ;; Read headers
   6954               (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk)))
   6955                   ;; We've got all the headers, handle them all at once:
   6956                   (setf body-length (lsp--get-body-length
   6957                                      (mapcar #'lsp--parse-header
   6958                                              (split-string
   6959                                               (substring-no-properties chunk
   6960                                                                        (or (string-match-p "Content-Length" chunk)
   6961                                                                            (error "Unable to find Content-Length header."))
   6962                                                                        body-sep-pos)
   6963                                               "\r\n")))
   6964                         body-received 0
   6965                         leftovers nil
   6966                         chunk (substring-no-properties chunk (+ body-sep-pos 4)))
   6967 
   6968                 ;; Haven't found the end of the headers yet. Save everything
   6969                 ;; for when the next chunk arrives and await further input.
   6970                 (setf leftovers chunk
   6971                       chunk nil))
   6972             (let* ((chunk-length (string-bytes chunk))
   6973                    (left-to-receive (- body-length body-received))
   6974                    (this-body (if (< left-to-receive chunk-length)
   6975                                   (prog1 (substring-no-properties chunk 0 left-to-receive)
   6976                                     (setf chunk (substring-no-properties chunk left-to-receive)))
   6977                                 (prog1 chunk
   6978                                   (setf chunk nil))))
   6979                    (body-bytes (string-bytes this-body)))
   6980               (push this-body body)
   6981               (setf body-received (+ body-received body-bytes))
   6982               (when (>= chunk-length left-to-receive)
   6983                 (condition-case err
   6984                     (with-temp-buffer
   6985                       (apply #'insert
   6986                              (nreverse
   6987                               (prog1 body
   6988                                 (setf leftovers nil
   6989                                       body-length nil
   6990                                       body-received nil
   6991                                       body nil))))
   6992                       (decode-coding-region (point-min)
   6993                                             (point-max)
   6994                                             'utf-8)
   6995                       (goto-char (point-min))
   6996                       (push (lsp-json-read-buffer) messages))
   6997 
   6998                   (error
   6999                    (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s"
   7000                              (concat leftovers input)
   7001                              err)))))))
   7002         (mapc (lambda (msg)
   7003                 (lsp--parser-on-message msg workspace))
   7004               (nreverse messages))))))
   7005 
   7006 (defvar-local lsp--line-col-to-point-hash-table nil
   7007   "Hash table with keys (line . col) and values that are either point positions
   7008 or markers.")
   7009 
   7010 (defcustom lsp-imenu-detailed-outline t
   7011   "Whether `lsp-imenu' should include signatures.
   7012 This will be ignored if the server doesn't provide the necessary
   7013 information, for example if it doesn't support DocumentSymbols."
   7014   :group 'lsp-imenu
   7015   :type 'boolean)
   7016 
   7017 (defcustom lsp-imenu-hide-parent-details t
   7018   "Whether `lsp-imenu' should hide signatures of parent nodes."
   7019   :group 'lsp-imenu
   7020   :type 'boolean)
   7021 
   7022 (defface lsp-details-face '((t :height 0.8 :inherit shadow))
   7023   "Used to display additional information throughout `lsp'.
   7024 Things like line numbers, signatures, ... are considered
   7025 additional information. Often, additional faces are defined that
   7026 inherit from this face by default, like `lsp-signature-face', and
   7027 they may be customized for finer control."
   7028   :group 'lsp-mode)
   7029 
   7030 (defface lsp-signature-face '((t :inherit lsp-details-face))
   7031   "Used to display signatures in `imenu', ...."
   7032   :group 'lsp-mode)
   7033 
   7034 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?)
   7035                               show-detail?)
   7036   "Render INPUT0, an `&DocumentSymbol', to a string.
   7037 If SHOW-DETAIL? is set, make use of its `:detail?' field (often
   7038 the signature)."
   7039   (let ((detail (and show-detail? (s-present? detail?)
   7040                      (propertize (concat " " (s-trim-left detail?))
   7041                                  'face 'lsp-signature-face)))
   7042         (name (if deprecated?
   7043                   (propertize name 'face 'lsp-face-semhl-deprecated) name)))
   7044     (concat name detail)))
   7045 
   7046 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?)
   7047                                           separator)
   7048   "Render a piece of SymbolInformation.
   7049 Handle :deprecated?. If SEPARATOR is non-nil, the
   7050 symbol's (optional) parent, SEPARATOR and the symbol itself are
   7051 concatenated."
   7052   (when (and separator container-name? (not (string-empty-p container-name?)))
   7053     (setq name (concat name separator container-name?)))
   7054   (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name))
   7055 
   7056 (defun lsp--symbol-to-imenu-elem (sym)
   7057   "Convert SYM to imenu element.
   7058 
   7059 SYM is a SymbolInformation message.
   7060 
   7061 Return a cons cell (full-name . start-point)."
   7062   (let ((start-point (ht-get lsp--line-col-to-point-hash-table
   7063                              (lsp--get-line-and-col sym))))
   7064     (cons (lsp-render-symbol-information
   7065            sym (and lsp-imenu-show-container-name
   7066                     lsp-imenu-container-name-separator))
   7067           start-point)))
   7068 
   7069 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?))
   7070   "Convert SYM to hierarchical imenu elements.
   7071 
   7072 SYM is a DocumentSymbol message.
   7073 
   7074 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if
   7075 SYM doesn't have any children. Otherwise return a cons cell with
   7076 an alist
   7077 
   7078   (\"symbol-name\" . ((\"(symbol-kind)\" . start-point)
   7079                     cons-cells-from-children))"
   7080   (let ((filtered-children (lsp--imenu-filter-symbols children?))
   7081         (signature (lsp-render-symbol sym lsp-imenu-detailed-outline)))
   7082     (if (seq-empty-p filtered-children)
   7083         (cons signature
   7084               (ht-get lsp--line-col-to-point-hash-table
   7085                       (lsp--get-line-and-col sym)))
   7086       (cons signature
   7087             (lsp--imenu-create-hierarchical-index filtered-children)))))
   7088 
   7089 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind))
   7090   "Determine if SYM is for the current document and is to be shown."
   7091   ;; It's a SymbolInformation or DocumentSymbol, which is always in the
   7092   ;; current buffer file.
   7093   (and lsp-imenu-index-symbol-kinds
   7094        (numberp kind)
   7095        (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup))
   7096                                kind
   7097                              0)))
   7098          (not (memql (aref lsp/symbol-kind-lookup clamped-kind)
   7099                      lsp-imenu-index-symbol-kinds)))))
   7100 
   7101 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind))
   7102   "The string name of the kind of SYM."
   7103   (alist-get kind lsp-symbol-kinds "Other"))
   7104 
   7105 (defun lsp--get-line-and-col (sym)
   7106   "Obtain the line and column corresponding to SYM."
   7107   (-let* ((location (lsp:symbol-information-location sym))
   7108           (name-range (or (and location (lsp:location-range location))
   7109                           (lsp:document-symbol-selection-range sym)))
   7110           ((&Range :start (&Position :line :character)) name-range))
   7111     (cons line character)))
   7112 
   7113 (defun lsp--collect-lines-and-cols (symbols)
   7114   "Return a sorted list ((line . col) ...) of the locations of SYMBOLS."
   7115   (let ((stack (mapcar 'identity symbols))
   7116         line-col-list)
   7117     (while stack
   7118       (let ((sym (pop stack)))
   7119         (push (lsp--get-line-and-col sym) line-col-list)
   7120         (unless (seq-empty-p (lsp:document-symbol-children? sym))
   7121           (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack)))))
   7122     (-sort #'lsp--line-col-comparator line-col-list)))
   7123 
   7124 (defun lsp--convert-line-col-to-points-batch (line-col-list)
   7125   "Convert a sorted list of positions from line-column
   7126 representation to point representation."
   7127   (let ((line-col-to-point-map (ht-create))
   7128         (inhibit-field-text-motion t)
   7129         (curr-line 0))
   7130     (lsp-save-restriction-and-excursion
   7131       (goto-char (point-min))
   7132       (cl-loop for (line . col) in line-col-list do
   7133                (forward-line (- line curr-line))
   7134                (setq curr-line line)
   7135                (let ((line-end (line-end-position)))
   7136                  (if (or (not col) (> col (- line-end (point))))
   7137                      (goto-char line-end)
   7138                    (forward-char col)))
   7139                (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers
   7140                                                                   (point-marker)
   7141                                                                 (point)))))
   7142     line-col-to-point-map))
   7143 
   7144 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2))
   7145   (or (< l1 l2)
   7146       (and (= l1 l2)
   7147            (cond ((and c1 c2)
   7148                   (< c1 c2))
   7149                  (c1 t)))))
   7150 
   7151 (defun lsp-imenu-create-uncategorized-index (symbols)
   7152   "Create imenu index from document SYMBOLS.
   7153 This function, unlike `lsp-imenu-create-categorized-index', does
   7154 not categorize by type, but instead returns an `imenu' index
   7155 corresponding to the symbol hierarchy returned by the server
   7156 directly."
   7157   (let* ((lsp--line-col-to-point-hash-table (-> symbols
   7158                                                 lsp--collect-lines-and-cols
   7159                                                 lsp--convert-line-col-to-points-batch)))
   7160     (if (lsp--imenu-hierarchical-p symbols)
   7161         (lsp--imenu-create-hierarchical-index symbols)
   7162       (lsp--imenu-create-non-hierarchical-index symbols))))
   7163 
   7164 (defcustom lsp-imenu-symbol-kinds
   7165   '((1 . "Files")
   7166     (2 . "Modules")
   7167     (3 . "Namespaces")
   7168     (4 . "Packages")
   7169     (5 . "Classes")
   7170     (6 . "Methods")
   7171     (7 . "Properties")
   7172     (8 . "Fields")
   7173     (9 . "Constructors")
   7174     (10 . "Enums")
   7175     (11 . "Interfaces")
   7176     (12 . "Functions")
   7177     (13 . "Variables")
   7178     (14 . "Constants")
   7179     (15 . "Strings")
   7180     (16 . "Numbers")
   7181     (17 . "Booleans")
   7182     (18 . "Arrays")
   7183     (19 . "Objects")
   7184     (20 . "Keys")
   7185     (21 . "Nulls")
   7186     (22 . "Enum Members")
   7187     (23 . "Structs")
   7188     (24 . "Events")
   7189     (25 . "Operators")
   7190     (26 . "Type Parameters"))
   7191   "`lsp-symbol-kinds', but only used by `imenu'.
   7192 A new variable is needed, as it is `imenu' convention to use
   7193 pluralized categories, which `lsp-symbol-kinds' doesn't. If the
   7194 non-pluralized names are preferred, this can be set to
   7195 `lsp-symbol-kinds'."
   7196   :type '(alist :key-type integer :value-type string))
   7197 
   7198 (defun lsp--imenu-kind->name (kind)
   7199   (alist-get kind lsp-imenu-symbol-kinds "?"))
   7200 
   7201 (defun lsp-imenu-create-top-level-categorized-index (symbols)
   7202   "Create an `imenu' index categorizing SYMBOLS by type.
   7203 Only root symbols are categorized.
   7204 
   7205 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS
   7206 shall be a list of DocumentSymbols or SymbolInformation."
   7207   (mapcan
   7208    (-lambda ((type . symbols))
   7209      (let ((cat (lsp--imenu-kind->name type))
   7210            (symbols (lsp-imenu-create-uncategorized-index symbols)))
   7211        ;; If there is no :kind (this is being defensive), or we couldn't look it
   7212        ;; up, just display the symbols inline, without categories.
   7213        (if cat (list (cons cat symbols)) symbols)))
   7214    (sort (seq-group-by #'lsp:document-symbol-kind symbols)
   7215          (-lambda ((kinda) (kindb)) (< kinda kindb)))))
   7216 
   7217 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start)))
   7218   "Convert an `&DocumentSymbol' to an `imenu' entry."
   7219   (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start))
   7220 
   7221 (defun lsp--imenu-create-categorized-index-1 (symbols)
   7222   "Returns an `imenu' index from SYMBOLS categorized by type.
   7223 The result looks like this: ((\"Variables\" . (...)))."
   7224   (->>
   7225    symbols
   7226    (mapcan
   7227     (-lambda ((sym &as &DocumentSymbol :kind :children?))
   7228       (if (seq-empty-p children?)
   7229           (list (list kind (lsp--symbol->imenu sym)))
   7230         (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline
   7231                                                   (not lsp-imenu-hide-parent-details)))))
   7232           (cons
   7233            (list kind (lsp--symbol->imenu sym))
   7234            (mapcar (-lambda ((type .  imenu-items))
   7235                      (list type (cons parent (mapcan #'cdr imenu-items))))
   7236                    (-group-by #'car (lsp--imenu-create-categorized-index-1 children?))))))))
   7237    (-group-by #'car)
   7238    (mapcar
   7239     (-lambda ((kind . syms))
   7240       (cons kind (mapcan #'cdr syms))))))
   7241 
   7242 (defun lsp--imenu-create-categorized-index (symbols)
   7243   (let ((syms (lsp--imenu-create-categorized-index-1 symbols)))
   7244     (dolist (sym syms)
   7245       (setcar sym (lsp--imenu-kind->name (car sym))))
   7246     syms))
   7247 
   7248 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start))))
   7249   (cons (lsp-render-symbol-information sym nil) start))
   7250 
   7251 (defun lsp--imenu-create-categorized-index-flat (symbols)
   7252   "Create a kind-categorized index for SymbolInformation."
   7253   (mapcar (-lambda ((kind . syms))
   7254             (cons (lsp--imenu-kind->name kind)
   7255                   (mapcan (-lambda ((parent . children))
   7256                             (let ((children (mapcar #'lsp--symbol-information->imenu children)))
   7257                               (if parent (list (cons parent children)) children)))
   7258                           (-group-by #'lsp:symbol-information-container-name? syms))))
   7259           (seq-group-by #'lsp:symbol-information-kind symbols)))
   7260 
   7261 (defun lsp-imenu-create-categorized-index (symbols)
   7262   (if (lsp--imenu-hierarchical-p symbols)
   7263       (lsp--imenu-create-categorized-index symbols)
   7264     (lsp--imenu-create-categorized-index-flat symbols)))
   7265 
   7266 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index
   7267   "Function that should create an `imenu' index.
   7268 It will be called with a list of SymbolInformation or
   7269 DocumentSymbols, whose first level is already filtered. It shall
   7270 then return an appropriate `imenu' index (see
   7271 `imenu-create-index-function').
   7272 
   7273 Note that this interface is not stable, and subject to change any
   7274 time."
   7275   :group 'lsp-imenu
   7276   :type '(radio
   7277           (const :tag "Categorize by type"
   7278                  lsp-imenu-create-categorized-index)
   7279           (const :tag "Categorize root symbols by type"
   7280                  lsp-imenu-create-top-level-categorized-index)
   7281           (const :tag "Uncategorized, inline entries"
   7282                  lsp-imenu-create-uncategorized-index)
   7283           (function :tag "Custom function")))
   7284 
   7285 (defun lsp--imenu-create-index ()
   7286   "Create an `imenu' index based on the language server.
   7287 Respects `lsp-imenu-index-function'."
   7288   (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols))))
   7289     (funcall lsp-imenu-index-function symbols)))
   7290 
   7291 (defun lsp--imenu-filter-symbols (symbols)
   7292   "Filter out unsupported symbols from SYMBOLS."
   7293   (seq-remove #'lsp--symbol-ignore symbols))
   7294 
   7295 (defun lsp--imenu-hierarchical-p (symbols)
   7296   "Determine whether any element in SYMBOLS has children."
   7297   (seq-some #'lsp-document-symbol? symbols))
   7298 
   7299 (defun lsp--imenu-create-non-hierarchical-index (symbols)
   7300   "Create imenu index for non-hierarchical SYMBOLS.
   7301 
   7302 SYMBOLS are a list of DocumentSymbol messages.
   7303 
   7304 Return a nested alist keyed by symbol names. e.g.
   7305 
   7306    ((\"SomeClass\" (\"(Class)\" . 10)
   7307                  (\"someField (Field)\" . 20)
   7308                  (\"someFunction (Function)\" . 25)
   7309                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7310                                   (\"someSubField (Field)\" . 35))
   7311     (\"someFunction (Function)\" . 40))"
   7312   (seq-map (lambda (nested-alist)
   7313              (cons (car nested-alist)
   7314                    (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist))))
   7315            (seq-group-by #'lsp--get-symbol-type symbols)))
   7316 
   7317 (defun lsp--imenu-create-hierarchical-index (symbols)
   7318   "Create imenu index for hierarchical SYMBOLS.
   7319 
   7320 SYMBOLS are a list of DocumentSymbol messages.
   7321 
   7322 Return a nested alist keyed by symbol names. e.g.
   7323 
   7324    ((\"SomeClass\" (\"(Class)\" . 10)
   7325                  (\"someField (Field)\" . 20)
   7326                  (\"someFunction (Function)\" . 25)
   7327                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7328                                   (\"someSubField (Field)\" . 35))
   7329     (\"someFunction (Function)\" . 40))"
   7330   (seq-map #'lsp--symbol-to-hierarchical-imenu-elem
   7331            (seq-sort #'lsp--imenu-symbol-lessp symbols)))
   7332 
   7333 (defun lsp--imenu-symbol-lessp (sym1 sym2)
   7334   (let* ((compare-results (mapcar (lambda (method)
   7335                                     (funcall (alist-get method lsp--imenu-compare-function-alist)
   7336                                              sym1 sym2))
   7337                                   lsp-imenu-sort-methods))
   7338          (result (seq-find (lambda (result)
   7339                              (not (= result 0)))
   7340                            compare-results
   7341                            0)))
   7342     (and (numberp result) (< result 0))))
   7343 
   7344 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left)
   7345                                     (&SymbolInformation :kind right))
   7346   "Compare SYM1 and SYM2 by kind."
   7347   (- left right))
   7348 
   7349 (defun lsp--imenu-compare-line-col (sym1 sym2)
   7350   (if (lsp--line-col-comparator
   7351        (lsp--get-line-and-col sym1)
   7352        (lsp--get-line-and-col sym2))
   7353       -1
   7354     1))
   7355 
   7356 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1)
   7357                                     (&SymbolInformation :name name2))
   7358   "Compare SYM1 and SYM2 by name."
   7359   (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2))))
   7360     (if (numberp result) result 0)))
   7361 
   7362 (defun lsp--imenu-refresh ()
   7363   "Force Imenu to refresh itself."
   7364   (imenu--menubar-select imenu--rescan-item))
   7365 
   7366 (defun lsp-enable-imenu ()
   7367   "Use lsp-imenu for the current buffer."
   7368   (imenu--cleanup)
   7369   (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   7370   (setq-local imenu-menubar-modified-tick -1)
   7371   (setq-local imenu--index-alist nil)
   7372   (when menu-bar-mode
   7373     (lsp--imenu-refresh)))
   7374 
   7375 (defun lsp-resolve-final-command (command &optional test?)
   7376   "Resolve final function COMMAND."
   7377   (let* ((command (lsp-resolve-value command))
   7378          (command (cl-etypecase command
   7379                     (list
   7380                      (cl-assert (seq-every-p (apply-partially #'stringp) command) nil
   7381                                 "Invalid command list")
   7382                      command)
   7383                     (string (list command)))))
   7384     (if (and (file-remote-p default-directory) (not test?))
   7385         (list shell-file-name "-c"
   7386               (string-join (cons "stty raw > /dev/null;"
   7387                                  (mapcar #'shell-quote-argument command))
   7388                            " "))
   7389       command)))
   7390 
   7391 (defun lsp-server-present? (final-command)
   7392   "Check whether FINAL-COMMAND is present."
   7393   (let ((binary-found? (executable-find (cl-first final-command) t)))
   7394     (if binary-found?
   7395         (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command))
   7396       (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command)))
   7397     binary-found?))
   7398 
   7399 (defun lsp--value-to-string (value)
   7400   "Convert VALUE to a string that can be set as value in an environment
   7401 variable."
   7402   (cond
   7403    ((stringp value) value)
   7404    ((booleanp value) (if value
   7405                          "1"
   7406                        "0"))
   7407    ((and (sequencep value)
   7408          (seq-every-p #'stringp value)) (string-join value ":"))
   7409    (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables"))))
   7410 
   7411 (defun lsp--compute-process-environment (environment-fn)
   7412   "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'.
   7413 Ignore non-boolean keys whose value is nil."
   7414   (let ((environment (if environment-fn
   7415                          (funcall environment-fn)
   7416                        nil)))
   7417     (-flatten (cons (cl-loop for (key . value) in environment
   7418                              if (or (eval value)
   7419                                     (eq (get value 'custom-type) 'boolean))
   7420                              collect (concat key "=" (lsp--value-to-string
   7421                                                       (eval value))))
   7422                     process-environment))))
   7423 
   7424 (defun lsp--default-directory-for-connection (&optional path)
   7425   "Return path to be used for the working directory of a LSP process.
   7426 
   7427 If `lsp-use-workspace-root-for-server-default-directory' is
   7428 non-nil, uses `lsp-workspace-root' to find the directory
   7429 corresponding to PATH, else returns `default-directory'."
   7430   (if lsp-use-workspace-root-for-server-default-directory
   7431       (lsp-workspace-root path)
   7432     default-directory))
   7433 
   7434 (defun lsp--fix-remote-cmd (program)
   7435   "Helper for `lsp-stdio-connection'.
   7436 Originally coppied from eglot."
   7437 
   7438   (if (file-remote-p default-directory)
   7439       (list shell-file-name "-c"
   7440             (string-join (cons "stty raw > /dev/null;"
   7441                                (mapcar #'shell-quote-argument program))
   7442                          " "))
   7443     program))
   7444 
   7445 (defvar tramp-use-ssh-controlmaster-options)
   7446 (defvar tramp-ssh-controlmaster-options)
   7447 
   7448 (defun lsp-stdio-connection (command &optional test-command)
   7449   "Returns a connection property list using COMMAND.
   7450 COMMAND can be: A string, denoting the command to launch the
   7451 language server. A list of strings, denoting an executable with
   7452 its command line arguments. A function, that either returns a
   7453 string or a list of strings. In all cases, the launched language
   7454 server should send and receive messages on standard I/O.
   7455 TEST-COMMAND is a function with no arguments which returns
   7456 whether the command is present or not. When not specified
   7457 `lsp-mode' will check whether the first element of the list
   7458 returned by COMMAND is available via `executable-find'"
   7459   (cl-check-type command (or string
   7460                              function
   7461                              (and list
   7462                                   (satisfies (lambda (l)
   7463                                                (seq-every-p (lambda (el)
   7464                                                               (stringp el))
   7465                                                             l))))))
   7466   (list :connect (lambda (filter sentinel name environment-fn workspace)
   7467                    (if (and (functionp 'json-rpc-connection)
   7468                             (not (file-remote-p default-directory)))
   7469                        (lsp-json-rpc-connection workspace (lsp-resolve-final-command command))
   7470                      (let ((final-command (lsp-resolve-final-command command))
   7471                            (process-name (generate-new-buffer-name name))
   7472                            (process-environment
   7473                             (lsp--compute-process-environment environment-fn)))
   7474                        (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name)))
   7475                               (default-directory (lsp--default-directory-for-connection))
   7476                               (tramp-use-ssh-controlmaster-options 'suppress)
   7477                               (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none")
   7478                               (proc (make-process
   7479                                      :name process-name
   7480                                      :connection-type 'pipe
   7481                                      :buffer (format "*%s*" process-name)
   7482                                      :coding 'no-conversion
   7483                                      :command final-command
   7484                                      :filter filter
   7485                                      :sentinel sentinel
   7486                                      :stderr stderr-buf
   7487                                      :noquery t
   7488                                      :file-handler t)))
   7489                          (set-process-query-on-exit-flag proc nil)
   7490                          (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil)
   7491                          (with-current-buffer (get-buffer stderr-buf)
   7492                            ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc.
   7493                            (special-mode))
   7494                          (cons proc proc)))))
   7495         :test? (or
   7496                 test-command
   7497                 (lambda ()
   7498                   (lsp-server-present? (lsp-resolve-final-command command t))))))
   7499 
   7500 (defun lsp--open-network-stream (host port name)
   7501   "Open network stream to HOST:PORT.
   7502   NAME will be passed to `open-network-stream'.
   7503   RETRY-COUNT is the number of the retries.
   7504   SLEEP-INTERVAL is the sleep interval between each retry."
   7505   (let* ((retries 0)
   7506          (sleep-interval 0.01)
   7507          (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval))
   7508          connection)
   7509     (while (and (not connection) (< retries number-of-retries))
   7510       (condition-case err
   7511           (setq connection (open-network-stream name nil host port
   7512                                                 :type 'plain
   7513                                                 :coding 'no-conversion))
   7514         (file-error
   7515          (let ((inhibit-message t))
   7516            (lsp--warn "Failed to connect to %s:%s with error message %s"
   7517                       host
   7518                       port
   7519                       (error-message-string err))
   7520            (sleep-for sleep-interval)
   7521            (cl-incf retries)))))
   7522     (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port))))
   7523 
   7524 (defun lsp--port-available (host port)
   7525   "Return non-nil if HOST and PORT are available."
   7526   (condition-case _err
   7527       (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain))
   7528     (file-error t)))
   7529 
   7530 (defun lsp--find-available-port (host starting-port)
   7531   "Find available port on HOST starting from STARTING-PORT."
   7532   (let ((port starting-port))
   7533     (while (not (lsp--port-available host port))
   7534       (cl-incf port))
   7535     port))
   7536 
   7537 (defun lsp-tcp-connection (command-fn)
   7538   "Returns a connection property list similar to `lsp-stdio-connection'.
   7539 COMMAND-FN can only be a function that takes a single argument, a
   7540 port number. It should return a command for launches a language server
   7541 process listening for TCP connections on the provided port."
   7542   (cl-check-type command-fn function)
   7543   (list
   7544    :connect (lambda (filter sentinel name environment-fn _workspace)
   7545               (let* ((host "localhost")
   7546                      (port (lsp--find-available-port host (cl-incf lsp--tcp-port)))
   7547                      (command (funcall command-fn port))
   7548                      (final-command (if (consp command) command (list command)))
   7549                      (_ (unless (lsp-server-present? final-command)
   7550                           (user-error (format "Couldn't find executable %s" (cl-first final-command)))))
   7551                      (process-environment
   7552                       (lsp--compute-process-environment environment-fn))
   7553                      (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion
   7554                                          :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t))
   7555                      (tcp-proc (lsp--open-network-stream host port (concat name "::tcp"))))
   7556 
   7557                 ;; TODO: Same :noquery issue (see above)
   7558                 (set-process-query-on-exit-flag proc nil)
   7559                 (set-process-query-on-exit-flag tcp-proc nil)
   7560                 (set-process-filter tcp-proc filter)
   7561                 (cons tcp-proc proc)))
   7562    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7563 
   7564 (defalias 'lsp-tcp-server 'lsp-tcp-server-command)
   7565 
   7566 (defun lsp-tcp-server-command (command-fn)
   7567   "Create tcp server connection.
   7568 In this mode Emacs is TCP server and the language server connects
   7569 to it. COMMAND is function with one parameter(the port) and it
   7570 should return the command to start the LS server."
   7571   (cl-check-type command-fn function)
   7572   (list
   7573    :connect (lambda (filter sentinel name environment-fn _workspace)
   7574               (let* (tcp-client-connection
   7575                      (tcp-server (make-network-process :name (format "*tcp-server-%s*" name)
   7576                                                        :buffer (format "*tcp-server-%s*" name)
   7577                                                        :family 'ipv4
   7578                                                        :service lsp--tcp-server-port
   7579                                                        :sentinel (lambda (proc _string)
   7580                                                                    (lsp-log "Language server %s is connected." name)
   7581                                                                    (setf tcp-client-connection proc))
   7582                                                        :server 't))
   7583                      (port (process-contact tcp-server :service))
   7584                      (final-command (funcall command-fn port))
   7585                      (process-environment
   7586                       (lsp--compute-process-environment environment-fn))
   7587                      (cmd-proc (make-process :name name
   7588                                              :connection-type 'pipe
   7589                                              :coding 'no-conversion
   7590                                              :command final-command
   7591                                              :stderr (format "*tcp-server-%s*::stderr" name)
   7592                                              :noquery t)))
   7593                 (let ((retries 0))
   7594                   ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds)
   7595                   (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds)))
   7596                     (lsp--info "Waiting for connection for %s, retries: %s" name retries)
   7597                     (sit-for 0.500)
   7598                     (cl-incf retries)))
   7599 
   7600                 (unless tcp-client-connection
   7601                   (condition-case nil (delete-process tcp-server) (error))
   7602                   (condition-case nil (delete-process cmd-proc) (error))
   7603                   (error "Failed to create connection to %s on port %s" name port))
   7604                 (lsp--info "Successfully connected to %s" name)
   7605 
   7606                 (set-process-query-on-exit-flag cmd-proc nil)
   7607                 (set-process-query-on-exit-flag tcp-client-connection nil)
   7608                 (set-process-query-on-exit-flag tcp-server nil)
   7609 
   7610                 (set-process-filter tcp-client-connection filter)
   7611                 (set-process-sentinel tcp-client-connection sentinel)
   7612                 (cons tcp-client-connection cmd-proc)))
   7613    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7614 
   7615 (defalias 'lsp-tramp-connection 'lsp-stdio-connection)
   7616 
   7617 (defun lsp--auto-configure ()
   7618   "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed."
   7619   (when (functionp 'lsp-ui-mode)
   7620     (lsp-ui-mode))
   7621 
   7622   (if lsp-headerline-breadcrumb-enable
   7623       (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)
   7624     (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode))
   7625   (if lsp-modeline-code-actions-enable
   7626       (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)
   7627     (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode))
   7628   (if lsp-modeline-diagnostics-enable
   7629       (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)
   7630     (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode))
   7631   (if lsp-modeline-workspace-status-enable
   7632       (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)
   7633     (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode))
   7634   (if lsp-lens-enable
   7635       (add-hook 'lsp-configure-hook 'lsp-lens--enable)
   7636     (remove-hook 'lsp-configure-hook 'lsp-lens--enable))
   7637   (if lsp-semantic-tokens-enable
   7638       (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)
   7639     (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable))
   7640 
   7641   ;; yas-snippet config
   7642   (setq-local yas-inhibit-overlay-modification-protection t))
   7643 
   7644 (defun lsp--restart-if-needed (workspace)
   7645   "Handler restart for WORKSPACE."
   7646   (when (or (eq lsp-restart 'auto-restart)
   7647             (eq (lsp--workspace-shutdown-action workspace) 'restart)
   7648             (and (eq lsp-restart 'interactive)
   7649                  (let ((query (format
   7650                                "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?"
   7651                                (lsp--workspace-print workspace))))
   7652                    (y-or-n-p query))))
   7653     (--each (lsp--workspace-buffers workspace)
   7654       (when (lsp-buffer-live-p it)
   7655         (lsp-with-current-buffer it
   7656           (if lsp--buffer-deferred
   7657               (lsp-deferred)
   7658             (lsp--info "Restarting LSP in buffer %s" (buffer-name))
   7659             (lsp)))))))
   7660 
   7661 (defun lsp--update-key (table key fn)
   7662   "Apply FN on value corresponding to KEY in TABLE."
   7663   (let ((existing-value (gethash key table)))
   7664     (if-let ((new-value (funcall fn existing-value)))
   7665         (puthash key new-value table)
   7666       (remhash key table))))
   7667 
   7668 (defun lsp--process-sentinel (workspace process exit-str)
   7669   "Create the sentinel for WORKSPACE."
   7670   (unless (process-live-p process)
   7671     (lsp--handle-process-exit workspace exit-str)))
   7672 
   7673 (defun lsp--handle-process-exit (workspace exit-str)
   7674   (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session)))
   7675          (proc (lsp--workspace-proc workspace)))
   7676     (lsp--warn "%s has exited (%s)"
   7677                (lsp-process-name proc)
   7678                (string-trim-right (or exit-str "")))
   7679     (with-lsp-workspace workspace
   7680       ;; Clean workspace related data in each of the buffers
   7681       ;; in the workspace.
   7682       (--each (lsp--workspace-buffers workspace)
   7683         (when (lsp-buffer-live-p it)
   7684           (lsp-with-current-buffer it
   7685             (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces))
   7686             (lsp--uninitialize-workspace)
   7687             (lsp--spinner-stop)
   7688             (lsp--remove-overlays 'lsp-highlight))))
   7689 
   7690       ;; Cleanup session from references to the closed workspace.
   7691       (--each (hash-table-keys folder->workspaces)
   7692         (lsp--update-key folder->workspaces it (apply-partially 'delete workspace)))
   7693 
   7694       (lsp-process-cleanup proc))
   7695 
   7696     (run-hook-with-args 'lsp-after-uninitialized-functions workspace)
   7697 
   7698     (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown)
   7699         (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace))
   7700       (lsp--restart-if-needed workspace))
   7701     (lsp--cleanup-hanging-watches)))
   7702 
   7703 (defun lsp-workspace-folders (workspace)
   7704   "Return all folders associated with WORKSPACE."
   7705   (let (result)
   7706     (->> (lsp-session)
   7707          (lsp-session-folder->servers)
   7708          (maphash (lambda (folder workspaces)
   7709                     (when (-contains? workspaces workspace)
   7710                       (push folder result)))))
   7711     result))
   7712 
   7713 (defun lsp--start-workspace (session client-template root &optional initialization-options)
   7714   "Create new workspace for CLIENT-TEMPLATE with project root ROOT.
   7715 INITIALIZATION-OPTIONS are passed to initialize function.
   7716 SESSION is the active session."
   7717   (lsp--spinner-start)
   7718   (-let* ((default-directory root)
   7719           (client (copy-lsp--client client-template))
   7720           (workspace (make-lsp--workspace
   7721                       :root root
   7722                       :client client
   7723                       :status 'starting
   7724                       :buffers (list (lsp-current-buffer))
   7725                       :host-root (file-remote-p root)))
   7726           ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities
   7727                      'multi-root 'initialized-fn) client)
   7728           ((proc . cmd-proc) (funcall
   7729                               (or (plist-get new-connection :connect)
   7730                                   (user-error "Client %s is configured incorrectly" client))
   7731                               (lsp--create-filter-function workspace)
   7732                               (apply-partially #'lsp--process-sentinel workspace)
   7733                               (format "%s" server-id)
   7734                               environment-fn
   7735                               workspace))
   7736           (workspace-folders (gethash server-id (lsp-session-server-id->folders session))))
   7737     (setf (lsp--workspace-proc workspace) proc
   7738           (lsp--workspace-cmd-proc workspace) cmd-proc)
   7739 
   7740     ;; update (lsp-session-folder->servers) depending on whether we are starting
   7741     ;; multi/single folder workspace
   7742     (mapc (lambda (project-root)
   7743             (->> session
   7744                  (lsp-session-folder->servers)
   7745                  (gethash project-root)
   7746                  (cl-pushnew workspace)))
   7747           (or workspace-folders (list root)))
   7748 
   7749     (with-lsp-workspace workspace
   7750       (run-hooks 'lsp-before-initialize-hook)
   7751       (lsp-request-async
   7752        "initialize"
   7753        (append
   7754         (list :processId (unless (file-remote-p (buffer-file-name))
   7755                            (emacs-pid))
   7756               :rootPath (lsp-file-local-name (expand-file-name root))
   7757               :clientInfo (list :name "emacs"
   7758                                 :version (emacs-version))
   7759               :rootUri (lsp--path-to-uri root)
   7760               :capabilities (lsp--client-capabilities custom-capabilities)
   7761               :initializationOptions initialization-options
   7762               :workDoneToken "1")
   7763         (when lsp-server-trace
   7764           (list :trace lsp-server-trace))
   7765         (when multi-root
   7766           (->> workspace-folders
   7767                (-distinct)
   7768                (-map (lambda (folder)
   7769                        (list :uri (lsp--path-to-uri folder)
   7770                              :name (f-filename folder))))
   7771                (apply 'vector)
   7772                (list :workspaceFolders))))
   7773        (-lambda ((&InitializeResult :capabilities))
   7774          ;; we know that Rust Analyzer will send {} which will be parsed as null
   7775          ;; when using plists
   7776          (when (equal 'rust-analyzer server-id)
   7777            (-> capabilities
   7778                (lsp:server-capabilities-text-document-sync?)
   7779                (lsp:set-text-document-sync-options-save? t)))
   7780 
   7781          (setf (lsp--workspace-server-capabilities workspace) capabilities
   7782                (lsp--workspace-status workspace) 'initialized)
   7783 
   7784          (with-lsp-workspace workspace
   7785            (lsp-notify "initialized" lsp--empty-ht))
   7786 
   7787          (when initialized-fn (funcall initialized-fn workspace))
   7788 
   7789          (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace))
   7790          (->> workspace
   7791               (lsp--workspace-buffers)
   7792               (mapc (lambda (buffer)
   7793                       (lsp-with-current-buffer buffer
   7794                         (lsp--open-in-workspace workspace)))))
   7795 
   7796          (with-lsp-workspace workspace
   7797            (run-hooks 'lsp-after-initialize-hook))
   7798          (lsp--info "%s initialized successfully in folders: %s"
   7799                     (lsp--workspace-print workspace)
   7800                     (lsp-workspace-folders workspace)))
   7801        :mode 'detached))
   7802     workspace))
   7803 
   7804 (defun lsp--load-default-session ()
   7805   "Load default session."
   7806   (setq lsp--session (or (condition-case err
   7807                              (lsp--read-from-file lsp-session-file)
   7808                            (error (lsp--error "Failed to parse the session %s, starting with clean one."
   7809                                               (error-message-string err))
   7810                                   nil))
   7811                          (make-lsp-session))))
   7812 
   7813 (defun lsp-session ()
   7814   "Get the session associated with the current buffer."
   7815   (or lsp--session (setq lsp--session (lsp--load-default-session))))
   7816 
   7817 (defun lsp--client-disabled-p (buffer-major-mode client)
   7818   (seq-some
   7819    (lambda (entry)
   7820      (pcase entry
   7821        ((pred symbolp) (eq entry client))
   7822        (`(,mode . ,client-or-list)
   7823         (and (eq mode buffer-major-mode)
   7824              (if (listp client-or-list)
   7825                  (memq client client-or-list)
   7826                (eq client client-or-list))))))
   7827    lsp-disabled-clients))
   7828 
   7829 
   7830 ;; download server
   7831 
   7832 (defcustom lsp-server-install-dir (expand-file-name
   7833                                    (locate-user-emacs-file (f-join ".cache" "lsp")))
   7834   "Directory in which the servers will be installed."
   7835   :risky t
   7836   :type 'directory
   7837   :package-version '(lsp-mode . "6.3")
   7838   :group 'lsp-mode)
   7839 
   7840 (defcustom lsp-verify-signature t
   7841   "Whether to check GPG signatures of downloaded files."
   7842   :type 'boolean
   7843   :package-version '(lsp-mode . "8.0.0")
   7844   :group 'lsp-mode)
   7845 
   7846 (defvar lsp--dependencies (ht))
   7847 
   7848 (defun lsp-dependency (name &rest definitions)
   7849   "Used to specify a language server DEPENDENCY, the server
   7850 executable or other required file path. Typically, the
   7851 DEPENDENCY is found by locating it on the system path using
   7852 `executable-find'.
   7853 
   7854 You can explicitly call lsp-dependency in your environment to
   7855 specify the absolute path to the DEPENDENCY. For example, the
   7856 typescript-language-server requires both the server and the
   7857 typescript compiler. If you have installed them in a team shared
   7858 read-only location, you can instruct lsp-mode to use them via
   7859 
   7860  (eval-after-load `lsp-mode
   7861    `(progn
   7862       (require lsp-javascript)
   7863       (lsp-dependency typescript-language-server (:system ,tls-exe))
   7864       (lsp-dependency typescript (:system ,ts-js))))
   7865 
   7866 where tls-exe is the absolute path to the typescript-language-server
   7867 executable and ts-js is the absolute path to the typescript compiler
   7868 JavaScript file, tsserver.js (the *.js is required for Windows)."
   7869   (ht-set lsp--dependencies name definitions))
   7870 
   7871 (defun lsp--server-binary-present? (client)
   7872   (unless (equal (lsp--client-server-id client) 'lsp-pwsh)
   7873     (condition-case ()
   7874         (-some-> client lsp--client-new-connection (plist-get :test?) funcall)
   7875       (error nil)
   7876       (args-out-of-range nil))))
   7877 
   7878 (define-minor-mode lsp-installation-buffer-mode
   7879   "Mode used in *lsp-installation* buffers.
   7880 It can be used to set-up keybindings, etc. Disabling this mode
   7881 detaches the installation buffer from commands like
   7882 `lsp-select-installation-buffer'."
   7883   :init-value nil
   7884   :lighter nil)
   7885 
   7886 (defface lsp-installation-finished-buffer-face '((t :foreground "orange"))
   7887   "Face used for finished installation buffers.
   7888 Used in `lsp-select-installation-buffer'."
   7889   :group 'lsp-mode)
   7890 
   7891 (defface lsp-installation-buffer-face '((t :foreground "green"))
   7892   "Face used for installation buffers still in progress.
   7893 Used in `lsp-select-installation-buffer'."
   7894   :group 'lsp-mode)
   7895 
   7896 (defun lsp--installation-buffer? (buf)
   7897   "Check whether BUF is an `lsp-async-start-process' buffer."
   7898   (buffer-local-value 'lsp-installation-buffer-mode buf))
   7899 
   7900 (defun lsp-select-installation-buffer (&optional show-finished)
   7901   "Interactively choose an installation buffer.
   7902 If SHOW-FINISHED is set, leftover (finished) installation buffers
   7903 are still shown."
   7904   (interactive "P")
   7905   (let ((bufs (--filter (and (lsp--installation-buffer? it)
   7906                              (or show-finished (get-buffer-process it)))
   7907                         (buffer-list))))
   7908     (pcase bufs
   7909       (`nil (user-error "No installation buffers"))
   7910       (`(,buf) (pop-to-buffer buf))
   7911       (bufs (pop-to-buffer (completing-read "Select installation buffer: "
   7912                                             (--map (propertize (buffer-name it) 'face
   7913                                                                (if (get-buffer-process it)
   7914                                                                    'lsp-installation-buffer-face
   7915                                                                  'lsp-installation-finished-buffer-face))
   7916                                                    bufs)))))))
   7917 
   7918 (defun lsp-cleanup-installation-buffers ()
   7919   "Delete finished *lsp-installation* buffers."
   7920   (interactive)
   7921   (dolist (buf (buffer-list))
   7922     (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf)))
   7923       (kill-buffer buf))))
   7924 
   7925 (defun lsp--download-status ()
   7926   (-some--> #'lsp--client-download-in-progress?
   7927     (lsp--filter-clients it)
   7928     (-map (-compose #'symbol-name #'lsp--client-server-id) it)
   7929     (format "%s" it)
   7930     (propertize it 'face 'success)
   7931     (format " Installing following servers: %s" it)
   7932     (propertize it
   7933                 'local-map (make-mode-line-mouse-map
   7934                             'mouse-1 #'lsp-select-installation-buffer)
   7935                 'mouse-face 'highlight)))
   7936 
   7937 (defun lsp--install-server-internal (client &optional update?)
   7938   (unless (lsp--client-download-server-fn client)
   7939     (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation."
   7940                 (lsp--client-server-id client)))
   7941 
   7942   (setf (lsp--client-download-in-progress? client) t)
   7943   (add-to-list 'global-mode-string '(t (:eval (lsp--download-status))))
   7944   (cl-flet ((done
   7945              (success? &optional error-message)
   7946              ;; run with idle timer to make sure the lsp command is executed in
   7947              ;; the main thread, see #2739.
   7948              (run-with-timer
   7949               0.0
   7950               nil
   7951               (lambda ()
   7952                 (-let [(&lsp-cln 'server-id 'buffers) client]
   7953                   (setf (lsp--client-download-in-progress? client) nil
   7954                         (lsp--client-buffers client) nil)
   7955                   (if success?
   7956                       (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id
   7957                                  (length buffers))
   7958                     (lsp--error "Server %s install process failed with the following error message: %s.
   7959 Check `*lsp-install*' and `*lsp-log*' buffer."
   7960                                 server-id
   7961                                 error-message))
   7962                   (seq-do
   7963                    (lambda (buffer)
   7964                      (when (lsp-buffer-live-p buffer)
   7965                        (lsp-with-current-buffer buffer
   7966                          (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   7967                                     global-mode-string)
   7968                          (when success? (lsp)))))
   7969                    buffers)
   7970                   (unless (lsp--filter-clients #'lsp--client-download-in-progress?)
   7971                     (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   7972                                global-mode-string)))))))
   7973     (lsp--info "Download %s started." (lsp--client-server-id client))
   7974     (condition-case err
   7975         (funcall
   7976          (lsp--client-download-server-fn client)
   7977          client
   7978          (lambda () (done t))
   7979          (lambda (msg) (done nil msg))
   7980          update?)
   7981       (error
   7982        (done nil (error-message-string err))))))
   7983 
   7984 (defun lsp--require-packages ()
   7985   "Load `lsp-client-packages' if needed."
   7986   (when (and lsp-auto-configure (not lsp--client-packages-required))
   7987     (seq-do (lambda (package)
   7988               ;; loading client is slow and `lsp' can be called repeatedly
   7989               (unless (featurep package)
   7990                 (require package nil t)))
   7991             lsp-client-packages)
   7992     (setq lsp--client-packages-required t)))
   7993 
   7994 ;;;###autoload
   7995 (defun lsp-install-server (update? &optional server-id)
   7996   "Interactively install or re-install server.
   7997 When prefix UPDATE? is t force installation even if the server is present."
   7998   (interactive "P")
   7999   (lsp--require-packages)
   8000   (let* ((chosen-client (or (gethash server-id lsp-clients)
   8001                             (lsp--completing-read
   8002                              "Select server to install/re-install: "
   8003                              (or (->> lsp-clients
   8004                                       (ht-values)
   8005                                       (-filter (-andfn
   8006                                                 (-not #'lsp--client-download-in-progress?)
   8007                                                 #'lsp--client-download-server-fn)))
   8008                                  (user-error "There are no servers with automatic installation"))
   8009                              (lambda (client)
   8010                                (let ((server-name (-> client lsp--client-server-id symbol-name)))
   8011                                  (if (lsp--server-binary-present? client)
   8012                                      (concat server-name " (Already installed)")
   8013                                    server-name)))
   8014                              nil
   8015                              t)))
   8016          (update? (or update?
   8017                       (and (not (lsp--client-download-in-progress? chosen-client))
   8018                            (lsp--server-binary-present? chosen-client)))))
   8019     (lsp--install-server-internal chosen-client update?)))
   8020 
   8021 ;;;###autoload
   8022 (defun lsp-uninstall-server (dir)
   8023   "Delete a LSP server from `lsp-server-install-dir'."
   8024   (interactive
   8025    (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir))))
   8026   (unless (file-directory-p dir)
   8027     (user-error "Couldn't find %s directory" dir))
   8028   (delete-directory dir 'recursive)
   8029   (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir))))
   8030 
   8031 ;;;###autoload
   8032 (defun lsp-uninstall-servers ()
   8033   "Uninstall all installed servers."
   8034   (interactive)
   8035   (let* ((dir lsp-server-install-dir)
   8036          (servers (ignore-errors
   8037                     (directory-files dir t
   8038                                      directory-files-no-dot-files-regexp))))
   8039     (if (or (not (file-directory-p dir)) (zerop (length servers)))
   8040         (user-error "No servers to uninstall")
   8041       (when (yes-or-no-p
   8042              (format "Servers to uninstall: %d (%s), proceed? "
   8043                      (length servers)
   8044                      (mapconcat (lambda (server)
   8045                                   (file-name-nondirectory (directory-file-name server)))
   8046                                 servers " ")))
   8047         (mapc #'lsp-uninstall-server servers)
   8048         (message "All servers uninstalled")))))
   8049 
   8050 ;;;###autoload
   8051 (defun lsp-update-server (&optional server-id)
   8052   "Interactively update (reinstall) a server."
   8053   (interactive)
   8054   (lsp--require-packages)
   8055   (let ((chosen-client (or (gethash server-id lsp-clients)
   8056                            (lsp--completing-read
   8057                             "Select server to update (if not on the list, probably you need to `lsp-install-server`): "
   8058                             (or (->> lsp-clients
   8059                                      (ht-values)
   8060                                      (-filter (-andfn
   8061                                                (-not #'lsp--client-download-in-progress?)
   8062                                                #'lsp--client-download-server-fn
   8063                                                #'lsp--server-binary-present?)))
   8064                                 (user-error "There are no servers to update"))
   8065                             (lambda (client)
   8066                               (-> client lsp--client-server-id symbol-name))
   8067                             nil
   8068                             t))))
   8069     (lsp--install-server-internal chosen-client t)))
   8070 
   8071 ;;;###autoload
   8072 (defun lsp-update-servers ()
   8073   "Update (reinstall) all installed servers."
   8074   (interactive)
   8075   (lsp--require-packages)
   8076   (mapc (lambda (client) (lsp--install-server-internal client t))
   8077         (-filter (-andfn
   8078                   (-not #'lsp--client-download-in-progress?)
   8079                   #'lsp--client-download-server-fn
   8080                   #'lsp--server-binary-present?) (hash-table-values lsp-clients))))
   8081 
   8082 ;;;###autoload
   8083 (defun lsp-ensure-server (server-id)
   8084   "Ensure server SERVER-ID"
   8085   (lsp--require-packages)
   8086   (if-let ((client (gethash server-id lsp-clients)))
   8087       (unless (lsp--server-binary-present? client)
   8088         (lsp--info "Server `%s' is not preset, installing..." server-id)
   8089         (lsp-install-server nil server-id))
   8090     (warn "Unable to find server registration with id %s" server-id)))
   8091 
   8092 (defun lsp-async-start-process (callback error-callback &rest command)
   8093   "Start async process COMMAND with CALLBACK and ERROR-CALLBACK."
   8094   (let ((name (cl-first command)))
   8095     (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd)
   8096                                                                                          (not (null cmd)))
   8097                                                                                        command)
   8098                                                        " ") t
   8099                                             (lambda (&rest _)
   8100                                               (generate-new-buffer-name (format "*lsp-install: %s*" name))))
   8101       (lsp-installation-buffer-mode +1)
   8102       (view-mode +1)
   8103       (add-hook
   8104        'compilation-finish-functions
   8105        (lambda (_buf status)
   8106          (if (string= "finished\n" status)
   8107              (condition-case err
   8108                  (funcall callback)
   8109                (error
   8110                 (funcall error-callback (error-message-string err))))
   8111            (funcall error-callback (s-trim-right status))))
   8112        nil t))))
   8113 
   8114 (defun lsp-resolve-value (value)
   8115   "Resolve VALUE's value.
   8116 If it is function - call it.
   8117 If it is a variable - return it's value
   8118 Otherwise returns value itself."
   8119   (cond
   8120    ((functionp value) (funcall value))
   8121    ((and (symbolp value) (boundp value)) (symbol-value value))
   8122    (value)))
   8123 
   8124 (defvar lsp-deps-providers
   8125   (list :npm (list :path #'lsp--npm-dependency-path
   8126                    :install #'lsp--npm-dependency-install)
   8127         :cargo (list :path #'lsp--cargo-dependency-path
   8128                      :install #'lsp--cargo-dependency-install)
   8129         :system (list :path #'lsp--system-path)
   8130         :download (list :path #'lsp-download-path
   8131                         :install #'lsp-download-install)))
   8132 
   8133 (defun lsp--system-path (path)
   8134   "If PATH is absolute and exists return it as is. Otherwise,
   8135 return the absolute path to the executable defined by PATH or
   8136 nil."
   8137   ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the
   8138   ;; typescript-language-server. When lsp invokes the server, lsp needs to
   8139   ;; supply the path to the typescript compiler, tsserver.js, as an argument. To
   8140   ;; make code platform independent, one must pass the absolute path to the
   8141   ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript
   8142   ;; child process spawn command that is invoked by the
   8143   ;; typescript-language-server). This is why we check for existence and not
   8144   ;; that the path is executable.
   8145   (let ((path (lsp-resolve-value path)))
   8146     (cond
   8147      ((and (f-absolute? path)
   8148            (f-exists? path))
   8149       path)
   8150      ((executable-find path t) path))))
   8151 
   8152 (defun lsp-package-path (dependency)
   8153   "Path to the DEPENDENCY each of the registered providers."
   8154   (let (path)
   8155     (-first (-lambda ((provider . rest))
   8156               (setq path (-some-> lsp-deps-providers
   8157                            (plist-get provider)
   8158                            (plist-get :path)
   8159                            (apply rest))))
   8160             (gethash dependency lsp--dependencies))
   8161     path))
   8162 
   8163 (defun lsp-package-ensure (dependency callback error-callback)
   8164   "Asynchronously ensure a package."
   8165   (or (-first (-lambda ((provider . rest))
   8166                 (-some-> lsp-deps-providers
   8167                   (plist-get provider)
   8168                   (plist-get :install)
   8169                   (apply (cl-list* callback error-callback rest))))
   8170               (gethash dependency lsp--dependencies))
   8171       (funcall error-callback (format "Unable to find a way to install %s" dependency))))
   8172 
   8173 
   8174 ;; npm handling
   8175 
   8176 ;; https://docs.npmjs.com/files/folders#executables
   8177 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys)
   8178   "Return npm dependency PATH for PACKAGE."
   8179   (let ((path (executable-find
   8180                (f-join lsp-server-install-dir "npm" package
   8181                        (cond ((eq system-type 'windows-nt) "")
   8182                              (t "bin"))
   8183                        path)
   8184                t)))
   8185     (unless (and path (f-exists? path))
   8186       (error "The package %s is not installed.  Unable to find %s" package path))
   8187     path))
   8188 
   8189 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys)
   8190   (if-let ((npm-binary (executable-find "npm")))
   8191       (progn
   8192         ;; Explicitly `make-directory' to work around NPM bug in
   8193         ;; versions 7.0.0 through 7.4.1. See
   8194         ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for
   8195         ;; discussion.
   8196         (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents)
   8197         (lsp-async-start-process (lambda ()
   8198                                    (if (string-empty-p
   8199                                         (string-trim (shell-command-to-string
   8200                                                       (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " "))))
   8201                                        (funcall callback)
   8202                                      (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json")))))
   8203                                            (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx
   8204                                        (when (f-dir-p default-directory)
   8205                                          (lsp-async-start-process callback
   8206                                                                   error-callback
   8207                                                                   (executable-find "npx")
   8208                                                                   "npm-install-peers")))))
   8209                                  error-callback
   8210                                  npm-binary
   8211                                  "-g"
   8212                                  "--prefix"
   8213                                  (f-join lsp-server-install-dir "npm" package)
   8214                                  "install"
   8215                                  package))
   8216     (lsp-log "Unable to install %s via `npm' because it is not present" package)
   8217     nil))
   8218 
   8219 
   8220 ;; Cargo dependency handling
   8221 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys)
   8222   (let ((path (executable-find
   8223                (f-join lsp-server-install-dir
   8224                        "cargo"
   8225                        package
   8226                        "bin"
   8227                        path)
   8228                t)))
   8229     (unless (and path (f-exists? path))
   8230       (error "The package %s is not installed.  Unable to find %s" package path))
   8231     path))
   8232 
   8233 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys)
   8234   (if-let ((cargo-binary (executable-find "cargo")))
   8235       (lsp-async-start-process
   8236        callback
   8237        error-callback
   8238        cargo-binary
   8239        "install"
   8240        package
   8241        (when git
   8242          "--git")
   8243        git
   8244        "--root"
   8245        (f-join lsp-server-install-dir "cargo" package))
   8246     (lsp-log "Unable to install %s via `cargo' because it is not present" package)
   8247     nil))
   8248 
   8249 
   8250 
   8251 ;; Download URL handling
   8252 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys)
   8253   (let* ((url (lsp-resolve-value url))
   8254          (store-path (lsp-resolve-value store-path))
   8255          ;; (decompress (lsp-resolve-value decompress))
   8256          (download-path
   8257           (pcase decompress
   8258             (:gzip (concat store-path ".gz"))
   8259             (:zip (concat store-path ".zip"))
   8260             (:targz (concat store-path ".tar.gz"))
   8261             (`nil store-path)
   8262             (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'")))))
   8263     (make-thread
   8264      (lambda ()
   8265        (condition-case err
   8266            (progn
   8267              (when (f-exists? download-path)
   8268                (f-delete download-path))
   8269              (when (f-exists? store-path)
   8270                (f-delete store-path))
   8271              (lsp--info "Starting to download %s to %s..." url download-path)
   8272              (mkdir (f-parent download-path) t)
   8273              (url-copy-file url download-path)
   8274              (lsp--info "Finished downloading %s..." download-path)
   8275              (when (and lsp-verify-signature asc-url pgp-key)
   8276                (if (executable-find epg-gpg-program)
   8277                    (let ((asc-download-path (concat download-path ".asc"))
   8278                          (context (epg-make-context))
   8279                          (fingerprint)
   8280                          (signature))
   8281                      (when (f-exists? asc-download-path)
   8282                        (f-delete asc-download-path))
   8283                      (lsp--info "Starting to download %s to %s..." asc-url asc-download-path)
   8284                      (url-copy-file asc-url asc-download-path)
   8285                      (lsp--info "Finished downloading %s..." asc-download-path)
   8286                      (epg-import-keys-from-string context pgp-key)
   8287                      (setq fingerprint (epg-import-status-fingerprint
   8288                                         (car
   8289                                          (epg-import-result-imports
   8290                                           (epg-context-result-for context 'import)))))
   8291                      (lsp--info "Verifying signature %s..." asc-download-path)
   8292                      (epg-verify-file context asc-download-path download-path)
   8293                      (setq signature (car (epg-context-result-for context 'verify)))
   8294                      (unless (and
   8295                               (eq (epg-signature-status signature) 'good)
   8296                               (equal (epg-signature-fingerprint signature) fingerprint))
   8297                        (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature))))
   8298                  (lsp--warn "GPG is not installed, skipping the signature check.")))
   8299              (when decompress
   8300                (lsp--info "Decompressing %s..." download-path)
   8301                (pcase decompress
   8302                  (:gzip
   8303                   (lsp-gunzip download-path))
   8304                  (:zip (lsp-unzip download-path (f-parent store-path)))
   8305                  (:targz (lsp-tar-gz-decompress download-path (f-parent store-path))))
   8306                (lsp--info "Decompressed %s..." store-path))
   8307              (funcall callback))
   8308          (error (funcall error-callback err)))))))
   8309 
   8310 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys)
   8311   "Download URL and store it into STORE-PATH.
   8312 
   8313 SET-EXECUTABLE? when non-nil change the executable flags of
   8314 STORE-PATH to make it executable. BINARY-PATH can be specified
   8315 when the binary to start does not match the name of the
   8316 archive (e.g. when the archive has multiple files)"
   8317   (let ((store-path (or (lsp-resolve-value binary-path)
   8318                         (lsp-resolve-value store-path))))
   8319     (cond
   8320      ((executable-find store-path) store-path)
   8321      ((and set-executable? (f-exists? store-path))
   8322       (set-file-modes store-path #o0700)
   8323       store-path)
   8324      ((f-exists? store-path) store-path))))
   8325 
   8326 (defun lsp--find-latest-gh-release-url (url regex)
   8327   "Fetch the latest version in the releases given by URL by using REGEX."
   8328   (let ((url-request-method "GET"))
   8329     (with-current-buffer (url-retrieve-synchronously url)
   8330       (goto-char (point-min))
   8331       (re-search-forward "\n\n" nil 'noerror)
   8332       (delete-region (point-min) (point))
   8333       (let* ((json-result (lsp-json-read-buffer)))
   8334         (message "Latest version found: %s" (lsp-get json-result :tag_name))
   8335         (--> json-result
   8336              (lsp-get it :assets)
   8337              (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it)
   8338              (lsp-get it :browser_download_url))))))
   8339 
   8340 ;; unzip
   8341 
   8342 (defconst lsp-ext-pwsh-script "powershell -noprofile -noninteractive \
   8343 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'"
   8344   "Powershell script to unzip file.")
   8345 
   8346 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'"
   8347   "Unzip script to unzip file.")
   8348 
   8349 (defcustom lsp-unzip-script (lambda ()
   8350                               (cond ((executable-find "unzip") lsp-ext-unzip-script)
   8351                                     ((executable-find "powershell") lsp-ext-pwsh-script)
   8352                                     (t nil)))
   8353   "The script to unzip."
   8354   :group 'lsp-mode
   8355   :type 'string
   8356   :package-version '(lsp-mode . "8.0.0"))
   8357 
   8358 (defun lsp-unzip (zip-file dest)
   8359   "Unzip ZIP-FILE to DEST."
   8360   (unless lsp-unzip-script
   8361     (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'"))
   8362   (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest)))
   8363 
   8364 ;; gunzip
   8365 
   8366 (defconst lsp-ext-gunzip-script "gzip -d %1$s"
   8367   "Script to decompress a gzippped file with gzip.")
   8368 
   8369 (defcustom lsp-gunzip-script (lambda ()
   8370                                (cond ((executable-find "gzip") lsp-ext-gunzip-script)
   8371                                      (t nil)))
   8372   "The script to decompress a gzipped file.
   8373 Should be a format string with one argument for the file to be decompressed
   8374 in place."
   8375   :group 'lsp-mode
   8376   :type 'string
   8377   :package-version '(lsp-mode . "8.0.0"))
   8378 
   8379 (defun lsp-gunzip (gz-file)
   8380   "Decompress GZ-FILE in place."
   8381   (unless lsp-gunzip-script
   8382     (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file))
   8383   (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file)))
   8384 
   8385 ;; tar.gz decompression
   8386 
   8387 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'"
   8388   "Script to decompress a .tar.gz file.")
   8389 
   8390 (defcustom lsp-tar-script (lambda ()
   8391                             (cond ((executable-find "tar") lsp-ext-tar-script)
   8392                                   (t nil)))
   8393   "The script to decompress a .tar.gz file.
   8394 Should be a format string with one argument for the file to be decompressed
   8395 in place."
   8396   :group 'lsp-mode
   8397   :type 'string)
   8398 
   8399 (defun lsp-tar-gz-decompress (targz-file dest)
   8400   "Decompress TARGZ-FILE in DEST."
   8401   (unless lsp-tar-script
   8402     (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file))
   8403   (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest)))
   8404 
   8405 
   8406 ;; VSCode marketplace
   8407 
   8408 (defcustom lsp-vscode-ext-url
   8409   "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s"
   8410   "Vscode extension template url."
   8411   :group 'lsp-mode
   8412   :type 'string
   8413   :package-version '(lsp-mode . "8.0.0"))
   8414 
   8415 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform)
   8416   "Return the URL to vscode extension.
   8417 PUBLISHER is the extension publisher.
   8418 NAME is the name of the extension.
   8419 VERSION is the version of the extension.
   8420 TARGETPLATFORM is the targetPlatform of the extension."
   8421   (format lsp-vscode-ext-url publisher name version (or targetPlatform "")))
   8422 
   8423 
   8424 
   8425 ;; Queueing prompts
   8426 
   8427 (defvar lsp--question-queue nil
   8428   "List of questions yet to be asked by `lsp-ask-question'.")
   8429 
   8430 (defun lsp-ask-question (question options callback)
   8431   "Prompt the user to answer the QUESTION with one of the OPTIONS from the
   8432 minibuffer. Once the user selects an option, the CALLBACK function will be
   8433 called, passing the selected option to it.
   8434 
   8435 If the user is currently being shown a question, the question will be stored in
   8436 `lsp--question-queue', and will be asked once the user has answered the current
   8437 question."
   8438   (add-to-list 'lsp--question-queue `(("question" . ,question)
   8439                                       ("options" . ,options)
   8440                                       ("callback" . ,callback)) t)
   8441   (when (eq (length lsp--question-queue) 1)
   8442     (lsp--process-question-queue)))
   8443 
   8444 (defun lsp--process-question-queue ()
   8445   "Take the first question from `lsp--question-queue', process it, then process
   8446 the next question until the queue is empty."
   8447   (-let* (((&alist "question" "options" "callback") (car lsp--question-queue))
   8448           (answer (completing-read question options nil t)))
   8449     (pop lsp--question-queue)
   8450     (funcall callback answer)
   8451     (when lsp--question-queue
   8452       (lsp--process-question-queue))))
   8453 
   8454 (defun lsp--supports-buffer? (client)
   8455   (and
   8456    ;; both file and client remote or both local
   8457    (eq (---truthy? (file-remote-p (buffer-file-name)))
   8458        (---truthy? (lsp--client-remote? client)))
   8459 
   8460    ;; activation function or major-mode match.
   8461    (if-let ((activation-fn (lsp--client-activation-fn client)))
   8462        (funcall activation-fn (buffer-file-name) major-mode)
   8463      (-contains? (lsp--client-major-modes client) major-mode))
   8464 
   8465    ;; check whether it is enabled if `lsp-enabled-clients' is not null
   8466    (or (null lsp-enabled-clients)
   8467        (or (member (lsp--client-server-id client) lsp-enabled-clients)
   8468            (ignore (lsp--info "Client %s is not in lsp-enabled-clients"
   8469                               (lsp--client-server-id client)))))
   8470 
   8471    ;; check whether it is not disabled.
   8472    (not (lsp--client-disabled-p major-mode (lsp--client-server-id client)))))
   8473 
   8474 (defun lsp--filter-clients (pred)
   8475   (->> lsp-clients hash-table-values (-filter pred)))
   8476 
   8477 (defun lsp--find-clients ()
   8478   "Find clients which can handle current buffer."
   8479   (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   8480                                                             #'lsp--server-binary-present?)))
   8481     (lsp-log "Found the following clients for %s: %s"
   8482              (buffer-file-name)
   8483              (s-join ", "
   8484                      (-map (lambda (client)
   8485                              (format "(server-id %s, priority %s)"
   8486                                      (lsp--client-server-id client)
   8487                                      (lsp--client-priority client)))
   8488                            matching-clients)))
   8489     (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients))
   8490             (selected-clients (if-let ((main-client (and main-clients
   8491                                                          (--max-by (> (lsp--client-priority it)
   8492                                                                       (lsp--client-priority other))
   8493                                                                    main-clients))))
   8494                                   (cons main-client add-on-clients)
   8495                                 add-on-clients)))
   8496       (lsp-log "The following clients were selected based on priority: %s"
   8497                (s-join ", "
   8498                        (-map (lambda (client)
   8499                                (format "(server-id %s, priority %s)"
   8500                                        (lsp--client-server-id client)
   8501                                        (lsp--client-priority client)))
   8502                              selected-clients)))
   8503       selected-clients)))
   8504 
   8505 (defun lsp-workspace-remove-all-folders()
   8506   "Delete all lsp tracked folders."
   8507   (interactive)
   8508   (--each (lsp-session-folders (lsp-session))
   8509     (lsp-workspace-folders-remove it)))
   8510 
   8511 (defun lsp-register-client (client)
   8512   "Registers LSP client CLIENT."
   8513   (let ((client-id (lsp--client-server-id client)))
   8514     (puthash client-id client lsp-clients)
   8515     (setplist (intern (format "lsp-%s-after-open-hook" client-id))
   8516               `( standard-value (nil) custom-type hook
   8517                  custom-package-version (lsp-mode . "7.0.1")
   8518                  variable-documentation ,(format "Hooks to run after `%s' server is run." client-id)
   8519                  custom-requests nil)))
   8520   (when (and lsp-auto-register-remote-clients
   8521              (not (lsp--client-remote? client)))
   8522     (let ((remote-client (copy-lsp--client client)))
   8523       (setf (lsp--client-remote? remote-client) t
   8524             (lsp--client-server-id remote-client) (intern
   8525                                                    (format "%s-tramp"
   8526                                                            (lsp--client-server-id client)))
   8527             ;; disable automatic download
   8528             (lsp--client-download-server-fn remote-client) nil)
   8529       (lsp-register-client remote-client))))
   8530 
   8531 (defun lsp--create-initialization-options (_session client)
   8532   "Create initialization-options from SESSION and CLIENT.
   8533 Add workspace folders depending on server being multiroot and
   8534 session workspace folder configuration for the server."
   8535   (let* ((initialization-options-or-fn (lsp--client-initialization-options client)))
   8536     (if (functionp initialization-options-or-fn)
   8537         (funcall initialization-options-or-fn)
   8538       initialization-options-or-fn)))
   8539 
   8540 (defvar lsp-client-settings (make-hash-table :test 'equal)
   8541   "For internal use, any external users please use
   8542   `lsp-register-custom-settings' function instead")
   8543 
   8544 (defun lsp-register-custom-settings (props)
   8545   "Register PROPS.
   8546 PROPS is list of triple (path value boolean?) where PATH is the path to the
   8547 property; VALUE can be a literal value, symbol to be evaluated, or either a
   8548 function or lambda function to be called without arguments; BOOLEAN? is an
   8549 optional flag that should be non-nil for boolean settings, when it is nil the
   8550 property will be ignored if the VALUE is nil.
   8551 
   8552 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))'
   8553 \(note the double parentheses)"
   8554   (mapc
   8555    (-lambda ((path . rest))
   8556      (puthash path rest lsp-client-settings))
   8557    props))
   8558 
   8559 (defun lsp-region-text (region)
   8560   "Get the text for REGION in current buffer."
   8561   (-let (((start . end) (lsp--range-to-region region)))
   8562     (buffer-substring-no-properties start end)))
   8563 
   8564 (defun lsp-ht-set (tbl paths value)
   8565   "Set nested hash table value.
   8566 TBL - a hash table, PATHS is the path to the nested VALUE."
   8567   (pcase paths
   8568     (`(,path) (ht-set! tbl path value))
   8569     (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl)
   8570                                            (let ((temp-tbl (ht)))
   8571                                              (ht-set! tbl path temp-tbl)
   8572                                              temp-tbl))))
   8573                        (lsp-ht-set nested-tbl rst value)))))
   8574 
   8575 ;; sections
   8576 
   8577 (defalias 'defcustom-lsp 'lsp-defcustom)
   8578 
   8579 (defmacro lsp-defcustom (symbol standard doc &rest args)
   8580   "Defines `lsp-mode' server property."
   8581   (declare (doc-string 3) (debug (name body))
   8582            (indent defun))
   8583   (let ((path (plist-get args :lsp-path)))
   8584     (cl-remf args :lsp-path)
   8585     `(progn
   8586        (lsp-register-custom-settings
   8587         (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type))))))
   8588 
   8589        (defcustom ,symbol ,standard ,doc
   8590          :set (lambda (sym val)
   8591                 (lsp--set-custom-property sym val ,path))
   8592          ,@args))))
   8593 
   8594 (defun lsp--set-custom-property (sym val path)
   8595   (set sym val)
   8596   (let ((section (cl-first (s-split "\\." path))))
   8597     (mapc (lambda (workspace)
   8598             (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace))
   8599                               section)
   8600               (with-lsp-workspace workspace
   8601                 (lsp--set-configuration (lsp-configuration-section section)))))
   8602           (lsp--session-workspaces (lsp-session)))))
   8603 
   8604 (defun lsp-configuration-section (section)
   8605   "Get settings for SECTION."
   8606   (let ((ret (ht-create)))
   8607     (maphash (-lambda (path (variable boolean?))
   8608                (when (s-matches? (concat (regexp-quote section) "\\..*") path)
   8609                  (let* ((symbol-value (-> variable
   8610                                           lsp-resolve-value
   8611                                           lsp-resolve-value))
   8612                         (value (if (and boolean? (not symbol-value))
   8613                                    :json-false
   8614                                  symbol-value)))
   8615                    (when (or boolean? value)
   8616                      (lsp-ht-set ret (s-split "\\." path) value)))))
   8617              lsp-client-settings)
   8618     ret))
   8619 
   8620 
   8621 (defun lsp--start-connection (session client project-root)
   8622   "Initiates connection created from CLIENT for PROJECT-ROOT.
   8623 SESSION is the active session."
   8624   (when (lsp--client-multi-root client)
   8625     (cl-pushnew project-root (gethash (lsp--client-server-id client)
   8626                                       (lsp-session-server-id->folders session))))
   8627   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)
   8628 
   8629   (unwind-protect
   8630       (lsp--start-workspace session client project-root (lsp--create-initialization-options session client))
   8631     (lsp--spinner-stop)))
   8632 
   8633 ;; lsp-log-io-mode
   8634 
   8635 (defvar lsp-log-io-mode-map
   8636   (let ((map (make-sparse-keymap)))
   8637     (define-key map (kbd "M-n") #'lsp-log-io-next)
   8638     (define-key map (kbd "M-p") #'lsp-log-io-prev)
   8639     (define-key map (kbd "k") #'lsp--erase-log-buffer)
   8640     (define-key map (kbd "K") #'lsp--erase-session-log-buffers)
   8641     map)
   8642   "Keymap for lsp log buffer mode.")
   8643 
   8644 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo"
   8645   "Special mode for viewing IO logs.")
   8646 
   8647 (defun lsp-workspace-show-log (workspace)
   8648   "Display the log buffer of WORKSPACE."
   8649   (interactive
   8650    (list (if lsp-log-io
   8651              (if (eq (length (lsp-workspaces)) 1)
   8652                  (cl-first (lsp-workspaces))
   8653                (lsp--completing-read "Workspace: " (lsp-workspaces)
   8654                                      #'lsp--workspace-print nil t))
   8655            (user-error "IO logging is disabled"))))
   8656   (pop-to-buffer (lsp--get-log-buffer-create workspace)))
   8657 
   8658 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log)
   8659 
   8660 (defun lsp--get-log-buffer-create (workspace)
   8661   "Return the lsp log buffer of WORKSPACE, creating a new one if needed."
   8662   (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8663          (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id)))
   8664     (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid))))
   8665 
   8666 (defun lsp--erase-log-buffer (&optional all)
   8667   "Delete contents of current lsp log buffer.
   8668 When ALL is t, erase all log buffers of the running session."
   8669   (interactive)
   8670   (let* ((workspaces (lsp--session-workspaces (lsp-session)))
   8671          (current-log-buffer (current-buffer)))
   8672     (dolist (w workspaces)
   8673       (let ((b (lsp--get-log-buffer-create w)))
   8674         (when (or all (eq b current-log-buffer))
   8675           (with-current-buffer b
   8676             (let ((inhibit-read-only t))
   8677               (erase-buffer))))))))
   8678 
   8679 (defun lsp--erase-session-log-buffers ()
   8680   "Erase log buffers of the running session."
   8681   (interactive)
   8682   (lsp--erase-log-buffer t))
   8683 
   8684 (defun lsp-log-io-next (arg)
   8685   "Move to next log entry."
   8686   (interactive "P")
   8687   (ewoc-goto-next lsp--log-io-ewoc (or arg 1)))
   8688 
   8689 (defun lsp-log-io-prev (arg)
   8690   "Move to previous log entry."
   8691   (interactive "P")
   8692   (ewoc-goto-prev lsp--log-io-ewoc (or arg 1)))
   8693 
   8694 
   8695 
   8696 (cl-defmethod lsp-process-id ((process process))
   8697   (process-id process))
   8698 
   8699 (cl-defmethod lsp-process-name ((process process)) (process-name process))
   8700 
   8701 (cl-defmethod lsp-process-status ((process process)) (process-status process))
   8702 
   8703 (cl-defmethod lsp-process-kill ((process process))
   8704   (when (process-live-p process)
   8705     (kill-process process)))
   8706 
   8707 (cl-defmethod lsp-process-send ((process process) message)
   8708   (condition-case err
   8709       (process-send-string process (lsp--make-message message))
   8710     (error (lsp--error "Sending to process failed with the following error: %s"
   8711                        (error-message-string err)))))
   8712 
   8713 (cl-defmethod lsp-process-cleanup (process)
   8714   ;; Kill standard error buffer only if the process exited normally.
   8715   ;; Leave it intact otherwise for debugging purposes.
   8716   (let ((buffer (-> process process-name get-buffer)))
   8717     (when (and (eq (process-status process) 'exit)
   8718                (zerop (process-exit-status process))
   8719                (buffer-live-p buffer))
   8720       (kill-buffer buffer))))
   8721 
   8722 
   8723 ;; native JSONRPC
   8724 
   8725 (declare-function json-rpc "ext:json")
   8726 (declare-function json-rpc-connection "ext:json")
   8727 (declare-function json-rpc-send "ext:json")
   8728 (declare-function json-rpc-shutdown "ext:json")
   8729 (declare-function json-rpc-stderr "ext:json")
   8730 (declare-function json-rpc-pid "ext:json")
   8731 
   8732 (defvar lsp-json-rpc-thread nil)
   8733 (defvar lsp-json-rpc-queue nil)
   8734 (defvar lsp-json-rpc-done nil)
   8735 (defvar lsp-json-rpc-mutex (make-mutex))
   8736 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex))
   8737 
   8738 (defun lsp-json-rpc-process-queue ()
   8739   (while (not lsp-json-rpc-done)
   8740     (while lsp-json-rpc-queue
   8741       (-let (((proc . message) (pop lsp-json-rpc-queue)))
   8742         (json-rpc-send
   8743          proc message
   8744          :null-object nil
   8745          :false-object :json-false)))
   8746     (with-mutex lsp-json-rpc-mutex
   8747       (condition-wait lsp-json-rpc-condition))))
   8748 
   8749 (cl-defmethod lsp-process-id (process) (json-rpc-pid process))
   8750 
   8751 (cl-defmethod lsp-process-name (_process) "TBD")
   8752 
   8753 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process))
   8754 
   8755 (cl-defmethod lsp-process-send (proc message)
   8756   (unless lsp-json-rpc-thread
   8757     (with-current-buffer (get-buffer-create " *json-rpc*")
   8758       (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*"))))
   8759 
   8760   (with-mutex lsp-json-rpc-mutex
   8761     (setq lsp-json-rpc-queue (append lsp-json-rpc-queue
   8762                                      (list (cons proc message))))
   8763     (condition-notify lsp-json-rpc-condition)))
   8764 
   8765 (cl-defmethod lsp-process-cleanup (_proc))
   8766 
   8767 (defun lsp-json-rpc-connection (workspace command)
   8768   (let ((con (apply #'json-rpc-connection command))
   8769         (object-type (if lsp-use-plists 'plist 'hash-table)))
   8770     (with-current-buffer (get-buffer-create " *json-rpc*")
   8771       (make-thread
   8772        (lambda ()
   8773          (json-rpc
   8774           con
   8775           (lambda (result err done)
   8776             (run-with-timer
   8777              0.0
   8778              nil
   8779              (lambda ()
   8780                (cond
   8781                 (result (lsp--parser-on-message result workspace))
   8782                 (err (warn "Json parsing failed with the following error: %s" err))
   8783                 (done (lsp--handle-process-exit workspace ""))))))
   8784           :object-type object-type
   8785           :null-object nil
   8786           :false-object nil))
   8787        "*json-rpc-connection*"))
   8788     (cons con con)))
   8789 
   8790 (defun lsp-json-rpc-stderr ()
   8791   (interactive)
   8792   (--when-let (pcase (lsp-workspaces)
   8793                 (`nil (user-error "There are no active servers in the current buffer"))
   8794                 (`(,workspace) workspace)
   8795                 (workspaces (lsp--completing-read "Select server: "
   8796                                                   workspaces
   8797                                                   'lsp--workspace-print nil t)))
   8798     (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it)))
   8799           (buffer (format "*stderr-%s*" (lsp--workspace-print it)) ))
   8800       (with-current-buffer (get-buffer-create buffer)
   8801         (with-help-window buffer
   8802           (insert content))))))
   8803 
   8804 
   8805 (defun lsp--workspace-print (workspace)
   8806   "Visual representation WORKSPACE."
   8807   (let* ((proc (lsp--workspace-cmd-proc workspace))
   8808          (status (lsp--workspace-status workspace))
   8809          (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8810          (pid (lsp-process-id proc)))
   8811 
   8812     (if (eq 'initialized status)
   8813         (format "%s:%s" server-id pid)
   8814       (format "%s:%s/%s" server-id pid status))))
   8815 
   8816 (defun lsp--map-tree-widget (m)
   8817   "Build `tree-widget' from a hash-table or plist M."
   8818   (when (lsp-structure-p m)
   8819     (let (nodes)
   8820       (lsp-map (lambda (k v)
   8821                  (push `(tree-widget
   8822                          :tag ,(if (lsp-structure-p v)
   8823                                    (format "%s:" k)
   8824                                  (format "%s: %s" k
   8825                                          (propertize (format "%s" v)
   8826                                                      'face
   8827                                                      'font-lock-string-face)))
   8828                          :open t
   8829                          ,@(lsp--map-tree-widget v))
   8830                        nodes))
   8831                m)
   8832       nodes)))
   8833 
   8834 (defun lsp-buffer-name (buffer-id)
   8835   (if-let ((buffer-name (plist-get buffer-id :buffer-name)))
   8836       (funcall buffer-name buffer-id)
   8837     (buffer-name buffer-id)))
   8838 
   8839 (defun lsp--render-workspace (workspace)
   8840   "Tree node representation of WORKSPACE."
   8841   `(tree-widget :tag ,(lsp--workspace-print workspace)
   8842                 :open t
   8843                 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face)
   8844                              :open t
   8845                              ,@(->> workspace
   8846                                     (lsp--workspace-buffers)
   8847                                     (--map `(tree-widget
   8848                                              :tag ,(when (lsp-buffer-live-p it)
   8849                                                      (let ((buffer-name (lsp-buffer-name it)))
   8850                                                        (if (lsp-with-current-buffer it buffer-read-only)
   8851                                                            (propertize buffer-name 'face 'font-lock-constant-face)
   8852                                                          buffer-name)))))))
   8853                 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face)
   8854                              ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget))))
   8855 
   8856 (define-derived-mode lsp-browser-mode special-mode "LspBrowser"
   8857   "Define mode for displaying lsp sessions."
   8858   (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t)))))
   8859 
   8860 (defun lsp-describe-session ()
   8861   "Describes current `lsp-session'."
   8862   (interactive)
   8863   (let ((session (lsp-session))
   8864         (buf (get-buffer-create "*lsp session*"))
   8865         (root (lsp-workspace-root)))
   8866     (with-current-buffer buf
   8867       (lsp-browser-mode)
   8868       (let ((inhibit-read-only t))
   8869         (erase-buffer)
   8870         (--each (lsp-session-folders session)
   8871           (widget-create
   8872            `(tree-widget
   8873              :tag ,(propertize it 'face 'font-lock-keyword-face)
   8874              :open t
   8875              ,@(->> session
   8876                     (lsp-session-folder->servers)
   8877                     (gethash it)
   8878                     (-map 'lsp--render-workspace)))))))
   8879     (pop-to-buffer buf)
   8880     (goto-char (point-min))
   8881     (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag)
   8882              until (or (and root (string= tag root)) (eobp))
   8883              do (goto-char (next-overlay-change (point))))))
   8884 
   8885 (defun lsp--session-workspaces (session)
   8886   "Get all workspaces that are part of the SESSION."
   8887   (-> session lsp-session-folder->servers hash-table-values -flatten -uniq))
   8888 
   8889 (defun lsp--find-multiroot-workspace (session client project-root)
   8890   "Look for a multiroot connection in SESSION created from CLIENT for
   8891 PROJECT-ROOT and BUFFER-MAJOR-MODE."
   8892   (when (lsp--client-multi-root client)
   8893     (-when-let (multi-root-workspace (->> session
   8894                                           (lsp--session-workspaces)
   8895                                           (--first (eq (-> it lsp--workspace-client lsp--client-server-id)
   8896                                                        (lsp--client-server-id client)))))
   8897       (with-lsp-workspace multi-root-workspace
   8898         (lsp-notify "workspace/didChangeWorkspaceFolders"
   8899                     (lsp-make-did-change-workspace-folders-params
   8900                      :event (lsp-make-workspace-folders-change-event
   8901                              :added (vector (lsp-make-workspace-folder
   8902                                              :uri (lsp--path-to-uri project-root)
   8903                                              :name (f-filename project-root)))
   8904                              :removed []))))
   8905 
   8906       (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace))
   8907       (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root))
   8908 
   8909       (lsp--persist-session session)
   8910 
   8911       (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace))
   8912       (lsp--open-in-workspace multi-root-workspace)
   8913 
   8914       multi-root-workspace)))
   8915 
   8916 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder)
   8917   "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT.
   8918 IGNORE-MULTI-FOLDER to ignore multi folder server."
   8919   (-map (lambda (client)
   8920           (or
   8921            (lsp--find-workspace session client project-root)
   8922            (unless ignore-multi-folder
   8923              (lsp--find-multiroot-workspace session client project-root))
   8924            (lsp--start-connection session client project-root)))
   8925         clients))
   8926 
   8927 (defun lsp--spinner-stop ()
   8928   "Stop the spinner in case all of the workspaces are started."
   8929   (when (--all? (eq (lsp--workspace-status it) 'initialized)
   8930                 lsp--buffer-workspaces)
   8931     (spinner-stop)))
   8932 
   8933 (defun lsp--open-in-workspace (workspace)
   8934   "Open in existing WORKSPACE."
   8935   (if (eq 'initialized (lsp--workspace-status workspace))
   8936       ;; when workspace is initialized just call document did open.
   8937       (progn
   8938         (with-lsp-workspace workspace
   8939           (when-let ((before-document-open-fn (-> workspace
   8940                                                   lsp--workspace-client
   8941                                                   lsp--client-before-file-open-fn)))
   8942             (funcall before-document-open-fn workspace))
   8943           (lsp--text-document-did-open))
   8944         (lsp--spinner-stop))
   8945     ;; when it is not initialized
   8946     (lsp--spinner-start)
   8947     (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace))))
   8948 
   8949 (defun lsp--find-workspace (session client project-root)
   8950   "Find server connection created with CLIENT in SESSION for PROJECT-ROOT."
   8951   (when-let ((workspace (->> session
   8952                              (lsp-session-folder->servers)
   8953                              (gethash project-root)
   8954                              (--first (eql (-> it lsp--workspace-client lsp--client-server-id)
   8955                                            (lsp--client-server-id client))))))
   8956     (lsp--open-in-workspace workspace)
   8957     workspace))
   8958 
   8959 (defun lsp--read-char (prompt &optional options)
   8960   "Wrapper for `read-char-from-minibuffer' if Emacs +27.
   8961 Fallback to `read-key' otherwise.
   8962 PROMPT is the message and OPTIONS the available options."
   8963   (if (fboundp 'read-char-from-minibuffer)
   8964       (read-char-from-minibuffer prompt options)
   8965     (read-key prompt)))
   8966 
   8967 (defun lsp--find-root-interactively (session)
   8968   "Find project interactively.
   8969 Returns nil if the project should not be added to the current SESSION."
   8970   (condition-case nil
   8971       (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory))
   8972              (action (lsp--read-char
   8973                       (format
   8974                        "%s is not part of any project.
   8975 
   8976 %s ==> Import project root %s
   8977 %s ==> Import project by selecting root directory interactively
   8978 %s ==> Import project at current directory %s
   8979 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist
   8980 %s ==> Do not ask again for the current project by selecting ignore path interactively
   8981 %s ==> Do nothing: ask again when opening other files from the current project
   8982 
   8983 Select action: "
   8984                        (propertize (buffer-name) 'face 'bold)
   8985                        (propertize "i" 'face 'success)
   8986                        (propertize project-root-suggestion 'face 'bold)
   8987                        (propertize "I" 'face 'success)
   8988                        (propertize "." 'face 'success)
   8989                        (propertize default-directory 'face 'bold)
   8990                        (propertize "d" 'face 'warning)
   8991                        (propertize project-root-suggestion 'face 'bold)
   8992                        (propertize "D" 'face 'warning)
   8993                        (propertize "n" 'face 'warning))
   8994                       '(?i ?\r ?I ?. ?d ?D ?n))))
   8995         (cl-case action
   8996           (?i project-root-suggestion)
   8997           (?\r project-root-suggestion)
   8998           (?I (read-directory-name "Select workspace folder to add: "
   8999                                    (or project-root-suggestion default-directory)
   9000                                    nil
   9001                                    t))
   9002           (?. default-directory)
   9003           (?d (push project-root-suggestion (lsp-session-folders-blocklist session))
   9004               (lsp--persist-session session)
   9005               nil)
   9006           (?D (push (read-directory-name "Select folder to blocklist: "
   9007                                          (or project-root-suggestion default-directory)
   9008                                          nil
   9009                                          t)
   9010                     (lsp-session-folders-blocklist session))
   9011               (lsp--persist-session session)
   9012               nil)
   9013           (t nil)))
   9014     (quit)))
   9015 
   9016 (declare-function tramp-file-name-host "ext:tramp" (file) t)
   9017 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault))
   9018 
   9019 (defun lsp--files-same-host (f1 f2)
   9020   "Predicate on whether or not two files are on the same host."
   9021   (or (not (or (file-remote-p f1) (file-remote-p f2)))
   9022       (and (file-remote-p f1)
   9023            (file-remote-p f2)
   9024            (progn (require 'tramp)
   9025                   (equal (tramp-file-name-host (tramp-dissect-file-name f1))
   9026                          (tramp-file-name-host (tramp-dissect-file-name f2)))))))
   9027 
   9028 (defun lsp-find-session-folder (session file-name)
   9029   "Look in the current SESSION for folder containing FILE-NAME."
   9030   (let ((file-name-canonical (lsp-f-canonical file-name)))
   9031     (->> session
   9032          (lsp-session-folders)
   9033          (--filter (and (lsp--files-same-host it file-name-canonical)
   9034                         (or (lsp-f-same? it file-name-canonical)
   9035                             (and (f-dir? it)
   9036                                  (lsp-f-ancestor-of? it file-name-canonical)))))
   9037          (--max-by (> (length it)
   9038                       (length other))))))
   9039 
   9040 (defun lsp-find-workspace (server-id &optional file-name)
   9041   "Find workspace for SERVER-ID for FILE-NAME."
   9042   (-when-let* ((session (lsp-session))
   9043                (folder->servers (lsp-session-folder->servers session))
   9044                (workspaces (if file-name
   9045                                (gethash (lsp-find-session-folder session file-name) folder->servers)
   9046                              (lsp--session-workspaces session))))
   9047 
   9048     (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces)))
   9049 
   9050 (defun lsp--calculate-root (session file-name)
   9051   "Calculate project root for FILE-NAME in SESSION."
   9052   (and
   9053    (->> session
   9054         (lsp-session-folders-blocklist)
   9055         (--first (and (lsp--files-same-host it file-name)
   9056                       (lsp-f-ancestor-of? it file-name)
   9057                       (prog1 t
   9058                         (lsp--info "File %s is in blocklisted directory %s" file-name it))))
   9059         not)
   9060    (or
   9061     (when lsp-auto-guess-root
   9062       (lsp--suggest-project-root))
   9063     (unless lsp-guess-root-without-session
   9064       (lsp-find-session-folder session file-name))
   9065     (unless lsp-auto-guess-root
   9066       (when-let ((root-folder (lsp--find-root-interactively session)))
   9067         (if (or (not (f-equal? root-folder (expand-file-name "~/")))
   9068                 (yes-or-no-p
   9069                  (concat
   9070                   (propertize "[WARNING] " 'face 'warning)
   9071                   "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:
   9072 
   9073 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/').
   9074 2. If your file is under `~/' then create a subfolder and move that file in this folder.
   9075 
   9076 Type `No' to go back to project selection.
   9077 Type `Yes' to confirm `HOME' as project root.
   9078 Type `C-g' to cancel project import process and stop `lsp'")))
   9079             root-folder
   9080           (lsp--calculate-root session file-name)))))))
   9081 
   9082 (defun lsp--try-open-in-library-workspace ()
   9083   "Try opening current file as library file in any of the active workspace.
   9084 The library folders are defined by each client for each of the active workspace."
   9085   (when-let ((workspace (->> (lsp-session)
   9086                              (lsp--session-workspaces)
   9087                              ;; Sort the last active workspaces first as they are more likely to be
   9088                              ;; the correct ones, especially when jumping to a definition.
   9089                              (-sort (lambda (a _b)
   9090                                       (-contains? lsp--last-active-workspaces a)))
   9091                              (--first
   9092                               (and (-> it lsp--workspace-client lsp--supports-buffer?)
   9093                                    (when-let ((library-folders-fn
   9094                                                (-> it lsp--workspace-client lsp--client-library-folders-fn)))
   9095                                      (-first (lambda (library-folder)
   9096                                                (lsp-f-ancestor-of? library-folder (buffer-file-name)))
   9097                                              (funcall library-folders-fn it))))))))
   9098     (lsp--open-in-workspace workspace)
   9099     (view-mode t)
   9100     (lsp--info "Opening read-only library file %s." (buffer-file-name))
   9101     (list workspace)))
   9102 
   9103 (defun lsp--persist-session (session)
   9104   "Persist SESSION to `lsp-session-file'."
   9105   (lsp--persist lsp-session-file (make-lsp-session
   9106                                   :folders (lsp-session-folders session)
   9107                                   :folders-blocklist (lsp-session-folders-blocklist session)
   9108                                   :server-id->folders (lsp-session-server-id->folders session))))
   9109 
   9110 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder)
   9111   "Try create opening file as a project file.
   9112 When IGNORE-MULTI-FOLDER is t the lsp mode will start new
   9113 language server even if there is language server which can handle
   9114 current language. When IGNORE-MULTI-FOLDER is nil current file
   9115 will be opened in multi folder language server if there is
   9116 such."
   9117   (-let ((session (lsp-session)))
   9118     (-if-let (clients (if ask-for-client
   9119                           (list (lsp--completing-read "Select server to start: "
   9120                                                       (ht-values lsp-clients)
   9121                                                       (-compose 'symbol-name 'lsp--client-server-id) nil t))
   9122                         (lsp--find-clients)))
   9123         (-if-let (project-root (-some-> session
   9124                                  (lsp--calculate-root (buffer-file-name))
   9125                                  (lsp-f-canonical)))
   9126             (progn
   9127               ;; update project roots if needed and persist the lsp session
   9128               (unless (-contains? (lsp-session-folders session) project-root)
   9129                 (cl-pushnew project-root (lsp-session-folders session))
   9130                 (lsp--persist-session session))
   9131               (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder))
   9132           (lsp--warn "%s not in project or it is blocklisted." (buffer-name))
   9133           nil)
   9134       (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode)
   9135       nil)))
   9136 
   9137 (defun lsp-shutdown-workspace ()
   9138   "Shutdown language server."
   9139   (interactive)
   9140   (--when-let (pcase (lsp-workspaces)
   9141                 (`nil (user-error "There are no active servers in the current buffer"))
   9142                 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?"
   9143                                                        (lsp--workspace-print workspace)))
   9144                                  workspace))
   9145                 (workspaces (lsp--completing-read "Select server: "
   9146                                                   workspaces
   9147                                                   'lsp--workspace-print nil t)))
   9148     (lsp-workspace-shutdown it)))
   9149 
   9150 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1")
   9151 
   9152 (defcustom lsp-auto-select-workspace t
   9153   "Shutdown or restart a single workspace.
   9154 If set and the current buffer has only a single workspace
   9155 associated with it, `lsp-shutdown-workspace' and
   9156 `lsp-restart-workspace' will act on it without asking."
   9157   :type 'boolean
   9158   :group 'lsp-mode)
   9159 
   9160 (defun lsp--read-workspace ()
   9161   "Ask the user to select a workspace.
   9162 Errors if there are none."
   9163   (pcase (lsp-workspaces)
   9164     (`nil (error "No workspaces associated with the current buffer"))
   9165     ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace)
   9166     (workspaces (lsp--completing-read "Select workspace: " workspaces
   9167                                       #'lsp--workspace-print nil t))))
   9168 
   9169 (defun lsp-workspace-shutdown (workspace)
   9170   "Shut the workspace WORKSPACE and the language server associated with it"
   9171   (interactive (list (lsp--read-workspace)))
   9172   (lsp--warn "Stopping %s" (lsp--workspace-print workspace))
   9173   (with-lsp-workspace workspace (lsp--shutdown-workspace)))
   9174 
   9175 (defun lsp-disconnect ()
   9176   "Disconnect the buffer from the language server."
   9177   (interactive)
   9178   (lsp--text-document-did-close t)
   9179   (lsp-managed-mode -1)
   9180   (lsp-mode -1)
   9181   (setq lsp--buffer-workspaces nil)
   9182   (lsp--info "Disconnected"))
   9183 
   9184 (defun lsp-restart-workspace ()
   9185   (interactive)
   9186   (--when-let (pcase (lsp-workspaces)
   9187                 (`nil (user-error "There are no active servers in the current buffer"))
   9188                 (`(,workspace) workspace)
   9189                 (workspaces (lsp--completing-read "Select server: "
   9190                                                   workspaces
   9191                                                   'lsp--workspace-print nil t)))
   9192     (lsp-workspace-restart it)))
   9193 
   9194 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1")
   9195 
   9196 (defun lsp-workspace-restart (workspace)
   9197   "Restart the workspace WORKSPACE and the language server associated with it"
   9198   (interactive (list (lsp--read-workspace)))
   9199   (lsp--warn "Restarting %s" (lsp--workspace-print workspace))
   9200   (with-lsp-workspace workspace (lsp--shutdown-workspace t)))
   9201 
   9202 ;;;###autoload
   9203 (defun lsp (&optional arg)
   9204   "Entry point for the server startup.
   9205 When ARG is t the lsp mode will start new language server even if
   9206 there is language server which can handle current language. When
   9207 ARG is nil current file will be opened in multi folder language
   9208 server if there is such. When `lsp' is called with prefix
   9209 argument ask the user to select which language server to start."
   9210   (interactive "P")
   9211 
   9212   (lsp--require-packages)
   9213 
   9214   (when (buffer-file-name)
   9215     (let (clients
   9216           (matching-clients (lsp--filter-clients
   9217                              (-andfn #'lsp--supports-buffer?
   9218                                      #'lsp--server-binary-present?))))
   9219       (cond
   9220        (matching-clients
   9221         (when (setq lsp--buffer-workspaces
   9222                     (or (and
   9223                          ;; Don't open as library file if file is part of a project.
   9224                          (not (lsp-find-session-folder (lsp-session) (buffer-file-name)))
   9225                          (lsp--try-open-in-library-workspace))
   9226                         (lsp--try-project-root-workspaces (equal arg '(4))
   9227                                                           (and arg (not (equal arg 1))))))
   9228           (lsp-mode 1)
   9229           (when lsp-auto-configure (lsp--auto-configure))
   9230           (setq lsp-buffer-uri (lsp--buffer-uri))
   9231           (lsp--info "Connected to %s."
   9232                      (apply 'concat (--map (format "[%s %s]"
   9233                                                    (lsp--workspace-print it)
   9234                                                    (lsp--workspace-root it))
   9235                                            lsp--buffer-workspaces)))))
   9236        ;; look for servers which are currently being downloaded.
   9237        ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9238                                                    #'lsp--client-download-in-progress?)))
   9239         (lsp--info "There are language server(%s) installation in progress.
   9240 The server(s) will be started in the buffer when it has finished."
   9241                    (-map #'lsp--client-server-id clients))
   9242         (seq-do (lambda (client)
   9243                   (cl-pushnew (current-buffer) (lsp--client-buffers client)))
   9244                 clients))
   9245        ;; look for servers to install
   9246        ((setq clients (lsp--filter-clients
   9247                        (-andfn #'lsp--supports-buffer?
   9248                                (-const lsp-enable-suggest-server-download)
   9249                                #'lsp--client-download-server-fn
   9250                                (-not #'lsp--client-download-in-progress?))))
   9251         (let ((client (lsp--completing-read
   9252                        (concat "Unable to find installed server supporting this file. "
   9253                                "The following servers could be installed automatically: ")
   9254                        clients
   9255                        (-compose #'symbol-name #'lsp--client-server-id)
   9256                        nil
   9257                        t)))
   9258           (cl-pushnew (current-buffer) (lsp--client-buffers client))
   9259           (lsp--install-server-internal client)))
   9260        ;; ignore other warnings
   9261        ((not lsp-warn-no-matched-clients)
   9262         nil)
   9263        ;; automatic installation disabled
   9264        ((setq clients (unless matching-clients
   9265                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9266                                                      #'lsp--client-download-server-fn
   9267                                                      (-not (-const lsp-enable-suggest-server-download))
   9268                                                      (-not #'lsp--server-binary-present?)))))
   9269         (lsp--warn "The following servers support current file but automatic download is disabled: %s
   9270 \(If you have already installed the server check *lsp-log*)."
   9271                    (mapconcat (lambda (client)
   9272                                 (symbol-name (lsp--client-server-id client)))
   9273                               clients
   9274                               " ")))
   9275        ;; no clients present
   9276        ((setq clients (unless matching-clients
   9277                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9278                                                      (-not #'lsp--server-binary-present?)))))
   9279         (lsp--warn "The following servers support current file but do not have automatic installation: %s
   9280 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages.
   9281 \(If you have already installed the server check *lsp-log*)."
   9282                    (mapconcat (lambda (client)
   9283                                 (symbol-name (lsp--client-server-id client)))
   9284                               clients
   9285                               " ")))
   9286        ;; no matches
   9287        ((-> #'lsp--supports-buffer? lsp--filter-clients not)
   9288         (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'.
   9289 This issue might be caused by:
   9290 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'.
   9291 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'.
   9292 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/ .
   9293 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/.
   9294 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients').
   9295 You can customize `lsp-warn-no-matched-clients' to disable this message."
   9296                     major-mode major-mode major-mode))))))
   9297 
   9298 (defun lsp--buffer-visible-p ()
   9299   "Return non nil if current buffer is visible."
   9300   (or (buffer-modified-p) (get-buffer-window nil t)))
   9301 
   9302 (defun lsp--init-if-visible ()
   9303   "Run `lsp' for the current buffer if the buffer is visible.
   9304 Returns non nil if `lsp' was run for the buffer."
   9305   (when (lsp--buffer-visible-p)
   9306     (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t)
   9307     (lsp)
   9308     t))
   9309 
   9310 ;;;###autoload
   9311 (defun lsp-deferred ()
   9312   "Entry point that defers server startup until buffer is visible.
   9313 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'.
   9314 This avoids overloading the server with many files when starting Emacs."
   9315   ;; Workspace may not be initialized yet. Use a buffer local variable to
   9316   ;; remember that we deferred loading of this buffer.
   9317   (setq lsp--buffer-deferred t)
   9318   (let ((buffer (current-buffer)))
   9319     ;; Avoid false positives as desktop-mode restores buffers by deferring
   9320     ;; visibility check until the stack clears.
   9321     (run-with-idle-timer 0 nil (lambda ()
   9322                                  (when (buffer-live-p buffer)
   9323                                    (with-current-buffer buffer
   9324                                      (unless (lsp--init-if-visible)
   9325                                        (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t))))))))
   9326 
   9327 
   9328 
   9329 (defvar lsp-file-truename-cache (ht))
   9330 
   9331 (defmacro lsp-with-cached-filetrue-name (&rest body)
   9332   "Executes BODY caching the `file-truename' calls."
   9333   `(let ((old-fn (symbol-function 'file-truename)))
   9334      (unwind-protect
   9335          (progn
   9336            (fset 'file-truename
   9337                  (lambda (file-name &optional counter prev-dirs)
   9338                    (or (gethash file-name lsp-file-truename-cache)
   9339                        (puthash file-name (apply old-fn (list file-name counter prev-dirs))
   9340                                 lsp-file-truename-cache))))
   9341            ,@body)
   9342        (fset 'file-truename old-fn))))
   9343 
   9344 
   9345 (defun lsp-virtual-buffer-call (key &rest args)
   9346   (when lsp--virtual-buffer
   9347     (when-let ((fn (plist-get lsp--virtual-buffer key)))
   9348       (apply fn args))))
   9349 
   9350 (defun lsp-translate-column (column)
   9351   "Translate COLUMN taking into account virtual buffers."
   9352   (or (lsp-virtual-buffer-call :real->virtual-char column)
   9353       column))
   9354 
   9355 (defun lsp-translate-line (line)
   9356   "Translate LINE taking into account virtual buffers."
   9357   (or (lsp-virtual-buffer-call :real->virtual-line line)
   9358       line))
   9359 
   9360 
   9361 ;; lsp internal validation.
   9362 
   9363 (defmacro lsp--doctor (&rest checks)
   9364   `(-let [buf (current-buffer)]
   9365      (with-current-buffer (get-buffer-create "*lsp-performance*")
   9366        (with-help-window (current-buffer)
   9367          ,@(-map (-lambda ((msg form))
   9368                    `(insert (format "%s: %s\n" ,msg
   9369                                     (let ((res (with-current-buffer buf
   9370                                                  ,form)))
   9371                                       (cond
   9372                                        ((eq res :optional) (propertize "OPTIONAL" 'face 'warning))
   9373                                        (res (propertize "OK" 'face 'success))
   9374                                        (t (propertize "ERROR" 'face 'error)))))))
   9375                  (-partition 2 checks))))))
   9376 
   9377 (define-obsolete-function-alias 'lsp-diagnose
   9378   'lsp-doctor "lsp-mode 8.0.0")
   9379 
   9380 (defun lsp-doctor ()
   9381   "Validate performance settings."
   9382   (interactive)
   9383   (lsp--doctor
   9384    "Checking for Native JSON support" (functionp 'json-serialize)
   9385    "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max)
   9386    "Check `read-process-output-max' default has been changed from 4k"
   9387    (and (boundp 'read-process-output-max)
   9388         (> read-process-output-max 4096))
   9389    "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)"
   9390    (condition-case _err
   9391        (progn (lsp--make-message (list "a" "b"))
   9392               nil)
   9393      (error t))
   9394    "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000)
   9395    "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)
   9396    "Using emacs 28+ with native compilation?"
   9397    (or (and (fboundp 'native-comp-available-p)
   9398             (native-comp-available-p))
   9399        :optional)))
   9400 
   9401 (declare-function package-version-join "ext:package")
   9402 (declare-function package-desc-version "ext:package")
   9403 (declare-function package--alist "ext:package")
   9404 
   9405 (defun lsp-version ()
   9406   "Return string describing current version of `lsp-mode'."
   9407   (interactive)
   9408   (unless (featurep 'package)
   9409     (require 'package))
   9410   (let ((ver (format "lsp-mode %s, Emacs %s, %s"
   9411                      (package-version-join
   9412                       (package-desc-version
   9413                        (car (alist-get 'lsp-mode (package--alist)))))
   9414                      emacs-version
   9415                      system-type)))
   9416     (if (called-interactively-p 'interactive)
   9417         (lsp--info "%s" ver)
   9418       ver)))
   9419 
   9420 
   9421 
   9422 ;; org-mode/virtual-buffer
   9423 
   9424 (declare-function org-babel-get-src-block-info "ext:ob-core")
   9425 (declare-function org-do-remove-indentation "ext:org-macs")
   9426 (declare-function org-src-get-lang-mode "ext:org-src")
   9427 (declare-function org-element-context "ext:org-element")
   9428 
   9429 (defun lsp--virtual-buffer-update-position ()
   9430   (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range))
   9431                                      (funcall in-range))
   9432                                    lsp--virtual-buffer-connections))
   9433       (unless (equal virtual-buffer lsp--virtual-buffer)
   9434         (lsp-org))
   9435     (when lsp-managed-mode
   9436       (lsp-managed-mode -1)
   9437       (lsp-mode -1)
   9438       (setq lsp--buffer-workspaces nil)
   9439       (setq lsp--virtual-buffer nil)
   9440       (setq lsp-buffer-uri nil)
   9441 
   9442       ;; force refresh of diagnostics
   9443       (run-hooks 'lsp-after-diagnostics-hook))))
   9444 
   9445 (defun lsp-virtual-buffer-on-change (start end length)
   9446   "Adjust on change event to be executed against the proper language server."
   9447   (let ((max-point (max end
   9448                         (or (plist-get lsp--before-change-vals :end) 0)
   9449                         (+ start length))))
   9450     (when-let ((virtual-buffer (-first (lambda (vb)
   9451                                          (let ((lsp--virtual-buffer vb))
   9452                                            (and (lsp-virtual-buffer-call :in-range start)
   9453                                                 (lsp-virtual-buffer-call :in-range max-point))))
   9454                                        lsp--virtual-buffer-connections)))
   9455       (lsp-with-current-buffer virtual-buffer
   9456         (lsp-on-change start end length
   9457                        (lambda (&rest _)
   9458                          (list :range (lsp--range (list :character 0 :line 0)
   9459                                                   lsp--virtual-buffer-point-max)
   9460                                :text (lsp--buffer-content))))))))
   9461 
   9462 (defun lsp-virtual-buffer-before-change (start _end)
   9463   (when-let ((virtual-buffer (-first (lambda (vb)
   9464                                        (lsp-with-current-buffer vb
   9465                                          (lsp-virtual-buffer-call :in-range start)))
   9466                                      lsp--virtual-buffer-connections)))
   9467     (lsp-with-current-buffer virtual-buffer
   9468       (setq lsp--virtual-buffer-point-max
   9469             (lsp--point-to-position (lsp-virtual-buffer-call :last-point))))))
   9470 
   9471 (defun lsp-patch-on-change-event ()
   9472   (remove-hook 'after-change-functions #'lsp-on-change t)
   9473   (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t)
   9474   (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t))
   9475 
   9476 (defun lsp-kill-virtual-buffers ()
   9477   (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections))
   9478 
   9479 (defun lsp--move-point-in-indentation (point indentation)
   9480   (save-excursion
   9481     (goto-char point)
   9482     (if (<= point (+ (line-beginning-position) indentation))
   9483         (line-beginning-position)
   9484       point)))
   9485 
   9486 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck")
   9487 (declare-function flycheck-add-mode "ext:flycheck")
   9488 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics")
   9489 
   9490 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn)
   9491 
   9492 (defun lsp-flycheck-add-mode (mode)
   9493   "Register flycheck support for MODE."
   9494   (lsp-diagnostics-lsp-checker-if-needed)
   9495   (unless (flycheck-checker-supports-major-mode-p 'lsp mode)
   9496     (flycheck-add-mode 'lsp mode)))
   9497 
   9498 (defun lsp-progress-spinner-type ()
   9499   "Retrieve the spinner type value, if value is not a symbol of `spinner-types
   9500 defaults to `progress-bar."
   9501   (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar))
   9502 
   9503 (defun lsp-org ()
   9504   (interactive)
   9505   (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range))
   9506                                                               (funcall in-range))
   9507                                                             lsp--virtual-buffer-connections))
   9508       (unless (equal lsp--virtual-buffer virtual-buffer)
   9509         (setq lsp--buffer-workspaces workspaces)
   9510         (setq lsp--virtual-buffer virtual-buffer)
   9511         (setq lsp-buffer-uri nil)
   9512         (lsp-mode 1)
   9513         (lsp-managed-mode 1)
   9514         (lsp-patch-on-change-event))
   9515 
   9516     (save-excursion
   9517       (-let* (virtual-buffer
   9518               (wcb (lambda (f)
   9519                      (with-current-buffer (plist-get virtual-buffer :buffer)
   9520                        (-let* (((&plist :major-mode :buffer-file-name
   9521                                         :goto-buffer :workspaces) virtual-buffer)
   9522                                (lsp--virtual-buffer virtual-buffer)
   9523                                (lsp--buffer-workspaces workspaces))
   9524                          (save-excursion
   9525                            (funcall goto-buffer)
   9526                            (funcall f))))))
   9527               ((&plist :begin :end :post-blank :language) (cl-second (org-element-context)))
   9528               ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light)))
   9529 
   9530               (file-name (if file-name
   9531                              (f-expand file-name)
   9532                            (user-error "You should specify file name in the src block header.")))
   9533               (begin-marker (progn
   9534                               (goto-char begin)
   9535                               (forward-line)
   9536                               (set-marker (make-marker) (point))))
   9537               (end-marker (progn
   9538                             (goto-char end)
   9539                             (forward-line (1- (- post-blank)))
   9540                             (set-marker (make-marker) (1+ (point)))))
   9541               (buf (current-buffer))
   9542               (src-block (buffer-substring-no-properties begin-marker
   9543                                                          (1- end-marker)))
   9544               (indentation (with-temp-buffer
   9545                              (insert src-block)
   9546 
   9547                              (goto-char (point-min))
   9548                              (let ((indentation (current-indentation)))
   9549                                (plist-put lsp--virtual-buffer :indentation indentation)
   9550                                (org-do-remove-indentation)
   9551                                (goto-char (point-min))
   9552                                (- indentation (current-indentation))))))
   9553         (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t)
   9554 
   9555         (when (fboundp 'flycheck-add-mode)
   9556           (lsp-flycheck-add-mode 'org-mode))
   9557 
   9558         (setq lsp--virtual-buffer
   9559               (list
   9560                :in-range (lambda (&optional point)
   9561                            (<= begin-marker (or point (point)) (1- end-marker)))
   9562                :goto-buffer (lambda () (goto-char begin-marker))
   9563                :buffer-string
   9564                (lambda ()
   9565                  (let ((src-block (buffer-substring-no-properties
   9566                                    begin-marker
   9567                                    (1- end-marker))))
   9568                    (with-temp-buffer
   9569                      (insert src-block)
   9570 
   9571                      (goto-char (point-min))
   9572                      (while (not (eobp))
   9573                        (delete-region (point) (if (> (+ (point) indentation) (line-end-position))
   9574                                                   (line-end-position)
   9575                                                 (+ (point) indentation)))
   9576                        (forward-line))
   9577                      (buffer-substring-no-properties (point-min)
   9578                                                      (point-max)))))
   9579                :buffer buf
   9580                :begin begin-marker
   9581                :end end-marker
   9582                :indentation indentation
   9583                :last-point (lambda () (1- end-marker))
   9584                :cur-position (lambda ()
   9585                                (lsp-save-restriction-and-excursion
   9586                                  (list :line (- (lsp--cur-line)
   9587                                                 (lsp--cur-line begin-marker))
   9588                                        :character (let ((character (- (point)
   9589                                                                       (line-beginning-position)
   9590                                                                       indentation)))
   9591                                                     (if (< character 0)
   9592                                                         0
   9593                                                       character)))))
   9594                :line/character->point (-lambda (line character)
   9595                                         (-let [inhibit-field-text-motion t]
   9596                                           (+ indentation
   9597                                              (lsp-save-restriction-and-excursion
   9598                                                (goto-char begin-marker)
   9599                                                (forward-line line)
   9600                                                (-let [line-end (line-end-position)]
   9601                                                  (if (> character (- line-end (point)))
   9602                                                      line-end
   9603                                                    (forward-char character)
   9604                                                    (point)))))))
   9605                :major-mode (org-src-get-lang-mode language)
   9606                :buffer-file-name file-name
   9607                :buffer-uri (lsp--path-to-uri file-name)
   9608                :with-current-buffer wcb
   9609                :buffer-live? (lambda (_) (buffer-live-p buf))
   9610                :buffer-name (lambda (_)
   9611                               (propertize (format "%s(%s:%s)%s"
   9612                                                   (buffer-name buf)
   9613                                                   begin-marker
   9614                                                   end-marker
   9615                                                   language)
   9616                                           'face 'italic))
   9617                :real->virtual-line (lambda (line)
   9618                                      (+ line (line-number-at-pos begin-marker) -1))
   9619                :real->virtual-char (lambda (char) (+ char indentation))
   9620                :cleanup (lambda ()
   9621                           (set-marker begin-marker nil)
   9622                           (set-marker end-marker nil))))
   9623         (setf virtual-buffer lsp--virtual-buffer)
   9624         (puthash file-name virtual-buffer lsp--virtual-buffer-mappings)
   9625         (push virtual-buffer lsp--virtual-buffer-connections)
   9626 
   9627         ;; TODO: tangle only connected sections
   9628         (add-hook 'after-save-hook 'org-babel-tangle nil t)
   9629         (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t)
   9630         (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t)
   9631 
   9632         (setq lsp--buffer-workspaces
   9633               (lsp-with-current-buffer virtual-buffer
   9634                 (lsp)
   9635                 (plist-put virtual-buffer :workspaces (lsp-workspaces))
   9636                 (lsp-workspaces)))))))
   9637 
   9638 (defun lsp-virtual-buffer-disconnect (virtual-buffer)
   9639   (interactive (list (or
   9640                       lsp--virtual-buffer
   9641                       (when lsp--virtual-buffer-connections
   9642                         (lsp--completing-read "Select virtual buffer to disconnect: "
   9643                                               lsp--virtual-buffer-connections
   9644                                               (-lambda ((&plist :buffer-file-name))
   9645                                                 buffer-file-name))))))
   9646   (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer)
   9647       (progn
   9648         (lsp-with-current-buffer virtual-buffer
   9649           (lsp--text-document-did-close))
   9650         (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections))
   9651         (when (eq virtual-buffer lsp--virtual-buffer)
   9652           (setf lsp--virtual-buffer nil))
   9653         (when cleanup (funcall cleanup))
   9654         (remhash file-name lsp--virtual-buffer-mappings)
   9655 
   9656         (lsp--virtual-buffer-update-position)
   9657         (lsp--info "Disconnected from buffer %s" file-name))
   9658     (lsp--error "Nothing to disconnect from?")))
   9659 
   9660 
   9661 ;; inlay hints
   9662 
   9663 (defface lsp-inlay-hint-face
   9664   '((t :inherit font-lock-comment-face))
   9665   "The face to use for the JavaScript inlays."
   9666   :group 'lsp-mode
   9667   :package-version '(lsp-mode . "9.0.0"))
   9668 
   9669 (defface lsp-inlay-hint-type-face
   9670   '((t :inherit lsp-inlay-hint-face))
   9671   "Face for inlay type hints (e.g. inferred variable types)."
   9672   :group 'lsp-mode
   9673   :package-version '(lsp-mode . "9.0.0"))
   9674 
   9675 (defcustom lsp-inlay-hint-type-format "%s"
   9676   "Format string for variable inlays (part of the inlay face)."
   9677   :type '(string :tag "String")
   9678   :group 'lsp-mode
   9679   :package-version '(lsp-mode . "9.0.0"))
   9680 
   9681 (defface lsp-inlay-hint-parameter-face
   9682   '((t :inherit lsp-inlay-hint-face))
   9683   "Face for inlay parameter hints (e.g. function parameter names at
   9684 call-site)."
   9685   :group 'lsp-mode
   9686   :package-version '(lsp-mode . "9.0.0"))
   9687 
   9688 (defcustom lsp-inlay-hint-param-format "%s"
   9689   "Format string for parameter inlays (part of the inlay face)."
   9690   :type '(string :tag "String")
   9691   :group 'lsp-mode
   9692   :package-version '(lsp-mode . "9.0.0"))
   9693 
   9694 (defcustom lsp-update-inlay-hints-on-scroll t
   9695   "If non-nil update inlay hints immediately when scrolling or
   9696 modifying window sizes."
   9697   :type 'boolean
   9698   :package-version '(lsp-mode . "9.0.0"))
   9699 
   9700 (defun lsp--format-inlay (text kind)
   9701   (cond
   9702    ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text))
   9703    ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text))
   9704    (t text)))
   9705 
   9706 (defun lsp--face-for-inlay (kind)
   9707   (cond
   9708    ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face)
   9709    ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face)
   9710    (t 'lsp-inlay-hint-face)))
   9711 
   9712 (defun lsp--update-inlay-hints-scroll-function (window start)
   9713   (lsp-update-inlay-hints start (window-end window t)))
   9714 
   9715 (defun lsp--update-inlay-hints ()
   9716   (lsp-update-inlay-hints (window-start) (window-end nil t)))
   9717 
   9718 (defun lsp--label-from-inlay-hints-response (label)
   9719   "Returns a string label built from an array of
   9720 InlayHintLabelParts or the argument itself if it's already a
   9721 string."
   9722   (cl-typecase label
   9723     (string label)
   9724     (vector
   9725      (string-join (mapcar (lambda (part)
   9726                             (-let (((&InlayHintLabelPart :value) part))
   9727                               value))
   9728                           label)))))
   9729 
   9730 (defun lsp-update-inlay-hints (start end)
   9731   (lsp-request-async
   9732    "textDocument/inlayHint"
   9733    (lsp-make-inlay-hints-params
   9734     :text-document (lsp--text-document-identifier)
   9735     :range (lsp-make-range :start
   9736                            (lsp-point-to-position start)
   9737                            :end
   9738                            (lsp-point-to-position end)))
   9739    (lambda (res)
   9740      (lsp--remove-overlays 'lsp-inlay-hint)
   9741      (dolist (hint res)
   9742        (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint)
   9743                (kind (or kind? lsp/inlay-hint-kind-type-hint))
   9744                (label (lsp--label-from-inlay-hints-response label))
   9745                (pos (lsp--position-to-point position))
   9746                (overlay (make-overlay pos pos nil 'front-advance 'end-advance)))
   9747          (when (stringp label)
   9748            (overlay-put overlay 'lsp-inlay-hint t)
   9749            (overlay-put overlay 'before-string
   9750                         (format "%s%s%s"
   9751                                 (if padding-left? " " "")
   9752                                 (propertize (lsp--format-inlay label kind)
   9753                                             'font-lock-face (lsp--face-for-inlay kind))
   9754                                 (if padding-right? " " "")))))))
   9755    :mode 'tick))
   9756 
   9757 (define-minor-mode lsp-inlay-hints-mode
   9758   "Mode for displaying inlay hints."
   9759   :lighter nil
   9760   (cond
   9761    ((and lsp-inlay-hints-mode lsp--buffer-workspaces)
   9762     (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t)
   9763     (when lsp-update-inlay-hints-on-scroll
   9764       (add-to-list (make-local-variable 'window-scroll-functions)
   9765                    #'lsp--update-inlay-hints-scroll-function)))
   9766    (t
   9767     (lsp--remove-overlays 'lsp-inlay-hint)
   9768     (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t)
   9769     (setf window-scroll-functions
   9770           (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions)))))
   9771 
   9772 
   9773 
   9774 ;;;###autoload
   9775 (defun lsp-start-plain ()
   9776   "Start `lsp-mode' using minimal configuration using the latest `melpa' version
   9777 of the packages.
   9778 
   9779 In case the major-mode that you are using for "
   9780   (interactive)
   9781   (let ((start-plain (make-temp-file "plain" nil ".el")))
   9782     (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el"
   9783                    start-plain t)
   9784     (async-shell-command
   9785      (format "%s -q -l %s %s"
   9786              (expand-file-name invocation-name invocation-directory)
   9787              start-plain
   9788              (or (buffer-file-name) ""))
   9789      (generate-new-buffer " *lsp-start-plain*"))))
   9790 
   9791 
   9792 
   9793 (provide 'lsp-mode)
   9794 ;;; lsp-mode.el ends here