config

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

lsp-mode.el (427693B)


      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-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 lsp-sqls
    191      lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform lsp-tex lsp-tilt
    192      lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-v lsp-vala lsp-verilog
    193      lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl lsp-xml lsp-yaml
    194      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     (ada-mode . "ada")
    805     (ada-ts-mode . "ada")
    806     (gpr-mode . "gpr")
    807     (gpr-ts-mode . "gpr")
    808     (awk-mode . "awk")
    809     (awk-ts-mode . "awk")
    810     (nxml-mode . "xml")
    811     (sql-mode . "sql")
    812     (vimrc-mode . "vim")
    813     (vimscript-ts-mode . "vim")
    814     (sh-mode . "shellscript")
    815     (bash-ts-mode . "shellscript")
    816     (ebuild-mode . "shellscript")
    817     (pkgbuild-mode . "shellscript")
    818     (envrc-file-mode . "shellscript")
    819     (scala-mode . "scala")
    820     (scala-ts-mode . "scala")
    821     (julia-mode . "julia")
    822     (julia-ts-mode . "julia")
    823     (clojure-mode . "clojure")
    824     (clojurec-mode . "clojure")
    825     (clojurescript-mode . "clojurescript")
    826     (clojure-ts-mode . "clojure")
    827     (clojure-ts-clojurec-mode . "clojure")
    828     (clojure-ts-clojurescript-mode . "clojurescript")
    829     (java-mode . "java")
    830     (java-ts-mode . "java")
    831     (jdee-mode . "java")
    832     (groovy-mode . "groovy")
    833     (python-mode . "python")
    834     (python-ts-mode . "python")
    835     (cython-mode . "python")
    836     ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo")
    837     (lsp--render-markdown . "markdown")
    838     (move-mode . "move")
    839     (rust-mode . "rust")
    840     (rust-ts-mode . "rust")
    841     (rustic-mode . "rust")
    842     (kotlin-mode . "kotlin")
    843     (kotlin-ts-mode . "kotlin")
    844     (css-mode . "css")
    845     (css-ts-mode . "css")
    846     (less-mode . "less")
    847     (less-css-mode . "less")
    848     (lua-mode . "lua")
    849     (lua-ts-mode . "lua")
    850     (sass-mode . "sass")
    851     (ssass-mode . "sass")
    852     (scss-mode . "scss")
    853     (scad-mode . "openscad")
    854     (xml-mode . "xml")
    855     (c-mode . "c")
    856     (c-ts-mode . "c")
    857     (c++-mode . "cpp")
    858     (c++-ts-mode . "cpp")
    859     (cuda-mode . "cuda")
    860     (objc-mode . "objective-c")
    861     (html-mode . "html")
    862     (html-ts-mode . "html")
    863     (sgml-mode . "html")
    864     (mhtml-mode . "html")
    865     (mint-mode . "mint")
    866     (go-dot-mod-mode . "go.mod")
    867     (go-mod-ts-mode . "go.mod")
    868     (go-mode . "go")
    869     (go-ts-mode . "go")
    870     (graphql-mode . "graphql")
    871     (haskell-mode . "haskell")
    872     (hack-mode . "hack")
    873     (php-mode . "php")
    874     (php-ts-mode . "php")
    875     (powershell-mode . "powershell")
    876     (powershell-mode . "PowerShell")
    877     (powershell-ts-mode . "powershell")
    878     (json-mode . "json")
    879     (json-ts-mode . "json")
    880     (jsonc-mode . "jsonc")
    881     (rjsx-mode . "javascript")
    882     (js2-mode . "javascript")
    883     (js-mode . "javascript")
    884     (js-ts-mode . "javascript")
    885     (typescript-mode . "typescript")
    886     (typescript-ts-mode . "typescript")
    887     (tsx-ts-mode . "typescriptreact")
    888     (svelte-mode . "svelte")
    889     (fsharp-mode . "fsharp")
    890     (reason-mode . "reason")
    891     (caml-mode . "ocaml")
    892     (tuareg-mode . "ocaml")
    893     (swift-mode . "swift")
    894     (elixir-mode . "elixir")
    895     (elixir-ts-mode . "elixir")
    896     (heex-ts-mode . "elixir")
    897     (conf-javaprop-mode . "spring-boot-properties")
    898     (yaml-mode . "yaml")
    899     (yaml-ts-mode . "yaml")
    900     (ruby-mode . "ruby")
    901     (enh-ruby-mode . "ruby")
    902     (ruby-ts-mode . "ruby")
    903     (fortran-mode . "fortran")
    904     (f90-mode . "fortran")
    905     (elm-mode . "elm")
    906     (dart-mode . "dart")
    907     (erlang-mode . "erlang")
    908     (dockerfile-mode . "dockerfile")
    909     (dockerfile-ts-mode . "dockerfile")
    910     (csharp-mode . "csharp")
    911     (csharp-tree-sitter-mode . "csharp")
    912     (csharp-ts-mode . "csharp")
    913     (plain-tex-mode . "plaintex")
    914     (context-mode . "context")
    915     (cypher-mode . "cypher")
    916     (latex-mode . "latex")
    917     (v-mode . "v")
    918     (vhdl-mode . "vhdl")
    919     (vhdl-ts-mode . "vhdl")
    920     (verilog-mode . "verilog")
    921     (terraform-mode . "terraform")
    922     (ess-julia-mode . "julia")
    923     (ess-r-mode . "r")
    924     (crystal-mode . "crystal")
    925     (nim-mode . "nim")
    926     (dhall-mode . "dhall")
    927     (cmake-mode . "cmake")
    928     (cmake-ts-mode . "cmake")
    929     (purescript-mode . "purescript")
    930     (gdscript-mode . "gdscript")
    931     (gdscript-ts-mode . "gdscript")
    932     (perl-mode . "perl")
    933     (cperl-mode . "perl")
    934     (robot-mode . "robot")
    935     (racket-mode . "racket")
    936     (nix-mode . "nix")
    937     (nix-ts-mode . "Nix")
    938     (prolog-mode . "prolog")
    939     (vala-mode . "vala")
    940     (actionscript-mode . "actionscript")
    941     (d-mode . "d")
    942     (zig-mode . "zig")
    943     (text-mode . "plaintext")
    944     (markdown-mode . "markdown")
    945     (gfm-mode . "markdown")
    946     (beancount-mode . "beancount")
    947     (conf-toml-mode . "toml")
    948     (toml-ts-mode . "toml")
    949     (org-mode . "org")
    950     (org-journal-mode . "org")
    951     (nginx-mode . "nginx")
    952     (magik-mode . "magik")
    953     (magik-ts-mode . "magik")
    954     (idris-mode . "idris")
    955     (idris2-mode . "idris2")
    956     (gleam-mode . "gleam")
    957     (graphviz-dot-mode . "dot")
    958     (tiltfile-mode . "tiltfile")
    959     (solidity-mode . "solidity")
    960     (bibtex-mode . "bibtex")
    961     (rst-mode . "restructuredtext")
    962     (glsl-mode . "glsl")
    963     (shader-mode . "shaderlab")
    964     (wgsl-mode . "wgsl")
    965     (jq-mode . "jq")
    966     (jq-ts-mode . "jq")
    967     (protobuf-mode . "protobuf")
    968     (nushell-mode . "nushell")
    969     (nushell-ts-mode . "nushell")
    970     (yang-mode . "yang"))
    971   "Language id configuration.")
    972 
    973 (defvar lsp--last-active-workspaces nil
    974   "Keep track of last active workspace.
    975 We want to try the last workspace first when jumping into a library
    976 directory")
    977 
    978 (defvar lsp-method-requirements
    979   '(("textDocument/callHierarchy" :capability :callHierarchyProvider)
    980     ("textDocument/codeAction" :capability :codeActionProvider)
    981     ("codeAction/resolve"
    982      :check-command (lambda (workspace)
    983                       (with-lsp-workspace workspace
    984                         (lsp:code-action-options-resolve-provider?
    985                          (lsp--capability-for-method "textDocument/codeAction")))))
    986     ("textDocument/codeLens" :capability :codeLensProvider)
    987     ("textDocument/completion" :capability :completionProvider)
    988     ("completionItem/resolve"
    989      :check-command (lambda (wk)
    990                       (with-lsp-workspace wk
    991                         (lsp:completion-options-resolve-provider?
    992                          (lsp--capability-for-method "textDocument/completion")))))
    993     ("textDocument/declaration" :capability :declarationProvider)
    994     ("textDocument/definition" :capability :definitionProvider)
    995     ("textDocument/documentColor" :capability :colorProvider)
    996     ("textDocument/documentLink" :capability :documentLinkProvider)
    997     ("textDocument/inlayHint" :capability :inlayHintProvider)
    998     ("textDocument/documentHighlight" :capability :documentHighlightProvider)
    999     ("textDocument/documentSymbol" :capability :documentSymbolProvider)
   1000     ("textDocument/foldingRange" :capability :foldingRangeProvider)
   1001     ("textDocument/formatting" :capability :documentFormattingProvider)
   1002     ("textDocument/hover" :capability :hoverProvider)
   1003     ("textDocument/implementation" :capability :implementationProvider)
   1004     ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider)
   1005     ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider)
   1006     ("textDocument/prepareRename"
   1007      :check-command (lambda (workspace)
   1008                       (with-lsp-workspace workspace
   1009                         (lsp:rename-options-prepare-provider?
   1010                          (lsp--capability-for-method "textDocument/rename")))))
   1011     ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider)
   1012     ("textDocument/references" :capability :referencesProvider)
   1013     ("textDocument/rename" :capability :renameProvider)
   1014     ("textDocument/selectionRange" :capability :selectionRangeProvider)
   1015     ("textDocument/semanticTokens" :capability :semanticTokensProvider)
   1016     ("textDocument/semanticTokensFull"
   1017      :check-command (lambda (workspace)
   1018                       (with-lsp-workspace workspace
   1019                         (lsp-get (lsp--capability :semanticTokensProvider) :full))))
   1020     ("textDocument/semanticTokensFull/Delta"
   1021      :check-command (lambda (workspace)
   1022                       (with-lsp-workspace workspace
   1023                         (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full)))
   1024                           (and (not (booleanp capFull)) (lsp-get capFull :delta))))))
   1025     ("textDocument/semanticTokensRangeProvider"
   1026      :check-command (lambda (workspace)
   1027                       (with-lsp-workspace workspace
   1028                         (lsp-get (lsp--capability :semanticTokensProvider) :range))))
   1029     ("textDocument/signatureHelp" :capability :signatureHelpProvider)
   1030     ("textDocument/typeDefinition" :capability :typeDefinitionProvider)
   1031     ("textDocument/typeHierarchy" :capability :typeHierarchyProvider)
   1032     ("workspace/executeCommand" :capability :executeCommandProvider)
   1033     ("workspace/symbol" :capability :workspaceSymbolProvider))
   1034 
   1035   "Map methods to requirements.
   1036 It is used by request-sending functions to determine which server
   1037 must be used for handling a particular message.")
   1038 
   1039 (defconst lsp--file-change-type
   1040   `((created . 1)
   1041     (changed . 2)
   1042     (deleted . 3)))
   1043 
   1044 (defconst lsp--watch-kind
   1045   `((create . 1)
   1046     (change . 2)
   1047     (delete . 4)))
   1048 
   1049 (defvar lsp-window-body-width 40
   1050   "Window body width when rendering doc.")
   1051 
   1052 (defface lsp-face-highlight-textual
   1053   '((t :inherit highlight))
   1054   "Face used for textual occurrences of symbols."
   1055   :group 'lsp-mode)
   1056 
   1057 (defface lsp-face-highlight-read
   1058   '((t :inherit highlight :underline t))
   1059   "Face used for highlighting symbols being read."
   1060   :group 'lsp-mode)
   1061 
   1062 (defface lsp-face-highlight-write
   1063   '((t :inherit highlight :weight bold))
   1064   "Face used for highlighting symbols being written to."
   1065   :group 'lsp-mode)
   1066 
   1067 (define-obsolete-variable-alias 'lsp-lens-auto-enable
   1068   'lsp-lens-enable "lsp-mode 7.0.1")
   1069 
   1070 (defcustom lsp-lens-enable t
   1071   "Auto enable lenses if server supports."
   1072   :group 'lsp-lens
   1073   :type 'boolean
   1074   :package-version '(lsp-mode . "6.3"))
   1075 
   1076 (defcustom lsp-symbol-highlighting-skip-current nil
   1077   "If non-nil skip current symbol when setting symbol highlights."
   1078   :group 'lsp-mode
   1079   :type 'boolean)
   1080 
   1081 (defcustom lsp-file-watch-threshold 1000
   1082   "Show warning if the files to watch are more than.
   1083 Set to nil to disable the warning."
   1084   :type 'number
   1085   :group 'lsp-mode)
   1086 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i))))
   1087 
   1088 (defvar lsp-custom-markup-modes
   1089   '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic"))
   1090   "Mode to uses with markdown code blocks.
   1091 They are added to `markdown-code-lang-modes'")
   1092 
   1093 (defcustom lsp-signature-render-documentation t
   1094   "Display signature documentation in `eldoc'."
   1095   :type 'boolean
   1096   :group 'lsp-mode
   1097   :package-version '(lsp-mode . "6.2"))
   1098 
   1099 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request)
   1100   "Auto activate signature conditions."
   1101   :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char)
   1102                          (const :tag "After selected completion." :after-completion)
   1103                          (const :tag "When the server has sent show signature help." :on-server-request)))
   1104   :group 'lsp-mode
   1105   :package-version '(lsp-mode . "6.2"))
   1106 
   1107 (defcustom lsp-signature-doc-lines 20
   1108   "If number, limit the number of lines to show in the docs."
   1109   :type 'number
   1110   :group 'lsp-mode
   1111   :package-version '(lsp-mode . "6.3"))
   1112 
   1113 (defcustom lsp-signature-function 'lsp-lv-message
   1114   "The function used for displaying signature info.
   1115 It will be called with one param - the signature info. When
   1116 called with nil the signature info must be cleared."
   1117   :type 'function
   1118   :group 'lsp-mode
   1119   :package-version '(lsp-mode . "6.3"))
   1120 
   1121 (defcustom lsp-keymap-prefix "s-l"
   1122   "LSP-mode keymap prefix."
   1123   :group 'lsp-mode
   1124   :type 'string
   1125   :package-version '(lsp-mode . "6.3"))
   1126 
   1127 (defvar-local lsp--buffer-workspaces ()
   1128   "List of the buffer workspaces.")
   1129 
   1130 (defvar-local lsp--buffer-deferred nil
   1131   "Whether buffer was loaded via `lsp-deferred'.")
   1132 
   1133 (defvar lsp--session nil
   1134   "Contain the `lsp-session' for the current Emacs instance.")
   1135 
   1136 (defvar lsp--tcp-port 10000)
   1137 
   1138 (defvar lsp--client-packages-required nil
   1139   "If nil, `lsp-client-packages' are yet to be required.")
   1140 
   1141 (defvar lsp--tcp-server-port 0
   1142   "The server socket which is opened when using `lsp-tcp-server' (a server
   1143 socket is opened in Emacs and the language server connects to it).  The
   1144 default value of 0 ensures that a random high port is used. Set it to a positive
   1145 integer to use a specific port.")
   1146 
   1147 (defvar lsp--tcp-server-wait-seconds 10
   1148   "Wait this amount of time for the client to connect to our server socket
   1149 when using `lsp-tcp-server'.")
   1150 
   1151 (defvar-local lsp--document-symbols nil
   1152   "The latest document symbols.")
   1153 
   1154 (defvar-local lsp--document-selection-range-cache nil
   1155   "The document selection cache.")
   1156 
   1157 (defvar-local lsp--document-symbols-request-async nil
   1158   "If non-nil, request document symbols asynchronously.")
   1159 
   1160 (defvar-local lsp--document-symbols-tick -1
   1161   "The value of `buffer-chars-modified-tick' when document
   1162   symbols were last retrieved.")
   1163 
   1164 (defvar-local lsp--have-document-highlights nil
   1165   "Set to `t' on symbol highlighting, cleared on
   1166 `lsp--cleanup-highlights-if-needed'. Checking a separately
   1167 defined flag is substantially faster than unconditionally
   1168 calling `remove-overlays'.")
   1169 
   1170 ;; Buffer local variable for storing number of lines.
   1171 (defvar lsp--log-lines)
   1172 
   1173 (defvar-local lsp--eldoc-saved-message nil)
   1174 
   1175 (defvar lsp--on-change-timer nil)
   1176 (defvar lsp--on-idle-timer nil)
   1177 
   1178 (defvar-local lsp--signature-last nil)
   1179 (defvar-local lsp--signature-last-index nil)
   1180 (defvar lsp--signature-last-buffer nil)
   1181 
   1182 (defvar-local lsp--virtual-buffer-point-max nil)
   1183 
   1184 (cl-defmethod lsp-execute-command (_server _command _arguments)
   1185   "Ask SERVER to execute COMMAND with ARGUMENTS.")
   1186 
   1187 (defun lsp-elt (sequence n)
   1188   "Return Nth element of SEQUENCE or nil if N is out of range."
   1189   (cond
   1190    ((listp sequence) (elt sequence n))
   1191    ((arrayp sequence)
   1192     (and (> (length sequence) n) (aref sequence n)))
   1193    (t (and (> (length sequence) n) (elt sequence n)))))
   1194 
   1195 ;; define seq-first and seq-rest for older emacs
   1196 (defun lsp-seq-first (sequence)
   1197   "Return the first element of SEQUENCE."
   1198   (lsp-elt sequence 0))
   1199 
   1200 (defun lsp-seq-rest (sequence)
   1201   "Return a sequence of the elements of SEQUENCE except the first one."
   1202   (seq-drop sequence 1))
   1203 
   1204 ;;;###autoload
   1205 (defun lsp--string-listp (sequence)
   1206   "Return t if all elements of SEQUENCE are strings, else nil."
   1207   (not (seq-find (lambda (x) (not (stringp x))) sequence)))
   1208 
   1209 (defun lsp--string-vector-p (candidate)
   1210   "Returns true if CANDIDATE is a vector data structure and
   1211 every element of it is of type string, else nil."
   1212   (and
   1213    (vectorp candidate)
   1214    (seq-every-p #'stringp candidate)))
   1215 
   1216 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0")
   1217 
   1218 (defun lsp--editable-vector-match (widget value)
   1219   "Function for `lsp-editable-vector' :match."
   1220   ;; Value must be a list or a vector and all the members must match the type.
   1221   (and (or (listp value) (vectorp value))
   1222        (length (cdr (lsp--editable-vector-match-inline widget value)))))
   1223 
   1224 (defun lsp--editable-vector-match-inline (widget value)
   1225   "Value for `lsp-editable-vector' :match-inline."
   1226   (let ((type (nth 0 (widget-get widget :args)))
   1227         (ok t)
   1228         found)
   1229     (while (and value ok)
   1230       (let ((answer (widget-match-inline type value)))
   1231         (if answer
   1232             (let ((head (if (vectorp answer) (aref answer 0) (car answer)))
   1233                   (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer))))
   1234               (setq found (append found head)
   1235                     value tail))
   1236           (setq ok nil))))
   1237     (cons found value)))
   1238 
   1239 (defun lsp--editable-vector-value-to-external (_widget internal-value)
   1240   "Convert the internal list value to a vector."
   1241   (if (listp internal-value)
   1242       (apply 'vector internal-value)
   1243     internal-value))
   1244 
   1245 (defun lsp--editable-vector-value-to-internal (_widget external-value)
   1246   "Convert the external vector value to a list."
   1247   (if (vectorp external-value)
   1248       (append external-value nil)
   1249     external-value))
   1250 
   1251 (define-widget 'lsp--editable-vector 'editable-list
   1252   "A subclass of `editable-list' that accepts and returns a
   1253 vector instead of a list."
   1254   :value-to-external 'lsp--editable-vector-value-to-external
   1255   :value-to-internal 'lsp--editable-vector-value-to-internal
   1256   :match 'lsp--editable-vector-match
   1257   :match-inline 'lsp--editable-vector-match-inline)
   1258 
   1259 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector
   1260   "A variable length homogeneous vector."
   1261   :tag "Repeat"
   1262   :format "%{%t%}:\n%v%i\n")
   1263 
   1264 (define-widget 'lsp-string-vector 'lazy
   1265   "A vector of zero or more elements, every element of which is a string.
   1266 Appropriate for any language-specific `defcustom' that needs to
   1267 serialize as a JSON array of strings.
   1268 
   1269 Deprecated. Use `lsp-repeatable-vector' instead. "
   1270   :offset 4
   1271   :tag "Vector"
   1272   :type '(lsp-repeatable-vector string))
   1273 
   1274 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0")
   1275 
   1276 (defvar lsp--show-message t
   1277   "If non-nil, show debug message from `lsp-mode'.")
   1278 
   1279 (defun lsp--message  (format &rest args)
   1280   "Wrapper for `message'
   1281 
   1282 We `inhibit-message' the message when the cursor is in the
   1283 minibuffer and when emacs version is before emacs 27 due to the
   1284 fact that we often use `lsp--info', `lsp--warn' and `lsp--error'
   1285 in async context and the call to these function is removing the
   1286 minibuffer prompt. The issue with async messages is already fixed
   1287 in emacs 27.
   1288 
   1289 See #2049"
   1290   (when lsp--show-message
   1291     (let ((inhibit-message (or inhibit-message
   1292                                (and (minibufferp)
   1293                                     (version< emacs-version "27.0")))))
   1294       (apply #'message format args))))
   1295 
   1296 (defun lsp--info (format &rest args)
   1297   "Display lsp info message with FORMAT with ARGS."
   1298   (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args)))
   1299 
   1300 (defun lsp--warn (format &rest args)
   1301   "Display lsp warn message with FORMAT with ARGS."
   1302   (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args)))
   1303 
   1304 (defun lsp--error (format &rest args)
   1305   "Display lsp error message with FORMAT with ARGS."
   1306   (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args)))
   1307 
   1308 (defun lsp-log (format &rest args)
   1309   "Log message to the ’*lsp-log*’ buffer.
   1310 
   1311 FORMAT and ARGS i the same as for `message'."
   1312   (when lsp-log-max
   1313     (let ((log-buffer (get-buffer "*lsp-log*"))
   1314           (inhibit-read-only t))
   1315       (unless log-buffer
   1316         (setq log-buffer (get-buffer-create "*lsp-log*"))
   1317         (with-current-buffer log-buffer
   1318           (buffer-disable-undo)
   1319           (view-mode 1)
   1320           (set (make-local-variable 'lsp--log-lines) 0)))
   1321       (with-current-buffer log-buffer
   1322         (save-excursion
   1323           (let* ((message (apply 'format format args))
   1324                  ;; Count newlines in message.
   1325                  (newlines (1+ (cl-loop with start = 0
   1326                                         for count from 0
   1327                                         while (string-match "\n" message start)
   1328                                         do (setq start (match-end 0))
   1329                                         finally return count))))
   1330             (goto-char (point-max))
   1331 
   1332             ;; in case the buffer is not empty insert before last \n to preserve
   1333             ;; the point position(in case it is in the end)
   1334             (if (eq (point) (point-min))
   1335                 (progn
   1336                   (insert "\n")
   1337                   (backward-char))
   1338               (backward-char)
   1339               (insert "\n"))
   1340             (insert message)
   1341 
   1342             (setq lsp--log-lines (+ lsp--log-lines newlines))
   1343 
   1344             (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max))
   1345               (let ((to-delete (- lsp--log-lines lsp-log-max)))
   1346                 (goto-char (point-min))
   1347                 (forward-line to-delete)
   1348                 (delete-region (point-min) (point))
   1349                 (setq lsp--log-lines lsp-log-max)))))))))
   1350 
   1351 (defalias 'lsp-message 'lsp-log)
   1352 
   1353 (defalias 'lsp-ht 'ht)
   1354 
   1355 (defalias 'lsp-file-local-name 'file-local-name)
   1356 
   1357 (defun lsp-f-canonical (file-name)
   1358   "Return the canonical FILE-NAME, without a trailing slash."
   1359   (directory-file-name (expand-file-name file-name)))
   1360 
   1361 (defalias 'lsp-canonical-file-name 'lsp-f-canonical)
   1362 
   1363 (defun lsp-f-same? (path-a path-b)
   1364   "Return t if PATH-A and PATH-B are references to the same file.
   1365 Symlinks are not followed."
   1366   (when (and (f-exists? path-a)
   1367              (f-exists? path-b))
   1368     (equal
   1369      (lsp-f-canonical (directory-file-name (f-expand path-a)))
   1370      (lsp-f-canonical (directory-file-name (f-expand path-b))))))
   1371 
   1372 (defun lsp-f-parent (path)
   1373   "Return the parent directory to PATH.
   1374 Symlinks are not followed."
   1375   (let ((parent (file-name-directory
   1376                  (directory-file-name (f-expand path default-directory)))))
   1377     (unless (lsp-f-same? path parent)
   1378       (if (f-relative? path)
   1379           (f-relative parent)
   1380         (directory-file-name parent)))))
   1381 
   1382 (defun lsp-f-ancestor-of? (path-a path-b)
   1383   "Return t if PATH-A is an ancestor of PATH-B.
   1384 Symlinks are not followed."
   1385   (unless (lsp-f-same? path-a path-b)
   1386     (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator))
   1387                (lsp-f-canonical path-b))))
   1388 
   1389 (defun lsp--merge-results (results method)
   1390   "Merge RESULTS by filtering the empty hash-tables and merging
   1391 the lists according to METHOD."
   1392   (pcase (--map (if (vectorp it)
   1393                     (append it nil) it)
   1394                 (-filter #'identity results))
   1395     (`() ())
   1396     ;; only one result - simply return it
   1397     (`(,fst) fst)
   1398     ;; multiple results merge it based on strategy
   1399     (results
   1400      (pcase method
   1401        ("textDocument/hover" (pcase (seq-filter
   1402                                      (-compose #'not #'lsp-empty?)
   1403                                      results)
   1404                                (`(,hover) hover)
   1405                                (hovers (lsp-make-hover
   1406                                         :contents
   1407                                         (-mapcat
   1408                                          (-lambda ((&Hover :contents))
   1409                                            (if (and (sequencep contents)
   1410                                                     (not (stringp contents)))
   1411                                                (append contents ())
   1412                                              (list contents)))
   1413                                          hovers)))))
   1414        ("textDocument/completion"
   1415         (lsp-make-completion-list
   1416          :is-incomplete (seq-some
   1417                          #'lsp:completion-list-is-incomplete
   1418                          results)
   1419          :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it)
   1420                                                     (lsp:completion-list-items it)
   1421                                                   it)
   1422                                                 nil))
   1423                            results)))
   1424        ("completionItem/resolve"
   1425         (let ((item (cl-first results)))
   1426           (when-let ((details (seq-filter #'identity
   1427                                           (seq-map #'lsp:completion-item-detail? results))))
   1428             (lsp:set-completion-item-detail?
   1429              item
   1430              (string-join details " ")))
   1431           (when-let ((docs (seq-filter #'identity
   1432                                        (seq-map #'lsp:completion-item-documentation? results))))
   1433             (lsp:set-completion-item-documentation?
   1434              item
   1435              (lsp-make-markup-content
   1436               :kind (or (seq-some (lambda (it)
   1437                                     (when (equal (lsp:markup-content-kind it)
   1438                                                  lsp/markup-kind-markdown)
   1439                                       lsp/markup-kind-markdown))
   1440                                   docs)
   1441                         lsp/markup-kind-plain-text)
   1442               :value (string-join (seq-map (lambda (doc)
   1443                                              (or (lsp:markup-content-value doc)
   1444                                                  (and (stringp doc) doc)))
   1445                                            docs)
   1446                                   "\n"))))
   1447           (when-let ((edits (seq-filter #'identity
   1448                                         (seq-map #'lsp:completion-item-additional-text-edits? results))))
   1449             (lsp:set-completion-item-additional-text-edits?
   1450              item
   1451              (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits)))
   1452           item))
   1453        (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results))))))
   1454 
   1455 (defun lsp--spinner-start ()
   1456   "Start spinner indication."
   1457   (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error)))
   1458 
   1459 (defun lsp--propertize (str type)
   1460   "Propertize STR as per TYPE."
   1461   (propertize str 'face (alist-get type lsp--message-type-face)))
   1462 
   1463 (defun lsp-workspaces ()
   1464   "Return the lsp workspaces associated with the current project."
   1465   (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces))
   1466 
   1467 (defun lsp--completing-read (prompt collection transform-fn &optional predicate
   1468                                     require-match initial-input
   1469                                     hist def inherit-input-method)
   1470   "Wrap `completing-read' to provide transformation function and disable sort.
   1471 
   1472 TRANSFORM-FN will be used to transform each of the items before displaying.
   1473 
   1474 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF
   1475 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes."
   1476   (let* ((col (--map (cons (funcall transform-fn it) it) collection))
   1477          (completion (completing-read prompt
   1478                                       (lambda (string pred action)
   1479                                         (if (eq action 'metadata)
   1480                                             `(metadata (display-sort-function . identity))
   1481                                           (complete-with-action action col string pred)))
   1482                                       predicate require-match initial-input hist
   1483                                       def inherit-input-method)))
   1484     (cdr (assoc completion col))))
   1485 
   1486 (defconst lsp--system-arch (lambda ()
   1487                              (setq lsp--system-arch
   1488                                    (pcase system-type
   1489                                      ('windows-nt
   1490                                       (pcase system-configuration
   1491                                         ((rx bol "x86_64-") 'x64)
   1492                                         (_ 'x86)))
   1493                                      ('darwin
   1494                                       (pcase system-configuration
   1495                                         ((rx "aarch64-") 'arm64)
   1496                                         (_ 'x64)))
   1497                                      ('gnu/linux
   1498                                        (pcase system-configuration
   1499                                          ((rx bol "x86_64") 'x64)
   1500                                          ((rx bol (| "i386" "i886")) 'x32)))
   1501                                      (_
   1502                                       (pcase system-configuration
   1503                                         ((rx bol "x86_64") 'x64)
   1504                                         ((rx bol (| "i386" "i886")) 'x32))))))
   1505   "Return the system architecture of `Emacs'.
   1506 Special values:
   1507   `x64'       64bit
   1508   `x32'       32bit
   1509   `arm64'     ARM 64bit")
   1510 
   1511 (defmacro lsp-with-current-buffer (buffer-id &rest body)
   1512   (declare (indent 1) (debug t))
   1513   `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer)))
   1514        (with-lsp-workspaces (plist-get ,buffer-id :workspaces)
   1515          (funcall wcb (lambda () ,@body)))
   1516      (with-current-buffer ,buffer-id
   1517        ,@body)))
   1518 
   1519 (defvar lsp--throw-on-input nil
   1520   "Make `lsp-*-while-no-input' throws `input' on interrupted.")
   1521 
   1522 (defmacro lsp--catch (tag bodyform &rest handlers)
   1523   "Catch TAG thrown in BODYFORM.
   1524 The return value from TAG will be handled in HANDLERS by `pcase'."
   1525   (declare (debug (form form &rest (pcase-PAT body))) (indent 2))
   1526   (let ((re-sym (make-symbol "re")))
   1527     `(let ((,re-sym (catch ,tag ,bodyform)))
   1528        (pcase ,re-sym
   1529          ,@handlers))))
   1530 
   1531 (defmacro lsp--while-no-input (&rest body)
   1532   "Wrap BODY in `while-no-input' and respecting `non-essential'.
   1533 If `lsp--throw-on-input' is set, will throw if input is pending, else
   1534 return value of `body' or nil if interrupted."
   1535   (declare (debug t) (indent 0))
   1536   `(if non-essential
   1537        (let ((res (while-no-input ,@body)))
   1538          (cond
   1539           ((and lsp--throw-on-input (equal res t))
   1540            (throw 'input :interrupted))
   1541           ((booleanp res) nil)
   1542           (t res)))
   1543      ,@body))
   1544 
   1545 ;; A ‘lsp--client’ object describes the client-side behavior of a language
   1546 ;; server.  It is used to start individual server processes, each of which is
   1547 ;; represented by a ‘lsp--workspace’ object.  Client objects are normally
   1548 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’.  Each
   1549 ;; workspace refers to exactly one client, but there can be multiple workspaces
   1550 ;; for a single client.
   1551 (cl-defstruct lsp--client
   1552   ;; ‘language-id’ is a function that receives a buffer as a single argument
   1553   ;; and should return the language identifier for that buffer.  See
   1554   ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem
   1555   ;; for a list of language identifiers.  Also consult the documentation for
   1556   ;; the language server represented by this client to find out what language
   1557   ;; identifiers it supports or expects.
   1558   (language-id nil)
   1559 
   1560   ;; ‘add-on?’ when set to t the server will be started no matter whether there
   1561   ;; is another server handling the same mode.
   1562   (add-on? nil)
   1563   ;; ‘new-connection’ is a function that should start a language server process
   1564   ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS).
   1565   ;; COMMAND-PROCESS must be a process object representing the server process
   1566   ;; just started.  COMMUNICATION-PROCESS must be a process (including pipe and
   1567   ;; network processes) that ‘lsp-mode’ uses to communicate with the language
   1568   ;; server using the language server protocol.  COMMAND-PROCESS and
   1569   ;; COMMUNICATION-PROCESS may be the same process; in that case
   1570   ;; ‘new-connection’ may also return that process as a single
   1571   ;; object. ‘new-connection’ is called with two arguments, FILTER and
   1572   ;; SENTINEL.  FILTER should be used as process filter for
   1573   ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for
   1574   ;; COMMAND-PROCESS.
   1575   (new-connection nil)
   1576 
   1577   ;; ‘ignore-regexps’ is a list of regexps.  When a data packet from the
   1578   ;; language server matches any of these regexps, it will be ignored.  This is
   1579   ;; intended for dealing with language servers that output non-protocol data.
   1580   (ignore-regexps nil)
   1581 
   1582   ;; ‘ignore-messages’ is a list of regexps.  When a message from the language
   1583   ;; server matches any of these regexps, it will be ignored.  This is useful
   1584   ;; for filtering out unwanted messages; such as servers that send nonstandard
   1585   ;; message types, or extraneous log messages.
   1586   (ignore-messages nil)
   1587 
   1588   ;; ‘notification-handlers’ is a hash table mapping notification method names
   1589   ;; (strings) to functions handling the respective notifications.  Upon
   1590   ;; receiving a notification, ‘lsp-mode’ will call the associated handler
   1591   ;; function passing two arguments, the ‘lsp--workspace’ object and the
   1592   ;; deserialized notification parameters.
   1593   (notification-handlers (make-hash-table :test 'equal))
   1594 
   1595   ;; ‘request-handlers’ is a hash table mapping request method names
   1596   ;; (strings) to functions handling the respective notifications.  Upon
   1597   ;; receiving a request, ‘lsp-mode’ will call the associated handler function
   1598   ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized
   1599   ;; request parameters.
   1600   (request-handlers (make-hash-table :test 'equal))
   1601 
   1602   ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request
   1603   ;; identifiers for pending asynchronous requests to functions handling the
   1604   ;; respective responses.  Upon receiving a response from the language server,
   1605   ;; ‘lsp-mode’ will call the associated response handler function with a
   1606   ;; single argument, the deserialized response parameters.
   1607   (response-handlers (make-hash-table :test 'eql))
   1608 
   1609   ;; ‘prefix-function’ is called for getting the prefix for completion.
   1610   ;; The function takes no parameter and returns a cons (start . end) representing
   1611   ;; the start and end bounds of the prefix. If it's not set, the client uses a
   1612   ;; default prefix function."
   1613   (prefix-function nil)
   1614 
   1615   ;; Contains mapping of scheme to the function that is going to be used to load
   1616   ;; the file.
   1617   (uri-handlers (make-hash-table :test #'equal))
   1618 
   1619   ;; ‘action-handlers’ is a hash table mapping action to a handler function. It
   1620   ;; can be used in `lsp-execute-code-action' to determine whether the action
   1621   ;; current client is interested in executing the action instead of sending it
   1622   ;; to the server.
   1623   (action-handlers (make-hash-table :test 'equal))
   1624 
   1625   ;; major modes supported by the client.
   1626   major-modes
   1627   ;; Function that will be called to decide if this language client
   1628   ;; should manage a particular buffer. The function will be passed
   1629   ;; the file name and major mode to inform the decision. Setting
   1630   ;; `activation-fn' will override `major-modes', if
   1631   ;; present.
   1632   activation-fn
   1633   ;; Break the tie when major-mode is supported by multiple clients.
   1634   (priority 0)
   1635   ;; Unique identifier for representing the client object.
   1636   server-id
   1637   ;; defines whether the client supports multi root workspaces.
   1638   multi-root
   1639   ;; Initialization options or a function that returns initialization options.
   1640   initialization-options
   1641   ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or
   1642   ;; completely replace, the faces used for semantic highlighting on a
   1643   ;; client-by-client basis.
   1644   ;;
   1645   ;; It recognizes four members, all of which are optional: `:types’ and
   1646   ;; `:modifiers’, respectively, should be face definition lists akin to
   1647   ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be
   1648   ;; merged with the default face definition list.
   1649   ;;
   1650   ;; Alternatively, if the plist members `:discard-default-types’ or
   1651   ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers'
   1652   ;; face definitions will be replaced entirely by their respective overrides.
   1653   ;;
   1654   ;; For example, setting `:semantic-tokens-faces-overrides' to
   1655   ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from
   1656   ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'.
   1657   ;;
   1658   ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))'
   1659   ;; will also remap "macro", but on top of that associate the fictional token type
   1660   ;; "not-quite-a-macro" with the face named `some-face'.
   1661   ;;
   1662   ;; `(:types (("macro" . font-lock-keyword-face))
   1663   ;;   :modifiers (("declaration" . lsp-face-semhl-interface))
   1664   ;;   :discard-default-types t
   1665   ;;   :discard-default-modifiers t)'
   1666   ;; will discard all default face definitions, hence leaving the client with
   1667   ;; only one token type "macro", mapped to `font-lock-keyword-face', and one
   1668   ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'.
   1669   semantic-tokens-faces-overrides
   1670   ;; Provides support for registering LSP Server specific capabilities.
   1671   custom-capabilities
   1672   ;; Function which returns the folders that are considered to be not projects but library files.
   1673   ;; The function accepts one parameter currently active workspace.
   1674   ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225.
   1675   library-folders-fn
   1676   ;; function which will be called when opening file in the workspace to perform
   1677   ;; client specific initialization. The function accepts one parameter
   1678   ;; currently active workspace.
   1679   before-file-open-fn
   1680   ;; Function which will be called right after a workspace has been initialized.
   1681   initialized-fn
   1682   ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP.
   1683   (remote? nil)
   1684 
   1685   ;; ‘completion-in-comments?’ t if the client supports completion in comments.
   1686   (completion-in-comments? nil)
   1687 
   1688   ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client.
   1689   (path->uri-fn nil)
   1690 
   1691   ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client.
   1692   (uri->path-fn nil)
   1693   ;; Function that returns an environment structure that will be used
   1694   ;; to set some environment variables when starting the language
   1695   ;; server process. These environment variables enable some
   1696   ;; additional features in the language server. The environment
   1697   ;; structure is an alist of the form (KEY . VALUE), where KEY is a
   1698   ;; string (regularly in all caps), and VALUE may be a string, a
   1699   ;; boolean, or a sequence of strings.
   1700   environment-fn
   1701 
   1702   ;; ‘after-open-fn’ workspace after open specific hooks.
   1703   (after-open-fn nil)
   1704 
   1705   ;; ‘async-request-handlers’ is a hash table mapping request method names
   1706   ;; (strings) to functions handling the respective requests that may take
   1707   ;; time to finish.  Upon receiving a request, ‘lsp-mode’ will call the
   1708   ;; associated handler function passing three arguments, the ‘lsp--workspace’
   1709   ;; object, the deserialized request parameters and the callback which accept
   1710   ;; result as its parameter.
   1711   (async-request-handlers (make-hash-table :test 'equal))
   1712   download-server-fn
   1713   download-in-progress?
   1714   buffers
   1715   synchronize-sections)
   1716 
   1717 (defun lsp-clients-executable-find (find-command &rest args)
   1718   "Finds an executable by invoking a search command.
   1719 
   1720 FIND-COMMAND is the executable finder that searches for the
   1721 actual language server executable. ARGS is a list of arguments to
   1722 give to FIND-COMMAND to find the language server.  Returns the
   1723 output of FIND-COMMAND if it exits successfully, nil otherwise.
   1724 
   1725 Typical uses include finding an executable by invoking `find' in
   1726 a project, finding LLVM commands on macOS with `xcrun', or
   1727 looking up project-specific language servers for projects written
   1728 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv'
   1729 etc."
   1730   (when-let* ((find-command-path (executable-find find-command))
   1731               (executable-path
   1732                (with-temp-buffer
   1733                  (when (zerop (apply 'call-process find-command-path nil t nil args))
   1734                    (buffer-substring-no-properties (point-min) (point-max))))))
   1735     (string-trim executable-path)))
   1736 
   1737 (defvar lsp--already-widened nil)
   1738 
   1739 (defmacro lsp-save-restriction-and-excursion (&rest form)
   1740   (declare (indent 0) (debug t))
   1741   `(if lsp--already-widened
   1742        (save-excursion ,@form)
   1743      (-let [lsp--already-widened t]
   1744        (save-restriction
   1745          (widen)
   1746          (save-excursion ,@form)))))
   1747 
   1748 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number
   1749 (defun lsp--line-character-to-point (line character)
   1750   "Return the point for character CHARACTER on line LINE."
   1751   (or (lsp-virtual-buffer-call :line/character->point line character)
   1752       (let ((inhibit-field-text-motion t))
   1753         (lsp-save-restriction-and-excursion
   1754           (goto-char (point-min))
   1755           (forward-line line)
   1756           ;; server may send character position beyond the current line and we
   1757           ;; should fallback to line end.
   1758           (-let [line-end (line-end-position)]
   1759             (if (> character (- line-end (point)))
   1760                 line-end
   1761               (forward-char character)
   1762               (point)))))))
   1763 
   1764 (lsp-defun lsp--position-to-point ((&Position :line :character))
   1765   "Convert `Position' object in PARAMS to a point."
   1766   (lsp--line-character-to-point line character))
   1767 
   1768 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end))
   1769   (cons start end))
   1770 
   1771 (lsp-defun lsp--range-text ((&RangeToPoint :start :end))
   1772   (buffer-substring start end))
   1773 
   1774 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end)))
   1775   (cond
   1776    ((and
   1777      (region-active-p)
   1778      (<= start (region-beginning) end)
   1779      (<= start (region-end) end)
   1780      (or (not (= start (region-beginning)))
   1781          (not (= end (region-end)))))
   1782     (cons start end))
   1783    ((and (<= start (point) end)
   1784          (not (region-active-p)))
   1785     (cons start end))
   1786    (parent? (lsp--find-wrapping-range parent?))))
   1787 
   1788 (defun lsp--get-selection-range ()
   1789   (or
   1790    (-when-let ((cache . cache-tick) lsp--document-selection-range-cache)
   1791      (when (= cache-tick (buffer-modified-tick)) cache))
   1792    (let ((response (cl-first
   1793                     (lsp-request
   1794                      "textDocument/selectionRange"
   1795                      (list :textDocument (lsp--text-document-identifier)
   1796                            :positions (vector (lsp--cur-position)))))))
   1797      (setq lsp--document-selection-range-cache
   1798            (cons response (buffer-modified-tick)))
   1799      response)))
   1800 
   1801 (defun lsp-extend-selection ()
   1802   "Extend selection."
   1803   (interactive)
   1804   (unless (lsp-feature? "textDocument/selectionRange")
   1805     (signal 'lsp-capability-not-supported (list "selectionRangeProvider")))
   1806   (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range)))
   1807     (goto-char start)
   1808     (set-mark (point))
   1809     (goto-char end)
   1810     (exchange-point-and-mark)))
   1811 
   1812 (defun lsp-warn (message &rest args)
   1813   "Display a warning message made from (`format-message' MESSAGE ARGS...).
   1814 This is equivalent to `display-warning', using `lsp-mode' as the type and
   1815 `:warning' as the level."
   1816   (display-warning 'lsp-mode (apply #'format-message message args)))
   1817 
   1818 (defun lsp--get-uri-handler (scheme)
   1819   "Get uri handler for SCHEME in the current workspace."
   1820   (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it)))
   1821           (or (lsp-workspaces) (lsp--session-workspaces (lsp-session)))))
   1822 
   1823 (defun lsp--fix-path-casing (path)
   1824   "On windows, downcases path because the windows file system is
   1825 case-insensitive.
   1826 
   1827 On other systems, returns path without change."
   1828   (if (eq system-type 'windows-nt) (downcase path) path))
   1829 
   1830 (defun lsp--uri-to-path (uri)
   1831   "Convert URI to a file path."
   1832   (if-let ((fn (->> (lsp-workspaces)
   1833                     (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client))
   1834                     (cl-first))))
   1835       (funcall fn uri)
   1836     (lsp--uri-to-path-1 uri)))
   1837 
   1838 (defun lsp-remap-path-if-needed (file-name)
   1839   (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings))
   1840       (propertize (buffer-local-value 'buffer-file-name buffer)
   1841                   'lsp-virtual-buffer virtual-buffer)
   1842     file-name))
   1843 
   1844 (defun lsp--uri-to-path-1 (uri)
   1845   "Convert URI to a file path."
   1846   (let* ((url (url-generic-parse-url (url-unhex-string uri)))
   1847          (type (url-type url))
   1848          (target (url-target url))
   1849          (file
   1850           (concat (decode-coding-string (url-filename url)
   1851                                         (or locale-coding-system 'utf-8))
   1852                   (when (and target
   1853                              (not (s-match
   1854                                    (rx "#" (group (1+ num)) (or "," "#")
   1855                                        (group (1+ num))
   1856                                        string-end)
   1857                                    uri)))
   1858                     (concat "#" target))))
   1859          (file-name (if (and type (not (string= type "file")))
   1860                         (if-let ((handler (lsp--get-uri-handler type)))
   1861                             (funcall handler uri)
   1862                           uri)
   1863                       ;; `url-generic-parse-url' is buggy on windows:
   1864                       ;; https://github.com/emacs-lsp/lsp-mode/pull/265
   1865                       (or (and (eq system-type 'windows-nt)
   1866                                (eq (elt file 0) ?\/)
   1867                                (substring file 1))
   1868                           file))))
   1869     (->> file-name
   1870          (concat (-some #'lsp--workspace-host-root (lsp-workspaces)))
   1871          (lsp-remap-path-if-needed))))
   1872 
   1873 (defun lsp--buffer-uri ()
   1874   "Return URI of the current buffer."
   1875   (or lsp-buffer-uri
   1876       (plist-get lsp--virtual-buffer :buffer-uri)
   1877       (lsp--path-to-uri
   1878        (or (buffer-file-name) (buffer-file-name (buffer-base-buffer))))))
   1879 
   1880 (defun lsp-register-client-capabilities (&rest _args)
   1881   "Implemented only to make `company-lsp' happy.
   1882 DELETE when `lsp-mode.el' is deleted.")
   1883 
   1884 (defconst lsp--url-path-allowed-chars
   1885   (url--allowed-chars (append '(?/) url-unreserved-chars))
   1886   "`url-unreserved-chars' with additional delim ?/.
   1887 This set of allowed chars is enough for hexifying local file paths.")
   1888 
   1889 (defun lsp--path-to-uri-1 (path)
   1890   (concat lsp--uri-file-prefix
   1891           (--> path
   1892             (expand-file-name it)
   1893             (or (file-remote-p it 'localname t) it)
   1894             (url-hexify-string it lsp--url-path-allowed-chars))))
   1895 
   1896 (defun lsp--path-to-uri (path)
   1897   "Convert PATH to a uri."
   1898   (if-let ((uri-fn (->> (lsp-workspaces)
   1899                         (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client))
   1900                         (cl-first))))
   1901       (funcall uri-fn path)
   1902     (lsp--path-to-uri-1 path)))
   1903 
   1904 (defun lsp--string-match-any (regex-list str)
   1905   "Return the first regex, if any, within REGEX-LIST matching STR."
   1906   (--first (string-match it str) regex-list))
   1907 
   1908 (cl-defstruct lsp-watch
   1909   (descriptors (make-hash-table :test 'equal))
   1910   root-directory)
   1911 
   1912 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories)
   1913   (let ((file-name (cl-third event))
   1914         (event-type (cl-second event)))
   1915     (cond
   1916      ((and (file-directory-p file-name)
   1917            (equal 'created event-type)
   1918            (not (lsp--string-match-any ignored-directories file-name)))
   1919 
   1920       (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch)
   1921 
   1922       ;; process the files that are already present in
   1923       ;; the directory.
   1924       (->> (directory-files-recursively file-name ".*" t)
   1925            (seq-do (lambda (f)
   1926                      (unless (file-directory-p f)
   1927                        (funcall callback (list nil 'created f)))))))
   1928      ((and (memq event-type '(created deleted changed))
   1929            (not (file-directory-p file-name))
   1930            (not (lsp--string-match-any ignored-files file-name)))
   1931       (funcall callback event))
   1932      ((and (memq event-type '(renamed))
   1933            (not (file-directory-p file-name))
   1934            (not (lsp--string-match-any ignored-files file-name)))
   1935       (funcall callback `(,(cl-first event) deleted ,(cl-third event)))
   1936       (funcall callback `(,(cl-first event) created ,(cl-fourth event)))))))
   1937 
   1938 (defun lsp--ask-about-watching-big-repo (number-of-directories dir)
   1939   "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR.
   1940 This is useful when there is a lot of files in a repository, as
   1941 that may slow Emacs down. Returns t if the user wants to watch
   1942 the entire repository, nil otherwise."
   1943   (prog1
   1944       (yes-or-no-p
   1945        (format
   1946         "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down.
   1947 Do you want to watch all files in %s? "
   1948         dir
   1949         number-of-directories
   1950         dir))
   1951     (lsp--info
   1952      (concat "You can configure this warning with the `lsp-enable-file-watchers' "
   1953              "and `lsp-file-watch-threshold' variables"))))
   1954 
   1955 
   1956 (defun lsp--path-is-watchable-directory (path dir ignored-directories)
   1957   "Figure out whether PATH (inside of DIR) is meant to have a file watcher set.
   1958 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't
   1959 want to watch."
   1960   (let
   1961       ((full-path (f-join dir path)))
   1962     (and (file-accessible-directory-p full-path)
   1963          (not (equal path "."))
   1964          (not (equal path ".."))
   1965          (not (lsp--string-match-any ignored-directories full-path)))))
   1966 
   1967 
   1968 (defun lsp--all-watchable-directories (dir ignored-directories)
   1969   "Traverse DIR recursively returning a list of paths that should have watchers.
   1970 IGNORED-DIRECTORIES will be used for exclusions"
   1971   (let* ((dir (if (f-symlink? dir)
   1972                   (file-truename dir)
   1973                 dir)))
   1974     (apply #'nconc
   1975            ;; the directory itself is assumed to be part of the set
   1976            (list dir)
   1977            ;; collect all subdirectories that are watchable
   1978            (-map
   1979             (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories))
   1980             ;; but only look at subdirectories that are watchable
   1981             (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories))
   1982                      (directory-files dir))))))
   1983 
   1984 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?)
   1985   "Create recursive file notification watch in DIR.
   1986 CALLBACK will be called when there are changes in any of
   1987 the monitored files. WATCHES is a hash table directory->file
   1988 notification handle which contains all of the watch that
   1989 already have been created. Watches will not be created for
   1990 any directory that matches any regex in IGNORED-DIRECTORIES.
   1991 Watches will not be created for any file that matches any
   1992 regex in IGNORED-FILES."
   1993   (let* ((dir (if (f-symlink? dir)
   1994                   (file-truename dir)
   1995                 dir))
   1996          (watch (or watch (make-lsp-watch :root-directory dir)))
   1997          (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories)))
   1998     (lsp-log "Creating watchers for following %s folders:\n  %s"
   1999              (length dirs-to-watch)
   2000              (s-join "\n  " dirs-to-watch))
   2001     (when (or
   2002            (not warn-big-repo?)
   2003            (not lsp-file-watch-threshold)
   2004            (let ((number-of-directories (length dirs-to-watch)))
   2005              (or
   2006               (< number-of-directories lsp-file-watch-threshold)
   2007               (condition-case nil
   2008                   (lsp--ask-about-watching-big-repo number-of-directories dir)
   2009                 (quit)))))
   2010       (dolist (current-dir dirs-to-watch)
   2011         (condition-case err
   2012             (progn
   2013               (puthash
   2014                current-dir
   2015                (file-notify-add-watch current-dir
   2016                                       '(change)
   2017                                       (lambda (event)
   2018                                         (lsp--folder-watch-callback event callback watch ignored-files ignored-directories)))
   2019                (lsp-watch-descriptors watch)))
   2020           (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))
   2021           (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err))))))
   2022     watch))
   2023 
   2024 (defun lsp-kill-watch (watch)
   2025   "Delete WATCH."
   2026   (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch))
   2027   (ht-clear! (lsp-watch-descriptors watch)))
   2028 
   2029 (defun lsp-json-bool (val)
   2030   "Convert VAL to JSON boolean."
   2031   (if val t :json-false))
   2032 
   2033 (defmacro with-lsp-workspace (workspace &rest body)
   2034   "Helper macro for invoking BODY in WORKSPACE context."
   2035   (declare (debug (form body))
   2036            (indent 1))
   2037   `(let ((lsp--cur-workspace ,workspace)) ,@body))
   2038 
   2039 (defmacro with-lsp-workspaces (workspaces &rest body)
   2040   "Helper macro for invoking BODY against multiple WORKSPACES."
   2041   (declare (debug (form body))
   2042            (indent 1))
   2043   `(let ((lsp--buffer-workspaces ,workspaces)) ,@body))
   2044 
   2045 
   2046 
   2047 (defmacro lsp-consistency-check (package)
   2048   `(defconst ,(intern (concat (symbol-name package)
   2049                               "-plist-value-when-compiled"))
   2050      (eval-when-compile lsp-use-plists)))
   2051 
   2052 
   2053 ;; loading code-workspace files
   2054 
   2055 ;;;###autoload
   2056 (defun lsp-load-vscode-workspace (file)
   2057   "Load vscode workspace from FILE"
   2058   (interactive "fSelect file to import: ")
   2059   (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session)))
   2060 
   2061   (let ((dir (f-dirname file)))
   2062     (->> file
   2063          (json-read-file)
   2064          (alist-get 'folders)
   2065          (-map (-lambda ((&alist 'path))
   2066                  (lsp-workspace-folders-add (expand-file-name path dir)))))))
   2067 
   2068 ;;;###autoload
   2069 (defun lsp-save-vscode-workspace (file)
   2070   "Save vscode workspace to FILE"
   2071   (interactive "FSelect file to save to: ")
   2072 
   2073   (let ((json-encoding-pretty-print t))
   2074     (f-write-text (json-encode
   2075                    `((folders . ,(->> (lsp-session)
   2076                                       (lsp-session-folders)
   2077                                       (--map `((path . ,it)))))))
   2078                   'utf-8
   2079                   file)))
   2080 
   2081 
   2082 (defmacro lsp-foreach-workspace (&rest body)
   2083   "Execute BODY for each of the current workspaces."
   2084   (declare (debug (form body)))
   2085   `(--map (with-lsp-workspace it ,@body) (lsp-workspaces)))
   2086 
   2087 (defmacro when-lsp-workspace (workspace &rest body)
   2088   "Helper macro for invoking BODY in WORKSPACE context if present."
   2089   (declare (debug (form body))
   2090            (indent 1))
   2091   `(when-let ((lsp--cur-workspace ,workspace)) ,@body))
   2092 
   2093 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items))
   2094   (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read))
   2095             (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label))
   2096                                  items))
   2097             (result (funcall-interactively
   2098                      selectfunc
   2099                      (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels))
   2100             (choices (if (listp result)
   2101                          (if (equal result '("*"))
   2102                              itemLabels
   2103                            result)
   2104                        (list result))))
   2105       (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data))
   2106                                                  (if (member label choices)
   2107                                                      (lsp-make-quick-pick-item :label label :picked t :user-data user-data)
   2108                                                    nil))
   2109                                                items)))))
   2110 
   2111 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?))
   2112   (read-string (format "%s: " prompt) (or value? "")))
   2113 
   2114 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type))
   2115   "Send the server's messages to log.
   2116 PARAMS - the data sent from _WORKSPACE."
   2117   (funcall (cl-case type
   2118              (1 'lsp--error)
   2119              (2 'lsp--warn)
   2120              (t 'lsp--info))
   2121            "%s"
   2122            message))
   2123 
   2124 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type))
   2125   "Send the server's messages to log.
   2126 PARAMS - the data sent from WORKSPACE."
   2127   (ignore
   2128    (let ((client (lsp--workspace-client workspace)))
   2129      (when (or (not client)
   2130                (cl-notany (-rpartial #'string-match-p message)
   2131                           (lsp--client-ignore-messages client)))
   2132        (lsp-log "%s" (lsp--propertize message type))))))
   2133 
   2134 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?))
   2135   "Display a message request to user sending the user selection back to server."
   2136   (let* ((message (lsp--propertize message type))
   2137          (choices (seq-map #'lsp:message-action-item-title actions?)))
   2138     (if choices
   2139         (completing-read (concat message " ") (seq-into choices 'list) nil t)
   2140       (lsp-log message))))
   2141 
   2142 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?))
   2143   "Show document URI in a buffer and go to SELECTION if any."
   2144   (let ((path (lsp--uri-to-path uri)))
   2145     (when (f-exists? path)
   2146       (with-current-buffer (find-file path)
   2147         (when selection?
   2148           (goto-char (lsp--position-to-point (lsp:range-start selection?))))
   2149         t))))
   2150 
   2151 (defcustom lsp-progress-prefix " ⌛ "
   2152   "Progress prefix."
   2153   :group 'lsp-mode
   2154   :type 'string
   2155   :package-version '(lsp-mode . "8.0.0"))
   2156 
   2157 (defcustom lsp-progress-function #'lsp-on-progress-modeline
   2158   "Function for handling the progress notifications."
   2159   :group 'lsp-mode
   2160   :type '(choice
   2161           (const :tag "Use modeline" lsp-on-progress-modeline)
   2162           (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')"
   2163                  lsp-on-progress-legacy)
   2164           (const :tag "Ignore" ignore)
   2165           (function :tag "Other function"))
   2166   :package-version '(lsp-mode . "8.0.0"))
   2167 
   2168 (defcustom lsp-request-while-no-input-may-block nil
   2169   "Have `lsp-request-while-no-input` block unless `non-essential` is t."
   2170   :group 'lsp-mode
   2171   :type 'boolean)
   2172 
   2173 (defun lsp--progress-status ()
   2174   "Returns the status of the progress for the current workspaces."
   2175   (-let ((progress-status
   2176           (s-join
   2177            "|"
   2178            (-keep
   2179             (lambda (workspace)
   2180               (let ((tokens (lsp--workspace-work-done-tokens workspace)))
   2181                 (unless (ht-empty? tokens)
   2182                   (mapconcat
   2183                    (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?))
   2184                      (concat (if percentage?
   2185                                  (if (numberp percentage?)
   2186                                      (format "%.0f%%%% " percentage?)
   2187                                    (format "%s%%%% " percentage?))
   2188                                "")
   2189                              (or message? title)))
   2190                    (ht-values tokens)
   2191                    "|"))))
   2192             (lsp-workspaces)))))
   2193     (unless (s-blank? progress-status)
   2194       (concat lsp-progress-prefix progress-status))))
   2195 
   2196 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value
   2197                                                                 (value &as &WorkDoneProgress :kind)))
   2198   "PARAMS contains the progress data.
   2199 WORKSPACE is the workspace that contains the progress token."
   2200   (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status))))
   2201   (pcase kind
   2202     ("begin" (lsp-workspace-set-work-done-token token value workspace))
   2203     ("report" (lsp-workspace-set-work-done-token token value workspace))
   2204     ("end" (lsp-workspace-rem-work-done-token token workspace)))
   2205   (force-mode-line-update))
   2206 
   2207 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value
   2208                                                               (value &as &WorkDoneProgress :kind)))
   2209   "PARAMS contains the progress data.
   2210 WORKSPACE is the workspace that contains the progress token."
   2211   (pcase kind
   2212     ("begin"
   2213      (-let* (((&WorkDoneProgressBegin :title :percentage?) value)
   2214              (reporter
   2215               (if lsp-progress-via-spinner
   2216                   (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types))
   2217                          ;; Set message as a tooltip for the spinner strings
   2218                          (propertized-strings
   2219                           (seq-map (lambda (string) (propertize string 'help-echo title))
   2220                                    spinner-strings))
   2221                          (spinner-type (vconcat propertized-strings)))
   2222                     ;; The progress relates to the server as a whole,
   2223                     ;; display it on all buffers.
   2224                     (mapcar (lambda (buffer)
   2225                               (lsp-with-current-buffer buffer
   2226                                 (spinner-start spinner-type))
   2227                               buffer)
   2228                             (lsp--workspace-buffers workspace)))
   2229                 (if percentage?
   2230                     (make-progress-reporter title 0 100 percentage?)
   2231                   ;; No percentage, just progress
   2232                   (make-progress-reporter title nil nil)))))
   2233        (lsp-workspace-set-work-done-token token reporter workspace)))
   2234     ("report"
   2235      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2236        (unless lsp-progress-via-spinner
   2237          (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value)))))
   2238 
   2239     ("end"
   2240      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2241        (if lsp-progress-via-spinner
   2242            (mapc (lambda (buffer)
   2243                    (when (lsp-buffer-live-p buffer)
   2244                      (lsp-with-current-buffer buffer
   2245                        (spinner-stop))))
   2246                  reporter)
   2247          (progress-reporter-done reporter))
   2248        (lsp-workspace-rem-work-done-token token workspace)))))
   2249 
   2250 
   2251 ;; diagnostics
   2252 
   2253 (defvar lsp-diagnostic-filter nil
   2254   "A a function which will be called with
   2255   `&PublishDiagnosticsParams' and `workspace' which can be used
   2256   to filter out the diagnostics. The function should return
   2257   `&PublishDiagnosticsParams'.
   2258 
   2259 Common usecase are:
   2260 1. Filter the diagnostics for a particular language server.
   2261 2. Filter out the diagnostics under specific level.")
   2262 
   2263 (defvar lsp-diagnostic-stats (ht))
   2264 
   2265 (defun lsp-diagnostics (&optional current-workspace?)
   2266   "Return the diagnostics from all workspaces."
   2267   (or (pcase (if current-workspace?
   2268                  (lsp-workspaces)
   2269                (lsp--session-workspaces (lsp-session)))
   2270         (`() ())
   2271         (`(,workspace) (lsp--workspace-diagnostics workspace))
   2272         (`,workspaces (let ((result (make-hash-table :test 'equal)))
   2273                         (mapc (lambda (workspace)
   2274                                 (->> workspace
   2275                                      (lsp--workspace-diagnostics)
   2276                                      (maphash (lambda (file-name diagnostics)
   2277                                                 (puthash file-name
   2278                                                          (append (gethash file-name result) diagnostics)
   2279                                                          result)))))
   2280                               workspaces)
   2281                         result)))
   2282       (ht)))
   2283 
   2284 (defun lsp-diagnostics-stats-for (path)
   2285   "Get diagnostics statistics for PATH.
   2286 The result format is vector [_ errors warnings infos hints] or nil."
   2287   (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats))
   2288 
   2289 (defun lsp-diagnostics--update-path (path new-stats)
   2290   (let ((new-stats (copy-sequence new-stats))
   2291         (path (lsp--fix-path-casing (directory-file-name path))))
   2292     (if-let ((old-data (gethash path lsp-diagnostic-stats)))
   2293         (dotimes (idx 5)
   2294           (cl-callf + (aref old-data idx)
   2295             (aref new-stats idx)))
   2296       (puthash path new-stats lsp-diagnostic-stats))))
   2297 
   2298 (lsp-defun lsp--on-diagnostics-update-stats (workspace
   2299                                              (&PublishDiagnosticsParams :uri :diagnostics))
   2300   (let ((path (lsp--fix-path-casing (lsp--uri-to-path uri)))
   2301         (new-stats (make-vector 5 0)))
   2302     (mapc (-lambda ((&Diagnostic :severity?))
   2303             (cl-incf (aref new-stats (or severity? 1))))
   2304           diagnostics)
   2305     (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace))))
   2306       (mapc (-lambda ((&Diagnostic :severity?))
   2307               (cl-decf (aref new-stats (or severity? 1))))
   2308             old-diags))
   2309     (lsp-diagnostics--update-path path new-stats)
   2310     (while (not (string= path (setf path (file-name-directory
   2311                                           (directory-file-name path)))))
   2312       (lsp-diagnostics--update-path path new-stats))))
   2313 
   2314 (defun lsp--on-diagnostics (workspace params)
   2315   "Callback for textDocument/publishDiagnostics.
   2316 interface PublishDiagnosticsParams {
   2317     uri: string;
   2318     diagnostics: Diagnostic[];
   2319 }
   2320 PARAMS contains the diagnostics data.
   2321 WORKSPACE is the workspace that contains the diagnostics."
   2322   (when lsp-diagnostic-filter
   2323     (setf params (funcall lsp-diagnostic-filter params workspace)))
   2324 
   2325   (lsp--on-diagnostics-update-stats workspace params)
   2326 
   2327   (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params)
   2328           (lsp--virtual-buffer-mappings (ht))
   2329           (file (lsp--fix-path-casing (lsp--uri-to-path uri)))
   2330           (workspace-diagnostics (lsp--workspace-diagnostics workspace)))
   2331 
   2332     (if (seq-empty-p diagnostics)
   2333         (remhash file workspace-diagnostics)
   2334       (puthash file (append diagnostics nil) workspace-diagnostics))
   2335 
   2336     (run-hooks 'lsp-diagnostics-updated-hook)))
   2337 
   2338 (defun lsp-diagnostics--workspace-cleanup (workspace)
   2339   (->> workspace
   2340        (lsp--workspace-diagnostics)
   2341        (maphash (lambda (key _)
   2342                   (lsp--on-diagnostics-update-stats
   2343                    workspace
   2344                    (lsp-make-publish-diagnostics-params
   2345                     :uri (lsp--path-to-uri key)
   2346                     :diagnostics [])))))
   2347   (clrhash (lsp--workspace-diagnostics workspace)))
   2348 
   2349 
   2350 
   2351 ;; textDocument/foldingRange support
   2352 
   2353 (cl-defstruct lsp--folding-range beg end kind children)
   2354 
   2355 (defvar-local lsp--cached-folding-ranges nil)
   2356 (defvar-local lsp--cached-nested-folding-ranges nil)
   2357 
   2358 (defun lsp--folding-range-width (range)
   2359   (- (lsp--folding-range-end range)
   2360      (lsp--folding-range-beg range)))
   2361 
   2362 (defun lsp--get-folding-ranges ()
   2363   "Get the folding ranges for the current buffer."
   2364   (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges))
   2365     (let* ((ranges (lsp-request "textDocument/foldingRange"
   2366                                 `(:textDocument ,(lsp--text-document-identifier))))
   2367            (sorted-line-col-pairs (->> ranges
   2368                                        (cl-mapcan (-lambda ((&FoldingRange :start-line
   2369                                                                            :start-character?
   2370                                                                            :end-line
   2371                                                                            :end-character?))
   2372                                                     (list (cons start-line start-character?)
   2373                                                           (cons end-line end-character?))))
   2374                                        (-sort #'lsp--line-col-comparator)))
   2375            (line-col-to-point-map (lsp--convert-line-col-to-points-batch
   2376                                    sorted-line-col-pairs)))
   2377       (setq lsp--cached-folding-ranges
   2378             (cons (buffer-chars-modified-tick)
   2379                   (--> ranges
   2380                     (seq-map (-lambda ((range &as
   2381                                               &FoldingRange :start-line
   2382                                               :start-character?
   2383                                               :end-line
   2384                                               :end-character?
   2385                                               :kind?))
   2386                                (make-lsp--folding-range
   2387                                 :beg (ht-get line-col-to-point-map
   2388                                              (cons start-line start-character?))
   2389                                 :end (ht-get line-col-to-point-map
   2390                                              (cons end-line end-character?))
   2391                                 :kind kind?))
   2392                              it)
   2393                     (seq-filter (lambda (folding-range)
   2394                                   (< (lsp--folding-range-beg folding-range)
   2395                                      (lsp--folding-range-end folding-range)))
   2396                                 it)
   2397                     (seq-into it 'list)
   2398                     (delete-dups it))))))
   2399   (cdr lsp--cached-folding-ranges))
   2400 
   2401 (defun lsp--get-nested-folding-ranges ()
   2402   "Get a list of nested folding ranges for the current buffer."
   2403   (-let [(tick . _) lsp--cached-folding-ranges]
   2404     (if (and (eq tick (buffer-chars-modified-tick))
   2405              lsp--cached-nested-folding-ranges)
   2406         lsp--cached-nested-folding-ranges
   2407       (setq lsp--cached-nested-folding-ranges
   2408             (lsp--folding-range-build-trees (lsp--get-folding-ranges))))))
   2409 
   2410 (defun lsp--folding-range-build-trees (ranges)
   2411   (setq ranges (seq-sort #'lsp--range-before-p ranges))
   2412   (let* ((dummy-node (make-lsp--folding-range
   2413                       :beg most-negative-fixnum
   2414                       :end most-positive-fixnum))
   2415          (stack (list dummy-node)))
   2416     (dolist (range ranges)
   2417       (while (not (lsp--range-inside-p range (car stack)))
   2418         (pop stack))
   2419       (push range (lsp--folding-range-children (car stack)))
   2420       (push range stack))
   2421     (lsp--folding-range-children dummy-node)))
   2422 
   2423 (defun lsp--range-inside-p (r1 r2)
   2424   "Return non-nil if folding range R1 lies inside R2"
   2425   (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2))
   2426        (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2))))
   2427 
   2428 (defun lsp--range-before-p (r1 r2)
   2429   "Return non-nil if folding range R1 ends before R2"
   2430   ;; Ensure r1 comes before r2
   2431   (or (< (lsp--folding-range-beg r1)
   2432          (lsp--folding-range-beg r2))
   2433       ;; If beg(r1) == beg(r2) make sure r2 ends first
   2434       (and (= (lsp--folding-range-beg r1)
   2435               (lsp--folding-range-beg r2))
   2436            (< (lsp--folding-range-end r2)
   2437               (lsp--folding-range-end r1)))))
   2438 
   2439 (defun lsp--point-inside-range-p (point range)
   2440   "Return non-nil if POINT lies inside folding range RANGE."
   2441   (and (>= point (lsp--folding-range-beg range))
   2442        (<= point (lsp--folding-range-end range))))
   2443 
   2444 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point)))
   2445   "Return the innermost folding range POINT lies in."
   2446   (seq-reduce (lambda (innermost-range curr-range)
   2447                 (if (and (lsp--point-inside-range-p point curr-range)
   2448                          (or (null innermost-range)
   2449                              (lsp--range-inside-p curr-range innermost-range)))
   2450                     curr-range
   2451                   innermost-range))
   2452               (lsp--get-folding-ranges)
   2453               nil))
   2454 
   2455 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point)))
   2456   "Return the outermost folding range POINT lies in."
   2457   (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range)
   2458                      (let ((curr-width (lsp--folding-range-width curr-range)))
   2459                        (if (and (lsp--point-inside-range-p point curr-range)
   2460                                 (or (null best-pair)
   2461                                     (> curr-width outermost-width)))
   2462                            (cons curr-width curr-range)
   2463                          best-pair)))
   2464                    (lsp--get-folding-ranges)
   2465                    nil)))
   2466 
   2467 (defun lsp--folding-range-at-point-bounds ()
   2468   (when (and lsp-enable-folding
   2469              (lsp-feature? "textDocument/foldingRange"))
   2470     (if-let ((range (lsp--get-current-innermost-folding-range)))
   2471         (cons (lsp--folding-range-beg range)
   2472               (lsp--folding-range-end range)))))
   2473 (put 'lsp--folding-range 'bounds-of-thing-at-point
   2474      #'lsp--folding-range-at-point-bounds)
   2475 
   2476 (defun lsp--get-nearest-folding-range (&optional backward)
   2477   (let ((point (point))
   2478         (found nil))
   2479     (while (not
   2480             (or found
   2481                 (if backward
   2482                     (<= point (point-min))
   2483                   (>= point (point-max)))))
   2484       (if backward (cl-decf point) (cl-incf point))
   2485       (setq found (lsp--get-current-innermost-folding-range point)))
   2486     found))
   2487 
   2488 (defun lsp--folding-range-at-point-forward-op (n)
   2489   (when (and lsp-enable-folding
   2490              (not (zerop n))
   2491              (lsp-feature? "textDocument/foldingRange"))
   2492     (cl-block break
   2493       (dotimes (_ (abs n))
   2494         (if-let ((range (lsp--get-nearest-folding-range (< n 0))))
   2495             (goto-char (if (< n 0)
   2496                            (lsp--folding-range-beg range)
   2497                          (lsp--folding-range-end range)))
   2498           (cl-return-from break))))))
   2499 (put 'lsp--folding-range 'forward-op
   2500      #'lsp--folding-range-at-point-forward-op)
   2501 
   2502 (defun lsp--folding-range-at-point-beginning-op ()
   2503   (goto-char (car (lsp--folding-range-at-point-bounds))))
   2504 (put 'lsp--folding-range 'beginning-op
   2505      #'lsp--folding-range-at-point-beginning-op)
   2506 
   2507 (defun lsp--folding-range-at-point-end-op ()
   2508   (goto-char (cdr (lsp--folding-range-at-point-bounds))))
   2509 (put 'lsp--folding-range 'end-op
   2510      #'lsp--folding-range-at-point-end-op)
   2511 
   2512 (defun lsp--range-at-point-bounds ()
   2513   (or (lsp--folding-range-at-point-bounds)
   2514       (when-let ((range (and
   2515                          (lsp-feature? "textDocument/hover")
   2516                          (->> (lsp--text-document-position-params)
   2517                               (lsp-request "textDocument/hover")
   2518                               (lsp:hover-range?)))))
   2519         (lsp--range-to-region range))))
   2520 
   2521 ;; A more general purpose "thing", useful for applications like focus.el
   2522 (put 'lsp--range 'bounds-of-thing-at-point
   2523      #'lsp--range-at-point-bounds)
   2524 
   2525 (defun lsp--log-io-p (method)
   2526   "Return non nil if should log for METHOD."
   2527   (and lsp-log-io
   2528        (or (not lsp-log-io-allowlist-methods)
   2529            (member method lsp-log-io-allowlist-methods))))
   2530 
   2531 
   2532 ;; toggles
   2533 
   2534 (defun lsp-toggle-trace-io ()
   2535   "Toggle client-server protocol logging."
   2536   (interactive)
   2537   (setq lsp-log-io (not lsp-log-io))
   2538   (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled")))
   2539 
   2540 (defun lsp-toggle-signature-auto-activate ()
   2541   "Toggle signature auto activate."
   2542   (interactive)
   2543   (setq lsp-signature-auto-activate
   2544         (unless lsp-signature-auto-activate '(:on-trigger-char)))
   2545   (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled"))
   2546   (lsp--update-signature-help-hook))
   2547 
   2548 (defun lsp-toggle-on-type-formatting ()
   2549   "Toggle on type formatting."
   2550   (interactive)
   2551   (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting))
   2552   (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled"))
   2553   (lsp--update-on-type-formatting-hook))
   2554 
   2555 (defun lsp-toggle-symbol-highlight ()
   2556   "Toggle symbol highlighting."
   2557   (interactive)
   2558   (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting))
   2559 
   2560   (cond
   2561    ((and lsp-enable-symbol-highlighting
   2562          (lsp-feature? "textDocument/documentHighlight"))
   2563     (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)
   2564     (lsp--info "Symbol highlighting enabled in current buffer."))
   2565    ((not lsp-enable-symbol-highlighting)
   2566     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   2567     (lsp--remove-overlays 'lsp-highlight)
   2568     (lsp--info "Symbol highlighting disabled in current buffer."))))
   2569 
   2570 
   2571 ;; keybindings
   2572 (defvar lsp--binding-descriptions nil
   2573   "List of key binding/short description pair.")
   2574 
   2575 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings)
   2576   "In KEYMAP, define key sequence KEY as DEF conditionally.
   2577 This is like `define-key', except the definition disappears
   2578 whenever COND evaluates to nil.
   2579 DESC is the short-description for the binding.
   2580 BINDINGS is a list of (key def desc cond)."
   2581   (declare (indent defun)
   2582            (debug (form form form form form &rest sexp)))
   2583   (->> (cl-list* key def desc cond bindings)
   2584        (-partition 4)
   2585        (-mapcat (-lambda ((key def desc cond))
   2586                   `((define-key ,keymap ,key
   2587                       '(menu-item
   2588                         ,(format "maybe-%s" def)
   2589                         ,def
   2590                         :filter
   2591                         (lambda (item)
   2592                           (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer)
   2593                                                            lsp--describe-buffer)
   2594                                                          (current-buffer))
   2595                                   ,cond)
   2596                             item))))
   2597                     (when (stringp ,key)
   2598                       (setq lsp--binding-descriptions
   2599                             (append lsp--binding-descriptions '(,key ,desc)))))))
   2600        macroexp-progn))
   2601 
   2602 (defvar lsp--describe-buffer nil)
   2603 
   2604 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus)
   2605   (let ((lsp--describe-buffer buffer))
   2606     (funcall fn buffer prefix menus)))
   2607 
   2608 (advice-add 'describe-buffer-bindings
   2609             :around
   2610             #'lsp-describe-buffer-bindings-advice)
   2611 
   2612 (defun lsp--prepend-prefix (mappings)
   2613   (->> mappings
   2614        (-partition 2)
   2615        (-mapcat (-lambda ((key description))
   2616                   (list (concat lsp-keymap-prefix " " key)
   2617                         description)))))
   2618 
   2619 (defvar lsp-command-map
   2620   (-doto (make-sparse-keymap)
   2621     (lsp-define-conditional-key
   2622       ;; workspaces
   2623       "wD" lsp-disconnect "disconnect" (lsp-workspaces)
   2624       "wd" lsp-describe-session "describe session" t
   2625       "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces)
   2626       "wr" lsp-workspace-restart "restart server" (lsp-workspaces)
   2627       "ws" lsp "start server" t
   2628 
   2629       ;; formatting
   2630       "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting")
   2631                                                  (lsp-feature? "textDocument/formatting"))
   2632       "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting")
   2633 
   2634       ;; folders
   2635       "Fa" lsp-workspace-folders-add "add folder" t
   2636       "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t
   2637       "Fr" lsp-workspace-folders-remove "remove folder" t
   2638 
   2639       ;; toggles
   2640       "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature?
   2641                                                                         "textDocument/publishDiagnostics")
   2642       "TL" lsp-toggle-trace-io "toggle log io" t
   2643       "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline)
   2644       "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs)
   2645       "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature?
   2646                                                                           "textDocument/codeAction")
   2647       "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature?
   2648                                                                "textDocument/documentSymbol")
   2649       "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc)
   2650       "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature?
   2651                                                                       "textDocument/onTypeFormatting")
   2652       "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight")
   2653       "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens")
   2654       "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp")
   2655 
   2656       ;; goto
   2657       "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol")
   2658       "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration")
   2659       "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list)
   2660       "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition")
   2661       "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls")
   2662                                                              (fboundp 'lsp-treemacs-call-hierarchy))
   2663       "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation")
   2664       "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references")
   2665       "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition")
   2666 
   2667       ;; help
   2668       "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc)
   2669                                                   (lsp-feature? "textDocument/hover"))
   2670       "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover")
   2671       "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp")
   2672 
   2673       ;; refactoring
   2674       "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction")
   2675       "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename")
   2676 
   2677       ;; actions
   2678       "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction")
   2679       "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight")
   2680       "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy))
   2681 
   2682       ;; peeks
   2683       "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition")
   2684                                                                 (fboundp 'lsp-ui-peek-find-definitions))
   2685       "Gi" lsp-ui-peek-find-implementation "peek implementations" (and
   2686                                                                    (fboundp 'lsp-ui-peek-find-implementation)
   2687                                                                    (lsp-feature? "textDocument/implementation"))
   2688       "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references)
   2689                                                               (lsp-feature? "textDocument/references"))
   2690       "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp
   2691                                                                            'lsp-ui-peek-find-workspace-symbol)
   2692                                                                           (lsp-feature? "workspace/symbol")))))
   2693 
   2694 
   2695 ;; which-key integration
   2696 
   2697 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key")
   2698 (declare-function which-key-add-key-based-replacements "ext:which-key")
   2699 
   2700 (defun lsp-enable-which-key-integration (&optional all-modes)
   2701   "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current
   2702 active `major-mode', or for all major modes when ALL-MODES is t."
   2703   (cl-flet ((which-key-fn (if all-modes
   2704                               'which-key-add-key-based-replacements
   2705                             (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode))))
   2706     (apply
   2707      #'which-key-fn
   2708      (lsp--prepend-prefix
   2709       (cl-list*
   2710        ""    "lsp"
   2711        "w"   "workspaces"
   2712        "F"   "folders"
   2713        "="   "formatting"
   2714        "T"   "toggle"
   2715        "g"   "goto"
   2716        "h"   "help"
   2717        "r"   "refactor"
   2718        "a"   "code actions"
   2719        "G"   "peek"
   2720        lsp--binding-descriptions)))))
   2721 
   2722 
   2723 ;; Globbing syntax
   2724 
   2725 ;; We port VSCode's glob-to-regexp code
   2726 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts)
   2727 ;; since the LSP globbing syntax seems to be the same as that of
   2728 ;; VSCode.
   2729 
   2730 (defconst lsp-globstar "**"
   2731   "Globstar pattern.")
   2732 
   2733 (defconst lsp-glob-split ?/
   2734   "The character by which we split path components in a glob
   2735 pattern.")
   2736 
   2737 (defconst lsp-path-regexp "[/\\\\]"
   2738   "Forward or backslash to be used as a path separator in
   2739 computed regexps.")
   2740 
   2741 (defconst lsp-non-path-regexp "[^/\\\\]"
   2742   "A regexp matching anything other than a slash.")
   2743 
   2744 (defconst lsp-globstar-regexp
   2745   (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?"
   2746           lsp-path-regexp
   2747           lsp-non-path-regexp lsp-path-regexp
   2748           lsp-path-regexp lsp-non-path-regexp)
   2749   "Globstar in regexp form.")
   2750 
   2751 (defun lsp-split-glob-pattern (pattern split-char)
   2752   "Split PATTERN at SPLIT-CHAR while respecting braces and brackets."
   2753   (when pattern
   2754     (let ((segments nil)
   2755           (in-braces nil)
   2756           (in-brackets nil)
   2757           (current-segment ""))
   2758       (dolist (char (string-to-list pattern))
   2759         (cl-block 'exit-point
   2760           (if (eq char split-char)
   2761               (when (and (null in-braces)
   2762                          (null in-brackets))
   2763                 (push current-segment segments)
   2764                 (setq current-segment "")
   2765                 (cl-return-from 'exit-point))
   2766             (pcase char
   2767               (?{
   2768                (setq in-braces t))
   2769               (?}
   2770                (setq in-braces nil))
   2771               (?\[
   2772                (setq in-brackets t))
   2773               (?\]
   2774                (setq in-brackets nil))))
   2775           (setq current-segment (concat current-segment
   2776                                         (char-to-string char)))))
   2777       (unless (string-empty-p current-segment)
   2778         (push current-segment segments))
   2779       (nreverse segments))))
   2780 
   2781 (defun lsp--glob-to-regexp (pattern)
   2782   "Helper function to convert a PATTERN from LSP's glob syntax to
   2783 an Elisp regexp."
   2784   (if (string-empty-p pattern)
   2785       ""
   2786     (let ((current-regexp "")
   2787           (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split)))
   2788       (if (-all? (lambda (segment) (eq segment lsp-globstar))
   2789                  glob-segments)
   2790           ".*"
   2791         (let ((prev-segment-was-globstar nil))
   2792           (seq-do-indexed
   2793            (lambda (segment index)
   2794              (if (string-equal segment lsp-globstar)
   2795                  (unless prev-segment-was-globstar
   2796                    (setq current-regexp (concat current-regexp
   2797                                                 lsp-globstar-regexp))
   2798                    (setq prev-segment-was-globstar t))
   2799                (let ((in-braces nil)
   2800                      (brace-val "")
   2801                      (in-brackets nil)
   2802                      (bracket-val ""))
   2803                  (dolist (char (string-to-list segment))
   2804                    (cond
   2805                     ((and (not (char-equal char ?\}))
   2806                           in-braces)
   2807                      (setq brace-val (concat brace-val
   2808                                              (char-to-string char))))
   2809                     ((and in-brackets
   2810                           (or (not (char-equal char ?\]))
   2811                               (string-empty-p bracket-val)))
   2812                      (let ((curr (cond
   2813                                   ((char-equal char ?-)
   2814                                    "-")
   2815                                   ;; NOTE: ?\^ and ?^ are different characters
   2816                                   ((and (memq char '(?^ ?!))
   2817                                         (string-empty-p bracket-val))
   2818                                    "^")
   2819                                   ((char-equal char lsp-glob-split)
   2820                                    "")
   2821                                   (t
   2822                                    (regexp-quote (char-to-string char))))))
   2823                        (setq bracket-val (concat bracket-val curr))))
   2824                     (t
   2825                      (cl-case char
   2826                        (?{
   2827                         (setq in-braces t))
   2828                        (?\[
   2829                         (setq in-brackets t))
   2830                        (?}
   2831                         (let* ((choices (lsp-split-glob-pattern brace-val ?\,))
   2832                                (brace-regexp (concat "\\(?:"
   2833                                                      (mapconcat #'lsp--glob-to-regexp choices "\\|")
   2834                                                      "\\)")))
   2835                           (setq current-regexp (concat current-regexp
   2836                                                        brace-regexp))
   2837                           (setq in-braces nil)
   2838                           (setq brace-val "")))
   2839                        (?\]
   2840                         (setq current-regexp
   2841                               (concat current-regexp
   2842                                       "[" bracket-val "]"))
   2843                         (setq in-brackets nil)
   2844                         (setq bracket-val ""))
   2845                        (??
   2846                         (setq current-regexp
   2847                               (concat current-regexp
   2848                                       lsp-non-path-regexp)))
   2849                        (?*
   2850                         (setq current-regexp
   2851                               (concat current-regexp
   2852                                       lsp-non-path-regexp "*?")))
   2853                        (t
   2854                         (setq current-regexp
   2855                               (concat current-regexp
   2856                                       (regexp-quote (char-to-string char)))))))))
   2857                  (when (and (< index (1- (length glob-segments)))
   2858                             (or (not (string-equal (nth (1+ index) glob-segments)
   2859                                                    lsp-globstar))
   2860                                 (< (+ index 2)
   2861                                    (length glob-segments))))
   2862                    (setq current-regexp
   2863                          (concat current-regexp
   2864                                  lsp-path-regexp)))
   2865                  (setq prev-segment-was-globstar nil))))
   2866            glob-segments)
   2867           current-regexp)))))
   2868 
   2869 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365
   2870 (defun lsp-glob-unbrace-at-top-level (glob-pattern)
   2871   "If GLOB-PATTERN does not start with a brace, return a singleton list
   2872 containing GLOB-PATTERN.
   2873 
   2874 If GLOB-PATTERN does start with a brace, return a list of the
   2875 comma-separated globs within the top-level braces."
   2876   (if (not (string-prefix-p "{" glob-pattern))
   2877       (list glob-pattern)
   2878     (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,)))
   2879 
   2880 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern)
   2881   "Convert GLOB-PATTERN to a regexp wrapped with the beginning-
   2882 and end-of-string meta-characters."
   2883   (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'"))
   2884 
   2885 (defun lsp-glob-to-regexps (glob-pattern)
   2886   "Convert a GLOB-PATTERN to a list of Elisp regexps."
   2887   (when-let*
   2888       ((glob-pattern (cond ((hash-table-p glob-pattern)
   2889                             (ht-get glob-pattern "pattern"))
   2890                            ((stringp glob-pattern) glob-pattern)
   2891                            (t (error "Unknown glob-pattern type: %s" glob-pattern))))
   2892        (trimmed-pattern (string-trim glob-pattern))
   2893        (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern)))
   2894     (seq-map #'lsp-glob-convert-to-wrapped-regexp
   2895              top-level-unbraced-patterns)))
   2896 
   2897 
   2898 
   2899 (defvar lsp-mode-menu)
   2900 
   2901 (defun lsp-mouse-click (event)
   2902   (interactive "e")
   2903   (let* ((ec (event-start event))
   2904          (choice (x-popup-menu event lsp-mode-menu))
   2905          (action (lookup-key lsp-mode-menu (apply 'vector choice))))
   2906 
   2907     (select-window (posn-window ec))
   2908 
   2909     (unless (and (region-active-p) (eq action 'lsp-execute-code-action))
   2910       (goto-char (posn-point ec)))
   2911     (run-with-idle-timer
   2912      0.001 nil
   2913      (lambda ()
   2914        (cl-labels ((check (value) (not (null value))))
   2915          (when choice
   2916            (call-interactively action)))))))
   2917 
   2918 (defvar lsp-mode-map
   2919   (let ((map (make-sparse-keymap)))
   2920     (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse)
   2921     (define-key map (kbd "C-<mouse-1>") #'ignore)
   2922     (define-key map (kbd "<mouse-3>") #'lsp-mouse-click)
   2923     (define-key map (kbd "C-S-SPC") #'lsp-signature-activate)
   2924     (when lsp-keymap-prefix
   2925       (define-key map (kbd lsp-keymap-prefix) lsp-command-map))
   2926     map)
   2927   "Keymap for `lsp-mode'.")
   2928 
   2929 (define-minor-mode lsp-mode "Mode for LSP interaction."
   2930   :keymap lsp-mode-map
   2931   :lighter
   2932   (" LSP["
   2933    (lsp--buffer-workspaces
   2934     (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "]["))
   2935     (:propertize "Disconnected" face warning))
   2936    "]")
   2937   :group 'lsp-mode
   2938   (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred))
   2939     ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp'
   2940     (lsp)))
   2941 
   2942 (defvar lsp-mode-menu
   2943   (easy-menu-create-menu
   2944    nil
   2945    `(["Go to definition" lsp-find-definition
   2946       :active (lsp-feature? "textDocument/definition")]
   2947      ["Find references" lsp-find-references
   2948       :active (lsp-feature? "textDocument/references")]
   2949      ["Find implementations" lsp-find-implementation
   2950       :active (lsp-feature? "textDocument/implementation")]
   2951      ["Find declarations" lsp-find-declaration
   2952       :active (lsp-feature? "textDocument/declaration")]
   2953      ["Go to type declaration" lsp-find-type-definition
   2954       :active (lsp-feature? "textDocument/typeDefinition")]
   2955      "--"
   2956      ["Describe" lsp-describe-thing-at-point]
   2957      ["Code action" lsp-execute-code-action]
   2958      ["Format" lsp-format-buffer]
   2959      ["Highlight references" lsp-document-highlight]
   2960      ["Type Hierarchy" lsp-java-type-hierarchy
   2961       :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")]
   2962      ["Type Hierarchy" lsp-treemacs-type-hierarchy
   2963       :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy"))
   2964                     (functionp 'lsp-treemacs-type-hierarchy)
   2965                     (lsp-feature? "textDocument/typeHierarchy"))]
   2966      ["Call Hierarchy" lsp-treemacs-call-hierarchy
   2967       :visible (and (functionp 'lsp-treemacs-call-hierarchy)
   2968                     (lsp-feature? "textDocument/callHierarchy"))]
   2969      ["Rename" lsp-rename
   2970       :active (lsp-feature? "textDocument/rename")]
   2971      "--"
   2972      ("Session"
   2973       ["View logs" lsp-workspace-show-log]
   2974       ["Describe" lsp-describe-session]
   2975       ["Shutdown" lsp-shutdown-workspace]
   2976       ["Restart" lsp-restart-workspace])
   2977      ("Workspace Folders"
   2978       ["Add" lsp-workspace-folders-add]
   2979       ["Remove" lsp-workspace-folders-remove]
   2980       ["Open" lsp-workspace-folders-open])
   2981      ("Toggle features"
   2982       ["Lenses" lsp-lens-mode]
   2983       ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode]
   2984       ["Modeline code actions" lsp-modeline-code-actions-mode]
   2985       ["Modeline diagnostics" lsp-modeline-diagnostics-mode])
   2986      "---"
   2987      ("Debug"
   2988       :active (bound-and-true-p dap-ui-mode)
   2989       :filter ,(lambda (_)
   2990                  (and (boundp 'dap-ui-menu-items)
   2991                       (nthcdr 3 dap-ui-menu-items))))))
   2992   "Menu for lsp-mode.")
   2993 
   2994 (defalias 'make-lsp-client 'make-lsp--client)
   2995 
   2996 (cl-defstruct lsp--registered-capability
   2997   (id "")
   2998   (method " ")
   2999   (options nil))
   3000 
   3001 ;; A ‘lsp--workspace’ object represents exactly one language server process.
   3002 (cl-defstruct lsp--workspace
   3003   ;; the `ewoc' object for displaying I/O to and from the server
   3004   (ewoc nil)
   3005 
   3006   ;; ‘server-capabilities’ is a hash table of the language server capabilities.
   3007   ;; It is the hash table representation of a LSP ServerCapabilities structure;
   3008   ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize.
   3009   (server-capabilities nil)
   3010 
   3011   ;; ‘registered-server-capabilities’ is a list of hash tables that represent
   3012   ;; dynamically-registered Registration objects.  See
   3013   ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability.
   3014   (registered-server-capabilities nil)
   3015 
   3016   ;; ‘root’ is a directory name or a directory file name for the workspace
   3017   ;; root.  ‘lsp-mode’ passes this directory to the ‘initialize’ method of the
   3018   ;; language server; see
   3019   ;; https://microsoft.github.io/language-server-protocol/specification#initialize.
   3020   (root nil)
   3021 
   3022   ;; ‘client’ is the ‘lsp--client’ object associated with this workspace.
   3023   (client nil)
   3024 
   3025   ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It
   3026   ;; used to derive the file path in `lsp--uri-to-path' when using tramp
   3027   ;; connection.
   3028   (host-root nil)
   3029 
   3030   ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or
   3031   ;; a network connection.  ‘lsp-mode’ communicates with ‘proc’ using the
   3032   ;; language server protocol.  ‘proc’ corresponds to the COMMUNICATION-PROCESS
   3033   ;; element of the return value of the client’s ‘get-root’ field, which see.
   3034   (proc nil)
   3035 
   3036   ;; ‘proc’ is a process object; it must represent a regular process, not a
   3037   ;; pipe or network process.  It represents the actual server process that
   3038   ;; corresponds to this workspace.  ‘cmd-proc’ corresponds to the
   3039   ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’
   3040   ;; field, which see.
   3041   (cmd-proc nil)
   3042 
   3043   ;; ‘buffers’ is a list of buffers associated with this workspace.
   3044   (buffers nil)
   3045 
   3046   ;; if semantic tokens is enabled, `semantic-tokens-faces' contains
   3047   ;; one face (or nil) for each token type supported by the language server.
   3048   (semantic-tokens-faces nil)
   3049 
   3050   ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces'
   3051   ;; contains one face (or nil) for each modifier type supported by the language
   3052   ;; server
   3053   (semantic-tokens-modifier-faces nil)
   3054 
   3055   ;; Extra client capabilities provided by third-party packages using
   3056   ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME
   3057   ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name,
   3058   ;; and CAPS is either a plist of the client capabilities, or a function that
   3059   ;; takes no argument and returns a plist of the client capabilities or nil.
   3060   (extra-client-capabilities nil)
   3061 
   3062   ;; Workspace status
   3063   (status nil)
   3064 
   3065   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3066   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3067   (metadata (make-hash-table :test 'equal))
   3068 
   3069   ;; contains all the file notification watches that have been created for the
   3070   ;; current workspace in format filePath->file notification handle.
   3071   (watches (make-hash-table :test 'equal))
   3072 
   3073   ;; list of workspace folders
   3074   (workspace-folders nil)
   3075 
   3076   ;; ‘last-id’ the last request id for the current workspace.
   3077   (last-id 0)
   3078 
   3079   ;; ‘status-string’ allows extensions to specify custom status string based on
   3080   ;; the Language Server specific messages.
   3081   (status-string nil)
   3082 
   3083   ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it
   3084   ;; was stopped).
   3085   shutdown-action
   3086 
   3087   ;; ‘diagnostics’ a hashmap with workspace diagnostics.
   3088   (diagnostics (make-hash-table :test 'equal))
   3089 
   3090   ;; contains all the workDone progress tokens that have been created
   3091   ;; for the current workspace.
   3092   (work-done-tokens (make-hash-table :test 'equal)))
   3093 
   3094 
   3095 (cl-defstruct lsp-session
   3096   ;; contains the folders that are part of the current session
   3097   folders
   3098   ;; contains the folders that must not be imported in the current workspace.
   3099   folders-blocklist
   3100   ;; contains the list of folders that must be imported in a project in case of
   3101   ;; multi root LSP server.
   3102   (server-id->folders (make-hash-table :test 'equal))
   3103   ;; folder to list of the servers that are associated with the folder.
   3104   (folder->servers (make-hash-table :test 'equal))
   3105   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3106   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3107   (metadata (make-hash-table :test 'equal)))
   3108 
   3109 (defun lsp-workspace-status (status-string &optional workspace)
   3110   "Set current workspace status to STATUS-STRING.
   3111 If WORKSPACE is not specified defaults to lsp--cur-workspace."
   3112   (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string))))
   3113     (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string)))
   3114 
   3115 (defun lsp-session-set-metadata (key value &optional _workspace)
   3116   "Associate KEY with VALUE in the WORKSPACE metadata.
   3117 If WORKSPACE is not provided current workspace will be used."
   3118   (puthash key value (lsp-session-metadata (lsp-session))))
   3119 
   3120 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata)
   3121 
   3122 (defun lsp-session-get-metadata (key &optional _workspace)
   3123   "Lookup KEY in WORKSPACE metadata.
   3124 If WORKSPACE is not provided current workspace will be used."
   3125   (gethash key (lsp-session-metadata (lsp-session))))
   3126 
   3127 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata)
   3128 
   3129 (defun lsp-workspace-set-work-done-token (token value workspace)
   3130   "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens."
   3131   (puthash token value (lsp--workspace-work-done-tokens workspace)))
   3132 
   3133 (defun lsp-workspace-get-work-done-token (token workspace)
   3134   "Lookup TOKEN in the WORKSPACE work-done-tokens."
   3135   (gethash token (lsp--workspace-work-done-tokens workspace)))
   3136 
   3137 (defun lsp-workspace-rem-work-done-token (token workspace)
   3138   "Remove TOKEN from the WORKSPACE work-done-tokens."
   3139   (remhash token (lsp--workspace-work-done-tokens workspace)))
   3140 
   3141 
   3142 (defun lsp--make-notification (method &optional params)
   3143   "Create notification body for method METHOD and parameters PARAMS."
   3144   (list :jsonrpc "2.0" :method method :params params))
   3145 
   3146 (defalias 'lsp--make-request 'lsp--make-notification)
   3147 (defalias 'lsp-make-request 'lsp--make-notification)
   3148 
   3149 (defun lsp--make-response (id result)
   3150   "Create response for REQUEST with RESULT."
   3151   `(:jsonrpc "2.0" :id ,id :result ,result))
   3152 
   3153 (defun lsp-make-notification (method &optional params)
   3154   "Create notification body for method METHOD and parameters PARAMS."
   3155   (lsp--make-notification method params))
   3156 
   3157 (defmacro lsp--json-serialize (params)
   3158   (if (progn
   3159         (require 'json)
   3160         (fboundp 'json-serialize))
   3161       `(json-serialize ,params
   3162                        :null-object nil
   3163                        :false-object :json-false)
   3164     `(let ((json-false :json-false))
   3165        (json-encode ,params))))
   3166 
   3167 (defun lsp--make-message (params)
   3168   "Create a LSP message from PARAMS, after encoding it to a JSON string."
   3169   (let ((body (lsp--json-serialize params)))
   3170     (concat "Content-Length: "
   3171             (number-to-string (1+ (string-bytes body)))
   3172             "\r\n\r\n"
   3173             body
   3174             "\n")))
   3175 
   3176 (cl-defstruct lsp--log-entry timestamp process-time type method id body)
   3177 
   3178 (defun lsp--make-log-entry (method id body type &optional process-time)
   3179   "Create an outgoing log object from BODY with method METHOD and id ID.
   3180 If ID is non-nil, then the body is assumed to be a notification.
   3181 TYPE can either be `incoming' or `outgoing'"
   3182   (cl-assert (memq type '(incoming-req outgoing-req incoming-notif
   3183                                        outgoing-notif incoming-resp
   3184                                        outgoing-resp)))
   3185   (make-lsp--log-entry
   3186    :timestamp (format-time-string "%I:%M:%S %p")
   3187    :process-time process-time
   3188    :method method
   3189    :id id
   3190    :type type
   3191    :body body))
   3192 
   3193 (defun lsp--log-font-lock-json (body)
   3194   "Font lock JSON BODY."
   3195   (with-temp-buffer
   3196     (insert body)
   3197     ;; We set the temp buffer file-name extension to .json and call `set-auto-mode'
   3198     ;; so the users configured json mode is used which could be
   3199     ;; `json-mode', `json-ts-mode', `jsonian-mode', etc.
   3200     (let ((buffer-file-name "lsp-log.json"))
   3201       (delay-mode-hooks
   3202         (set-auto-mode)
   3203         (if (fboundp 'font-lock-ensure)
   3204             (font-lock-ensure)
   3205           (with-no-warnings
   3206             (font-lock-fontify-buffer)))))
   3207     (buffer-string)))
   3208 
   3209 (defun lsp--log-entry-pp (entry)
   3210   (cl-assert (lsp--log-entry-p entry))
   3211   (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time
   3212                           body)
   3213                entry)
   3214               (json-false :json-false)
   3215               (json-encoding-pretty-print t)
   3216               (str nil))
   3217     (setq str
   3218           (concat (format "[Trace - %s] " timestamp)
   3219                   (pcase type
   3220                     ('incoming-req (format "Received request '%s - (%s)." method id))
   3221                     ('outgoing-req (format "Sending request '%s - (%s)'." method id))
   3222 
   3223                     ('incoming-notif (format "Received notification '%s'." method))
   3224                     ('outgoing-notif (format "Sending notification '%s'." method))
   3225 
   3226                     ('incoming-resp (format "Received response '%s - (%s)' in %dms."
   3227                                             method id process-time))
   3228                     ('outgoing-resp
   3229                      (format
   3230                       "Sending response '%s - (%s)'. Processing request took %dms"
   3231                       method id process-time)))
   3232                   "\n"
   3233                   (if (memq type '(incoming-resp ougoing-resp))
   3234                       "Result: "
   3235                     "Params: ")
   3236                   (lsp--log-font-lock-json (json-encode body))
   3237                   "\n\n\n"))
   3238     (setq str (propertize str 'mouse-face 'highlight 'read-only t))
   3239     (insert str)))
   3240 
   3241 (defvar-local lsp--log-io-ewoc nil)
   3242 
   3243 (defun lsp--get-create-io-ewoc (workspace)
   3244   (if (and (lsp--workspace-ewoc workspace)
   3245            (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace))))
   3246       (lsp--workspace-ewoc workspace)
   3247     (with-current-buffer (lsp--get-log-buffer-create workspace)
   3248       (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode))
   3249       (setq-local window-point-insertion-type t)
   3250       (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t))
   3251       (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc))
   3252     (lsp--workspace-ewoc workspace)))
   3253 
   3254 (defun lsp--ewoc-count (ewoc)
   3255   (let* ((count 0)
   3256          (count-fn (lambda (_) (setq count (1+ count)))))
   3257     (ewoc-map count-fn ewoc)
   3258     count))
   3259 
   3260 (defun lsp--log-entry-new (entry workspace)
   3261   (let* ((ewoc (lsp--get-create-io-ewoc workspace))
   3262          (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc)))
   3263          (node (if (or (eq lsp-io-messages-max t)
   3264                        (>= lsp-io-messages-max count))
   3265                    nil
   3266                  (ewoc-nth ewoc (1- lsp-io-messages-max))))
   3267          (prev nil)
   3268          (inhibit-read-only t))
   3269     (while node
   3270       (setq prev (ewoc-prev ewoc node))
   3271       (ewoc-delete ewoc node)
   3272       (setq node prev))
   3273     (ewoc-enter-last ewoc entry)))
   3274 
   3275 (defun lsp--send-notification (body)
   3276   "Send BODY as a notification to the language server."
   3277   (lsp-foreach-workspace
   3278    (when (lsp--log-io-p (plist-get body :method))
   3279      (lsp--log-entry-new (lsp--make-log-entry
   3280                           (plist-get body :method)
   3281                           nil (plist-get body :params) 'outgoing-notif)
   3282                          lsp--cur-workspace))
   3283    (lsp--send-no-wait body
   3284                       (lsp--workspace-proc lsp--cur-workspace))))
   3285 
   3286 (defalias 'lsp-send-notification 'lsp--send-notification)
   3287 
   3288 (defun lsp-notify (method params)
   3289   "Send notification METHOD with PARAMS."
   3290   (lsp--send-notification (lsp--make-notification method params)))
   3291 
   3292 (defun lsp--cur-workspace-check ()
   3293   "Check whether buffer lsp workspace(s) are set."
   3294   (cl-assert (lsp-workspaces) nil
   3295              "No language server(s) is associated with this buffer."))
   3296 
   3297 (defun lsp--send-request (body &optional no-wait no-merge)
   3298   "Send BODY as a request to the language server, get the response.
   3299 If NO-WAIT is non-nil, don't synchronously wait for a response.
   3300 If NO-MERGE is non-nil, don't merge the results but return an
   3301 alist mapping workspace->result."
   3302   (lsp-request (plist-get body :method)
   3303                (plist-get body :params)
   3304                :no-wait no-wait
   3305                :no-merge no-merge))
   3306 
   3307 (defalias 'lsp-send-request 'lsp--send-request
   3308   "Send BODY as a request to the language server and return the response
   3309 synchronously.
   3310 \n(fn BODY)")
   3311 
   3312 (cl-defun lsp-request (method params &key no-wait no-merge)
   3313   "Send request METHOD with PARAMS.
   3314 If NO-MERGE is non-nil, don't merge the results but return alist
   3315 workspace->result.
   3316 If NO-WAIT is non-nil send the request as notification."
   3317   (if no-wait
   3318       (lsp-notify method params)
   3319     (let* ((send-time (float-time))
   3320            ;; max time by which we must get a response
   3321            (expected-time
   3322             (and
   3323              lsp-response-timeout
   3324              (+ send-time lsp-response-timeout)))
   3325            resp-result resp-error done?)
   3326       (unwind-protect
   3327           (progn
   3328             (lsp-request-async method params
   3329                                (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3330                                :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3331                                :no-merge no-merge
   3332                                :mode 'detached
   3333                                :cancel-token :sync-request)
   3334             (while (not (or resp-error resp-result))
   3335               (if (functionp 'json-rpc-connection)
   3336                   (catch 'lsp-done (sit-for 0.01))
   3337                 (catch 'lsp-done
   3338                   (accept-process-output
   3339                    nil
   3340                    (if expected-time (- expected-time send-time) 1))))
   3341               (setq send-time (float-time))
   3342               (when (and expected-time (< expected-time send-time))
   3343                 (error "Timeout while waiting for response.  Method: %s" method)))
   3344             (setq done? t)
   3345             (cond
   3346              ((eq resp-result :finished) nil)
   3347              (resp-result resp-result)
   3348              ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3349              ((lsp-json-error? (cl-first resp-error))
   3350               (error (lsp:json-error-message (cl-first resp-error))))))
   3351         (unless done?
   3352           (lsp-cancel-request-by-token :sync-request))))))
   3353 
   3354 (cl-defun lsp-request-while-no-input (method params)
   3355   "Send request METHOD with PARAMS and waits until there is no input.
   3356 Return same value as `lsp--while-no-input' and respecting `non-essential'."
   3357   (if (or non-essential (not lsp-request-while-no-input-may-block))
   3358       (let* ((send-time (float-time))
   3359              ;; max time by which we must get a response
   3360              (expected-time
   3361               (and
   3362                lsp-response-timeout
   3363                (+ send-time lsp-response-timeout)))
   3364              resp-result resp-error done?)
   3365         (unwind-protect
   3366             (progn
   3367               (lsp-request-async method params
   3368                                  (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3369                                  :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3370                                  :mode 'detached
   3371                                  :cancel-token :sync-request)
   3372               (while (not (or resp-error resp-result (input-pending-p)))
   3373                 (catch 'lsp-done
   3374                   (sit-for
   3375                    (if expected-time (- expected-time send-time) 1)))
   3376                 (setq send-time (float-time))
   3377                 (when (and expected-time (< expected-time send-time))
   3378                   (error "Timeout while waiting for response.  Method: %s" method)))
   3379               (setq done? (or resp-error resp-result))
   3380               (cond
   3381                ((eq resp-result :finished) nil)
   3382                (resp-result resp-result)
   3383                ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3384                ((lsp-json-error? (cl-first resp-error))
   3385                 (error (lsp:json-error-message (cl-first resp-error))))))
   3386           (unless done?
   3387             (lsp-cancel-request-by-token :sync-request))
   3388           (when (and (input-pending-p) lsp--throw-on-input)
   3389             (throw 'input :interrupted))))
   3390     (lsp-request method params)))
   3391 
   3392 (defvar lsp--cancelable-requests (ht))
   3393 
   3394 (cl-defun lsp-request-async (method params callback
   3395                                     &key mode error-handler cancel-handler no-merge cancel-token)
   3396   "Send METHOD with PARAMS as a request to the language server.
   3397 Call CALLBACK with the response received from the server
   3398 asynchronously.
   3399 MODE determines when the callback will be called depending on the
   3400 condition of the original buffer.  It could be:
   3401 - `detached' which means that the callback will be executed no
   3402 matter what has happened to the buffer.
   3403 - `alive' - the callback will be executed only if the buffer from
   3404 which the call was executed is still alive.
   3405 - `current' the callback will be executed only if the original buffer
   3406 is still selected.
   3407 - `tick' - the callback will be executed only if the buffer was not modified.
   3408 - `unchanged' - the callback will be executed only if the buffer hasn't
   3409 changed and if the buffer is not modified.
   3410 
   3411 ERROR-HANDLER will be called in case the request has failed.
   3412 CANCEL-HANDLER will be called in case the request is being canceled.
   3413 If NO-MERGE is non-nil, don't merge the results but return alist
   3414 workspace->result.
   3415 CANCEL-TOKEN is the token that can be used to cancel request."
   3416   (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params)
   3417                            callback mode error-handler cancel-handler no-merge cancel-token))
   3418 
   3419 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback)
   3420   (lambda (&rest _)
   3421     (unless (and (equal 'post-command-hook hook)
   3422                  (equal (current-buffer) buf))
   3423       (lsp--request-cleanup-hooks id)
   3424       (with-lsp-workspaces workspaces
   3425         (lsp--cancel-request id)
   3426         (when cancel-callback (funcall cancel-callback)))
   3427       (lsp-log "Cancelling %s(%s) in hook %s" method id hook))))
   3428 
   3429 (defun lsp--create-async-callback
   3430     (callback method no-merge workspaces)
   3431   "Create async handler expecting COUNT results, merge them and call CALLBACK.
   3432 MODE determines when the callback will be called depending on the
   3433 condition of the original buffer. METHOD is the invoked method.
   3434 If NO-MERGE is non-nil, don't merge the results but return alist
   3435 workspace->result. ID is the request id."
   3436   (let (results errors)
   3437     (lambda (result)
   3438       (push (cons lsp--cur-workspace result)
   3439             (if (eq result :error) errors results))
   3440       (when (and (not (eq (length errors) (length workspaces)))
   3441                  (eq (+ (length errors) (length results)) (length workspaces)))
   3442         (funcall callback
   3443                  (if no-merge
   3444                      results
   3445                    (lsp--merge-results (-map #'cl-rest results) method)))))))
   3446 
   3447 (defcustom lsp-default-create-error-handler-fn nil
   3448   "Default error handler customization.
   3449 Handler should give METHOD as argument and return function of one argument
   3450 ERROR."
   3451   :type 'function
   3452   :group 'lsp-mode
   3453   :package-version '(lsp-mode . "9.0.0"))
   3454 
   3455 (defun lsp--create-default-error-handler (method)
   3456   "Default error handler.
   3457 METHOD is the executed method."
   3458   (if lsp-default-create-error-handler-fn
   3459       (funcall lsp-default-create-error-handler-fn method)
   3460     (lambda (error)
   3461       (lsp--warn "%s" (or (lsp--error-string error)
   3462                           (format "%s Request has failed" method))))))
   3463 
   3464 (defvar lsp--request-cleanup-hooks (ht))
   3465 
   3466 (defun lsp--request-cleanup-hooks (request-id)
   3467   (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks)))
   3468     (funcall cleanup-function)
   3469     (remhash request-id lsp--request-cleanup-hooks)))
   3470 
   3471 (defun lsp-cancel-request-by-token (cancel-token)
   3472   "Cancel request using CANCEL-TOKEN."
   3473   (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests))
   3474     (with-lsp-workspaces workspaces
   3475       (lsp--cancel-request request-id))
   3476     (remhash cancel-token lsp--cancelable-requests)
   3477     (lsp--request-cleanup-hooks request-id)))
   3478 
   3479 (defun lsp--send-request-async (body callback
   3480                                      &optional mode error-callback cancel-callback
   3481                                      no-merge cancel-token)
   3482   "Send BODY as a request to the language server.
   3483 Call CALLBACK with the response received from the server
   3484 asynchronously.
   3485 MODE determines when the callback will be called depending on the
   3486 condition of the original buffer.  It could be:
   3487 - `detached' which means that the callback will be executed no
   3488 matter what has happened to the buffer.
   3489 - `alive' - the callback will be executed only if the buffer from
   3490 which the call was executed is still alive.
   3491 - `current' the callback will be executed only if the original buffer
   3492 is still selected.
   3493 - `tick' - the callback will be executed only if the buffer was not modified.
   3494 - `unchanged' - the callback will be executed only if the buffer hasn't
   3495 changed and if the buffer is not modified.
   3496 
   3497 ERROR-CALLBACK will be called in case the request has failed.
   3498 CANCEL-CALLBACK will be called in case the request is being canceled.
   3499 If NO-MERGE is non-nil, don't merge the results but return alist
   3500 workspace->result.
   3501 CANCEL-TOKEN is the token that can be used to cancel request."
   3502   (when cancel-token
   3503     (lsp-cancel-request-by-token cancel-token))
   3504 
   3505   (if-let ((target-workspaces (lsp--find-workspaces-for body)))
   3506       (let* ((start-time (current-time))
   3507              (method (plist-get body :method))
   3508              (id (cl-incf lsp-last-id))
   3509              (buf (current-buffer))
   3510              (cancel-callback (when cancel-callback
   3511                                 (pcase mode
   3512                                   ((or 'alive 'tick 'unchanged)
   3513                                    (lambda ()
   3514                                      (with-current-buffer buf
   3515                                        (funcall cancel-callback))))
   3516                                   (_ cancel-callback))))
   3517              ;; calculate what are the (hook . local) pairs which will cancel
   3518              ;; the request
   3519              (hooks (pcase mode
   3520                       ('alive     '((kill-buffer-hook . t)))
   3521                       ('tick      '((kill-buffer-hook . t) (after-change-functions . t)))
   3522                       ('unchanged '((after-change-functions . t) (post-command-hook . nil)))
   3523                       ('current   '((post-command-hook . nil)))))
   3524              ;; note: lambdas in emacs can be compared but we should make sure
   3525              ;; that all of the captured arguments are the same - in our case
   3526              ;; `lsp--create-request-cancel' will return the same lambda when
   3527              ;; called with the same params.
   3528              (cleanup-hooks
   3529               (lambda () (mapc
   3530                           (-lambda ((hook . local))
   3531                             (if local
   3532                                 (when (buffer-live-p buf)
   3533                                   (with-current-buffer buf
   3534                                     (remove-hook hook
   3535                                                  (lsp--create-request-cancel
   3536                                                   id target-workspaces hook buf method cancel-callback)
   3537                                                  t)))
   3538                               (remove-hook hook (lsp--create-request-cancel
   3539                                                  id target-workspaces hook buf method cancel-callback))))
   3540                           hooks)
   3541                 (remhash cancel-token lsp--cancelable-requests)))
   3542              (callback (pcase mode
   3543                          ((or 'alive 'tick 'unchanged) (lambda (&rest args)
   3544                                                          (with-current-buffer buf
   3545                                                            (apply callback args))))
   3546                          (_ callback)))
   3547              (callback (lsp--create-async-callback callback
   3548                                                    method
   3549                                                    no-merge
   3550                                                    target-workspaces))
   3551              (callback (lambda (result)
   3552                          (lsp--request-cleanup-hooks id)
   3553                          (funcall callback result)))
   3554              (error-callback (lsp--create-async-callback
   3555                               (or error-callback
   3556                                   (lsp--create-default-error-handler method))
   3557                               method
   3558                               nil
   3559                               target-workspaces))
   3560              (error-callback (lambda (error)
   3561                                (funcall callback :error)
   3562                                (lsp--request-cleanup-hooks id)
   3563                                (funcall error-callback error)))
   3564              (body (plist-put body :id id)))
   3565 
   3566         ;; cancel request in any of the hooks
   3567         (mapc (-lambda ((hook . local))
   3568                 (add-hook hook
   3569                           (lsp--create-request-cancel
   3570                            id target-workspaces hook buf method cancel-callback)
   3571                           nil local))
   3572               hooks)
   3573         (puthash id cleanup-hooks lsp--request-cleanup-hooks)
   3574 
   3575         (setq lsp--last-active-workspaces target-workspaces)
   3576 
   3577         (when cancel-token
   3578           (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests))
   3579 
   3580         (seq-doseq (workspace target-workspaces)
   3581           (when (lsp--log-io-p method)
   3582             (lsp--log-entry-new (lsp--make-log-entry method id
   3583                                                      (plist-get body :params)
   3584                                                      'outgoing-req)
   3585                                 workspace))
   3586           (puthash id
   3587                    (list callback error-callback method start-time (current-time))
   3588                    (-> workspace
   3589                        (lsp--workspace-client)
   3590                        (lsp--client-response-handlers)))
   3591           (lsp--send-no-wait body (lsp--workspace-proc workspace)))
   3592         body)
   3593     (error "The connected server(s) does not support method %s.
   3594 To find out what capabilities support your server use `M-x lsp-describe-session'
   3595 and expand the capabilities section"
   3596            (plist-get body :method))))
   3597 
   3598 ;; deprecated, use lsp-request-async.
   3599 (defalias 'lsp-send-request-async 'lsp--send-request-async)
   3600 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1")
   3601 
   3602 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any
   3603 ;; pending language servers.
   3604 (add-hook 'kill-emacs-hook #'lsp--global-teardown)
   3605 
   3606 (defun lsp--global-teardown ()
   3607   "Unload working workspaces."
   3608   (lsp-foreach-workspace (lsp--shutdown-workspace)))
   3609 
   3610 (defun lsp--shutdown-workspace (&optional restart)
   3611   "Shut down the language server process for ‘lsp--cur-workspace’."
   3612   (with-demoted-errors "LSP error: %S"
   3613     (let ((lsp-response-timeout 0.5))
   3614       (condition-case err
   3615           (lsp-request "shutdown" nil)
   3616         (error (lsp--error "%s" err))))
   3617     (lsp-notify "exit" nil))
   3618   (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown))
   3619   (lsp--uninitialize-workspace))
   3620 
   3621 (defcustom lsp-inlay-hint-enable nil
   3622   "If non-nil it will enable inlay hints."
   3623   :type 'boolean
   3624   :group 'lsp-mode
   3625   :package-version '(lsp-mode . "9.0.0"))
   3626 
   3627 (defun lsp--uninitialize-workspace ()
   3628   "Cleanup buffer state.
   3629 When a workspace is shut down, by request or from just
   3630 disappearing, unset all the variables related to it."
   3631   (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace]
   3632     (lsp-process-kill cmd-proc)
   3633     (mapc (lambda (buf)
   3634             (when (lsp-buffer-live-p buf)
   3635               (lsp-with-current-buffer buf
   3636                                        (lsp-managed-mode -1))))
   3637           buffers)
   3638     (lsp-diagnostics--workspace-cleanup lsp--cur-workspace)))
   3639 
   3640 (defun lsp--client-capabilities (&optional custom-capabilities)
   3641   "Return the client capabilities appending CUSTOM-CAPABILITIES."
   3642   (append
   3643    `((general . ((positionEncodings . ["utf-32", "utf-16"])))
   3644      (workspace . ((workspaceEdit . ((documentChanges . t)
   3645                                      (resourceOperations . ["create" "rename" "delete"])))
   3646                    (applyEdit . t)
   3647                    (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))))
   3648                    (executeCommand . ((dynamicRegistration . :json-false)))
   3649                    ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t)))))
   3650                    (workspaceFolders . t)
   3651                    (configuration . t)
   3652                    ,@(when lsp-semantic-tokens-enable
   3653                        `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests)
   3654                                                                         lsp-semantic-tokens-honor-refresh-requests)
   3655                                                                    :json-false))))))
   3656                    ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t)))))
   3657                    ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false)))))
   3658                    (fileOperations . ((didCreate . :json-false)
   3659                                       (willCreate . :json-false)
   3660                                       (didRename . t)
   3661                                       (willRename . t)
   3662                                       (didDelete . :json-false)
   3663                                       (willDelete . :json-false)))))
   3664      (textDocument . ((declaration . ((dynamicRegistration . t)
   3665                                       (linkSupport . t)))
   3666                       (definition . ((dynamicRegistration . t)
   3667                                      (linkSupport . t)))
   3668                       (references . ((dynamicRegistration . t)))
   3669                       (implementation . ((dynamicRegistration . t)
   3670                                          (linkSupport . t)))
   3671                       (typeDefinition . ((dynamicRegistration . t)
   3672                                          (linkSupport . t)))
   3673                       (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t)))
   3674                       (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))
   3675                                          (hierarchicalDocumentSymbolSupport . t)))
   3676                       (formatting . ((dynamicRegistration . t)))
   3677                       (rangeFormatting . ((dynamicRegistration . t)))
   3678                       (onTypeFormatting . ((dynamicRegistration . t)))
   3679                       ,@(when (and lsp-semantic-tokens-enable
   3680                                    (functionp 'lsp--semantic-tokens-capabilities))
   3681                           (lsp--semantic-tokens-capabilities))
   3682                       (rename . ((dynamicRegistration . t) (prepareSupport . t)))
   3683                       (codeAction . ((dynamicRegistration . t)
   3684                                      (isPreferredSupport . t)
   3685                                      (codeActionLiteralSupport . ((codeActionKind . ((valueSet . [""
   3686                                                                                                   "quickfix"
   3687                                                                                                   "refactor"
   3688                                                                                                   "refactor.extract"
   3689                                                                                                   "refactor.inline"
   3690                                                                                                   "refactor.rewrite"
   3691                                                                                                   "source"
   3692                                                                                                   "source.organizeImports"])))))
   3693                                      (resolveSupport . ((properties . ["edit" "command"])))
   3694                                      (dataSupport . t)))
   3695                       (completion . ((completionItem . ((snippetSupport . ,(cond
   3696                                                                             ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode)))
   3697                                                                              (lsp--warn (concat
   3698                                                                                          "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. "
   3699                                                                                          "You must either install yasnippet, or disable snippet support."))
   3700                                                                              :json-false)
   3701                                                                             (lsp-enable-snippet t)
   3702                                                                             (t :json-false)))
   3703                                                         (documentationFormat . ["markdown" "plaintext"])
   3704                                                         ;; Remove this after jdtls support resolveSupport
   3705                                                         (resolveAdditionalTextEditsSupport . t)
   3706                                                         (insertReplaceSupport . t)
   3707                                                         (deprecatedSupport . t)
   3708                                                         (resolveSupport
   3709                                                          . ((properties . ["documentation"
   3710                                                                            "detail"
   3711                                                                            "additionalTextEdits"
   3712                                                                            "command"])))
   3713                                                         (insertTextModeSupport . ((valueSet . [1 2])))))
   3714                                      (contextSupport . t)
   3715                                      (dynamicRegistration . t)))
   3716                       (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t)))))
   3717                                         (dynamicRegistration . t)))
   3718                       (documentLink . ((dynamicRegistration . t)
   3719                                        (tooltipSupport . t)))
   3720                       (hover . ((contentFormat . ["markdown" "plaintext"])
   3721                                 (dynamicRegistration . t)))
   3722                       ,@(when lsp-enable-folding
   3723                           `((foldingRange . ((dynamicRegistration . t)
   3724                                              ,@(when lsp-folding-range-limit
   3725                                                  `((rangeLimit . ,lsp-folding-range-limit)))
   3726                                              ,@(when lsp-folding-line-folding-only
   3727                                                  `((lineFoldingOnly . t)))))))
   3728                       (selectionRange . ((dynamicRegistration . t)))
   3729                       (callHierarchy . ((dynamicRegistration . :json-false)))
   3730                       (typeHierarchy . ((dynamicRegistration . t)))
   3731                       (publishDiagnostics . ((relatedInformation . t)
   3732                                              (tagSupport . ((valueSet . [1 2])))
   3733                                              (versionSupport . t)))
   3734                       (linkedEditingRange . ((dynamicRegistration . t)))))
   3735      (window . ((workDoneProgress . t)
   3736                 (showDocument . ((support . t))))))
   3737    custom-capabilities))
   3738 
   3739 (defun lsp-find-roots-for-workspace (workspace session)
   3740   "Get all roots for the WORKSPACE."
   3741   (-filter #'identity (ht-map (lambda (folder workspaces)
   3742                                 (when (-contains? workspaces workspace)
   3743                                   folder))
   3744                               (lsp-session-folder->servers session))))
   3745 
   3746 (defun lsp-session-watches (&optional session)
   3747   "Get watches created for SESSION."
   3748   (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session))))
   3749       (-let [res (make-hash-table :test 'equal)]
   3750         (puthash "__watches" res (lsp-session-metadata (or session (lsp-session))))
   3751         res)))
   3752 
   3753 (defun lsp--file-process-event (session root-folder event)
   3754   "Process file event."
   3755   (let* ((changed-file (cl-third event))
   3756          (rel-changed-file (f-relative changed-file root-folder))
   3757          (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type))
   3758          (bit-position (1- event-numeric-kind))
   3759          (watch-bit (ash 1 bit-position)))
   3760     (->>
   3761      session
   3762      lsp-session-folder->servers
   3763      (gethash root-folder)
   3764      (seq-do (lambda (workspace)
   3765                (when (->>
   3766                       workspace
   3767                       lsp--workspace-registered-server-capabilities
   3768                       (-any?
   3769                        (lambda (capability)
   3770                          (and
   3771                           (equal (lsp--registered-capability-method capability)
   3772                                  "workspace/didChangeWatchedFiles")
   3773                           (->>
   3774                            capability
   3775                            lsp--registered-capability-options
   3776                            (lsp:did-change-watched-files-registration-options-watchers)
   3777                            (seq-find
   3778                             (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp))
   3779                               (when (or (null kind?)
   3780                                         (> (logand kind? watch-bit) 0))
   3781                                 (-let [regexes (or cached-regexp
   3782                                                    (let ((regexp (lsp-glob-to-regexps glob-pattern)))
   3783                                                      (lsp-put fs-watcher :_cachedRegexp regexp)
   3784                                                      regexp))]
   3785                                   (-any? (lambda (re)
   3786                                            (or (string-match re changed-file)
   3787                                                (string-match re rel-changed-file)))
   3788                                          regexes))))))))))
   3789                  (with-lsp-workspace workspace
   3790                    (lsp-notify
   3791                     "workspace/didChangeWatchedFiles"
   3792                     `((changes . [((type . ,event-numeric-kind)
   3793                                    (uri . ,(lsp--path-to-uri changed-file)))]))))))))))
   3794 
   3795 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?))
   3796   "Register capability REG."
   3797   (when (and lsp-enable-file-watchers
   3798              (equal method "workspace/didChangeWatchedFiles"))
   3799     (-let* ((created-watches (lsp-session-watches (lsp-session)))
   3800             (root-folders (cl-set-difference
   3801                            (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session))
   3802                            (ht-keys created-watches))))
   3803       ;; create watch for each root folder without such
   3804       (dolist (folder root-folders)
   3805         (let* ((watch (make-lsp-watch :root-directory folder))
   3806                (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder))
   3807                (ignored-files-regex-list (car ignored-things))
   3808                (ignored-directories-regex-list (cadr ignored-things)))
   3809           (puthash folder watch created-watches)
   3810           (lsp-watch-root-folder (file-truename folder)
   3811                                  (-partial #'lsp--file-process-event (lsp-session) folder)
   3812                                  ignored-files-regex-list
   3813                                  ignored-directories-regex-list
   3814                                  watch
   3815                                  t)))))
   3816 
   3817   (push
   3818    (make-lsp--registered-capability :id id :method method :options register-options?)
   3819    (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3820 
   3821 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body)
   3822   "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to
   3823 access dir-local variables."
   3824   (declare (indent 1) (debug t))
   3825   `(with-temp-buffer
   3826      ;; Set the buffer's name to something under the root so that we can hack the local variables
   3827      ;; This file doesn't need to exist and will not be created due to this.
   3828      (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root)))
   3829      (hack-local-variables)
   3830      (prog1 ,@body
   3831        (setq-local buffer-file-name nil))))
   3832 
   3833 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root)
   3834   "Return a list of the form
   3835 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given
   3836 WORKSPACE-ROOT."
   3837   ;; The intent of this function is to provide per-root workspace-level customization of the
   3838   ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables.
   3839   (lsp--with-workspace-temp-buffer workspace-root
   3840     (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories))))
   3841 
   3842 
   3843 (defun lsp--cleanup-hanging-watches ()
   3844   "Cleanup watches in case there are no more workspaces that are interested
   3845 in that particular folder."
   3846   (let* ((session (lsp-session))
   3847          (watches (lsp-session-watches session)))
   3848     (dolist (watched-folder (ht-keys watches))
   3849       (when (-none? (lambda (workspace)
   3850                       (with-lsp-workspace workspace
   3851                         (lsp--registered-capability "workspace/didChangeWatchedFiles")))
   3852                     (gethash watched-folder (lsp-session-folder->servers (lsp-session))))
   3853         (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder)
   3854         (lsp-kill-watch (gethash watched-folder watches))
   3855         (remhash watched-folder watches)))))
   3856 
   3857 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method))
   3858   "Unregister capability UNREG."
   3859   (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace)
   3860         (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id))
   3861                     (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3862   (when (equal method "workspace/didChangeWatchedFiles")
   3863     (lsp--cleanup-hanging-watches)))
   3864 
   3865 (defun lsp--server-capabilities ()
   3866   "Return the capabilities of the language server associated with the buffer."
   3867   (->> (lsp-workspaces)
   3868        (-keep #'lsp--workspace-server-capabilities)
   3869        (apply #'lsp-merge)))
   3870 
   3871 (defun lsp--send-open-close-p ()
   3872   "Return whether open and close notifications should be sent to the server."
   3873   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3874     (or (memq sync '(1 2))
   3875         (lsp:text-document-sync-options-open-close? sync))))
   3876 
   3877 (defun lsp--send-will-save-p ()
   3878   "Return whether willSave notifications should be sent to the server."
   3879   (-> (lsp--server-capabilities)
   3880       (lsp:server-capabilities-text-document-sync?)
   3881       (lsp:text-document-sync-options-will-save?)))
   3882 
   3883 (defun lsp--send-will-save-wait-until-p ()
   3884   "Return whether willSaveWaitUntil notifications should be sent to the server."
   3885   (-> (lsp--server-capabilities)
   3886       (lsp:server-capabilities-text-document-sync?)
   3887       (lsp:text-document-sync-options-will-save-wait-until?)))
   3888 
   3889 (defun lsp--send-did-save-p ()
   3890   "Return whether didSave notifications should be sent to the server."
   3891   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3892     (or (memq sync '(1 2))
   3893         (lsp:text-document-sync-options-save? sync))))
   3894 
   3895 (defun lsp--save-include-text-p ()
   3896   "Return whether save notifications should include the text document's contents."
   3897   (->> (lsp--server-capabilities)
   3898        (lsp:server-capabilities-text-document-sync?)
   3899        (lsp:text-document-sync-options-save?)
   3900        (lsp:text-document-save-registration-options-include-text?)))
   3901 
   3902 (defun lsp--send-will-rename-files-p (path)
   3903   "Return whether willRenameFiles request should be sent to the server.
   3904 If any filters, checks if it applies for PATH."
   3905   (let* ((will-rename (-> (lsp--server-capabilities)
   3906                           (lsp:server-capabilities-workspace?)
   3907                           (lsp:workspace-server-capabilities-file-operations?)
   3908                           (lsp:workspace-file-operations-will-rename?)))
   3909          (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list)))
   3910     (and will-rename
   3911          (or (seq-empty-p filters)
   3912              (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob)))
   3913                       (-let [regexes (lsp-glob-to-regexps glob)]
   3914                         (and (or (not scheme?)
   3915                                  (string-prefix-p scheme? (lsp--path-to-uri path)))
   3916                              (-any? (lambda (re)
   3917                                       (string-match re path))
   3918                                     regexes))))
   3919                     filters)))))
   3920 
   3921 (defun lsp--send-did-rename-files-p ()
   3922   "Return whether didRenameFiles notification should be sent to the server."
   3923   (-> (lsp--server-capabilities)
   3924       (lsp:server-capabilities-workspace?)
   3925       (lsp:workspace-server-capabilities-file-operations?)
   3926       (lsp:workspace-file-operations-did-rename?)))
   3927 
   3928 (declare-function project-roots "ext:project" (project) t)
   3929 (declare-function project-root "ext:project" (project) t)
   3930 
   3931 (defun lsp--suggest-project-root ()
   3932   "Get project root."
   3933   (or
   3934    (when (featurep 'projectile) (condition-case nil
   3935                                     (projectile-project-root)
   3936                                   (error nil)))
   3937    (when (featurep 'project)
   3938      (when-let ((project (project-current)))
   3939        (if (fboundp 'project-root)
   3940            (project-root project)
   3941          (car (with-no-warnings
   3942                 (project-roots project))))))
   3943    default-directory))
   3944 
   3945 (defun lsp--read-from-file (file)
   3946   "Read FILE content."
   3947   (when (file-exists-p file)
   3948     (cl-first (read-from-string (f-read-text file 'utf-8)))))
   3949 
   3950 (defun lsp--persist (file-name to-persist)
   3951   "Persist TO-PERSIST in FILE-NAME.
   3952 
   3953 This function creates the parent directories if they don't exist
   3954 yet."
   3955   (let ((print-length nil)
   3956         (print-level nil))
   3957     ;; Create all parent directories:
   3958     (make-directory (f-parent file-name) t)
   3959     (f-write-text (prin1-to-string to-persist) 'utf-8 file-name)))
   3960 
   3961 (defun lsp-workspace-folders-add (project-root)
   3962   "Add PROJECT-ROOT to the list of workspace folders."
   3963   (interactive
   3964    (list (read-directory-name "Select folder to add: "
   3965                               (or (lsp--suggest-project-root) default-directory) nil t)))
   3966   (cl-pushnew (lsp-f-canonical project-root)
   3967               (lsp-session-folders (lsp-session)) :test 'equal)
   3968   (lsp--persist-session (lsp-session))
   3969 
   3970   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil))
   3971 
   3972 (defun lsp-workspace-folders-remove (project-root)
   3973   "Remove PROJECT-ROOT from the list of workspace folders."
   3974   (interactive (list (completing-read "Select folder to remove: "
   3975                                       (lsp-session-folders (lsp-session))
   3976                                       nil t nil nil
   3977                                       (lsp-find-session-folder (lsp-session) default-directory))))
   3978 
   3979   (setq project-root (lsp-f-canonical project-root))
   3980 
   3981   ;; send remove folder to each multiroot workspace associated with the folder
   3982   (dolist (wks (->> (lsp-session)
   3983                     (lsp-session-folder->servers)
   3984                     (gethash project-root)
   3985                     (--filter (lsp--client-multi-root (lsp--workspace-client it)))))
   3986     (with-lsp-workspace wks
   3987       (lsp-notify "workspace/didChangeWorkspaceFolders"
   3988                   (lsp-make-did-change-workspace-folders-params
   3989                    :event (lsp-make-workspace-folders-change-event
   3990                            :removed (vector (lsp-make-workspace-folder
   3991                                              :uri (lsp--path-to-uri project-root)
   3992                                              :name (f-filename project-root)))
   3993                            :added [])))))
   3994 
   3995   ;; turn off servers in the removed directory
   3996   (let* ((session (lsp-session))
   3997          (folder->servers (lsp-session-folder->servers session))
   3998          (server-id->folders (lsp-session-server-id->folders session))
   3999          (workspaces (gethash project-root folder->servers)))
   4000 
   4001     (remhash project-root folder->servers)
   4002 
   4003     ;; turn off the servers without root folders
   4004     (dolist (workspace workspaces)
   4005       (when (--none? (-contains? it workspace) (ht-values folder->servers))
   4006         (lsp--info "Shutdown %s since folder %s is removed..."
   4007                    (lsp--workspace-print workspace) project-root)
   4008         (with-lsp-workspace workspace (lsp--shutdown-workspace))))
   4009 
   4010     (setf (lsp-session-folders session)
   4011           (-remove-item project-root (lsp-session-folders session)))
   4012 
   4013     (ht-aeach (puthash key
   4014                        (-remove-item project-root value)
   4015                        server-id->folders)
   4016               server-id->folders)
   4017     (lsp--persist-session (lsp-session)))
   4018 
   4019   (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root)))
   4020 
   4021 (defun lsp-workspace-blocklist-remove (project-root)
   4022   "Remove PROJECT-ROOT from the workspace blocklist."
   4023   (interactive (list (completing-read "Select folder to remove:"
   4024                                       (lsp-session-folders-blocklist (lsp-session))
   4025                                       nil t)))
   4026   (setf (lsp-session-folders-blocklist (lsp-session))
   4027         (delete project-root
   4028                 (lsp-session-folders-blocklist (lsp-session))))
   4029   (lsp--persist-session (lsp-session)))
   4030 
   4031 (define-obsolete-function-alias 'lsp-workspace-folders-switch
   4032   'lsp-workspace-folders-open "lsp-mode 6.1")
   4033 
   4034 (defun lsp-workspace-folders-open (project-root)
   4035   "Open the directory located at PROJECT-ROOT"
   4036   (interactive (list (completing-read "Open folder: "
   4037                                       (lsp-session-folders (lsp-session))
   4038                                       nil t)))
   4039   (find-file project-root))
   4040 
   4041 (defun lsp--maybe-enable-signature-help (trigger-characters)
   4042   (let ((ch last-command-event))
   4043     (when (cl-find ch trigger-characters :key #'string-to-char)
   4044       (lsp-signature-activate))))
   4045 
   4046 (defun lsp--on-type-formatting-handler-create ()
   4047   (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" )))
   4048     (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character?
   4049                                              :first-trigger-character) provider]
   4050       (lambda ()
   4051         (lsp--on-type-formatting first-trigger-character
   4052                                  more-trigger-character?)))))
   4053 
   4054 (defun lsp--update-on-type-formatting-hook (&optional cleanup?)
   4055   (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create)))
   4056     (cond
   4057      ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?))
   4058       (add-hook 'post-self-insert-hook on-type-formatting-handler nil t))
   4059      ((or cleanup?
   4060           (not lsp-enable-on-type-formatting))
   4061       (remove-hook 'post-self-insert-hook on-type-formatting-handler t)))))
   4062 
   4063 (defun lsp--signature-help-handler-create ()
   4064   (-when-let ((&SignatureHelpOptions? :trigger-characters?)
   4065               (lsp--capability-for-method "textDocument/signatureHelp"))
   4066     (lambda ()
   4067       (lsp--maybe-enable-signature-help trigger-characters?))))
   4068 
   4069 (defun lsp--update-signature-help-hook (&optional cleanup?)
   4070   (let ((signature-help-handler (lsp--signature-help-handler-create)))
   4071     (cond
   4072      ((and (or (equal lsp-signature-auto-activate t)
   4073                (memq :on-trigger-char lsp-signature-auto-activate))
   4074            signature-help-handler)
   4075       (add-hook 'post-self-insert-hook signature-help-handler nil t))
   4076 
   4077      ((or cleanup?
   4078           (not (or (equal lsp-signature-auto-activate t)
   4079                    (memq :on-trigger-char lsp-signature-auto-activate))))
   4080       (remove-hook 'post-self-insert-hook signature-help-handler t)))))
   4081 
   4082 (defun lsp--after-set-visited-file-name ()
   4083   (lsp-disconnect)
   4084   (lsp))
   4085 
   4086 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27
   4087 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099
   4088 (defvar eldoc-documentation-default) ; CI
   4089 (when (< emacs-major-version 28)
   4090   (unless (boundp 'eldoc-documentation-functions)
   4091     (load "eldoc"))
   4092   (when (memq (default-value 'eldoc-documentation-function) '(nil ignore))
   4093     ;; actually `eldoc-documentation-strategy', but CI was failing
   4094     (setq-default eldoc-documentation-function 'eldoc-documentation-default)))
   4095 
   4096 (define-minor-mode lsp-managed-mode
   4097   "Mode for source buffers managed by lsp-mode."
   4098   :lighter nil
   4099   (cond
   4100    (lsp-managed-mode
   4101     (when (lsp-feature? "textDocument/hover")
   4102       (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t)
   4103       (eldoc-mode 1))
   4104 
   4105     (add-hook 'after-change-functions #'lsp-on-change nil t)
   4106     (add-hook 'after-revert-hook #'lsp-on-revert nil t)
   4107     (add-hook 'after-save-hook #'lsp-on-save nil t)
   4108     (add-hook 'auto-save-hook #'lsp--on-auto-save nil t)
   4109     (add-hook 'before-change-functions #'lsp-before-change nil t)
   4110     (add-hook 'before-save-hook #'lsp--before-save nil t)
   4111     (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t)
   4112     (add-hook 'post-command-hook #'lsp--post-command nil t)
   4113 
   4114     (lsp--update-on-type-formatting-hook)
   4115     (lsp--update-signature-help-hook)
   4116 
   4117     (when lsp-enable-xref
   4118       (add-hook 'xref-backend-functions #'lsp--xref-backend nil t))
   4119 
   4120     (lsp-configure-buffer)
   4121 
   4122     ;; make sure we turn off lsp-mode in case major mode changes, because major
   4123     ;; mode change will wipe the buffer locals.
   4124     (add-hook 'change-major-mode-hook #'lsp-disconnect nil t)
   4125     (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t)
   4126 
   4127     (let ((buffer (lsp-current-buffer)))
   4128       (run-with-idle-timer
   4129        0.0 nil
   4130        (lambda ()
   4131          (when (lsp-buffer-live-p buffer)
   4132            (lsp-with-current-buffer buffer
   4133              (lsp--on-change-debounce buffer)
   4134              (lsp--on-idle buffer)))))))
   4135    (t
   4136     (lsp-unconfig-buffer)
   4137 
   4138     (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t)
   4139     (remove-hook 'post-command-hook #'lsp--post-command t)
   4140     (remove-hook 'after-change-functions #'lsp-on-change t)
   4141     (remove-hook 'after-revert-hook #'lsp-on-revert t)
   4142     (remove-hook 'after-save-hook #'lsp-on-save t)
   4143     (remove-hook 'auto-save-hook #'lsp--on-auto-save t)
   4144     (remove-hook 'before-change-functions #'lsp-before-change t)
   4145     (remove-hook 'before-save-hook #'lsp--before-save t)
   4146     (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t)
   4147 
   4148     (lsp--update-on-type-formatting-hook :cleanup)
   4149     (lsp--update-signature-help-hook :cleanup)
   4150 
   4151     (when lsp--on-idle-timer
   4152       (cancel-timer lsp--on-idle-timer)
   4153       (setq lsp--on-idle-timer nil))
   4154 
   4155     (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4156     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4157 
   4158     (lsp--remove-overlays 'lsp-highlight)
   4159     (lsp--remove-overlays 'lsp-links)
   4160 
   4161     (remove-hook 'xref-backend-functions #'lsp--xref-backend t)
   4162     (remove-hook 'change-major-mode-hook #'lsp-disconnect t)
   4163     (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t)
   4164     (setq-local lsp-buffer-uri nil))))
   4165 
   4166 (defun lsp-configure-buffer ()
   4167   "Configure LSP features for current buffer."
   4168   ;; make sure the core is running in the context of all available workspaces
   4169   ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context
   4170   (let ((lsp--buffer-workspaces (cond
   4171                                  (lsp--buffer-workspaces)
   4172                                  (lsp--cur-workspace (list lsp--cur-workspace))))
   4173         lsp--cur-workspace)
   4174     (when lsp-auto-configure
   4175       (lsp--auto-configure)
   4176 
   4177       (when (and lsp-enable-text-document-color
   4178                  (lsp-feature? "textDocument/documentColor"))
   4179         (add-hook 'lsp-on-change-hook #'lsp--document-color nil t))
   4180 
   4181       (when (and lsp-enable-imenu
   4182                  (lsp-feature? "textDocument/documentSymbol"))
   4183         (lsp-enable-imenu))
   4184 
   4185       (when (and lsp-enable-indentation
   4186                  (lsp-feature? "textDocument/rangeFormatting"))
   4187         (add-function :override (local 'indent-region-function) #'lsp-format-region))
   4188 
   4189       (when (and lsp-enable-symbol-highlighting
   4190                  (lsp-feature? "textDocument/documentHighlight"))
   4191         (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t))
   4192 
   4193       (when (and lsp-enable-links
   4194                  (lsp-feature? "textDocument/documentLink"))
   4195         (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t))
   4196 
   4197       (when (and lsp-inlay-hint-enable
   4198                  (lsp-feature? "textDocument/inlayHint"))
   4199         (lsp-inlay-hints-mode))
   4200 
   4201       (when (and lsp-enable-dap-auto-configure
   4202                  (functionp 'dap-mode))
   4203         (dap-auto-configure-mode 1)))
   4204     (run-hooks 'lsp-configure-hook)))
   4205 
   4206 (defun lsp-unconfig-buffer ()
   4207   "Unconfigure LSP features for buffer."
   4208   (lsp--remove-overlays 'lsp-color)
   4209 
   4210   (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function)
   4211     (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   4212     (setq-local imenu-menubar-modified-tick 0)
   4213     (setq-local imenu--index-alist nil)
   4214     (imenu--cleanup))
   4215 
   4216   (remove-function (local 'indent-region-function) #'lsp-format-region)
   4217 
   4218   (remove-hook 'lsp-on-change-hook #'lsp--document-color t)
   4219   (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4220   (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4221 
   4222   (when (and lsp-enable-dap-auto-configure
   4223              (functionp 'dap-mode))
   4224     (dap-auto-configure-mode -1))
   4225 
   4226   (run-hooks 'lsp-unconfigure-hook))
   4227 
   4228 (defun lsp--buffer-content ()
   4229   (lsp-save-restriction-and-excursion
   4230     (or (lsp-virtual-buffer-call :buffer-string)
   4231         (buffer-substring-no-properties (point-min)
   4232                                         (point-max)))))
   4233 
   4234 (defun lsp--text-document-did-open ()
   4235   "`document/didOpen' event."
   4236   (run-hooks 'lsp-before-open-hook)
   4237   (when (and lsp-auto-touch-files
   4238              (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri)))))
   4239     (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri))
   4240     (save-buffer))
   4241 
   4242   (setq lsp--cur-version (or lsp--cur-version 0))
   4243   (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   4244   (lsp-notify
   4245    "textDocument/didOpen"
   4246    (list :textDocument
   4247          (list :uri (lsp--buffer-uri)
   4248                :languageId (lsp-buffer-language)
   4249                :version lsp--cur-version
   4250                :text (lsp--buffer-content))))
   4251 
   4252   (lsp-managed-mode 1)
   4253 
   4254   (run-hooks 'lsp-after-open-hook)
   4255   (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client))))
   4256     (-some-> (lsp--client-after-open-fn client)
   4257       (funcall))
   4258     (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client))
   4259       (intern-soft)
   4260       (run-hooks))))
   4261 
   4262 (defun lsp--text-document-identifier ()
   4263   "Make TextDocumentIdentifier."
   4264   (list :uri (lsp--buffer-uri)))
   4265 
   4266 (defun lsp--versioned-text-document-identifier ()
   4267   "Make VersionedTextDocumentIdentifier."
   4268   (plist-put (lsp--text-document-identifier) :version lsp--cur-version))
   4269 
   4270 (defun lsp--cur-line (&optional point)
   4271   (1- (line-number-at-pos point)))
   4272 
   4273 (defun lsp--cur-position ()
   4274   "Make a Position object for the current point."
   4275   (or (lsp-virtual-buffer-call :cur-position)
   4276       (lsp-save-restriction-and-excursion
   4277         (list :line (lsp--cur-line)
   4278               :character (- (point) (line-beginning-position))))))
   4279 
   4280 (defun lsp--point-to-position (point)
   4281   "Convert POINT to Position."
   4282   (lsp-save-restriction-and-excursion
   4283     (goto-char point)
   4284     (lsp--cur-position)))
   4285 
   4286 (defun lsp--range (start end)
   4287   "Make Range body from START and END."
   4288   ;; make sure start and end are Position objects
   4289   (list :start start :end end))
   4290 
   4291 (defun lsp--region-to-range (start end)
   4292   "Make Range object for the current region."
   4293   (lsp--range (lsp--point-to-position start)
   4294               (lsp--point-to-position end)))
   4295 
   4296 (defun lsp--region-or-line ()
   4297   "The active region or the current line."
   4298   (if (use-region-p)
   4299       (lsp--region-to-range (region-beginning) (region-end))
   4300     (lsp--region-to-range (line-beginning-position) (line-end-position))))
   4301 
   4302 (defun lsp--check-document-changes-version (document-changes)
   4303   "Verify that DOCUMENT-CHANGES have the proper version."
   4304   (unless (seq-every-p
   4305            (-lambda ((&TextDocumentEdit :text-document))
   4306              (or
   4307               (not text-document)
   4308               (let* ((filename (-> text-document
   4309                                    lsp:versioned-text-document-identifier-uri
   4310                                    lsp--uri-to-path))
   4311                      (version (lsp:versioned-text-document-identifier-version? text-document)))
   4312                 (with-current-buffer (find-file-noselect filename)
   4313                   (or (null version) (zerop version) (= -1 version)
   4314                       (equal version lsp--cur-version))))))
   4315            document-changes)
   4316     (error "Document changes cannot be applied due to different document version")))
   4317 
   4318 (defun lsp--apply-workspace-edit (workspace-edit &optional operation)
   4319   "Apply the WorkspaceEdit object WORKSPACE-EDIT.
   4320 OPERATION is symbol representing the source of this text edit."
   4321   (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit))
   4322     (if-let ((document-changes (seq-reverse document-changes?)))
   4323         (progn
   4324           (lsp--check-document-changes-version document-changes)
   4325           (->> document-changes
   4326                (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create")))
   4327                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4328           (->> document-changes
   4329                (seq-filter (-lambda ((&CreateFile :kind))
   4330                              (and (or (not kind) (equal kind "edit"))
   4331                                   (not (equal kind "create")))))
   4332                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4333           (->> document-changes
   4334                (seq-filter (-lambda ((&CreateFile :kind))
   4335                              (and (not (or (not kind) (equal kind "edit")))
   4336                                   (not (equal kind "create")))))
   4337                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))))
   4338       (lsp-map
   4339        (lambda (uri text-edits)
   4340          (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect)
   4341            (lsp--apply-text-edits text-edits operation)))
   4342        changes?))))
   4343 
   4344 (defmacro lsp-with-filename (file &rest body)
   4345   "Execute BODY with FILE as a context.
   4346 Need to handle the case when FILE indicates virtual buffer."
   4347   (declare (indent 1) (debug t))
   4348   `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file)))
   4349        (lsp-with-current-buffer lsp--virtual-buffer
   4350          ,@body)
   4351      ,@body))
   4352 
   4353 (defun lsp--apply-text-document-edit (edit &optional operation)
   4354   "Apply the TextDocumentEdit object EDIT.
   4355 OPERATION is symbol representing the source of this text edit.
   4356 If the file is not being visited by any buffer, it is opened with
   4357 `find-file-noselect'.
   4358 Because lsp-mode does not store previous document versions, the edit is only
   4359 applied if the version of the textDocument matches the version of the
   4360 corresponding file.
   4361 
   4362 interface TextDocumentEdit {
   4363   textDocument: VersionedTextDocumentIdentifier;
   4364   edits: TextEdit[];
   4365 }"
   4366   (pcase (lsp:edit-kind edit)
   4367     ("create" (-let* (((&CreateFile :uri :options?) edit)
   4368                       (file-name (lsp--uri-to-path uri)))
   4369                 (mkdir (f-dirname file-name) t)
   4370                 (f-touch file-name)
   4371                 (when (lsp:create-file-options-overwrite? options?)
   4372                   (f-write-text "" nil file-name))
   4373                 (find-file-noselect file-name)))
   4374     ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit))
   4375                 (f-delete (lsp--uri-to-path uri) recursive?)))
   4376     ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit)
   4377                       (old-file-name (lsp--uri-to-path old-uri))
   4378                       (new-file-name (lsp--uri-to-path new-uri))
   4379                       (buf (find-buffer-visiting old-file-name)))
   4380                 (when buf
   4381                   (lsp-with-current-buffer buf
   4382                     (save-buffer)
   4383                     (lsp--text-document-did-close)))
   4384                 (mkdir (f-dirname new-file-name) t)
   4385                 (rename-file old-file-name new-file-name overwrite?)
   4386                 (when buf
   4387                   (lsp-with-current-buffer buf
   4388                     (set-buffer-modified-p nil)
   4389                     (setq lsp-buffer-uri nil)
   4390                     (set-visited-file-name new-file-name)
   4391                     (lsp)))))
   4392     (_ (let ((file-name (->> edit
   4393                              (lsp:text-document-edit-text-document)
   4394                              (lsp:versioned-text-document-identifier-uri)
   4395                              (lsp--uri-to-path))))
   4396          (lsp-with-current-buffer (find-buffer-visiting file-name)
   4397            (lsp-with-filename file-name
   4398              (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation)))))))
   4399 
   4400 (lsp-defun lsp--position-compare ((&Position :line left-line
   4401                                              :character left-character)
   4402                                   (&Position :line right-line
   4403                                              :character right-character))
   4404   "Return t if position LEFT is greater than RIGHT."
   4405   (if (= left-line right-line)
   4406       (> left-character right-character)
   4407     (> left-line right-line)))
   4408 
   4409 (lsp-defun lsp-point-in-range? (position (&Range :start :end))
   4410   "Returns if POINT is in RANGE."
   4411   (not (or (lsp--position-compare start position)
   4412            (lsp--position-compare position end))))
   4413 
   4414 (lsp-defun lsp--position-equal ((&Position :line left-line
   4415                                            :character left-character)
   4416                                 (&Position :line right-line
   4417                                            :character right-character))
   4418   "Return whether LEFT and RIGHT positions are equal."
   4419   (and (= left-line right-line)
   4420        (= left-character right-character)))
   4421 
   4422 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end))
   4423                                           (&TextEdit :range (&Range :start right-start :end right-end)))
   4424   (if (lsp--position-equal left-start right-start)
   4425       (lsp--position-compare left-end right-end)
   4426     (lsp--position-compare left-start right-start)))
   4427 
   4428 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text))
   4429   "Apply the edits described in the TextEdit object in TEXT-EDIT."
   4430   (setq new-text (s-replace "\r" "" (or new-text "")))
   4431   (lsp:set-text-edit-new-text edit new-text)
   4432   (goto-char start)
   4433   (delete-region start end)
   4434   (insert new-text))
   4435 
   4436 ;; WORKAROUND: typescript-language might send -1 when applying code actions.
   4437 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582
   4438 (lsp-defun lsp--fix-point ((point &as &Position :character :line))
   4439   (-doto point
   4440     (lsp:set-position-line (max 0 line))
   4441     (lsp:set-position-character (max 0 character))))
   4442 
   4443 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as
   4444                                                                &TextEdit
   4445                                                                :range (&Range :start :end)
   4446                                                                :new-text))
   4447   "Apply the edits described in the TextEdit object in TEXT-EDIT.
   4448 The method uses `replace-buffer-contents'."
   4449   (setq new-text (s-replace "\r" "" (or new-text "")))
   4450   (lsp:set-text-edit-new-text edit new-text)
   4451   (-let* ((source (current-buffer))
   4452           ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start)
   4453                                                              :end (lsp--fix-point end)))))
   4454     (with-temp-buffer
   4455       (insert new-text)
   4456       (let ((temp (current-buffer)))
   4457         (with-current-buffer source
   4458           (save-excursion
   4459             (save-restriction
   4460               (narrow-to-region beg end)
   4461 
   4462               ;; On emacs versions < 26.2,
   4463               ;; `replace-buffer-contents' is buggy - it calls
   4464               ;; change functions with invalid arguments - so we
   4465               ;; manually call the change functions here.
   4466               ;;
   4467               ;; See emacs bugs #32237, #32278:
   4468               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237
   4469               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278
   4470               (let ((inhibit-modification-hooks t)
   4471                     (length (- end beg)))
   4472                 (run-hook-with-args 'before-change-functions
   4473                                     beg end)
   4474                 (replace-buffer-contents temp)
   4475                 (run-hook-with-args 'after-change-functions
   4476                                     beg (+ beg (length new-text))
   4477                                     length)))))))))
   4478 
   4479 (defun lsp--to-yasnippet-snippet (snippet)
   4480   "Convert LSP SNIPPET to yasnippet snippet."
   4481   ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it.
   4482   (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`")))
   4483                             (rx "\\" (backref 1))
   4484                             snippet
   4485                             nil nil 1))
   4486 
   4487 (defvar-local lsp-enable-relative-indentation nil
   4488   "Enable relative indentation when insert texts, snippets ...
   4489 from language server.")
   4490 
   4491 (defun lsp--expand-snippet (snippet &optional start end expand-env)
   4492   "Wrapper of `yas-expand-snippet' with all of it arguments.
   4493 The snippet will be convert to LSP style and indent according to
   4494 LSP server result."
   4495   (require 'yasnippet nil t)
   4496   (let* ((inhibit-field-text-motion t)
   4497          (yas-wrap-around-region nil)
   4498          (yas-indent-line 'none)
   4499          (yas-also-auto-indent-first-line nil))
   4500     (yas-expand-snippet
   4501      (lsp--to-yasnippet-snippet snippet)
   4502      start end expand-env)))
   4503 
   4504 (defun lsp--indent-lines (start end &optional insert-text-mode?)
   4505   "Indent from START to END based on INSERT-TEXT-MODE? value.
   4506 - When INSERT-TEXT-MODE? is provided
   4507   - if it's `lsp/insert-text-mode-as-it', do no editor indentation.
   4508   - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading
   4509     whitespaces to match the line where text is inserted.
   4510 - When it's not provided, using `indent-line-function' for each line."
   4511   (save-excursion
   4512     (goto-char end)
   4513     (let* ((end-line (line-number-at-pos))
   4514            (offset (save-excursion
   4515                      (goto-char start)
   4516                      (current-indentation)))
   4517            (indent-line-function
   4518             (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it)
   4519                    #'ignore)
   4520                   ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation)
   4521                        lsp-enable-relative-indentation
   4522                        ;; Indenting snippets is extremely slow in `org-mode' buffers
   4523                        ;; since it has to calculate indentation based on SRC block
   4524                        ;; position.  Thus we use relative indentation as default.
   4525                        (derived-mode-p 'org-mode))
   4526                    (lambda () (save-excursion
   4527                                 (beginning-of-line)
   4528                                 (indent-to-column offset))))
   4529                   (t indent-line-function))))
   4530       (goto-char start)
   4531       (forward-line)
   4532       (while (and (not (eobp))
   4533                   (<= (line-number-at-pos) end-line))
   4534         (funcall indent-line-function)
   4535         (forward-line)))))
   4536 
   4537 (defun lsp--apply-text-edits (edits &optional operation)
   4538   "Apply the EDITS described in the TextEdit[] object.
   4539 OPERATION is symbol representing the source of this text edit."
   4540   (unless (seq-empty-p edits)
   4541     (atomic-change-group
   4542       (run-hooks 'lsp-before-apply-edits-hook)
   4543       (let* ((change-group (prepare-change-group))
   4544              (howmany (length edits))
   4545              (message (format "Applying %s edits to `%s' ..." howmany (current-buffer)))
   4546              (_ (lsp--info message))
   4547              (reporter (make-progress-reporter message 0 howmany))
   4548              (done 0)
   4549              (apply-edit (if (not lsp--virtual-buffer)
   4550                              #'lsp--apply-text-edit-replace-buffer-contents
   4551                            #'lsp--apply-text-edit)))
   4552         (unwind-protect
   4553             (->> edits
   4554                  ;; We sort text edits so as to apply edits that modify latter
   4555                  ;; parts of the document first. Furthermore, because the LSP
   4556                  ;; spec dictates that: "If multiple inserts have the same
   4557                  ;; position, the order in the array defines which edit to
   4558                  ;; apply first."  We reverse the initial list and sort stably
   4559                  ;; to make sure the order among edits with the same position
   4560                  ;; is preserved.
   4561                  (nreverse)
   4562                  (seq-sort #'lsp--text-edit-sort-predicate)
   4563                  (mapc (lambda (edit)
   4564                          (progress-reporter-update reporter (cl-incf done))
   4565                          (funcall apply-edit edit)
   4566                          (when (lsp:snippet-text-edit-insert-text-format? edit)
   4567                            (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start)
   4568                                                          :insert-text-format? :new-text) edit)
   4569                              (when (eq insert-text-format? lsp/insert-text-format-snippet)
   4570                                ;; No `save-excursion' needed since expand snippet will change point anyway
   4571                                (goto-char (+ start (length new-text)))
   4572                                (lsp--indent-lines start (point))
   4573                                (lsp--expand-snippet new-text start (point)))))
   4574                          (run-hook-with-args 'lsp-after-apply-edits-hook operation))))
   4575           (undo-amalgamate-change-group change-group)
   4576           (progress-reporter-done reporter))))))
   4577 
   4578 (defun lsp--create-apply-text-edits-handlers ()
   4579   "Create (handler cleanup-fn) for applying text edits in async request.
   4580 Only works when mode is `tick or `alive."
   4581   (let* (first-edited
   4582          (func (lambda (start &rest _)
   4583                  (setq first-edited (if first-edited
   4584                                         (min start first-edited)
   4585                                       start)))))
   4586     (add-hook 'before-change-functions func nil t)
   4587     (list
   4588      (lambda (edits)
   4589        (if (and first-edited
   4590                 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end)))
   4591                             ;; Text edit region is overlapped
   4592                             (> end first-edited))
   4593                           edits))
   4594            (lsp--warn "TextEdits will not be applied since document has been modified before of them.")
   4595          (lsp--apply-text-edits edits 'completion-cleanup)))
   4596      (lambda ()
   4597        (remove-hook 'before-change-functions func t)))))
   4598 
   4599 (defun lsp--capability (cap &optional capabilities)
   4600   "Get the value of capability CAP.  If CAPABILITIES is non-nil, use them instead."
   4601   (when (stringp cap)
   4602     (setq cap (intern (concat ":" cap))))
   4603 
   4604   (lsp-get (or capabilities
   4605                (lsp--server-capabilities))
   4606            cap))
   4607 
   4608 (defun lsp--registered-capability (method)
   4609   "Check whether there is workspace providing METHOD."
   4610   (->> (lsp-workspaces)
   4611        (--keep (seq-find (lambda (reg)
   4612                            (equal (lsp--registered-capability-method reg) method))
   4613                          (lsp--workspace-registered-server-capabilities it)))
   4614        cl-first))
   4615 
   4616 (defun lsp--capability-for-method (method)
   4617   "Get the value of capability for METHOD."
   4618   (-let* ((reqs (cdr (assoc method lsp-method-requirements)))
   4619           ((&plist :capability) reqs))
   4620     (or (and capability (lsp--capability capability))
   4621         (-some-> (lsp--registered-capability method)
   4622           (lsp--registered-capability-options)))))
   4623 
   4624 (defvar-local lsp--before-change-vals nil
   4625   "Store the positions from the `lsp-before-change' function call, for
   4626 validation and use in the `lsp-on-change' function.")
   4627 
   4628 (defun lsp--text-document-content-change-event (start end length)
   4629   "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH."
   4630   ;; So (47 54 0) means add    7 chars starting at pos 47
   4631   ;; must become
   4632   ;;   {"range":{"start":{"line":5,"character":6}
   4633   ;;             ,"end" :{"line":5,"character":6}}
   4634   ;;             ,"rangeLength":0
   4635   ;;             ,"text":"\nbb = 5"}
   4636   ;;
   4637   ;; And (47 47 7) means delete 7 chars starting at pos 47
   4638   ;; must become
   4639   ;;   {"range":{"start":{"line":6,"character":0}
   4640   ;;            ,"end"  :{"line":7,"character":0}}
   4641   ;;            ,"rangeLength":7
   4642   ;;            ,"text":""}
   4643   ;;
   4644   ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with
   4645   ;; 13 chars. So it must become
   4646   ;;   {"range":{"start":{"line":5,"character":8}
   4647   ;;             ,"end" :{"line":5,"character":11}}
   4648   ;;             ,"rangeLength":3
   4649   ;;             ,"text":"new-chars-xxx"}
   4650   ;;
   4651 
   4652   ;; Adding text:
   4653   ;;   lsp-before-change:(start,end)=(33,33)
   4654   ;;   lsp-on-change:(start,end,length)=(33,34,0)
   4655   ;;
   4656   ;; Changing text:
   4657   ;;   lsp-before-change:(start,end)=(208,211)
   4658   ;;   lsp-on-change:(start,end,length)=(208,221,3)
   4659   ;;
   4660   ;; Deleting text:
   4661   ;;   lsp-before-change:(start,end)=(19,27)
   4662   ;;   lsp-on-change:(start,end,length)=(19,19,8)
   4663   (if (zerop length)
   4664       ;; Adding something only, work from start only
   4665       `( :range ,(lsp--range
   4666                   (lsp--point-to-position start)
   4667                   (lsp--point-to-position start))
   4668          :rangeLength 0
   4669          :text ,(buffer-substring-no-properties start end))
   4670 
   4671     (if (eq start end)
   4672         ;; Deleting something only
   4673         (if (lsp--bracketed-change-p start length)
   4674             ;; The before-change value is bracketed, use it
   4675             `( :range ,(lsp--range
   4676                         (lsp--point-to-position start)
   4677                         (plist-get lsp--before-change-vals :end-pos))
   4678                :rangeLength ,length
   4679                :text "")
   4680           ;; If the change is not bracketed, send a full change event instead.
   4681           (lsp--full-change-event))
   4682 
   4683       ;; Deleting some things, adding others
   4684       (if (lsp--bracketed-change-p start length)
   4685           ;; The before-change value is valid, use it
   4686           `( :range ,(lsp--range
   4687                       (lsp--point-to-position start)
   4688                       (plist-get lsp--before-change-vals :end-pos))
   4689              :rangeLength ,length
   4690              :text ,(buffer-substring-no-properties start end))
   4691         (lsp--full-change-event)))))
   4692 
   4693 (defun lsp--bracketed-change-p (start length)
   4694   "If the before and after positions are the same, and the length
   4695 is the size of the start range, we are probably good."
   4696   (-let [(&plist :end before-end :start before-start) lsp--before-change-vals]
   4697     (and (eq start before-start)
   4698          (eq length (- before-end before-start)))))
   4699 
   4700 (defun lsp--full-change-event ()
   4701   `(:text ,(lsp--buffer-content)))
   4702 
   4703 (defun lsp-before-change (start end)
   4704   "Executed before a file is changed.
   4705 Added to `before-change-functions'."
   4706   ;; Note:
   4707   ;;
   4708   ;; This variable holds a list of functions to call when Emacs is about to
   4709   ;; modify a buffer. Each function gets two arguments, the beginning and end of
   4710   ;; the region that is about to change, represented as integers. The buffer
   4711   ;; that is about to change is always the current buffer when the function is
   4712   ;; called.
   4713   ;;
   4714   ;; WARNING:
   4715   ;;
   4716   ;; Do not expect the before-change hooks and the after-change hooks be called
   4717   ;; in balanced pairs around each buffer change. Also don't expect the
   4718   ;; before-change hooks to be called for every chunk of text Emacs is about to
   4719   ;; delete. These hooks are provided on the assumption that Lisp programs will
   4720   ;; use either before- or the after-change hooks, but not both, and the
   4721   ;; boundaries of the region where the changes happen might include more than
   4722   ;; just the actual changed text, or even lump together several changes done
   4723   ;; piecemeal.
   4724   (save-match-data
   4725     (lsp-save-restriction-and-excursion
   4726       (setq lsp--before-change-vals
   4727             (list :start start
   4728                   :end end
   4729                   :end-pos (lsp--point-to-position end))))))
   4730 
   4731 (defun lsp--flush-delayed-changes ()
   4732   (let ((inhibit-quit t))
   4733     (when lsp--delay-timer
   4734       (cancel-timer lsp--delay-timer))
   4735     (mapc (-lambda ((workspace buffer document change))
   4736             (with-current-buffer buffer
   4737               (with-lsp-workspace workspace
   4738                 (lsp-notify "textDocument/didChange"
   4739                             (list :textDocument document
   4740                                   :contentChanges (vector change))))))
   4741           (prog1 (nreverse lsp--delayed-requests)
   4742             (setq lsp--delayed-requests nil)))))
   4743 
   4744 (defun lsp--workspace-sync-method (workspace)
   4745   (let ((sync (-> workspace
   4746                   (lsp--workspace-server-capabilities)
   4747                   (lsp:server-capabilities-text-document-sync?))))
   4748     (if (lsp-text-document-sync-options? sync)
   4749         (lsp:text-document-sync-options-change? sync)
   4750       sync)))
   4751 
   4752 (defun lsp-on-change (start end length &optional content-change-event-fn)
   4753   "Executed when a file is changed.
   4754 Added to `after-change-functions'."
   4755   ;; Note:
   4756   ;;
   4757   ;; Each function receives three arguments: the beginning and end of the region
   4758   ;; just changed, and the length of the text that existed before the change.
   4759   ;; All three arguments are integers. The buffer that has been changed is
   4760   ;; always the current buffer when the function is called.
   4761   ;;
   4762   ;; The length of the old text is the difference between the buffer positions
   4763   ;; before and after that text as it was before the change. As for the
   4764   ;; changed text, its length is simply the difference between the first two
   4765   ;; arguments.
   4766   ;;
   4767   ;; So (47 54 0) means add    7 chars starting at pos 47
   4768   ;; So (47 47 7) means delete 7 chars starting at pos 47
   4769   (save-match-data
   4770     (let ((inhibit-quit t)
   4771           ;; make sure that `lsp-on-change' is called in multi-workspace context
   4772           ;; see #2901
   4773           lsp--cur-workspace)
   4774       ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done
   4775       ;; by auto-revert-mode) will cause this handler to get called with a nil
   4776       ;; buffer-file-name. We need the buffer-file-name to send notifications;
   4777       ;; so we skip handling revert-buffer-caused changes and instead handle
   4778       ;; reverts separately in lsp-on-revert
   4779       (when (not revert-buffer-in-progress-p)
   4780         (cl-incf lsp--cur-version)
   4781         (mapc
   4782          (lambda (workspace)
   4783            (pcase (or lsp-document-sync-method
   4784                       (lsp--workspace-sync-method workspace))
   4785              (1
   4786               (if lsp-debounce-full-sync-notifications
   4787                   (setq lsp--delayed-requests
   4788                         (->> lsp--delayed-requests
   4789                              (-remove (-lambda ((_ buffer))
   4790                                         (equal (current-buffer) buffer)))
   4791                              (cons (list workspace
   4792                                          (current-buffer)
   4793                                          (lsp--versioned-text-document-identifier)
   4794                                          (lsp--full-change-event)))))
   4795                 (with-lsp-workspace workspace
   4796                   (lsp-notify "textDocument/didChange"
   4797                               (list :contentChanges (vector (lsp--full-change-event))
   4798                                     :textDocument (lsp--versioned-text-document-identifier))))))
   4799              (2
   4800               (with-lsp-workspace workspace
   4801                 (lsp-notify
   4802                  "textDocument/didChange"
   4803                  (list :textDocument (lsp--versioned-text-document-identifier)
   4804                        :contentChanges (vector
   4805                                         (if content-change-event-fn
   4806                                             (funcall content-change-event-fn start end length)
   4807                                           (lsp--text-document-content-change-event
   4808                                            start end length)))))))))
   4809          (lsp-workspaces))
   4810         (when lsp--delay-timer (cancel-timer lsp--delay-timer))
   4811         (setq lsp--delay-timer (run-with-idle-timer
   4812                                 lsp-debounce-full-sync-notifications-interval
   4813                                 nil
   4814                                 #'lsp--flush-delayed-changes))
   4815         ;; force cleanup overlays after each change
   4816         (lsp--remove-overlays 'lsp-highlight)
   4817         (lsp--after-change (current-buffer))
   4818         (setq lsp--signature-last-index nil
   4819               lsp--signature-last nil)
   4820         ;; cleanup diagnostics
   4821         (when lsp-diagnostic-clean-after-change
   4822           (lsp-foreach-workspace
   4823            (-let [diagnostics (lsp--workspace-diagnostics lsp--cur-workspace)]
   4824              (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics))))))))
   4825 
   4826 
   4827 
   4828 ;; facilities for on change hooks. We do not want to make lsp calls on each
   4829 ;; change event so we add debounce to avoid flooding the server with events.
   4830 ;; Additionally, we want to have a mechanism for stopping the server calls in
   4831 ;; particular cases like, e. g. when performing completion.
   4832 
   4833 (defvar lsp-inhibit-lsp-hooks nil
   4834   "Flag to control.")
   4835 
   4836 (defcustom lsp-on-change-hook nil
   4837   "Hooks to run when buffer has changed."
   4838   :type 'hook
   4839   :group 'lsp-mode)
   4840 
   4841 (defcustom lsp-idle-delay 0.500
   4842   "Debounce interval for `after-change-functions'."
   4843   :type 'number
   4844   :group 'lsp-mode)
   4845 
   4846 (defcustom lsp-on-idle-hook nil
   4847   "Hooks to run after `lsp-idle-delay'."
   4848   :type 'hook
   4849   :group 'lsp-mode)
   4850 
   4851 (defun lsp--idle-reschedule (buffer)
   4852   (when lsp--on-idle-timer
   4853     (cancel-timer lsp--on-idle-timer))
   4854 
   4855   (setq lsp--on-idle-timer (run-with-idle-timer
   4856                             lsp-idle-delay
   4857                             nil
   4858                             #'lsp--on-idle
   4859                             buffer)))
   4860 
   4861 (defun lsp--post-command ()
   4862   (lsp--cleanup-highlights-if-needed)
   4863   (lsp--idle-reschedule (current-buffer)))
   4864 
   4865 (defun lsp--on-idle (buffer)
   4866   "Start post command loop."
   4867   (when (and (buffer-live-p buffer)
   4868              (equal buffer (current-buffer))
   4869              (not lsp-inhibit-lsp-hooks)
   4870              lsp-managed-mode)
   4871     (run-hooks 'lsp-on-idle-hook)))
   4872 
   4873 (defun lsp--on-change-debounce (buffer)
   4874   (when (and (buffer-live-p buffer)
   4875              (equal buffer (current-buffer))
   4876              (not lsp-inhibit-lsp-hooks)
   4877              lsp-managed-mode)
   4878     (run-hooks 'lsp-on-change-hook)))
   4879 
   4880 (defun lsp--after-change (buffer)
   4881   (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled)
   4882     (lsp--semantic-tokens-refresh-if-enabled buffer))
   4883   (when lsp--on-change-timer
   4884     (cancel-timer lsp--on-change-timer))
   4885   (setq lsp--on-change-timer (run-with-idle-timer
   4886                               lsp-idle-delay
   4887                               nil
   4888                               #'lsp--on-change-debounce
   4889                               buffer))
   4890   (lsp--idle-reschedule buffer))
   4891 
   4892 
   4893 (defcustom lsp-trim-trailing-whitespace t
   4894   "Trim trailing whitespace on a line."
   4895   :group 'lsp-mode
   4896   :type 'boolean)
   4897 
   4898 (defcustom lsp-insert-final-newline t
   4899   "Insert a newline character at the end of the file if one does not exist."
   4900   :group 'lsp-mode
   4901   :type 'boolean)
   4902 
   4903 (defcustom lsp-trim-final-newlines t
   4904   "Trim all newlines after the final newline at the end of the file."
   4905   :group 'lsp-mode
   4906   :type 'boolean)
   4907 
   4908 
   4909 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters)
   4910   "Self insert handling.
   4911 Applies on type formatting."
   4912   (let ((ch last-command-event))
   4913     (when (or (eq (string-to-char first-trigger-characters) ch)
   4914               (cl-find ch more-trigger-characters :key #'string-to-char))
   4915       (lsp-request-async "textDocument/onTypeFormatting"
   4916                          (lsp-make-document-on-type-formatting-params
   4917                           :text-document (lsp--text-document-identifier)
   4918                           :options (lsp-make-formatting-options
   4919                                     :tab-size (symbol-value (lsp--get-indent-width major-mode))
   4920                                     :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   4921                                     :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   4922                                     :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   4923                                     :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))
   4924                           :ch (char-to-string ch)
   4925                           :position (lsp--cur-position))
   4926                          (lambda (data) (lsp--apply-text-edits data 'format))
   4927                          :mode 'tick))))
   4928 
   4929 
   4930 ;; links
   4931 (defun lsp--document-links ()
   4932   (when (lsp-feature? "textDocument/documentLink")
   4933     (lsp-request-async
   4934      "textDocument/documentLink"
   4935      `(:textDocument ,(lsp--text-document-identifier))
   4936      (lambda (links)
   4937        (lsp--remove-overlays 'lsp-link)
   4938        (seq-do
   4939         (-lambda ((link &as &DocumentLink :range (&Range :start :end)))
   4940           (-doto (make-button (lsp--position-to-point start)
   4941                               (lsp--position-to-point end)
   4942                               'action (lsp--document-link-keymap link)
   4943                               'keymap (let ((map (make-sparse-keymap)))
   4944                                         (define-key map [M-return] 'push-button)
   4945                                         (define-key map [mouse-2] 'push-button)
   4946                                         map)
   4947                               'help-echo "mouse-2, M-RET: Visit this link")
   4948             (overlay-put 'lsp-link t)))
   4949         links))
   4950      :mode 'unchanged)))
   4951 
   4952 (defun lsp--document-link-handle-target (url)
   4953   (let* ((parsed-url (url-generic-parse-url (url-unhex-string url)))
   4954          (type (url-type parsed-url)))
   4955     (pcase type
   4956       ("file"
   4957        (xref-push-marker-stack)
   4958        (find-file (lsp--uri-to-path url))
   4959        (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url))
   4960          (goto-char (lsp--position-to-point
   4961                      (lsp-make-position :character (1- (string-to-number column))
   4962                                         :line (1- (string-to-number line)))))))
   4963       ((or "http" "https") (browse-url url))
   4964       (type (if-let ((handler (lsp--get-uri-handler type)))
   4965                 (funcall handler url)
   4966               (signal 'lsp-file-scheme-not-supported (list url)))))))
   4967 
   4968 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?))
   4969   (if target?
   4970       (lambda (_)
   4971         (interactive)
   4972         (lsp--document-link-handle-target target?))
   4973     (lambda (_)
   4974       (interactive)
   4975       (when (lsp:document-link-registration-options-resolve-provider?
   4976              (lsp--capability-for-method "textDocument/documentLink"))
   4977         (lsp-request-async
   4978          "documentLink/resolve"
   4979          link
   4980          (-lambda ((&DocumentLink :target?))
   4981            (lsp--document-link-handle-target target?)))))))
   4982 
   4983 
   4984 
   4985 (defcustom lsp-warn-no-matched-clients t
   4986   "Whether to show messages when there are no supported clients."
   4987   :group 'lsp-mode
   4988   :type 'boolean)
   4989 
   4990 (defun lsp-buffer-language--configured-id ()
   4991   "Return nil when not registered."
   4992   (->> lsp-language-id-configuration
   4993        (-first
   4994         (-lambda ((mode-or-pattern . language))
   4995           (cond
   4996            ((and (stringp mode-or-pattern)
   4997                  (s-matches? mode-or-pattern (buffer-file-name)))
   4998             language)
   4999            ((eq mode-or-pattern major-mode) language))))
   5000        cl-rest))
   5001 
   5002 (defvar-local lsp--buffer-language nil
   5003   "Locally cached returned value of `lsp-buffer-language'.")
   5004 
   5005 (defun lsp-buffer-language ()
   5006   "Get language corresponding current buffer."
   5007   (or lsp--buffer-language
   5008       (let* ((configured-language (lsp-buffer-language--configured-id)))
   5009         (setq lsp--buffer-language
   5010               (or configured-language
   5011                   ;; ensure non-nil
   5012                   (string-remove-suffix "-mode" (symbol-name major-mode))))
   5013         (when (and lsp-warn-no-matched-clients
   5014                    (null configured-language))
   5015           (lsp-warn "Unable to calculate the languageId for buffer `%s'. \
   5016 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s"
   5017                     (buffer-name)
   5018                     major-mode))
   5019         lsp--buffer-language)))
   5020 
   5021 (defun lsp-activate-on (&rest languages)
   5022   "Returns language activation function.
   5023 The function will return t when the `lsp-buffer-language' returns
   5024 one of the LANGUAGES."
   5025   (lambda (_file-name _mode)
   5026     (-contains? languages (lsp-buffer-language))))
   5027 
   5028 (defun lsp-workspace-root (&optional path)
   5029   "Find the workspace root for the current file or PATH."
   5030   (-when-let* ((file-name (or path (buffer-file-name)))
   5031                (file-name (lsp-f-canonical file-name)))
   5032     (->> (lsp-session)
   5033          (lsp-session-folders)
   5034          (--filter (and (lsp--files-same-host it file-name)
   5035                         (or (lsp-f-ancestor-of? it file-name)
   5036                             (equal it file-name))))
   5037          (--max-by (> (length it) (length other))))))
   5038 
   5039 (defun lsp-on-revert ()
   5040   "Executed when a file is reverted.
   5041 Added to `after-revert-hook'."
   5042   (let ((n (buffer-size))
   5043         (revert-buffer-in-progress-p nil))
   5044     (lsp-on-change 0 n n)))
   5045 
   5046 (defun lsp--text-document-did-close (&optional keep-workspace-alive)
   5047   "Executed when the file is closed, added to `kill-buffer-hook'.
   5048 
   5049 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace
   5050 if it's closing the last buffer in the workspace."
   5051   (lsp-foreach-workspace
   5052    (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   5053    (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S"
   5054      (lsp-notify "textDocument/didClose"
   5055                  `(:textDocument ,(lsp--text-document-identifier))))
   5056    (when (and (not lsp-keep-workspace-alive)
   5057               (not keep-workspace-alive)
   5058               (not (lsp--workspace-buffers lsp--cur-workspace)))
   5059      (lsp--shutdown-workspace))))
   5060 
   5061 (defun lsp--will-save-text-document-params (reason)
   5062   (list :textDocument (lsp--text-document-identifier)
   5063         :reason reason))
   5064 
   5065 (defun lsp--before-save ()
   5066   "Before save handler."
   5067   (with-demoted-errors "Error in ‘lsp--before-save’: %S"
   5068     (let ((params (lsp--will-save-text-document-params 1)))
   5069       (when (lsp--send-will-save-p)
   5070         (lsp-notify "textDocument/willSave" params))
   5071       (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits)
   5072         (let ((lsp-response-timeout 0.1))
   5073           (condition-case nil
   5074               (lsp--apply-text-edits
   5075                (lsp-request "textDocument/willSaveWaitUntil"
   5076                             params)
   5077                'before-save)
   5078             (error)))))))
   5079 
   5080 (defun lsp--on-auto-save ()
   5081   "Handler for auto-save."
   5082   (when (lsp--send-will-save-p)
   5083     (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S"
   5084       (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2)))))
   5085 
   5086 (defun lsp--text-document-did-save ()
   5087   "Executed when the file is closed, added to `after-save-hook''."
   5088   (when (lsp--send-did-save-p)
   5089     (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’"
   5090       (lsp-notify "textDocument/didSave"
   5091                   `( :textDocument ,(lsp--versioned-text-document-identifier)
   5092                      ,@(when (lsp--save-include-text-p)
   5093                          (list :text (lsp--buffer-content))))))))
   5094 
   5095 (defun lsp--text-document-position-params (&optional identifier position)
   5096   "Make TextDocumentPositionParams for the current point in the current document.
   5097 If IDENTIFIER and POSITION are non-nil, they will be used as the document
   5098 identifier and the position respectively."
   5099   (list :textDocument (or identifier (lsp--text-document-identifier))
   5100         :position (or position (lsp--cur-position))))
   5101 
   5102 (defun lsp--get-buffer-diagnostics ()
   5103   "Return buffer diagnostics."
   5104   (gethash (or
   5105             (plist-get lsp--virtual-buffer :buffer-file-name)
   5106             (lsp--fix-path-casing (buffer-file-name)))
   5107            (lsp-diagnostics t)))
   5108 
   5109 (defun lsp-cur-line-diagnostics ()
   5110   "Return any diagnostics that apply to the current line."
   5111   (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)]
   5112     (cl-coerce (-filter
   5113                 (-lambda ((&Diagnostic :range (&Range :start (&Position :line))))
   5114                   (and (>= line start) (<= line end)))
   5115                 (lsp--get-buffer-diagnostics))
   5116                'vector)))
   5117 
   5118 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end)
   5119                                   (right &as &Range :start right-start :end right-end))
   5120   (or (lsp-point-in-range? right-start left)
   5121       (lsp-point-in-range? right-end left)
   5122       (lsp-point-in-range? left-start right)
   5123       (lsp-point-in-range? left-end right)))
   5124 
   5125 (defun lsp-make-position-1 (position)
   5126   (lsp-make-position :line (plist-get position :line)
   5127                      :character (plist-get position :character)))
   5128 
   5129 (defun lsp-cur-possition-diagnostics ()
   5130   "Return any diagnostics that apply to the current line."
   5131   (-let* ((start (if (use-region-p) (region-beginning) (point)))
   5132           (end (if (use-region-p) (region-end) (point)))
   5133           (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start))
   5134                                          :end (lsp-make-position-1 (lsp-point-to-position end)))))
   5135     (->> (lsp--get-buffer-diagnostics)
   5136          (-filter
   5137           (-lambda ((&Diagnostic :range))
   5138             (lsp-range-overlapping? range current-range)))
   5139          (apply 'vector))))
   5140 
   5141 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics)
   5142 
   5143 (defun lsp--extract-line-from-buffer (pos)
   5144   "Return the line pointed to by POS (a Position object) in the current buffer."
   5145   (let* ((point (lsp--position-to-point pos))
   5146          (inhibit-field-text-motion t))
   5147     (save-excursion
   5148       (goto-char point)
   5149       (buffer-substring (line-beginning-position) (line-end-position)))))
   5150 
   5151 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line)
   5152                                                  :end (end &as &Position :character end-char)))
   5153   "Return a xref-item from a RANGE in FILENAME."
   5154   (let* ((line (lsp--extract-line-from-buffer start))
   5155          (len (length line)))
   5156     (add-face-text-property (max (min start-char len) 0)
   5157                             (max (min end-char len) 0)
   5158                             'xref-match t line)
   5159     ;; LINE is nil when FILENAME is not being current visited by any buffer.
   5160     (xref-make (or line filename)
   5161                (xref-make-file-location
   5162                 filename
   5163                 (lsp-translate-line (1+ start-line))
   5164                 (lsp-translate-column start-char)))))
   5165 
   5166 (defun lsp--location-uri (loc)
   5167   (if (lsp-location? loc)
   5168       (lsp:location-uri loc)
   5169     (lsp:location-link-target-uri loc)))
   5170 
   5171 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start)))
   5172   "Go to location."
   5173   (let ((path (lsp--uri-to-path uri)))
   5174     (if (f-exists? path)
   5175         (with-current-buffer (find-file path)
   5176           (goto-char (lsp--position-to-point start)))
   5177       (error "There is no file %s" path))))
   5178 
   5179 (defun lsp--location-range (loc)
   5180   (if (lsp-location? loc)
   5181       (lsp:location-range loc)
   5182     (lsp:location-link-target-selection-range loc)))
   5183 
   5184 (defun lsp--locations-to-xref-items (locations)
   5185   "Return a list of `xref-item' given LOCATIONS, which can be of
   5186 type Location, LocationLink, Location[] or LocationLink[]."
   5187   (setq locations
   5188         (pcase locations
   5189           ((seq (or (Location)
   5190                     (LocationLink)))
   5191            (append locations nil))
   5192           ((or (Location)
   5193                (LocationLink))
   5194            (list locations))))
   5195 
   5196   (cl-labels ((get-xrefs-in-file
   5197                (file-locs)
   5198                (-let [(filename . matches) file-locs]
   5199                  (condition-case err
   5200                      (let ((visiting (find-buffer-visiting filename))
   5201                            (fn (lambda (loc)
   5202                                  (lsp-with-filename filename
   5203                                    (lsp--xref-make-item filename
   5204                                                         (lsp--location-range loc))))))
   5205                        (if visiting
   5206                            (with-current-buffer visiting
   5207                              (seq-map fn matches))
   5208                          (when (file-readable-p filename)
   5209                            (with-temp-buffer
   5210                              (insert-file-contents-literally filename)
   5211                              (seq-map fn matches)))))
   5212                    (error (lsp-warn "Failed to process xref entry for filename '%s': %s"
   5213                                     filename (error-message-string err)))
   5214                    (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s"
   5215                                          filename (error-message-string err)))))))
   5216 
   5217     (->> locations
   5218          (seq-sort #'lsp--location-before-p)
   5219          (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri))
   5220          (seq-map #'get-xrefs-in-file)
   5221          (apply #'nconc))))
   5222 
   5223 (defun lsp--location-before-p (left right)
   5224   "Sort first by file, then by line, then by column."
   5225   (let ((left-uri (lsp--location-uri left))
   5226         (right-uri (lsp--location-uri right)))
   5227     (if (not (string= left-uri right-uri))
   5228         (string< left-uri right-uri)
   5229       (-let (((&Range :start left-start) (lsp--location-range left))
   5230              ((&Range :start right-start) (lsp--location-range right)))
   5231         (lsp--position-compare right-start left-start)))))
   5232 
   5233 (defun lsp--make-reference-params (&optional td-position exclude-declaration)
   5234   "Make a ReferenceParam object.
   5235 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead.
   5236 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations."
   5237   (let ((json-false :json-false))
   5238     (plist-put (or td-position (lsp--text-document-position-params))
   5239                :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration))))))
   5240 
   5241 (defun lsp--cancel-request (id)
   5242   "Cancel request with ID in all workspaces."
   5243   (lsp-foreach-workspace
   5244    (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id))
   5245    (lsp-notify "$/cancelRequest" `(:id ,id))))
   5246 
   5247 (defvar-local lsp--hover-saved-bounds nil)
   5248 
   5249 (defun lsp-eldoc-function (cb &rest _ignored)
   5250   "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')."
   5251   (if (and lsp--hover-saved-bounds
   5252            (lsp--point-in-bounds-p lsp--hover-saved-bounds))
   5253       lsp--eldoc-saved-message
   5254     (setq lsp--hover-saved-bounds nil
   5255           lsp--eldoc-saved-message nil)
   5256     (if (looking-at-p "[[:space:]\n]")
   5257         (setq lsp--eldoc-saved-message nil) ; And returns nil.
   5258       (when (and lsp-eldoc-enable-hover (lsp--capability :hoverProvider))
   5259         (lsp-request-async
   5260          "textDocument/hover"
   5261          (lsp--text-document-position-params)
   5262          (-lambda ((hover &as &Hover? :range? :contents))
   5263            (setq lsp--hover-saved-bounds (when range?
   5264                                            (lsp--range-to-region range?)))
   5265            (funcall cb (setq lsp--eldoc-saved-message
   5266                              (when contents
   5267                                (lsp--render-on-hover-content
   5268                                 contents
   5269                                 lsp-eldoc-render-all)))))
   5270          :error-handler #'ignore
   5271          :mode 'tick
   5272          :cancel-token :eldoc-hover)))))
   5273 
   5274 (defun lsp--point-on-highlight? ()
   5275   (-some? (lambda (overlay)
   5276             (overlay-get overlay 'lsp-highlight))
   5277           (overlays-at (point))))
   5278 
   5279 (defun lsp--cleanup-highlights-if-needed ()
   5280   (when (and lsp-enable-symbol-highlighting
   5281              lsp--have-document-highlights
   5282              (not (lsp--point-on-highlight?)))
   5283     (lsp--remove-overlays 'lsp-highlight)
   5284     (setq lsp--have-document-highlights nil)
   5285     (lsp-cancel-request-by-token :highlights)))
   5286 
   5287 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil
   5288   "The bounds of the symbol from which `lsp--document-highlight'
   5289   most recently requested highlights.")
   5290 
   5291 (defun lsp--document-highlight ()
   5292   (when (lsp-feature? "textDocument/documentHighlight")
   5293     (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol)))
   5294       (unless (or (looking-at-p "[[:space:]\n]")
   5295                   (not lsp-enable-symbol-highlighting)
   5296                   (and lsp--have-document-highlights
   5297                        curr-sym-bounds
   5298                        (equal curr-sym-bounds
   5299                               lsp--symbol-bounds-of-last-highlight-invocation)))
   5300         (setq lsp--symbol-bounds-of-last-highlight-invocation
   5301               curr-sym-bounds)
   5302         (lsp-request-async "textDocument/documentHighlight"
   5303                            (lsp--text-document-position-params)
   5304                            #'lsp--document-highlight-callback
   5305                            :mode 'tick
   5306                            :cancel-token :highlights)))))
   5307 
   5308 (defun lsp--help-open-link (&rest _)
   5309   "Open markdown link at point via mouse or keyboard."
   5310   (interactive "P")
   5311   (let ((buffer-list-update-hook nil))
   5312     (-let [(buffer point) (if-let* ((valid (and (listp last-input-event)
   5313                                                 (eq (car last-input-event) 'mouse-2)))
   5314                                     (event (cadr last-input-event))
   5315                                     (win (posn-window event))
   5316                                     (buffer (window-buffer win)))
   5317                               `(,buffer ,(posn-point event))
   5318                             `(,(current-buffer) ,(point)))]
   5319       (with-current-buffer buffer
   5320         (when-let* ((face (get-text-property point 'face))
   5321                     (url (or (and (eq face 'markdown-link-face)
   5322                                   (get-text-property point 'help-echo))
   5323                              (and (memq face '(markdown-url-face markdown-plain-url-face))
   5324                                   (nth 3 (markdown-link-at-pos point))))))
   5325           (lsp--document-link-handle-target url))))))
   5326 
   5327 (defvar lsp-help-mode-map
   5328   (-doto (make-sparse-keymap)
   5329     (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link))
   5330   "Keymap for `lsp-help-mode'.")
   5331 
   5332 (define-derived-mode lsp-help-mode help-mode "LspHelp"
   5333   "Major mode for displaying lsp help.")
   5334 
   5335 (defun lsp-describe-thing-at-point ()
   5336   "Display the type signature and documentation of the thing at point."
   5337   (interactive)
   5338   (let ((contents (-some->> (lsp--text-document-position-params)
   5339                     (lsp--make-request "textDocument/hover")
   5340                     (lsp--send-request)
   5341                     (lsp:hover-contents))))
   5342     (if (and contents (not (equal contents "")))
   5343         (let ((lsp-help-buf-name "*lsp-help*"))
   5344           (with-current-buffer (get-buffer-create lsp-help-buf-name)
   5345             (delay-mode-hooks
   5346               (lsp-help-mode)
   5347               (with-help-window lsp-help-buf-name
   5348                 (insert (string-trim-right (lsp--render-on-hover-content contents t)))))
   5349             (run-mode-hooks)))
   5350       (lsp--info "No content at point."))))
   5351 
   5352 (defun lsp--point-in-bounds-p (bounds)
   5353   "Return whether the current point is within BOUNDS."
   5354   (and (<= (car bounds) (point)) (< (point) (cdr bounds))))
   5355 
   5356 (defun lsp-get-renderer (language)
   5357   "Get renderer for LANGUAGE."
   5358   (lambda (str)
   5359     (lsp--render-string str language)))
   5360 
   5361 (defun lsp--setup-markdown (mode)
   5362   "Setup the ‘markdown-mode’ in the frame.
   5363 MODE is the mode used in the parent frame."
   5364   (make-local-variable 'markdown-code-lang-modes)
   5365   (dolist (mark (alist-get mode lsp-custom-markup-modes))
   5366     (add-to-list 'markdown-code-lang-modes (cons mark mode)))
   5367   (setq-local markdown-fontify-code-blocks-natively t)
   5368   (setq-local markdown-fontify-code-block-default-mode mode)
   5369   (setq-local markdown-hide-markup t)
   5370 
   5371   ;; Render some common HTML entities.
   5372   ;; This should really happen in markdown-mode instead,
   5373   ;; but it doesn't, so we do it here for now.
   5374   (setq prettify-symbols-alist
   5375         (cl-loop for i from 0 to 255
   5376                  collect (cons (format "&#x%02X;" i) i)))
   5377   (push '("&lt;" . ?<) prettify-symbols-alist)
   5378   (push '("&gt;" . ?>) prettify-symbols-alist)
   5379   (push '("&amp;" . ?&) prettify-symbols-alist)
   5380   (push '("&nbsp;" . ? ) prettify-symbols-alist)
   5381   (setq prettify-symbols-compose-predicate
   5382         (lambda (_start _end _match) t))
   5383   (prettify-symbols-mode 1))
   5384 
   5385 (defvar lsp-help-link-keymap
   5386   (let ((map (make-sparse-keymap)))
   5387     (define-key map [mouse-2] #'lsp--help-open-link)
   5388     (define-key map "\r" #'lsp--help-open-link)
   5389     map)
   5390   "Keymap active on links in *lsp-help* mode.")
   5391 
   5392 (defun lsp--fix-markdown-links ()
   5393   (let ((inhibit-read-only t)
   5394         (inhibit-modification-hooks t)
   5395         (prop))
   5396     (save-restriction
   5397       (goto-char (point-min))
   5398       (while (setq prop (markdown-find-next-prop 'face))
   5399         (let ((end (or (next-single-property-change (car prop) 'face)
   5400                        (point-max))))
   5401           (when (memq (get-text-property (car prop) 'face)
   5402                       '(markdown-link-face
   5403                         markdown-url-face
   5404                         markdown-plain-url-face))
   5405             (add-text-properties (car prop) end
   5406                                  (list 'button t
   5407                                        'category 'lsp-help-link
   5408                                        'follow-link t
   5409                                        'keymap lsp-help-link-keymap)))
   5410           (goto-char end))))))
   5411 
   5412 (defun lsp--buffer-string-visible ()
   5413   "Return visible buffer string.
   5414 Stolen from `org-copy-visible'."
   5415   (let ((temp (generate-new-buffer " *temp*"))
   5416         (beg (point-min))
   5417         (end (point-max)))
   5418     (while (/= beg end)
   5419       (when (get-char-property beg 'invisible)
   5420         (setq beg (next-single-char-property-change beg 'invisible nil end)))
   5421       (let* ((next (next-single-char-property-change beg 'invisible nil end))
   5422              (substring (buffer-substring beg next)))
   5423         (with-current-buffer temp (insert substring))
   5424         ;; (setq result (concat result substring))
   5425         (setq beg next)))
   5426     (setq deactivate-mark t)
   5427     (prog1 (with-current-buffer temp
   5428              (s-chop-suffix "\n" (buffer-string)))
   5429       (kill-buffer temp))))
   5430 
   5431 (defvar lsp-buffer-major-mode nil
   5432   "Holds the major mode when fontification function is running.
   5433 See #2588")
   5434 
   5435 (defvar view-inhibit-help-message)
   5436 
   5437 (defun lsp--render-markdown ()
   5438   "Render markdown."
   5439 
   5440   (let ((markdown-enable-math nil))
   5441     (goto-char (point-min))
   5442     (while (re-search-forward
   5443             (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/"
   5444                                      "{" "}" "[" "]" "(" ")"
   5445                                      "#" "+" "-" "." "!" "|"))))
   5446             nil t)
   5447       (replace-match (rx (backref 1))))
   5448 
   5449     ;; markdown-mode v2.3 does not yet provide gfm-view-mode
   5450     (if (fboundp 'gfm-view-mode)
   5451         (let ((view-inhibit-help-message t))
   5452           (gfm-view-mode))
   5453       (gfm-mode))
   5454 
   5455     (lsp--setup-markdown lsp-buffer-major-mode)))
   5456 
   5457 (defvar lsp--display-inline-image-alist
   5458   '((lsp--render-markdown
   5459      (:regexp
   5460       "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)"
   5461       :sexp
   5462       (create-image
   5463        (base64-decode-string
   5464         (buffer-substring-no-properties (match-beginning 1) (match-end 1)))
   5465        nil t))))
   5466   "Replaced string regexp and function returning image.
   5467 Each element should have the form (MODE . (PROPERTY-LIST...)).
   5468 MODE (car) is function which is defined in `lsp-language-id-configuration'.
   5469 Cdr should be list of PROPERTY-LIST.
   5470 
   5471 Each PROPERTY-LIST should have properties:
   5472 :regexp  Regexp which determines what string is relpaced to image.
   5473          You should also get information of image, by parenthesis constructs.
   5474          By default, all matched string is replaced to image, but you can
   5475          change index of replaced string by keyword :replaced-index.
   5476 
   5477 :sexp    Return image when evaluated. You can use information of regexp
   5478          by using (match-beggining N), (match-end N) or (match-substring N).
   5479 
   5480 In addition, each can have property:
   5481 :replaced-index  Determine index which is used to replace regexp to image.
   5482                  The value means first argument of `match-beginning' and
   5483                  `match-end'. If omitted, interpreted as index 0.")
   5484 
   5485 (defcustom lsp-display-inline-image t
   5486   "Showing inline image or not."
   5487   :group 'lsp-mode
   5488   :type 'boolean)
   5489 
   5490 (defcustom lsp-enable-suggest-server-download t
   5491   "When non-nil enable server downloading suggestions."
   5492   :group 'lsp-mode
   5493   :type 'boolean
   5494   :package-version '(lsp-mode . "9.0.0"))
   5495 
   5496 (defcustom lsp-auto-register-remote-clients t
   5497   "When non-nil register remote when registering the local one."
   5498   :group 'lsp-mode
   5499   :type 'boolean
   5500   :package-version '(lsp-mode . "9.0.0"))
   5501 
   5502 (defun lsp--display-inline-image (mode)
   5503   "Add image property if available."
   5504   (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist))))
   5505     (when (and (display-images-p) lsp-display-inline-image)
   5506       (cl-loop
   5507        for plist in plist-list
   5508        with regexp with replaced-index
   5509        do
   5510        (setq regexp (plist-get plist :regexp))
   5511        (setq replaced-index (or (plist-get plist :replaced-index) 0))
   5512 
   5513        (font-lock-remove-keywords nil (list regexp replaced-index))
   5514        (let ((inhibit-read-only t))
   5515          (save-excursion
   5516            (goto-char (point-min))
   5517            (while (re-search-forward regexp nil t)
   5518              (set-text-properties
   5519               (match-beginning replaced-index) (match-end replaced-index)
   5520               nil)
   5521              (add-text-properties
   5522               (match-beginning replaced-index) (match-end replaced-index)
   5523               `(display ,(eval (plist-get plist :sexp)))))))))))
   5524 
   5525 (defun lsp--fontlock-with-mode (str mode)
   5526   "Fontlock STR with MODE."
   5527   (let ((lsp-buffer-major-mode major-mode))
   5528     (with-temp-buffer
   5529       (with-demoted-errors "Error during doc rendering: %s"
   5530         (insert str)
   5531         (delay-mode-hooks (funcall mode))
   5532         (cl-flet ((window-body-width () lsp-window-body-width))
   5533           ;; This can go wrong in some cases, and the fontification would
   5534           ;; not work as expected.
   5535           ;;
   5536           ;; See #2984
   5537           (ignore-errors (font-lock-ensure))
   5538           (lsp--display-inline-image mode)
   5539           (when (eq mode 'lsp--render-markdown)
   5540             (lsp--fix-markdown-links))))
   5541       (lsp--buffer-string-visible))))
   5542 
   5543 (defun lsp--render-string (str language)
   5544   "Render STR using `major-mode' corresponding to LANGUAGE.
   5545 When language is nil render as markup if `markdown-mode' is loaded."
   5546   (setq str (s-replace "\r" "" (or str "")))
   5547   (if-let* ((modes (-keep (-lambda ((mode . lang))
   5548                             (when (and (equal lang language) (functionp mode))
   5549                               mode))
   5550                           lsp-language-id-configuration))
   5551             (mode (car (or (member major-mode modes) modes))))
   5552       (lsp--fontlock-with-mode str mode)
   5553     str))
   5554 
   5555 (defun lsp--render-element (content)
   5556   "Render CONTENT element."
   5557   (let ((inhibit-message t))
   5558     (or
   5559      (pcase content
   5560        ((MarkedString :value :language)
   5561         (lsp--render-string value language))
   5562        ((MarkupContent :value :kind)
   5563         (lsp--render-string value kind))
   5564        ;; plain string
   5565        ((pred stringp) (lsp--render-string content "markdown"))
   5566        ((pred null) "")
   5567        (_ (error "Failed to handle %s" content)))
   5568      "")))
   5569 
   5570 (defun lsp--create-unique-string-fn ()
   5571   (let (elements)
   5572     (lambda (element)
   5573       (let ((count (cl-count element elements :test #'string=)))
   5574         (prog1 (if (zerop count)
   5575                    element
   5576                  (format "%s (%s)" element count))
   5577           (push element elements))))))
   5578 
   5579 (defun lsp--select-action (actions)
   5580   "Select an action to execute from ACTIONS."
   5581   (cond
   5582    ((seq-empty-p actions) (signal 'lsp-no-code-actions nil))
   5583    ((and (eq (seq-length actions) 1) lsp-auto-execute-action)
   5584     (lsp-seq-first actions))
   5585    (t (let ((completion-ignore-case t))
   5586         (lsp--completing-read "Select code action: "
   5587                               (seq-into actions 'list)
   5588                               (-compose (lsp--create-unique-string-fn)
   5589                                         #'lsp:code-action-title)
   5590                               nil t)))))
   5591 
   5592 (defun lsp--workspace-server-id (workspace)
   5593   "Return the server ID of WORKSPACE."
   5594   (-> workspace lsp--workspace-client lsp--client-server-id))
   5595 
   5596 (defun lsp--handle-rendered-for-echo-area (contents)
   5597   "Return a single line from RENDERED, appropriate for display in the echo area."
   5598   (pcase (lsp-workspaces)
   5599     (`(,workspace)
   5600      (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace)))
   5601     ;; For projects with multiple active workspaces we also default to
   5602     ;; render the first line.
   5603     (_ (lsp-clients-extract-signature-on-hover contents nil))))
   5604 
   5605 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id)
   5606   "Extract a representative line from CONTENTS, to show in the echo area."
   5607   (car (s-lines (s-trim (lsp--render-element contents)))))
   5608 
   5609 (defun lsp--render-on-hover-content (contents render-all)
   5610   "Render the content received from `document/onHover' request.
   5611 CONTENTS  - MarkedString | MarkedString[] | MarkupContent
   5612 RENDER-ALL - nil if only the signature should be rendered."
   5613   (cond
   5614    ((lsp-markup-content? contents)
   5615     ;; MarkupContent.
   5616     ;; It tends to be long and is not suitable to display fully in the echo area.
   5617     ;; Just display the first line which is typically the signature.
   5618     (if render-all
   5619         (lsp--render-element contents)
   5620       (lsp--handle-rendered-for-echo-area contents)))
   5621    ((and (stringp contents) (not (string-match-p "\n" contents)))
   5622     ;; If the contents is a single string containing a single line,
   5623     ;; render it always.
   5624     (lsp--render-element contents))
   5625    (t
   5626     ;; MarkedString -> MarkedString[]
   5627     (when (or (lsp-marked-string? contents) (stringp contents))
   5628       (setq contents (list contents)))
   5629     ;; Consider the signature consisting of the elements who have a renderable
   5630     ;; "language" property. When render-all is nil, ignore other elements.
   5631     (string-join
   5632      (seq-map
   5633       #'lsp--render-element
   5634       (if render-all
   5635           contents
   5636         ;; Only render contents that have an available renderer.
   5637         (seq-take
   5638          (seq-filter
   5639           (-andfn #'lsp-marked-string?
   5640                   (-compose #'lsp-get-renderer #'lsp:marked-string-language))
   5641           contents)
   5642          1)))
   5643      (if (bound-and-true-p page-break-lines-mode)
   5644          "\n\n"
   5645        "\n")))))
   5646 
   5647 
   5648 
   5649 (defvar lsp-signature-mode-map
   5650   (-doto (make-sparse-keymap)
   5651     (define-key (kbd "M-n") #'lsp-signature-next)
   5652     (define-key (kbd "M-p") #'lsp-signature-previous)
   5653     (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs)
   5654     (define-key (kbd "C-c C-k") #'lsp-signature-stop)
   5655     (define-key (kbd "C-g") #'lsp-signature-stop))
   5656   "Keymap for `lsp-signature-mode'.")
   5657 
   5658 (define-minor-mode lsp-signature-mode
   5659   "Mode used to show signature popup."
   5660   :keymap lsp-signature-mode-map
   5661   :lighter ""
   5662   :group 'lsp-mode)
   5663 
   5664 (defun lsp-signature-stop ()
   5665   "Stop showing current signature help."
   5666   (interactive)
   5667   (lsp-cancel-request-by-token :signature)
   5668   (remove-hook 'post-command-hook #'lsp-signature)
   5669   (funcall lsp-signature-function nil)
   5670   (lsp-signature-mode -1))
   5671 
   5672 (declare-function page-break-lines--update-display-tables "ext:page-break-lines")
   5673 
   5674 (defun lsp--setup-page-break-mode-if-present ()
   5675   "Enable `page-break-lines-mode' in current buffer."
   5676   (when (fboundp 'page-break-lines-mode)
   5677     (page-break-lines-mode)
   5678     ;; force page-break-lines-mode to update the display tables.
   5679     (page-break-lines--update-display-tables)))
   5680 
   5681 (defun lsp-lv-message (message)
   5682   (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)
   5683   (if message
   5684       (progn
   5685         (setq lsp--signature-last-buffer (current-buffer))
   5686         (let ((lv-force-update t))
   5687           (lv-message "%s" message)))
   5688     (lv-delete-window)
   5689     (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)))
   5690 
   5691 (declare-function posframe-show "ext:posframe")
   5692 (declare-function posframe-hide "ext:posframe")
   5693 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe")
   5694 
   5695 (defface lsp-signature-posframe
   5696   '((t :inherit tooltip))
   5697   "Background and foreground for `lsp-signature-posframe'."
   5698   :group 'lsp-mode)
   5699 
   5700 (defvar lsp-signature-posframe-params
   5701   (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward
   5702         :height 10
   5703         :width 60
   5704         :border-width 1
   5705         :min-width 60)
   5706   "Params for signature and `posframe-show'.")
   5707 
   5708 (defun lsp-signature-posframe (str)
   5709   "Use posframe to show the STR signatureHelp string."
   5710   (if str
   5711       (apply #'posframe-show
   5712              (with-current-buffer (get-buffer-create " *lsp-signature*")
   5713                (erase-buffer)
   5714                (insert str)
   5715                (visual-line-mode 1)
   5716                (lsp--setup-page-break-mode-if-present)
   5717                (current-buffer))
   5718              (append
   5719               lsp-signature-posframe-params
   5720               (list :position (point)
   5721                     :background-color (face-attribute 'lsp-signature-posframe :background nil t)
   5722                     :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t)
   5723                     :border-color (face-attribute 'font-lock-comment-face :foreground nil t))))
   5724     (posframe-hide " *lsp-signature*")))
   5725 
   5726 (defun lsp--handle-signature-update (signature)
   5727   (let ((message
   5728          (if (lsp-signature-help? signature)
   5729              (lsp--signature->message signature)
   5730            (mapconcat #'lsp--signature->message signature "\n"))))
   5731     (if (s-present? message)
   5732         (funcall lsp-signature-function message)
   5733       (lsp-signature-stop))))
   5734 
   5735 (defun lsp-signature-activate ()
   5736   "Activate signature help.
   5737 It will show up only if current point has signature help."
   5738   (interactive)
   5739   (setq lsp--signature-last nil
   5740         lsp--signature-last-index nil
   5741         lsp--signature-last-buffer (current-buffer))
   5742   (add-hook 'post-command-hook #'lsp-signature)
   5743   (lsp-signature-mode t))
   5744 
   5745 (defcustom lsp-signature-cycle t
   5746   "Whether `lsp-signature-next' and prev should cycle."
   5747   :type 'boolean
   5748   :group 'lsp-mode)
   5749 
   5750 (defun lsp-signature-next ()
   5751   "Show next signature."
   5752   (interactive)
   5753   (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last))))
   5754     (when (and lsp--signature-last-index
   5755                lsp--signature-last
   5756                (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs)))
   5757       (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs))
   5758       (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))))
   5759 
   5760 (defun lsp-signature-previous ()
   5761   "Next signature."
   5762   (interactive)
   5763   (when (and lsp--signature-last-index
   5764              lsp--signature-last
   5765              (or lsp-signature-cycle (not (zerop lsp--signature-last-index))))
   5766     (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index)
   5767                                             (length (lsp:signature-help-signatures lsp--signature-last))
   5768                                           lsp--signature-last-index)))
   5769     (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))
   5770 
   5771 (defun lsp-signature-toggle-full-docs ()
   5772   "Toggle full/partial signature documentation."
   5773   (interactive)
   5774   (let ((all? (not (numberp lsp-signature-doc-lines))))
   5775     (setq lsp-signature-doc-lines (if all?
   5776                                       (or (car-safe lsp-signature-doc-lines)
   5777                                           20)
   5778                                     (list lsp-signature-doc-lines))))
   5779   (lsp-signature-activate))
   5780 
   5781 (defun lsp--signature->message (signature-help)
   5782   "Generate eldoc message from SIGNATURE-HELP response."
   5783   (setq lsp--signature-last signature-help)
   5784 
   5785   (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help))))
   5786     (-let* (((&SignatureHelp :active-signature?
   5787                              :active-parameter?
   5788                              :signatures) signature-help)
   5789             (active-signature? (or lsp--signature-last-index active-signature? 0))
   5790             (_ (setq lsp--signature-last-index active-signature?))
   5791             ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?))
   5792             (prefix (if (= (length signatures) 1)
   5793                         ""
   5794                       (concat (propertize (format " %s/%s"
   5795                                                   (1+ active-signature?)
   5796                                                   (length signatures))
   5797                                           'face 'success)
   5798                               " ")))
   5799             (method-docs (when
   5800                              (and lsp-signature-render-documentation
   5801                                   (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines)))
   5802                            (let ((docs (lsp--render-element
   5803                                         (lsp:parameter-information-documentation? signature))))
   5804                              (when (s-present? docs)
   5805                                (concat
   5806                                 "\n"
   5807                                 (if (fboundp 'page-break-lines-mode)
   5808                                     "\n"
   5809                                   "")
   5810                                 (if (and (numberp lsp-signature-doc-lines)
   5811                                          (> (length (s-lines docs)) lsp-signature-doc-lines))
   5812                                     (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs)))
   5813                                             (propertize "\nTruncated..." 'face 'highlight))
   5814                                   docs)))))))
   5815       (when (and active-parameter? (not (seq-empty-p parameters?)))
   5816         (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?)))
   5817                               (seq-elt parameters? active-parameter?)))
   5818                      (selected-param-label (let ((label (lsp:parameter-information-label param)))
   5819                                              (if (stringp label) label (append label nil))))
   5820                      (start (if (stringp selected-param-label)
   5821                                 (s-index-of selected-param-label label)
   5822                               (cl-first selected-param-label)))
   5823                      (end (if (stringp selected-param-label)
   5824                               (+ start (length selected-param-label))
   5825                             (cl-second selected-param-label))))
   5826           (add-face-text-property start end 'eldoc-highlight-function-argument nil label)))
   5827       (concat prefix label method-docs))))
   5828 
   5829 (defun lsp-signature ()
   5830   "Display signature info (based on `textDocument/signatureHelp')"
   5831   (if (and lsp--signature-last-buffer
   5832            (not (equal (current-buffer) lsp--signature-last-buffer)))
   5833       (lsp-signature-stop)
   5834     (lsp-request-async "textDocument/signatureHelp"
   5835                        (lsp--text-document-position-params)
   5836                        #'lsp--handle-signature-update
   5837                        :cancel-token :signature)))
   5838 
   5839 
   5840 (defcustom lsp-overlay-document-color-char "■"
   5841   "Display the char represent the document color in overlay"
   5842   :type 'string
   5843   :group 'lsp-mode)
   5844 
   5845 ;; color presentation
   5846 (defun lsp--color-create-interactive-command (color range)
   5847   (lambda ()
   5848     (interactive)
   5849     (-let [(&ColorPresentation? :text-edit?
   5850                                 :additional-text-edits?)
   5851            (lsp--completing-read
   5852             "Select color presentation: "
   5853             (lsp-request
   5854              "textDocument/colorPresentation"
   5855              `( :textDocument ,(lsp--text-document-identifier)
   5856                 :color ,color
   5857                 :range ,range))
   5858             #'lsp:color-presentation-label
   5859             nil
   5860             t)]
   5861       (when text-edit?
   5862         (lsp--apply-text-edit text-edit?))
   5863       (when additional-text-edits?
   5864         (lsp--apply-text-edits additional-text-edits? 'color-presentation)))))
   5865 
   5866 (defun lsp--number->color (number)
   5867   (let ((result (format "%x"
   5868                         (round (* (or number 0) 255.0)))))
   5869     (if (= 1 (length result))
   5870         (concat "0" result)
   5871       result)))
   5872 
   5873 (defun lsp--document-color ()
   5874   "Document color handler."
   5875   (when (lsp-feature? "textDocument/documentColor")
   5876     (lsp-request-async
   5877      "textDocument/documentColor"
   5878      `(:textDocument ,(lsp--text-document-identifier))
   5879      (lambda (result)
   5880        (lsp--remove-overlays 'lsp-color)
   5881        (seq-do
   5882         (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue)
   5883                                      :range))
   5884           (-let* (((beg . end) (lsp--range-to-region range))
   5885                   (overlay (make-overlay beg end))
   5886                   (command (lsp--color-create-interactive-command color range)))
   5887             (overlay-put overlay 'lsp-color t)
   5888             (overlay-put overlay 'evaporate t)
   5889             (overlay-put overlay
   5890                          'before-string
   5891                          (propertize
   5892                           lsp-overlay-document-color-char
   5893                           'face `((:foreground ,(format
   5894                                                  "#%s%s%s"
   5895                                                  (lsp--number->color red)
   5896                                                  (lsp--number->color green)
   5897                                                  (lsp--number->color blue))))
   5898                           'action command
   5899                           'mouse-face 'lsp-lens-mouse-face
   5900                           'local-map (-doto (make-sparse-keymap)
   5901                                        (define-key [mouse-1] command))))))
   5902         result))
   5903      :mode 'unchanged
   5904      :cancel-token :document-color-token)))
   5905 
   5906 
   5907 
   5908 (defun lsp--action-trigger-parameter-hints (_command)
   5909   "Handler for editor.action.triggerParameterHints."
   5910   (when (member :on-server-request lsp-signature-auto-activate)
   5911     (lsp-signature-activate)))
   5912 
   5913 (defun lsp--action-trigger-suggest (_command)
   5914   "Handler for editor.action.triggerSuggest."
   5915   (cond
   5916    ((and (bound-and-true-p company-mode)
   5917          (fboundp 'company-auto-begin)
   5918          (fboundp 'company-post-command))
   5919     (run-at-time 0 nil
   5920                  (lambda ()
   5921                    (let ((this-command 'company-idle-begin)
   5922                          (company-minimum-prefix-length 0))
   5923                      (company-auto-begin)
   5924                      (company-post-command)))))
   5925    (t
   5926     (completion-at-point))))
   5927 
   5928 (defconst lsp--default-action-handlers
   5929   (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints)
   5930       ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest))
   5931   "Default action handlers.")
   5932 
   5933 (defun lsp--find-action-handler (command)
   5934   "Find action handler for particular COMMAND."
   5935   (or
   5936    (--some (-some->> it
   5937              (lsp--workspace-client)
   5938              (lsp--client-action-handlers)
   5939              (gethash command))
   5940            (lsp-workspaces))
   5941    (gethash command lsp--default-action-handlers)))
   5942 
   5943 (defun lsp--text-document-code-action-params (&optional kind)
   5944   "Code action params."
   5945   (list :textDocument (lsp--text-document-identifier)
   5946         :range (if (use-region-p)
   5947                    (lsp--region-to-range (region-beginning) (region-end))
   5948                  (lsp--region-to-range (point) (point)))
   5949         :context `( :diagnostics ,(lsp-cur-possition-diagnostics)
   5950                     ,@(when kind (list :only (vector kind))))))
   5951 
   5952 (defun lsp-code-actions-at-point (&optional kind)
   5953   "Retrieve the code actions for the active region or the current line.
   5954 It will filter by KIND if non nil."
   5955   (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind)))
   5956 
   5957 (defun lsp-execute-code-action-by-kind (command-kind)
   5958   "Execute code action by COMMAND-KIND."
   5959   (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind)
   5960                         (-filter (-lambda ((&CodeAction :kind?))
   5961                                    (and kind? (s-prefix? command-kind kind?))))
   5962                         lsp--select-action)))
   5963       (lsp-execute-code-action action)
   5964     (signal 'lsp-no-code-actions '(command-kind))))
   5965 
   5966 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point)
   5967 
   5968 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?))
   5969   "Parse and execute a code ACTION represented as a Command LSP type."
   5970   (let ((server-id (->> (lsp-workspaces)
   5971                         (cl-first)
   5972                         (or lsp--cur-workspace)
   5973                         (lsp--workspace-client)
   5974                         (lsp--client-server-id))))
   5975     (condition-case nil
   5976         (with-no-warnings
   5977           (lsp-execute-command server-id (intern command) arguments?))
   5978       (cl-no-applicable-method
   5979        (if-let ((action-handler (lsp--find-action-handler command)))
   5980            (funcall action-handler action)
   5981          (lsp-send-execute-command command arguments?))))))
   5982 
   5983 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?))
   5984   "Execute code action ACTION. For example, when text under the
   5985 caret has a suggestion to apply a fix from an lsp-server, calling
   5986 this function will do so.
   5987 If ACTION is not set it will be selected from `lsp-code-actions-at-point'.
   5988 Request codeAction/resolve for more info if server supports."
   5989   (interactive (list (lsp--select-action (lsp-code-actions-at-point))))
   5990   (if (and (lsp-feature? "codeAction/resolve")
   5991            (not command?)
   5992            (not edit?))
   5993       (lsp--execute-code-action (lsp-request "codeAction/resolve" action))
   5994     (lsp--execute-code-action action)))
   5995 
   5996 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?))
   5997   "Execute code action ACTION."
   5998   (when edit?
   5999     (lsp--apply-workspace-edit edit? 'code-action))
   6000 
   6001   (cond
   6002    ((stringp command?) (lsp--execute-command action))
   6003    ((lsp-command? command?) (lsp--execute-command command?))))
   6004 
   6005 (defvar lsp--formatting-indent-alist
   6006   ;; Taken from `dtrt-indent-mode'
   6007   '(
   6008     (ada-mode                   . ada-indent)                       ; Ada
   6009     (ada-ts-mode                . ada-ts-mode-indent-offset)
   6010     (c++-mode                   . c-basic-offset)                   ; C++
   6011     (c++-ts-mode                . c-ts-mode-indent-offset)
   6012     (c-mode                     . c-basic-offset)                   ; C
   6013     (c-ts-mode                  . c-ts-mode-indent-offset)
   6014     (cperl-mode                 . cperl-indent-level)               ; Perl
   6015     (crystal-mode               . crystal-indent-level)             ; Crystal (Ruby)
   6016     (csharp-mode                . c-basic-offset)                   ; C#
   6017     (csharp-tree-sitter-mode    . csharp-tree-sitter-indent-offset) ; C#
   6018     (csharp-ts-mode             . csharp-ts-mode-indent-offset)     ; C# (tree-sitter, Emacs29)
   6019     (css-mode                   . css-indent-offset)                ; CSS
   6020     (d-mode                     . c-basic-offset)                   ; D
   6021     (enh-ruby-mode              . enh-ruby-indent-level)            ; Ruby
   6022     (erlang-mode                . erlang-indent-level)              ; Erlang
   6023     (ess-mode                   . ess-indent-offset)                ; ESS (R)
   6024     (go-ts-mode                 . go-ts-mode-indent-offset)
   6025     (gpr-mode                   . gpr-indent-offset)                ; GNAT Project
   6026     (gpr-ts-mode                . gpr-ts-mode-indent-offset)
   6027     (hack-mode                  . hack-indent-offset)               ; Hack
   6028     (java-mode                  . c-basic-offset)                   ; Java
   6029     (java-ts-mode               . java-ts-mode-indent-offset)
   6030     (jde-mode                   . c-basic-offset)                   ; Java (JDE)
   6031     (js-mode                    . js-indent-level)                  ; JavaScript
   6032     (js-ts-mode                 . js-indent-level)
   6033     (js2-mode                   . js2-basic-offset)                 ; JavaScript-IDE
   6034     (js3-mode                   . js3-indent-level)                 ; JavaScript-IDE
   6035     (json-mode                  . js-indent-level)                  ; JSON
   6036     (json-ts-mode               . json-ts-mode-indent-offset)
   6037     (lua-mode                   . lua-indent-level)                 ; Lua
   6038     (lua-ts-mode                . lua-ts-indent-offset)
   6039     (nxml-mode                  . nxml-child-indent)                ; XML
   6040     (objc-mode                  . c-basic-offset)                   ; Objective C
   6041     (pascal-mode                . pascal-indent-level)              ; Pascal
   6042     (perl-mode                  . perl-indent-level)                ; Perl
   6043     (php-mode                   . c-basic-offset)                   ; PHP
   6044     (php-ts-mode                . php-ts-mode-indent-offset)        ; PHP
   6045     (powershell-mode            . powershell-indent)                ; PowerShell
   6046     (powershell-ts-mode         . powershell-ts-mode-indent-offset) ; PowerShell
   6047     (raku-mode                  . raku-indent-offset)               ; Perl6/Raku
   6048     (ruby-mode                  . ruby-indent-level)                ; Ruby
   6049     (rust-mode                  . rust-indent-offset)               ; Rust
   6050     (rust-ts-mode               . rust-ts-mode-indent-offset)
   6051     (rustic-mode                . rustic-indent-offset)             ; Rust
   6052     (scala-mode                 . scala-indent:step)                ; Scala
   6053     (sgml-mode                  . sgml-basic-offset)                ; SGML
   6054     (sh-mode                    . sh-basic-offset)                  ; Shell Script
   6055     (toml-ts-mode               . toml-ts-mode-indent-offset)
   6056     (typescript-mode            . typescript-indent-level)          ; Typescript
   6057     (typescript-ts-mode         . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29)
   6058     (yaml-mode                  . yaml-indent-offset)               ; YAML
   6059     (yang-mode                  . c-basic-offset)                   ; YANG (yang-mode)
   6060 
   6061     (default                    . standard-indent))                 ; default fallback
   6062   "A mapping from `major-mode' to its indent variable.")
   6063 
   6064 (defun lsp--get-indent-width (mode)
   6065   "Get indentation offset for MODE."
   6066   (or (alist-get mode lsp--formatting-indent-alist)
   6067       (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default))))
   6068 
   6069 (defun lsp--make-document-formatting-params ()
   6070   "Create document formatting params."
   6071   (lsp-make-document-formatting-params
   6072    :text-document (lsp--text-document-identifier)
   6073    :options (lsp-make-formatting-options
   6074              :tab-size (symbol-value (lsp--get-indent-width major-mode))
   6075              :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   6076              :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   6077              :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   6078              :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))))
   6079 
   6080 (defun lsp-format-buffer ()
   6081   "Ask the server to format this document."
   6082   (interactive "*")
   6083   (cond ((lsp-feature? "textDocument/formatting")
   6084          (let ((edits (lsp-request "textDocument/formatting"
   6085                                    (lsp--make-document-formatting-params))))
   6086            (if (seq-empty-p edits)
   6087                (lsp--info "No formatting changes provided")
   6088              (lsp--apply-text-edits edits 'format))))
   6089         ((lsp-feature? "textDocument/rangeFormatting")
   6090          (save-restriction
   6091            (widen)
   6092            (lsp-format-region (point-min) (point-max))))
   6093         (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider")))))
   6094 
   6095 (defun lsp-format-region (s e)
   6096   "Ask the server to format the region, or if none is selected, the current line."
   6097   (interactive "r")
   6098   (let ((edits (lsp-request
   6099                 "textDocument/rangeFormatting"
   6100                 (lsp--make-document-range-formatting-params s e))))
   6101     (if (seq-empty-p edits)
   6102         (lsp--info "No formatting changes provided")
   6103       (lsp--apply-text-edits edits 'format))))
   6104 
   6105 (defmacro lsp-make-interactive-code-action (func-name code-action-kind)
   6106   "Define an interactive function FUNC-NAME that attempts to
   6107 execute a CODE-ACTION-KIND action."
   6108   `(defun ,(intern (concat "lsp-" (symbol-name func-name))) ()
   6109      ,(format "Perform the %s code action, if available." code-action-kind)
   6110      (interactive)
   6111      ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to
   6112      ;; auto-execute here: the user has specified exactly what they want.
   6113      (let ((lsp-auto-execute-action t))
   6114        (condition-case nil
   6115            (lsp-execute-code-action-by-kind ,code-action-kind)
   6116          (lsp-no-code-actions
   6117           (when (called-interactively-p 'any)
   6118             (lsp--info ,(format "%s action not available" code-action-kind))))))))
   6119 
   6120 (lsp-make-interactive-code-action organize-imports "source.organizeImports")
   6121 
   6122 (defun lsp--make-document-range-formatting-params (start end)
   6123   "Make DocumentRangeFormattingParams for selected region."
   6124   (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params)
   6125                                                   (lsp--region-to-range start end)))
   6126 
   6127 (defconst lsp--highlight-kind-face
   6128   '((1 . lsp-face-highlight-textual)
   6129     (2 . lsp-face-highlight-read)
   6130     (3 . lsp-face-highlight-write)))
   6131 
   6132 (defun lsp--remove-overlays (name)
   6133   (save-restriction
   6134     (widen)
   6135     (remove-overlays (point-min) (point-max) name t)))
   6136 
   6137 (defun lsp-document-highlight ()
   6138   "Highlight all relevant references to the symbol under point."
   6139   (interactive)
   6140   (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights
   6141   (setq lsp--have-document-highlights nil
   6142         lsp--symbol-bounds-of-last-highlight-invocation nil)
   6143   (let ((lsp-enable-symbol-highlighting t))
   6144     (lsp--document-highlight)))
   6145 
   6146 (defun lsp--document-highlight-callback (highlights)
   6147   "Create a callback to process the reply of a
   6148 `textDocument/documentHighlight' message for the buffer BUF.
   6149 A reference is highlighted only if it is visible in a window."
   6150   (lsp--remove-overlays 'lsp-highlight)
   6151 
   6152   (let* ((wins-visible-pos (-map (lambda (win)
   6153                                    (cons (1- (line-number-at-pos (window-start win) t))
   6154                                          (1+ (line-number-at-pos (window-end win) t))))
   6155                                  (get-buffer-window-list nil nil 'visible))))
   6156     (setq lsp--have-document-highlights t)
   6157     (-map
   6158      (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line)
   6159                                                   :end (end &as &Position :line end-line))
   6160                                    :kind?))
   6161        (-map
   6162         (-lambda ((start-window . end-window))
   6163           ;; Make the overlay only if the reference is visible
   6164           (let ((start-point (lsp--position-to-point start))
   6165                 (end-point (lsp--position-to-point end)))
   6166             (when (and (> (1+ start-line) start-window)
   6167                        (< (1+ end-line) end-window)
   6168                        (not (and lsp-symbol-highlighting-skip-current
   6169                                  (<= start-point (point) end-point))))
   6170               (-doto (make-overlay start-point end-point)
   6171                 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face)))
   6172                 (overlay-put 'lsp-highlight t)))))
   6173         wins-visible-pos))
   6174      highlights)))
   6175 
   6176 (defcustom lsp-symbol-kinds
   6177   '((1 . "File")
   6178     (2 . "Module")
   6179     (3 . "Namespace")
   6180     (4 . "Package")
   6181     (5 . "Class")
   6182     (6 . "Method")
   6183     (7 . "Property")
   6184     (8 . "Field")
   6185     (9 . "Constructor")
   6186     (10 . "Enum")
   6187     (11 . "Interface")
   6188     (12 . "Function")
   6189     (13 . "Variable")
   6190     (14 . "Constant")
   6191     (15 . "String")
   6192     (16 . "Number")
   6193     (17 . "Boolean")
   6194     (18 . "Array")
   6195     (19 . "Object")
   6196     (20 . "Key")
   6197     (21 . "Null")
   6198     (22 . "Enum Member")
   6199     (23 . "Struct")
   6200     (24 . "Event")
   6201     (25 . "Operator")
   6202     (26 . "Type Parameter"))
   6203   "Alist mapping SymbolKinds to human-readable strings.
   6204 Various Symbol objects in the LSP protocol have an integral type,
   6205 specifying what they are. This alist maps such type integrals to
   6206 readable representations of them. See
   6207 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/',
   6208 namespace SymbolKind."
   6209   :group 'lsp-mode
   6210   :type '(alist :key-type integer :value-type string))
   6211 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds)
   6212 
   6213 (lsp-defun lsp--symbol-information-to-xref
   6214   ((&SymbolInformation :kind :name
   6215                        :location (&Location :uri :range (&Range :start
   6216                                                                 (&Position :line :character)))))
   6217   "Return a `xref-item' from SYMBOL information."
   6218   (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name)
   6219              (xref-make-file-location (lsp--uri-to-path uri)
   6220                                       line
   6221                                       character)))
   6222 
   6223 (defun lsp--get-document-symbols ()
   6224   "Get document symbols.
   6225 
   6226 If the buffer has not been modified since symbols were last
   6227 retrieved, simply return the latest result.
   6228 
   6229 Else, if the request was initiated by Imenu updating its menu-bar
   6230 entry, perform it asynchronously; i.e., give Imenu the latest
   6231 result and then force a refresh when a new one is available.
   6232 
   6233 Else (e.g., due to interactive use of `imenu' or `xref'),
   6234 perform the request synchronously."
   6235   (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick)
   6236       lsp--document-symbols
   6237     (let ((method "textDocument/documentSymbol")
   6238           (params `(:textDocument ,(lsp--text-document-identifier)))
   6239           (tick (buffer-chars-modified-tick)))
   6240       (if (not lsp--document-symbols-request-async)
   6241           (prog1
   6242               (setq lsp--document-symbols (lsp-request method params))
   6243             (setq lsp--document-symbols-tick tick))
   6244         (lsp-request-async method params
   6245                            (lambda (document-symbols)
   6246                              (setq lsp--document-symbols document-symbols
   6247                                    lsp--document-symbols-tick tick)
   6248                              (lsp--imenu-refresh))
   6249                            :mode 'alive
   6250                            :cancel-token :document-symbols)
   6251         lsp--document-symbols))))
   6252 
   6253 (advice-add 'imenu-update-menubar :around
   6254             (lambda (oldfun &rest r)
   6255               (let ((lsp--document-symbols-request-async t))
   6256                 (apply oldfun r))))
   6257 
   6258 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position)
   6259   "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION."
   6260   (-let (((symbol &as &DocumentSymbol? :children?)
   6261           (seq-find (-lambda ((&DocumentSymbol :range))
   6262                       (lsp-point-in-range? current-position range))
   6263                     document-symbols)))
   6264     (if children?
   6265         (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position))
   6266       (when symbol
   6267         (list symbol)))))
   6268 
   6269 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?))
   6270   "Convert a SymbolInformation to a DocumentInformation"
   6271   (lsp-make-document-symbol :name name
   6272                             :kind kind
   6273                             :range (lsp:location-range location)
   6274                             :children? nil
   6275                             :deprecated? deprecated?
   6276                             :selection-range (lsp:location-range location)
   6277                             :detail? container-name?))
   6278 
   6279 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position)
   6280   "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION."
   6281   (--> symbols-informations
   6282     (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range)))
   6283              (when (lsp-point-in-range? current-position range)
   6284                (lsp--symbol-information->document-symbol symbol)))
   6285            it)
   6286     (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position))
   6287                        (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position)))
   6288                (and (lsp--position-compare b-start-position a-start-position)
   6289                     (lsp--position-compare a-end-position b-end-position))))))
   6290 
   6291 (defun lsp--symbols->document-symbols-hierarchy (symbols)
   6292   "Convert SYMBOLS to symbols-hierarchy."
   6293   (when-let ((first-symbol (lsp-seq-first symbols)))
   6294     (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line)
   6295                                            :character (plist-get (lsp--cur-position) :character))))
   6296       (if (lsp-symbol-information? first-symbol)
   6297           (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position)
   6298         (lsp--document-symbols->document-symbols-hierarchy symbols cur-position)))))
   6299 
   6300 (defun lsp--xref-backend () 'xref-lsp)
   6301 
   6302 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp)))
   6303   (propertize (or (thing-at-point 'symbol) "")
   6304               'identifier-at-point t))
   6305 
   6306 (defun lsp--xref-elements-index (symbols path)
   6307   (-mapcat
   6308    (-lambda (sym)
   6309      (pcase-exhaustive sym
   6310        ((DocumentSymbol :name :children? :selection-range (Range :start))
   6311         (cons (cons (concat path name)
   6312                     (lsp--position-to-point start))
   6313               (lsp--xref-elements-index children? (concat path name " / "))))
   6314        ((SymbolInformation :name :location (Location :range (Range :start)))
   6315         (list (cons (concat path name)
   6316                     (lsp--position-to-point start))))))
   6317    symbols))
   6318 
   6319 (defvar-local lsp--symbols-cache nil)
   6320 
   6321 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp)))
   6322   (if (lsp--find-workspaces-for "textDocument/documentSymbol")
   6323       (progn
   6324         (setq lsp--symbols-cache (lsp--xref-elements-index
   6325                                   (lsp--get-document-symbols) nil))
   6326         lsp--symbols-cache)
   6327     (list (propertize (or (thing-at-point 'symbol) "")
   6328                       'identifier-at-point t))))
   6329 
   6330 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier)
   6331   (save-excursion
   6332     (unless (get-text-property 0 'identifier-at-point identifier)
   6333       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6334                               (user-error "Unable to find symbol %s in current document" identifier)))))
   6335     (lsp--locations-to-xref-items (lsp-request "textDocument/definition"
   6336                                                (lsp--text-document-position-params)))))
   6337 
   6338 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier)
   6339   (save-excursion
   6340     (unless (get-text-property 0 'identifier-at-point identifier)
   6341       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6342                               (user-error "Unable to find symbol %s" identifier)))))
   6343     (lsp--locations-to-xref-items (lsp-request "textDocument/references"
   6344                                                (lsp--make-reference-params nil lsp-references-exclude-definition)))))
   6345 
   6346 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern)
   6347   (seq-map #'lsp--symbol-information-to-xref
   6348            (lsp-request "workspace/symbol" `(:query ,pattern))))
   6349 
   6350 (defcustom lsp-rename-use-prepare t
   6351   "Whether `lsp-rename' should do a prepareRename first.
   6352 For some language servers, textDocument/prepareRename might be
   6353 too slow, in which case this variable may be set to nil.
   6354 `lsp-rename' will then use `thing-at-point' `symbol' to determine
   6355 the symbol to rename at point."
   6356   :group 'lsp-mode
   6357   :type 'boolean)
   6358 
   6359 (defun lsp--get-symbol-to-rename ()
   6360   "Get a symbol to rename and placeholder at point.
   6361 Returns a cons ((START . END) . PLACEHOLDER?), and nil if
   6362 renaming is generally supported but cannot be done at point.
   6363 START and END are the bounds of the identifiers being renamed,
   6364 while PLACEHOLDER?, is either nil or a string suggested by the
   6365 language server as the initial input of a new-name prompt."
   6366   (unless (lsp-feature? "textDocument/rename")
   6367     (error "The connected server(s) doesn't support renaming"))
   6368   (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename"))
   6369       (when-let ((response
   6370                   (lsp-request "textDocument/prepareRename"
   6371                                (lsp--text-document-position-params))))
   6372         (let* ((bounds (lsp--range-to-region
   6373                         (if (lsp-range? response)
   6374                             response
   6375                           (lsp:prepare-rename-result-range response))))
   6376                (placeholder
   6377                 (and (not (lsp-range? response))
   6378                      (lsp:prepare-rename-result-placeholder response))))
   6379           (cons bounds placeholder)))
   6380     (when-let ((bounds (bounds-of-thing-at-point 'symbol)))
   6381       (cons bounds nil))))
   6382 
   6383 (defface lsp-face-rename '((t :underline t))
   6384   "Face used to highlight the identifier being renamed.
   6385 Renaming can be done using `lsp-rename'."
   6386   :group 'lsp-mode)
   6387 
   6388 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face))
   6389   "Face used to display the rename placeholder in.
   6390 When calling `lsp-rename' interactively, this will be the face of
   6391 the new name."
   6392   :group 'lsp-mode)
   6393 
   6394 (defvar lsp-rename-history '()
   6395   "History for `lsp--read-rename'.")
   6396 
   6397 (defun lsp--read-rename (at-point)
   6398   "Read a new name for a `lsp-rename' at `point' from the user.
   6399 AT-POINT shall be a structure as returned by
   6400 `lsp--get-symbol-to-rename'.
   6401 
   6402 Returns a string, which should be the new name for the identifier
   6403 at point. If renaming cannot be done at point (as determined from
   6404 AT-POINT), throw a `user-error'.
   6405 
   6406 This function is for use in `lsp-rename' only, and shall not be
   6407 relied upon."
   6408   (unless at-point
   6409     (user-error "`lsp-rename' is invalid here"))
   6410   (-let* ((((start . end) . placeholder?) at-point)
   6411           ;; Do the `buffer-substring' first to not include `lsp-face-rename'
   6412           (rename-me (buffer-substring start end))
   6413           (placeholder (or placeholder? rename-me))
   6414           (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face))
   6415 
   6416           overlay)
   6417     ;; We need unwind protect, as the user might cancel here, causing the
   6418     ;; overlay to linger.
   6419     (unwind-protect
   6420         (progn
   6421           (setq overlay (make-overlay start end))
   6422           (overlay-put overlay 'face 'lsp-face-rename)
   6423 
   6424           (read-string (format "Rename %s to: " rename-me) placeholder
   6425                        'lsp-rename-history))
   6426       (and overlay (delete-overlay overlay)))))
   6427 
   6428 (defun lsp-rename (newname)
   6429   "Rename the symbol (and all references to it) under point to NEWNAME."
   6430   (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename))))
   6431   (when-let ((edits (lsp-request "textDocument/rename"
   6432                                  `( :textDocument ,(lsp--text-document-identifier)
   6433                                     :position ,(lsp--cur-position)
   6434                                     :newName ,newname))))
   6435     (lsp--apply-workspace-edit edits 'rename)))
   6436 
   6437 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?)
   6438   "Advice around function `rename-file'.
   6439 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?.
   6440 
   6441 This advice sends workspace/willRenameFiles before renaming file
   6442 to check if server wants to apply any workspaceEdits after renamed."
   6443   (if (and lsp-apply-edits-after-file-operations
   6444            (lsp--send-will-rename-files-p old-name))
   6445       (let ((params (lsp-make-rename-files-params
   6446                      :files (vector (lsp-make-file-rename
   6447                                      :oldUri (lsp--path-to-uri old-name)
   6448                                      :newUri (lsp--path-to-uri new-name))))))
   6449         (when-let ((edits (lsp-request "workspace/willRenameFiles" params)))
   6450           (lsp--apply-workspace-edit edits 'rename-file)
   6451           (funcall old-func old-name new-name ok-if-already-exists?)
   6452           (when (lsp--send-did-rename-files-p)
   6453             (lsp-notify "workspace/didRenameFiles" params))))
   6454     (funcall old-func old-name new-name ok-if-already-exists?)))
   6455 
   6456 (advice-add 'rename-file :around #'lsp--on-rename-file)
   6457 
   6458 (defcustom lsp-xref-force-references nil
   6459   "If non-nil threat everything as references(e. g. jump if only one item.)"
   6460   :group 'lsp-mode
   6461   :type 'boolean)
   6462 
   6463 (defun lsp-show-xrefs (xrefs display-action references?)
   6464   (unless (region-active-p) (push-mark nil t))
   6465   (if (boundp 'xref-show-definitions-function)
   6466       (with-no-warnings
   6467         (xref-push-marker-stack)
   6468         (funcall (if (and references? (not lsp-xref-force-references))
   6469                      xref-show-xrefs-function
   6470                    xref-show-definitions-function)
   6471                  (-const xrefs)
   6472                  `((window . ,(selected-window))
   6473                    (display-action . ,display-action)
   6474                    ,(if (and references? (not lsp-xref-force-references))
   6475                         `(auto-jump . ,xref-auto-jump-to-first-xref)
   6476                       `(auto-jump . ,xref-auto-jump-to-first-definition)))))
   6477     (xref--show-xrefs xrefs display-action)))
   6478 
   6479 (cl-defmethod seq-empty-p ((ht hash-table))
   6480   "Function `seq-empty-p' for hash-table."
   6481   (hash-table-empty-p ht))
   6482 
   6483 (cl-defun lsp-find-locations (method &optional extra &key display-action references?)
   6484   "Send request named METHOD and get cross references of the symbol under point.
   6485 EXTRA is a plist of extra parameters.
   6486 REFERENCES? t when METHOD returns references."
   6487   (let ((loc (lsp-request method
   6488                           (append (lsp--text-document-position-params) extra))))
   6489     (if (seq-empty-p loc)
   6490         (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) ""))
   6491       (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?))))
   6492 
   6493 (cl-defun lsp-find-declaration (&key display-action)
   6494   "Find declarations of the symbol under point."
   6495   (interactive)
   6496   (lsp-find-locations "textDocument/declaration" nil :display-action display-action))
   6497 
   6498 (cl-defun lsp-find-definition (&key display-action)
   6499   "Find definitions of the symbol under point."
   6500   (interactive)
   6501   (lsp-find-locations "textDocument/definition" nil :display-action display-action))
   6502 
   6503 (defun lsp-find-definition-mouse (click)
   6504   "Click to start `lsp-find-definition' at clicked point."
   6505   (interactive "e")
   6506   (let* ((ec (event-start click))
   6507          (p1 (posn-point ec))
   6508          (w1 (posn-window ec)))
   6509     (select-window w1)
   6510     (goto-char p1)
   6511     (lsp-find-definition)))
   6512 
   6513 (cl-defun lsp-find-implementation (&key display-action)
   6514   "Find implementations of the symbol under point."
   6515   (interactive)
   6516   (lsp-find-locations "textDocument/implementation"
   6517                       nil
   6518                       :display-action display-action
   6519                       :references? t))
   6520 
   6521 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action)
   6522   "Find references of the symbol under point."
   6523   (interactive "P")
   6524   (lsp-find-locations "textDocument/references"
   6525                       (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition)))))
   6526                       :display-action display-action
   6527                       :references? t))
   6528 
   6529 (cl-defun lsp-find-type-definition (&key display-action)
   6530   "Find type definitions of the symbol under point."
   6531   (interactive)
   6532   (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action))
   6533 
   6534 (defalias 'lsp-find-custom #'lsp-find-locations)
   6535 (defalias 'lsp-goto-implementation #'lsp-find-implementation)
   6536 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition)
   6537 
   6538 (with-eval-after-load 'evil
   6539   (evil-set-command-property 'lsp-find-definition :jump t)
   6540   (evil-set-command-property 'lsp-find-implementation :jump t)
   6541   (evil-set-command-property 'lsp-find-references :jump t)
   6542   (evil-set-command-property 'lsp-find-type-definition :jump t))
   6543 
   6544 (defun lsp--workspace-method-supported? (check-command method capability workspace)
   6545   (with-lsp-workspace workspace
   6546     (if check-command
   6547         (funcall check-command workspace)
   6548       (or
   6549        (when capability (lsp--capability capability))
   6550        (lsp--registered-capability method)
   6551        (and (not capability) (not check-command))))))
   6552 
   6553 (defun lsp-disable-method-for-server (method server-id)
   6554   "Disable METHOD for SERVER-ID."
   6555   (cl-callf
   6556       (lambda (reqs)
   6557         (-let (((&plist :check-command :capability) reqs))
   6558           (list :check-command
   6559                 (lambda (workspace)
   6560                   (unless (-> workspace
   6561                               lsp--workspace-client
   6562                               lsp--client-server-id
   6563                               (eq server-id))
   6564                     (lsp--workspace-method-supported? check-command
   6565                                                       method
   6566                                                       capability
   6567                                                       workspace))))))
   6568       (alist-get method lsp-method-requirements nil nil 'string=)))
   6569 
   6570 (defun lsp--find-workspaces-for (msg-or-method)
   6571   "Find all workspaces in the current project that can handle MSG."
   6572   (let ((method (if (stringp msg-or-method)
   6573                     msg-or-method
   6574                   (plist-get msg-or-method :method))))
   6575     (-if-let (reqs (cdr (assoc method lsp-method-requirements)))
   6576         (-let (((&plist :capability :check-command) reqs))
   6577           (-filter
   6578            (-partial #'lsp--workspace-method-supported?
   6579                      check-command method capability)
   6580            (lsp-workspaces)))
   6581       (lsp-workspaces))))
   6582 
   6583 (defun lsp-can-execute-command? (command-name)
   6584   "Returns non-nil if current language server(s) can execute COMMAND-NAME.
   6585 The command is executed via `workspace/executeCommand'"
   6586   (cl-position
   6587    command-name
   6588    (lsp:execute-command-options-commands
   6589     (lsp:server-capabilities-execute-command-provider?
   6590      (lsp--server-capabilities)))
   6591    :test #'equal))
   6592 
   6593 (defalias 'lsp-feature? 'lsp--find-workspaces-for)
   6594 
   6595 (cl-defmethod lsp-execute-command (_server _command _arguments)
   6596   "Dispatch COMMAND execution."
   6597   (signal 'cl-no-applicable-method nil))
   6598 
   6599 (defun lsp-workspace-command-execute (command &optional args)
   6600   "Execute workspace COMMAND with ARGS."
   6601   (condition-case-unless-debug err
   6602       (let ((params (if args
   6603                         (list :command command :arguments args)
   6604                       (list :command command))))
   6605         (lsp-request "workspace/executeCommand" params))
   6606     (error
   6607      (error "`workspace/executeCommand' with `%s' failed.\n\n%S"
   6608             command err))))
   6609 
   6610 (defun lsp-send-execute-command (command &optional args)
   6611   "Create and send a `workspace/executeCommand' message having command COMMAND
   6612 and optional ARGS."
   6613   (lsp-workspace-command-execute command args))
   6614 
   6615 (defalias 'lsp-point-to-position #'lsp--point-to-position)
   6616 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier)
   6617 (defalias 'lsp--send-execute-command #'lsp-send-execute-command)
   6618 (defalias 'lsp-on-open #'lsp--text-document-did-open)
   6619 (defalias 'lsp-on-save #'lsp--text-document-did-save)
   6620 
   6621 (defun lsp--set-configuration (settings)
   6622   "Set the SETTINGS for the lsp server."
   6623   (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings)))
   6624 
   6625 (defun lsp-current-buffer ()
   6626   (or lsp--virtual-buffer
   6627       (current-buffer)))
   6628 
   6629 (defun lsp-buffer-live-p (buffer-id)
   6630   (if-let ((buffer-live (plist-get buffer-id :buffer-live?)))
   6631       (funcall buffer-live buffer-id)
   6632     (buffer-live-p buffer-id)))
   6633 
   6634 (defun lsp--on-set-visited-file-name (old-func &rest args)
   6635   "Advice around function `set-visited-file-name'.
   6636 
   6637 This advice sends textDocument/didClose for the old file and
   6638 textDocument/didOpen for the new file."
   6639   (when lsp--cur-workspace
   6640     (lsp--text-document-did-close t))
   6641   (prog1 (apply old-func args)
   6642     (when lsp--cur-workspace
   6643       (lsp--text-document-did-open))))
   6644 
   6645 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name)
   6646 
   6647 (defvar lsp--flushing-delayed-changes nil)
   6648 
   6649 (defun lsp--send-no-wait (message proc)
   6650   "Send MESSAGE to PROC without waiting for further output."
   6651 
   6652   (unless lsp--flushing-delayed-changes
   6653     (let ((lsp--flushing-delayed-changes t))
   6654       (lsp--flush-delayed-changes)))
   6655   (lsp-process-send proc message))
   6656 
   6657 (define-error 'lsp-parse-error
   6658   "Error parsing message from language server" 'lsp-error)
   6659 (define-error 'lsp-unknown-message-type
   6660   "Unknown message type" '(lsp-error lsp-parse-error))
   6661 (define-error 'lsp-unknown-json-rpc-version
   6662   "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error))
   6663 (define-error 'lsp-no-content-length
   6664   "Content-Length header missing in message" '(lsp-error lsp-parse-error))
   6665 (define-error 'lsp-invalid-header-name
   6666   "Invalid header name" '(lsp-error lsp-parse-error))
   6667 
   6668 ;;  id  method
   6669 ;;   x    x     request
   6670 ;;   x    .     response
   6671 ;;   .    x     notification
   6672 (defun lsp--get-message-type (json-data)
   6673   "Get the message type from JSON-DATA."
   6674   (if (lsp:json-message-id? json-data)
   6675       (if (lsp:json-message-error? json-data)
   6676           'response-error
   6677         (if (lsp:json-message-method? json-data)
   6678             'request
   6679           'response))
   6680     'notification))
   6681 
   6682 (defconst lsp--default-notification-handlers
   6683   (ht ("window/showMessage" #'lsp--window-show-message)
   6684       ("window/logMessage" #'lsp--window-log-message)
   6685       ("window/showInputBox" #'lsp--window-show-input-box)
   6686       ("window/showQuickPick" #'lsp--window-show-quick-pick)
   6687       ("textDocument/publishDiagnostics" #'lsp--on-diagnostics)
   6688       ("textDocument/diagnosticsEnd" #'ignore)
   6689       ("textDocument/diagnosticsBegin" #'ignore)
   6690       ("telemetry/event" #'ignore)
   6691       ("$/progress" (lambda (workspace params)
   6692                       (funcall lsp-progress-function workspace params)))))
   6693 
   6694 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method))
   6695   "Call the appropriate handler for NOTIFICATION."
   6696   (-let ((client (lsp--workspace-client workspace)))
   6697     (when (lsp--log-io-p method)
   6698       (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif)
   6699                           lsp--cur-workspace))
   6700     (if-let ((handler (or (gethash method (lsp--client-notification-handlers client))
   6701                           (gethash method lsp--default-notification-handlers))))
   6702         (funcall handler workspace params)
   6703       (when (and method (not (string-prefix-p "$" method)))
   6704         (lsp-warn "Unknown notification: %s" method)))))
   6705 
   6706 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items))
   6707   "Get section configuration.
   6708 PARAMS are the `workspace/configuration' request params"
   6709   (->> items
   6710        (-map (-lambda ((&ConfigurationItem :section?))
   6711                (-let* ((path-parts (split-string section? "\\."))
   6712                        (path-without-last (s-join "." (-slice path-parts 0 -1)))
   6713                        (path-parts-len (length path-parts)))
   6714                  (cond
   6715                   ((<= path-parts-len 1)
   6716                    (ht-get (lsp-configuration-section section?)
   6717                            (car-safe path-parts)
   6718                            (ht-create)))
   6719                   ((> path-parts-len 1)
   6720                    (when-let ((section (lsp-configuration-section path-without-last))
   6721                               (keys path-parts))
   6722                      (while (and keys section)
   6723                        (setf section (ht-get section (pop keys))))
   6724                      section))))))
   6725        (apply #'vector)))
   6726 
   6727 (defun lsp--ms-since (timestamp)
   6728   "Integer number of milliseconds since TIMESTAMP.  Fractions discarded."
   6729   (floor (* 1000 (float-time (time-since timestamp)))))
   6730 
   6731 (defun lsp--send-request-response (workspace recv-time request response)
   6732   "Send the RESPONSE for REQUEST in WORKSPACE and log if needed."
   6733   (-let* (((&JSONResponse :params :method :id) request)
   6734           (process (lsp--workspace-proc workspace))
   6735           (response (lsp--make-response id response))
   6736           (req-entry (and lsp-log-io
   6737                           (lsp--make-log-entry method id params 'incoming-req)))
   6738           (resp-entry (and lsp-log-io
   6739                            (lsp--make-log-entry method id response 'outgoing-resp
   6740                                                 (lsp--ms-since recv-time)))))
   6741     ;; Send response to the server.
   6742     (when (lsp--log-io-p method)
   6743       (lsp--log-entry-new req-entry workspace)
   6744       (lsp--log-entry-new resp-entry workspace))
   6745     (lsp--send-no-wait response process)))
   6746 
   6747 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method))
   6748   "Call the appropriate handler for REQUEST, and send the return value to the
   6749 server. WORKSPACE is the active workspace."
   6750   (-let* ((recv-time (current-time))
   6751           (client (lsp--workspace-client workspace))
   6752           (buffers (lsp--workspace-buffers workspace))
   6753           handler
   6754           (response (cond
   6755                      ((setq handler (gethash method (lsp--client-request-handlers client) nil))
   6756                       (funcall handler workspace params))
   6757                      ((setq handler (gethash method (lsp--client-async-request-handlers client) nil))
   6758                       (funcall handler workspace params
   6759                                (-partial #'lsp--send-request-response
   6760                                          workspace recv-time request))
   6761                       'delay-response)
   6762                      ((equal method "client/registerCapability")
   6763                       (mapc #'lsp--server-register-capability
   6764                             (lsp:registration-params-registrations params))
   6765                       (mapc (lambda (buf)
   6766                               (when (lsp-buffer-live-p buf)
   6767                                 (lsp-with-current-buffer buf
   6768                                   (lsp-unconfig-buffer)
   6769                                   (lsp-configure-buffer))))
   6770                             buffers)
   6771                       nil)
   6772                      ((equal method "window/showMessageRequest")
   6773                       (let ((choice (lsp--window-log-message-request params)))
   6774                         `(:title ,choice)))
   6775                      ((equal method "window/showDocument")
   6776                       (let ((success? (lsp--window-show-document params)))
   6777                         (lsp-make-show-document-result :success (or success?
   6778                                                                     :json-false))))
   6779                      ((equal method "client/unregisterCapability")
   6780                       (mapc #'lsp--server-unregister-capability
   6781                             (lsp:unregistration-params-unregisterations params))
   6782                       (mapc (lambda (buf)
   6783                               (when (lsp-buffer-live-p buf)
   6784                                 (lsp-with-current-buffer buf
   6785                                   (lsp-unconfig-buffer)
   6786                                   (lsp-configure-buffer))))
   6787                             buffers)
   6788                       nil)
   6789                      ((equal method "workspace/applyEdit")
   6790                       (list :applied (condition-case err
   6791                                          (prog1 t
   6792                                            (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested))
   6793                                        (error
   6794                                         (lsp--error "Failed to apply edits with message %s"
   6795                                                     (error-message-string err))
   6796                                         :json-false))))
   6797                      ((equal method "workspace/configuration")
   6798                       (with-lsp-workspace workspace
   6799                         (if-let ((buf (car buffers)))
   6800                             (lsp-with-current-buffer buf
   6801                               (lsp--build-workspace-configuration-response params))
   6802                           (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace)
   6803                             (lsp--build-workspace-configuration-response params)))))
   6804                      ((equal method "workspace/workspaceFolders")
   6805                       (let ((folders (or (-> workspace
   6806                                              (lsp--workspace-client)
   6807                                              (lsp--client-server-id)
   6808                                              (gethash (lsp-session-server-id->folders (lsp-session))))
   6809                                          (lsp-session-folders (lsp-session)))))
   6810                         (->> folders
   6811                              (-distinct)
   6812                              (-map (lambda (folder)
   6813                                      (list :uri (lsp--path-to-uri folder))))
   6814                              (apply #'vector))))
   6815                      ((equal method "window/workDoneProgress/create")
   6816                       nil ;; no specific reply, no processing required
   6817                       )
   6818                      ((equal method "workspace/semanticTokens/refresh")
   6819                       (when (and lsp-semantic-tokens-enable
   6820                                  (fboundp 'lsp--semantic-tokens-on-refresh))
   6821                         (lsp--semantic-tokens-on-refresh workspace))
   6822                       nil)
   6823                      ((equal method "workspace/codeLens/refresh")
   6824                       (when (and lsp-lens-enable
   6825                                  (fboundp 'lsp--lens-on-refresh))
   6826                         (lsp--lens-on-refresh workspace))
   6827                       nil)
   6828                      (t (lsp-warn "Unknown request method: %s" method) nil))))
   6829     ;; Send response to the server.
   6830     (unless (eq response 'delay-response)
   6831       (lsp--send-request-response workspace recv-time request response))))
   6832 
   6833 (lsp-defun lsp--error-string ((&JSONError :message :code))
   6834   "Format ERR as a user friendly string."
   6835   (format "Error from the Language Server: %s (%s)"
   6836           message
   6837           (or (car (alist-get code lsp--errors)) "Unknown error")))
   6838 
   6839 (defun lsp--get-body-length (headers)
   6840   (let ((content-length (cdr (assoc "Content-Length" headers))))
   6841     (if content-length
   6842         (string-to-number content-length)
   6843 
   6844       ;; This usually means either the server or our parser is
   6845       ;; screwed up with a previous Content-Length
   6846       (error "No Content-Length header"))))
   6847 
   6848 (defun lsp--parse-header (s)
   6849   "Parse string S as a LSP (KEY . VAL) header."
   6850   (let ((pos (string-match "\:" s))
   6851         key val)
   6852     (unless pos
   6853       (signal 'lsp-invalid-header-name (list s)))
   6854     (setq key (substring s 0 pos)
   6855           val (s-trim-left (substring s (+ 1 pos))))
   6856     (when (equal key "Content-Length")
   6857       (cl-assert (cl-loop for c across val
   6858                           when (or (> c ?9) (< c ?0)) return nil
   6859                           finally return t)
   6860                  nil (format "Invalid Content-Length value: %s" val)))
   6861     (cons key val)))
   6862 
   6863 (defmacro lsp--read-json (str)
   6864   "Read json string STR."
   6865   (if (progn
   6866         (require 'json)
   6867         (fboundp 'json-parse-string))
   6868       `(json-parse-string ,str
   6869                           :object-type (if lsp-use-plists
   6870                                            'plist
   6871                                          'hash-table)
   6872                           :null-object nil
   6873                           :false-object nil)
   6874     `(let ((json-array-type 'vector)
   6875            (json-object-type (if lsp-use-plists
   6876                                  'plist
   6877                                'hash-table))
   6878            (json-false nil))
   6879        (json-read-from-string ,str))))
   6880 
   6881 (defmacro lsp-json-read-buffer ()
   6882   "Read json from the current buffer."
   6883   (if (progn
   6884         (require 'json)
   6885         (fboundp 'json-parse-buffer))
   6886       `(json-parse-buffer :object-type (if lsp-use-plists
   6887                                            'plist
   6888                                          'hash-table)
   6889                           :null-object nil
   6890                           :false-object nil)
   6891     `(let ((json-array-type 'vector)
   6892            (json-object-type (if lsp-use-plists
   6893                                  'plist
   6894                                'hash-table))
   6895            (json-false nil))
   6896        (json-read))))
   6897 
   6898 (defun lsp--read-json-file (file-path)
   6899   "Read json file."
   6900   (-> file-path
   6901     (f-read-text)
   6902     (lsp--read-json)))
   6903 
   6904 (defun lsp--parser-on-message (json-data workspace)
   6905   "Called when the parser P read a complete MSG from the server."
   6906   (with-demoted-errors "Error processing message %S."
   6907     (with-lsp-workspace workspace
   6908       (let* ((client (lsp--workspace-client workspace))
   6909              (id (--when-let (lsp:json-response-id json-data)
   6910                    (if (stringp it) (string-to-number it) it)))
   6911              (data (lsp:json-response-result json-data)))
   6912         (pcase (lsp--get-message-type json-data)
   6913           ('response
   6914            (cl-assert id)
   6915            (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))]
   6916              (when (lsp--log-io-p method)
   6917                (lsp--log-entry-new
   6918                 (lsp--make-log-entry method id data 'incoming-resp
   6919                                      (lsp--ms-since before-send))
   6920                 workspace))
   6921              (when callback
   6922                (remhash id (lsp--client-response-handlers client))
   6923                (funcall callback (lsp:json-response-result json-data)))))
   6924           ('response-error
   6925            (cl-assert id)
   6926            (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))]
   6927              (when (lsp--log-io-p method)
   6928                (lsp--log-entry-new
   6929                 (lsp--make-log-entry method id (lsp:json-response-error-error json-data)
   6930                                      'incoming-resp (lsp--ms-since before-send))
   6931                 workspace))
   6932              (when callback
   6933                (remhash id (lsp--client-response-handlers client))
   6934                (funcall callback (lsp:json-response-error-error json-data)))))
   6935           ('notification
   6936            (lsp--on-notification workspace json-data))
   6937           ('request (lsp--on-request workspace json-data)))))))
   6938 
   6939 (defun lsp--create-filter-function (workspace)
   6940   "Make filter for the workspace."
   6941   (let ((body-received 0)
   6942         leftovers body-length body chunk)
   6943     (lambda (_proc input)
   6944       (setf chunk (if (s-blank? leftovers)
   6945                       input
   6946                     (concat leftovers input)))
   6947 
   6948       (let (messages)
   6949         (while (not (s-blank? chunk))
   6950           (if (not body-length)
   6951               ;; Read headers
   6952               (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk)))
   6953                   ;; We've got all the headers, handle them all at once:
   6954                   (setf body-length (lsp--get-body-length
   6955                                      (mapcar #'lsp--parse-header
   6956                                              (split-string
   6957                                               (substring-no-properties chunk
   6958                                                                        (or (string-match-p "Content-Length" chunk)
   6959                                                                            (error "Unable to find Content-Length header."))
   6960                                                                        body-sep-pos)
   6961                                               "\r\n")))
   6962                         body-received 0
   6963                         leftovers nil
   6964                         chunk (substring-no-properties chunk (+ body-sep-pos 4)))
   6965 
   6966                 ;; Haven't found the end of the headers yet. Save everything
   6967                 ;; for when the next chunk arrives and await further input.
   6968                 (setf leftovers chunk
   6969                       chunk nil))
   6970             (let* ((chunk-length (string-bytes chunk))
   6971                    (left-to-receive (- body-length body-received))
   6972                    (this-body (if (< left-to-receive chunk-length)
   6973                                   (prog1 (substring-no-properties chunk 0 left-to-receive)
   6974                                     (setf chunk (substring-no-properties chunk left-to-receive)))
   6975                                 (prog1 chunk
   6976                                   (setf chunk nil))))
   6977                    (body-bytes (string-bytes this-body)))
   6978               (push this-body body)
   6979               (setf body-received (+ body-received body-bytes))
   6980               (when (>= chunk-length left-to-receive)
   6981                 (condition-case err
   6982                     (with-temp-buffer
   6983                       (apply #'insert
   6984                              (nreverse
   6985                               (prog1 body
   6986                                 (setf leftovers nil
   6987                                       body-length nil
   6988                                       body-received nil
   6989                                       body nil))))
   6990                       (decode-coding-region (point-min)
   6991                                             (point-max)
   6992                                             'utf-8)
   6993                       (goto-char (point-min))
   6994                       (push (lsp-json-read-buffer) messages))
   6995 
   6996                   (error
   6997                    (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s"
   6998                              (concat leftovers input)
   6999                              err)))))))
   7000         (mapc (lambda (msg)
   7001                 (lsp--parser-on-message msg workspace))
   7002               (nreverse messages))))))
   7003 
   7004 (defvar-local lsp--line-col-to-point-hash-table nil
   7005   "Hash table with keys (line . col) and values that are either point positions
   7006 or markers.")
   7007 
   7008 (defcustom lsp-imenu-detailed-outline t
   7009   "Whether `lsp-imenu' should include signatures.
   7010 This will be ignored if the server doesn't provide the necessary
   7011 information, for example if it doesn't support DocumentSymbols."
   7012   :group 'lsp-imenu
   7013   :type 'boolean)
   7014 
   7015 (defcustom lsp-imenu-hide-parent-details t
   7016   "Whether `lsp-imenu' should hide signatures of parent nodes."
   7017   :group 'lsp-imenu
   7018   :type 'boolean)
   7019 
   7020 (defface lsp-details-face '((t :height 0.8 :inherit shadow))
   7021   "Used to display additional information throughout `lsp'.
   7022 Things like line numbers, signatures, ... are considered
   7023 additional information. Often, additional faces are defined that
   7024 inherit from this face by default, like `lsp-signature-face', and
   7025 they may be customized for finer control."
   7026   :group 'lsp-mode)
   7027 
   7028 (defface lsp-signature-face '((t :inherit lsp-details-face))
   7029   "Used to display signatures in `imenu', ...."
   7030   :group 'lsp-mode)
   7031 
   7032 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?)
   7033                               show-detail?)
   7034   "Render INPUT0, an `&DocumentSymbol', to a string.
   7035 If SHOW-DETAIL? is set, make use of its `:detail?' field (often
   7036 the signature)."
   7037   (let ((detail (and show-detail? (s-present? detail?)
   7038                      (propertize (concat " " (s-trim-left detail?))
   7039                                  'face 'lsp-signature-face)))
   7040         (name (if deprecated?
   7041                   (propertize name 'face 'lsp-face-semhl-deprecated) name)))
   7042     (concat name detail)))
   7043 
   7044 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?)
   7045                                           separator)
   7046   "Render a piece of SymbolInformation.
   7047 Handle :deprecated?. If SEPARATOR is non-nil, the
   7048 symbol's (optional) parent, SEPARATOR and the symbol itself are
   7049 concatenated."
   7050   (when (and separator container-name? (not (string-empty-p container-name?)))
   7051     (setq name (concat name separator container-name?)))
   7052   (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name))
   7053 
   7054 (defun lsp--symbol-to-imenu-elem (sym)
   7055   "Convert SYM to imenu element.
   7056 
   7057 SYM is a SymbolInformation message.
   7058 
   7059 Return a cons cell (full-name . start-point)."
   7060   (let ((start-point (ht-get lsp--line-col-to-point-hash-table
   7061                              (lsp--get-line-and-col sym))))
   7062     (cons (lsp-render-symbol-information
   7063            sym (and lsp-imenu-show-container-name
   7064                     lsp-imenu-container-name-separator))
   7065           start-point)))
   7066 
   7067 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?))
   7068   "Convert SYM to hierarchical imenu elements.
   7069 
   7070 SYM is a DocumentSymbol message.
   7071 
   7072 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if
   7073 SYM doesn't have any children. Otherwise return a cons cell with
   7074 an alist
   7075 
   7076   (\"symbol-name\" . ((\"(symbol-kind)\" . start-point)
   7077                     cons-cells-from-children))"
   7078   (let ((filtered-children (lsp--imenu-filter-symbols children?))
   7079         (signature (lsp-render-symbol sym lsp-imenu-detailed-outline)))
   7080     (if (seq-empty-p filtered-children)
   7081         (cons signature
   7082               (ht-get lsp--line-col-to-point-hash-table
   7083                       (lsp--get-line-and-col sym)))
   7084       (cons signature
   7085             (lsp--imenu-create-hierarchical-index filtered-children)))))
   7086 
   7087 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind))
   7088   "Determine if SYM is for the current document and is to be shown."
   7089   ;; It's a SymbolInformation or DocumentSymbol, which is always in the
   7090   ;; current buffer file.
   7091   (and lsp-imenu-index-symbol-kinds
   7092        (numberp kind)
   7093        (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup))
   7094                                kind
   7095                              0)))
   7096          (not (memql (aref lsp/symbol-kind-lookup clamped-kind)
   7097                      lsp-imenu-index-symbol-kinds)))))
   7098 
   7099 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind))
   7100   "The string name of the kind of SYM."
   7101   (alist-get kind lsp-symbol-kinds "Other"))
   7102 
   7103 (defun lsp--get-line-and-col (sym)
   7104   "Obtain the line and column corresponding to SYM."
   7105   (-let* ((location (lsp:symbol-information-location sym))
   7106           (name-range (or (and location (lsp:location-range location))
   7107                           (lsp:document-symbol-selection-range sym)))
   7108           ((&Range :start (&Position :line :character)) name-range))
   7109     (cons line character)))
   7110 
   7111 (defun lsp--collect-lines-and-cols (symbols)
   7112   "Return a sorted list ((line . col) ...) of the locations of SYMBOLS."
   7113   (let ((stack (mapcar 'identity symbols))
   7114         line-col-list)
   7115     (while stack
   7116       (let ((sym (pop stack)))
   7117         (push (lsp--get-line-and-col sym) line-col-list)
   7118         (unless (seq-empty-p (lsp:document-symbol-children? sym))
   7119           (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack)))))
   7120     (-sort #'lsp--line-col-comparator line-col-list)))
   7121 
   7122 (defun lsp--convert-line-col-to-points-batch (line-col-list)
   7123   "Convert a sorted list of positions from line-column
   7124 representation to point representation."
   7125   (let ((line-col-to-point-map (ht-create))
   7126         (inhibit-field-text-motion t)
   7127         (curr-line 0))
   7128     (lsp-save-restriction-and-excursion
   7129       (goto-char (point-min))
   7130       (cl-loop for (line . col) in line-col-list do
   7131                (forward-line (- line curr-line))
   7132                (setq curr-line line)
   7133                (let ((line-end (line-end-position)))
   7134                  (if (or (not col) (> col (- line-end (point))))
   7135                      (goto-char line-end)
   7136                    (forward-char col)))
   7137                (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers
   7138                                                                   (point-marker)
   7139                                                                 (point)))))
   7140     line-col-to-point-map))
   7141 
   7142 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2))
   7143   (or (< l1 l2)
   7144       (and (= l1 l2)
   7145            (cond ((and c1 c2)
   7146                   (< c1 c2))
   7147                  (c1 t)))))
   7148 
   7149 (defun lsp-imenu-create-uncategorized-index (symbols)
   7150   "Create imenu index from document SYMBOLS.
   7151 This function, unlike `lsp-imenu-create-categorized-index', does
   7152 not categorize by type, but instead returns an `imenu' index
   7153 corresponding to the symbol hierarchy returned by the server
   7154 directly."
   7155   (let* ((lsp--line-col-to-point-hash-table (-> symbols
   7156                                                 lsp--collect-lines-and-cols
   7157                                                 lsp--convert-line-col-to-points-batch)))
   7158     (if (lsp--imenu-hierarchical-p symbols)
   7159         (lsp--imenu-create-hierarchical-index symbols)
   7160       (lsp--imenu-create-non-hierarchical-index symbols))))
   7161 
   7162 (defcustom lsp-imenu-symbol-kinds
   7163   '((1 . "Files")
   7164     (2 . "Modules")
   7165     (3 . "Namespaces")
   7166     (4 . "Packages")
   7167     (5 . "Classes")
   7168     (6 . "Methods")
   7169     (7 . "Properties")
   7170     (8 . "Fields")
   7171     (9 . "Constructors")
   7172     (10 . "Enums")
   7173     (11 . "Interfaces")
   7174     (12 . "Functions")
   7175     (13 . "Variables")
   7176     (14 . "Constants")
   7177     (15 . "Strings")
   7178     (16 . "Numbers")
   7179     (17 . "Booleans")
   7180     (18 . "Arrays")
   7181     (19 . "Objects")
   7182     (20 . "Keys")
   7183     (21 . "Nulls")
   7184     (22 . "Enum Members")
   7185     (23 . "Structs")
   7186     (24 . "Events")
   7187     (25 . "Operators")
   7188     (26 . "Type Parameters"))
   7189   "`lsp-symbol-kinds', but only used by `imenu'.
   7190 A new variable is needed, as it is `imenu' convention to use
   7191 pluralized categories, which `lsp-symbol-kinds' doesn't. If the
   7192 non-pluralized names are preferred, this can be set to
   7193 `lsp-symbol-kinds'."
   7194   :type '(alist :key-type integer :value-type string))
   7195 
   7196 (defun lsp--imenu-kind->name (kind)
   7197   (alist-get kind lsp-imenu-symbol-kinds "?"))
   7198 
   7199 (defun lsp-imenu-create-top-level-categorized-index (symbols)
   7200   "Create an `imenu' index categorizing SYMBOLS by type.
   7201 Only root symbols are categorized.
   7202 
   7203 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS
   7204 shall be a list of DocumentSymbols or SymbolInformation."
   7205   (mapcan
   7206    (-lambda ((type . symbols))
   7207      (let ((cat (lsp--imenu-kind->name type))
   7208            (symbols (lsp-imenu-create-uncategorized-index symbols)))
   7209        ;; If there is no :kind (this is being defensive), or we couldn't look it
   7210        ;; up, just display the symbols inline, without categories.
   7211        (if cat (list (cons cat symbols)) symbols)))
   7212    (sort (seq-group-by #'lsp:document-symbol-kind symbols)
   7213          (-lambda ((kinda) (kindb)) (< kinda kindb)))))
   7214 
   7215 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start)))
   7216   "Convert an `&DocumentSymbol' to an `imenu' entry."
   7217   (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start))
   7218 
   7219 (defun lsp--imenu-create-categorized-index-1 (symbols)
   7220   "Returns an `imenu' index from SYMBOLS categorized by type.
   7221 The result looks like this: ((\"Variables\" . (...)))."
   7222   (->>
   7223    symbols
   7224    (mapcan
   7225     (-lambda ((sym &as &DocumentSymbol :kind :children?))
   7226       (if (seq-empty-p children?)
   7227           (list (list kind (lsp--symbol->imenu sym)))
   7228         (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline
   7229                                                   (not lsp-imenu-hide-parent-details)))))
   7230           (cons
   7231            (list kind (lsp--symbol->imenu sym))
   7232            (mapcar (-lambda ((type .  imenu-items))
   7233                      (list type (cons parent (mapcan #'cdr imenu-items))))
   7234                    (-group-by #'car (lsp--imenu-create-categorized-index-1 children?))))))))
   7235    (-group-by #'car)
   7236    (mapcar
   7237     (-lambda ((kind . syms))
   7238       (cons kind (mapcan #'cdr syms))))))
   7239 
   7240 (defun lsp--imenu-create-categorized-index (symbols)
   7241   (let ((syms (lsp--imenu-create-categorized-index-1 symbols)))
   7242     (dolist (sym syms)
   7243       (setcar sym (lsp--imenu-kind->name (car sym))))
   7244     syms))
   7245 
   7246 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start))))
   7247   (cons (lsp-render-symbol-information sym nil) start))
   7248 
   7249 (defun lsp--imenu-create-categorized-index-flat (symbols)
   7250   "Create a kind-categorized index for SymbolInformation."
   7251   (mapcar (-lambda ((kind . syms))
   7252             (cons (lsp--imenu-kind->name kind)
   7253                   (mapcan (-lambda ((parent . children))
   7254                             (let ((children (mapcar #'lsp--symbol-information->imenu children)))
   7255                               (if parent (list (cons parent children)) children)))
   7256                           (-group-by #'lsp:symbol-information-container-name? syms))))
   7257           (seq-group-by #'lsp:symbol-information-kind symbols)))
   7258 
   7259 (defun lsp-imenu-create-categorized-index (symbols)
   7260   (if (lsp--imenu-hierarchical-p symbols)
   7261       (lsp--imenu-create-categorized-index symbols)
   7262     (lsp--imenu-create-categorized-index-flat symbols)))
   7263 
   7264 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index
   7265   "Function that should create an `imenu' index.
   7266 It will be called with a list of SymbolInformation or
   7267 DocumentSymbols, whose first level is already filtered. It shall
   7268 then return an appropriate `imenu' index (see
   7269 `imenu-create-index-function').
   7270 
   7271 Note that this interface is not stable, and subject to change any
   7272 time."
   7273   :group 'lsp-imenu
   7274   :type '(radio
   7275           (const :tag "Categorize by type"
   7276                  lsp-imenu-create-categorized-index)
   7277           (const :tag "Categorize root symbols by type"
   7278                  lsp-imenu-create-top-level-categorized-index)
   7279           (const :tag "Uncategorized, inline entries"
   7280                  lsp-imenu-create-uncategorized-index)
   7281           (function :tag "Custom function")))
   7282 
   7283 (defun lsp--imenu-create-index ()
   7284   "Create an `imenu' index based on the language server.
   7285 Respects `lsp-imenu-index-function'."
   7286   (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols))))
   7287     (funcall lsp-imenu-index-function symbols)))
   7288 
   7289 (defun lsp--imenu-filter-symbols (symbols)
   7290   "Filter out unsupported symbols from SYMBOLS."
   7291   (seq-remove #'lsp--symbol-ignore symbols))
   7292 
   7293 (defun lsp--imenu-hierarchical-p (symbols)
   7294   "Determine whether any element in SYMBOLS has children."
   7295   (seq-some #'lsp-document-symbol? symbols))
   7296 
   7297 (defun lsp--imenu-create-non-hierarchical-index (symbols)
   7298   "Create imenu index for non-hierarchical SYMBOLS.
   7299 
   7300 SYMBOLS are a list of DocumentSymbol messages.
   7301 
   7302 Return a nested alist keyed by symbol names. e.g.
   7303 
   7304    ((\"SomeClass\" (\"(Class)\" . 10)
   7305                  (\"someField (Field)\" . 20)
   7306                  (\"someFunction (Function)\" . 25)
   7307                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7308                                   (\"someSubField (Field)\" . 35))
   7309     (\"someFunction (Function)\" . 40))"
   7310   (seq-map (lambda (nested-alist)
   7311              (cons (car nested-alist)
   7312                    (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist))))
   7313            (seq-group-by #'lsp--get-symbol-type symbols)))
   7314 
   7315 (defun lsp--imenu-create-hierarchical-index (symbols)
   7316   "Create imenu index for hierarchical SYMBOLS.
   7317 
   7318 SYMBOLS are a list of DocumentSymbol messages.
   7319 
   7320 Return a nested alist keyed by symbol names. e.g.
   7321 
   7322    ((\"SomeClass\" (\"(Class)\" . 10)
   7323                  (\"someField (Field)\" . 20)
   7324                  (\"someFunction (Function)\" . 25)
   7325                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7326                                   (\"someSubField (Field)\" . 35))
   7327     (\"someFunction (Function)\" . 40))"
   7328   (seq-map #'lsp--symbol-to-hierarchical-imenu-elem
   7329            (seq-sort #'lsp--imenu-symbol-lessp symbols)))
   7330 
   7331 (defun lsp--imenu-symbol-lessp (sym1 sym2)
   7332   (let* ((compare-results (mapcar (lambda (method)
   7333                                     (funcall (alist-get method lsp--imenu-compare-function-alist)
   7334                                              sym1 sym2))
   7335                                   lsp-imenu-sort-methods))
   7336          (result (seq-find (lambda (result)
   7337                              (not (= result 0)))
   7338                            compare-results
   7339                            0)))
   7340     (and (numberp result) (< result 0))))
   7341 
   7342 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left)
   7343                                     (&SymbolInformation :kind right))
   7344   "Compare SYM1 and SYM2 by kind."
   7345   (- left right))
   7346 
   7347 (defun lsp--imenu-compare-line-col (sym1 sym2)
   7348   (if (lsp--line-col-comparator
   7349        (lsp--get-line-and-col sym1)
   7350        (lsp--get-line-and-col sym2))
   7351       -1
   7352     1))
   7353 
   7354 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1)
   7355                                     (&SymbolInformation :name name2))
   7356   "Compare SYM1 and SYM2 by name."
   7357   (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2))))
   7358     (if (numberp result) result 0)))
   7359 
   7360 (defun lsp--imenu-refresh ()
   7361   "Force Imenu to refresh itself."
   7362   (imenu--menubar-select imenu--rescan-item))
   7363 
   7364 (defun lsp-enable-imenu ()
   7365   "Use lsp-imenu for the current buffer."
   7366   (imenu--cleanup)
   7367   (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   7368   (setq-local imenu-menubar-modified-tick -1)
   7369   (setq-local imenu--index-alist nil)
   7370   (when menu-bar-mode
   7371     (lsp--imenu-refresh)))
   7372 
   7373 (defun lsp-resolve-final-command (command &optional test?)
   7374   "Resolve final function COMMAND."
   7375   (let* ((command (lsp-resolve-value command))
   7376          (command (cl-etypecase command
   7377                     (list
   7378                      (cl-assert (seq-every-p (apply-partially #'stringp) command) nil
   7379                                 "Invalid command list")
   7380                      command)
   7381                     (string (list command)))))
   7382     (if (and (file-remote-p default-directory) (not test?))
   7383         (list shell-file-name "-c"
   7384               (string-join (cons "stty raw > /dev/null;"
   7385                                  (mapcar #'shell-quote-argument command))
   7386                            " "))
   7387       command)))
   7388 
   7389 (defun lsp-server-present? (final-command)
   7390   "Check whether FINAL-COMMAND is present."
   7391   (let ((binary-found? (executable-find (cl-first final-command) t)))
   7392     (if binary-found?
   7393         (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command))
   7394       (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command)))
   7395     binary-found?))
   7396 
   7397 (defun lsp--value-to-string (value)
   7398   "Convert VALUE to a string that can be set as value in an environment
   7399 variable."
   7400   (cond
   7401    ((stringp value) value)
   7402    ((booleanp value) (if value
   7403                          "1"
   7404                        "0"))
   7405    ((and (sequencep value)
   7406          (seq-every-p #'stringp value)) (string-join value ":"))
   7407    (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables"))))
   7408 
   7409 (defun lsp--compute-process-environment (environment-fn)
   7410   "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'.
   7411 Ignore non-boolean keys whose value is nil."
   7412   (let ((environment (if environment-fn
   7413                          (funcall environment-fn)
   7414                        nil)))
   7415     (-flatten (cons (cl-loop for (key . value) in environment
   7416                              if (or (eval value)
   7417                                     (eq (get value 'custom-type) 'boolean))
   7418                              collect (concat key "=" (lsp--value-to-string
   7419                                                       (eval value))))
   7420                     process-environment))))
   7421 
   7422 (defun lsp--default-directory-for-connection (&optional path)
   7423   "Return path to be used for the working directory of a LSP process.
   7424 
   7425 If `lsp-use-workspace-root-for-server-default-directory' is
   7426 non-nil, uses `lsp-workspace-root' to find the directory
   7427 corresponding to PATH, else returns `default-directory'."
   7428   (if lsp-use-workspace-root-for-server-default-directory
   7429       (lsp-workspace-root path)
   7430     default-directory))
   7431 
   7432 (defun lsp--fix-remote-cmd (program)
   7433   "Helper for `lsp-stdio-connection'.
   7434 Originally coppied from eglot."
   7435 
   7436   (if (file-remote-p default-directory)
   7437       (list shell-file-name "-c"
   7438             (string-join (cons "stty raw > /dev/null;"
   7439                                (mapcar #'shell-quote-argument program))
   7440                          " "))
   7441     program))
   7442 
   7443 (defvar tramp-use-ssh-controlmaster-options)
   7444 (defvar tramp-ssh-controlmaster-options)
   7445 
   7446 (defun lsp-stdio-connection (command &optional test-command)
   7447   "Returns a connection property list using COMMAND.
   7448 COMMAND can be: A string, denoting the command to launch the
   7449 language server. A list of strings, denoting an executable with
   7450 its command line arguments. A function, that either returns a
   7451 string or a list of strings. In all cases, the launched language
   7452 server should send and receive messages on standard I/O.
   7453 TEST-COMMAND is a function with no arguments which returns
   7454 whether the command is present or not. When not specified
   7455 `lsp-mode' will check whether the first element of the list
   7456 returned by COMMAND is available via `executable-find'"
   7457   (cl-check-type command (or string
   7458                              function
   7459                              (and list
   7460                                   (satisfies (lambda (l)
   7461                                                (seq-every-p (lambda (el)
   7462                                                               (stringp el))
   7463                                                             l))))))
   7464   (list :connect (lambda (filter sentinel name environment-fn workspace)
   7465                    (if (and (functionp 'json-rpc-connection)
   7466                             (not (file-remote-p default-directory)))
   7467                        (lsp-json-rpc-connection workspace (lsp-resolve-final-command command))
   7468                      (let ((final-command (lsp-resolve-final-command command))
   7469                            (process-name (generate-new-buffer-name name))
   7470                            (process-environment
   7471                             (lsp--compute-process-environment environment-fn)))
   7472                        (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name)))
   7473                               (default-directory (lsp--default-directory-for-connection))
   7474                               (tramp-use-ssh-controlmaster-options 'suppress)
   7475                               (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none")
   7476                               (proc (make-process
   7477                                      :name process-name
   7478                                      :connection-type 'pipe
   7479                                      :buffer (format "*%s*" process-name)
   7480                                      :coding 'no-conversion
   7481                                      :command final-command
   7482                                      :filter filter
   7483                                      :sentinel sentinel
   7484                                      :stderr stderr-buf
   7485                                      :noquery t
   7486                                      :file-handler t)))
   7487                          (set-process-query-on-exit-flag proc nil)
   7488                          (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil)
   7489                          (with-current-buffer (get-buffer stderr-buf)
   7490                            ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc.
   7491                            (special-mode))
   7492                          (cons proc proc)))))
   7493         :test? (or
   7494                 test-command
   7495                 (lambda ()
   7496                   (lsp-server-present? (lsp-resolve-final-command command t))))))
   7497 
   7498 (defun lsp--open-network-stream (host port name)
   7499   "Open network stream to HOST:PORT.
   7500   NAME will be passed to `open-network-stream'.
   7501   RETRY-COUNT is the number of the retries.
   7502   SLEEP-INTERVAL is the sleep interval between each retry."
   7503   (let* ((retries 0)
   7504          (sleep-interval 0.01)
   7505          (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval))
   7506          connection)
   7507     (while (and (not connection) (< retries number-of-retries))
   7508       (condition-case err
   7509           (setq connection (open-network-stream name nil host port
   7510                                                 :type 'plain
   7511                                                 :coding 'no-conversion))
   7512         (file-error
   7513          (let ((inhibit-message t))
   7514            (lsp--warn "Failed to connect to %s:%s with error message %s"
   7515                       host
   7516                       port
   7517                       (error-message-string err))
   7518            (sleep-for sleep-interval)
   7519            (cl-incf retries)))))
   7520     (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port))))
   7521 
   7522 (defun lsp--port-available (host port)
   7523   "Return non-nil if HOST and PORT are available."
   7524   (condition-case _err
   7525       (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain))
   7526     (file-error t)))
   7527 
   7528 (defun lsp--find-available-port (host starting-port)
   7529   "Find available port on HOST starting from STARTING-PORT."
   7530   (let ((port starting-port))
   7531     (while (not (lsp--port-available host port))
   7532       (cl-incf port))
   7533     port))
   7534 
   7535 (defun lsp-tcp-connection (command-fn)
   7536   "Returns a connection property list similar to `lsp-stdio-connection'.
   7537 COMMAND-FN can only be a function that takes a single argument, a
   7538 port number. It should return a command for launches a language server
   7539 process listening for TCP connections on the provided port."
   7540   (cl-check-type command-fn function)
   7541   (list
   7542    :connect (lambda (filter sentinel name environment-fn _workspace)
   7543               (let* ((host "localhost")
   7544                      (port (lsp--find-available-port host (cl-incf lsp--tcp-port)))
   7545                      (command (funcall command-fn port))
   7546                      (final-command (if (consp command) command (list command)))
   7547                      (_ (unless (lsp-server-present? final-command)
   7548                           (user-error (format "Couldn't find executable %s" (cl-first final-command)))))
   7549                      (process-environment
   7550                       (lsp--compute-process-environment environment-fn))
   7551                      (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion
   7552                                          :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t))
   7553                      (tcp-proc (lsp--open-network-stream host port (concat name "::tcp"))))
   7554 
   7555                 ;; TODO: Same :noquery issue (see above)
   7556                 (set-process-query-on-exit-flag proc nil)
   7557                 (set-process-query-on-exit-flag tcp-proc nil)
   7558                 (set-process-filter tcp-proc filter)
   7559                 (cons tcp-proc proc)))
   7560    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7561 
   7562 (defalias 'lsp-tcp-server 'lsp-tcp-server-command)
   7563 
   7564 (defun lsp-tcp-server-command (command-fn)
   7565   "Create tcp server connection.
   7566 In this mode Emacs is TCP server and the language server connects
   7567 to it. COMMAND is function with one parameter(the port) and it
   7568 should return the command to start the LS server."
   7569   (cl-check-type command-fn function)
   7570   (list
   7571    :connect (lambda (filter sentinel name environment-fn _workspace)
   7572               (let* (tcp-client-connection
   7573                      (tcp-server (make-network-process :name (format "*tcp-server-%s*" name)
   7574                                                        :buffer (format "*tcp-server-%s*" name)
   7575                                                        :family 'ipv4
   7576                                                        :service lsp--tcp-server-port
   7577                                                        :sentinel (lambda (proc _string)
   7578                                                                    (lsp-log "Language server %s is connected." name)
   7579                                                                    (setf tcp-client-connection proc))
   7580                                                        :server 't))
   7581                      (port (process-contact tcp-server :service))
   7582                      (final-command (funcall command-fn port))
   7583                      (process-environment
   7584                       (lsp--compute-process-environment environment-fn))
   7585                      (cmd-proc (make-process :name name
   7586                                              :connection-type 'pipe
   7587                                              :coding 'no-conversion
   7588                                              :command final-command
   7589                                              :stderr (format "*tcp-server-%s*::stderr" name)
   7590                                              :noquery t)))
   7591                 (let ((retries 0))
   7592                   ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds)
   7593                   (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds)))
   7594                     (lsp--info "Waiting for connection for %s, retries: %s" name retries)
   7595                     (sit-for 0.500)
   7596                     (cl-incf retries)))
   7597 
   7598                 (unless tcp-client-connection
   7599                   (condition-case nil (delete-process tcp-server) (error))
   7600                   (condition-case nil (delete-process cmd-proc) (error))
   7601                   (error "Failed to create connection to %s on port %s" name port))
   7602                 (lsp--info "Successfully connected to %s" name)
   7603 
   7604                 (set-process-query-on-exit-flag cmd-proc nil)
   7605                 (set-process-query-on-exit-flag tcp-client-connection nil)
   7606                 (set-process-query-on-exit-flag tcp-server nil)
   7607 
   7608                 (set-process-filter tcp-client-connection filter)
   7609                 (set-process-sentinel tcp-client-connection sentinel)
   7610                 (cons tcp-client-connection cmd-proc)))
   7611    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7612 
   7613 (defalias 'lsp-tramp-connection 'lsp-stdio-connection)
   7614 
   7615 (defun lsp--auto-configure ()
   7616   "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed."
   7617   (when (functionp 'lsp-ui-mode)
   7618     (lsp-ui-mode))
   7619 
   7620   (if lsp-headerline-breadcrumb-enable
   7621       (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)
   7622     (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode))
   7623   (if lsp-modeline-code-actions-enable
   7624       (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)
   7625     (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode))
   7626   (if lsp-modeline-diagnostics-enable
   7627       (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)
   7628     (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode))
   7629   (if lsp-modeline-workspace-status-enable
   7630       (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)
   7631     (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode))
   7632   (if lsp-lens-enable
   7633       (add-hook 'lsp-configure-hook 'lsp-lens--enable)
   7634     (remove-hook 'lsp-configure-hook 'lsp-lens--enable))
   7635   (if lsp-semantic-tokens-enable
   7636       (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)
   7637     (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable))
   7638 
   7639   ;; yas-snippet config
   7640   (setq-local yas-inhibit-overlay-modification-protection t))
   7641 
   7642 (defun lsp--restart-if-needed (workspace)
   7643   "Handler restart for WORKSPACE."
   7644   (when (or (eq lsp-restart 'auto-restart)
   7645             (eq (lsp--workspace-shutdown-action workspace) 'restart)
   7646             (and (eq lsp-restart 'interactive)
   7647                  (let ((query (format
   7648                                "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?"
   7649                                (lsp--workspace-print workspace))))
   7650                    (y-or-n-p query))))
   7651     (--each (lsp--workspace-buffers workspace)
   7652       (when (lsp-buffer-live-p it)
   7653         (lsp-with-current-buffer it
   7654           (if lsp--buffer-deferred
   7655               (lsp-deferred)
   7656             (lsp--info "Restarting LSP in buffer %s" (buffer-name))
   7657             (lsp)))))))
   7658 
   7659 (defun lsp--update-key (table key fn)
   7660   "Apply FN on value corresponding to KEY in TABLE."
   7661   (let ((existing-value (gethash key table)))
   7662     (if-let ((new-value (funcall fn existing-value)))
   7663         (puthash key new-value table)
   7664       (remhash key table))))
   7665 
   7666 (defun lsp--process-sentinel (workspace process exit-str)
   7667   "Create the sentinel for WORKSPACE."
   7668   (unless (process-live-p process)
   7669     (lsp--handle-process-exit workspace exit-str)))
   7670 
   7671 (defun lsp--handle-process-exit (workspace exit-str)
   7672   (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session)))
   7673          (proc (lsp--workspace-proc workspace)))
   7674     (lsp--warn "%s has exited (%s)"
   7675                (lsp-process-name proc)
   7676                (string-trim-right (or exit-str "")))
   7677     (with-lsp-workspace workspace
   7678       ;; Clean workspace related data in each of the buffers
   7679       ;; in the workspace.
   7680       (--each (lsp--workspace-buffers workspace)
   7681         (when (lsp-buffer-live-p it)
   7682           (lsp-with-current-buffer it
   7683             (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces))
   7684             (lsp--uninitialize-workspace)
   7685             (lsp--spinner-stop)
   7686             (lsp--remove-overlays 'lsp-highlight))))
   7687 
   7688       ;; Cleanup session from references to the closed workspace.
   7689       (--each (hash-table-keys folder->workspaces)
   7690         (lsp--update-key folder->workspaces it (apply-partially 'delete workspace)))
   7691 
   7692       (lsp-process-cleanup proc))
   7693 
   7694     (run-hook-with-args 'lsp-after-uninitialized-functions workspace)
   7695 
   7696     (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown)
   7697         (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace))
   7698       (lsp--restart-if-needed workspace))
   7699     (lsp--cleanup-hanging-watches)))
   7700 
   7701 (defun lsp-workspace-folders (workspace)
   7702   "Return all folders associated with WORKSPACE."
   7703   (let (result)
   7704     (->> (lsp-session)
   7705          (lsp-session-folder->servers)
   7706          (maphash (lambda (folder workspaces)
   7707                     (when (-contains? workspaces workspace)
   7708                       (push folder result)))))
   7709     result))
   7710 
   7711 (defun lsp--start-workspace (session client-template root &optional initialization-options)
   7712   "Create new workspace for CLIENT-TEMPLATE with project root ROOT.
   7713 INITIALIZATION-OPTIONS are passed to initialize function.
   7714 SESSION is the active session."
   7715   (lsp--spinner-start)
   7716   (-let* ((default-directory root)
   7717           (client (copy-lsp--client client-template))
   7718           (workspace (make-lsp--workspace
   7719                       :root root
   7720                       :client client
   7721                       :status 'starting
   7722                       :buffers (list (lsp-current-buffer))
   7723                       :host-root (file-remote-p root)))
   7724           ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities
   7725                      'multi-root 'initialized-fn) client)
   7726           ((proc . cmd-proc) (funcall
   7727                               (or (plist-get new-connection :connect)
   7728                                   (user-error "Client %s is configured incorrectly" client))
   7729                               (lsp--create-filter-function workspace)
   7730                               (apply-partially #'lsp--process-sentinel workspace)
   7731                               (format "%s" server-id)
   7732                               environment-fn
   7733                               workspace))
   7734           (workspace-folders (gethash server-id (lsp-session-server-id->folders session))))
   7735     (setf (lsp--workspace-proc workspace) proc
   7736           (lsp--workspace-cmd-proc workspace) cmd-proc)
   7737 
   7738     ;; update (lsp-session-folder->servers) depending on whether we are starting
   7739     ;; multi/single folder workspace
   7740     (mapc (lambda (project-root)
   7741             (->> session
   7742                  (lsp-session-folder->servers)
   7743                  (gethash project-root)
   7744                  (cl-pushnew workspace)))
   7745           (or workspace-folders (list root)))
   7746 
   7747     (with-lsp-workspace workspace
   7748       (run-hooks 'lsp-before-initialize-hook)
   7749       (lsp-request-async
   7750        "initialize"
   7751        (append
   7752         (list :processId (unless (file-remote-p (buffer-file-name))
   7753                            (emacs-pid))
   7754               :rootPath (lsp-file-local-name (expand-file-name root))
   7755               :clientInfo (list :name "emacs"
   7756                                 :version (emacs-version))
   7757               :rootUri (lsp--path-to-uri root)
   7758               :capabilities (lsp--client-capabilities custom-capabilities)
   7759               :initializationOptions initialization-options
   7760               :workDoneToken "1")
   7761         (when lsp-server-trace
   7762           (list :trace lsp-server-trace))
   7763         (when multi-root
   7764           (->> workspace-folders
   7765                (-distinct)
   7766                (-map (lambda (folder)
   7767                        (list :uri (lsp--path-to-uri folder)
   7768                              :name (f-filename folder))))
   7769                (apply 'vector)
   7770                (list :workspaceFolders))))
   7771        (-lambda ((&InitializeResult :capabilities))
   7772          ;; we know that Rust Analyzer will send {} which will be parsed as null
   7773          ;; when using plists
   7774          (when (equal 'rust-analyzer server-id)
   7775            (-> capabilities
   7776                (lsp:server-capabilities-text-document-sync?)
   7777                (lsp:set-text-document-sync-options-save? t)))
   7778 
   7779          (setf (lsp--workspace-server-capabilities workspace) capabilities
   7780                (lsp--workspace-status workspace) 'initialized)
   7781 
   7782          (with-lsp-workspace workspace
   7783            (lsp-notify "initialized" lsp--empty-ht))
   7784 
   7785          (when initialized-fn (funcall initialized-fn workspace))
   7786 
   7787          (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace))
   7788          (->> workspace
   7789               (lsp--workspace-buffers)
   7790               (mapc (lambda (buffer)
   7791                       (lsp-with-current-buffer buffer
   7792                         (lsp--open-in-workspace workspace)))))
   7793 
   7794          (with-lsp-workspace workspace
   7795            (run-hooks 'lsp-after-initialize-hook))
   7796          (lsp--info "%s initialized successfully in folders: %s"
   7797                     (lsp--workspace-print workspace)
   7798                     (lsp-workspace-folders workspace)))
   7799        :mode 'detached))
   7800     workspace))
   7801 
   7802 (defun lsp--load-default-session ()
   7803   "Load default session."
   7804   (setq lsp--session (or (condition-case err
   7805                              (lsp--read-from-file lsp-session-file)
   7806                            (error (lsp--error "Failed to parse the session %s, starting with clean one."
   7807                                               (error-message-string err))
   7808                                   nil))
   7809                          (make-lsp-session))))
   7810 
   7811 (defun lsp-session ()
   7812   "Get the session associated with the current buffer."
   7813   (or lsp--session (setq lsp--session (lsp--load-default-session))))
   7814 
   7815 (defun lsp--client-disabled-p (buffer-major-mode client)
   7816   (seq-some
   7817    (lambda (entry)
   7818      (pcase entry
   7819        ((pred symbolp) (eq entry client))
   7820        (`(,mode . ,client-or-list)
   7821         (and (eq mode buffer-major-mode)
   7822              (if (listp client-or-list)
   7823                  (memq client client-or-list)
   7824                (eq client client-or-list))))))
   7825    lsp-disabled-clients))
   7826 
   7827 
   7828 ;; download server
   7829 
   7830 (defcustom lsp-server-install-dir (expand-file-name
   7831                                    (locate-user-emacs-file (f-join ".cache" "lsp")))
   7832   "Directory in which the servers will be installed."
   7833   :risky t
   7834   :type 'directory
   7835   :package-version '(lsp-mode . "6.3")
   7836   :group 'lsp-mode)
   7837 
   7838 (defcustom lsp-verify-signature t
   7839   "Whether to check GPG signatures of downloaded files."
   7840   :type 'boolean
   7841   :package-version '(lsp-mode . "8.0.0")
   7842   :group 'lsp-mode)
   7843 
   7844 (defvar lsp--dependencies (ht))
   7845 
   7846 (defun lsp-dependency (name &rest definitions)
   7847   "Used to specify a language server DEPENDENCY, the server
   7848 executable or other required file path. Typically, the
   7849 DEPENDENCY is found by locating it on the system path using
   7850 `executable-find'.
   7851 
   7852 You can explicitly call lsp-dependency in your environment to
   7853 specify the absolute path to the DEPENDENCY. For example, the
   7854 typescript-language-server requires both the server and the
   7855 typescript compiler. If you have installed them in a team shared
   7856 read-only location, you can instruct lsp-mode to use them via
   7857 
   7858  (eval-after-load `lsp-mode
   7859    `(progn
   7860       (require lsp-javascript)
   7861       (lsp-dependency typescript-language-server (:system ,tls-exe))
   7862       (lsp-dependency typescript (:system ,ts-js))))
   7863 
   7864 where tls-exe is the absolute path to the typescript-language-server
   7865 executable and ts-js is the absolute path to the typescript compiler
   7866 JavaScript file, tsserver.js (the *.js is required for Windows)."
   7867   (ht-set lsp--dependencies name definitions))
   7868 
   7869 (defun lsp--server-binary-present? (client)
   7870   (unless (equal (lsp--client-server-id client) 'lsp-pwsh)
   7871     (condition-case ()
   7872         (-some-> client lsp--client-new-connection (plist-get :test?) funcall)
   7873       (error nil)
   7874       (args-out-of-range nil))))
   7875 
   7876 (define-minor-mode lsp-installation-buffer-mode
   7877   "Mode used in *lsp-installation* buffers.
   7878 It can be used to set-up keybindings, etc. Disabling this mode
   7879 detaches the installation buffer from commands like
   7880 `lsp-select-installation-buffer'."
   7881   :init-value nil
   7882   :lighter nil)
   7883 
   7884 (defface lsp-installation-finished-buffer-face '((t :foreground "orange"))
   7885   "Face used for finished installation buffers.
   7886 Used in `lsp-select-installation-buffer'."
   7887   :group 'lsp-mode)
   7888 
   7889 (defface lsp-installation-buffer-face '((t :foreground "green"))
   7890   "Face used for installation buffers still in progress.
   7891 Used in `lsp-select-installation-buffer'."
   7892   :group 'lsp-mode)
   7893 
   7894 (defun lsp--installation-buffer? (buf)
   7895   "Check whether BUF is an `lsp-async-start-process' buffer."
   7896   (buffer-local-value 'lsp-installation-buffer-mode buf))
   7897 
   7898 (defun lsp-select-installation-buffer (&optional show-finished)
   7899   "Interactively choose an installation buffer.
   7900 If SHOW-FINISHED is set, leftover (finished) installation buffers
   7901 are still shown."
   7902   (interactive "P")
   7903   (let ((bufs (--filter (and (lsp--installation-buffer? it)
   7904                              (or show-finished (get-buffer-process it)))
   7905                         (buffer-list))))
   7906     (pcase bufs
   7907       (`nil (user-error "No installation buffers"))
   7908       (`(,buf) (pop-to-buffer buf))
   7909       (bufs (pop-to-buffer (completing-read "Select installation buffer: "
   7910                                             (--map (propertize (buffer-name it) 'face
   7911                                                                (if (get-buffer-process it)
   7912                                                                    'lsp-installation-buffer-face
   7913                                                                  'lsp-installation-finished-buffer-face))
   7914                                                    bufs)))))))
   7915 
   7916 (defun lsp-cleanup-installation-buffers ()
   7917   "Delete finished *lsp-installation* buffers."
   7918   (interactive)
   7919   (dolist (buf (buffer-list))
   7920     (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf)))
   7921       (kill-buffer buf))))
   7922 
   7923 (defun lsp--download-status ()
   7924   (-some--> #'lsp--client-download-in-progress?
   7925     (lsp--filter-clients it)
   7926     (-map (-compose #'symbol-name #'lsp--client-server-id) it)
   7927     (format "%s" it)
   7928     (propertize it 'face 'success)
   7929     (format " Installing following servers: %s" it)
   7930     (propertize it
   7931                 'local-map (make-mode-line-mouse-map
   7932                             'mouse-1 #'lsp-select-installation-buffer)
   7933                 'mouse-face 'highlight)))
   7934 
   7935 (defun lsp--install-server-internal (client &optional update?)
   7936   (unless (lsp--client-download-server-fn client)
   7937     (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation."
   7938                 (lsp--client-server-id client)))
   7939 
   7940   (setf (lsp--client-download-in-progress? client) t)
   7941   (add-to-list 'global-mode-string '(t (:eval (lsp--download-status))))
   7942   (cl-flet ((done
   7943              (success? &optional error-message)
   7944              ;; run with idle timer to make sure the lsp command is executed in
   7945              ;; the main thread, see #2739.
   7946              (run-with-timer
   7947               0.0
   7948               nil
   7949               (lambda ()
   7950                 (-let [(&lsp-cln 'server-id 'buffers) client]
   7951                   (setf (lsp--client-download-in-progress? client) nil
   7952                         (lsp--client-buffers client) nil)
   7953                   (if success?
   7954                       (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id
   7955                                  (length buffers))
   7956                     (lsp--error "Server %s install process failed with the following error message: %s.
   7957 Check `*lsp-install*' and `*lsp-log*' buffer."
   7958                                 server-id
   7959                                 error-message))
   7960                   (seq-do
   7961                    (lambda (buffer)
   7962                      (when (lsp-buffer-live-p buffer)
   7963                        (lsp-with-current-buffer buffer
   7964                          (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   7965                                     global-mode-string)
   7966                          (when success? (lsp)))))
   7967                    buffers)
   7968                   (unless (lsp--filter-clients #'lsp--client-download-in-progress?)
   7969                     (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   7970                                global-mode-string)))))))
   7971     (lsp--info "Download %s started." (lsp--client-server-id client))
   7972     (condition-case err
   7973         (funcall
   7974          (lsp--client-download-server-fn client)
   7975          client
   7976          (lambda () (done t))
   7977          (lambda (msg) (done nil msg))
   7978          update?)
   7979       (error
   7980        (done nil (error-message-string err))))))
   7981 
   7982 (defun lsp--require-packages ()
   7983   "Load `lsp-client-packages' if needed."
   7984   (when (and lsp-auto-configure (not lsp--client-packages-required))
   7985     (seq-do (lambda (package)
   7986               ;; loading client is slow and `lsp' can be called repeatedly
   7987               (unless (featurep package)
   7988                 (require package nil t)))
   7989             lsp-client-packages)
   7990     (setq lsp--client-packages-required t)))
   7991 
   7992 ;;;###autoload
   7993 (defun lsp-install-server (update? &optional server-id)
   7994   "Interactively install or re-install server.
   7995 When prefix UPDATE? is t force installation even if the server is present."
   7996   (interactive "P")
   7997   (lsp--require-packages)
   7998   (let* ((chosen-client (or (gethash server-id lsp-clients)
   7999                             (lsp--completing-read
   8000                              "Select server to install/re-install: "
   8001                              (or (->> lsp-clients
   8002                                       (ht-values)
   8003                                       (-filter (-andfn
   8004                                                 (-not #'lsp--client-download-in-progress?)
   8005                                                 #'lsp--client-download-server-fn)))
   8006                                  (user-error "There are no servers with automatic installation"))
   8007                              (lambda (client)
   8008                                (let ((server-name (-> client lsp--client-server-id symbol-name)))
   8009                                  (if (lsp--server-binary-present? client)
   8010                                      (concat server-name " (Already installed)")
   8011                                    server-name)))
   8012                              nil
   8013                              t)))
   8014          (update? (or update?
   8015                       (and (not (lsp--client-download-in-progress? chosen-client))
   8016                            (lsp--server-binary-present? chosen-client)))))
   8017     (lsp--install-server-internal chosen-client update?)))
   8018 
   8019 ;;;###autoload
   8020 (defun lsp-uninstall-server (dir)
   8021   "Delete a LSP server from `lsp-server-install-dir'."
   8022   (interactive
   8023    (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir))))
   8024   (unless (file-directory-p dir)
   8025     (user-error "Couldn't find %s directory" dir))
   8026   (delete-directory dir 'recursive)
   8027   (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir))))
   8028 
   8029 ;;;###autoload
   8030 (defun lsp-uninstall-servers ()
   8031   "Uninstall all installed servers."
   8032   (interactive)
   8033   (let* ((dir lsp-server-install-dir)
   8034          (servers (ignore-errors
   8035                     (directory-files dir t
   8036                                      directory-files-no-dot-files-regexp))))
   8037     (if (or (not (file-directory-p dir)) (zerop (length servers)))
   8038         (user-error "No servers to uninstall")
   8039       (when (yes-or-no-p
   8040              (format "Servers to uninstall: %d (%s), proceed? "
   8041                      (length servers)
   8042                      (mapconcat (lambda (server)
   8043                                   (file-name-nondirectory (directory-file-name server)))
   8044                                 servers " ")))
   8045         (mapc #'lsp-uninstall-server servers)
   8046         (message "All servers uninstalled")))))
   8047 
   8048 ;;;###autoload
   8049 (defun lsp-update-server (&optional server-id)
   8050   "Interactively update (reinstall) a server."
   8051   (interactive)
   8052   (lsp--require-packages)
   8053   (let ((chosen-client (or (gethash server-id lsp-clients)
   8054                            (lsp--completing-read
   8055                             "Select server to update (if not on the list, probably you need to `lsp-install-server`): "
   8056                             (or (->> lsp-clients
   8057                                      (ht-values)
   8058                                      (-filter (-andfn
   8059                                                (-not #'lsp--client-download-in-progress?)
   8060                                                #'lsp--client-download-server-fn
   8061                                                #'lsp--server-binary-present?)))
   8062                                 (user-error "There are no servers to update"))
   8063                             (lambda (client)
   8064                               (-> client lsp--client-server-id symbol-name))
   8065                             nil
   8066                             t))))
   8067     (lsp--install-server-internal chosen-client t)))
   8068 
   8069 ;;;###autoload
   8070 (defun lsp-update-servers ()
   8071   "Update (reinstall) all installed servers."
   8072   (interactive)
   8073   (lsp--require-packages)
   8074   (mapc (lambda (client) (lsp--install-server-internal client t))
   8075         (-filter (-andfn
   8076                   (-not #'lsp--client-download-in-progress?)
   8077                   #'lsp--client-download-server-fn
   8078                   #'lsp--server-binary-present?) (hash-table-values lsp-clients))))
   8079 
   8080 ;;;###autoload
   8081 (defun lsp-ensure-server (server-id)
   8082   "Ensure server SERVER-ID"
   8083   (lsp--require-packages)
   8084   (if-let ((client (gethash server-id lsp-clients)))
   8085       (unless (lsp--server-binary-present? client)
   8086         (lsp--info "Server `%s' is not preset, installing..." server-id)
   8087         (lsp-install-server nil server-id))
   8088     (warn "Unable to find server registration with id %s" server-id)))
   8089 
   8090 (defun lsp-async-start-process (callback error-callback &rest command)
   8091   "Start async process COMMAND with CALLBACK and ERROR-CALLBACK."
   8092   (let ((name (cl-first command)))
   8093     (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd)
   8094                                                                                          (not (null cmd)))
   8095                                                                                        command)
   8096                                                        " ") t
   8097                                             (lambda (&rest _)
   8098                                               (generate-new-buffer-name (format "*lsp-install: %s*" name))))
   8099       (lsp-installation-buffer-mode +1)
   8100       (view-mode +1)
   8101       (add-hook
   8102        'compilation-finish-functions
   8103        (lambda (_buf status)
   8104          (if (string= "finished\n" status)
   8105              (condition-case err
   8106                  (funcall callback)
   8107                (error
   8108                 (funcall error-callback (error-message-string err))))
   8109            (funcall error-callback (s-trim-right status))))
   8110        nil t))))
   8111 
   8112 (defun lsp-resolve-value (value)
   8113   "Resolve VALUE's value.
   8114 If it is function - call it.
   8115 If it is a variable - return it's value
   8116 Otherwise returns value itself."
   8117   (cond
   8118    ((functionp value) (funcall value))
   8119    ((and (symbolp value) (boundp value)) (symbol-value value))
   8120    (value)))
   8121 
   8122 (defvar lsp-deps-providers
   8123   (list :npm (list :path #'lsp--npm-dependency-path
   8124                    :install #'lsp--npm-dependency-install)
   8125         :cargo (list :path #'lsp--cargo-dependency-path
   8126                      :install #'lsp--cargo-dependency-install)
   8127         :system (list :path #'lsp--system-path)
   8128         :download (list :path #'lsp-download-path
   8129                         :install #'lsp-download-install)))
   8130 
   8131 (defun lsp--system-path (path)
   8132   "If PATH is absolute and exists return it as is. Otherwise,
   8133 return the absolute path to the executable defined by PATH or
   8134 nil."
   8135   ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the
   8136   ;; typescript-language-server. When lsp invokes the server, lsp needs to
   8137   ;; supply the path to the typescript compiler, tsserver.js, as an argument. To
   8138   ;; make code platform independent, one must pass the absolute path to the
   8139   ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript
   8140   ;; child process spawn command that is invoked by the
   8141   ;; typescript-language-server). This is why we check for existence and not
   8142   ;; that the path is executable.
   8143   (let ((path (lsp-resolve-value path)))
   8144     (cond
   8145      ((and (f-absolute? path)
   8146            (f-exists? path))
   8147       path)
   8148      ((executable-find path t) path))))
   8149 
   8150 (defun lsp-package-path (dependency)
   8151   "Path to the DEPENDENCY each of the registered providers."
   8152   (let (path)
   8153     (-first (-lambda ((provider . rest))
   8154               (setq path (-some-> lsp-deps-providers
   8155                            (plist-get provider)
   8156                            (plist-get :path)
   8157                            (apply rest))))
   8158             (gethash dependency lsp--dependencies))
   8159     path))
   8160 
   8161 (defun lsp-package-ensure (dependency callback error-callback)
   8162   "Asynchronously ensure a package."
   8163   (or (-first (-lambda ((provider . rest))
   8164                 (-some-> lsp-deps-providers
   8165                   (plist-get provider)
   8166                   (plist-get :install)
   8167                   (apply (cl-list* callback error-callback rest))))
   8168               (gethash dependency lsp--dependencies))
   8169       (funcall error-callback (format "Unable to find a way to install %s" dependency))))
   8170 
   8171 
   8172 ;; npm handling
   8173 
   8174 ;; https://docs.npmjs.com/files/folders#executables
   8175 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys)
   8176   "Return npm dependency PATH for PACKAGE."
   8177   (let ((path (executable-find
   8178                (f-join lsp-server-install-dir "npm" package
   8179                        (cond ((eq system-type 'windows-nt) "")
   8180                              (t "bin"))
   8181                        path)
   8182                t)))
   8183     (unless (and path (f-exists? path))
   8184       (error "The package %s is not installed.  Unable to find %s" package path))
   8185     path))
   8186 
   8187 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys)
   8188   (if-let ((npm-binary (executable-find "npm")))
   8189       (progn
   8190         ;; Explicitly `make-directory' to work around NPM bug in
   8191         ;; versions 7.0.0 through 7.4.1. See
   8192         ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for
   8193         ;; discussion.
   8194         (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents)
   8195         (lsp-async-start-process (lambda ()
   8196                                    (if (string-empty-p
   8197                                         (string-trim (shell-command-to-string
   8198                                                       (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " "))))
   8199                                        (funcall callback)
   8200                                      (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json")))))
   8201                                            (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx
   8202                                        (when (f-dir-p default-directory)
   8203                                          (lsp-async-start-process callback
   8204                                                                   error-callback
   8205                                                                   (executable-find "npx")
   8206                                                                   "npm-install-peers")))))
   8207                                  error-callback
   8208                                  npm-binary
   8209                                  "-g"
   8210                                  "--prefix"
   8211                                  (f-join lsp-server-install-dir "npm" package)
   8212                                  "install"
   8213                                  package))
   8214     (lsp-log "Unable to install %s via `npm' because it is not present" package)
   8215     nil))
   8216 
   8217 
   8218 ;; Cargo dependency handling
   8219 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys)
   8220   (let ((path (executable-find
   8221                (f-join lsp-server-install-dir
   8222                        "cargo"
   8223                        package
   8224                        "bin"
   8225                        path)
   8226                t)))
   8227     (unless (and path (f-exists? path))
   8228       (error "The package %s is not installed.  Unable to find %s" package path))
   8229     path))
   8230 
   8231 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys)
   8232   (if-let ((cargo-binary (executable-find "cargo")))
   8233       (lsp-async-start-process
   8234        callback
   8235        error-callback
   8236        cargo-binary
   8237        "install"
   8238        package
   8239        (when git
   8240          "--git")
   8241        git
   8242        "--root"
   8243        (f-join lsp-server-install-dir "cargo" package))
   8244     (lsp-log "Unable to install %s via `cargo' because it is not present" package)
   8245     nil))
   8246 
   8247 
   8248 
   8249 ;; Download URL handling
   8250 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys)
   8251   (let* ((url (lsp-resolve-value url))
   8252          (store-path (lsp-resolve-value store-path))
   8253          ;; (decompress (lsp-resolve-value decompress))
   8254          (download-path
   8255           (pcase decompress
   8256             (:gzip (concat store-path ".gz"))
   8257             (:zip (concat store-path ".zip"))
   8258             (:targz (concat store-path ".tar.gz"))
   8259             (`nil store-path)
   8260             (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'")))))
   8261     (make-thread
   8262      (lambda ()
   8263        (condition-case err
   8264            (progn
   8265              (when (f-exists? download-path)
   8266                (f-delete download-path))
   8267              (when (f-exists? store-path)
   8268                (f-delete store-path))
   8269              (lsp--info "Starting to download %s to %s..." url download-path)
   8270              (mkdir (f-parent download-path) t)
   8271              (url-copy-file url download-path)
   8272              (lsp--info "Finished downloading %s..." download-path)
   8273              (when (and lsp-verify-signature asc-url pgp-key)
   8274                (if (executable-find epg-gpg-program)
   8275                    (let ((asc-download-path (concat download-path ".asc"))
   8276                          (context (epg-make-context))
   8277                          (fingerprint)
   8278                          (signature))
   8279                      (when (f-exists? asc-download-path)
   8280                        (f-delete asc-download-path))
   8281                      (lsp--info "Starting to download %s to %s..." asc-url asc-download-path)
   8282                      (url-copy-file asc-url asc-download-path)
   8283                      (lsp--info "Finished downloading %s..." asc-download-path)
   8284                      (epg-import-keys-from-string context pgp-key)
   8285                      (setq fingerprint (epg-import-status-fingerprint
   8286                                         (car
   8287                                          (epg-import-result-imports
   8288                                           (epg-context-result-for context 'import)))))
   8289                      (lsp--info "Verifying signature %s..." asc-download-path)
   8290                      (epg-verify-file context asc-download-path download-path)
   8291                      (setq signature (car (epg-context-result-for context 'verify)))
   8292                      (unless (and
   8293                               (eq (epg-signature-status signature) 'good)
   8294                               (equal (epg-signature-fingerprint signature) fingerprint))
   8295                        (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature))))
   8296                  (lsp--warn "GPG is not installed, skipping the signature check.")))
   8297              (when decompress
   8298                (lsp--info "Decompressing %s..." download-path)
   8299                (pcase decompress
   8300                  (:gzip
   8301                   (lsp-gunzip download-path))
   8302                  (:zip (lsp-unzip download-path (f-parent store-path)))
   8303                  (:targz (lsp-tar-gz-decompress download-path (f-parent store-path))))
   8304                (lsp--info "Decompressed %s..." store-path))
   8305              (funcall callback))
   8306          (error (funcall error-callback err)))))))
   8307 
   8308 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys)
   8309   "Download URL and store it into STORE-PATH.
   8310 
   8311 SET-EXECUTABLE? when non-nil change the executable flags of
   8312 STORE-PATH to make it executable. BINARY-PATH can be specified
   8313 when the binary to start does not match the name of the
   8314 archive (e.g. when the archive has multiple files)"
   8315   (let ((store-path (or (lsp-resolve-value binary-path)
   8316                         (lsp-resolve-value store-path))))
   8317     (cond
   8318      ((executable-find store-path) store-path)
   8319      ((and set-executable? (f-exists? store-path))
   8320       (set-file-modes store-path #o0700)
   8321       store-path)
   8322      ((f-exists? store-path) store-path))))
   8323 
   8324 (defun lsp--find-latest-gh-release-url (url regex)
   8325   "Fetch the latest version in the releases given by URL by using REGEX."
   8326   (let ((url-request-method "GET"))
   8327     (with-current-buffer (url-retrieve-synchronously url)
   8328       (goto-char (point-min))
   8329       (re-search-forward "\n\n" nil 'noerror)
   8330       (delete-region (point-min) (point))
   8331       (let* ((json-result (lsp-json-read-buffer)))
   8332         (message "Latest version found: %s" (lsp-get json-result :tag_name))
   8333         (--> json-result
   8334              (lsp-get it :assets)
   8335              (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it)
   8336              (lsp-get it :browser_download_url))))))
   8337 
   8338 ;; unzip
   8339 
   8340 (defconst lsp-ext-pwsh-script "powershell -noprofile -noninteractive \
   8341 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'"
   8342   "Powershell script to unzip file.")
   8343 
   8344 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'"
   8345   "Unzip script to unzip file.")
   8346 
   8347 (defcustom lsp-unzip-script (lambda ()
   8348                               (cond ((executable-find "unzip") lsp-ext-unzip-script)
   8349                                     ((executable-find "powershell") lsp-ext-pwsh-script)
   8350                                     (t nil)))
   8351   "The script to unzip."
   8352   :group 'lsp-mode
   8353   :type 'string
   8354   :package-version '(lsp-mode . "8.0.0"))
   8355 
   8356 (defun lsp-unzip (zip-file dest)
   8357   "Unzip ZIP-FILE to DEST."
   8358   (unless lsp-unzip-script
   8359     (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'"))
   8360   (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest)))
   8361 
   8362 ;; gunzip
   8363 
   8364 (defconst lsp-ext-gunzip-script "gzip -d %1$s"
   8365   "Script to decompress a gzippped file with gzip.")
   8366 
   8367 (defcustom lsp-gunzip-script (lambda ()
   8368                                (cond ((executable-find "gzip") lsp-ext-gunzip-script)
   8369                                      (t nil)))
   8370   "The script to decompress a gzipped file.
   8371 Should be a format string with one argument for the file to be decompressed
   8372 in place."
   8373   :group 'lsp-mode
   8374   :type 'string
   8375   :package-version '(lsp-mode . "8.0.0"))
   8376 
   8377 (defun lsp-gunzip (gz-file)
   8378   "Decompress GZ-FILE in place."
   8379   (unless lsp-gunzip-script
   8380     (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file))
   8381   (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file)))
   8382 
   8383 ;; tar.gz decompression
   8384 
   8385 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'"
   8386   "Script to decompress a .tar.gz file.")
   8387 
   8388 (defcustom lsp-tar-script (lambda ()
   8389                             (cond ((executable-find "tar") lsp-ext-tar-script)
   8390                                   (t nil)))
   8391   "The script to decompress a .tar.gz file.
   8392 Should be a format string with one argument for the file to be decompressed
   8393 in place."
   8394   :group 'lsp-mode
   8395   :type 'string)
   8396 
   8397 (defun lsp-tar-gz-decompress (targz-file dest)
   8398   "Decompress TARGZ-FILE in DEST."
   8399   (unless lsp-tar-script
   8400     (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file))
   8401   (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest)))
   8402 
   8403 
   8404 ;; VSCode marketplace
   8405 
   8406 (defcustom lsp-vscode-ext-url
   8407   "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s"
   8408   "Vscode extension template url."
   8409   :group 'lsp-mode
   8410   :type 'string
   8411   :package-version '(lsp-mode . "8.0.0"))
   8412 
   8413 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform)
   8414   "Return the URL to vscode extension.
   8415 PUBLISHER is the extension publisher.
   8416 NAME is the name of the extension.
   8417 VERSION is the version of the extension.
   8418 TARGETPLATFORM is the targetPlatform of the extension."
   8419   (format lsp-vscode-ext-url publisher name version (or targetPlatform "")))
   8420 
   8421 
   8422 
   8423 ;; Queueing prompts
   8424 
   8425 (defvar lsp--question-queue nil
   8426   "List of questions yet to be asked by `lsp-ask-question'.")
   8427 
   8428 (defun lsp-ask-question (question options callback)
   8429   "Prompt the user to answer the QUESTION with one of the OPTIONS from the
   8430 minibuffer. Once the user selects an option, the CALLBACK function will be
   8431 called, passing the selected option to it.
   8432 
   8433 If the user is currently being shown a question, the question will be stored in
   8434 `lsp--question-queue', and will be asked once the user has answered the current
   8435 question."
   8436   (add-to-list 'lsp--question-queue `(("question" . ,question)
   8437                                       ("options" . ,options)
   8438                                       ("callback" . ,callback)) t)
   8439   (when (eq (length lsp--question-queue) 1)
   8440     (lsp--process-question-queue)))
   8441 
   8442 (defun lsp--process-question-queue ()
   8443   "Take the first question from `lsp--question-queue', process it, then process
   8444 the next question until the queue is empty."
   8445   (-let* (((&alist "question" "options" "callback") (car lsp--question-queue))
   8446           (answer (completing-read question options nil t)))
   8447     (pop lsp--question-queue)
   8448     (funcall callback answer)
   8449     (when lsp--question-queue
   8450       (lsp--process-question-queue))))
   8451 
   8452 (defun lsp--supports-buffer? (client)
   8453   (and
   8454    ;; both file and client remote or both local
   8455    (eq (---truthy? (file-remote-p (buffer-file-name)))
   8456        (---truthy? (lsp--client-remote? client)))
   8457 
   8458    ;; activation function or major-mode match.
   8459    (if-let ((activation-fn (lsp--client-activation-fn client)))
   8460        (funcall activation-fn (buffer-file-name) major-mode)
   8461      (-contains? (lsp--client-major-modes client) major-mode))
   8462 
   8463    ;; check whether it is enabled if `lsp-enabled-clients' is not null
   8464    (or (null lsp-enabled-clients)
   8465        (or (member (lsp--client-server-id client) lsp-enabled-clients)
   8466            (ignore (lsp--info "Client %s is not in lsp-enabled-clients"
   8467                               (lsp--client-server-id client)))))
   8468 
   8469    ;; check whether it is not disabled.
   8470    (not (lsp--client-disabled-p major-mode (lsp--client-server-id client)))))
   8471 
   8472 (defun lsp--filter-clients (pred)
   8473   (->> lsp-clients hash-table-values (-filter pred)))
   8474 
   8475 (defun lsp--find-clients ()
   8476   "Find clients which can handle current buffer."
   8477   (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   8478                                                             #'lsp--server-binary-present?)))
   8479     (lsp-log "Found the following clients for %s: %s"
   8480              (buffer-file-name)
   8481              (s-join ", "
   8482                      (-map (lambda (client)
   8483                              (format "(server-id %s, priority %s)"
   8484                                      (lsp--client-server-id client)
   8485                                      (lsp--client-priority client)))
   8486                            matching-clients)))
   8487     (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients))
   8488             (selected-clients (if-let ((main-client (and main-clients
   8489                                                          (--max-by (> (lsp--client-priority it)
   8490                                                                       (lsp--client-priority other))
   8491                                                                    main-clients))))
   8492                                   (cons main-client add-on-clients)
   8493                                 add-on-clients)))
   8494       (lsp-log "The following clients were selected based on priority: %s"
   8495                (s-join ", "
   8496                        (-map (lambda (client)
   8497                                (format "(server-id %s, priority %s)"
   8498                                        (lsp--client-server-id client)
   8499                                        (lsp--client-priority client)))
   8500                              selected-clients)))
   8501       selected-clients)))
   8502 
   8503 (defun lsp-workspace-remove-all-folders()
   8504   "Delete all lsp tracked folders."
   8505   (interactive)
   8506   (--each (lsp-session-folders (lsp-session))
   8507     (lsp-workspace-folders-remove it)))
   8508 
   8509 (defun lsp-register-client (client)
   8510   "Registers LSP client CLIENT."
   8511   (let ((client-id (lsp--client-server-id client)))
   8512     (puthash client-id client lsp-clients)
   8513     (setplist (intern (format "lsp-%s-after-open-hook" client-id))
   8514               `( standard-value (nil) custom-type hook
   8515                  custom-package-version (lsp-mode . "7.0.1")
   8516                  variable-documentation ,(format "Hooks to run after `%s' server is run." client-id)
   8517                  custom-requests nil)))
   8518   (when (and lsp-auto-register-remote-clients
   8519              (not (lsp--client-remote? client)))
   8520     (let ((remote-client (copy-lsp--client client)))
   8521       (setf (lsp--client-remote? remote-client) t
   8522             (lsp--client-server-id remote-client) (intern
   8523                                                    (format "%s-tramp"
   8524                                                            (lsp--client-server-id client)))
   8525             ;; disable automatic download
   8526             (lsp--client-download-server-fn remote-client) nil)
   8527       (lsp-register-client remote-client))))
   8528 
   8529 (defun lsp--create-initialization-options (_session client)
   8530   "Create initialization-options from SESSION and CLIENT.
   8531 Add workspace folders depending on server being multiroot and
   8532 session workspace folder configuration for the server."
   8533   (let* ((initialization-options-or-fn (lsp--client-initialization-options client)))
   8534     (if (functionp initialization-options-or-fn)
   8535         (funcall initialization-options-or-fn)
   8536       initialization-options-or-fn)))
   8537 
   8538 (defvar lsp-client-settings (make-hash-table :test 'equal)
   8539   "For internal use, any external users please use
   8540   `lsp-register-custom-settings' function instead")
   8541 
   8542 (defun lsp-register-custom-settings (props)
   8543   "Register PROPS.
   8544 PROPS is list of triple (path value boolean?) where PATH is the path to the
   8545 property; VALUE can be a literal value, symbol to be evaluated, or either a
   8546 function or lambda function to be called without arguments; BOOLEAN? is an
   8547 optional flag that should be non-nil for boolean settings, when it is nil the
   8548 property will be ignored if the VALUE is nil.
   8549 
   8550 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))'
   8551 \(note the double parentheses)"
   8552   (mapc
   8553    (-lambda ((path . rest))
   8554      (puthash path rest lsp-client-settings))
   8555    props))
   8556 
   8557 (defun lsp-region-text (region)
   8558   "Get the text for REGION in current buffer."
   8559   (-let (((start . end) (lsp--range-to-region region)))
   8560     (buffer-substring-no-properties start end)))
   8561 
   8562 (defun lsp-ht-set (tbl paths value)
   8563   "Set nested hash table value.
   8564 TBL - a hash table, PATHS is the path to the nested VALUE."
   8565   (pcase paths
   8566     (`(,path) (ht-set! tbl path value))
   8567     (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl)
   8568                                            (let ((temp-tbl (ht)))
   8569                                              (ht-set! tbl path temp-tbl)
   8570                                              temp-tbl))))
   8571                        (lsp-ht-set nested-tbl rst value)))))
   8572 
   8573 ;; sections
   8574 
   8575 (defalias 'defcustom-lsp 'lsp-defcustom)
   8576 
   8577 (defmacro lsp-defcustom (symbol standard doc &rest args)
   8578   "Defines `lsp-mode' server property."
   8579   (declare (doc-string 3) (debug (name body))
   8580            (indent defun))
   8581   (let ((path (plist-get args :lsp-path)))
   8582     (cl-remf args :lsp-path)
   8583     `(progn
   8584        (lsp-register-custom-settings
   8585         (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type))))))
   8586 
   8587        (defcustom ,symbol ,standard ,doc
   8588          :set (lambda (sym val)
   8589                 (lsp--set-custom-property sym val ,path))
   8590          ,@args))))
   8591 
   8592 (defun lsp--set-custom-property (sym val path)
   8593   (set sym val)
   8594   (let ((section (cl-first (s-split "\\." path))))
   8595     (mapc (lambda (workspace)
   8596             (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace))
   8597                               section)
   8598               (with-lsp-workspace workspace
   8599                 (lsp--set-configuration (lsp-configuration-section section)))))
   8600           (lsp--session-workspaces (lsp-session)))))
   8601 
   8602 (defun lsp-configuration-section (section)
   8603   "Get settings for SECTION."
   8604   (let ((ret (ht-create)))
   8605     (maphash (-lambda (path (variable boolean?))
   8606                (when (s-matches? (concat (regexp-quote section) "\\..*") path)
   8607                  (let* ((symbol-value (-> variable
   8608                                           lsp-resolve-value
   8609                                           lsp-resolve-value))
   8610                         (value (if (and boolean? (not symbol-value))
   8611                                    :json-false
   8612                                  symbol-value)))
   8613                    (when (or boolean? value)
   8614                      (lsp-ht-set ret (s-split "\\." path) value)))))
   8615              lsp-client-settings)
   8616     ret))
   8617 
   8618 
   8619 (defun lsp--start-connection (session client project-root)
   8620   "Initiates connection created from CLIENT for PROJECT-ROOT.
   8621 SESSION is the active session."
   8622   (when (lsp--client-multi-root client)
   8623     (cl-pushnew project-root (gethash (lsp--client-server-id client)
   8624                                       (lsp-session-server-id->folders session))))
   8625   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)
   8626 
   8627   (unwind-protect
   8628       (lsp--start-workspace session client project-root (lsp--create-initialization-options session client))
   8629     (lsp--spinner-stop)))
   8630 
   8631 ;; lsp-log-io-mode
   8632 
   8633 (defvar lsp-log-io-mode-map
   8634   (let ((map (make-sparse-keymap)))
   8635     (define-key map (kbd "M-n") #'lsp-log-io-next)
   8636     (define-key map (kbd "M-p") #'lsp-log-io-prev)
   8637     (define-key map (kbd "k") #'lsp--erase-log-buffer)
   8638     (define-key map (kbd "K") #'lsp--erase-session-log-buffers)
   8639     map)
   8640   "Keymap for lsp log buffer mode.")
   8641 
   8642 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo"
   8643   "Special mode for viewing IO logs.")
   8644 
   8645 (defun lsp-workspace-show-log (workspace)
   8646   "Display the log buffer of WORKSPACE."
   8647   (interactive
   8648    (list (if lsp-log-io
   8649              (if (eq (length (lsp-workspaces)) 1)
   8650                  (cl-first (lsp-workspaces))
   8651                (lsp--completing-read "Workspace: " (lsp-workspaces)
   8652                                      #'lsp--workspace-print nil t))
   8653            (user-error "IO logging is disabled"))))
   8654   (pop-to-buffer (lsp--get-log-buffer-create workspace)))
   8655 
   8656 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log)
   8657 
   8658 (defun lsp--get-log-buffer-create (workspace)
   8659   "Return the lsp log buffer of WORKSPACE, creating a new one if needed."
   8660   (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8661          (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id)))
   8662     (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid))))
   8663 
   8664 (defun lsp--erase-log-buffer (&optional all)
   8665   "Delete contents of current lsp log buffer.
   8666 When ALL is t, erase all log buffers of the running session."
   8667   (interactive)
   8668   (let* ((workspaces (lsp--session-workspaces (lsp-session)))
   8669          (current-log-buffer (current-buffer)))
   8670     (dolist (w workspaces)
   8671       (let ((b (lsp--get-log-buffer-create w)))
   8672         (when (or all (eq b current-log-buffer))
   8673           (with-current-buffer b
   8674             (let ((inhibit-read-only t))
   8675               (erase-buffer))))))))
   8676 
   8677 (defun lsp--erase-session-log-buffers ()
   8678   "Erase log buffers of the running session."
   8679   (interactive)
   8680   (lsp--erase-log-buffer t))
   8681 
   8682 (defun lsp-log-io-next (arg)
   8683   "Move to next log entry."
   8684   (interactive "P")
   8685   (ewoc-goto-next lsp--log-io-ewoc (or arg 1)))
   8686 
   8687 (defun lsp-log-io-prev (arg)
   8688   "Move to previous log entry."
   8689   (interactive "P")
   8690   (ewoc-goto-prev lsp--log-io-ewoc (or arg 1)))
   8691 
   8692 
   8693 
   8694 (cl-defmethod lsp-process-id ((process process))
   8695   (process-id process))
   8696 
   8697 (cl-defmethod lsp-process-name ((process process)) (process-name process))
   8698 
   8699 (cl-defmethod lsp-process-status ((process process)) (process-status process))
   8700 
   8701 (cl-defmethod lsp-process-kill ((process process))
   8702   (when (process-live-p process)
   8703     (kill-process process)))
   8704 
   8705 (cl-defmethod lsp-process-send ((process process) message)
   8706   (condition-case err
   8707       (process-send-string process (lsp--make-message message))
   8708     (error (lsp--error "Sending to process failed with the following error: %s"
   8709                        (error-message-string err)))))
   8710 
   8711 (cl-defmethod lsp-process-cleanup (process)
   8712   ;; Kill standard error buffer only if the process exited normally.
   8713   ;; Leave it intact otherwise for debugging purposes.
   8714   (let ((buffer (-> process process-name get-buffer)))
   8715     (when (and (eq (process-status process) 'exit)
   8716                (zerop (process-exit-status process))
   8717                (buffer-live-p buffer))
   8718       (kill-buffer buffer))))
   8719 
   8720 
   8721 ;; native JSONRPC
   8722 
   8723 (declare-function json-rpc "ext:json")
   8724 (declare-function json-rpc-connection "ext:json")
   8725 (declare-function json-rpc-send "ext:json")
   8726 (declare-function json-rpc-shutdown "ext:json")
   8727 (declare-function json-rpc-stderr "ext:json")
   8728 (declare-function json-rpc-pid "ext:json")
   8729 
   8730 (defvar lsp-json-rpc-thread nil)
   8731 (defvar lsp-json-rpc-queue nil)
   8732 (defvar lsp-json-rpc-done nil)
   8733 (defvar lsp-json-rpc-mutex (make-mutex))
   8734 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex))
   8735 
   8736 (defun lsp-json-rpc-process-queue ()
   8737   (while (not lsp-json-rpc-done)
   8738     (while lsp-json-rpc-queue
   8739       (-let (((proc . message) (pop lsp-json-rpc-queue)))
   8740         (json-rpc-send
   8741          proc message
   8742          :null-object nil
   8743          :false-object :json-false)))
   8744     (with-mutex lsp-json-rpc-mutex
   8745       (condition-wait lsp-json-rpc-condition))))
   8746 
   8747 (cl-defmethod lsp-process-id (process) (json-rpc-pid process))
   8748 
   8749 (cl-defmethod lsp-process-name (_process) "TBD")
   8750 
   8751 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process))
   8752 
   8753 (cl-defmethod lsp-process-send (proc message)
   8754   (unless lsp-json-rpc-thread
   8755     (with-current-buffer (get-buffer-create " *json-rpc*")
   8756       (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*"))))
   8757 
   8758   (with-mutex lsp-json-rpc-mutex
   8759     (setq lsp-json-rpc-queue (append lsp-json-rpc-queue
   8760                                      (list (cons proc message))))
   8761     (condition-notify lsp-json-rpc-condition)))
   8762 
   8763 (cl-defmethod lsp-process-cleanup (_proc))
   8764 
   8765 (defun lsp-json-rpc-connection (workspace command)
   8766   (let ((con (apply #'json-rpc-connection command))
   8767         (object-type (if lsp-use-plists 'plist 'hash-table)))
   8768     (with-current-buffer (get-buffer-create " *json-rpc*")
   8769       (make-thread
   8770        (lambda ()
   8771          (json-rpc
   8772           con
   8773           (lambda (result err done)
   8774             (run-with-timer
   8775              0.0
   8776              nil
   8777              (lambda ()
   8778                (cond
   8779                 (result (lsp--parser-on-message result workspace))
   8780                 (err (warn "Json parsing failed with the following error: %s" err))
   8781                 (done (lsp--handle-process-exit workspace ""))))))
   8782           :object-type object-type
   8783           :null-object nil
   8784           :false-object nil))
   8785        "*json-rpc-connection*"))
   8786     (cons con con)))
   8787 
   8788 (defun lsp-json-rpc-stderr ()
   8789   (interactive)
   8790   (--when-let (pcase (lsp-workspaces)
   8791                 (`nil (user-error "There are no active servers in the current buffer"))
   8792                 (`(,workspace) workspace)
   8793                 (workspaces (lsp--completing-read "Select server: "
   8794                                                   workspaces
   8795                                                   'lsp--workspace-print nil t)))
   8796     (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it)))
   8797           (buffer (format "*stderr-%s*" (lsp--workspace-print it)) ))
   8798       (with-current-buffer (get-buffer-create buffer)
   8799         (with-help-window buffer
   8800           (insert content))))))
   8801 
   8802 
   8803 (defun lsp--workspace-print (workspace)
   8804   "Visual representation WORKSPACE."
   8805   (let* ((proc (lsp--workspace-cmd-proc workspace))
   8806          (status (lsp--workspace-status workspace))
   8807          (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8808          (pid (lsp-process-id proc)))
   8809 
   8810     (if (eq 'initialized status)
   8811         (format "%s:%s" server-id pid)
   8812       (format "%s:%s/%s" server-id pid status))))
   8813 
   8814 (defun lsp--map-tree-widget (m)
   8815   "Build `tree-widget' from a hash-table or plist M."
   8816   (when (lsp-structure-p m)
   8817     (let (nodes)
   8818       (lsp-map (lambda (k v)
   8819                  (push `(tree-widget
   8820                          :tag ,(if (lsp-structure-p v)
   8821                                    (format "%s:" k)
   8822                                  (format "%s: %s" k
   8823                                          (propertize (format "%s" v)
   8824                                                      'face
   8825                                                      'font-lock-string-face)))
   8826                          :open t
   8827                          ,@(lsp--map-tree-widget v))
   8828                        nodes))
   8829                m)
   8830       nodes)))
   8831 
   8832 (defun lsp-buffer-name (buffer-id)
   8833   (if-let ((buffer-name (plist-get buffer-id :buffer-name)))
   8834       (funcall buffer-name buffer-id)
   8835     (buffer-name buffer-id)))
   8836 
   8837 (defun lsp--render-workspace (workspace)
   8838   "Tree node representation of WORKSPACE."
   8839   `(tree-widget :tag ,(lsp--workspace-print workspace)
   8840                 :open t
   8841                 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face)
   8842                              :open t
   8843                              ,@(->> workspace
   8844                                     (lsp--workspace-buffers)
   8845                                     (--map `(tree-widget
   8846                                              :tag ,(when (lsp-buffer-live-p it)
   8847                                                      (let ((buffer-name (lsp-buffer-name it)))
   8848                                                        (if (lsp-with-current-buffer it buffer-read-only)
   8849                                                            (propertize buffer-name 'face 'font-lock-constant-face)
   8850                                                          buffer-name)))))))
   8851                 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face)
   8852                              ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget))))
   8853 
   8854 (define-derived-mode lsp-browser-mode special-mode "LspBrowser"
   8855   "Define mode for displaying lsp sessions."
   8856   (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t)))))
   8857 
   8858 (defun lsp-describe-session ()
   8859   "Describes current `lsp-session'."
   8860   (interactive)
   8861   (let ((session (lsp-session))
   8862         (buf (get-buffer-create "*lsp session*"))
   8863         (root (lsp-workspace-root)))
   8864     (with-current-buffer buf
   8865       (lsp-browser-mode)
   8866       (let ((inhibit-read-only t))
   8867         (erase-buffer)
   8868         (--each (lsp-session-folders session)
   8869           (widget-create
   8870            `(tree-widget
   8871              :tag ,(propertize it 'face 'font-lock-keyword-face)
   8872              :open t
   8873              ,@(->> session
   8874                     (lsp-session-folder->servers)
   8875                     (gethash it)
   8876                     (-map 'lsp--render-workspace)))))))
   8877     (pop-to-buffer buf)
   8878     (goto-char (point-min))
   8879     (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag)
   8880              until (or (and root (string= tag root)) (eobp))
   8881              do (goto-char (next-overlay-change (point))))))
   8882 
   8883 (defun lsp--session-workspaces (session)
   8884   "Get all workspaces that are part of the SESSION."
   8885   (-> session lsp-session-folder->servers hash-table-values -flatten -uniq))
   8886 
   8887 (defun lsp--find-multiroot-workspace (session client project-root)
   8888   "Look for a multiroot connection in SESSION created from CLIENT for
   8889 PROJECT-ROOT and BUFFER-MAJOR-MODE."
   8890   (when (lsp--client-multi-root client)
   8891     (-when-let (multi-root-workspace (->> session
   8892                                           (lsp--session-workspaces)
   8893                                           (--first (eq (-> it lsp--workspace-client lsp--client-server-id)
   8894                                                        (lsp--client-server-id client)))))
   8895       (with-lsp-workspace multi-root-workspace
   8896         (lsp-notify "workspace/didChangeWorkspaceFolders"
   8897                     (lsp-make-did-change-workspace-folders-params
   8898                      :event (lsp-make-workspace-folders-change-event
   8899                              :added (vector (lsp-make-workspace-folder
   8900                                              :uri (lsp--path-to-uri project-root)
   8901                                              :name (f-filename project-root)))
   8902                              :removed []))))
   8903 
   8904       (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace))
   8905       (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root))
   8906 
   8907       (lsp--persist-session session)
   8908 
   8909       (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace))
   8910       (lsp--open-in-workspace multi-root-workspace)
   8911 
   8912       multi-root-workspace)))
   8913 
   8914 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder)
   8915   "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT.
   8916 IGNORE-MULTI-FOLDER to ignore multi folder server."
   8917   (-map (lambda (client)
   8918           (or
   8919            (lsp--find-workspace session client project-root)
   8920            (unless ignore-multi-folder
   8921              (lsp--find-multiroot-workspace session client project-root))
   8922            (lsp--start-connection session client project-root)))
   8923         clients))
   8924 
   8925 (defun lsp--spinner-stop ()
   8926   "Stop the spinner in case all of the workspaces are started."
   8927   (when (--all? (eq (lsp--workspace-status it) 'initialized)
   8928                 lsp--buffer-workspaces)
   8929     (spinner-stop)))
   8930 
   8931 (defun lsp--open-in-workspace (workspace)
   8932   "Open in existing WORKSPACE."
   8933   (if (eq 'initialized (lsp--workspace-status workspace))
   8934       ;; when workspace is initialized just call document did open.
   8935       (progn
   8936         (with-lsp-workspace workspace
   8937           (when-let ((before-document-open-fn (-> workspace
   8938                                                   lsp--workspace-client
   8939                                                   lsp--client-before-file-open-fn)))
   8940             (funcall before-document-open-fn workspace))
   8941           (lsp--text-document-did-open))
   8942         (lsp--spinner-stop))
   8943     ;; when it is not initialized
   8944     (lsp--spinner-start)
   8945     (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace))))
   8946 
   8947 (defun lsp--find-workspace (session client project-root)
   8948   "Find server connection created with CLIENT in SESSION for PROJECT-ROOT."
   8949   (when-let ((workspace (->> session
   8950                              (lsp-session-folder->servers)
   8951                              (gethash project-root)
   8952                              (--first (eql (-> it lsp--workspace-client lsp--client-server-id)
   8953                                            (lsp--client-server-id client))))))
   8954     (lsp--open-in-workspace workspace)
   8955     workspace))
   8956 
   8957 (defun lsp--read-char (prompt &optional options)
   8958   "Wrapper for `read-char-from-minibuffer' if Emacs +27.
   8959 Fallback to `read-key' otherwise.
   8960 PROMPT is the message and OPTIONS the available options."
   8961   (if (fboundp 'read-char-from-minibuffer)
   8962       (read-char-from-minibuffer prompt options)
   8963     (read-key prompt)))
   8964 
   8965 (defun lsp--find-root-interactively (session)
   8966   "Find project interactively.
   8967 Returns nil if the project should not be added to the current SESSION."
   8968   (condition-case nil
   8969       (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory))
   8970              (action (lsp--read-char
   8971                       (format
   8972                        "%s is not part of any project.
   8973 
   8974 %s ==> Import project root %s
   8975 %s ==> Import project by selecting root directory interactively
   8976 %s ==> Import project at current directory %s
   8977 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist
   8978 %s ==> Do not ask again for the current project by selecting ignore path interactively
   8979 %s ==> Do nothing: ask again when opening other files from the current project
   8980 
   8981 Select action: "
   8982                        (propertize (buffer-name) 'face 'bold)
   8983                        (propertize "i" 'face 'success)
   8984                        (propertize project-root-suggestion 'face 'bold)
   8985                        (propertize "I" 'face 'success)
   8986                        (propertize "." 'face 'success)
   8987                        (propertize default-directory 'face 'bold)
   8988                        (propertize "d" 'face 'warning)
   8989                        (propertize project-root-suggestion 'face 'bold)
   8990                        (propertize "D" 'face 'warning)
   8991                        (propertize "n" 'face 'warning))
   8992                       '(?i ?\r ?I ?. ?d ?D ?n))))
   8993         (cl-case action
   8994           (?i project-root-suggestion)
   8995           (?\r project-root-suggestion)
   8996           (?I (read-directory-name "Select workspace folder to add: "
   8997                                    (or project-root-suggestion default-directory)
   8998                                    nil
   8999                                    t))
   9000           (?. default-directory)
   9001           (?d (push project-root-suggestion (lsp-session-folders-blocklist session))
   9002               (lsp--persist-session session)
   9003               nil)
   9004           (?D (push (read-directory-name "Select folder to blocklist: "
   9005                                          (or project-root-suggestion default-directory)
   9006                                          nil
   9007                                          t)
   9008                     (lsp-session-folders-blocklist session))
   9009               (lsp--persist-session session)
   9010               nil)
   9011           (t nil)))
   9012     (quit)))
   9013 
   9014 (declare-function tramp-file-name-host "ext:tramp" (file) t)
   9015 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault))
   9016 
   9017 (defun lsp--files-same-host (f1 f2)
   9018   "Predicate on whether or not two files are on the same host."
   9019   (or (not (or (file-remote-p f1) (file-remote-p f2)))
   9020       (and (file-remote-p f1)
   9021            (file-remote-p f2)
   9022            (progn (require 'tramp)
   9023                   (equal (tramp-file-name-host (tramp-dissect-file-name f1))
   9024                          (tramp-file-name-host (tramp-dissect-file-name f2)))))))
   9025 
   9026 (defun lsp-find-session-folder (session file-name)
   9027   "Look in the current SESSION for folder containing FILE-NAME."
   9028   (let ((file-name-canonical (lsp-f-canonical file-name)))
   9029     (->> session
   9030          (lsp-session-folders)
   9031          (--filter (and (lsp--files-same-host it file-name-canonical)
   9032                         (or (lsp-f-same? it file-name-canonical)
   9033                             (and (f-dir? it)
   9034                                  (lsp-f-ancestor-of? it file-name-canonical)))))
   9035          (--max-by (> (length it)
   9036                       (length other))))))
   9037 
   9038 (defun lsp-find-workspace (server-id &optional file-name)
   9039   "Find workspace for SERVER-ID for FILE-NAME."
   9040   (-when-let* ((session (lsp-session))
   9041                (folder->servers (lsp-session-folder->servers session))
   9042                (workspaces (if file-name
   9043                                (gethash (lsp-find-session-folder session file-name) folder->servers)
   9044                              (lsp--session-workspaces session))))
   9045 
   9046     (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces)))
   9047 
   9048 (defun lsp--calculate-root (session file-name)
   9049   "Calculate project root for FILE-NAME in SESSION."
   9050   (and
   9051    (->> session
   9052         (lsp-session-folders-blocklist)
   9053         (--first (and (lsp--files-same-host it file-name)
   9054                       (lsp-f-ancestor-of? it file-name)
   9055                       (prog1 t
   9056                         (lsp--info "File %s is in blocklisted directory %s" file-name it))))
   9057         not)
   9058    (or
   9059     (when lsp-auto-guess-root
   9060       (lsp--suggest-project-root))
   9061     (unless lsp-guess-root-without-session
   9062       (lsp-find-session-folder session file-name))
   9063     (unless lsp-auto-guess-root
   9064       (when-let ((root-folder (lsp--find-root-interactively session)))
   9065         (if (or (not (f-equal? root-folder (expand-file-name "~/")))
   9066                 (yes-or-no-p
   9067                  (concat
   9068                   (propertize "[WARNING] " 'face 'warning)
   9069                   "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:
   9070 
   9071 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/').
   9072 2. If your file is under `~/' then create a subfolder and move that file in this folder.
   9073 
   9074 Type `No' to go back to project selection.
   9075 Type `Yes' to confirm `HOME' as project root.
   9076 Type `C-g' to cancel project import process and stop `lsp'")))
   9077             root-folder
   9078           (lsp--calculate-root session file-name)))))))
   9079 
   9080 (defun lsp--try-open-in-library-workspace ()
   9081   "Try opening current file as library file in any of the active workspace.
   9082 The library folders are defined by each client for each of the active workspace."
   9083   (when-let ((workspace (->> (lsp-session)
   9084                              (lsp--session-workspaces)
   9085                              ;; Sort the last active workspaces first as they are more likely to be
   9086                              ;; the correct ones, especially when jumping to a definition.
   9087                              (-sort (lambda (a _b)
   9088                                       (-contains? lsp--last-active-workspaces a)))
   9089                              (--first
   9090                               (and (-> it lsp--workspace-client lsp--supports-buffer?)
   9091                                    (when-let ((library-folders-fn
   9092                                                (-> it lsp--workspace-client lsp--client-library-folders-fn)))
   9093                                      (-first (lambda (library-folder)
   9094                                                (lsp-f-ancestor-of? library-folder (buffer-file-name)))
   9095                                              (funcall library-folders-fn it))))))))
   9096     (lsp--open-in-workspace workspace)
   9097     (view-mode t)
   9098     (lsp--info "Opening read-only library file %s." (buffer-file-name))
   9099     (list workspace)))
   9100 
   9101 (defun lsp--persist-session (session)
   9102   "Persist SESSION to `lsp-session-file'."
   9103   (lsp--persist lsp-session-file (make-lsp-session
   9104                                   :folders (lsp-session-folders session)
   9105                                   :folders-blocklist (lsp-session-folders-blocklist session)
   9106                                   :server-id->folders (lsp-session-server-id->folders session))))
   9107 
   9108 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder)
   9109   "Try create opening file as a project file.
   9110 When IGNORE-MULTI-FOLDER is t the lsp mode will start new
   9111 language server even if there is language server which can handle
   9112 current language. When IGNORE-MULTI-FOLDER is nil current file
   9113 will be opened in multi folder language server if there is
   9114 such."
   9115   (-let ((session (lsp-session)))
   9116     (-if-let (clients (if ask-for-client
   9117                           (list (lsp--completing-read "Select server to start: "
   9118                                                       (ht-values lsp-clients)
   9119                                                       (-compose 'symbol-name 'lsp--client-server-id) nil t))
   9120                         (lsp--find-clients)))
   9121         (-if-let (project-root (-some-> session
   9122                                  (lsp--calculate-root (buffer-file-name))
   9123                                  (lsp-f-canonical)))
   9124             (progn
   9125               ;; update project roots if needed and persist the lsp session
   9126               (unless (-contains? (lsp-session-folders session) project-root)
   9127                 (cl-pushnew project-root (lsp-session-folders session))
   9128                 (lsp--persist-session session))
   9129               (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder))
   9130           (lsp--warn "%s not in project or it is blocklisted." (buffer-name))
   9131           nil)
   9132       (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode)
   9133       nil)))
   9134 
   9135 (defun lsp-shutdown-workspace ()
   9136   "Shutdown language server."
   9137   (interactive)
   9138   (--when-let (pcase (lsp-workspaces)
   9139                 (`nil (user-error "There are no active servers in the current buffer"))
   9140                 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?"
   9141                                                        (lsp--workspace-print workspace)))
   9142                                  workspace))
   9143                 (workspaces (lsp--completing-read "Select server: "
   9144                                                   workspaces
   9145                                                   'lsp--workspace-print nil t)))
   9146     (lsp-workspace-shutdown it)))
   9147 
   9148 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1")
   9149 
   9150 (defcustom lsp-auto-select-workspace t
   9151   "Shutdown or restart a single workspace.
   9152 If set and the current buffer has only a single workspace
   9153 associated with it, `lsp-shutdown-workspace' and
   9154 `lsp-restart-workspace' will act on it without asking."
   9155   :type 'boolean
   9156   :group 'lsp-mode)
   9157 
   9158 (defun lsp--read-workspace ()
   9159   "Ask the user to select a workspace.
   9160 Errors if there are none."
   9161   (pcase (lsp-workspaces)
   9162     (`nil (error "No workspaces associated with the current buffer"))
   9163     ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace)
   9164     (workspaces (lsp--completing-read "Select workspace: " workspaces
   9165                                       #'lsp--workspace-print nil t))))
   9166 
   9167 (defun lsp-workspace-shutdown (workspace)
   9168   "Shut the workspace WORKSPACE and the language server associated with it"
   9169   (interactive (list (lsp--read-workspace)))
   9170   (lsp--warn "Stopping %s" (lsp--workspace-print workspace))
   9171   (with-lsp-workspace workspace (lsp--shutdown-workspace)))
   9172 
   9173 (defun lsp-disconnect ()
   9174   "Disconnect the buffer from the language server."
   9175   (interactive)
   9176   (lsp--text-document-did-close t)
   9177   (lsp-managed-mode -1)
   9178   (lsp-mode -1)
   9179   (setq lsp--buffer-workspaces nil)
   9180   (lsp--info "Disconnected"))
   9181 
   9182 (defun lsp-restart-workspace ()
   9183   (interactive)
   9184   (--when-let (pcase (lsp-workspaces)
   9185                 (`nil (user-error "There are no active servers in the current buffer"))
   9186                 (`(,workspace) workspace)
   9187                 (workspaces (lsp--completing-read "Select server: "
   9188                                                   workspaces
   9189                                                   'lsp--workspace-print nil t)))
   9190     (lsp-workspace-restart it)))
   9191 
   9192 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1")
   9193 
   9194 (defun lsp-workspace-restart (workspace)
   9195   "Restart the workspace WORKSPACE and the language server associated with it"
   9196   (interactive (list (lsp--read-workspace)))
   9197   (lsp--warn "Restarting %s" (lsp--workspace-print workspace))
   9198   (with-lsp-workspace workspace (lsp--shutdown-workspace t)))
   9199 
   9200 ;;;###autoload
   9201 (defun lsp (&optional arg)
   9202   "Entry point for the server startup.
   9203 When ARG is t the lsp mode will start new language server even if
   9204 there is language server which can handle current language. When
   9205 ARG is nil current file will be opened in multi folder language
   9206 server if there is such. When `lsp' is called with prefix
   9207 argument ask the user to select which language server to start."
   9208   (interactive "P")
   9209 
   9210   (lsp--require-packages)
   9211 
   9212   (when (buffer-file-name)
   9213     (let (clients
   9214           (matching-clients (lsp--filter-clients
   9215                              (-andfn #'lsp--supports-buffer?
   9216                                      #'lsp--server-binary-present?))))
   9217       (cond
   9218        (matching-clients
   9219         (when (setq lsp--buffer-workspaces
   9220                     (or (and
   9221                          ;; Don't open as library file if file is part of a project.
   9222                          (not (lsp-find-session-folder (lsp-session) (buffer-file-name)))
   9223                          (lsp--try-open-in-library-workspace))
   9224                         (lsp--try-project-root-workspaces (equal arg '(4))
   9225                                                           (and arg (not (equal arg 1))))))
   9226           (lsp-mode 1)
   9227           (when lsp-auto-configure (lsp--auto-configure))
   9228           (setq lsp-buffer-uri (lsp--buffer-uri))
   9229           (lsp--info "Connected to %s."
   9230                      (apply 'concat (--map (format "[%s %s]"
   9231                                                    (lsp--workspace-print it)
   9232                                                    (lsp--workspace-root it))
   9233                                            lsp--buffer-workspaces)))))
   9234        ;; look for servers which are currently being downloaded.
   9235        ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9236                                                    #'lsp--client-download-in-progress?)))
   9237         (lsp--info "There are language server(%s) installation in progress.
   9238 The server(s) will be started in the buffer when it has finished."
   9239                    (-map #'lsp--client-server-id clients))
   9240         (seq-do (lambda (client)
   9241                   (cl-pushnew (current-buffer) (lsp--client-buffers client)))
   9242                 clients))
   9243        ;; look for servers to install
   9244        ((setq clients (lsp--filter-clients
   9245                        (-andfn #'lsp--supports-buffer?
   9246                                (-const lsp-enable-suggest-server-download)
   9247                                #'lsp--client-download-server-fn
   9248                                (-not #'lsp--client-download-in-progress?))))
   9249         (let ((client (lsp--completing-read
   9250                        (concat "Unable to find installed server supporting this file. "
   9251                                "The following servers could be installed automatically: ")
   9252                        clients
   9253                        (-compose #'symbol-name #'lsp--client-server-id)
   9254                        nil
   9255                        t)))
   9256           (cl-pushnew (current-buffer) (lsp--client-buffers client))
   9257           (lsp--install-server-internal client)))
   9258        ;; ignore other warnings
   9259        ((not lsp-warn-no-matched-clients)
   9260         nil)
   9261        ;; automatic installation disabled
   9262        ((setq clients (unless matching-clients
   9263                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9264                                                      #'lsp--client-download-server-fn
   9265                                                      (-not (-const lsp-enable-suggest-server-download))
   9266                                                      (-not #'lsp--server-binary-present?)))))
   9267         (lsp--warn "The following servers support current file but automatic download is disabled: %s
   9268 \(If you have already installed the server check *lsp-log*)."
   9269                    (mapconcat (lambda (client)
   9270                                 (symbol-name (lsp--client-server-id client)))
   9271                               clients
   9272                               " ")))
   9273        ;; no clients present
   9274        ((setq clients (unless matching-clients
   9275                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9276                                                      (-not #'lsp--server-binary-present?)))))
   9277         (lsp--warn "The following servers support current file but do not have automatic installation: %s
   9278 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages.
   9279 \(If you have already installed the server check *lsp-log*)."
   9280                    (mapconcat (lambda (client)
   9281                                 (symbol-name (lsp--client-server-id client)))
   9282                               clients
   9283                               " ")))
   9284        ;; no matches
   9285        ((-> #'lsp--supports-buffer? lsp--filter-clients not)
   9286         (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'.
   9287 This issue might be caused by:
   9288 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'.
   9289 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'.
   9290 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/ .
   9291 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/.
   9292 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients').
   9293 You can customize `lsp-warn-no-matched-clients' to disable this message."
   9294                     major-mode major-mode major-mode))))))
   9295 
   9296 (defun lsp--buffer-visible-p ()
   9297   "Return non nil if current buffer is visible."
   9298   (or (buffer-modified-p) (get-buffer-window nil t)))
   9299 
   9300 (defun lsp--init-if-visible ()
   9301   "Run `lsp' for the current buffer if the buffer is visible.
   9302 Returns non nil if `lsp' was run for the buffer."
   9303   (when (lsp--buffer-visible-p)
   9304     (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t)
   9305     (lsp)
   9306     t))
   9307 
   9308 ;;;###autoload
   9309 (defun lsp-deferred ()
   9310   "Entry point that defers server startup until buffer is visible.
   9311 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'.
   9312 This avoids overloading the server with many files when starting Emacs."
   9313   ;; Workspace may not be initialized yet. Use a buffer local variable to
   9314   ;; remember that we deferred loading of this buffer.
   9315   (setq lsp--buffer-deferred t)
   9316   (let ((buffer (current-buffer)))
   9317     ;; Avoid false positives as desktop-mode restores buffers by deferring
   9318     ;; visibility check until the stack clears.
   9319     (run-with-idle-timer 0 nil (lambda ()
   9320                                  (when (buffer-live-p buffer)
   9321                                    (with-current-buffer buffer
   9322                                      (unless (lsp--init-if-visible)
   9323                                        (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t))))))))
   9324 
   9325 
   9326 
   9327 (defvar lsp-file-truename-cache (ht))
   9328 
   9329 (defmacro lsp-with-cached-filetrue-name (&rest body)
   9330   "Executes BODY caching the `file-truename' calls."
   9331   `(let ((old-fn (symbol-function 'file-truename)))
   9332      (unwind-protect
   9333          (progn
   9334            (fset 'file-truename
   9335                  (lambda (file-name &optional counter prev-dirs)
   9336                    (or (gethash file-name lsp-file-truename-cache)
   9337                        (puthash file-name (apply old-fn (list file-name counter prev-dirs))
   9338                                 lsp-file-truename-cache))))
   9339            ,@body)
   9340        (fset 'file-truename old-fn))))
   9341 
   9342 
   9343 (defun lsp-virtual-buffer-call (key &rest args)
   9344   (when lsp--virtual-buffer
   9345     (when-let ((fn (plist-get lsp--virtual-buffer key)))
   9346       (apply fn args))))
   9347 
   9348 (defun lsp-translate-column (column)
   9349   "Translate COLUMN taking into account virtual buffers."
   9350   (or (lsp-virtual-buffer-call :real->virtual-char column)
   9351       column))
   9352 
   9353 (defun lsp-translate-line (line)
   9354   "Translate LINE taking into account virtual buffers."
   9355   (or (lsp-virtual-buffer-call :real->virtual-line line)
   9356       line))
   9357 
   9358 
   9359 ;; lsp internal validation.
   9360 
   9361 (defmacro lsp--doctor (&rest checks)
   9362   `(-let [buf (current-buffer)]
   9363      (with-current-buffer (get-buffer-create "*lsp-performance*")
   9364        (with-help-window (current-buffer)
   9365          ,@(-map (-lambda ((msg form))
   9366                    `(insert (format "%s: %s\n" ,msg
   9367                                     (let ((res (with-current-buffer buf
   9368                                                  ,form)))
   9369                                       (cond
   9370                                        ((eq res :optional) (propertize "OPTIONAL" 'face 'warning))
   9371                                        (res (propertize "OK" 'face 'success))
   9372                                        (t (propertize "ERROR" 'face 'error)))))))
   9373                  (-partition 2 checks))))))
   9374 
   9375 (define-obsolete-function-alias 'lsp-diagnose
   9376   'lsp-doctor "lsp-mode 8.0.0")
   9377 
   9378 (defun lsp-doctor ()
   9379   "Validate performance settings."
   9380   (interactive)
   9381   (lsp--doctor
   9382    "Checking for Native JSON support" (functionp 'json-serialize)
   9383    "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max)
   9384    "Check `read-process-output-max' default has been changed from 4k"
   9385    (and (boundp 'read-process-output-max)
   9386         (> read-process-output-max 4096))
   9387    "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)"
   9388    (condition-case _err
   9389        (progn (lsp--make-message (list "a" "b"))
   9390               nil)
   9391      (error t))
   9392    "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000)
   9393    "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)
   9394    "Using emacs 28+ with native compilation?"
   9395    (or (and (fboundp 'native-comp-available-p)
   9396             (native-comp-available-p))
   9397        :optional)))
   9398 
   9399 (declare-function package-version-join "ext:package")
   9400 (declare-function package-desc-version "ext:package")
   9401 (declare-function package--alist "ext:package")
   9402 
   9403 (defun lsp-version ()
   9404   "Return string describing current version of `lsp-mode'."
   9405   (interactive)
   9406   (unless (featurep 'package)
   9407     (require 'package))
   9408   (let ((ver (format "lsp-mode %s, Emacs %s, %s"
   9409                      (package-version-join
   9410                       (package-desc-version
   9411                        (car (alist-get 'lsp-mode (package--alist)))))
   9412                      emacs-version
   9413                      system-type)))
   9414     (if (called-interactively-p 'interactive)
   9415         (lsp--info "%s" ver)
   9416       ver)))
   9417 
   9418 
   9419 
   9420 ;; org-mode/virtual-buffer
   9421 
   9422 (declare-function org-babel-get-src-block-info "ext:ob-core")
   9423 (declare-function org-do-remove-indentation "ext:org-macs")
   9424 (declare-function org-src-get-lang-mode "ext:org-src")
   9425 (declare-function org-element-context "ext:org-element")
   9426 
   9427 (defun lsp--virtual-buffer-update-position ()
   9428   (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range))
   9429                                      (funcall in-range))
   9430                                    lsp--virtual-buffer-connections))
   9431       (unless (equal virtual-buffer lsp--virtual-buffer)
   9432         (lsp-org))
   9433     (when lsp-managed-mode
   9434       (lsp-managed-mode -1)
   9435       (lsp-mode -1)
   9436       (setq lsp--buffer-workspaces nil)
   9437       (setq lsp--virtual-buffer nil)
   9438       (setq lsp-buffer-uri nil)
   9439 
   9440       ;; force refresh of diagnostics
   9441       (run-hooks 'lsp-after-diagnostics-hook))))
   9442 
   9443 (defun lsp-virtual-buffer-on-change (start end length)
   9444   "Adjust on change event to be executed against the proper language server."
   9445   (let ((max-point (max end
   9446                         (or (plist-get lsp--before-change-vals :end) 0)
   9447                         (+ start length))))
   9448     (when-let ((virtual-buffer (-first (lambda (vb)
   9449                                          (let ((lsp--virtual-buffer vb))
   9450                                            (and (lsp-virtual-buffer-call :in-range start)
   9451                                                 (lsp-virtual-buffer-call :in-range max-point))))
   9452                                        lsp--virtual-buffer-connections)))
   9453       (lsp-with-current-buffer virtual-buffer
   9454         (lsp-on-change start end length
   9455                        (lambda (&rest _)
   9456                          (list :range (lsp--range (list :character 0 :line 0)
   9457                                                   lsp--virtual-buffer-point-max)
   9458                                :text (lsp--buffer-content))))))))
   9459 
   9460 (defun lsp-virtual-buffer-before-change (start _end)
   9461   (when-let ((virtual-buffer (-first (lambda (vb)
   9462                                        (lsp-with-current-buffer vb
   9463                                          (lsp-virtual-buffer-call :in-range start)))
   9464                                      lsp--virtual-buffer-connections)))
   9465     (lsp-with-current-buffer virtual-buffer
   9466       (setq lsp--virtual-buffer-point-max
   9467             (lsp--point-to-position (lsp-virtual-buffer-call :last-point))))))
   9468 
   9469 (defun lsp-patch-on-change-event ()
   9470   (remove-hook 'after-change-functions #'lsp-on-change t)
   9471   (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t)
   9472   (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t))
   9473 
   9474 (defun lsp-kill-virtual-buffers ()
   9475   (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections))
   9476 
   9477 (defun lsp--move-point-in-indentation (point indentation)
   9478   (save-excursion
   9479     (goto-char point)
   9480     (if (<= point (+ (line-beginning-position) indentation))
   9481         (line-beginning-position)
   9482       point)))
   9483 
   9484 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck")
   9485 (declare-function flycheck-add-mode "ext:flycheck")
   9486 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics")
   9487 
   9488 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn)
   9489 
   9490 (defun lsp-flycheck-add-mode (mode)
   9491   "Register flycheck support for MODE."
   9492   (lsp-diagnostics-lsp-checker-if-needed)
   9493   (unless (flycheck-checker-supports-major-mode-p 'lsp mode)
   9494     (flycheck-add-mode 'lsp mode)))
   9495 
   9496 (defun lsp-progress-spinner-type ()
   9497   "Retrieve the spinner type value, if value is not a symbol of `spinner-types
   9498 defaults to `progress-bar."
   9499   (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar))
   9500 
   9501 (defun lsp-org ()
   9502   (interactive)
   9503   (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range))
   9504                                                               (funcall in-range))
   9505                                                             lsp--virtual-buffer-connections))
   9506       (unless (equal lsp--virtual-buffer virtual-buffer)
   9507         (setq lsp--buffer-workspaces workspaces)
   9508         (setq lsp--virtual-buffer virtual-buffer)
   9509         (setq lsp-buffer-uri nil)
   9510         (lsp-mode 1)
   9511         (lsp-managed-mode 1)
   9512         (lsp-patch-on-change-event))
   9513 
   9514     (save-excursion
   9515       (-let* (virtual-buffer
   9516               (wcb (lambda (f)
   9517                      (with-current-buffer (plist-get virtual-buffer :buffer)
   9518                        (-let* (((&plist :major-mode :buffer-file-name
   9519                                         :goto-buffer :workspaces) virtual-buffer)
   9520                                (lsp--virtual-buffer virtual-buffer)
   9521                                (lsp--buffer-workspaces workspaces))
   9522                          (save-excursion
   9523                            (funcall goto-buffer)
   9524                            (funcall f))))))
   9525               ((&plist :begin :end :post-blank :language) (cl-second (org-element-context)))
   9526               ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light)))
   9527 
   9528               (file-name (if file-name
   9529                              (f-expand file-name)
   9530                            (user-error "You should specify file name in the src block header.")))
   9531               (begin-marker (progn
   9532                               (goto-char begin)
   9533                               (forward-line)
   9534                               (set-marker (make-marker) (point))))
   9535               (end-marker (progn
   9536                             (goto-char end)
   9537                             (forward-line (1- (- post-blank)))
   9538                             (set-marker (make-marker) (1+ (point)))))
   9539               (buf (current-buffer))
   9540               (src-block (buffer-substring-no-properties begin-marker
   9541                                                          (1- end-marker)))
   9542               (indentation (with-temp-buffer
   9543                              (insert src-block)
   9544 
   9545                              (goto-char (point-min))
   9546                              (let ((indentation (current-indentation)))
   9547                                (plist-put lsp--virtual-buffer :indentation indentation)
   9548                                (org-do-remove-indentation)
   9549                                (goto-char (point-min))
   9550                                (- indentation (current-indentation))))))
   9551         (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t)
   9552 
   9553         (when (fboundp 'flycheck-add-mode)
   9554           (lsp-flycheck-add-mode 'org-mode))
   9555 
   9556         (setq lsp--virtual-buffer
   9557               (list
   9558                :in-range (lambda (&optional point)
   9559                            (<= begin-marker (or point (point)) (1- end-marker)))
   9560                :goto-buffer (lambda () (goto-char begin-marker))
   9561                :buffer-string
   9562                (lambda ()
   9563                  (let ((src-block (buffer-substring-no-properties
   9564                                    begin-marker
   9565                                    (1- end-marker))))
   9566                    (with-temp-buffer
   9567                      (insert src-block)
   9568 
   9569                      (goto-char (point-min))
   9570                      (while (not (eobp))
   9571                        (delete-region (point) (if (> (+ (point) indentation) (line-end-position))
   9572                                                   (line-end-position)
   9573                                                 (+ (point) indentation)))
   9574                        (forward-line))
   9575                      (buffer-substring-no-properties (point-min)
   9576                                                      (point-max)))))
   9577                :buffer buf
   9578                :begin begin-marker
   9579                :end end-marker
   9580                :indentation indentation
   9581                :last-point (lambda () (1- end-marker))
   9582                :cur-position (lambda ()
   9583                                (lsp-save-restriction-and-excursion
   9584                                  (list :line (- (lsp--cur-line)
   9585                                                 (lsp--cur-line begin-marker))
   9586                                        :character (let ((character (- (point)
   9587                                                                       (line-beginning-position)
   9588                                                                       indentation)))
   9589                                                     (if (< character 0)
   9590                                                         0
   9591                                                       character)))))
   9592                :line/character->point (-lambda (line character)
   9593                                         (-let [inhibit-field-text-motion t]
   9594                                           (+ indentation
   9595                                              (lsp-save-restriction-and-excursion
   9596                                                (goto-char begin-marker)
   9597                                                (forward-line line)
   9598                                                (-let [line-end (line-end-position)]
   9599                                                  (if (> character (- line-end (point)))
   9600                                                      line-end
   9601                                                    (forward-char character)
   9602                                                    (point)))))))
   9603                :major-mode (org-src-get-lang-mode language)
   9604                :buffer-file-name file-name
   9605                :buffer-uri (lsp--path-to-uri file-name)
   9606                :with-current-buffer wcb
   9607                :buffer-live? (lambda (_) (buffer-live-p buf))
   9608                :buffer-name (lambda (_)
   9609                               (propertize (format "%s(%s:%s)%s"
   9610                                                   (buffer-name buf)
   9611                                                   begin-marker
   9612                                                   end-marker
   9613                                                   language)
   9614                                           'face 'italic))
   9615                :real->virtual-line (lambda (line)
   9616                                      (+ line (line-number-at-pos begin-marker) -1))
   9617                :real->virtual-char (lambda (char) (+ char indentation))
   9618                :cleanup (lambda ()
   9619                           (set-marker begin-marker nil)
   9620                           (set-marker end-marker nil))))
   9621         (setf virtual-buffer lsp--virtual-buffer)
   9622         (puthash file-name virtual-buffer lsp--virtual-buffer-mappings)
   9623         (push virtual-buffer lsp--virtual-buffer-connections)
   9624 
   9625         ;; TODO: tangle only connected sections
   9626         (add-hook 'after-save-hook 'org-babel-tangle nil t)
   9627         (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t)
   9628         (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t)
   9629 
   9630         (setq lsp--buffer-workspaces
   9631               (lsp-with-current-buffer virtual-buffer
   9632                 (lsp)
   9633                 (plist-put virtual-buffer :workspaces (lsp-workspaces))
   9634                 (lsp-workspaces)))))))
   9635 
   9636 (defun lsp-virtual-buffer-disconnect (virtual-buffer)
   9637   (interactive (list (or
   9638                       lsp--virtual-buffer
   9639                       (when lsp--virtual-buffer-connections
   9640                         (lsp--completing-read "Select virtual buffer to disconnect: "
   9641                                               lsp--virtual-buffer-connections
   9642                                               (-lambda ((&plist :buffer-file-name))
   9643                                                 buffer-file-name))))))
   9644   (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer)
   9645       (progn
   9646         (lsp-with-current-buffer virtual-buffer
   9647           (lsp--text-document-did-close))
   9648         (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections))
   9649         (when (eq virtual-buffer lsp--virtual-buffer)
   9650           (setf lsp--virtual-buffer nil))
   9651         (when cleanup (funcall cleanup))
   9652         (remhash file-name lsp--virtual-buffer-mappings)
   9653 
   9654         (lsp--virtual-buffer-update-position)
   9655         (lsp--info "Disconnected from buffer %s" file-name))
   9656     (lsp--error "Nothing to disconnect from?")))
   9657 
   9658 
   9659 ;; inlay hints
   9660 
   9661 (defface lsp-inlay-hint-face
   9662   '((t :inherit font-lock-comment-face))
   9663   "The face to use for the JavaScript inlays."
   9664   :group 'lsp-mode
   9665   :package-version '(lsp-mode . "9.0.0"))
   9666 
   9667 (defface lsp-inlay-hint-type-face
   9668   '((t :inherit lsp-inlay-hint-face))
   9669   "Face for inlay type hints (e.g. inferred variable types)."
   9670   :group 'lsp-mode
   9671   :package-version '(lsp-mode . "9.0.0"))
   9672 
   9673 (defcustom lsp-inlay-hint-type-format "%s"
   9674   "Format string for variable inlays (part of the inlay face)."
   9675   :type '(string :tag "String")
   9676   :group 'lsp-mode
   9677   :package-version '(lsp-mode . "9.0.0"))
   9678 
   9679 (defface lsp-inlay-hint-parameter-face
   9680   '((t :inherit lsp-inlay-hint-face))
   9681   "Face for inlay parameter hints (e.g. function parameter names at
   9682 call-site)."
   9683   :group 'lsp-mode
   9684   :package-version '(lsp-mode . "9.0.0"))
   9685 
   9686 (defcustom lsp-inlay-hint-param-format "%s"
   9687   "Format string for parameter inlays (part of the inlay face)."
   9688   :type '(string :tag "String")
   9689   :group 'lsp-mode
   9690   :package-version '(lsp-mode . "9.0.0"))
   9691 
   9692 (defcustom lsp-update-inlay-hints-on-scroll t
   9693   "If non-nil update inlay hints immediately when scrolling or
   9694 modifying window sizes."
   9695   :type 'boolean
   9696   :package-version '(lsp-mode . "9.0.0"))
   9697 
   9698 (defun lsp--format-inlay (text kind)
   9699   (cond
   9700    ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text))
   9701    ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text))
   9702    (t text)))
   9703 
   9704 (defun lsp--face-for-inlay (kind)
   9705   (cond
   9706    ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face)
   9707    ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face)
   9708    (t 'lsp-inlay-hint-face)))
   9709 
   9710 (defun lsp--update-inlay-hints-scroll-function (window start)
   9711   (lsp-update-inlay-hints start (window-end window t)))
   9712 
   9713 (defun lsp--update-inlay-hints ()
   9714   (lsp-update-inlay-hints (window-start) (window-end nil t)))
   9715 
   9716 (defun lsp--label-from-inlay-hints-response (label)
   9717   "Returns a string label built from an array of
   9718 InlayHintLabelParts or the argument itself if it's already a
   9719 string."
   9720   (cl-typecase label
   9721     (string label)
   9722     (vector
   9723      (string-join (mapcar (lambda (part)
   9724                             (-let (((&InlayHintLabelPart :value) part))
   9725                               value))
   9726                           label)))))
   9727 
   9728 (defun lsp-update-inlay-hints (start end)
   9729   (lsp-request-async
   9730    "textDocument/inlayHint"
   9731    (lsp-make-inlay-hints-params
   9732     :text-document (lsp--text-document-identifier)
   9733     :range (lsp-make-range :start
   9734                            (lsp-point-to-position start)
   9735                            :end
   9736                            (lsp-point-to-position end)))
   9737    (lambda (res)
   9738      (lsp--remove-overlays 'lsp-inlay-hint)
   9739      (dolist (hint res)
   9740        (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint)
   9741                (kind (or kind? lsp/inlay-hint-kind-type-hint))
   9742                (label (lsp--label-from-inlay-hints-response label))
   9743                (pos (lsp--position-to-point position))
   9744                (overlay (make-overlay pos pos nil 'front-advance 'end-advance)))
   9745          (when (stringp label)
   9746            (overlay-put overlay 'lsp-inlay-hint t)
   9747            (overlay-put overlay 'before-string
   9748                         (format "%s%s%s"
   9749                                 (if padding-left? " " "")
   9750                                 (propertize (lsp--format-inlay label kind)
   9751                                             'font-lock-face (lsp--face-for-inlay kind))
   9752                                 (if padding-right? " " "")))))))
   9753    :mode 'tick))
   9754 
   9755 (define-minor-mode lsp-inlay-hints-mode
   9756   "Mode for displaying inlay hints."
   9757   :lighter nil
   9758   (cond
   9759    ((and lsp-inlay-hints-mode lsp--buffer-workspaces)
   9760     (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t)
   9761     (when lsp-update-inlay-hints-on-scroll
   9762       (add-to-list (make-local-variable 'window-scroll-functions)
   9763                    #'lsp--update-inlay-hints-scroll-function)))
   9764    (t
   9765     (lsp--remove-overlays 'lsp-inlay-hint)
   9766     (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t)
   9767     (setf window-scroll-functions
   9768           (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions)))))
   9769 
   9770 
   9771 
   9772 ;;;###autoload
   9773 (defun lsp-start-plain ()
   9774   "Start `lsp-mode' using minimal configuration using the latest `melpa' version
   9775 of the packages.
   9776 
   9777 In case the major-mode that you are using for "
   9778   (interactive)
   9779   (let ((start-plain (make-temp-file "plain" nil ".el")))
   9780     (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el"
   9781                    start-plain t)
   9782     (async-shell-command
   9783      (format "%s -q -l %s %s"
   9784              (expand-file-name invocation-name invocation-directory)
   9785              start-plain
   9786              (or (buffer-file-name) ""))
   9787      (generate-new-buffer " *lsp-start-plain*"))))
   9788 
   9789 
   9790 
   9791 (provide 'lsp-mode)
   9792 ;;; lsp-mode.el ends here