config

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

lsp-mode.el (434177B)


      1 ;;; lsp-mode.el --- LSP mode                              -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2020-2024 emacs-lsp maintainers
      4 
      5 ;; Author: Vibhav Pant, Fangrui Song, Ivan Yonchovski
      6 ;; Keywords: languages
      7 ;; Package-Requires: ((emacs "27.1") (dash "2.18.0") (f "0.20.0") (ht "2.3") (spinner "1.7.3") (markdown-mode "2.3") (lv "0") (eldoc "1.11"))
      8 ;; Package-Version: 20241003.636
      9 ;; Package-Revision: 52de586352de
     10 
     11 ;; URL: https://github.com/emacs-lsp/lsp-mode
     12 ;; This program is free software; you can redistribute it and/or modify
     13 ;; it under the terms of the GNU General Public License as published by
     14 ;; the Free Software Foundation, either version 3 of the License, or
     15 ;; (at your option) any later version.
     16 
     17 ;; This program is distributed in the hope that it will be useful,
     18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     20 ;; GNU General Public License for more details.
     21 
     22 ;; You should have received a copy of the GNU General Public License
     23 ;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
     24 
     25 ;;; Commentary:
     26 
     27 ;; Emacs client/library for the Language Server Protocol
     28 
     29 ;;; Code:
     30 
     31 (require 'cl-generic)
     32 (require 'cl-lib)
     33 (require 'compile)
     34 (require 'dash)
     35 (require 'epg)
     36 (require 'ewoc)
     37 (require 'f)
     38 (require 'filenotify)
     39 (require 'files)
     40 (require 'ht)
     41 (require 'imenu)
     42 (require 'inline)
     43 (require 'json)
     44 (require 'lv)
     45 (require 'markdown-mode)
     46 (require 'network-stream)
     47 (require 'pcase)
     48 (require 'rx)
     49 (require 's)
     50 (require 'seq)
     51 (require 'spinner)
     52 (require 'subr-x)
     53 (require 'tree-widget)
     54 (require 'url-parse)
     55 (require 'url-util)
     56 (require 'widget)
     57 (require 'xref)
     58 (require 'minibuffer)
     59 (require 'help-mode)
     60 (require 'lsp-protocol)
     61 
     62 (defgroup lsp-mode nil
     63   "Language Server Protocol client."
     64   :group 'tools
     65   :tag "Language Server (lsp-mode)")
     66 
     67 (declare-function evil-set-command-property "ext:evil-common")
     68 (declare-function projectile-project-root "ext:projectile")
     69 (declare-function yas-expand-snippet "ext:yasnippet")
     70 (declare-function dap-mode "ext:dap-mode")
     71 (declare-function dap-auto-configure-mode "ext:dap-mode")
     72 
     73 (defvar yas-inhibit-overlay-modification-protection)
     74 (defvar yas-indent-line)
     75 (defvar yas-wrap-around-region)
     76 (defvar yas-also-auto-indent-first-line)
     77 (defvar dap-auto-configure-mode)
     78 (defvar dap-ui-menu-items)
     79 (defvar company-minimum-prefix-length)
     80 
     81 (defconst lsp--message-type-face
     82   `((1 . ,compilation-error-face)
     83     (2 . ,compilation-warning-face)
     84     (3 . ,compilation-message-face)
     85     (4 . ,compilation-info-face)))
     86 
     87 (defconst lsp--errors
     88   '((-32700 "Parse Error")
     89     (-32600 "Invalid Request")
     90     (-32601 "Method not Found")
     91     (-32602 "Invalid Parameters")
     92     (-32603 "Internal Error")
     93     (-32099 "Server Start Error")
     94     (-32000 "Server End Error")
     95     (-32002 "Server Not Initialized")
     96     (-32001 "Unknown Error Code")
     97     (-32800 "Request Cancelled"))
     98   "Alist of error codes to user friendly strings.")
     99 
    100 (defconst lsp--empty-ht (make-hash-table))
    101 
    102 (eval-and-compile
    103   (defun dash-expand:&lsp-wks (key source)
    104     `(,(intern-soft (format "lsp--workspace-%s" (eval key))) ,source))
    105 
    106   (defun dash-expand:&lsp-cln (key source)
    107     `(,(intern-soft (format "lsp--client-%s" (eval key))) ,source)))
    108 
    109 (define-obsolete-variable-alias 'lsp-print-io 'lsp-log-io "lsp-mode 6.1")
    110 
    111 (defcustom lsp-log-io nil
    112   "If non-nil, log all messages from the language server to a *lsp-log* buffer."
    113   :group 'lsp-mode
    114   :type 'boolean)
    115 
    116 (defcustom lsp-log-io-allowlist-methods '()
    117   "The methods to filter before print to lsp-log-io."
    118   :group 'lsp-mode
    119   :type '(repeat string)
    120   :package-version '(lsp-mode . "9.0.0"))
    121 
    122 (defcustom lsp-log-max message-log-max
    123   "Maximum number of lines to keep in the log buffer.
    124 If nil, disable message logging.  If t, log messages but don’t truncate
    125 the buffer when it becomes large."
    126   :group 'lsp-mode
    127   :type '(choice (const :tag "Disable" nil)
    128                  (integer :tag "lines")
    129                  (const :tag "Unlimited" t))
    130   :package-version '(lsp-mode . "6.1"))
    131 
    132 (defcustom lsp-io-messages-max t
    133   "Maximum number of messages that can be locked in a `lsp-io' buffer."
    134   :group 'lsp-mode
    135   :type '(choice (const :tag "Unlimited" t)
    136                  (integer :tag "Messages"))
    137   :package-version '(lsp-mode . "6.1"))
    138 
    139 (defcustom lsp-keep-workspace-alive t
    140   "If non nil keep workspace alive when the last workspace buffer is closed."
    141   :group 'lsp-mode
    142   :type 'boolean)
    143 
    144 (defcustom lsp-enable-snippet t
    145   "Enable/disable snippet completion support."
    146   :group 'lsp-completion
    147   :type 'boolean)
    148 
    149 (defcustom lsp-enable-folding t
    150   "Enable/disable code folding support."
    151   :group 'lsp-mode
    152   :type 'boolean
    153   :package-version '(lsp-mode . "6.1"))
    154 
    155 (define-obsolete-variable-alias 'lsp-enable-semantic-highlighting 'lsp-semantic-tokens-enable "lsp-mode 8.0.0")
    156 
    157 (defcustom lsp-semantic-tokens-enable nil
    158   "Enable/disable support for semantic tokens.
    159 As defined by the Language Server Protocol 3.16."
    160   :group 'lsp-semantic-tokens
    161   :type 'boolean)
    162 
    163 (defcustom lsp-folding-range-limit nil
    164   "The maximum number of folding ranges to receive from the language server."
    165   :group 'lsp-mode
    166   :type '(choice (const :tag "No limit." nil)
    167                  (integer :tag "Number of lines."))
    168   :package-version '(lsp-mode . "6.1"))
    169 
    170 (defcustom lsp-folding-line-folding-only nil
    171   "If non-nil, only fold complete lines."
    172   :group 'lsp-mode
    173   :type 'boolean
    174   :package-version '(lsp-mode . "6.1"))
    175 
    176 (defcustom lsp-client-packages
    177   '( ccls lsp-actionscript lsp-ada lsp-angular lsp-ansible lsp-asm lsp-astro
    178      lsp-autotools lsp-awk lsp-bash lsp-beancount lsp-bufls lsp-clangd
    179      lsp-clojure lsp-cmake lsp-cobol lsp-credo lsp-crystal lsp-csharp lsp-css
    180      lsp-cucumber lsp-cypher lsp-d lsp-dart lsp-dhall lsp-docker lsp-dockerfile
    181      lsp-earthly lsp-elixir lsp-elm lsp-emmet lsp-erlang lsp-eslint lsp-fortran lsp-futhark
    182      lsp-fsharp lsp-gdscript lsp-gleam lsp-glsl lsp-go lsp-golangci-lint lsp-grammarly
    183      lsp-graphql lsp-groovy lsp-hack lsp-haskell lsp-haxe lsp-idris lsp-java
    184      lsp-javascript lsp-jq lsp-json lsp-kotlin lsp-latex lsp-lisp lsp-ltex
    185      lsp-lua lsp-magik lsp-markdown lsp-marksman lsp-mdx lsp-meson lsp-metals lsp-mint
    186      lsp-mojo lsp-move lsp-mssql lsp-nginx lsp-nim lsp-nix lsp-nushell lsp-ocaml
    187      lsp-openscad lsp-pascal lsp-perl lsp-perlnavigator lsp-php lsp-pls
    188      lsp-purescript lsp-pwsh lsp-pyls lsp-pylsp lsp-pyright lsp-python-ms
    189      lsp-qml lsp-r lsp-racket lsp-remark lsp-rf lsp-roslyn lsp-rubocop lsp-ruby-lsp
    190      lsp-ruby-syntax-tree lsp-ruff lsp-rust lsp-semgrep lsp-shader
    191      lsp-solargraph lsp-solidity lsp-sonarlint lsp-sorbet lsp-sourcekit
    192      lsp-sql lsp-sqls lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform
    193      lsp-tex lsp-tilt lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-v
    194      lsp-vala lsp-verilog lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl
    195      lsp-xml lsp-yaml lsp-yang lsp-zig)
    196   "List of the clients to be automatically required."
    197   :group 'lsp-mode
    198   :type '(repeat symbol))
    199 
    200 (defcustom lsp-progress-via-spinner t
    201   "If non-nil, display LSP $/progress reports via a spinner in the modeline."
    202   :group 'lsp-mode
    203   :type 'boolean)
    204 
    205 (defcustom lsp-progress-spinner-type 'progress-bar
    206   "Holds the type of spinner to be used in the mode-line.
    207 Takes a value accepted by `spinner-start'."
    208   :group 'lsp-mode
    209   :type `(choice :tag "Choose a spinner by name"
    210                  ,@(mapcar (lambda (c) (list 'const (car c)))
    211                            spinner-types)))
    212 
    213 (defvar-local lsp-use-workspace-root-for-server-default-directory nil
    214   "Use `lsp-workspace-root' for `default-directory' when starting LSP process.")
    215 
    216 (defvar-local lsp--cur-workspace nil)
    217 
    218 (defvar-local lsp--cur-version 0)
    219 (defvar-local lsp--virtual-buffer-connections nil)
    220 (defvar-local lsp--virtual-buffer nil)
    221 (defvar lsp--virtual-buffer-mappings (ht))
    222 
    223 (defvar lsp--uri-file-prefix (pcase system-type
    224                                (`windows-nt "file:///")
    225                                (_ "file://"))
    226   "Prefix for a file-uri.")
    227 
    228 (defvar-local lsp-buffer-uri nil
    229   "If set, return it instead of calculating it using `buffer-file-name'.")
    230 
    231 (define-error 'lsp-error "Unknown lsp-mode error")
    232 (define-error 'lsp-empty-response-error
    233   "Empty response from the language server" 'lsp-error)
    234 (define-error 'lsp-timed-out-error
    235   "Timed out while waiting for a response from the language server" 'lsp-error)
    236 (define-error 'lsp-capability-not-supported
    237   "Capability not supported by the language server" 'lsp-error)
    238 (define-error 'lsp-file-scheme-not-supported
    239   "Unsupported file scheme" 'lsp-error)
    240 (define-error 'lsp-client-already-exists-error
    241   "A client with this server-id already exists" 'lsp-error)
    242 (define-error 'lsp-no-code-actions
    243   "No code actions" 'lsp-error)
    244 
    245 (defcustom lsp-auto-guess-root nil
    246   "Automatically guess the project root using projectile/project.
    247 Do *not* use this setting unless you are familiar with `lsp-mode'
    248 internals and you are sure that all of your projects are
    249 following `projectile'/`project.el' conventions."
    250   :group 'lsp-mode
    251   :type 'boolean)
    252 
    253 (defcustom lsp-guess-root-without-session nil
    254   "Ignore the session file when calculating the project root.
    255 You almost always want to set lsp-auto-guess-root too.
    256 Do *not* use this setting unless you are familiar with `lsp-mode'
    257 internals and you are sure that all of your projects are
    258 following `projectile'/`project.el' conventions."
    259   :group 'lsp-mode
    260   :type 'boolean)
    261 
    262 (defcustom lsp-restart 'interactive
    263   "Defines how server-exited events must be handled."
    264   :group 'lsp-mode
    265   :type '(choice (const interactive)
    266                  (const auto-restart)
    267                  (const ignore)))
    268 
    269 (defcustom lsp-session-file (expand-file-name (locate-user-emacs-file ".lsp-session-v1"))
    270   "File where session information is stored."
    271   :group 'lsp-mode
    272   :type 'file)
    273 
    274 (defcustom lsp-auto-configure t
    275   "Auto configure `lsp-mode' main features.
    276 When set to t `lsp-mode' will auto-configure completion,
    277 code-actions, breadcrumb, `flycheck', `flymake', `imenu', symbol highlighting,
    278 lenses, links, and so on.
    279 
    280 For finer granularity you may use `lsp-enable-*' properties."
    281   :group 'lsp-mode
    282   :type 'boolean
    283   :package-version '(lsp-mode . "6.1"))
    284 
    285 (defcustom lsp-disabled-clients nil
    286   "A list of disabled/blocklisted clients.
    287 Each entry in the list can be either:
    288 a symbol, the server-id for the LSP client, or
    289 a cons pair (MAJOR-MODE . CLIENTS), where MAJOR-MODE is the major-mode,
    290 and CLIENTS is either a client or a list of clients.
    291 
    292 This option can also be used as a file- or directory-local variable to
    293 disable a language server for individual files or directories/projects
    294 respectively."
    295   :group 'lsp-mode
    296   :type '(repeat (symbol))
    297   :safe 'listp
    298   :package-version '(lsp-mode . "6.1"))
    299 
    300 (defvar lsp-clients (make-hash-table :test 'eql)
    301   "Hash table server-id -> client.
    302 It contains all of the clients that are currently registered.")
    303 
    304 (defvar lsp-enabled-clients nil
    305   "List of clients allowed to be used for projects.
    306 When nil, all registered clients are considered candidates.")
    307 
    308 (defvar lsp-last-id 0
    309   "Last request id.")
    310 
    311 (defcustom lsp-before-initialize-hook nil
    312   "List of functions to be called before a Language Server has been initialized
    313 for a new workspace."
    314   :type 'hook
    315   :group 'lsp-mode)
    316 
    317 (defcustom lsp-after-initialize-hook nil
    318   "List of functions to be called after a Language Server has been initialized
    319 for a new workspace."
    320   :type 'hook
    321   :group 'lsp-mode)
    322 
    323 (defcustom lsp-before-open-hook nil
    324   "List of functions to be called before a new file with LSP support is opened."
    325   :type 'hook
    326   :group 'lsp-mode)
    327 
    328 (defcustom lsp-after-open-hook nil
    329   "List of functions to be called after a new file with LSP support is opened."
    330   :type 'hook
    331   :group 'lsp-mode)
    332 
    333 (defcustom lsp-enable-file-watchers t
    334   "If non-nil lsp-mode will watch the files in the workspace if
    335 the server has requested that."
    336   :type 'boolean
    337   :group 'lsp-mode
    338   :package-version '(lsp-mode . "6.1"))
    339 ;;;###autoload(put 'lsp-enable-file-watchers 'safe-local-variable #'booleanp)
    340 
    341 (define-obsolete-variable-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "8.0.0")
    342 
    343 (defcustom lsp-file-watch-ignored-directories
    344   '(; SCM tools
    345     "[/\\\\]\\.git\\'"
    346     "[/\\\\]\\.github\\'"
    347     "[/\\\\]\\.gitlab\\'"
    348     "[/\\\\]\\.circleci\\'"
    349     "[/\\\\]\\.hg\\'"
    350     "[/\\\\]\\.bzr\\'"
    351     "[/\\\\]_darcs\\'"
    352     "[/\\\\]\\.svn\\'"
    353     "[/\\\\]_FOSSIL_\\'"
    354     ;; IDE or build tools
    355     "[/\\\\]\\.idea\\'"
    356     "[/\\\\]\\.ensime_cache\\'"
    357     "[/\\\\]\\.eunit\\'"
    358     "[/\\\\]node_modules"
    359     "[/\\\\]\\.yarn\\'"
    360     "[/\\\\]\\.fslckout\\'"
    361     "[/\\\\]\\.tox\\'"
    362     "[/\\\\]\\.nox\\'"
    363     "[/\\\\]dist\\'"
    364     "[/\\\\]dist-newstyle\\'"
    365     "[/\\\\]\\.stack-work\\'"
    366     "[/\\\\]\\.bloop\\'"
    367     "[/\\\\]\\.metals\\'"
    368     "[/\\\\]target\\'"
    369     "[/\\\\]\\.ccls-cache\\'"
    370     "[/\\\\]\\.vs\\'"
    371     "[/\\\\]\\.vscode\\'"
    372     "[/\\\\]\\.venv\\'"
    373     "[/\\\\]\\.mypy_cache\\'"
    374     "[/\\\\]\\.pytest_cache\\'"
    375     ;; Swift Package Manager
    376     "[/\\\\]\\.build\\'"
    377     ;; Python
    378     "[/\\\\]__pycache__\\'"
    379     ;; Autotools output
    380     "[/\\\\]\\.deps\\'"
    381     "[/\\\\]build-aux\\'"
    382     "[/\\\\]autom4te.cache\\'"
    383     "[/\\\\]\\.reference\\'"
    384     ;; Bazel
    385     "[/\\\\]bazel-[^/\\\\]+\\'"
    386     ;; CSharp
    387     "[/\\\\]\\.cache[/\\\\]lsp-csharp\\'"
    388     "[/\\\\]\\.meta\\'"
    389     "[/\\\\]\\.nuget\\'"
    390     ;; Unity
    391     "[/\\\\]Library\\'"
    392     ;; Clojure
    393     "[/\\\\]\\.lsp\\'"
    394     "[/\\\\]\\.clj-kondo\\'"
    395     "[/\\\\]\\.shadow-cljs\\'"
    396     "[/\\\\]\\.babel_cache\\'"
    397     "[/\\\\]\\.cpcache\\'"
    398     "[/\\\\]\\checkouts\\'"
    399     ;; Gradle
    400     "[/\\\\]\\.gradle\\'"
    401     ;; Maven
    402     "[/\\\\]\\.m2\\'"
    403     ;; .Net Core build-output
    404     "[/\\\\]bin/Debug\\'"
    405     "[/\\\\]obj\\'"
    406     ;; OCaml and Dune
    407     "[/\\\\]_opam\\'"
    408     "[/\\\\]_build\\'"
    409     ;; Elixir
    410     "[/\\\\]\\.elixir_ls\\'"
    411     ;; Elixir Credo
    412     "[/\\\\]\\.elixir-tools\\'"
    413     ;; terraform and terragrunt
    414     "[/\\\\]\\.terraform\\'"
    415     "[/\\\\]\\.terragrunt-cache\\'"
    416     ;; nix-direnv
    417     "[/\\\\]\\result"
    418     "[/\\\\]\\result-bin"
    419     "[/\\\\]\\.direnv\\'")
    420   "List of regexps matching directory paths which won't be monitored when
    421 creating file watches. Customization of this variable is only honored at
    422 the global level or at a root of an lsp workspace."
    423   :group 'lsp-mode
    424   :type '(repeat string)
    425   :package-version '(lsp-mode . "8.0.0"))
    426 
    427 (define-obsolete-function-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "7.0.1")
    428 
    429 (defun lsp-file-watch-ignored-directories ()
    430   lsp-file-watch-ignored-directories)
    431 
    432 ;; Allow lsp-file-watch-ignored-directories as a file or directory-local variable
    433 ;;;###autoload(put 'lsp-file-watch-ignored-directories 'safe-local-variable 'lsp--string-listp)
    434 
    435 (defcustom lsp-file-watch-ignored-files
    436   '(
    437     ;; Flycheck tempfiles
    438     "[/\\\\]flycheck_[^/\\\\]+\\'"
    439     ;; lockfiles
    440     "[/\\\\]\\.#[^/\\\\]+\\'"
    441     ;; backup files
    442     "[/\\\\][^/\\\\]+~\\'" )
    443   "List of regexps matching files for which change events will
    444 not be sent to the server.
    445 
    446 This setting has no impact on whether a file-watch is created for
    447 a directory; it merely prevents notifications pertaining to
    448 matched files from being sent to the server.  To prevent a
    449 file-watch from being created for a directory, customize
    450 `lsp-file-watch-ignored-directories'
    451 
    452 Customization of this variable is only honored at the global
    453 level or at a root of an lsp workspace."
    454   :group 'lsp-mode
    455   :type '(repeat string)
    456   :package-version '(lsp-mode . "8.0.0"))
    457 
    458 ;; Allow lsp-file-watch-ignored-files as a file or directory-local variable
    459 ;;;###autoload(put 'lsp-file-watch-ignored-files 'safe-local-variable 'lsp--string-listp)
    460 
    461 (defcustom lsp-after-uninitialized-functions nil
    462   "List of functions to be called after a Language Server has been uninitialized."
    463   :type 'hook
    464   :group 'lsp-mode
    465   :package-version '(lsp-mode . "6.3"))
    466 
    467 (defconst lsp--sync-full 1)
    468 (defconst lsp--sync-incremental 2)
    469 
    470 (defcustom lsp-debounce-full-sync-notifications t
    471   "If non-nil debounce full sync events.
    472 This flag affects only servers which do not support incremental updates."
    473   :type 'boolean
    474   :group 'lsp-mode
    475   :package-version '(lsp-mode . "6.1"))
    476 
    477 (defcustom lsp-debounce-full-sync-notifications-interval 1.0
    478   "Time to wait before sending full sync synchronization after buffer modification."
    479   :type 'float
    480   :group 'lsp-mode
    481   :package-version '(lsp-mode . "6.1"))
    482 
    483 (defvar lsp--stderr-index 0)
    484 
    485 (defvar lsp--delayed-requests nil)
    486 (defvar lsp--delay-timer nil)
    487 
    488 (defcustom lsp-document-sync-method nil
    489   "How to sync the document with the language server."
    490   :type '(choice (const :tag "Documents are synced by always sending the full content of the document." lsp--sync-full)
    491                  (const :tag "Documents are synced by always sending incremental changes to the document." lsp--sync-incremental)
    492                  (const :tag "Use the method recommended by the language server." nil))
    493   :group 'lsp-mode)
    494 
    495 (defcustom lsp-auto-execute-action t
    496   "Auto-execute single action."
    497   :type 'boolean
    498   :group 'lsp-mode)
    499 
    500 (defcustom lsp-enable-links t
    501   "If non-nil, all references to links in a file will be made clickable, if
    502 supported by the language server."
    503   :type 'boolean
    504   :group 'lsp-mode
    505   :package-version '(lsp-mode . "6.1"))
    506 
    507 (defcustom lsp-enable-imenu t
    508   "If non-nil, automatically enable `imenu' integration when server provides
    509 `textDocument/documentSymbol'."
    510   :type 'boolean
    511   :group 'lsp-mode
    512   :package-version '(lsp-mode . "6.2"))
    513 
    514 (defcustom lsp-enable-dap-auto-configure t
    515   "If non-nil, enable `dap-auto-configure-mode`."
    516   :type 'boolean
    517   :group 'lsp-mode
    518   :package-version '(lsp-mode . "7.0"))
    519 
    520 (defcustom lsp-eldoc-enable-hover t
    521   "If non-nil, `eldoc' will display hover info when it is present."
    522   :type 'boolean
    523   :group 'lsp-mode)
    524 
    525 (defcustom lsp-eldoc-render-all nil
    526   "Display all of the info returned by document/onHover.
    527 If this is set to nil, `eldoc' will show only the symbol information."
    528   :type 'boolean
    529   :group 'lsp-mode)
    530 
    531 (define-obsolete-variable-alias 'lsp-enable-completion-at-point
    532   'lsp-completion-enable "lsp-mode 7.0.1")
    533 
    534 (defcustom lsp-completion-enable t
    535   "Enable `completion-at-point' integration."
    536   :type 'boolean
    537   :group 'lsp-completion)
    538 
    539 (defcustom lsp-enable-symbol-highlighting t
    540   "Highlight references of the symbol at point."
    541   :type 'boolean
    542   :group 'lsp-mode)
    543 
    544 (defcustom lsp-enable-xref t
    545   "Enable xref integration."
    546   :type 'boolean
    547   :group 'lsp-mode)
    548 
    549 (defcustom lsp-references-exclude-definition nil
    550   "If non-nil, exclude declarations when finding references."
    551   :type 'boolean
    552   :group 'lsp-mode)
    553 
    554 (defcustom lsp-enable-indentation t
    555   "Indent regions using the file formatting functionality provided by the
    556 language server."
    557   :type 'boolean
    558   :group 'lsp-mode)
    559 
    560 (defcustom lsp-enable-on-type-formatting t
    561   "Enable `textDocument/onTypeFormatting' integration."
    562   :type 'boolean
    563   :group 'lsp-mode)
    564 
    565 (defcustom lsp-enable-text-document-color t
    566   "Enable `textDocument/documentColor' integration."
    567   :type 'boolean
    568   :group 'lsp-mode)
    569 
    570 (defcustom lsp-before-save-edits t
    571   "If non-nil, `lsp-mode' will apply edits suggested by the language server
    572 before saving a document."
    573   :type 'boolean
    574   :group 'lsp-mode)
    575 
    576 (defcustom lsp-after-apply-edits-hook nil
    577   "Hooks to run when text edit is applied.
    578 It contains the operation source."
    579   :type 'hook
    580   :group 'lsp-mode
    581   :package-version '(lsp-mode . "8.0.0"))
    582 
    583 (defcustom lsp-apply-edits-after-file-operations t
    584   "Whether to apply edits returned by server after file operations if any.
    585 Applicable only if server supports workspace.fileOperations for operations:
    586 `workspace/willRenameFiles', `workspace/willCreateFiles' and
    587 `workspace/willDeleteFiles'."
    588   :group 'lsp-mode
    589   :type 'boolean)
    590 
    591 (defcustom lsp-modeline-code-actions-enable t
    592   "Whether to show code actions on modeline."
    593   :type 'boolean
    594   :group 'lsp-modeline)
    595 
    596 (defcustom lsp-modeline-diagnostics-enable t
    597   "Whether to show diagnostics on modeline."
    598   :type 'boolean
    599   :group 'lsp-modeline)
    600 
    601 (defcustom lsp-modeline-workspace-status-enable t
    602   "Whether to show workspace status on modeline."
    603   :type 'boolean
    604   :group 'lsp-modeline
    605   :package-version '(lsp-mode . "8.0.0"))
    606 
    607 (defcustom lsp-headerline-breadcrumb-enable t
    608   "Whether to enable breadcrumb on headerline."
    609   :type 'boolean
    610   :group 'lsp-headerline)
    611 
    612 (defcustom lsp-configure-hook nil
    613   "Hooks to run when `lsp-configure-buffer' is called."
    614   :type 'hook
    615   :group 'lsp-mode)
    616 
    617 (defcustom lsp-unconfigure-hook nil
    618   "Hooks to run when `lsp-unconfig-buffer' is called."
    619   :type 'hook
    620   :group 'lsp-mode)
    621 
    622 (defcustom lsp-after-diagnostics-hook nil
    623   "Hooks to run after diagnostics are received.
    624 Note: it runs only if the receiving buffer is open. Use
    625 `lsp-diagnostics-updated-hook'if you want to be notified when
    626 diagnostics have changed."
    627   :type 'hook
    628   :group 'lsp-mode)
    629 
    630 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook
    631   'lsp-diagnostics-updated-hook "lsp-mode 6.4")
    632 
    633 (defcustom lsp-diagnostics-updated-hook nil
    634   "Hooks to run after diagnostics are received."
    635   :type 'hook
    636   :group 'lsp-mode)
    637 
    638 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook
    639   'lsp-workspace-folders-changed-functions "lsp-mode 6.3")
    640 
    641 (defcustom lsp-workspace-folders-changed-functions nil
    642   "Hooks to run after the folders has changed.
    643 The hook will receive two parameters list of added and removed folders."
    644   :type 'hook
    645   :group 'lsp-mode)
    646 
    647 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0")
    648 
    649 (defcustom lsp-before-apply-edits-hook nil
    650   "Hooks to run before applying edits."
    651   :type 'hook
    652   :group 'lsp-mode)
    653 
    654 (defgroup lsp-imenu nil
    655   "LSP Imenu."
    656   :group 'lsp-mode
    657   :tag "LSP Imenu")
    658 
    659 (defcustom lsp-imenu-show-container-name t
    660   "Display the symbol's container name in an imenu entry."
    661   :type 'boolean
    662   :group 'lsp-imenu)
    663 
    664 (defcustom lsp-imenu-container-name-separator "/"
    665   "Separator string to use to separate the container name from the symbol while
    666 displaying imenu entries."
    667   :type 'string
    668   :group 'lsp-imenu)
    669 
    670 (defcustom lsp-imenu-sort-methods '(kind name)
    671   "How to sort the imenu items.
    672 
    673 The value is a list of `kind' `name' or `position'.  Priorities
    674 are determined by the index of the element."
    675   :type '(repeat (choice (const name)
    676                          (const position)
    677                          (const kind)))
    678   :group 'lsp-imenu)
    679 
    680 (defcustom lsp-imenu-index-symbol-kinds nil
    681   "Which symbol kinds to show in imenu."
    682   :type '(repeat (choice (const :tag "Miscellaneous" nil)
    683                          (const :tag "File" File)
    684                          (const :tag "Module" Module)
    685                          (const :tag "Namespace" Namespace)
    686                          (const :tag "Package" Package)
    687                          (const :tag "Class" Class)
    688                          (const :tag "Method" Method)
    689                          (const :tag "Property" Property)
    690                          (const :tag "Field" Field)
    691                          (const :tag "Constructor" Constructor)
    692                          (const :tag "Enum" Enum)
    693                          (const :tag "Interface" Interface)
    694                          (const :tag "Function" Function)
    695                          (const :tag "Variable" Variable)
    696                          (const :tag "Constant" Constant)
    697                          (const :tag "String" String)
    698                          (const :tag "Number" Number)
    699                          (const :tag "Boolean" Boolean)
    700                          (const :tag "Array" Array)
    701                          (const :tag "Object" Object)
    702                          (const :tag "Key" Key)
    703                          (const :tag "Null" Null)
    704                          (const :tag "Enum Member" EnumMember)
    705                          (const :tag "Struct" Struct)
    706                          (const :tag "Event" Event)
    707                          (const :tag "Operator" Operator)
    708                          (const :tag "Type Parameter" TypeParameter)))
    709   :group 'lsp-imenu)
    710 
    711 ;; vibhavp: Should we use a lower value (5)?
    712 (defcustom lsp-response-timeout 10
    713   "Number of seconds to wait for a response from the language server before
    714 timing out. Nil if no timeout."
    715   :type '(choice
    716           (number :tag "Seconds")
    717           (const :tag "No timeout" nil))
    718   :group 'lsp-mode)
    719 
    720 (defcustom lsp-tcp-connection-timeout 2
    721   "The timeout for tcp connection in seconds."
    722   :type 'number
    723   :group 'lsp-mode
    724   :package-version '(lsp-mode . "6.2"))
    725 
    726 (defconst lsp--imenu-compare-function-alist
    727   (list (cons 'name #'lsp--imenu-compare-name)
    728         (cons 'kind #'lsp--imenu-compare-kind)
    729         (cons 'position #'lsp--imenu-compare-line-col))
    730   "An alist of (METHOD . FUNCTION).
    731 METHOD is one of the symbols accepted by
    732 `lsp-imenu-sort-methods'.
    733 
    734 FUNCTION takes two hash tables representing DocumentSymbol.  It
    735 returns a negative number, 0, or a positive number indicating
    736 whether the first parameter is less than, equal to, or greater
    737 than the second parameter.")
    738 
    739 (defcustom lsp-diagnostic-clean-after-change nil
    740   "When non-nil, clean the diagnostics on change.
    741 
    742 Note that when that setting is nil, `lsp-mode' will show stale
    743 diagnostics until server publishes the new set of diagnostics"
    744   :type 'boolean
    745   :group 'lsp-diagnostics
    746   :package-version '(lsp-mode . "7.0.1"))
    747 
    748 (defcustom lsp-server-trace nil
    749   "Request tracing on the server side.
    750 The actual trace output at each level depends on the language server in use.
    751 Changes take effect only when a new session is started."
    752   :type '(choice (const :tag "Disabled" "off")
    753                  (const :tag "Messages only" "messages")
    754                  (const :tag "Verbose" "verbose")
    755                  (const :tag "Default (disabled)" nil))
    756   :group 'lsp-mode
    757   :package-version '(lsp-mode . "6.1"))
    758 
    759 (defcustom lsp-auto-touch-files t
    760   "If non-nil ensure the files exist before sending
    761 `textDocument/didOpen' notification."
    762   :type 'boolean
    763   :group 'lsp-mode
    764   :package-version '(lsp-mode . "9.0.0"))
    765 
    766 (defvar lsp-language-id-configuration
    767   '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake")
    768     ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile")
    769     ("\\.astro$" . "astro")
    770     ("\\.cs\\'" . "csharp")
    771     ("\\.css$" . "css")
    772     ("\\.cypher$" . "cypher")
    773     ("Earthfile" . "earthfile")
    774     ("\\.ebuild$" . "shellscript")
    775     ("\\.go\\'" . "go")
    776     ("\\.html$" . "html")
    777     ("\\.hx$" . "haxe")
    778     ("\\.hy$" . "hy")
    779     ("\\.java\\'" . "java")
    780     ("\\.jq$"  . "jq")
    781     ("\\.js$" . "javascript")
    782     ("\\.json$" . "json")
    783     ("\\.jsonc$" . "jsonc")
    784     ("\\.jsonnet$" . "jsonnet")
    785     ("\\.jsx$" . "javascriptreact")
    786     ("\\.lua$" . "lua")
    787     ("\\.mdx\\'" . "mdx")
    788     ("\\.nu$" . "nushell")
    789     ("\\.php$" . "php")
    790     ("\\.ps[dm]?1\\'" . "powershell")
    791     ("\\.rs\\'" . "rust")
    792     ("\\.spec\\'" . "rpm-spec")
    793     ("\\.sql$" . "sql")
    794     ("\\.svelte$" . "svelte")
    795     ("\\.toml\\'" . "toml")
    796     ("\\.ts$" . "typescript")
    797     ("\\.tsx$" . "typescriptreact")
    798     ("\\.ttcn3$" . "ttcn3")
    799     ("\\.vue$" . "vue")
    800     ("\\.xml$" . "xml")
    801     ("\\ya?ml$" . "yaml")
    802     ("^PKGBUILD$" . "shellscript")
    803     ("^go\\.mod\\'" . "go.mod")
    804     ("^settings\\.json$" . "jsonc")
    805     ("^yang\\.settings$" . "jsonc")
    806     ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson")
    807     (ada-mode . "ada")
    808     (ada-ts-mode . "ada")
    809     (gpr-mode . "gpr")
    810     (gpr-ts-mode . "gpr")
    811     (awk-mode . "awk")
    812     (awk-ts-mode . "awk")
    813     (nxml-mode . "xml")
    814     (sql-mode . "sql")
    815     (vimrc-mode . "vim")
    816     (vimscript-ts-mode . "vim")
    817     (sh-mode . "shellscript")
    818     (bash-ts-mode . "shellscript")
    819     (ebuild-mode . "shellscript")
    820     (pkgbuild-mode . "shellscript")
    821     (envrc-file-mode . "shellscript")
    822     (scala-mode . "scala")
    823     (scala-ts-mode . "scala")
    824     (julia-mode . "julia")
    825     (julia-ts-mode . "julia")
    826     (clojure-mode . "clojure")
    827     (clojurec-mode . "clojure")
    828     (clojurescript-mode . "clojurescript")
    829     (clojure-ts-mode . "clojure")
    830     (clojure-ts-clojurec-mode . "clojure")
    831     (clojure-ts-clojurescript-mode . "clojurescript")
    832     (java-mode . "java")
    833     (java-ts-mode . "java")
    834     (jdee-mode . "java")
    835     (groovy-mode . "groovy")
    836     (python-mode . "python")
    837     (python-ts-mode . "python")
    838     (cython-mode . "python")
    839     ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo")
    840     (lsp--render-markdown . "markdown")
    841     (move-mode . "move")
    842     (rust-mode . "rust")
    843     (rust-ts-mode . "rust")
    844     (rustic-mode . "rust")
    845     (kotlin-mode . "kotlin")
    846     (kotlin-ts-mode . "kotlin")
    847     (css-mode . "css")
    848     (css-ts-mode . "css")
    849     (less-mode . "less")
    850     (less-css-mode . "less")
    851     (lua-mode . "lua")
    852     (lua-ts-mode . "lua")
    853     (sass-mode . "sass")
    854     (ssass-mode . "sass")
    855     (scss-mode . "scss")
    856     (scad-mode . "openscad")
    857     (xml-mode . "xml")
    858     (c-mode . "c")
    859     (c-ts-mode . "c")
    860     (c++-mode . "cpp")
    861     (c++-ts-mode . "cpp")
    862     (cuda-mode . "cuda")
    863     (objc-mode . "objective-c")
    864     (html-mode . "html")
    865     (html-ts-mode . "html")
    866     (sgml-mode . "html")
    867     (mhtml-mode . "html")
    868     (mint-mode . "mint")
    869     (go-dot-mod-mode . "go.mod")
    870     (go-mod-ts-mode . "go.mod")
    871     (go-mode . "go")
    872     (go-ts-mode . "go")
    873     (graphql-mode . "graphql")
    874     (haskell-mode . "haskell")
    875     (haskell-ts-mode . "haskell")
    876     (hack-mode . "hack")
    877     (php-mode . "php")
    878     (php-ts-mode . "php")
    879     (powershell-mode . "powershell")
    880     (powershell-mode . "PowerShell")
    881     (powershell-ts-mode . "powershell")
    882     (json-mode . "json")
    883     (json-ts-mode . "json")
    884     (jsonc-mode . "jsonc")
    885     (rjsx-mode . "javascript")
    886     (js2-mode . "javascript")
    887     (js-mode . "javascript")
    888     (js-ts-mode . "javascript")
    889     (typescript-mode . "typescript")
    890     (typescript-ts-mode . "typescript")
    891     (tsx-ts-mode . "typescriptreact")
    892     (svelte-mode . "svelte")
    893     (fsharp-mode . "fsharp")
    894     (reason-mode . "reason")
    895     (caml-mode . "ocaml")
    896     (tuareg-mode . "ocaml")
    897     (futhark-mode . "futhark")
    898     (swift-mode . "swift")
    899     (elixir-mode . "elixir")
    900     (elixir-ts-mode . "elixir")
    901     (heex-ts-mode . "elixir")
    902     (conf-javaprop-mode . "spring-boot-properties")
    903     (yaml-mode . "yaml")
    904     (yaml-ts-mode . "yaml")
    905     (ruby-mode . "ruby")
    906     (enh-ruby-mode . "ruby")
    907     (ruby-ts-mode . "ruby")
    908     (feature-mode . "cucumber")
    909     (fortran-mode . "fortran")
    910     (f90-mode . "fortran")
    911     (elm-mode . "elm")
    912     (dart-mode . "dart")
    913     (erlang-mode . "erlang")
    914     (dockerfile-mode . "dockerfile")
    915     (dockerfile-ts-mode . "dockerfile")
    916     (csharp-mode . "csharp")
    917     (csharp-tree-sitter-mode . "csharp")
    918     (csharp-ts-mode . "csharp")
    919     (plain-tex-mode . "plaintex")
    920     (context-mode . "context")
    921     (cypher-mode . "cypher")
    922     (latex-mode . "latex")
    923     (LaTeX-mode . "latex")
    924     (v-mode . "v")
    925     (vhdl-mode . "vhdl")
    926     (vhdl-ts-mode . "vhdl")
    927     (verilog-mode . "verilog")
    928     (terraform-mode . "terraform")
    929     (ess-julia-mode . "julia")
    930     (ess-r-mode . "r")
    931     (crystal-mode . "crystal")
    932     (nim-mode . "nim")
    933     (dhall-mode . "dhall")
    934     (cmake-mode . "cmake")
    935     (cmake-ts-mode . "cmake")
    936     (purescript-mode . "purescript")
    937     (gdscript-mode . "gdscript")
    938     (gdscript-ts-mode . "gdscript")
    939     (perl-mode . "perl")
    940     (cperl-mode . "perl")
    941     (robot-mode . "robot")
    942     (racket-mode . "racket")
    943     (nix-mode . "nix")
    944     (nix-ts-mode . "nix")
    945     (prolog-mode . "prolog")
    946     (vala-mode . "vala")
    947     (actionscript-mode . "actionscript")
    948     (d-mode . "d")
    949     (zig-mode . "zig")
    950     (text-mode . "plaintext")
    951     (markdown-mode . "markdown")
    952     (gfm-mode . "markdown")
    953     (beancount-mode . "beancount")
    954     (conf-toml-mode . "toml")
    955     (toml-ts-mode . "toml")
    956     (org-mode . "org")
    957     (org-journal-mode . "org")
    958     (nginx-mode . "nginx")
    959     (magik-mode . "magik")
    960     (magik-ts-mode . "magik")
    961     (idris-mode . "idris")
    962     (idris2-mode . "idris2")
    963     (gleam-mode . "gleam")
    964     (gleam-ts-mode . "gleam")
    965     (graphviz-dot-mode . "dot")
    966     (tiltfile-mode . "tiltfile")
    967     (solidity-mode . "solidity")
    968     (bibtex-mode . "bibtex")
    969     (rst-mode . "restructuredtext")
    970     (glsl-mode . "glsl")
    971     (shader-mode . "shaderlab")
    972     (wgsl-mode . "wgsl")
    973     (jq-mode . "jq")
    974     (jq-ts-mode . "jq")
    975     (protobuf-mode . "protobuf")
    976     (nushell-mode . "nushell")
    977     (nushell-ts-mode . "nushell")
    978     (meson-mode . "meson")
    979     (yang-mode . "yang"))
    980   "Language id configuration.")
    981 
    982 (defvar lsp--last-active-workspaces nil
    983   "Keep track of last active workspace.
    984 We want to try the last workspace first when jumping into a library
    985 directory")
    986 
    987 (defvar lsp-method-requirements
    988   '(("textDocument/callHierarchy" :capability :callHierarchyProvider)
    989     ("textDocument/codeAction" :capability :codeActionProvider)
    990     ("codeAction/resolve"
    991      :check-command (lambda (workspace)
    992                       (with-lsp-workspace workspace
    993                         (lsp:code-action-options-resolve-provider?
    994                          (lsp--capability-for-method "textDocument/codeAction")))))
    995     ("textDocument/codeLens" :capability :codeLensProvider)
    996     ("textDocument/completion" :capability :completionProvider)
    997     ("completionItem/resolve"
    998      :check-command (lambda (wk)
    999                       (with-lsp-workspace wk
   1000                         (lsp:completion-options-resolve-provider?
   1001                          (lsp--capability-for-method "textDocument/completion")))))
   1002     ("textDocument/declaration" :capability :declarationProvider)
   1003     ("textDocument/definition" :capability :definitionProvider)
   1004     ("textDocument/documentColor" :capability :colorProvider)
   1005     ("textDocument/documentLink" :capability :documentLinkProvider)
   1006     ("textDocument/inlayHint" :capability :inlayHintProvider)
   1007     ("textDocument/documentHighlight" :capability :documentHighlightProvider)
   1008     ("textDocument/documentSymbol" :capability :documentSymbolProvider)
   1009     ("textDocument/foldingRange" :capability :foldingRangeProvider)
   1010     ("textDocument/formatting" :capability :documentFormattingProvider)
   1011     ("textDocument/hover" :capability :hoverProvider)
   1012     ("textDocument/implementation" :capability :implementationProvider)
   1013     ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider)
   1014     ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider)
   1015     ("textDocument/prepareRename"
   1016      :check-command (lambda (workspace)
   1017                       (with-lsp-workspace workspace
   1018                         (lsp:rename-options-prepare-provider?
   1019                          (lsp--capability-for-method "textDocument/rename")))))
   1020     ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider)
   1021     ("textDocument/references" :capability :referencesProvider)
   1022     ("textDocument/rename" :capability :renameProvider)
   1023     ("textDocument/selectionRange" :capability :selectionRangeProvider)
   1024     ("textDocument/semanticTokens" :capability :semanticTokensProvider)
   1025     ("textDocument/semanticTokensFull"
   1026      :check-command (lambda (workspace)
   1027                       (with-lsp-workspace workspace
   1028                         (lsp-get (lsp--capability :semanticTokensProvider) :full))))
   1029     ("textDocument/semanticTokensFull/Delta"
   1030      :check-command (lambda (workspace)
   1031                       (with-lsp-workspace workspace
   1032                         (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full)))
   1033                           (and (not (booleanp capFull)) (lsp-get capFull :delta))))))
   1034     ("textDocument/semanticTokensRangeProvider"
   1035      :check-command (lambda (workspace)
   1036                       (with-lsp-workspace workspace
   1037                         (lsp-get (lsp--capability :semanticTokensProvider) :range))))
   1038     ("textDocument/signatureHelp" :capability :signatureHelpProvider)
   1039     ("textDocument/typeDefinition" :capability :typeDefinitionProvider)
   1040     ("textDocument/typeHierarchy" :capability :typeHierarchyProvider)
   1041     ("textDocument/diagnostic" :capability :diagnosticProvider)
   1042     ("workspace/executeCommand" :capability :executeCommandProvider)
   1043     ("workspace/symbol" :capability :workspaceSymbolProvider))
   1044 
   1045   "Map methods to requirements.
   1046 It is used by request-sending functions to determine which server
   1047 must be used for handling a particular message.")
   1048 
   1049 (defconst lsp--file-change-type
   1050   `((created . 1)
   1051     (changed . 2)
   1052     (deleted . 3)))
   1053 
   1054 (defconst lsp--watch-kind
   1055   `((create . 1)
   1056     (change . 2)
   1057     (delete . 4)))
   1058 
   1059 (defvar lsp-window-body-width 40
   1060   "Window body width when rendering doc.")
   1061 
   1062 (defface lsp-face-highlight-textual
   1063   '((t :inherit highlight))
   1064   "Face used for textual occurrences of symbols."
   1065   :group 'lsp-mode)
   1066 
   1067 (defface lsp-face-highlight-read
   1068   '((t :inherit highlight :underline t))
   1069   "Face used for highlighting symbols being read."
   1070   :group 'lsp-mode)
   1071 
   1072 (defface lsp-face-highlight-write
   1073   '((t :inherit highlight :weight bold))
   1074   "Face used for highlighting symbols being written to."
   1075   :group 'lsp-mode)
   1076 
   1077 (define-obsolete-variable-alias 'lsp-lens-auto-enable
   1078   'lsp-lens-enable "lsp-mode 7.0.1")
   1079 
   1080 (defcustom lsp-lens-enable t
   1081   "Auto enable lenses if server supports."
   1082   :group 'lsp-lens
   1083   :type 'boolean
   1084   :package-version '(lsp-mode . "6.3"))
   1085 
   1086 (defcustom lsp-symbol-highlighting-skip-current nil
   1087   "If non-nil skip current symbol when setting symbol highlights."
   1088   :group 'lsp-mode
   1089   :type 'boolean)
   1090 
   1091 (defcustom lsp-file-watch-threshold 1000
   1092   "Show warning if the files to watch are more than.
   1093 Set to nil to disable the warning."
   1094   :type 'number
   1095   :group 'lsp-mode)
   1096 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i))))
   1097 
   1098 (defvar lsp-custom-markup-modes
   1099   '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic"))
   1100   "Mode to uses with markdown code blocks.
   1101 They are added to `markdown-code-lang-modes'")
   1102 
   1103 (defcustom lsp-signature-render-documentation t
   1104   "Display signature documentation in `eldoc'."
   1105   :type 'boolean
   1106   :group 'lsp-mode
   1107   :package-version '(lsp-mode . "6.2"))
   1108 
   1109 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request)
   1110   "Auto activate signature conditions."
   1111   :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char)
   1112                          (const :tag "After selected completion." :after-completion)
   1113                          (const :tag "When the server has sent show signature help." :on-server-request)))
   1114   :group 'lsp-mode
   1115   :package-version '(lsp-mode . "6.2"))
   1116 
   1117 (defcustom lsp-signature-doc-lines 20
   1118   "If number, limit the number of lines to show in the docs."
   1119   :type 'number
   1120   :group 'lsp-mode
   1121   :package-version '(lsp-mode . "6.3"))
   1122 
   1123 (defcustom lsp-signature-function 'lsp-lv-message
   1124   "The function used for displaying signature info.
   1125 It will be called with one param - the signature info. When
   1126 called with nil the signature info must be cleared."
   1127   :type 'function
   1128   :group 'lsp-mode
   1129   :package-version '(lsp-mode . "6.3"))
   1130 
   1131 (defcustom lsp-keymap-prefix "s-l"
   1132   "LSP-mode keymap prefix."
   1133   :group 'lsp-mode
   1134   :type 'string
   1135   :package-version '(lsp-mode . "6.3"))
   1136 
   1137 (defvar-local lsp--buffer-workspaces ()
   1138   "List of the buffer workspaces.")
   1139 
   1140 (defvar-local lsp--buffer-deferred nil
   1141   "Whether buffer was loaded via `lsp-deferred'.")
   1142 
   1143 (defvar lsp--session nil
   1144   "Contain the `lsp-session' for the current Emacs instance.")
   1145 
   1146 (defvar lsp--tcp-port 10000)
   1147 
   1148 (defvar lsp--client-packages-required nil
   1149   "If nil, `lsp-client-packages' are yet to be required.")
   1150 
   1151 (defvar lsp--tcp-server-port 0
   1152   "The server socket which is opened when using `lsp-tcp-server' (a server
   1153 socket is opened in Emacs and the language server connects to it).  The
   1154 default value of 0 ensures that a random high port is used. Set it to a positive
   1155 integer to use a specific port.")
   1156 
   1157 (defvar lsp--tcp-server-wait-seconds 10
   1158   "Wait this amount of time for the client to connect to our server socket
   1159 when using `lsp-tcp-server'.")
   1160 
   1161 (defvar-local lsp--document-symbols nil
   1162   "The latest document symbols.")
   1163 
   1164 (defvar-local lsp--document-selection-range-cache nil
   1165   "The document selection cache.")
   1166 
   1167 (defvar-local lsp--document-symbols-request-async nil
   1168   "If non-nil, request document symbols asynchronously.")
   1169 
   1170 (defvar-local lsp--document-symbols-tick -1
   1171   "The value of `buffer-chars-modified-tick' when document
   1172   symbols were last retrieved.")
   1173 
   1174 (defvar-local lsp--have-document-highlights nil
   1175   "Set to `t' on symbol highlighting, cleared on
   1176 `lsp--cleanup-highlights-if-needed'. Checking a separately
   1177 defined flag is substantially faster than unconditionally
   1178 calling `remove-overlays'.")
   1179 
   1180 ;; Buffer local variable for storing number of lines.
   1181 (defvar lsp--log-lines)
   1182 
   1183 (defvar-local lsp--eldoc-saved-message nil)
   1184 
   1185 (defvar lsp--on-change-timer nil)
   1186 (defvar lsp--on-idle-timer nil)
   1187 
   1188 (defvar-local lsp--signature-last nil)
   1189 (defvar-local lsp--signature-last-index nil)
   1190 (defvar lsp--signature-last-buffer nil)
   1191 
   1192 (defvar-local lsp--virtual-buffer-point-max nil)
   1193 
   1194 (cl-defmethod lsp-execute-command (_server _command _arguments)
   1195   "Ask SERVER to execute COMMAND with ARGUMENTS.")
   1196 
   1197 (defun lsp-elt (sequence n)
   1198   "Return Nth element of SEQUENCE or nil if N is out of range."
   1199   (cond
   1200    ((listp sequence) (elt sequence n))
   1201    ((arrayp sequence)
   1202     (and (> (length sequence) n) (aref sequence n)))
   1203    (t (and (> (length sequence) n) (elt sequence n)))))
   1204 
   1205 ;; define seq-first and seq-rest for older emacs
   1206 (defun lsp-seq-first (sequence)
   1207   "Return the first element of SEQUENCE."
   1208   (lsp-elt sequence 0))
   1209 
   1210 (defun lsp-seq-rest (sequence)
   1211   "Return a sequence of the elements of SEQUENCE except the first one."
   1212   (seq-drop sequence 1))
   1213 
   1214 ;;;###autoload
   1215 (defun lsp--string-listp (sequence)
   1216   "Return t if all elements of SEQUENCE are strings, else nil."
   1217   (not (seq-find (lambda (x) (not (stringp x))) sequence)))
   1218 
   1219 (defun lsp--string-vector-p (candidate)
   1220   "Returns true if CANDIDATE is a vector data structure and
   1221 every element of it is of type string, else nil."
   1222   (and
   1223    (vectorp candidate)
   1224    (seq-every-p #'stringp candidate)))
   1225 
   1226 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0")
   1227 
   1228 (defun lsp--editable-vector-match (widget value)
   1229   "Function for `lsp-editable-vector' :match."
   1230   ;; Value must be a list or a vector and all the members must match the type.
   1231   (and (or (listp value) (vectorp value))
   1232        (length (cdr (lsp--editable-vector-match-inline widget value)))))
   1233 
   1234 (defun lsp--editable-vector-match-inline (widget value)
   1235   "Value for `lsp-editable-vector' :match-inline."
   1236   (let ((type (nth 0 (widget-get widget :args)))
   1237         (ok t)
   1238         found)
   1239     (while (and value ok)
   1240       (let ((answer (widget-match-inline type value)))
   1241         (if answer
   1242             (let ((head (if (vectorp answer) (aref answer 0) (car answer)))
   1243                   (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer))))
   1244               (setq found (append found head)
   1245                     value tail))
   1246           (setq ok nil))))
   1247     (cons found value)))
   1248 
   1249 (defun lsp--editable-vector-value-to-external (_widget internal-value)
   1250   "Convert the internal list value to a vector."
   1251   (if (listp internal-value)
   1252       (apply 'vector internal-value)
   1253     internal-value))
   1254 
   1255 (defun lsp--editable-vector-value-to-internal (_widget external-value)
   1256   "Convert the external vector value to a list."
   1257   (if (vectorp external-value)
   1258       (append external-value nil)
   1259     external-value))
   1260 
   1261 (define-widget 'lsp--editable-vector 'editable-list
   1262   "A subclass of `editable-list' that accepts and returns a
   1263 vector instead of a list."
   1264   :value-to-external 'lsp--editable-vector-value-to-external
   1265   :value-to-internal 'lsp--editable-vector-value-to-internal
   1266   :match 'lsp--editable-vector-match
   1267   :match-inline 'lsp--editable-vector-match-inline)
   1268 
   1269 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector
   1270   "A variable length homogeneous vector."
   1271   :tag "Repeat"
   1272   :format "%{%t%}:\n%v%i\n")
   1273 
   1274 (define-widget 'lsp-string-vector 'lazy
   1275   "A vector of zero or more elements, every element of which is a string.
   1276 Appropriate for any language-specific `defcustom' that needs to
   1277 serialize as a JSON array of strings.
   1278 
   1279 Deprecated. Use `lsp-repeatable-vector' instead. "
   1280   :offset 4
   1281   :tag "Vector"
   1282   :type '(lsp-repeatable-vector string))
   1283 
   1284 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0")
   1285 
   1286 (defvar lsp--show-message t
   1287   "If non-nil, show debug message from `lsp-mode'.")
   1288 
   1289 (defun lsp--message  (format &rest args)
   1290   "Wrapper for `message'
   1291 
   1292 We `inhibit-message' the message when the cursor is in the
   1293 minibuffer and when emacs version is before emacs 27 due to the
   1294 fact that we often use `lsp--info', `lsp--warn' and `lsp--error'
   1295 in async context and the call to these function is removing the
   1296 minibuffer prompt. The issue with async messages is already fixed
   1297 in emacs 27.
   1298 
   1299 See #2049"
   1300   (when lsp--show-message
   1301     (let ((inhibit-message (or inhibit-message
   1302                                (and (minibufferp)
   1303                                     (version< emacs-version "27.0")))))
   1304       (apply #'message format args))))
   1305 
   1306 (defun lsp--info (format &rest args)
   1307   "Display lsp info message with FORMAT with ARGS."
   1308   (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args)))
   1309 
   1310 (defun lsp--warn (format &rest args)
   1311   "Display lsp warn message with FORMAT with ARGS."
   1312   (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args)))
   1313 
   1314 (defun lsp--error (format &rest args)
   1315   "Display lsp error message with FORMAT with ARGS."
   1316   (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args)))
   1317 
   1318 (defun lsp-log (format &rest args)
   1319   "Log message to the ’*lsp-log*’ buffer.
   1320 
   1321 FORMAT and ARGS i the same as for `message'."
   1322   (when lsp-log-max
   1323     (let ((log-buffer (get-buffer "*lsp-log*"))
   1324           (inhibit-read-only t))
   1325       (unless log-buffer
   1326         (setq log-buffer (get-buffer-create "*lsp-log*"))
   1327         (with-current-buffer log-buffer
   1328           (buffer-disable-undo)
   1329           (view-mode 1)
   1330           (set (make-local-variable 'lsp--log-lines) 0)))
   1331       (with-current-buffer log-buffer
   1332         (save-excursion
   1333           (let* ((message (apply 'format format args))
   1334                  ;; Count newlines in message.
   1335                  (newlines (1+ (cl-loop with start = 0
   1336                                         for count from 0
   1337                                         while (string-match "\n" message start)
   1338                                         do (setq start (match-end 0))
   1339                                         finally return count))))
   1340             (goto-char (point-max))
   1341 
   1342             ;; in case the buffer is not empty insert before last \n to preserve
   1343             ;; the point position(in case it is in the end)
   1344             (if (eq (point) (point-min))
   1345                 (progn
   1346                   (insert "\n")
   1347                   (backward-char))
   1348               (backward-char)
   1349               (insert "\n"))
   1350             (insert message)
   1351 
   1352             (setq lsp--log-lines (+ lsp--log-lines newlines))
   1353 
   1354             (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max))
   1355               (let ((to-delete (- lsp--log-lines lsp-log-max)))
   1356                 (goto-char (point-min))
   1357                 (forward-line to-delete)
   1358                 (delete-region (point-min) (point))
   1359                 (setq lsp--log-lines lsp-log-max)))))))))
   1360 
   1361 (defalias 'lsp-message 'lsp-log)
   1362 
   1363 (defalias 'lsp-ht 'ht)
   1364 
   1365 (defalias 'lsp-file-local-name 'file-local-name)
   1366 
   1367 (defun lsp-f-canonical (file-name)
   1368   "Return the canonical FILE-NAME, without a trailing slash."
   1369   (directory-file-name (expand-file-name file-name)))
   1370 
   1371 (defalias 'lsp-canonical-file-name 'lsp-f-canonical)
   1372 
   1373 (defun lsp-f-same? (path-a path-b)
   1374   "Return t if PATH-A and PATH-B are references to the same file.
   1375 Symlinks are not followed."
   1376   (when (and (f-exists? path-a)
   1377              (f-exists? path-b))
   1378     (equal
   1379      (lsp-f-canonical (directory-file-name (f-expand path-a)))
   1380      (lsp-f-canonical (directory-file-name (f-expand path-b))))))
   1381 
   1382 (defun lsp-f-parent (path)
   1383   "Return the parent directory to PATH.
   1384 Symlinks are not followed."
   1385   (let ((parent (file-name-directory
   1386                  (directory-file-name (f-expand path default-directory)))))
   1387     (unless (lsp-f-same? path parent)
   1388       (if (f-relative? path)
   1389           (f-relative parent)
   1390         (directory-file-name parent)))))
   1391 
   1392 (defun lsp-f-ancestor-of? (path-a path-b)
   1393   "Return t if PATH-A is an ancestor of PATH-B.
   1394 Symlinks are not followed."
   1395   (unless (lsp-f-same? path-a path-b)
   1396     (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator))
   1397                (lsp-f-canonical path-b))))
   1398 
   1399 (defun lsp--merge-results (results method)
   1400   "Merge RESULTS by filtering the empty hash-tables and merging
   1401 the lists according to METHOD."
   1402   (pcase (--map (if (vectorp it)
   1403                     (append it nil) it)
   1404                 (-filter #'identity results))
   1405     (`() ())
   1406     ;; only one result - simply return it
   1407     (`(,fst) fst)
   1408     ;; multiple results merge it based on strategy
   1409     (results
   1410      (pcase method
   1411        ("textDocument/hover" (pcase (seq-filter
   1412                                      (-compose #'not #'lsp-empty?)
   1413                                      results)
   1414                                (`(,hover) hover)
   1415                                (hovers (lsp-make-hover
   1416                                         :contents
   1417                                         (-mapcat
   1418                                          (-lambda ((&Hover :contents))
   1419                                            (if (and (sequencep contents)
   1420                                                     (not (stringp contents)))
   1421                                                (append contents ())
   1422                                              (list contents)))
   1423                                          hovers)))))
   1424        ("textDocument/completion"
   1425         (lsp-make-completion-list
   1426          :is-incomplete (seq-some
   1427                          #'lsp:completion-list-is-incomplete
   1428                          results)
   1429          :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it)
   1430                                                     (lsp:completion-list-items it)
   1431                                                   it)
   1432                                                 nil))
   1433                            results)))
   1434        ("completionItem/resolve"
   1435         (let ((item (cl-first results)))
   1436           (when-let ((details (seq-filter #'identity
   1437                                           (seq-map #'lsp:completion-item-detail? results))))
   1438             (lsp:set-completion-item-detail?
   1439              item
   1440              (string-join details " ")))
   1441           (when-let ((docs (seq-filter #'identity
   1442                                        (seq-map #'lsp:completion-item-documentation? results))))
   1443             (lsp:set-completion-item-documentation?
   1444              item
   1445              (lsp-make-markup-content
   1446               :kind (or (seq-some (lambda (it)
   1447                                     (when (equal (lsp:markup-content-kind it)
   1448                                                  lsp/markup-kind-markdown)
   1449                                       lsp/markup-kind-markdown))
   1450                                   docs)
   1451                         lsp/markup-kind-plain-text)
   1452               :value (string-join (seq-map (lambda (doc)
   1453                                              (or (lsp:markup-content-value doc)
   1454                                                  (and (stringp doc) doc)))
   1455                                            docs)
   1456                                   "\n"))))
   1457           (when-let ((edits (seq-filter #'identity
   1458                                         (seq-map #'lsp:completion-item-additional-text-edits? results))))
   1459             (lsp:set-completion-item-additional-text-edits?
   1460              item
   1461              (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits)))
   1462           item))
   1463        (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results))))))
   1464 
   1465 (defun lsp--spinner-start ()
   1466   "Start spinner indication."
   1467   (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error)))
   1468 
   1469 (defun lsp--propertize (str type)
   1470   "Propertize STR as per TYPE."
   1471   (propertize str 'face (alist-get type lsp--message-type-face)))
   1472 
   1473 (defun lsp-workspaces ()
   1474   "Return the lsp workspaces associated with the current project."
   1475   (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces))
   1476 
   1477 (defun lsp--completing-read (prompt collection transform-fn &optional predicate
   1478                                     require-match initial-input
   1479                                     hist def inherit-input-method)
   1480   "Wrap `completing-read' to provide transformation function and disable sort.
   1481 
   1482 TRANSFORM-FN will be used to transform each of the items before displaying.
   1483 
   1484 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF
   1485 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes."
   1486   (let* ((col (--map (cons (funcall transform-fn it) it) collection))
   1487          (completion (completing-read prompt
   1488                                       (lambda (string pred action)
   1489                                         (if (eq action 'metadata)
   1490                                             `(metadata (display-sort-function . identity))
   1491                                           (complete-with-action action col string pred)))
   1492                                       predicate require-match initial-input hist
   1493                                       def inherit-input-method)))
   1494     (cdr (assoc completion col))))
   1495 
   1496 (defconst lsp--system-arch (lambda ()
   1497                              (setq lsp--system-arch
   1498                                    (pcase system-type
   1499                                      ('windows-nt
   1500                                       (pcase system-configuration
   1501                                         ((rx bol "x86_64-") 'x64)
   1502                                         (_ 'x86)))
   1503                                      ('darwin
   1504                                       (pcase system-configuration
   1505                                         ((rx "aarch64-") 'arm64)
   1506                                         (_ 'x64)))
   1507                                      ('gnu/linux
   1508                                        (pcase system-configuration
   1509                                          ((rx bol "aarch64-") 'arm64)
   1510                                          ((rx bol "x86_64") 'x64)
   1511                                          ((rx bol (| "i386" "i886")) 'x32)))
   1512                                      (_
   1513                                       (pcase system-configuration
   1514                                         ((rx bol "x86_64") 'x64)
   1515                                         ((rx bol (| "i386" "i886")) 'x32))))))
   1516   "Return the system architecture of `Emacs'.
   1517 Special values:
   1518   `x64'       64bit
   1519   `x32'       32bit
   1520   `arm64'     ARM 64bit")
   1521 
   1522 (defmacro lsp-with-current-buffer (buffer-id &rest body)
   1523   (declare (indent 1) (debug t))
   1524   `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer)))
   1525        (with-lsp-workspaces (plist-get ,buffer-id :workspaces)
   1526          (funcall wcb (lambda () ,@body)))
   1527      (with-current-buffer ,buffer-id
   1528        ,@body)))
   1529 
   1530 (defvar lsp--throw-on-input nil
   1531   "Make `lsp-*-while-no-input' throws `input' on interrupted.")
   1532 
   1533 (defmacro lsp--catch (tag bodyform &rest handlers)
   1534   "Catch TAG thrown in BODYFORM.
   1535 The return value from TAG will be handled in HANDLERS by `pcase'."
   1536   (declare (debug (form form &rest (pcase-PAT body))) (indent 2))
   1537   (let ((re-sym (make-symbol "re")))
   1538     `(let ((,re-sym (catch ,tag ,bodyform)))
   1539        (pcase ,re-sym
   1540          ,@handlers))))
   1541 
   1542 (defmacro lsp--while-no-input (&rest body)
   1543   "Wrap BODY in `while-no-input' and respecting `non-essential'.
   1544 If `lsp--throw-on-input' is set, will throw if input is pending, else
   1545 return value of `body' or nil if interrupted."
   1546   (declare (debug t) (indent 0))
   1547   `(if non-essential
   1548        (let ((res (while-no-input ,@body)))
   1549          (cond
   1550           ((and lsp--throw-on-input (equal res t))
   1551            (throw 'input :interrupted))
   1552           ((booleanp res) nil)
   1553           (t res)))
   1554      ,@body))
   1555 
   1556 ;; A ‘lsp--client’ object describes the client-side behavior of a language
   1557 ;; server.  It is used to start individual server processes, each of which is
   1558 ;; represented by a ‘lsp--workspace’ object.  Client objects are normally
   1559 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’.  Each
   1560 ;; workspace refers to exactly one client, but there can be multiple workspaces
   1561 ;; for a single client.
   1562 (cl-defstruct lsp--client
   1563   ;; ‘language-id’ is a function that receives a buffer as a single argument
   1564   ;; and should return the language identifier for that buffer.  See
   1565   ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem
   1566   ;; for a list of language identifiers.  Also consult the documentation for
   1567   ;; the language server represented by this client to find out what language
   1568   ;; identifiers it supports or expects.
   1569   (language-id nil)
   1570 
   1571   ;; ‘add-on?’ when set to t the server will be started no matter whether there
   1572   ;; is another server handling the same mode.
   1573   (add-on? nil)
   1574   ;; ‘new-connection’ is a function that should start a language server process
   1575   ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS).
   1576   ;; COMMAND-PROCESS must be a process object representing the server process
   1577   ;; just started.  COMMUNICATION-PROCESS must be a process (including pipe and
   1578   ;; network processes) that ‘lsp-mode’ uses to communicate with the language
   1579   ;; server using the language server protocol.  COMMAND-PROCESS and
   1580   ;; COMMUNICATION-PROCESS may be the same process; in that case
   1581   ;; ‘new-connection’ may also return that process as a single
   1582   ;; object. ‘new-connection’ is called with two arguments, FILTER and
   1583   ;; SENTINEL.  FILTER should be used as process filter for
   1584   ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for
   1585   ;; COMMAND-PROCESS.
   1586   (new-connection nil)
   1587 
   1588   ;; ‘ignore-regexps’ is a list of regexps.  When a data packet from the
   1589   ;; language server matches any of these regexps, it will be ignored.  This is
   1590   ;; intended for dealing with language servers that output non-protocol data.
   1591   (ignore-regexps nil)
   1592 
   1593   ;; ‘ignore-messages’ is a list of regexps.  When a message from the language
   1594   ;; server matches any of these regexps, it will be ignored.  This is useful
   1595   ;; for filtering out unwanted messages; such as servers that send nonstandard
   1596   ;; message types, or extraneous log messages.
   1597   (ignore-messages nil)
   1598 
   1599   ;; ‘notification-handlers’ is a hash table mapping notification method names
   1600   ;; (strings) to functions handling the respective notifications.  Upon
   1601   ;; receiving a notification, ‘lsp-mode’ will call the associated handler
   1602   ;; function passing two arguments, the ‘lsp--workspace’ object and the
   1603   ;; deserialized notification parameters.
   1604   (notification-handlers (make-hash-table :test 'equal))
   1605 
   1606   ;; ‘request-handlers’ is a hash table mapping request method names
   1607   ;; (strings) to functions handling the respective notifications.  Upon
   1608   ;; receiving a request, ‘lsp-mode’ will call the associated handler function
   1609   ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized
   1610   ;; request parameters.
   1611   (request-handlers (make-hash-table :test 'equal))
   1612 
   1613   ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request
   1614   ;; identifiers for pending asynchronous requests to functions handling the
   1615   ;; respective responses.  Upon receiving a response from the language server,
   1616   ;; ‘lsp-mode’ will call the associated response handler function with a
   1617   ;; single argument, the deserialized response parameters.
   1618   (response-handlers (make-hash-table :test 'eql))
   1619 
   1620   ;; ‘prefix-function’ is called for getting the prefix for completion.
   1621   ;; The function takes no parameter and returns a cons (start . end) representing
   1622   ;; the start and end bounds of the prefix. If it's not set, the client uses a
   1623   ;; default prefix function."
   1624   (prefix-function nil)
   1625 
   1626   ;; Contains mapping of scheme to the function that is going to be used to load
   1627   ;; the file.
   1628   (uri-handlers (make-hash-table :test #'equal))
   1629 
   1630   ;; ‘action-handlers’ is a hash table mapping action to a handler function. It
   1631   ;; can be used in `lsp-execute-code-action' to determine whether the action
   1632   ;; current client is interested in executing the action instead of sending it
   1633   ;; to the server.
   1634   (action-handlers (make-hash-table :test 'equal))
   1635 
   1636   ;; `action-filter' can be set to a function that modifies any incoming
   1637   ;; `CodeAction' in place before it is executed. The return value is ignored.
   1638   ;; This can be used to patch up broken code action requests before they are
   1639   ;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an
   1640   ;; example of a function that can be useful here.
   1641   (action-filter nil)
   1642 
   1643   ;; major modes supported by the client.
   1644   major-modes
   1645   ;; Function that will be called to decide if this language client
   1646   ;; should manage a particular buffer. The function will be passed
   1647   ;; the file name and major mode to inform the decision. Setting
   1648   ;; `activation-fn' will override `major-modes', if
   1649   ;; present.
   1650   activation-fn
   1651   ;; Break the tie when major-mode is supported by multiple clients.
   1652   (priority 0)
   1653   ;; Unique identifier for representing the client object.
   1654   server-id
   1655   ;; defines whether the client supports multi root workspaces.
   1656   multi-root
   1657   ;; Initialization options or a function that returns initialization options.
   1658   initialization-options
   1659   ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or
   1660   ;; completely replace, the faces used for semantic highlighting on a
   1661   ;; client-by-client basis.
   1662   ;;
   1663   ;; It recognizes four members, all of which are optional: `:types’ and
   1664   ;; `:modifiers’, respectively, should be face definition lists akin to
   1665   ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be
   1666   ;; merged with the default face definition list.
   1667   ;;
   1668   ;; Alternatively, if the plist members `:discard-default-types’ or
   1669   ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers'
   1670   ;; face definitions will be replaced entirely by their respective overrides.
   1671   ;;
   1672   ;; For example, setting `:semantic-tokens-faces-overrides' to
   1673   ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from
   1674   ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'.
   1675   ;;
   1676   ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))'
   1677   ;; will also remap "macro", but on top of that associate the fictional token type
   1678   ;; "not-quite-a-macro" with the face named `some-face'.
   1679   ;;
   1680   ;; `(:types (("macro" . font-lock-keyword-face))
   1681   ;;   :modifiers (("declaration" . lsp-face-semhl-interface))
   1682   ;;   :discard-default-types t
   1683   ;;   :discard-default-modifiers t)'
   1684   ;; will discard all default face definitions, hence leaving the client with
   1685   ;; only one token type "macro", mapped to `font-lock-keyword-face', and one
   1686   ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'.
   1687   semantic-tokens-faces-overrides
   1688   ;; Provides support for registering LSP Server specific capabilities.
   1689   custom-capabilities
   1690   ;; Function which returns the folders that are considered to be not projects but library files.
   1691   ;; The function accepts one parameter currently active workspace.
   1692   ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225.
   1693   library-folders-fn
   1694   ;; function which will be called when opening file in the workspace to perform
   1695   ;; client specific initialization. The function accepts one parameter
   1696   ;; currently active workspace.
   1697   before-file-open-fn
   1698   ;; Function which will be called right after a workspace has been initialized.
   1699   initialized-fn
   1700   ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP.
   1701   (remote? nil)
   1702 
   1703   ;; ‘completion-in-comments?’ t if the client supports completion in comments.
   1704   (completion-in-comments? nil)
   1705 
   1706   ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client.
   1707   (path->uri-fn nil)
   1708 
   1709   ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client.
   1710   (uri->path-fn nil)
   1711   ;; Function that returns an environment structure that will be used
   1712   ;; to set some environment variables when starting the language
   1713   ;; server process. These environment variables enable some
   1714   ;; additional features in the language server. The environment
   1715   ;; structure is an alist of the form (KEY . VALUE), where KEY is a
   1716   ;; string (regularly in all caps), and VALUE may be a string, a
   1717   ;; boolean, or a sequence of strings.
   1718   environment-fn
   1719 
   1720   ;; ‘after-open-fn’ workspace after open specific hooks.
   1721   (after-open-fn nil)
   1722 
   1723   ;; ‘async-request-handlers’ is a hash table mapping request method names
   1724   ;; (strings) to functions handling the respective requests that may take
   1725   ;; time to finish.  Upon receiving a request, ‘lsp-mode’ will call the
   1726   ;; associated handler function passing three arguments, the ‘lsp--workspace’
   1727   ;; object, the deserialized request parameters and the callback which accept
   1728   ;; result as its parameter.
   1729   (async-request-handlers (make-hash-table :test 'equal))
   1730   download-server-fn
   1731   download-in-progress?
   1732   buffers
   1733   synchronize-sections)
   1734 
   1735 (defun lsp-clients-executable-find (find-command &rest args)
   1736   "Finds an executable by invoking a search command.
   1737 
   1738 FIND-COMMAND is the executable finder that searches for the
   1739 actual language server executable. ARGS is a list of arguments to
   1740 give to FIND-COMMAND to find the language server.  Returns the
   1741 output of FIND-COMMAND if it exits successfully, nil otherwise.
   1742 
   1743 Typical uses include finding an executable by invoking `find' in
   1744 a project, finding LLVM commands on macOS with `xcrun', or
   1745 looking up project-specific language servers for projects written
   1746 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv'
   1747 etc."
   1748   (when-let* ((find-command-path (executable-find find-command))
   1749               (executable-path
   1750                (with-temp-buffer
   1751                  (when (zerop (apply 'call-process find-command-path nil t nil args))
   1752                    (buffer-substring-no-properties (point-min) (point-max))))))
   1753     (string-trim executable-path)))
   1754 
   1755 (defvar lsp--already-widened nil)
   1756 
   1757 (defmacro lsp-save-restriction-and-excursion (&rest form)
   1758   (declare (indent 0) (debug t))
   1759   `(if lsp--already-widened
   1760        (save-excursion ,@form)
   1761      (-let [lsp--already-widened t]
   1762        (save-restriction
   1763          (widen)
   1764          (save-excursion ,@form)))))
   1765 
   1766 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number
   1767 (defun lsp--line-character-to-point (line character)
   1768   "Return the point for character CHARACTER on line LINE."
   1769   (or (lsp-virtual-buffer-call :line/character->point line character)
   1770       (let ((inhibit-field-text-motion t))
   1771         (lsp-save-restriction-and-excursion
   1772           (goto-char (point-min))
   1773           (forward-line line)
   1774           ;; server may send character position beyond the current line and we
   1775           ;; should fallback to line end.
   1776           (-let [line-end (line-end-position)]
   1777             (if (> character (- line-end (point)))
   1778                 line-end
   1779               (forward-char character)
   1780               (point)))))))
   1781 
   1782 (lsp-defun lsp--position-to-point ((&Position :line :character))
   1783   "Convert `Position' object in PARAMS to a point."
   1784   (lsp--line-character-to-point line character))
   1785 
   1786 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end))
   1787   (cons start end))
   1788 
   1789 (lsp-defun lsp--range-text ((&RangeToPoint :start :end))
   1790   (buffer-substring start end))
   1791 
   1792 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end)))
   1793   (cond
   1794    ((and
   1795      (region-active-p)
   1796      (<= start (region-beginning) end)
   1797      (<= start (region-end) end)
   1798      (or (not (= start (region-beginning)))
   1799          (not (= end (region-end)))))
   1800     (cons start end))
   1801    ((and (<= start (point) end)
   1802          (not (region-active-p)))
   1803     (cons start end))
   1804    (parent? (lsp--find-wrapping-range parent?))))
   1805 
   1806 (defun lsp--get-selection-range ()
   1807   (or
   1808    (-when-let ((cache . cache-tick) lsp--document-selection-range-cache)
   1809      (when (= cache-tick (buffer-modified-tick)) cache))
   1810    (let ((response (cl-first
   1811                     (lsp-request
   1812                      "textDocument/selectionRange"
   1813                      (list :textDocument (lsp--text-document-identifier)
   1814                            :positions (vector (lsp--cur-position)))))))
   1815      (setq lsp--document-selection-range-cache
   1816            (cons response (buffer-modified-tick)))
   1817      response)))
   1818 
   1819 (defun lsp-extend-selection ()
   1820   "Extend selection."
   1821   (interactive)
   1822   (unless (lsp-feature? "textDocument/selectionRange")
   1823     (signal 'lsp-capability-not-supported (list "selectionRangeProvider")))
   1824   (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range)))
   1825     (goto-char start)
   1826     (set-mark (point))
   1827     (goto-char end)
   1828     (exchange-point-and-mark)))
   1829 
   1830 (defun lsp-warn (message &rest args)
   1831   "Display a warning message made from (`format-message' MESSAGE ARGS...).
   1832 This is equivalent to `display-warning', using `lsp-mode' as the type and
   1833 `:warning' as the level."
   1834   (display-warning 'lsp-mode (apply #'format-message message args)))
   1835 
   1836 (defun lsp--get-uri-handler (scheme)
   1837   "Get uri handler for SCHEME in the current workspace."
   1838   (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it)))
   1839           (or (lsp-workspaces) (lsp--session-workspaces (lsp-session)))))
   1840 
   1841 (defun lsp--fix-path-casing (path)
   1842   "On windows, downcases path because the windows file system is
   1843 case-insensitive.
   1844 
   1845 On other systems, returns path without change."
   1846   (if (eq system-type 'windows-nt) (downcase path) path))
   1847 
   1848 (defun lsp--uri-to-path (uri)
   1849   "Convert URI to a file path."
   1850   (if-let ((fn (->> (lsp-workspaces)
   1851                     (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client))
   1852                     (cl-first))))
   1853       (funcall fn uri)
   1854     (lsp--uri-to-path-1 uri)))
   1855 
   1856 (defun lsp-remap-path-if-needed (file-name)
   1857   (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings))
   1858       (propertize (buffer-local-value 'buffer-file-name buffer)
   1859                   'lsp-virtual-buffer virtual-buffer)
   1860     file-name))
   1861 
   1862 (defun lsp--uri-to-path-1 (uri)
   1863   "Convert URI to a file path."
   1864   (let* ((url (url-generic-parse-url (url-unhex-string uri)))
   1865          (type (url-type url))
   1866          (target (url-target url))
   1867          (file
   1868           (concat (decode-coding-string (url-filename url)
   1869                                         (or locale-coding-system 'utf-8))
   1870                   (when (and target
   1871                              (not (s-match
   1872                                    (rx "#" (group (1+ num)) (or "," "#")
   1873                                        (group (1+ num))
   1874                                        string-end)
   1875                                    uri)))
   1876                     (concat "#" target))))
   1877          (file-name (if (and type (not (string= type "file")))
   1878                         (if-let ((handler (lsp--get-uri-handler type)))
   1879                             (funcall handler uri)
   1880                           uri)
   1881                       ;; `url-generic-parse-url' is buggy on windows:
   1882                       ;; https://github.com/emacs-lsp/lsp-mode/pull/265
   1883                       (or (and (eq system-type 'windows-nt)
   1884                                (eq (elt file 0) ?\/)
   1885                                (substring file 1))
   1886                           file))))
   1887     (->> file-name
   1888          (concat (-some #'lsp--workspace-host-root (lsp-workspaces)))
   1889          (lsp-remap-path-if-needed))))
   1890 
   1891 (defun lsp--buffer-uri ()
   1892   "Return URI of the current buffer."
   1893   (or lsp-buffer-uri
   1894       (plist-get lsp--virtual-buffer :buffer-uri)
   1895       (lsp--path-to-uri
   1896        (or (buffer-file-name) (buffer-file-name (buffer-base-buffer))))))
   1897 
   1898 (defun lsp-register-client-capabilities (&rest _args)
   1899   "Implemented only to make `company-lsp' happy.
   1900 DELETE when `lsp-mode.el' is deleted.")
   1901 
   1902 (defconst lsp--url-path-allowed-chars
   1903   (url--allowed-chars (append '(?/) url-unreserved-chars))
   1904   "`url-unreserved-chars' with additional delim ?/.
   1905 This set of allowed chars is enough for hexifying local file paths.")
   1906 
   1907 (defun lsp--path-to-uri-1 (path)
   1908   (concat lsp--uri-file-prefix
   1909           (--> path
   1910             (expand-file-name it)
   1911             (or (file-remote-p it 'localname t) it)
   1912             (url-hexify-string it lsp--url-path-allowed-chars))))
   1913 
   1914 (defun lsp--path-to-uri (path)
   1915   "Convert PATH to a uri."
   1916   (if-let ((uri-fn (->> (lsp-workspaces)
   1917                         (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client))
   1918                         (cl-first))))
   1919       (funcall uri-fn path)
   1920     (lsp--path-to-uri-1 path)))
   1921 
   1922 (defun lsp--string-match-any (regex-list str)
   1923   "Return the first regex, if any, within REGEX-LIST matching STR."
   1924   (--first (string-match it str) regex-list))
   1925 
   1926 (cl-defstruct lsp-watch
   1927   (descriptors (make-hash-table :test 'equal))
   1928   root-directory)
   1929 
   1930 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories)
   1931   (let ((file-name (cl-third event))
   1932         (event-type (cl-second event)))
   1933     (cond
   1934      ((and (file-directory-p file-name)
   1935            (equal 'created event-type)
   1936            (not (lsp--string-match-any ignored-directories file-name)))
   1937 
   1938       (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch)
   1939 
   1940       ;; process the files that are already present in
   1941       ;; the directory.
   1942       (->> (directory-files-recursively file-name ".*" t)
   1943            (seq-do (lambda (f)
   1944                      (unless (file-directory-p f)
   1945                        (funcall callback (list nil 'created f)))))))
   1946      ((and (memq event-type '(created deleted changed))
   1947            (not (file-directory-p file-name))
   1948            (not (lsp--string-match-any ignored-files file-name)))
   1949       (funcall callback event))
   1950      ((and (memq event-type '(renamed))
   1951            (not (file-directory-p file-name))
   1952            (not (lsp--string-match-any ignored-files file-name)))
   1953       (funcall callback `(,(cl-first event) deleted ,(cl-third event)))
   1954       (funcall callback `(,(cl-first event) created ,(cl-fourth event)))))))
   1955 
   1956 (defun lsp--ask-about-watching-big-repo (number-of-directories dir)
   1957   "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR.
   1958 This is useful when there is a lot of files in a repository, as
   1959 that may slow Emacs down. Returns t if the user wants to watch
   1960 the entire repository, nil otherwise."
   1961   (prog1
   1962       (yes-or-no-p
   1963        (format
   1964         "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down.
   1965 Do you want to watch all files in %s? "
   1966         dir
   1967         number-of-directories
   1968         dir))
   1969     (lsp--info
   1970      (concat "You can configure this warning with the `lsp-enable-file-watchers' "
   1971              "and `lsp-file-watch-threshold' variables"))))
   1972 
   1973 
   1974 (defun lsp--path-is-watchable-directory (path dir ignored-directories)
   1975   "Figure out whether PATH (inside of DIR) is meant to have a file watcher set.
   1976 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't
   1977 want to watch."
   1978   (let
   1979       ((full-path (f-join dir path)))
   1980     (and (file-accessible-directory-p full-path)
   1981          (not (equal path "."))
   1982          (not (equal path ".."))
   1983          (not (lsp--string-match-any ignored-directories full-path)))))
   1984 
   1985 
   1986 (defun lsp--all-watchable-directories (dir ignored-directories)
   1987   "Traverse DIR recursively returning a list of paths that should have watchers.
   1988 IGNORED-DIRECTORIES will be used for exclusions"
   1989   (let* ((dir (if (f-symlink? dir)
   1990                   (file-truename dir)
   1991                 dir)))
   1992     (apply #'nconc
   1993            ;; the directory itself is assumed to be part of the set
   1994            (list dir)
   1995            ;; collect all subdirectories that are watchable
   1996            (-map
   1997             (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories))
   1998             ;; but only look at subdirectories that are watchable
   1999             (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories))
   2000                      (directory-files dir))))))
   2001 
   2002 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?)
   2003   "Create recursive file notification watch in DIR.
   2004 CALLBACK will be called when there are changes in any of
   2005 the monitored files. WATCHES is a hash table directory->file
   2006 notification handle which contains all of the watch that
   2007 already have been created. Watches will not be created for
   2008 any directory that matches any regex in IGNORED-DIRECTORIES.
   2009 Watches will not be created for any file that matches any
   2010 regex in IGNORED-FILES."
   2011   (let* ((dir (if (f-symlink? dir)
   2012                   (file-truename dir)
   2013                 dir))
   2014          (watch (or watch (make-lsp-watch :root-directory dir)))
   2015          (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories)))
   2016     (lsp-log "Creating watchers for following %s folders:\n  %s"
   2017              (length dirs-to-watch)
   2018              (s-join "\n  " dirs-to-watch))
   2019     (when (or
   2020            (not warn-big-repo?)
   2021            (not lsp-file-watch-threshold)
   2022            (let ((number-of-directories (length dirs-to-watch)))
   2023              (or
   2024               (< number-of-directories lsp-file-watch-threshold)
   2025               (condition-case nil
   2026                   (lsp--ask-about-watching-big-repo number-of-directories dir)
   2027                 (quit)))))
   2028       (dolist (current-dir dirs-to-watch)
   2029         (condition-case err
   2030             (progn
   2031               (puthash
   2032                current-dir
   2033                (file-notify-add-watch current-dir
   2034                                       '(change)
   2035                                       (lambda (event)
   2036                                         (lsp--folder-watch-callback event callback watch ignored-files ignored-directories)))
   2037                (lsp-watch-descriptors watch)))
   2038           (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))
   2039           (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err))))))
   2040     watch))
   2041 
   2042 (defun lsp-kill-watch (watch)
   2043   "Delete WATCH."
   2044   (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch))
   2045   (ht-clear! (lsp-watch-descriptors watch)))
   2046 
   2047 (defun lsp-json-bool (val)
   2048   "Convert VAL to JSON boolean."
   2049   (if val t :json-false))
   2050 
   2051 (defmacro with-lsp-workspace (workspace &rest body)
   2052   "Helper macro for invoking BODY in WORKSPACE context."
   2053   (declare (debug (form body))
   2054            (indent 1))
   2055   `(let ((lsp--cur-workspace ,workspace)) ,@body))
   2056 
   2057 (defmacro with-lsp-workspaces (workspaces &rest body)
   2058   "Helper macro for invoking BODY against multiple WORKSPACES."
   2059   (declare (debug (form body))
   2060            (indent 1))
   2061   `(let ((lsp--buffer-workspaces ,workspaces)) ,@body))
   2062 
   2063 
   2064 
   2065 (defmacro lsp-consistency-check (package)
   2066   `(defconst ,(intern (concat (symbol-name package)
   2067                               "-plist-value-when-compiled"))
   2068      (eval-when-compile lsp-use-plists)))
   2069 
   2070 
   2071 ;; loading code-workspace files
   2072 
   2073 ;;;###autoload
   2074 (defun lsp-load-vscode-workspace (file)
   2075   "Load vscode workspace from FILE"
   2076   (interactive "fSelect file to import: ")
   2077   (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session)))
   2078 
   2079   (let ((dir (f-dirname file)))
   2080     (->> file
   2081          (json-read-file)
   2082          (alist-get 'folders)
   2083          (-map (-lambda ((&alist 'path))
   2084                  (lsp-workspace-folders-add (expand-file-name path dir)))))))
   2085 
   2086 ;;;###autoload
   2087 (defun lsp-save-vscode-workspace (file)
   2088   "Save vscode workspace to FILE"
   2089   (interactive "FSelect file to save to: ")
   2090 
   2091   (let ((json-encoding-pretty-print t))
   2092     (f-write-text (json-encode
   2093                    `((folders . ,(->> (lsp-session)
   2094                                       (lsp-session-folders)
   2095                                       (--map `((path . ,it)))))))
   2096                   'utf-8
   2097                   file)))
   2098 
   2099 
   2100 (defmacro lsp-foreach-workspace (&rest body)
   2101   "Execute BODY for each of the current workspaces."
   2102   (declare (debug (form body)))
   2103   `(--map (with-lsp-workspace it ,@body) (lsp-workspaces)))
   2104 
   2105 (defmacro when-lsp-workspace (workspace &rest body)
   2106   "Helper macro for invoking BODY in WORKSPACE context if present."
   2107   (declare (debug (form body))
   2108            (indent 1))
   2109   `(when-let ((lsp--cur-workspace ,workspace)) ,@body))
   2110 
   2111 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items))
   2112   (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read))
   2113             (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label))
   2114                                  items))
   2115             (result (funcall-interactively
   2116                      selectfunc
   2117                      (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels))
   2118             (choices (if (listp result)
   2119                          (if (equal result '("*"))
   2120                              itemLabels
   2121                            result)
   2122                        (list result))))
   2123       (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data))
   2124                                                  (if (member label choices)
   2125                                                      (lsp-make-quick-pick-item :label label :picked t :user-data user-data)
   2126                                                    nil))
   2127                                                items)))))
   2128 
   2129 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?))
   2130   (read-string (format "%s: " prompt) (or value? "")))
   2131 
   2132 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type))
   2133   "Send the server's messages to log.
   2134 PARAMS - the data sent from _WORKSPACE."
   2135   (funcall (cl-case type
   2136              (1 'lsp--error)
   2137              (2 'lsp--warn)
   2138              (t 'lsp--info))
   2139            "%s"
   2140            message))
   2141 
   2142 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type))
   2143   "Send the server's messages to log.
   2144 PARAMS - the data sent from WORKSPACE."
   2145   (ignore
   2146    (let ((client (lsp--workspace-client workspace)))
   2147      (when (or (not client)
   2148                (cl-notany (-rpartial #'string-match-p message)
   2149                           (lsp--client-ignore-messages client)))
   2150        (lsp-log "%s" (lsp--propertize message type))))))
   2151 
   2152 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?))
   2153   "Display a message request to user sending the user selection back to server."
   2154   (let* ((message (lsp--propertize message type))
   2155          (choices (seq-map #'lsp:message-action-item-title actions?)))
   2156     (if choices
   2157         (completing-read (concat message " ") (seq-into choices 'list) nil t)
   2158       (lsp-log message))))
   2159 
   2160 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?))
   2161   "Show document URI in a buffer and go to SELECTION if any."
   2162   (let ((path (lsp--uri-to-path uri)))
   2163     (when (f-exists? path)
   2164       (with-current-buffer (find-file path)
   2165         (when selection?
   2166           (goto-char (lsp--position-to-point (lsp:range-start selection?))))
   2167         t))))
   2168 
   2169 (defcustom lsp-progress-prefix "⌛ "
   2170   "Progress prefix."
   2171   :group 'lsp-mode
   2172   :type 'string
   2173   :package-version '(lsp-mode . "8.0.0"))
   2174 
   2175 (defcustom lsp-progress-function #'lsp-on-progress-modeline
   2176   "Function for handling the progress notifications."
   2177   :group 'lsp-mode
   2178   :type '(choice
   2179           (const :tag "Use modeline" lsp-on-progress-modeline)
   2180           (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')"
   2181                  lsp-on-progress-legacy)
   2182           (const :tag "Ignore" ignore)
   2183           (function :tag "Other function"))
   2184   :package-version '(lsp-mode . "8.0.0"))
   2185 
   2186 (defcustom lsp-request-while-no-input-may-block nil
   2187   "Have `lsp-request-while-no-input` block unless `non-essential` is t."
   2188   :group 'lsp-mode
   2189   :type 'boolean)
   2190 
   2191 (defun lsp--progress-status ()
   2192   "Returns the status of the progress for the current workspaces."
   2193   (-let ((progress-status
   2194           (s-join
   2195            "|"
   2196            (-keep
   2197             (lambda (workspace)
   2198               (let ((tokens (lsp--workspace-work-done-tokens workspace)))
   2199                 (unless (ht-empty? tokens)
   2200                   (mapconcat
   2201                    (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?))
   2202                      (concat (if percentage?
   2203                                  (if (numberp percentage?)
   2204                                      (format "%.0f%%%% " percentage?)
   2205                                    (format "%s%%%% " percentage?))
   2206                                "")
   2207                              (or message? title)))
   2208                    (ht-values tokens)
   2209                    "|"))))
   2210             (lsp-workspaces)))))
   2211     (unless (s-blank? progress-status)
   2212       (concat lsp-progress-prefix progress-status " "))))
   2213 
   2214 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value
   2215                                                                 (value &as &WorkDoneProgress :kind)))
   2216   "PARAMS contains the progress data.
   2217 WORKSPACE is the workspace that contains the progress token."
   2218   (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status))))
   2219   (pcase kind
   2220     ("begin" (lsp-workspace-set-work-done-token token value workspace))
   2221     ("report" (lsp-workspace-set-work-done-token token value workspace))
   2222     ("end" (lsp-workspace-rem-work-done-token token workspace)))
   2223   (force-mode-line-update))
   2224 
   2225 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value
   2226                                                               (value &as &WorkDoneProgress :kind)))
   2227   "PARAMS contains the progress data.
   2228 WORKSPACE is the workspace that contains the progress token."
   2229   (pcase kind
   2230     ("begin"
   2231      (-let* (((&WorkDoneProgressBegin :title :percentage?) value)
   2232              (reporter
   2233               (if lsp-progress-via-spinner
   2234                   (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types))
   2235                          ;; Set message as a tooltip for the spinner strings
   2236                          (propertized-strings
   2237                           (seq-map (lambda (string) (propertize string 'help-echo title))
   2238                                    spinner-strings))
   2239                          (spinner-type (vconcat propertized-strings)))
   2240                     ;; The progress relates to the server as a whole,
   2241                     ;; display it on all buffers.
   2242                     (mapcar (lambda (buffer)
   2243                               (lsp-with-current-buffer buffer
   2244                                 (spinner-start spinner-type))
   2245                               buffer)
   2246                             (lsp--workspace-buffers workspace)))
   2247                 (if percentage?
   2248                     (make-progress-reporter title 0 100 percentage?)
   2249                   ;; No percentage, just progress
   2250                   (make-progress-reporter title nil nil)))))
   2251        (lsp-workspace-set-work-done-token token reporter workspace)))
   2252     ("report"
   2253      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2254        (unless lsp-progress-via-spinner
   2255          (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value)))))
   2256 
   2257     ("end"
   2258      (when-let ((reporter (lsp-workspace-get-work-done-token token workspace)))
   2259        (if lsp-progress-via-spinner
   2260            (mapc (lambda (buffer)
   2261                    (when (lsp-buffer-live-p buffer)
   2262                      (lsp-with-current-buffer buffer
   2263                        (spinner-stop))))
   2264                  reporter)
   2265          (progress-reporter-done reporter))
   2266        (lsp-workspace-rem-work-done-token token workspace)))))
   2267 
   2268 
   2269 ;; diagnostics
   2270 
   2271 (defvar lsp-diagnostic-filter nil
   2272   "A a function which will be called with
   2273   `&PublishDiagnosticsParams' and `workspace' which can be used
   2274   to filter out the diagnostics. The function should return
   2275   `&PublishDiagnosticsParams'.
   2276 
   2277 Common usecase are:
   2278 1. Filter the diagnostics for a particular language server.
   2279 2. Filter out the diagnostics under specific level.")
   2280 
   2281 (defvar lsp-diagnostic-stats (ht))
   2282 
   2283 (defun lsp-diagnostics (&optional current-workspace?)
   2284   "Return the diagnostics from all workspaces."
   2285   (or (pcase (if current-workspace?
   2286                  (lsp-workspaces)
   2287                (lsp--session-workspaces (lsp-session)))
   2288         (`() ())
   2289         (`(,workspace) (lsp--workspace-diagnostics workspace))
   2290         (`,workspaces (let ((result (make-hash-table :test 'equal)))
   2291                         (mapc (lambda (workspace)
   2292                                 (->> workspace
   2293                                      (lsp--workspace-diagnostics)
   2294                                      (maphash (lambda (file-name diagnostics)
   2295                                                 (puthash file-name
   2296                                                          (append (gethash file-name result) diagnostics)
   2297                                                          result)))))
   2298                               workspaces)
   2299                         result)))
   2300       (ht)))
   2301 
   2302 (defun lsp-diagnostics-stats-for (path)
   2303   "Get diagnostics statistics for PATH.
   2304 The result format is vector [_ errors warnings infos hints] or nil."
   2305   (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats))
   2306 
   2307 (defun lsp-diagnostics--request-pull-diagnostics (workspace)
   2308   "Request new diagnostics for the current file within WORKSPACE.
   2309 This is only executed if the server supports pull diagnostics."
   2310   (when (lsp-feature? "textDocument/diagnostic")
   2311     (let ((path (lsp--fix-path-casing (buffer-file-name))))
   2312       (lsp-request-async "textDocument/diagnostic"
   2313                          (list :textDocument (lsp--text-document-identifier))
   2314                          (-lambda ((&DocumentDiagnosticReport :kind :items?))
   2315                            (lsp-diagnostics--apply-pull-diagnostics workspace path kind items?))
   2316                          :mode 'tick))))
   2317 
   2318 (defun lsp-diagnostics--update-path (path new-stats)
   2319   (let ((new-stats (copy-sequence new-stats))
   2320         (path (lsp--fix-path-casing (directory-file-name path))))
   2321     (if-let ((old-data (gethash path lsp-diagnostic-stats)))
   2322         (dotimes (idx 5)
   2323           (cl-callf + (aref old-data idx)
   2324             (aref new-stats idx)))
   2325       (puthash path new-stats lsp-diagnostic-stats))))
   2326 
   2327 (defun lsp-diagnostics--convert-and-update-path-stats (workspace path diagnostics)
   2328   (let ((path (lsp--fix-path-casing path))
   2329         (new-stats (make-vector 5 0)))
   2330     (mapc (-lambda ((&Diagnostic :severity?))
   2331             (cl-incf (aref new-stats (or severity? 1))))
   2332           diagnostics)
   2333     (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace))))
   2334       (mapc (-lambda ((&Diagnostic :severity?))
   2335               (cl-decf (aref new-stats (or severity? 1))))
   2336             old-diags))
   2337     (lsp-diagnostics--update-path path new-stats)
   2338     (while (not (string= path (setf path (file-name-directory
   2339                                           (directory-file-name path)))))
   2340       (lsp-diagnostics--update-path path new-stats))))
   2341 
   2342 (lsp-defun lsp--on-diagnostics-update-stats (workspace
   2343                                              (&PublishDiagnosticsParams :uri :diagnostics))
   2344   (lsp-diagnostics--convert-and-update-path-stats workspace (lsp--uri-to-path uri) diagnostics))
   2345 
   2346 (defun lsp-diagnostics--apply-pull-diagnostics (workspace path kind diagnostics?)
   2347   "Update WORKSPACE diagnostics at PATH with DIAGNOSTICS?.
   2348 Depends on KIND being a \\='full\\=' update."
   2349   (cond
   2350    ((equal kind "full")
   2351     ;; TODO support `lsp-diagnostic-filter'
   2352     ;; (the params types differ from the published diagnostics response)
   2353     (lsp-diagnostics--convert-and-update-path-stats workspace path diagnostics?)
   2354     (-let* ((lsp--virtual-buffer-mappings (ht))
   2355             (workspace-diagnostics (lsp--workspace-diagnostics workspace)))
   2356       (if (seq-empty-p diagnostics?)
   2357           (remhash path workspace-diagnostics)
   2358         (puthash path (append diagnostics? nil) workspace-diagnostics))
   2359       (run-hooks 'lsp-diagnostics-updated-hook)))
   2360     ((equal kind "unchanged") t)
   2361     (t (lsp--error "Unknown pull diagnostic result kind '%s'" kind))))
   2362 
   2363 (defun lsp--on-diagnostics (workspace params)
   2364   "Callback for textDocument/publishDiagnostics.
   2365 interface PublishDiagnosticsParams {
   2366     uri: string;
   2367     diagnostics: Diagnostic[];
   2368 }
   2369 PARAMS contains the diagnostics data.
   2370 WORKSPACE is the workspace that contains the diagnostics."
   2371   (when lsp-diagnostic-filter
   2372     (setf params (funcall lsp-diagnostic-filter params workspace)))
   2373 
   2374   (lsp--on-diagnostics-update-stats workspace params)
   2375 
   2376   (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params)
   2377           (lsp--virtual-buffer-mappings (ht))
   2378           (file (lsp--fix-path-casing (lsp--uri-to-path uri)))
   2379           (workspace-diagnostics (lsp--workspace-diagnostics workspace)))
   2380 
   2381     (if (seq-empty-p diagnostics)
   2382         (remhash file workspace-diagnostics)
   2383       (puthash file (append diagnostics nil) workspace-diagnostics))
   2384 
   2385     (run-hooks 'lsp-diagnostics-updated-hook)))
   2386 
   2387 (defun lsp-diagnostics--workspace-cleanup (workspace)
   2388   (->> workspace
   2389        (lsp--workspace-diagnostics)
   2390        (maphash (lambda (key _)
   2391                   (lsp--on-diagnostics-update-stats
   2392                    workspace
   2393                    (lsp-make-publish-diagnostics-params
   2394                     :uri (lsp--path-to-uri key)
   2395                     :diagnostics [])))))
   2396   (clrhash (lsp--workspace-diagnostics workspace)))
   2397 
   2398 
   2399 
   2400 ;; textDocument/foldingRange support
   2401 
   2402 (cl-defstruct lsp--folding-range beg end kind children)
   2403 
   2404 (defvar-local lsp--cached-folding-ranges nil)
   2405 (defvar-local lsp--cached-nested-folding-ranges nil)
   2406 
   2407 (defun lsp--folding-range-width (range)
   2408   (- (lsp--folding-range-end range)
   2409      (lsp--folding-range-beg range)))
   2410 
   2411 (defun lsp--get-folding-ranges ()
   2412   "Get the folding ranges for the current buffer."
   2413   (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges))
   2414     (let* ((ranges (lsp-request "textDocument/foldingRange"
   2415                                 `(:textDocument ,(lsp--text-document-identifier))))
   2416            (sorted-line-col-pairs (->> ranges
   2417                                        (cl-mapcan (-lambda ((&FoldingRange :start-line
   2418                                                                            :start-character?
   2419                                                                            :end-line
   2420                                                                            :end-character?))
   2421                                                     (list (cons start-line start-character?)
   2422                                                           (cons end-line end-character?))))
   2423                                        (-sort #'lsp--line-col-comparator)))
   2424            (line-col-to-point-map (lsp--convert-line-col-to-points-batch
   2425                                    sorted-line-col-pairs)))
   2426       (setq lsp--cached-folding-ranges
   2427             (cons (buffer-chars-modified-tick)
   2428                   (--> ranges
   2429                     (seq-map (-lambda ((range &as
   2430                                               &FoldingRange :start-line
   2431                                               :start-character?
   2432                                               :end-line
   2433                                               :end-character?
   2434                                               :kind?))
   2435                                (make-lsp--folding-range
   2436                                 :beg (ht-get line-col-to-point-map
   2437                                              (cons start-line start-character?))
   2438                                 :end (ht-get line-col-to-point-map
   2439                                              (cons end-line end-character?))
   2440                                 :kind kind?))
   2441                              it)
   2442                     (seq-filter (lambda (folding-range)
   2443                                   (< (lsp--folding-range-beg folding-range)
   2444                                      (lsp--folding-range-end folding-range)))
   2445                                 it)
   2446                     (seq-into it 'list)
   2447                     (delete-dups it))))))
   2448   (cdr lsp--cached-folding-ranges))
   2449 
   2450 (defun lsp--get-nested-folding-ranges ()
   2451   "Get a list of nested folding ranges for the current buffer."
   2452   (-let [(tick . _) lsp--cached-folding-ranges]
   2453     (if (and (eq tick (buffer-chars-modified-tick))
   2454              lsp--cached-nested-folding-ranges)
   2455         lsp--cached-nested-folding-ranges
   2456       (setq lsp--cached-nested-folding-ranges
   2457             (lsp--folding-range-build-trees (lsp--get-folding-ranges))))))
   2458 
   2459 (defun lsp--folding-range-build-trees (ranges)
   2460   (setq ranges (seq-sort #'lsp--range-before-p ranges))
   2461   (let* ((dummy-node (make-lsp--folding-range
   2462                       :beg most-negative-fixnum
   2463                       :end most-positive-fixnum))
   2464          (stack (list dummy-node)))
   2465     (dolist (range ranges)
   2466       (while (not (lsp--range-inside-p range (car stack)))
   2467         (pop stack))
   2468       (push range (lsp--folding-range-children (car stack)))
   2469       (push range stack))
   2470     (lsp--folding-range-children dummy-node)))
   2471 
   2472 (defun lsp--range-inside-p (r1 r2)
   2473   "Return non-nil if folding range R1 lies inside R2"
   2474   (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2))
   2475        (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2))))
   2476 
   2477 (defun lsp--range-before-p (r1 r2)
   2478   "Return non-nil if folding range R1 ends before R2"
   2479   ;; Ensure r1 comes before r2
   2480   (or (< (lsp--folding-range-beg r1)
   2481          (lsp--folding-range-beg r2))
   2482       ;; If beg(r1) == beg(r2) make sure r2 ends first
   2483       (and (= (lsp--folding-range-beg r1)
   2484               (lsp--folding-range-beg r2))
   2485            (< (lsp--folding-range-end r2)
   2486               (lsp--folding-range-end r1)))))
   2487 
   2488 (defun lsp--point-inside-range-p (point range)
   2489   "Return non-nil if POINT lies inside folding range RANGE."
   2490   (and (>= point (lsp--folding-range-beg range))
   2491        (<= point (lsp--folding-range-end range))))
   2492 
   2493 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point)))
   2494   "Return the innermost folding range POINT lies in."
   2495   (seq-reduce (lambda (innermost-range curr-range)
   2496                 (if (and (lsp--point-inside-range-p point curr-range)
   2497                          (or (null innermost-range)
   2498                              (lsp--range-inside-p curr-range innermost-range)))
   2499                     curr-range
   2500                   innermost-range))
   2501               (lsp--get-folding-ranges)
   2502               nil))
   2503 
   2504 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point)))
   2505   "Return the outermost folding range POINT lies in."
   2506   (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range)
   2507                      (let ((curr-width (lsp--folding-range-width curr-range)))
   2508                        (if (and (lsp--point-inside-range-p point curr-range)
   2509                                 (or (null best-pair)
   2510                                     (> curr-width outermost-width)))
   2511                            (cons curr-width curr-range)
   2512                          best-pair)))
   2513                    (lsp--get-folding-ranges)
   2514                    nil)))
   2515 
   2516 (defun lsp--folding-range-at-point-bounds ()
   2517   (when (and lsp-enable-folding
   2518              (lsp-feature? "textDocument/foldingRange"))
   2519     (if-let ((range (lsp--get-current-innermost-folding-range)))
   2520         (cons (lsp--folding-range-beg range)
   2521               (lsp--folding-range-end range)))))
   2522 (put 'lsp--folding-range 'bounds-of-thing-at-point
   2523      #'lsp--folding-range-at-point-bounds)
   2524 
   2525 (defun lsp--get-nearest-folding-range (&optional backward)
   2526   (let ((point (point))
   2527         (found nil))
   2528     (while (not
   2529             (or found
   2530                 (if backward
   2531                     (<= point (point-min))
   2532                   (>= point (point-max)))))
   2533       (if backward (cl-decf point) (cl-incf point))
   2534       (setq found (lsp--get-current-innermost-folding-range point)))
   2535     found))
   2536 
   2537 (defun lsp--folding-range-at-point-forward-op (n)
   2538   (when (and lsp-enable-folding
   2539              (not (zerop n))
   2540              (lsp-feature? "textDocument/foldingRange"))
   2541     (cl-block break
   2542       (dotimes (_ (abs n))
   2543         (if-let ((range (lsp--get-nearest-folding-range (< n 0))))
   2544             (goto-char (if (< n 0)
   2545                            (lsp--folding-range-beg range)
   2546                          (lsp--folding-range-end range)))
   2547           (cl-return-from break))))))
   2548 (put 'lsp--folding-range 'forward-op
   2549      #'lsp--folding-range-at-point-forward-op)
   2550 
   2551 (defun lsp--folding-range-at-point-beginning-op ()
   2552   (goto-char (car (lsp--folding-range-at-point-bounds))))
   2553 (put 'lsp--folding-range 'beginning-op
   2554      #'lsp--folding-range-at-point-beginning-op)
   2555 
   2556 (defun lsp--folding-range-at-point-end-op ()
   2557   (goto-char (cdr (lsp--folding-range-at-point-bounds))))
   2558 (put 'lsp--folding-range 'end-op
   2559      #'lsp--folding-range-at-point-end-op)
   2560 
   2561 (defun lsp--range-at-point-bounds ()
   2562   (or (lsp--folding-range-at-point-bounds)
   2563       (when-let ((range (and
   2564                          (lsp-feature? "textDocument/hover")
   2565                          (->> (lsp--text-document-position-params)
   2566                               (lsp-request "textDocument/hover")
   2567                               (lsp:hover-range?)))))
   2568         (lsp--range-to-region range))))
   2569 
   2570 ;; A more general purpose "thing", useful for applications like focus.el
   2571 (put 'lsp--range 'bounds-of-thing-at-point
   2572      #'lsp--range-at-point-bounds)
   2573 
   2574 (defun lsp--log-io-p (method)
   2575   "Return non nil if should log for METHOD."
   2576   (and lsp-log-io
   2577        (or (not lsp-log-io-allowlist-methods)
   2578            (member method lsp-log-io-allowlist-methods))))
   2579 
   2580 
   2581 ;; toggles
   2582 
   2583 (defun lsp-toggle-trace-io ()
   2584   "Toggle client-server protocol logging."
   2585   (interactive)
   2586   (setq lsp-log-io (not lsp-log-io))
   2587   (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled")))
   2588 
   2589 (defun lsp-toggle-signature-auto-activate ()
   2590   "Toggle signature auto activate."
   2591   (interactive)
   2592   (setq lsp-signature-auto-activate
   2593         (unless lsp-signature-auto-activate '(:on-trigger-char)))
   2594   (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled"))
   2595   (lsp--update-signature-help-hook))
   2596 
   2597 (defun lsp-toggle-on-type-formatting ()
   2598   "Toggle on type formatting."
   2599   (interactive)
   2600   (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting))
   2601   (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled"))
   2602   (lsp--update-on-type-formatting-hook))
   2603 
   2604 (defun lsp-toggle-symbol-highlight ()
   2605   "Toggle symbol highlighting."
   2606   (interactive)
   2607   (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting))
   2608 
   2609   (cond
   2610    ((and lsp-enable-symbol-highlighting
   2611          (lsp-feature? "textDocument/documentHighlight"))
   2612     (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)
   2613     (lsp--info "Symbol highlighting enabled in current buffer."))
   2614    ((not lsp-enable-symbol-highlighting)
   2615     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   2616     (lsp--remove-overlays 'lsp-highlight)
   2617     (lsp--info "Symbol highlighting disabled in current buffer."))))
   2618 
   2619 
   2620 ;; keybindings
   2621 (defvar lsp--binding-descriptions nil
   2622   "List of key binding/short description pair.")
   2623 
   2624 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings)
   2625   "In KEYMAP, define key sequence KEY as DEF conditionally.
   2626 This is like `define-key', except the definition disappears
   2627 whenever COND evaluates to nil.
   2628 DESC is the short-description for the binding.
   2629 BINDINGS is a list of (key def desc cond)."
   2630   (declare (indent defun)
   2631            (debug (form form form form form &rest sexp)))
   2632   (->> (cl-list* key def desc cond bindings)
   2633        (-partition 4)
   2634        (-mapcat (-lambda ((key def desc cond))
   2635                   `((define-key ,keymap ,key
   2636                       '(menu-item
   2637                         ,(format "maybe-%s" def)
   2638                         ,def
   2639                         :filter
   2640                         (lambda (item)
   2641                           (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer)
   2642                                                            lsp--describe-buffer)
   2643                                                          (current-buffer))
   2644                                   ,cond)
   2645                             item))))
   2646                     (when (stringp ,key)
   2647                       (setq lsp--binding-descriptions
   2648                             (append lsp--binding-descriptions '(,key ,desc)))))))
   2649        macroexp-progn))
   2650 
   2651 (defvar lsp--describe-buffer nil)
   2652 
   2653 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus)
   2654   (let ((lsp--describe-buffer buffer))
   2655     (funcall fn buffer prefix menus)))
   2656 
   2657 (advice-add 'describe-buffer-bindings
   2658             :around
   2659             #'lsp-describe-buffer-bindings-advice)
   2660 
   2661 (defun lsp--prepend-prefix (mappings)
   2662   (->> mappings
   2663        (-partition 2)
   2664        (-mapcat (-lambda ((key description))
   2665                   (list (concat lsp-keymap-prefix " " key)
   2666                         description)))))
   2667 
   2668 (defvar lsp-command-map
   2669   (-doto (make-sparse-keymap)
   2670     (lsp-define-conditional-key
   2671       ;; workspaces
   2672       "wD" lsp-disconnect "disconnect" (lsp-workspaces)
   2673       "wd" lsp-describe-session "describe session" t
   2674       "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces)
   2675       "wr" lsp-workspace-restart "restart server" (lsp-workspaces)
   2676       "ws" lsp "start server" t
   2677 
   2678       ;; formatting
   2679       "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting")
   2680                                                  (lsp-feature? "textDocument/formatting"))
   2681       "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting")
   2682 
   2683       ;; folders
   2684       "Fa" lsp-workspace-folders-add "add folder" t
   2685       "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t
   2686       "Fr" lsp-workspace-folders-remove "remove folder" t
   2687 
   2688       ;; toggles
   2689       "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature?
   2690                                                                         "textDocument/publishDiagnostics")
   2691       "TL" lsp-toggle-trace-io "toggle log io" t
   2692       "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline)
   2693       "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs)
   2694       "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature?
   2695                                                                           "textDocument/codeAction")
   2696       "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature?
   2697                                                                "textDocument/documentSymbol")
   2698       "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc)
   2699       "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature?
   2700                                                                       "textDocument/onTypeFormatting")
   2701       "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight")
   2702       "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens")
   2703       "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp")
   2704 
   2705       ;; goto
   2706       "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol")
   2707       "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration")
   2708       "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list)
   2709       "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition")
   2710       "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls")
   2711                                                              (fboundp 'lsp-treemacs-call-hierarchy))
   2712       "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation")
   2713       "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references")
   2714       "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition")
   2715 
   2716       ;; help
   2717       "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc)
   2718                                                   (lsp-feature? "textDocument/hover"))
   2719       "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover")
   2720       "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp")
   2721 
   2722       ;; refactoring
   2723       "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction")
   2724       "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename")
   2725 
   2726       ;; actions
   2727       "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction")
   2728       "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight")
   2729       "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy))
   2730 
   2731       ;; peeks
   2732       "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition")
   2733                                                                 (fboundp 'lsp-ui-peek-find-definitions))
   2734       "Gi" lsp-ui-peek-find-implementation "peek implementations" (and
   2735                                                                    (fboundp 'lsp-ui-peek-find-implementation)
   2736                                                                    (lsp-feature? "textDocument/implementation"))
   2737       "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references)
   2738                                                               (lsp-feature? "textDocument/references"))
   2739       "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp
   2740                                                                            'lsp-ui-peek-find-workspace-symbol)
   2741                                                                           (lsp-feature? "workspace/symbol")))))
   2742 
   2743 
   2744 ;; which-key integration
   2745 
   2746 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key")
   2747 (declare-function which-key-add-key-based-replacements "ext:which-key")
   2748 
   2749 (defun lsp-enable-which-key-integration (&optional all-modes)
   2750   "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current
   2751 active `major-mode', or for all major modes when ALL-MODES is t."
   2752   (cl-flet ((which-key-fn (if all-modes
   2753                               'which-key-add-key-based-replacements
   2754                             (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode))))
   2755     (apply
   2756      #'which-key-fn
   2757      (lsp--prepend-prefix
   2758       (cl-list*
   2759        ""    "lsp"
   2760        "w"   "workspaces"
   2761        "F"   "folders"
   2762        "="   "formatting"
   2763        "T"   "toggle"
   2764        "g"   "goto"
   2765        "h"   "help"
   2766        "r"   "refactor"
   2767        "a"   "code actions"
   2768        "G"   "peek"
   2769        lsp--binding-descriptions)))))
   2770 
   2771 
   2772 ;; Globbing syntax
   2773 
   2774 ;; We port VSCode's glob-to-regexp code
   2775 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts)
   2776 ;; since the LSP globbing syntax seems to be the same as that of
   2777 ;; VSCode.
   2778 
   2779 (defconst lsp-globstar "**"
   2780   "Globstar pattern.")
   2781 
   2782 (defconst lsp-glob-split ?/
   2783   "The character by which we split path components in a glob
   2784 pattern.")
   2785 
   2786 (defconst lsp-path-regexp "[/\\\\]"
   2787   "Forward or backslash to be used as a path separator in
   2788 computed regexps.")
   2789 
   2790 (defconst lsp-non-path-regexp "[^/\\\\]"
   2791   "A regexp matching anything other than a slash.")
   2792 
   2793 (defconst lsp-globstar-regexp
   2794   (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?"
   2795           lsp-path-regexp
   2796           lsp-non-path-regexp lsp-path-regexp
   2797           lsp-path-regexp lsp-non-path-regexp)
   2798   "Globstar in regexp form.")
   2799 
   2800 (defun lsp-split-glob-pattern (pattern split-char)
   2801   "Split PATTERN at SPLIT-CHAR while respecting braces and brackets."
   2802   (when pattern
   2803     (let ((segments nil)
   2804           (in-braces nil)
   2805           (in-brackets nil)
   2806           (current-segment ""))
   2807       (dolist (char (string-to-list pattern))
   2808         (cl-block 'exit-point
   2809           (if (eq char split-char)
   2810               (when (and (null in-braces)
   2811                          (null in-brackets))
   2812                 (push current-segment segments)
   2813                 (setq current-segment "")
   2814                 (cl-return-from 'exit-point))
   2815             (pcase char
   2816               (?{
   2817                (setq in-braces t))
   2818               (?}
   2819                (setq in-braces nil))
   2820               (?\[
   2821                (setq in-brackets t))
   2822               (?\]
   2823                (setq in-brackets nil))))
   2824           (setq current-segment (concat current-segment
   2825                                         (char-to-string char)))))
   2826       (unless (string-empty-p current-segment)
   2827         (push current-segment segments))
   2828       (nreverse segments))))
   2829 
   2830 (defun lsp--glob-to-regexp (pattern)
   2831   "Helper function to convert a PATTERN from LSP's glob syntax to
   2832 an Elisp regexp."
   2833   (if (string-empty-p pattern)
   2834       ""
   2835     (let ((current-regexp "")
   2836           (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split)))
   2837       (if (-all? (lambda (segment) (eq segment lsp-globstar))
   2838                  glob-segments)
   2839           ".*"
   2840         (let ((prev-segment-was-globstar nil))
   2841           (seq-do-indexed
   2842            (lambda (segment index)
   2843              (if (string-equal segment lsp-globstar)
   2844                  (unless prev-segment-was-globstar
   2845                    (setq current-regexp (concat current-regexp
   2846                                                 lsp-globstar-regexp))
   2847                    (setq prev-segment-was-globstar t))
   2848                (let ((in-braces nil)
   2849                      (brace-val "")
   2850                      (in-brackets nil)
   2851                      (bracket-val ""))
   2852                  (dolist (char (string-to-list segment))
   2853                    (cond
   2854                     ((and (not (char-equal char ?\}))
   2855                           in-braces)
   2856                      (setq brace-val (concat brace-val
   2857                                              (char-to-string char))))
   2858                     ((and in-brackets
   2859                           (or (not (char-equal char ?\]))
   2860                               (string-empty-p bracket-val)))
   2861                      (let ((curr (cond
   2862                                   ((char-equal char ?-)
   2863                                    "-")
   2864                                   ;; NOTE: ?\^ and ?^ are different characters
   2865                                   ((and (memq char '(?^ ?!))
   2866                                         (string-empty-p bracket-val))
   2867                                    "^")
   2868                                   ((char-equal char lsp-glob-split)
   2869                                    "")
   2870                                   (t
   2871                                    (regexp-quote (char-to-string char))))))
   2872                        (setq bracket-val (concat bracket-val curr))))
   2873                     (t
   2874                      (cl-case char
   2875                        (?{
   2876                         (setq in-braces t))
   2877                        (?\[
   2878                         (setq in-brackets t))
   2879                        (?}
   2880                         (let* ((choices (lsp-split-glob-pattern brace-val ?\,))
   2881                                (brace-regexp (concat "\\(?:"
   2882                                                      (mapconcat #'lsp--glob-to-regexp choices "\\|")
   2883                                                      "\\)")))
   2884                           (setq current-regexp (concat current-regexp
   2885                                                        brace-regexp))
   2886                           (setq in-braces nil)
   2887                           (setq brace-val "")))
   2888                        (?\]
   2889                         (setq current-regexp
   2890                               (concat current-regexp
   2891                                       "[" bracket-val "]"))
   2892                         (setq in-brackets nil)
   2893                         (setq bracket-val ""))
   2894                        (??
   2895                         (setq current-regexp
   2896                               (concat current-regexp
   2897                                       lsp-non-path-regexp)))
   2898                        (?*
   2899                         (setq current-regexp
   2900                               (concat current-regexp
   2901                                       lsp-non-path-regexp "*?")))
   2902                        (t
   2903                         (setq current-regexp
   2904                               (concat current-regexp
   2905                                       (regexp-quote (char-to-string char)))))))))
   2906                  (when (and (< index (1- (length glob-segments)))
   2907                             (or (not (string-equal (nth (1+ index) glob-segments)
   2908                                                    lsp-globstar))
   2909                                 (< (+ index 2)
   2910                                    (length glob-segments))))
   2911                    (setq current-regexp
   2912                          (concat current-regexp
   2913                                  lsp-path-regexp)))
   2914                  (setq prev-segment-was-globstar nil))))
   2915            glob-segments)
   2916           current-regexp)))))
   2917 
   2918 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365
   2919 (defun lsp-glob-unbrace-at-top-level (glob-pattern)
   2920   "If GLOB-PATTERN does not start with a brace, return a singleton list
   2921 containing GLOB-PATTERN.
   2922 
   2923 If GLOB-PATTERN does start with a brace, return a list of the
   2924 comma-separated globs within the top-level braces."
   2925   (if (not (string-prefix-p "{" glob-pattern))
   2926       (list glob-pattern)
   2927     (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,)))
   2928 
   2929 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern)
   2930   "Convert GLOB-PATTERN to a regexp wrapped with the beginning-
   2931 and end-of-string meta-characters."
   2932   (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'"))
   2933 
   2934 (defun lsp-glob-to-regexps (glob-pattern)
   2935   "Convert a GLOB-PATTERN to a list of Elisp regexps."
   2936   (when-let*
   2937       ((glob-pattern (cond ((hash-table-p glob-pattern)
   2938                             (ht-get glob-pattern "pattern"))
   2939                            ((stringp glob-pattern) glob-pattern)
   2940                            (t (error "Unknown glob-pattern type: %s" glob-pattern))))
   2941        (trimmed-pattern (string-trim glob-pattern))
   2942        (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern)))
   2943     (seq-map #'lsp-glob-convert-to-wrapped-regexp
   2944              top-level-unbraced-patterns)))
   2945 
   2946 
   2947 
   2948 (defvar lsp-mode-menu)
   2949 
   2950 (defun lsp-mouse-click (event)
   2951   (interactive "e")
   2952   (let* ((ec (event-start event))
   2953          (choice (x-popup-menu event lsp-mode-menu))
   2954          (action (lookup-key lsp-mode-menu (apply 'vector choice))))
   2955 
   2956     (select-window (posn-window ec))
   2957 
   2958     (unless (and (region-active-p) (eq action 'lsp-execute-code-action))
   2959       (goto-char (posn-point ec)))
   2960     (run-with-idle-timer
   2961      0.001 nil
   2962      (lambda ()
   2963        (cl-labels ((check (value) (not (null value))))
   2964          (when choice
   2965            (call-interactively action)))))))
   2966 
   2967 (defvar lsp-mode-map
   2968   (let ((map (make-sparse-keymap)))
   2969     (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse)
   2970     (define-key map (kbd "C-<mouse-1>") #'ignore)
   2971     (define-key map (kbd "<mouse-3>") #'lsp-mouse-click)
   2972     (define-key map (kbd "C-S-SPC") #'lsp-signature-activate)
   2973     (when lsp-keymap-prefix
   2974       (define-key map (kbd lsp-keymap-prefix) lsp-command-map))
   2975     map)
   2976   "Keymap for `lsp-mode'.")
   2977 
   2978 (define-minor-mode lsp-mode "Mode for LSP interaction."
   2979   :keymap lsp-mode-map
   2980   :lighter
   2981   (" LSP["
   2982    (lsp--buffer-workspaces
   2983     (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "]["))
   2984     (:propertize "Disconnected" face warning))
   2985    "]")
   2986   :group 'lsp-mode
   2987   (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred))
   2988     ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp'
   2989     (lsp)))
   2990 
   2991 (defvar lsp-mode-menu
   2992   (easy-menu-create-menu
   2993    nil
   2994    `(["Go to definition" lsp-find-definition
   2995       :active (lsp-feature? "textDocument/definition")]
   2996      ["Find references" lsp-find-references
   2997       :active (lsp-feature? "textDocument/references")]
   2998      ["Find implementations" lsp-find-implementation
   2999       :active (lsp-feature? "textDocument/implementation")]
   3000      ["Find declarations" lsp-find-declaration
   3001       :active (lsp-feature? "textDocument/declaration")]
   3002      ["Go to type declaration" lsp-find-type-definition
   3003       :active (lsp-feature? "textDocument/typeDefinition")]
   3004      "--"
   3005      ["Describe" lsp-describe-thing-at-point]
   3006      ["Code action" lsp-execute-code-action]
   3007      ["Format" lsp-format-buffer]
   3008      ["Highlight references" lsp-document-highlight]
   3009      ["Type Hierarchy" lsp-java-type-hierarchy
   3010       :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")]
   3011      ["Type Hierarchy" lsp-treemacs-type-hierarchy
   3012       :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy"))
   3013                     (functionp 'lsp-treemacs-type-hierarchy)
   3014                     (lsp-feature? "textDocument/typeHierarchy"))]
   3015      ["Call Hierarchy" lsp-treemacs-call-hierarchy
   3016       :visible (and (functionp 'lsp-treemacs-call-hierarchy)
   3017                     (lsp-feature? "textDocument/callHierarchy"))]
   3018      ["Rename" lsp-rename
   3019       :active (lsp-feature? "textDocument/rename")]
   3020      "--"
   3021      ("Session"
   3022       ["View logs" lsp-workspace-show-log]
   3023       ["Describe" lsp-describe-session]
   3024       ["Shutdown" lsp-shutdown-workspace]
   3025       ["Restart" lsp-restart-workspace])
   3026      ("Workspace Folders"
   3027       ["Add" lsp-workspace-folders-add]
   3028       ["Remove" lsp-workspace-folders-remove]
   3029       ["Open" lsp-workspace-folders-open])
   3030      ("Toggle features"
   3031       ["Lenses" lsp-lens-mode]
   3032       ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode]
   3033       ["Modeline code actions" lsp-modeline-code-actions-mode]
   3034       ["Modeline diagnostics" lsp-modeline-diagnostics-mode])
   3035      "---"
   3036      ("Debug"
   3037       :active (bound-and-true-p dap-ui-mode)
   3038       :filter ,(lambda (_)
   3039                  (and (boundp 'dap-ui-menu-items)
   3040                       (nthcdr 3 dap-ui-menu-items))))))
   3041   "Menu for lsp-mode.")
   3042 
   3043 (defalias 'make-lsp-client 'make-lsp--client)
   3044 
   3045 (cl-defstruct lsp--registered-capability
   3046   (id "")
   3047   (method " ")
   3048   (options nil))
   3049 
   3050 ;; A ‘lsp--workspace’ object represents exactly one language server process.
   3051 (cl-defstruct lsp--workspace
   3052   ;; the `ewoc' object for displaying I/O to and from the server
   3053   (ewoc nil)
   3054 
   3055   ;; ‘server-capabilities’ is a hash table of the language server capabilities.
   3056   ;; It is the hash table representation of a LSP ServerCapabilities structure;
   3057   ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize.
   3058   (server-capabilities nil)
   3059 
   3060   ;; ‘registered-server-capabilities’ is a list of hash tables that represent
   3061   ;; dynamically-registered Registration objects.  See
   3062   ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability.
   3063   (registered-server-capabilities nil)
   3064 
   3065   ;; ‘root’ is a directory name or a directory file name for the workspace
   3066   ;; root.  ‘lsp-mode’ passes this directory to the ‘initialize’ method of the
   3067   ;; language server; see
   3068   ;; https://microsoft.github.io/language-server-protocol/specification#initialize.
   3069   (root nil)
   3070 
   3071   ;; ‘client’ is the ‘lsp--client’ object associated with this workspace.
   3072   (client nil)
   3073 
   3074   ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It
   3075   ;; used to derive the file path in `lsp--uri-to-path' when using tramp
   3076   ;; connection.
   3077   (host-root nil)
   3078 
   3079   ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or
   3080   ;; a network connection.  ‘lsp-mode’ communicates with ‘proc’ using the
   3081   ;; language server protocol.  ‘proc’ corresponds to the COMMUNICATION-PROCESS
   3082   ;; element of the return value of the client’s ‘get-root’ field, which see.
   3083   (proc nil)
   3084 
   3085   ;; ‘proc’ is a process object; it must represent a regular process, not a
   3086   ;; pipe or network process.  It represents the actual server process that
   3087   ;; corresponds to this workspace.  ‘cmd-proc’ corresponds to the
   3088   ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’
   3089   ;; field, which see.
   3090   (cmd-proc nil)
   3091 
   3092   ;; ‘buffers’ is a list of buffers associated with this workspace.
   3093   (buffers nil)
   3094 
   3095   ;; if semantic tokens is enabled, `semantic-tokens-faces' contains
   3096   ;; one face (or nil) for each token type supported by the language server.
   3097   (semantic-tokens-faces nil)
   3098 
   3099   ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces'
   3100   ;; contains one face (or nil) for each modifier type supported by the language
   3101   ;; server
   3102   (semantic-tokens-modifier-faces nil)
   3103 
   3104   ;; Extra client capabilities provided by third-party packages using
   3105   ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME
   3106   ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name,
   3107   ;; and CAPS is either a plist of the client capabilities, or a function that
   3108   ;; takes no argument and returns a plist of the client capabilities or nil.
   3109   (extra-client-capabilities nil)
   3110 
   3111   ;; Workspace status
   3112   (status nil)
   3113 
   3114   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3115   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3116   (metadata (make-hash-table :test 'equal))
   3117 
   3118   ;; contains all the file notification watches that have been created for the
   3119   ;; current workspace in format filePath->file notification handle.
   3120   (watches (make-hash-table :test 'equal))
   3121 
   3122   ;; list of workspace folders
   3123   (workspace-folders nil)
   3124 
   3125   ;; ‘last-id’ the last request id for the current workspace.
   3126   (last-id 0)
   3127 
   3128   ;; ‘status-string’ allows extensions to specify custom status string based on
   3129   ;; the Language Server specific messages.
   3130   (status-string nil)
   3131 
   3132   ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it
   3133   ;; was stopped).
   3134   shutdown-action
   3135 
   3136   ;; ‘diagnostics’ a hashmap with workspace diagnostics.
   3137   (diagnostics (make-hash-table :test 'equal))
   3138 
   3139   ;; contains all the workDone progress tokens that have been created
   3140   ;; for the current workspace.
   3141   (work-done-tokens (make-hash-table :test 'equal)))
   3142 
   3143 
   3144 (cl-defstruct lsp-session
   3145   ;; contains the folders that are part of the current session
   3146   folders
   3147   ;; contains the folders that must not be imported in the current workspace.
   3148   folders-blocklist
   3149   ;; contains the list of folders that must be imported in a project in case of
   3150   ;; multi root LSP server.
   3151   (server-id->folders (make-hash-table :test 'equal))
   3152   ;; folder to list of the servers that are associated with the folder.
   3153   (folder->servers (make-hash-table :test 'equal))
   3154   ;; ‘metadata’ is a generic storage for workspace specific data. It is
   3155   ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
   3156   (metadata (make-hash-table :test 'equal)))
   3157 
   3158 (defun lsp-workspace-status (status-string &optional workspace)
   3159   "Set current workspace status to STATUS-STRING.
   3160 If WORKSPACE is not specified defaults to lsp--cur-workspace."
   3161   (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string))))
   3162     (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string)))
   3163 
   3164 (defun lsp-session-set-metadata (key value &optional _workspace)
   3165   "Associate KEY with VALUE in the WORKSPACE metadata.
   3166 If WORKSPACE is not provided current workspace will be used."
   3167   (puthash key value (lsp-session-metadata (lsp-session))))
   3168 
   3169 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata)
   3170 
   3171 (defun lsp-session-get-metadata (key &optional _workspace)
   3172   "Lookup KEY in WORKSPACE metadata.
   3173 If WORKSPACE is not provided current workspace will be used."
   3174   (gethash key (lsp-session-metadata (lsp-session))))
   3175 
   3176 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata)
   3177 
   3178 (defun lsp-workspace-set-work-done-token (token value workspace)
   3179   "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens."
   3180   (puthash token value (lsp--workspace-work-done-tokens workspace)))
   3181 
   3182 (defun lsp-workspace-get-work-done-token (token workspace)
   3183   "Lookup TOKEN in the WORKSPACE work-done-tokens."
   3184   (gethash token (lsp--workspace-work-done-tokens workspace)))
   3185 
   3186 (defun lsp-workspace-rem-work-done-token (token workspace)
   3187   "Remove TOKEN from the WORKSPACE work-done-tokens."
   3188   (remhash token (lsp--workspace-work-done-tokens workspace)))
   3189 
   3190 
   3191 (defun lsp--make-notification (method &optional params)
   3192   "Create notification body for method METHOD and parameters PARAMS."
   3193   (list :jsonrpc "2.0" :method method :params params))
   3194 
   3195 (defalias 'lsp--make-request 'lsp--make-notification)
   3196 (defalias 'lsp-make-request 'lsp--make-notification)
   3197 
   3198 (defun lsp--make-response (id result)
   3199   "Create response for REQUEST with RESULT."
   3200   `(:jsonrpc "2.0" :id ,id :result ,result))
   3201 
   3202 (defun lsp-make-notification (method &optional params)
   3203   "Create notification body for method METHOD and parameters PARAMS."
   3204   (lsp--make-notification method params))
   3205 
   3206 (defmacro lsp--json-serialize (params)
   3207   (if (progn
   3208         (require 'json)
   3209         (fboundp 'json-serialize))
   3210       `(json-serialize ,params
   3211                        :null-object nil
   3212                        :false-object :json-false)
   3213     `(let ((json-false :json-false))
   3214        (json-encode ,params))))
   3215 
   3216 (defun lsp--make-message (params)
   3217   "Create a LSP message from PARAMS, after encoding it to a JSON string."
   3218   (let ((body (lsp--json-serialize params)))
   3219     (concat "Content-Length: "
   3220             (number-to-string (1+ (string-bytes body)))
   3221             "\r\n\r\n"
   3222             body
   3223             "\n")))
   3224 
   3225 (cl-defstruct lsp--log-entry timestamp process-time type method id body)
   3226 
   3227 (defun lsp--make-log-entry (method id body type &optional process-time)
   3228   "Create an outgoing log object from BODY with method METHOD and id ID.
   3229 If ID is non-nil, then the body is assumed to be a notification.
   3230 TYPE can either be `incoming' or `outgoing'"
   3231   (cl-assert (memq type '(incoming-req outgoing-req incoming-notif
   3232                                        outgoing-notif incoming-resp
   3233                                        outgoing-resp)))
   3234   (make-lsp--log-entry
   3235    :timestamp (format-time-string "%I:%M:%S %p")
   3236    :process-time process-time
   3237    :method method
   3238    :id id
   3239    :type type
   3240    :body body))
   3241 
   3242 (defun lsp--log-font-lock-json (body)
   3243   "Font lock JSON BODY."
   3244   (with-temp-buffer
   3245     (insert body)
   3246     ;; We set the temp buffer file-name extension to .json and call `set-auto-mode'
   3247     ;; so the users configured json mode is used which could be
   3248     ;; `json-mode', `json-ts-mode', `jsonian-mode', etc.
   3249     (let ((buffer-file-name "lsp-log.json"))
   3250       (delay-mode-hooks
   3251         (set-auto-mode)
   3252         (if (fboundp 'font-lock-ensure)
   3253             (font-lock-ensure)
   3254           (with-no-warnings
   3255             (font-lock-fontify-buffer)))))
   3256     (buffer-string)))
   3257 
   3258 (defun lsp--log-entry-pp (entry)
   3259   (cl-assert (lsp--log-entry-p entry))
   3260   (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time
   3261                           body)
   3262                entry)
   3263               (json-false :json-false)
   3264               (json-encoding-pretty-print t)
   3265               (str nil))
   3266     (setq str
   3267           (concat (format "[Trace - %s] " timestamp)
   3268                   (pcase type
   3269                     ('incoming-req (format "Received request '%s - (%s)." method id))
   3270                     ('outgoing-req (format "Sending request '%s - (%s)'." method id))
   3271 
   3272                     ('incoming-notif (format "Received notification '%s'." method))
   3273                     ('outgoing-notif (format "Sending notification '%s'." method))
   3274 
   3275                     ('incoming-resp (format "Received response '%s - (%s)' in %dms."
   3276                                             method id process-time))
   3277                     ('outgoing-resp
   3278                      (format
   3279                       "Sending response '%s - (%s)'. Processing request took %dms"
   3280                       method id process-time)))
   3281                   "\n"
   3282                   (if (memq type '(incoming-resp ougoing-resp))
   3283                       "Result: "
   3284                     "Params: ")
   3285                   (lsp--log-font-lock-json (json-encode body))
   3286                   "\n\n\n"))
   3287     (setq str (propertize str 'mouse-face 'highlight 'read-only t))
   3288     (insert str)))
   3289 
   3290 (defvar-local lsp--log-io-ewoc nil)
   3291 
   3292 (defun lsp--get-create-io-ewoc (workspace)
   3293   (if (and (lsp--workspace-ewoc workspace)
   3294            (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace))))
   3295       (lsp--workspace-ewoc workspace)
   3296     (with-current-buffer (lsp--get-log-buffer-create workspace)
   3297       (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode))
   3298       (setq-local window-point-insertion-type t)
   3299       (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t))
   3300       (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc))
   3301     (lsp--workspace-ewoc workspace)))
   3302 
   3303 (defun lsp--ewoc-count (ewoc)
   3304   (let* ((count 0)
   3305          (count-fn (lambda (_) (setq count (1+ count)))))
   3306     (ewoc-map count-fn ewoc)
   3307     count))
   3308 
   3309 (defun lsp--log-entry-new (entry workspace)
   3310   (let* ((ewoc (lsp--get-create-io-ewoc workspace))
   3311          (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc)))
   3312          (node (if (or (eq lsp-io-messages-max t)
   3313                        (>= lsp-io-messages-max count))
   3314                    nil
   3315                  (ewoc-nth ewoc (1- lsp-io-messages-max))))
   3316          (prev nil)
   3317          (inhibit-read-only t))
   3318     (while node
   3319       (setq prev (ewoc-prev ewoc node))
   3320       (ewoc-delete ewoc node)
   3321       (setq node prev))
   3322     (ewoc-enter-last ewoc entry)))
   3323 
   3324 (defun lsp--send-notification (body)
   3325   "Send BODY as a notification to the language server."
   3326   (lsp-foreach-workspace
   3327    (when (lsp--log-io-p (plist-get body :method))
   3328      (lsp--log-entry-new (lsp--make-log-entry
   3329                           (plist-get body :method)
   3330                           nil (plist-get body :params) 'outgoing-notif)
   3331                          lsp--cur-workspace))
   3332    (lsp--send-no-wait body
   3333                       (lsp--workspace-proc lsp--cur-workspace))))
   3334 
   3335 (defalias 'lsp-send-notification 'lsp--send-notification)
   3336 
   3337 (defun lsp-notify (method params)
   3338   "Send notification METHOD with PARAMS."
   3339   (lsp--send-notification (lsp--make-notification method params)))
   3340 
   3341 (defun lsp--cur-workspace-check ()
   3342   "Check whether buffer lsp workspace(s) are set."
   3343   (cl-assert (lsp-workspaces) nil
   3344              "No language server(s) is associated with this buffer."))
   3345 
   3346 (defun lsp--send-request (body &optional no-wait no-merge)
   3347   "Send BODY as a request to the language server, get the response.
   3348 If NO-WAIT is non-nil, don't synchronously wait for a response.
   3349 If NO-MERGE is non-nil, don't merge the results but return an
   3350 alist mapping workspace->result."
   3351   (lsp-request (plist-get body :method)
   3352                (plist-get body :params)
   3353                :no-wait no-wait
   3354                :no-merge no-merge))
   3355 
   3356 (defalias 'lsp-send-request 'lsp--send-request
   3357   "Send BODY as a request to the language server and return the response
   3358 synchronously.
   3359 \n(fn BODY)")
   3360 
   3361 (cl-defun lsp-request (method params &key no-wait no-merge)
   3362   "Send request METHOD with PARAMS.
   3363 If NO-MERGE is non-nil, don't merge the results but return alist
   3364 workspace->result.
   3365 If NO-WAIT is non-nil send the request as notification."
   3366   (if no-wait
   3367       (lsp-notify method params)
   3368     (let* ((send-time (float-time))
   3369            ;; max time by which we must get a response
   3370            (expected-time
   3371             (and
   3372              lsp-response-timeout
   3373              (+ send-time lsp-response-timeout)))
   3374            resp-result resp-error done?)
   3375       (unwind-protect
   3376           (progn
   3377             (lsp-request-async method params
   3378                                (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3379                                :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3380                                :no-merge no-merge
   3381                                :mode 'detached
   3382                                :cancel-token :sync-request)
   3383             (while (not (or resp-error resp-result))
   3384               (if (functionp 'json-rpc-connection)
   3385                   (catch 'lsp-done (sit-for 0.01))
   3386                 (catch 'lsp-done
   3387                   (accept-process-output
   3388                    nil
   3389                    (if expected-time (- expected-time send-time) 1))))
   3390               (setq send-time (float-time))
   3391               (when (and expected-time (< expected-time send-time))
   3392                 (error "Timeout while waiting for response.  Method: %s" method)))
   3393             (setq done? t)
   3394             (cond
   3395              ((eq resp-result :finished) nil)
   3396              (resp-result resp-result)
   3397              ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3398              ((lsp-json-error? (cl-first resp-error))
   3399               (error (lsp:json-error-message (cl-first resp-error))))))
   3400         (unless done?
   3401           (lsp-cancel-request-by-token :sync-request))))))
   3402 
   3403 (cl-defun lsp-request-while-no-input (method params)
   3404   "Send request METHOD with PARAMS and waits until there is no input.
   3405 Return same value as `lsp--while-no-input' and respecting `non-essential'."
   3406   (if (or non-essential (not lsp-request-while-no-input-may-block))
   3407       (let* ((send-time (float-time))
   3408              ;; max time by which we must get a response
   3409              (expected-time
   3410               (and
   3411                lsp-response-timeout
   3412                (+ send-time lsp-response-timeout)))
   3413              resp-result resp-error done?)
   3414         (unwind-protect
   3415             (progn
   3416               (lsp-request-async method params
   3417                                  (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_))
   3418                                  :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_))
   3419                                  :mode 'detached
   3420                                  :cancel-token :sync-request)
   3421               (while (not (or resp-error resp-result (input-pending-p)))
   3422                 (catch 'lsp-done
   3423                   (sit-for
   3424                    (if expected-time (- expected-time send-time) 1)))
   3425                 (setq send-time (float-time))
   3426                 (when (and expected-time (< expected-time send-time))
   3427                   (error "Timeout while waiting for response.  Method: %s" method)))
   3428               (setq done? (or resp-error resp-result))
   3429               (cond
   3430                ((eq resp-result :finished) nil)
   3431                (resp-result resp-result)
   3432                ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error)))
   3433                ((lsp-json-error? (cl-first resp-error))
   3434                 (error (lsp:json-error-message (cl-first resp-error))))))
   3435           (unless done?
   3436             (lsp-cancel-request-by-token :sync-request))
   3437           (when (and (input-pending-p) lsp--throw-on-input)
   3438             (throw 'input :interrupted))))
   3439     (lsp-request method params)))
   3440 
   3441 (defvar lsp--cancelable-requests (ht))
   3442 
   3443 (cl-defun lsp-request-async (method params callback
   3444                                     &key mode error-handler cancel-handler no-merge cancel-token)
   3445   "Send METHOD with PARAMS as a request to the language server.
   3446 Call CALLBACK with the response received from the server
   3447 asynchronously.
   3448 MODE determines when the callback will be called depending on the
   3449 condition of the original buffer.  It could be:
   3450 - `detached' which means that the callback will be executed no
   3451 matter what has happened to the buffer.
   3452 - `alive' - the callback will be executed only if the buffer from
   3453 which the call was executed is still alive.
   3454 - `current' the callback will be executed only if the original buffer
   3455 is still selected.
   3456 - `tick' - the callback will be executed only if the buffer was not modified.
   3457 - `unchanged' - the callback will be executed only if the buffer hasn't
   3458 changed and if the buffer is not modified.
   3459 
   3460 ERROR-HANDLER will be called in case the request has failed.
   3461 CANCEL-HANDLER will be called in case the request is being canceled.
   3462 If NO-MERGE is non-nil, don't merge the results but return alist
   3463 workspace->result.
   3464 CANCEL-TOKEN is the token that can be used to cancel request."
   3465   (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params)
   3466                            callback mode error-handler cancel-handler no-merge cancel-token))
   3467 
   3468 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback)
   3469   (lambda (&rest _)
   3470     (unless (and (equal 'post-command-hook hook)
   3471                  (equal (current-buffer) buf))
   3472       (lsp--request-cleanup-hooks id)
   3473       (with-lsp-workspaces workspaces
   3474         (lsp--cancel-request id)
   3475         (when cancel-callback (funcall cancel-callback)))
   3476       (lsp-log "Cancelling %s(%s) in hook %s" method id hook))))
   3477 
   3478 (defun lsp--create-async-callback
   3479     (callback method no-merge workspaces)
   3480   "Create async handler expecting COUNT results, merge them and call CALLBACK.
   3481 MODE determines when the callback will be called depending on the
   3482 condition of the original buffer. METHOD is the invoked method.
   3483 If NO-MERGE is non-nil, don't merge the results but return alist
   3484 workspace->result. ID is the request id."
   3485   (let (results errors)
   3486     (lambda (result)
   3487       (push (cons lsp--cur-workspace result)
   3488             (if (eq result :error) errors results))
   3489       (when (and (not (eq (length errors) (length workspaces)))
   3490                  (eq (+ (length errors) (length results)) (length workspaces)))
   3491         (funcall callback
   3492                  (if no-merge
   3493                      results
   3494                    (lsp--merge-results (-map #'cl-rest results) method)))))))
   3495 
   3496 (defcustom lsp-default-create-error-handler-fn nil
   3497   "Default error handler customization.
   3498 Handler should give METHOD as argument and return function of one argument
   3499 ERROR."
   3500   :type 'function
   3501   :group 'lsp-mode
   3502   :package-version '(lsp-mode . "9.0.0"))
   3503 
   3504 (defun lsp--create-default-error-handler (method)
   3505   "Default error handler.
   3506 METHOD is the executed method."
   3507   (if lsp-default-create-error-handler-fn
   3508       (funcall lsp-default-create-error-handler-fn method)
   3509     (lambda (error)
   3510       (lsp--warn "%s" (or (lsp--error-string error)
   3511                           (format "%s Request has failed" method))))))
   3512 
   3513 (defvar lsp--request-cleanup-hooks (ht))
   3514 
   3515 (defun lsp--request-cleanup-hooks (request-id)
   3516   (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks)))
   3517     (funcall cleanup-function)
   3518     (remhash request-id lsp--request-cleanup-hooks)))
   3519 
   3520 (defun lsp-cancel-request-by-token (cancel-token)
   3521   "Cancel request using CANCEL-TOKEN."
   3522   (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests))
   3523     (with-lsp-workspaces workspaces
   3524       (lsp--cancel-request request-id))
   3525     (remhash cancel-token lsp--cancelable-requests)
   3526     (lsp--request-cleanup-hooks request-id)))
   3527 
   3528 (defun lsp--send-request-async (body callback
   3529                                      &optional mode error-callback cancel-callback
   3530                                      no-merge cancel-token)
   3531   "Send BODY as a request to the language server.
   3532 Call CALLBACK with the response received from the server
   3533 asynchronously.
   3534 MODE determines when the callback will be called depending on the
   3535 condition of the original buffer.  It could be:
   3536 - `detached' which means that the callback will be executed no
   3537 matter what has happened to the buffer.
   3538 - `alive' - the callback will be executed only if the buffer from
   3539 which the call was executed is still alive.
   3540 - `current' the callback will be executed only if the original buffer
   3541 is still selected.
   3542 - `tick' - the callback will be executed only if the buffer was not modified.
   3543 - `unchanged' - the callback will be executed only if the buffer hasn't
   3544 changed and if the buffer is not modified.
   3545 
   3546 ERROR-CALLBACK will be called in case the request has failed.
   3547 CANCEL-CALLBACK will be called in case the request is being canceled.
   3548 If NO-MERGE is non-nil, don't merge the results but return alist
   3549 workspace->result.
   3550 CANCEL-TOKEN is the token that can be used to cancel request."
   3551   (when cancel-token
   3552     (lsp-cancel-request-by-token cancel-token))
   3553 
   3554   (if-let ((target-workspaces (lsp--find-workspaces-for body)))
   3555       (let* ((start-time (current-time))
   3556              (method (plist-get body :method))
   3557              (id (cl-incf lsp-last-id))
   3558              (buf (current-buffer))
   3559              (cancel-callback (when cancel-callback
   3560                                 (pcase mode
   3561                                   ((or 'alive 'tick 'unchanged)
   3562                                    (lambda ()
   3563                                      (with-current-buffer buf
   3564                                        (funcall cancel-callback))))
   3565                                   (_ cancel-callback))))
   3566              ;; calculate what are the (hook . local) pairs which will cancel
   3567              ;; the request
   3568              (hooks (pcase mode
   3569                       ('alive     '((kill-buffer-hook . t)))
   3570                       ('tick      '((kill-buffer-hook . t) (after-change-functions . t)))
   3571                       ('unchanged '((after-change-functions . t) (post-command-hook . nil)))
   3572                       ('current   '((post-command-hook . nil)))))
   3573              ;; note: lambdas in emacs can be compared but we should make sure
   3574              ;; that all of the captured arguments are the same - in our case
   3575              ;; `lsp--create-request-cancel' will return the same lambda when
   3576              ;; called with the same params.
   3577              (cleanup-hooks
   3578               (lambda () (mapc
   3579                           (-lambda ((hook . local))
   3580                             (if local
   3581                                 (when (buffer-live-p buf)
   3582                                   (with-current-buffer buf
   3583                                     (remove-hook hook
   3584                                                  (lsp--create-request-cancel
   3585                                                   id target-workspaces hook buf method cancel-callback)
   3586                                                  t)))
   3587                               (remove-hook hook (lsp--create-request-cancel
   3588                                                  id target-workspaces hook buf method cancel-callback))))
   3589                           hooks)
   3590                 (remhash cancel-token lsp--cancelable-requests)))
   3591              (callback (pcase mode
   3592                          ((or 'alive 'tick 'unchanged) (lambda (&rest args)
   3593                                                          (with-current-buffer buf
   3594                                                            (apply callback args))))
   3595                          (_ callback)))
   3596              (callback (lsp--create-async-callback callback
   3597                                                    method
   3598                                                    no-merge
   3599                                                    target-workspaces))
   3600              (callback (lambda (result)
   3601                          (lsp--request-cleanup-hooks id)
   3602                          (funcall callback result)))
   3603              (error-callback (lsp--create-async-callback
   3604                               (or error-callback
   3605                                   (lsp--create-default-error-handler method))
   3606                               method
   3607                               nil
   3608                               target-workspaces))
   3609              (error-callback (lambda (error)
   3610                                (funcall callback :error)
   3611                                (lsp--request-cleanup-hooks id)
   3612                                (funcall error-callback error)))
   3613              (body (plist-put body :id id)))
   3614 
   3615         ;; cancel request in any of the hooks
   3616         (mapc (-lambda ((hook . local))
   3617                 (add-hook hook
   3618                           (lsp--create-request-cancel
   3619                            id target-workspaces hook buf method cancel-callback)
   3620                           nil local))
   3621               hooks)
   3622         (puthash id cleanup-hooks lsp--request-cleanup-hooks)
   3623 
   3624         (setq lsp--last-active-workspaces target-workspaces)
   3625 
   3626         (when cancel-token
   3627           (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests))
   3628 
   3629         (seq-doseq (workspace target-workspaces)
   3630           (when (lsp--log-io-p method)
   3631             (lsp--log-entry-new (lsp--make-log-entry method id
   3632                                                      (plist-get body :params)
   3633                                                      'outgoing-req)
   3634                                 workspace))
   3635           (puthash id
   3636                    (list callback error-callback method start-time (current-time))
   3637                    (-> workspace
   3638                        (lsp--workspace-client)
   3639                        (lsp--client-response-handlers)))
   3640           (lsp--send-no-wait body (lsp--workspace-proc workspace)))
   3641         body)
   3642     (error "The connected server(s) does not support method %s.
   3643 To find out what capabilities support your server use `M-x lsp-describe-session'
   3644 and expand the capabilities section"
   3645            (plist-get body :method))))
   3646 
   3647 ;; deprecated, use lsp-request-async.
   3648 (defalias 'lsp-send-request-async 'lsp--send-request-async)
   3649 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1")
   3650 
   3651 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any
   3652 ;; pending language servers.
   3653 (add-hook 'kill-emacs-hook #'lsp--global-teardown)
   3654 
   3655 (defun lsp--global-teardown ()
   3656   "Unload working workspaces."
   3657   (lsp-foreach-workspace (lsp--shutdown-workspace)))
   3658 
   3659 (defun lsp--shutdown-workspace (&optional restart)
   3660   "Shut down the language server process for ‘lsp--cur-workspace’."
   3661   (with-demoted-errors "LSP error: %S"
   3662     (let ((lsp-response-timeout 0.5))
   3663       (condition-case err
   3664           (lsp-request "shutdown" nil)
   3665         (error (lsp--error "%s" err))))
   3666     (lsp-notify "exit" nil))
   3667   (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown))
   3668   (lsp--uninitialize-workspace))
   3669 
   3670 (defcustom lsp-inlay-hint-enable nil
   3671   "If non-nil it will enable inlay hints."
   3672   :type 'boolean
   3673   :group 'lsp-mode
   3674   :package-version '(lsp-mode . "9.0.0"))
   3675 
   3676 (defun lsp--uninitialize-workspace ()
   3677   "Cleanup buffer state.
   3678 When a workspace is shut down, by request or from just
   3679 disappearing, unset all the variables related to it."
   3680   (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace]
   3681     (lsp-process-kill cmd-proc)
   3682     (mapc (lambda (buf)
   3683             (when (lsp-buffer-live-p buf)
   3684               (lsp-with-current-buffer buf
   3685                                        (lsp-managed-mode -1))))
   3686           buffers)
   3687     (lsp-diagnostics--workspace-cleanup lsp--cur-workspace)))
   3688 
   3689 (defun lsp--client-capabilities (&optional custom-capabilities)
   3690   "Return the client capabilities appending CUSTOM-CAPABILITIES."
   3691   (append
   3692    `((general . ((positionEncodings . ["utf-32", "utf-16"])))
   3693      (workspace . ((workspaceEdit . ((documentChanges . t)
   3694                                      (resourceOperations . ["create" "rename" "delete"])))
   3695                    (applyEdit . t)
   3696                    (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))))
   3697                    (executeCommand . ((dynamicRegistration . :json-false)))
   3698                    ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t)))))
   3699                    (workspaceFolders . t)
   3700                    (configuration . t)
   3701                    ,@(when lsp-semantic-tokens-enable
   3702                        `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests)
   3703                                                                         lsp-semantic-tokens-honor-refresh-requests)
   3704                                                                    :json-false))))))
   3705                    ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t)))))
   3706                    ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false)))))
   3707                    (fileOperations . ((didCreate . :json-false)
   3708                                       (willCreate . :json-false)
   3709                                       (didRename . t)
   3710                                       (willRename . t)
   3711                                       (didDelete . :json-false)
   3712                                       (willDelete . :json-false)))))
   3713      (textDocument . ((declaration . ((dynamicRegistration . t)
   3714                                       (linkSupport . t)))
   3715                       (definition . ((dynamicRegistration . t)
   3716                                      (linkSupport . t)))
   3717                       (references . ((dynamicRegistration . t)))
   3718                       (implementation . ((dynamicRegistration . t)
   3719                                          (linkSupport . t)))
   3720                       (typeDefinition . ((dynamicRegistration . t)
   3721                                          (linkSupport . t)))
   3722                       (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t)))
   3723                       (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26)))))
   3724                                          (hierarchicalDocumentSymbolSupport . t)))
   3725                       (formatting . ((dynamicRegistration . t)))
   3726                       (rangeFormatting . ((dynamicRegistration . t)))
   3727                       (onTypeFormatting . ((dynamicRegistration . t)))
   3728                       ,@(when (and lsp-semantic-tokens-enable
   3729                                    (functionp 'lsp--semantic-tokens-capabilities))
   3730                           (lsp--semantic-tokens-capabilities))
   3731                       (rename . ((dynamicRegistration . t) (prepareSupport . t)))
   3732                       (codeAction . ((dynamicRegistration . t)
   3733                                      (isPreferredSupport . t)
   3734                                      (codeActionLiteralSupport . ((codeActionKind . ((valueSet . [""
   3735                                                                                                   "quickfix"
   3736                                                                                                   "refactor"
   3737                                                                                                   "refactor.extract"
   3738                                                                                                   "refactor.inline"
   3739                                                                                                   "refactor.rewrite"
   3740                                                                                                   "source"
   3741                                                                                                   "source.organizeImports"])))))
   3742                                      (resolveSupport . ((properties . ["edit" "command"])))
   3743                                      (dataSupport . t)))
   3744                       (completion . ((completionItem . ((snippetSupport . ,(cond
   3745                                                                             ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode)))
   3746                                                                              (lsp--warn (concat
   3747                                                                                          "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. "
   3748                                                                                          "You must either install yasnippet, or disable snippet support."))
   3749                                                                              :json-false)
   3750                                                                             (lsp-enable-snippet t)
   3751                                                                             (t :json-false)))
   3752                                                         (documentationFormat . ["markdown" "plaintext"])
   3753                                                         ;; Remove this after jdtls support resolveSupport
   3754                                                         (resolveAdditionalTextEditsSupport . t)
   3755                                                         (insertReplaceSupport . t)
   3756                                                         (deprecatedSupport . t)
   3757                                                         (resolveSupport
   3758                                                          . ((properties . ["documentation"
   3759                                                                            "detail"
   3760                                                                            "additionalTextEdits"
   3761                                                                            "command"])))
   3762                                                         (insertTextModeSupport . ((valueSet . [1 2])))))
   3763                                      (contextSupport . t)
   3764                                      (dynamicRegistration . t)))
   3765                       (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t)))))
   3766                                         (dynamicRegistration . t)))
   3767                       (documentLink . ((dynamicRegistration . t)
   3768                                        (tooltipSupport . t)))
   3769                       (hover . ((contentFormat . ["markdown" "plaintext"])
   3770                                 (dynamicRegistration . t)))
   3771                       ,@(when lsp-enable-folding
   3772                           `((foldingRange . ((dynamicRegistration . t)
   3773                                              ,@(when lsp-folding-range-limit
   3774                                                  `((rangeLimit . ,lsp-folding-range-limit)))
   3775                                              ,@(when lsp-folding-line-folding-only
   3776                                                  `((lineFoldingOnly . t)))))))
   3777                       (selectionRange . ((dynamicRegistration . t)))
   3778                       (callHierarchy . ((dynamicRegistration . :json-false)))
   3779                       (typeHierarchy . ((dynamicRegistration . t)))
   3780                       (publishDiagnostics . ((relatedInformation . t)
   3781                                              (tagSupport . ((valueSet . [1 2])))
   3782                                              (versionSupport . t)))
   3783                       (diagnostic . ((dynamicRegistration . :json-false)
   3784                                      (relatedDocumentSupport . :json-false)))
   3785                       (linkedEditingRange . ((dynamicRegistration . t)))))
   3786      (window . ((workDoneProgress . t)
   3787                 (showDocument . ((support . t))))))
   3788    custom-capabilities))
   3789 
   3790 (defun lsp-find-roots-for-workspace (workspace session)
   3791   "Get all roots for the WORKSPACE."
   3792   (-filter #'identity (ht-map (lambda (folder workspaces)
   3793                                 (when (-contains? workspaces workspace)
   3794                                   folder))
   3795                               (lsp-session-folder->servers session))))
   3796 
   3797 (defun lsp-session-watches (&optional session)
   3798   "Get watches created for SESSION."
   3799   (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session))))
   3800       (-let [res (make-hash-table :test 'equal)]
   3801         (puthash "__watches" res (lsp-session-metadata (or session (lsp-session))))
   3802         res)))
   3803 
   3804 (defun lsp--file-process-event (session root-folder event)
   3805   "Process file event."
   3806   (let* ((changed-file (cl-third event))
   3807          (rel-changed-file (f-relative changed-file root-folder))
   3808          (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type))
   3809          (bit-position (1- event-numeric-kind))
   3810          (watch-bit (ash 1 bit-position)))
   3811     (->>
   3812      session
   3813      lsp-session-folder->servers
   3814      (gethash root-folder)
   3815      (seq-do (lambda (workspace)
   3816                (when (->>
   3817                       workspace
   3818                       lsp--workspace-registered-server-capabilities
   3819                       (-any?
   3820                        (lambda (capability)
   3821                          (and
   3822                           (equal (lsp--registered-capability-method capability)
   3823                                  "workspace/didChangeWatchedFiles")
   3824                           (->>
   3825                            capability
   3826                            lsp--registered-capability-options
   3827                            (lsp:did-change-watched-files-registration-options-watchers)
   3828                            (seq-find
   3829                             (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp))
   3830                               (when (or (null kind?)
   3831                                         (> (logand kind? watch-bit) 0))
   3832                                 (-let [regexes (or cached-regexp
   3833                                                    (let ((regexp (lsp-glob-to-regexps glob-pattern)))
   3834                                                      (lsp-put fs-watcher :_cachedRegexp regexp)
   3835                                                      regexp))]
   3836                                   (-any? (lambda (re)
   3837                                            (or (string-match re changed-file)
   3838                                                (string-match re rel-changed-file)))
   3839                                          regexes))))))))))
   3840                  (with-lsp-workspace workspace
   3841                    (lsp-notify
   3842                     "workspace/didChangeWatchedFiles"
   3843                     `((changes . [((type . ,event-numeric-kind)
   3844                                    (uri . ,(lsp--path-to-uri changed-file)))]))))))))))
   3845 
   3846 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?))
   3847   "Register capability REG."
   3848   (when (and lsp-enable-file-watchers
   3849              (equal method "workspace/didChangeWatchedFiles"))
   3850     (-let* ((created-watches (lsp-session-watches (lsp-session)))
   3851             (root-folders (cl-set-difference
   3852                            (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session))
   3853                            (ht-keys created-watches))))
   3854       ;; create watch for each root folder without such
   3855       (dolist (folder root-folders)
   3856         (let* ((watch (make-lsp-watch :root-directory folder))
   3857                (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder))
   3858                (ignored-files-regex-list (car ignored-things))
   3859                (ignored-directories-regex-list (cadr ignored-things)))
   3860           (puthash folder watch created-watches)
   3861           (lsp-watch-root-folder (file-truename folder)
   3862                                  (-partial #'lsp--file-process-event (lsp-session) folder)
   3863                                  ignored-files-regex-list
   3864                                  ignored-directories-regex-list
   3865                                  watch
   3866                                  t)))))
   3867 
   3868   (push
   3869    (make-lsp--registered-capability :id id :method method :options register-options?)
   3870    (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3871 
   3872 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body)
   3873   "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to
   3874 access dir-local variables."
   3875   (declare (indent 1) (debug t))
   3876   `(with-temp-buffer
   3877      ;; Set the buffer's name to something under the root so that we can hack the local variables
   3878      ;; This file doesn't need to exist and will not be created due to this.
   3879      (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root)))
   3880      (hack-local-variables)
   3881      (prog1 ,@body
   3882        (setq-local buffer-file-name nil))))
   3883 
   3884 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root)
   3885   "Return a list of the form
   3886 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given
   3887 WORKSPACE-ROOT."
   3888   ;; The intent of this function is to provide per-root workspace-level customization of the
   3889   ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables.
   3890   (lsp--with-workspace-temp-buffer workspace-root
   3891     (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories))))
   3892 
   3893 
   3894 (defun lsp--cleanup-hanging-watches ()
   3895   "Cleanup watches in case there are no more workspaces that are interested
   3896 in that particular folder."
   3897   (let* ((session (lsp-session))
   3898          (watches (lsp-session-watches session)))
   3899     (dolist (watched-folder (ht-keys watches))
   3900       (when (-none? (lambda (workspace)
   3901                       (with-lsp-workspace workspace
   3902                         (lsp--registered-capability "workspace/didChangeWatchedFiles")))
   3903                     (gethash watched-folder (lsp-session-folder->servers (lsp-session))))
   3904         (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder)
   3905         (lsp-kill-watch (gethash watched-folder watches))
   3906         (remhash watched-folder watches)))))
   3907 
   3908 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method))
   3909   "Unregister capability UNREG."
   3910   (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace)
   3911         (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id))
   3912                     (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))
   3913   (when (equal method "workspace/didChangeWatchedFiles")
   3914     (lsp--cleanup-hanging-watches)))
   3915 
   3916 (defun lsp--server-capabilities ()
   3917   "Return the capabilities of the language server associated with the buffer."
   3918   (->> (lsp-workspaces)
   3919        (-keep #'lsp--workspace-server-capabilities)
   3920        (apply #'lsp-merge)))
   3921 
   3922 (defun lsp--send-open-close-p ()
   3923   "Return whether open and close notifications should be sent to the server."
   3924   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3925     (or (memq sync '(1 2))
   3926         (lsp:text-document-sync-options-open-close? sync))))
   3927 
   3928 (defun lsp--send-will-save-p ()
   3929   "Return whether willSave notifications should be sent to the server."
   3930   (-> (lsp--server-capabilities)
   3931       (lsp:server-capabilities-text-document-sync?)
   3932       (lsp:text-document-sync-options-will-save?)))
   3933 
   3934 (defun lsp--send-will-save-wait-until-p ()
   3935   "Return whether willSaveWaitUntil notifications should be sent to the server."
   3936   (-> (lsp--server-capabilities)
   3937       (lsp:server-capabilities-text-document-sync?)
   3938       (lsp:text-document-sync-options-will-save-wait-until?)))
   3939 
   3940 (defun lsp--send-did-save-p ()
   3941   "Return whether didSave notifications should be sent to the server."
   3942   (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities))))
   3943     (or (memq sync '(1 2))
   3944         (lsp:text-document-sync-options-save? sync))))
   3945 
   3946 (defun lsp--save-include-text-p ()
   3947   "Return whether save notifications should include the text document's contents."
   3948   (->> (lsp--server-capabilities)
   3949        (lsp:server-capabilities-text-document-sync?)
   3950        (lsp:text-document-sync-options-save?)
   3951        (lsp:text-document-save-registration-options-include-text?)))
   3952 
   3953 (defun lsp--send-will-rename-files-p (path)
   3954   "Return whether willRenameFiles request should be sent to the server.
   3955 If any filters, checks if it applies for PATH."
   3956   (let* ((will-rename (-> (lsp--server-capabilities)
   3957                           (lsp:server-capabilities-workspace?)
   3958                           (lsp:workspace-server-capabilities-file-operations?)
   3959                           (lsp:workspace-file-operations-will-rename?)))
   3960          (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list)))
   3961     (and will-rename
   3962          (or (seq-empty-p filters)
   3963              (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob)))
   3964                       (-let [regexes (lsp-glob-to-regexps glob)]
   3965                         (and (or (not scheme?)
   3966                                  (string-prefix-p scheme? (lsp--path-to-uri path)))
   3967                              (-any? (lambda (re)
   3968                                       (string-match re path))
   3969                                     regexes))))
   3970                     filters)))))
   3971 
   3972 (defun lsp--send-did-rename-files-p ()
   3973   "Return whether didRenameFiles notification should be sent to the server."
   3974   (-> (lsp--server-capabilities)
   3975       (lsp:server-capabilities-workspace?)
   3976       (lsp:workspace-server-capabilities-file-operations?)
   3977       (lsp:workspace-file-operations-did-rename?)))
   3978 
   3979 (declare-function project-roots "ext:project" (project) t)
   3980 (declare-function project-root "ext:project" (project) t)
   3981 
   3982 (defun lsp--suggest-project-root ()
   3983   "Get project root."
   3984   (or
   3985    (when (fboundp 'projectile-project-root)
   3986      (condition-case nil
   3987          (projectile-project-root)
   3988        (error nil)))
   3989    (when (fboundp 'project-current)
   3990      (when-let ((project (project-current)))
   3991        (if (fboundp 'project-root)
   3992            (project-root project)
   3993          (car (with-no-warnings
   3994                 (project-roots project))))))
   3995    default-directory))
   3996 
   3997 (defun lsp--read-from-file (file)
   3998   "Read FILE content."
   3999   (when (file-exists-p file)
   4000     (cl-first (read-from-string (f-read-text file 'utf-8)))))
   4001 
   4002 (defun lsp--persist (file-name to-persist)
   4003   "Persist TO-PERSIST in FILE-NAME.
   4004 
   4005 This function creates the parent directories if they don't exist
   4006 yet."
   4007   (let ((print-length nil)
   4008         (print-level nil))
   4009     ;; Create all parent directories:
   4010     (make-directory (f-parent file-name) t)
   4011     (f-write-text (prin1-to-string to-persist) 'utf-8 file-name)))
   4012 
   4013 (defun lsp-workspace-folders-add (project-root)
   4014   "Add PROJECT-ROOT to the list of workspace folders."
   4015   (interactive
   4016    (list (read-directory-name "Select folder to add: "
   4017                               (or (lsp--suggest-project-root) default-directory) nil t)))
   4018   (cl-pushnew (lsp-f-canonical project-root)
   4019               (lsp-session-folders (lsp-session)) :test 'equal)
   4020   (lsp--persist-session (lsp-session))
   4021 
   4022   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil))
   4023 
   4024 (defun lsp-workspace-folders-remove (project-root)
   4025   "Remove PROJECT-ROOT from the list of workspace folders."
   4026   (interactive (list (completing-read "Select folder to remove: "
   4027                                       (lsp-session-folders (lsp-session))
   4028                                       nil t nil nil
   4029                                       (lsp-find-session-folder (lsp-session) default-directory))))
   4030 
   4031   (setq project-root (lsp-f-canonical project-root))
   4032 
   4033   ;; send remove folder to each multiroot workspace associated with the folder
   4034   (dolist (wks (->> (lsp-session)
   4035                     (lsp-session-folder->servers)
   4036                     (gethash project-root)
   4037                     (--filter (lsp--client-multi-root (lsp--workspace-client it)))))
   4038     (with-lsp-workspace wks
   4039       (lsp-notify "workspace/didChangeWorkspaceFolders"
   4040                   (lsp-make-did-change-workspace-folders-params
   4041                    :event (lsp-make-workspace-folders-change-event
   4042                            :removed (vector (lsp-make-workspace-folder
   4043                                              :uri (lsp--path-to-uri project-root)
   4044                                              :name (f-filename project-root)))
   4045                            :added [])))))
   4046 
   4047   ;; turn off servers in the removed directory
   4048   (let* ((session (lsp-session))
   4049          (folder->servers (lsp-session-folder->servers session))
   4050          (server-id->folders (lsp-session-server-id->folders session))
   4051          (workspaces (gethash project-root folder->servers)))
   4052 
   4053     (remhash project-root folder->servers)
   4054 
   4055     ;; turn off the servers without root folders
   4056     (dolist (workspace workspaces)
   4057       (when (--none? (-contains? it workspace) (ht-values folder->servers))
   4058         (lsp--info "Shutdown %s since folder %s is removed..."
   4059                    (lsp--workspace-print workspace) project-root)
   4060         (with-lsp-workspace workspace (lsp--shutdown-workspace))))
   4061 
   4062     (setf (lsp-session-folders session)
   4063           (-remove-item project-root (lsp-session-folders session)))
   4064 
   4065     (ht-aeach (puthash key
   4066                        (-remove-item project-root value)
   4067                        server-id->folders)
   4068               server-id->folders)
   4069     (lsp--persist-session (lsp-session)))
   4070 
   4071   (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root)))
   4072 
   4073 (defun lsp-workspace-blocklist-remove (project-root)
   4074   "Remove PROJECT-ROOT from the workspace blocklist."
   4075   (interactive (list (completing-read "Select folder to remove:"
   4076                                       (lsp-session-folders-blocklist (lsp-session))
   4077                                       nil t)))
   4078   (setf (lsp-session-folders-blocklist (lsp-session))
   4079         (delete project-root
   4080                 (lsp-session-folders-blocklist (lsp-session))))
   4081   (lsp--persist-session (lsp-session)))
   4082 
   4083 (define-obsolete-function-alias 'lsp-workspace-folders-switch
   4084   'lsp-workspace-folders-open "lsp-mode 6.1")
   4085 
   4086 (defun lsp-workspace-folders-open (project-root)
   4087   "Open the directory located at PROJECT-ROOT"
   4088   (interactive (list (completing-read "Open folder: "
   4089                                       (lsp-session-folders (lsp-session))
   4090                                       nil t)))
   4091   (find-file project-root))
   4092 
   4093 (defun lsp--maybe-enable-signature-help (trigger-characters)
   4094   (let ((ch last-command-event))
   4095     (when (cl-find ch trigger-characters :key #'string-to-char)
   4096       (lsp-signature-activate))))
   4097 
   4098 (defun lsp--on-type-formatting-handler-create ()
   4099   (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" )))
   4100     (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character?
   4101                                              :first-trigger-character) provider]
   4102       (lambda ()
   4103         (lsp--on-type-formatting first-trigger-character
   4104                                  more-trigger-character?)))))
   4105 
   4106 (defun lsp--update-on-type-formatting-hook (&optional cleanup?)
   4107   (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create)))
   4108     (cond
   4109      ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?))
   4110       (add-hook 'post-self-insert-hook on-type-formatting-handler nil t))
   4111      ((or cleanup?
   4112           (not lsp-enable-on-type-formatting))
   4113       (remove-hook 'post-self-insert-hook on-type-formatting-handler t)))))
   4114 
   4115 (defun lsp--signature-help-handler-create ()
   4116   (-when-let ((&SignatureHelpOptions? :trigger-characters?)
   4117               (lsp--capability-for-method "textDocument/signatureHelp"))
   4118     (lambda ()
   4119       (lsp--maybe-enable-signature-help trigger-characters?))))
   4120 
   4121 (defun lsp--update-signature-help-hook (&optional cleanup?)
   4122   (let ((signature-help-handler (lsp--signature-help-handler-create)))
   4123     (cond
   4124      ((and (or (equal lsp-signature-auto-activate t)
   4125                (memq :on-trigger-char lsp-signature-auto-activate))
   4126            signature-help-handler
   4127            (not cleanup?))
   4128       (add-hook 'post-self-insert-hook signature-help-handler nil t))
   4129 
   4130      ((or cleanup?
   4131           (not (or (equal lsp-signature-auto-activate t)
   4132                    (memq :on-trigger-char lsp-signature-auto-activate))))
   4133       (remove-hook 'post-self-insert-hook signature-help-handler t)))))
   4134 
   4135 (defun lsp--after-set-visited-file-name ()
   4136   (lsp-disconnect)
   4137   (lsp))
   4138 
   4139 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27
   4140 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099
   4141 (defvar eldoc-documentation-default) ; CI
   4142 (when (< emacs-major-version 28)
   4143   (unless (boundp 'eldoc-documentation-functions)
   4144     (load "eldoc" nil 'nomessage))
   4145   (when (memq (default-value 'eldoc-documentation-function) '(nil ignore))
   4146     ;; actually `eldoc-documentation-strategy', but CI was failing
   4147     (setq-default eldoc-documentation-function 'eldoc-documentation-default)))
   4148 
   4149 (define-minor-mode lsp-managed-mode
   4150   "Mode for source buffers managed by lsp-mode."
   4151   :lighter nil
   4152   (cond
   4153    (lsp-managed-mode
   4154     (when (lsp-feature? "textDocument/hover")
   4155       (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t)
   4156       (eldoc-mode 1))
   4157 
   4158     (add-hook 'after-change-functions #'lsp-on-change nil t)
   4159     (add-hook 'after-revert-hook #'lsp-on-revert nil t)
   4160     (add-hook 'after-save-hook #'lsp-on-save nil t)
   4161     (add-hook 'auto-save-hook #'lsp--on-auto-save nil t)
   4162     (add-hook 'before-change-functions #'lsp-before-change nil t)
   4163     (add-hook 'before-save-hook #'lsp--before-save nil t)
   4164     (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t)
   4165     (add-hook 'post-command-hook #'lsp--post-command nil t)
   4166 
   4167     (lsp--update-on-type-formatting-hook)
   4168     (lsp--update-signature-help-hook)
   4169 
   4170     (when lsp-enable-xref
   4171       (add-hook 'xref-backend-functions #'lsp--xref-backend nil t))
   4172 
   4173     (lsp-configure-buffer)
   4174 
   4175     ;; make sure we turn off lsp-mode in case major mode changes, because major
   4176     ;; mode change will wipe the buffer locals.
   4177     (add-hook 'change-major-mode-hook #'lsp-disconnect nil t)
   4178     (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t)
   4179 
   4180     (let ((buffer (lsp-current-buffer)))
   4181       (run-with-idle-timer
   4182        0.0 nil
   4183        (lambda ()
   4184          (when (lsp-buffer-live-p buffer)
   4185            (lsp-with-current-buffer buffer
   4186              (lsp--on-change-debounce buffer)
   4187              (lsp--on-idle buffer)))))))
   4188    (t
   4189     (lsp-unconfig-buffer)
   4190 
   4191     (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t)
   4192     (remove-hook 'post-command-hook #'lsp--post-command t)
   4193     (remove-hook 'after-change-functions #'lsp-on-change t)
   4194     (remove-hook 'after-revert-hook #'lsp-on-revert t)
   4195     (remove-hook 'after-save-hook #'lsp-on-save t)
   4196     (remove-hook 'auto-save-hook #'lsp--on-auto-save t)
   4197     (remove-hook 'before-change-functions #'lsp-before-change t)
   4198     (remove-hook 'before-save-hook #'lsp--before-save t)
   4199     (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t)
   4200 
   4201     (lsp--update-on-type-formatting-hook :cleanup)
   4202     (lsp--update-signature-help-hook :cleanup)
   4203 
   4204     (when lsp--on-idle-timer
   4205       (cancel-timer lsp--on-idle-timer)
   4206       (setq lsp--on-idle-timer nil))
   4207 
   4208     (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4209     (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4210 
   4211     (lsp--remove-overlays 'lsp-highlight)
   4212     (lsp--remove-overlays 'lsp-links)
   4213 
   4214     (remove-hook 'xref-backend-functions #'lsp--xref-backend t)
   4215     (remove-hook 'change-major-mode-hook #'lsp-disconnect t)
   4216     (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t)
   4217     (setq-local lsp-buffer-uri nil))))
   4218 
   4219 (defun lsp-configure-buffer ()
   4220   "Configure LSP features for current buffer."
   4221   ;; make sure the core is running in the context of all available workspaces
   4222   ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context
   4223   (let ((lsp--buffer-workspaces (cond
   4224                                  (lsp--buffer-workspaces)
   4225                                  (lsp--cur-workspace (list lsp--cur-workspace))))
   4226         lsp--cur-workspace)
   4227     (when lsp-auto-configure
   4228       (lsp--auto-configure)
   4229 
   4230       (when (and lsp-enable-text-document-color
   4231                  (lsp-feature? "textDocument/documentColor"))
   4232         (add-hook 'lsp-on-change-hook #'lsp--document-color nil t))
   4233 
   4234       (when (and lsp-enable-imenu
   4235                  (lsp-feature? "textDocument/documentSymbol"))
   4236         (lsp-enable-imenu))
   4237 
   4238       (when (and lsp-enable-indentation
   4239                  (lsp-feature? "textDocument/rangeFormatting"))
   4240         (add-function :override (local 'indent-region-function) #'lsp-format-region))
   4241 
   4242       (when (and lsp-enable-symbol-highlighting
   4243                  (lsp-feature? "textDocument/documentHighlight"))
   4244         (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t))
   4245 
   4246       (when (and lsp-enable-links
   4247                  (lsp-feature? "textDocument/documentLink"))
   4248         (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t))
   4249 
   4250       (when (and lsp-inlay-hint-enable
   4251                  (lsp-feature? "textDocument/inlayHint"))
   4252         (lsp-inlay-hints-mode))
   4253 
   4254       (when (and lsp-enable-dap-auto-configure
   4255                  (functionp 'dap-mode))
   4256         (dap-auto-configure-mode 1)))
   4257     (run-hooks 'lsp-configure-hook)))
   4258 
   4259 (defun lsp-unconfig-buffer ()
   4260   "Unconfigure LSP features for buffer."
   4261   (lsp--remove-overlays 'lsp-color)
   4262 
   4263   (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function)
   4264     (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   4265     (setq-local imenu-menubar-modified-tick 0)
   4266     (setq-local imenu--index-alist nil)
   4267     (imenu--cleanup))
   4268 
   4269   (remove-function (local 'indent-region-function) #'lsp-format-region)
   4270 
   4271   (remove-hook 'lsp-on-change-hook #'lsp--document-color t)
   4272   (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
   4273   (remove-hook 'lsp-on-idle-hook #'lsp--document-links t)
   4274 
   4275   (when (and lsp-enable-dap-auto-configure
   4276              (functionp 'dap-mode))
   4277     (dap-auto-configure-mode -1))
   4278 
   4279   (run-hooks 'lsp-unconfigure-hook))
   4280 
   4281 (defun lsp--buffer-content ()
   4282   (lsp-save-restriction-and-excursion
   4283     (or (lsp-virtual-buffer-call :buffer-string)
   4284         (buffer-substring-no-properties (point-min)
   4285                                         (point-max)))))
   4286 
   4287 (defun lsp--text-document-did-open ()
   4288   "`document/didOpen' event."
   4289   (run-hooks 'lsp-before-open-hook)
   4290   (when (and lsp-auto-touch-files
   4291              (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri)))))
   4292     (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri))
   4293     (save-buffer))
   4294 
   4295   (setq lsp--cur-version (or lsp--cur-version 0))
   4296   (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   4297   (lsp-notify
   4298    "textDocument/didOpen"
   4299    (list :textDocument
   4300          (list :uri (lsp--buffer-uri)
   4301                :languageId (lsp-buffer-language)
   4302                :version lsp--cur-version
   4303                :text (lsp--buffer-content))))
   4304 
   4305   (lsp-managed-mode 1)
   4306 
   4307   (lsp-diagnostics--request-pull-diagnostics lsp--cur-workspace)
   4308 
   4309   (run-hooks 'lsp-after-open-hook)
   4310   (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client))))
   4311     (-some-> (lsp--client-after-open-fn client)
   4312       (funcall))
   4313     (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client))
   4314       (intern-soft)
   4315       (run-hooks))))
   4316 
   4317 (defun lsp--text-document-identifier ()
   4318   "Make TextDocumentIdentifier."
   4319   (list :uri (lsp--buffer-uri)))
   4320 
   4321 (defun lsp--versioned-text-document-identifier ()
   4322   "Make VersionedTextDocumentIdentifier."
   4323   (plist-put (lsp--text-document-identifier) :version lsp--cur-version))
   4324 
   4325 (defun lsp--cur-line (&optional point)
   4326   (1- (line-number-at-pos point)))
   4327 
   4328 (defun lsp--cur-position ()
   4329   "Make a Position object for the current point."
   4330   (or (lsp-virtual-buffer-call :cur-position)
   4331       (lsp-save-restriction-and-excursion
   4332         (list :line (lsp--cur-line)
   4333               :character (- (point) (line-beginning-position))))))
   4334 
   4335 (defun lsp--point-to-position (point)
   4336   "Convert POINT to Position."
   4337   (lsp-save-restriction-and-excursion
   4338     (goto-char point)
   4339     (lsp--cur-position)))
   4340 
   4341 (defun lsp--range (start end)
   4342   "Make Range body from START and END."
   4343   ;; make sure start and end are Position objects
   4344   (list :start start :end end))
   4345 
   4346 (defun lsp--region-to-range (start end)
   4347   "Make Range object for the current region."
   4348   (lsp--range (lsp--point-to-position start)
   4349               (lsp--point-to-position end)))
   4350 
   4351 (defun lsp--region-or-line ()
   4352   "The active region or the current line."
   4353   (if (use-region-p)
   4354       (lsp--region-to-range (region-beginning) (region-end))
   4355     (lsp--region-to-range (line-beginning-position) (line-end-position))))
   4356 
   4357 (defun lsp--check-document-changes-version (document-changes)
   4358   "Verify that DOCUMENT-CHANGES have the proper version."
   4359   (unless (seq-every-p
   4360            (-lambda ((&TextDocumentEdit :text-document))
   4361              (or
   4362               (not text-document)
   4363               (let* ((filename (-> text-document
   4364                                    lsp:versioned-text-document-identifier-uri
   4365                                    lsp--uri-to-path))
   4366                      (version (lsp:versioned-text-document-identifier-version? text-document)))
   4367                 (with-current-buffer (find-file-noselect filename)
   4368                   (or (null version) (zerop version) (= -1 version)
   4369                       (equal version lsp--cur-version))))))
   4370            document-changes)
   4371     (error "Document changes cannot be applied due to different document version")))
   4372 
   4373 (defun lsp--apply-workspace-edit (workspace-edit &optional operation)
   4374   "Apply the WorkspaceEdit object WORKSPACE-EDIT.
   4375 OPERATION is symbol representing the source of this text edit."
   4376   (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit))
   4377     (if-let ((document-changes (seq-reverse document-changes?)))
   4378         (progn
   4379           (lsp--check-document-changes-version document-changes)
   4380           (->> document-changes
   4381                (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create")))
   4382                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4383           (->> document-changes
   4384                (seq-filter (-lambda ((&CreateFile :kind))
   4385                              (and (or (not kind) (equal kind "edit"))
   4386                                   (not (equal kind "create")))))
   4387                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))
   4388           (->> document-changes
   4389                (seq-filter (-lambda ((&CreateFile :kind))
   4390                              (and (not (or (not kind) (equal kind "edit")))
   4391                                   (not (equal kind "create")))))
   4392                (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))))
   4393       (lsp-map
   4394        (lambda (uri text-edits)
   4395          (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect)
   4396            (lsp--apply-text-edits text-edits operation)))
   4397        changes?))))
   4398 
   4399 (defmacro lsp-with-filename (file &rest body)
   4400   "Execute BODY with FILE as a context.
   4401 Need to handle the case when FILE indicates virtual buffer."
   4402   (declare (indent 1) (debug t))
   4403   `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file)))
   4404        (lsp-with-current-buffer lsp--virtual-buffer
   4405          ,@body)
   4406      ,@body))
   4407 
   4408 (defun lsp--apply-text-document-edit (edit &optional operation)
   4409   "Apply the TextDocumentEdit object EDIT.
   4410 OPERATION is symbol representing the source of this text edit.
   4411 If the file is not being visited by any buffer, it is opened with
   4412 `find-file-noselect'.
   4413 Because lsp-mode does not store previous document versions, the edit is only
   4414 applied if the version of the textDocument matches the version of the
   4415 corresponding file.
   4416 
   4417 interface TextDocumentEdit {
   4418   textDocument: VersionedTextDocumentIdentifier;
   4419   edits: TextEdit[];
   4420 }"
   4421   (pcase (lsp:edit-kind edit)
   4422     ("create" (-let* (((&CreateFile :uri :options?) edit)
   4423                       (file-name (lsp--uri-to-path uri)))
   4424                 (mkdir (f-dirname file-name) t)
   4425                 (f-touch file-name)
   4426                 (when (lsp:create-file-options-overwrite? options?)
   4427                   (f-write-text "" nil file-name))
   4428                 (find-file-noselect file-name)))
   4429     ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit))
   4430                 (f-delete (lsp--uri-to-path uri) recursive?)))
   4431     ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit)
   4432                       (old-file-name (lsp--uri-to-path old-uri))
   4433                       (new-file-name (lsp--uri-to-path new-uri))
   4434                       (buf (find-buffer-visiting old-file-name)))
   4435                 (when buf
   4436                   (lsp-with-current-buffer buf
   4437                     (save-buffer)
   4438                     (lsp--text-document-did-close)))
   4439                 (mkdir (f-dirname new-file-name) t)
   4440                 (rename-file old-file-name new-file-name overwrite?)
   4441                 (when buf
   4442                   (lsp-with-current-buffer buf
   4443                     (set-buffer-modified-p nil)
   4444                     (setq lsp-buffer-uri nil)
   4445                     (set-visited-file-name new-file-name)
   4446                     (lsp)))))
   4447     (_ (let ((file-name (->> edit
   4448                              (lsp:text-document-edit-text-document)
   4449                              (lsp:versioned-text-document-identifier-uri)
   4450                              (lsp--uri-to-path))))
   4451          (lsp-with-current-buffer (find-buffer-visiting file-name)
   4452            (lsp-with-filename file-name
   4453              (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation)))))))
   4454 
   4455 (lsp-defun lsp--position-compare ((&Position :line left-line
   4456                                              :character left-character)
   4457                                   (&Position :line right-line
   4458                                              :character right-character))
   4459   "Return t if position LEFT is greater than RIGHT."
   4460   (if (= left-line right-line)
   4461       (> left-character right-character)
   4462     (> left-line right-line)))
   4463 
   4464 (lsp-defun lsp-point-in-range? (position (&Range :start :end))
   4465   "Returns if POINT is in RANGE."
   4466   (not (or (lsp--position-compare start position)
   4467            (lsp--position-compare position end))))
   4468 
   4469 (lsp-defun lsp--position-equal ((&Position :line left-line
   4470                                            :character left-character)
   4471                                 (&Position :line right-line
   4472                                            :character right-character))
   4473   "Return whether LEFT and RIGHT positions are equal."
   4474   (and (= left-line right-line)
   4475        (= left-character right-character)))
   4476 
   4477 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end))
   4478                                           (&TextEdit :range (&Range :start right-start :end right-end)))
   4479   (if (lsp--position-equal left-start right-start)
   4480       (lsp--position-compare left-end right-end)
   4481     (lsp--position-compare left-start right-start)))
   4482 
   4483 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text))
   4484   "Apply the edits described in the TextEdit object in TEXT-EDIT."
   4485   (setq new-text (s-replace "\r" "" (or new-text "")))
   4486   (lsp:set-text-edit-new-text edit new-text)
   4487   (goto-char start)
   4488   (delete-region start end)
   4489   (insert new-text))
   4490 
   4491 ;; WORKAROUND: typescript-language might send -1 when applying code actions.
   4492 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582
   4493 (lsp-defun lsp--fix-point ((point &as &Position :character :line))
   4494   (-doto point
   4495     (lsp:set-position-line (max 0 line))
   4496     (lsp:set-position-character (max 0 character))))
   4497 
   4498 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as
   4499                                                                &TextEdit
   4500                                                                :range (&Range :start :end)
   4501                                                                :new-text))
   4502   "Apply the edits described in the TextEdit object in TEXT-EDIT.
   4503 The method uses `replace-buffer-contents'."
   4504   (setq new-text (s-replace "\r" "" (or new-text "")))
   4505   (lsp:set-text-edit-new-text edit new-text)
   4506   (-let* ((source (current-buffer))
   4507           ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start)
   4508                                                              :end (lsp--fix-point end)))))
   4509     (with-temp-buffer
   4510       (insert new-text)
   4511       (let ((temp (current-buffer)))
   4512         (with-current-buffer source
   4513           (save-excursion
   4514             (save-restriction
   4515               (narrow-to-region beg end)
   4516 
   4517               ;; On emacs versions < 26.2,
   4518               ;; `replace-buffer-contents' is buggy - it calls
   4519               ;; change functions with invalid arguments - so we
   4520               ;; manually call the change functions here.
   4521               ;;
   4522               ;; See emacs bugs #32237, #32278:
   4523               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237
   4524               ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278
   4525               (let ((inhibit-modification-hooks t)
   4526                     (length (- end beg)))
   4527                 (run-hook-with-args 'before-change-functions
   4528                                     beg end)
   4529                 (replace-buffer-contents temp)
   4530                 (run-hook-with-args 'after-change-functions
   4531                                     beg (+ beg (length new-text))
   4532                                     length)))))))))
   4533 
   4534 (defun lsp--to-yasnippet-snippet (snippet)
   4535   "Convert LSP SNIPPET to yasnippet snippet."
   4536   ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it.
   4537   (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`")))
   4538                             (rx "\\" (backref 1))
   4539                             snippet
   4540                             nil nil 1))
   4541 
   4542 (defvar-local lsp-enable-relative-indentation nil
   4543   "Enable relative indentation when insert texts, snippets ...
   4544 from language server.")
   4545 
   4546 (defun lsp--expand-snippet (snippet &optional start end expand-env)
   4547   "Wrapper of `yas-expand-snippet' with all of it arguments.
   4548 The snippet will be convert to LSP style and indent according to
   4549 LSP server result."
   4550   (require 'yasnippet nil t)
   4551   (let* ((inhibit-field-text-motion t)
   4552          (yas-wrap-around-region nil)
   4553          (yas-indent-line 'none)
   4554          (yas-also-auto-indent-first-line nil))
   4555     (yas-expand-snippet
   4556      (lsp--to-yasnippet-snippet snippet)
   4557      start end expand-env)))
   4558 
   4559 (defun lsp--indent-lines (start end &optional insert-text-mode?)
   4560   "Indent from START to END based on INSERT-TEXT-MODE? value.
   4561 - When INSERT-TEXT-MODE? is provided
   4562   - if it's `lsp/insert-text-mode-as-it', do no editor indentation.
   4563   - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading
   4564     whitespaces to match the line where text is inserted.
   4565 - When it's not provided, using `indent-line-function' for each line."
   4566   (save-excursion
   4567     (goto-char end)
   4568     (let* ((end-line (line-number-at-pos))
   4569            (offset (save-excursion
   4570                      (goto-char start)
   4571                      (current-indentation)))
   4572            (indent-line-function
   4573             (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it)
   4574                    #'ignore)
   4575                   ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation)
   4576                        lsp-enable-relative-indentation
   4577                        ;; Indenting snippets is extremely slow in `org-mode' buffers
   4578                        ;; since it has to calculate indentation based on SRC block
   4579                        ;; position.  Thus we use relative indentation as default.
   4580                        (derived-mode-p 'org-mode))
   4581                    (lambda () (save-excursion
   4582                                 (beginning-of-line)
   4583                                 (indent-to-column offset))))
   4584                   (t indent-line-function))))
   4585       (goto-char start)
   4586       (forward-line)
   4587       (while (and (not (eobp))
   4588                   (<= (line-number-at-pos) end-line))
   4589         (funcall indent-line-function)
   4590         (forward-line)))))
   4591 
   4592 (defun lsp--apply-text-edits (edits &optional operation)
   4593   "Apply the EDITS described in the TextEdit[] object.
   4594 OPERATION is symbol representing the source of this text edit."
   4595   (unless (seq-empty-p edits)
   4596     (atomic-change-group
   4597       (run-hooks 'lsp-before-apply-edits-hook)
   4598       (let* ((change-group (prepare-change-group))
   4599              (howmany (length edits))
   4600              (message (format "Applying %s edits to `%s' ..." howmany (current-buffer)))
   4601              (_ (lsp--info message))
   4602              (reporter (make-progress-reporter message 0 howmany))
   4603              (done 0)
   4604              (apply-edit (if (not lsp--virtual-buffer)
   4605                              #'lsp--apply-text-edit-replace-buffer-contents
   4606                            #'lsp--apply-text-edit)))
   4607         (unwind-protect
   4608             (->> edits
   4609                  ;; We sort text edits so as to apply edits that modify latter
   4610                  ;; parts of the document first. Furthermore, because the LSP
   4611                  ;; spec dictates that: "If multiple inserts have the same
   4612                  ;; position, the order in the array defines which edit to
   4613                  ;; apply first."  We reverse the initial list and sort stably
   4614                  ;; to make sure the order among edits with the same position
   4615                  ;; is preserved.
   4616                  (nreverse)
   4617                  (seq-sort #'lsp--text-edit-sort-predicate)
   4618                  (mapc (lambda (edit)
   4619                          (progress-reporter-update reporter (cl-incf done))
   4620                          (funcall apply-edit edit)
   4621                          (when (lsp:snippet-text-edit-insert-text-format? edit)
   4622                            (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start)
   4623                                                          :insert-text-format? :new-text) edit)
   4624                              (when (eq insert-text-format? lsp/insert-text-format-snippet)
   4625                                ;; No `save-excursion' needed since expand snippet will change point anyway
   4626                                (goto-char (+ start (length new-text)))
   4627                                (lsp--indent-lines start (point))
   4628                                (lsp--expand-snippet new-text start (point)))))
   4629                          (run-hook-with-args 'lsp-after-apply-edits-hook operation))))
   4630           (undo-amalgamate-change-group change-group)
   4631           (progress-reporter-done reporter))))))
   4632 
   4633 (defun lsp--create-apply-text-edits-handlers ()
   4634   "Create (handler cleanup-fn) for applying text edits in async request.
   4635 Only works when mode is `tick or `alive."
   4636   (let* (first-edited
   4637          (func (lambda (start &rest _)
   4638                  (setq first-edited (if first-edited
   4639                                         (min start first-edited)
   4640                                       start)))))
   4641     (add-hook 'before-change-functions func nil t)
   4642     (list
   4643      (lambda (edits)
   4644        (if (and first-edited
   4645                 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end)))
   4646                             ;; Text edit region is overlapped
   4647                             (> end first-edited))
   4648                           edits))
   4649            (lsp--warn "TextEdits will not be applied since document has been modified before of them.")
   4650          (lsp--apply-text-edits edits 'completion-cleanup)))
   4651      (lambda ()
   4652        (remove-hook 'before-change-functions func t)))))
   4653 
   4654 (defun lsp--capability (cap &optional capabilities)
   4655   "Get the value of capability CAP.  If CAPABILITIES is non-nil, use them instead."
   4656   (when (stringp cap)
   4657     (setq cap (intern (concat ":" cap))))
   4658 
   4659   (lsp-get (or capabilities
   4660                (lsp--server-capabilities))
   4661            cap))
   4662 
   4663 (defun lsp--registered-capability (method)
   4664   "Check whether there is workspace providing METHOD."
   4665   (->> (lsp-workspaces)
   4666        (--keep (seq-find (lambda (reg)
   4667                            (equal (lsp--registered-capability-method reg) method))
   4668                          (lsp--workspace-registered-server-capabilities it)))
   4669        cl-first))
   4670 
   4671 (defun lsp--capability-for-method (method)
   4672   "Get the value of capability for METHOD."
   4673   (-let* ((reqs (cdr (assoc method lsp-method-requirements)))
   4674           ((&plist :capability) reqs))
   4675     (or (and capability (lsp--capability capability))
   4676         (-some-> (lsp--registered-capability method)
   4677           (lsp--registered-capability-options)))))
   4678 
   4679 (defvar-local lsp--before-change-vals nil
   4680   "Store the positions from the `lsp-before-change' function call, for
   4681 validation and use in the `lsp-on-change' function.")
   4682 
   4683 (defun lsp--text-document-content-change-event (start end length)
   4684   "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH."
   4685   ;; So (47 54 0) means add    7 chars starting at pos 47
   4686   ;; must become
   4687   ;;   {"range":{"start":{"line":5,"character":6}
   4688   ;;             ,"end" :{"line":5,"character":6}}
   4689   ;;             ,"rangeLength":0
   4690   ;;             ,"text":"\nbb = 5"}
   4691   ;;
   4692   ;; And (47 47 7) means delete 7 chars starting at pos 47
   4693   ;; must become
   4694   ;;   {"range":{"start":{"line":6,"character":0}
   4695   ;;            ,"end"  :{"line":7,"character":0}}
   4696   ;;            ,"rangeLength":7
   4697   ;;            ,"text":""}
   4698   ;;
   4699   ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with
   4700   ;; 13 chars. So it must become
   4701   ;;   {"range":{"start":{"line":5,"character":8}
   4702   ;;             ,"end" :{"line":5,"character":11}}
   4703   ;;             ,"rangeLength":3
   4704   ;;             ,"text":"new-chars-xxx"}
   4705   ;;
   4706 
   4707   ;; Adding text:
   4708   ;;   lsp-before-change:(start,end)=(33,33)
   4709   ;;   lsp-on-change:(start,end,length)=(33,34,0)
   4710   ;;
   4711   ;; Changing text:
   4712   ;;   lsp-before-change:(start,end)=(208,211)
   4713   ;;   lsp-on-change:(start,end,length)=(208,221,3)
   4714   ;;
   4715   ;; Deleting text:
   4716   ;;   lsp-before-change:(start,end)=(19,27)
   4717   ;;   lsp-on-change:(start,end,length)=(19,19,8)
   4718   (if (zerop length)
   4719       ;; Adding something only, work from start only
   4720       `( :range ,(lsp--range
   4721                   (lsp--point-to-position start)
   4722                   (lsp--point-to-position start))
   4723          :rangeLength 0
   4724          :text ,(buffer-substring-no-properties start end))
   4725 
   4726     (if (eq start end)
   4727         ;; Deleting something only
   4728         (if (lsp--bracketed-change-p start length)
   4729             ;; The before-change value is bracketed, use it
   4730             `( :range ,(lsp--range
   4731                         (lsp--point-to-position start)
   4732                         (plist-get lsp--before-change-vals :end-pos))
   4733                :rangeLength ,length
   4734                :text "")
   4735           ;; If the change is not bracketed, send a full change event instead.
   4736           (lsp--full-change-event))
   4737 
   4738       ;; Deleting some things, adding others
   4739       (if (lsp--bracketed-change-p start length)
   4740           ;; The before-change value is valid, use it
   4741           `( :range ,(lsp--range
   4742                       (lsp--point-to-position start)
   4743                       (plist-get lsp--before-change-vals :end-pos))
   4744              :rangeLength ,length
   4745              :text ,(buffer-substring-no-properties start end))
   4746         (lsp--full-change-event)))))
   4747 
   4748 (defun lsp--bracketed-change-p (start length)
   4749   "If the before and after positions are the same, and the length
   4750 is the size of the start range, we are probably good."
   4751   (-let [(&plist :end before-end :start before-start) lsp--before-change-vals]
   4752     (and (eq start before-start)
   4753          (eq length (- before-end before-start)))))
   4754 
   4755 (defun lsp--full-change-event ()
   4756   `(:text ,(lsp--buffer-content)))
   4757 
   4758 (defun lsp-before-change (start end)
   4759   "Executed before a file is changed.
   4760 Added to `before-change-functions'."
   4761   ;; Note:
   4762   ;;
   4763   ;; This variable holds a list of functions to call when Emacs is about to
   4764   ;; modify a buffer. Each function gets two arguments, the beginning and end of
   4765   ;; the region that is about to change, represented as integers. The buffer
   4766   ;; that is about to change is always the current buffer when the function is
   4767   ;; called.
   4768   ;;
   4769   ;; WARNING:
   4770   ;;
   4771   ;; Do not expect the before-change hooks and the after-change hooks be called
   4772   ;; in balanced pairs around each buffer change. Also don't expect the
   4773   ;; before-change hooks to be called for every chunk of text Emacs is about to
   4774   ;; delete. These hooks are provided on the assumption that Lisp programs will
   4775   ;; use either before- or the after-change hooks, but not both, and the
   4776   ;; boundaries of the region where the changes happen might include more than
   4777   ;; just the actual changed text, or even lump together several changes done
   4778   ;; piecemeal.
   4779   (save-match-data
   4780     (lsp-save-restriction-and-excursion
   4781       (setq lsp--before-change-vals
   4782             (list :start start
   4783                   :end end
   4784                   :end-pos (lsp--point-to-position end))))))
   4785 
   4786 (defun lsp--flush-delayed-changes ()
   4787   (let ((inhibit-quit t))
   4788     (when lsp--delay-timer
   4789       (cancel-timer lsp--delay-timer))
   4790     (mapc (-lambda ((workspace buffer document change))
   4791             (with-current-buffer buffer
   4792               (with-lsp-workspace workspace
   4793                 (lsp-notify "textDocument/didChange"
   4794                             (list :textDocument document
   4795                                   :contentChanges (vector change))))))
   4796           (prog1 (nreverse lsp--delayed-requests)
   4797             (setq lsp--delayed-requests nil)))))
   4798 
   4799 (defun lsp--workspace-sync-method (workspace)
   4800   (let ((sync (-> workspace
   4801                   (lsp--workspace-server-capabilities)
   4802                   (lsp:server-capabilities-text-document-sync?))))
   4803     (if (lsp-text-document-sync-options? sync)
   4804         (lsp:text-document-sync-options-change? sync)
   4805       sync)))
   4806 
   4807 (defun lsp-on-change (start end length &optional content-change-event-fn)
   4808   "Executed when a file is changed.
   4809 Added to `after-change-functions'."
   4810   ;; Note:
   4811   ;;
   4812   ;; Each function receives three arguments: the beginning and end of the region
   4813   ;; just changed, and the length of the text that existed before the change.
   4814   ;; All three arguments are integers. The buffer that has been changed is
   4815   ;; always the current buffer when the function is called.
   4816   ;;
   4817   ;; The length of the old text is the difference between the buffer positions
   4818   ;; before and after that text as it was before the change. As for the
   4819   ;; changed text, its length is simply the difference between the first two
   4820   ;; arguments.
   4821   ;;
   4822   ;; So (47 54 0) means add    7 chars starting at pos 47
   4823   ;; So (47 47 7) means delete 7 chars starting at pos 47
   4824   (save-match-data
   4825     (let ((inhibit-quit t)
   4826           ;; make sure that `lsp-on-change' is called in multi-workspace context
   4827           ;; see #2901
   4828           lsp--cur-workspace)
   4829       ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done
   4830       ;; by auto-revert-mode) will cause this handler to get called with a nil
   4831       ;; buffer-file-name. We need the buffer-file-name to send notifications;
   4832       ;; so we skip handling revert-buffer-caused changes and instead handle
   4833       ;; reverts separately in lsp-on-revert
   4834       (when (not revert-buffer-in-progress-p)
   4835         (cl-incf lsp--cur-version)
   4836         (mapc
   4837          (lambda (workspace)
   4838            (pcase (or lsp-document-sync-method
   4839                       (lsp--workspace-sync-method workspace))
   4840              (1
   4841               (if lsp-debounce-full-sync-notifications
   4842                   (setq lsp--delayed-requests
   4843                         (->> lsp--delayed-requests
   4844                              (-remove (-lambda ((_ buffer))
   4845                                         (equal (current-buffer) buffer)))
   4846                              (cons (list workspace
   4847                                          (current-buffer)
   4848                                          (lsp--versioned-text-document-identifier)
   4849                                          (lsp--full-change-event)))))
   4850                 (with-lsp-workspace workspace
   4851                   (lsp-notify "textDocument/didChange"
   4852                               (list :contentChanges (vector (lsp--full-change-event))
   4853                                     :textDocument (lsp--versioned-text-document-identifier)))
   4854                   (lsp-diagnostics--request-pull-diagnostics workspace))))
   4855              (2
   4856               (with-lsp-workspace workspace
   4857                 (lsp-notify
   4858                  "textDocument/didChange"
   4859                  (list :textDocument (lsp--versioned-text-document-identifier)
   4860                        :contentChanges (vector
   4861                                         (if content-change-event-fn
   4862                                             (funcall content-change-event-fn start end length)
   4863                                           (lsp--text-document-content-change-event
   4864                                            start end length)))))
   4865                 (lsp-diagnostics--request-pull-diagnostics workspace)))))
   4866          (lsp-workspaces))
   4867         (when lsp--delay-timer (cancel-timer lsp--delay-timer))
   4868         (setq lsp--delay-timer (run-with-idle-timer
   4869                                 lsp-debounce-full-sync-notifications-interval
   4870                                 nil
   4871                                 #'lsp--flush-delayed-changes))
   4872         ;; force cleanup overlays after each change
   4873         (lsp--remove-overlays 'lsp-highlight)
   4874         (lsp--after-change (current-buffer))))))
   4875 
   4876 
   4877 
   4878 ;; facilities for on change hooks. We do not want to make lsp calls on each
   4879 ;; change event so we add debounce to avoid flooding the server with events.
   4880 ;; Additionally, we want to have a mechanism for stopping the server calls in
   4881 ;; particular cases like, e. g. when performing completion.
   4882 
   4883 (defvar lsp-inhibit-lsp-hooks nil
   4884   "Flag to control.")
   4885 
   4886 (defcustom lsp-on-change-hook nil
   4887   "Hooks to run when buffer has changed."
   4888   :type 'hook
   4889   :group 'lsp-mode)
   4890 
   4891 (defcustom lsp-idle-delay 0.500
   4892   "Debounce interval for `after-change-functions'."
   4893   :type 'number
   4894   :group 'lsp-mode)
   4895 
   4896 (defcustom lsp-on-idle-hook nil
   4897   "Hooks to run after `lsp-idle-delay'."
   4898   :type 'hook
   4899   :group 'lsp-mode)
   4900 
   4901 (defun lsp--idle-reschedule (buffer)
   4902   (when lsp--on-idle-timer
   4903     (cancel-timer lsp--on-idle-timer))
   4904 
   4905   (setq lsp--on-idle-timer (run-with-idle-timer
   4906                             lsp-idle-delay
   4907                             nil
   4908                             #'lsp--on-idle
   4909                             buffer)))
   4910 
   4911 (defun lsp--post-command ()
   4912   (lsp--cleanup-highlights-if-needed)
   4913   (lsp--idle-reschedule (current-buffer)))
   4914 
   4915 (defun lsp--on-idle (buffer)
   4916   "Start post command loop."
   4917   (when (and (buffer-live-p buffer)
   4918              (equal buffer (current-buffer))
   4919              (not lsp-inhibit-lsp-hooks)
   4920              lsp-managed-mode)
   4921     (run-hooks 'lsp-on-idle-hook)))
   4922 
   4923 (defun lsp--on-change-debounce (buffer)
   4924   (when (and (buffer-live-p buffer)
   4925              (equal buffer (current-buffer))
   4926              (not lsp-inhibit-lsp-hooks)
   4927              lsp-managed-mode)
   4928     (run-hooks 'lsp-on-change-hook)))
   4929 
   4930 (defun lsp--after-change (buffer)
   4931   "Called after most textDocument/didChange events."
   4932   (setq lsp--signature-last-index nil
   4933         lsp--signature-last nil)
   4934 
   4935   ;; cleanup diagnostics
   4936   (when lsp-diagnostic-clean-after-change
   4937     (dolist (workspace (lsp-workspaces))
   4938       (-let [diagnostics (lsp--workspace-diagnostics workspace)]
   4939         (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics))))
   4940 
   4941   (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled)
   4942     (lsp--semantic-tokens-refresh-if-enabled buffer))
   4943   (when lsp--on-change-timer
   4944     (cancel-timer lsp--on-change-timer))
   4945   (setq lsp--on-change-timer (run-with-idle-timer
   4946                               lsp-idle-delay
   4947                               nil
   4948                               #'lsp--on-change-debounce
   4949                               buffer))
   4950   (lsp--idle-reschedule buffer))
   4951 
   4952 
   4953 (defcustom lsp-trim-trailing-whitespace t
   4954   "Trim trailing whitespace on a line."
   4955   :group 'lsp-mode
   4956   :type 'boolean)
   4957 
   4958 (defcustom lsp-insert-final-newline t
   4959   "Insert a newline character at the end of the file if one does not exist."
   4960   :group 'lsp-mode
   4961   :type 'boolean)
   4962 
   4963 (defcustom lsp-trim-final-newlines t
   4964   "Trim all newlines after the final newline at the end of the file."
   4965   :group 'lsp-mode
   4966   :type 'boolean)
   4967 
   4968 
   4969 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters)
   4970   "Self insert handling.
   4971 Applies on type formatting."
   4972   (let ((ch last-command-event))
   4973     (when (or (eq (string-to-char first-trigger-characters) ch)
   4974               (cl-find ch more-trigger-characters :key #'string-to-char))
   4975       (lsp-request-async "textDocument/onTypeFormatting"
   4976                          (lsp-make-document-on-type-formatting-params
   4977                           :text-document (lsp--text-document-identifier)
   4978                           :options (lsp-make-formatting-options
   4979                                     :tab-size (symbol-value (lsp--get-indent-width major-mode))
   4980                                     :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   4981                                     :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   4982                                     :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   4983                                     :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))
   4984                           :ch (char-to-string ch)
   4985                           :position (lsp--cur-position))
   4986                          (lambda (data) (lsp--apply-text-edits data 'format))
   4987                          :mode 'tick))))
   4988 
   4989 
   4990 ;; links
   4991 (defun lsp--document-links ()
   4992   (when (lsp-feature? "textDocument/documentLink")
   4993     (lsp-request-async
   4994      "textDocument/documentLink"
   4995      `(:textDocument ,(lsp--text-document-identifier))
   4996      (lambda (links)
   4997        (lsp--remove-overlays 'lsp-link)
   4998        (seq-do
   4999         (-lambda ((link &as &DocumentLink :range (&Range :start :end)))
   5000           (-doto (make-button (lsp--position-to-point start)
   5001                               (lsp--position-to-point end)
   5002                               'action (lsp--document-link-keymap link)
   5003                               'keymap (let ((map (make-sparse-keymap)))
   5004                                         (define-key map [M-return] 'push-button)
   5005                                         (define-key map [mouse-2] 'push-button)
   5006                                         map)
   5007                               'help-echo "mouse-2, M-RET: Visit this link")
   5008             (overlay-put 'lsp-link t)))
   5009         links))
   5010      :mode 'unchanged)))
   5011 
   5012 (defun lsp--document-link-handle-target (url)
   5013   (let* ((parsed-url (url-generic-parse-url (url-unhex-string url)))
   5014          (type (url-type parsed-url)))
   5015     (pcase type
   5016       ("file"
   5017        (xref-push-marker-stack)
   5018        (find-file (lsp--uri-to-path url))
   5019        (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url))
   5020          (goto-char (lsp--position-to-point
   5021                      (lsp-make-position :character (1- (string-to-number column))
   5022                                         :line (1- (string-to-number line)))))))
   5023       ((or "http" "https") (browse-url url))
   5024       (type (if-let ((handler (lsp--get-uri-handler type)))
   5025                 (funcall handler url)
   5026               (signal 'lsp-file-scheme-not-supported (list url)))))))
   5027 
   5028 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?))
   5029   (if target?
   5030       (lambda (_)
   5031         (interactive)
   5032         (lsp--document-link-handle-target target?))
   5033     (lambda (_)
   5034       (interactive)
   5035       (when (lsp:document-link-registration-options-resolve-provider?
   5036              (lsp--capability-for-method "textDocument/documentLink"))
   5037         (lsp-request-async
   5038          "documentLink/resolve"
   5039          link
   5040          (-lambda ((&DocumentLink :target?))
   5041            (lsp--document-link-handle-target target?)))))))
   5042 
   5043 
   5044 
   5045 (defcustom lsp-warn-no-matched-clients t
   5046   "Whether to show messages when there are no supported clients."
   5047   :group 'lsp-mode
   5048   :type 'boolean)
   5049 
   5050 (defun lsp-buffer-language--configured-id ()
   5051   "Return nil when not registered."
   5052   (->> lsp-language-id-configuration
   5053        (-first
   5054         (-lambda ((mode-or-pattern . language))
   5055           (cond
   5056            ((and (stringp mode-or-pattern)
   5057                  (s-matches? mode-or-pattern (buffer-file-name)))
   5058             language)
   5059            ((eq mode-or-pattern major-mode) language))))
   5060        cl-rest))
   5061 
   5062 (defvar-local lsp--buffer-language nil
   5063   "Locally cached returned value of `lsp-buffer-language'.")
   5064 
   5065 (defun lsp-buffer-language ()
   5066   "Get language corresponding current buffer."
   5067   (or lsp--buffer-language
   5068       (let* ((configured-language (lsp-buffer-language--configured-id)))
   5069         (setq lsp--buffer-language
   5070               (or configured-language
   5071                   ;; ensure non-nil
   5072                   (string-remove-suffix "-mode" (symbol-name major-mode))))
   5073         (when (and lsp-warn-no-matched-clients
   5074                    (null configured-language))
   5075           (lsp-warn "Unable to calculate the languageId for buffer `%s'. \
   5076 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s"
   5077                     (buffer-name)
   5078                     major-mode))
   5079         lsp--buffer-language)))
   5080 
   5081 (defun lsp-activate-on (&rest languages)
   5082   "Returns language activation function.
   5083 The function will return t when the `lsp-buffer-language' returns
   5084 one of the LANGUAGES."
   5085   (lambda (_file-name _mode)
   5086     (-contains? languages (lsp-buffer-language))))
   5087 
   5088 (defun lsp-workspace-root (&optional path)
   5089   "Find the workspace root for the current file or PATH."
   5090   (-when-let* ((file-name (or path (buffer-file-name)))
   5091                (file-name (lsp-f-canonical file-name)))
   5092     (->> (lsp-session)
   5093          (lsp-session-folders)
   5094          (--filter (and (lsp--files-same-host it file-name)
   5095                         (or (lsp-f-ancestor-of? it file-name)
   5096                             (equal it file-name))))
   5097          (--max-by (> (length it) (length other))))))
   5098 
   5099 (defun lsp-on-revert ()
   5100   "Executed when a file is reverted.
   5101 Added to `after-revert-hook'."
   5102   (let ((n (buffer-size))
   5103         (revert-buffer-in-progress-p nil))
   5104     (lsp-on-change 0 n n)))
   5105 
   5106 (defun lsp--text-document-did-close (&optional keep-workspace-alive)
   5107   "Executed when the file is closed, added to `kill-buffer-hook'.
   5108 
   5109 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace
   5110 if it's closing the last buffer in the workspace."
   5111   (lsp-foreach-workspace
   5112    (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
   5113    (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S"
   5114      (lsp-notify "textDocument/didClose"
   5115                  `(:textDocument ,(lsp--text-document-identifier))))
   5116    (when (and (not lsp-keep-workspace-alive)
   5117               (not keep-workspace-alive)
   5118               (not (lsp--workspace-buffers lsp--cur-workspace)))
   5119      (lsp--shutdown-workspace))))
   5120 
   5121 (defun lsp--will-save-text-document-params (reason)
   5122   (list :textDocument (lsp--text-document-identifier)
   5123         :reason reason))
   5124 
   5125 (defun lsp--before-save ()
   5126   "Before save handler."
   5127   (with-demoted-errors "Error in ‘lsp--before-save’: %S"
   5128     (let ((params (lsp--will-save-text-document-params 1)))
   5129       (when (lsp--send-will-save-p)
   5130         (lsp-notify "textDocument/willSave" params))
   5131       (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits)
   5132         (let ((lsp-response-timeout 0.1))
   5133           (condition-case nil
   5134               (lsp--apply-text-edits
   5135                (lsp-request "textDocument/willSaveWaitUntil"
   5136                             params)
   5137                'before-save)
   5138             (error)))))))
   5139 
   5140 (defun lsp--on-auto-save ()
   5141   "Handler for auto-save."
   5142   (when (lsp--send-will-save-p)
   5143     (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S"
   5144       (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2)))))
   5145 
   5146 (defun lsp--text-document-did-save ()
   5147   "Executed when the file is closed, added to `after-save-hook''."
   5148   (when (lsp--send-did-save-p)
   5149     (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’"
   5150       (lsp-notify "textDocument/didSave"
   5151                   `( :textDocument ,(lsp--versioned-text-document-identifier)
   5152                      ,@(when (lsp--save-include-text-p)
   5153                          (list :text (lsp--buffer-content))))))))
   5154 
   5155 (defun lsp--text-document-position-params (&optional identifier position)
   5156   "Make TextDocumentPositionParams for the current point in the current document.
   5157 If IDENTIFIER and POSITION are non-nil, they will be used as the document
   5158 identifier and the position respectively."
   5159   (list :textDocument (or identifier (lsp--text-document-identifier))
   5160         :position (or position (lsp--cur-position))))
   5161 
   5162 (defun lsp--get-buffer-diagnostics ()
   5163   "Return buffer diagnostics."
   5164   (gethash (or
   5165             (plist-get lsp--virtual-buffer :buffer-file-name)
   5166             (lsp--fix-path-casing (buffer-file-name)))
   5167            (lsp-diagnostics t)))
   5168 
   5169 (defun lsp-cur-line-diagnostics ()
   5170   "Return any diagnostics that apply to the current line."
   5171   (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)]
   5172     (cl-coerce (-filter
   5173                 (-lambda ((&Diagnostic :range (&Range :start (&Position :line))))
   5174                   (and (>= line start) (<= line end)))
   5175                 (lsp--get-buffer-diagnostics))
   5176                'vector)))
   5177 
   5178 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end)
   5179                                   (right &as &Range :start right-start :end right-end))
   5180   (or (lsp-point-in-range? right-start left)
   5181       (lsp-point-in-range? right-end left)
   5182       (lsp-point-in-range? left-start right)
   5183       (lsp-point-in-range? left-end right)))
   5184 
   5185 (defun lsp-make-position-1 (position)
   5186   (lsp-make-position :line (plist-get position :line)
   5187                      :character (plist-get position :character)))
   5188 
   5189 (defun lsp-cur-possition-diagnostics ()
   5190   "Return any diagnostics that apply to the current line."
   5191   (-let* ((start (if (use-region-p) (region-beginning) (point)))
   5192           (end (if (use-region-p) (region-end) (point)))
   5193           (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start))
   5194                                          :end (lsp-make-position-1 (lsp-point-to-position end)))))
   5195     (->> (lsp--get-buffer-diagnostics)
   5196          (-filter
   5197           (-lambda ((&Diagnostic :range))
   5198             (lsp-range-overlapping? range current-range)))
   5199          (apply 'vector))))
   5200 
   5201 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics)
   5202 
   5203 (defun lsp--extract-line-from-buffer (pos)
   5204   "Return the line pointed to by POS (a Position object) in the current buffer."
   5205   (let* ((point (lsp--position-to-point pos))
   5206          (inhibit-field-text-motion t))
   5207     (save-excursion
   5208       (goto-char point)
   5209       (buffer-substring (line-beginning-position) (line-end-position)))))
   5210 
   5211 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line)
   5212                                                  :end (end &as &Position :character end-char)))
   5213   "Return a xref-item from a RANGE in FILENAME."
   5214   (let* ((line (lsp--extract-line-from-buffer start))
   5215          (len (length line)))
   5216     (add-face-text-property (max (min start-char len) 0)
   5217                             (max (min end-char len) 0)
   5218                             'xref-match t line)
   5219     ;; LINE is nil when FILENAME is not being current visited by any buffer.
   5220     (xref-make-match (or line filename)
   5221                      (xref-make-file-location
   5222                       filename
   5223                       (lsp-translate-line (1+ start-line))
   5224                       (lsp-translate-column start-char))
   5225                      (- end-char start-char))))
   5226 
   5227 (defun lsp--location-uri (loc)
   5228   (if (lsp-location? loc)
   5229       (lsp:location-uri loc)
   5230     (lsp:location-link-target-uri loc)))
   5231 
   5232 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start)))
   5233   "Go to location."
   5234   (let ((path (lsp--uri-to-path uri)))
   5235     (if (f-exists? path)
   5236         (with-current-buffer (find-file path)
   5237           (goto-char (lsp--position-to-point start)))
   5238       (error "There is no file %s" path))))
   5239 
   5240 (defun lsp--location-range (loc)
   5241   (if (lsp-location? loc)
   5242       (lsp:location-range loc)
   5243     (lsp:location-link-target-selection-range loc)))
   5244 
   5245 (defun lsp--locations-to-xref-items (locations)
   5246   "Return a list of `xref-item' given LOCATIONS, which can be of
   5247 type Location, LocationLink, Location[] or LocationLink[]."
   5248   (setq locations
   5249         (pcase locations
   5250           ((seq (or (lsp-interface Location)
   5251                     (lsp-interface LocationLink)))
   5252            (append locations nil))
   5253           ((or (lsp-interface Location)
   5254                (lsp-interface LocationLink))
   5255            (list locations))))
   5256 
   5257   (cl-labels ((get-xrefs-in-file
   5258                (file-locs)
   5259                (-let [(filename . matches) file-locs]
   5260                  (condition-case err
   5261                      (let ((visiting (find-buffer-visiting filename))
   5262                            (fn (lambda (loc)
   5263                                  (lsp-with-filename filename
   5264                                    (lsp--xref-make-item filename
   5265                                                         (lsp--location-range loc))))))
   5266                        (if visiting
   5267                            (with-current-buffer visiting
   5268                              (seq-map fn matches))
   5269                          (when (file-readable-p filename)
   5270                            (with-temp-buffer
   5271                              (insert-file-contents-literally filename)
   5272                              (seq-map fn matches)))))
   5273                    (error (lsp-warn "Failed to process xref entry for filename '%s': %s"
   5274                                     filename (error-message-string err)))
   5275                    (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s"
   5276                                          filename (error-message-string err)))))))
   5277 
   5278     (->> locations
   5279          (seq-sort #'lsp--location-before-p)
   5280          (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri))
   5281          (seq-map #'get-xrefs-in-file)
   5282          (apply #'nconc))))
   5283 
   5284 (defun lsp--location-before-p (left right)
   5285   "Sort first by file, then by line, then by column."
   5286   (let ((left-uri (lsp--location-uri left))
   5287         (right-uri (lsp--location-uri right)))
   5288     (if (not (string= left-uri right-uri))
   5289         (string< left-uri right-uri)
   5290       (-let (((&Range :start left-start) (lsp--location-range left))
   5291              ((&Range :start right-start) (lsp--location-range right)))
   5292         (lsp--position-compare right-start left-start)))))
   5293 
   5294 (defun lsp--make-reference-params (&optional td-position exclude-declaration)
   5295   "Make a ReferenceParam object.
   5296 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead.
   5297 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations."
   5298   (let ((json-false :json-false))
   5299     (plist-put (or td-position (lsp--text-document-position-params))
   5300                :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration))))))
   5301 
   5302 (defun lsp--cancel-request (id)
   5303   "Cancel request with ID in all workspaces."
   5304   (lsp-foreach-workspace
   5305    (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id))
   5306    (lsp-notify "$/cancelRequest" `(:id ,id))))
   5307 
   5308 (defvar-local lsp--hover-saved-bounds nil)
   5309 
   5310 (defun lsp-eldoc-function (cb &rest _ignored)
   5311   "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')."
   5312   (if (and lsp--hover-saved-bounds
   5313            (lsp--point-in-bounds-p lsp--hover-saved-bounds))
   5314       lsp--eldoc-saved-message
   5315     (setq lsp--hover-saved-bounds nil
   5316           lsp--eldoc-saved-message nil)
   5317     (if (looking-at-p "[[:space:]\n]")
   5318         (setq lsp--eldoc-saved-message nil) ; And returns nil.
   5319       (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover"))
   5320         (lsp-request-async
   5321          "textDocument/hover"
   5322          (lsp--text-document-position-params)
   5323          (-lambda ((hover &as &Hover? :range? :contents))
   5324            (setq lsp--hover-saved-bounds (when range?
   5325                                            (lsp--range-to-region range?)))
   5326            (funcall cb (setq lsp--eldoc-saved-message
   5327                              (when contents
   5328                                (lsp--render-on-hover-content
   5329                                 contents
   5330                                 lsp-eldoc-render-all)))))
   5331          :error-handler #'ignore
   5332          :mode 'tick
   5333          :cancel-token :eldoc-hover)))))
   5334 
   5335 (defun lsp--point-on-highlight? ()
   5336   (-some? (lambda (overlay)
   5337             (overlay-get overlay 'lsp-highlight))
   5338           (overlays-at (point))))
   5339 
   5340 (defun lsp--cleanup-highlights-if-needed ()
   5341   (when (and lsp-enable-symbol-highlighting
   5342              lsp--have-document-highlights
   5343              (not (lsp--point-on-highlight?)))
   5344     (lsp--remove-overlays 'lsp-highlight)
   5345     (setq lsp--have-document-highlights nil)
   5346     (lsp-cancel-request-by-token :highlights)))
   5347 
   5348 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil
   5349   "The bounds of the symbol from which `lsp--document-highlight'
   5350   most recently requested highlights.")
   5351 
   5352 (defun lsp--document-highlight ()
   5353   (when (lsp-feature? "textDocument/documentHighlight")
   5354     (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol)))
   5355       (unless (or (looking-at-p "[[:space:]\n]")
   5356                   (not lsp-enable-symbol-highlighting)
   5357                   (and lsp--have-document-highlights
   5358                        curr-sym-bounds
   5359                        (equal curr-sym-bounds
   5360                               lsp--symbol-bounds-of-last-highlight-invocation)))
   5361         (setq lsp--symbol-bounds-of-last-highlight-invocation
   5362               curr-sym-bounds)
   5363         (lsp-request-async "textDocument/documentHighlight"
   5364                            (lsp--text-document-position-params)
   5365                            #'lsp--document-highlight-callback
   5366                            :mode 'tick
   5367                            :cancel-token :highlights)))))
   5368 
   5369 (defun lsp--help-open-link (&rest _)
   5370   "Open markdown link at point via mouse or keyboard."
   5371   (interactive "P")
   5372   (let ((buffer-list-update-hook nil))
   5373     (-let [(buffer point) (if-let* ((valid (and (listp last-input-event)
   5374                                                 (eq (car last-input-event) 'mouse-2)))
   5375                                     (event (cadr last-input-event))
   5376                                     (win (posn-window event))
   5377                                     (buffer (window-buffer win)))
   5378                               `(,buffer ,(posn-point event))
   5379                             `(,(current-buffer) ,(point)))]
   5380       (with-current-buffer buffer
   5381         (when-let* ((face (get-text-property point 'face))
   5382                     (url (or (and (eq face 'markdown-link-face)
   5383                                   (get-text-property point 'help-echo))
   5384                              (and (memq face '(markdown-url-face markdown-plain-url-face))
   5385                                   (nth 3 (markdown-link-at-pos point))))))
   5386           (lsp--document-link-handle-target url))))))
   5387 
   5388 (defvar lsp-help-mode-map
   5389   (-doto (make-sparse-keymap)
   5390     (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link))
   5391   "Keymap for `lsp-help-mode'.")
   5392 
   5393 (define-derived-mode lsp-help-mode help-mode "LspHelp"
   5394   "Major mode for displaying lsp help.")
   5395 
   5396 (defun lsp-describe-thing-at-point ()
   5397   "Display the type signature and documentation of the thing at point."
   5398   (interactive)
   5399   (let ((contents (-some->> (lsp--text-document-position-params)
   5400                     (lsp--make-request "textDocument/hover")
   5401                     (lsp--send-request)
   5402                     (lsp:hover-contents))))
   5403     (if (and contents (not (equal contents "")))
   5404         (let ((lsp-help-buf-name "*lsp-help*"))
   5405           (with-current-buffer (get-buffer-create lsp-help-buf-name)
   5406             (delay-mode-hooks
   5407               (lsp-help-mode)
   5408               (with-help-window lsp-help-buf-name
   5409                 (insert (string-trim-right (lsp--render-on-hover-content contents t)))))
   5410             (run-mode-hooks)))
   5411       (lsp--info "No content at point."))))
   5412 
   5413 (defun lsp--point-in-bounds-p (bounds)
   5414   "Return whether the current point is within BOUNDS."
   5415   (and (<= (car bounds) (point)) (< (point) (cdr bounds))))
   5416 
   5417 (defun lsp-get-renderer (language)
   5418   "Get renderer for LANGUAGE."
   5419   (lambda (str)
   5420     (lsp--render-string str language)))
   5421 
   5422 (defun lsp--setup-markdown (mode)
   5423   "Setup the ‘markdown-mode’ in the frame.
   5424 MODE is the mode used in the parent frame."
   5425   (make-local-variable 'markdown-code-lang-modes)
   5426   (dolist (mark (alist-get mode lsp-custom-markup-modes))
   5427     (add-to-list 'markdown-code-lang-modes (cons mark mode)))
   5428   (setq-local markdown-fontify-code-blocks-natively t)
   5429   (setq-local markdown-fontify-code-block-default-mode mode)
   5430   (setq-local markdown-hide-markup t)
   5431 
   5432   ;; Render some common HTML entities.
   5433   ;; This should really happen in markdown-mode instead,
   5434   ;; but it doesn't, so we do it here for now.
   5435   (setq prettify-symbols-alist
   5436         (cl-loop for i from 0 to 255
   5437                  collect (cons (format "&#x%02X;" i) i)))
   5438   (push '("&lt;" . ?<) prettify-symbols-alist)
   5439   (push '("&gt;" . ?>) prettify-symbols-alist)
   5440   (push '("&amp;" . ?&) prettify-symbols-alist)
   5441   (push '("&nbsp;" . ? ) prettify-symbols-alist)
   5442   (setq prettify-symbols-compose-predicate
   5443         (lambda (_start _end _match) t))
   5444   (prettify-symbols-mode 1))
   5445 
   5446 (defvar lsp-help-link-keymap
   5447   (let ((map (make-sparse-keymap)))
   5448     (define-key map [mouse-2] #'lsp--help-open-link)
   5449     (define-key map "\r" #'lsp--help-open-link)
   5450     map)
   5451   "Keymap active on links in *lsp-help* mode.")
   5452 
   5453 (defun lsp--fix-markdown-links ()
   5454   (let ((inhibit-read-only t)
   5455         (inhibit-modification-hooks t)
   5456         (prop))
   5457     (save-restriction
   5458       (goto-char (point-min))
   5459       (while (setq prop (markdown-find-next-prop 'face))
   5460         (let ((end (or (next-single-property-change (car prop) 'face)
   5461                        (point-max))))
   5462           (when (memq (get-text-property (car prop) 'face)
   5463                       '(markdown-link-face
   5464                         markdown-url-face
   5465                         markdown-plain-url-face))
   5466             (add-text-properties (car prop) end
   5467                                  (list 'button t
   5468                                        'category 'lsp-help-link
   5469                                        'follow-link t
   5470                                        'keymap lsp-help-link-keymap)))
   5471           (goto-char end))))))
   5472 
   5473 (defun lsp--buffer-string-visible ()
   5474   "Return visible buffer string.
   5475 Stolen from `org-copy-visible'."
   5476   (let ((temp (generate-new-buffer " *temp*"))
   5477         (beg (point-min))
   5478         (end (point-max)))
   5479     (while (/= beg end)
   5480       (when (get-char-property beg 'invisible)
   5481         (setq beg (next-single-char-property-change beg 'invisible nil end)))
   5482       (let* ((next (next-single-char-property-change beg 'invisible nil end))
   5483              (substring (buffer-substring beg next)))
   5484         (with-current-buffer temp (insert substring))
   5485         ;; (setq result (concat result substring))
   5486         (setq beg next)))
   5487     (setq deactivate-mark t)
   5488     (prog1 (with-current-buffer temp
   5489              (s-chop-suffix "\n" (buffer-string)))
   5490       (kill-buffer temp))))
   5491 
   5492 (defvar lsp-buffer-major-mode nil
   5493   "Holds the major mode when fontification function is running.
   5494 See #2588")
   5495 
   5496 (defvar view-inhibit-help-message)
   5497 
   5498 (defun lsp--render-markdown ()
   5499   "Render markdown."
   5500 
   5501   (let ((markdown-enable-math nil))
   5502     (goto-char (point-min))
   5503     (while (re-search-forward
   5504             (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/"
   5505                                      "{" "}" "[" "]" "(" ")"
   5506                                      "#" "+" "-" "." "!" "|"))))
   5507             nil t)
   5508       (replace-match (rx (backref 1))))
   5509 
   5510     ;; markdown-mode v2.3 does not yet provide gfm-view-mode
   5511     (if (fboundp 'gfm-view-mode)
   5512         (let ((view-inhibit-help-message t))
   5513           (gfm-view-mode))
   5514       (gfm-mode))
   5515 
   5516     (lsp--setup-markdown lsp-buffer-major-mode)))
   5517 
   5518 (defvar lsp--display-inline-image-alist
   5519   '((lsp--render-markdown
   5520      (:regexp
   5521       "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)"
   5522       :sexp
   5523       (create-image
   5524        (base64-decode-string
   5525         (buffer-substring-no-properties (match-beginning 1) (match-end 1)))
   5526        nil t))))
   5527   "Replaced string regexp and function returning image.
   5528 Each element should have the form (MODE . (PROPERTY-LIST...)).
   5529 MODE (car) is function which is defined in `lsp-language-id-configuration'.
   5530 Cdr should be list of PROPERTY-LIST.
   5531 
   5532 Each PROPERTY-LIST should have properties:
   5533 :regexp  Regexp which determines what string is relpaced to image.
   5534          You should also get information of image, by parenthesis constructs.
   5535          By default, all matched string is replaced to image, but you can
   5536          change index of replaced string by keyword :replaced-index.
   5537 
   5538 :sexp    Return image when evaluated. You can use information of regexp
   5539          by using (match-beggining N), (match-end N) or (match-substring N).
   5540 
   5541 In addition, each can have property:
   5542 :replaced-index  Determine index which is used to replace regexp to image.
   5543                  The value means first argument of `match-beginning' and
   5544                  `match-end'. If omitted, interpreted as index 0.")
   5545 
   5546 (defcustom lsp-display-inline-image t
   5547   "Showing inline image or not."
   5548   :group 'lsp-mode
   5549   :type 'boolean)
   5550 
   5551 (defcustom lsp-enable-suggest-server-download t
   5552   "When non-nil enable server downloading suggestions."
   5553   :group 'lsp-mode
   5554   :type 'boolean
   5555   :package-version '(lsp-mode . "9.0.0"))
   5556 
   5557 (defcustom lsp-auto-register-remote-clients t
   5558   "When non-nil register remote when registering the local one."
   5559   :group 'lsp-mode
   5560   :type 'boolean
   5561   :package-version '(lsp-mode . "9.0.0"))
   5562 
   5563 (defun lsp--display-inline-image (mode)
   5564   "Add image property if available."
   5565   (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist))))
   5566     (when (and (display-images-p) lsp-display-inline-image)
   5567       (cl-loop
   5568        for plist in plist-list
   5569        with regexp with replaced-index
   5570        do
   5571        (setq regexp (plist-get plist :regexp))
   5572        (setq replaced-index (or (plist-get plist :replaced-index) 0))
   5573 
   5574        (font-lock-remove-keywords nil (list regexp replaced-index))
   5575        (let ((inhibit-read-only t))
   5576          (save-excursion
   5577            (goto-char (point-min))
   5578            (while (re-search-forward regexp nil t)
   5579              (set-text-properties
   5580               (match-beginning replaced-index) (match-end replaced-index)
   5581               nil)
   5582              (add-text-properties
   5583               (match-beginning replaced-index) (match-end replaced-index)
   5584               `(display ,(eval (plist-get plist :sexp)))))))))))
   5585 
   5586 (defun lsp--fontlock-with-mode (str mode)
   5587   "Fontlock STR with MODE."
   5588   (let ((lsp-buffer-major-mode major-mode))
   5589     (with-temp-buffer
   5590       (with-demoted-errors "Error during doc rendering: %s"
   5591         (insert str)
   5592         (delay-mode-hooks (funcall mode))
   5593         (cl-flet ((window-body-width () lsp-window-body-width))
   5594           ;; This can go wrong in some cases, and the fontification would
   5595           ;; not work as expected.
   5596           ;;
   5597           ;; See #2984
   5598           (ignore-errors (font-lock-ensure))
   5599           (lsp--display-inline-image mode)
   5600           (when (eq mode 'lsp--render-markdown)
   5601             (lsp--fix-markdown-links))))
   5602       (lsp--buffer-string-visible))))
   5603 
   5604 (defun lsp--render-string (str language)
   5605   "Render STR using `major-mode' corresponding to LANGUAGE.
   5606 When language is nil render as markup if `markdown-mode' is loaded."
   5607   (setq str (s-replace "\r" "" (or str "")))
   5608   (if-let* ((modes (-keep (-lambda ((mode . lang))
   5609                             (when (and (equal lang language) (functionp mode))
   5610                               mode))
   5611                           lsp-language-id-configuration))
   5612             (mode (car (or (member major-mode modes) modes))))
   5613       (lsp--fontlock-with-mode str mode)
   5614     str))
   5615 
   5616 (defun lsp--render-element (content)
   5617   "Render CONTENT element."
   5618   (let ((inhibit-message t))
   5619     (or
   5620      (pcase content
   5621        ((lsp-interface MarkedString :value :language)
   5622         (lsp--render-string value language))
   5623        ((lsp-interface MarkupContent :value :kind)
   5624         (lsp--render-string value kind))
   5625        ;; plain string
   5626        ((pred stringp) (lsp--render-string content "markdown"))
   5627        ((pred null) "")
   5628        (_ (error "Failed to handle %s" content)))
   5629      "")))
   5630 
   5631 (defun lsp--create-unique-string-fn ()
   5632   (let (elements)
   5633     (lambda (element)
   5634       (let ((count (cl-count element elements :test #'string=)))
   5635         (prog1 (if (zerop count)
   5636                    element
   5637                  (format "%s (%s)" element count))
   5638           (push element elements))))))
   5639 
   5640 (defun lsp--select-action (actions)
   5641   "Select an action to execute from ACTIONS."
   5642   (cond
   5643    ((seq-empty-p actions) (signal 'lsp-no-code-actions nil))
   5644    ((and (eq (seq-length actions) 1) lsp-auto-execute-action)
   5645     (lsp-seq-first actions))
   5646    (t (let ((completion-ignore-case t))
   5647         (lsp--completing-read "Select code action: "
   5648                               (seq-into actions 'list)
   5649                               (-compose (lsp--create-unique-string-fn)
   5650                                         #'lsp:code-action-title)
   5651                               nil t)))))
   5652 
   5653 (defun lsp--workspace-server-id (workspace)
   5654   "Return the server ID of WORKSPACE."
   5655   (-> workspace lsp--workspace-client lsp--client-server-id))
   5656 
   5657 (defun lsp--handle-rendered-for-echo-area (contents)
   5658   "Return a single line from RENDERED, appropriate for display in the echo area."
   5659   (pcase (lsp-workspaces)
   5660     (`(,workspace)
   5661      (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace)))
   5662     ;; For projects with multiple active workspaces we also default to
   5663     ;; render the first line.
   5664     (_ (lsp-clients-extract-signature-on-hover contents nil))))
   5665 
   5666 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id)
   5667   "Extract a representative line from CONTENTS, to show in the echo area."
   5668   (car (s-lines (s-trim (lsp--render-element contents)))))
   5669 
   5670 (defun lsp--render-on-hover-content (contents render-all)
   5671   "Render the content received from `document/onHover' request.
   5672 CONTENTS  - MarkedString | MarkedString[] | MarkupContent
   5673 RENDER-ALL - nil if only the signature should be rendered."
   5674   (cond
   5675    ((lsp-markup-content? contents)
   5676     ;; MarkupContent.
   5677     ;; It tends to be long and is not suitable to display fully in the echo area.
   5678     ;; Just display the first line which is typically the signature.
   5679     (if render-all
   5680         (lsp--render-element contents)
   5681       (lsp--handle-rendered-for-echo-area contents)))
   5682    ((and (stringp contents) (not (string-match-p "\n" contents)))
   5683     ;; If the contents is a single string containing a single line,
   5684     ;; render it always.
   5685     (lsp--render-element contents))
   5686    (t
   5687     ;; MarkedString -> MarkedString[]
   5688     (when (or (lsp-marked-string? contents) (stringp contents))
   5689       (setq contents (list contents)))
   5690     ;; Consider the signature consisting of the elements who have a renderable
   5691     ;; "language" property. When render-all is nil, ignore other elements.
   5692     (string-join
   5693      (seq-map
   5694       #'lsp--render-element
   5695       (if render-all
   5696           contents
   5697         ;; Only render contents that have an available renderer.
   5698         (seq-take
   5699          (seq-filter
   5700           (-andfn #'lsp-marked-string?
   5701                   (-compose #'lsp-get-renderer #'lsp:marked-string-language))
   5702           contents)
   5703          1)))
   5704      (if (bound-and-true-p page-break-lines-mode)
   5705          "\n\n"
   5706        "\n")))))
   5707 
   5708 
   5709 
   5710 (defvar lsp-signature-mode-map
   5711   (-doto (make-sparse-keymap)
   5712     (define-key (kbd "M-n") #'lsp-signature-next)
   5713     (define-key (kbd "M-p") #'lsp-signature-previous)
   5714     (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs)
   5715     (define-key (kbd "C-c C-k") #'lsp-signature-stop)
   5716     (define-key (kbd "C-g") #'lsp-signature-stop))
   5717   "Keymap for `lsp-signature-mode'.")
   5718 
   5719 (define-minor-mode lsp-signature-mode
   5720   "Mode used to show signature popup."
   5721   :keymap lsp-signature-mode-map
   5722   :lighter ""
   5723   :group 'lsp-mode)
   5724 
   5725 (defun lsp-signature-stop ()
   5726   "Stop showing current signature help."
   5727   (interactive)
   5728   (lsp-cancel-request-by-token :signature)
   5729   (remove-hook 'post-command-hook #'lsp-signature)
   5730   (funcall lsp-signature-function nil)
   5731   (lsp-signature-mode -1))
   5732 
   5733 (declare-function page-break-lines--update-display-tables "ext:page-break-lines")
   5734 
   5735 (defun lsp--setup-page-break-mode-if-present ()
   5736   "Enable `page-break-lines-mode' in current buffer."
   5737   (when (fboundp 'page-break-lines-mode)
   5738     (page-break-lines-mode)
   5739     ;; force page-break-lines-mode to update the display tables.
   5740     (page-break-lines--update-display-tables)))
   5741 
   5742 (defun lsp-lv-message (message)
   5743   (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)
   5744   (if message
   5745       (progn
   5746         (setq lsp--signature-last-buffer (current-buffer))
   5747         (let ((lv-force-update t))
   5748           (lv-message "%s" message)))
   5749     (lv-delete-window)
   5750     (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present)))
   5751 
   5752 (declare-function posframe-show "ext:posframe")
   5753 (declare-function posframe-hide "ext:posframe")
   5754 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe")
   5755 
   5756 (defface lsp-signature-posframe
   5757   '((t :inherit tooltip))
   5758   "Background and foreground for `lsp-signature-posframe'."
   5759   :group 'lsp-mode)
   5760 
   5761 (defvar lsp-signature-posframe-params
   5762   (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward
   5763         :height 10
   5764         :width 60
   5765         :border-width 1
   5766         :min-width 60)
   5767   "Params for signature and `posframe-show'.")
   5768 
   5769 (defun lsp-signature-posframe (str)
   5770   "Use posframe to show the STR signatureHelp string."
   5771   (if str
   5772       (apply #'posframe-show
   5773              (with-current-buffer (get-buffer-create " *lsp-signature*")
   5774                (erase-buffer)
   5775                (insert str)
   5776                (visual-line-mode 1)
   5777                (lsp--setup-page-break-mode-if-present)
   5778                (current-buffer))
   5779              (append
   5780               lsp-signature-posframe-params
   5781               (list :position (point)
   5782                     :background-color (face-attribute 'lsp-signature-posframe :background nil t)
   5783                     :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t)
   5784                     :border-color (face-attribute 'font-lock-comment-face :foreground nil t))))
   5785     (posframe-hide " *lsp-signature*")))
   5786 
   5787 (defun lsp--handle-signature-update (signature)
   5788   (let ((message
   5789          (if (lsp-signature-help? signature)
   5790              (lsp--signature->message signature)
   5791            (mapconcat #'lsp--signature->message signature "\n"))))
   5792     (if (s-present? message)
   5793         (funcall lsp-signature-function message)
   5794       (lsp-signature-stop))))
   5795 
   5796 (defun lsp-signature-activate ()
   5797   "Activate signature help.
   5798 It will show up only if current point has signature help."
   5799   (interactive)
   5800   (setq lsp--signature-last nil
   5801         lsp--signature-last-index nil
   5802         lsp--signature-last-buffer (current-buffer))
   5803   (add-hook 'post-command-hook #'lsp-signature)
   5804   (lsp-signature-mode t))
   5805 
   5806 (defcustom lsp-signature-cycle t
   5807   "Whether `lsp-signature-next' and prev should cycle."
   5808   :type 'boolean
   5809   :group 'lsp-mode)
   5810 
   5811 (defun lsp-signature-next ()
   5812   "Show next signature."
   5813   (interactive)
   5814   (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last))))
   5815     (when (and lsp--signature-last-index
   5816                lsp--signature-last
   5817                (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs)))
   5818       (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs))
   5819       (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))))
   5820 
   5821 (defun lsp-signature-previous ()
   5822   "Next signature."
   5823   (interactive)
   5824   (when (and lsp--signature-last-index
   5825              lsp--signature-last
   5826              (or lsp-signature-cycle (not (zerop lsp--signature-last-index))))
   5827     (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index)
   5828                                             (length (lsp:signature-help-signatures lsp--signature-last))
   5829                                           lsp--signature-last-index)))
   5830     (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))
   5831 
   5832 (defun lsp-signature-toggle-full-docs ()
   5833   "Toggle full/partial signature documentation."
   5834   (interactive)
   5835   (let ((all? (not (numberp lsp-signature-doc-lines))))
   5836     (setq lsp-signature-doc-lines (if all?
   5837                                       (or (car-safe lsp-signature-doc-lines)
   5838                                           20)
   5839                                     (list lsp-signature-doc-lines))))
   5840   (lsp-signature-activate))
   5841 
   5842 (defun lsp--signature->message (signature-help)
   5843   "Generate eldoc message from SIGNATURE-HELP response."
   5844   (setq lsp--signature-last signature-help)
   5845 
   5846   (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help))))
   5847     (-let* (((&SignatureHelp :active-signature?
   5848                              :active-parameter?
   5849                              :signatures) signature-help)
   5850             (active-signature? (or lsp--signature-last-index active-signature? 0))
   5851             (_ (setq lsp--signature-last-index active-signature?))
   5852             ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?))
   5853             (prefix (if (= (length signatures) 1)
   5854                         ""
   5855                       (concat (propertize (format " %s/%s"
   5856                                                   (1+ active-signature?)
   5857                                                   (length signatures))
   5858                                           'face 'success)
   5859                               " ")))
   5860             (method-docs (when
   5861                              (and lsp-signature-render-documentation
   5862                                   (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines)))
   5863                            (let ((docs (lsp--render-element
   5864                                         (lsp:parameter-information-documentation? signature))))
   5865                              (when (s-present? docs)
   5866                                (concat
   5867                                 "\n"
   5868                                 (if (fboundp 'page-break-lines-mode)
   5869                                     "\n"
   5870                                   "")
   5871                                 (if (and (numberp lsp-signature-doc-lines)
   5872                                          (> (length (s-lines docs)) lsp-signature-doc-lines))
   5873                                     (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs)))
   5874                                             (propertize "\nTruncated..." 'face 'highlight))
   5875                                   docs)))))))
   5876       (when (and active-parameter? (not (seq-empty-p parameters?)))
   5877         (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?)))
   5878                               (seq-elt parameters? active-parameter?)))
   5879                      (selected-param-label (let ((label (lsp:parameter-information-label param)))
   5880                                              (if (stringp label) label (append label nil))))
   5881                      (start (if (stringp selected-param-label)
   5882                                 (s-index-of selected-param-label label)
   5883                               (cl-first selected-param-label)))
   5884                      (end (if (stringp selected-param-label)
   5885                               (+ start (length selected-param-label))
   5886                             (cl-second selected-param-label))))
   5887           (add-face-text-property start end 'eldoc-highlight-function-argument nil label)))
   5888       (concat prefix label method-docs))))
   5889 
   5890 (defun lsp-signature ()
   5891   "Display signature info (based on `textDocument/signatureHelp')"
   5892   (if (and lsp--signature-last-buffer
   5893            (not (equal (current-buffer) lsp--signature-last-buffer)))
   5894       (lsp-signature-stop)
   5895     (lsp-request-async "textDocument/signatureHelp"
   5896                        (lsp--text-document-position-params)
   5897                        #'lsp--handle-signature-update
   5898                        :cancel-token :signature)))
   5899 
   5900 
   5901 (defcustom lsp-overlay-document-color-char "■"
   5902   "Display the char represent the document color in overlay"
   5903   :type 'string
   5904   :group 'lsp-mode)
   5905 
   5906 ;; color presentation
   5907 (defun lsp--color-create-interactive-command (color range)
   5908   (lambda ()
   5909     (interactive)
   5910     (-let [(&ColorPresentation? :text-edit?
   5911                                 :additional-text-edits?)
   5912            (lsp--completing-read
   5913             "Select color presentation: "
   5914             (lsp-request
   5915              "textDocument/colorPresentation"
   5916              `( :textDocument ,(lsp--text-document-identifier)
   5917                 :color ,color
   5918                 :range ,range))
   5919             #'lsp:color-presentation-label
   5920             nil
   5921             t)]
   5922       (when text-edit?
   5923         (lsp--apply-text-edit text-edit?))
   5924       (when additional-text-edits?
   5925         (lsp--apply-text-edits additional-text-edits? 'color-presentation)))))
   5926 
   5927 (defun lsp--number->color (number)
   5928   (let ((result (format "%x"
   5929                         (round (* (or number 0) 255.0)))))
   5930     (if (= 1 (length result))
   5931         (concat "0" result)
   5932       result)))
   5933 
   5934 (defun lsp--document-color ()
   5935   "Document color handler."
   5936   (when (lsp-feature? "textDocument/documentColor")
   5937     (lsp-request-async
   5938      "textDocument/documentColor"
   5939      `(:textDocument ,(lsp--text-document-identifier))
   5940      (lambda (result)
   5941        (lsp--remove-overlays 'lsp-color)
   5942        (seq-do
   5943         (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue)
   5944                                      :range))
   5945           (-let* (((beg . end) (lsp--range-to-region range))
   5946                   (overlay (make-overlay beg end))
   5947                   (command (lsp--color-create-interactive-command color range)))
   5948             (overlay-put overlay 'lsp-color t)
   5949             (overlay-put overlay 'evaporate t)
   5950             (overlay-put overlay
   5951                          'before-string
   5952                          (propertize
   5953                           lsp-overlay-document-color-char
   5954                           'face `((:foreground ,(format
   5955                                                  "#%s%s%s"
   5956                                                  (lsp--number->color red)
   5957                                                  (lsp--number->color green)
   5958                                                  (lsp--number->color blue))))
   5959                           'action command
   5960                           'mouse-face 'lsp-lens-mouse-face
   5961                           'local-map (-doto (make-sparse-keymap)
   5962                                        (define-key [mouse-1] command))))))
   5963         result))
   5964      :mode 'unchanged
   5965      :cancel-token :document-color-token)))
   5966 
   5967 
   5968 
   5969 (defun lsp--action-trigger-parameter-hints (_command)
   5970   "Handler for editor.action.triggerParameterHints."
   5971   (when (member :on-server-request lsp-signature-auto-activate)
   5972     (lsp-signature-activate)))
   5973 
   5974 (defun lsp--action-trigger-suggest (_command)
   5975   "Handler for editor.action.triggerSuggest."
   5976   (cond
   5977    ((and (bound-and-true-p company-mode)
   5978          (fboundp 'company-auto-begin)
   5979          (fboundp 'company-post-command))
   5980     (run-at-time 0 nil
   5981                  (lambda ()
   5982                    (let ((this-command 'company-idle-begin)
   5983                          (company-minimum-prefix-length 0))
   5984                      (company-auto-begin)
   5985                      (company-post-command)))))
   5986    (t
   5987     (completion-at-point))))
   5988 
   5989 (defconst lsp--default-action-handlers
   5990   (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints)
   5991       ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest))
   5992   "Default action handlers.")
   5993 
   5994 (defun lsp--find-action-handler (command)
   5995   "Find action handler for particular COMMAND."
   5996   (or
   5997    (--some (-some->> it
   5998              (lsp--workspace-client)
   5999              (lsp--client-action-handlers)
   6000              (gethash command))
   6001            (lsp-workspaces))
   6002    (gethash command lsp--default-action-handlers)))
   6003 
   6004 (defun lsp--text-document-code-action-params (&optional kind)
   6005   "Code action params."
   6006   (list :textDocument (lsp--text-document-identifier)
   6007         :range (if (use-region-p)
   6008                    (lsp--region-to-range (region-beginning) (region-end))
   6009                  (lsp--region-to-range (point) (point)))
   6010         :context `( :diagnostics ,(lsp-cur-possition-diagnostics)
   6011                     ,@(when kind (list :only (vector kind))))))
   6012 
   6013 (defun lsp-code-actions-at-point (&optional kind)
   6014   "Retrieve the code actions for the active region or the current line.
   6015 It will filter by KIND if non nil."
   6016   (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind)))
   6017 
   6018 (defun lsp-execute-code-action-by-kind (command-kind)
   6019   "Execute code action by COMMAND-KIND."
   6020   (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind)
   6021                         (-filter (-lambda ((&CodeAction :kind?))
   6022                                    (and kind? (s-prefix? command-kind kind?))))
   6023                         lsp--select-action)))
   6024       (lsp-execute-code-action action)
   6025     (signal 'lsp-no-code-actions '(command-kind))))
   6026 
   6027 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point)
   6028 
   6029 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?))
   6030   "Parse and execute a code ACTION represented as a Command LSP type."
   6031   (let ((server-id (->> (lsp-workspaces)
   6032                         (cl-first)
   6033                         (or lsp--cur-workspace)
   6034                         (lsp--workspace-client)
   6035                         (lsp--client-server-id))))
   6036     (condition-case nil
   6037         (with-no-warnings
   6038           (lsp-execute-command server-id (intern command) arguments?))
   6039       (cl-no-applicable-method
   6040        (if-let ((action-handler (lsp--find-action-handler command)))
   6041            (funcall action-handler action)
   6042          (lsp-send-execute-command command arguments?))))))
   6043 
   6044 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?))
   6045   "Execute code action ACTION. For example, when text under the
   6046 caret has a suggestion to apply a fix from an lsp-server, calling
   6047 this function will do so.
   6048 If ACTION is not set it will be selected from `lsp-code-actions-at-point'.
   6049 Request codeAction/resolve for more info if server supports."
   6050   (interactive (list (lsp--select-action (lsp-code-actions-at-point))))
   6051   (if (and (lsp-feature? "codeAction/resolve")
   6052            (not command?)
   6053            (not edit?))
   6054       (lsp--execute-code-action (lsp-request "codeAction/resolve" action))
   6055     (lsp--execute-code-action action)))
   6056 
   6057 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?))
   6058   "Execute code action ACTION."
   6059   (when edit?
   6060     (lsp--apply-workspace-edit edit? 'code-action))
   6061 
   6062   (cond
   6063    ((stringp command?) (lsp--execute-command action))
   6064    ((lsp-command? command?) (progn
   6065                               (when-let ((action-filter (->> (lsp-workspaces)
   6066                                                              (cl-first)
   6067                                                              (or lsp--cur-workspace)
   6068                                                              (lsp--workspace-client)
   6069                                                              (lsp--client-action-filter))))
   6070                                 (funcall action-filter command?))
   6071                               (lsp--execute-command command?)))))
   6072 
   6073 (lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments)
   6074   "Patch incorrect boolean argument values in the provided `CodeAction' command
   6075 in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values
   6076 in this list can be either symbols or lists of symbols that
   6077 represent paths to boolean arguments in code actions:
   6078 
   6079 > (lsp-fix-code-action-booleans command `(:foo :bar (:some :nested :boolean)))
   6080 
   6081 When there are available code actions, the server sends
   6082 `lsp-mode' a list of possible command names and arguments as
   6083 JSON. `lsp-mode' parses all boolean false values as `nil'. As a
   6084 result code action arguments containing falsy values don't
   6085 roundtrip correctly because `lsp-mode' will end up sending null
   6086 values back to the client. This list makes it possible to
   6087 selectively transform `nil' values back into `:json-false'."
   6088   (seq-doseq (path boolean-action-arguments)
   6089     (seq-doseq (args arguments?)
   6090       (lsp--fix-nested-boolean args (if (listp path) path (list path))))))
   6091 
   6092 (defun lsp--fix-nested-boolean (structure path)
   6093   "Traverse STRUCTURE using the paths from the PATH list, changing the value to
   6094 `:json-false' if it was `nil'. PATH should be a list containing
   6095 one or more symbols, and STRUCTURE should be compatible with
   6096 `lsp-member?', `lsp-get', and `lsp-put'."
   6097   (let ((key (car path))
   6098         (rest (cdr path)))
   6099     (if (null rest)
   6100         ;; `lsp-put' returns `nil' both when the key doesn't exist and when the
   6101         ;; value is `nil', so we need to explicitly check its presence here
   6102         (when (and (lsp-member? structure key) (not (lsp-get structure key)))
   6103           (lsp-put structure key :json-false))
   6104       ;; If `key' does not exist, then we'll silently ignore it
   6105       (when-let ((child (lsp-get structure key)))
   6106         (lsp--fix-nested-boolean child rest)))))
   6107 
   6108 (defvar lsp--formatting-indent-alist
   6109   ;; Taken from `dtrt-indent-mode'
   6110   '(
   6111     (ada-mode                   . ada-indent)                       ; Ada
   6112     (ada-ts-mode                . ada-ts-mode-indent-offset)
   6113     (c++-mode                   . c-basic-offset)                   ; C++
   6114     (c++-ts-mode                . c-ts-mode-indent-offset)
   6115     (c-mode                     . c-basic-offset)                   ; C
   6116     (c-ts-mode                  . c-ts-mode-indent-offset)
   6117     (cperl-mode                 . cperl-indent-level)               ; Perl
   6118     (crystal-mode               . crystal-indent-level)             ; Crystal (Ruby)
   6119     (csharp-mode                . c-basic-offset)                   ; C#
   6120     (csharp-tree-sitter-mode    . csharp-tree-sitter-indent-offset) ; C#
   6121     (csharp-ts-mode             . csharp-ts-mode-indent-offset)     ; C# (tree-sitter, Emacs29)
   6122     (css-mode                   . css-indent-offset)                ; CSS
   6123     (d-mode                     . c-basic-offset)                   ; D
   6124     (enh-ruby-mode              . enh-ruby-indent-level)            ; Ruby
   6125     (erlang-mode                . erlang-indent-level)              ; Erlang
   6126     (ess-mode                   . ess-indent-offset)                ; ESS (R)
   6127     (go-ts-mode                 . go-ts-mode-indent-offset)
   6128     (gpr-mode                   . gpr-indent-offset)                ; GNAT Project
   6129     (gpr-ts-mode                . gpr-ts-mode-indent-offset)
   6130     (hack-mode                  . hack-indent-offset)               ; Hack
   6131     (java-mode                  . c-basic-offset)                   ; Java
   6132     (java-ts-mode               . java-ts-mode-indent-offset)
   6133     (jde-mode                   . c-basic-offset)                   ; Java (JDE)
   6134     (js-mode                    . js-indent-level)                  ; JavaScript
   6135     (js-ts-mode                 . js-indent-level)
   6136     (js2-mode                   . js2-basic-offset)                 ; JavaScript-IDE
   6137     (js3-mode                   . js3-indent-level)                 ; JavaScript-IDE
   6138     (json-mode                  . js-indent-level)                  ; JSON
   6139     (json-ts-mode               . json-ts-mode-indent-offset)
   6140     (lua-mode                   . lua-indent-level)                 ; Lua
   6141     (lua-ts-mode                . lua-ts-indent-offset)
   6142     (nxml-mode                  . nxml-child-indent)                ; XML
   6143     (objc-mode                  . c-basic-offset)                   ; Objective C
   6144     (pascal-mode                . pascal-indent-level)              ; Pascal
   6145     (perl-mode                  . perl-indent-level)                ; Perl
   6146     (php-mode                   . c-basic-offset)                   ; PHP
   6147     (php-ts-mode                . php-ts-mode-indent-offset)        ; PHP
   6148     (powershell-mode            . powershell-indent)                ; PowerShell
   6149     (powershell-ts-mode         . powershell-ts-mode-indent-offset) ; PowerShell
   6150     (raku-mode                  . raku-indent-offset)               ; Perl6/Raku
   6151     (ruby-mode                  . ruby-indent-level)                ; Ruby
   6152     (rust-mode                  . rust-indent-offset)               ; Rust
   6153     (rust-ts-mode               . rust-ts-mode-indent-offset)
   6154     (rustic-mode                . rustic-indent-offset)             ; Rust
   6155     (scala-mode                 . scala-indent:step)                ; Scala
   6156     (sgml-mode                  . sgml-basic-offset)                ; SGML
   6157     (sh-mode                    . sh-basic-offset)                  ; Shell Script
   6158     (toml-ts-mode               . toml-ts-mode-indent-offset)
   6159     (typescript-mode            . typescript-indent-level)          ; Typescript
   6160     (typescript-ts-mode         . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29)
   6161     (yaml-mode                  . yaml-indent-offset)               ; YAML
   6162     (yang-mode                  . c-basic-offset)                   ; YANG (yang-mode)
   6163 
   6164     (default                    . standard-indent))                 ; default fallback
   6165   "A mapping from `major-mode' to its indent variable.")
   6166 
   6167 (defun lsp--get-indent-width (mode)
   6168   "Get indentation offset for MODE."
   6169   (or (alist-get mode lsp--formatting-indent-alist)
   6170       (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default))))
   6171 
   6172 (defun lsp--make-document-formatting-params ()
   6173   "Create document formatting params."
   6174   (lsp-make-document-formatting-params
   6175    :text-document (lsp--text-document-identifier)
   6176    :options (lsp-make-formatting-options
   6177              :tab-size (symbol-value (lsp--get-indent-width major-mode))
   6178              :insert-spaces (lsp-json-bool (not indent-tabs-mode))
   6179              :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace)
   6180              :insert-final-newline? (lsp-json-bool lsp-insert-final-newline)
   6181              :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines))))
   6182 
   6183 (defun lsp-format-buffer ()
   6184   "Ask the server to format this document."
   6185   (interactive "*")
   6186   (cond ((lsp-feature? "textDocument/formatting")
   6187          (let ((edits (lsp-request "textDocument/formatting"
   6188                                    (lsp--make-document-formatting-params))))
   6189            (if (seq-empty-p edits)
   6190                (lsp--info "No formatting changes provided")
   6191              (lsp--apply-text-edits edits 'format))))
   6192         ((lsp-feature? "textDocument/rangeFormatting")
   6193          (save-restriction
   6194            (widen)
   6195            (lsp-format-region (point-min) (point-max))))
   6196         (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider")))))
   6197 
   6198 (defun lsp-format-region (s e)
   6199   "Ask the server to format the region, or if none is selected, the current line."
   6200   (interactive "r")
   6201   (let ((edits (lsp-request
   6202                 "textDocument/rangeFormatting"
   6203                 (lsp--make-document-range-formatting-params s e))))
   6204     (if (seq-empty-p edits)
   6205         (lsp--info "No formatting changes provided")
   6206       (lsp--apply-text-edits edits 'format))))
   6207 
   6208 (defmacro lsp-make-interactive-code-action (func-name code-action-kind)
   6209   "Define an interactive function FUNC-NAME that attempts to
   6210 execute a CODE-ACTION-KIND action."
   6211   `(defun ,(intern (concat "lsp-" (symbol-name func-name))) ()
   6212      ,(format "Perform the %s code action, if available." code-action-kind)
   6213      (interactive)
   6214      ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to
   6215      ;; auto-execute here: the user has specified exactly what they want.
   6216      (let ((lsp-auto-execute-action t))
   6217        (condition-case nil
   6218            (lsp-execute-code-action-by-kind ,code-action-kind)
   6219          (lsp-no-code-actions
   6220           (when (called-interactively-p 'any)
   6221             (lsp--info ,(format "%s action not available" code-action-kind))))))))
   6222 
   6223 (lsp-make-interactive-code-action organize-imports "source.organizeImports")
   6224 
   6225 (defun lsp--make-document-range-formatting-params (start end)
   6226   "Make DocumentRangeFormattingParams for selected region."
   6227   (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params)
   6228                                                   (lsp--region-to-range start end)))
   6229 
   6230 (defconst lsp--highlight-kind-face
   6231   '((1 . lsp-face-highlight-textual)
   6232     (2 . lsp-face-highlight-read)
   6233     (3 . lsp-face-highlight-write)))
   6234 
   6235 (defun lsp--remove-overlays (name)
   6236   (save-restriction
   6237     (widen)
   6238     (remove-overlays (point-min) (point-max) name t)))
   6239 
   6240 (defun lsp-document-highlight ()
   6241   "Highlight all relevant references to the symbol under point."
   6242   (interactive)
   6243   (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights
   6244   (setq lsp--have-document-highlights nil
   6245         lsp--symbol-bounds-of-last-highlight-invocation nil)
   6246   (let ((lsp-enable-symbol-highlighting t))
   6247     (lsp--document-highlight)))
   6248 
   6249 (defun lsp--document-highlight-callback (highlights)
   6250   "Create a callback to process the reply of a
   6251 `textDocument/documentHighlight' message for the buffer BUF.
   6252 A reference is highlighted only if it is visible in a window."
   6253   (lsp--remove-overlays 'lsp-highlight)
   6254 
   6255   (let* ((wins-visible-pos (-map (lambda (win)
   6256                                    (cons (1- (line-number-at-pos (window-start win) t))
   6257                                          (1+ (line-number-at-pos (window-end win) t))))
   6258                                  (get-buffer-window-list nil nil 'visible))))
   6259     (setq lsp--have-document-highlights t)
   6260     (-map
   6261      (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line)
   6262                                                   :end (end &as &Position :line end-line))
   6263                                    :kind?))
   6264        (-map
   6265         (-lambda ((start-window . end-window))
   6266           ;; Make the overlay only if the reference is visible
   6267           (when (and (> (1+ start-line) start-window)
   6268                      (< (1+ end-line) end-window))
   6269             (let ((start-point (lsp--position-to-point start))
   6270                   (end-point (lsp--position-to-point end)))
   6271               (when (not (and lsp-symbol-highlighting-skip-current
   6272                               (<= start-point (point) end-point)))
   6273                 (-doto (make-overlay start-point end-point)
   6274                   (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face)))
   6275                   (overlay-put 'lsp-highlight t))))))
   6276         wins-visible-pos))
   6277      highlights)))
   6278 
   6279 (defcustom lsp-symbol-kinds
   6280   '((1 . "File")
   6281     (2 . "Module")
   6282     (3 . "Namespace")
   6283     (4 . "Package")
   6284     (5 . "Class")
   6285     (6 . "Method")
   6286     (7 . "Property")
   6287     (8 . "Field")
   6288     (9 . "Constructor")
   6289     (10 . "Enum")
   6290     (11 . "Interface")
   6291     (12 . "Function")
   6292     (13 . "Variable")
   6293     (14 . "Constant")
   6294     (15 . "String")
   6295     (16 . "Number")
   6296     (17 . "Boolean")
   6297     (18 . "Array")
   6298     (19 . "Object")
   6299     (20 . "Key")
   6300     (21 . "Null")
   6301     (22 . "Enum Member")
   6302     (23 . "Struct")
   6303     (24 . "Event")
   6304     (25 . "Operator")
   6305     (26 . "Type Parameter"))
   6306   "Alist mapping SymbolKinds to human-readable strings.
   6307 Various Symbol objects in the LSP protocol have an integral type,
   6308 specifying what they are. This alist maps such type integrals to
   6309 readable representations of them. See
   6310 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/',
   6311 namespace SymbolKind."
   6312   :group 'lsp-mode
   6313   :type '(alist :key-type integer :value-type string))
   6314 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds)
   6315 
   6316 (lsp-defun lsp--symbol-information-to-xref
   6317   ((&SymbolInformation :kind :name
   6318                        :location (&Location :uri :range (&Range :start
   6319                                                                 (&Position :line :character)))))
   6320   "Return a `xref-item' from SYMBOL information."
   6321   (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name)
   6322              (xref-make-file-location (lsp--uri-to-path uri)
   6323                                       line
   6324                                       character)))
   6325 
   6326 (defun lsp--get-document-symbols ()
   6327   "Get document symbols.
   6328 
   6329 If the buffer has not been modified since symbols were last
   6330 retrieved, simply return the latest result.
   6331 
   6332 Else, if the request was initiated by Imenu updating its menu-bar
   6333 entry, perform it asynchronously; i.e., give Imenu the latest
   6334 result and then force a refresh when a new one is available.
   6335 
   6336 Else (e.g., due to interactive use of `imenu' or `xref'),
   6337 perform the request synchronously."
   6338   (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick)
   6339       lsp--document-symbols
   6340     (let ((method "textDocument/documentSymbol")
   6341           (params `(:textDocument ,(lsp--text-document-identifier)))
   6342           (tick (buffer-chars-modified-tick)))
   6343       (if (not lsp--document-symbols-request-async)
   6344           (prog1
   6345               (setq lsp--document-symbols (lsp-request method params))
   6346             (setq lsp--document-symbols-tick tick))
   6347         (lsp-request-async method params
   6348                            (lambda (document-symbols)
   6349                              (setq lsp--document-symbols document-symbols
   6350                                    lsp--document-symbols-tick tick)
   6351                              (lsp--imenu-refresh))
   6352                            :mode 'alive
   6353                            :cancel-token :document-symbols)
   6354         lsp--document-symbols))))
   6355 
   6356 (advice-add 'imenu-update-menubar :around
   6357             (lambda (oldfun &rest r)
   6358               (let ((lsp--document-symbols-request-async t))
   6359                 (apply oldfun r))))
   6360 
   6361 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position)
   6362   "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION."
   6363   (-let (((symbol &as &DocumentSymbol? :children?)
   6364           (seq-find (-lambda ((&DocumentSymbol :range))
   6365                       (lsp-point-in-range? current-position range))
   6366                     document-symbols)))
   6367     (if children?
   6368         (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position))
   6369       (when symbol
   6370         (list symbol)))))
   6371 
   6372 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?))
   6373   "Convert a SymbolInformation to a DocumentInformation"
   6374   (lsp-make-document-symbol :name name
   6375                             :kind kind
   6376                             :range (lsp:location-range location)
   6377                             :children? nil
   6378                             :deprecated? deprecated?
   6379                             :selection-range (lsp:location-range location)
   6380                             :detail? container-name?))
   6381 
   6382 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position)
   6383   "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION."
   6384   (--> symbols-informations
   6385     (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range)))
   6386              (when (lsp-point-in-range? current-position range)
   6387                (lsp--symbol-information->document-symbol symbol)))
   6388            it)
   6389     (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position))
   6390                        (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position)))
   6391                (and (lsp--position-compare b-start-position a-start-position)
   6392                     (lsp--position-compare a-end-position b-end-position))))))
   6393 
   6394 (defun lsp--symbols->document-symbols-hierarchy (symbols)
   6395   "Convert SYMBOLS to symbols-hierarchy."
   6396   (when-let ((first-symbol (lsp-seq-first symbols)))
   6397     (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line)
   6398                                            :character (plist-get (lsp--cur-position) :character))))
   6399       (if (lsp-symbol-information? first-symbol)
   6400           (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position)
   6401         (lsp--document-symbols->document-symbols-hierarchy symbols cur-position)))))
   6402 
   6403 (defun lsp--xref-backend () 'xref-lsp)
   6404 
   6405 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp)))
   6406   (propertize (or (thing-at-point 'symbol) "")
   6407               'identifier-at-point t))
   6408 
   6409 (defun lsp--xref-elements-index (symbols path)
   6410   (-mapcat
   6411    (-lambda (sym)
   6412      (pcase-exhaustive sym
   6413        ((lsp-interface DocumentSymbol :name :children? :selection-range (lsp-interface Range :start))
   6414         (cons (cons (concat path name)
   6415                     (lsp--position-to-point start))
   6416               (lsp--xref-elements-index children? (concat path name " / "))))
   6417        ((lsp-interface SymbolInformation :name :location (lsp-interface Location :range (lsp-interface Range :start)))
   6418         (list (cons (concat path name)
   6419                     (lsp--position-to-point start))))))
   6420    symbols))
   6421 
   6422 (defvar-local lsp--symbols-cache nil)
   6423 
   6424 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp)))
   6425   (if (lsp--find-workspaces-for "textDocument/documentSymbol")
   6426       (progn
   6427         (setq lsp--symbols-cache (lsp--xref-elements-index
   6428                                   (lsp--get-document-symbols) nil))
   6429         lsp--symbols-cache)
   6430     (list (propertize (or (thing-at-point 'symbol) "")
   6431                       'identifier-at-point t))))
   6432 
   6433 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier)
   6434   (save-excursion
   6435     (unless (get-text-property 0 'identifier-at-point identifier)
   6436       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6437                               (user-error "Unable to find symbol %s in current document" identifier)))))
   6438     (lsp--locations-to-xref-items (lsp-request "textDocument/definition"
   6439                                                (lsp--text-document-position-params)))))
   6440 
   6441 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier)
   6442   (save-excursion
   6443     (unless (get-text-property 0 'identifier-at-point identifier)
   6444       (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache)
   6445                               (user-error "Unable to find symbol %s" identifier)))))
   6446     (lsp--locations-to-xref-items (lsp-request "textDocument/references"
   6447                                                (lsp--make-reference-params nil lsp-references-exclude-definition)))))
   6448 
   6449 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern)
   6450   (seq-map #'lsp--symbol-information-to-xref
   6451            (lsp-request "workspace/symbol" `(:query ,pattern))))
   6452 
   6453 (defcustom lsp-rename-use-prepare t
   6454   "Whether `lsp-rename' should do a prepareRename first.
   6455 For some language servers, textDocument/prepareRename might be
   6456 too slow, in which case this variable may be set to nil.
   6457 `lsp-rename' will then use `thing-at-point' `symbol' to determine
   6458 the symbol to rename at point."
   6459   :group 'lsp-mode
   6460   :type 'boolean)
   6461 
   6462 (defun lsp--get-symbol-to-rename ()
   6463   "Get a symbol to rename and placeholder at point.
   6464 Returns a cons ((START . END) . PLACEHOLDER?), and nil if
   6465 renaming is generally supported but cannot be done at point.
   6466 START and END are the bounds of the identifiers being renamed,
   6467 while PLACEHOLDER?, is either nil or a string suggested by the
   6468 language server as the initial input of a new-name prompt."
   6469   (unless (lsp-feature? "textDocument/rename")
   6470     (error "The connected server(s) doesn't support renaming"))
   6471   (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename"))
   6472       (when-let ((response
   6473                   (lsp-request "textDocument/prepareRename"
   6474                                (lsp--text-document-position-params))))
   6475         (let* ((bounds (lsp--range-to-region
   6476                         (if (lsp-range? response)
   6477                             response
   6478                           (lsp:prepare-rename-result-range response))))
   6479                (placeholder
   6480                 (and (not (lsp-range? response))
   6481                      (lsp:prepare-rename-result-placeholder response))))
   6482           (cons bounds placeholder)))
   6483     (when-let ((bounds (bounds-of-thing-at-point 'symbol)))
   6484       (cons bounds nil))))
   6485 
   6486 (defface lsp-face-rename '((t :underline t))
   6487   "Face used to highlight the identifier being renamed.
   6488 Renaming can be done using `lsp-rename'."
   6489   :group 'lsp-mode)
   6490 
   6491 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face))
   6492   "Face used to display the rename placeholder in.
   6493 When calling `lsp-rename' interactively, this will be the face of
   6494 the new name."
   6495   :group 'lsp-mode)
   6496 
   6497 (defvar lsp-rename-history '()
   6498   "History for `lsp--read-rename'.")
   6499 
   6500 (defun lsp--read-rename (at-point)
   6501   "Read a new name for a `lsp-rename' at `point' from the user.
   6502 AT-POINT shall be a structure as returned by
   6503 `lsp--get-symbol-to-rename'.
   6504 
   6505 Returns a string, which should be the new name for the identifier
   6506 at point. If renaming cannot be done at point (as determined from
   6507 AT-POINT), throw a `user-error'.
   6508 
   6509 This function is for use in `lsp-rename' only, and shall not be
   6510 relied upon."
   6511   (unless at-point
   6512     (user-error "`lsp-rename' is invalid here"))
   6513   (-let* ((((start . end) . placeholder?) at-point)
   6514           ;; Do the `buffer-substring' first to not include `lsp-face-rename'
   6515           (rename-me (buffer-substring start end))
   6516           (placeholder (or placeholder? rename-me))
   6517           (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face))
   6518 
   6519           overlay)
   6520     ;; We need unwind protect, as the user might cancel here, causing the
   6521     ;; overlay to linger.
   6522     (unwind-protect
   6523         (progn
   6524           (setq overlay (make-overlay start end))
   6525           (overlay-put overlay 'face 'lsp-face-rename)
   6526 
   6527           (read-string (format "Rename %s to: " rename-me) placeholder
   6528                        'lsp-rename-history))
   6529       (and overlay (delete-overlay overlay)))))
   6530 
   6531 (defun lsp-rename (newname)
   6532   "Rename the symbol (and all references to it) under point to NEWNAME."
   6533   (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename))))
   6534   (when-let ((edits (lsp-request "textDocument/rename"
   6535                                  `( :textDocument ,(lsp--text-document-identifier)
   6536                                     :position ,(lsp--cur-position)
   6537                                     :newName ,newname))))
   6538     (lsp--apply-workspace-edit edits 'rename)))
   6539 
   6540 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?)
   6541   "Advice around function `rename-file'.
   6542 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?.
   6543 
   6544 This advice sends workspace/willRenameFiles before renaming file
   6545 to check if server wants to apply any workspaceEdits after renamed."
   6546   (if (and lsp-apply-edits-after-file-operations
   6547            (lsp--send-will-rename-files-p old-name))
   6548       (let ((params (lsp-make-rename-files-params
   6549                      :files (vector (lsp-make-file-rename
   6550                                      :oldUri (lsp--path-to-uri old-name)
   6551                                      :newUri (lsp--path-to-uri new-name))))))
   6552         (when-let ((edits (lsp-request "workspace/willRenameFiles" params)))
   6553           (lsp--apply-workspace-edit edits 'rename-file)
   6554           (funcall old-func old-name new-name ok-if-already-exists?)
   6555           (when (lsp--send-did-rename-files-p)
   6556             (lsp-notify "workspace/didRenameFiles" params))))
   6557     (funcall old-func old-name new-name ok-if-already-exists?)))
   6558 
   6559 (advice-add 'rename-file :around #'lsp--on-rename-file)
   6560 
   6561 (defcustom lsp-xref-force-references nil
   6562   "If non-nil threat everything as references(e. g. jump if only one item.)"
   6563   :group 'lsp-mode
   6564   :type 'boolean)
   6565 
   6566 (defun lsp-show-xrefs (xrefs display-action references?)
   6567   (unless (region-active-p) (push-mark nil t))
   6568   (if (boundp 'xref-show-definitions-function)
   6569       (with-no-warnings
   6570         (xref-push-marker-stack)
   6571         (funcall (if (and references? (not lsp-xref-force-references))
   6572                      xref-show-xrefs-function
   6573                    xref-show-definitions-function)
   6574                  (-const xrefs)
   6575                  `((window . ,(selected-window))
   6576                    (display-action . ,display-action)
   6577                    ,(if (and references? (not lsp-xref-force-references))
   6578                         `(auto-jump . ,xref-auto-jump-to-first-xref)
   6579                       `(auto-jump . ,xref-auto-jump-to-first-definition)))))
   6580     (xref--show-xrefs xrefs display-action)))
   6581 
   6582 (cl-defmethod seq-empty-p ((ht hash-table))
   6583   "Function `seq-empty-p' for hash-table."
   6584   (hash-table-empty-p ht))
   6585 
   6586 (cl-defun lsp-find-locations (method &optional extra &key display-action references?)
   6587   "Send request named METHOD and get cross references of the symbol under point.
   6588 EXTRA is a plist of extra parameters.
   6589 REFERENCES? t when METHOD returns references."
   6590   (let ((loc (lsp-request method
   6591                           (append (lsp--text-document-position-params) extra))))
   6592     (if (seq-empty-p loc)
   6593         (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) ""))
   6594       (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?))))
   6595 
   6596 (cl-defun lsp-find-declaration (&key display-action)
   6597   "Find declarations of the symbol under point."
   6598   (interactive)
   6599   (lsp-find-locations "textDocument/declaration" nil :display-action display-action))
   6600 
   6601 (cl-defun lsp-find-definition (&key display-action)
   6602   "Find definitions of the symbol under point."
   6603   (interactive)
   6604   (lsp-find-locations "textDocument/definition" nil :display-action display-action))
   6605 
   6606 (defun lsp-find-definition-mouse (click)
   6607   "Click to start `lsp-find-definition' at clicked point."
   6608   (interactive "e")
   6609   (let* ((ec (event-start click))
   6610          (p1 (posn-point ec))
   6611          (w1 (posn-window ec)))
   6612     (select-window w1)
   6613     (goto-char p1)
   6614     (lsp-find-definition)))
   6615 
   6616 (cl-defun lsp-find-implementation (&key display-action)
   6617   "Find implementations of the symbol under point."
   6618   (interactive)
   6619   (lsp-find-locations "textDocument/implementation"
   6620                       nil
   6621                       :display-action display-action
   6622                       :references? t))
   6623 
   6624 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action)
   6625   "Find references of the symbol under point."
   6626   (interactive "P")
   6627   (lsp-find-locations "textDocument/references"
   6628                       (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition)))))
   6629                       :display-action display-action
   6630                       :references? t))
   6631 
   6632 (cl-defun lsp-find-type-definition (&key display-action)
   6633   "Find type definitions of the symbol under point."
   6634   (interactive)
   6635   (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action))
   6636 
   6637 (defalias 'lsp-find-custom #'lsp-find-locations)
   6638 (defalias 'lsp-goto-implementation #'lsp-find-implementation)
   6639 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition)
   6640 
   6641 (with-eval-after-load 'evil
   6642   (evil-set-command-property 'lsp-find-definition :jump t)
   6643   (evil-set-command-property 'lsp-find-implementation :jump t)
   6644   (evil-set-command-property 'lsp-find-references :jump t)
   6645   (evil-set-command-property 'lsp-find-type-definition :jump t))
   6646 
   6647 (defun lsp--workspace-method-supported? (check-command method capability workspace)
   6648   (with-lsp-workspace workspace
   6649     (if check-command
   6650         (funcall check-command workspace)
   6651       (or
   6652        (when capability (lsp--capability capability))
   6653        (lsp--registered-capability method)
   6654        (and (not capability) (not check-command))))))
   6655 
   6656 (defun lsp-disable-method-for-server (method server-id)
   6657   "Disable METHOD for SERVER-ID."
   6658   (cl-callf
   6659       (lambda (reqs)
   6660         (-let (((&plist :check-command :capability) reqs))
   6661           (list :check-command
   6662                 (lambda (workspace)
   6663                   (unless (-> workspace
   6664                               lsp--workspace-client
   6665                               lsp--client-server-id
   6666                               (eq server-id))
   6667                     (lsp--workspace-method-supported? check-command
   6668                                                       method
   6669                                                       capability
   6670                                                       workspace))))))
   6671       (alist-get method lsp-method-requirements nil nil 'string=)))
   6672 
   6673 (defun lsp--find-workspaces-for (msg-or-method)
   6674   "Find all workspaces in the current project that can handle MSG."
   6675   (let ((method (if (stringp msg-or-method)
   6676                     msg-or-method
   6677                   (plist-get msg-or-method :method))))
   6678     (-if-let (reqs (cdr (assoc method lsp-method-requirements)))
   6679         (-let (((&plist :capability :check-command) reqs))
   6680           (-filter
   6681            (-partial #'lsp--workspace-method-supported?
   6682                      check-command method capability)
   6683            (lsp-workspaces)))
   6684       (lsp-workspaces))))
   6685 
   6686 (defun lsp-can-execute-command? (command-name)
   6687   "Returns non-nil if current language server(s) can execute COMMAND-NAME.
   6688 The command is executed via `workspace/executeCommand'"
   6689   (cl-position
   6690    command-name
   6691    (lsp:execute-command-options-commands
   6692     (lsp:server-capabilities-execute-command-provider?
   6693      (lsp--server-capabilities)))
   6694    :test #'equal))
   6695 
   6696 (defalias 'lsp-feature? 'lsp--find-workspaces-for)
   6697 
   6698 (cl-defmethod lsp-execute-command (_server _command _arguments)
   6699   "Dispatch COMMAND execution."
   6700   (signal 'cl-no-applicable-method nil))
   6701 
   6702 (defun lsp-workspace-command-execute (command &optional args)
   6703   "Execute workspace COMMAND with ARGS."
   6704   (condition-case-unless-debug err
   6705       (let ((params (if args
   6706                         (list :command command :arguments args)
   6707                       (list :command command))))
   6708         (lsp-request "workspace/executeCommand" params))
   6709     (error
   6710      (error "`workspace/executeCommand' with `%s' failed.\n\n%S"
   6711             command err))))
   6712 
   6713 (defun lsp-send-execute-command (command &optional args)
   6714   "Create and send a `workspace/executeCommand' message having command COMMAND
   6715 and optional ARGS."
   6716   (lsp-workspace-command-execute command args))
   6717 
   6718 (defalias 'lsp-point-to-position #'lsp--point-to-position)
   6719 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier)
   6720 (defalias 'lsp--send-execute-command #'lsp-send-execute-command)
   6721 (defalias 'lsp-on-open #'lsp--text-document-did-open)
   6722 (defalias 'lsp-on-save #'lsp--text-document-did-save)
   6723 
   6724 (defun lsp--set-configuration (settings)
   6725   "Set the SETTINGS for the lsp server."
   6726   (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings)))
   6727 
   6728 (defun lsp-current-buffer ()
   6729   (or lsp--virtual-buffer
   6730       (current-buffer)))
   6731 
   6732 (defun lsp-buffer-live-p (buffer-id)
   6733   (if-let ((buffer-live (plist-get buffer-id :buffer-live?)))
   6734       (funcall buffer-live buffer-id)
   6735     (buffer-live-p buffer-id)))
   6736 
   6737 (defun lsp--on-set-visited-file-name (old-func &rest args)
   6738   "Advice around function `set-visited-file-name'.
   6739 
   6740 This advice sends textDocument/didClose for the old file and
   6741 textDocument/didOpen for the new file."
   6742   (when lsp--cur-workspace
   6743     (lsp--text-document-did-close t))
   6744   (prog1 (apply old-func args)
   6745     (when lsp--cur-workspace
   6746       (lsp--text-document-did-open))))
   6747 
   6748 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name)
   6749 
   6750 (defcustom lsp-flush-delayed-changes-before-next-message t
   6751   "If non-nil send the document changes update before sending other messages.
   6752 
   6753 If nil, and `lsp-debounce-full-sync-notifications' is non-nil,
   6754  change notifications will be throttled by
   6755  `lsp-debounce-full-sync-notifications-interval' regardless of
   6756  other messages."
   6757   :group 'lsp-mode
   6758   :type 'boolean)
   6759 
   6760 (defvar lsp--not-flushing-delayed-changes t)
   6761 
   6762 (defun lsp--send-no-wait (message proc)
   6763   "Send MESSAGE to PROC without waiting for further output."
   6764 
   6765   (when (and lsp--not-flushing-delayed-changes
   6766              lsp-flush-delayed-changes-before-next-message)
   6767     (let ((lsp--not-flushing-delayed-changes nil))
   6768       (lsp--flush-delayed-changes)))
   6769   (lsp-process-send proc message))
   6770 
   6771 (define-error 'lsp-parse-error
   6772   "Error parsing message from language server" 'lsp-error)
   6773 (define-error 'lsp-unknown-message-type
   6774   "Unknown message type" '(lsp-error lsp-parse-error))
   6775 (define-error 'lsp-unknown-json-rpc-version
   6776   "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error))
   6777 (define-error 'lsp-no-content-length
   6778   "Content-Length header missing in message" '(lsp-error lsp-parse-error))
   6779 (define-error 'lsp-invalid-header-name
   6780   "Invalid header name" '(lsp-error lsp-parse-error))
   6781 
   6782 ;;  id  method
   6783 ;;   x    x     request
   6784 ;;   x    .     response
   6785 ;;   .    x     notification
   6786 (defun lsp--get-message-type (json-data)
   6787   "Get the message type from JSON-DATA."
   6788   (if (lsp:json-message-id? json-data)
   6789       (if (lsp:json-message-error? json-data)
   6790           'response-error
   6791         (if (lsp:json-message-method? json-data)
   6792             'request
   6793           'response))
   6794     'notification))
   6795 
   6796 (defconst lsp--default-notification-handlers
   6797   (ht ("window/showMessage" #'lsp--window-show-message)
   6798       ("window/logMessage" #'lsp--window-log-message)
   6799       ("window/showInputBox" #'lsp--window-show-input-box)
   6800       ("window/showQuickPick" #'lsp--window-show-quick-pick)
   6801       ("textDocument/publishDiagnostics" #'lsp--on-diagnostics)
   6802       ("textDocument/diagnosticsEnd" #'ignore)
   6803       ("textDocument/diagnosticsBegin" #'ignore)
   6804       ("telemetry/event" #'ignore)
   6805       ("$/progress" (lambda (workspace params)
   6806                       (funcall lsp-progress-function workspace params)))))
   6807 
   6808 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method))
   6809   "Call the appropriate handler for NOTIFICATION."
   6810   (-let ((client (lsp--workspace-client workspace)))
   6811     (when (lsp--log-io-p method)
   6812       (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif)
   6813                           lsp--cur-workspace))
   6814     (if-let ((handler (or (gethash method (lsp--client-notification-handlers client))
   6815                           (gethash method lsp--default-notification-handlers))))
   6816         (funcall handler workspace params)
   6817       (when (and method (not (string-prefix-p "$" method)))
   6818         (lsp-warn "Unknown notification: %s" method)))))
   6819 
   6820 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items))
   6821   "Get section configuration.
   6822 PARAMS are the `workspace/configuration' request params"
   6823   (->> items
   6824        (-map (-lambda ((&ConfigurationItem :section?))
   6825                (-let* ((path-parts (split-string section? "\\."))
   6826                        (path-without-last (s-join "." (-slice path-parts 0 -1)))
   6827                        (path-parts-len (length path-parts)))
   6828                  (cond
   6829                   ((<= path-parts-len 1)
   6830                    (ht-get (lsp-configuration-section section?)
   6831                            (car-safe path-parts)
   6832                            (ht-create)))
   6833                   ((> path-parts-len 1)
   6834                    (when-let ((section (lsp-configuration-section path-without-last))
   6835                               (keys path-parts))
   6836                      (while (and keys section)
   6837                        (setf section (ht-get section (pop keys))))
   6838                      section))))))
   6839        (apply #'vector)))
   6840 
   6841 (defun lsp--ms-since (timestamp)
   6842   "Integer number of milliseconds since TIMESTAMP.  Fractions discarded."
   6843   (floor (* 1000 (float-time (time-since timestamp)))))
   6844 
   6845 (defun lsp--send-request-response (workspace recv-time request response)
   6846   "Send the RESPONSE for REQUEST in WORKSPACE and log if needed."
   6847   (-let* (((&JSONResponse :params :method :id) request)
   6848           (process (lsp--workspace-proc workspace))
   6849           (response (lsp--make-response id response))
   6850           (req-entry (and lsp-log-io
   6851                           (lsp--make-log-entry method id params 'incoming-req)))
   6852           (resp-entry (and lsp-log-io
   6853                            (lsp--make-log-entry method id response 'outgoing-resp
   6854                                                 (lsp--ms-since recv-time)))))
   6855     ;; Send response to the server.
   6856     (when (lsp--log-io-p method)
   6857       (lsp--log-entry-new req-entry workspace)
   6858       (lsp--log-entry-new resp-entry workspace))
   6859     (lsp--send-no-wait response process)))
   6860 
   6861 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method))
   6862   "Call the appropriate handler for REQUEST, and send the return value to the
   6863 server. WORKSPACE is the active workspace."
   6864   (-let* ((recv-time (current-time))
   6865           (client (lsp--workspace-client workspace))
   6866           (buffers (lsp--workspace-buffers workspace))
   6867           handler
   6868           (response (cond
   6869                      ((setq handler (gethash method (lsp--client-request-handlers client) nil))
   6870                       (funcall handler workspace params))
   6871                      ((setq handler (gethash method (lsp--client-async-request-handlers client) nil))
   6872                       (funcall handler workspace params
   6873                                (-partial #'lsp--send-request-response
   6874                                          workspace recv-time request))
   6875                       'delay-response)
   6876                      ((equal method "client/registerCapability")
   6877                       (mapc #'lsp--server-register-capability
   6878                             (lsp:registration-params-registrations params))
   6879                       (mapc (lambda (buf)
   6880                               (when (lsp-buffer-live-p buf)
   6881                                 (lsp-with-current-buffer buf
   6882                                   (lsp-unconfig-buffer)
   6883                                   (lsp-configure-buffer))))
   6884                             buffers)
   6885                       nil)
   6886                      ((equal method "window/showMessageRequest")
   6887                       (let ((choice (lsp--window-log-message-request params)))
   6888                         `(:title ,choice)))
   6889                      ((equal method "window/showDocument")
   6890                       (let ((success? (lsp--window-show-document params)))
   6891                         (lsp-make-show-document-result :success (or success?
   6892                                                                     :json-false))))
   6893                      ((equal method "client/unregisterCapability")
   6894                       (mapc #'lsp--server-unregister-capability
   6895                             (lsp:unregistration-params-unregisterations params))
   6896                       (mapc (lambda (buf)
   6897                               (when (lsp-buffer-live-p buf)
   6898                                 (lsp-with-current-buffer buf
   6899                                   (lsp-unconfig-buffer)
   6900                                   (lsp-configure-buffer))))
   6901                             buffers)
   6902                       nil)
   6903                      ((equal method "workspace/applyEdit")
   6904                       (list :applied (condition-case err
   6905                                          (prog1 t
   6906                                            (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested))
   6907                                        (error
   6908                                         (lsp--error "Failed to apply edits with message %s"
   6909                                                     (error-message-string err))
   6910                                         :json-false))))
   6911                      ((equal method "workspace/configuration")
   6912                       (with-lsp-workspace workspace
   6913                         (if-let ((buf (car buffers)))
   6914                             (lsp-with-current-buffer buf
   6915                               (lsp--build-workspace-configuration-response params))
   6916                           (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace)
   6917                             (lsp--build-workspace-configuration-response params)))))
   6918                      ((equal method "workspace/workspaceFolders")
   6919                       (let ((folders (or (-> workspace
   6920                                              (lsp--workspace-client)
   6921                                              (lsp--client-server-id)
   6922                                              (gethash (lsp-session-server-id->folders (lsp-session))))
   6923                                          (lsp-session-folders (lsp-session)))))
   6924                         (->> folders
   6925                              (-distinct)
   6926                              (-map (lambda (folder)
   6927                                      (list :uri (lsp--path-to-uri folder))))
   6928                              (apply #'vector))))
   6929                      ((equal method "window/workDoneProgress/create")
   6930                       nil ;; no specific reply, no processing required
   6931                       )
   6932                      ((equal method "workspace/semanticTokens/refresh")
   6933                       (when (and lsp-semantic-tokens-enable
   6934                                  (fboundp 'lsp--semantic-tokens-on-refresh))
   6935                         (lsp--semantic-tokens-on-refresh workspace))
   6936                       nil)
   6937                      ((equal method "workspace/codeLens/refresh")
   6938                       (when (and lsp-lens-enable
   6939                                  (fboundp 'lsp--lens-on-refresh))
   6940                         (lsp--lens-on-refresh workspace))
   6941                       nil)
   6942                      (t (lsp-warn "Unknown request method: %s" method) nil))))
   6943     ;; Send response to the server.
   6944     (unless (eq response 'delay-response)
   6945       (lsp--send-request-response workspace recv-time request response))))
   6946 
   6947 (lsp-defun lsp--error-string ((&JSONError :message :code))
   6948   "Format ERR as a user friendly string."
   6949   (format "Error from the Language Server: %s (%s)"
   6950           message
   6951           (or (car (alist-get code lsp--errors)) "Unknown error")))
   6952 
   6953 (defun lsp--get-body-length (headers)
   6954   (let ((content-length (cdr (assoc "Content-Length" headers))))
   6955     (if content-length
   6956         (string-to-number content-length)
   6957 
   6958       ;; This usually means either the server or our parser is
   6959       ;; screwed up with a previous Content-Length
   6960       (error "No Content-Length header"))))
   6961 
   6962 (defun lsp--parse-header (s)
   6963   "Parse string S as a LSP (KEY . VAL) header."
   6964   (let ((pos (string-match "\:" s))
   6965         key val)
   6966     (unless pos
   6967       (signal 'lsp-invalid-header-name (list s)))
   6968     (setq key (substring s 0 pos)
   6969           val (s-trim-left (substring s (+ 1 pos))))
   6970     (when (equal key "Content-Length")
   6971       (cl-assert (cl-loop for c across val
   6972                           when (or (> c ?9) (< c ?0)) return nil
   6973                           finally return t)
   6974                  nil (format "Invalid Content-Length value: %s" val)))
   6975     (cons key val)))
   6976 
   6977 (defmacro lsp--read-json (str)
   6978   "Read json string STR."
   6979   (if (progn
   6980         (require 'json)
   6981         (fboundp 'json-parse-string))
   6982       `(json-parse-string ,str
   6983                           :object-type (if lsp-use-plists
   6984                                            'plist
   6985                                          'hash-table)
   6986                           :null-object nil
   6987                           :false-object nil)
   6988     `(let ((json-array-type 'vector)
   6989            (json-object-type (if lsp-use-plists
   6990                                  'plist
   6991                                'hash-table))
   6992            (json-false nil))
   6993        (json-read-from-string ,str))))
   6994 
   6995 (defmacro lsp-json-read-buffer ()
   6996   "Read json from the current buffer."
   6997   (if (progn
   6998         (require 'json)
   6999         (fboundp 'json-parse-buffer))
   7000       `(json-parse-buffer :object-type (if lsp-use-plists
   7001                                            'plist
   7002                                          'hash-table)
   7003                           :null-object nil
   7004                           :false-object nil)
   7005     `(let ((json-array-type 'vector)
   7006            (json-object-type (if lsp-use-plists
   7007                                  'plist
   7008                                'hash-table))
   7009            (json-false nil))
   7010        (json-read))))
   7011 
   7012 (defun lsp--read-json-file (file-path)
   7013   "Read json file."
   7014   (-> file-path
   7015     (f-read-text)
   7016     (lsp--read-json)))
   7017 
   7018 (defun lsp--parser-on-message (json-data workspace)
   7019   "Called when the parser P read a complete MSG from the server."
   7020   (with-demoted-errors "Error processing message %S."
   7021     (with-lsp-workspace workspace
   7022       (let* ((client (lsp--workspace-client workspace))
   7023              (id (--when-let (lsp:json-response-id json-data)
   7024                    (if (stringp it) (string-to-number it) it)))
   7025              (data (lsp:json-response-result json-data)))
   7026         (pcase (lsp--get-message-type json-data)
   7027           ('response
   7028            (cl-assert id)
   7029            (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))]
   7030              (when (lsp--log-io-p method)
   7031                (lsp--log-entry-new
   7032                 (lsp--make-log-entry method id data 'incoming-resp
   7033                                      (lsp--ms-since before-send))
   7034                 workspace))
   7035              (when callback
   7036                (remhash id (lsp--client-response-handlers client))
   7037                (funcall callback (lsp:json-response-result json-data)))))
   7038           ('response-error
   7039            (cl-assert id)
   7040            (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))]
   7041              (when (lsp--log-io-p method)
   7042                (lsp--log-entry-new
   7043                 (lsp--make-log-entry method id (lsp:json-response-error-error json-data)
   7044                                      'incoming-resp (lsp--ms-since before-send))
   7045                 workspace))
   7046              (when callback
   7047                (remhash id (lsp--client-response-handlers client))
   7048                (funcall callback (lsp:json-response-error-error json-data)))))
   7049           ('notification
   7050            (lsp--on-notification workspace json-data))
   7051           ('request (lsp--on-request workspace json-data)))))))
   7052 
   7053 (defun lsp--create-filter-function (workspace)
   7054   "Make filter for the workspace."
   7055   (let ((body-received 0)
   7056         leftovers body-length body chunk)
   7057     (lambda (_proc input)
   7058       (setf chunk (if (s-blank? leftovers)
   7059                       input
   7060                     (concat leftovers input)))
   7061 
   7062       (let (messages)
   7063         (while (not (s-blank? chunk))
   7064           (if (not body-length)
   7065               ;; Read headers
   7066               (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk)))
   7067                   ;; We've got all the headers, handle them all at once:
   7068                   (setf body-length (lsp--get-body-length
   7069                                      (mapcar #'lsp--parse-header
   7070                                              (split-string
   7071                                               (substring-no-properties chunk
   7072                                                                        (or (string-match-p "Content-Length" chunk)
   7073                                                                            (error "Unable to find Content-Length header."))
   7074                                                                        body-sep-pos)
   7075                                               "\r\n")))
   7076                         body-received 0
   7077                         leftovers nil
   7078                         chunk (substring-no-properties chunk (+ body-sep-pos 4)))
   7079 
   7080                 ;; Haven't found the end of the headers yet. Save everything
   7081                 ;; for when the next chunk arrives and await further input.
   7082                 (setf leftovers chunk
   7083                       chunk nil))
   7084             (let* ((chunk-length (string-bytes chunk))
   7085                    (left-to-receive (- body-length body-received))
   7086                    (this-body (if (< left-to-receive chunk-length)
   7087                                   (prog1 (substring-no-properties chunk 0 left-to-receive)
   7088                                     (setf chunk (substring-no-properties chunk left-to-receive)))
   7089                                 (prog1 chunk
   7090                                   (setf chunk nil))))
   7091                    (body-bytes (string-bytes this-body)))
   7092               (push this-body body)
   7093               (setf body-received (+ body-received body-bytes))
   7094               (when (>= chunk-length left-to-receive)
   7095                 (condition-case err
   7096                     (with-temp-buffer
   7097                       (apply #'insert
   7098                              (nreverse
   7099                               (prog1 body
   7100                                 (setf leftovers nil
   7101                                       body-length nil
   7102                                       body-received nil
   7103                                       body nil))))
   7104                       (decode-coding-region (point-min)
   7105                                             (point-max)
   7106                                             'utf-8)
   7107                       (goto-char (point-min))
   7108                       (push (lsp-json-read-buffer) messages))
   7109 
   7110                   (error
   7111                    (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s"
   7112                              (concat leftovers input)
   7113                              err)))))))
   7114         (mapc (lambda (msg)
   7115                 (lsp--parser-on-message msg workspace))
   7116               (nreverse messages))))))
   7117 
   7118 (defvar-local lsp--line-col-to-point-hash-table nil
   7119   "Hash table with keys (line . col) and values that are either point positions
   7120 or markers.")
   7121 
   7122 (defcustom lsp-imenu-detailed-outline t
   7123   "Whether `lsp-imenu' should include signatures.
   7124 This will be ignored if the server doesn't provide the necessary
   7125 information, for example if it doesn't support DocumentSymbols."
   7126   :group 'lsp-imenu
   7127   :type 'boolean)
   7128 
   7129 (defcustom lsp-imenu-hide-parent-details t
   7130   "Whether `lsp-imenu' should hide signatures of parent nodes."
   7131   :group 'lsp-imenu
   7132   :type 'boolean)
   7133 
   7134 (defface lsp-details-face '((t :height 0.8 :inherit shadow))
   7135   "Used to display additional information throughout `lsp'.
   7136 Things like line numbers, signatures, ... are considered
   7137 additional information. Often, additional faces are defined that
   7138 inherit from this face by default, like `lsp-signature-face', and
   7139 they may be customized for finer control."
   7140   :group 'lsp-mode)
   7141 
   7142 (defface lsp-signature-face '((t :inherit lsp-details-face))
   7143   "Used to display signatures in `imenu', ...."
   7144   :group 'lsp-mode)
   7145 
   7146 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?)
   7147                               show-detail?)
   7148   "Render INPUT0, an `&DocumentSymbol', to a string.
   7149 If SHOW-DETAIL? is set, make use of its `:detail?' field (often
   7150 the signature)."
   7151   (let ((detail (and show-detail? (s-present? detail?)
   7152                      (propertize (concat " " (s-trim-left detail?))
   7153                                  'face 'lsp-signature-face)))
   7154         (name (if deprecated?
   7155                   (propertize name 'face 'lsp-face-semhl-deprecated) name)))
   7156     (concat name detail)))
   7157 
   7158 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?)
   7159                                           separator)
   7160   "Render a piece of SymbolInformation.
   7161 Handle :deprecated?. If SEPARATOR is non-nil, the
   7162 symbol's (optional) parent, SEPARATOR and the symbol itself are
   7163 concatenated."
   7164   (when (and separator container-name? (not (string-empty-p container-name?)))
   7165     (setq name (concat name separator container-name?)))
   7166   (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name))
   7167 
   7168 (defun lsp--symbol-to-imenu-elem (sym)
   7169   "Convert SYM to imenu element.
   7170 
   7171 SYM is a SymbolInformation message.
   7172 
   7173 Return a cons cell (full-name . start-point)."
   7174   (let ((start-point (ht-get lsp--line-col-to-point-hash-table
   7175                              (lsp--get-line-and-col sym))))
   7176     (cons (lsp-render-symbol-information
   7177            sym (and lsp-imenu-show-container-name
   7178                     lsp-imenu-container-name-separator))
   7179           start-point)))
   7180 
   7181 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?))
   7182   "Convert SYM to hierarchical imenu elements.
   7183 
   7184 SYM is a DocumentSymbol message.
   7185 
   7186 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if
   7187 SYM doesn't have any children. Otherwise return a cons cell with
   7188 an alist
   7189 
   7190   (\"symbol-name\" . ((\"(symbol-kind)\" . start-point)
   7191                     cons-cells-from-children))"
   7192   (let ((filtered-children (lsp--imenu-filter-symbols children?))
   7193         (signature (lsp-render-symbol sym lsp-imenu-detailed-outline)))
   7194     (if (seq-empty-p filtered-children)
   7195         (cons signature
   7196               (ht-get lsp--line-col-to-point-hash-table
   7197                       (lsp--get-line-and-col sym)))
   7198       (cons signature
   7199             (lsp--imenu-create-hierarchical-index filtered-children)))))
   7200 
   7201 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind))
   7202   "Determine if SYM is for the current document and is to be shown."
   7203   ;; It's a SymbolInformation or DocumentSymbol, which is always in the
   7204   ;; current buffer file.
   7205   (and lsp-imenu-index-symbol-kinds
   7206        (numberp kind)
   7207        (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup))
   7208                                kind
   7209                              0)))
   7210          (not (memql (aref lsp/symbol-kind-lookup clamped-kind)
   7211                      lsp-imenu-index-symbol-kinds)))))
   7212 
   7213 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind))
   7214   "The string name of the kind of SYM."
   7215   (alist-get kind lsp-symbol-kinds "Other"))
   7216 
   7217 (defun lsp--get-line-and-col (sym)
   7218   "Obtain the line and column corresponding to SYM."
   7219   (-let* ((location (lsp:symbol-information-location sym))
   7220           (name-range (or (and location (lsp:location-range location))
   7221                           (lsp:document-symbol-selection-range sym)))
   7222           ((&Range :start (&Position :line :character)) name-range))
   7223     (cons line character)))
   7224 
   7225 (defun lsp--collect-lines-and-cols (symbols)
   7226   "Return a sorted list ((line . col) ...) of the locations of SYMBOLS."
   7227   (let ((stack (mapcar 'identity symbols))
   7228         line-col-list)
   7229     (while stack
   7230       (let ((sym (pop stack)))
   7231         (push (lsp--get-line-and-col sym) line-col-list)
   7232         (unless (seq-empty-p (lsp:document-symbol-children? sym))
   7233           (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack)))))
   7234     (-sort #'lsp--line-col-comparator line-col-list)))
   7235 
   7236 (defun lsp--convert-line-col-to-points-batch (line-col-list)
   7237   "Convert a sorted list of positions from line-column
   7238 representation to point representation."
   7239   (let ((line-col-to-point-map (ht-create))
   7240         (inhibit-field-text-motion t)
   7241         (curr-line 0))
   7242     (lsp-save-restriction-and-excursion
   7243       (goto-char (point-min))
   7244       (cl-loop for (line . col) in line-col-list do
   7245                (forward-line (- line curr-line))
   7246                (setq curr-line line)
   7247                (let ((line-end (line-end-position)))
   7248                  (if (or (not col) (> col (- line-end (point))))
   7249                      (goto-char line-end)
   7250                    (forward-char col)))
   7251                (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers
   7252                                                                   (point-marker)
   7253                                                                 (point)))))
   7254     line-col-to-point-map))
   7255 
   7256 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2))
   7257   (or (< l1 l2)
   7258       (and (= l1 l2)
   7259            (cond ((and c1 c2)
   7260                   (< c1 c2))
   7261                  (c1 t)))))
   7262 
   7263 (defun lsp-imenu-create-uncategorized-index (symbols)
   7264   "Create imenu index from document SYMBOLS.
   7265 This function, unlike `lsp-imenu-create-categorized-index', does
   7266 not categorize by type, but instead returns an `imenu' index
   7267 corresponding to the symbol hierarchy returned by the server
   7268 directly."
   7269   (let* ((lsp--line-col-to-point-hash-table (-> symbols
   7270                                                 lsp--collect-lines-and-cols
   7271                                                 lsp--convert-line-col-to-points-batch)))
   7272     (if (lsp--imenu-hierarchical-p symbols)
   7273         (lsp--imenu-create-hierarchical-index symbols)
   7274       (lsp--imenu-create-non-hierarchical-index symbols))))
   7275 
   7276 (defcustom lsp-imenu-symbol-kinds
   7277   '((1 . "Files")
   7278     (2 . "Modules")
   7279     (3 . "Namespaces")
   7280     (4 . "Packages")
   7281     (5 . "Classes")
   7282     (6 . "Methods")
   7283     (7 . "Properties")
   7284     (8 . "Fields")
   7285     (9 . "Constructors")
   7286     (10 . "Enums")
   7287     (11 . "Interfaces")
   7288     (12 . "Functions")
   7289     (13 . "Variables")
   7290     (14 . "Constants")
   7291     (15 . "Strings")
   7292     (16 . "Numbers")
   7293     (17 . "Booleans")
   7294     (18 . "Arrays")
   7295     (19 . "Objects")
   7296     (20 . "Keys")
   7297     (21 . "Nulls")
   7298     (22 . "Enum Members")
   7299     (23 . "Structs")
   7300     (24 . "Events")
   7301     (25 . "Operators")
   7302     (26 . "Type Parameters"))
   7303   "`lsp-symbol-kinds', but only used by `imenu'.
   7304 A new variable is needed, as it is `imenu' convention to use
   7305 pluralized categories, which `lsp-symbol-kinds' doesn't. If the
   7306 non-pluralized names are preferred, this can be set to
   7307 `lsp-symbol-kinds'."
   7308   :type '(alist :key-type integer :value-type string))
   7309 
   7310 (defun lsp--imenu-kind->name (kind)
   7311   (alist-get kind lsp-imenu-symbol-kinds "?"))
   7312 
   7313 (defun lsp-imenu-create-top-level-categorized-index (symbols)
   7314   "Create an `imenu' index categorizing SYMBOLS by type.
   7315 Only root symbols are categorized.
   7316 
   7317 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS
   7318 shall be a list of DocumentSymbols or SymbolInformation."
   7319   (mapcan
   7320    (-lambda ((type . symbols))
   7321      (let ((cat (lsp--imenu-kind->name type))
   7322            (symbols (lsp-imenu-create-uncategorized-index symbols)))
   7323        ;; If there is no :kind (this is being defensive), or we couldn't look it
   7324        ;; up, just display the symbols inline, without categories.
   7325        (if cat (list (cons cat symbols)) symbols)))
   7326    (sort (seq-group-by #'lsp:document-symbol-kind symbols)
   7327          (-lambda ((kinda) (kindb)) (< kinda kindb)))))
   7328 
   7329 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start)))
   7330   "Convert an `&DocumentSymbol' to an `imenu' entry."
   7331   (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start))
   7332 
   7333 (defun lsp--imenu-create-categorized-index-1 (symbols)
   7334   "Returns an `imenu' index from SYMBOLS categorized by type.
   7335 The result looks like this: ((\"Variables\" . (...)))."
   7336   (->>
   7337    symbols
   7338    (mapcan
   7339     (-lambda ((sym &as &DocumentSymbol :kind :children?))
   7340       (if (seq-empty-p children?)
   7341           (list (list kind (lsp--symbol->imenu sym)))
   7342         (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline
   7343                                                   (not lsp-imenu-hide-parent-details)))))
   7344           (cons
   7345            (list kind (lsp--symbol->imenu sym))
   7346            (mapcar (-lambda ((type .  imenu-items))
   7347                      (list type (cons parent (mapcan #'cdr imenu-items))))
   7348                    (-group-by #'car (lsp--imenu-create-categorized-index-1 children?))))))))
   7349    (-group-by #'car)
   7350    (mapcar
   7351     (-lambda ((kind . syms))
   7352       (cons kind (mapcan #'cdr syms))))))
   7353 
   7354 (defun lsp--imenu-create-categorized-index (symbols)
   7355   (let ((syms (lsp--imenu-create-categorized-index-1 symbols)))
   7356     (dolist (sym syms)
   7357       (setcar sym (lsp--imenu-kind->name (car sym))))
   7358     syms))
   7359 
   7360 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start))))
   7361   (cons (lsp-render-symbol-information sym nil) start))
   7362 
   7363 (defun lsp--imenu-create-categorized-index-flat (symbols)
   7364   "Create a kind-categorized index for SymbolInformation."
   7365   (mapcar (-lambda ((kind . syms))
   7366             (cons (lsp--imenu-kind->name kind)
   7367                   (mapcan (-lambda ((parent . children))
   7368                             (let ((children (mapcar #'lsp--symbol-information->imenu children)))
   7369                               (if parent (list (cons parent children)) children)))
   7370                           (-group-by #'lsp:symbol-information-container-name? syms))))
   7371           (seq-group-by #'lsp:symbol-information-kind symbols)))
   7372 
   7373 (defun lsp-imenu-create-categorized-index (symbols)
   7374   (if (lsp--imenu-hierarchical-p symbols)
   7375       (lsp--imenu-create-categorized-index symbols)
   7376     (lsp--imenu-create-categorized-index-flat symbols)))
   7377 
   7378 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index
   7379   "Function that should create an `imenu' index.
   7380 It will be called with a list of SymbolInformation or
   7381 DocumentSymbols, whose first level is already filtered. It shall
   7382 then return an appropriate `imenu' index (see
   7383 `imenu-create-index-function').
   7384 
   7385 Note that this interface is not stable, and subject to change any
   7386 time."
   7387   :group 'lsp-imenu
   7388   :type '(radio
   7389           (const :tag "Categorize by type"
   7390                  lsp-imenu-create-categorized-index)
   7391           (const :tag "Categorize root symbols by type"
   7392                  lsp-imenu-create-top-level-categorized-index)
   7393           (const :tag "Uncategorized, inline entries"
   7394                  lsp-imenu-create-uncategorized-index)
   7395           (function :tag "Custom function")))
   7396 
   7397 (defun lsp--imenu-create-index ()
   7398   "Create an `imenu' index based on the language server.
   7399 Respects `lsp-imenu-index-function'."
   7400   (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols))))
   7401     (funcall lsp-imenu-index-function symbols)))
   7402 
   7403 (defun lsp--imenu-filter-symbols (symbols)
   7404   "Filter out unsupported symbols from SYMBOLS."
   7405   (seq-remove #'lsp--symbol-ignore symbols))
   7406 
   7407 (defun lsp--imenu-hierarchical-p (symbols)
   7408   "Determine whether any element in SYMBOLS has children."
   7409   (seq-some #'lsp-document-symbol? symbols))
   7410 
   7411 (defun lsp--imenu-create-non-hierarchical-index (symbols)
   7412   "Create imenu index for non-hierarchical SYMBOLS.
   7413 
   7414 SYMBOLS are a list of DocumentSymbol messages.
   7415 
   7416 Return a nested alist keyed by symbol names. e.g.
   7417 
   7418    ((\"SomeClass\" (\"(Class)\" . 10)
   7419                  (\"someField (Field)\" . 20)
   7420                  (\"someFunction (Function)\" . 25)
   7421                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7422                                   (\"someSubField (Field)\" . 35))
   7423     (\"someFunction (Function)\" . 40))"
   7424   (seq-map (lambda (nested-alist)
   7425              (cons (car nested-alist)
   7426                    (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist))))
   7427            (seq-group-by #'lsp--get-symbol-type symbols)))
   7428 
   7429 (defun lsp--imenu-create-hierarchical-index (symbols)
   7430   "Create imenu index for hierarchical SYMBOLS.
   7431 
   7432 SYMBOLS are a list of DocumentSymbol messages.
   7433 
   7434 Return a nested alist keyed by symbol names. e.g.
   7435 
   7436    ((\"SomeClass\" (\"(Class)\" . 10)
   7437                  (\"someField (Field)\" . 20)
   7438                  (\"someFunction (Function)\" . 25)
   7439                  (\"SomeSubClass\" (\"(Class)\" . 30)
   7440                                   (\"someSubField (Field)\" . 35))
   7441     (\"someFunction (Function)\" . 40))"
   7442   (seq-map #'lsp--symbol-to-hierarchical-imenu-elem
   7443            (seq-sort #'lsp--imenu-symbol-lessp symbols)))
   7444 
   7445 (defun lsp--imenu-symbol-lessp (sym1 sym2)
   7446   (let* ((compare-results (mapcar (lambda (method)
   7447                                     (funcall (alist-get method lsp--imenu-compare-function-alist)
   7448                                              sym1 sym2))
   7449                                   lsp-imenu-sort-methods))
   7450          (result (seq-find (lambda (result)
   7451                              (not (= result 0)))
   7452                            compare-results
   7453                            0)))
   7454     (and (numberp result) (< result 0))))
   7455 
   7456 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left)
   7457                                     (&SymbolInformation :kind right))
   7458   "Compare SYM1 and SYM2 by kind."
   7459   (- left right))
   7460 
   7461 (defun lsp--imenu-compare-line-col (sym1 sym2)
   7462   (if (lsp--line-col-comparator
   7463        (lsp--get-line-and-col sym1)
   7464        (lsp--get-line-and-col sym2))
   7465       -1
   7466     1))
   7467 
   7468 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1)
   7469                                     (&SymbolInformation :name name2))
   7470   "Compare SYM1 and SYM2 by name."
   7471   (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2))))
   7472     (if (numberp result) result 0)))
   7473 
   7474 (defun lsp--imenu-refresh ()
   7475   "Force Imenu to refresh itself."
   7476   (imenu--menubar-select imenu--rescan-item))
   7477 
   7478 (defun lsp-enable-imenu ()
   7479   "Use lsp-imenu for the current buffer."
   7480   (imenu--cleanup)
   7481   (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index)
   7482   (setq-local imenu-menubar-modified-tick -1)
   7483   (setq-local imenu--index-alist nil)
   7484   (when menu-bar-mode
   7485     (lsp--imenu-refresh)))
   7486 
   7487 (defun lsp-resolve-final-command (command &optional test?)
   7488   "Resolve final function COMMAND."
   7489   (let* ((command (lsp-resolve-value command))
   7490          (command (cl-etypecase command
   7491                     (list
   7492                      (cl-assert (seq-every-p (apply-partially #'stringp) command) nil
   7493                                 "Invalid command list")
   7494                      command)
   7495                     (string (list command)))))
   7496     (if (and (file-remote-p default-directory) (not test?))
   7497         (list shell-file-name "-c"
   7498               (string-join (cons "stty raw > /dev/null;"
   7499                                  (mapcar #'shell-quote-argument command))
   7500                            " "))
   7501       command)))
   7502 
   7503 (defun lsp-server-present? (final-command)
   7504   "Check whether FINAL-COMMAND is present."
   7505   (let ((binary-found? (executable-find (cl-first final-command) t)))
   7506     (if binary-found?
   7507         (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command))
   7508       (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command)))
   7509     binary-found?))
   7510 
   7511 (defun lsp--value-to-string (value)
   7512   "Convert VALUE to a string that can be set as value in an environment
   7513 variable."
   7514   (cond
   7515    ((stringp value) value)
   7516    ((booleanp value) (if value
   7517                          "1"
   7518                        "0"))
   7519    ((and (sequencep value)
   7520          (seq-every-p #'stringp value)) (string-join value ":"))
   7521    (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables"))))
   7522 
   7523 (defun lsp--compute-process-environment (environment-fn)
   7524   "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'.
   7525 Ignore non-boolean keys whose value is nil."
   7526   (let ((environment (if environment-fn
   7527                          (funcall environment-fn)
   7528                        nil)))
   7529     (-flatten (cons (cl-loop for (key . value) in environment
   7530                              if (or (eval value)
   7531                                     (eq (get value 'custom-type) 'boolean))
   7532                              collect (concat key "=" (lsp--value-to-string
   7533                                                       (eval value))))
   7534                     process-environment))))
   7535 
   7536 (defun lsp--default-directory-for-connection (&optional path)
   7537   "Return path to be used for the working directory of a LSP process.
   7538 
   7539 If `lsp-use-workspace-root-for-server-default-directory' is
   7540 non-nil, uses `lsp-workspace-root' to find the directory
   7541 corresponding to PATH, else returns `default-directory'."
   7542   (if lsp-use-workspace-root-for-server-default-directory
   7543       (lsp-workspace-root path)
   7544     default-directory))
   7545 
   7546 (defun lsp--fix-remote-cmd (program)
   7547   "Helper for `lsp-stdio-connection'.
   7548 Originally coppied from eglot."
   7549 
   7550   (if (file-remote-p default-directory)
   7551       (list shell-file-name "-c"
   7552             (string-join (cons "stty raw > /dev/null;"
   7553                                (mapcar #'shell-quote-argument program))
   7554                          " "))
   7555     program))
   7556 
   7557 (defvar tramp-use-ssh-controlmaster-options)
   7558 (defvar tramp-ssh-controlmaster-options)
   7559 
   7560 (defun lsp-stdio-connection (command &optional test-command)
   7561   "Returns a connection property list using COMMAND.
   7562 COMMAND can be: A string, denoting the command to launch the
   7563 language server. A list of strings, denoting an executable with
   7564 its command line arguments. A function, that either returns a
   7565 string or a list of strings. In all cases, the launched language
   7566 server should send and receive messages on standard I/O.
   7567 TEST-COMMAND is a function with no arguments which returns
   7568 whether the command is present or not. When not specified
   7569 `lsp-mode' will check whether the first element of the list
   7570 returned by COMMAND is available via `executable-find'"
   7571   (cl-check-type command (or string
   7572                              function
   7573                              (and list
   7574                                   (satisfies (lambda (l)
   7575                                                (seq-every-p (lambda (el)
   7576                                                               (stringp el))
   7577                                                             l))))))
   7578   (list :connect (lambda (filter sentinel name environment-fn workspace)
   7579                    (if (and (functionp 'json-rpc-connection)
   7580                             (not (file-remote-p default-directory)))
   7581                        (lsp-json-rpc-connection workspace (lsp-resolve-final-command command))
   7582                      (let ((final-command (lsp-resolve-final-command command))
   7583                            (process-name (generate-new-buffer-name name))
   7584                            (process-environment
   7585                             (lsp--compute-process-environment environment-fn)))
   7586                        (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name)))
   7587                               (default-directory (lsp--default-directory-for-connection))
   7588                               (tramp-use-ssh-controlmaster-options 'suppress)
   7589                               (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none")
   7590                               (proc (make-process
   7591                                      :name process-name
   7592                                      :connection-type 'pipe
   7593                                      :buffer (format "*%s*" process-name)
   7594                                      :coding 'no-conversion
   7595                                      :command final-command
   7596                                      :filter filter
   7597                                      :sentinel sentinel
   7598                                      :stderr stderr-buf
   7599                                      :noquery t
   7600                                      :file-handler t)))
   7601                          (set-process-query-on-exit-flag proc nil)
   7602                          (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil)
   7603                          (with-current-buffer (get-buffer stderr-buf)
   7604                            ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc.
   7605                            (special-mode))
   7606                          (cons proc proc)))))
   7607         :test? (or
   7608                 test-command
   7609                 (lambda ()
   7610                   (lsp-server-present? (lsp-resolve-final-command command t))))))
   7611 
   7612 (defun lsp--open-network-stream (host port name)
   7613   "Open network stream to HOST:PORT.
   7614   NAME will be passed to `open-network-stream'.
   7615   RETRY-COUNT is the number of the retries.
   7616   SLEEP-INTERVAL is the sleep interval between each retry."
   7617   (let* ((retries 0)
   7618          (sleep-interval 0.01)
   7619          (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval))
   7620          connection)
   7621     (while (and (not connection) (< retries number-of-retries))
   7622       (condition-case err
   7623           (setq connection (open-network-stream name nil host port
   7624                                                 :type 'plain
   7625                                                 :coding 'no-conversion))
   7626         (file-error
   7627          (let ((inhibit-message t))
   7628            (lsp--warn "Failed to connect to %s:%s with error message %s"
   7629                       host
   7630                       port
   7631                       (error-message-string err))
   7632            (sleep-for sleep-interval)
   7633            (cl-incf retries)))))
   7634     (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port))))
   7635 
   7636 (defun lsp--port-available (host port)
   7637   "Return non-nil if HOST and PORT are available."
   7638   (condition-case _err
   7639       (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain))
   7640     (file-error t)))
   7641 
   7642 (defun lsp--find-available-port (host starting-port)
   7643   "Find available port on HOST starting from STARTING-PORT."
   7644   (let ((port starting-port))
   7645     (while (not (lsp--port-available host port))
   7646       (cl-incf port))
   7647     port))
   7648 
   7649 (defun lsp-tcp-connection (command-fn)
   7650   "Returns a connection property list similar to `lsp-stdio-connection'.
   7651 COMMAND-FN can only be a function that takes a single argument, a
   7652 port number. It should return a command for launches a language server
   7653 process listening for TCP connections on the provided port."
   7654   (cl-check-type command-fn function)
   7655   (list
   7656    :connect (lambda (filter sentinel name environment-fn _workspace)
   7657               (let* ((host "localhost")
   7658                      (port (lsp--find-available-port host (cl-incf lsp--tcp-port)))
   7659                      (command (funcall command-fn port))
   7660                      (final-command (if (consp command) command (list command)))
   7661                      (_ (unless (lsp-server-present? final-command)
   7662                           (user-error (format "Couldn't find executable %s" (cl-first final-command)))))
   7663                      (process-environment
   7664                       (lsp--compute-process-environment environment-fn))
   7665                      (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion
   7666                                          :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t))
   7667                      (tcp-proc (lsp--open-network-stream host port (concat name "::tcp"))))
   7668 
   7669                 ;; TODO: Same :noquery issue (see above)
   7670                 (set-process-query-on-exit-flag proc nil)
   7671                 (set-process-query-on-exit-flag tcp-proc nil)
   7672                 (set-process-filter tcp-proc filter)
   7673                 (cons tcp-proc proc)))
   7674    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7675 
   7676 (defalias 'lsp-tcp-server 'lsp-tcp-server-command)
   7677 
   7678 (defun lsp-tcp-server-command (command-fn)
   7679   "Create tcp server connection.
   7680 In this mode Emacs is TCP server and the language server connects
   7681 to it. COMMAND is function with one parameter(the port) and it
   7682 should return the command to start the LS server."
   7683   (cl-check-type command-fn function)
   7684   (list
   7685    :connect (lambda (filter sentinel name environment-fn _workspace)
   7686               (let* (tcp-client-connection
   7687                      (tcp-server (make-network-process :name (format "*tcp-server-%s*" name)
   7688                                                        :buffer (format "*tcp-server-%s*" name)
   7689                                                        :family 'ipv4
   7690                                                        :service lsp--tcp-server-port
   7691                                                        :sentinel (lambda (proc _string)
   7692                                                                    (lsp-log "Language server %s is connected." name)
   7693                                                                    (setf tcp-client-connection proc))
   7694                                                        :server 't))
   7695                      (port (process-contact tcp-server :service))
   7696                      (final-command (funcall command-fn port))
   7697                      (process-environment
   7698                       (lsp--compute-process-environment environment-fn))
   7699                      (cmd-proc (make-process :name name
   7700                                              :connection-type 'pipe
   7701                                              :coding 'no-conversion
   7702                                              :command final-command
   7703                                              :stderr (format "*tcp-server-%s*::stderr" name)
   7704                                              :noquery t)))
   7705                 (let ((retries 0))
   7706                   ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds)
   7707                   (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds)))
   7708                     (lsp--info "Waiting for connection for %s, retries: %s" name retries)
   7709                     (sit-for 0.500)
   7710                     (cl-incf retries)))
   7711 
   7712                 (unless tcp-client-connection
   7713                   (condition-case nil (delete-process tcp-server) (error))
   7714                   (condition-case nil (delete-process cmd-proc) (error))
   7715                   (error "Failed to create connection to %s on port %s" name port))
   7716                 (lsp--info "Successfully connected to %s" name)
   7717 
   7718                 (set-process-query-on-exit-flag cmd-proc nil)
   7719                 (set-process-query-on-exit-flag tcp-client-connection nil)
   7720                 (set-process-query-on-exit-flag tcp-server nil)
   7721 
   7722                 (set-process-filter tcp-client-connection filter)
   7723                 (set-process-sentinel tcp-client-connection sentinel)
   7724                 (cons tcp-client-connection cmd-proc)))
   7725    :test? (lambda () (lsp-server-present? (funcall command-fn 0)))))
   7726 
   7727 (defalias 'lsp-tramp-connection 'lsp-stdio-connection)
   7728 
   7729 (defun lsp--auto-configure ()
   7730   "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed."
   7731   (when (functionp 'lsp-ui-mode)
   7732     (lsp-ui-mode))
   7733 
   7734   (if lsp-headerline-breadcrumb-enable
   7735       (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)
   7736     (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode))
   7737   (if lsp-modeline-code-actions-enable
   7738       (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)
   7739     (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode))
   7740   (if lsp-modeline-diagnostics-enable
   7741       (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)
   7742     (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode))
   7743   (if lsp-modeline-workspace-status-enable
   7744       (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)
   7745     (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode))
   7746   (if lsp-lens-enable
   7747       (add-hook 'lsp-configure-hook 'lsp-lens--enable)
   7748     (remove-hook 'lsp-configure-hook 'lsp-lens--enable))
   7749   (if lsp-semantic-tokens-enable
   7750       (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)
   7751     (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable))
   7752 
   7753   ;; yas-snippet config
   7754   (setq-local yas-inhibit-overlay-modification-protection t))
   7755 
   7756 (defun lsp--restart-if-needed (workspace)
   7757   "Handler restart for WORKSPACE."
   7758   (when (or (eq lsp-restart 'auto-restart)
   7759             (eq (lsp--workspace-shutdown-action workspace) 'restart)
   7760             (and (eq lsp-restart 'interactive)
   7761                  (let ((query (format
   7762                                "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?"
   7763                                (lsp--workspace-print workspace))))
   7764                    (y-or-n-p query))))
   7765     (--each (lsp--workspace-buffers workspace)
   7766       (when (lsp-buffer-live-p it)
   7767         (lsp-with-current-buffer it
   7768           (if lsp--buffer-deferred
   7769               (lsp-deferred)
   7770             (lsp--info "Restarting LSP in buffer %s" (buffer-name))
   7771             (lsp)))))))
   7772 
   7773 (defun lsp--update-key (table key fn)
   7774   "Apply FN on value corresponding to KEY in TABLE."
   7775   (let ((existing-value (gethash key table)))
   7776     (if-let ((new-value (funcall fn existing-value)))
   7777         (puthash key new-value table)
   7778       (remhash key table))))
   7779 
   7780 (defun lsp--process-sentinel (workspace process exit-str)
   7781   "Create the sentinel for WORKSPACE."
   7782   (unless (process-live-p process)
   7783     (lsp--handle-process-exit workspace exit-str)))
   7784 
   7785 (defun lsp--handle-process-exit (workspace exit-str)
   7786   (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session)))
   7787          (proc (lsp--workspace-proc workspace)))
   7788     (lsp--warn "%s has exited (%s)"
   7789                (lsp-process-name proc)
   7790                (string-trim-right (or exit-str "")))
   7791     (with-lsp-workspace workspace
   7792       ;; Clean workspace related data in each of the buffers
   7793       ;; in the workspace.
   7794       (--each (lsp--workspace-buffers workspace)
   7795         (when (lsp-buffer-live-p it)
   7796           (lsp-with-current-buffer it
   7797             (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces))
   7798             (lsp--uninitialize-workspace)
   7799             (lsp--spinner-stop)
   7800             (lsp--remove-overlays 'lsp-highlight))))
   7801 
   7802       ;; Cleanup session from references to the closed workspace.
   7803       (--each (hash-table-keys folder->workspaces)
   7804         (lsp--update-key folder->workspaces it (apply-partially 'delete workspace)))
   7805 
   7806       (lsp-process-cleanup proc))
   7807 
   7808     (run-hook-with-args 'lsp-after-uninitialized-functions workspace)
   7809 
   7810     (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown)
   7811         (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace))
   7812       (lsp--restart-if-needed workspace))
   7813     (lsp--cleanup-hanging-watches)))
   7814 
   7815 (defun lsp-workspace-folders (workspace)
   7816   "Return all folders associated with WORKSPACE."
   7817   (let (result)
   7818     (->> (lsp-session)
   7819          (lsp-session-folder->servers)
   7820          (maphash (lambda (folder workspaces)
   7821                     (when (-contains? workspaces workspace)
   7822                       (push folder result)))))
   7823     result))
   7824 
   7825 (defun lsp--start-workspace (session client-template root &optional initialization-options)
   7826   "Create new workspace for CLIENT-TEMPLATE with project root ROOT.
   7827 INITIALIZATION-OPTIONS are passed to initialize function.
   7828 SESSION is the active session."
   7829   (lsp--spinner-start)
   7830   (-let* ((default-directory root)
   7831           (client (copy-lsp--client client-template))
   7832           (workspace (make-lsp--workspace
   7833                       :root root
   7834                       :client client
   7835                       :status 'starting
   7836                       :buffers (list (lsp-current-buffer))
   7837                       :host-root (file-remote-p root)))
   7838           ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities
   7839                      'multi-root 'initialized-fn) client)
   7840           ((proc . cmd-proc) (funcall
   7841                               (or (plist-get new-connection :connect)
   7842                                   (user-error "Client %s is configured incorrectly" client))
   7843                               (lsp--create-filter-function workspace)
   7844                               (apply-partially #'lsp--process-sentinel workspace)
   7845                               (format "%s" server-id)
   7846                               environment-fn
   7847                               workspace))
   7848           (workspace-folders (gethash server-id (lsp-session-server-id->folders session))))
   7849     (setf (lsp--workspace-proc workspace) proc
   7850           (lsp--workspace-cmd-proc workspace) cmd-proc)
   7851 
   7852     ;; update (lsp-session-folder->servers) depending on whether we are starting
   7853     ;; multi/single folder workspace
   7854     (mapc (lambda (project-root)
   7855             (->> session
   7856                  (lsp-session-folder->servers)
   7857                  (gethash project-root)
   7858                  (cl-pushnew workspace)))
   7859           (or workspace-folders (list root)))
   7860 
   7861     (with-lsp-workspace workspace
   7862       (run-hooks 'lsp-before-initialize-hook)
   7863       (lsp-request-async
   7864        "initialize"
   7865        (append
   7866         (list :processId (unless (file-remote-p (buffer-file-name))
   7867                            (emacs-pid))
   7868               :rootPath (lsp-file-local-name (expand-file-name root))
   7869               :clientInfo (list :name "emacs"
   7870                                 :version (emacs-version))
   7871               :rootUri (lsp--path-to-uri root)
   7872               :capabilities (lsp--client-capabilities custom-capabilities)
   7873               :initializationOptions initialization-options
   7874               :workDoneToken "1")
   7875         (when lsp-server-trace
   7876           (list :trace lsp-server-trace))
   7877         (when multi-root
   7878           (->> workspace-folders
   7879                (-distinct)
   7880                (-map (lambda (folder)
   7881                        (list :uri (lsp--path-to-uri folder)
   7882                              :name (f-filename folder))))
   7883                (apply 'vector)
   7884                (list :workspaceFolders))))
   7885        (-lambda ((&InitializeResult :capabilities))
   7886          ;; we know that Rust Analyzer will send {} which will be parsed as null
   7887          ;; when using plists
   7888          (when (equal 'rust-analyzer server-id)
   7889            (-> capabilities
   7890                (lsp:server-capabilities-text-document-sync?)
   7891                (lsp:set-text-document-sync-options-save? t)))
   7892 
   7893          (setf (lsp--workspace-server-capabilities workspace) capabilities
   7894                (lsp--workspace-status workspace) 'initialized)
   7895 
   7896          (with-lsp-workspace workspace
   7897            (lsp-notify "initialized" lsp--empty-ht))
   7898 
   7899          (when initialized-fn (funcall initialized-fn workspace))
   7900 
   7901          (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace))
   7902          (->> workspace
   7903               (lsp--workspace-buffers)
   7904               (mapc (lambda (buffer)
   7905                       (lsp-with-current-buffer buffer
   7906                         (lsp--open-in-workspace workspace)))))
   7907 
   7908          (with-lsp-workspace workspace
   7909            (run-hooks 'lsp-after-initialize-hook))
   7910          (lsp--info "%s initialized successfully in folders: %s"
   7911                     (lsp--workspace-print workspace)
   7912                     (lsp-workspace-folders workspace)))
   7913        :mode 'detached))
   7914     workspace))
   7915 
   7916 (defun lsp--load-default-session ()
   7917   "Load default session."
   7918   (setq lsp--session (or (condition-case err
   7919                              (lsp--read-from-file lsp-session-file)
   7920                            (error (lsp--error "Failed to parse the session %s, starting with clean one."
   7921                                               (error-message-string err))
   7922                                   nil))
   7923                          (make-lsp-session))))
   7924 
   7925 (defun lsp-session ()
   7926   "Get the session associated with the current buffer."
   7927   (or lsp--session (setq lsp--session (lsp--load-default-session))))
   7928 
   7929 (defun lsp--client-disabled-p (buffer-major-mode client)
   7930   (seq-some
   7931    (lambda (entry)
   7932      (pcase entry
   7933        ((pred symbolp) (eq entry client))
   7934        (`(,mode . ,client-or-list)
   7935         (and (eq mode buffer-major-mode)
   7936              (if (listp client-or-list)
   7937                  (memq client client-or-list)
   7938                (eq client client-or-list))))))
   7939    lsp-disabled-clients))
   7940 
   7941 
   7942 ;; download server
   7943 
   7944 (defcustom lsp-server-install-dir (expand-file-name
   7945                                    (locate-user-emacs-file (f-join ".cache" "lsp")))
   7946   "Directory in which the servers will be installed."
   7947   :risky t
   7948   :type 'directory
   7949   :package-version '(lsp-mode . "6.3")
   7950   :group 'lsp-mode)
   7951 
   7952 (defcustom lsp-verify-signature t
   7953   "Whether to check GPG signatures of downloaded files."
   7954   :type 'boolean
   7955   :package-version '(lsp-mode . "8.0.0")
   7956   :group 'lsp-mode)
   7957 
   7958 (defvar lsp--dependencies (ht))
   7959 
   7960 (defun lsp-dependency (name &rest definitions)
   7961   "Used to specify a language server DEPENDENCY, the server
   7962 executable or other required file path. Typically, the
   7963 DEPENDENCY is found by locating it on the system path using
   7964 `executable-find'.
   7965 
   7966 You can explicitly call lsp-dependency in your environment to
   7967 specify the absolute path to the DEPENDENCY. For example, the
   7968 typescript-language-server requires both the server and the
   7969 typescript compiler. If you have installed them in a team shared
   7970 read-only location, you can instruct lsp-mode to use them via
   7971 
   7972  (eval-after-load `lsp-mode
   7973    `(progn
   7974       (require lsp-javascript)
   7975       (lsp-dependency typescript-language-server (:system ,tls-exe))
   7976       (lsp-dependency typescript (:system ,ts-js))))
   7977 
   7978 where tls-exe is the absolute path to the typescript-language-server
   7979 executable and ts-js is the absolute path to the typescript compiler
   7980 JavaScript file, tsserver.js (the *.js is required for Windows)."
   7981   (ht-set lsp--dependencies name definitions))
   7982 
   7983 (defun lsp--server-binary-present? (client)
   7984   (unless (equal (lsp--client-server-id client) 'lsp-pwsh)
   7985     (condition-case ()
   7986         (-some-> client lsp--client-new-connection (plist-get :test?) funcall)
   7987       (error nil)
   7988       (args-out-of-range nil))))
   7989 
   7990 (define-minor-mode lsp-installation-buffer-mode
   7991   "Mode used in *lsp-installation* buffers.
   7992 It can be used to set-up keybindings, etc. Disabling this mode
   7993 detaches the installation buffer from commands like
   7994 `lsp-select-installation-buffer'."
   7995   :init-value nil
   7996   :lighter nil)
   7997 
   7998 (defface lsp-installation-finished-buffer-face '((t :foreground "orange"))
   7999   "Face used for finished installation buffers.
   8000 Used in `lsp-select-installation-buffer'."
   8001   :group 'lsp-mode)
   8002 
   8003 (defface lsp-installation-buffer-face '((t :foreground "green"))
   8004   "Face used for installation buffers still in progress.
   8005 Used in `lsp-select-installation-buffer'."
   8006   :group 'lsp-mode)
   8007 
   8008 (defun lsp--installation-buffer? (buf)
   8009   "Check whether BUF is an `lsp-async-start-process' buffer."
   8010   (buffer-local-value 'lsp-installation-buffer-mode buf))
   8011 
   8012 (defun lsp-select-installation-buffer (&optional show-finished)
   8013   "Interactively choose an installation buffer.
   8014 If SHOW-FINISHED is set, leftover (finished) installation buffers
   8015 are still shown."
   8016   (interactive "P")
   8017   (let ((bufs (--filter (and (lsp--installation-buffer? it)
   8018                              (or show-finished (get-buffer-process it)))
   8019                         (buffer-list))))
   8020     (pcase bufs
   8021       (`nil (user-error "No installation buffers"))
   8022       (`(,buf) (pop-to-buffer buf))
   8023       (bufs (pop-to-buffer (completing-read "Select installation buffer: "
   8024                                             (--map (propertize (buffer-name it) 'face
   8025                                                                (if (get-buffer-process it)
   8026                                                                    'lsp-installation-buffer-face
   8027                                                                  'lsp-installation-finished-buffer-face))
   8028                                                    bufs)))))))
   8029 
   8030 (defun lsp-cleanup-installation-buffers ()
   8031   "Delete finished *lsp-installation* buffers."
   8032   (interactive)
   8033   (dolist (buf (buffer-list))
   8034     (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf)))
   8035       (kill-buffer buf))))
   8036 
   8037 (defun lsp--download-status ()
   8038   (-some--> #'lsp--client-download-in-progress?
   8039     (lsp--filter-clients it)
   8040     (-map (-compose #'symbol-name #'lsp--client-server-id) it)
   8041     (format "%s" it)
   8042     (propertize it 'face 'success)
   8043     (format " Installing following servers: %s" it)
   8044     (propertize it
   8045                 'local-map (make-mode-line-mouse-map
   8046                             'mouse-1 #'lsp-select-installation-buffer)
   8047                 'mouse-face 'highlight)))
   8048 
   8049 (defun lsp--install-server-internal (client &optional update?)
   8050   (unless (lsp--client-download-server-fn client)
   8051     (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation."
   8052                 (lsp--client-server-id client)))
   8053 
   8054   (setf (lsp--client-download-in-progress? client) t)
   8055   (add-to-list 'global-mode-string '(t (:eval (lsp--download-status))))
   8056   (cl-flet ((done
   8057              (success? &optional error-message)
   8058              ;; run with idle timer to make sure the lsp command is executed in
   8059              ;; the main thread, see #2739.
   8060              (run-with-timer
   8061               0.0
   8062               nil
   8063               (lambda ()
   8064                 (-let [(&lsp-cln 'server-id 'buffers) client]
   8065                   (setf (lsp--client-download-in-progress? client) nil
   8066                         (lsp--client-buffers client) nil)
   8067                   (if success?
   8068                       (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id
   8069                                  (length buffers))
   8070                     (lsp--error "Server %s install process failed with the following error message: %s.
   8071 Check `*lsp-install*' and `*lsp-log*' buffer."
   8072                                 server-id
   8073                                 error-message))
   8074                   (seq-do
   8075                    (lambda (buffer)
   8076                      (when (lsp-buffer-live-p buffer)
   8077                        (lsp-with-current-buffer buffer
   8078                          (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   8079                                     global-mode-string)
   8080                          (when success? (lsp)))))
   8081                    buffers)
   8082                   (unless (lsp--filter-clients #'lsp--client-download-in-progress?)
   8083                     (cl-callf2 -remove-item '(t (:eval (lsp--download-status)))
   8084                                global-mode-string)))))))
   8085     (lsp--info "Download %s started." (lsp--client-server-id client))
   8086     (condition-case err
   8087         (funcall
   8088          (lsp--client-download-server-fn client)
   8089          client
   8090          (lambda () (done t))
   8091          (lambda (msg) (done nil msg))
   8092          update?)
   8093       (error
   8094        (done nil (error-message-string err))))))
   8095 
   8096 (defun lsp--require-packages ()
   8097   "Load `lsp-client-packages' if needed."
   8098   (when (and lsp-auto-configure (not lsp--client-packages-required))
   8099     (seq-do (lambda (package)
   8100               ;; loading client is slow and `lsp' can be called repeatedly
   8101               (unless (featurep package)
   8102                 (require package nil t)))
   8103             lsp-client-packages)
   8104     (setq lsp--client-packages-required t)))
   8105 
   8106 ;;;###autoload
   8107 (defun lsp-install-server (update? &optional server-id)
   8108   "Interactively install or re-install server.
   8109 When prefix UPDATE? is t force installation even if the server is present."
   8110   (interactive "P")
   8111   (lsp--require-packages)
   8112   (let* ((chosen-client (or (gethash server-id lsp-clients)
   8113                             (lsp--completing-read
   8114                              "Select server to install/re-install: "
   8115                              (or (->> lsp-clients
   8116                                       (ht-values)
   8117                                       (-filter (-andfn
   8118                                                 (-not #'lsp--client-download-in-progress?)
   8119                                                 #'lsp--client-download-server-fn)))
   8120                                  (user-error "There are no servers with automatic installation"))
   8121                              (lambda (client)
   8122                                (let ((server-name (-> client lsp--client-server-id symbol-name)))
   8123                                  (if (lsp--server-binary-present? client)
   8124                                      (concat server-name " (Already installed)")
   8125                                    server-name)))
   8126                              nil
   8127                              t)))
   8128          (update? (or update?
   8129                       (and (not (lsp--client-download-in-progress? chosen-client))
   8130                            (lsp--server-binary-present? chosen-client)))))
   8131     (lsp--install-server-internal chosen-client update?)))
   8132 
   8133 ;;;###autoload
   8134 (defun lsp-uninstall-server (dir)
   8135   "Delete a LSP server from `lsp-server-install-dir'."
   8136   (interactive
   8137    (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir))))
   8138   (unless (file-directory-p dir)
   8139     (user-error "Couldn't find %s directory" dir))
   8140   (delete-directory dir 'recursive)
   8141   (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir))))
   8142 
   8143 ;;;###autoload
   8144 (defun lsp-uninstall-servers ()
   8145   "Uninstall all installed servers."
   8146   (interactive)
   8147   (let* ((dir lsp-server-install-dir)
   8148          (servers (ignore-errors
   8149                     (directory-files dir t
   8150                                      directory-files-no-dot-files-regexp))))
   8151     (if (or (not (file-directory-p dir)) (zerop (length servers)))
   8152         (user-error "No servers to uninstall")
   8153       (when (yes-or-no-p
   8154              (format "Servers to uninstall: %d (%s), proceed? "
   8155                      (length servers)
   8156                      (mapconcat (lambda (server)
   8157                                   (file-name-nondirectory (directory-file-name server)))
   8158                                 servers " ")))
   8159         (mapc #'lsp-uninstall-server servers)
   8160         (message "All servers uninstalled")))))
   8161 
   8162 ;;;###autoload
   8163 (defun lsp-update-server (&optional server-id)
   8164   "Interactively update (reinstall) a server."
   8165   (interactive)
   8166   (lsp--require-packages)
   8167   (let ((chosen-client (or (gethash server-id lsp-clients)
   8168                            (lsp--completing-read
   8169                             "Select server to update (if not on the list, probably you need to `lsp-install-server`): "
   8170                             (or (->> lsp-clients
   8171                                      (ht-values)
   8172                                      (-filter (-andfn
   8173                                                (-not #'lsp--client-download-in-progress?)
   8174                                                #'lsp--client-download-server-fn
   8175                                                #'lsp--server-binary-present?)))
   8176                                 (user-error "There are no servers to update"))
   8177                             (lambda (client)
   8178                               (-> client lsp--client-server-id symbol-name))
   8179                             nil
   8180                             t))))
   8181     (lsp--install-server-internal chosen-client t)))
   8182 
   8183 ;;;###autoload
   8184 (defun lsp-update-servers ()
   8185   "Update (reinstall) all installed servers."
   8186   (interactive)
   8187   (lsp--require-packages)
   8188   (mapc (lambda (client) (lsp--install-server-internal client t))
   8189         (-filter (-andfn
   8190                   (-not #'lsp--client-download-in-progress?)
   8191                   #'lsp--client-download-server-fn
   8192                   #'lsp--server-binary-present?) (hash-table-values lsp-clients))))
   8193 
   8194 ;;;###autoload
   8195 (defun lsp-ensure-server (server-id)
   8196   "Ensure server SERVER-ID"
   8197   (lsp--require-packages)
   8198   (if-let ((client (gethash server-id lsp-clients)))
   8199       (unless (lsp--server-binary-present? client)
   8200         (lsp--info "Server `%s' is not preset, installing..." server-id)
   8201         (lsp-install-server nil server-id))
   8202     (warn "Unable to find server registration with id %s" server-id)))
   8203 
   8204 (defun lsp-async-start-process (callback error-callback &rest command)
   8205   "Start async process COMMAND with CALLBACK and ERROR-CALLBACK."
   8206   (let ((name (cl-first command)))
   8207     (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd)
   8208                                                                                          (not (null cmd)))
   8209                                                                                        command)
   8210                                                        " ") t
   8211                                             (lambda (&rest _)
   8212                                               (generate-new-buffer-name (format "*lsp-install: %s*" name))))
   8213       (lsp-installation-buffer-mode +1)
   8214       (view-mode +1)
   8215       (add-hook
   8216        'compilation-finish-functions
   8217        (lambda (_buf status)
   8218          (if (string= "finished\n" status)
   8219              (condition-case err
   8220                  (funcall callback)
   8221                (error
   8222                 (funcall error-callback (error-message-string err))))
   8223            (funcall error-callback (s-trim-right status))))
   8224        nil t))))
   8225 
   8226 (defun lsp-resolve-value (value)
   8227   "Resolve VALUE's value.
   8228 If it is function - call it.
   8229 If it is a variable - return it's value
   8230 Otherwise returns value itself."
   8231   (cond
   8232    ((functionp value) (funcall value))
   8233    ((and (symbolp value) (boundp value)) (symbol-value value))
   8234    (value)))
   8235 
   8236 (defvar lsp-deps-providers
   8237   (list :npm (list :path #'lsp--npm-dependency-path
   8238                    :install #'lsp--npm-dependency-install)
   8239         :cargo (list :path #'lsp--cargo-dependency-path
   8240                      :install #'lsp--cargo-dependency-install)
   8241         :system (list :path #'lsp--system-path)
   8242         :download (list :path #'lsp-download-path
   8243                         :install #'lsp-download-install)))
   8244 
   8245 (defun lsp--system-path (path)
   8246   "If PATH is absolute and exists return it as is. Otherwise,
   8247 return the absolute path to the executable defined by PATH or
   8248 nil."
   8249   ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the
   8250   ;; typescript-language-server. When lsp invokes the server, lsp needs to
   8251   ;; supply the path to the typescript compiler, tsserver.js, as an argument. To
   8252   ;; make code platform independent, one must pass the absolute path to the
   8253   ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript
   8254   ;; child process spawn command that is invoked by the
   8255   ;; typescript-language-server). This is why we check for existence and not
   8256   ;; that the path is executable.
   8257   (let ((path (lsp-resolve-value path)))
   8258     (cond
   8259      ((and (f-absolute? path)
   8260            (f-exists? path))
   8261       path)
   8262      ((executable-find path t) path))))
   8263 
   8264 (defun lsp-package-path (dependency)
   8265   "Path to the DEPENDENCY each of the registered providers."
   8266   (let (path)
   8267     (-first (-lambda ((provider . rest))
   8268               (setq path (-some-> lsp-deps-providers
   8269                            (plist-get provider)
   8270                            (plist-get :path)
   8271                            (apply rest))))
   8272             (gethash dependency lsp--dependencies))
   8273     path))
   8274 
   8275 (defun lsp-package-ensure (dependency callback error-callback)
   8276   "Asynchronously ensure a package."
   8277   (or (-first (-lambda ((provider . rest))
   8278                 (-some-> lsp-deps-providers
   8279                   (plist-get provider)
   8280                   (plist-get :install)
   8281                   (apply (cl-list* callback error-callback rest))))
   8282               (gethash dependency lsp--dependencies))
   8283       (funcall error-callback (format "Unable to find a way to install %s" dependency))))
   8284 
   8285 
   8286 ;; npm handling
   8287 
   8288 ;; https://docs.npmjs.com/files/folders#executables
   8289 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys)
   8290   "Return npm dependency PATH for PACKAGE."
   8291   (let ((path (executable-find
   8292                (f-join lsp-server-install-dir "npm" package
   8293                        (cond ((eq system-type 'windows-nt) "")
   8294                              (t "bin"))
   8295                        path)
   8296                t)))
   8297     (unless (and path (f-exists? path))
   8298       (error "The package %s is not installed.  Unable to find %s" package path))
   8299     path))
   8300 
   8301 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys)
   8302   (if-let ((npm-binary (executable-find "npm")))
   8303       (progn
   8304         ;; Explicitly `make-directory' to work around NPM bug in
   8305         ;; versions 7.0.0 through 7.4.1. See
   8306         ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for
   8307         ;; discussion.
   8308         (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents)
   8309         (lsp-async-start-process (lambda ()
   8310                                    (if (string-empty-p
   8311                                         (string-trim (shell-command-to-string
   8312                                                       (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " "))))
   8313                                        (funcall callback)
   8314                                      (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json")))))
   8315                                            (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx
   8316                                        (when (f-dir-p default-directory)
   8317                                          (lsp-async-start-process callback
   8318                                                                   error-callback
   8319                                                                   (executable-find "npx")
   8320                                                                   "npm-install-peers")))))
   8321                                  error-callback
   8322                                  npm-binary
   8323                                  "-g"
   8324                                  "--prefix"
   8325                                  (f-join lsp-server-install-dir "npm" package)
   8326                                  "install"
   8327                                  package))
   8328     (lsp-log "Unable to install %s via `npm' because it is not present" package)
   8329     nil))
   8330 
   8331 
   8332 ;; Cargo dependency handling
   8333 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys)
   8334   (let ((path (executable-find
   8335                (f-join lsp-server-install-dir
   8336                        "cargo"
   8337                        package
   8338                        "bin"
   8339                        path)
   8340                t)))
   8341     (unless (and path (f-exists? path))
   8342       (error "The package %s is not installed.  Unable to find %s" package path))
   8343     path))
   8344 
   8345 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys)
   8346   (if-let ((cargo-binary (executable-find "cargo")))
   8347       (lsp-async-start-process
   8348        callback
   8349        error-callback
   8350        cargo-binary
   8351        "install"
   8352        package
   8353        (when git
   8354          "--git")
   8355        git
   8356        "--root"
   8357        (f-join lsp-server-install-dir "cargo" package))
   8358     (lsp-log "Unable to install %s via `cargo' because it is not present" package)
   8359     nil))
   8360 
   8361 
   8362 
   8363 ;; Download URL handling
   8364 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys)
   8365   (let* ((url (lsp-resolve-value url))
   8366          (store-path (lsp-resolve-value store-path))
   8367          ;; (decompress (lsp-resolve-value decompress))
   8368          (download-path
   8369           (pcase decompress
   8370             (:gzip (concat store-path ".gz"))
   8371             (:zip (concat store-path ".zip"))
   8372             (:targz (concat store-path ".tar.gz"))
   8373             (`nil store-path)
   8374             (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'")))))
   8375     (make-thread
   8376      (lambda ()
   8377        (condition-case err
   8378            (progn
   8379              (when (f-exists? download-path)
   8380                (f-delete download-path))
   8381              (when (f-exists? store-path)
   8382                (f-delete store-path))
   8383              (lsp--info "Starting to download %s to %s..." url download-path)
   8384              (mkdir (f-parent download-path) t)
   8385              (url-copy-file url download-path)
   8386              (lsp--info "Finished downloading %s..." download-path)
   8387              (when (and lsp-verify-signature asc-url pgp-key)
   8388                (if (executable-find epg-gpg-program)
   8389                    (let ((asc-download-path (concat download-path ".asc"))
   8390                          (context (epg-make-context))
   8391                          (fingerprint)
   8392                          (signature))
   8393                      (when (f-exists? asc-download-path)
   8394                        (f-delete asc-download-path))
   8395                      (lsp--info "Starting to download %s to %s..." asc-url asc-download-path)
   8396                      (url-copy-file asc-url asc-download-path)
   8397                      (lsp--info "Finished downloading %s..." asc-download-path)
   8398                      (epg-import-keys-from-string context pgp-key)
   8399                      (setq fingerprint (epg-import-status-fingerprint
   8400                                         (car
   8401                                          (epg-import-result-imports
   8402                                           (epg-context-result-for context 'import)))))
   8403                      (lsp--info "Verifying signature %s..." asc-download-path)
   8404                      (epg-verify-file context asc-download-path download-path)
   8405                      (setq signature (car (epg-context-result-for context 'verify)))
   8406                      (unless (and
   8407                               (eq (epg-signature-status signature) 'good)
   8408                               (equal (epg-signature-fingerprint signature) fingerprint))
   8409                        (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature))))
   8410                  (lsp--warn "GPG is not installed, skipping the signature check.")))
   8411              (when decompress
   8412                (lsp--info "Decompressing %s..." download-path)
   8413                (pcase decompress
   8414                  (:gzip
   8415                   (lsp-gunzip download-path))
   8416                  (:zip (lsp-unzip download-path (f-parent store-path)))
   8417                  (:targz (lsp-tar-gz-decompress download-path (f-parent store-path))))
   8418                (lsp--info "Decompressed %s..." store-path))
   8419              (funcall callback))
   8420          (error (funcall error-callback err)))))))
   8421 
   8422 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys)
   8423   "Download URL and store it into STORE-PATH.
   8424 
   8425 SET-EXECUTABLE? when non-nil change the executable flags of
   8426 STORE-PATH to make it executable. BINARY-PATH can be specified
   8427 when the binary to start does not match the name of the
   8428 archive (e.g. when the archive has multiple files)"
   8429   (let ((store-path (or (lsp-resolve-value binary-path)
   8430                         (lsp-resolve-value store-path))))
   8431     (cond
   8432      ((executable-find store-path) store-path)
   8433      ((and set-executable? (f-exists? store-path))
   8434       (set-file-modes store-path #o0700)
   8435       store-path)
   8436      ((f-exists? store-path) store-path))))
   8437 
   8438 (defun lsp--find-latest-gh-release-url (url regex)
   8439   "Fetch the latest version in the releases given by URL by using REGEX."
   8440   (let ((url-request-method "GET"))
   8441     (with-current-buffer (url-retrieve-synchronously url)
   8442       (goto-char (point-min))
   8443       (re-search-forward "\n\n" nil 'noerror)
   8444       (delete-region (point-min) (point))
   8445       (let* ((json-result (lsp-json-read-buffer)))
   8446         (message "Latest version found: %s" (lsp-get json-result :tag_name))
   8447         (--> json-result
   8448              (lsp-get it :assets)
   8449              (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it)
   8450              (lsp-get it :browser_download_url))))))
   8451 
   8452 ;; unzip
   8453 
   8454 (defconst lsp-ext-pwsh-script "powershell -noprofile -noninteractive \
   8455 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'"
   8456   "Powershell script to unzip file.")
   8457 
   8458 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'"
   8459   "Unzip script to unzip file.")
   8460 
   8461 (defcustom lsp-unzip-script (lambda ()
   8462                               (cond ((and (eq system-type 'windows-nt)
   8463                                           (executable-find "powershell"))
   8464                                      lsp-ext-pwsh-script)
   8465                                     ((executable-find "unzip") lsp-ext-unzip-script)
   8466                                     ((executable-find "powershell") lsp-ext-pwsh-script)
   8467                                     (t nil)))
   8468   "The script to unzip."
   8469   :group 'lsp-mode
   8470   :type 'string
   8471   :package-version '(lsp-mode . "8.0.0"))
   8472 
   8473 (defun lsp-unzip (zip-file dest)
   8474   "Unzip ZIP-FILE to DEST."
   8475   (unless lsp-unzip-script
   8476     (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'"))
   8477   (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest)))
   8478 
   8479 ;; gunzip
   8480 
   8481 (defconst lsp-ext-gunzip-script "gzip -d %1$s"
   8482   "Script to decompress a gzippped file with gzip.")
   8483 
   8484 (defcustom lsp-gunzip-script (lambda ()
   8485                                (cond ((executable-find "gzip") lsp-ext-gunzip-script)
   8486                                      (t nil)))
   8487   "The script to decompress a gzipped file.
   8488 Should be a format string with one argument for the file to be decompressed
   8489 in place."
   8490   :group 'lsp-mode
   8491   :type 'string
   8492   :package-version '(lsp-mode . "8.0.0"))
   8493 
   8494 (defun lsp-gunzip (gz-file)
   8495   "Decompress GZ-FILE in place."
   8496   (unless lsp-gunzip-script
   8497     (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file))
   8498   (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file)))
   8499 
   8500 ;; tar.gz decompression
   8501 
   8502 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'"
   8503   "Script to decompress a .tar.gz file.")
   8504 
   8505 (defcustom lsp-tar-script (lambda ()
   8506                             (cond ((executable-find "tar") lsp-ext-tar-script)
   8507                                   (t nil)))
   8508   "The script to decompress a .tar.gz file.
   8509 Should be a format string with one argument for the file to be decompressed
   8510 in place."
   8511   :group 'lsp-mode
   8512   :type 'string)
   8513 
   8514 (defun lsp-tar-gz-decompress (targz-file dest)
   8515   "Decompress TARGZ-FILE in DEST."
   8516   (unless lsp-tar-script
   8517     (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file))
   8518   (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest)))
   8519 
   8520 
   8521 ;; VSCode marketplace
   8522 
   8523 (defcustom lsp-vscode-ext-url
   8524   "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s"
   8525   "Vscode extension template url."
   8526   :group 'lsp-mode
   8527   :type 'string
   8528   :package-version '(lsp-mode . "8.0.0"))
   8529 
   8530 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform)
   8531   "Return the URL to vscode extension.
   8532 PUBLISHER is the extension publisher.
   8533 NAME is the name of the extension.
   8534 VERSION is the version of the extension.
   8535 TARGETPLATFORM is the targetPlatform of the extension."
   8536   (format lsp-vscode-ext-url publisher name version (or targetPlatform "")))
   8537 
   8538 
   8539 
   8540 ;; Queueing prompts
   8541 
   8542 (defvar lsp--question-queue nil
   8543   "List of questions yet to be asked by `lsp-ask-question'.")
   8544 
   8545 (defun lsp-ask-question (question options callback)
   8546   "Prompt the user to answer the QUESTION with one of the OPTIONS from the
   8547 minibuffer. Once the user selects an option, the CALLBACK function will be
   8548 called, passing the selected option to it.
   8549 
   8550 If the user is currently being shown a question, the question will be stored in
   8551 `lsp--question-queue', and will be asked once the user has answered the current
   8552 question."
   8553   (add-to-list 'lsp--question-queue `(("question" . ,question)
   8554                                       ("options" . ,options)
   8555                                       ("callback" . ,callback)) t)
   8556   (when (eq (length lsp--question-queue) 1)
   8557     (lsp--process-question-queue)))
   8558 
   8559 (defun lsp--process-question-queue ()
   8560   "Take the first question from `lsp--question-queue', process it, then process
   8561 the next question until the queue is empty."
   8562   (-let* (((&alist "question" "options" "callback") (car lsp--question-queue))
   8563           (answer (completing-read question options nil t)))
   8564     (pop lsp--question-queue)
   8565     (funcall callback answer)
   8566     (when lsp--question-queue
   8567       (lsp--process-question-queue))))
   8568 
   8569 (defun lsp--supports-buffer? (client)
   8570   (and
   8571    ;; both file and client remote or both local
   8572    (eq (---truthy? (file-remote-p (buffer-file-name)))
   8573        (---truthy? (lsp--client-remote? client)))
   8574 
   8575    ;; activation function or major-mode match.
   8576    (if-let ((activation-fn (lsp--client-activation-fn client)))
   8577        (funcall activation-fn (buffer-file-name) major-mode)
   8578      (-contains? (lsp--client-major-modes client) major-mode))
   8579 
   8580    ;; check whether it is enabled if `lsp-enabled-clients' is not null
   8581    (or (null lsp-enabled-clients)
   8582        (or (member (lsp--client-server-id client) lsp-enabled-clients)
   8583            (ignore (lsp--info "Client %s is not in lsp-enabled-clients"
   8584                               (lsp--client-server-id client)))))
   8585 
   8586    ;; check whether it is not disabled.
   8587    (not (lsp--client-disabled-p major-mode (lsp--client-server-id client)))))
   8588 
   8589 (defun lsp--filter-clients (pred)
   8590   (->> lsp-clients hash-table-values (-filter pred)))
   8591 
   8592 (defun lsp--find-clients ()
   8593   "Find clients which can handle current buffer."
   8594   (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   8595                                                             #'lsp--server-binary-present?)))
   8596     (lsp-log "Found the following clients for %s: %s"
   8597              (buffer-file-name)
   8598              (s-join ", "
   8599                      (-map (lambda (client)
   8600                              (format "(server-id %s, priority %s)"
   8601                                      (lsp--client-server-id client)
   8602                                      (lsp--client-priority client)))
   8603                            matching-clients)))
   8604     (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients))
   8605             (selected-clients (if-let ((main-client (and main-clients
   8606                                                          (--max-by (> (lsp--client-priority it)
   8607                                                                       (lsp--client-priority other))
   8608                                                                    main-clients))))
   8609                                   (cons main-client add-on-clients)
   8610                                 add-on-clients)))
   8611       (lsp-log "The following clients were selected based on priority: %s"
   8612                (s-join ", "
   8613                        (-map (lambda (client)
   8614                                (format "(server-id %s, priority %s)"
   8615                                        (lsp--client-server-id client)
   8616                                        (lsp--client-priority client)))
   8617                              selected-clients)))
   8618       selected-clients)))
   8619 
   8620 (defun lsp-workspace-remove-all-folders()
   8621   "Delete all lsp tracked folders."
   8622   (interactive)
   8623   (--each (lsp-session-folders (lsp-session))
   8624     (lsp-workspace-folders-remove it)))
   8625 
   8626 (defun lsp-register-client (client)
   8627   "Registers LSP client CLIENT."
   8628   (let ((client-id (lsp--client-server-id client)))
   8629     (puthash client-id client lsp-clients)
   8630     (setplist (intern (format "lsp-%s-after-open-hook" client-id))
   8631               `( standard-value (nil) custom-type hook
   8632                  custom-package-version (lsp-mode . "7.0.1")
   8633                  variable-documentation ,(format "Hooks to run after `%s' server is run." client-id)
   8634                  custom-requests nil)))
   8635   (when (and lsp-auto-register-remote-clients
   8636              (not (lsp--client-remote? client)))
   8637     (let ((remote-client (copy-lsp--client client)))
   8638       (setf (lsp--client-remote? remote-client) t
   8639             (lsp--client-server-id remote-client) (intern
   8640                                                    (format "%s-tramp"
   8641                                                            (lsp--client-server-id client)))
   8642             ;; disable automatic download
   8643             (lsp--client-download-server-fn remote-client) nil)
   8644       (lsp-register-client remote-client))))
   8645 
   8646 (defun lsp--create-initialization-options (_session client)
   8647   "Create initialization-options from SESSION and CLIENT.
   8648 Add workspace folders depending on server being multiroot and
   8649 session workspace folder configuration for the server."
   8650   (let* ((initialization-options-or-fn (lsp--client-initialization-options client)))
   8651     (if (functionp initialization-options-or-fn)
   8652         (funcall initialization-options-or-fn)
   8653       initialization-options-or-fn)))
   8654 
   8655 (defvar lsp-client-settings (make-hash-table :test 'equal)
   8656   "For internal use, any external users please use
   8657   `lsp-register-custom-settings' function instead")
   8658 
   8659 (defun lsp-register-custom-settings (props)
   8660   "Register PROPS.
   8661 PROPS is list of triple (path value boolean?) where PATH is the path to the
   8662 property; VALUE can be a literal value, symbol to be evaluated, or either a
   8663 function or lambda function to be called without arguments; BOOLEAN? is an
   8664 optional flag that should be non-nil for boolean settings, when it is nil the
   8665 property will be ignored if the VALUE is nil.
   8666 
   8667 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))'
   8668 \(note the double parentheses)"
   8669   (mapc
   8670    (-lambda ((path . rest))
   8671      (puthash path rest lsp-client-settings))
   8672    props))
   8673 
   8674 (defun lsp-region-text (region)
   8675   "Get the text for REGION in current buffer."
   8676   (-let (((start . end) (lsp--range-to-region region)))
   8677     (buffer-substring-no-properties start end)))
   8678 
   8679 (defun lsp-ht-set (tbl paths value)
   8680   "Set nested hash table value.
   8681 TBL - a hash table, PATHS is the path to the nested VALUE."
   8682   (pcase paths
   8683     (`(,path) (ht-set! tbl path value))
   8684     (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl)
   8685                                            (let ((temp-tbl (ht)))
   8686                                              (ht-set! tbl path temp-tbl)
   8687                                              temp-tbl))))
   8688                        (lsp-ht-set nested-tbl rst value)))))
   8689 
   8690 ;; sections
   8691 
   8692 (defalias 'defcustom-lsp 'lsp-defcustom)
   8693 
   8694 (defmacro lsp-defcustom (symbol standard doc &rest args)
   8695   "Defines `lsp-mode' server property."
   8696   (declare (doc-string 3) (debug (name body))
   8697            (indent defun))
   8698   (let ((path (plist-get args :lsp-path))
   8699         (setter (intern (concat (symbol-name symbol) "--set"))))
   8700     (cl-remf args :lsp-path)
   8701     `(progn
   8702        (lsp-register-custom-settings
   8703         (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type))))))
   8704 
   8705        (defcustom ,symbol ,standard ,doc ,@args)
   8706 
   8707        ;; Use a variable watcher instead of registering a `defcustom'
   8708        ;; setter since `hack-local-variables' is not aware of custom
   8709        ;; setters and won't invoke them.
   8710 
   8711        (defun ,setter (sym val op _where)
   8712          (when (eq op 'set)
   8713            (lsp--set-custom-property sym val ,path)))
   8714 
   8715        (add-variable-watcher ',symbol #',setter))))
   8716 
   8717 (defun lsp--set-custom-property (sym val path)
   8718   (set sym val)
   8719   (let ((section (cl-first (s-split "\\." path))))
   8720     (mapc (lambda (workspace)
   8721             (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace))
   8722                               section)
   8723               (with-lsp-workspace workspace
   8724                 (lsp--set-configuration (lsp-configuration-section section)))))
   8725           (lsp--session-workspaces (lsp-session)))))
   8726 
   8727 (defun lsp-configuration-section (section)
   8728   "Get settings for SECTION."
   8729   (let ((ret (ht-create)))
   8730     (maphash (-lambda (path (variable boolean?))
   8731                (when (s-matches? (concat (regexp-quote section) "\\..*") path)
   8732                  (let* ((symbol-value (-> variable
   8733                                           lsp-resolve-value
   8734                                           lsp-resolve-value))
   8735                         (value (if (and boolean? (not symbol-value))
   8736                                    :json-false
   8737                                  symbol-value)))
   8738                    (when (or boolean? value)
   8739                      (lsp-ht-set ret (s-split "\\." path) value)))))
   8740              lsp-client-settings)
   8741     ret))
   8742 
   8743 
   8744 (defun lsp--start-connection (session client project-root)
   8745   "Initiates connection created from CLIENT for PROJECT-ROOT.
   8746 SESSION is the active session."
   8747   (when (lsp--client-multi-root client)
   8748     (cl-pushnew project-root (gethash (lsp--client-server-id client)
   8749                                       (lsp-session-server-id->folders session))))
   8750   (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)
   8751 
   8752   (unwind-protect
   8753       (lsp--start-workspace session client project-root (lsp--create-initialization-options session client))
   8754     (lsp--spinner-stop)))
   8755 
   8756 ;; lsp-log-io-mode
   8757 
   8758 (defvar lsp-log-io-mode-map
   8759   (let ((map (make-sparse-keymap)))
   8760     (define-key map (kbd "M-n") #'lsp-log-io-next)
   8761     (define-key map (kbd "M-p") #'lsp-log-io-prev)
   8762     (define-key map (kbd "k") #'lsp--erase-log-buffer)
   8763     (define-key map (kbd "K") #'lsp--erase-session-log-buffers)
   8764     map)
   8765   "Keymap for lsp log buffer mode.")
   8766 
   8767 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo"
   8768   "Special mode for viewing IO logs.")
   8769 
   8770 (defun lsp-workspace-show-log (workspace)
   8771   "Display the log buffer of WORKSPACE."
   8772   (interactive
   8773    (list (if lsp-log-io
   8774              (if (eq (length (lsp-workspaces)) 1)
   8775                  (cl-first (lsp-workspaces))
   8776                (lsp--completing-read "Workspace: " (lsp-workspaces)
   8777                                      #'lsp--workspace-print nil t))
   8778            (user-error "IO logging is disabled"))))
   8779   (pop-to-buffer (lsp--get-log-buffer-create workspace)))
   8780 
   8781 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log)
   8782 
   8783 (defun lsp--get-log-buffer-create (workspace)
   8784   "Return the lsp log buffer of WORKSPACE, creating a new one if needed."
   8785   (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8786          (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id)))
   8787     (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid))))
   8788 
   8789 (defun lsp--erase-log-buffer (&optional all)
   8790   "Delete contents of current lsp log buffer.
   8791 When ALL is t, erase all log buffers of the running session."
   8792   (interactive)
   8793   (let* ((workspaces (lsp--session-workspaces (lsp-session)))
   8794          (current-log-buffer (current-buffer)))
   8795     (dolist (w workspaces)
   8796       (let ((b (lsp--get-log-buffer-create w)))
   8797         (when (or all (eq b current-log-buffer))
   8798           (with-current-buffer b
   8799             (let ((inhibit-read-only t))
   8800               (erase-buffer))))))))
   8801 
   8802 (defun lsp--erase-session-log-buffers ()
   8803   "Erase log buffers of the running session."
   8804   (interactive)
   8805   (lsp--erase-log-buffer t))
   8806 
   8807 (defun lsp-log-io-next (arg)
   8808   "Move to next log entry."
   8809   (interactive "P")
   8810   (ewoc-goto-next lsp--log-io-ewoc (or arg 1)))
   8811 
   8812 (defun lsp-log-io-prev (arg)
   8813   "Move to previous log entry."
   8814   (interactive "P")
   8815   (ewoc-goto-prev lsp--log-io-ewoc (or arg 1)))
   8816 
   8817 
   8818 
   8819 (cl-defmethod lsp-process-id ((process process))
   8820   (process-id process))
   8821 
   8822 (cl-defmethod lsp-process-name ((process process)) (process-name process))
   8823 
   8824 (cl-defmethod lsp-process-status ((process process)) (process-status process))
   8825 
   8826 (cl-defmethod lsp-process-kill ((process process))
   8827   (when (process-live-p process)
   8828     (kill-process process)))
   8829 
   8830 (cl-defmethod lsp-process-send ((process process) message)
   8831   (condition-case err
   8832       (process-send-string process (lsp--make-message message))
   8833     (error (lsp--error "Sending to process failed with the following error: %s"
   8834                        (error-message-string err)))))
   8835 
   8836 (cl-defmethod lsp-process-cleanup (process)
   8837   ;; Kill standard error buffer only if the process exited normally.
   8838   ;; Leave it intact otherwise for debugging purposes.
   8839   (let ((buffer (-> process process-name get-buffer)))
   8840     (when (and (eq (process-status process) 'exit)
   8841                (zerop (process-exit-status process))
   8842                (buffer-live-p buffer))
   8843       (kill-buffer buffer))))
   8844 
   8845 
   8846 ;; native JSONRPC
   8847 
   8848 (declare-function json-rpc "ext:json")
   8849 (declare-function json-rpc-connection "ext:json")
   8850 (declare-function json-rpc-send "ext:json")
   8851 (declare-function json-rpc-shutdown "ext:json")
   8852 (declare-function json-rpc-stderr "ext:json")
   8853 (declare-function json-rpc-pid "ext:json")
   8854 
   8855 (defvar lsp-json-rpc-thread nil)
   8856 (defvar lsp-json-rpc-queue nil)
   8857 (defvar lsp-json-rpc-done nil)
   8858 (defvar lsp-json-rpc-mutex (make-mutex))
   8859 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex))
   8860 
   8861 (defun lsp-json-rpc-process-queue ()
   8862   (while (not lsp-json-rpc-done)
   8863     (while lsp-json-rpc-queue
   8864       (-let (((proc . message) (pop lsp-json-rpc-queue)))
   8865         (json-rpc-send
   8866          proc message
   8867          :null-object nil
   8868          :false-object :json-false)))
   8869     (with-mutex lsp-json-rpc-mutex
   8870       (condition-wait lsp-json-rpc-condition))))
   8871 
   8872 (cl-defmethod lsp-process-id (process) (json-rpc-pid process))
   8873 
   8874 (cl-defmethod lsp-process-name (_process) "TBD")
   8875 
   8876 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process))
   8877 
   8878 (cl-defmethod lsp-process-send (proc message)
   8879   (unless lsp-json-rpc-thread
   8880     (with-current-buffer (get-buffer-create " *json-rpc*")
   8881       (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*"))))
   8882 
   8883   (with-mutex lsp-json-rpc-mutex
   8884     (setq lsp-json-rpc-queue (append lsp-json-rpc-queue
   8885                                      (list (cons proc message))))
   8886     (condition-notify lsp-json-rpc-condition)))
   8887 
   8888 (cl-defmethod lsp-process-cleanup (_proc))
   8889 
   8890 (defun lsp-json-rpc-connection (workspace command)
   8891   (let ((con (apply #'json-rpc-connection command))
   8892         (object-type (if lsp-use-plists 'plist 'hash-table)))
   8893     (with-current-buffer (get-buffer-create " *json-rpc*")
   8894       (make-thread
   8895        (lambda ()
   8896          (json-rpc
   8897           con
   8898           (lambda (result err done)
   8899             (run-with-timer
   8900              0.0
   8901              nil
   8902              (lambda ()
   8903                (cond
   8904                 (result (lsp--parser-on-message result workspace))
   8905                 (err (warn "Json parsing failed with the following error: %s" err))
   8906                 (done (lsp--handle-process-exit workspace ""))))))
   8907           :object-type object-type
   8908           :null-object nil
   8909           :false-object nil))
   8910        "*json-rpc-connection*"))
   8911     (cons con con)))
   8912 
   8913 (defun lsp-json-rpc-stderr ()
   8914   (interactive)
   8915   (--when-let (pcase (lsp-workspaces)
   8916                 (`nil (user-error "There are no active servers in the current buffer"))
   8917                 (`(,workspace) workspace)
   8918                 (workspaces (lsp--completing-read "Select server: "
   8919                                                   workspaces
   8920                                                   'lsp--workspace-print nil t)))
   8921     (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it)))
   8922           (buffer (format "*stderr-%s*" (lsp--workspace-print it)) ))
   8923       (with-current-buffer (get-buffer-create buffer)
   8924         (with-help-window buffer
   8925           (insert content))))))
   8926 
   8927 
   8928 (defun lsp--workspace-print (workspace)
   8929   "Visual representation WORKSPACE."
   8930   (let* ((proc (lsp--workspace-cmd-proc workspace))
   8931          (status (lsp--workspace-status workspace))
   8932          (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name))
   8933          (pid (lsp-process-id proc)))
   8934 
   8935     (if (eq 'initialized status)
   8936         (format "%s:%s" server-id pid)
   8937       (format "%s:%s/%s" server-id pid status))))
   8938 
   8939 (defun lsp--map-tree-widget (m)
   8940   "Build `tree-widget' from a hash-table or plist M."
   8941   (when (lsp-structure-p m)
   8942     (let (nodes)
   8943       (lsp-map (lambda (k v)
   8944                  (push `(tree-widget
   8945                          :tag ,(if (lsp-structure-p v)
   8946                                    (format "%s:" k)
   8947                                  (format "%s: %s" k
   8948                                          (propertize (format "%s" v)
   8949                                                      'face
   8950                                                      'font-lock-string-face)))
   8951                          :open t
   8952                          ,@(lsp--map-tree-widget v))
   8953                        nodes))
   8954                m)
   8955       nodes)))
   8956 
   8957 (defun lsp-buffer-name (buffer-id)
   8958   (if-let ((buffer-name (plist-get buffer-id :buffer-name)))
   8959       (funcall buffer-name buffer-id)
   8960     (buffer-name buffer-id)))
   8961 
   8962 (defun lsp--render-workspace (workspace)
   8963   "Tree node representation of WORKSPACE."
   8964   `(tree-widget :tag ,(lsp--workspace-print workspace)
   8965                 :open t
   8966                 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face)
   8967                              :open t
   8968                              ,@(->> workspace
   8969                                     (lsp--workspace-buffers)
   8970                                     (--map `(tree-widget
   8971                                              :tag ,(when (lsp-buffer-live-p it)
   8972                                                      (let ((buffer-name (lsp-buffer-name it)))
   8973                                                        (if (lsp-with-current-buffer it buffer-read-only)
   8974                                                            (propertize buffer-name 'face 'font-lock-constant-face)
   8975                                                          buffer-name)))))))
   8976                 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face)
   8977                              ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget))))
   8978 
   8979 (define-derived-mode lsp-browser-mode special-mode "LspBrowser"
   8980   "Define mode for displaying lsp sessions."
   8981   (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t)))))
   8982 
   8983 (defun lsp-describe-session ()
   8984   "Describes current `lsp-session'."
   8985   (interactive)
   8986   (let ((session (lsp-session))
   8987         (buf (get-buffer-create "*lsp session*"))
   8988         (root (lsp-workspace-root)))
   8989     (with-current-buffer buf
   8990       (lsp-browser-mode)
   8991       (let ((inhibit-read-only t))
   8992         (erase-buffer)
   8993         (--each (lsp-session-folders session)
   8994           (widget-create
   8995            `(tree-widget
   8996              :tag ,(propertize it 'face 'font-lock-keyword-face)
   8997              :open t
   8998              ,@(->> session
   8999                     (lsp-session-folder->servers)
   9000                     (gethash it)
   9001                     (-map 'lsp--render-workspace)))))))
   9002     (pop-to-buffer buf)
   9003     (goto-char (point-min))
   9004     (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag)
   9005              until (or (and root (string= tag root)) (eobp))
   9006              do (goto-char (next-overlay-change (point))))))
   9007 
   9008 (defun lsp--session-workspaces (session)
   9009   "Get all workspaces that are part of the SESSION."
   9010   (-> session lsp-session-folder->servers hash-table-values -flatten -uniq))
   9011 
   9012 (defun lsp--find-multiroot-workspace (session client project-root)
   9013   "Look for a multiroot connection in SESSION created from CLIENT for
   9014 PROJECT-ROOT and BUFFER-MAJOR-MODE."
   9015   (when (lsp--client-multi-root client)
   9016     (-when-let (multi-root-workspace (->> session
   9017                                           (lsp--session-workspaces)
   9018                                           (--first (eq (-> it lsp--workspace-client lsp--client-server-id)
   9019                                                        (lsp--client-server-id client)))))
   9020       (with-lsp-workspace multi-root-workspace
   9021         (lsp-notify "workspace/didChangeWorkspaceFolders"
   9022                     (lsp-make-did-change-workspace-folders-params
   9023                      :event (lsp-make-workspace-folders-change-event
   9024                              :added (vector (lsp-make-workspace-folder
   9025                                              :uri (lsp--path-to-uri project-root)
   9026                                              :name (f-filename project-root)))
   9027                              :removed []))))
   9028 
   9029       (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace))
   9030       (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root))
   9031 
   9032       (lsp--persist-session session)
   9033 
   9034       (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace))
   9035       (lsp--open-in-workspace multi-root-workspace)
   9036 
   9037       multi-root-workspace)))
   9038 
   9039 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder)
   9040   "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT.
   9041 IGNORE-MULTI-FOLDER to ignore multi folder server."
   9042   (-map (lambda (client)
   9043           (or
   9044            (lsp--find-workspace session client project-root)
   9045            (unless ignore-multi-folder
   9046              (lsp--find-multiroot-workspace session client project-root))
   9047            (lsp--start-connection session client project-root)))
   9048         clients))
   9049 
   9050 (defun lsp--spinner-stop ()
   9051   "Stop the spinner in case all of the workspaces are started."
   9052   (when (--all? (eq (lsp--workspace-status it) 'initialized)
   9053                 lsp--buffer-workspaces)
   9054     (spinner-stop)))
   9055 
   9056 (defun lsp--open-in-workspace (workspace)
   9057   "Open in existing WORKSPACE."
   9058   (if (eq 'initialized (lsp--workspace-status workspace))
   9059       ;; when workspace is initialized just call document did open.
   9060       (progn
   9061         (with-lsp-workspace workspace
   9062           (when-let ((before-document-open-fn (-> workspace
   9063                                                   lsp--workspace-client
   9064                                                   lsp--client-before-file-open-fn)))
   9065             (funcall before-document-open-fn workspace))
   9066           (lsp--text-document-did-open))
   9067         (lsp--spinner-stop))
   9068     ;; when it is not initialized
   9069     (lsp--spinner-start)
   9070     (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace))))
   9071 
   9072 (defun lsp--find-workspace (session client project-root)
   9073   "Find server connection created with CLIENT in SESSION for PROJECT-ROOT."
   9074   (when-let ((workspace (->> session
   9075                              (lsp-session-folder->servers)
   9076                              (gethash project-root)
   9077                              (--first (eql (-> it lsp--workspace-client lsp--client-server-id)
   9078                                            (lsp--client-server-id client))))))
   9079     (lsp--open-in-workspace workspace)
   9080     workspace))
   9081 
   9082 (defun lsp--read-char (prompt &optional options)
   9083   "Wrapper for `read-char-from-minibuffer' if Emacs +27.
   9084 Fallback to `read-key' otherwise.
   9085 PROMPT is the message and OPTIONS the available options."
   9086   (if (fboundp 'read-char-from-minibuffer)
   9087       (read-char-from-minibuffer prompt options)
   9088     (read-key prompt)))
   9089 
   9090 (defun lsp--find-root-interactively (session)
   9091   "Find project interactively.
   9092 Returns nil if the project should not be added to the current SESSION."
   9093   (condition-case nil
   9094       (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory))
   9095              (action (lsp--read-char
   9096                       (format
   9097                        "%s is not part of any project.
   9098 
   9099 %s ==> Import project root %s
   9100 %s ==> Import project by selecting root directory interactively
   9101 %s ==> Import project at current directory %s
   9102 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist
   9103 %s ==> Do not ask again for the current project by selecting ignore path interactively
   9104 %s ==> Do nothing: ask again when opening other files from the current project
   9105 
   9106 Select action: "
   9107                        (propertize (buffer-name) 'face 'bold)
   9108                        (propertize "i" 'face 'success)
   9109                        (propertize project-root-suggestion 'face 'bold)
   9110                        (propertize "I" 'face 'success)
   9111                        (propertize "." 'face 'success)
   9112                        (propertize default-directory 'face 'bold)
   9113                        (propertize "d" 'face 'warning)
   9114                        (propertize project-root-suggestion 'face 'bold)
   9115                        (propertize "D" 'face 'warning)
   9116                        (propertize "n" 'face 'warning))
   9117                       '(?i ?\r ?I ?. ?d ?D ?n))))
   9118         (cl-case action
   9119           (?i project-root-suggestion)
   9120           (?\r project-root-suggestion)
   9121           (?I (read-directory-name "Select workspace folder to add: "
   9122                                    (or project-root-suggestion default-directory)
   9123                                    nil
   9124                                    t))
   9125           (?. default-directory)
   9126           (?d (push project-root-suggestion (lsp-session-folders-blocklist session))
   9127               (lsp--persist-session session)
   9128               nil)
   9129           (?D (push (read-directory-name "Select folder to blocklist: "
   9130                                          (or project-root-suggestion default-directory)
   9131                                          nil
   9132                                          t)
   9133                     (lsp-session-folders-blocklist session))
   9134               (lsp--persist-session session)
   9135               nil)
   9136           (t nil)))
   9137     (quit)))
   9138 
   9139 (declare-function tramp-file-name-host "ext:tramp" (file) t)
   9140 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault))
   9141 
   9142 (defun lsp--files-same-host (f1 f2)
   9143   "Predicate on whether or not two files are on the same host."
   9144   (or (not (or (file-remote-p f1) (file-remote-p f2)))
   9145       (and (file-remote-p f1)
   9146            (file-remote-p f2)
   9147            (progn (require 'tramp)
   9148                   (equal (tramp-file-name-host (tramp-dissect-file-name f1))
   9149                          (tramp-file-name-host (tramp-dissect-file-name f2)))))))
   9150 
   9151 (defun lsp-find-session-folder (session file-name)
   9152   "Look in the current SESSION for folder containing FILE-NAME."
   9153   (let ((file-name-canonical (lsp-f-canonical file-name)))
   9154     (->> session
   9155          (lsp-session-folders)
   9156          (--filter (and (lsp--files-same-host it file-name-canonical)
   9157                         (or (lsp-f-same? it file-name-canonical)
   9158                             (and (f-dir? it)
   9159                                  (lsp-f-ancestor-of? it file-name-canonical)))))
   9160          (--max-by (> (length it)
   9161                       (length other))))))
   9162 
   9163 (defun lsp-find-workspace (server-id &optional file-name)
   9164   "Find workspace for SERVER-ID for FILE-NAME."
   9165   (-when-let* ((session (lsp-session))
   9166                (folder->servers (lsp-session-folder->servers session))
   9167                (workspaces (if file-name
   9168                                (gethash (lsp-find-session-folder session file-name) folder->servers)
   9169                              (lsp--session-workspaces session))))
   9170 
   9171     (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces)))
   9172 
   9173 (defun lsp--calculate-root (session file-name)
   9174   "Calculate project root for FILE-NAME in SESSION."
   9175   (and
   9176    (->> session
   9177         (lsp-session-folders-blocklist)
   9178         (--first (and (lsp--files-same-host it file-name)
   9179                       (lsp-f-ancestor-of? it file-name)
   9180                       (prog1 t
   9181                         (lsp--info "File %s is in blocklisted directory %s" file-name it))))
   9182         not)
   9183    (or
   9184     (when lsp-auto-guess-root
   9185       (lsp--suggest-project-root))
   9186     (unless lsp-guess-root-without-session
   9187       (lsp-find-session-folder session file-name))
   9188     (unless lsp-auto-guess-root
   9189       (when-let ((root-folder (lsp--find-root-interactively session)))
   9190         (if (or (not (f-equal? root-folder (expand-file-name "~/")))
   9191                 (yes-or-no-p
   9192                  (concat
   9193                   (propertize "[WARNING] " 'face 'warning)
   9194                   "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:
   9195 
   9196 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/').
   9197 2. If your file is under `~/' then create a subfolder and move that file in this folder.
   9198 
   9199 Type `No' to go back to project selection.
   9200 Type `Yes' to confirm `HOME' as project root.
   9201 Type `C-g' to cancel project import process and stop `lsp'")))
   9202             root-folder
   9203           (lsp--calculate-root session file-name)))))))
   9204 
   9205 (defun lsp--try-open-in-library-workspace ()
   9206   "Try opening current file as library file in any of the active workspace.
   9207 The library folders are defined by each client for each of the active workspace."
   9208   (when-let ((workspace (->> (lsp-session)
   9209                              (lsp--session-workspaces)
   9210                              ;; Sort the last active workspaces first as they are more likely to be
   9211                              ;; the correct ones, especially when jumping to a definition.
   9212                              (-sort (lambda (a _b)
   9213                                       (-contains? lsp--last-active-workspaces a)))
   9214                              (--first
   9215                               (and (-> it lsp--workspace-client lsp--supports-buffer?)
   9216                                    (when-let ((library-folders-fn
   9217                                                (-> it lsp--workspace-client lsp--client-library-folders-fn)))
   9218                                      (-first (lambda (library-folder)
   9219                                                (lsp-f-ancestor-of? library-folder (buffer-file-name)))
   9220                                              (funcall library-folders-fn it))))))))
   9221     (lsp--open-in-workspace workspace)
   9222     (view-mode t)
   9223     (lsp--info "Opening read-only library file %s." (buffer-file-name))
   9224     (list workspace)))
   9225 
   9226 (defun lsp--persist-session (session)
   9227   "Persist SESSION to `lsp-session-file'."
   9228   (lsp--persist lsp-session-file (make-lsp-session
   9229                                   :folders (lsp-session-folders session)
   9230                                   :folders-blocklist (lsp-session-folders-blocklist session)
   9231                                   :server-id->folders (lsp-session-server-id->folders session))))
   9232 
   9233 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder)
   9234   "Try create opening file as a project file.
   9235 When IGNORE-MULTI-FOLDER is t the lsp mode will start new
   9236 language server even if there is language server which can handle
   9237 current language. When IGNORE-MULTI-FOLDER is nil current file
   9238 will be opened in multi folder language server if there is
   9239 such."
   9240   (-let ((session (lsp-session)))
   9241     (-if-let (clients (if ask-for-client
   9242                           (list (lsp--completing-read "Select server to start: "
   9243                                                       (ht-values lsp-clients)
   9244                                                       (-compose 'symbol-name 'lsp--client-server-id) nil t))
   9245                         (lsp--find-clients)))
   9246         (-if-let (project-root (-some-> session
   9247                                  (lsp--calculate-root (buffer-file-name))
   9248                                  (lsp-f-canonical)))
   9249             (progn
   9250               ;; update project roots if needed and persist the lsp session
   9251               (unless (-contains? (lsp-session-folders session) project-root)
   9252                 (cl-pushnew project-root (lsp-session-folders session))
   9253                 (lsp--persist-session session))
   9254               (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder))
   9255           (lsp--warn "%s not in project or it is blocklisted." (buffer-name))
   9256           nil)
   9257       (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode)
   9258       nil)))
   9259 
   9260 (defun lsp-shutdown-workspace ()
   9261   "Shutdown language server."
   9262   (interactive)
   9263   (--when-let (pcase (lsp-workspaces)
   9264                 (`nil (user-error "There are no active servers in the current buffer"))
   9265                 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?"
   9266                                                        (lsp--workspace-print workspace)))
   9267                                  workspace))
   9268                 (workspaces (lsp--completing-read "Select server: "
   9269                                                   workspaces
   9270                                                   'lsp--workspace-print nil t)))
   9271     (lsp-workspace-shutdown it)))
   9272 
   9273 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1")
   9274 
   9275 (defcustom lsp-auto-select-workspace t
   9276   "Shutdown or restart a single workspace.
   9277 If set and the current buffer has only a single workspace
   9278 associated with it, `lsp-shutdown-workspace' and
   9279 `lsp-restart-workspace' will act on it without asking."
   9280   :type 'boolean
   9281   :group 'lsp-mode)
   9282 
   9283 (defun lsp--read-workspace ()
   9284   "Ask the user to select a workspace.
   9285 Errors if there are none."
   9286   (pcase (lsp-workspaces)
   9287     (`nil (error "No workspaces associated with the current buffer"))
   9288     ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace)
   9289     (workspaces (lsp--completing-read "Select workspace: " workspaces
   9290                                       #'lsp--workspace-print nil t))))
   9291 
   9292 (defun lsp-workspace-shutdown (workspace)
   9293   "Shut the workspace WORKSPACE and the language server associated with it"
   9294   (interactive (list (lsp--read-workspace)))
   9295   (lsp--warn "Stopping %s" (lsp--workspace-print workspace))
   9296   (with-lsp-workspace workspace (lsp--shutdown-workspace)))
   9297 
   9298 (defun lsp-disconnect ()
   9299   "Disconnect the buffer from the language server."
   9300   (interactive)
   9301   (lsp--text-document-did-close t)
   9302   (lsp-managed-mode -1)
   9303   (lsp-mode -1)
   9304   (setq lsp--buffer-workspaces nil)
   9305   (lsp--info "Disconnected"))
   9306 
   9307 (defun lsp-restart-workspace ()
   9308   (interactive)
   9309   (--when-let (pcase (lsp-workspaces)
   9310                 (`nil (user-error "There are no active servers in the current buffer"))
   9311                 (`(,workspace) workspace)
   9312                 (workspaces (lsp--completing-read "Select server: "
   9313                                                   workspaces
   9314                                                   'lsp--workspace-print nil t)))
   9315     (lsp-workspace-restart it)))
   9316 
   9317 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1")
   9318 
   9319 (defun lsp-workspace-restart (workspace)
   9320   "Restart the workspace WORKSPACE and the language server associated with it"
   9321   (interactive (list (lsp--read-workspace)))
   9322   (lsp--warn "Restarting %s" (lsp--workspace-print workspace))
   9323   (with-lsp-workspace workspace (lsp--shutdown-workspace t)))
   9324 
   9325 ;;;###autoload
   9326 (defun lsp (&optional arg)
   9327   "Entry point for the server startup.
   9328 When ARG is t the lsp mode will start new language server even if
   9329 there is language server which can handle current language. When
   9330 ARG is nil current file will be opened in multi folder language
   9331 server if there is such. When `lsp' is called with prefix
   9332 argument ask the user to select which language server to start."
   9333   (interactive "P")
   9334 
   9335   (lsp--require-packages)
   9336 
   9337   (when (buffer-file-name)
   9338     (let (clients
   9339           (matching-clients (lsp--filter-clients
   9340                              (-andfn #'lsp--supports-buffer?
   9341                                      #'lsp--server-binary-present?))))
   9342       (cond
   9343        (matching-clients
   9344         (when (setq lsp--buffer-workspaces
   9345                     (or (and
   9346                          ;; Don't open as library file if file is part of a project.
   9347                          (not (lsp-find-session-folder (lsp-session) (buffer-file-name)))
   9348                          (lsp--try-open-in-library-workspace))
   9349                         (lsp--try-project-root-workspaces (equal arg '(4))
   9350                                                           (and arg (not (equal arg 1))))))
   9351           (lsp-mode 1)
   9352           (when lsp-auto-configure (lsp--auto-configure))
   9353           (setq lsp-buffer-uri (lsp--buffer-uri))
   9354           (lsp--info "Connected to %s."
   9355                      (apply 'concat (--map (format "[%s %s]"
   9356                                                    (lsp--workspace-print it)
   9357                                                    (lsp--workspace-root it))
   9358                                            lsp--buffer-workspaces)))))
   9359        ;; look for servers which are currently being downloaded.
   9360        ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9361                                                    #'lsp--client-download-in-progress?)))
   9362         (lsp--info "There are language server(%s) installation in progress.
   9363 The server(s) will be started in the buffer when it has finished."
   9364                    (-map #'lsp--client-server-id clients))
   9365         (seq-do (lambda (client)
   9366                   (cl-pushnew (current-buffer) (lsp--client-buffers client)))
   9367                 clients))
   9368        ;; look for servers to install
   9369        ((setq clients (lsp--filter-clients
   9370                        (-andfn #'lsp--supports-buffer?
   9371                                (-const lsp-enable-suggest-server-download)
   9372                                #'lsp--client-download-server-fn
   9373                                (-not #'lsp--client-download-in-progress?))))
   9374         (let ((client (lsp--completing-read
   9375                        (concat "Unable to find installed server supporting this file. "
   9376                                "The following servers could be installed automatically: ")
   9377                        clients
   9378                        (-compose #'symbol-name #'lsp--client-server-id)
   9379                        nil
   9380                        t)))
   9381           (cl-pushnew (current-buffer) (lsp--client-buffers client))
   9382           (lsp--install-server-internal client)))
   9383        ;; ignore other warnings
   9384        ((not lsp-warn-no-matched-clients)
   9385         nil)
   9386        ;; automatic installation disabled
   9387        ((setq clients (unless matching-clients
   9388                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9389                                                      #'lsp--client-download-server-fn
   9390                                                      (-not (-const lsp-enable-suggest-server-download))
   9391                                                      (-not #'lsp--server-binary-present?)))))
   9392         (lsp--warn "The following servers support current file but automatic download is disabled: %s
   9393 \(If you have already installed the server check *lsp-log*)."
   9394                    (mapconcat (lambda (client)
   9395                                 (symbol-name (lsp--client-server-id client)))
   9396                               clients
   9397                               " ")))
   9398        ;; no clients present
   9399        ((setq clients (unless matching-clients
   9400                         (lsp--filter-clients (-andfn #'lsp--supports-buffer?
   9401                                                      (-not #'lsp--server-binary-present?)))))
   9402         (lsp--warn "The following servers support current file but do not have automatic installation: %s
   9403 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages.
   9404 \(If you have already installed the server check *lsp-log*)."
   9405                    (mapconcat (lambda (client)
   9406                                 (symbol-name (lsp--client-server-id client)))
   9407                               clients
   9408                               " ")))
   9409        ;; no matches
   9410        ((-> #'lsp--supports-buffer? lsp--filter-clients not)
   9411         (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'.
   9412 This issue might be caused by:
   9413 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'.
   9414 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'.
   9415 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/ .
   9416 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/.
   9417 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients').
   9418 You can customize `lsp-warn-no-matched-clients' to disable this message."
   9419                     major-mode major-mode major-mode))))))
   9420 
   9421 (defun lsp--buffer-visible-p ()
   9422   "Return non nil if current buffer is visible."
   9423   (or (buffer-modified-p) (get-buffer-window nil t)))
   9424 
   9425 (defun lsp--init-if-visible ()
   9426   "Run `lsp' for the current buffer if the buffer is visible.
   9427 Returns non nil if `lsp' was run for the buffer."
   9428   (when (lsp--buffer-visible-p)
   9429     (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t)
   9430     (lsp)
   9431     t))
   9432 
   9433 ;;;###autoload
   9434 (defun lsp-deferred ()
   9435   "Entry point that defers server startup until buffer is visible.
   9436 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'.
   9437 This avoids overloading the server with many files when starting Emacs."
   9438   ;; Workspace may not be initialized yet. Use a buffer local variable to
   9439   ;; remember that we deferred loading of this buffer.
   9440   (setq lsp--buffer-deferred t)
   9441   (let ((buffer (current-buffer)))
   9442     ;; Avoid false positives as desktop-mode restores buffers by deferring
   9443     ;; visibility check until the stack clears.
   9444     (run-with-idle-timer 0 nil (lambda ()
   9445                                  (when (buffer-live-p buffer)
   9446                                    (with-current-buffer buffer
   9447                                      (unless (lsp--init-if-visible)
   9448                                        (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t))))))))
   9449 
   9450 
   9451 
   9452 (defvar lsp-file-truename-cache (ht))
   9453 
   9454 (defmacro lsp-with-cached-filetrue-name (&rest body)
   9455   "Executes BODY caching the `file-truename' calls."
   9456   `(let ((old-fn (symbol-function 'file-truename)))
   9457      (unwind-protect
   9458          (progn
   9459            (fset 'file-truename
   9460                  (lambda (file-name &optional counter prev-dirs)
   9461                    (or (gethash file-name lsp-file-truename-cache)
   9462                        (puthash file-name (apply old-fn (list file-name counter prev-dirs))
   9463                                 lsp-file-truename-cache))))
   9464            ,@body)
   9465        (fset 'file-truename old-fn))))
   9466 
   9467 
   9468 (defun lsp-virtual-buffer-call (key &rest args)
   9469   (when lsp--virtual-buffer
   9470     (when-let ((fn (plist-get lsp--virtual-buffer key)))
   9471       (apply fn args))))
   9472 
   9473 (defun lsp-translate-column (column)
   9474   "Translate COLUMN taking into account virtual buffers."
   9475   (or (lsp-virtual-buffer-call :real->virtual-char column)
   9476       column))
   9477 
   9478 (defun lsp-translate-line (line)
   9479   "Translate LINE taking into account virtual buffers."
   9480   (or (lsp-virtual-buffer-call :real->virtual-line line)
   9481       line))
   9482 
   9483 
   9484 ;; lsp internal validation.
   9485 
   9486 (defmacro lsp--doctor (&rest checks)
   9487   `(-let [buf (current-buffer)]
   9488      (with-current-buffer (get-buffer-create "*lsp-performance*")
   9489        (with-help-window (current-buffer)
   9490          ,@(-map (-lambda ((msg form))
   9491                    `(insert (format "%s: %s\n" ,msg
   9492                                     (let ((res (with-current-buffer buf
   9493                                                  ,form)))
   9494                                       (cond
   9495                                        ((eq res :optional) (propertize "OPTIONAL" 'face 'warning))
   9496                                        (res (propertize "OK" 'face 'success))
   9497                                        (t (propertize "ERROR" 'face 'error)))))))
   9498                  (-partition 2 checks))))))
   9499 
   9500 (define-obsolete-function-alias 'lsp-diagnose
   9501   'lsp-doctor "lsp-mode 8.0.0")
   9502 
   9503 (defun lsp-doctor ()
   9504   "Validate performance settings."
   9505   (interactive)
   9506   (lsp--doctor
   9507    "Checking for Native JSON support" (functionp 'json-serialize)
   9508    "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max)
   9509    "Check `read-process-output-max' default has been changed from 4k"
   9510    (and (boundp 'read-process-output-max)
   9511         (> read-process-output-max 4096))
   9512    "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)"
   9513    (condition-case _err
   9514        (progn (lsp--make-message (list "a" "b"))
   9515               nil)
   9516      (error t))
   9517    "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000)
   9518    "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)
   9519    "Using emacs 28+ with native compilation?"
   9520    (or (and (fboundp 'native-comp-available-p)
   9521             (native-comp-available-p))
   9522        :optional)))
   9523 
   9524 (declare-function package-version-join "ext:package")
   9525 (declare-function package-desc-version "ext:package")
   9526 (declare-function package--alist "ext:package")
   9527 
   9528 (defun lsp-version ()
   9529   "Return string describing current version of `lsp-mode'."
   9530   (interactive)
   9531   (unless (featurep 'package)
   9532     (require 'package))
   9533   (let ((ver (format "lsp-mode %s, Emacs %s, %s"
   9534                      (package-version-join
   9535                       (package-desc-version
   9536                        (car (alist-get 'lsp-mode (package--alist)))))
   9537                      emacs-version
   9538                      system-type)))
   9539     (if (called-interactively-p 'interactive)
   9540         (lsp--info "%s" ver)
   9541       ver)))
   9542 
   9543 
   9544 
   9545 ;; org-mode/virtual-buffer
   9546 
   9547 (declare-function org-babel-get-src-block-info "ext:ob-core")
   9548 (declare-function org-do-remove-indentation "ext:org-macs")
   9549 (declare-function org-src-get-lang-mode "ext:org-src")
   9550 (declare-function org-element-context "ext:org-element")
   9551 
   9552 (defun lsp--virtual-buffer-update-position ()
   9553   (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range))
   9554                                      (funcall in-range))
   9555                                    lsp--virtual-buffer-connections))
   9556       (unless (equal virtual-buffer lsp--virtual-buffer)
   9557         (lsp-org))
   9558     (when lsp-managed-mode
   9559       (lsp-managed-mode -1)
   9560       (lsp-mode -1)
   9561       (setq lsp--buffer-workspaces nil)
   9562       (setq lsp--virtual-buffer nil)
   9563       (setq lsp-buffer-uri nil)
   9564 
   9565       ;; force refresh of diagnostics
   9566       (run-hooks 'lsp-after-diagnostics-hook))))
   9567 
   9568 (defun lsp-virtual-buffer-on-change (start end length)
   9569   "Adjust on change event to be executed against the proper language server."
   9570   (let ((max-point (max end
   9571                         (or (plist-get lsp--before-change-vals :end) 0)
   9572                         (+ start length))))
   9573     (when-let ((virtual-buffer (-first (lambda (vb)
   9574                                          (let ((lsp--virtual-buffer vb))
   9575                                            (and (lsp-virtual-buffer-call :in-range start)
   9576                                                 (lsp-virtual-buffer-call :in-range max-point))))
   9577                                        lsp--virtual-buffer-connections)))
   9578       (lsp-with-current-buffer virtual-buffer
   9579         (lsp-on-change start end length
   9580                        (lambda (&rest _)
   9581                          (list :range (lsp--range (list :character 0 :line 0)
   9582                                                   lsp--virtual-buffer-point-max)
   9583                                :text (lsp--buffer-content))))))))
   9584 
   9585 (defun lsp-virtual-buffer-before-change (start _end)
   9586   (when-let ((virtual-buffer (-first (lambda (vb)
   9587                                        (lsp-with-current-buffer vb
   9588                                          (lsp-virtual-buffer-call :in-range start)))
   9589                                      lsp--virtual-buffer-connections)))
   9590     (lsp-with-current-buffer virtual-buffer
   9591       (setq lsp--virtual-buffer-point-max
   9592             (lsp--point-to-position (lsp-virtual-buffer-call :last-point))))))
   9593 
   9594 (defun lsp-patch-on-change-event ()
   9595   (remove-hook 'after-change-functions #'lsp-on-change t)
   9596   (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t)
   9597   (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t))
   9598 
   9599 (defun lsp-kill-virtual-buffers ()
   9600   (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections))
   9601 
   9602 (defun lsp--move-point-in-indentation (point indentation)
   9603   (save-excursion
   9604     (goto-char point)
   9605     (if (<= point (+ (line-beginning-position) indentation))
   9606         (line-beginning-position)
   9607       point)))
   9608 
   9609 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck")
   9610 (declare-function flycheck-add-mode "ext:flycheck")
   9611 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics")
   9612 
   9613 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn)
   9614 
   9615 (defun lsp-flycheck-add-mode (mode)
   9616   "Register flycheck support for MODE."
   9617   (lsp-diagnostics-lsp-checker-if-needed)
   9618   (unless (flycheck-checker-supports-major-mode-p 'lsp mode)
   9619     (flycheck-add-mode 'lsp mode)))
   9620 
   9621 (defun lsp-progress-spinner-type ()
   9622   "Retrieve the spinner type value, if value is not a symbol of `spinner-types
   9623 defaults to `progress-bar."
   9624   (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar))
   9625 
   9626 (defun lsp-org ()
   9627   (interactive)
   9628   (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range))
   9629                                                               (funcall in-range))
   9630                                                             lsp--virtual-buffer-connections))
   9631       (unless (equal lsp--virtual-buffer virtual-buffer)
   9632         (setq lsp--buffer-workspaces workspaces)
   9633         (setq lsp--virtual-buffer virtual-buffer)
   9634         (setq lsp-buffer-uri nil)
   9635         (lsp-mode 1)
   9636         (lsp-managed-mode 1)
   9637         (lsp-patch-on-change-event))
   9638 
   9639     (save-excursion
   9640       (-let* (virtual-buffer
   9641               (wcb (lambda (f)
   9642                      (with-current-buffer (plist-get virtual-buffer :buffer)
   9643                        (-let* (((&plist :major-mode :buffer-file-name
   9644                                         :goto-buffer :workspaces) virtual-buffer)
   9645                                (lsp--virtual-buffer virtual-buffer)
   9646                                (lsp--buffer-workspaces workspaces))
   9647                          (save-excursion
   9648                            (funcall goto-buffer)
   9649                            (funcall f))))))
   9650               ((&plist :begin :end :post-blank :language) (cl-second (org-element-context)))
   9651               ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light)))
   9652 
   9653               (file-name (if file-name
   9654                              (f-expand file-name)
   9655                            (user-error "You should specify file name in the src block header.")))
   9656               (begin-marker (progn
   9657                               (goto-char begin)
   9658                               (forward-line)
   9659                               (set-marker (make-marker) (point))))
   9660               (end-marker (progn
   9661                             (goto-char end)
   9662                             (forward-line (1- (- post-blank)))
   9663                             (set-marker (make-marker) (1+ (point)))))
   9664               (buf (current-buffer))
   9665               (src-block (buffer-substring-no-properties begin-marker
   9666                                                          (1- end-marker)))
   9667               (indentation (with-temp-buffer
   9668                              (insert src-block)
   9669 
   9670                              (goto-char (point-min))
   9671                              (let ((indentation (current-indentation)))
   9672                                (plist-put lsp--virtual-buffer :indentation indentation)
   9673                                (org-do-remove-indentation)
   9674                                (goto-char (point-min))
   9675                                (- indentation (current-indentation))))))
   9676         (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t)
   9677 
   9678         (when (fboundp 'flycheck-add-mode)
   9679           (lsp-flycheck-add-mode 'org-mode))
   9680 
   9681         (setq lsp--virtual-buffer
   9682               (list
   9683                :in-range (lambda (&optional point)
   9684                            (<= begin-marker (or point (point)) (1- end-marker)))
   9685                :goto-buffer (lambda () (goto-char begin-marker))
   9686                :buffer-string
   9687                (lambda ()
   9688                  (let ((src-block (buffer-substring-no-properties
   9689                                    begin-marker
   9690                                    (1- end-marker))))
   9691                    (with-temp-buffer
   9692                      (insert src-block)
   9693 
   9694                      (goto-char (point-min))
   9695                      (while (not (eobp))
   9696                        (delete-region (point) (if (> (+ (point) indentation) (line-end-position))
   9697                                                   (line-end-position)
   9698                                                 (+ (point) indentation)))
   9699                        (forward-line))
   9700                      (buffer-substring-no-properties (point-min)
   9701                                                      (point-max)))))
   9702                :buffer buf
   9703                :begin begin-marker
   9704                :end end-marker
   9705                :indentation indentation
   9706                :last-point (lambda () (1- end-marker))
   9707                :cur-position (lambda ()
   9708                                (lsp-save-restriction-and-excursion
   9709                                  (list :line (- (lsp--cur-line)
   9710                                                 (lsp--cur-line begin-marker))
   9711                                        :character (let ((character (- (point)
   9712                                                                       (line-beginning-position)
   9713                                                                       indentation)))
   9714                                                     (if (< character 0)
   9715                                                         0
   9716                                                       character)))))
   9717                :line/character->point (-lambda (line character)
   9718                                         (-let [inhibit-field-text-motion t]
   9719                                           (+ indentation
   9720                                              (lsp-save-restriction-and-excursion
   9721                                                (goto-char begin-marker)
   9722                                                (forward-line line)
   9723                                                (-let [line-end (line-end-position)]
   9724                                                  (if (> character (- line-end (point)))
   9725                                                      line-end
   9726                                                    (forward-char character)
   9727                                                    (point)))))))
   9728                :major-mode (org-src-get-lang-mode language)
   9729                :buffer-file-name file-name
   9730                :buffer-uri (lsp--path-to-uri file-name)
   9731                :with-current-buffer wcb
   9732                :buffer-live? (lambda (_) (buffer-live-p buf))
   9733                :buffer-name (lambda (_)
   9734                               (propertize (format "%s(%s:%s)%s"
   9735                                                   (buffer-name buf)
   9736                                                   begin-marker
   9737                                                   end-marker
   9738                                                   language)
   9739                                           'face 'italic))
   9740                :real->virtual-line (lambda (line)
   9741                                      (+ line (line-number-at-pos begin-marker) -1))
   9742                :real->virtual-char (lambda (char) (+ char indentation))
   9743                :cleanup (lambda ()
   9744                           (set-marker begin-marker nil)
   9745                           (set-marker end-marker nil))))
   9746         (setf virtual-buffer lsp--virtual-buffer)
   9747         (puthash file-name virtual-buffer lsp--virtual-buffer-mappings)
   9748         (push virtual-buffer lsp--virtual-buffer-connections)
   9749 
   9750         ;; TODO: tangle only connected sections
   9751         (add-hook 'after-save-hook 'org-babel-tangle nil t)
   9752         (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t)
   9753         (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t)
   9754 
   9755         (setq lsp--buffer-workspaces
   9756               (lsp-with-current-buffer virtual-buffer
   9757                 (lsp)
   9758                 (plist-put virtual-buffer :workspaces (lsp-workspaces))
   9759                 (lsp-workspaces)))))))
   9760 
   9761 (defun lsp-virtual-buffer-disconnect (virtual-buffer)
   9762   (interactive (list (or
   9763                       lsp--virtual-buffer
   9764                       (when lsp--virtual-buffer-connections
   9765                         (lsp--completing-read "Select virtual buffer to disconnect: "
   9766                                               lsp--virtual-buffer-connections
   9767                                               (-lambda ((&plist :buffer-file-name))
   9768                                                 buffer-file-name))))))
   9769   (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer)
   9770       (progn
   9771         (lsp-with-current-buffer virtual-buffer
   9772           (lsp--text-document-did-close))
   9773         (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections))
   9774         (when (eq virtual-buffer lsp--virtual-buffer)
   9775           (setf lsp--virtual-buffer nil))
   9776         (when cleanup (funcall cleanup))
   9777         (remhash file-name lsp--virtual-buffer-mappings)
   9778 
   9779         (lsp--virtual-buffer-update-position)
   9780         (lsp--info "Disconnected from buffer %s" file-name))
   9781     (lsp--error "Nothing to disconnect from?")))
   9782 
   9783 
   9784 ;; inlay hints
   9785 
   9786 (defface lsp-inlay-hint-face
   9787   '((t :inherit font-lock-comment-face))
   9788   "The face to use for the JavaScript inlays."
   9789   :group 'lsp-mode
   9790   :package-version '(lsp-mode . "9.0.0"))
   9791 
   9792 (defface lsp-inlay-hint-type-face
   9793   '((t :inherit lsp-inlay-hint-face))
   9794   "Face for inlay type hints (e.g. inferred variable types)."
   9795   :group 'lsp-mode
   9796   :package-version '(lsp-mode . "9.0.0"))
   9797 
   9798 (defcustom lsp-inlay-hint-type-format "%s"
   9799   "Format string for variable inlays (part of the inlay face)."
   9800   :type '(string :tag "String")
   9801   :group 'lsp-mode
   9802   :package-version '(lsp-mode . "9.0.0"))
   9803 
   9804 (defface lsp-inlay-hint-parameter-face
   9805   '((t :inherit lsp-inlay-hint-face))
   9806   "Face for inlay parameter hints (e.g. function parameter names at
   9807 call-site)."
   9808   :group 'lsp-mode
   9809   :package-version '(lsp-mode . "9.0.0"))
   9810 
   9811 (defcustom lsp-inlay-hint-param-format "%s"
   9812   "Format string for parameter inlays (part of the inlay face)."
   9813   :type '(string :tag "String")
   9814   :group 'lsp-mode
   9815   :package-version '(lsp-mode . "9.0.0"))
   9816 
   9817 (defcustom lsp-update-inlay-hints-on-scroll t
   9818   "If non-nil update inlay hints immediately when scrolling or
   9819 modifying window sizes."
   9820   :type 'boolean
   9821   :package-version '(lsp-mode . "9.0.0"))
   9822 
   9823 (defun lsp--format-inlay (text kind)
   9824   (cond
   9825    ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text))
   9826    ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text))
   9827    (t text)))
   9828 
   9829 (defun lsp--face-for-inlay (kind)
   9830   (cond
   9831    ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face)
   9832    ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face)
   9833    (t 'lsp-inlay-hint-face)))
   9834 
   9835 (defun lsp--update-inlay-hints-scroll-function (window start)
   9836   (lsp-update-inlay-hints start (window-end window t)))
   9837 
   9838 (defun lsp--update-inlay-hints ()
   9839   (lsp-update-inlay-hints (window-start) (window-end nil t)))
   9840 
   9841 (defun lsp--label-from-inlay-hints-response (label)
   9842   "Returns a string label built from an array of
   9843 InlayHintLabelParts or the argument itself if it's already a
   9844 string."
   9845   (cl-typecase label
   9846     (string label)
   9847     (vector
   9848      (string-join (mapcar (lambda (part)
   9849                             (-let (((&InlayHintLabelPart :value) part))
   9850                               value))
   9851                           label)))))
   9852 
   9853 (defun lsp-update-inlay-hints (start end)
   9854   (lsp-request-async
   9855    "textDocument/inlayHint"
   9856    (lsp-make-inlay-hints-params
   9857     :text-document (lsp--text-document-identifier)
   9858     :range (lsp-make-range :start
   9859                            (lsp-point-to-position start)
   9860                            :end
   9861                            (lsp-point-to-position end)))
   9862    (lambda (res)
   9863      (lsp--remove-overlays 'lsp-inlay-hint)
   9864      (dolist (hint res)
   9865        (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint)
   9866                (kind (or kind? lsp/inlay-hint-kind-type-hint))
   9867                (label (lsp--label-from-inlay-hints-response label))
   9868                (pos (lsp--position-to-point position))
   9869                (overlay (make-overlay pos pos nil 'front-advance 'end-advance)))
   9870          (when (stringp label)
   9871            (overlay-put overlay 'lsp-inlay-hint t)
   9872            (overlay-put overlay 'before-string
   9873                         (format "%s%s%s"
   9874                                 (if padding-left? " " "")
   9875                                 (propertize (lsp--format-inlay label kind)
   9876                                             'font-lock-face (lsp--face-for-inlay kind))
   9877                                 (if padding-right? " " "")))))))
   9878    :mode 'tick))
   9879 
   9880 (define-minor-mode lsp-inlay-hints-mode
   9881   "Mode for displaying inlay hints."
   9882   :lighter nil
   9883   (cond
   9884    ((and lsp-inlay-hints-mode lsp--buffer-workspaces)
   9885     (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t)
   9886     (when lsp-update-inlay-hints-on-scroll
   9887       (add-to-list (make-local-variable 'window-scroll-functions)
   9888                    #'lsp--update-inlay-hints-scroll-function)))
   9889    (t
   9890     (lsp--remove-overlays 'lsp-inlay-hint)
   9891     (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t)
   9892     (setf window-scroll-functions
   9893           (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions)))))
   9894 
   9895 
   9896 
   9897 ;;;###autoload
   9898 (defun lsp-start-plain ()
   9899   "Start `lsp-mode' using minimal configuration using the latest `melpa' version
   9900 of the packages.
   9901 
   9902 In case the major-mode that you are using for "
   9903   (interactive)
   9904   (let ((start-plain (make-temp-file "plain" nil ".el")))
   9905     (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el"
   9906                    start-plain t)
   9907     (async-shell-command
   9908      (format "%s -q -l %s %s"
   9909              (expand-file-name invocation-name invocation-directory)
   9910              start-plain
   9911              (or (buffer-file-name) ""))
   9912      (generate-new-buffer " *lsp-start-plain*"))))
   9913 
   9914 
   9915 
   9916 (provide 'lsp-mode)
   9917 ;;; lsp-mode.el ends here