lsp-mode.el (436096B)
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: 20241113.743 9 ;; Package-Revision: c41769e32c8d 10 11 ;; URL: https://github.com/emacs-lsp/lsp-mode 12 ;; This program is free software; you can redistribute it and/or modify 13 ;; it under the terms of the GNU General Public License as published by 14 ;; the Free Software Foundation, either version 3 of the License, or 15 ;; (at your option) any later version. 16 17 ;; This program is distributed in the hope that it will be useful, 18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 ;; GNU General Public License for more details. 21 22 ;; You should have received a copy of the GNU General Public License 23 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 24 25 ;;; Commentary: 26 27 ;; Emacs client/library for the Language Server Protocol 28 29 ;;; Code: 30 31 (require 'cl-generic) 32 (require 'cl-lib) 33 (require 'compile) 34 (require 'dash) 35 (require 'epg) 36 (require 'ewoc) 37 (require 'f) 38 (require 'filenotify) 39 (require 'files) 40 (require 'ht) 41 (require 'imenu) 42 (require 'inline) 43 (require 'json) 44 (require 'lv) 45 (require 'markdown-mode) 46 (require 'network-stream) 47 (require 'pcase) 48 (require 'rx) 49 (require 's) 50 (require 'seq) 51 (require 'spinner) 52 (require 'subr-x) 53 (require 'tree-widget) 54 (require 'url-parse) 55 (require 'url-util) 56 (require 'widget) 57 (require 'xref) 58 (require 'minibuffer) 59 (require 'help-mode) 60 (require 'lsp-protocol) 61 62 (defgroup lsp-mode nil 63 "Language Server Protocol client." 64 :group 'tools 65 :tag "Language Server (lsp-mode)") 66 67 (declare-function evil-set-command-property "ext:evil-common") 68 (declare-function projectile-project-root "ext:projectile") 69 (declare-function yas-expand-snippet "ext:yasnippet") 70 (declare-function dap-mode "ext:dap-mode") 71 (declare-function dap-auto-configure-mode "ext:dap-mode") 72 73 (defvar yas-inhibit-overlay-modification-protection) 74 (defvar yas-indent-line) 75 (defvar yas-wrap-around-region) 76 (defvar yas-also-auto-indent-first-line) 77 (defvar dap-auto-configure-mode) 78 (defvar dap-ui-menu-items) 79 (defvar company-minimum-prefix-length) 80 81 (defconst lsp--message-type-face 82 `((1 . ,compilation-error-face) 83 (2 . ,compilation-warning-face) 84 (3 . ,compilation-message-face) 85 (4 . ,compilation-info-face))) 86 87 (defconst lsp--errors 88 '((-32700 "Parse Error") 89 (-32600 "Invalid Request") 90 (-32601 "Method not Found") 91 (-32602 "Invalid Parameters") 92 (-32603 "Internal Error") 93 (-32099 "Server Start Error") 94 (-32000 "Server End Error") 95 (-32002 "Server Not Initialized") 96 (-32001 "Unknown Error Code") 97 (-32800 "Request Cancelled")) 98 "Alist of error codes to user friendly strings.") 99 100 (defconst lsp--empty-ht (make-hash-table)) 101 102 (eval-and-compile 103 (defun dash-expand:&lsp-wks (key source) 104 `(,(intern-soft (format "lsp--workspace-%s" (eval key))) ,source)) 105 106 (defun dash-expand:&lsp-cln (key source) 107 `(,(intern-soft (format "lsp--client-%s" (eval key))) ,source))) 108 109 (define-obsolete-variable-alias 'lsp-print-io 'lsp-log-io "lsp-mode 6.1") 110 111 (defcustom lsp-log-io nil 112 "If non-nil, log all messages from the language server to a *lsp-log* buffer." 113 :group 'lsp-mode 114 :type 'boolean) 115 116 (defcustom lsp-log-io-allowlist-methods '() 117 "The methods to filter before print to lsp-log-io." 118 :group 'lsp-mode 119 :type '(repeat string) 120 :package-version '(lsp-mode . "9.0.0")) 121 122 (defcustom lsp-log-max message-log-max 123 "Maximum number of lines to keep in the log buffer. 124 If nil, disable message logging. If t, log messages but don’t truncate 125 the buffer when it becomes large." 126 :group 'lsp-mode 127 :type '(choice (const :tag "Disable" nil) 128 (integer :tag "lines") 129 (const :tag "Unlimited" t)) 130 :package-version '(lsp-mode . "6.1")) 131 132 (defcustom lsp-io-messages-max t 133 "Maximum number of messages that can be locked in a `lsp-io' buffer." 134 :group 'lsp-mode 135 :type '(choice (const :tag "Unlimited" t) 136 (integer :tag "Messages")) 137 :package-version '(lsp-mode . "6.1")) 138 139 (defcustom lsp-keep-workspace-alive t 140 "If non nil keep workspace alive when the last workspace buffer is closed." 141 :group 'lsp-mode 142 :type 'boolean) 143 144 (defcustom lsp-enable-snippet t 145 "Enable/disable snippet completion support." 146 :group 'lsp-completion 147 :type 'boolean) 148 149 (defcustom lsp-enable-folding t 150 "Enable/disable code folding support." 151 :group 'lsp-mode 152 :type 'boolean 153 :package-version '(lsp-mode . "6.1")) 154 155 (define-obsolete-variable-alias 'lsp-enable-semantic-highlighting 'lsp-semantic-tokens-enable "lsp-mode 8.0.0") 156 157 (defcustom lsp-semantic-tokens-enable nil 158 "Enable/disable support for semantic tokens. 159 As defined by the Language Server Protocol 3.16." 160 :group 'lsp-semantic-tokens 161 :type 'boolean) 162 163 (defcustom lsp-folding-range-limit nil 164 "The maximum number of folding ranges to receive from the language server." 165 :group 'lsp-mode 166 :type '(choice (const :tag "No limit." nil) 167 (integer :tag "Number of lines.")) 168 :package-version '(lsp-mode . "6.1")) 169 170 (defcustom lsp-folding-line-folding-only nil 171 "If non-nil, only fold complete lines." 172 :group 'lsp-mode 173 :type 'boolean 174 :package-version '(lsp-mode . "6.1")) 175 176 (defcustom lsp-client-packages 177 '( ccls lsp-actionscript lsp-ada lsp-angular lsp-ansible lsp-asm lsp-astro 178 lsp-autotools lsp-awk lsp-bash lsp-beancount lsp-bufls lsp-clangd 179 lsp-clojure lsp-cmake lsp-cobol lsp-credo lsp-crystal lsp-csharp lsp-css 180 lsp-cucumber lsp-cypher lsp-d lsp-dart lsp-dhall lsp-docker lsp-dockerfile 181 lsp-earthly lsp-elixir lsp-elm lsp-emmet lsp-erlang lsp-eslint lsp-fortran lsp-futhark 182 lsp-fsharp lsp-gdscript lsp-gleam lsp-glsl lsp-go lsp-golangci-lint lsp-grammarly 183 lsp-graphql lsp-groovy lsp-hack lsp-haskell lsp-haxe lsp-idris lsp-java 184 lsp-javascript lsp-jq lsp-json lsp-kotlin lsp-latex lsp-lisp lsp-ltex 185 lsp-lua lsp-fennel lsp-magik lsp-markdown lsp-marksman lsp-mdx lsp-meson lsp-metals lsp-mint 186 lsp-mojo lsp-move lsp-mssql lsp-nextflow lsp-nginx lsp-nim lsp-nix lsp-nushell lsp-ocaml 187 lsp-openscad lsp-pascal lsp-perl lsp-perlnavigator lsp-php lsp-pls 188 lsp-purescript lsp-pwsh lsp-pyls lsp-pylsp lsp-pyright lsp-python-ms 189 lsp-qml lsp-r lsp-racket lsp-remark lsp-rf lsp-roslyn lsp-rubocop lsp-ruby-lsp 190 lsp-ruby-syntax-tree lsp-ruff lsp-rust lsp-semgrep lsp-shader 191 lsp-solargraph lsp-solidity lsp-sonarlint lsp-sorbet lsp-sourcekit 192 lsp-sql lsp-sqls lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform 193 lsp-tex lsp-tilt lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-typespec lsp-v 194 lsp-vala lsp-verilog lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl 195 lsp-xml lsp-yaml lsp-yang lsp-zig) 196 "List of the clients to be automatically required." 197 :group 'lsp-mode 198 :type '(repeat symbol)) 199 200 (defcustom lsp-progress-via-spinner t 201 "If non-nil, display LSP $/progress reports via a spinner in the modeline." 202 :group 'lsp-mode 203 :type 'boolean) 204 205 (defcustom lsp-progress-spinner-type 'progress-bar 206 "Holds the type of spinner to be used in the mode-line. 207 Takes a value accepted by `spinner-start'." 208 :group 'lsp-mode 209 :type `(choice :tag "Choose a spinner by name" 210 ,@(mapcar (lambda (c) (list 'const (car c))) 211 spinner-types))) 212 213 (defvar-local lsp-use-workspace-root-for-server-default-directory nil 214 "Use `lsp-workspace-root' for `default-directory' when starting LSP process.") 215 216 (defvar-local lsp--cur-workspace nil) 217 218 (defvar-local lsp--cur-version 0) 219 (defvar-local lsp--virtual-buffer-connections nil) 220 (defvar-local lsp--virtual-buffer nil) 221 (defvar lsp--virtual-buffer-mappings (ht)) 222 223 (defvar lsp--uri-file-prefix (pcase system-type 224 (`windows-nt "file:///") 225 (_ "file://")) 226 "Prefix for a file-uri.") 227 228 (defvar-local lsp-buffer-uri nil 229 "If set, return it instead of calculating it using `buffer-file-name'.") 230 231 (define-error 'lsp-error "Unknown lsp-mode error") 232 (define-error 'lsp-empty-response-error 233 "Empty response from the language server" 'lsp-error) 234 (define-error 'lsp-timed-out-error 235 "Timed out while waiting for a response from the language server" 'lsp-error) 236 (define-error 'lsp-capability-not-supported 237 "Capability not supported by the language server" 'lsp-error) 238 (define-error 'lsp-file-scheme-not-supported 239 "Unsupported file scheme" 'lsp-error) 240 (define-error 'lsp-client-already-exists-error 241 "A client with this server-id already exists" 'lsp-error) 242 (define-error 'lsp-no-code-actions 243 "No code actions" 'lsp-error) 244 245 (defcustom lsp-auto-guess-root nil 246 "Automatically guess the project root using projectile/project. 247 Do *not* use this setting unless you are familiar with `lsp-mode' 248 internals and you are sure that all of your projects are 249 following `projectile'/`project.el' conventions." 250 :group 'lsp-mode 251 :type 'boolean) 252 253 (defcustom lsp-guess-root-without-session nil 254 "Ignore the session file when calculating the project root. 255 You almost always want to set lsp-auto-guess-root too. 256 Do *not* use this setting unless you are familiar with `lsp-mode' 257 internals and you are sure that all of your projects are 258 following `projectile'/`project.el' conventions." 259 :group 'lsp-mode 260 :type 'boolean) 261 262 (defcustom lsp-restart 'interactive 263 "Defines how server-exited events must be handled." 264 :group 'lsp-mode 265 :type '(choice (const interactive) 266 (const auto-restart) 267 (const ignore))) 268 269 (defcustom lsp-session-file (expand-file-name (locate-user-emacs-file ".lsp-session-v1")) 270 "File where session information is stored." 271 :group 'lsp-mode 272 :type 'file) 273 274 (defcustom lsp-auto-configure t 275 "Auto configure `lsp-mode' main features. 276 When set to t `lsp-mode' will auto-configure completion, 277 code-actions, breadcrumb, `flycheck', `flymake', `imenu', symbol highlighting, 278 lenses, links, and so on. 279 280 For finer granularity you may use `lsp-enable-*' properties." 281 :group 'lsp-mode 282 :type 'boolean 283 :package-version '(lsp-mode . "6.1")) 284 285 (defcustom lsp-disabled-clients nil 286 "A list of disabled/blocklisted clients. 287 Each entry in the list can be either: 288 a symbol, the server-id for the LSP client, or 289 a cons pair (MAJOR-MODE . CLIENTS), where MAJOR-MODE is the major-mode, 290 and CLIENTS is either a client or a list of clients. 291 292 This option can also be used as a file- or directory-local variable to 293 disable a language server for individual files or directories/projects 294 respectively." 295 :group 'lsp-mode 296 :type '(repeat (symbol)) 297 :safe 'listp 298 :package-version '(lsp-mode . "6.1")) 299 300 (defvar lsp-clients (make-hash-table :test 'eql) 301 "Hash table server-id -> client. 302 It contains all of the clients that are currently registered.") 303 304 (defvar lsp-enabled-clients nil 305 "List of clients allowed to be used for projects. 306 When nil, all registered clients are considered candidates.") 307 308 (defvar lsp-last-id 0 309 "Last request id.") 310 311 (defcustom lsp-before-initialize-hook nil 312 "List of functions to be called before a Language Server has been initialized 313 for a new workspace." 314 :type 'hook 315 :group 'lsp-mode) 316 317 (defcustom lsp-after-initialize-hook nil 318 "List of functions to be called after a Language Server has been initialized 319 for a new workspace." 320 :type 'hook 321 :group 'lsp-mode) 322 323 (defcustom lsp-before-open-hook nil 324 "List of functions to be called before a new file with LSP support is opened." 325 :type 'hook 326 :group 'lsp-mode) 327 328 (defcustom lsp-after-open-hook nil 329 "List of functions to be called after a new file with LSP support is opened." 330 :type 'hook 331 :group 'lsp-mode) 332 333 (defcustom lsp-enable-file-watchers t 334 "If non-nil lsp-mode will watch the files in the workspace if 335 the server has requested that." 336 :type 'boolean 337 :group 'lsp-mode 338 :package-version '(lsp-mode . "6.1")) 339 ;;;###autoload(put 'lsp-enable-file-watchers 'safe-local-variable #'booleanp) 340 341 (define-obsolete-variable-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "8.0.0") 342 343 (defcustom lsp-file-watch-ignored-directories 344 '(; SCM tools 345 "[/\\\\]\\.git\\'" 346 "[/\\\\]\\.github\\'" 347 "[/\\\\]\\.gitlab\\'" 348 "[/\\\\]\\.circleci\\'" 349 "[/\\\\]\\.hg\\'" 350 "[/\\\\]\\.bzr\\'" 351 "[/\\\\]_darcs\\'" 352 "[/\\\\]\\.svn\\'" 353 "[/\\\\]_FOSSIL_\\'" 354 ;; IDE or build tools 355 "[/\\\\]\\.idea\\'" 356 "[/\\\\]\\.ensime_cache\\'" 357 "[/\\\\]\\.eunit\\'" 358 "[/\\\\]node_modules" 359 "[/\\\\]\\.yarn\\'" 360 "[/\\\\]\\.fslckout\\'" 361 "[/\\\\]\\.tox\\'" 362 "[/\\\\]\\.nox\\'" 363 "[/\\\\]dist\\'" 364 "[/\\\\]dist-newstyle\\'" 365 "[/\\\\]\\.stack-work\\'" 366 "[/\\\\]\\.bloop\\'" 367 "[/\\\\]\\.metals\\'" 368 "[/\\\\]target\\'" 369 "[/\\\\]\\.ccls-cache\\'" 370 "[/\\\\]\\.vs\\'" 371 "[/\\\\]\\.vscode\\'" 372 "[/\\\\]\\.venv\\'" 373 "[/\\\\]\\.mypy_cache\\'" 374 "[/\\\\]\\.pytest_cache\\'" 375 ;; Swift Package Manager 376 "[/\\\\]\\.build\\'" 377 ;; Python 378 "[/\\\\]__pycache__\\'" 379 "[/\\\\]site-packages\\'" 380 "[/\\\\].pyenv\\'" 381 ;; Autotools output 382 "[/\\\\]\\.deps\\'" 383 "[/\\\\]build-aux\\'" 384 "[/\\\\]autom4te.cache\\'" 385 "[/\\\\]\\.reference\\'" 386 ;; Bazel 387 "[/\\\\]bazel-[^/\\\\]+\\'" 388 ;; CSharp 389 "[/\\\\]\\.cache[/\\\\]lsp-csharp\\'" 390 "[/\\\\]\\.meta\\'" 391 "[/\\\\]\\.nuget\\'" 392 ;; Unity 393 "[/\\\\]Library\\'" 394 ;; Clojure 395 "[/\\\\]\\.lsp\\'" 396 "[/\\\\]\\.clj-kondo\\'" 397 "[/\\\\]\\.shadow-cljs\\'" 398 "[/\\\\]\\.babel_cache\\'" 399 "[/\\\\]\\.cpcache\\'" 400 "[/\\\\]\\checkouts\\'" 401 ;; Gradle 402 "[/\\\\]\\.gradle\\'" 403 ;; Maven 404 "[/\\\\]\\.m2\\'" 405 ;; .Net Core build-output 406 "[/\\\\]bin/Debug\\'" 407 "[/\\\\]obj\\'" 408 ;; OCaml and Dune 409 "[/\\\\]_opam\\'" 410 "[/\\\\]_build\\'" 411 ;; Elixir 412 "[/\\\\]\\.elixir_ls\\'" 413 ;; Elixir Credo 414 "[/\\\\]\\.elixir-tools\\'" 415 ;; terraform and terragrunt 416 "[/\\\\]\\.terraform\\'" 417 "[/\\\\]\\.terragrunt-cache\\'" 418 ;; nix-direnv 419 "[/\\\\]\\result" 420 "[/\\\\]\\result-bin" 421 "[/\\\\]\\.direnv\\'") 422 "List of regexps matching directory paths which won't be monitored when 423 creating file watches. Customization of this variable is only honored at 424 the global level or at a root of an lsp workspace." 425 :group 'lsp-mode 426 :type '(repeat string) 427 :package-version '(lsp-mode . "8.0.0")) 428 429 (define-obsolete-function-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "7.0.1") 430 431 (defun lsp-file-watch-ignored-directories () 432 lsp-file-watch-ignored-directories) 433 434 ;; Allow lsp-file-watch-ignored-directories as a file or directory-local variable 435 ;;;###autoload(put 'lsp-file-watch-ignored-directories 'safe-local-variable 'lsp--string-listp) 436 437 (defcustom lsp-file-watch-ignored-files 438 '( 439 ;; Flycheck tempfiles 440 "[/\\\\]flycheck_[^/\\\\]+\\'" 441 ;; lockfiles 442 "[/\\\\]\\.#[^/\\\\]+\\'" 443 ;; backup files 444 "[/\\\\][^/\\\\]+~\\'" ) 445 "List of regexps matching files for which change events will 446 not be sent to the server. 447 448 This setting has no impact on whether a file-watch is created for 449 a directory; it merely prevents notifications pertaining to 450 matched files from being sent to the server. To prevent a 451 file-watch from being created for a directory, customize 452 `lsp-file-watch-ignored-directories' 453 454 Customization of this variable is only honored at the global 455 level or at a root of an lsp workspace." 456 :group 'lsp-mode 457 :type '(repeat string) 458 :package-version '(lsp-mode . "8.0.0")) 459 460 ;; Allow lsp-file-watch-ignored-files as a file or directory-local variable 461 ;;;###autoload(put 'lsp-file-watch-ignored-files 'safe-local-variable 'lsp--string-listp) 462 463 (defcustom lsp-after-uninitialized-functions nil 464 "List of functions to be called after a Language Server has been uninitialized." 465 :type 'hook 466 :group 'lsp-mode 467 :package-version '(lsp-mode . "6.3")) 468 469 (defconst lsp--sync-full 1) 470 (defconst lsp--sync-incremental 2) 471 472 (defcustom lsp-debounce-full-sync-notifications t 473 "If non-nil debounce full sync events. 474 This flag affects only servers which do not support incremental updates." 475 :type 'boolean 476 :group 'lsp-mode 477 :package-version '(lsp-mode . "6.1")) 478 479 (defcustom lsp-debounce-full-sync-notifications-interval 1.0 480 "Time to wait before sending full sync synchronization after buffer modification." 481 :type 'float 482 :group 'lsp-mode 483 :package-version '(lsp-mode . "6.1")) 484 485 (defvar lsp--stderr-index 0) 486 487 (defvar lsp--delayed-requests nil) 488 (defvar lsp--delay-timer nil) 489 490 (defcustom lsp-document-sync-method nil 491 "How to sync the document with the language server." 492 :type '(choice (const :tag "Documents are synced by always sending the full content of the document." lsp--sync-full) 493 (const :tag "Documents are synced by always sending incremental changes to the document." lsp--sync-incremental) 494 (const :tag "Use the method recommended by the language server." nil)) 495 :group 'lsp-mode) 496 497 (defcustom lsp-auto-execute-action t 498 "Auto-execute single action." 499 :type 'boolean 500 :group 'lsp-mode) 501 502 (defcustom lsp-enable-links t 503 "If non-nil, all references to links in a file will be made clickable, if 504 supported by the language server." 505 :type 'boolean 506 :group 'lsp-mode 507 :package-version '(lsp-mode . "6.1")) 508 509 (defcustom lsp-enable-imenu t 510 "If non-nil, automatically enable `imenu' integration when server provides 511 `textDocument/documentSymbol'." 512 :type 'boolean 513 :group 'lsp-mode 514 :package-version '(lsp-mode . "6.2")) 515 516 (defcustom lsp-enable-dap-auto-configure t 517 "If non-nil, enable `dap-auto-configure-mode`." 518 :type 'boolean 519 :group 'lsp-mode 520 :package-version '(lsp-mode . "7.0")) 521 522 (defcustom lsp-eldoc-enable-hover t 523 "If non-nil, `eldoc' will display hover info when it is present." 524 :type 'boolean 525 :group 'lsp-mode) 526 527 (defcustom lsp-eldoc-render-all nil 528 "Display all of the info returned by document/onHover. 529 If this is set to nil, `eldoc' will show only the symbol information." 530 :type 'boolean 531 :group 'lsp-mode) 532 533 (define-obsolete-variable-alias 'lsp-enable-completion-at-point 534 'lsp-completion-enable "lsp-mode 7.0.1") 535 536 (defcustom lsp-completion-enable t 537 "Enable `completion-at-point' integration." 538 :type 'boolean 539 :group 'lsp-completion) 540 541 (defcustom lsp-enable-symbol-highlighting t 542 "Highlight references of the symbol at point." 543 :type 'boolean 544 :group 'lsp-mode) 545 546 (defcustom lsp-enable-xref t 547 "Enable xref integration." 548 :type 'boolean 549 :group 'lsp-mode) 550 551 (defcustom lsp-references-exclude-definition nil 552 "If non-nil, exclude declarations when finding references." 553 :type 'boolean 554 :group 'lsp-mode) 555 556 (defcustom lsp-enable-indentation t 557 "Indent regions using the file formatting functionality provided by the 558 language server." 559 :type 'boolean 560 :group 'lsp-mode) 561 562 (defcustom lsp-enable-on-type-formatting t 563 "Enable `textDocument/onTypeFormatting' integration." 564 :type 'boolean 565 :group 'lsp-mode) 566 567 (defcustom lsp-enable-text-document-color t 568 "Enable `textDocument/documentColor' integration." 569 :type 'boolean 570 :group 'lsp-mode) 571 572 (defcustom lsp-before-save-edits t 573 "If non-nil, `lsp-mode' will apply edits suggested by the language server 574 before saving a document." 575 :type 'boolean 576 :group 'lsp-mode) 577 578 (defcustom lsp-after-apply-edits-hook nil 579 "Hooks to run when text edit is applied. 580 It contains the operation source." 581 :type 'hook 582 :group 'lsp-mode 583 :package-version '(lsp-mode . "8.0.0")) 584 585 (defcustom lsp-apply-edits-after-file-operations t 586 "Whether to apply edits returned by server after file operations if any. 587 Applicable only if server supports workspace.fileOperations for operations: 588 `workspace/willRenameFiles', `workspace/willCreateFiles' and 589 `workspace/willDeleteFiles'." 590 :group 'lsp-mode 591 :type 'boolean) 592 593 (defcustom lsp-modeline-code-actions-enable t 594 "Whether to show code actions on modeline." 595 :type 'boolean 596 :group 'lsp-modeline) 597 598 (defcustom lsp-modeline-diagnostics-enable t 599 "Whether to show diagnostics on modeline." 600 :type 'boolean 601 :group 'lsp-modeline) 602 603 (defcustom lsp-modeline-workspace-status-enable t 604 "Whether to show workspace status on modeline." 605 :type 'boolean 606 :group 'lsp-modeline 607 :package-version '(lsp-mode . "8.0.0")) 608 609 (defcustom lsp-headerline-breadcrumb-enable t 610 "Whether to enable breadcrumb on headerline." 611 :type 'boolean 612 :group 'lsp-headerline) 613 614 (defcustom lsp-configure-hook nil 615 "Hooks to run when `lsp-configure-buffer' is called." 616 :type 'hook 617 :group 'lsp-mode) 618 619 (defcustom lsp-unconfigure-hook nil 620 "Hooks to run when `lsp-unconfig-buffer' is called." 621 :type 'hook 622 :group 'lsp-mode) 623 624 (defcustom lsp-after-diagnostics-hook nil 625 "Hooks to run after diagnostics are received. 626 Note: it runs only if the receiving buffer is open. Use 627 `lsp-diagnostics-updated-hook'if you want to be notified when 628 diagnostics have changed." 629 :type 'hook 630 :group 'lsp-mode) 631 632 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook 633 'lsp-diagnostics-updated-hook "lsp-mode 6.4") 634 635 (defcustom lsp-diagnostics-updated-hook nil 636 "Hooks to run after diagnostics are received." 637 :type 'hook 638 :group 'lsp-mode) 639 640 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook 641 'lsp-workspace-folders-changed-functions "lsp-mode 6.3") 642 643 (defcustom lsp-workspace-folders-changed-functions nil 644 "Hooks to run after the folders has changed. 645 The hook will receive two parameters list of added and removed folders." 646 :type 'hook 647 :group 'lsp-mode) 648 649 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0") 650 651 (defcustom lsp-before-apply-edits-hook nil 652 "Hooks to run before applying edits." 653 :type 'hook 654 :group 'lsp-mode) 655 656 (defgroup lsp-imenu nil 657 "LSP Imenu." 658 :group 'lsp-mode 659 :tag "LSP Imenu") 660 661 (defcustom lsp-imenu-show-container-name t 662 "Display the symbol's container name in an imenu entry." 663 :type 'boolean 664 :group 'lsp-imenu) 665 666 (defcustom lsp-imenu-container-name-separator "/" 667 "Separator string to use to separate the container name from the symbol while 668 displaying imenu entries." 669 :type 'string 670 :group 'lsp-imenu) 671 672 (defcustom lsp-imenu-sort-methods '(kind name) 673 "How to sort the imenu items. 674 675 The value is a list of `kind' `name' or `position'. Priorities 676 are determined by the index of the element." 677 :type '(repeat (choice (const name) 678 (const position) 679 (const kind))) 680 :group 'lsp-imenu) 681 682 (defcustom lsp-imenu-index-symbol-kinds nil 683 "Which symbol kinds to show in imenu." 684 :type '(repeat (choice (const :tag "Miscellaneous" nil) 685 (const :tag "File" File) 686 (const :tag "Module" Module) 687 (const :tag "Namespace" Namespace) 688 (const :tag "Package" Package) 689 (const :tag "Class" Class) 690 (const :tag "Method" Method) 691 (const :tag "Property" Property) 692 (const :tag "Field" Field) 693 (const :tag "Constructor" Constructor) 694 (const :tag "Enum" Enum) 695 (const :tag "Interface" Interface) 696 (const :tag "Function" Function) 697 (const :tag "Variable" Variable) 698 (const :tag "Constant" Constant) 699 (const :tag "String" String) 700 (const :tag "Number" Number) 701 (const :tag "Boolean" Boolean) 702 (const :tag "Array" Array) 703 (const :tag "Object" Object) 704 (const :tag "Key" Key) 705 (const :tag "Null" Null) 706 (const :tag "Enum Member" EnumMember) 707 (const :tag "Struct" Struct) 708 (const :tag "Event" Event) 709 (const :tag "Operator" Operator) 710 (const :tag "Type Parameter" TypeParameter))) 711 :group 'lsp-imenu) 712 713 ;; vibhavp: Should we use a lower value (5)? 714 (defcustom lsp-response-timeout 10 715 "Number of seconds to wait for a response from the language server before 716 timing out. Nil if no timeout." 717 :type '(choice 718 (number :tag "Seconds") 719 (const :tag "No timeout" nil)) 720 :group 'lsp-mode) 721 722 (defcustom lsp-tcp-connection-timeout 2 723 "The timeout for tcp connection in seconds." 724 :type 'number 725 :group 'lsp-mode 726 :package-version '(lsp-mode . "6.2")) 727 728 (defconst lsp--imenu-compare-function-alist 729 (list (cons 'name #'lsp--imenu-compare-name) 730 (cons 'kind #'lsp--imenu-compare-kind) 731 (cons 'position #'lsp--imenu-compare-line-col)) 732 "An alist of (METHOD . FUNCTION). 733 METHOD is one of the symbols accepted by 734 `lsp-imenu-sort-methods'. 735 736 FUNCTION takes two hash tables representing DocumentSymbol. It 737 returns a negative number, 0, or a positive number indicating 738 whether the first parameter is less than, equal to, or greater 739 than the second parameter.") 740 741 (defcustom lsp-diagnostic-clean-after-change nil 742 "When non-nil, clean the diagnostics on change. 743 744 Note that when that setting is nil, `lsp-mode' will show stale 745 diagnostics until server publishes the new set of diagnostics" 746 :type 'boolean 747 :group 'lsp-diagnostics 748 :package-version '(lsp-mode . "7.0.1")) 749 750 (defcustom lsp-server-trace nil 751 "Request tracing on the server side. 752 The actual trace output at each level depends on the language server in use. 753 Changes take effect only when a new session is started." 754 :type '(choice (const :tag "Disabled" "off") 755 (const :tag "Messages only" "messages") 756 (const :tag "Verbose" "verbose") 757 (const :tag "Default (disabled)" nil)) 758 :group 'lsp-mode 759 :package-version '(lsp-mode . "6.1")) 760 761 (defcustom lsp-auto-touch-files t 762 "If non-nil ensure the files exist before sending 763 `textDocument/didOpen' notification." 764 :type 'boolean 765 :group 'lsp-mode 766 :package-version '(lsp-mode . "9.0.0")) 767 768 (defvar lsp-language-id-configuration 769 '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake") 770 ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile") 771 ("\\.astro$" . "astro") 772 ("\\.cs\\'" . "csharp") 773 ("\\.css$" . "css") 774 ("\\.cypher$" . "cypher") 775 ("Earthfile" . "earthfile") 776 ("\\.ebuild$" . "shellscript") 777 ("\\.go\\'" . "go") 778 ("\\.html$" . "html") 779 ("\\.hx$" . "haxe") 780 ("\\.hy$" . "hy") 781 ("\\.java\\'" . "java") 782 ("\\.jq$" . "jq") 783 ("\\.js$" . "javascript") 784 ("\\.json$" . "json") 785 ("\\.jsonc$" . "jsonc") 786 ("\\.jsonnet$" . "jsonnet") 787 ("\\.jsx$" . "javascriptreact") 788 ("\\.lua$" . "lua") 789 ("\\.fnl$" . "fennel") 790 ("\\.mdx\\'" . "mdx") 791 ("\\.nu$" . "nushell") 792 ("\\.php$" . "php") 793 ("\\.ps[dm]?1\\'" . "powershell") 794 ("\\.rs\\'" . "rust") 795 ("\\.spec\\'" . "rpm-spec") 796 ("\\.sql$" . "sql") 797 ("\\.svelte$" . "svelte") 798 ("\\.toml\\'" . "toml") 799 ("\\.ts$" . "typescript") 800 ("\\.tsp$" . "typespec") 801 ("\\.tsx$" . "typescriptreact") 802 ("\\.ttcn3$" . "ttcn3") 803 ("\\.vue$" . "vue") 804 ("\\.xml$" . "xml") 805 ("\\ya?ml$" . "yaml") 806 ("^PKGBUILD$" . "shellscript") 807 ("^go\\.mod\\'" . "go.mod") 808 ("^settings\\.json$" . "jsonc") 809 ("^yang\\.settings$" . "jsonc") 810 ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson") 811 (ada-mode . "ada") 812 (ada-ts-mode . "ada") 813 (gpr-mode . "gpr") 814 (gpr-ts-mode . "gpr") 815 (awk-mode . "awk") 816 (awk-ts-mode . "awk") 817 (nxml-mode . "xml") 818 (sql-mode . "sql") 819 (vimrc-mode . "vim") 820 (vimscript-ts-mode . "vim") 821 (sh-mode . "shellscript") 822 (bash-ts-mode . "shellscript") 823 (ebuild-mode . "shellscript") 824 (pkgbuild-mode . "shellscript") 825 (envrc-file-mode . "shellscript") 826 (scala-mode . "scala") 827 (scala-ts-mode . "scala") 828 (julia-mode . "julia") 829 (julia-ts-mode . "julia") 830 (clojure-mode . "clojure") 831 (clojurec-mode . "clojure") 832 (clojurescript-mode . "clojurescript") 833 (clojure-ts-mode . "clojure") 834 (clojure-ts-clojurec-mode . "clojure") 835 (clojure-ts-clojurescript-mode . "clojurescript") 836 (java-mode . "java") 837 (java-ts-mode . "java") 838 (jdee-mode . "java") 839 (groovy-mode . "groovy") 840 (nextflow-mode . "nextflow") 841 (python-mode . "python") 842 (python-ts-mode . "python") 843 (cython-mode . "python") 844 ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo") 845 (lsp--render-markdown . "markdown") 846 (move-mode . "move") 847 (rust-mode . "rust") 848 (rust-ts-mode . "rust") 849 (rustic-mode . "rust") 850 (kotlin-mode . "kotlin") 851 (kotlin-ts-mode . "kotlin") 852 (css-mode . "css") 853 (css-ts-mode . "css") 854 (less-mode . "less") 855 (less-css-mode . "less") 856 (lua-mode . "lua") 857 (lua-ts-mode . "lua") 858 (sass-mode . "sass") 859 (ssass-mode . "sass") 860 (scss-mode . "scss") 861 (scad-mode . "openscad") 862 (xml-mode . "xml") 863 (c-mode . "c") 864 (c-ts-mode . "c") 865 (c++-mode . "cpp") 866 (c++-ts-mode . "cpp") 867 (cuda-mode . "cuda") 868 (objc-mode . "objective-c") 869 (html-mode . "html") 870 (html-ts-mode . "html") 871 (sgml-mode . "html") 872 (mhtml-mode . "html") 873 (mint-mode . "mint") 874 (go-dot-mod-mode . "go.mod") 875 (go-mod-ts-mode . "go.mod") 876 (go-mode . "go") 877 (go-ts-mode . "go") 878 (graphql-mode . "graphql") 879 (haskell-mode . "haskell") 880 (haskell-ts-mode . "haskell") 881 (hack-mode . "hack") 882 (php-mode . "php") 883 (php-ts-mode . "php") 884 (powershell-mode . "powershell") 885 (powershell-mode . "PowerShell") 886 (powershell-ts-mode . "powershell") 887 (json-mode . "json") 888 (json-ts-mode . "json") 889 (jsonc-mode . "jsonc") 890 (rjsx-mode . "javascript") 891 (js2-mode . "javascript") 892 (js-mode . "javascript") 893 (js-ts-mode . "javascript") 894 (typescript-mode . "typescript") 895 (typescript-ts-mode . "typescript") 896 (typespec-mode . "typespec") 897 (tsx-ts-mode . "typescriptreact") 898 (svelte-mode . "svelte") 899 (fsharp-mode . "fsharp") 900 (reason-mode . "reason") 901 (caml-mode . "ocaml") 902 (tuareg-mode . "ocaml") 903 (futhark-mode . "futhark") 904 (swift-mode . "swift") 905 (elixir-mode . "elixir") 906 (elixir-ts-mode . "elixir") 907 (heex-ts-mode . "elixir") 908 (conf-javaprop-mode . "spring-boot-properties") 909 (yaml-mode . "yaml") 910 (yaml-ts-mode . "yaml") 911 (ruby-mode . "ruby") 912 (enh-ruby-mode . "ruby") 913 (ruby-ts-mode . "ruby") 914 (feature-mode . "cucumber") 915 (fortran-mode . "fortran") 916 (f90-mode . "fortran") 917 (elm-mode . "elm") 918 (dart-mode . "dart") 919 (erlang-mode . "erlang") 920 (dockerfile-mode . "dockerfile") 921 (dockerfile-ts-mode . "dockerfile") 922 (csharp-mode . "csharp") 923 (csharp-tree-sitter-mode . "csharp") 924 (csharp-ts-mode . "csharp") 925 (plain-tex-mode . "plaintex") 926 (context-mode . "context") 927 (cypher-mode . "cypher") 928 (latex-mode . "latex") 929 (LaTeX-mode . "latex") 930 (v-mode . "v") 931 (vhdl-mode . "vhdl") 932 (vhdl-ts-mode . "vhdl") 933 (verilog-mode . "verilog") 934 (terraform-mode . "terraform") 935 (ess-julia-mode . "julia") 936 (ess-r-mode . "r") 937 (crystal-mode . "crystal") 938 (nim-mode . "nim") 939 (dhall-mode . "dhall") 940 (cmake-mode . "cmake") 941 (cmake-ts-mode . "cmake") 942 (purescript-mode . "purescript") 943 (gdscript-mode . "gdscript") 944 (gdscript-ts-mode . "gdscript") 945 (perl-mode . "perl") 946 (cperl-mode . "perl") 947 (robot-mode . "robot") 948 (racket-mode . "racket") 949 (nix-mode . "nix") 950 (nix-ts-mode . "nix") 951 (prolog-mode . "prolog") 952 (vala-mode . "vala") 953 (actionscript-mode . "actionscript") 954 (d-mode . "d") 955 (zig-mode . "zig") 956 (zig-ts-mode . "zig") 957 (text-mode . "plaintext") 958 (markdown-mode . "markdown") 959 (gfm-mode . "markdown") 960 (beancount-mode . "beancount") 961 (conf-toml-mode . "toml") 962 (toml-ts-mode . "toml") 963 (org-mode . "org") 964 (org-journal-mode . "org") 965 (nginx-mode . "nginx") 966 (magik-mode . "magik") 967 (magik-ts-mode . "magik") 968 (idris-mode . "idris") 969 (idris2-mode . "idris2") 970 (gleam-mode . "gleam") 971 (gleam-ts-mode . "gleam") 972 (graphviz-dot-mode . "dot") 973 (tiltfile-mode . "tiltfile") 974 (solidity-mode . "solidity") 975 (bibtex-mode . "bibtex") 976 (rst-mode . "restructuredtext") 977 (glsl-mode . "glsl") 978 (shader-mode . "shaderlab") 979 (wgsl-mode . "wgsl") 980 (jq-mode . "jq") 981 (jq-ts-mode . "jq") 982 (protobuf-mode . "protobuf") 983 (nushell-mode . "nushell") 984 (nushell-ts-mode . "nushell") 985 (meson-mode . "meson") 986 (yang-mode . "yang")) 987 "Language id configuration.") 988 989 (defvar lsp--last-active-workspaces nil 990 "Keep track of last active workspace. 991 We want to try the last workspace first when jumping into a library 992 directory") 993 994 (defvar lsp-method-requirements 995 '(("textDocument/callHierarchy" :capability :callHierarchyProvider) 996 ("textDocument/codeAction" :capability :codeActionProvider) 997 ("codeAction/resolve" 998 :check-command (lambda (workspace) 999 (with-lsp-workspace workspace 1000 (lsp:code-action-options-resolve-provider? 1001 (lsp--capability-for-method "textDocument/codeAction"))))) 1002 ("textDocument/codeLens" :capability :codeLensProvider) 1003 ("textDocument/completion" :capability :completionProvider) 1004 ("completionItem/resolve" 1005 :check-command (lambda (wk) 1006 (with-lsp-workspace wk 1007 (lsp:completion-options-resolve-provider? 1008 (lsp--capability-for-method "textDocument/completion"))))) 1009 ("textDocument/declaration" :capability :declarationProvider) 1010 ("textDocument/definition" :capability :definitionProvider) 1011 ("textDocument/documentColor" :capability :colorProvider) 1012 ("textDocument/documentLink" :capability :documentLinkProvider) 1013 ("textDocument/inlayHint" :capability :inlayHintProvider) 1014 ("textDocument/documentHighlight" :capability :documentHighlightProvider) 1015 ("textDocument/documentSymbol" :capability :documentSymbolProvider) 1016 ("textDocument/foldingRange" :capability :foldingRangeProvider) 1017 ("textDocument/formatting" :capability :documentFormattingProvider) 1018 ("textDocument/hover" :capability :hoverProvider) 1019 ("textDocument/implementation" :capability :implementationProvider) 1020 ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider) 1021 ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider) 1022 ("textDocument/prepareRename" 1023 :check-command (lambda (workspace) 1024 (with-lsp-workspace workspace 1025 (lsp:rename-options-prepare-provider? 1026 (lsp--capability-for-method "textDocument/rename"))))) 1027 ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider) 1028 ("textDocument/references" :capability :referencesProvider) 1029 ("textDocument/rename" :capability :renameProvider) 1030 ("textDocument/selectionRange" :capability :selectionRangeProvider) 1031 ("textDocument/semanticTokens" :capability :semanticTokensProvider) 1032 ("textDocument/semanticTokensFull" 1033 :check-command (lambda (workspace) 1034 (with-lsp-workspace workspace 1035 (lsp-get (lsp--capability :semanticTokensProvider) :full)))) 1036 ("textDocument/semanticTokensFull/Delta" 1037 :check-command (lambda (workspace) 1038 (with-lsp-workspace workspace 1039 (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full))) 1040 (and (not (booleanp capFull)) (lsp-get capFull :delta)))))) 1041 ("textDocument/semanticTokensRangeProvider" 1042 :check-command (lambda (workspace) 1043 (with-lsp-workspace workspace 1044 (lsp-get (lsp--capability :semanticTokensProvider) :range)))) 1045 ("textDocument/signatureHelp" :capability :signatureHelpProvider) 1046 ("textDocument/typeDefinition" :capability :typeDefinitionProvider) 1047 ("textDocument/typeHierarchy" :capability :typeHierarchyProvider) 1048 ("textDocument/diagnostic" :capability :diagnosticProvider) 1049 ("workspace/executeCommand" :capability :executeCommandProvider) 1050 ("workspace/symbol" :capability :workspaceSymbolProvider)) 1051 1052 "Map methods to requirements. 1053 It is used by request-sending functions to determine which server 1054 must be used for handling a particular message.") 1055 1056 (defconst lsp--file-change-type 1057 `((created . 1) 1058 (changed . 2) 1059 (deleted . 3))) 1060 1061 (defconst lsp--watch-kind 1062 `((create . 1) 1063 (change . 2) 1064 (delete . 4))) 1065 1066 (defvar lsp-window-body-width 40 1067 "Window body width when rendering doc.") 1068 1069 (defface lsp-face-highlight-textual 1070 '((t :inherit highlight)) 1071 "Face used for textual occurrences of symbols." 1072 :group 'lsp-mode) 1073 1074 (defface lsp-face-highlight-read 1075 '((t :inherit highlight :underline t)) 1076 "Face used for highlighting symbols being read." 1077 :group 'lsp-mode) 1078 1079 (defface lsp-face-highlight-write 1080 '((t :inherit highlight :weight bold)) 1081 "Face used for highlighting symbols being written to." 1082 :group 'lsp-mode) 1083 1084 (define-obsolete-variable-alias 'lsp-lens-auto-enable 1085 'lsp-lens-enable "lsp-mode 7.0.1") 1086 1087 (defcustom lsp-lens-enable t 1088 "Auto enable lenses if server supports." 1089 :group 'lsp-lens 1090 :type 'boolean 1091 :package-version '(lsp-mode . "6.3")) 1092 1093 (defcustom lsp-symbol-highlighting-skip-current nil 1094 "If non-nil skip current symbol when setting symbol highlights." 1095 :group 'lsp-mode 1096 :type 'boolean) 1097 1098 (defcustom lsp-file-watch-threshold 1000 1099 "Show warning if the files to watch are more than. 1100 Set to nil to disable the warning." 1101 :type 'number 1102 :group 'lsp-mode) 1103 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i)))) 1104 1105 (defvar lsp-custom-markup-modes 1106 '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic")) 1107 "Mode to uses with markdown code blocks. 1108 They are added to `markdown-code-lang-modes'") 1109 1110 (defcustom lsp-signature-render-documentation t 1111 "Display signature documentation in `eldoc'." 1112 :type 'boolean 1113 :group 'lsp-mode 1114 :package-version '(lsp-mode . "6.2")) 1115 1116 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request) 1117 "Auto activate signature conditions." 1118 :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char) 1119 (const :tag "After selected completion." :after-completion) 1120 (const :tag "When the server has sent show signature help." :on-server-request))) 1121 :group 'lsp-mode 1122 :package-version '(lsp-mode . "6.2")) 1123 1124 (defcustom lsp-signature-doc-lines 20 1125 "If number, limit the number of lines to show in the docs." 1126 :type 'number 1127 :group 'lsp-mode 1128 :package-version '(lsp-mode . "6.3")) 1129 1130 (defcustom lsp-signature-function 'lsp-lv-message 1131 "The function used for displaying signature info. 1132 It will be called with one param - the signature info. When 1133 called with nil the signature info must be cleared." 1134 :type 'function 1135 :group 'lsp-mode 1136 :package-version '(lsp-mode . "6.3")) 1137 1138 (defcustom lsp-keymap-prefix "s-l" 1139 "LSP-mode keymap prefix." 1140 :group 'lsp-mode 1141 :type 'string 1142 :package-version '(lsp-mode . "6.3")) 1143 1144 (defvar-local lsp--buffer-workspaces () 1145 "List of the buffer workspaces.") 1146 1147 (defvar-local lsp--buffer-deferred nil 1148 "Whether buffer was loaded via `lsp-deferred'.") 1149 1150 (defvar lsp--session nil 1151 "Contain the `lsp-session' for the current Emacs instance.") 1152 1153 (defvar lsp--tcp-port 10000) 1154 1155 (defvar lsp--client-packages-required nil 1156 "If nil, `lsp-client-packages' are yet to be required.") 1157 1158 (defvar lsp--tcp-server-port 0 1159 "The server socket which is opened when using `lsp-tcp-server' (a server 1160 socket is opened in Emacs and the language server connects to it). The 1161 default value of 0 ensures that a random high port is used. Set it to a positive 1162 integer to use a specific port.") 1163 1164 (defvar lsp--tcp-server-wait-seconds 10 1165 "Wait this amount of time for the client to connect to our server socket 1166 when using `lsp-tcp-server'.") 1167 1168 (defvar-local lsp--document-symbols nil 1169 "The latest document symbols.") 1170 1171 (defvar-local lsp--document-selection-range-cache nil 1172 "The document selection cache.") 1173 1174 (defvar-local lsp--document-symbols-request-async nil 1175 "If non-nil, request document symbols asynchronously.") 1176 1177 (defvar-local lsp--document-symbols-tick -1 1178 "The value of `buffer-chars-modified-tick' when document 1179 symbols were last retrieved.") 1180 1181 (defvar-local lsp--have-document-highlights nil 1182 "Set to `t' on symbol highlighting, cleared on 1183 `lsp--cleanup-highlights-if-needed'. Checking a separately 1184 defined flag is substantially faster than unconditionally 1185 calling `remove-overlays'.") 1186 1187 ;; Buffer local variable for storing number of lines. 1188 (defvar lsp--log-lines) 1189 1190 (defvar-local lsp--eldoc-saved-message nil) 1191 1192 (defvar lsp--on-change-timer nil) 1193 (defvar lsp--on-idle-timer nil) 1194 1195 (defvar-local lsp--signature-last nil) 1196 (defvar-local lsp--signature-last-index nil) 1197 (defvar lsp--signature-last-buffer nil) 1198 1199 (defvar-local lsp--virtual-buffer-point-max nil) 1200 1201 (cl-defmethod lsp-execute-command (_server _command _arguments) 1202 "Ask SERVER to execute COMMAND with ARGUMENTS.") 1203 1204 (defun lsp-elt (sequence n) 1205 "Return Nth element of SEQUENCE or nil if N is out of range." 1206 (cond 1207 ((listp sequence) (elt sequence n)) 1208 ((arrayp sequence) 1209 (and (> (length sequence) n) (aref sequence n))) 1210 (t (and (> (length sequence) n) (elt sequence n))))) 1211 1212 ;; define seq-first and seq-rest for older emacs 1213 (defun lsp-seq-first (sequence) 1214 "Return the first element of SEQUENCE." 1215 (lsp-elt sequence 0)) 1216 1217 (defun lsp-seq-rest (sequence) 1218 "Return a sequence of the elements of SEQUENCE except the first one." 1219 (seq-drop sequence 1)) 1220 1221 ;;;###autoload 1222 (defun lsp--string-listp (sequence) 1223 "Return t if all elements of SEQUENCE are strings, else nil." 1224 (not (seq-find (lambda (x) (not (stringp x))) sequence))) 1225 1226 (defun lsp--string-vector-p (candidate) 1227 "Returns true if CANDIDATE is a vector data structure and 1228 every element of it is of type string, else nil." 1229 (and 1230 (vectorp candidate) 1231 (seq-every-p #'stringp candidate))) 1232 1233 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0") 1234 1235 (defun lsp--editable-vector-match (widget value) 1236 "Function for `lsp-editable-vector' :match." 1237 ;; Value must be a list or a vector and all the members must match the type. 1238 (and (or (listp value) (vectorp value)) 1239 (length (cdr (lsp--editable-vector-match-inline widget value))))) 1240 1241 (defun lsp--editable-vector-match-inline (widget value) 1242 "Value for `lsp-editable-vector' :match-inline." 1243 (let ((type (nth 0 (widget-get widget :args))) 1244 (ok t) 1245 found) 1246 (while (and value ok) 1247 (let ((answer (widget-match-inline type value))) 1248 (if answer 1249 (let ((head (if (vectorp answer) (aref answer 0) (car answer))) 1250 (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer)))) 1251 (setq found (append found head) 1252 value tail)) 1253 (setq ok nil)))) 1254 (cons found value))) 1255 1256 (defun lsp--editable-vector-value-to-external (_widget internal-value) 1257 "Convert the internal list value to a vector." 1258 (if (listp internal-value) 1259 (apply 'vector internal-value) 1260 internal-value)) 1261 1262 (defun lsp--editable-vector-value-to-internal (_widget external-value) 1263 "Convert the external vector value to a list." 1264 (if (vectorp external-value) 1265 (append external-value nil) 1266 external-value)) 1267 1268 (define-widget 'lsp--editable-vector 'editable-list 1269 "A subclass of `editable-list' that accepts and returns a 1270 vector instead of a list." 1271 :value-to-external 'lsp--editable-vector-value-to-external 1272 :value-to-internal 'lsp--editable-vector-value-to-internal 1273 :match 'lsp--editable-vector-match 1274 :match-inline 'lsp--editable-vector-match-inline) 1275 1276 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector 1277 "A variable length homogeneous vector." 1278 :tag "Repeat" 1279 :format "%{%t%}:\n%v%i\n") 1280 1281 (define-widget 'lsp-string-vector 'lazy 1282 "A vector of zero or more elements, every element of which is a string. 1283 Appropriate for any language-specific `defcustom' that needs to 1284 serialize as a JSON array of strings. 1285 1286 Deprecated. Use `lsp-repeatable-vector' instead. " 1287 :offset 4 1288 :tag "Vector" 1289 :type '(lsp-repeatable-vector string)) 1290 1291 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0") 1292 1293 (defvar lsp--show-message t 1294 "If non-nil, show debug message from `lsp-mode'.") 1295 1296 (defun lsp--message (format &rest args) 1297 "Wrapper for `message' 1298 1299 We `inhibit-message' the message when the cursor is in the 1300 minibuffer and when emacs version is before emacs 27 due to the 1301 fact that we often use `lsp--info', `lsp--warn' and `lsp--error' 1302 in async context and the call to these function is removing the 1303 minibuffer prompt. The issue with async messages is already fixed 1304 in emacs 27. 1305 1306 See #2049" 1307 (when lsp--show-message 1308 (let ((inhibit-message (or inhibit-message 1309 (and (minibufferp) 1310 (version< emacs-version "27.0"))))) 1311 (apply #'message format args)))) 1312 1313 (defun lsp--info (format &rest args) 1314 "Display lsp info message with FORMAT with ARGS." 1315 (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args))) 1316 1317 (defun lsp--warn (format &rest args) 1318 "Display lsp warn message with FORMAT with ARGS." 1319 (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args))) 1320 1321 (defun lsp--error (format &rest args) 1322 "Display lsp error message with FORMAT with ARGS." 1323 (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args))) 1324 1325 (defun lsp-log (format &rest args) 1326 "Log message to the ’*lsp-log*’ buffer. 1327 1328 FORMAT and ARGS i the same as for `message'." 1329 (when lsp-log-max 1330 (let ((log-buffer (get-buffer "*lsp-log*")) 1331 (inhibit-read-only t)) 1332 (unless log-buffer 1333 (setq log-buffer (get-buffer-create "*lsp-log*")) 1334 (with-current-buffer log-buffer 1335 (buffer-disable-undo) 1336 (view-mode 1) 1337 (set (make-local-variable 'lsp--log-lines) 0))) 1338 (with-current-buffer log-buffer 1339 (save-excursion 1340 (let* ((message (apply 'format format args)) 1341 ;; Count newlines in message. 1342 (newlines (1+ (cl-loop with start = 0 1343 for count from 0 1344 while (string-match "\n" message start) 1345 do (setq start (match-end 0)) 1346 finally return count)))) 1347 (goto-char (point-max)) 1348 1349 ;; in case the buffer is not empty insert before last \n to preserve 1350 ;; the point position(in case it is in the end) 1351 (if (eq (point) (point-min)) 1352 (progn 1353 (insert "\n") 1354 (backward-char)) 1355 (backward-char) 1356 (insert "\n")) 1357 (insert message) 1358 1359 (setq lsp--log-lines (+ lsp--log-lines newlines)) 1360 1361 (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max)) 1362 (let ((to-delete (- lsp--log-lines lsp-log-max))) 1363 (goto-char (point-min)) 1364 (forward-line to-delete) 1365 (delete-region (point-min) (point)) 1366 (setq lsp--log-lines lsp-log-max))))))))) 1367 1368 (defalias 'lsp-message 'lsp-log) 1369 1370 (defalias 'lsp-ht 'ht) 1371 1372 (defalias 'lsp-file-local-name 'file-local-name) 1373 1374 (defun lsp-f-canonical (file-name) 1375 "Return the canonical FILE-NAME, without a trailing slash." 1376 (directory-file-name (expand-file-name file-name))) 1377 1378 (defalias 'lsp-canonical-file-name 'lsp-f-canonical) 1379 1380 (defun lsp-f-same? (path-a path-b) 1381 "Return t if PATH-A and PATH-B are references to the same file. 1382 Symlinks are not followed." 1383 (when (and (f-exists? path-a) 1384 (f-exists? path-b)) 1385 (equal 1386 (lsp-f-canonical (directory-file-name (f-expand path-a))) 1387 (lsp-f-canonical (directory-file-name (f-expand path-b)))))) 1388 1389 (defun lsp-f-parent (path) 1390 "Return the parent directory to PATH. 1391 Symlinks are not followed." 1392 (let ((parent (file-name-directory 1393 (directory-file-name (f-expand path default-directory))))) 1394 (unless (lsp-f-same? path parent) 1395 (if (f-relative? path) 1396 (f-relative parent) 1397 (directory-file-name parent))))) 1398 1399 (defun lsp-f-ancestor-of? (path-a path-b) 1400 "Return t if PATH-A is an ancestor of PATH-B. 1401 Symlinks are not followed." 1402 (unless (lsp-f-same? path-a path-b) 1403 (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator)) 1404 (lsp-f-canonical path-b)))) 1405 1406 (defun lsp--merge-results (results method) 1407 "Merge RESULTS by filtering the empty hash-tables and merging 1408 the lists according to METHOD." 1409 (pcase (--map (if (vectorp it) 1410 (append it nil) it) 1411 (-filter #'identity results)) 1412 (`() ()) 1413 ;; only one result - simply return it 1414 (`(,fst) fst) 1415 ;; multiple results merge it based on strategy 1416 (results 1417 (pcase method 1418 ("textDocument/hover" (pcase (seq-filter 1419 (-compose #'not #'lsp-empty?) 1420 results) 1421 (`(,hover) hover) 1422 (hovers (lsp-make-hover 1423 :contents 1424 (-mapcat 1425 (-lambda ((&Hover :contents)) 1426 (if (and (sequencep contents) 1427 (not (stringp contents))) 1428 (append contents ()) 1429 (list contents))) 1430 hovers))))) 1431 ("textDocument/completion" 1432 (lsp-make-completion-list 1433 :is-incomplete (seq-some 1434 #'lsp:completion-list-is-incomplete 1435 results) 1436 :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it) 1437 (lsp:completion-list-items it) 1438 it) 1439 nil)) 1440 results))) 1441 ("completionItem/resolve" 1442 (let ((item (cl-first results))) 1443 (when-let ((details (seq-filter #'identity 1444 (seq-map #'lsp:completion-item-detail? results)))) 1445 (lsp:set-completion-item-detail? 1446 item 1447 (string-join details " "))) 1448 (when-let ((docs (seq-filter #'identity 1449 (seq-map #'lsp:completion-item-documentation? results)))) 1450 (lsp:set-completion-item-documentation? 1451 item 1452 (lsp-make-markup-content 1453 :kind (or (seq-some (lambda (it) 1454 (when (equal (lsp:markup-content-kind it) 1455 lsp/markup-kind-markdown) 1456 lsp/markup-kind-markdown)) 1457 docs) 1458 lsp/markup-kind-plain-text) 1459 :value (string-join (seq-map (lambda (doc) 1460 (or (lsp:markup-content-value doc) 1461 (and (stringp doc) doc))) 1462 docs) 1463 "\n")))) 1464 (when-let ((edits (seq-filter #'identity 1465 (seq-map #'lsp:completion-item-additional-text-edits? results)))) 1466 (lsp:set-completion-item-additional-text-edits? 1467 item 1468 (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits))) 1469 item)) 1470 (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results)))))) 1471 1472 (defun lsp--spinner-start () 1473 "Start spinner indication." 1474 (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error))) 1475 1476 (defun lsp--propertize (str type) 1477 "Propertize STR as per TYPE." 1478 (propertize str 'face (alist-get type lsp--message-type-face))) 1479 1480 (defun lsp-workspaces () 1481 "Return the lsp workspaces associated with the current project." 1482 (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces)) 1483 1484 (defun lsp--completing-read (prompt collection transform-fn &optional predicate 1485 require-match initial-input 1486 hist def inherit-input-method) 1487 "Wrap `completing-read' to provide transformation function and disable sort. 1488 1489 TRANSFORM-FN will be used to transform each of the items before displaying. 1490 1491 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF 1492 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes." 1493 (let* ((col (--map (cons (funcall transform-fn it) it) collection)) 1494 (completion (completing-read prompt 1495 (lambda (string pred action) 1496 (if (eq action 'metadata) 1497 `(metadata (display-sort-function . identity)) 1498 (complete-with-action action col string pred))) 1499 predicate require-match initial-input hist 1500 def inherit-input-method))) 1501 (cdr (assoc completion col)))) 1502 1503 (defconst lsp--system-arch (lambda () 1504 (setq lsp--system-arch 1505 (pcase system-type 1506 ('windows-nt 1507 (pcase system-configuration 1508 ((rx bol "x86_64-") 'x64) 1509 (_ 'x86))) 1510 ('darwin 1511 (pcase system-configuration 1512 ((rx "aarch64-") 'arm64) 1513 (_ 'x64))) 1514 ('gnu/linux 1515 (pcase system-configuration 1516 ((rx bol "aarch64-") 'arm64) 1517 ((rx bol "x86_64") 'x64) 1518 ((rx bol (| "i386" "i886")) 'x32))) 1519 (_ 1520 (pcase system-configuration 1521 ((rx bol "x86_64") 'x64) 1522 ((rx bol (| "i386" "i886")) 'x32)))))) 1523 "Return the system architecture of `Emacs'. 1524 Special values: 1525 `x64' 64bit 1526 `x32' 32bit 1527 `arm64' ARM 64bit") 1528 1529 (defmacro lsp-with-current-buffer (buffer-id &rest body) 1530 (declare (indent 1) (debug t)) 1531 `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer))) 1532 (with-lsp-workspaces (plist-get ,buffer-id :workspaces) 1533 (funcall wcb (lambda () ,@body))) 1534 (with-current-buffer ,buffer-id 1535 ,@body))) 1536 1537 (defvar lsp--throw-on-input nil 1538 "Make `lsp-*-while-no-input' throws `input' on interrupted.") 1539 1540 (defmacro lsp--catch (tag bodyform &rest handlers) 1541 "Catch TAG thrown in BODYFORM. 1542 The return value from TAG will be handled in HANDLERS by `pcase'." 1543 (declare (debug (form form &rest (pcase-PAT body))) (indent 2)) 1544 (let ((re-sym (make-symbol "re"))) 1545 `(let ((,re-sym (catch ,tag ,bodyform))) 1546 (pcase ,re-sym 1547 ,@handlers)))) 1548 1549 (defmacro lsp--while-no-input (&rest body) 1550 "Wrap BODY in `while-no-input' and respecting `non-essential'. 1551 If `lsp--throw-on-input' is set, will throw if input is pending, else 1552 return value of `body' or nil if interrupted." 1553 (declare (debug t) (indent 0)) 1554 `(if non-essential 1555 (let ((res (while-no-input ,@body))) 1556 (cond 1557 ((and lsp--throw-on-input (equal res t)) 1558 (throw 'input :interrupted)) 1559 ((booleanp res) nil) 1560 (t res))) 1561 ,@body)) 1562 1563 ;; A ‘lsp--client’ object describes the client-side behavior of a language 1564 ;; server. It is used to start individual server processes, each of which is 1565 ;; represented by a ‘lsp--workspace’ object. Client objects are normally 1566 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’. Each 1567 ;; workspace refers to exactly one client, but there can be multiple workspaces 1568 ;; for a single client. 1569 (cl-defstruct lsp--client 1570 ;; ‘language-id’ is a function that receives a buffer as a single argument 1571 ;; and should return the language identifier for that buffer. See 1572 ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem 1573 ;; for a list of language identifiers. Also consult the documentation for 1574 ;; the language server represented by this client to find out what language 1575 ;; identifiers it supports or expects. 1576 (language-id nil) 1577 1578 ;; ‘add-on?’ when set to t the server will be started no matter whether there 1579 ;; is another server handling the same mode. 1580 (add-on? nil) 1581 ;; ‘new-connection’ is a function that should start a language server process 1582 ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS). 1583 ;; COMMAND-PROCESS must be a process object representing the server process 1584 ;; just started. COMMUNICATION-PROCESS must be a process (including pipe and 1585 ;; network processes) that ‘lsp-mode’ uses to communicate with the language 1586 ;; server using the language server protocol. COMMAND-PROCESS and 1587 ;; COMMUNICATION-PROCESS may be the same process; in that case 1588 ;; ‘new-connection’ may also return that process as a single 1589 ;; object. ‘new-connection’ is called with two arguments, FILTER and 1590 ;; SENTINEL. FILTER should be used as process filter for 1591 ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for 1592 ;; COMMAND-PROCESS. 1593 (new-connection nil) 1594 1595 ;; ‘ignore-regexps’ is a list of regexps. When a data packet from the 1596 ;; language server matches any of these regexps, it will be ignored. This is 1597 ;; intended for dealing with language servers that output non-protocol data. 1598 (ignore-regexps nil) 1599 1600 ;; ‘ignore-messages’ is a list of regexps. When a message from the language 1601 ;; server matches any of these regexps, it will be ignored. This is useful 1602 ;; for filtering out unwanted messages; such as servers that send nonstandard 1603 ;; message types, or extraneous log messages. 1604 (ignore-messages nil) 1605 1606 ;; ‘notification-handlers’ is a hash table mapping notification method names 1607 ;; (strings) to functions handling the respective notifications. Upon 1608 ;; receiving a notification, ‘lsp-mode’ will call the associated handler 1609 ;; function passing two arguments, the ‘lsp--workspace’ object and the 1610 ;; deserialized notification parameters. 1611 (notification-handlers (make-hash-table :test 'equal)) 1612 1613 ;; ‘request-handlers’ is a hash table mapping request method names 1614 ;; (strings) to functions handling the respective notifications. Upon 1615 ;; receiving a request, ‘lsp-mode’ will call the associated handler function 1616 ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized 1617 ;; request parameters. 1618 (request-handlers (make-hash-table :test 'equal)) 1619 1620 ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request 1621 ;; identifiers for pending asynchronous requests to functions handling the 1622 ;; respective responses. Upon receiving a response from the language server, 1623 ;; ‘lsp-mode’ will call the associated response handler function with a 1624 ;; single argument, the deserialized response parameters. 1625 (response-handlers (make-hash-table :test 'eql)) 1626 1627 ;; ‘prefix-function’ is called for getting the prefix for completion. 1628 ;; The function takes no parameter and returns a cons (start . end) representing 1629 ;; the start and end bounds of the prefix. If it's not set, the client uses a 1630 ;; default prefix function." 1631 (prefix-function nil) 1632 1633 ;; Contains mapping of scheme to the function that is going to be used to load 1634 ;; the file. 1635 (uri-handlers (make-hash-table :test #'equal)) 1636 1637 ;; ‘action-handlers’ is a hash table mapping action to a handler function. It 1638 ;; can be used in `lsp-execute-code-action' to determine whether the action 1639 ;; current client is interested in executing the action instead of sending it 1640 ;; to the server. 1641 (action-handlers (make-hash-table :test 'equal)) 1642 1643 ;; `action-filter' can be set to a function that modifies any incoming 1644 ;; `CodeAction' in place before it is executed. The return value is ignored. 1645 ;; This can be used to patch up broken code action requests before they are 1646 ;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an 1647 ;; example of a function that can be useful here. 1648 (action-filter nil) 1649 1650 ;; major modes supported by the client. 1651 major-modes 1652 ;; Function that will be called to decide if this language client 1653 ;; should manage a particular buffer. The function will be passed 1654 ;; the file name and major mode to inform the decision. Setting 1655 ;; `activation-fn' will override `major-modes', if 1656 ;; present. 1657 activation-fn 1658 ;; Break the tie when major-mode is supported by multiple clients. 1659 (priority 0) 1660 ;; Unique identifier for representing the client object. 1661 server-id 1662 ;; defines whether the client supports multi root workspaces. 1663 multi-root 1664 ;; Initialization options or a function that returns initialization options. 1665 initialization-options 1666 ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or 1667 ;; completely replace, the faces used for semantic highlighting on a 1668 ;; client-by-client basis. 1669 ;; 1670 ;; It recognizes four members, all of which are optional: `:types’ and 1671 ;; `:modifiers’, respectively, should be face definition lists akin to 1672 ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be 1673 ;; merged with the default face definition list. 1674 ;; 1675 ;; Alternatively, if the plist members `:discard-default-types’ or 1676 ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers' 1677 ;; face definitions will be replaced entirely by their respective overrides. 1678 ;; 1679 ;; For example, setting `:semantic-tokens-faces-overrides' to 1680 ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from 1681 ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'. 1682 ;; 1683 ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))' 1684 ;; will also remap "macro", but on top of that associate the fictional token type 1685 ;; "not-quite-a-macro" with the face named `some-face'. 1686 ;; 1687 ;; `(:types (("macro" . font-lock-keyword-face)) 1688 ;; :modifiers (("declaration" . lsp-face-semhl-interface)) 1689 ;; :discard-default-types t 1690 ;; :discard-default-modifiers t)' 1691 ;; will discard all default face definitions, hence leaving the client with 1692 ;; only one token type "macro", mapped to `font-lock-keyword-face', and one 1693 ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'. 1694 semantic-tokens-faces-overrides 1695 ;; Provides support for registering LSP Server specific capabilities. 1696 custom-capabilities 1697 ;; Function which returns the folders that are considered to be not projects but library files. 1698 ;; The function accepts one parameter currently active workspace. 1699 ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225. 1700 library-folders-fn 1701 ;; function which will be called when opening file in the workspace to perform 1702 ;; client specific initialization. The function accepts one parameter 1703 ;; currently active workspace. 1704 before-file-open-fn 1705 ;; Function which will be called right after a workspace has been initialized. 1706 initialized-fn 1707 ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP. 1708 (remote? nil) 1709 1710 ;; ‘completion-in-comments?’ t if the client supports completion in comments. 1711 (completion-in-comments? nil) 1712 1713 ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client. 1714 (path->uri-fn nil) 1715 1716 ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client. 1717 (uri->path-fn nil) 1718 ;; Function that returns an environment structure that will be used 1719 ;; to set some environment variables when starting the language 1720 ;; server process. These environment variables enable some 1721 ;; additional features in the language server. The environment 1722 ;; structure is an alist of the form (KEY . VALUE), where KEY is a 1723 ;; string (regularly in all caps), and VALUE may be a string, a 1724 ;; boolean, or a sequence of strings. 1725 environment-fn 1726 1727 ;; ‘after-open-fn’ workspace after open specific hooks. 1728 (after-open-fn nil) 1729 1730 ;; ‘async-request-handlers’ is a hash table mapping request method names 1731 ;; (strings) to functions handling the respective requests that may take 1732 ;; time to finish. Upon receiving a request, ‘lsp-mode’ will call the 1733 ;; associated handler function passing three arguments, the ‘lsp--workspace’ 1734 ;; object, the deserialized request parameters and the callback which accept 1735 ;; result as its parameter. 1736 (async-request-handlers (make-hash-table :test 'equal)) 1737 download-server-fn 1738 download-in-progress? 1739 buffers 1740 synchronize-sections) 1741 1742 (defun lsp-clients-executable-find (find-command &rest args) 1743 "Finds an executable by invoking a search command. 1744 1745 FIND-COMMAND is the executable finder that searches for the 1746 actual language server executable. ARGS is a list of arguments to 1747 give to FIND-COMMAND to find the language server. Returns the 1748 output of FIND-COMMAND if it exits successfully, nil otherwise. 1749 1750 Typical uses include finding an executable by invoking `find' in 1751 a project, finding LLVM commands on macOS with `xcrun', or 1752 looking up project-specific language servers for projects written 1753 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv' 1754 etc." 1755 (when-let* ((find-command-path (executable-find find-command)) 1756 (executable-path 1757 (with-temp-buffer 1758 (when (zerop (apply 'call-process find-command-path nil t nil args)) 1759 (buffer-substring-no-properties (point-min) (point-max)))))) 1760 (string-trim executable-path))) 1761 1762 (defvar lsp--already-widened nil) 1763 1764 (defmacro lsp-save-restriction-and-excursion (&rest form) 1765 (declare (indent 0) (debug t)) 1766 `(if lsp--already-widened 1767 (save-excursion ,@form) 1768 (-let [lsp--already-widened t] 1769 (save-restriction 1770 (widen) 1771 (save-excursion ,@form))))) 1772 1773 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number 1774 (defun lsp--line-character-to-point (line character) 1775 "Return the point for character CHARACTER on line LINE." 1776 (or (lsp-virtual-buffer-call :line/character->point line character) 1777 (let ((inhibit-field-text-motion t)) 1778 (lsp-save-restriction-and-excursion 1779 (goto-char (point-min)) 1780 (forward-line line) 1781 ;; server may send character position beyond the current line and we 1782 ;; should fallback to line end. 1783 (-let [line-end (line-end-position)] 1784 (if (> character (- line-end (point))) 1785 line-end 1786 (forward-char character) 1787 (point))))))) 1788 1789 (lsp-defun lsp--position-to-point ((&Position :line :character)) 1790 "Convert `Position' object in PARAMS to a point." 1791 (lsp--line-character-to-point line character)) 1792 1793 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end)) 1794 (cons start end)) 1795 1796 (lsp-defun lsp--range-text ((&RangeToPoint :start :end)) 1797 (buffer-substring start end)) 1798 1799 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end))) 1800 (cond 1801 ((and 1802 (region-active-p) 1803 (<= start (region-beginning) end) 1804 (<= start (region-end) end) 1805 (or (not (= start (region-beginning))) 1806 (not (= end (region-end))))) 1807 (cons start end)) 1808 ((and (<= start (point) end) 1809 (not (region-active-p))) 1810 (cons start end)) 1811 (parent? (lsp--find-wrapping-range parent?)))) 1812 1813 (defun lsp--get-selection-range () 1814 (or 1815 (-when-let ((cache . cache-tick) lsp--document-selection-range-cache) 1816 (when (= cache-tick (buffer-modified-tick)) cache)) 1817 (let ((response (cl-first 1818 (lsp-request 1819 "textDocument/selectionRange" 1820 (list :textDocument (lsp--text-document-identifier) 1821 :positions (vector (lsp--cur-position))))))) 1822 (setq lsp--document-selection-range-cache 1823 (cons response (buffer-modified-tick))) 1824 response))) 1825 1826 (defun lsp-extend-selection () 1827 "Extend selection." 1828 (interactive) 1829 (unless (lsp-feature? "textDocument/selectionRange") 1830 (signal 'lsp-capability-not-supported (list "selectionRangeProvider"))) 1831 (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range))) 1832 (goto-char start) 1833 (set-mark (point)) 1834 (goto-char end) 1835 (exchange-point-and-mark))) 1836 1837 (defun lsp-warn (message &rest args) 1838 "Display a warning message made from (`format-message' MESSAGE ARGS...). 1839 This is equivalent to `display-warning', using `lsp-mode' as the type and 1840 `:warning' as the level." 1841 (display-warning 'lsp-mode (apply #'format-message message args))) 1842 1843 (defun lsp--get-uri-handler (scheme) 1844 "Get uri handler for SCHEME in the current workspace." 1845 (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it))) 1846 (or (lsp-workspaces) (lsp--session-workspaces (lsp-session))))) 1847 1848 (defun lsp--fix-path-casing (path) 1849 "On windows, downcases path because the windows file system is 1850 case-insensitive. 1851 1852 On other systems, returns path without change." 1853 (if (eq system-type 'windows-nt) (downcase path) path)) 1854 1855 (defun lsp--uri-to-path (uri) 1856 "Convert URI to a file path." 1857 (if-let ((fn (->> (lsp-workspaces) 1858 (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client)) 1859 (cl-first)))) 1860 (funcall fn uri) 1861 (lsp--uri-to-path-1 uri))) 1862 1863 (defun lsp-remap-path-if-needed (file-name) 1864 (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings)) 1865 (propertize (buffer-local-value 'buffer-file-name buffer) 1866 'lsp-virtual-buffer virtual-buffer) 1867 file-name)) 1868 1869 (defun lsp--uri-to-path-1 (uri) 1870 "Convert URI to a file path." 1871 (let* ((url (url-generic-parse-url (url-unhex-string uri))) 1872 (type (url-type url)) 1873 (target (url-target url)) 1874 (file 1875 (concat (decode-coding-string (url-filename url) 1876 (or locale-coding-system 'utf-8)) 1877 (when (and target 1878 (not (s-match 1879 (rx "#" (group (1+ num)) (or "," "#") 1880 (group (1+ num)) 1881 string-end) 1882 uri))) 1883 (concat "#" target)))) 1884 (file-name (if (and type (not (string= type "file"))) 1885 (if-let ((handler (lsp--get-uri-handler type))) 1886 (funcall handler uri) 1887 uri) 1888 ;; `url-generic-parse-url' is buggy on windows: 1889 ;; https://github.com/emacs-lsp/lsp-mode/pull/265 1890 (or (and (eq system-type 'windows-nt) 1891 (eq (elt file 0) ?\/) 1892 (substring file 1)) 1893 file)))) 1894 (->> file-name 1895 (concat (-some #'lsp--workspace-host-root (lsp-workspaces))) 1896 (lsp-remap-path-if-needed)))) 1897 1898 (defun lsp--buffer-uri () 1899 "Return URI of the current buffer." 1900 (or lsp-buffer-uri 1901 (plist-get lsp--virtual-buffer :buffer-uri) 1902 (lsp--path-to-uri 1903 (or (buffer-file-name) (buffer-file-name (buffer-base-buffer)))))) 1904 1905 (defun lsp-register-client-capabilities (&rest _args) 1906 "Implemented only to make `company-lsp' happy. 1907 DELETE when `lsp-mode.el' is deleted.") 1908 1909 (defconst lsp--url-path-allowed-chars 1910 (url--allowed-chars (append '(?/) url-unreserved-chars)) 1911 "`url-unreserved-chars' with additional delim ?/. 1912 This set of allowed chars is enough for hexifying local file paths.") 1913 1914 (defun lsp--path-to-uri-1 (path) 1915 (concat lsp--uri-file-prefix 1916 (--> path 1917 (expand-file-name it) 1918 (or (file-remote-p it 'localname t) it) 1919 (url-hexify-string it lsp--url-path-allowed-chars)))) 1920 1921 (defun lsp--path-to-uri (path) 1922 "Convert PATH to a uri." 1923 (if-let ((uri-fn (->> (lsp-workspaces) 1924 (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client)) 1925 (cl-first)))) 1926 (funcall uri-fn path) 1927 (lsp--path-to-uri-1 path))) 1928 1929 (defun lsp--string-match-any (regex-list str) 1930 "Return the first regex, if any, within REGEX-LIST matching STR." 1931 (--first (string-match it str) regex-list)) 1932 1933 (cl-defstruct lsp-watch 1934 (descriptors (make-hash-table :test 'equal)) 1935 root-directory) 1936 1937 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories) 1938 (let ((file-name (cl-third event)) 1939 (event-type (cl-second event))) 1940 (cond 1941 ((and (file-directory-p file-name) 1942 (equal 'created event-type) 1943 (not (lsp--string-match-any ignored-directories file-name))) 1944 1945 (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch) 1946 1947 ;; process the files that are already present in 1948 ;; the directory. 1949 (->> (directory-files-recursively file-name ".*" t) 1950 (seq-do (lambda (f) 1951 (unless (file-directory-p f) 1952 (funcall callback (list nil 'created f))))))) 1953 ((and (memq event-type '(created deleted changed)) 1954 (not (file-directory-p file-name)) 1955 (not (lsp--string-match-any ignored-files file-name))) 1956 (funcall callback event)) 1957 ((and (memq event-type '(renamed)) 1958 (not (file-directory-p file-name)) 1959 (not (lsp--string-match-any ignored-files file-name))) 1960 (funcall callback `(,(cl-first event) deleted ,(cl-third event))) 1961 (funcall callback `(,(cl-first event) created ,(cl-fourth event))))))) 1962 1963 (defun lsp--ask-about-watching-big-repo (number-of-directories dir) 1964 "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR. 1965 This is useful when there is a lot of files in a repository, as 1966 that may slow Emacs down. Returns t if the user wants to watch 1967 the entire repository, nil otherwise." 1968 (prog1 1969 (yes-or-no-p 1970 (format 1971 "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down. 1972 Do you want to watch all files in %s? " 1973 dir 1974 number-of-directories 1975 dir)) 1976 (lsp--info 1977 (concat "You can configure this warning with the `lsp-enable-file-watchers' " 1978 "and `lsp-file-watch-threshold' variables")))) 1979 1980 1981 (defun lsp--path-is-watchable-directory (path dir ignored-directories) 1982 "Figure out whether PATH (inside of DIR) is meant to have a file watcher set. 1983 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't 1984 want to watch." 1985 (let 1986 ((full-path (f-join dir path))) 1987 (and (file-accessible-directory-p full-path) 1988 (not (equal path ".")) 1989 (not (equal path "..")) 1990 (not (lsp--string-match-any ignored-directories full-path))))) 1991 1992 1993 (defun lsp--all-watchable-directories (dir ignored-directories &optional visited) 1994 "Traverse DIR recursively returning a list of paths that should have watchers. 1995 IGNORED-DIRECTORIES will be used for exclusions. 1996 VISITED is used to track already-visited directories to avoid infinite loops." 1997 (let* ((dir (if (f-symlink? dir) 1998 (file-truename dir) 1999 dir)) 2000 ;; Initialize visited directories if not provided 2001 (visited (or visited (make-hash-table :test 'equal)))) 2002 (if (gethash dir visited) 2003 ;; If the directory has already been visited, skip it 2004 nil 2005 ;; Mark the current directory as visited 2006 (puthash dir t visited) 2007 (apply #'nconc 2008 ;; the directory itself is assumed to be part of the set 2009 (list dir) 2010 ;; collect all subdirectories that are watchable 2011 (-map 2012 (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories visited)) 2013 ;; but only look at subdirectories that are watchable 2014 (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories)) 2015 (directory-files dir))))))) 2016 2017 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?) 2018 "Create recursive file notification watch in DIR. 2019 CALLBACK will be called when there are changes in any of 2020 the monitored files. WATCHES is a hash table directory->file 2021 notification handle which contains all of the watch that 2022 already have been created. Watches will not be created for 2023 any directory that matches any regex in IGNORED-DIRECTORIES. 2024 Watches will not be created for any file that matches any 2025 regex in IGNORED-FILES." 2026 (let* ((dir (if (f-symlink? dir) 2027 (file-truename dir) 2028 dir)) 2029 (watch (or watch (make-lsp-watch :root-directory dir))) 2030 (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories))) 2031 (lsp-log "Creating watchers for following %s folders:\n %s" 2032 (length dirs-to-watch) 2033 (s-join "\n " dirs-to-watch)) 2034 (when (or 2035 (not warn-big-repo?) 2036 (not lsp-file-watch-threshold) 2037 (let ((number-of-directories (length dirs-to-watch))) 2038 (or 2039 (< number-of-directories lsp-file-watch-threshold) 2040 (condition-case nil 2041 (lsp--ask-about-watching-big-repo number-of-directories dir) 2042 (quit))))) 2043 (dolist (current-dir dirs-to-watch) 2044 (condition-case err 2045 (progn 2046 (puthash 2047 current-dir 2048 (file-notify-add-watch current-dir 2049 '(change) 2050 (lambda (event) 2051 (lsp--folder-watch-callback event callback watch ignored-files ignored-directories))) 2052 (lsp-watch-descriptors watch))) 2053 (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err))) 2054 (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))))) 2055 watch)) 2056 2057 (defun lsp-kill-watch (watch) 2058 "Delete WATCH." 2059 (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch)) 2060 (ht-clear! (lsp-watch-descriptors watch))) 2061 2062 (defun lsp-json-bool (val) 2063 "Convert VAL to JSON boolean." 2064 (if val t :json-false)) 2065 2066 (defmacro with-lsp-workspace (workspace &rest body) 2067 "Helper macro for invoking BODY in WORKSPACE context." 2068 (declare (debug (form body)) 2069 (indent 1)) 2070 `(let ((lsp--cur-workspace ,workspace)) ,@body)) 2071 2072 (defmacro with-lsp-workspaces (workspaces &rest body) 2073 "Helper macro for invoking BODY against multiple WORKSPACES." 2074 (declare (debug (form body)) 2075 (indent 1)) 2076 `(let ((lsp--buffer-workspaces ,workspaces)) ,@body)) 2077 2078 2079 2080 (defmacro lsp-consistency-check (package) 2081 `(defconst ,(intern (concat (symbol-name package) 2082 "-plist-value-when-compiled")) 2083 (eval-when-compile lsp-use-plists))) 2084 2085 2086 ;; loading code-workspace files 2087 2088 ;;;###autoload 2089 (defun lsp-load-vscode-workspace (file) 2090 "Load vscode workspace from FILE" 2091 (interactive "fSelect file to import: ") 2092 (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session))) 2093 2094 (let ((dir (f-dirname file))) 2095 (->> file 2096 (json-read-file) 2097 (alist-get 'folders) 2098 (-map (-lambda ((&alist 'path)) 2099 (lsp-workspace-folders-add (expand-file-name path dir))))))) 2100 2101 ;;;###autoload 2102 (defun lsp-save-vscode-workspace (file) 2103 "Save vscode workspace to FILE" 2104 (interactive "FSelect file to save to: ") 2105 2106 (let ((json-encoding-pretty-print t)) 2107 (f-write-text (json-encode 2108 `((folders . ,(->> (lsp-session) 2109 (lsp-session-folders) 2110 (--map `((path . ,it))))))) 2111 'utf-8 2112 file))) 2113 2114 2115 (defmacro lsp-foreach-workspace (&rest body) 2116 "Execute BODY for each of the current workspaces." 2117 (declare (debug (form body))) 2118 `(--map (with-lsp-workspace it ,@body) (lsp-workspaces))) 2119 2120 (defmacro when-lsp-workspace (workspace &rest body) 2121 "Helper macro for invoking BODY in WORKSPACE context if present." 2122 (declare (debug (form body)) 2123 (indent 1)) 2124 `(when-let ((lsp--cur-workspace ,workspace)) ,@body)) 2125 2126 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items)) 2127 (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read)) 2128 (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label)) 2129 items)) 2130 (result (funcall-interactively 2131 selectfunc 2132 (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels)) 2133 (choices (if (listp result) 2134 (if (equal result '("*")) 2135 itemLabels 2136 result) 2137 (list result)))) 2138 (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data)) 2139 (if (member label choices) 2140 (lsp-make-quick-pick-item :label label :picked t :user-data user-data) 2141 nil)) 2142 items))))) 2143 2144 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?)) 2145 (read-string (format "%s: " prompt) (or value? ""))) 2146 2147 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type)) 2148 "Send the server's messages to log. 2149 PARAMS - the data sent from _WORKSPACE." 2150 (funcall (cl-case type 2151 (1 'lsp--error) 2152 (2 'lsp--warn) 2153 (t 'lsp--info)) 2154 "%s" 2155 message)) 2156 2157 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type)) 2158 "Send the server's messages to log. 2159 PARAMS - the data sent from WORKSPACE." 2160 (ignore 2161 (let ((client (lsp--workspace-client workspace))) 2162 (when (or (not client) 2163 (cl-notany (-rpartial #'string-match-p message) 2164 (lsp--client-ignore-messages client))) 2165 (lsp-log "%s" (lsp--propertize message type)))))) 2166 2167 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?)) 2168 "Display a message request to user sending the user selection back to server." 2169 (let* ((message (lsp--propertize message type)) 2170 (choices (seq-map #'lsp:message-action-item-title actions?))) 2171 (if choices 2172 (completing-read (concat message " ") (seq-into choices 'list) nil t) 2173 (lsp-log message)))) 2174 2175 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?)) 2176 "Show document URI in a buffer and go to SELECTION if any." 2177 (let ((path (lsp--uri-to-path uri))) 2178 (when (f-exists? path) 2179 (with-current-buffer (find-file path) 2180 (when selection? 2181 (goto-char (lsp--position-to-point (lsp:range-start selection?)))) 2182 t)))) 2183 2184 (defcustom lsp-progress-prefix "⌛ " 2185 "Progress prefix." 2186 :group 'lsp-mode 2187 :type 'string 2188 :package-version '(lsp-mode . "8.0.0")) 2189 2190 (defcustom lsp-progress-function #'lsp-on-progress-modeline 2191 "Function for handling the progress notifications." 2192 :group 'lsp-mode 2193 :type '(choice 2194 (const :tag "Use modeline" lsp-on-progress-modeline) 2195 (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')" 2196 lsp-on-progress-legacy) 2197 (const :tag "Ignore" ignore) 2198 (function :tag "Other function")) 2199 :package-version '(lsp-mode . "8.0.0")) 2200 2201 (defcustom lsp-request-while-no-input-may-block nil 2202 "Have `lsp-request-while-no-input` block unless `non-essential` is t." 2203 :group 'lsp-mode 2204 :type 'boolean) 2205 2206 (defun lsp--progress-status () 2207 "Returns the status of the progress for the current workspaces." 2208 (-let ((progress-status 2209 (s-join 2210 "|" 2211 (-keep 2212 (lambda (workspace) 2213 (let ((tokens (lsp--workspace-work-done-tokens workspace))) 2214 (unless (ht-empty? tokens) 2215 (mapconcat 2216 (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?)) 2217 (concat (if percentage? 2218 (if (numberp percentage?) 2219 (format "%.0f%%%% " percentage?) 2220 (format "%s%%%% " percentage?)) 2221 "") 2222 (or message? title))) 2223 (ht-values tokens) 2224 "|")))) 2225 (lsp-workspaces))))) 2226 (unless (s-blank? progress-status) 2227 (concat lsp-progress-prefix progress-status " ")))) 2228 2229 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value 2230 (value &as &WorkDoneProgress :kind))) 2231 "PARAMS contains the progress data. 2232 WORKSPACE is the workspace that contains the progress token." 2233 (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status)))) 2234 (pcase kind 2235 ("begin" (lsp-workspace-set-work-done-token token value workspace)) 2236 ("report" (lsp-workspace-set-work-done-token token value workspace)) 2237 ("end" (lsp-workspace-rem-work-done-token token workspace))) 2238 (force-mode-line-update)) 2239 2240 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value 2241 (value &as &WorkDoneProgress :kind))) 2242 "PARAMS contains the progress data. 2243 WORKSPACE is the workspace that contains the progress token." 2244 (pcase kind 2245 ("begin" 2246 (-let* (((&WorkDoneProgressBegin :title :percentage?) value) 2247 (reporter 2248 (if lsp-progress-via-spinner 2249 (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types)) 2250 ;; Set message as a tooltip for the spinner strings 2251 (propertized-strings 2252 (seq-map (lambda (string) (propertize string 'help-echo title)) 2253 spinner-strings)) 2254 (spinner-type (vconcat propertized-strings))) 2255 ;; The progress relates to the server as a whole, 2256 ;; display it on all buffers. 2257 (mapcar (lambda (buffer) 2258 (lsp-with-current-buffer buffer 2259 (spinner-start spinner-type)) 2260 buffer) 2261 (lsp--workspace-buffers workspace))) 2262 (if percentage? 2263 (make-progress-reporter title 0 100 percentage?) 2264 ;; No percentage, just progress 2265 (make-progress-reporter title nil nil))))) 2266 (lsp-workspace-set-work-done-token token reporter workspace))) 2267 ("report" 2268 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2269 (unless lsp-progress-via-spinner 2270 (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value))))) 2271 2272 ("end" 2273 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2274 (if lsp-progress-via-spinner 2275 (mapc (lambda (buffer) 2276 (when (lsp-buffer-live-p buffer) 2277 (lsp-with-current-buffer buffer 2278 (spinner-stop)))) 2279 reporter) 2280 (progress-reporter-done reporter)) 2281 (lsp-workspace-rem-work-done-token token workspace))))) 2282 2283 2284 ;; diagnostics 2285 2286 (defvar lsp-diagnostic-filter nil 2287 "A a function which will be called with 2288 `&PublishDiagnosticsParams' and `workspace' which can be used 2289 to filter out the diagnostics. The function should return 2290 `&PublishDiagnosticsParams'. 2291 2292 Common usecase are: 2293 1. Filter the diagnostics for a particular language server. 2294 2. Filter out the diagnostics under specific level.") 2295 2296 (defvar lsp-diagnostic-stats (ht)) 2297 2298 (defun lsp-diagnostics (&optional current-workspace?) 2299 "Return the diagnostics from all workspaces." 2300 (or (pcase (if current-workspace? 2301 (lsp-workspaces) 2302 (lsp--session-workspaces (lsp-session))) 2303 (`() ()) 2304 (`(,workspace) (lsp--workspace-diagnostics workspace)) 2305 (`,workspaces (let ((result (make-hash-table :test 'equal))) 2306 (mapc (lambda (workspace) 2307 (->> workspace 2308 (lsp--workspace-diagnostics) 2309 (maphash (lambda (file-name diagnostics) 2310 (puthash file-name 2311 (append (gethash file-name result) diagnostics) 2312 result))))) 2313 workspaces) 2314 result))) 2315 (ht))) 2316 2317 (defun lsp-diagnostics-stats-for (path) 2318 "Get diagnostics statistics for PATH. 2319 The result format is vector [_ errors warnings infos hints] or nil." 2320 (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats)) 2321 2322 (defun lsp-diagnostics--request-pull-diagnostics (workspace) 2323 "Request new diagnostics for the current file within WORKSPACE. 2324 This is only executed if the server supports pull diagnostics." 2325 (when (lsp-feature? "textDocument/diagnostic") 2326 (let ((path (lsp--fix-path-casing (buffer-file-name)))) 2327 (lsp-request-async "textDocument/diagnostic" 2328 (list :textDocument (lsp--text-document-identifier)) 2329 (-lambda ((&DocumentDiagnosticReport :kind :items?)) 2330 (lsp-diagnostics--apply-pull-diagnostics workspace path kind items?)) 2331 :mode 'tick)))) 2332 2333 (defun lsp-diagnostics--update-path (path new-stats) 2334 (let ((new-stats (copy-sequence new-stats)) 2335 (path (lsp--fix-path-casing (directory-file-name path)))) 2336 (if-let ((old-data (gethash path lsp-diagnostic-stats))) 2337 (dotimes (idx 5) 2338 (cl-callf + (aref old-data idx) 2339 (aref new-stats idx))) 2340 (puthash path new-stats lsp-diagnostic-stats)))) 2341 2342 (defun lsp-diagnostics--convert-and-update-path-stats (workspace path diagnostics) 2343 (let ((path (lsp--fix-path-casing path)) 2344 (new-stats (make-vector 5 0))) 2345 (mapc (-lambda ((&Diagnostic :severity?)) 2346 (cl-incf (aref new-stats (or severity? 1)))) 2347 diagnostics) 2348 (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace)))) 2349 (mapc (-lambda ((&Diagnostic :severity?)) 2350 (cl-decf (aref new-stats (or severity? 1)))) 2351 old-diags)) 2352 (lsp-diagnostics--update-path path new-stats) 2353 (while (not (string= path (setf path (file-name-directory 2354 (directory-file-name path))))) 2355 (lsp-diagnostics--update-path path new-stats)))) 2356 2357 (lsp-defun lsp--on-diagnostics-update-stats (workspace 2358 (&PublishDiagnosticsParams :uri :diagnostics)) 2359 (lsp-diagnostics--convert-and-update-path-stats workspace (lsp--uri-to-path uri) diagnostics)) 2360 2361 (defun lsp-diagnostics--apply-pull-diagnostics (workspace path kind diagnostics?) 2362 "Update WORKSPACE diagnostics at PATH with DIAGNOSTICS?. 2363 Depends on KIND being a \\='full\\=' update." 2364 (cond 2365 ((equal kind "full") 2366 ;; TODO support `lsp-diagnostic-filter' 2367 ;; (the params types differ from the published diagnostics response) 2368 (lsp-diagnostics--convert-and-update-path-stats workspace path diagnostics?) 2369 (-let* ((lsp--virtual-buffer-mappings (ht)) 2370 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2371 (if (seq-empty-p diagnostics?) 2372 (remhash path workspace-diagnostics) 2373 (puthash path (append diagnostics? nil) workspace-diagnostics)) 2374 (run-hooks 'lsp-diagnostics-updated-hook))) 2375 ((equal kind "unchanged") t) 2376 (t (lsp--error "Unknown pull diagnostic result kind '%s'" kind)))) 2377 2378 (defun lsp--on-diagnostics (workspace params) 2379 "Callback for textDocument/publishDiagnostics. 2380 interface PublishDiagnosticsParams { 2381 uri: string; 2382 diagnostics: Diagnostic[]; 2383 } 2384 PARAMS contains the diagnostics data. 2385 WORKSPACE is the workspace that contains the diagnostics." 2386 (when lsp-diagnostic-filter 2387 (setf params (funcall lsp-diagnostic-filter params workspace))) 2388 2389 (lsp--on-diagnostics-update-stats workspace params) 2390 2391 (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params) 2392 (lsp--virtual-buffer-mappings (ht)) 2393 (file (lsp--fix-path-casing (lsp--uri-to-path uri))) 2394 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2395 2396 (if (seq-empty-p diagnostics) 2397 (remhash file workspace-diagnostics) 2398 (puthash file (append diagnostics nil) workspace-diagnostics)) 2399 2400 (run-hooks 'lsp-diagnostics-updated-hook))) 2401 2402 (defun lsp-diagnostics--workspace-cleanup (workspace) 2403 (->> workspace 2404 (lsp--workspace-diagnostics) 2405 (maphash (lambda (key _) 2406 (lsp--on-diagnostics-update-stats 2407 workspace 2408 (lsp-make-publish-diagnostics-params 2409 :uri (lsp--path-to-uri key) 2410 :diagnostics []))))) 2411 (clrhash (lsp--workspace-diagnostics workspace))) 2412 2413 2414 2415 ;; textDocument/foldingRange support 2416 2417 (cl-defstruct lsp--folding-range beg end kind children) 2418 2419 (defvar-local lsp--cached-folding-ranges nil) 2420 (defvar-local lsp--cached-nested-folding-ranges nil) 2421 2422 (defun lsp--folding-range-width (range) 2423 (- (lsp--folding-range-end range) 2424 (lsp--folding-range-beg range))) 2425 2426 (defun lsp--get-folding-ranges () 2427 "Get the folding ranges for the current buffer." 2428 (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges)) 2429 (let* ((ranges (lsp-request "textDocument/foldingRange" 2430 `(:textDocument ,(lsp--text-document-identifier)))) 2431 (sorted-line-col-pairs (->> ranges 2432 (cl-mapcan (-lambda ((&FoldingRange :start-line 2433 :start-character? 2434 :end-line 2435 :end-character?)) 2436 (list (cons start-line start-character?) 2437 (cons end-line end-character?)))) 2438 (-sort #'lsp--line-col-comparator))) 2439 (line-col-to-point-map (lsp--convert-line-col-to-points-batch 2440 sorted-line-col-pairs))) 2441 (setq lsp--cached-folding-ranges 2442 (cons (buffer-chars-modified-tick) 2443 (--> ranges 2444 (seq-map (-lambda ((range &as 2445 &FoldingRange :start-line 2446 :start-character? 2447 :end-line 2448 :end-character? 2449 :kind?)) 2450 (make-lsp--folding-range 2451 :beg (ht-get line-col-to-point-map 2452 (cons start-line start-character?)) 2453 :end (ht-get line-col-to-point-map 2454 (cons end-line end-character?)) 2455 :kind kind?)) 2456 it) 2457 (seq-filter (lambda (folding-range) 2458 (< (lsp--folding-range-beg folding-range) 2459 (lsp--folding-range-end folding-range))) 2460 it) 2461 (seq-into it 'list) 2462 (delete-dups it)))))) 2463 (cdr lsp--cached-folding-ranges)) 2464 2465 (defun lsp--get-nested-folding-ranges () 2466 "Get a list of nested folding ranges for the current buffer." 2467 (-let [(tick . _) lsp--cached-folding-ranges] 2468 (if (and (eq tick (buffer-chars-modified-tick)) 2469 lsp--cached-nested-folding-ranges) 2470 lsp--cached-nested-folding-ranges 2471 (setq lsp--cached-nested-folding-ranges 2472 (lsp--folding-range-build-trees (lsp--get-folding-ranges)))))) 2473 2474 (defun lsp--folding-range-build-trees (ranges) 2475 (setq ranges (seq-sort #'lsp--range-before-p ranges)) 2476 (let* ((dummy-node (make-lsp--folding-range 2477 :beg most-negative-fixnum 2478 :end most-positive-fixnum)) 2479 (stack (list dummy-node))) 2480 (dolist (range ranges) 2481 (while (not (lsp--range-inside-p range (car stack))) 2482 (pop stack)) 2483 (push range (lsp--folding-range-children (car stack))) 2484 (push range stack)) 2485 (lsp--folding-range-children dummy-node))) 2486 2487 (defun lsp--range-inside-p (r1 r2) 2488 "Return non-nil if folding range R1 lies inside R2" 2489 (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2)) 2490 (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2)))) 2491 2492 (defun lsp--range-before-p (r1 r2) 2493 "Return non-nil if folding range R1 ends before R2" 2494 ;; Ensure r1 comes before r2 2495 (or (< (lsp--folding-range-beg r1) 2496 (lsp--folding-range-beg r2)) 2497 ;; If beg(r1) == beg(r2) make sure r2 ends first 2498 (and (= (lsp--folding-range-beg r1) 2499 (lsp--folding-range-beg r2)) 2500 (< (lsp--folding-range-end r2) 2501 (lsp--folding-range-end r1))))) 2502 2503 (defun lsp--point-inside-range-p (point range) 2504 "Return non-nil if POINT lies inside folding range RANGE." 2505 (and (>= point (lsp--folding-range-beg range)) 2506 (<= point (lsp--folding-range-end range)))) 2507 2508 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point))) 2509 "Return the innermost folding range POINT lies in." 2510 (seq-reduce (lambda (innermost-range curr-range) 2511 (if (and (lsp--point-inside-range-p point curr-range) 2512 (or (null innermost-range) 2513 (lsp--range-inside-p curr-range innermost-range))) 2514 curr-range 2515 innermost-range)) 2516 (lsp--get-folding-ranges) 2517 nil)) 2518 2519 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point))) 2520 "Return the outermost folding range POINT lies in." 2521 (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range) 2522 (let ((curr-width (lsp--folding-range-width curr-range))) 2523 (if (and (lsp--point-inside-range-p point curr-range) 2524 (or (null best-pair) 2525 (> curr-width outermost-width))) 2526 (cons curr-width curr-range) 2527 best-pair))) 2528 (lsp--get-folding-ranges) 2529 nil))) 2530 2531 (defun lsp--folding-range-at-point-bounds () 2532 (when (and lsp-enable-folding 2533 (lsp-feature? "textDocument/foldingRange")) 2534 (if-let ((range (lsp--get-current-innermost-folding-range))) 2535 (cons (lsp--folding-range-beg range) 2536 (lsp--folding-range-end range))))) 2537 (put 'lsp--folding-range 'bounds-of-thing-at-point 2538 #'lsp--folding-range-at-point-bounds) 2539 2540 (defun lsp--get-nearest-folding-range (&optional backward) 2541 (let ((point (point)) 2542 (found nil)) 2543 (while (not 2544 (or found 2545 (if backward 2546 (<= point (point-min)) 2547 (>= point (point-max))))) 2548 (if backward (cl-decf point) (cl-incf point)) 2549 (setq found (lsp--get-current-innermost-folding-range point))) 2550 found)) 2551 2552 (defun lsp--folding-range-at-point-forward-op (n) 2553 (when (and lsp-enable-folding 2554 (not (zerop n)) 2555 (lsp-feature? "textDocument/foldingRange")) 2556 (cl-block break 2557 (dotimes (_ (abs n)) 2558 (if-let ((range (lsp--get-nearest-folding-range (< n 0)))) 2559 (goto-char (if (< n 0) 2560 (lsp--folding-range-beg range) 2561 (lsp--folding-range-end range))) 2562 (cl-return-from break)))))) 2563 (put 'lsp--folding-range 'forward-op 2564 #'lsp--folding-range-at-point-forward-op) 2565 2566 (defun lsp--folding-range-at-point-beginning-op () 2567 (goto-char (car (lsp--folding-range-at-point-bounds)))) 2568 (put 'lsp--folding-range 'beginning-op 2569 #'lsp--folding-range-at-point-beginning-op) 2570 2571 (defun lsp--folding-range-at-point-end-op () 2572 (goto-char (cdr (lsp--folding-range-at-point-bounds)))) 2573 (put 'lsp--folding-range 'end-op 2574 #'lsp--folding-range-at-point-end-op) 2575 2576 (defun lsp--range-at-point-bounds () 2577 (or (lsp--folding-range-at-point-bounds) 2578 (when-let ((range (and 2579 (lsp-feature? "textDocument/hover") 2580 (->> (lsp--text-document-position-params) 2581 (lsp-request "textDocument/hover") 2582 (lsp:hover-range?))))) 2583 (lsp--range-to-region range)))) 2584 2585 ;; A more general purpose "thing", useful for applications like focus.el 2586 (put 'lsp--range 'bounds-of-thing-at-point 2587 #'lsp--range-at-point-bounds) 2588 2589 (defun lsp--log-io-p (method) 2590 "Return non nil if should log for METHOD." 2591 (and lsp-log-io 2592 (or (not lsp-log-io-allowlist-methods) 2593 (member method lsp-log-io-allowlist-methods)))) 2594 2595 2596 ;; toggles 2597 2598 (defun lsp-toggle-trace-io () 2599 "Toggle client-server protocol logging." 2600 (interactive) 2601 (setq lsp-log-io (not lsp-log-io)) 2602 (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled"))) 2603 2604 (defun lsp-toggle-signature-auto-activate () 2605 "Toggle signature auto activate." 2606 (interactive) 2607 (setq lsp-signature-auto-activate 2608 (unless lsp-signature-auto-activate '(:on-trigger-char))) 2609 (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled")) 2610 (lsp--update-signature-help-hook)) 2611 2612 (defun lsp-toggle-on-type-formatting () 2613 "Toggle on type formatting." 2614 (interactive) 2615 (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting)) 2616 (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled")) 2617 (lsp--update-on-type-formatting-hook)) 2618 2619 (defun lsp-toggle-symbol-highlight () 2620 "Toggle symbol highlighting." 2621 (interactive) 2622 (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting)) 2623 2624 (cond 2625 ((and lsp-enable-symbol-highlighting 2626 (lsp-feature? "textDocument/documentHighlight")) 2627 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t) 2628 (lsp--info "Symbol highlighting enabled in current buffer.")) 2629 ((not lsp-enable-symbol-highlighting) 2630 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 2631 (lsp--remove-overlays 'lsp-highlight) 2632 (lsp--info "Symbol highlighting disabled in current buffer.")))) 2633 2634 2635 ;; keybindings 2636 (defvar lsp--binding-descriptions nil 2637 "List of key binding/short description pair.") 2638 2639 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings) 2640 "In KEYMAP, define key sequence KEY as DEF conditionally. 2641 This is like `define-key', except the definition disappears 2642 whenever COND evaluates to nil. 2643 DESC is the short-description for the binding. 2644 BINDINGS is a list of (key def desc cond)." 2645 (declare (indent defun) 2646 (debug (form form form form form &rest sexp))) 2647 (->> (cl-list* key def desc cond bindings) 2648 (-partition 4) 2649 (-mapcat (-lambda ((key def desc cond)) 2650 `((define-key ,keymap ,key 2651 '(menu-item 2652 ,(format "maybe-%s" def) 2653 ,def 2654 :filter 2655 (lambda (item) 2656 (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer) 2657 lsp--describe-buffer) 2658 (current-buffer)) 2659 ,cond) 2660 item)))) 2661 (when (stringp ,key) 2662 (setq lsp--binding-descriptions 2663 (append lsp--binding-descriptions '(,key ,desc))))))) 2664 macroexp-progn)) 2665 2666 (defvar lsp--describe-buffer nil) 2667 2668 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus) 2669 (let ((lsp--describe-buffer buffer)) 2670 (funcall fn buffer prefix menus))) 2671 2672 (advice-add 'describe-buffer-bindings 2673 :around 2674 #'lsp-describe-buffer-bindings-advice) 2675 2676 (defun lsp--prepend-prefix (mappings) 2677 (->> mappings 2678 (-partition 2) 2679 (-mapcat (-lambda ((key description)) 2680 (list (concat lsp-keymap-prefix " " key) 2681 description))))) 2682 2683 (defvar lsp-command-map 2684 (-doto (make-sparse-keymap) 2685 (lsp-define-conditional-key 2686 ;; workspaces 2687 "wD" lsp-disconnect "disconnect" (lsp-workspaces) 2688 "wd" lsp-describe-session "describe session" t 2689 "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces) 2690 "wr" lsp-workspace-restart "restart server" (lsp-workspaces) 2691 "ws" lsp "start server" t 2692 2693 ;; formatting 2694 "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting") 2695 (lsp-feature? "textDocument/formatting")) 2696 "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting") 2697 2698 ;; folders 2699 "Fa" lsp-workspace-folders-add "add folder" t 2700 "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t 2701 "Fr" lsp-workspace-folders-remove "remove folder" t 2702 2703 ;; toggles 2704 "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature? 2705 "textDocument/publishDiagnostics") 2706 "TL" lsp-toggle-trace-io "toggle log io" t 2707 "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline) 2708 "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs) 2709 "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature? 2710 "textDocument/codeAction") 2711 "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature? 2712 "textDocument/documentSymbol") 2713 "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc) 2714 "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature? 2715 "textDocument/onTypeFormatting") 2716 "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight") 2717 "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens") 2718 "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp") 2719 2720 ;; goto 2721 "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol") 2722 "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration") 2723 "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list) 2724 "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition") 2725 "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls") 2726 (fboundp 'lsp-treemacs-call-hierarchy)) 2727 "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation") 2728 "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references") 2729 "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition") 2730 2731 ;; help 2732 "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc) 2733 (lsp-feature? "textDocument/hover")) 2734 "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover") 2735 "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp") 2736 2737 ;; refactoring 2738 "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction") 2739 "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename") 2740 2741 ;; actions 2742 "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction") 2743 "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight") 2744 "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy)) 2745 2746 ;; peeks 2747 "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition") 2748 (fboundp 'lsp-ui-peek-find-definitions)) 2749 "Gi" lsp-ui-peek-find-implementation "peek implementations" (and 2750 (fboundp 'lsp-ui-peek-find-implementation) 2751 (lsp-feature? "textDocument/implementation")) 2752 "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references) 2753 (lsp-feature? "textDocument/references")) 2754 "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp 2755 'lsp-ui-peek-find-workspace-symbol) 2756 (lsp-feature? "workspace/symbol"))))) 2757 2758 2759 ;; which-key integration 2760 2761 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key") 2762 (declare-function which-key-add-key-based-replacements "ext:which-key") 2763 2764 (defun lsp-enable-which-key-integration (&optional all-modes) 2765 "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current 2766 active `major-mode', or for all major modes when ALL-MODES is t." 2767 (cl-flet ((which-key-fn (if all-modes 2768 'which-key-add-key-based-replacements 2769 (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode)))) 2770 (apply 2771 #'which-key-fn 2772 (lsp--prepend-prefix 2773 (cl-list* 2774 "" "lsp" 2775 "w" "workspaces" 2776 "F" "folders" 2777 "=" "formatting" 2778 "T" "toggle" 2779 "g" "goto" 2780 "h" "help" 2781 "r" "refactor" 2782 "a" "code actions" 2783 "G" "peek" 2784 lsp--binding-descriptions))))) 2785 2786 2787 ;; Globbing syntax 2788 2789 ;; We port VSCode's glob-to-regexp code 2790 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts) 2791 ;; since the LSP globbing syntax seems to be the same as that of 2792 ;; VSCode. 2793 2794 (defconst lsp-globstar "**" 2795 "Globstar pattern.") 2796 2797 (defconst lsp-glob-split ?/ 2798 "The character by which we split path components in a glob 2799 pattern.") 2800 2801 (defconst lsp-path-regexp "[/\\\\]" 2802 "Forward or backslash to be used as a path separator in 2803 computed regexps.") 2804 2805 (defconst lsp-non-path-regexp "[^/\\\\]" 2806 "A regexp matching anything other than a slash.") 2807 2808 (defconst lsp-globstar-regexp 2809 (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?" 2810 lsp-path-regexp 2811 lsp-non-path-regexp lsp-path-regexp 2812 lsp-path-regexp lsp-non-path-regexp) 2813 "Globstar in regexp form.") 2814 2815 (defun lsp-split-glob-pattern (pattern split-char) 2816 "Split PATTERN at SPLIT-CHAR while respecting braces and brackets." 2817 (when pattern 2818 (let ((segments nil) 2819 (in-braces nil) 2820 (in-brackets nil) 2821 (current-segment "")) 2822 (dolist (char (string-to-list pattern)) 2823 (cl-block 'exit-point 2824 (if (eq char split-char) 2825 (when (and (null in-braces) 2826 (null in-brackets)) 2827 (push current-segment segments) 2828 (setq current-segment "") 2829 (cl-return-from 'exit-point)) 2830 (pcase char 2831 (?{ 2832 (setq in-braces t)) 2833 (?} 2834 (setq in-braces nil)) 2835 (?\[ 2836 (setq in-brackets t)) 2837 (?\] 2838 (setq in-brackets nil)))) 2839 (setq current-segment (concat current-segment 2840 (char-to-string char))))) 2841 (unless (string-empty-p current-segment) 2842 (push current-segment segments)) 2843 (nreverse segments)))) 2844 2845 (defun lsp--glob-to-regexp (pattern) 2846 "Helper function to convert a PATTERN from LSP's glob syntax to 2847 an Elisp regexp." 2848 (if (string-empty-p pattern) 2849 "" 2850 (let ((current-regexp "") 2851 (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split))) 2852 (if (-all? (lambda (segment) (eq segment lsp-globstar)) 2853 glob-segments) 2854 ".*" 2855 (let ((prev-segment-was-globstar nil)) 2856 (seq-do-indexed 2857 (lambda (segment index) 2858 (if (string-equal segment lsp-globstar) 2859 (unless prev-segment-was-globstar 2860 (setq current-regexp (concat current-regexp 2861 lsp-globstar-regexp)) 2862 (setq prev-segment-was-globstar t)) 2863 (let ((in-braces nil) 2864 (brace-val "") 2865 (in-brackets nil) 2866 (bracket-val "")) 2867 (dolist (char (string-to-list segment)) 2868 (cond 2869 ((and (not (char-equal char ?\})) 2870 in-braces) 2871 (setq brace-val (concat brace-val 2872 (char-to-string char)))) 2873 ((and in-brackets 2874 (or (not (char-equal char ?\])) 2875 (string-empty-p bracket-val))) 2876 (let ((curr (cond 2877 ((char-equal char ?-) 2878 "-") 2879 ;; NOTE: ?\^ and ?^ are different characters 2880 ((and (memq char '(?^ ?!)) 2881 (string-empty-p bracket-val)) 2882 "^") 2883 ((char-equal char lsp-glob-split) 2884 "") 2885 (t 2886 (regexp-quote (char-to-string char)))))) 2887 (setq bracket-val (concat bracket-val curr)))) 2888 (t 2889 (cl-case char 2890 (?{ 2891 (setq in-braces t)) 2892 (?\[ 2893 (setq in-brackets t)) 2894 (?} 2895 (let* ((choices (lsp-split-glob-pattern brace-val ?\,)) 2896 (brace-regexp (concat "\\(?:" 2897 (mapconcat #'lsp--glob-to-regexp choices "\\|") 2898 "\\)"))) 2899 (setq current-regexp (concat current-regexp 2900 brace-regexp)) 2901 (setq in-braces nil) 2902 (setq brace-val ""))) 2903 (?\] 2904 (setq current-regexp 2905 (concat current-regexp 2906 "[" bracket-val "]")) 2907 (setq in-brackets nil) 2908 (setq bracket-val "")) 2909 (?? 2910 (setq current-regexp 2911 (concat current-regexp 2912 lsp-non-path-regexp))) 2913 (?* 2914 (setq current-regexp 2915 (concat current-regexp 2916 lsp-non-path-regexp "*?"))) 2917 (t 2918 (setq current-regexp 2919 (concat current-regexp 2920 (regexp-quote (char-to-string char))))))))) 2921 (when (and (< index (1- (length glob-segments))) 2922 (or (not (string-equal (nth (1+ index) glob-segments) 2923 lsp-globstar)) 2924 (< (+ index 2) 2925 (length glob-segments)))) 2926 (setq current-regexp 2927 (concat current-regexp 2928 lsp-path-regexp))) 2929 (setq prev-segment-was-globstar nil)))) 2930 glob-segments) 2931 current-regexp))))) 2932 2933 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365 2934 (defun lsp-glob-unbrace-at-top-level (glob-pattern) 2935 "If GLOB-PATTERN does not start with a brace, return a singleton list 2936 containing GLOB-PATTERN. 2937 2938 If GLOB-PATTERN does start with a brace, return a list of the 2939 comma-separated globs within the top-level braces." 2940 (if (not (string-prefix-p "{" glob-pattern)) 2941 (list glob-pattern) 2942 (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,))) 2943 2944 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern) 2945 "Convert GLOB-PATTERN to a regexp wrapped with the beginning- 2946 and end-of-string meta-characters." 2947 (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'")) 2948 2949 (defun lsp-glob-to-regexps (glob-pattern) 2950 "Convert a GLOB-PATTERN to a list of Elisp regexps." 2951 (when-let* 2952 ((glob-pattern (cond ((hash-table-p glob-pattern) 2953 (ht-get glob-pattern "pattern")) 2954 ((stringp glob-pattern) glob-pattern) 2955 (t (error "Unknown glob-pattern type: %s" glob-pattern)))) 2956 (trimmed-pattern (string-trim glob-pattern)) 2957 (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern))) 2958 (seq-map #'lsp-glob-convert-to-wrapped-regexp 2959 top-level-unbraced-patterns))) 2960 2961 2962 2963 (defvar lsp-mode-menu) 2964 2965 (defun lsp-mouse-click (event) 2966 (interactive "e") 2967 (let* ((ec (event-start event)) 2968 (choice (x-popup-menu event lsp-mode-menu)) 2969 (action (lookup-key lsp-mode-menu (apply 'vector choice)))) 2970 2971 (select-window (posn-window ec)) 2972 2973 (unless (and (region-active-p) (eq action 'lsp-execute-code-action)) 2974 (goto-char (posn-point ec))) 2975 (run-with-idle-timer 2976 0.001 nil 2977 (lambda () 2978 (cl-labels ((check (value) (not (null value)))) 2979 (when choice 2980 (call-interactively action))))))) 2981 2982 (defvar lsp-mode-map 2983 (let ((map (make-sparse-keymap))) 2984 (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse) 2985 (define-key map (kbd "C-<mouse-1>") #'ignore) 2986 (define-key map (kbd "<mouse-3>") #'lsp-mouse-click) 2987 (define-key map (kbd "C-S-SPC") #'lsp-signature-activate) 2988 (when lsp-keymap-prefix 2989 (define-key map (kbd lsp-keymap-prefix) lsp-command-map)) 2990 map) 2991 "Keymap for `lsp-mode'.") 2992 2993 (define-minor-mode lsp-mode "Mode for LSP interaction." 2994 :keymap lsp-mode-map 2995 :lighter 2996 (" LSP[" 2997 (lsp--buffer-workspaces 2998 (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "][")) 2999 (:propertize "Disconnected" face warning)) 3000 "]") 3001 :group 'lsp-mode 3002 (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred)) 3003 ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp' 3004 (lsp))) 3005 3006 (defvar lsp-mode-menu 3007 (easy-menu-create-menu 3008 nil 3009 `(["Go to definition" lsp-find-definition 3010 :active (lsp-feature? "textDocument/definition")] 3011 ["Find references" lsp-find-references 3012 :active (lsp-feature? "textDocument/references")] 3013 ["Find implementations" lsp-find-implementation 3014 :active (lsp-feature? "textDocument/implementation")] 3015 ["Find declarations" lsp-find-declaration 3016 :active (lsp-feature? "textDocument/declaration")] 3017 ["Go to type declaration" lsp-find-type-definition 3018 :active (lsp-feature? "textDocument/typeDefinition")] 3019 "--" 3020 ["Describe" lsp-describe-thing-at-point] 3021 ["Code action" lsp-execute-code-action] 3022 ["Format" lsp-format-buffer] 3023 ["Highlight references" lsp-document-highlight] 3024 ["Type Hierarchy" lsp-java-type-hierarchy 3025 :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")] 3026 ["Type Hierarchy" lsp-treemacs-type-hierarchy 3027 :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")) 3028 (functionp 'lsp-treemacs-type-hierarchy) 3029 (lsp-feature? "textDocument/typeHierarchy"))] 3030 ["Call Hierarchy" lsp-treemacs-call-hierarchy 3031 :visible (and (functionp 'lsp-treemacs-call-hierarchy) 3032 (lsp-feature? "textDocument/callHierarchy"))] 3033 ["Rename" lsp-rename 3034 :active (lsp-feature? "textDocument/rename")] 3035 "--" 3036 ("Session" 3037 ["View logs" lsp-workspace-show-log] 3038 ["Describe" lsp-describe-session] 3039 ["Shutdown" lsp-shutdown-workspace] 3040 ["Restart" lsp-restart-workspace]) 3041 ("Workspace Folders" 3042 ["Add" lsp-workspace-folders-add] 3043 ["Remove" lsp-workspace-folders-remove] 3044 ["Open" lsp-workspace-folders-open]) 3045 ("Toggle features" 3046 ["Lenses" lsp-lens-mode] 3047 ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode] 3048 ["Modeline code actions" lsp-modeline-code-actions-mode] 3049 ["Modeline diagnostics" lsp-modeline-diagnostics-mode]) 3050 "---" 3051 ("Debug" 3052 :active (bound-and-true-p dap-ui-mode) 3053 :filter ,(lambda (_) 3054 (and (boundp 'dap-ui-menu-items) 3055 (nthcdr 3 dap-ui-menu-items)))))) 3056 "Menu for lsp-mode.") 3057 3058 (defalias 'make-lsp-client 'make-lsp--client) 3059 3060 (cl-defstruct lsp--registered-capability 3061 (id "") 3062 (method " ") 3063 (options nil)) 3064 3065 ;; A ‘lsp--workspace’ object represents exactly one language server process. 3066 (cl-defstruct lsp--workspace 3067 ;; the `ewoc' object for displaying I/O to and from the server 3068 (ewoc nil) 3069 3070 ;; ‘server-capabilities’ is a hash table of the language server capabilities. 3071 ;; It is the hash table representation of a LSP ServerCapabilities structure; 3072 ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize. 3073 (server-capabilities nil) 3074 3075 ;; ‘registered-server-capabilities’ is a list of hash tables that represent 3076 ;; dynamically-registered Registration objects. See 3077 ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability. 3078 (registered-server-capabilities nil) 3079 3080 ;; ‘root’ is a directory name or a directory file name for the workspace 3081 ;; root. ‘lsp-mode’ passes this directory to the ‘initialize’ method of the 3082 ;; language server; see 3083 ;; https://microsoft.github.io/language-server-protocol/specification#initialize. 3084 (root nil) 3085 3086 ;; ‘client’ is the ‘lsp--client’ object associated with this workspace. 3087 (client nil) 3088 3089 ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It 3090 ;; used to derive the file path in `lsp--uri-to-path' when using tramp 3091 ;; connection. 3092 (host-root nil) 3093 3094 ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or 3095 ;; a network connection. ‘lsp-mode’ communicates with ‘proc’ using the 3096 ;; language server protocol. ‘proc’ corresponds to the COMMUNICATION-PROCESS 3097 ;; element of the return value of the client’s ‘get-root’ field, which see. 3098 (proc nil) 3099 3100 ;; ‘proc’ is a process object; it must represent a regular process, not a 3101 ;; pipe or network process. It represents the actual server process that 3102 ;; corresponds to this workspace. ‘cmd-proc’ corresponds to the 3103 ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’ 3104 ;; field, which see. 3105 (cmd-proc nil) 3106 3107 ;; ‘buffers’ is a list of buffers associated with this workspace. 3108 (buffers nil) 3109 3110 ;; if semantic tokens is enabled, `semantic-tokens-faces' contains 3111 ;; one face (or nil) for each token type supported by the language server. 3112 (semantic-tokens-faces nil) 3113 3114 ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces' 3115 ;; contains one face (or nil) for each modifier type supported by the language 3116 ;; server 3117 (semantic-tokens-modifier-faces nil) 3118 3119 ;; Extra client capabilities provided by third-party packages using 3120 ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME 3121 ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name, 3122 ;; and CAPS is either a plist of the client capabilities, or a function that 3123 ;; takes no argument and returns a plist of the client capabilities or nil. 3124 (extra-client-capabilities nil) 3125 3126 ;; Workspace status 3127 (status nil) 3128 3129 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3130 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3131 (metadata (make-hash-table :test 'equal)) 3132 3133 ;; contains all the file notification watches that have been created for the 3134 ;; current workspace in format filePath->file notification handle. 3135 (watches (make-hash-table :test 'equal)) 3136 3137 ;; list of workspace folders 3138 (workspace-folders nil) 3139 3140 ;; ‘last-id’ the last request id for the current workspace. 3141 (last-id 0) 3142 3143 ;; ‘status-string’ allows extensions to specify custom status string based on 3144 ;; the Language Server specific messages. 3145 (status-string nil) 3146 3147 ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it 3148 ;; was stopped). 3149 shutdown-action 3150 3151 ;; ‘diagnostics’ a hashmap with workspace diagnostics. 3152 (diagnostics (make-hash-table :test 'equal)) 3153 3154 ;; contains all the workDone progress tokens that have been created 3155 ;; for the current workspace. 3156 (work-done-tokens (make-hash-table :test 'equal))) 3157 3158 3159 (cl-defstruct lsp-session 3160 ;; contains the folders that are part of the current session 3161 folders 3162 ;; contains the folders that must not be imported in the current workspace. 3163 folders-blocklist 3164 ;; contains the list of folders that must be imported in a project in case of 3165 ;; multi root LSP server. 3166 (server-id->folders (make-hash-table :test 'equal)) 3167 ;; folder to list of the servers that are associated with the folder. 3168 (folder->servers (make-hash-table :test 'equal)) 3169 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3170 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3171 (metadata (make-hash-table :test 'equal))) 3172 3173 (defun lsp-workspace-status (status-string &optional workspace) 3174 "Set current workspace status to STATUS-STRING. 3175 If WORKSPACE is not specified defaults to lsp--cur-workspace." 3176 (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string)))) 3177 (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string))) 3178 3179 (defun lsp-session-set-metadata (key value &optional _workspace) 3180 "Associate KEY with VALUE in the WORKSPACE metadata. 3181 If WORKSPACE is not provided current workspace will be used." 3182 (puthash key value (lsp-session-metadata (lsp-session)))) 3183 3184 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata) 3185 3186 (defun lsp-session-get-metadata (key &optional _workspace) 3187 "Lookup KEY in WORKSPACE metadata. 3188 If WORKSPACE is not provided current workspace will be used." 3189 (gethash key (lsp-session-metadata (lsp-session)))) 3190 3191 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata) 3192 3193 (defun lsp-workspace-set-work-done-token (token value workspace) 3194 "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens." 3195 (puthash token value (lsp--workspace-work-done-tokens workspace))) 3196 3197 (defun lsp-workspace-get-work-done-token (token workspace) 3198 "Lookup TOKEN in the WORKSPACE work-done-tokens." 3199 (gethash token (lsp--workspace-work-done-tokens workspace))) 3200 3201 (defun lsp-workspace-rem-work-done-token (token workspace) 3202 "Remove TOKEN from the WORKSPACE work-done-tokens." 3203 (remhash token (lsp--workspace-work-done-tokens workspace))) 3204 3205 3206 (defun lsp--make-notification (method &optional params) 3207 "Create notification body for method METHOD and parameters PARAMS." 3208 (list :jsonrpc "2.0" :method method :params params)) 3209 3210 (defalias 'lsp--make-request 'lsp--make-notification) 3211 (defalias 'lsp-make-request 'lsp--make-notification) 3212 3213 (defun lsp--make-response (id result) 3214 "Create response for REQUEST with RESULT." 3215 `(:jsonrpc "2.0" :id ,id :result ,result)) 3216 3217 (defun lsp-make-notification (method &optional params) 3218 "Create notification body for method METHOD and parameters PARAMS." 3219 (lsp--make-notification method params)) 3220 3221 (defmacro lsp--json-serialize (params) 3222 (if (progn 3223 (require 'json) 3224 (fboundp 'json-serialize)) 3225 `(json-serialize ,params 3226 :null-object nil 3227 :false-object :json-false) 3228 `(let ((json-false :json-false)) 3229 (json-encode ,params)))) 3230 3231 (defun lsp--make-message (params) 3232 "Create a LSP message from PARAMS, after encoding it to a JSON string." 3233 (let ((body (lsp--json-serialize params))) 3234 (concat "Content-Length: " 3235 (number-to-string (1+ (string-bytes body))) 3236 "\r\n\r\n" 3237 body 3238 "\n"))) 3239 3240 (cl-defstruct lsp--log-entry timestamp process-time type method id body) 3241 3242 (defun lsp--make-log-entry (method id body type &optional process-time) 3243 "Create an outgoing log object from BODY with method METHOD and id ID. 3244 If ID is non-nil, then the body is assumed to be a notification. 3245 TYPE can either be `incoming' or `outgoing'" 3246 (cl-assert (memq type '(incoming-req outgoing-req incoming-notif 3247 outgoing-notif incoming-resp 3248 outgoing-resp))) 3249 (make-lsp--log-entry 3250 :timestamp (format-time-string "%I:%M:%S %p") 3251 :process-time process-time 3252 :method method 3253 :id id 3254 :type type 3255 :body body)) 3256 3257 (defun lsp--log-font-lock-json (body) 3258 "Font lock JSON BODY." 3259 (with-temp-buffer 3260 (insert body) 3261 ;; We set the temp buffer file-name extension to .json and call `set-auto-mode' 3262 ;; so the users configured json mode is used which could be 3263 ;; `json-mode', `json-ts-mode', `jsonian-mode', etc. 3264 (let ((buffer-file-name "lsp-log.json")) 3265 (delay-mode-hooks 3266 (set-auto-mode) 3267 (if (fboundp 'font-lock-ensure) 3268 (font-lock-ensure) 3269 (with-no-warnings 3270 (font-lock-fontify-buffer))))) 3271 (buffer-string))) 3272 3273 (defun lsp--log-entry-pp (entry) 3274 (cl-assert (lsp--log-entry-p entry)) 3275 (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time 3276 body) 3277 entry) 3278 (json-false :json-false) 3279 (json-encoding-pretty-print t) 3280 (str nil)) 3281 (setq str 3282 (concat (format "[Trace - %s] " timestamp) 3283 (pcase type 3284 ('incoming-req (format "Received request '%s - (%s)." method id)) 3285 ('outgoing-req (format "Sending request '%s - (%s)'." method id)) 3286 3287 ('incoming-notif (format "Received notification '%s'." method)) 3288 ('outgoing-notif (format "Sending notification '%s'." method)) 3289 3290 ('incoming-resp (format "Received response '%s - (%s)' in %dms." 3291 method id process-time)) 3292 ('outgoing-resp 3293 (format 3294 "Sending response '%s - (%s)'. Processing request took %dms" 3295 method id process-time))) 3296 "\n" 3297 (if (memq type '(incoming-resp ougoing-resp)) 3298 "Result: " 3299 "Params: ") 3300 (lsp--log-font-lock-json (json-encode body)) 3301 "\n\n\n")) 3302 (setq str (propertize str 'mouse-face 'highlight 'read-only t)) 3303 (insert str))) 3304 3305 (defvar-local lsp--log-io-ewoc nil) 3306 3307 (defun lsp--get-create-io-ewoc (workspace) 3308 (if (and (lsp--workspace-ewoc workspace) 3309 (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace)))) 3310 (lsp--workspace-ewoc workspace) 3311 (with-current-buffer (lsp--get-log-buffer-create workspace) 3312 (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode)) 3313 (setq-local window-point-insertion-type t) 3314 (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t)) 3315 (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc)) 3316 (lsp--workspace-ewoc workspace))) 3317 3318 (defun lsp--ewoc-count (ewoc) 3319 (let* ((count 0) 3320 (count-fn (lambda (_) (setq count (1+ count))))) 3321 (ewoc-map count-fn ewoc) 3322 count)) 3323 3324 (defun lsp--log-entry-new (entry workspace) 3325 (let* ((ewoc (lsp--get-create-io-ewoc workspace)) 3326 (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc))) 3327 (node (if (or (eq lsp-io-messages-max t) 3328 (>= lsp-io-messages-max count)) 3329 nil 3330 (ewoc-nth ewoc (1- lsp-io-messages-max)))) 3331 (prev nil) 3332 (inhibit-read-only t)) 3333 (while node 3334 (setq prev (ewoc-prev ewoc node)) 3335 (ewoc-delete ewoc node) 3336 (setq node prev)) 3337 (ewoc-enter-last ewoc entry))) 3338 3339 (defun lsp--send-notification (body) 3340 "Send BODY as a notification to the language server." 3341 (lsp-foreach-workspace 3342 (when (lsp--log-io-p (plist-get body :method)) 3343 (lsp--log-entry-new (lsp--make-log-entry 3344 (plist-get body :method) 3345 nil (plist-get body :params) 'outgoing-notif) 3346 lsp--cur-workspace)) 3347 (lsp--send-no-wait body 3348 (lsp--workspace-proc lsp--cur-workspace)))) 3349 3350 (defalias 'lsp-send-notification 'lsp--send-notification) 3351 3352 (defun lsp-notify (method params) 3353 "Send notification METHOD with PARAMS." 3354 (lsp--send-notification (lsp--make-notification method params))) 3355 3356 (defun lsp--cur-workspace-check () 3357 "Check whether buffer lsp workspace(s) are set." 3358 (cl-assert (lsp-workspaces) nil 3359 "No language server(s) is associated with this buffer.")) 3360 3361 (defun lsp--send-request (body &optional no-wait no-merge) 3362 "Send BODY as a request to the language server, get the response. 3363 If NO-WAIT is non-nil, don't synchronously wait for a response. 3364 If NO-MERGE is non-nil, don't merge the results but return an 3365 alist mapping workspace->result." 3366 (lsp-request (plist-get body :method) 3367 (plist-get body :params) 3368 :no-wait no-wait 3369 :no-merge no-merge)) 3370 3371 (defalias 'lsp-send-request 'lsp--send-request 3372 "Send BODY as a request to the language server and return the response 3373 synchronously. 3374 \n(fn BODY)") 3375 3376 (cl-defun lsp-request (method params &key no-wait no-merge) 3377 "Send request METHOD with PARAMS. 3378 If NO-MERGE is non-nil, don't merge the results but return alist 3379 workspace->result. 3380 If NO-WAIT is non-nil send the request as notification." 3381 (if no-wait 3382 (lsp-notify method params) 3383 (let* ((send-time (float-time)) 3384 ;; max time by which we must get a response 3385 (expected-time 3386 (and 3387 lsp-response-timeout 3388 (+ send-time lsp-response-timeout))) 3389 resp-result resp-error done?) 3390 (unwind-protect 3391 (progn 3392 (lsp-request-async method params 3393 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3394 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3395 :no-merge no-merge 3396 :mode 'detached 3397 :cancel-token :sync-request) 3398 (while (not (or resp-error resp-result)) 3399 (if (functionp 'json-rpc-connection) 3400 (catch 'lsp-done (sit-for 0.01)) 3401 (catch 'lsp-done 3402 (accept-process-output 3403 nil 3404 (if expected-time (- expected-time send-time) 1)))) 3405 (setq send-time (float-time)) 3406 (when (and expected-time (< expected-time send-time)) 3407 (error "Timeout while waiting for response. Method: %s" method))) 3408 (setq done? t) 3409 (cond 3410 ((eq resp-result :finished) nil) 3411 (resp-result resp-result) 3412 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3413 ((lsp-json-error? (cl-first resp-error)) 3414 (error (lsp:json-error-message (cl-first resp-error)))))) 3415 (unless done? 3416 (lsp-cancel-request-by-token :sync-request)))))) 3417 3418 (cl-defun lsp-request-while-no-input (method params) 3419 "Send request METHOD with PARAMS and waits until there is no input. 3420 Return same value as `lsp--while-no-input' and respecting `non-essential'." 3421 (if (or non-essential (not lsp-request-while-no-input-may-block)) 3422 (let* ((send-time (float-time)) 3423 ;; max time by which we must get a response 3424 (expected-time 3425 (and 3426 lsp-response-timeout 3427 (+ send-time lsp-response-timeout))) 3428 resp-result resp-error done?) 3429 (unwind-protect 3430 (progn 3431 (lsp-request-async method params 3432 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3433 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3434 :mode 'detached 3435 :cancel-token :sync-request) 3436 (while (not (or resp-error resp-result (input-pending-p))) 3437 (catch 'lsp-done 3438 (sit-for 3439 (if expected-time (- expected-time send-time) 1))) 3440 (setq send-time (float-time)) 3441 (when (and expected-time (< expected-time send-time)) 3442 (error "Timeout while waiting for response. Method: %s" method))) 3443 (setq done? (or resp-error resp-result)) 3444 (cond 3445 ((eq resp-result :finished) nil) 3446 (resp-result resp-result) 3447 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3448 ((lsp-json-error? (cl-first resp-error)) 3449 (error (lsp:json-error-message (cl-first resp-error)))))) 3450 (unless done? 3451 (lsp-cancel-request-by-token :sync-request)) 3452 (when (and (input-pending-p) lsp--throw-on-input) 3453 (throw 'input :interrupted)))) 3454 (lsp-request method params))) 3455 3456 (defvar lsp--cancelable-requests (ht)) 3457 3458 (cl-defun lsp-request-async (method params callback 3459 &key mode error-handler cancel-handler no-merge cancel-token) 3460 "Send METHOD with PARAMS as a request to the language server. 3461 Call CALLBACK with the response received from the server 3462 asynchronously. 3463 MODE determines when the callback will be called depending on the 3464 condition of the original buffer. It could be: 3465 - `detached' which means that the callback will be executed no 3466 matter what has happened to the buffer. 3467 - `alive' - the callback will be executed only if the buffer from 3468 which the call was executed is still alive. 3469 - `current' the callback will be executed only if the original buffer 3470 is still selected. 3471 - `tick' - the callback will be executed only if the buffer was not modified. 3472 - `unchanged' - the callback will be executed only if the buffer hasn't 3473 changed and if the buffer is not modified. 3474 3475 ERROR-HANDLER will be called in case the request has failed. 3476 CANCEL-HANDLER will be called in case the request is being canceled. 3477 If NO-MERGE is non-nil, don't merge the results but return alist 3478 workspace->result. 3479 CANCEL-TOKEN is the token that can be used to cancel request." 3480 (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params) 3481 callback mode error-handler cancel-handler no-merge cancel-token)) 3482 3483 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback) 3484 (lambda (&rest _) 3485 (unless (and (equal 'post-command-hook hook) 3486 (equal (current-buffer) buf)) 3487 (lsp--request-cleanup-hooks id) 3488 (with-lsp-workspaces workspaces 3489 (lsp--cancel-request id) 3490 (when cancel-callback (funcall cancel-callback))) 3491 (lsp-log "Cancelling %s(%s) in hook %s" method id hook)))) 3492 3493 (defun lsp--create-async-callback 3494 (callback method no-merge workspaces) 3495 "Create async handler expecting COUNT results, merge them and call CALLBACK. 3496 MODE determines when the callback will be called depending on the 3497 condition of the original buffer. METHOD is the invoked method. 3498 If NO-MERGE is non-nil, don't merge the results but return alist 3499 workspace->result. ID is the request id." 3500 (let (results errors) 3501 (lambda (result) 3502 (push (cons lsp--cur-workspace result) 3503 (if (eq result :error) errors results)) 3504 (when (and (not (eq (length errors) (length workspaces))) 3505 (eq (+ (length errors) (length results)) (length workspaces))) 3506 (funcall callback 3507 (if no-merge 3508 results 3509 (lsp--merge-results (-map #'cl-rest results) method))))))) 3510 3511 (defcustom lsp-default-create-error-handler-fn nil 3512 "Default error handler customization. 3513 Handler should give METHOD as argument and return function of one argument 3514 ERROR." 3515 :type 'function 3516 :group 'lsp-mode 3517 :package-version '(lsp-mode . "9.0.0")) 3518 3519 (defun lsp--create-default-error-handler (method) 3520 "Default error handler. 3521 METHOD is the executed method." 3522 (if lsp-default-create-error-handler-fn 3523 (funcall lsp-default-create-error-handler-fn method) 3524 (lambda (error) 3525 (lsp--warn "%s" (or (lsp--error-string error) 3526 (format "%s Request has failed" method)))))) 3527 3528 (defvar lsp--request-cleanup-hooks (ht)) 3529 3530 (defun lsp--request-cleanup-hooks (request-id) 3531 (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks))) 3532 (funcall cleanup-function) 3533 (remhash request-id lsp--request-cleanup-hooks))) 3534 3535 (defun lsp-cancel-request-by-token (cancel-token) 3536 "Cancel request using CANCEL-TOKEN." 3537 (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests)) 3538 (with-lsp-workspaces workspaces 3539 (lsp--cancel-request request-id)) 3540 (remhash cancel-token lsp--cancelable-requests) 3541 (lsp--request-cleanup-hooks request-id))) 3542 3543 (defun lsp--send-request-async (body callback 3544 &optional mode error-callback cancel-callback 3545 no-merge cancel-token) 3546 "Send BODY as a request to the language server. 3547 Call CALLBACK with the response received from the server 3548 asynchronously. 3549 MODE determines when the callback will be called depending on the 3550 condition of the original buffer. It could be: 3551 - `detached' which means that the callback will be executed no 3552 matter what has happened to the buffer. 3553 - `alive' - the callback will be executed only if the buffer from 3554 which the call was executed is still alive. 3555 - `current' the callback will be executed only if the original buffer 3556 is still selected. 3557 - `tick' - the callback will be executed only if the buffer was not modified. 3558 - `unchanged' - the callback will be executed only if the buffer hasn't 3559 changed and if the buffer is not modified. 3560 3561 ERROR-CALLBACK will be called in case the request has failed. 3562 CANCEL-CALLBACK will be called in case the request is being canceled. 3563 If NO-MERGE is non-nil, don't merge the results but return alist 3564 workspace->result. 3565 CANCEL-TOKEN is the token that can be used to cancel request." 3566 (when cancel-token 3567 (lsp-cancel-request-by-token cancel-token)) 3568 3569 (if-let ((target-workspaces (lsp--find-workspaces-for body))) 3570 (let* ((start-time (current-time)) 3571 (method (plist-get body :method)) 3572 (id (cl-incf lsp-last-id)) 3573 (buf (current-buffer)) 3574 (cancel-callback (when cancel-callback 3575 (pcase mode 3576 ((or 'alive 'tick 'unchanged) 3577 (lambda () 3578 (with-current-buffer buf 3579 (funcall cancel-callback)))) 3580 (_ cancel-callback)))) 3581 ;; calculate what are the (hook . local) pairs which will cancel 3582 ;; the request 3583 (hooks (pcase mode 3584 ('alive '((kill-buffer-hook . t))) 3585 ('tick '((kill-buffer-hook . t) (after-change-functions . t))) 3586 ('unchanged '((after-change-functions . t) (post-command-hook . nil))) 3587 ('current '((post-command-hook . nil))))) 3588 ;; note: lambdas in emacs can be compared but we should make sure 3589 ;; that all of the captured arguments are the same - in our case 3590 ;; `lsp--create-request-cancel' will return the same lambda when 3591 ;; called with the same params. 3592 (cleanup-hooks 3593 (lambda () (mapc 3594 (-lambda ((hook . local)) 3595 (if local 3596 (when (buffer-live-p buf) 3597 (with-current-buffer buf 3598 (remove-hook hook 3599 (lsp--create-request-cancel 3600 id target-workspaces hook buf method cancel-callback) 3601 t))) 3602 (remove-hook hook (lsp--create-request-cancel 3603 id target-workspaces hook buf method cancel-callback)))) 3604 hooks) 3605 (remhash cancel-token lsp--cancelable-requests))) 3606 (callback (pcase mode 3607 ((or 'alive 'tick 'unchanged) (lambda (&rest args) 3608 (with-current-buffer buf 3609 (apply callback args)))) 3610 (_ callback))) 3611 (callback (lsp--create-async-callback callback 3612 method 3613 no-merge 3614 target-workspaces)) 3615 (callback (lambda (result) 3616 (lsp--request-cleanup-hooks id) 3617 (funcall callback result))) 3618 (error-callback (lsp--create-async-callback 3619 (or error-callback 3620 (lsp--create-default-error-handler method)) 3621 method 3622 nil 3623 target-workspaces)) 3624 (error-callback (lambda (error) 3625 (funcall callback :error) 3626 (lsp--request-cleanup-hooks id) 3627 (funcall error-callback error))) 3628 (body (plist-put body :id id))) 3629 3630 ;; cancel request in any of the hooks 3631 (mapc (-lambda ((hook . local)) 3632 (add-hook hook 3633 (lsp--create-request-cancel 3634 id target-workspaces hook buf method cancel-callback) 3635 nil local)) 3636 hooks) 3637 (puthash id cleanup-hooks lsp--request-cleanup-hooks) 3638 3639 (setq lsp--last-active-workspaces target-workspaces) 3640 3641 (when cancel-token 3642 (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests)) 3643 3644 (seq-doseq (workspace target-workspaces) 3645 (when (lsp--log-io-p method) 3646 (lsp--log-entry-new (lsp--make-log-entry method id 3647 (plist-get body :params) 3648 'outgoing-req) 3649 workspace)) 3650 (puthash id 3651 (list callback error-callback method start-time (current-time)) 3652 (-> workspace 3653 (lsp--workspace-client) 3654 (lsp--client-response-handlers))) 3655 (lsp--send-no-wait body (lsp--workspace-proc workspace))) 3656 body) 3657 (error "The connected server(s) does not support method %s. 3658 To find out what capabilities support your server use `M-x lsp-describe-session' 3659 and expand the capabilities section" 3660 (plist-get body :method)))) 3661 3662 ;; deprecated, use lsp-request-async. 3663 (defalias 'lsp-send-request-async 'lsp--send-request-async) 3664 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1") 3665 3666 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any 3667 ;; pending language servers. 3668 (add-hook 'kill-emacs-hook #'lsp--global-teardown) 3669 3670 (defun lsp--global-teardown () 3671 "Unload working workspaces." 3672 (lsp-foreach-workspace (lsp--shutdown-workspace))) 3673 3674 (defun lsp--shutdown-workspace (&optional restart) 3675 "Shut down the language server process for ‘lsp--cur-workspace’." 3676 (with-demoted-errors "LSP error: %S" 3677 (let ((lsp-response-timeout 0.5)) 3678 (condition-case err 3679 (lsp-request "shutdown" nil) 3680 (error (lsp--error "%s" err)))) 3681 (lsp-notify "exit" nil)) 3682 (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown)) 3683 (lsp--uninitialize-workspace)) 3684 3685 (defcustom lsp-inlay-hint-enable nil 3686 "If non-nil it will enable inlay hints." 3687 :type 'boolean 3688 :group 'lsp-mode 3689 :package-version '(lsp-mode . "9.0.0")) 3690 3691 (defun lsp--uninitialize-workspace () 3692 "Cleanup buffer state. 3693 When a workspace is shut down, by request or from just 3694 disappearing, unset all the variables related to it." 3695 (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace] 3696 (lsp-process-kill cmd-proc) 3697 (mapc (lambda (buf) 3698 (when (lsp-buffer-live-p buf) 3699 (lsp-with-current-buffer buf 3700 (lsp-managed-mode -1)))) 3701 buffers) 3702 (lsp-diagnostics--workspace-cleanup lsp--cur-workspace))) 3703 3704 (defun lsp--client-capabilities (&optional custom-capabilities) 3705 "Return the client capabilities appending CUSTOM-CAPABILITIES." 3706 (append 3707 `((general . ((positionEncodings . ["utf-32", "utf-16"]))) 3708 (workspace . ((workspaceEdit . ((documentChanges . t) 3709 (resourceOperations . ["create" "rename" "delete"]))) 3710 (applyEdit . t) 3711 (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))))) 3712 (executeCommand . ((dynamicRegistration . :json-false))) 3713 ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t))))) 3714 (workspaceFolders . t) 3715 (configuration . t) 3716 ,@(when lsp-semantic-tokens-enable 3717 `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests) 3718 lsp-semantic-tokens-honor-refresh-requests) 3719 :json-false)))))) 3720 ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t))))) 3721 ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false))))) 3722 (diagnostics . ((refreshSupport . :json-false))) 3723 (fileOperations . ((didCreate . :json-false) 3724 (willCreate . :json-false) 3725 (didRename . t) 3726 (willRename . t) 3727 (didDelete . :json-false) 3728 (willDelete . :json-false))))) 3729 (textDocument . ((declaration . ((dynamicRegistration . t) 3730 (linkSupport . t))) 3731 (definition . ((dynamicRegistration . t) 3732 (linkSupport . t))) 3733 (references . ((dynamicRegistration . t))) 3734 (implementation . ((dynamicRegistration . t) 3735 (linkSupport . t))) 3736 (typeDefinition . ((dynamicRegistration . t) 3737 (linkSupport . t))) 3738 (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t))) 3739 (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))) 3740 (hierarchicalDocumentSymbolSupport . t))) 3741 (formatting . ((dynamicRegistration . t))) 3742 (rangeFormatting . ((dynamicRegistration . t))) 3743 (onTypeFormatting . ((dynamicRegistration . t))) 3744 ,@(when (and lsp-semantic-tokens-enable 3745 (functionp 'lsp--semantic-tokens-capabilities)) 3746 (lsp--semantic-tokens-capabilities)) 3747 (rename . ((dynamicRegistration . t) (prepareSupport . t))) 3748 (codeAction . ((dynamicRegistration . t) 3749 (isPreferredSupport . t) 3750 (codeActionLiteralSupport . ((codeActionKind . ((valueSet . ["" 3751 "quickfix" 3752 "refactor" 3753 "refactor.extract" 3754 "refactor.inline" 3755 "refactor.rewrite" 3756 "source" 3757 "source.organizeImports"]))))) 3758 (resolveSupport . ((properties . ["edit" "command"]))) 3759 (dataSupport . t))) 3760 (completion . ((completionItem . ((snippetSupport . ,(cond 3761 ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode))) 3762 (lsp--warn (concat 3763 "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. " 3764 "You must either install yasnippet, or disable snippet support.")) 3765 :json-false) 3766 (lsp-enable-snippet t) 3767 (t :json-false))) 3768 (documentationFormat . ["markdown" "plaintext"]) 3769 ;; Remove this after jdtls support resolveSupport 3770 (resolveAdditionalTextEditsSupport . t) 3771 (insertReplaceSupport . t) 3772 (deprecatedSupport . t) 3773 (resolveSupport 3774 . ((properties . ["documentation" 3775 "detail" 3776 "additionalTextEdits" 3777 "command" 3778 "insertTextFormat" 3779 "insertTextMode"]))) 3780 (insertTextModeSupport . ((valueSet . [1 2]))))) 3781 (contextSupport . t) 3782 (dynamicRegistration . t))) 3783 (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t))))) 3784 (dynamicRegistration . t))) 3785 (documentLink . ((dynamicRegistration . t) 3786 (tooltipSupport . t))) 3787 (hover . ((contentFormat . ["markdown" "plaintext"]) 3788 (dynamicRegistration . t))) 3789 ,@(when lsp-enable-folding 3790 `((foldingRange . ((dynamicRegistration . t) 3791 ,@(when lsp-folding-range-limit 3792 `((rangeLimit . ,lsp-folding-range-limit))) 3793 ,@(when lsp-folding-line-folding-only 3794 `((lineFoldingOnly . t))))))) 3795 (selectionRange . ((dynamicRegistration . t))) 3796 (callHierarchy . ((dynamicRegistration . :json-false))) 3797 (typeHierarchy . ((dynamicRegistration . t))) 3798 (publishDiagnostics . ((relatedInformation . t) 3799 (tagSupport . ((valueSet . [1 2]))) 3800 (versionSupport . t))) 3801 (diagnostic . ((dynamicRegistration . :json-false) 3802 (relatedDocumentSupport . :json-false))) 3803 (linkedEditingRange . ((dynamicRegistration . t))))) 3804 (window . ((workDoneProgress . t) 3805 (showDocument . ((support . t)))))) 3806 custom-capabilities)) 3807 3808 (defun lsp-find-roots-for-workspace (workspace session) 3809 "Get all roots for the WORKSPACE." 3810 (-filter #'identity (ht-map (lambda (folder workspaces) 3811 (when (-contains? workspaces workspace) 3812 folder)) 3813 (lsp-session-folder->servers session)))) 3814 3815 (defun lsp-session-watches (&optional session) 3816 "Get watches created for SESSION." 3817 (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session)))) 3818 (-let [res (make-hash-table :test 'equal)] 3819 (puthash "__watches" res (lsp-session-metadata (or session (lsp-session)))) 3820 res))) 3821 3822 (defun lsp--file-process-event (session root-folder event) 3823 "Process file event." 3824 (let* ((changed-file (cl-third event)) 3825 (rel-changed-file (f-relative changed-file root-folder)) 3826 (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type)) 3827 (bit-position (1- event-numeric-kind)) 3828 (watch-bit (ash 1 bit-position))) 3829 (->> 3830 session 3831 lsp-session-folder->servers 3832 (gethash root-folder) 3833 (seq-do (lambda (workspace) 3834 (when (->> 3835 workspace 3836 lsp--workspace-registered-server-capabilities 3837 (-any? 3838 (lambda (capability) 3839 (and 3840 (equal (lsp--registered-capability-method capability) 3841 "workspace/didChangeWatchedFiles") 3842 (->> 3843 capability 3844 lsp--registered-capability-options 3845 (lsp:did-change-watched-files-registration-options-watchers) 3846 (seq-find 3847 (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp)) 3848 (when (or (null kind?) 3849 (> (logand kind? watch-bit) 0)) 3850 (-let [regexes (or cached-regexp 3851 (let ((regexp (lsp-glob-to-regexps glob-pattern))) 3852 (lsp-put fs-watcher :_cachedRegexp regexp) 3853 regexp))] 3854 (-any? (lambda (re) 3855 (or (string-match re changed-file) 3856 (string-match re rel-changed-file))) 3857 regexes)))))))))) 3858 (with-lsp-workspace workspace 3859 (lsp-notify 3860 "workspace/didChangeWatchedFiles" 3861 `((changes . [((type . ,event-numeric-kind) 3862 (uri . ,(lsp--path-to-uri changed-file)))])))))))))) 3863 3864 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?)) 3865 "Register capability REG." 3866 (when (and lsp-enable-file-watchers 3867 (equal method "workspace/didChangeWatchedFiles")) 3868 (-let* ((created-watches (lsp-session-watches (lsp-session))) 3869 (root-folders (cl-set-difference 3870 (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session)) 3871 (ht-keys created-watches)))) 3872 ;; create watch for each root folder without such 3873 (dolist (folder root-folders) 3874 (let* ((watch (make-lsp-watch :root-directory folder)) 3875 (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder)) 3876 (ignored-files-regex-list (car ignored-things)) 3877 (ignored-directories-regex-list (cadr ignored-things))) 3878 (puthash folder watch created-watches) 3879 (lsp-watch-root-folder (file-truename folder) 3880 (-partial #'lsp--file-process-event (lsp-session) folder) 3881 ignored-files-regex-list 3882 ignored-directories-regex-list 3883 watch 3884 t))))) 3885 3886 (push 3887 (make-lsp--registered-capability :id id :method method :options register-options?) 3888 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3889 3890 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body) 3891 "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to 3892 access dir-local variables." 3893 (declare (indent 1) (debug t)) 3894 `(with-temp-buffer 3895 ;; Set the buffer's name to something under the root so that we can hack the local variables 3896 ;; This file doesn't need to exist and will not be created due to this. 3897 (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root))) 3898 (hack-local-variables) 3899 (prog1 ,@body 3900 (setq-local buffer-file-name nil)))) 3901 3902 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root) 3903 "Return a list of the form 3904 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given 3905 WORKSPACE-ROOT." 3906 ;; The intent of this function is to provide per-root workspace-level customization of the 3907 ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables. 3908 (lsp--with-workspace-temp-buffer workspace-root 3909 (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories)))) 3910 3911 3912 (defun lsp--cleanup-hanging-watches () 3913 "Cleanup watches in case there are no more workspaces that are interested 3914 in that particular folder." 3915 (let* ((session (lsp-session)) 3916 (watches (lsp-session-watches session))) 3917 (dolist (watched-folder (ht-keys watches)) 3918 (when (-none? (lambda (workspace) 3919 (with-lsp-workspace workspace 3920 (lsp--registered-capability "workspace/didChangeWatchedFiles"))) 3921 (gethash watched-folder (lsp-session-folder->servers (lsp-session)))) 3922 (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder) 3923 (lsp-kill-watch (gethash watched-folder watches)) 3924 (remhash watched-folder watches))))) 3925 3926 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method)) 3927 "Unregister capability UNREG." 3928 (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace) 3929 (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id)) 3930 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3931 (when (equal method "workspace/didChangeWatchedFiles") 3932 (lsp--cleanup-hanging-watches))) 3933 3934 (defun lsp--server-capabilities () 3935 "Return the capabilities of the language server associated with the buffer." 3936 (->> (lsp-workspaces) 3937 (-keep #'lsp--workspace-server-capabilities) 3938 (apply #'lsp-merge))) 3939 3940 (defun lsp--send-open-close-p () 3941 "Return whether open and close 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-open-close? sync)))) 3945 3946 (defun lsp--send-will-save-p () 3947 "Return whether willSave notifications should be sent to the server." 3948 (-> (lsp--server-capabilities) 3949 (lsp:server-capabilities-text-document-sync?) 3950 (lsp:text-document-sync-options-will-save?))) 3951 3952 (defun lsp--send-will-save-wait-until-p () 3953 "Return whether willSaveWaitUntil notifications should be sent to the server." 3954 (-> (lsp--server-capabilities) 3955 (lsp:server-capabilities-text-document-sync?) 3956 (lsp:text-document-sync-options-will-save-wait-until?))) 3957 3958 (defun lsp--send-did-save-p () 3959 "Return whether didSave notifications should be sent to the server." 3960 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3961 (or (memq sync '(1 2)) 3962 (lsp:text-document-sync-options-save? sync)))) 3963 3964 (defun lsp--save-include-text-p () 3965 "Return whether save notifications should include the text document's contents." 3966 (->> (lsp--server-capabilities) 3967 (lsp:server-capabilities-text-document-sync?) 3968 (lsp:text-document-sync-options-save?) 3969 (lsp:text-document-save-registration-options-include-text?))) 3970 3971 (defun lsp--send-will-rename-files-p (path) 3972 "Return whether willRenameFiles request should be sent to the server. 3973 If any filters, checks if it applies for PATH." 3974 (let* ((will-rename (-> (lsp--server-capabilities) 3975 (lsp:server-capabilities-workspace?) 3976 (lsp:workspace-server-capabilities-file-operations?) 3977 (lsp:workspace-file-operations-will-rename?))) 3978 (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list))) 3979 (and will-rename 3980 (or (seq-empty-p filters) 3981 (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob))) 3982 (-let [regexes (lsp-glob-to-regexps glob)] 3983 (and (or (not scheme?) 3984 (string-prefix-p scheme? (lsp--path-to-uri path))) 3985 (-any? (lambda (re) 3986 (string-match re path)) 3987 regexes)))) 3988 filters))))) 3989 3990 (defun lsp--send-did-rename-files-p () 3991 "Return whether didRenameFiles notification should be sent to the server." 3992 (-> (lsp--server-capabilities) 3993 (lsp:server-capabilities-workspace?) 3994 (lsp:workspace-server-capabilities-file-operations?) 3995 (lsp:workspace-file-operations-did-rename?))) 3996 3997 (declare-function project-roots "ext:project" (project) t) 3998 (declare-function project-root "ext:project" (project) t) 3999 4000 (defun lsp--suggest-project-root () 4001 "Get project root." 4002 (or 4003 (when (fboundp 'projectile-project-root) 4004 (condition-case nil 4005 (projectile-project-root) 4006 (error nil))) 4007 (when (fboundp 'project-current) 4008 (when-let ((project (project-current))) 4009 (if (fboundp 'project-root) 4010 (project-root project) 4011 (car (with-no-warnings 4012 (project-roots project)))))) 4013 default-directory)) 4014 4015 (defun lsp--read-from-file (file) 4016 "Read FILE content." 4017 (when (file-exists-p file) 4018 (cl-first (read-from-string (f-read-text file 'utf-8))))) 4019 4020 (defun lsp--persist (file-name to-persist) 4021 "Persist TO-PERSIST in FILE-NAME. 4022 4023 This function creates the parent directories if they don't exist 4024 yet." 4025 (let ((print-length nil) 4026 (print-level nil)) 4027 ;; Create all parent directories: 4028 (make-directory (f-parent file-name) t) 4029 (f-write-text (prin1-to-string to-persist) 'utf-8 file-name))) 4030 4031 (defun lsp-workspace-folders-add (project-root) 4032 "Add PROJECT-ROOT to the list of workspace folders." 4033 (interactive 4034 (list (read-directory-name "Select folder to add: " 4035 (or (lsp--suggest-project-root) default-directory) nil t))) 4036 (cl-pushnew (lsp-f-canonical project-root) 4037 (lsp-session-folders (lsp-session)) :test 'equal) 4038 (lsp--persist-session (lsp-session)) 4039 4040 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)) 4041 4042 (defun lsp-workspace-folders-remove (project-root) 4043 "Remove PROJECT-ROOT from the list of workspace folders." 4044 (interactive (list (completing-read "Select folder to remove: " 4045 (lsp-session-folders (lsp-session)) 4046 nil t nil nil 4047 (lsp-find-session-folder (lsp-session) default-directory)))) 4048 4049 (setq project-root (lsp-f-canonical project-root)) 4050 4051 ;; send remove folder to each multiroot workspace associated with the folder 4052 (dolist (wks (->> (lsp-session) 4053 (lsp-session-folder->servers) 4054 (gethash project-root) 4055 (--filter (lsp--client-multi-root (lsp--workspace-client it))))) 4056 (with-lsp-workspace wks 4057 (lsp-notify "workspace/didChangeWorkspaceFolders" 4058 (lsp-make-did-change-workspace-folders-params 4059 :event (lsp-make-workspace-folders-change-event 4060 :removed (vector (lsp-make-workspace-folder 4061 :uri (lsp--path-to-uri project-root) 4062 :name (f-filename project-root))) 4063 :added []))))) 4064 4065 ;; turn off servers in the removed directory 4066 (let* ((session (lsp-session)) 4067 (folder->servers (lsp-session-folder->servers session)) 4068 (server-id->folders (lsp-session-server-id->folders session)) 4069 (workspaces (gethash project-root folder->servers))) 4070 4071 (remhash project-root folder->servers) 4072 4073 ;; turn off the servers without root folders 4074 (dolist (workspace workspaces) 4075 (when (--none? (-contains? it workspace) (ht-values folder->servers)) 4076 (lsp--info "Shutdown %s since folder %s is removed..." 4077 (lsp--workspace-print workspace) project-root) 4078 (with-lsp-workspace workspace (lsp--shutdown-workspace)))) 4079 4080 (setf (lsp-session-folders session) 4081 (-remove-item project-root (lsp-session-folders session))) 4082 4083 (ht-aeach (puthash key 4084 (-remove-item project-root value) 4085 server-id->folders) 4086 server-id->folders) 4087 (lsp--persist-session (lsp-session))) 4088 4089 (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root))) 4090 4091 (defun lsp-workspace-blocklist-remove (project-root) 4092 "Remove PROJECT-ROOT from the workspace blocklist." 4093 (interactive (list (completing-read "Select folder to remove:" 4094 (lsp-session-folders-blocklist (lsp-session)) 4095 nil t))) 4096 (setf (lsp-session-folders-blocklist (lsp-session)) 4097 (delete project-root 4098 (lsp-session-folders-blocklist (lsp-session)))) 4099 (lsp--persist-session (lsp-session))) 4100 4101 (define-obsolete-function-alias 'lsp-workspace-folders-switch 4102 'lsp-workspace-folders-open "lsp-mode 6.1") 4103 4104 (defun lsp-workspace-folders-open (project-root) 4105 "Open the directory located at PROJECT-ROOT" 4106 (interactive (list (completing-read "Open folder: " 4107 (lsp-session-folders (lsp-session)) 4108 nil t))) 4109 (find-file project-root)) 4110 4111 (defun lsp--maybe-enable-signature-help (trigger-characters) 4112 (let ((ch last-command-event)) 4113 (when (cl-find ch trigger-characters :key #'string-to-char) 4114 (lsp-signature-activate)))) 4115 4116 (defun lsp--on-type-formatting-handler-create () 4117 (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" ))) 4118 (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character? 4119 :first-trigger-character) provider] 4120 (lambda () 4121 (lsp--on-type-formatting first-trigger-character 4122 more-trigger-character?))))) 4123 4124 (defun lsp--update-on-type-formatting-hook (&optional cleanup?) 4125 (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create))) 4126 (cond 4127 ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?)) 4128 (add-hook 'post-self-insert-hook on-type-formatting-handler nil t)) 4129 ((or cleanup? 4130 (not lsp-enable-on-type-formatting)) 4131 (remove-hook 'post-self-insert-hook on-type-formatting-handler t))))) 4132 4133 (defun lsp--signature-help-handler-create () 4134 (-when-let ((&SignatureHelpOptions? :trigger-characters?) 4135 (lsp--capability-for-method "textDocument/signatureHelp")) 4136 (lambda () 4137 (lsp--maybe-enable-signature-help trigger-characters?)))) 4138 4139 (defun lsp--update-signature-help-hook (&optional cleanup?) 4140 (let ((signature-help-handler (lsp--signature-help-handler-create))) 4141 (cond 4142 ((and (or (equal lsp-signature-auto-activate t) 4143 (memq :on-trigger-char lsp-signature-auto-activate)) 4144 signature-help-handler 4145 (not cleanup?)) 4146 (add-hook 'post-self-insert-hook signature-help-handler nil t)) 4147 4148 ((or cleanup? 4149 (not (or (equal lsp-signature-auto-activate t) 4150 (memq :on-trigger-char lsp-signature-auto-activate)))) 4151 (remove-hook 'post-self-insert-hook signature-help-handler t))))) 4152 4153 (defun lsp--after-set-visited-file-name () 4154 (lsp-disconnect) 4155 (lsp)) 4156 4157 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27 4158 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099 4159 (defvar eldoc-documentation-default) ; CI 4160 (when (< emacs-major-version 28) 4161 (unless (boundp 'eldoc-documentation-functions) 4162 (load "eldoc" nil 'nomessage)) 4163 (when (memq (default-value 'eldoc-documentation-function) '(nil ignore)) 4164 ;; actually `eldoc-documentation-strategy', but CI was failing 4165 (setq-default eldoc-documentation-function 'eldoc-documentation-default))) 4166 4167 (define-minor-mode lsp-managed-mode 4168 "Mode for source buffers managed by lsp-mode." 4169 :lighter nil 4170 (cond 4171 (lsp-managed-mode 4172 (when (lsp-feature? "textDocument/hover") 4173 (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t) 4174 (eldoc-mode 1)) 4175 4176 (add-hook 'after-change-functions #'lsp-on-change nil t) 4177 (add-hook 'after-revert-hook #'lsp-on-revert nil t) 4178 (add-hook 'after-save-hook #'lsp-on-save nil t) 4179 (add-hook 'auto-save-hook #'lsp--on-auto-save nil t) 4180 (add-hook 'before-change-functions #'lsp-before-change nil t) 4181 (add-hook 'before-save-hook #'lsp--before-save nil t) 4182 (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t) 4183 (add-hook 'post-command-hook #'lsp--post-command nil t) 4184 4185 (lsp--update-on-type-formatting-hook) 4186 (lsp--update-signature-help-hook) 4187 4188 (when lsp-enable-xref 4189 (add-hook 'xref-backend-functions #'lsp--xref-backend nil t)) 4190 4191 (lsp-configure-buffer) 4192 4193 ;; make sure we turn off lsp-mode in case major mode changes, because major 4194 ;; mode change will wipe the buffer locals. 4195 (add-hook 'change-major-mode-hook #'lsp-disconnect nil t) 4196 (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t) 4197 4198 (let ((buffer (lsp-current-buffer))) 4199 (run-with-idle-timer 4200 0.0 nil 4201 (lambda () 4202 (when (lsp-buffer-live-p buffer) 4203 (lsp-with-current-buffer buffer 4204 (lsp--on-change-debounce buffer) 4205 (lsp--on-idle buffer))))))) 4206 (t 4207 (lsp-unconfig-buffer) 4208 4209 (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t) 4210 (remove-hook 'post-command-hook #'lsp--post-command t) 4211 (remove-hook 'after-change-functions #'lsp-on-change t) 4212 (remove-hook 'after-revert-hook #'lsp-on-revert t) 4213 (remove-hook 'after-save-hook #'lsp-on-save t) 4214 (remove-hook 'auto-save-hook #'lsp--on-auto-save t) 4215 (remove-hook 'before-change-functions #'lsp-before-change t) 4216 (remove-hook 'before-save-hook #'lsp--before-save t) 4217 (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t) 4218 4219 (lsp--update-on-type-formatting-hook :cleanup) 4220 (lsp--update-signature-help-hook :cleanup) 4221 4222 (when lsp--on-idle-timer 4223 (cancel-timer lsp--on-idle-timer) 4224 (setq lsp--on-idle-timer nil)) 4225 4226 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4227 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4228 4229 (lsp--remove-overlays 'lsp-highlight) 4230 (lsp--remove-overlays 'lsp-links) 4231 4232 (remove-hook 'xref-backend-functions #'lsp--xref-backend t) 4233 (remove-hook 'change-major-mode-hook #'lsp-disconnect t) 4234 (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t) 4235 (setq-local lsp-buffer-uri nil)))) 4236 4237 (defun lsp-configure-buffer () 4238 "Configure LSP features for current buffer." 4239 ;; make sure the core is running in the context of all available workspaces 4240 ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context 4241 (let ((lsp--buffer-workspaces (cond 4242 (lsp--buffer-workspaces) 4243 (lsp--cur-workspace (list lsp--cur-workspace)))) 4244 lsp--cur-workspace) 4245 (when lsp-auto-configure 4246 (lsp--auto-configure) 4247 4248 (when (and lsp-enable-text-document-color 4249 (lsp-feature? "textDocument/documentColor")) 4250 (add-hook 'lsp-on-change-hook #'lsp--document-color nil t)) 4251 4252 (when (and lsp-enable-imenu 4253 (lsp-feature? "textDocument/documentSymbol")) 4254 (lsp-enable-imenu)) 4255 4256 (when (and lsp-enable-indentation 4257 (lsp-feature? "textDocument/rangeFormatting")) 4258 (add-function :override (local 'indent-region-function) #'lsp-format-region)) 4259 4260 (when (and lsp-enable-symbol-highlighting 4261 (lsp-feature? "textDocument/documentHighlight")) 4262 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)) 4263 4264 (when (and lsp-enable-links 4265 (lsp-feature? "textDocument/documentLink")) 4266 (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t)) 4267 4268 (when (and lsp-inlay-hint-enable 4269 (lsp-feature? "textDocument/inlayHint")) 4270 (lsp-inlay-hints-mode)) 4271 4272 (when (and lsp-enable-dap-auto-configure 4273 (functionp 'dap-mode)) 4274 (dap-auto-configure-mode 1))) 4275 (run-hooks 'lsp-configure-hook))) 4276 4277 (defun lsp-unconfig-buffer () 4278 "Unconfigure LSP features for buffer." 4279 (lsp--remove-overlays 'lsp-color) 4280 4281 (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function) 4282 (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index) 4283 (setq-local imenu-menubar-modified-tick 0) 4284 (setq-local imenu--index-alist nil) 4285 (imenu--cleanup)) 4286 4287 (remove-function (local 'indent-region-function) #'lsp-format-region) 4288 4289 (remove-hook 'lsp-on-change-hook #'lsp--document-color t) 4290 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4291 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4292 4293 (when (and lsp-enable-dap-auto-configure 4294 (functionp 'dap-mode)) 4295 (dap-auto-configure-mode -1)) 4296 4297 (run-hooks 'lsp-unconfigure-hook)) 4298 4299 (defun lsp--buffer-content () 4300 (lsp-save-restriction-and-excursion 4301 (or (lsp-virtual-buffer-call :buffer-string) 4302 (buffer-substring-no-properties (point-min) 4303 (point-max))))) 4304 4305 (defun lsp--text-document-did-open () 4306 "`document/didOpen' event." 4307 (run-hooks 'lsp-before-open-hook) 4308 (when (and lsp-auto-touch-files 4309 (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri))))) 4310 (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri)) 4311 (save-buffer)) 4312 4313 (setq lsp--cur-version (or lsp--cur-version 0)) 4314 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 4315 (lsp-notify 4316 "textDocument/didOpen" 4317 (list :textDocument 4318 (list :uri (lsp--buffer-uri) 4319 :languageId (lsp-buffer-language) 4320 :version lsp--cur-version 4321 :text (lsp--buffer-content)))) 4322 4323 (lsp-managed-mode 1) 4324 4325 (lsp-diagnostics--request-pull-diagnostics lsp--cur-workspace) 4326 4327 (run-hooks 'lsp-after-open-hook) 4328 (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client)))) 4329 (-some-> (lsp--client-after-open-fn client) 4330 (funcall)) 4331 (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client)) 4332 (intern-soft) 4333 (run-hooks)))) 4334 4335 (defun lsp--text-document-identifier () 4336 "Make TextDocumentIdentifier." 4337 (list :uri (lsp--buffer-uri))) 4338 4339 (defun lsp--versioned-text-document-identifier () 4340 "Make VersionedTextDocumentIdentifier." 4341 (plist-put (lsp--text-document-identifier) :version lsp--cur-version)) 4342 4343 (defun lsp--cur-line (&optional point) 4344 (1- (line-number-at-pos point))) 4345 4346 (defun lsp--cur-position () 4347 "Make a Position object for the current point." 4348 (or (lsp-virtual-buffer-call :cur-position) 4349 (lsp-save-restriction-and-excursion 4350 (list :line (lsp--cur-line) 4351 :character (- (point) (line-beginning-position)))))) 4352 4353 (defun lsp--point-to-position (point) 4354 "Convert POINT to Position." 4355 (lsp-save-restriction-and-excursion 4356 (goto-char point) 4357 (lsp--cur-position))) 4358 4359 (defun lsp--range (start end) 4360 "Make Range body from START and END." 4361 ;; make sure start and end are Position objects 4362 (list :start start :end end)) 4363 4364 (defun lsp--region-to-range (start end) 4365 "Make Range object for the current region." 4366 (lsp--range (lsp--point-to-position start) 4367 (lsp--point-to-position end))) 4368 4369 (defun lsp--region-or-line () 4370 "The active region or the current line." 4371 (if (use-region-p) 4372 (lsp--region-to-range (region-beginning) (region-end)) 4373 (lsp--region-to-range (line-beginning-position) (line-end-position)))) 4374 4375 (defun lsp--check-document-changes-version (document-changes) 4376 "Verify that DOCUMENT-CHANGES have the proper version." 4377 (unless (seq-every-p 4378 (-lambda ((&TextDocumentEdit :text-document)) 4379 (or 4380 (not text-document) 4381 (let* ((filename (-> text-document 4382 lsp:versioned-text-document-identifier-uri 4383 lsp--uri-to-path)) 4384 (version (lsp:versioned-text-document-identifier-version? text-document))) 4385 (with-current-buffer (find-file-noselect filename) 4386 (or (null version) (zerop version) (= -1 version) 4387 (equal version lsp--cur-version)))))) 4388 document-changes) 4389 (error "Document changes cannot be applied due to different document version"))) 4390 4391 (defun lsp--apply-workspace-edit (workspace-edit &optional operation) 4392 "Apply the WorkspaceEdit object WORKSPACE-EDIT. 4393 OPERATION is symbol representing the source of this text edit." 4394 (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit)) 4395 (if-let ((document-changes (seq-reverse document-changes?))) 4396 (progn 4397 (lsp--check-document-changes-version document-changes) 4398 (->> document-changes 4399 (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create"))) 4400 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4401 (->> document-changes 4402 (seq-filter (-lambda ((&CreateFile :kind)) 4403 (and (or (not kind) (equal kind "edit")) 4404 (not (equal kind "create"))))) 4405 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4406 (->> document-changes 4407 (seq-filter (-lambda ((&CreateFile :kind)) 4408 (and (not (or (not kind) (equal kind "edit"))) 4409 (not (equal kind "create"))))) 4410 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))) 4411 (lsp-map 4412 (lambda (uri text-edits) 4413 (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect) 4414 (lsp--apply-text-edits text-edits operation))) 4415 changes?)))) 4416 4417 (defmacro lsp-with-filename (file &rest body) 4418 "Execute BODY with FILE as a context. 4419 Need to handle the case when FILE indicates virtual buffer." 4420 (declare (indent 1) (debug t)) 4421 `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file))) 4422 (lsp-with-current-buffer lsp--virtual-buffer 4423 ,@body) 4424 ,@body)) 4425 4426 (defun lsp--apply-text-document-edit (edit &optional operation) 4427 "Apply the TextDocumentEdit object EDIT. 4428 OPERATION is symbol representing the source of this text edit. 4429 If the file is not being visited by any buffer, it is opened with 4430 `find-file-noselect'. 4431 Because lsp-mode does not store previous document versions, the edit is only 4432 applied if the version of the textDocument matches the version of the 4433 corresponding file. 4434 4435 interface TextDocumentEdit { 4436 textDocument: VersionedTextDocumentIdentifier; 4437 edits: TextEdit[]; 4438 }" 4439 (pcase (lsp:edit-kind edit) 4440 ("create" (-let* (((&CreateFile :uri :options?) edit) 4441 (file-name (lsp--uri-to-path uri))) 4442 (mkdir (f-dirname file-name) t) 4443 (f-touch file-name) 4444 (when (lsp:create-file-options-overwrite? options?) 4445 (f-write-text "" nil file-name)) 4446 (find-file-noselect file-name))) 4447 ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit)) 4448 (f-delete (lsp--uri-to-path uri) recursive?))) 4449 ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit) 4450 (old-file-name (lsp--uri-to-path old-uri)) 4451 (new-file-name (lsp--uri-to-path new-uri)) 4452 (buf (find-buffer-visiting old-file-name))) 4453 (when buf 4454 (lsp-with-current-buffer buf 4455 (save-buffer) 4456 (lsp--text-document-did-close))) 4457 (mkdir (f-dirname new-file-name) t) 4458 (rename-file old-file-name new-file-name overwrite?) 4459 (when buf 4460 (lsp-with-current-buffer buf 4461 (set-buffer-modified-p nil) 4462 (setq lsp-buffer-uri nil) 4463 (set-visited-file-name new-file-name) 4464 (lsp))))) 4465 (_ (let ((file-name (->> edit 4466 (lsp:text-document-edit-text-document) 4467 (lsp:versioned-text-document-identifier-uri) 4468 (lsp--uri-to-path)))) 4469 (lsp-with-current-buffer (find-buffer-visiting file-name) 4470 (lsp-with-filename file-name 4471 (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation))))))) 4472 4473 (lsp-defun lsp--position-compare ((&Position :line left-line 4474 :character left-character) 4475 (&Position :line right-line 4476 :character right-character)) 4477 "Return t if position LEFT is greater than RIGHT." 4478 (if (= left-line right-line) 4479 (> left-character right-character) 4480 (> left-line right-line))) 4481 4482 (lsp-defun lsp-point-in-range? (position (&Range :start :end)) 4483 "Returns if POINT is in RANGE." 4484 (not (or (lsp--position-compare start position) 4485 (lsp--position-compare position end)))) 4486 4487 (lsp-defun lsp--position-equal ((&Position :line left-line 4488 :character left-character) 4489 (&Position :line right-line 4490 :character right-character)) 4491 "Return whether LEFT and RIGHT positions are equal." 4492 (and (= left-line right-line) 4493 (= left-character right-character))) 4494 4495 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end)) 4496 (&TextEdit :range (&Range :start right-start :end right-end))) 4497 (if (lsp--position-equal left-start right-start) 4498 (lsp--position-compare left-end right-end) 4499 (lsp--position-compare left-start right-start))) 4500 4501 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text)) 4502 "Apply the edits described in the TextEdit object in TEXT-EDIT." 4503 (setq new-text (s-replace "\r" "" (or new-text ""))) 4504 (lsp:set-text-edit-new-text edit new-text) 4505 (goto-char start) 4506 (delete-region start end) 4507 (insert new-text)) 4508 4509 ;; WORKAROUND: typescript-language might send -1 when applying code actions. 4510 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582 4511 (lsp-defun lsp--fix-point ((point &as &Position :character :line)) 4512 (-doto point 4513 (lsp:set-position-line (max 0 line)) 4514 (lsp:set-position-character (max 0 character)))) 4515 4516 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as 4517 &TextEdit 4518 :range (&Range :start :end) 4519 :new-text)) 4520 "Apply the edits described in the TextEdit object in TEXT-EDIT. 4521 The method uses `replace-buffer-contents'." 4522 (setq new-text (s-replace "\r" "" (or new-text ""))) 4523 (lsp:set-text-edit-new-text edit new-text) 4524 (-let* ((source (current-buffer)) 4525 ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start) 4526 :end (lsp--fix-point end))))) 4527 (with-temp-buffer 4528 (insert new-text) 4529 (let ((temp (current-buffer))) 4530 (with-current-buffer source 4531 (save-excursion 4532 (save-restriction 4533 (narrow-to-region beg end) 4534 4535 ;; On emacs versions < 26.2, 4536 ;; `replace-buffer-contents' is buggy - it calls 4537 ;; change functions with invalid arguments - so we 4538 ;; manually call the change functions here. 4539 ;; 4540 ;; See emacs bugs #32237, #32278: 4541 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237 4542 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278 4543 (let ((inhibit-modification-hooks t) 4544 (length (- end beg))) 4545 (run-hook-with-args 'before-change-functions 4546 beg end) 4547 (replace-buffer-contents temp) 4548 (run-hook-with-args 'after-change-functions 4549 beg (+ beg (length new-text)) 4550 length))))))))) 4551 4552 (defun lsp--to-yasnippet-snippet (snippet) 4553 "Convert LSP SNIPPET to yasnippet snippet." 4554 ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it. 4555 (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`"))) 4556 (rx "\\" (backref 1)) 4557 snippet 4558 nil nil 1)) 4559 4560 (defvar-local lsp-enable-relative-indentation nil 4561 "Enable relative indentation when insert texts, snippets ... 4562 from language server.") 4563 4564 (defun lsp--expand-snippet (snippet &optional start end expand-env) 4565 "Wrapper of `yas-expand-snippet' with all of it arguments. 4566 The snippet will be convert to LSP style and indent according to 4567 LSP server result." 4568 (require 'yasnippet nil t) 4569 (let* ((inhibit-field-text-motion t) 4570 (yas-wrap-around-region nil) 4571 (yas-indent-line 'none) 4572 (yas-also-auto-indent-first-line nil)) 4573 (yas-expand-snippet 4574 (lsp--to-yasnippet-snippet snippet) 4575 start end expand-env))) 4576 4577 (defun lsp--indent-lines (start end &optional insert-text-mode?) 4578 "Indent from START to END based on INSERT-TEXT-MODE? value. 4579 - When INSERT-TEXT-MODE? is provided 4580 - if it's `lsp/insert-text-mode-as-it', do no editor indentation. 4581 - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading 4582 whitespaces to match the line where text is inserted. 4583 - When it's not provided, using `indent-line-function' for each line." 4584 (save-excursion 4585 (goto-char end) 4586 (let* ((end-line (line-number-at-pos)) 4587 (offset (save-excursion 4588 (goto-char start) 4589 (current-indentation))) 4590 (indent-line-function 4591 (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it) 4592 #'ignore) 4593 ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation) 4594 lsp-enable-relative-indentation 4595 ;; Indenting snippets is extremely slow in `org-mode' buffers 4596 ;; since it has to calculate indentation based on SRC block 4597 ;; position. Thus we use relative indentation as default. 4598 (derived-mode-p 'org-mode)) 4599 (lambda () (save-excursion 4600 (beginning-of-line) 4601 (indent-to-column offset)))) 4602 (t indent-line-function)))) 4603 (goto-char start) 4604 (forward-line) 4605 (while (and (not (eobp)) 4606 (<= (line-number-at-pos) end-line)) 4607 (funcall indent-line-function) 4608 (forward-line))))) 4609 4610 (defun lsp--apply-text-edits (edits &optional operation) 4611 "Apply the EDITS described in the TextEdit[] object. 4612 OPERATION is symbol representing the source of this text edit." 4613 (unless (seq-empty-p edits) 4614 (atomic-change-group 4615 (run-hooks 'lsp-before-apply-edits-hook) 4616 (let* ((change-group (prepare-change-group)) 4617 (howmany (length edits)) 4618 (message (format "Applying %s edits to `%s' ..." howmany (current-buffer))) 4619 (_ (lsp--info message)) 4620 (reporter (make-progress-reporter message 0 howmany)) 4621 (done 0) 4622 (apply-edit (if (not lsp--virtual-buffer) 4623 #'lsp--apply-text-edit-replace-buffer-contents 4624 #'lsp--apply-text-edit))) 4625 (unwind-protect 4626 (->> edits 4627 ;; We sort text edits so as to apply edits that modify latter 4628 ;; parts of the document first. Furthermore, because the LSP 4629 ;; spec dictates that: "If multiple inserts have the same 4630 ;; position, the order in the array defines which edit to 4631 ;; apply first." We reverse the initial list and sort stably 4632 ;; to make sure the order among edits with the same position 4633 ;; is preserved. 4634 (nreverse) 4635 (seq-sort #'lsp--text-edit-sort-predicate) 4636 (mapc (lambda (edit) 4637 (progress-reporter-update reporter (cl-incf done)) 4638 (funcall apply-edit edit) 4639 (when (lsp:snippet-text-edit-insert-text-format? edit) 4640 (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start) 4641 :insert-text-format? :new-text) edit) 4642 (when (eq insert-text-format? lsp/insert-text-format-snippet) 4643 ;; No `save-excursion' needed since expand snippet will change point anyway 4644 (goto-char (+ start (length new-text))) 4645 (lsp--indent-lines start (point)) 4646 (lsp--expand-snippet new-text start (point))))) 4647 (run-hook-with-args 'lsp-after-apply-edits-hook operation)))) 4648 (undo-amalgamate-change-group change-group) 4649 (progress-reporter-done reporter)))))) 4650 4651 (defun lsp--create-apply-text-edits-handlers () 4652 "Create (handler cleanup-fn) for applying text edits in async request. 4653 Only works when mode is `tick or `alive." 4654 (let* (first-edited 4655 (func (lambda (start &rest _) 4656 (setq first-edited (if first-edited 4657 (min start first-edited) 4658 start))))) 4659 (add-hook 'before-change-functions func nil t) 4660 (list 4661 (lambda (edits) 4662 (if (and first-edited 4663 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end))) 4664 ;; Text edit region is overlapped 4665 (> end first-edited)) 4666 edits)) 4667 (lsp--warn "TextEdits will not be applied since document has been modified before of them.") 4668 (lsp--apply-text-edits edits 'completion-cleanup))) 4669 (lambda () 4670 (remove-hook 'before-change-functions func t))))) 4671 4672 (defun lsp--capability (cap &optional capabilities) 4673 "Get the value of capability CAP. If CAPABILITIES is non-nil, use them instead." 4674 (when (stringp cap) 4675 (setq cap (intern (concat ":" cap)))) 4676 4677 (lsp-get (or capabilities 4678 (lsp--server-capabilities)) 4679 cap)) 4680 4681 (defun lsp--registered-capability (method) 4682 "Check whether there is workspace providing METHOD." 4683 (->> (lsp-workspaces) 4684 (--keep (seq-find (lambda (reg) 4685 (equal (lsp--registered-capability-method reg) method)) 4686 (lsp--workspace-registered-server-capabilities it))) 4687 cl-first)) 4688 4689 (defun lsp--capability-for-method (method) 4690 "Get the value of capability for METHOD." 4691 (-let* ((reqs (cdr (assoc method lsp-method-requirements))) 4692 ((&plist :capability) reqs)) 4693 (or (and capability (lsp--capability capability)) 4694 (-some-> (lsp--registered-capability method) 4695 (lsp--registered-capability-options))))) 4696 4697 (defvar-local lsp--before-change-vals nil 4698 "Store the positions from the `lsp-before-change' function call, for 4699 validation and use in the `lsp-on-change' function.") 4700 4701 (defun lsp--text-document-content-change-event (start end length) 4702 "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH." 4703 ;; So (47 54 0) means add 7 chars starting at pos 47 4704 ;; must become 4705 ;; {"range":{"start":{"line":5,"character":6} 4706 ;; ,"end" :{"line":5,"character":6}} 4707 ;; ,"rangeLength":0 4708 ;; ,"text":"\nbb = 5"} 4709 ;; 4710 ;; And (47 47 7) means delete 7 chars starting at pos 47 4711 ;; must become 4712 ;; {"range":{"start":{"line":6,"character":0} 4713 ;; ,"end" :{"line":7,"character":0}} 4714 ;; ,"rangeLength":7 4715 ;; ,"text":""} 4716 ;; 4717 ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with 4718 ;; 13 chars. So it must become 4719 ;; {"range":{"start":{"line":5,"character":8} 4720 ;; ,"end" :{"line":5,"character":11}} 4721 ;; ,"rangeLength":3 4722 ;; ,"text":"new-chars-xxx"} 4723 ;; 4724 4725 ;; Adding text: 4726 ;; lsp-before-change:(start,end)=(33,33) 4727 ;; lsp-on-change:(start,end,length)=(33,34,0) 4728 ;; 4729 ;; Changing text: 4730 ;; lsp-before-change:(start,end)=(208,211) 4731 ;; lsp-on-change:(start,end,length)=(208,221,3) 4732 ;; 4733 ;; Deleting text: 4734 ;; lsp-before-change:(start,end)=(19,27) 4735 ;; lsp-on-change:(start,end,length)=(19,19,8) 4736 (if (zerop length) 4737 ;; Adding something only, work from start only 4738 `( :range ,(lsp--range 4739 (lsp--point-to-position start) 4740 (lsp--point-to-position start)) 4741 :rangeLength 0 4742 :text ,(buffer-substring-no-properties start end)) 4743 4744 (if (eq start end) 4745 ;; Deleting something only 4746 (if (lsp--bracketed-change-p start length) 4747 ;; The before-change value is bracketed, use it 4748 `( :range ,(lsp--range 4749 (lsp--point-to-position start) 4750 (plist-get lsp--before-change-vals :end-pos)) 4751 :rangeLength ,length 4752 :text "") 4753 ;; If the change is not bracketed, send a full change event instead. 4754 (lsp--full-change-event)) 4755 4756 ;; Deleting some things, adding others 4757 (if (lsp--bracketed-change-p start length) 4758 ;; The before-change value is valid, use it 4759 `( :range ,(lsp--range 4760 (lsp--point-to-position start) 4761 (plist-get lsp--before-change-vals :end-pos)) 4762 :rangeLength ,length 4763 :text ,(buffer-substring-no-properties start end)) 4764 (lsp--full-change-event))))) 4765 4766 (defun lsp--bracketed-change-p (start length) 4767 "If the before and after positions are the same, and the length 4768 is the size of the start range, we are probably good." 4769 (-let [(&plist :end before-end :start before-start) lsp--before-change-vals] 4770 (and (eq start before-start) 4771 (eq length (- before-end before-start))))) 4772 4773 (defun lsp--full-change-event () 4774 `(:text ,(lsp--buffer-content))) 4775 4776 (defun lsp-before-change (start end) 4777 "Executed before a file is changed. 4778 Added to `before-change-functions'." 4779 ;; Note: 4780 ;; 4781 ;; This variable holds a list of functions to call when Emacs is about to 4782 ;; modify a buffer. Each function gets two arguments, the beginning and end of 4783 ;; the region that is about to change, represented as integers. The buffer 4784 ;; that is about to change is always the current buffer when the function is 4785 ;; called. 4786 ;; 4787 ;; WARNING: 4788 ;; 4789 ;; Do not expect the before-change hooks and the after-change hooks be called 4790 ;; in balanced pairs around each buffer change. Also don't expect the 4791 ;; before-change hooks to be called for every chunk of text Emacs is about to 4792 ;; delete. These hooks are provided on the assumption that Lisp programs will 4793 ;; use either before- or the after-change hooks, but not both, and the 4794 ;; boundaries of the region where the changes happen might include more than 4795 ;; just the actual changed text, or even lump together several changes done 4796 ;; piecemeal. 4797 (save-match-data 4798 (lsp-save-restriction-and-excursion 4799 (setq lsp--before-change-vals 4800 (list :start start 4801 :end end 4802 :end-pos (lsp--point-to-position end)))))) 4803 4804 (defun lsp--flush-delayed-changes () 4805 (let ((inhibit-quit t)) 4806 (when lsp--delay-timer 4807 (cancel-timer lsp--delay-timer)) 4808 (mapc (-lambda ((workspace buffer document change)) 4809 (with-current-buffer buffer 4810 (with-lsp-workspace workspace 4811 (lsp-notify "textDocument/didChange" 4812 (list :textDocument document 4813 :contentChanges (vector change)))))) 4814 (prog1 (nreverse lsp--delayed-requests) 4815 (setq lsp--delayed-requests nil))))) 4816 4817 (defun lsp--workspace-sync-method (workspace) 4818 (let ((sync (-> workspace 4819 (lsp--workspace-server-capabilities) 4820 (lsp:server-capabilities-text-document-sync?)))) 4821 (if (lsp-text-document-sync-options? sync) 4822 (lsp:text-document-sync-options-change? sync) 4823 sync))) 4824 4825 (defun lsp-on-change (start end length &optional content-change-event-fn) 4826 "Executed when a file is changed. 4827 Added to `after-change-functions'." 4828 ;; Note: 4829 ;; 4830 ;; Each function receives three arguments: the beginning and end of the region 4831 ;; just changed, and the length of the text that existed before the change. 4832 ;; All three arguments are integers. The buffer that has been changed is 4833 ;; always the current buffer when the function is called. 4834 ;; 4835 ;; The length of the old text is the difference between the buffer positions 4836 ;; before and after that text as it was before the change. As for the 4837 ;; changed text, its length is simply the difference between the first two 4838 ;; arguments. 4839 ;; 4840 ;; So (47 54 0) means add 7 chars starting at pos 47 4841 ;; So (47 47 7) means delete 7 chars starting at pos 47 4842 (save-match-data 4843 (let ((inhibit-quit t) 4844 ;; make sure that `lsp-on-change' is called in multi-workspace context 4845 ;; see #2901 4846 lsp--cur-workspace) 4847 ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done 4848 ;; by auto-revert-mode) will cause this handler to get called with a nil 4849 ;; buffer-file-name. We need the buffer-file-name to send notifications; 4850 ;; so we skip handling revert-buffer-caused changes and instead handle 4851 ;; reverts separately in lsp-on-revert 4852 (when (not revert-buffer-in-progress-p) 4853 (cl-incf lsp--cur-version) 4854 (mapc 4855 (lambda (workspace) 4856 (pcase (or lsp-document-sync-method 4857 (lsp--workspace-sync-method workspace)) 4858 (1 4859 (if lsp-debounce-full-sync-notifications 4860 (setq lsp--delayed-requests 4861 (->> lsp--delayed-requests 4862 (-remove (-lambda ((_ buffer)) 4863 (equal (current-buffer) buffer))) 4864 (cons (list workspace 4865 (current-buffer) 4866 (lsp--versioned-text-document-identifier) 4867 (lsp--full-change-event))))) 4868 (with-lsp-workspace workspace 4869 (lsp-notify "textDocument/didChange" 4870 (list :contentChanges (vector (lsp--full-change-event)) 4871 :textDocument (lsp--versioned-text-document-identifier))) 4872 (lsp-diagnostics--request-pull-diagnostics workspace)))) 4873 (2 4874 (with-lsp-workspace workspace 4875 (lsp-notify 4876 "textDocument/didChange" 4877 (list :textDocument (lsp--versioned-text-document-identifier) 4878 :contentChanges (vector 4879 (if content-change-event-fn 4880 (funcall content-change-event-fn start end length) 4881 (lsp--text-document-content-change-event 4882 start end length))))) 4883 (lsp-diagnostics--request-pull-diagnostics workspace))))) 4884 (lsp-workspaces)) 4885 (when lsp--delay-timer (cancel-timer lsp--delay-timer)) 4886 (setq lsp--delay-timer (run-with-idle-timer 4887 lsp-debounce-full-sync-notifications-interval 4888 nil 4889 #'lsp--flush-delayed-changes)) 4890 ;; force cleanup overlays after each change 4891 (lsp--remove-overlays 'lsp-highlight) 4892 (lsp--after-change (current-buffer)))))) 4893 4894 4895 4896 ;; facilities for on change hooks. We do not want to make lsp calls on each 4897 ;; change event so we add debounce to avoid flooding the server with events. 4898 ;; Additionally, we want to have a mechanism for stopping the server calls in 4899 ;; particular cases like, e. g. when performing completion. 4900 4901 (defvar lsp-inhibit-lsp-hooks nil 4902 "Flag to control.") 4903 4904 (defcustom lsp-on-change-hook nil 4905 "Hooks to run when buffer has changed." 4906 :type 'hook 4907 :group 'lsp-mode) 4908 4909 (defcustom lsp-idle-delay 0.500 4910 "Debounce interval for `after-change-functions'." 4911 :type 'number 4912 :group 'lsp-mode) 4913 4914 (defcustom lsp-on-idle-hook nil 4915 "Hooks to run after `lsp-idle-delay'." 4916 :type 'hook 4917 :group 'lsp-mode) 4918 4919 (defun lsp--idle-reschedule (buffer) 4920 (when lsp--on-idle-timer 4921 (cancel-timer lsp--on-idle-timer)) 4922 4923 (setq lsp--on-idle-timer (run-with-idle-timer 4924 lsp-idle-delay 4925 nil 4926 #'lsp--on-idle 4927 buffer))) 4928 4929 (defun lsp--post-command () 4930 (lsp--cleanup-highlights-if-needed) 4931 (lsp--idle-reschedule (current-buffer))) 4932 4933 (defun lsp--on-idle (buffer) 4934 "Start post command loop." 4935 (when (and (buffer-live-p buffer) 4936 (equal buffer (current-buffer)) 4937 (not lsp-inhibit-lsp-hooks) 4938 lsp-managed-mode) 4939 (run-hooks 'lsp-on-idle-hook))) 4940 4941 (defun lsp--on-change-debounce (buffer) 4942 (when (and (buffer-live-p buffer) 4943 (equal buffer (current-buffer)) 4944 (not lsp-inhibit-lsp-hooks) 4945 lsp-managed-mode) 4946 (run-hooks 'lsp-on-change-hook))) 4947 4948 (defun lsp--after-change (buffer) 4949 "Called after most textDocument/didChange events." 4950 (setq lsp--signature-last-index nil 4951 lsp--signature-last nil) 4952 4953 ;; cleanup diagnostics 4954 (when lsp-diagnostic-clean-after-change 4955 (dolist (workspace (lsp-workspaces)) 4956 (-let [diagnostics (lsp--workspace-diagnostics workspace)] 4957 (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics)))) 4958 4959 (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled) 4960 (lsp--semantic-tokens-refresh-if-enabled buffer)) 4961 (when lsp--on-change-timer 4962 (cancel-timer lsp--on-change-timer)) 4963 (setq lsp--on-change-timer (run-with-idle-timer 4964 lsp-idle-delay 4965 nil 4966 #'lsp--on-change-debounce 4967 buffer)) 4968 (lsp--idle-reschedule buffer)) 4969 4970 4971 (defcustom lsp-trim-trailing-whitespace t 4972 "Trim trailing whitespace on a line." 4973 :group 'lsp-mode 4974 :type 'boolean) 4975 4976 (defcustom lsp-insert-final-newline t 4977 "Insert a newline character at the end of the file if one does not exist." 4978 :group 'lsp-mode 4979 :type 'boolean) 4980 4981 (defcustom lsp-trim-final-newlines t 4982 "Trim all newlines after the final newline at the end of the file." 4983 :group 'lsp-mode 4984 :type 'boolean) 4985 4986 4987 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters) 4988 "Self insert handling. 4989 Applies on type formatting." 4990 (let ((ch last-command-event)) 4991 (when (or (eq (string-to-char first-trigger-characters) ch) 4992 (cl-find ch more-trigger-characters :key #'string-to-char)) 4993 (lsp-request-async "textDocument/onTypeFormatting" 4994 (lsp-make-document-on-type-formatting-params 4995 :text-document (lsp--text-document-identifier) 4996 :options (lsp-make-formatting-options 4997 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 4998 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 4999 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 5000 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 5001 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)) 5002 :ch (char-to-string ch) 5003 :position (lsp--cur-position)) 5004 (lambda (data) (lsp--apply-text-edits data 'format)) 5005 :mode 'tick)))) 5006 5007 5008 ;; links 5009 (defun lsp--document-links () 5010 (when (lsp-feature? "textDocument/documentLink") 5011 (lsp-request-async 5012 "textDocument/documentLink" 5013 `(:textDocument ,(lsp--text-document-identifier)) 5014 (lambda (links) 5015 (lsp--remove-overlays 'lsp-link) 5016 (seq-do 5017 (-lambda ((link &as &DocumentLink :range (&Range :start :end))) 5018 (-doto (make-button (lsp--position-to-point start) 5019 (lsp--position-to-point end) 5020 'action (lsp--document-link-keymap link) 5021 'keymap (let ((map (make-sparse-keymap))) 5022 (define-key map [M-return] 'push-button) 5023 (define-key map [mouse-2] 'push-button) 5024 map) 5025 'help-echo "mouse-2, M-RET: Visit this link") 5026 (overlay-put 'lsp-link t))) 5027 links)) 5028 :mode 'unchanged))) 5029 5030 (defun lsp--document-link-handle-target (url) 5031 (let* ((parsed-url (url-generic-parse-url (url-unhex-string url))) 5032 (type (url-type parsed-url))) 5033 (pcase type 5034 ("file" 5035 (xref-push-marker-stack) 5036 (find-file (lsp--uri-to-path url)) 5037 (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url)) 5038 (goto-char (lsp--position-to-point 5039 (lsp-make-position :character (1- (string-to-number column)) 5040 :line (1- (string-to-number line))))))) 5041 ((or "http" "https") (browse-url url)) 5042 (type (if-let ((handler (lsp--get-uri-handler type))) 5043 (funcall handler url) 5044 (signal 'lsp-file-scheme-not-supported (list url))))))) 5045 5046 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?)) 5047 (if target? 5048 (lambda (_) 5049 (interactive) 5050 (lsp--document-link-handle-target target?)) 5051 (lambda (_) 5052 (interactive) 5053 (when (lsp:document-link-registration-options-resolve-provider? 5054 (lsp--capability-for-method "textDocument/documentLink")) 5055 (lsp-request-async 5056 "documentLink/resolve" 5057 link 5058 (-lambda ((&DocumentLink :target?)) 5059 (lsp--document-link-handle-target target?))))))) 5060 5061 5062 5063 (defcustom lsp-warn-no-matched-clients t 5064 "Whether to show messages when there are no supported clients." 5065 :group 'lsp-mode 5066 :type 'boolean) 5067 5068 (defun lsp-buffer-language--configured-id () 5069 "Return nil when not registered." 5070 (->> lsp-language-id-configuration 5071 (-first 5072 (-lambda ((mode-or-pattern . language)) 5073 (cond 5074 ((and (stringp mode-or-pattern) 5075 (s-matches? mode-or-pattern (buffer-file-name))) 5076 language) 5077 ((eq mode-or-pattern major-mode) language)))) 5078 cl-rest)) 5079 5080 (defvar-local lsp--buffer-language nil 5081 "Locally cached returned value of `lsp-buffer-language'.") 5082 5083 (defun lsp-buffer-language () 5084 "Get language corresponding current buffer." 5085 (or lsp--buffer-language 5086 (let* ((configured-language (lsp-buffer-language--configured-id))) 5087 (setq lsp--buffer-language 5088 (or configured-language 5089 ;; ensure non-nil 5090 (string-remove-suffix "-mode" (symbol-name major-mode)))) 5091 (when (and lsp-warn-no-matched-clients 5092 (null configured-language)) 5093 (lsp-warn "Unable to calculate the languageId for buffer `%s'. \ 5094 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s" 5095 (buffer-name) 5096 major-mode)) 5097 lsp--buffer-language))) 5098 5099 (defun lsp-activate-on (&rest languages) 5100 "Returns language activation function. 5101 The function will return t when the `lsp-buffer-language' returns 5102 one of the LANGUAGES." 5103 (lambda (_file-name _mode) 5104 (-contains? languages (lsp-buffer-language)))) 5105 5106 (defun lsp-workspace-root (&optional path) 5107 "Find the workspace root for the current file or PATH." 5108 (-when-let* ((file-name (or path (buffer-file-name))) 5109 (file-name (lsp-f-canonical file-name))) 5110 (->> (lsp-session) 5111 (lsp-session-folders) 5112 (--filter (and (lsp--files-same-host it file-name) 5113 (or (lsp-f-ancestor-of? it file-name) 5114 (equal it file-name)))) 5115 (--max-by (> (length it) (length other)))))) 5116 5117 (defun lsp-on-revert () 5118 "Executed when a file is reverted. 5119 Added to `after-revert-hook'." 5120 (let ((n (buffer-size)) 5121 (revert-buffer-in-progress-p nil)) 5122 (lsp-on-change 0 n n))) 5123 5124 (defun lsp--text-document-did-close (&optional keep-workspace-alive) 5125 "Executed when the file is closed, added to `kill-buffer-hook'. 5126 5127 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace 5128 if it's closing the last buffer in the workspace." 5129 (lsp-foreach-workspace 5130 (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 5131 (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S" 5132 (lsp-notify "textDocument/didClose" 5133 `(:textDocument ,(lsp--text-document-identifier)))) 5134 (when (and (not lsp-keep-workspace-alive) 5135 (not keep-workspace-alive) 5136 (not (lsp--workspace-buffers lsp--cur-workspace))) 5137 (lsp--shutdown-workspace)))) 5138 5139 (defun lsp--will-save-text-document-params (reason) 5140 (list :textDocument (lsp--text-document-identifier) 5141 :reason reason)) 5142 5143 (defun lsp--before-save () 5144 "Before save handler." 5145 (with-demoted-errors "Error in ‘lsp--before-save’: %S" 5146 (let ((params (lsp--will-save-text-document-params 1))) 5147 (when (lsp--send-will-save-p) 5148 (lsp-notify "textDocument/willSave" params)) 5149 (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits) 5150 (let ((lsp-response-timeout 0.1)) 5151 (condition-case nil 5152 (lsp--apply-text-edits 5153 (lsp-request "textDocument/willSaveWaitUntil" 5154 params) 5155 'before-save) 5156 (error))))))) 5157 5158 (defun lsp--on-auto-save () 5159 "Handler for auto-save." 5160 (when (lsp--send-will-save-p) 5161 (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S" 5162 (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2))))) 5163 5164 (defun lsp--text-document-did-save () 5165 "Executed when the file is closed, added to `after-save-hook''." 5166 (when (lsp--send-did-save-p) 5167 (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’" 5168 (lsp-notify "textDocument/didSave" 5169 `( :textDocument ,(lsp--versioned-text-document-identifier) 5170 ,@(when (lsp--save-include-text-p) 5171 (list :text (lsp--buffer-content)))))))) 5172 5173 (defun lsp--text-document-position-params (&optional identifier position) 5174 "Make TextDocumentPositionParams for the current point in the current document. 5175 If IDENTIFIER and POSITION are non-nil, they will be used as the document 5176 identifier and the position respectively." 5177 (list :textDocument (or identifier (lsp--text-document-identifier)) 5178 :position (or position (lsp--cur-position)))) 5179 5180 (defun lsp--get-buffer-diagnostics () 5181 "Return buffer diagnostics." 5182 (gethash (or 5183 (plist-get lsp--virtual-buffer :buffer-file-name) 5184 (lsp--fix-path-casing (buffer-file-name))) 5185 (lsp-diagnostics t))) 5186 5187 (defun lsp-cur-line-diagnostics () 5188 "Return any diagnostics that apply to the current line." 5189 (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)] 5190 (cl-coerce (-filter 5191 (-lambda ((&Diagnostic :range (&Range :start (&Position :line)))) 5192 (and (>= line start) (<= line end))) 5193 (lsp--get-buffer-diagnostics)) 5194 'vector))) 5195 5196 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end) 5197 (right &as &Range :start right-start :end right-end)) 5198 (or (lsp-point-in-range? right-start left) 5199 (lsp-point-in-range? right-end left) 5200 (lsp-point-in-range? left-start right) 5201 (lsp-point-in-range? left-end right))) 5202 5203 (defun lsp-make-position-1 (position) 5204 (lsp-make-position :line (plist-get position :line) 5205 :character (plist-get position :character))) 5206 5207 (defun lsp-cur-possition-diagnostics () 5208 "Return any diagnostics that apply to the current line." 5209 (-let* ((start (if (use-region-p) (region-beginning) (point))) 5210 (end (if (use-region-p) (region-end) (point))) 5211 (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start)) 5212 :end (lsp-make-position-1 (lsp-point-to-position end))))) 5213 (->> (lsp--get-buffer-diagnostics) 5214 (-filter 5215 (-lambda ((&Diagnostic :range)) 5216 (lsp-range-overlapping? range current-range))) 5217 (apply 'vector)))) 5218 5219 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics) 5220 5221 (defun lsp--extract-line-from-buffer (pos) 5222 "Return the line pointed to by POS (a Position object) in the current buffer." 5223 (let* ((point (lsp--position-to-point pos)) 5224 (inhibit-field-text-motion t)) 5225 (save-excursion 5226 (goto-char point) 5227 (buffer-substring (line-beginning-position) (line-end-position))))) 5228 5229 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line) 5230 :end (end &as &Position :character end-char))) 5231 "Return a xref-item from a RANGE in FILENAME." 5232 (let* ((line (lsp--extract-line-from-buffer start)) 5233 (len (length line))) 5234 (add-face-text-property (max (min start-char len) 0) 5235 (max (min end-char len) 0) 5236 'xref-match t line) 5237 ;; LINE is nil when FILENAME is not being current visited by any buffer. 5238 (xref-make-match (or line filename) 5239 (xref-make-file-location 5240 filename 5241 (lsp-translate-line (1+ start-line)) 5242 (lsp-translate-column start-char)) 5243 (- end-char start-char)))) 5244 5245 (defun lsp--location-uri (loc) 5246 (if (lsp-location? loc) 5247 (lsp:location-uri loc) 5248 (lsp:location-link-target-uri loc))) 5249 5250 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start))) 5251 "Go to location." 5252 (let ((path (lsp--uri-to-path uri))) 5253 (if (f-exists? path) 5254 (with-current-buffer (find-file path) 5255 (goto-char (lsp--position-to-point start))) 5256 (error "There is no file %s" path)))) 5257 5258 (defun lsp--location-range (loc) 5259 (if (lsp-location? loc) 5260 (lsp:location-range loc) 5261 (lsp:location-link-target-selection-range loc))) 5262 5263 (defun lsp--locations-to-xref-items (locations) 5264 "Return a list of `xref-item' given LOCATIONS, which can be of 5265 type Location, LocationLink, Location[] or LocationLink[]." 5266 (setq locations 5267 (pcase locations 5268 ((seq (or (lsp-interface Location) 5269 (lsp-interface LocationLink))) 5270 (append locations nil)) 5271 ((or (lsp-interface Location) 5272 (lsp-interface LocationLink)) 5273 (list locations)))) 5274 5275 (cl-labels ((get-xrefs-in-file 5276 (file-locs) 5277 (-let [(filename . matches) file-locs] 5278 (condition-case err 5279 (let ((visiting (find-buffer-visiting filename)) 5280 (fn (lambda (loc) 5281 (lsp-with-filename filename 5282 (lsp--xref-make-item filename 5283 (lsp--location-range loc)))))) 5284 (if visiting 5285 (with-current-buffer visiting 5286 (seq-map fn matches)) 5287 (when (file-readable-p filename) 5288 (with-temp-buffer 5289 (insert-file-contents-literally filename) 5290 (seq-map fn matches))))) 5291 (error (lsp-warn "Failed to process xref entry for filename '%s': %s" 5292 filename (error-message-string err))) 5293 (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s" 5294 filename (error-message-string err))))))) 5295 5296 (->> locations 5297 (seq-sort #'lsp--location-before-p) 5298 (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri)) 5299 (seq-map #'get-xrefs-in-file) 5300 (apply #'nconc)))) 5301 5302 (defun lsp--location-before-p (left right) 5303 "Sort first by file, then by line, then by column." 5304 (let ((left-uri (lsp--location-uri left)) 5305 (right-uri (lsp--location-uri right))) 5306 (if (not (string= left-uri right-uri)) 5307 (string< left-uri right-uri) 5308 (-let (((&Range :start left-start) (lsp--location-range left)) 5309 ((&Range :start right-start) (lsp--location-range right))) 5310 (lsp--position-compare right-start left-start))))) 5311 5312 (defun lsp--make-reference-params (&optional td-position exclude-declaration) 5313 "Make a ReferenceParam object. 5314 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead. 5315 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." 5316 (let ((json-false :json-false)) 5317 (plist-put (or td-position (lsp--text-document-position-params)) 5318 :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration)))))) 5319 5320 (defun lsp--cancel-request (id) 5321 "Cancel request with ID in all workspaces." 5322 (lsp-foreach-workspace 5323 (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id)) 5324 (lsp-notify "$/cancelRequest" `(:id ,id)))) 5325 5326 (defvar-local lsp--hover-saved-bounds nil) 5327 5328 (defun lsp-eldoc-function (cb &rest _ignored) 5329 "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')." 5330 (if (and lsp--hover-saved-bounds 5331 (lsp--point-in-bounds-p lsp--hover-saved-bounds)) 5332 lsp--eldoc-saved-message 5333 (setq lsp--hover-saved-bounds nil 5334 lsp--eldoc-saved-message nil) 5335 (if (looking-at-p "[[:space:]\n]") 5336 (setq lsp--eldoc-saved-message nil) ; And returns nil. 5337 (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover")) 5338 (lsp-request-async 5339 "textDocument/hover" 5340 (lsp--text-document-position-params) 5341 (-lambda ((hover &as &Hover? :range? :contents)) 5342 (setq lsp--hover-saved-bounds (when range? 5343 (lsp--range-to-region range?))) 5344 (funcall cb (setq lsp--eldoc-saved-message 5345 (when contents 5346 (lsp--render-on-hover-content 5347 contents 5348 lsp-eldoc-render-all))))) 5349 :error-handler #'ignore 5350 :mode 'tick 5351 :cancel-token :eldoc-hover))))) 5352 5353 (defun lsp--point-on-highlight? () 5354 (-some? (lambda (overlay) 5355 (overlay-get overlay 'lsp-highlight)) 5356 (overlays-at (point)))) 5357 5358 (defun lsp--cleanup-highlights-if-needed () 5359 (when (and lsp-enable-symbol-highlighting 5360 lsp--have-document-highlights 5361 (not (lsp--point-on-highlight?))) 5362 (lsp--remove-overlays 'lsp-highlight) 5363 (setq lsp--have-document-highlights nil) 5364 (lsp-cancel-request-by-token :highlights))) 5365 5366 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil 5367 "The bounds of the symbol from which `lsp--document-highlight' 5368 most recently requested highlights.") 5369 5370 (defun lsp--document-highlight () 5371 (when (lsp-feature? "textDocument/documentHighlight") 5372 (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol))) 5373 (unless (or (looking-at-p "[[:space:]\n]") 5374 (not lsp-enable-symbol-highlighting) 5375 (and lsp--have-document-highlights 5376 curr-sym-bounds 5377 (equal curr-sym-bounds 5378 lsp--symbol-bounds-of-last-highlight-invocation))) 5379 (setq lsp--symbol-bounds-of-last-highlight-invocation 5380 curr-sym-bounds) 5381 (lsp-request-async "textDocument/documentHighlight" 5382 (lsp--text-document-position-params) 5383 #'lsp--document-highlight-callback 5384 :mode 'tick 5385 :cancel-token :highlights))))) 5386 5387 (defun lsp--help-open-link (&rest _) 5388 "Open markdown link at point via mouse or keyboard." 5389 (interactive "P") 5390 (let ((buffer-list-update-hook nil)) 5391 (-let [(buffer point) (if-let* ((valid (and (listp last-input-event) 5392 (eq (car last-input-event) 'mouse-2))) 5393 (event (cadr last-input-event)) 5394 (win (posn-window event)) 5395 (buffer (window-buffer win))) 5396 `(,buffer ,(posn-point event)) 5397 `(,(current-buffer) ,(point)))] 5398 (with-current-buffer buffer 5399 (when-let* ((face (get-text-property point 'face)) 5400 (url (or (and (eq face 'markdown-link-face) 5401 (get-text-property point 'help-echo)) 5402 (and (memq face '(markdown-url-face markdown-plain-url-face)) 5403 (nth 3 (markdown-link-at-pos point)))))) 5404 (lsp--document-link-handle-target url)))))) 5405 5406 (defvar lsp-help-mode-map 5407 (-doto (make-sparse-keymap) 5408 (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link)) 5409 "Keymap for `lsp-help-mode'.") 5410 5411 (define-derived-mode lsp-help-mode help-mode "LspHelp" 5412 "Major mode for displaying lsp help.") 5413 5414 (defun lsp-describe-thing-at-point () 5415 "Display the type signature and documentation of the thing at point." 5416 (interactive) 5417 (let ((contents (-some->> (lsp--text-document-position-params) 5418 (lsp--make-request "textDocument/hover") 5419 (lsp--send-request) 5420 (lsp:hover-contents)))) 5421 (if (and contents (not (equal contents ""))) 5422 (let ((lsp-help-buf-name "*lsp-help*")) 5423 (with-current-buffer (get-buffer-create lsp-help-buf-name) 5424 (delay-mode-hooks 5425 (lsp-help-mode) 5426 (with-help-window lsp-help-buf-name 5427 (insert 5428 (mapconcat 'string-trim-right 5429 (split-string (lsp--render-on-hover-content contents t) "\n") 5430 "\n")))) 5431 (run-mode-hooks))) 5432 (lsp--info "No content at point.")))) 5433 5434 (defun lsp--point-in-bounds-p (bounds) 5435 "Return whether the current point is within BOUNDS." 5436 (and (<= (car bounds) (point)) (< (point) (cdr bounds)))) 5437 5438 (defun lsp-get-renderer (language) 5439 "Get renderer for LANGUAGE." 5440 (lambda (str) 5441 (lsp--render-string str language))) 5442 5443 (defun lsp--setup-markdown (mode) 5444 "Setup the ‘markdown-mode’ in the frame. 5445 MODE is the mode used in the parent frame." 5446 (make-local-variable 'markdown-code-lang-modes) 5447 (dolist (mark (alist-get mode lsp-custom-markup-modes)) 5448 (add-to-list 'markdown-code-lang-modes (cons mark mode))) 5449 (setq-local markdown-fontify-code-blocks-natively t) 5450 (setq-local markdown-fontify-code-block-default-mode mode) 5451 (setq-local markdown-hide-markup t) 5452 5453 ;; Render some common HTML entities. 5454 ;; This should really happen in markdown-mode instead, 5455 ;; but it doesn't, so we do it here for now. 5456 (setq prettify-symbols-alist 5457 (cl-loop for i from 0 to 255 5458 collect (cons (format "&#x%02X;" i) i))) 5459 (push '("<" . ?<) prettify-symbols-alist) 5460 (push '(">" . ?>) prettify-symbols-alist) 5461 (push '("&" . ?&) prettify-symbols-alist) 5462 (push '(" " . ? ) prettify-symbols-alist) 5463 (setq prettify-symbols-compose-predicate 5464 (lambda (_start _end _match) t)) 5465 (prettify-symbols-mode 1)) 5466 5467 (defvar lsp-help-link-keymap 5468 (let ((map (make-sparse-keymap))) 5469 (define-key map [mouse-2] #'lsp--help-open-link) 5470 (define-key map "\r" #'lsp--help-open-link) 5471 map) 5472 "Keymap active on links in *lsp-help* mode.") 5473 5474 (defun lsp--fix-markdown-links () 5475 (let ((inhibit-read-only t) 5476 (inhibit-modification-hooks t) 5477 (prop)) 5478 (save-restriction 5479 (goto-char (point-min)) 5480 (while (setq prop (markdown-find-next-prop 'face)) 5481 (let ((end (or (next-single-property-change (car prop) 'face) 5482 (point-max)))) 5483 (when (memq (get-text-property (car prop) 'face) 5484 '(markdown-link-face 5485 markdown-url-face 5486 markdown-plain-url-face)) 5487 (add-text-properties (car prop) end 5488 (list 'button t 5489 'category 'lsp-help-link 5490 'follow-link t 5491 'keymap lsp-help-link-keymap))) 5492 (goto-char end)))))) 5493 5494 (defun lsp--buffer-string-visible () 5495 "Return visible buffer string. 5496 Stolen from `org-copy-visible'." 5497 (let ((temp (generate-new-buffer " *temp*")) 5498 (beg (point-min)) 5499 (end (point-max))) 5500 (while (/= beg end) 5501 (when (get-char-property beg 'invisible) 5502 (setq beg (next-single-char-property-change beg 'invisible nil end))) 5503 (let* ((next (next-single-char-property-change beg 'invisible nil end)) 5504 (substring (buffer-substring beg next))) 5505 (with-current-buffer temp (insert substring)) 5506 ;; (setq result (concat result substring)) 5507 (setq beg next))) 5508 (setq deactivate-mark t) 5509 (prog1 (with-current-buffer temp 5510 (s-chop-suffix "\n" (buffer-string))) 5511 (kill-buffer temp)))) 5512 5513 (defvar lsp-buffer-major-mode nil 5514 "Holds the major mode when fontification function is running. 5515 See #2588") 5516 5517 (defvar view-inhibit-help-message) 5518 5519 (defun lsp--render-markdown () 5520 "Render markdown." 5521 5522 (let ((markdown-enable-math nil)) 5523 (goto-char (point-min)) 5524 (while (re-search-forward 5525 (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/" 5526 "{" "}" "[" "]" "(" ")" 5527 "#" "+" "-" "." "!" "|")))) 5528 nil t) 5529 (replace-match (rx (backref 1)))) 5530 5531 ;; markdown-mode v2.3 does not yet provide gfm-view-mode 5532 (if (fboundp 'gfm-view-mode) 5533 (let ((view-inhibit-help-message t)) 5534 (gfm-view-mode)) 5535 (gfm-mode)) 5536 5537 (lsp--setup-markdown lsp-buffer-major-mode))) 5538 5539 (defvar lsp--display-inline-image-alist 5540 '((lsp--render-markdown 5541 (:regexp 5542 "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)" 5543 :sexp 5544 (create-image 5545 (base64-decode-string 5546 (buffer-substring-no-properties (match-beginning 1) (match-end 1))) 5547 nil t)))) 5548 "Replaced string regexp and function returning image. 5549 Each element should have the form (MODE . (PROPERTY-LIST...)). 5550 MODE (car) is function which is defined in `lsp-language-id-configuration'. 5551 Cdr should be list of PROPERTY-LIST. 5552 5553 Each PROPERTY-LIST should have properties: 5554 :regexp Regexp which determines what string is relpaced to image. 5555 You should also get information of image, by parenthesis constructs. 5556 By default, all matched string is replaced to image, but you can 5557 change index of replaced string by keyword :replaced-index. 5558 5559 :sexp Return image when evaluated. You can use information of regexp 5560 by using (match-beggining N), (match-end N) or (match-substring N). 5561 5562 In addition, each can have property: 5563 :replaced-index Determine index which is used to replace regexp to image. 5564 The value means first argument of `match-beginning' and 5565 `match-end'. If omitted, interpreted as index 0.") 5566 5567 (defcustom lsp-display-inline-image t 5568 "Showing inline image or not." 5569 :group 'lsp-mode 5570 :type 'boolean) 5571 5572 (defcustom lsp-enable-suggest-server-download t 5573 "When non-nil enable server downloading suggestions." 5574 :group 'lsp-mode 5575 :type 'boolean 5576 :package-version '(lsp-mode . "9.0.0")) 5577 5578 (defcustom lsp-auto-register-remote-clients t 5579 "When non-nil register remote when registering the local one." 5580 :group 'lsp-mode 5581 :type 'boolean 5582 :package-version '(lsp-mode . "9.0.0")) 5583 5584 (defun lsp--display-inline-image (mode) 5585 "Add image property if available." 5586 (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist)))) 5587 (when (and (display-images-p) lsp-display-inline-image) 5588 (cl-loop 5589 for plist in plist-list 5590 with regexp with replaced-index 5591 do 5592 (setq regexp (plist-get plist :regexp)) 5593 (setq replaced-index (or (plist-get plist :replaced-index) 0)) 5594 5595 (font-lock-remove-keywords nil (list regexp replaced-index)) 5596 (let ((inhibit-read-only t)) 5597 (save-excursion 5598 (goto-char (point-min)) 5599 (while (re-search-forward regexp nil t) 5600 (set-text-properties 5601 (match-beginning replaced-index) (match-end replaced-index) 5602 nil) 5603 (add-text-properties 5604 (match-beginning replaced-index) (match-end replaced-index) 5605 `(display ,(eval (plist-get plist :sexp))))))))))) 5606 5607 (defun lsp--fontlock-with-mode (str mode) 5608 "Fontlock STR with MODE." 5609 (let ((lsp-buffer-major-mode major-mode)) 5610 (with-temp-buffer 5611 (with-demoted-errors "Error during doc rendering: %s" 5612 (insert str) 5613 (delay-mode-hooks (funcall mode)) 5614 (cl-flet ((window-body-width () lsp-window-body-width)) 5615 ;; This can go wrong in some cases, and the fontification would 5616 ;; not work as expected. 5617 ;; 5618 ;; See #2984 5619 (ignore-errors (font-lock-ensure)) 5620 (lsp--display-inline-image mode) 5621 (when (eq mode 'lsp--render-markdown) 5622 (lsp--fix-markdown-links)))) 5623 (lsp--buffer-string-visible)))) 5624 5625 (defun lsp--render-string (str language) 5626 "Render STR using `major-mode' corresponding to LANGUAGE. 5627 When language is nil render as markup if `markdown-mode' is loaded." 5628 (setq str (s-replace "\r" "" (or str ""))) 5629 (if-let* ((modes (-keep (-lambda ((mode . lang)) 5630 (when (and (equal lang language) (functionp mode)) 5631 mode)) 5632 lsp-language-id-configuration)) 5633 (mode (car (or (member major-mode modes) modes)))) 5634 (lsp--fontlock-with-mode str mode) 5635 str)) 5636 5637 (defun lsp--render-element (content) 5638 "Render CONTENT element." 5639 (let ((inhibit-message t)) 5640 (or 5641 (pcase content 5642 ((lsp-interface MarkedString :value :language) 5643 (lsp--render-string value language)) 5644 ((lsp-interface MarkupContent :value :kind) 5645 (lsp--render-string value kind)) 5646 ;; plain string 5647 ((pred stringp) (lsp--render-string content "markdown")) 5648 ((pred null) "") 5649 (_ (error "Failed to handle %s" content))) 5650 ""))) 5651 5652 (defun lsp--create-unique-string-fn () 5653 (let (elements) 5654 (lambda (element) 5655 (let ((count (cl-count element elements :test #'string=))) 5656 (prog1 (if (zerop count) 5657 element 5658 (format "%s (%s)" element count)) 5659 (push element elements)))))) 5660 5661 (defun lsp--select-action (actions) 5662 "Select an action to execute from ACTIONS." 5663 (cond 5664 ((seq-empty-p actions) (signal 'lsp-no-code-actions nil)) 5665 ((and (eq (seq-length actions) 1) lsp-auto-execute-action) 5666 (lsp-seq-first actions)) 5667 (t (let ((completion-ignore-case t)) 5668 (lsp--completing-read "Select code action: " 5669 (seq-into actions 'list) 5670 (-compose (lsp--create-unique-string-fn) 5671 #'lsp:code-action-title) 5672 nil t))))) 5673 5674 (defun lsp--workspace-server-id (workspace) 5675 "Return the server ID of WORKSPACE." 5676 (-> workspace lsp--workspace-client lsp--client-server-id)) 5677 5678 (defun lsp--handle-rendered-for-echo-area (contents) 5679 "Return a single line from RENDERED, appropriate for display in the echo area." 5680 (pcase (lsp-workspaces) 5681 (`(,workspace) 5682 (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace))) 5683 ;; For projects with multiple active workspaces we also default to 5684 ;; render the first line. 5685 (_ (lsp-clients-extract-signature-on-hover contents nil)))) 5686 5687 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id) 5688 "Extract a representative line from CONTENTS, to show in the echo area." 5689 (car (s-lines (s-trim (lsp--render-element contents))))) 5690 5691 (defun lsp--render-on-hover-content (contents render-all) 5692 "Render the content received from `document/onHover' request. 5693 CONTENTS - MarkedString | MarkedString[] | MarkupContent 5694 RENDER-ALL - nil if only the signature should be rendered." 5695 (cond 5696 ((lsp-markup-content? contents) 5697 ;; MarkupContent. 5698 ;; It tends to be long and is not suitable to display fully in the echo area. 5699 ;; Just display the first line which is typically the signature. 5700 (if render-all 5701 (lsp--render-element contents) 5702 (lsp--handle-rendered-for-echo-area contents))) 5703 ((and (stringp contents) (not (string-match-p "\n" contents))) 5704 ;; If the contents is a single string containing a single line, 5705 ;; render it always. 5706 (lsp--render-element contents)) 5707 (t 5708 ;; MarkedString -> MarkedString[] 5709 (when (or (lsp-marked-string? contents) (stringp contents)) 5710 (setq contents (list contents))) 5711 ;; Consider the signature consisting of the elements who have a renderable 5712 ;; "language" property. When render-all is nil, ignore other elements. 5713 (string-join 5714 (seq-map 5715 #'lsp--render-element 5716 (if render-all 5717 contents 5718 ;; Only render contents that have an available renderer. 5719 (seq-take 5720 (seq-filter 5721 (-andfn #'lsp-marked-string? 5722 (-compose #'lsp-get-renderer #'lsp:marked-string-language)) 5723 contents) 5724 1))) 5725 (if (bound-and-true-p page-break-lines-mode) 5726 "\n\n" 5727 "\n"))))) 5728 5729 5730 5731 (defvar lsp-signature-mode-map 5732 (-doto (make-sparse-keymap) 5733 (define-key (kbd "M-n") #'lsp-signature-next) 5734 (define-key (kbd "M-p") #'lsp-signature-previous) 5735 (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs) 5736 (define-key (kbd "C-c C-k") #'lsp-signature-stop) 5737 (define-key (kbd "C-g") #'lsp-signature-stop)) 5738 "Keymap for `lsp-signature-mode'.") 5739 5740 (define-minor-mode lsp-signature-mode 5741 "Mode used to show signature popup." 5742 :keymap lsp-signature-mode-map 5743 :lighter "" 5744 :group 'lsp-mode) 5745 5746 (defun lsp-signature-stop () 5747 "Stop showing current signature help." 5748 (interactive) 5749 (lsp-cancel-request-by-token :signature) 5750 (remove-hook 'post-command-hook #'lsp-signature) 5751 (funcall lsp-signature-function nil) 5752 (lsp-signature-mode -1)) 5753 5754 (declare-function page-break-lines--update-display-tables "ext:page-break-lines") 5755 5756 (defun lsp--setup-page-break-mode-if-present () 5757 "Enable `page-break-lines-mode' in current buffer." 5758 (when (fboundp 'page-break-lines-mode) 5759 (page-break-lines-mode) 5760 ;; force page-break-lines-mode to update the display tables. 5761 (page-break-lines--update-display-tables))) 5762 5763 (defun lsp-lv-message (message) 5764 (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present) 5765 (if message 5766 (progn 5767 (setq lsp--signature-last-buffer (current-buffer)) 5768 (let ((lv-force-update t)) 5769 (lv-message "%s" message))) 5770 (lv-delete-window) 5771 (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present))) 5772 5773 (declare-function posframe-show "ext:posframe") 5774 (declare-function posframe-hide "ext:posframe") 5775 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe") 5776 5777 (defface lsp-signature-posframe 5778 '((t :inherit tooltip)) 5779 "Background and foreground for `lsp-signature-posframe'." 5780 :group 'lsp-mode) 5781 5782 (defvar lsp-signature-posframe-params 5783 (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward 5784 :height 10 5785 :width 60 5786 :border-width 1 5787 :min-width 60) 5788 "Params for signature and `posframe-show'.") 5789 5790 (defun lsp-signature-posframe (str) 5791 "Use posframe to show the STR signatureHelp string." 5792 (if str 5793 (apply #'posframe-show 5794 (with-current-buffer (get-buffer-create " *lsp-signature*") 5795 (erase-buffer) 5796 (insert str) 5797 (visual-line-mode 1) 5798 (lsp--setup-page-break-mode-if-present) 5799 (current-buffer)) 5800 (append 5801 lsp-signature-posframe-params 5802 (list :position (point) 5803 :background-color (face-attribute 'lsp-signature-posframe :background nil t) 5804 :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t) 5805 :border-color (face-attribute (if (facep 'child-frame-border) 5806 'child-frame-border 5807 'internal-border) 5808 :background nil t)))) 5809 (posframe-hide " *lsp-signature*"))) 5810 5811 (defun lsp--handle-signature-update (signature) 5812 (let ((message 5813 (if (lsp-signature-help? signature) 5814 (lsp--signature->message signature) 5815 (mapconcat #'lsp--signature->message signature "\n")))) 5816 (if (s-present? message) 5817 (funcall lsp-signature-function message) 5818 (lsp-signature-stop)))) 5819 5820 (defun lsp-signature-activate () 5821 "Activate signature help. 5822 It will show up only if current point has signature help." 5823 (interactive) 5824 (setq lsp--signature-last nil 5825 lsp--signature-last-index nil 5826 lsp--signature-last-buffer (current-buffer)) 5827 (add-hook 'post-command-hook #'lsp-signature) 5828 (lsp-signature-mode t)) 5829 5830 (defcustom lsp-signature-cycle t 5831 "Whether `lsp-signature-next' and prev should cycle." 5832 :type 'boolean 5833 :group 'lsp-mode) 5834 5835 (defun lsp-signature-next () 5836 "Show next signature." 5837 (interactive) 5838 (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last)))) 5839 (when (and lsp--signature-last-index 5840 lsp--signature-last 5841 (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs))) 5842 (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs)) 5843 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))) 5844 5845 (defun lsp-signature-previous () 5846 "Next signature." 5847 (interactive) 5848 (when (and lsp--signature-last-index 5849 lsp--signature-last 5850 (or lsp-signature-cycle (not (zerop lsp--signature-last-index)))) 5851 (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index) 5852 (length (lsp:signature-help-signatures lsp--signature-last)) 5853 lsp--signature-last-index))) 5854 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))) 5855 5856 (defun lsp-signature-toggle-full-docs () 5857 "Toggle full/partial signature documentation." 5858 (interactive) 5859 (let ((all? (not (numberp lsp-signature-doc-lines)))) 5860 (setq lsp-signature-doc-lines (if all? 5861 (or (car-safe lsp-signature-doc-lines) 5862 20) 5863 (list lsp-signature-doc-lines)))) 5864 (lsp-signature-activate)) 5865 5866 (defface lsp-signature-highlight-function-argument 5867 '((t :inherit eldoc-highlight-function-argument)) 5868 "The face to use to highlight function arguments in signatures." 5869 :group 'lsp-mode) 5870 5871 (defun lsp--signature->message (signature-help) 5872 "Generate eldoc message from SIGNATURE-HELP response." 5873 (setq lsp--signature-last signature-help) 5874 5875 (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help)))) 5876 (-let* (((&SignatureHelp :active-signature? 5877 :active-parameter? 5878 :signatures) signature-help) 5879 (active-signature? (or lsp--signature-last-index active-signature? 0)) 5880 (_ (setq lsp--signature-last-index active-signature?)) 5881 ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?)) 5882 (prefix (if (= (length signatures) 1) 5883 "" 5884 (concat (propertize (format " %s/%s" 5885 (1+ active-signature?) 5886 (length signatures)) 5887 'face 'success) 5888 " "))) 5889 (method-docs (when 5890 (and lsp-signature-render-documentation 5891 (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines))) 5892 (let ((docs (lsp--render-element 5893 (lsp:parameter-information-documentation? signature)))) 5894 (when (s-present? docs) 5895 (concat 5896 "\n" 5897 (if (fboundp 'page-break-lines-mode) 5898 "\n" 5899 "") 5900 (if (and (numberp lsp-signature-doc-lines) 5901 (> (length (s-lines docs)) lsp-signature-doc-lines)) 5902 (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs))) 5903 (propertize "\nTruncated..." 'face 'highlight)) 5904 docs))))))) 5905 (when (and active-parameter? (not (seq-empty-p parameters?))) 5906 (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?))) 5907 (seq-elt parameters? active-parameter?))) 5908 (selected-param-label (let ((label (lsp:parameter-information-label param))) 5909 (if (stringp label) label (append label nil)))) 5910 (start (if (stringp selected-param-label) 5911 (s-index-of selected-param-label label) 5912 (cl-first selected-param-label))) 5913 (end (if (stringp selected-param-label) 5914 (+ start (length selected-param-label)) 5915 (cl-second selected-param-label)))) 5916 (add-face-text-property start end 'lsp-signature-highlight-function-argument nil label))) 5917 (concat prefix label method-docs)))) 5918 5919 (defun lsp-signature () 5920 "Display signature info (based on `textDocument/signatureHelp')" 5921 (if (and lsp--signature-last-buffer 5922 (not (equal (current-buffer) lsp--signature-last-buffer))) 5923 (lsp-signature-stop) 5924 (lsp-request-async "textDocument/signatureHelp" 5925 (lsp--text-document-position-params) 5926 #'lsp--handle-signature-update 5927 :cancel-token :signature))) 5928 5929 5930 (defcustom lsp-overlay-document-color-char "■" 5931 "Display the char represent the document color in overlay" 5932 :type 'string 5933 :group 'lsp-mode) 5934 5935 ;; color presentation 5936 (defun lsp--color-create-interactive-command (color range) 5937 (lambda () 5938 (interactive) 5939 (-let [(&ColorPresentation? :text-edit? 5940 :additional-text-edits?) 5941 (lsp--completing-read 5942 "Select color presentation: " 5943 (lsp-request 5944 "textDocument/colorPresentation" 5945 `( :textDocument ,(lsp--text-document-identifier) 5946 :color ,color 5947 :range ,range)) 5948 #'lsp:color-presentation-label 5949 nil 5950 t)] 5951 (when text-edit? 5952 (lsp--apply-text-edit text-edit?)) 5953 (when additional-text-edits? 5954 (lsp--apply-text-edits additional-text-edits? 'color-presentation))))) 5955 5956 (defun lsp--number->color (number) 5957 (let ((result (format "%x" 5958 (round (* (or number 0) 255.0))))) 5959 (if (= 1 (length result)) 5960 (concat "0" result) 5961 result))) 5962 5963 (defun lsp--document-color () 5964 "Document color handler." 5965 (when (lsp-feature? "textDocument/documentColor") 5966 (lsp-request-async 5967 "textDocument/documentColor" 5968 `(:textDocument ,(lsp--text-document-identifier)) 5969 (lambda (result) 5970 (lsp--remove-overlays 'lsp-color) 5971 (seq-do 5972 (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue) 5973 :range)) 5974 (-let* (((beg . end) (lsp--range-to-region range)) 5975 (overlay (make-overlay beg end)) 5976 (command (lsp--color-create-interactive-command color range))) 5977 (overlay-put overlay 'lsp-color t) 5978 (overlay-put overlay 'evaporate t) 5979 (overlay-put overlay 5980 'before-string 5981 (propertize 5982 lsp-overlay-document-color-char 5983 'face `((:foreground ,(format 5984 "#%s%s%s" 5985 (lsp--number->color red) 5986 (lsp--number->color green) 5987 (lsp--number->color blue)))) 5988 'action command 5989 'mouse-face 'lsp-lens-mouse-face 5990 'local-map (-doto (make-sparse-keymap) 5991 (define-key [mouse-1] command)))))) 5992 result)) 5993 :mode 'unchanged 5994 :cancel-token :document-color-token))) 5995 5996 5997 5998 (defun lsp--action-trigger-parameter-hints (_command) 5999 "Handler for editor.action.triggerParameterHints." 6000 (when (member :on-server-request lsp-signature-auto-activate) 6001 (lsp-signature-activate))) 6002 6003 (defun lsp--action-trigger-suggest (_command) 6004 "Handler for editor.action.triggerSuggest." 6005 (cond 6006 ((and (bound-and-true-p company-mode) 6007 (fboundp 'company-auto-begin) 6008 (fboundp 'company-post-command)) 6009 (run-at-time 0 nil 6010 (lambda () 6011 (let ((this-command 'company-idle-begin) 6012 (company-minimum-prefix-length 0)) 6013 (company-auto-begin) 6014 (company-post-command))))) 6015 (t 6016 (completion-at-point)))) 6017 6018 (defconst lsp--default-action-handlers 6019 (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints) 6020 ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest)) 6021 "Default action handlers.") 6022 6023 (defun lsp--find-action-handler (command) 6024 "Find action handler for particular COMMAND." 6025 (or 6026 (--some (-some->> it 6027 (lsp--workspace-client) 6028 (lsp--client-action-handlers) 6029 (gethash command)) 6030 (lsp-workspaces)) 6031 (gethash command lsp--default-action-handlers))) 6032 6033 (defun lsp--text-document-code-action-params (&optional kind) 6034 "Code action params." 6035 (list :textDocument (lsp--text-document-identifier) 6036 :range (if (use-region-p) 6037 (lsp--region-to-range (region-beginning) (region-end)) 6038 (lsp--region-to-range (point) (point))) 6039 :context `( :diagnostics ,(lsp-cur-possition-diagnostics) 6040 ,@(when kind (list :only (vector kind)))))) 6041 6042 (defun lsp-code-actions-at-point (&optional kind) 6043 "Retrieve the code actions for the active region or the current line. 6044 It will filter by KIND if non nil." 6045 (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind))) 6046 6047 (defun lsp-execute-code-action-by-kind (command-kind) 6048 "Execute code action by COMMAND-KIND." 6049 (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind) 6050 (-filter (-lambda ((&CodeAction :kind?)) 6051 (and kind? (s-prefix? command-kind kind?)))) 6052 lsp--select-action))) 6053 (lsp-execute-code-action action) 6054 (signal 'lsp-no-code-actions '(command-kind)))) 6055 6056 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point) 6057 6058 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?)) 6059 "Parse and execute a code ACTION represented as a Command LSP type." 6060 (let ((server-id (->> (lsp-workspaces) 6061 (cl-first) 6062 (or lsp--cur-workspace) 6063 (lsp--workspace-client) 6064 (lsp--client-server-id)))) 6065 (condition-case nil 6066 (with-no-warnings 6067 (lsp-execute-command server-id (intern command) arguments?)) 6068 (cl-no-applicable-method 6069 (if-let ((action-handler (lsp--find-action-handler command))) 6070 (funcall action-handler action) 6071 (lsp-send-execute-command command arguments?)))))) 6072 6073 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?)) 6074 "Execute code action ACTION. For example, when text under the 6075 caret has a suggestion to apply a fix from an lsp-server, calling 6076 this function will do so. 6077 If ACTION is not set it will be selected from `lsp-code-actions-at-point'. 6078 Request codeAction/resolve for more info if server supports." 6079 (interactive (list (lsp--select-action (lsp-code-actions-at-point)))) 6080 (if (and (lsp-feature? "codeAction/resolve") 6081 (not command?) 6082 (not edit?)) 6083 (lsp--execute-code-action (lsp-request "codeAction/resolve" action)) 6084 (lsp--execute-code-action action))) 6085 6086 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?)) 6087 "Execute code action ACTION." 6088 (when edit? 6089 (lsp--apply-workspace-edit edit? 'code-action)) 6090 6091 (cond 6092 ((stringp command?) (lsp--execute-command action)) 6093 ((lsp-command? command?) (progn 6094 (when-let ((action-filter (->> (lsp-workspaces) 6095 (cl-first) 6096 (or lsp--cur-workspace) 6097 (lsp--workspace-client) 6098 (lsp--client-action-filter)))) 6099 (funcall action-filter command?)) 6100 (lsp--execute-command command?))))) 6101 6102 (lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments) 6103 "Patch incorrect boolean argument values in the provided `CodeAction' command 6104 in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values 6105 in this list can be either symbols or lists of symbols that 6106 represent paths to boolean arguments in code actions: 6107 6108 > (lsp-fix-code-action-booleans command `(:foo :bar (:some :nested :boolean))) 6109 6110 When there are available code actions, the server sends 6111 `lsp-mode' a list of possible command names and arguments as 6112 JSON. `lsp-mode' parses all boolean false values as `nil'. As a 6113 result code action arguments containing falsy values don't 6114 roundtrip correctly because `lsp-mode' will end up sending null 6115 values back to the client. This list makes it possible to 6116 selectively transform `nil' values back into `:json-false'." 6117 (seq-doseq (path boolean-action-arguments) 6118 (seq-doseq (args arguments?) 6119 (lsp--fix-nested-boolean args (if (listp path) path (list path)))))) 6120 6121 (defun lsp--fix-nested-boolean (structure path) 6122 "Traverse STRUCTURE using the paths from the PATH list, changing the value to 6123 `:json-false' if it was `nil'. PATH should be a list containing 6124 one or more symbols, and STRUCTURE should be compatible with 6125 `lsp-member?', `lsp-get', and `lsp-put'." 6126 (let ((key (car path)) 6127 (rest (cdr path))) 6128 (if (null rest) 6129 ;; `lsp-put' returns `nil' both when the key doesn't exist and when the 6130 ;; value is `nil', so we need to explicitly check its presence here 6131 (when (and (lsp-member? structure key) (not (lsp-get structure key))) 6132 (lsp-put structure key :json-false)) 6133 ;; If `key' does not exist, then we'll silently ignore it 6134 (when-let ((child (lsp-get structure key))) 6135 (lsp--fix-nested-boolean child rest))))) 6136 6137 (defvar lsp--formatting-indent-alist 6138 ;; Taken from `dtrt-indent-mode' 6139 '( 6140 (ada-mode . ada-indent) ; Ada 6141 (ada-ts-mode . ada-ts-mode-indent-offset) 6142 (c++-mode . c-basic-offset) ; C++ 6143 (c++-ts-mode . c-ts-mode-indent-offset) 6144 (c-mode . c-basic-offset) ; C 6145 (c-ts-mode . c-ts-mode-indent-offset) 6146 (cperl-mode . cperl-indent-level) ; Perl 6147 (crystal-mode . crystal-indent-level) ; Crystal (Ruby) 6148 (csharp-mode . c-basic-offset) ; C# 6149 (csharp-tree-sitter-mode . csharp-tree-sitter-indent-offset) ; C# 6150 (csharp-ts-mode . csharp-ts-mode-indent-offset) ; C# (tree-sitter, Emacs29) 6151 (css-mode . css-indent-offset) ; CSS 6152 (d-mode . c-basic-offset) ; D 6153 (enh-ruby-mode . enh-ruby-indent-level) ; Ruby 6154 (erlang-mode . erlang-indent-level) ; Erlang 6155 (ess-mode . ess-indent-offset) ; ESS (R) 6156 (go-ts-mode . go-ts-mode-indent-offset) 6157 (gpr-mode . gpr-indent-offset) ; GNAT Project 6158 (gpr-ts-mode . gpr-ts-mode-indent-offset) 6159 (hack-mode . hack-indent-offset) ; Hack 6160 (java-mode . c-basic-offset) ; Java 6161 (java-ts-mode . java-ts-mode-indent-offset) 6162 (jde-mode . c-basic-offset) ; Java (JDE) 6163 (js-mode . js-indent-level) ; JavaScript 6164 (js-ts-mode . js-indent-level) 6165 (js2-mode . js2-basic-offset) ; JavaScript-IDE 6166 (js3-mode . js3-indent-level) ; JavaScript-IDE 6167 (json-mode . js-indent-level) ; JSON 6168 (json-ts-mode . json-ts-mode-indent-offset) 6169 (lua-mode . lua-indent-level) ; Lua 6170 (lua-ts-mode . lua-ts-indent-offset) 6171 (nxml-mode . nxml-child-indent) ; XML 6172 (objc-mode . c-basic-offset) ; Objective C 6173 (pascal-mode . pascal-indent-level) ; Pascal 6174 (perl-mode . perl-indent-level) ; Perl 6175 (php-mode . c-basic-offset) ; PHP 6176 (php-ts-mode . php-ts-mode-indent-offset) ; PHP 6177 (powershell-mode . powershell-indent) ; PowerShell 6178 (powershell-ts-mode . powershell-ts-mode-indent-offset) ; PowerShell 6179 (raku-mode . raku-indent-offset) ; Perl6/Raku 6180 (ruby-mode . ruby-indent-level) ; Ruby 6181 (rust-mode . rust-indent-offset) ; Rust 6182 (rust-ts-mode . rust-ts-mode-indent-offset) 6183 (rustic-mode . rustic-indent-offset) ; Rust 6184 (scala-mode . scala-indent:step) ; Scala 6185 (sgml-mode . sgml-basic-offset) ; SGML 6186 (sh-mode . sh-basic-offset) ; Shell Script 6187 (toml-ts-mode . toml-ts-mode-indent-offset) 6188 (typescript-mode . typescript-indent-level) ; Typescript 6189 (typescript-ts-mode . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29) 6190 (yaml-mode . yaml-indent-offset) ; YAML 6191 (yang-mode . c-basic-offset) ; YANG (yang-mode) 6192 6193 (default . standard-indent)) ; default fallback 6194 "A mapping from `major-mode' to its indent variable.") 6195 6196 (defun lsp--get-indent-width (mode) 6197 "Get indentation offset for MODE." 6198 (or (alist-get mode lsp--formatting-indent-alist) 6199 (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default)))) 6200 6201 (defun lsp--make-document-formatting-params () 6202 "Create document formatting params." 6203 (lsp-make-document-formatting-params 6204 :text-document (lsp--text-document-identifier) 6205 :options (lsp-make-formatting-options 6206 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 6207 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 6208 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 6209 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 6210 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)))) 6211 6212 (defun lsp-format-buffer () 6213 "Ask the server to format this document." 6214 (interactive "*") 6215 (cond ((lsp-feature? "textDocument/formatting") 6216 (let ((edits (lsp-request "textDocument/formatting" 6217 (lsp--make-document-formatting-params)))) 6218 (if (seq-empty-p edits) 6219 (lsp--info "No formatting changes provided") 6220 (lsp--apply-text-edits edits 'format)))) 6221 ((lsp-feature? "textDocument/rangeFormatting") 6222 (save-restriction 6223 (widen) 6224 (lsp-format-region (point-min) (point-max)))) 6225 (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider"))))) 6226 6227 (defun lsp-format-region (s e) 6228 "Ask the server to format the region, or if none is selected, the current line." 6229 (interactive "r") 6230 (let ((edits (lsp-request 6231 "textDocument/rangeFormatting" 6232 (lsp--make-document-range-formatting-params s e)))) 6233 (if (seq-empty-p edits) 6234 (lsp--info "No formatting changes provided") 6235 (lsp--apply-text-edits edits 'format)))) 6236 6237 (defmacro lsp-make-interactive-code-action (func-name code-action-kind) 6238 "Define an interactive function FUNC-NAME that attempts to 6239 execute a CODE-ACTION-KIND action." 6240 `(defun ,(intern (concat "lsp-" (symbol-name func-name))) () 6241 ,(format "Perform the %s code action, if available." code-action-kind) 6242 (interactive) 6243 ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to 6244 ;; auto-execute here: the user has specified exactly what they want. 6245 (let ((lsp-auto-execute-action t)) 6246 (condition-case nil 6247 (lsp-execute-code-action-by-kind ,code-action-kind) 6248 (lsp-no-code-actions 6249 (when (called-interactively-p 'any) 6250 (lsp--info ,(format "%s action not available" code-action-kind)))))))) 6251 6252 (lsp-make-interactive-code-action organize-imports "source.organizeImports") 6253 6254 (defun lsp--make-document-range-formatting-params (start end) 6255 "Make DocumentRangeFormattingParams for selected region." 6256 (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params) 6257 (lsp--region-to-range start end))) 6258 6259 (defconst lsp--highlight-kind-face 6260 '((1 . lsp-face-highlight-textual) 6261 (2 . lsp-face-highlight-read) 6262 (3 . lsp-face-highlight-write))) 6263 6264 (defun lsp--remove-overlays (name) 6265 (save-restriction 6266 (widen) 6267 (remove-overlays (point-min) (point-max) name t))) 6268 6269 (defun lsp-document-highlight () 6270 "Highlight all relevant references to the symbol under point." 6271 (interactive) 6272 (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights 6273 (setq lsp--have-document-highlights nil 6274 lsp--symbol-bounds-of-last-highlight-invocation nil) 6275 (let ((lsp-enable-symbol-highlighting t)) 6276 (lsp--document-highlight))) 6277 6278 (defun lsp--document-highlight-callback (highlights) 6279 "Create a callback to process the reply of a 6280 `textDocument/documentHighlight' message for the buffer BUF. 6281 A reference is highlighted only if it is visible in a window." 6282 (lsp--remove-overlays 'lsp-highlight) 6283 6284 (let* ((wins-visible-pos (-map (lambda (win) 6285 (cons (1- (line-number-at-pos (window-start win) t)) 6286 (1+ (line-number-at-pos (window-end win) t)))) 6287 (get-buffer-window-list nil nil 'visible)))) 6288 (setq lsp--have-document-highlights t) 6289 (-map 6290 (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line) 6291 :end (end &as &Position :line end-line)) 6292 :kind?)) 6293 (-map 6294 (-lambda ((start-window . end-window)) 6295 ;; Make the overlay only if the reference is visible 6296 (when (and (> (1+ start-line) start-window) 6297 (< (1+ end-line) end-window)) 6298 (let ((start-point (lsp--position-to-point start)) 6299 (end-point (lsp--position-to-point end))) 6300 (when (not (and lsp-symbol-highlighting-skip-current 6301 (<= start-point (point) end-point))) 6302 (-doto (make-overlay start-point end-point) 6303 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face))) 6304 (overlay-put 'lsp-highlight t)))))) 6305 wins-visible-pos)) 6306 highlights))) 6307 6308 (defcustom lsp-symbol-kinds 6309 '((1 . "File") 6310 (2 . "Module") 6311 (3 . "Namespace") 6312 (4 . "Package") 6313 (5 . "Class") 6314 (6 . "Method") 6315 (7 . "Property") 6316 (8 . "Field") 6317 (9 . "Constructor") 6318 (10 . "Enum") 6319 (11 . "Interface") 6320 (12 . "Function") 6321 (13 . "Variable") 6322 (14 . "Constant") 6323 (15 . "String") 6324 (16 . "Number") 6325 (17 . "Boolean") 6326 (18 . "Array") 6327 (19 . "Object") 6328 (20 . "Key") 6329 (21 . "Null") 6330 (22 . "Enum Member") 6331 (23 . "Struct") 6332 (24 . "Event") 6333 (25 . "Operator") 6334 (26 . "Type Parameter")) 6335 "Alist mapping SymbolKinds to human-readable strings. 6336 Various Symbol objects in the LSP protocol have an integral type, 6337 specifying what they are. This alist maps such type integrals to 6338 readable representations of them. See 6339 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/', 6340 namespace SymbolKind." 6341 :group 'lsp-mode 6342 :type '(alist :key-type integer :value-type string)) 6343 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds) 6344 6345 (lsp-defun lsp--symbol-information-to-xref 6346 ((&SymbolInformation :kind :name 6347 :location (&Location :uri :range (&Range :start 6348 (&Position :line :character))))) 6349 "Return a `xref-item' from SYMBOL information." 6350 (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name) 6351 (xref-make-file-location (lsp--uri-to-path uri) 6352 line 6353 character))) 6354 6355 (defun lsp--get-document-symbols () 6356 "Get document symbols. 6357 6358 If the buffer has not been modified since symbols were last 6359 retrieved, simply return the latest result. 6360 6361 Else, if the request was initiated by Imenu updating its menu-bar 6362 entry, perform it asynchronously; i.e., give Imenu the latest 6363 result and then force a refresh when a new one is available. 6364 6365 Else (e.g., due to interactive use of `imenu' or `xref'), 6366 perform the request synchronously." 6367 (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick) 6368 lsp--document-symbols 6369 (let ((method "textDocument/documentSymbol") 6370 (params `(:textDocument ,(lsp--text-document-identifier))) 6371 (tick (buffer-chars-modified-tick))) 6372 (if (not lsp--document-symbols-request-async) 6373 (prog1 6374 (setq lsp--document-symbols (lsp-request method params)) 6375 (setq lsp--document-symbols-tick tick)) 6376 (lsp-request-async method params 6377 (lambda (document-symbols) 6378 (setq lsp--document-symbols document-symbols 6379 lsp--document-symbols-tick tick) 6380 (lsp--imenu-refresh)) 6381 :mode 'alive 6382 :cancel-token :document-symbols) 6383 lsp--document-symbols)))) 6384 6385 (advice-add 'imenu-update-menubar :around 6386 (lambda (oldfun &rest r) 6387 (let ((lsp--document-symbols-request-async t)) 6388 (apply oldfun r)))) 6389 6390 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position) 6391 "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION." 6392 (-let (((symbol &as &DocumentSymbol? :children?) 6393 (seq-find (-lambda ((&DocumentSymbol :range)) 6394 (lsp-point-in-range? current-position range)) 6395 document-symbols))) 6396 (if children? 6397 (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position)) 6398 (when symbol 6399 (list symbol))))) 6400 6401 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?)) 6402 "Convert a SymbolInformation to a DocumentInformation" 6403 (lsp-make-document-symbol :name name 6404 :kind kind 6405 :range (lsp:location-range location) 6406 :children? nil 6407 :deprecated? deprecated? 6408 :selection-range (lsp:location-range location) 6409 :detail? container-name?)) 6410 6411 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position) 6412 "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION." 6413 (--> symbols-informations 6414 (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range))) 6415 (when (lsp-point-in-range? current-position range) 6416 (lsp--symbol-information->document-symbol symbol))) 6417 it) 6418 (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position)) 6419 (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position))) 6420 (and (lsp--position-compare b-start-position a-start-position) 6421 (lsp--position-compare a-end-position b-end-position)))))) 6422 6423 (defun lsp--symbols->document-symbols-hierarchy (symbols) 6424 "Convert SYMBOLS to symbols-hierarchy." 6425 (when-let ((first-symbol (lsp-seq-first symbols))) 6426 (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line) 6427 :character (plist-get (lsp--cur-position) :character)))) 6428 (if (lsp-symbol-information? first-symbol) 6429 (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position) 6430 (lsp--document-symbols->document-symbols-hierarchy symbols cur-position))))) 6431 6432 (defun lsp--xref-backend () 'xref-lsp) 6433 6434 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp))) 6435 (propertize (or (thing-at-point 'symbol) "") 6436 'identifier-at-point t)) 6437 6438 (defun lsp--xref-elements-index (symbols path) 6439 (-mapcat 6440 (-lambda (sym) 6441 (pcase-exhaustive sym 6442 ((lsp-interface DocumentSymbol :name :children? :selection-range (lsp-interface Range :start)) 6443 (cons (cons (concat path name) 6444 (lsp--position-to-point start)) 6445 (lsp--xref-elements-index children? (concat path name " / ")))) 6446 ((lsp-interface SymbolInformation :name :location (lsp-interface Location :range (lsp-interface Range :start))) 6447 (list (cons (concat path name) 6448 (lsp--position-to-point start)))))) 6449 symbols)) 6450 6451 (defvar-local lsp--symbols-cache nil) 6452 6453 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp))) 6454 (if (lsp--find-workspaces-for "textDocument/documentSymbol") 6455 (progn 6456 (setq lsp--symbols-cache (lsp--xref-elements-index 6457 (lsp--get-document-symbols) nil)) 6458 lsp--symbols-cache) 6459 (list (propertize (or (thing-at-point 'symbol) "") 6460 'identifier-at-point t)))) 6461 6462 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier) 6463 (save-excursion 6464 (unless (get-text-property 0 'identifier-at-point identifier) 6465 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6466 (user-error "Unable to find symbol %s in current document" identifier))))) 6467 (lsp--locations-to-xref-items (lsp-request "textDocument/definition" 6468 (lsp--text-document-position-params))))) 6469 6470 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier) 6471 (save-excursion 6472 (unless (get-text-property 0 'identifier-at-point identifier) 6473 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6474 (user-error "Unable to find symbol %s" identifier))))) 6475 (lsp--locations-to-xref-items (lsp-request "textDocument/references" 6476 (lsp--make-reference-params nil lsp-references-exclude-definition))))) 6477 6478 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern) 6479 (seq-map #'lsp--symbol-information-to-xref 6480 (lsp-request "workspace/symbol" `(:query ,pattern)))) 6481 6482 (defcustom lsp-rename-use-prepare t 6483 "Whether `lsp-rename' should do a prepareRename first. 6484 For some language servers, textDocument/prepareRename might be 6485 too slow, in which case this variable may be set to nil. 6486 `lsp-rename' will then use `thing-at-point' `symbol' to determine 6487 the symbol to rename at point." 6488 :group 'lsp-mode 6489 :type 'boolean) 6490 6491 (defun lsp--get-symbol-to-rename () 6492 "Get a symbol to rename and placeholder at point. 6493 Returns a cons ((START . END) . PLACEHOLDER?), and nil if 6494 renaming is generally supported but cannot be done at point. 6495 START and END are the bounds of the identifiers being renamed, 6496 while PLACEHOLDER?, is either nil or a string suggested by the 6497 language server as the initial input of a new-name prompt." 6498 (unless (lsp-feature? "textDocument/rename") 6499 (error "The connected server(s) doesn't support renaming")) 6500 (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename")) 6501 (when-let ((response 6502 (lsp-request "textDocument/prepareRename" 6503 (lsp--text-document-position-params)))) 6504 (let* ((bounds (lsp--range-to-region 6505 (if (lsp-range? response) 6506 response 6507 (lsp:prepare-rename-result-range response)))) 6508 (placeholder 6509 (and (not (lsp-range? response)) 6510 (lsp:prepare-rename-result-placeholder response)))) 6511 (cons bounds placeholder))) 6512 (when-let ((bounds (bounds-of-thing-at-point 'symbol))) 6513 (cons bounds nil)))) 6514 6515 (defface lsp-face-rename '((t :underline t)) 6516 "Face used to highlight the identifier being renamed. 6517 Renaming can be done using `lsp-rename'." 6518 :group 'lsp-mode) 6519 6520 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face)) 6521 "Face used to display the rename placeholder in. 6522 When calling `lsp-rename' interactively, this will be the face of 6523 the new name." 6524 :group 'lsp-mode) 6525 6526 (defvar lsp-rename-history '() 6527 "History for `lsp--read-rename'.") 6528 6529 (defun lsp--read-rename (at-point) 6530 "Read a new name for a `lsp-rename' at `point' from the user. 6531 AT-POINT shall be a structure as returned by 6532 `lsp--get-symbol-to-rename'. 6533 6534 Returns a string, which should be the new name for the identifier 6535 at point. If renaming cannot be done at point (as determined from 6536 AT-POINT), throw a `user-error'. 6537 6538 This function is for use in `lsp-rename' only, and shall not be 6539 relied upon." 6540 (unless at-point 6541 (user-error "`lsp-rename' is invalid here")) 6542 (-let* ((((start . end) . placeholder?) at-point) 6543 ;; Do the `buffer-substring' first to not include `lsp-face-rename' 6544 (rename-me (buffer-substring start end)) 6545 (placeholder (or placeholder? rename-me)) 6546 (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face)) 6547 6548 overlay) 6549 ;; We need unwind protect, as the user might cancel here, causing the 6550 ;; overlay to linger. 6551 (unwind-protect 6552 (progn 6553 (setq overlay (make-overlay start end)) 6554 (overlay-put overlay 'face 'lsp-face-rename) 6555 6556 (read-string (format "Rename %s to: " rename-me) placeholder 6557 'lsp-rename-history)) 6558 (and overlay (delete-overlay overlay))))) 6559 6560 (defun lsp-rename (newname) 6561 "Rename the symbol (and all references to it) under point to NEWNAME." 6562 (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename)))) 6563 (when-let ((edits (lsp-request "textDocument/rename" 6564 `( :textDocument ,(lsp--text-document-identifier) 6565 :position ,(lsp--cur-position) 6566 :newName ,newname)))) 6567 (lsp--apply-workspace-edit edits 'rename))) 6568 6569 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?) 6570 "Advice around function `rename-file'. 6571 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?. 6572 6573 This advice sends workspace/willRenameFiles before renaming file 6574 to check if server wants to apply any workspaceEdits after renamed." 6575 (if (and lsp-apply-edits-after-file-operations 6576 (lsp--send-will-rename-files-p old-name)) 6577 (let ((params (lsp-make-rename-files-params 6578 :files (vector (lsp-make-file-rename 6579 :oldUri (lsp--path-to-uri old-name) 6580 :newUri (lsp--path-to-uri new-name)))))) 6581 (when-let ((edits (lsp-request "workspace/willRenameFiles" params))) 6582 (lsp--apply-workspace-edit edits 'rename-file) 6583 (funcall old-func old-name new-name ok-if-already-exists?) 6584 (when (lsp--send-did-rename-files-p) 6585 (lsp-notify "workspace/didRenameFiles" params)))) 6586 (funcall old-func old-name new-name ok-if-already-exists?))) 6587 6588 (advice-add 'rename-file :around #'lsp--on-rename-file) 6589 6590 (defcustom lsp-xref-force-references nil 6591 "If non-nil threat everything as references(e. g. jump if only one item.)" 6592 :group 'lsp-mode 6593 :type 'boolean) 6594 6595 (defun lsp-show-xrefs (xrefs display-action references?) 6596 (unless (region-active-p) (push-mark nil t)) 6597 (if (boundp 'xref-show-definitions-function) 6598 (with-no-warnings 6599 (xref-push-marker-stack) 6600 (funcall (if (and references? (not lsp-xref-force-references)) 6601 xref-show-xrefs-function 6602 xref-show-definitions-function) 6603 (-const xrefs) 6604 `((window . ,(selected-window)) 6605 (display-action . ,display-action) 6606 ,(if (and references? (not lsp-xref-force-references)) 6607 `(auto-jump . ,xref-auto-jump-to-first-xref) 6608 `(auto-jump . ,xref-auto-jump-to-first-definition))))) 6609 (xref--show-xrefs xrefs display-action))) 6610 6611 (cl-defmethod seq-empty-p ((ht hash-table)) 6612 "Function `seq-empty-p' for hash-table." 6613 (hash-table-empty-p ht)) 6614 6615 (cl-defun lsp-find-locations (method &optional extra &key display-action references?) 6616 "Send request named METHOD and get cross references of the symbol under point. 6617 EXTRA is a plist of extra parameters. 6618 REFERENCES? t when METHOD returns references." 6619 (let ((loc (lsp-request method 6620 (append (lsp--text-document-position-params) extra)))) 6621 (if (seq-empty-p loc) 6622 (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) "")) 6623 (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?)))) 6624 6625 (cl-defun lsp-find-declaration (&key display-action) 6626 "Find declarations of the symbol under point." 6627 (interactive) 6628 (lsp-find-locations "textDocument/declaration" nil :display-action display-action)) 6629 6630 (cl-defun lsp-find-definition (&key display-action) 6631 "Find definitions of the symbol under point." 6632 (interactive) 6633 (lsp-find-locations "textDocument/definition" nil :display-action display-action)) 6634 6635 (defun lsp-find-definition-mouse (click) 6636 "Click to start `lsp-find-definition' at clicked point." 6637 (interactive "e") 6638 (let* ((ec (event-start click)) 6639 (p1 (posn-point ec)) 6640 (w1 (posn-window ec))) 6641 (select-window w1) 6642 (goto-char p1) 6643 (lsp-find-definition))) 6644 6645 (cl-defun lsp-find-implementation (&key display-action) 6646 "Find implementations of the symbol under point." 6647 (interactive) 6648 (lsp-find-locations "textDocument/implementation" 6649 nil 6650 :display-action display-action 6651 :references? t)) 6652 6653 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action) 6654 "Find references of the symbol under point." 6655 (interactive "P") 6656 (lsp-find-locations "textDocument/references" 6657 (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition))))) 6658 :display-action display-action 6659 :references? t)) 6660 6661 (cl-defun lsp-find-type-definition (&key display-action) 6662 "Find type definitions of the symbol under point." 6663 (interactive) 6664 (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action)) 6665 6666 (defalias 'lsp-find-custom #'lsp-find-locations) 6667 (defalias 'lsp-goto-implementation #'lsp-find-implementation) 6668 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition) 6669 6670 (with-eval-after-load 'evil 6671 (evil-set-command-property 'lsp-find-definition :jump t) 6672 (evil-set-command-property 'lsp-find-implementation :jump t) 6673 (evil-set-command-property 'lsp-find-references :jump t) 6674 (evil-set-command-property 'lsp-find-type-definition :jump t)) 6675 6676 (defun lsp--workspace-method-supported? (check-command method capability workspace) 6677 (with-lsp-workspace workspace 6678 (if check-command 6679 (funcall check-command workspace) 6680 (or 6681 (when capability (lsp--capability capability)) 6682 (lsp--registered-capability method) 6683 (and (not capability) (not check-command)))))) 6684 6685 (defun lsp-disable-method-for-server (method server-id) 6686 "Disable METHOD for SERVER-ID." 6687 (cl-callf 6688 (lambda (reqs) 6689 (-let (((&plist :check-command :capability) reqs)) 6690 (list :check-command 6691 (lambda (workspace) 6692 (unless (-> workspace 6693 lsp--workspace-client 6694 lsp--client-server-id 6695 (eq server-id)) 6696 (lsp--workspace-method-supported? check-command 6697 method 6698 capability 6699 workspace)))))) 6700 (alist-get method lsp-method-requirements nil nil 'string=))) 6701 6702 (defun lsp--find-workspaces-for (msg-or-method) 6703 "Find all workspaces in the current project that can handle MSG." 6704 (let ((method (if (stringp msg-or-method) 6705 msg-or-method 6706 (plist-get msg-or-method :method)))) 6707 (-if-let (reqs (cdr (assoc method lsp-method-requirements))) 6708 (-let (((&plist :capability :check-command) reqs)) 6709 (-filter 6710 (-partial #'lsp--workspace-method-supported? 6711 check-command method capability) 6712 (lsp-workspaces))) 6713 (lsp-workspaces)))) 6714 6715 (defun lsp-can-execute-command? (command-name) 6716 "Returns non-nil if current language server(s) can execute COMMAND-NAME. 6717 The command is executed via `workspace/executeCommand'" 6718 (cl-position 6719 command-name 6720 (lsp:execute-command-options-commands 6721 (lsp:server-capabilities-execute-command-provider? 6722 (lsp--server-capabilities))) 6723 :test #'equal)) 6724 6725 (defalias 'lsp-feature? 'lsp--find-workspaces-for) 6726 6727 (cl-defmethod lsp-execute-command (_server _command _arguments) 6728 "Dispatch COMMAND execution." 6729 (signal 'cl-no-applicable-method nil)) 6730 6731 (defun lsp-workspace-command-execute (command &optional args) 6732 "Execute workspace COMMAND with ARGS." 6733 (condition-case-unless-debug err 6734 (let ((params (if args 6735 (list :command command :arguments args) 6736 (list :command command)))) 6737 (lsp-request "workspace/executeCommand" params)) 6738 (error 6739 (error "`workspace/executeCommand' with `%s' failed.\n\n%S" 6740 command err)))) 6741 6742 (defun lsp-send-execute-command (command &optional args) 6743 "Create and send a `workspace/executeCommand' message having command COMMAND 6744 and optional ARGS." 6745 (lsp-workspace-command-execute command args)) 6746 6747 (defalias 'lsp-point-to-position #'lsp--point-to-position) 6748 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier) 6749 (defalias 'lsp--send-execute-command #'lsp-send-execute-command) 6750 (defalias 'lsp-on-open #'lsp--text-document-did-open) 6751 (defalias 'lsp-on-save #'lsp--text-document-did-save) 6752 6753 (defun lsp--set-configuration (settings) 6754 "Set the SETTINGS for the lsp server." 6755 (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings))) 6756 6757 (defun lsp-current-buffer () 6758 (or lsp--virtual-buffer 6759 (current-buffer))) 6760 6761 (defun lsp-buffer-live-p (buffer-id) 6762 (if-let ((buffer-live (plist-get buffer-id :buffer-live?))) 6763 (funcall buffer-live buffer-id) 6764 (buffer-live-p buffer-id))) 6765 6766 (defun lsp--on-set-visited-file-name (old-func &rest args) 6767 "Advice around function `set-visited-file-name'. 6768 6769 This advice sends textDocument/didClose for the old file and 6770 textDocument/didOpen for the new file." 6771 (when lsp--cur-workspace 6772 (lsp--text-document-did-close t)) 6773 (prog1 (apply old-func args) 6774 (when lsp--cur-workspace 6775 (lsp--text-document-did-open)))) 6776 6777 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name) 6778 6779 (defcustom lsp-flush-delayed-changes-before-next-message t 6780 "If non-nil send the document changes update before sending other messages. 6781 6782 If nil, and `lsp-debounce-full-sync-notifications' is non-nil, 6783 change notifications will be throttled by 6784 `lsp-debounce-full-sync-notifications-interval' regardless of 6785 other messages." 6786 :group 'lsp-mode 6787 :type 'boolean) 6788 6789 (defvar lsp--not-flushing-delayed-changes t) 6790 6791 (defun lsp--send-no-wait (message proc) 6792 "Send MESSAGE to PROC without waiting for further output." 6793 6794 (when (and lsp--not-flushing-delayed-changes 6795 lsp-flush-delayed-changes-before-next-message) 6796 (let ((lsp--not-flushing-delayed-changes nil)) 6797 (lsp--flush-delayed-changes))) 6798 (lsp-process-send proc message)) 6799 6800 (define-error 'lsp-parse-error 6801 "Error parsing message from language server" 'lsp-error) 6802 (define-error 'lsp-unknown-message-type 6803 "Unknown message type" '(lsp-error lsp-parse-error)) 6804 (define-error 'lsp-unknown-json-rpc-version 6805 "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error)) 6806 (define-error 'lsp-no-content-length 6807 "Content-Length header missing in message" '(lsp-error lsp-parse-error)) 6808 (define-error 'lsp-invalid-header-name 6809 "Invalid header name" '(lsp-error lsp-parse-error)) 6810 6811 ;; id method 6812 ;; x x request 6813 ;; x . response 6814 ;; . x notification 6815 (defun lsp--get-message-type (json-data) 6816 "Get the message type from JSON-DATA." 6817 (if (lsp:json-message-id? json-data) 6818 (if (lsp:json-message-error? json-data) 6819 'response-error 6820 (if (lsp:json-message-method? json-data) 6821 'request 6822 'response)) 6823 'notification)) 6824 6825 (defconst lsp--default-notification-handlers 6826 (ht ("window/showMessage" #'lsp--window-show-message) 6827 ("window/logMessage" #'lsp--window-log-message) 6828 ("window/showInputBox" #'lsp--window-show-input-box) 6829 ("window/showQuickPick" #'lsp--window-show-quick-pick) 6830 ("textDocument/publishDiagnostics" #'lsp--on-diagnostics) 6831 ("textDocument/diagnosticsEnd" #'ignore) 6832 ("textDocument/diagnosticsBegin" #'ignore) 6833 ("telemetry/event" #'ignore) 6834 ("$/progress" (lambda (workspace params) 6835 (funcall lsp-progress-function workspace params))))) 6836 6837 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method)) 6838 "Call the appropriate handler for NOTIFICATION." 6839 (-let ((client (lsp--workspace-client workspace))) 6840 (when (lsp--log-io-p method) 6841 (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif) 6842 lsp--cur-workspace)) 6843 (if-let ((handler (or (gethash method (lsp--client-notification-handlers client)) 6844 (gethash method lsp--default-notification-handlers)))) 6845 (funcall handler workspace params) 6846 (when (and method (not (string-prefix-p "$" method))) 6847 (lsp-warn "Unknown notification: %s" method))))) 6848 6849 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items)) 6850 "Get section configuration. 6851 PARAMS are the `workspace/configuration' request params" 6852 (->> items 6853 (-map (-lambda ((&ConfigurationItem :section?)) 6854 (-let* ((path-parts (split-string section? "\\.")) 6855 (path-without-last (s-join "." (-slice path-parts 0 -1))) 6856 (path-parts-len (length path-parts))) 6857 (cond 6858 ((<= path-parts-len 1) 6859 (ht-get (lsp-configuration-section section?) 6860 (car-safe path-parts) 6861 (ht-create))) 6862 ((> path-parts-len 1) 6863 (when-let ((section (lsp-configuration-section path-without-last)) 6864 (keys path-parts)) 6865 (while (and keys section) 6866 (setf section (ht-get section (pop keys)))) 6867 section)))))) 6868 (apply #'vector))) 6869 6870 (defun lsp--ms-since (timestamp) 6871 "Integer number of milliseconds since TIMESTAMP. Fractions discarded." 6872 (floor (* 1000 (float-time (time-since timestamp))))) 6873 6874 (defun lsp--send-request-response (workspace recv-time request response) 6875 "Send the RESPONSE for REQUEST in WORKSPACE and log if needed." 6876 (-let* (((&JSONResponse :params :method :id) request) 6877 (process (lsp--workspace-proc workspace)) 6878 (response (lsp--make-response id response)) 6879 (req-entry (and lsp-log-io 6880 (lsp--make-log-entry method id params 'incoming-req))) 6881 (resp-entry (and lsp-log-io 6882 (lsp--make-log-entry method id response 'outgoing-resp 6883 (lsp--ms-since recv-time))))) 6884 ;; Send response to the server. 6885 (when (lsp--log-io-p method) 6886 (lsp--log-entry-new req-entry workspace) 6887 (lsp--log-entry-new resp-entry workspace)) 6888 (lsp--send-no-wait response process))) 6889 6890 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method)) 6891 "Call the appropriate handler for REQUEST, and send the return value to the 6892 server. WORKSPACE is the active workspace." 6893 (-let* ((recv-time (current-time)) 6894 (client (lsp--workspace-client workspace)) 6895 (buffers (lsp--workspace-buffers workspace)) 6896 handler 6897 (response (cond 6898 ((setq handler (gethash method (lsp--client-request-handlers client) nil)) 6899 (funcall handler workspace params)) 6900 ((setq handler (gethash method (lsp--client-async-request-handlers client) nil)) 6901 (funcall handler workspace params 6902 (-partial #'lsp--send-request-response 6903 workspace recv-time request)) 6904 'delay-response) 6905 ((equal method "client/registerCapability") 6906 (mapc #'lsp--server-register-capability 6907 (lsp:registration-params-registrations params)) 6908 (mapc (lambda (buf) 6909 (when (lsp-buffer-live-p buf) 6910 (lsp-with-current-buffer buf 6911 (lsp-unconfig-buffer) 6912 (lsp-configure-buffer)))) 6913 buffers) 6914 nil) 6915 ((equal method "window/showMessageRequest") 6916 (let ((choice (lsp--window-log-message-request params))) 6917 `(:title ,choice))) 6918 ((equal method "window/showDocument") 6919 (let ((success? (lsp--window-show-document params))) 6920 (lsp-make-show-document-result :success (or success? 6921 :json-false)))) 6922 ((equal method "client/unregisterCapability") 6923 (mapc #'lsp--server-unregister-capability 6924 (lsp:unregistration-params-unregisterations params)) 6925 (mapc (lambda (buf) 6926 (when (lsp-buffer-live-p buf) 6927 (lsp-with-current-buffer buf 6928 (lsp-unconfig-buffer) 6929 (lsp-configure-buffer)))) 6930 buffers) 6931 nil) 6932 ((equal method "workspace/applyEdit") 6933 (list :applied (condition-case err 6934 (prog1 t 6935 (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested)) 6936 (error 6937 (lsp--error "Failed to apply edits with message %s" 6938 (error-message-string err)) 6939 :json-false)))) 6940 ((equal method "workspace/configuration") 6941 (with-lsp-workspace workspace 6942 (if-let ((buf (car buffers))) 6943 (lsp-with-current-buffer buf 6944 (lsp--build-workspace-configuration-response params)) 6945 (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace) 6946 (lsp--build-workspace-configuration-response params))))) 6947 ((equal method "workspace/workspaceFolders") 6948 (let ((folders (or (-> workspace 6949 (lsp--workspace-client) 6950 (lsp--client-server-id) 6951 (gethash (lsp-session-server-id->folders (lsp-session)))) 6952 (lsp-session-folders (lsp-session))))) 6953 (->> folders 6954 (-distinct) 6955 (-map (lambda (folder) 6956 (list :uri (lsp--path-to-uri folder)))) 6957 (apply #'vector)))) 6958 ((equal method "window/workDoneProgress/create") 6959 nil ;; no specific reply, no processing required 6960 ) 6961 ((equal method "workspace/semanticTokens/refresh") 6962 (when (and lsp-semantic-tokens-enable 6963 (fboundp 'lsp--semantic-tokens-on-refresh)) 6964 (lsp--semantic-tokens-on-refresh workspace)) 6965 nil) 6966 ((equal method "workspace/codeLens/refresh") 6967 (when (and lsp-lens-enable 6968 (fboundp 'lsp--lens-on-refresh)) 6969 (lsp--lens-on-refresh workspace)) 6970 nil) 6971 ((equal method "workspace/diagnostic/refresh") 6972 nil) 6973 (t (lsp-warn "Unknown request method: %s" method) nil)))) 6974 ;; Send response to the server. 6975 (unless (eq response 'delay-response) 6976 (lsp--send-request-response workspace recv-time request response)))) 6977 6978 (lsp-defun lsp--error-string ((&JSONError :message :code)) 6979 "Format ERR as a user friendly string." 6980 (format "Error from the Language Server: %s (%s)" 6981 message 6982 (or (car (alist-get code lsp--errors)) "Unknown error"))) 6983 6984 (defun lsp--get-body-length (headers) 6985 (let ((content-length (cdr (assoc "Content-Length" headers)))) 6986 (if content-length 6987 (string-to-number content-length) 6988 6989 ;; This usually means either the server or our parser is 6990 ;; screwed up with a previous Content-Length 6991 (error "No Content-Length header")))) 6992 6993 (defun lsp--parse-header (s) 6994 "Parse string S as a LSP (KEY . VAL) header." 6995 (let ((pos (string-match "\:" s)) 6996 key val) 6997 (unless pos 6998 (signal 'lsp-invalid-header-name (list s))) 6999 (setq key (substring s 0 pos) 7000 val (s-trim-left (substring s (+ 1 pos)))) 7001 (when (equal key "Content-Length") 7002 (cl-assert (cl-loop for c across val 7003 when (or (> c ?9) (< c ?0)) return nil 7004 finally return t) 7005 nil (format "Invalid Content-Length value: %s" val))) 7006 (cons key val))) 7007 7008 (defmacro lsp--read-json (str) 7009 "Read json string STR." 7010 (if (progn 7011 (require 'json) 7012 (fboundp 'json-parse-string)) 7013 `(json-parse-string ,str 7014 :object-type (if lsp-use-plists 7015 'plist 7016 'hash-table) 7017 :null-object nil 7018 :false-object nil) 7019 `(let ((json-array-type 'vector) 7020 (json-object-type (if lsp-use-plists 7021 'plist 7022 'hash-table)) 7023 (json-false nil)) 7024 (json-read-from-string ,str)))) 7025 7026 (defmacro lsp-json-read-buffer () 7027 "Read json from the current buffer." 7028 (if (progn 7029 (require 'json) 7030 (fboundp 'json-parse-buffer)) 7031 `(json-parse-buffer :object-type (if lsp-use-plists 7032 'plist 7033 'hash-table) 7034 :null-object nil 7035 :false-object nil) 7036 `(let ((json-array-type 'vector) 7037 (json-object-type (if lsp-use-plists 7038 'plist 7039 'hash-table)) 7040 (json-false nil)) 7041 (json-read)))) 7042 7043 (defun lsp--read-json-file (file-path) 7044 "Read json file." 7045 (-> file-path 7046 (f-read-text) 7047 (lsp--read-json))) 7048 7049 (defun lsp--parser-on-message (json-data workspace) 7050 "Called when the parser P read a complete MSG from the server." 7051 (with-demoted-errors "Error processing message %S." 7052 (with-lsp-workspace workspace 7053 (let* ((client (lsp--workspace-client workspace)) 7054 (id (--when-let (lsp:json-response-id json-data) 7055 (if (stringp it) (string-to-number it) it))) 7056 (data (lsp:json-response-result json-data))) 7057 (pcase (lsp--get-message-type json-data) 7058 ('response 7059 (cl-assert id) 7060 (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))] 7061 (when (lsp--log-io-p method) 7062 (lsp--log-entry-new 7063 (lsp--make-log-entry method id data 'incoming-resp 7064 (lsp--ms-since before-send)) 7065 workspace)) 7066 (when callback 7067 (remhash id (lsp--client-response-handlers client)) 7068 (funcall callback (lsp:json-response-result json-data))))) 7069 ('response-error 7070 (cl-assert id) 7071 (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))] 7072 (when (lsp--log-io-p method) 7073 (lsp--log-entry-new 7074 (lsp--make-log-entry method id (lsp:json-response-error-error json-data) 7075 'incoming-resp (lsp--ms-since before-send)) 7076 workspace)) 7077 (when callback 7078 (remhash id (lsp--client-response-handlers client)) 7079 (funcall callback (lsp:json-response-error-error json-data))))) 7080 ('notification 7081 (lsp--on-notification workspace json-data)) 7082 ('request (lsp--on-request workspace json-data))))))) 7083 7084 (defun lsp--create-filter-function (workspace) 7085 "Make filter for the workspace." 7086 (let ((body-received 0) 7087 leftovers body-length body chunk) 7088 (lambda (_proc input) 7089 (setf chunk (if (s-blank? leftovers) 7090 (encode-coding-string input 'utf-8-unix t) 7091 (concat leftovers (encode-coding-string input 'utf-8-unix t)))) 7092 7093 (let (messages) 7094 (while (not (s-blank? chunk)) 7095 (if (not body-length) 7096 ;; Read headers 7097 (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk))) 7098 ;; We've got all the headers, handle them all at once: 7099 (setf body-length (lsp--get-body-length 7100 (mapcar #'lsp--parse-header 7101 (split-string 7102 (substring-no-properties chunk 7103 (or (string-match-p "Content-Length" chunk) 7104 (error "Unable to find Content-Length header.")) 7105 body-sep-pos) 7106 "\r\n"))) 7107 body-received 0 7108 leftovers nil 7109 chunk (substring-no-properties chunk (+ body-sep-pos 4))) 7110 7111 ;; Haven't found the end of the headers yet. Save everything 7112 ;; for when the next chunk arrives and await further input. 7113 (setf leftovers chunk 7114 chunk nil)) 7115 (let* ((chunk-length (string-bytes chunk)) 7116 (left-to-receive (- body-length body-received)) 7117 (this-body (if (< left-to-receive chunk-length) 7118 (prog1 (substring-no-properties chunk 0 left-to-receive) 7119 (setf chunk (substring-no-properties chunk left-to-receive))) 7120 (prog1 chunk 7121 (setf chunk nil)))) 7122 (body-bytes (string-bytes this-body))) 7123 (push this-body body) 7124 (setf body-received (+ body-received body-bytes)) 7125 (when (>= chunk-length left-to-receive) 7126 (condition-case err 7127 (with-temp-buffer 7128 (apply #'insert 7129 (nreverse 7130 (prog1 body 7131 (setf leftovers nil 7132 body-length nil 7133 body-received nil 7134 body nil)))) 7135 (decode-coding-region (point-min) 7136 (point-max) 7137 'utf-8) 7138 (goto-char (point-min)) 7139 (push (lsp-json-read-buffer) messages)) 7140 7141 (error 7142 (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s" 7143 (concat leftovers input) 7144 err))))))) 7145 (mapc (lambda (msg) 7146 (lsp--parser-on-message msg workspace)) 7147 (nreverse messages)))))) 7148 7149 (defvar-local lsp--line-col-to-point-hash-table nil 7150 "Hash table with keys (line . col) and values that are either point positions 7151 or markers.") 7152 7153 (defcustom lsp-imenu-detailed-outline t 7154 "Whether `lsp-imenu' should include signatures. 7155 This will be ignored if the server doesn't provide the necessary 7156 information, for example if it doesn't support DocumentSymbols." 7157 :group 'lsp-imenu 7158 :type 'boolean) 7159 7160 (defcustom lsp-imenu-hide-parent-details t 7161 "Whether `lsp-imenu' should hide signatures of parent nodes." 7162 :group 'lsp-imenu 7163 :type 'boolean) 7164 7165 (defface lsp-details-face '((t :height 0.8 :inherit shadow)) 7166 "Used to display additional information throughout `lsp'. 7167 Things like line numbers, signatures, ... are considered 7168 additional information. Often, additional faces are defined that 7169 inherit from this face by default, like `lsp-signature-face', and 7170 they may be customized for finer control." 7171 :group 'lsp-mode) 7172 7173 (defface lsp-signature-face '((t :inherit lsp-details-face)) 7174 "Used to display signatures in `imenu', ...." 7175 :group 'lsp-mode) 7176 7177 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?) 7178 show-detail?) 7179 "Render INPUT0, an `&DocumentSymbol', to a string. 7180 If SHOW-DETAIL? is set, make use of its `:detail?' field (often 7181 the signature)." 7182 (let ((detail (and show-detail? (s-present? detail?) 7183 (propertize (concat " " (s-trim-left detail?)) 7184 'face 'lsp-signature-face))) 7185 (name (if deprecated? 7186 (propertize name 'face 'lsp-face-semhl-deprecated) name))) 7187 (concat name detail))) 7188 7189 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?) 7190 separator) 7191 "Render a piece of SymbolInformation. 7192 Handle :deprecated?. If SEPARATOR is non-nil, the 7193 symbol's (optional) parent, SEPARATOR and the symbol itself are 7194 concatenated." 7195 (when (and separator container-name? (not (string-empty-p container-name?))) 7196 (setq name (concat name separator container-name?))) 7197 (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name)) 7198 7199 (defun lsp--symbol-to-imenu-elem (sym) 7200 "Convert SYM to imenu element. 7201 7202 SYM is a SymbolInformation message. 7203 7204 Return a cons cell (full-name . start-point)." 7205 (let ((start-point (ht-get lsp--line-col-to-point-hash-table 7206 (lsp--get-line-and-col sym)))) 7207 (cons (lsp-render-symbol-information 7208 sym (and lsp-imenu-show-container-name 7209 lsp-imenu-container-name-separator)) 7210 start-point))) 7211 7212 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?)) 7213 "Convert SYM to hierarchical imenu elements. 7214 7215 SYM is a DocumentSymbol message. 7216 7217 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if 7218 SYM doesn't have any children. Otherwise return a cons cell with 7219 an alist 7220 7221 (\"symbol-name\" . ((\"(symbol-kind)\" . start-point) 7222 cons-cells-from-children))" 7223 (let ((filtered-children (lsp--imenu-filter-symbols children?)) 7224 (signature (lsp-render-symbol sym lsp-imenu-detailed-outline))) 7225 (if (seq-empty-p filtered-children) 7226 (cons signature 7227 (ht-get lsp--line-col-to-point-hash-table 7228 (lsp--get-line-and-col sym))) 7229 (cons signature 7230 (lsp--imenu-create-hierarchical-index filtered-children))))) 7231 7232 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind)) 7233 "Determine if SYM is for the current document and is to be shown." 7234 ;; It's a SymbolInformation or DocumentSymbol, which is always in the 7235 ;; current buffer file. 7236 (and lsp-imenu-index-symbol-kinds 7237 (numberp kind) 7238 (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup)) 7239 kind 7240 0))) 7241 (not (memql (aref lsp/symbol-kind-lookup clamped-kind) 7242 lsp-imenu-index-symbol-kinds))))) 7243 7244 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind)) 7245 "The string name of the kind of SYM." 7246 (alist-get kind lsp-symbol-kinds "Other")) 7247 7248 (defun lsp--get-line-and-col (sym) 7249 "Obtain the line and column corresponding to SYM." 7250 (-let* ((location (lsp:symbol-information-location sym)) 7251 (name-range (or (and location (lsp:location-range location)) 7252 (lsp:document-symbol-selection-range sym))) 7253 ((&Range :start (&Position :line :character)) name-range)) 7254 (cons line character))) 7255 7256 (defun lsp--collect-lines-and-cols (symbols) 7257 "Return a sorted list ((line . col) ...) of the locations of SYMBOLS." 7258 (let ((stack (mapcar 'identity symbols)) 7259 line-col-list) 7260 (while stack 7261 (let ((sym (pop stack))) 7262 (push (lsp--get-line-and-col sym) line-col-list) 7263 (unless (seq-empty-p (lsp:document-symbol-children? sym)) 7264 (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack))))) 7265 (-sort #'lsp--line-col-comparator line-col-list))) 7266 7267 (defun lsp--convert-line-col-to-points-batch (line-col-list) 7268 "Convert a sorted list of positions from line-column 7269 representation to point representation." 7270 (let ((line-col-to-point-map (ht-create)) 7271 (inhibit-field-text-motion t) 7272 (curr-line 0)) 7273 (lsp-save-restriction-and-excursion 7274 (goto-char (point-min)) 7275 (cl-loop for (line . col) in line-col-list do 7276 (forward-line (- line curr-line)) 7277 (setq curr-line line) 7278 (let ((line-end (line-end-position))) 7279 (if (or (not col) (> col (- line-end (point)))) 7280 (goto-char line-end) 7281 (forward-char col))) 7282 (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers 7283 (point-marker) 7284 (point))))) 7285 line-col-to-point-map)) 7286 7287 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2)) 7288 (or (< l1 l2) 7289 (and (= l1 l2) 7290 (cond ((and c1 c2) 7291 (< c1 c2)) 7292 (c1 t))))) 7293 7294 (defun lsp-imenu-create-uncategorized-index (symbols) 7295 "Create imenu index from document SYMBOLS. 7296 This function, unlike `lsp-imenu-create-categorized-index', does 7297 not categorize by type, but instead returns an `imenu' index 7298 corresponding to the symbol hierarchy returned by the server 7299 directly." 7300 (let* ((lsp--line-col-to-point-hash-table (-> symbols 7301 lsp--collect-lines-and-cols 7302 lsp--convert-line-col-to-points-batch))) 7303 (if (lsp--imenu-hierarchical-p symbols) 7304 (lsp--imenu-create-hierarchical-index symbols) 7305 (lsp--imenu-create-non-hierarchical-index symbols)))) 7306 7307 (defcustom lsp-imenu-symbol-kinds 7308 '((1 . "Files") 7309 (2 . "Modules") 7310 (3 . "Namespaces") 7311 (4 . "Packages") 7312 (5 . "Classes") 7313 (6 . "Methods") 7314 (7 . "Properties") 7315 (8 . "Fields") 7316 (9 . "Constructors") 7317 (10 . "Enums") 7318 (11 . "Interfaces") 7319 (12 . "Functions") 7320 (13 . "Variables") 7321 (14 . "Constants") 7322 (15 . "Strings") 7323 (16 . "Numbers") 7324 (17 . "Booleans") 7325 (18 . "Arrays") 7326 (19 . "Objects") 7327 (20 . "Keys") 7328 (21 . "Nulls") 7329 (22 . "Enum Members") 7330 (23 . "Structs") 7331 (24 . "Events") 7332 (25 . "Operators") 7333 (26 . "Type Parameters")) 7334 "`lsp-symbol-kinds', but only used by `imenu'. 7335 A new variable is needed, as it is `imenu' convention to use 7336 pluralized categories, which `lsp-symbol-kinds' doesn't. If the 7337 non-pluralized names are preferred, this can be set to 7338 `lsp-symbol-kinds'." 7339 :type '(alist :key-type integer :value-type string)) 7340 7341 (defun lsp--imenu-kind->name (kind) 7342 (alist-get kind lsp-imenu-symbol-kinds "?")) 7343 7344 (defun lsp-imenu-create-top-level-categorized-index (symbols) 7345 "Create an `imenu' index categorizing SYMBOLS by type. 7346 Only root symbols are categorized. 7347 7348 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS 7349 shall be a list of DocumentSymbols or SymbolInformation." 7350 (mapcan 7351 (-lambda ((type . symbols)) 7352 (let ((cat (lsp--imenu-kind->name type)) 7353 (symbols (lsp-imenu-create-uncategorized-index symbols))) 7354 ;; If there is no :kind (this is being defensive), or we couldn't look it 7355 ;; up, just display the symbols inline, without categories. 7356 (if cat (list (cons cat symbols)) symbols))) 7357 (sort (seq-group-by #'lsp:document-symbol-kind symbols) 7358 (-lambda ((kinda) (kindb)) (< kinda kindb))))) 7359 7360 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start))) 7361 "Convert an `&DocumentSymbol' to an `imenu' entry." 7362 (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start)) 7363 7364 (defun lsp--imenu-create-categorized-index-1 (symbols) 7365 "Returns an `imenu' index from SYMBOLS categorized by type. 7366 The result looks like this: ((\"Variables\" . (...)))." 7367 (->> 7368 symbols 7369 (mapcan 7370 (-lambda ((sym &as &DocumentSymbol :kind :children?)) 7371 (if (seq-empty-p children?) 7372 (list (list kind (lsp--symbol->imenu sym))) 7373 (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline 7374 (not lsp-imenu-hide-parent-details))))) 7375 (cons 7376 (list kind (lsp--symbol->imenu sym)) 7377 (mapcar (-lambda ((type . imenu-items)) 7378 (list type (cons parent (mapcan #'cdr imenu-items)))) 7379 (-group-by #'car (lsp--imenu-create-categorized-index-1 children?)))))))) 7380 (-group-by #'car) 7381 (mapcar 7382 (-lambda ((kind . syms)) 7383 (cons kind (mapcan #'cdr syms)))))) 7384 7385 (defun lsp--imenu-create-categorized-index (symbols) 7386 (let ((syms (lsp--imenu-create-categorized-index-1 symbols))) 7387 (dolist (sym syms) 7388 (setcar sym (lsp--imenu-kind->name (car sym)))) 7389 syms)) 7390 7391 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start)))) 7392 (cons (lsp-render-symbol-information sym nil) start)) 7393 7394 (defun lsp--imenu-create-categorized-index-flat (symbols) 7395 "Create a kind-categorized index for SymbolInformation." 7396 (mapcar (-lambda ((kind . syms)) 7397 (cons (lsp--imenu-kind->name kind) 7398 (mapcan (-lambda ((parent . children)) 7399 (let ((children (mapcar #'lsp--symbol-information->imenu children))) 7400 (if parent (list (cons parent children)) children))) 7401 (-group-by #'lsp:symbol-information-container-name? syms)))) 7402 (seq-group-by #'lsp:symbol-information-kind symbols))) 7403 7404 (defun lsp-imenu-create-categorized-index (symbols) 7405 (if (lsp--imenu-hierarchical-p symbols) 7406 (lsp--imenu-create-categorized-index symbols) 7407 (lsp--imenu-create-categorized-index-flat symbols))) 7408 7409 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index 7410 "Function that should create an `imenu' index. 7411 It will be called with a list of SymbolInformation or 7412 DocumentSymbols, whose first level is already filtered. It shall 7413 then return an appropriate `imenu' index (see 7414 `imenu-create-index-function'). 7415 7416 Note that this interface is not stable, and subject to change any 7417 time." 7418 :group 'lsp-imenu 7419 :type '(radio 7420 (const :tag "Categorize by type" 7421 lsp-imenu-create-categorized-index) 7422 (const :tag "Categorize root symbols by type" 7423 lsp-imenu-create-top-level-categorized-index) 7424 (const :tag "Uncategorized, inline entries" 7425 lsp-imenu-create-uncategorized-index) 7426 (function :tag "Custom function"))) 7427 7428 (defun lsp--imenu-create-index () 7429 "Create an `imenu' index based on the language server. 7430 Respects `lsp-imenu-index-function'." 7431 (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols)))) 7432 (funcall lsp-imenu-index-function symbols))) 7433 7434 (defun lsp--imenu-filter-symbols (symbols) 7435 "Filter out unsupported symbols from SYMBOLS." 7436 (seq-remove #'lsp--symbol-ignore symbols)) 7437 7438 (defun lsp--imenu-hierarchical-p (symbols) 7439 "Determine whether any element in SYMBOLS has children." 7440 (seq-some #'lsp-document-symbol? symbols)) 7441 7442 (defun lsp--imenu-create-non-hierarchical-index (symbols) 7443 "Create imenu index for non-hierarchical SYMBOLS. 7444 7445 SYMBOLS are a list of DocumentSymbol messages. 7446 7447 Return a nested alist keyed by symbol names. e.g. 7448 7449 ((\"SomeClass\" (\"(Class)\" . 10) 7450 (\"someField (Field)\" . 20) 7451 (\"someFunction (Function)\" . 25) 7452 (\"SomeSubClass\" (\"(Class)\" . 30) 7453 (\"someSubField (Field)\" . 35)) 7454 (\"someFunction (Function)\" . 40))" 7455 (seq-map (lambda (nested-alist) 7456 (cons (car nested-alist) 7457 (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist)))) 7458 (seq-group-by #'lsp--get-symbol-type symbols))) 7459 7460 (defun lsp--imenu-create-hierarchical-index (symbols) 7461 "Create imenu index for hierarchical SYMBOLS. 7462 7463 SYMBOLS are a list of DocumentSymbol messages. 7464 7465 Return a nested alist keyed by symbol names. e.g. 7466 7467 ((\"SomeClass\" (\"(Class)\" . 10) 7468 (\"someField (Field)\" . 20) 7469 (\"someFunction (Function)\" . 25) 7470 (\"SomeSubClass\" (\"(Class)\" . 30) 7471 (\"someSubField (Field)\" . 35)) 7472 (\"someFunction (Function)\" . 40))" 7473 (seq-map #'lsp--symbol-to-hierarchical-imenu-elem 7474 (seq-sort #'lsp--imenu-symbol-lessp symbols))) 7475 7476 (defun lsp--imenu-symbol-lessp (sym1 sym2) 7477 (let* ((compare-results (mapcar (lambda (method) 7478 (funcall (alist-get method lsp--imenu-compare-function-alist) 7479 sym1 sym2)) 7480 lsp-imenu-sort-methods)) 7481 (result (seq-find (lambda (result) 7482 (not (= result 0))) 7483 compare-results 7484 0))) 7485 (and (numberp result) (< result 0)))) 7486 7487 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left) 7488 (&SymbolInformation :kind right)) 7489 "Compare SYM1 and SYM2 by kind." 7490 (- left right)) 7491 7492 (defun lsp--imenu-compare-line-col (sym1 sym2) 7493 (if (lsp--line-col-comparator 7494 (lsp--get-line-and-col sym1) 7495 (lsp--get-line-and-col sym2)) 7496 -1 7497 1)) 7498 7499 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1) 7500 (&SymbolInformation :name name2)) 7501 "Compare SYM1 and SYM2 by name." 7502 (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2)))) 7503 (if (numberp result) result 0))) 7504 7505 (defun lsp--imenu-refresh () 7506 "Force Imenu to refresh itself." 7507 (imenu--menubar-select imenu--rescan-item)) 7508 7509 (defun lsp-enable-imenu () 7510 "Use lsp-imenu for the current buffer." 7511 (imenu--cleanup) 7512 (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index) 7513 (setq-local imenu-menubar-modified-tick -1) 7514 (setq-local imenu--index-alist nil) 7515 (when menu-bar-mode 7516 (lsp--imenu-refresh))) 7517 7518 (defun lsp-resolve-final-command (command &optional test?) 7519 "Resolve final function COMMAND." 7520 (let* ((command (lsp-resolve-value command)) 7521 (command (cl-etypecase command 7522 (list 7523 (cl-assert (seq-every-p (apply-partially #'stringp) command) nil 7524 "Invalid command list") 7525 command) 7526 (string (list command))))) 7527 (if (and (file-remote-p default-directory) (not test?)) 7528 (list shell-file-name "-c" 7529 (string-join (cons "stty raw > /dev/null;" 7530 (mapcar #'shell-quote-argument command)) 7531 " ")) 7532 command))) 7533 7534 (defun lsp-server-present? (final-command) 7535 "Check whether FINAL-COMMAND is present." 7536 (let ((binary-found? (executable-find (cl-first final-command) t))) 7537 (if binary-found? 7538 (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command)) 7539 (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command))) 7540 binary-found?)) 7541 7542 (defun lsp--value-to-string (value) 7543 "Convert VALUE to a string that can be set as value in an environment 7544 variable." 7545 (cond 7546 ((stringp value) value) 7547 ((booleanp value) (if value 7548 "1" 7549 "0")) 7550 ((and (sequencep value) 7551 (seq-every-p #'stringp value)) (string-join value ":")) 7552 (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables")))) 7553 7554 (defun lsp--compute-process-environment (environment-fn) 7555 "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'. 7556 Ignore non-boolean keys whose value is nil." 7557 (let ((environment (if environment-fn 7558 (funcall environment-fn) 7559 nil))) 7560 (-flatten (cons (cl-loop for (key . value) in environment 7561 if (or (eval value) 7562 (eq (get value 'custom-type) 'boolean)) 7563 collect (concat key "=" (lsp--value-to-string 7564 (eval value)))) 7565 process-environment)))) 7566 7567 (defun lsp--default-directory-for-connection (&optional path) 7568 "Return path to be used for the working directory of a LSP process. 7569 7570 If `lsp-use-workspace-root-for-server-default-directory' is 7571 non-nil, uses `lsp-workspace-root' to find the directory 7572 corresponding to PATH, else returns `default-directory'." 7573 (if lsp-use-workspace-root-for-server-default-directory 7574 (lsp-workspace-root path) 7575 default-directory)) 7576 7577 (defun lsp--fix-remote-cmd (program) 7578 "Helper for `lsp-stdio-connection'. 7579 Originally coppied from eglot." 7580 7581 (if (file-remote-p default-directory) 7582 (list shell-file-name "-c" 7583 (string-join (cons "stty raw > /dev/null;" 7584 (mapcar #'shell-quote-argument program)) 7585 " ")) 7586 program)) 7587 7588 (defvar tramp-use-ssh-controlmaster-options) 7589 (defvar tramp-ssh-controlmaster-options) 7590 7591 (defun lsp-stdio-connection (command &optional test-command) 7592 "Returns a connection property list using COMMAND. 7593 COMMAND can be: A string, denoting the command to launch the 7594 language server. A list of strings, denoting an executable with 7595 its command line arguments. A function, that either returns a 7596 string or a list of strings. In all cases, the launched language 7597 server should send and receive messages on standard I/O. 7598 TEST-COMMAND is a function with no arguments which returns 7599 whether the command is present or not. When not specified 7600 `lsp-mode' will check whether the first element of the list 7601 returned by COMMAND is available via `executable-find'" 7602 (cl-check-type command (or string 7603 function 7604 (and list 7605 (satisfies (lambda (l) 7606 (seq-every-p (lambda (el) 7607 (stringp el)) 7608 l)))))) 7609 (list :connect (lambda (filter sentinel name environment-fn workspace) 7610 (if (and (functionp 'json-rpc-connection) 7611 (not (file-remote-p default-directory))) 7612 (lsp-json-rpc-connection workspace (lsp-resolve-final-command command)) 7613 (let ((final-command (lsp-resolve-final-command command)) 7614 (process-name (generate-new-buffer-name name)) 7615 (process-environment 7616 (lsp--compute-process-environment environment-fn))) 7617 (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name))) 7618 (default-directory (lsp--default-directory-for-connection)) 7619 (tramp-use-ssh-controlmaster-options 'suppress) 7620 (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none") 7621 (proc (make-process 7622 :name process-name 7623 :connection-type 'pipe 7624 :buffer (format "*%s*" process-name) 7625 :coding 'no-conversion 7626 :command final-command 7627 :filter filter 7628 :sentinel sentinel 7629 :stderr stderr-buf 7630 :noquery t 7631 :file-handler t))) 7632 (set-process-query-on-exit-flag proc nil) 7633 (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) 7634 (with-current-buffer (get-buffer stderr-buf) 7635 ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc. 7636 (special-mode)) 7637 (cons proc proc))))) 7638 :test? (or 7639 test-command 7640 (lambda () 7641 (lsp-server-present? (lsp-resolve-final-command command t)))))) 7642 7643 (defun lsp--open-network-stream (host port name) 7644 "Open network stream to HOST:PORT. 7645 NAME will be passed to `open-network-stream'. 7646 RETRY-COUNT is the number of the retries. 7647 SLEEP-INTERVAL is the sleep interval between each retry." 7648 (let* ((retries 0) 7649 (sleep-interval 0.01) 7650 (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval)) 7651 connection) 7652 (while (and (not connection) (< retries number-of-retries)) 7653 (condition-case err 7654 (setq connection (open-network-stream name nil host port 7655 :type 'plain 7656 :coding 'no-conversion)) 7657 (file-error 7658 (let ((inhibit-message t)) 7659 (lsp--warn "Failed to connect to %s:%s with error message %s" 7660 host 7661 port 7662 (error-message-string err)) 7663 (sleep-for sleep-interval) 7664 (cl-incf retries))))) 7665 (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port)))) 7666 7667 (defun lsp--port-available (host port) 7668 "Return non-nil if HOST and PORT are available." 7669 (condition-case _err 7670 (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain)) 7671 (file-error t))) 7672 7673 (defun lsp--find-available-port (host starting-port) 7674 "Find available port on HOST starting from STARTING-PORT." 7675 (let ((port starting-port)) 7676 (while (not (lsp--port-available host port)) 7677 (cl-incf port)) 7678 port)) 7679 7680 (defun lsp-tcp-connection (command-fn) 7681 "Returns a connection property list similar to `lsp-stdio-connection'. 7682 COMMAND-FN can only be a function that takes a single argument, a 7683 port number. It should return a command for launches a language server 7684 process listening for TCP connections on the provided port." 7685 (cl-check-type command-fn function) 7686 (list 7687 :connect (lambda (filter sentinel name environment-fn _workspace) 7688 (let* ((host "localhost") 7689 (port (lsp--find-available-port host (cl-incf lsp--tcp-port))) 7690 (command (funcall command-fn port)) 7691 (final-command (if (consp command) command (list command))) 7692 (_ (unless (lsp-server-present? final-command) 7693 (user-error (format "Couldn't find executable %s" (cl-first final-command))))) 7694 (process-environment 7695 (lsp--compute-process-environment environment-fn)) 7696 (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion 7697 :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t)) 7698 (tcp-proc (lsp--open-network-stream host port (concat name "::tcp")))) 7699 7700 ;; TODO: Same :noquery issue (see above) 7701 (set-process-query-on-exit-flag proc nil) 7702 (set-process-query-on-exit-flag tcp-proc nil) 7703 (set-process-filter tcp-proc filter) 7704 (cons tcp-proc proc))) 7705 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7706 7707 (defalias 'lsp-tcp-server 'lsp-tcp-server-command) 7708 7709 (defun lsp-tcp-server-command (command-fn) 7710 "Create tcp server connection. 7711 In this mode Emacs is TCP server and the language server connects 7712 to it. COMMAND is function with one parameter(the port) and it 7713 should return the command to start the LS server." 7714 (cl-check-type command-fn function) 7715 (list 7716 :connect (lambda (filter sentinel name environment-fn _workspace) 7717 (let* (tcp-client-connection 7718 (tcp-server (make-network-process :name (format "*tcp-server-%s*" name) 7719 :buffer (format "*tcp-server-%s*" name) 7720 :family 'ipv4 7721 :service lsp--tcp-server-port 7722 :sentinel (lambda (proc _string) 7723 (lsp-log "Language server %s is connected." name) 7724 (setf tcp-client-connection proc)) 7725 :server 't)) 7726 (port (process-contact tcp-server :service)) 7727 (final-command (funcall command-fn port)) 7728 (process-environment 7729 (lsp--compute-process-environment environment-fn)) 7730 (cmd-proc (make-process :name name 7731 :connection-type 'pipe 7732 :coding 'no-conversion 7733 :command final-command 7734 :stderr (format "*tcp-server-%s*::stderr" name) 7735 :noquery t))) 7736 (let ((retries 0)) 7737 ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds) 7738 (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds))) 7739 (lsp--info "Waiting for connection for %s, retries: %s" name retries) 7740 (sit-for 0.500) 7741 (cl-incf retries))) 7742 7743 (unless tcp-client-connection 7744 (condition-case nil (delete-process tcp-server) (error)) 7745 (condition-case nil (delete-process cmd-proc) (error)) 7746 (error "Failed to create connection to %s on port %s" name port)) 7747 (lsp--info "Successfully connected to %s" name) 7748 7749 (set-process-query-on-exit-flag cmd-proc nil) 7750 (set-process-query-on-exit-flag tcp-client-connection nil) 7751 (set-process-query-on-exit-flag tcp-server nil) 7752 7753 (set-process-filter tcp-client-connection filter) 7754 (set-process-sentinel tcp-client-connection sentinel) 7755 (cons tcp-client-connection cmd-proc))) 7756 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7757 7758 (defalias 'lsp-tramp-connection 'lsp-stdio-connection) 7759 7760 (defun lsp--auto-configure () 7761 "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed." 7762 (when (functionp 'lsp-ui-mode) 7763 (lsp-ui-mode)) 7764 7765 (if lsp-headerline-breadcrumb-enable 7766 (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode) 7767 (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)) 7768 (if lsp-modeline-code-actions-enable 7769 (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode) 7770 (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)) 7771 (if lsp-modeline-diagnostics-enable 7772 (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode) 7773 (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)) 7774 (if lsp-modeline-workspace-status-enable 7775 (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode) 7776 (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)) 7777 (if lsp-lens-enable 7778 (add-hook 'lsp-configure-hook 'lsp-lens--enable) 7779 (remove-hook 'lsp-configure-hook 'lsp-lens--enable)) 7780 (if lsp-semantic-tokens-enable 7781 (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable) 7782 (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)) 7783 7784 ;; yas-snippet config 7785 (setq-local yas-inhibit-overlay-modification-protection t)) 7786 7787 (defun lsp--restart-if-needed (workspace) 7788 "Handler restart for WORKSPACE." 7789 (when (or (eq lsp-restart 'auto-restart) 7790 (eq (lsp--workspace-shutdown-action workspace) 'restart) 7791 (and (eq lsp-restart 'interactive) 7792 (let ((query (format 7793 "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?" 7794 (lsp--workspace-print workspace)))) 7795 (y-or-n-p query)))) 7796 (--each (lsp--workspace-buffers workspace) 7797 (when (lsp-buffer-live-p it) 7798 (lsp-with-current-buffer it 7799 (if lsp--buffer-deferred 7800 (lsp-deferred) 7801 (lsp--info "Restarting LSP in buffer %s" (buffer-name)) 7802 (lsp))))))) 7803 7804 (defun lsp--update-key (table key fn) 7805 "Apply FN on value corresponding to KEY in TABLE." 7806 (let ((existing-value (gethash key table))) 7807 (if-let ((new-value (funcall fn existing-value))) 7808 (puthash key new-value table) 7809 (remhash key table)))) 7810 7811 (defun lsp--process-sentinel (workspace process exit-str) 7812 "Create the sentinel for WORKSPACE." 7813 (unless (process-live-p process) 7814 (lsp--handle-process-exit workspace exit-str))) 7815 7816 (defun lsp--handle-process-exit (workspace exit-str) 7817 (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session))) 7818 (proc (lsp--workspace-proc workspace))) 7819 (lsp--warn "%s has exited (%s)" 7820 (lsp-process-name proc) 7821 (string-trim-right (or exit-str ""))) 7822 (with-lsp-workspace workspace 7823 ;; Clean workspace related data in each of the buffers 7824 ;; in the workspace. 7825 (--each (lsp--workspace-buffers workspace) 7826 (when (lsp-buffer-live-p it) 7827 (lsp-with-current-buffer it 7828 (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces)) 7829 (lsp--uninitialize-workspace) 7830 (lsp--spinner-stop) 7831 (lsp--remove-overlays 'lsp-highlight)))) 7832 7833 ;; Cleanup session from references to the closed workspace. 7834 (--each (hash-table-keys folder->workspaces) 7835 (lsp--update-key folder->workspaces it (apply-partially 'delete workspace))) 7836 7837 (lsp-process-cleanup proc)) 7838 7839 (run-hook-with-args 'lsp-after-uninitialized-functions workspace) 7840 7841 (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown) 7842 (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace)) 7843 (lsp--restart-if-needed workspace)) 7844 (lsp--cleanup-hanging-watches))) 7845 7846 (defun lsp-workspace-folders (workspace) 7847 "Return all folders associated with WORKSPACE." 7848 (let (result) 7849 (->> (lsp-session) 7850 (lsp-session-folder->servers) 7851 (maphash (lambda (folder workspaces) 7852 (when (-contains? workspaces workspace) 7853 (push folder result))))) 7854 result)) 7855 7856 (defun lsp--start-workspace (session client-template root &optional initialization-options) 7857 "Create new workspace for CLIENT-TEMPLATE with project root ROOT. 7858 INITIALIZATION-OPTIONS are passed to initialize function. 7859 SESSION is the active session." 7860 (lsp--spinner-start) 7861 (-let* ((default-directory root) 7862 (client (copy-lsp--client client-template)) 7863 (workspace (make-lsp--workspace 7864 :root root 7865 :client client 7866 :status 'starting 7867 :buffers (list (lsp-current-buffer)) 7868 :host-root (file-remote-p root))) 7869 ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities 7870 'multi-root 'initialized-fn) client) 7871 ((proc . cmd-proc) (funcall 7872 (or (plist-get new-connection :connect) 7873 (user-error "Client %s is configured incorrectly" client)) 7874 (lsp--create-filter-function workspace) 7875 (apply-partially #'lsp--process-sentinel workspace) 7876 (format "%s" server-id) 7877 environment-fn 7878 workspace)) 7879 (workspace-folders (gethash server-id (lsp-session-server-id->folders session)))) 7880 (setf (lsp--workspace-proc workspace) proc 7881 (lsp--workspace-cmd-proc workspace) cmd-proc) 7882 7883 ;; update (lsp-session-folder->servers) depending on whether we are starting 7884 ;; multi/single folder workspace 7885 (mapc (lambda (project-root) 7886 (->> session 7887 (lsp-session-folder->servers) 7888 (gethash project-root) 7889 (cl-pushnew workspace))) 7890 (or workspace-folders (list root))) 7891 7892 (with-lsp-workspace workspace 7893 (run-hooks 'lsp-before-initialize-hook) 7894 (lsp-request-async 7895 "initialize" 7896 (append 7897 (list :processId (unless (file-remote-p (buffer-file-name)) 7898 (emacs-pid)) 7899 :rootPath (lsp-file-local-name (expand-file-name root)) 7900 :clientInfo (list :name "emacs" 7901 :version (emacs-version)) 7902 :rootUri (lsp--path-to-uri root) 7903 :capabilities (lsp--client-capabilities custom-capabilities) 7904 :initializationOptions initialization-options 7905 :workDoneToken "1") 7906 (when lsp-server-trace 7907 (list :trace lsp-server-trace)) 7908 (when multi-root 7909 (->> workspace-folders 7910 (-distinct) 7911 (-map (lambda (folder) 7912 (list :uri (lsp--path-to-uri folder) 7913 :name (f-filename folder)))) 7914 (apply 'vector) 7915 (list :workspaceFolders)))) 7916 (-lambda ((&InitializeResult :capabilities)) 7917 ;; we know that Rust Analyzer will send {} which will be parsed as null 7918 ;; when using plists 7919 (when (equal 'rust-analyzer server-id) 7920 (-> capabilities 7921 (lsp:server-capabilities-text-document-sync?) 7922 (lsp:set-text-document-sync-options-save? t))) 7923 7924 (setf (lsp--workspace-server-capabilities workspace) capabilities 7925 (lsp--workspace-status workspace) 'initialized) 7926 7927 (with-lsp-workspace workspace 7928 (lsp-notify "initialized" lsp--empty-ht)) 7929 7930 (when initialized-fn (funcall initialized-fn workspace)) 7931 7932 (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace)) 7933 (->> workspace 7934 (lsp--workspace-buffers) 7935 (mapc (lambda (buffer) 7936 (lsp-with-current-buffer buffer 7937 (lsp--open-in-workspace workspace))))) 7938 7939 (with-lsp-workspace workspace 7940 (run-hooks 'lsp-after-initialize-hook)) 7941 (lsp--info "%s initialized successfully in folders: %s" 7942 (lsp--workspace-print workspace) 7943 (lsp-workspace-folders workspace))) 7944 :mode 'detached)) 7945 workspace)) 7946 7947 (defun lsp--load-default-session () 7948 "Load default session." 7949 (setq lsp--session (or (condition-case err 7950 (lsp--read-from-file lsp-session-file) 7951 (error (lsp--error "Failed to parse the session %s, starting with clean one." 7952 (error-message-string err)) 7953 nil)) 7954 (make-lsp-session)))) 7955 7956 (defun lsp-session () 7957 "Get the session associated with the current buffer." 7958 (or lsp--session (setq lsp--session (lsp--load-default-session)))) 7959 7960 (defun lsp--client-disabled-p (buffer-major-mode client) 7961 (seq-some 7962 (lambda (entry) 7963 (pcase entry 7964 ((pred symbolp) (eq entry client)) 7965 (`(,mode . ,client-or-list) 7966 (and (eq mode buffer-major-mode) 7967 (if (listp client-or-list) 7968 (memq client client-or-list) 7969 (eq client client-or-list)))))) 7970 lsp-disabled-clients)) 7971 7972 7973 ;; download server 7974 7975 (defcustom lsp-server-install-dir (expand-file-name 7976 (locate-user-emacs-file (f-join ".cache" "lsp"))) 7977 "Directory in which the servers will be installed." 7978 :risky t 7979 :type 'directory 7980 :package-version '(lsp-mode . "6.3") 7981 :group 'lsp-mode) 7982 7983 (defcustom lsp-verify-signature t 7984 "Whether to check GPG signatures of downloaded files." 7985 :type 'boolean 7986 :package-version '(lsp-mode . "8.0.0") 7987 :group 'lsp-mode) 7988 7989 (defvar lsp--dependencies (ht)) 7990 7991 (defun lsp-dependency (name &rest definitions) 7992 "Used to specify a language server DEPENDENCY, the server 7993 executable or other required file path. Typically, the 7994 DEPENDENCY is found by locating it on the system path using 7995 `executable-find'. 7996 7997 You can explicitly call lsp-dependency in your environment to 7998 specify the absolute path to the DEPENDENCY. For example, the 7999 typescript-language-server requires both the server and the 8000 typescript compiler. If you have installed them in a team shared 8001 read-only location, you can instruct lsp-mode to use them via 8002 8003 (eval-after-load `lsp-mode 8004 `(progn 8005 (require lsp-javascript) 8006 (lsp-dependency typescript-language-server (:system ,tls-exe)) 8007 (lsp-dependency typescript (:system ,ts-js)))) 8008 8009 where tls-exe is the absolute path to the typescript-language-server 8010 executable and ts-js is the absolute path to the typescript compiler 8011 JavaScript file, tsserver.js (the *.js is required for Windows)." 8012 (ht-set lsp--dependencies name definitions)) 8013 8014 (defun lsp--server-binary-present? (client) 8015 (unless (equal (lsp--client-server-id client) 'lsp-pwsh) 8016 (condition-case () 8017 (-some-> client lsp--client-new-connection (plist-get :test?) funcall) 8018 (error nil) 8019 (args-out-of-range nil)))) 8020 8021 (define-minor-mode lsp-installation-buffer-mode 8022 "Mode used in *lsp-installation* buffers. 8023 It can be used to set-up keybindings, etc. Disabling this mode 8024 detaches the installation buffer from commands like 8025 `lsp-select-installation-buffer'." 8026 :init-value nil 8027 :lighter nil) 8028 8029 (defface lsp-installation-finished-buffer-face '((t :foreground "orange")) 8030 "Face used for finished installation buffers. 8031 Used in `lsp-select-installation-buffer'." 8032 :group 'lsp-mode) 8033 8034 (defface lsp-installation-buffer-face '((t :foreground "green")) 8035 "Face used for installation buffers still in progress. 8036 Used in `lsp-select-installation-buffer'." 8037 :group 'lsp-mode) 8038 8039 (defun lsp--installation-buffer? (buf) 8040 "Check whether BUF is an `lsp-async-start-process' buffer." 8041 (buffer-local-value 'lsp-installation-buffer-mode buf)) 8042 8043 (defun lsp-select-installation-buffer (&optional show-finished) 8044 "Interactively choose an installation buffer. 8045 If SHOW-FINISHED is set, leftover (finished) installation buffers 8046 are still shown." 8047 (interactive "P") 8048 (let ((bufs (--filter (and (lsp--installation-buffer? it) 8049 (or show-finished (get-buffer-process it))) 8050 (buffer-list)))) 8051 (pcase bufs 8052 (`nil (user-error "No installation buffers")) 8053 (`(,buf) (pop-to-buffer buf)) 8054 (bufs (pop-to-buffer (completing-read "Select installation buffer: " 8055 (--map (propertize (buffer-name it) 'face 8056 (if (get-buffer-process it) 8057 'lsp-installation-buffer-face 8058 'lsp-installation-finished-buffer-face)) 8059 bufs))))))) 8060 8061 (defun lsp-cleanup-installation-buffers () 8062 "Delete finished *lsp-installation* buffers." 8063 (interactive) 8064 (dolist (buf (buffer-list)) 8065 (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf))) 8066 (kill-buffer buf)))) 8067 8068 (defun lsp--download-status () 8069 (-some--> #'lsp--client-download-in-progress? 8070 (lsp--filter-clients it) 8071 (-map (-compose #'symbol-name #'lsp--client-server-id) it) 8072 (format "%s" it) 8073 (propertize it 'face 'success) 8074 (format " Installing following servers: %s" it) 8075 (propertize it 8076 'local-map (make-mode-line-mouse-map 8077 'mouse-1 #'lsp-select-installation-buffer) 8078 'mouse-face 'highlight))) 8079 8080 (defun lsp--install-server-internal (client &optional update?) 8081 (unless (lsp--client-download-server-fn client) 8082 (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation." 8083 (lsp--client-server-id client))) 8084 8085 (setf (lsp--client-download-in-progress? client) t) 8086 (add-to-list 'global-mode-string '(t (:eval (lsp--download-status)))) 8087 (cl-flet ((done 8088 (success? &optional error-message) 8089 ;; run with idle timer to make sure the lsp command is executed in 8090 ;; the main thread, see #2739. 8091 (run-with-timer 8092 0.0 8093 nil 8094 (lambda () 8095 (-let [(&lsp-cln 'server-id 'buffers) client] 8096 (setf (lsp--client-download-in-progress? client) nil 8097 (lsp--client-buffers client) nil) 8098 (if success? 8099 (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id 8100 (length buffers)) 8101 (lsp--error "Server %s install process failed with the following error message: %s. 8102 Check `*lsp-install*' and `*lsp-log*' buffer." 8103 server-id 8104 error-message)) 8105 (seq-do 8106 (lambda (buffer) 8107 (when (lsp-buffer-live-p buffer) 8108 (lsp-with-current-buffer buffer 8109 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8110 global-mode-string) 8111 (when success? (lsp))))) 8112 buffers) 8113 (unless (lsp--filter-clients #'lsp--client-download-in-progress?) 8114 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8115 global-mode-string))))))) 8116 (lsp--info "Download %s started." (lsp--client-server-id client)) 8117 (condition-case err 8118 (funcall 8119 (lsp--client-download-server-fn client) 8120 client 8121 (lambda () (done t)) 8122 (lambda (msg) (done nil msg)) 8123 update?) 8124 (error 8125 (done nil (error-message-string err)))))) 8126 8127 (defun lsp--require-packages () 8128 "Load `lsp-client-packages' if needed." 8129 (when (and lsp-auto-configure (not lsp--client-packages-required)) 8130 (seq-do (lambda (package) 8131 ;; loading client is slow and `lsp' can be called repeatedly 8132 (unless (featurep package) 8133 (require package nil t))) 8134 lsp-client-packages) 8135 (setq lsp--client-packages-required t))) 8136 8137 ;;;###autoload 8138 (defun lsp-install-server (update? &optional server-id) 8139 "Interactively install or re-install server. 8140 When prefix UPDATE? is t force installation even if the server is present." 8141 (interactive "P") 8142 (lsp--require-packages) 8143 (let* ((chosen-client (or (gethash server-id lsp-clients) 8144 (lsp--completing-read 8145 "Select server to install/re-install: " 8146 (or (->> lsp-clients 8147 (ht-values) 8148 (-filter (-andfn 8149 (-not #'lsp--client-download-in-progress?) 8150 #'lsp--client-download-server-fn))) 8151 (user-error "There are no servers with automatic installation")) 8152 (lambda (client) 8153 (let ((server-name (-> client lsp--client-server-id symbol-name))) 8154 (if (lsp--server-binary-present? client) 8155 (concat server-name " (Already installed)") 8156 server-name))) 8157 nil 8158 t))) 8159 (update? (or update? 8160 (and (not (lsp--client-download-in-progress? chosen-client)) 8161 (lsp--server-binary-present? chosen-client))))) 8162 (lsp--install-server-internal chosen-client update?))) 8163 8164 ;;;###autoload 8165 (defun lsp-uninstall-server (dir) 8166 "Delete a LSP server from `lsp-server-install-dir'." 8167 (interactive 8168 (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir)))) 8169 (unless (file-directory-p dir) 8170 (user-error "Couldn't find %s directory" dir)) 8171 (delete-directory dir 'recursive) 8172 (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir)))) 8173 8174 ;;;###autoload 8175 (defun lsp-uninstall-servers () 8176 "Uninstall all installed servers." 8177 (interactive) 8178 (let* ((dir lsp-server-install-dir) 8179 (servers (ignore-errors 8180 (directory-files dir t 8181 directory-files-no-dot-files-regexp)))) 8182 (if (or (not (file-directory-p dir)) (zerop (length servers))) 8183 (user-error "No servers to uninstall") 8184 (when (yes-or-no-p 8185 (format "Servers to uninstall: %d (%s), proceed? " 8186 (length servers) 8187 (mapconcat (lambda (server) 8188 (file-name-nondirectory (directory-file-name server))) 8189 servers " "))) 8190 (mapc #'lsp-uninstall-server servers) 8191 (message "All servers uninstalled"))))) 8192 8193 ;;;###autoload 8194 (defun lsp-update-server (&optional server-id) 8195 "Interactively update (reinstall) a server." 8196 (interactive) 8197 (lsp--require-packages) 8198 (let ((chosen-client (or (gethash server-id lsp-clients) 8199 (lsp--completing-read 8200 "Select server to update (if not on the list, probably you need to `lsp-install-server`): " 8201 (or (->> lsp-clients 8202 (ht-values) 8203 (-filter (-andfn 8204 (-not #'lsp--client-download-in-progress?) 8205 #'lsp--client-download-server-fn 8206 #'lsp--server-binary-present?))) 8207 (user-error "There are no servers to update")) 8208 (lambda (client) 8209 (-> client lsp--client-server-id symbol-name)) 8210 nil 8211 t)))) 8212 (lsp--install-server-internal chosen-client t))) 8213 8214 ;;;###autoload 8215 (defun lsp-update-servers () 8216 "Update (reinstall) all installed servers." 8217 (interactive) 8218 (lsp--require-packages) 8219 (mapc (lambda (client) (lsp--install-server-internal client t)) 8220 (-filter (-andfn 8221 (-not #'lsp--client-download-in-progress?) 8222 #'lsp--client-download-server-fn 8223 #'lsp--server-binary-present?) (hash-table-values lsp-clients)))) 8224 8225 ;;;###autoload 8226 (defun lsp-ensure-server (server-id) 8227 "Ensure server SERVER-ID" 8228 (lsp--require-packages) 8229 (if-let ((client (gethash server-id lsp-clients))) 8230 (unless (lsp--server-binary-present? client) 8231 (lsp--info "Server `%s' is not preset, installing..." server-id) 8232 (lsp-install-server nil server-id)) 8233 (warn "Unable to find server registration with id %s" server-id))) 8234 8235 (defun lsp-async-start-process (callback error-callback &rest command) 8236 "Start async process COMMAND with CALLBACK and ERROR-CALLBACK." 8237 (let ((name (cl-first command))) 8238 (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd) 8239 (not (null cmd))) 8240 command) 8241 " ") t 8242 (lambda (&rest _) 8243 (generate-new-buffer-name (format "*lsp-install: %s*" name)))) 8244 (lsp-installation-buffer-mode +1) 8245 (view-mode +1) 8246 (add-hook 8247 'compilation-finish-functions 8248 (lambda (_buf status) 8249 (if (string= "finished\n" status) 8250 (condition-case err 8251 (funcall callback) 8252 (error 8253 (funcall error-callback (error-message-string err)))) 8254 (funcall error-callback (s-trim-right status)))) 8255 nil t)))) 8256 8257 (defun lsp-resolve-value (value) 8258 "Resolve VALUE's value. 8259 If it is function - call it. 8260 If it is a variable - return it's value 8261 Otherwise returns value itself." 8262 (cond 8263 ((functionp value) (funcall value)) 8264 ((and (symbolp value) (boundp value)) (symbol-value value)) 8265 (value))) 8266 8267 (defvar lsp-deps-providers 8268 (list :npm (list :path #'lsp--npm-dependency-path 8269 :install #'lsp--npm-dependency-install) 8270 :cargo (list :path #'lsp--cargo-dependency-path 8271 :install #'lsp--cargo-dependency-install) 8272 :system (list :path #'lsp--system-path) 8273 :download (list :path #'lsp-download-path 8274 :install #'lsp-download-install))) 8275 8276 (defun lsp--system-path (path) 8277 "If PATH is absolute and exists return it as is. Otherwise, 8278 return the absolute path to the executable defined by PATH or 8279 nil." 8280 ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the 8281 ;; typescript-language-server. When lsp invokes the server, lsp needs to 8282 ;; supply the path to the typescript compiler, tsserver.js, as an argument. To 8283 ;; make code platform independent, one must pass the absolute path to the 8284 ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript 8285 ;; child process spawn command that is invoked by the 8286 ;; typescript-language-server). This is why we check for existence and not 8287 ;; that the path is executable. 8288 (let ((path (lsp-resolve-value path))) 8289 (cond 8290 ((and (f-absolute? path) 8291 (f-exists? path)) 8292 path) 8293 ((executable-find path t) path)))) 8294 8295 (defun lsp-package-path (dependency) 8296 "Path to the DEPENDENCY each of the registered providers." 8297 (let (path) 8298 (-first (-lambda ((provider . rest)) 8299 (setq path (-some-> lsp-deps-providers 8300 (plist-get provider) 8301 (plist-get :path) 8302 (apply rest)))) 8303 (gethash dependency lsp--dependencies)) 8304 path)) 8305 8306 (defun lsp-package-ensure (dependency callback error-callback) 8307 "Asynchronously ensure a package." 8308 (or (-first (-lambda ((provider . rest)) 8309 (-some-> lsp-deps-providers 8310 (plist-get provider) 8311 (plist-get :install) 8312 (apply (cl-list* callback error-callback rest)))) 8313 (gethash dependency lsp--dependencies)) 8314 (funcall error-callback (format "Unable to find a way to install %s" dependency)))) 8315 8316 8317 ;; npm handling 8318 8319 ;; https://docs.npmjs.com/files/folders#executables 8320 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys) 8321 "Return npm dependency PATH for PACKAGE." 8322 (let ((path (executable-find 8323 (f-join lsp-server-install-dir "npm" package 8324 (cond ((eq system-type 'windows-nt) "") 8325 (t "bin")) 8326 path) 8327 t))) 8328 (unless (and path (f-exists? path)) 8329 (error "The package %s is not installed. Unable to find %s" package path)) 8330 path)) 8331 8332 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys) 8333 (if-let ((npm-binary (executable-find "npm"))) 8334 (progn 8335 ;; Explicitly `make-directory' to work around NPM bug in 8336 ;; versions 7.0.0 through 7.4.1. See 8337 ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for 8338 ;; discussion. 8339 (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents) 8340 (lsp-async-start-process (lambda () 8341 (if (string-empty-p 8342 (string-trim (shell-command-to-string 8343 (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " ")))) 8344 (funcall callback) 8345 (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json"))))) 8346 (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx 8347 (when (f-dir-p default-directory) 8348 (lsp-async-start-process callback 8349 error-callback 8350 (executable-find "npx") 8351 "npm-install-peers"))))) 8352 error-callback 8353 npm-binary 8354 "-g" 8355 "--prefix" 8356 (f-join lsp-server-install-dir "npm" package) 8357 "install" 8358 package)) 8359 (lsp-log "Unable to install %s via `npm' because it is not present" package) 8360 nil)) 8361 8362 8363 ;; Cargo dependency handling 8364 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys) 8365 (let ((path (executable-find 8366 (f-join lsp-server-install-dir 8367 "cargo" 8368 package 8369 "bin" 8370 path) 8371 t))) 8372 (unless (and path (f-exists? path)) 8373 (error "The package %s is not installed. Unable to find %s" package path)) 8374 path)) 8375 8376 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys) 8377 (if-let ((cargo-binary (executable-find "cargo"))) 8378 (lsp-async-start-process 8379 callback 8380 error-callback 8381 cargo-binary 8382 "install" 8383 package 8384 (when git 8385 "--git") 8386 git 8387 "--root" 8388 (f-join lsp-server-install-dir "cargo" package)) 8389 (lsp-log "Unable to install %s via `cargo' because it is not present" package) 8390 nil)) 8391 8392 8393 8394 ;; Download URL handling 8395 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys) 8396 (let* ((url (lsp-resolve-value url)) 8397 (store-path (lsp-resolve-value store-path)) 8398 ;; (decompress (lsp-resolve-value decompress)) 8399 (download-path 8400 (pcase decompress 8401 (:gzip (concat store-path ".gz")) 8402 (:zip (concat store-path ".zip")) 8403 (:targz (concat store-path ".tar.gz")) 8404 (`nil store-path) 8405 (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'"))))) 8406 (make-thread 8407 (lambda () 8408 (condition-case err 8409 (progn 8410 (when (f-exists? download-path) 8411 (f-delete download-path)) 8412 (when (f-exists? store-path) 8413 (f-delete store-path)) 8414 (lsp--info "Starting to download %s to %s..." url download-path) 8415 (mkdir (f-parent download-path) t) 8416 (url-copy-file url download-path) 8417 (lsp--info "Finished downloading %s..." download-path) 8418 (when (and lsp-verify-signature asc-url pgp-key) 8419 (if (executable-find epg-gpg-program) 8420 (let ((asc-download-path (concat download-path ".asc")) 8421 (context (epg-make-context)) 8422 (fingerprint) 8423 (signature)) 8424 (when (f-exists? asc-download-path) 8425 (f-delete asc-download-path)) 8426 (lsp--info "Starting to download %s to %s..." asc-url asc-download-path) 8427 (url-copy-file asc-url asc-download-path) 8428 (lsp--info "Finished downloading %s..." asc-download-path) 8429 (epg-import-keys-from-string context pgp-key) 8430 (setq fingerprint (epg-import-status-fingerprint 8431 (car 8432 (epg-import-result-imports 8433 (epg-context-result-for context 'import))))) 8434 (lsp--info "Verifying signature %s..." asc-download-path) 8435 (epg-verify-file context asc-download-path download-path) 8436 (setq signature (car (epg-context-result-for context 'verify))) 8437 (unless (and 8438 (eq (epg-signature-status signature) 'good) 8439 (equal (epg-signature-fingerprint signature) fingerprint)) 8440 (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature)))) 8441 (lsp--warn "GPG is not installed, skipping the signature check."))) 8442 (when decompress 8443 (lsp--info "Decompressing %s..." download-path) 8444 (pcase decompress 8445 (:gzip 8446 (lsp-gunzip download-path)) 8447 (:zip (lsp-unzip download-path (f-parent store-path))) 8448 (:targz (lsp-tar-gz-decompress download-path (f-parent store-path)))) 8449 (lsp--info "Decompressed %s..." store-path)) 8450 (funcall callback)) 8451 (error (funcall error-callback err))))))) 8452 8453 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys) 8454 "Download URL and store it into STORE-PATH. 8455 8456 SET-EXECUTABLE? when non-nil change the executable flags of 8457 STORE-PATH to make it executable. BINARY-PATH can be specified 8458 when the binary to start does not match the name of the 8459 archive (e.g. when the archive has multiple files)" 8460 (let ((store-path (or (lsp-resolve-value binary-path) 8461 (lsp-resolve-value store-path)))) 8462 (cond 8463 ((executable-find store-path) store-path) 8464 ((and set-executable? (f-exists? store-path)) 8465 (set-file-modes store-path #o0700) 8466 store-path) 8467 ((f-exists? store-path) store-path)))) 8468 8469 (defun lsp--find-latest-gh-release-url (url regex) 8470 "Fetch the latest version in the releases given by URL by using REGEX." 8471 (let ((url-request-method "GET")) 8472 (with-current-buffer (url-retrieve-synchronously url) 8473 (goto-char (point-min)) 8474 (re-search-forward "\n\n" nil 'noerror) 8475 (delete-region (point-min) (point)) 8476 (let* ((json-result (lsp-json-read-buffer))) 8477 (message "Latest version found: %s" (lsp-get json-result :tag_name)) 8478 (--> json-result 8479 (lsp-get it :assets) 8480 (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it) 8481 (lsp-get it :browser_download_url)))))) 8482 8483 ;; unzip 8484 8485 (defconst lsp-ext-pwsh-script "pwsh -noprofile -noninteractive \ 8486 -nologo -ex bypass -c Expand-Archive -Path '%s' -DestinationPath '%s'" 8487 "Pwsh script to unzip file.") 8488 8489 (defconst lsp-ext-powershell-script "powershell -noprofile -noninteractive \ 8490 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'" 8491 "Powershell script to unzip file.") 8492 8493 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'" 8494 "Unzip script to unzip file.") 8495 8496 (defcustom lsp-unzip-script (lambda () 8497 (cond ((and (eq system-type 'windows-nt) 8498 (executable-find "pwsh")) 8499 lsp-ext-pwsh-script) 8500 ((and (eq system-type 'windows-nt) 8501 (executable-find "powershell")) 8502 lsp-ext-powershell-script) 8503 ((executable-find "unzip") lsp-ext-unzip-script) 8504 ((executable-find "pwsh") lsp-ext-pwsh-script) 8505 (t nil))) 8506 "The script to unzip." 8507 :group 'lsp-mode 8508 :type 'string 8509 :package-version '(lsp-mode . "8.0.0")) 8510 8511 (defun lsp-unzip (zip-file dest) 8512 "Unzip ZIP-FILE to DEST." 8513 (unless lsp-unzip-script 8514 (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'")) 8515 (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest))) 8516 8517 ;; gunzip 8518 8519 (defconst lsp-ext-gunzip-script "gzip -d %1$s" 8520 "Script to decompress a gzippped file with gzip.") 8521 8522 (defcustom lsp-gunzip-script (lambda () 8523 (cond ((executable-find "gzip") lsp-ext-gunzip-script) 8524 (t nil))) 8525 "The script to decompress a gzipped file. 8526 Should be a format string with one argument for the file to be decompressed 8527 in place." 8528 :group 'lsp-mode 8529 :type 'string 8530 :package-version '(lsp-mode . "8.0.0")) 8531 8532 (defun lsp-gunzip (gz-file) 8533 "Decompress GZ-FILE in place." 8534 (unless lsp-gunzip-script 8535 (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file)) 8536 (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file))) 8537 8538 ;; tar.gz decompression 8539 8540 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'" 8541 "Script to decompress a .tar.gz file.") 8542 8543 (defcustom lsp-tar-script (lambda () 8544 (cond ((executable-find "tar") lsp-ext-tar-script) 8545 (t nil))) 8546 "The script to decompress a .tar.gz file. 8547 Should be a format string with one argument for the file to be decompressed 8548 in place." 8549 :group 'lsp-mode 8550 :type 'string) 8551 8552 (defun lsp-tar-gz-decompress (targz-file dest) 8553 "Decompress TARGZ-FILE in DEST." 8554 (unless lsp-tar-script 8555 (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file)) 8556 (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest))) 8557 8558 8559 ;; VSCode marketplace 8560 8561 (defcustom lsp-vscode-ext-url 8562 "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s" 8563 "Vscode extension template url." 8564 :group 'lsp-mode 8565 :type 'string 8566 :package-version '(lsp-mode . "8.0.0")) 8567 8568 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform) 8569 "Return the URL to vscode extension. 8570 PUBLISHER is the extension publisher. 8571 NAME is the name of the extension. 8572 VERSION is the version of the extension. 8573 TARGETPLATFORM is the targetPlatform of the extension." 8574 (format lsp-vscode-ext-url publisher name version (or targetPlatform ""))) 8575 8576 8577 8578 ;; Queueing prompts 8579 8580 (defvar lsp--question-queue nil 8581 "List of questions yet to be asked by `lsp-ask-question'.") 8582 8583 (defun lsp-ask-question (question options callback) 8584 "Prompt the user to answer the QUESTION with one of the OPTIONS from the 8585 minibuffer. Once the user selects an option, the CALLBACK function will be 8586 called, passing the selected option to it. 8587 8588 If the user is currently being shown a question, the question will be stored in 8589 `lsp--question-queue', and will be asked once the user has answered the current 8590 question." 8591 (add-to-list 'lsp--question-queue `(("question" . ,question) 8592 ("options" . ,options) 8593 ("callback" . ,callback)) t) 8594 (when (eq (length lsp--question-queue) 1) 8595 (lsp--process-question-queue))) 8596 8597 (defun lsp--process-question-queue () 8598 "Take the first question from `lsp--question-queue', process it, then process 8599 the next question until the queue is empty." 8600 (-let* (((&alist "question" "options" "callback") (car lsp--question-queue)) 8601 (answer (completing-read question options nil t))) 8602 (pop lsp--question-queue) 8603 (funcall callback answer) 8604 (when lsp--question-queue 8605 (lsp--process-question-queue)))) 8606 8607 (defun lsp--supports-buffer? (client) 8608 (and 8609 ;; both file and client remote or both local 8610 (eq (---truthy? (file-remote-p (buffer-file-name))) 8611 (---truthy? (lsp--client-remote? client))) 8612 8613 ;; activation function or major-mode match. 8614 (if-let ((activation-fn (lsp--client-activation-fn client))) 8615 (funcall activation-fn (buffer-file-name) major-mode) 8616 (-contains? (lsp--client-major-modes client) major-mode)) 8617 8618 ;; check whether it is enabled if `lsp-enabled-clients' is not null 8619 (or (null lsp-enabled-clients) 8620 (or (member (lsp--client-server-id client) lsp-enabled-clients) 8621 (ignore (lsp--info "Client %s is not in lsp-enabled-clients" 8622 (lsp--client-server-id client))))) 8623 8624 ;; check whether it is not disabled. 8625 (not (lsp--client-disabled-p major-mode (lsp--client-server-id client))))) 8626 8627 (defun lsp--filter-clients (pred) 8628 (->> lsp-clients hash-table-values (-filter pred))) 8629 8630 (defun lsp--find-clients () 8631 "Find clients which can handle current buffer." 8632 (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 8633 #'lsp--server-binary-present?))) 8634 (lsp-log "Found the following clients for %s: %s" 8635 (buffer-file-name) 8636 (s-join ", " 8637 (-map (lambda (client) 8638 (format "(server-id %s, priority %s)" 8639 (lsp--client-server-id client) 8640 (lsp--client-priority client))) 8641 matching-clients))) 8642 (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients)) 8643 (selected-clients (if-let ((main-client (and main-clients 8644 (--max-by (> (lsp--client-priority it) 8645 (lsp--client-priority other)) 8646 main-clients)))) 8647 (cons main-client add-on-clients) 8648 add-on-clients))) 8649 (lsp-log "The following clients were selected based on priority: %s" 8650 (s-join ", " 8651 (-map (lambda (client) 8652 (format "(server-id %s, priority %s)" 8653 (lsp--client-server-id client) 8654 (lsp--client-priority client))) 8655 selected-clients))) 8656 selected-clients))) 8657 8658 (defun lsp-workspace-remove-all-folders() 8659 "Delete all lsp tracked folders." 8660 (interactive) 8661 (--each (lsp-session-folders (lsp-session)) 8662 (lsp-workspace-folders-remove it))) 8663 8664 (defun lsp-register-client (client) 8665 "Registers LSP client CLIENT." 8666 (let ((client-id (lsp--client-server-id client))) 8667 (puthash client-id client lsp-clients) 8668 (setplist (intern (format "lsp-%s-after-open-hook" client-id)) 8669 `( standard-value (nil) custom-type hook 8670 custom-package-version (lsp-mode . "7.0.1") 8671 variable-documentation ,(format "Hooks to run after `%s' server is run." client-id) 8672 custom-requests nil))) 8673 (when (and lsp-auto-register-remote-clients 8674 (not (lsp--client-remote? client))) 8675 (let ((remote-client (copy-lsp--client client))) 8676 (setf (lsp--client-remote? remote-client) t 8677 (lsp--client-server-id remote-client) (intern 8678 (format "%s-tramp" 8679 (lsp--client-server-id client))) 8680 ;; disable automatic download 8681 (lsp--client-download-server-fn remote-client) nil) 8682 (lsp-register-client remote-client)))) 8683 8684 (defun lsp--create-initialization-options (_session client) 8685 "Create initialization-options from SESSION and CLIENT. 8686 Add workspace folders depending on server being multiroot and 8687 session workspace folder configuration for the server." 8688 (let* ((initialization-options-or-fn (lsp--client-initialization-options client))) 8689 (if (functionp initialization-options-or-fn) 8690 (funcall initialization-options-or-fn) 8691 initialization-options-or-fn))) 8692 8693 (defvar lsp-client-settings (make-hash-table :test 'equal) 8694 "For internal use, any external users please use 8695 `lsp-register-custom-settings' function instead") 8696 8697 (defun lsp-register-custom-settings (props) 8698 "Register PROPS. 8699 PROPS is list of triple (path value boolean?) where PATH is the path to the 8700 property; VALUE can be a literal value, symbol to be evaluated, or either a 8701 function or lambda function to be called without arguments; BOOLEAN? is an 8702 optional flag that should be non-nil for boolean settings, when it is nil the 8703 property will be ignored if the VALUE is nil. 8704 8705 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))' 8706 \(note the double parentheses)" 8707 (mapc 8708 (-lambda ((path . rest)) 8709 (puthash path rest lsp-client-settings)) 8710 props)) 8711 8712 (defun lsp-region-text (region) 8713 "Get the text for REGION in current buffer." 8714 (-let (((start . end) (lsp--range-to-region region))) 8715 (buffer-substring-no-properties start end))) 8716 8717 (defun lsp-ht-set (tbl paths value) 8718 "Set nested hash table value. 8719 TBL - a hash table, PATHS is the path to the nested VALUE." 8720 (pcase paths 8721 (`(,path) (ht-set! tbl path value)) 8722 (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl) 8723 (let ((temp-tbl (ht))) 8724 (ht-set! tbl path temp-tbl) 8725 temp-tbl)))) 8726 (lsp-ht-set nested-tbl rst value))))) 8727 8728 ;; sections 8729 8730 (defalias 'defcustom-lsp 'lsp-defcustom) 8731 8732 (defmacro lsp-defcustom (symbol standard doc &rest args) 8733 "Defines `lsp-mode' server property." 8734 (declare (doc-string 3) (debug (name body)) 8735 (indent defun)) 8736 (let ((path (plist-get args :lsp-path)) 8737 (setter (intern (concat (symbol-name symbol) "--set")))) 8738 (cl-remf args :lsp-path) 8739 `(progn 8740 (lsp-register-custom-settings 8741 (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type)))))) 8742 8743 (defcustom ,symbol ,standard ,doc ,@args) 8744 8745 ;; Use a variable watcher instead of registering a `defcustom' 8746 ;; setter since `hack-local-variables' is not aware of custom 8747 ;; setters and won't invoke them. 8748 8749 (defun ,setter (sym val op _where) 8750 (when (eq op 'set) 8751 (lsp--set-custom-property sym val ,path))) 8752 8753 (add-variable-watcher ',symbol #',setter)))) 8754 8755 (defun lsp--set-custom-property (sym val path) 8756 (set sym val) 8757 (let ((section (cl-first (s-split "\\." path)))) 8758 (mapc (lambda (workspace) 8759 (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace)) 8760 section) 8761 (with-lsp-workspace workspace 8762 (lsp--set-configuration (lsp-configuration-section section))))) 8763 (lsp--session-workspaces (lsp-session))))) 8764 8765 (defun lsp-configuration-section (section) 8766 "Get settings for SECTION." 8767 (let ((ret (ht-create))) 8768 (maphash (-lambda (path (variable boolean?)) 8769 (when (s-matches? (concat (regexp-quote section) "\\..*") path) 8770 (let* ((symbol-value (-> variable 8771 lsp-resolve-value 8772 lsp-resolve-value)) 8773 (value (if (and boolean? (not symbol-value)) 8774 :json-false 8775 symbol-value))) 8776 (when (or boolean? value) 8777 (lsp-ht-set ret (s-split "\\." path) value))))) 8778 lsp-client-settings) 8779 ret)) 8780 8781 8782 (defun lsp--start-connection (session client project-root) 8783 "Initiates connection created from CLIENT for PROJECT-ROOT. 8784 SESSION is the active session." 8785 (when (lsp--client-multi-root client) 8786 (cl-pushnew project-root (gethash (lsp--client-server-id client) 8787 (lsp-session-server-id->folders session)))) 8788 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil) 8789 8790 (unwind-protect 8791 (lsp--start-workspace session client project-root (lsp--create-initialization-options session client)) 8792 (lsp--spinner-stop))) 8793 8794 ;; lsp-log-io-mode 8795 8796 (defvar lsp-log-io-mode-map 8797 (let ((map (make-sparse-keymap))) 8798 (define-key map (kbd "M-n") #'lsp-log-io-next) 8799 (define-key map (kbd "M-p") #'lsp-log-io-prev) 8800 (define-key map (kbd "k") #'lsp--erase-log-buffer) 8801 (define-key map (kbd "K") #'lsp--erase-session-log-buffers) 8802 map) 8803 "Keymap for lsp log buffer mode.") 8804 8805 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo" 8806 "Special mode for viewing IO logs.") 8807 8808 (defun lsp-workspace-show-log (workspace) 8809 "Display the log buffer of WORKSPACE." 8810 (interactive 8811 (list (if lsp-log-io 8812 (if (eq (length (lsp-workspaces)) 1) 8813 (cl-first (lsp-workspaces)) 8814 (lsp--completing-read "Workspace: " (lsp-workspaces) 8815 #'lsp--workspace-print nil t)) 8816 (user-error "IO logging is disabled")))) 8817 (pop-to-buffer (lsp--get-log-buffer-create workspace))) 8818 8819 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log) 8820 8821 (defun lsp--get-log-buffer-create (workspace) 8822 "Return the lsp log buffer of WORKSPACE, creating a new one if needed." 8823 (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8824 (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id))) 8825 (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid)))) 8826 8827 (defun lsp--erase-log-buffer (&optional all) 8828 "Delete contents of current lsp log buffer. 8829 When ALL is t, erase all log buffers of the running session." 8830 (interactive) 8831 (let* ((workspaces (lsp--session-workspaces (lsp-session))) 8832 (current-log-buffer (current-buffer))) 8833 (dolist (w workspaces) 8834 (let ((b (lsp--get-log-buffer-create w))) 8835 (when (or all (eq b current-log-buffer)) 8836 (with-current-buffer b 8837 (let ((inhibit-read-only t)) 8838 (erase-buffer)))))))) 8839 8840 (defun lsp--erase-session-log-buffers () 8841 "Erase log buffers of the running session." 8842 (interactive) 8843 (lsp--erase-log-buffer t)) 8844 8845 (defun lsp-log-io-next (arg) 8846 "Move to next log entry." 8847 (interactive "P") 8848 (ewoc-goto-next lsp--log-io-ewoc (or arg 1))) 8849 8850 (defun lsp-log-io-prev (arg) 8851 "Move to previous log entry." 8852 (interactive "P") 8853 (ewoc-goto-prev lsp--log-io-ewoc (or arg 1))) 8854 8855 8856 8857 (cl-defmethod lsp-process-id ((process process)) 8858 (process-id process)) 8859 8860 (cl-defmethod lsp-process-name ((process process)) (process-name process)) 8861 8862 (cl-defmethod lsp-process-status ((process process)) (process-status process)) 8863 8864 (cl-defmethod lsp-process-kill ((process process)) 8865 (when (process-live-p process) 8866 (kill-process process))) 8867 8868 (cl-defmethod lsp-process-send ((process process) message) 8869 (condition-case err 8870 (process-send-string process (lsp--make-message message)) 8871 (error (lsp--error "Sending to process failed with the following error: %s" 8872 (error-message-string err))))) 8873 8874 (cl-defmethod lsp-process-cleanup (process) 8875 ;; Kill standard error buffer only if the process exited normally. 8876 ;; Leave it intact otherwise for debugging purposes. 8877 (let ((buffer (-> process process-name get-buffer))) 8878 (when (and (eq (process-status process) 'exit) 8879 (zerop (process-exit-status process)) 8880 (buffer-live-p buffer)) 8881 (kill-buffer buffer)))) 8882 8883 8884 ;; native JSONRPC 8885 8886 (declare-function json-rpc "ext:json") 8887 (declare-function json-rpc-connection "ext:json") 8888 (declare-function json-rpc-send "ext:json") 8889 (declare-function json-rpc-shutdown "ext:json") 8890 (declare-function json-rpc-stderr "ext:json") 8891 (declare-function json-rpc-pid "ext:json") 8892 8893 (defvar lsp-json-rpc-thread nil) 8894 (defvar lsp-json-rpc-queue nil) 8895 (defvar lsp-json-rpc-done nil) 8896 (defvar lsp-json-rpc-mutex (make-mutex)) 8897 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex)) 8898 8899 (defun lsp-json-rpc-process-queue () 8900 (while (not lsp-json-rpc-done) 8901 (while lsp-json-rpc-queue 8902 (-let (((proc . message) (pop lsp-json-rpc-queue))) 8903 (json-rpc-send 8904 proc message 8905 :null-object nil 8906 :false-object :json-false))) 8907 (with-mutex lsp-json-rpc-mutex 8908 (condition-wait lsp-json-rpc-condition)))) 8909 8910 (cl-defmethod lsp-process-id (process) (json-rpc-pid process)) 8911 8912 (cl-defmethod lsp-process-name (_process) "TBD") 8913 8914 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process)) 8915 8916 (cl-defmethod lsp-process-send (proc message) 8917 (unless lsp-json-rpc-thread 8918 (with-current-buffer (get-buffer-create " *json-rpc*") 8919 (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*")))) 8920 8921 (with-mutex lsp-json-rpc-mutex 8922 (setq lsp-json-rpc-queue (append lsp-json-rpc-queue 8923 (list (cons proc message)))) 8924 (condition-notify lsp-json-rpc-condition))) 8925 8926 (cl-defmethod lsp-process-cleanup (_proc)) 8927 8928 (defun lsp-json-rpc-connection (workspace command) 8929 (let ((con (apply #'json-rpc-connection command)) 8930 (object-type (if lsp-use-plists 'plist 'hash-table))) 8931 (with-current-buffer (get-buffer-create " *json-rpc*") 8932 (make-thread 8933 (lambda () 8934 (json-rpc 8935 con 8936 (lambda (result err done) 8937 (run-with-timer 8938 0.0 8939 nil 8940 (lambda () 8941 (cond 8942 (result (lsp--parser-on-message result workspace)) 8943 (err (warn "Json parsing failed with the following error: %s" err)) 8944 (done (lsp--handle-process-exit workspace "")))))) 8945 :object-type object-type 8946 :null-object nil 8947 :false-object nil)) 8948 "*json-rpc-connection*")) 8949 (cons con con))) 8950 8951 (defun lsp-json-rpc-stderr () 8952 (interactive) 8953 (--when-let (pcase (lsp-workspaces) 8954 (`nil (user-error "There are no active servers in the current buffer")) 8955 (`(,workspace) workspace) 8956 (workspaces (lsp--completing-read "Select server: " 8957 workspaces 8958 'lsp--workspace-print nil t))) 8959 (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it))) 8960 (buffer (format "*stderr-%s*" (lsp--workspace-print it)) )) 8961 (with-current-buffer (get-buffer-create buffer) 8962 (with-help-window buffer 8963 (insert content)))))) 8964 8965 8966 (defun lsp--workspace-print (workspace) 8967 "Visual representation WORKSPACE." 8968 (let* ((proc (lsp--workspace-cmd-proc workspace)) 8969 (status (lsp--workspace-status workspace)) 8970 (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8971 (pid (lsp-process-id proc))) 8972 8973 (if (eq 'initialized status) 8974 (format "%s:%s" server-id pid) 8975 (format "%s:%s/%s" server-id pid status)))) 8976 8977 (defun lsp--map-tree-widget (m) 8978 "Build `tree-widget' from a hash-table or plist M." 8979 (when (lsp-structure-p m) 8980 (let (nodes) 8981 (lsp-map (lambda (k v) 8982 (push `(tree-widget 8983 :tag ,(if (lsp-structure-p v) 8984 (format "%s:" k) 8985 (format "%s: %s" k 8986 (propertize (format "%s" v) 8987 'face 8988 'font-lock-string-face))) 8989 :open t 8990 ,@(lsp--map-tree-widget v)) 8991 nodes)) 8992 m) 8993 nodes))) 8994 8995 (defun lsp-buffer-name (buffer-id) 8996 (if-let ((buffer-name (plist-get buffer-id :buffer-name))) 8997 (funcall buffer-name buffer-id) 8998 (buffer-name buffer-id))) 8999 9000 (defun lsp--render-workspace (workspace) 9001 "Tree node representation of WORKSPACE." 9002 `(tree-widget :tag ,(lsp--workspace-print workspace) 9003 :open t 9004 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face) 9005 :open t 9006 ,@(->> workspace 9007 (lsp--workspace-buffers) 9008 (--map `(tree-widget 9009 :tag ,(when (lsp-buffer-live-p it) 9010 (let ((buffer-name (lsp-buffer-name it))) 9011 (if (lsp-with-current-buffer it buffer-read-only) 9012 (propertize buffer-name 'face 'font-lock-constant-face) 9013 buffer-name))))))) 9014 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face) 9015 ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget)))) 9016 9017 (define-derived-mode lsp-browser-mode special-mode "LspBrowser" 9018 "Define mode for displaying lsp sessions." 9019 (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t))))) 9020 9021 (defun lsp-describe-session () 9022 "Describes current `lsp-session'." 9023 (interactive) 9024 (let ((session (lsp-session)) 9025 (buf (get-buffer-create "*lsp session*")) 9026 (root (lsp-workspace-root))) 9027 (with-current-buffer buf 9028 (lsp-browser-mode) 9029 (let ((inhibit-read-only t)) 9030 (erase-buffer) 9031 (--each (lsp-session-folders session) 9032 (widget-create 9033 `(tree-widget 9034 :tag ,(propertize it 'face 'font-lock-keyword-face) 9035 :open t 9036 ,@(->> session 9037 (lsp-session-folder->servers) 9038 (gethash it) 9039 (-map 'lsp--render-workspace))))))) 9040 (pop-to-buffer buf) 9041 (goto-char (point-min)) 9042 (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag) 9043 until (or (and root (string= tag root)) (eobp)) 9044 do (goto-char (next-overlay-change (point)))))) 9045 9046 (defun lsp--session-workspaces (session) 9047 "Get all workspaces that are part of the SESSION." 9048 (-> session lsp-session-folder->servers hash-table-values -flatten -uniq)) 9049 9050 (defun lsp--find-multiroot-workspace (session client project-root) 9051 "Look for a multiroot connection in SESSION created from CLIENT for 9052 PROJECT-ROOT and BUFFER-MAJOR-MODE." 9053 (when (lsp--client-multi-root client) 9054 (-when-let (multi-root-workspace (->> session 9055 (lsp--session-workspaces) 9056 (--first (eq (-> it lsp--workspace-client lsp--client-server-id) 9057 (lsp--client-server-id client))))) 9058 (with-lsp-workspace multi-root-workspace 9059 (lsp-notify "workspace/didChangeWorkspaceFolders" 9060 (lsp-make-did-change-workspace-folders-params 9061 :event (lsp-make-workspace-folders-change-event 9062 :added (vector (lsp-make-workspace-folder 9063 :uri (lsp--path-to-uri project-root) 9064 :name (f-filename project-root))) 9065 :removed [])))) 9066 9067 (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace)) 9068 (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root)) 9069 9070 (lsp--persist-session session) 9071 9072 (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace)) 9073 (lsp--open-in-workspace multi-root-workspace) 9074 9075 multi-root-workspace))) 9076 9077 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder) 9078 "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT. 9079 IGNORE-MULTI-FOLDER to ignore multi folder server." 9080 (-map (lambda (client) 9081 (or 9082 (lsp--find-workspace session client project-root) 9083 (unless ignore-multi-folder 9084 (lsp--find-multiroot-workspace session client project-root)) 9085 (lsp--start-connection session client project-root))) 9086 clients)) 9087 9088 (defun lsp--spinner-stop () 9089 "Stop the spinner in case all of the workspaces are started." 9090 (when (--all? (eq (lsp--workspace-status it) 'initialized) 9091 lsp--buffer-workspaces) 9092 (spinner-stop))) 9093 9094 (defun lsp--open-in-workspace (workspace) 9095 "Open in existing WORKSPACE." 9096 (if (eq 'initialized (lsp--workspace-status workspace)) 9097 ;; when workspace is initialized just call document did open. 9098 (progn 9099 (with-lsp-workspace workspace 9100 (when-let ((before-document-open-fn (-> workspace 9101 lsp--workspace-client 9102 lsp--client-before-file-open-fn))) 9103 (funcall before-document-open-fn workspace)) 9104 (lsp--text-document-did-open)) 9105 (lsp--spinner-stop)) 9106 ;; when it is not initialized 9107 (lsp--spinner-start) 9108 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace)))) 9109 9110 (defun lsp--find-workspace (session client project-root) 9111 "Find server connection created with CLIENT in SESSION for PROJECT-ROOT." 9112 (when-let ((workspace (->> session 9113 (lsp-session-folder->servers) 9114 (gethash project-root) 9115 (--first (eql (-> it lsp--workspace-client lsp--client-server-id) 9116 (lsp--client-server-id client)))))) 9117 (lsp--open-in-workspace workspace) 9118 workspace)) 9119 9120 (defun lsp--read-char (prompt &optional options) 9121 "Wrapper for `read-char-from-minibuffer' if Emacs +27. 9122 Fallback to `read-key' otherwise. 9123 PROMPT is the message and OPTIONS the available options." 9124 (if (fboundp 'read-char-from-minibuffer) 9125 (read-char-from-minibuffer prompt options) 9126 (read-key prompt))) 9127 9128 (defun lsp--find-root-interactively (session) 9129 "Find project interactively. 9130 Returns nil if the project should not be added to the current SESSION." 9131 (condition-case nil 9132 (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory)) 9133 (action (lsp--read-char 9134 (format 9135 "%s is not part of any project. 9136 9137 %s ==> Import project root %s 9138 %s ==> Import project by selecting root directory interactively 9139 %s ==> Import project at current directory %s 9140 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist 9141 %s ==> Do not ask again for the current project by selecting ignore path interactively 9142 %s ==> Do nothing: ask again when opening other files from the current project 9143 9144 Select action: " 9145 (propertize (buffer-name) 'face 'bold) 9146 (propertize "i" 'face 'success) 9147 (propertize project-root-suggestion 'face 'bold) 9148 (propertize "I" 'face 'success) 9149 (propertize "." 'face 'success) 9150 (propertize default-directory 'face 'bold) 9151 (propertize "d" 'face 'warning) 9152 (propertize project-root-suggestion 'face 'bold) 9153 (propertize "D" 'face 'warning) 9154 (propertize "n" 'face 'warning)) 9155 '(?i ?\r ?I ?. ?d ?D ?n)))) 9156 (cl-case action 9157 (?i project-root-suggestion) 9158 (?\r project-root-suggestion) 9159 (?I (read-directory-name "Select workspace folder to add: " 9160 (or project-root-suggestion default-directory) 9161 nil 9162 t)) 9163 (?. default-directory) 9164 (?d (push project-root-suggestion (lsp-session-folders-blocklist session)) 9165 (lsp--persist-session session) 9166 nil) 9167 (?D (push (read-directory-name "Select folder to blocklist: " 9168 (or project-root-suggestion default-directory) 9169 nil 9170 t) 9171 (lsp-session-folders-blocklist session)) 9172 (lsp--persist-session session) 9173 nil) 9174 (t nil))) 9175 (quit))) 9176 9177 (declare-function tramp-file-name-host "ext:tramp" (file) t) 9178 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault)) 9179 9180 (defun lsp--files-same-host (f1 f2) 9181 "Predicate on whether or not two files are on the same host." 9182 (or (not (or (file-remote-p f1) (file-remote-p f2))) 9183 (and (file-remote-p f1) 9184 (file-remote-p f2) 9185 (progn (require 'tramp) 9186 (equal (tramp-file-name-host (tramp-dissect-file-name f1)) 9187 (tramp-file-name-host (tramp-dissect-file-name f2))))))) 9188 9189 (defun lsp-find-session-folder (session file-name) 9190 "Look in the current SESSION for folder containing FILE-NAME." 9191 (let ((file-name-canonical (lsp-f-canonical file-name))) 9192 (->> session 9193 (lsp-session-folders) 9194 (--filter (and (lsp--files-same-host it file-name-canonical) 9195 (or (lsp-f-same? it file-name-canonical) 9196 (and (f-dir? it) 9197 (lsp-f-ancestor-of? it file-name-canonical))))) 9198 (--max-by (> (length it) 9199 (length other)))))) 9200 9201 (defun lsp-find-workspace (server-id &optional file-name) 9202 "Find workspace for SERVER-ID for FILE-NAME." 9203 (-when-let* ((session (lsp-session)) 9204 (folder->servers (lsp-session-folder->servers session)) 9205 (workspaces (if file-name 9206 (gethash (lsp-find-session-folder session file-name) folder->servers) 9207 (lsp--session-workspaces session)))) 9208 9209 (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces))) 9210 9211 (defun lsp--calculate-root (session file-name) 9212 "Calculate project root for FILE-NAME in SESSION." 9213 (and 9214 (->> session 9215 (lsp-session-folders-blocklist) 9216 (--first (and (lsp--files-same-host it file-name) 9217 (lsp-f-ancestor-of? it file-name) 9218 (prog1 t 9219 (lsp--info "File %s is in blocklisted directory %s" file-name it)))) 9220 not) 9221 (or 9222 (when lsp-auto-guess-root 9223 (lsp--suggest-project-root)) 9224 (unless lsp-guess-root-without-session 9225 (lsp-find-session-folder session file-name)) 9226 (unless lsp-auto-guess-root 9227 (when-let ((root-folder (lsp--find-root-interactively session))) 9228 (if (or (not (f-equal? root-folder (expand-file-name "~/"))) 9229 (yes-or-no-p 9230 (concat 9231 (propertize "[WARNING] " 'face 'warning) 9232 "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: 9233 9234 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/'). 9235 2. If your file is under `~/' then create a subfolder and move that file in this folder. 9236 9237 Type `No' to go back to project selection. 9238 Type `Yes' to confirm `HOME' as project root. 9239 Type `C-g' to cancel project import process and stop `lsp'"))) 9240 root-folder 9241 (lsp--calculate-root session file-name))))))) 9242 9243 (defun lsp--try-open-in-library-workspace () 9244 "Try opening current file as library file in any of the active workspace. 9245 The library folders are defined by each client for each of the active workspace." 9246 (when-let ((workspace (->> (lsp-session) 9247 (lsp--session-workspaces) 9248 ;; Sort the last active workspaces first as they are more likely to be 9249 ;; the correct ones, especially when jumping to a definition. 9250 (-sort (lambda (a _b) 9251 (-contains? lsp--last-active-workspaces a))) 9252 (--first 9253 (and (-> it lsp--workspace-client lsp--supports-buffer?) 9254 (when-let ((library-folders-fn 9255 (-> it lsp--workspace-client lsp--client-library-folders-fn))) 9256 (-first (lambda (library-folder) 9257 (lsp-f-ancestor-of? library-folder (buffer-file-name))) 9258 (funcall library-folders-fn it)))))))) 9259 (lsp--open-in-workspace workspace) 9260 (view-mode t) 9261 (lsp--info "Opening read-only library file %s." (buffer-file-name)) 9262 (list workspace))) 9263 9264 (defun lsp--persist-session (session) 9265 "Persist SESSION to `lsp-session-file'." 9266 (lsp--persist lsp-session-file (make-lsp-session 9267 :folders (lsp-session-folders session) 9268 :folders-blocklist (lsp-session-folders-blocklist session) 9269 :server-id->folders (lsp-session-server-id->folders session)))) 9270 9271 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder) 9272 "Try create opening file as a project file. 9273 When IGNORE-MULTI-FOLDER is t the lsp mode will start new 9274 language server even if there is language server which can handle 9275 current language. When IGNORE-MULTI-FOLDER is nil current file 9276 will be opened in multi folder language server if there is 9277 such." 9278 (-let ((session (lsp-session))) 9279 (-if-let (clients (if ask-for-client 9280 (list (lsp--completing-read "Select server to start: " 9281 (ht-values lsp-clients) 9282 (-compose 'symbol-name 'lsp--client-server-id) nil t)) 9283 (lsp--find-clients))) 9284 (-if-let (project-root (-some-> session 9285 (lsp--calculate-root (buffer-file-name)) 9286 (lsp-f-canonical))) 9287 (progn 9288 ;; update project roots if needed and persist the lsp session 9289 (unless (-contains? (lsp-session-folders session) project-root) 9290 (cl-pushnew project-root (lsp-session-folders session)) 9291 (lsp--persist-session session)) 9292 (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder)) 9293 (lsp--warn "%s not in project or it is blocklisted." (buffer-name)) 9294 nil) 9295 (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode) 9296 nil))) 9297 9298 (defun lsp-shutdown-workspace () 9299 "Shutdown language server." 9300 (interactive) 9301 (--when-let (pcase (lsp-workspaces) 9302 (`nil (user-error "There are no active servers in the current buffer")) 9303 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?" 9304 (lsp--workspace-print workspace))) 9305 workspace)) 9306 (workspaces (lsp--completing-read "Select server: " 9307 workspaces 9308 'lsp--workspace-print nil t))) 9309 (lsp-workspace-shutdown it))) 9310 9311 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1") 9312 9313 (defcustom lsp-auto-select-workspace t 9314 "Shutdown or restart a single workspace. 9315 If set and the current buffer has only a single workspace 9316 associated with it, `lsp-shutdown-workspace' and 9317 `lsp-restart-workspace' will act on it without asking." 9318 :type 'boolean 9319 :group 'lsp-mode) 9320 9321 (defun lsp--read-workspace () 9322 "Ask the user to select a workspace. 9323 Errors if there are none." 9324 (pcase (lsp-workspaces) 9325 (`nil (error "No workspaces associated with the current buffer")) 9326 ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace) 9327 (workspaces (lsp--completing-read "Select workspace: " workspaces 9328 #'lsp--workspace-print nil t)))) 9329 9330 (defun lsp-workspace-shutdown (workspace) 9331 "Shut the workspace WORKSPACE and the language server associated with it" 9332 (interactive (list (lsp--read-workspace))) 9333 (lsp--warn "Stopping %s" (lsp--workspace-print workspace)) 9334 (with-lsp-workspace workspace (lsp--shutdown-workspace))) 9335 9336 (defun lsp-disconnect () 9337 "Disconnect the buffer from the language server." 9338 (interactive) 9339 (lsp--text-document-did-close t) 9340 (lsp-managed-mode -1) 9341 (lsp-mode -1) 9342 (setq lsp--buffer-workspaces nil) 9343 (lsp--info "Disconnected")) 9344 9345 (defun lsp-restart-workspace () 9346 (interactive) 9347 (--when-let (pcase (lsp-workspaces) 9348 (`nil (user-error "There are no active servers in the current buffer")) 9349 (`(,workspace) workspace) 9350 (workspaces (lsp--completing-read "Select server: " 9351 workspaces 9352 'lsp--workspace-print nil t))) 9353 (lsp-workspace-restart it))) 9354 9355 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1") 9356 9357 (defun lsp-workspace-restart (workspace) 9358 "Restart the workspace WORKSPACE and the language server associated with it" 9359 (interactive (list (lsp--read-workspace))) 9360 (lsp--warn "Restarting %s" (lsp--workspace-print workspace)) 9361 (with-lsp-workspace workspace (lsp--shutdown-workspace t))) 9362 9363 ;;;###autoload 9364 (defun lsp (&optional arg) 9365 "Entry point for the server startup. 9366 When ARG is t the lsp mode will start new language server even if 9367 there is language server which can handle current language. When 9368 ARG is nil current file will be opened in multi folder language 9369 server if there is such. When `lsp' is called with prefix 9370 argument ask the user to select which language server to start." 9371 (interactive "P") 9372 9373 (lsp--require-packages) 9374 9375 (when (buffer-file-name) 9376 (let (clients 9377 (matching-clients (lsp--filter-clients 9378 (-andfn #'lsp--supports-buffer? 9379 #'lsp--server-binary-present?)))) 9380 (cond 9381 (matching-clients 9382 (when (setq lsp--buffer-workspaces 9383 (or (and 9384 ;; Don't open as library file if file is part of a project. 9385 (not (lsp-find-session-folder (lsp-session) (buffer-file-name))) 9386 (lsp--try-open-in-library-workspace)) 9387 (lsp--try-project-root-workspaces (equal arg '(4)) 9388 (and arg (not (equal arg 1)))))) 9389 (lsp-mode 1) 9390 (when lsp-auto-configure (lsp--auto-configure)) 9391 (setq lsp-buffer-uri (lsp--buffer-uri)) 9392 (lsp--info "Connected to %s." 9393 (apply 'concat (--map (format "[%s %s]" 9394 (lsp--workspace-print it) 9395 (lsp--workspace-root it)) 9396 lsp--buffer-workspaces))))) 9397 ;; look for servers which are currently being downloaded. 9398 ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9399 #'lsp--client-download-in-progress?))) 9400 (lsp--info "There are language server(%s) installation in progress. 9401 The server(s) will be started in the buffer when it has finished." 9402 (-map #'lsp--client-server-id clients)) 9403 (seq-do (lambda (client) 9404 (cl-pushnew (current-buffer) (lsp--client-buffers client))) 9405 clients)) 9406 ;; look for servers to install 9407 ((setq clients (lsp--filter-clients 9408 (-andfn #'lsp--supports-buffer? 9409 (-const lsp-enable-suggest-server-download) 9410 #'lsp--client-download-server-fn 9411 (-not #'lsp--client-download-in-progress?)))) 9412 (let ((client (lsp--completing-read 9413 (concat "Unable to find installed server supporting this file. " 9414 "The following servers could be installed automatically: ") 9415 clients 9416 (-compose #'symbol-name #'lsp--client-server-id) 9417 nil 9418 t))) 9419 (cl-pushnew (current-buffer) (lsp--client-buffers client)) 9420 (lsp--install-server-internal client))) 9421 ;; ignore other warnings 9422 ((not lsp-warn-no-matched-clients) 9423 nil) 9424 ;; automatic installation disabled 9425 ((setq clients (unless matching-clients 9426 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9427 #'lsp--client-download-server-fn 9428 (-not (-const lsp-enable-suggest-server-download)) 9429 (-not #'lsp--server-binary-present?))))) 9430 (lsp--warn "The following servers support current file but automatic download is disabled: %s 9431 \(If you have already installed the server check *lsp-log*)." 9432 (mapconcat (lambda (client) 9433 (symbol-name (lsp--client-server-id client))) 9434 clients 9435 " "))) 9436 ;; no clients present 9437 ((setq clients (unless matching-clients 9438 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9439 (-not #'lsp--server-binary-present?))))) 9440 (lsp--warn "The following servers support current file but do not have automatic installation: %s 9441 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages. 9442 \(If you have already installed the server check *lsp-log*)." 9443 (mapconcat (lambda (client) 9444 (symbol-name (lsp--client-server-id client))) 9445 clients 9446 " "))) 9447 ;; no matches 9448 ((-> #'lsp--supports-buffer? lsp--filter-clients not) 9449 (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'. 9450 This issue might be caused by: 9451 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'. 9452 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'. 9453 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/ . 9454 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/. 9455 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients'). 9456 You can customize `lsp-warn-no-matched-clients' to disable this message." 9457 major-mode major-mode major-mode)))))) 9458 9459 (defun lsp--buffer-visible-p () 9460 "Return non nil if current buffer is visible." 9461 (or (buffer-modified-p) (get-buffer-window nil t))) 9462 9463 (defun lsp--init-if-visible () 9464 "Run `lsp' for the current buffer if the buffer is visible. 9465 Returns non nil if `lsp' was run for the buffer." 9466 (when (lsp--buffer-visible-p) 9467 (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t) 9468 (lsp) 9469 t)) 9470 9471 ;;;###autoload 9472 (defun lsp-deferred () 9473 "Entry point that defers server startup until buffer is visible. 9474 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'. 9475 This avoids overloading the server with many files when starting Emacs." 9476 ;; Workspace may not be initialized yet. Use a buffer local variable to 9477 ;; remember that we deferred loading of this buffer. 9478 (setq lsp--buffer-deferred t) 9479 (let ((buffer (current-buffer))) 9480 ;; Avoid false positives as desktop-mode restores buffers by deferring 9481 ;; visibility check until the stack clears. 9482 (run-with-idle-timer 0 nil (lambda () 9483 (when (buffer-live-p buffer) 9484 (with-current-buffer buffer 9485 (unless (lsp--init-if-visible) 9486 (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t)))))))) 9487 9488 9489 9490 (defvar lsp-file-truename-cache (ht)) 9491 9492 (defmacro lsp-with-cached-filetrue-name (&rest body) 9493 "Executes BODY caching the `file-truename' calls." 9494 `(let ((old-fn (symbol-function 'file-truename))) 9495 (unwind-protect 9496 (progn 9497 (fset 'file-truename 9498 (lambda (file-name &optional counter prev-dirs) 9499 (or (gethash file-name lsp-file-truename-cache) 9500 (puthash file-name (apply old-fn (list file-name counter prev-dirs)) 9501 lsp-file-truename-cache)))) 9502 ,@body) 9503 (fset 'file-truename old-fn)))) 9504 9505 9506 (defun lsp-virtual-buffer-call (key &rest args) 9507 (when lsp--virtual-buffer 9508 (when-let ((fn (plist-get lsp--virtual-buffer key))) 9509 (apply fn args)))) 9510 9511 (defun lsp-translate-column (column) 9512 "Translate COLUMN taking into account virtual buffers." 9513 (or (lsp-virtual-buffer-call :real->virtual-char column) 9514 column)) 9515 9516 (defun lsp-translate-line (line) 9517 "Translate LINE taking into account virtual buffers." 9518 (or (lsp-virtual-buffer-call :real->virtual-line line) 9519 line)) 9520 9521 9522 ;; lsp internal validation. 9523 9524 (defmacro lsp--doctor (&rest checks) 9525 `(-let [buf (current-buffer)] 9526 (with-current-buffer (get-buffer-create "*lsp-performance*") 9527 (with-help-window (current-buffer) 9528 ,@(-map (-lambda ((msg form)) 9529 `(insert (format "%s: %s\n" ,msg 9530 (let ((res (with-current-buffer buf 9531 ,form))) 9532 (cond 9533 ((eq res :optional) (propertize "OPTIONAL" 'face 'warning)) 9534 (res (propertize "OK" 'face 'success)) 9535 (t (propertize "ERROR" 'face 'error))))))) 9536 (-partition 2 checks)))))) 9537 9538 (define-obsolete-function-alias 'lsp-diagnose 9539 'lsp-doctor "lsp-mode 8.0.0") 9540 9541 (defun lsp-doctor () 9542 "Validate performance settings." 9543 (interactive) 9544 (lsp--doctor 9545 "Checking for Native JSON support" (functionp 'json-serialize) 9546 "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max) 9547 "Check `read-process-output-max' default has been changed from 4k" 9548 (and (boundp 'read-process-output-max) 9549 (> read-process-output-max 4096)) 9550 "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)" 9551 (condition-case _err 9552 (progn (lsp--make-message (list "a" "b")) 9553 nil) 9554 (error t)) 9555 "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000) 9556 "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) 9557 "Using emacs 28+ with native compilation?" 9558 (or (and (fboundp 'native-comp-available-p) 9559 (native-comp-available-p)) 9560 :optional))) 9561 9562 (declare-function package-version-join "ext:package") 9563 (declare-function package-desc-version "ext:package") 9564 (declare-function package--alist "ext:package") 9565 9566 (defun lsp-version () 9567 "Return string describing current version of `lsp-mode'." 9568 (interactive) 9569 (unless (featurep 'package) 9570 (require 'package)) 9571 (let ((ver (format "lsp-mode %s, Emacs %s, %s" 9572 (package-version-join 9573 (package-desc-version 9574 (car (alist-get 'lsp-mode (package--alist))))) 9575 emacs-version 9576 system-type))) 9577 (if (called-interactively-p 'interactive) 9578 (lsp--info "%s" ver) 9579 ver))) 9580 9581 9582 9583 ;; org-mode/virtual-buffer 9584 9585 (declare-function org-babel-get-src-block-info "ext:ob-core") 9586 (declare-function org-do-remove-indentation "ext:org-macs") 9587 (declare-function org-src-get-lang-mode "ext:org-src") 9588 (declare-function org-element-context "ext:org-element") 9589 9590 (defun lsp--virtual-buffer-update-position () 9591 (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range)) 9592 (funcall in-range)) 9593 lsp--virtual-buffer-connections)) 9594 (unless (equal virtual-buffer lsp--virtual-buffer) 9595 (lsp-org)) 9596 (when lsp-managed-mode 9597 (lsp-managed-mode -1) 9598 (lsp-mode -1) 9599 (setq lsp--buffer-workspaces nil) 9600 (setq lsp--virtual-buffer nil) 9601 (setq lsp-buffer-uri nil) 9602 9603 ;; force refresh of diagnostics 9604 (run-hooks 'lsp-after-diagnostics-hook)))) 9605 9606 (defun lsp-virtual-buffer-on-change (start end length) 9607 "Adjust on change event to be executed against the proper language server." 9608 (let ((max-point (max end 9609 (or (plist-get lsp--before-change-vals :end) 0) 9610 (+ start length)))) 9611 (when-let ((virtual-buffer (-first (lambda (vb) 9612 (let ((lsp--virtual-buffer vb)) 9613 (and (lsp-virtual-buffer-call :in-range start) 9614 (lsp-virtual-buffer-call :in-range max-point)))) 9615 lsp--virtual-buffer-connections))) 9616 (lsp-with-current-buffer virtual-buffer 9617 (lsp-on-change start end length 9618 (lambda (&rest _) 9619 (list :range (lsp--range (list :character 0 :line 0) 9620 lsp--virtual-buffer-point-max) 9621 :text (lsp--buffer-content)))))))) 9622 9623 (defun lsp-virtual-buffer-before-change (start _end) 9624 (when-let ((virtual-buffer (-first (lambda (vb) 9625 (lsp-with-current-buffer vb 9626 (lsp-virtual-buffer-call :in-range start))) 9627 lsp--virtual-buffer-connections))) 9628 (lsp-with-current-buffer virtual-buffer 9629 (setq lsp--virtual-buffer-point-max 9630 (lsp--point-to-position (lsp-virtual-buffer-call :last-point)))))) 9631 9632 (defun lsp-patch-on-change-event () 9633 (remove-hook 'after-change-functions #'lsp-on-change t) 9634 (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t) 9635 (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t)) 9636 9637 (defun lsp-kill-virtual-buffers () 9638 (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections)) 9639 9640 (defun lsp--move-point-in-indentation (point indentation) 9641 (save-excursion 9642 (goto-char point) 9643 (if (<= point (+ (line-beginning-position) indentation)) 9644 (line-beginning-position) 9645 point))) 9646 9647 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck") 9648 (declare-function flycheck-add-mode "ext:flycheck") 9649 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics") 9650 9651 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn) 9652 9653 (defun lsp-flycheck-add-mode (mode) 9654 "Register flycheck support for MODE." 9655 (lsp-diagnostics-lsp-checker-if-needed) 9656 (unless (flycheck-checker-supports-major-mode-p 'lsp mode) 9657 (flycheck-add-mode 'lsp mode))) 9658 9659 (defun lsp-progress-spinner-type () 9660 "Retrieve the spinner type value, if value is not a symbol of `spinner-types 9661 defaults to `progress-bar." 9662 (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar)) 9663 9664 (defun lsp-org () 9665 (interactive) 9666 (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range)) 9667 (funcall in-range)) 9668 lsp--virtual-buffer-connections)) 9669 (unless (equal lsp--virtual-buffer virtual-buffer) 9670 (setq lsp--buffer-workspaces workspaces) 9671 (setq lsp--virtual-buffer virtual-buffer) 9672 (setq lsp-buffer-uri nil) 9673 (lsp-mode 1) 9674 (lsp-managed-mode 1) 9675 (lsp-patch-on-change-event)) 9676 9677 (save-excursion 9678 (-let* (virtual-buffer 9679 (wcb (lambda (f) 9680 (with-current-buffer (plist-get virtual-buffer :buffer) 9681 (-let* (((&plist :major-mode :buffer-file-name 9682 :goto-buffer :workspaces) virtual-buffer) 9683 (lsp--virtual-buffer virtual-buffer) 9684 (lsp--buffer-workspaces workspaces)) 9685 (save-excursion 9686 (funcall goto-buffer) 9687 (funcall f)))))) 9688 ((&plist :begin :end :post-blank :language) (cl-second (org-element-context))) 9689 ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light))) 9690 9691 (file-name (if file-name 9692 (f-expand file-name) 9693 (user-error "You should specify file name in the src block header."))) 9694 (begin-marker (progn 9695 (goto-char begin) 9696 (forward-line) 9697 (set-marker (make-marker) (point)))) 9698 (end-marker (progn 9699 (goto-char end) 9700 (forward-line (1- (- post-blank))) 9701 (set-marker (make-marker) (1+ (point))))) 9702 (buf (current-buffer)) 9703 (src-block (buffer-substring-no-properties begin-marker 9704 (1- end-marker))) 9705 (indentation (with-temp-buffer 9706 (insert src-block) 9707 9708 (goto-char (point-min)) 9709 (let ((indentation (current-indentation))) 9710 (plist-put lsp--virtual-buffer :indentation indentation) 9711 (org-do-remove-indentation) 9712 (goto-char (point-min)) 9713 (- indentation (current-indentation)))))) 9714 (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t) 9715 9716 (when (fboundp 'flycheck-add-mode) 9717 (lsp-flycheck-add-mode 'org-mode)) 9718 9719 (setq lsp--virtual-buffer 9720 (list 9721 :in-range (lambda (&optional point) 9722 (<= begin-marker (or point (point)) (1- end-marker))) 9723 :goto-buffer (lambda () (goto-char begin-marker)) 9724 :buffer-string 9725 (lambda () 9726 (let ((src-block (buffer-substring-no-properties 9727 begin-marker 9728 (1- end-marker)))) 9729 (with-temp-buffer 9730 (insert src-block) 9731 9732 (goto-char (point-min)) 9733 (while (not (eobp)) 9734 (delete-region (point) (if (> (+ (point) indentation) (line-end-position)) 9735 (line-end-position) 9736 (+ (point) indentation))) 9737 (forward-line)) 9738 (buffer-substring-no-properties (point-min) 9739 (point-max))))) 9740 :buffer buf 9741 :begin begin-marker 9742 :end end-marker 9743 :indentation indentation 9744 :last-point (lambda () (1- end-marker)) 9745 :cur-position (lambda () 9746 (lsp-save-restriction-and-excursion 9747 (list :line (- (lsp--cur-line) 9748 (lsp--cur-line begin-marker)) 9749 :character (let ((character (- (point) 9750 (line-beginning-position) 9751 indentation))) 9752 (if (< character 0) 9753 0 9754 character))))) 9755 :line/character->point (-lambda (line character) 9756 (-let [inhibit-field-text-motion t] 9757 (+ indentation 9758 (lsp-save-restriction-and-excursion 9759 (goto-char begin-marker) 9760 (forward-line line) 9761 (-let [line-end (line-end-position)] 9762 (if (> character (- line-end (point))) 9763 line-end 9764 (forward-char character) 9765 (point))))))) 9766 :major-mode (org-src-get-lang-mode language) 9767 :buffer-file-name file-name 9768 :buffer-uri (lsp--path-to-uri file-name) 9769 :with-current-buffer wcb 9770 :buffer-live? (lambda (_) (buffer-live-p buf)) 9771 :buffer-name (lambda (_) 9772 (propertize (format "%s(%s:%s)%s" 9773 (buffer-name buf) 9774 begin-marker 9775 end-marker 9776 language) 9777 'face 'italic)) 9778 :real->virtual-line (lambda (line) 9779 (+ line (line-number-at-pos begin-marker) -1)) 9780 :real->virtual-char (lambda (char) (+ char indentation)) 9781 :cleanup (lambda () 9782 (set-marker begin-marker nil) 9783 (set-marker end-marker nil)))) 9784 (setf virtual-buffer lsp--virtual-buffer) 9785 (puthash file-name virtual-buffer lsp--virtual-buffer-mappings) 9786 (push virtual-buffer lsp--virtual-buffer-connections) 9787 9788 ;; TODO: tangle only connected sections 9789 (add-hook 'after-save-hook 'org-babel-tangle nil t) 9790 (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t) 9791 (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t) 9792 9793 (setq lsp--buffer-workspaces 9794 (lsp-with-current-buffer virtual-buffer 9795 (lsp) 9796 (plist-put virtual-buffer :workspaces (lsp-workspaces)) 9797 (lsp-workspaces))))))) 9798 9799 (defun lsp-virtual-buffer-disconnect (virtual-buffer) 9800 (interactive (list (or 9801 lsp--virtual-buffer 9802 (when lsp--virtual-buffer-connections 9803 (lsp--completing-read "Select virtual buffer to disconnect: " 9804 lsp--virtual-buffer-connections 9805 (-lambda ((&plist :buffer-file-name)) 9806 buffer-file-name)))))) 9807 (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer) 9808 (progn 9809 (lsp-with-current-buffer virtual-buffer 9810 (lsp--text-document-did-close)) 9811 (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections)) 9812 (when (eq virtual-buffer lsp--virtual-buffer) 9813 (setf lsp--virtual-buffer nil)) 9814 (when cleanup (funcall cleanup)) 9815 (remhash file-name lsp--virtual-buffer-mappings) 9816 9817 (lsp--virtual-buffer-update-position) 9818 (lsp--info "Disconnected from buffer %s" file-name)) 9819 (lsp--error "Nothing to disconnect from?"))) 9820 9821 9822 ;; inlay hints 9823 9824 (defface lsp-inlay-hint-face 9825 '((t :inherit font-lock-comment-face)) 9826 "The face to use for the JavaScript inlays." 9827 :group 'lsp-mode 9828 :package-version '(lsp-mode . "9.0.0")) 9829 9830 (defface lsp-inlay-hint-type-face 9831 '((t :inherit lsp-inlay-hint-face)) 9832 "Face for inlay type hints (e.g. inferred variable types)." 9833 :group 'lsp-mode 9834 :package-version '(lsp-mode . "9.0.0")) 9835 9836 (defcustom lsp-inlay-hint-type-format "%s" 9837 "Format string for variable inlays (part of the inlay face)." 9838 :type '(string :tag "String") 9839 :group 'lsp-mode 9840 :package-version '(lsp-mode . "9.0.0")) 9841 9842 (defface lsp-inlay-hint-parameter-face 9843 '((t :inherit lsp-inlay-hint-face)) 9844 "Face for inlay parameter hints (e.g. function parameter names at 9845 call-site)." 9846 :group 'lsp-mode 9847 :package-version '(lsp-mode . "9.0.0")) 9848 9849 (defcustom lsp-inlay-hint-param-format "%s" 9850 "Format string for parameter inlays (part of the inlay face)." 9851 :type '(string :tag "String") 9852 :group 'lsp-mode 9853 :package-version '(lsp-mode . "9.0.0")) 9854 9855 (defcustom lsp-update-inlay-hints-on-scroll t 9856 "If non-nil update inlay hints immediately when scrolling or 9857 modifying window sizes." 9858 :type 'boolean 9859 :package-version '(lsp-mode . "9.0.0")) 9860 9861 (defun lsp--format-inlay (text kind) 9862 (cond 9863 ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text)) 9864 ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text)) 9865 (t text))) 9866 9867 (defun lsp--face-for-inlay (kind) 9868 (cond 9869 ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face) 9870 ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face) 9871 (t 'lsp-inlay-hint-face))) 9872 9873 (defun lsp--update-inlay-hints-scroll-function (window start) 9874 (lsp-update-inlay-hints start (window-end window t))) 9875 9876 (defun lsp--update-inlay-hints () 9877 (lsp-update-inlay-hints (window-start) (window-end nil t))) 9878 9879 (defun lsp--label-from-inlay-hints-response (label) 9880 "Returns a string label built from an array of 9881 InlayHintLabelParts or the argument itself if it's already a 9882 string." 9883 (cl-typecase label 9884 (string label) 9885 (vector 9886 (string-join (mapcar (lambda (part) 9887 (-let (((&InlayHintLabelPart :value) part)) 9888 value)) 9889 label))))) 9890 9891 (defun lsp-update-inlay-hints (start end) 9892 (lsp-request-async 9893 "textDocument/inlayHint" 9894 (lsp-make-inlay-hints-params 9895 :text-document (lsp--text-document-identifier) 9896 :range (lsp-make-range :start 9897 (lsp-point-to-position start) 9898 :end 9899 (lsp-point-to-position end))) 9900 (lambda (res) 9901 (lsp--remove-overlays 'lsp-inlay-hint) 9902 (dolist (hint res) 9903 (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint) 9904 (kind (or kind? lsp/inlay-hint-kind-type-hint)) 9905 (label (lsp--label-from-inlay-hints-response label)) 9906 (pos (lsp--position-to-point position)) 9907 (overlay (make-overlay pos pos nil 'front-advance 'end-advance))) 9908 (when (stringp label) 9909 (overlay-put overlay 'lsp-inlay-hint t) 9910 (overlay-put overlay 'before-string 9911 (format "%s%s%s" 9912 (if padding-left? " " "") 9913 (propertize (lsp--format-inlay label kind) 9914 'font-lock-face (lsp--face-for-inlay kind)) 9915 (if padding-right? " " ""))))))) 9916 :mode 'tick)) 9917 9918 (define-minor-mode lsp-inlay-hints-mode 9919 "Mode for displaying inlay hints." 9920 :lighter nil 9921 (cond 9922 ((and lsp-inlay-hints-mode lsp--buffer-workspaces) 9923 (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t) 9924 (when lsp-update-inlay-hints-on-scroll 9925 (add-to-list (make-local-variable 'window-scroll-functions) 9926 #'lsp--update-inlay-hints-scroll-function))) 9927 (t 9928 (lsp--remove-overlays 'lsp-inlay-hint) 9929 (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t) 9930 (setf window-scroll-functions 9931 (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions))))) 9932 9933 9934 9935 ;;;###autoload 9936 (defun lsp-start-plain () 9937 "Start `lsp-mode' using minimal configuration using the latest `melpa' version 9938 of the packages. 9939 9940 In case the major-mode that you are using for " 9941 (interactive) 9942 (let ((start-plain (make-temp-file "plain" nil ".el"))) 9943 (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el" 9944 start-plain t) 9945 (start-process "lsp-start-plain" 9946 (generate-new-buffer " *lsp-start-plain*") 9947 (expand-file-name invocation-name invocation-directory) 9948 "-q" "-l" start-plain (or (buffer-file-name) "")))) 9949 9950 9951 9952 (provide 'lsp-mode) 9953 ;;; lsp-mode.el ends here