lsp-mode.el (438020B)
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: 20241202.334 9 ;; Package-Revision: c3be413f8545 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 textDocument/hover. 529 If this is set to nil, `eldoc' will show only the symbol information." 530 :type 'boolean 531 :group 'lsp-mode) 532 533 (defcustom lsp-enable-symbol-highlighting t 534 "Highlight references of the symbol at point." 535 :type 'boolean 536 :group 'lsp-mode) 537 538 (defcustom lsp-enable-xref t 539 "Enable xref integration." 540 :type 'boolean 541 :group 'lsp-mode) 542 543 (define-obsolete-variable-alias 544 'lsp-references-exclude-definition 545 'lsp-references-exclude-declaration 546 "9.0.1") 547 548 (defcustom lsp-references-exclude-declaration nil 549 "If non-nil, exclude declarations when finding references." 550 :type 'boolean 551 :group 'lsp-mode) 552 553 (defcustom lsp-enable-indentation t 554 "Indent regions using the file formatting functionality provided by the 555 language server." 556 :type 'boolean 557 :group 'lsp-mode) 558 559 (defcustom lsp-enable-on-type-formatting t 560 "Enable `textDocument/onTypeFormatting' integration." 561 :type 'boolean 562 :group 'lsp-mode) 563 564 (defcustom lsp-enable-text-document-color t 565 "Enable `textDocument/documentColor' integration." 566 :type 'boolean 567 :group 'lsp-mode) 568 569 (defcustom lsp-before-save-edits t 570 "If non-nil, `lsp-mode' will apply edits suggested by the language server 571 before saving a document." 572 :type 'boolean 573 :group 'lsp-mode) 574 575 (defcustom lsp-after-apply-edits-hook nil 576 "Hooks to run when text edit is applied. 577 It contains the operation source." 578 :type 'hook 579 :group 'lsp-mode 580 :package-version '(lsp-mode . "8.0.0")) 581 582 (defcustom lsp-apply-edits-after-file-operations t 583 "Whether to apply edits returned by server after file operations if any. 584 Applicable only if server supports workspace.fileOperations for operations: 585 `workspace/willRenameFiles', `workspace/willCreateFiles' and 586 `workspace/willDeleteFiles'." 587 :group 'lsp-mode 588 :type 'boolean) 589 590 (defcustom lsp-modeline-code-actions-enable t 591 "Whether to show code actions on modeline." 592 :type 'boolean 593 :group 'lsp-modeline) 594 595 (defcustom lsp-modeline-diagnostics-enable t 596 "Whether to show diagnostics on modeline." 597 :type 'boolean 598 :group 'lsp-modeline) 599 600 (defcustom lsp-modeline-workspace-status-enable t 601 "Whether to show workspace status on modeline." 602 :type 'boolean 603 :group 'lsp-modeline 604 :package-version '(lsp-mode . "8.0.0")) 605 606 (defcustom lsp-headerline-breadcrumb-enable t 607 "Whether to enable breadcrumb on headerline." 608 :type 'boolean 609 :group 'lsp-headerline) 610 611 (defcustom lsp-configure-hook nil 612 "Hooks to run when `lsp-configure-buffer' is called." 613 :type 'hook 614 :group 'lsp-mode) 615 616 (defcustom lsp-unconfigure-hook nil 617 "Hooks to run when `lsp-unconfig-buffer' is called." 618 :type 'hook 619 :group 'lsp-mode) 620 621 (defcustom lsp-after-diagnostics-hook nil 622 "Hooks to run after diagnostics are received. 623 Note: it runs only if the receiving buffer is open. Use 624 `lsp-diagnostics-updated-hook'if you want to be notified when 625 diagnostics have changed." 626 :type 'hook 627 :group 'lsp-mode) 628 629 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook 630 'lsp-diagnostics-updated-hook "lsp-mode 6.4") 631 632 (defcustom lsp-diagnostics-updated-hook nil 633 "Hooks to run after diagnostics are received." 634 :type 'hook 635 :group 'lsp-mode) 636 637 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook 638 'lsp-workspace-folders-changed-functions "lsp-mode 6.3") 639 640 (defcustom lsp-workspace-folders-changed-functions nil 641 "Hooks to run after the folders has changed. 642 The hook will receive two parameters list of added and removed folders." 643 :type 'hook 644 :group 'lsp-mode) 645 646 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0") 647 648 (defcustom lsp-before-apply-edits-hook nil 649 "Hooks to run before applying edits." 650 :type 'hook 651 :group 'lsp-mode) 652 653 (defgroup lsp-imenu nil 654 "LSP Imenu." 655 :group 'lsp-mode 656 :tag "LSP Imenu") 657 658 (defcustom lsp-imenu-show-container-name t 659 "Display the symbol's container name in an imenu entry." 660 :type 'boolean 661 :group 'lsp-imenu) 662 663 (defcustom lsp-imenu-container-name-separator "/" 664 "Separator string to use to separate the container name from the symbol while 665 displaying imenu entries." 666 :type 'string 667 :group 'lsp-imenu) 668 669 (defcustom lsp-imenu-sort-methods '(kind name) 670 "How to sort the imenu items. 671 672 The value is a list of `kind' `name' or `position'. Priorities 673 are determined by the index of the element." 674 :type '(repeat (choice (const name) 675 (const position) 676 (const kind))) 677 :group 'lsp-imenu) 678 679 (defcustom lsp-imenu-index-symbol-kinds nil 680 "Which symbol kinds to show in imenu." 681 :type '(repeat (choice (const :tag "Miscellaneous" nil) 682 (const :tag "File" File) 683 (const :tag "Module" Module) 684 (const :tag "Namespace" Namespace) 685 (const :tag "Package" Package) 686 (const :tag "Class" Class) 687 (const :tag "Method" Method) 688 (const :tag "Property" Property) 689 (const :tag "Field" Field) 690 (const :tag "Constructor" Constructor) 691 (const :tag "Enum" Enum) 692 (const :tag "Interface" Interface) 693 (const :tag "Function" Function) 694 (const :tag "Variable" Variable) 695 (const :tag "Constant" Constant) 696 (const :tag "String" String) 697 (const :tag "Number" Number) 698 (const :tag "Boolean" Boolean) 699 (const :tag "Array" Array) 700 (const :tag "Object" Object) 701 (const :tag "Key" Key) 702 (const :tag "Null" Null) 703 (const :tag "Enum Member" EnumMember) 704 (const :tag "Struct" Struct) 705 (const :tag "Event" Event) 706 (const :tag "Operator" Operator) 707 (const :tag "Type Parameter" TypeParameter))) 708 :group 'lsp-imenu) 709 710 ;; vibhavp: Should we use a lower value (5)? 711 (defcustom lsp-response-timeout 10 712 "Number of seconds to wait for a response from the language server before 713 timing out. Nil if no timeout." 714 :type '(choice 715 (number :tag "Seconds") 716 (const :tag "No timeout" nil)) 717 :group 'lsp-mode) 718 719 (defcustom lsp-tcp-connection-timeout 2 720 "The timeout for tcp connection in seconds." 721 :type 'number 722 :group 'lsp-mode 723 :package-version '(lsp-mode . "6.2")) 724 725 (defconst lsp--imenu-compare-function-alist 726 (list (cons 'name #'lsp--imenu-compare-name) 727 (cons 'kind #'lsp--imenu-compare-kind) 728 (cons 'position #'lsp--imenu-compare-line-col)) 729 "An alist of (METHOD . FUNCTION). 730 METHOD is one of the symbols accepted by 731 `lsp-imenu-sort-methods'. 732 733 FUNCTION takes two hash tables representing DocumentSymbol. It 734 returns a negative number, 0, or a positive number indicating 735 whether the first parameter is less than, equal to, or greater 736 than the second parameter.") 737 738 (defcustom lsp-diagnostic-clean-after-change nil 739 "When non-nil, clean the diagnostics on change. 740 741 Note that when that setting is nil, `lsp-mode' will show stale 742 diagnostics until server publishes the new set of diagnostics" 743 :type 'boolean 744 :group 'lsp-diagnostics 745 :package-version '(lsp-mode . "7.0.1")) 746 747 (defcustom lsp-server-trace nil 748 "Request tracing on the server side. 749 The actual trace output at each level depends on the language server in use. 750 Changes take effect only when a new session is started." 751 :type '(choice (const :tag "Disabled" "off") 752 (const :tag "Messages only" "messages") 753 (const :tag "Verbose" "verbose") 754 (const :tag "Default (disabled)" nil)) 755 :group 'lsp-mode 756 :package-version '(lsp-mode . "6.1")) 757 758 (defcustom lsp-auto-touch-files t 759 "If non-nil ensure the files exist before sending 760 `textDocument/didOpen' notification." 761 :type 'boolean 762 :group 'lsp-mode 763 :package-version '(lsp-mode . "9.0.0")) 764 765 (defvar lsp-language-id-configuration 766 '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake") 767 ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile") 768 ("\\.astro$" . "astro") 769 ("\\.cs\\'" . "csharp") 770 ("\\.css$" . "css") 771 ("\\.cypher$" . "cypher") 772 ("Earthfile" . "earthfile") 773 ("\\.ebuild$" . "shellscript") 774 ("\\.go\\'" . "go") 775 ("\\.html$" . "html") 776 ("\\.hx$" . "haxe") 777 ("\\.hy$" . "hy") 778 ("\\.java\\'" . "java") 779 ("\\.jq$" . "jq") 780 ("\\.js$" . "javascript") 781 ("\\.json$" . "json") 782 ("\\.jsonc$" . "jsonc") 783 ("\\.jsonnet$" . "jsonnet") 784 ("\\.jsx$" . "javascriptreact") 785 ("\\.lua$" . "lua") 786 ("\\.fnl$" . "fennel") 787 ("\\.mdx\\'" . "mdx") 788 ("\\.nu$" . "nushell") 789 ("\\.php$" . "php") 790 ("\\.ps[dm]?1\\'" . "powershell") 791 ("\\.rs\\'" . "rust") 792 ("\\.spec\\'" . "rpm-spec") 793 ("\\.sql$" . "sql") 794 ("\\.svelte$" . "svelte") 795 ("\\.toml\\'" . "toml") 796 ("\\.ts$" . "typescript") 797 ("\\.tsp$" . "typespec") 798 ("\\.tsx$" . "typescriptreact") 799 ("\\.ttcn3$" . "ttcn3") 800 ("\\.vue$" . "vue") 801 ("\\.xml$" . "xml") 802 ("\\ya?ml$" . "yaml") 803 ("^PKGBUILD$" . "shellscript") 804 ("^go\\.mod\\'" . "go.mod") 805 ("^settings\\.json$" . "jsonc") 806 ("^yang\\.settings$" . "jsonc") 807 ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson") 808 (ada-mode . "ada") 809 (ada-ts-mode . "ada") 810 (gpr-mode . "gpr") 811 (gpr-ts-mode . "gpr") 812 (awk-mode . "awk") 813 (awk-ts-mode . "awk") 814 (nxml-mode . "xml") 815 (sql-mode . "sql") 816 (vimrc-mode . "vim") 817 (vimscript-ts-mode . "vim") 818 (sh-mode . "shellscript") 819 (bash-ts-mode . "shellscript") 820 (ebuild-mode . "shellscript") 821 (pkgbuild-mode . "shellscript") 822 (envrc-file-mode . "shellscript") 823 (scala-mode . "scala") 824 (scala-ts-mode . "scala") 825 (julia-mode . "julia") 826 (julia-ts-mode . "julia") 827 (clojure-mode . "clojure") 828 (clojurec-mode . "clojure") 829 (clojurescript-mode . "clojurescript") 830 (clojure-ts-mode . "clojure") 831 (clojure-ts-clojurec-mode . "clojure") 832 (clojure-ts-clojurescript-mode . "clojurescript") 833 (java-mode . "java") 834 (java-ts-mode . "java") 835 (jdee-mode . "java") 836 (groovy-mode . "groovy") 837 (nextflow-mode . "nextflow") 838 (python-mode . "python") 839 (python-ts-mode . "python") 840 (cython-mode . "python") 841 ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo") 842 (lsp--render-markdown . "markdown") 843 (move-mode . "move") 844 (rust-mode . "rust") 845 (rust-ts-mode . "rust") 846 (rustic-mode . "rust") 847 (kotlin-mode . "kotlin") 848 (kotlin-ts-mode . "kotlin") 849 (css-mode . "css") 850 (css-ts-mode . "css") 851 (less-mode . "less") 852 (less-css-mode . "less") 853 (lua-mode . "lua") 854 (lua-ts-mode . "lua") 855 (sass-mode . "sass") 856 (ssass-mode . "sass") 857 (scss-mode . "scss") 858 (scad-mode . "openscad") 859 (xml-mode . "xml") 860 (c-mode . "c") 861 (c-ts-mode . "c") 862 (c++-mode . "cpp") 863 (c++-ts-mode . "cpp") 864 (cuda-mode . "cuda") 865 (objc-mode . "objective-c") 866 (html-mode . "html") 867 (html-ts-mode . "html") 868 (sgml-mode . "html") 869 (mhtml-mode . "html") 870 (mint-mode . "mint") 871 (go-dot-mod-mode . "go.mod") 872 (go-mod-ts-mode . "go.mod") 873 (go-mode . "go") 874 (go-ts-mode . "go") 875 (graphql-mode . "graphql") 876 (haskell-mode . "haskell") 877 (haskell-ts-mode . "haskell") 878 (hack-mode . "hack") 879 (php-mode . "php") 880 (php-ts-mode . "php") 881 (powershell-mode . "powershell") 882 (powershell-mode . "PowerShell") 883 (powershell-ts-mode . "powershell") 884 (json-mode . "json") 885 (json-ts-mode . "json") 886 (jsonc-mode . "jsonc") 887 (rjsx-mode . "javascript") 888 (js2-mode . "javascript") 889 (js-mode . "javascript") 890 (js-ts-mode . "javascript") 891 (typescript-mode . "typescript") 892 (typescript-ts-mode . "typescript") 893 (typespec-mode . "typespec") 894 (tsx-ts-mode . "typescriptreact") 895 (svelte-mode . "svelte") 896 (fsharp-mode . "fsharp") 897 (reason-mode . "reason") 898 (caml-mode . "ocaml") 899 (tuareg-mode . "ocaml") 900 (futhark-mode . "futhark") 901 (swift-mode . "swift") 902 (elixir-mode . "elixir") 903 (elixir-ts-mode . "elixir") 904 (heex-ts-mode . "elixir") 905 (conf-javaprop-mode . "spring-boot-properties") 906 (yaml-mode . "yaml") 907 (yaml-ts-mode . "yaml") 908 (ruby-mode . "ruby") 909 (enh-ruby-mode . "ruby") 910 (ruby-ts-mode . "ruby") 911 (feature-mode . "cucumber") 912 (fortran-mode . "fortran") 913 (f90-mode . "fortran") 914 (elm-mode . "elm") 915 (dart-mode . "dart") 916 (erlang-mode . "erlang") 917 (dockerfile-mode . "dockerfile") 918 (dockerfile-ts-mode . "dockerfile") 919 (csharp-mode . "csharp") 920 (csharp-tree-sitter-mode . "csharp") 921 (csharp-ts-mode . "csharp") 922 (plain-tex-mode . "plaintex") 923 (context-mode . "context") 924 (cypher-mode . "cypher") 925 (latex-mode . "latex") 926 (LaTeX-mode . "latex") 927 (v-mode . "v") 928 (vhdl-mode . "vhdl") 929 (vhdl-ts-mode . "vhdl") 930 (verilog-mode . "verilog") 931 (terraform-mode . "terraform") 932 (ess-julia-mode . "julia") 933 (ess-r-mode . "r") 934 (crystal-mode . "crystal") 935 (nim-mode . "nim") 936 (dhall-mode . "dhall") 937 (cmake-mode . "cmake") 938 (cmake-ts-mode . "cmake") 939 (purescript-mode . "purescript") 940 (gdscript-mode . "gdscript") 941 (gdscript-ts-mode . "gdscript") 942 (perl-mode . "perl") 943 (cperl-mode . "perl") 944 (robot-mode . "robot") 945 (racket-mode . "racket") 946 (nix-mode . "nix") 947 (nix-ts-mode . "nix") 948 (prolog-mode . "prolog") 949 (vala-mode . "vala") 950 (actionscript-mode . "actionscript") 951 (d-mode . "d") 952 (zig-mode . "zig") 953 (zig-ts-mode . "zig") 954 (text-mode . "plaintext") 955 (markdown-mode . "markdown") 956 (gfm-mode . "markdown") 957 (beancount-mode . "beancount") 958 (conf-toml-mode . "toml") 959 (toml-ts-mode . "toml") 960 (org-mode . "org") 961 (org-journal-mode . "org") 962 (nginx-mode . "nginx") 963 (magik-mode . "magik") 964 (magik-ts-mode . "magik") 965 (idris-mode . "idris") 966 (idris2-mode . "idris2") 967 (gleam-mode . "gleam") 968 (gleam-ts-mode . "gleam") 969 (graphviz-dot-mode . "dot") 970 (tiltfile-mode . "tiltfile") 971 (solidity-mode . "solidity") 972 (bibtex-mode . "bibtex") 973 (rst-mode . "restructuredtext") 974 (glsl-mode . "glsl") 975 (shader-mode . "shaderlab") 976 (wgsl-mode . "wgsl") 977 (jq-mode . "jq") 978 (jq-ts-mode . "jq") 979 (protobuf-mode . "protobuf") 980 (nushell-mode . "nushell") 981 (nushell-ts-mode . "nushell") 982 (meson-mode . "meson") 983 (yang-mode . "yang")) 984 "Language id configuration.") 985 986 (defvar lsp--last-active-workspaces nil 987 "Keep track of last active workspace. 988 We want to try the last workspace first when jumping into a library 989 directory") 990 991 (defvar lsp-method-requirements 992 '(("textDocument/callHierarchy" :capability :callHierarchyProvider) 993 ("textDocument/codeAction" :capability :codeActionProvider) 994 ("codeAction/resolve" 995 :check-command (lambda (workspace) 996 (with-lsp-workspace workspace 997 (lsp:code-action-options-resolve-provider? 998 (lsp--capability-for-method "textDocument/codeAction"))))) 999 ("textDocument/codeLens" :capability :codeLensProvider) 1000 ("textDocument/completion" :capability :completionProvider) 1001 ("completionItem/resolve" 1002 :check-command (lambda (wk) 1003 (with-lsp-workspace wk 1004 (lsp:completion-options-resolve-provider? 1005 (lsp--capability-for-method "textDocument/completion"))))) 1006 ("textDocument/inlineCompletion" :capability :inlineCompletionProvider) 1007 ("textDocument/declaration" :capability :declarationProvider) 1008 ("textDocument/definition" :capability :definitionProvider) 1009 ("textDocument/documentColor" :capability :colorProvider) 1010 ("textDocument/documentLink" :capability :documentLinkProvider) 1011 ("textDocument/inlayHint" :capability :inlayHintProvider) 1012 ("textDocument/documentHighlight" :capability :documentHighlightProvider) 1013 ("textDocument/documentSymbol" :capability :documentSymbolProvider) 1014 ("textDocument/foldingRange" :capability :foldingRangeProvider) 1015 ("textDocument/formatting" :capability :documentFormattingProvider) 1016 ("textDocument/hover" :capability :hoverProvider) 1017 ("textDocument/implementation" :capability :implementationProvider) 1018 ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider) 1019 ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider) 1020 ("textDocument/prepareRename" 1021 :check-command (lambda (workspace) 1022 (with-lsp-workspace workspace 1023 (lsp:rename-options-prepare-provider? 1024 (lsp--capability-for-method "textDocument/rename"))))) 1025 ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider) 1026 ("textDocument/references" :capability :referencesProvider) 1027 ("textDocument/rename" :capability :renameProvider) 1028 ("textDocument/selectionRange" :capability :selectionRangeProvider) 1029 ("textDocument/semanticTokens" :capability :semanticTokensProvider) 1030 ("textDocument/semanticTokensFull" 1031 :check-command (lambda (workspace) 1032 (with-lsp-workspace workspace 1033 (lsp-get (lsp--capability :semanticTokensProvider) :full)))) 1034 ("textDocument/semanticTokensFull/Delta" 1035 :check-command (lambda (workspace) 1036 (with-lsp-workspace workspace 1037 (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full))) 1038 (and (not (booleanp capFull)) (lsp-get capFull :delta)))))) 1039 ("textDocument/semanticTokensRangeProvider" 1040 :check-command (lambda (workspace) 1041 (with-lsp-workspace workspace 1042 (lsp-get (lsp--capability :semanticTokensProvider) :range)))) 1043 ("textDocument/signatureHelp" :capability :signatureHelpProvider) 1044 ("textDocument/typeDefinition" :capability :typeDefinitionProvider) 1045 ("textDocument/typeHierarchy" :capability :typeHierarchyProvider) 1046 ("textDocument/diagnostic" :capability :diagnosticProvider) 1047 ("workspace/executeCommand" :capability :executeCommandProvider) 1048 ("workspace/symbol" :capability :workspaceSymbolProvider)) 1049 1050 "Map methods to requirements. 1051 It is used by request-sending functions to determine which server 1052 must be used for handling a particular message.") 1053 1054 (defconst lsp--file-change-type 1055 `((created . 1) 1056 (changed . 2) 1057 (deleted . 3))) 1058 1059 (defconst lsp--watch-kind 1060 `((create . 1) 1061 (change . 2) 1062 (delete . 4))) 1063 1064 (defvar lsp-window-body-width 40 1065 "Window body width when rendering doc.") 1066 1067 (defface lsp-face-highlight-textual 1068 '((t :inherit highlight)) 1069 "Face used for textual occurrences of symbols." 1070 :group 'lsp-mode) 1071 1072 (defface lsp-face-highlight-read 1073 '((t :inherit highlight :underline t)) 1074 "Face used for highlighting symbols being read." 1075 :group 'lsp-mode) 1076 1077 (defface lsp-face-highlight-write 1078 '((t :inherit highlight :weight bold)) 1079 "Face used for highlighting symbols being written to." 1080 :group 'lsp-mode) 1081 1082 (define-obsolete-variable-alias 'lsp-lens-auto-enable 1083 'lsp-lens-enable "lsp-mode 7.0.1") 1084 1085 (defcustom lsp-lens-enable t 1086 "Auto enable lenses if server supports." 1087 :group 'lsp-lens 1088 :type 'boolean 1089 :package-version '(lsp-mode . "6.3")) 1090 1091 (defcustom lsp-symbol-highlighting-skip-current nil 1092 "If non-nil skip current symbol when setting symbol highlights." 1093 :group 'lsp-mode 1094 :type 'boolean) 1095 1096 (defcustom lsp-file-watch-threshold 1000 1097 "Show warning if the files to watch are more than. 1098 Set to nil to disable the warning." 1099 :type 'number 1100 :group 'lsp-mode) 1101 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i)))) 1102 1103 (defvar lsp-custom-markup-modes 1104 '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic")) 1105 "Mode to uses with markdown code blocks. 1106 They are added to `markdown-code-lang-modes'") 1107 1108 (defcustom lsp-signature-render-documentation t 1109 "Display signature documentation in `eldoc'." 1110 :type 'boolean 1111 :group 'lsp-mode 1112 :package-version '(lsp-mode . "6.2")) 1113 1114 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request) 1115 "Auto activate signature conditions." 1116 :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char) 1117 (const :tag "After selected completion." :after-completion) 1118 (const :tag "When the server has sent show signature help." :on-server-request))) 1119 :group 'lsp-mode 1120 :package-version '(lsp-mode . "6.2")) 1121 1122 (defcustom lsp-signature-doc-lines 20 1123 "If number, limit the number of lines to show in the docs." 1124 :type 'number 1125 :group 'lsp-mode 1126 :package-version '(lsp-mode . "6.3")) 1127 1128 (defcustom lsp-signature-function 'lsp-lv-message 1129 "The function used for displaying signature info. 1130 It will be called with one param - the signature info. When 1131 called with nil the signature info must be cleared." 1132 :type 'function 1133 :group 'lsp-mode 1134 :package-version '(lsp-mode . "6.3")) 1135 1136 (defcustom lsp-keymap-prefix "s-l" 1137 "LSP-mode keymap prefix." 1138 :group 'lsp-mode 1139 :type 'string 1140 :package-version '(lsp-mode . "6.3")) 1141 1142 (defvar-local lsp--buffer-workspaces () 1143 "List of the buffer workspaces.") 1144 1145 (defvar-local lsp--buffer-deferred nil 1146 "Whether buffer was loaded via `lsp-deferred'.") 1147 1148 (defvar lsp--session nil 1149 "Contain the `lsp-session' for the current Emacs instance.") 1150 1151 (defvar lsp--tcp-port 10000) 1152 1153 (defvar lsp--client-packages-required nil 1154 "If nil, `lsp-client-packages' are yet to be required.") 1155 1156 (defvar lsp--tcp-server-port 0 1157 "The server socket which is opened when using `lsp-tcp-server' (a server 1158 socket is opened in Emacs and the language server connects to it). The 1159 default value of 0 ensures that a random high port is used. Set it to a positive 1160 integer to use a specific port.") 1161 1162 (defvar lsp--tcp-server-wait-seconds 10 1163 "Wait this amount of time for the client to connect to our server socket 1164 when using `lsp-tcp-server'.") 1165 1166 (defvar-local lsp--document-symbols nil 1167 "The latest document symbols.") 1168 1169 (defvar-local lsp--document-selection-range-cache nil 1170 "The document selection cache.") 1171 1172 (defvar-local lsp--document-symbols-request-async nil 1173 "If non-nil, request document symbols asynchronously.") 1174 1175 (defvar-local lsp--document-symbols-tick -1 1176 "The value of `buffer-chars-modified-tick' when document 1177 symbols were last retrieved.") 1178 1179 (defvar-local lsp--have-document-highlights nil 1180 "Set to `t' on symbol highlighting, cleared on 1181 `lsp--cleanup-highlights-if-needed'. Checking a separately 1182 defined flag is substantially faster than unconditionally 1183 calling `remove-overlays'.") 1184 1185 ;; Buffer local variable for storing number of lines. 1186 (defvar lsp--log-lines) 1187 1188 (defvar-local lsp--eldoc-saved-message nil) 1189 1190 (defvar lsp--on-change-timer nil) 1191 (defvar lsp--on-idle-timer nil) 1192 1193 (defvar-local lsp--signature-last nil) 1194 (defvar-local lsp--signature-last-index nil) 1195 (defvar lsp--signature-last-buffer nil) 1196 1197 (defvar-local lsp--virtual-buffer-point-max nil) 1198 1199 (cl-defmethod lsp-execute-command (_server _command _arguments) 1200 "Ask SERVER to execute COMMAND with ARGUMENTS.") 1201 1202 (defun lsp-elt (sequence n) 1203 "Return Nth element of SEQUENCE or nil if N is out of range." 1204 (cond 1205 ((listp sequence) (elt sequence n)) 1206 ((arrayp sequence) 1207 (and (> (length sequence) n) (aref sequence n))) 1208 (t (and (> (length sequence) n) (elt sequence n))))) 1209 1210 ;; define seq-first and seq-rest for older emacs 1211 (defun lsp-seq-first (sequence) 1212 "Return the first element of SEQUENCE." 1213 (lsp-elt sequence 0)) 1214 1215 (defun lsp-seq-rest (sequence) 1216 "Return a sequence of the elements of SEQUENCE except the first one." 1217 (seq-drop sequence 1)) 1218 1219 ;;;###autoload 1220 (defun lsp--string-listp (sequence) 1221 "Return t if all elements of SEQUENCE are strings, else nil." 1222 (not (seq-find (lambda (x) (not (stringp x))) sequence))) 1223 1224 (defun lsp--string-vector-p (candidate) 1225 "Returns true if CANDIDATE is a vector data structure and 1226 every element of it is of type string, else nil." 1227 (and 1228 (vectorp candidate) 1229 (seq-every-p #'stringp candidate))) 1230 1231 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0") 1232 1233 (defun lsp--editable-vector-match (widget value) 1234 "Function for `lsp-editable-vector' :match." 1235 ;; Value must be a list or a vector and all the members must match the type. 1236 (and (or (listp value) (vectorp value)) 1237 (length (cdr (lsp--editable-vector-match-inline widget value))))) 1238 1239 (defun lsp--editable-vector-match-inline (widget value) 1240 "Value for `lsp-editable-vector' :match-inline." 1241 (let ((type (nth 0 (widget-get widget :args))) 1242 (ok t) 1243 found) 1244 (while (and value ok) 1245 (let ((answer (widget-match-inline type value))) 1246 (if answer 1247 (let ((head (if (vectorp answer) (aref answer 0) (car answer))) 1248 (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer)))) 1249 (setq found (append found head) 1250 value tail)) 1251 (setq ok nil)))) 1252 (cons found value))) 1253 1254 (defun lsp--editable-vector-value-to-external (_widget internal-value) 1255 "Convert the internal list value to a vector." 1256 (if (listp internal-value) 1257 (apply 'vector internal-value) 1258 internal-value)) 1259 1260 (defun lsp--editable-vector-value-to-internal (_widget external-value) 1261 "Convert the external vector value to a list." 1262 (if (vectorp external-value) 1263 (append external-value nil) 1264 external-value)) 1265 1266 (define-widget 'lsp--editable-vector 'editable-list 1267 "A subclass of `editable-list' that accepts and returns a 1268 vector instead of a list." 1269 :value-to-external 'lsp--editable-vector-value-to-external 1270 :value-to-internal 'lsp--editable-vector-value-to-internal 1271 :match 'lsp--editable-vector-match 1272 :match-inline 'lsp--editable-vector-match-inline) 1273 1274 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector 1275 "A variable length homogeneous vector." 1276 :tag "Repeat" 1277 :format "%{%t%}:\n%v%i\n") 1278 1279 (define-widget 'lsp-string-vector 'lazy 1280 "A vector of zero or more elements, every element of which is a string. 1281 Appropriate for any language-specific `defcustom' that needs to 1282 serialize as a JSON array of strings. 1283 1284 Deprecated. Use `lsp-repeatable-vector' instead. " 1285 :offset 4 1286 :tag "Vector" 1287 :type '(lsp-repeatable-vector string)) 1288 1289 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0") 1290 1291 (defvar lsp--show-message t 1292 "If non-nil, show debug message from `lsp-mode'.") 1293 1294 (defun lsp--message (format &rest args) 1295 "Wrapper for `message' 1296 1297 We `inhibit-message' the message when the cursor is in the 1298 minibuffer and when emacs version is before emacs 27 due to the 1299 fact that we often use `lsp--info', `lsp--warn' and `lsp--error' 1300 in async context and the call to these function is removing the 1301 minibuffer prompt. The issue with async messages is already fixed 1302 in emacs 27. 1303 1304 See #2049" 1305 (when lsp--show-message 1306 (let ((inhibit-message (or inhibit-message 1307 (and (minibufferp) 1308 (version< emacs-version "27.0"))))) 1309 (apply #'message format args)))) 1310 1311 (defun lsp--info (format &rest args) 1312 "Display lsp info message with FORMAT with ARGS." 1313 (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args))) 1314 1315 (defun lsp--warn (format &rest args) 1316 "Display lsp warn message with FORMAT with ARGS." 1317 (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args))) 1318 1319 (defun lsp--error (format &rest args) 1320 "Display lsp error message with FORMAT with ARGS." 1321 (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args))) 1322 1323 (defun lsp-log (format &rest args) 1324 "Log message to the ’*lsp-log*’ buffer. 1325 1326 FORMAT and ARGS i the same as for `message'." 1327 (when lsp-log-max 1328 (let ((log-buffer (get-buffer "*lsp-log*")) 1329 (inhibit-read-only t)) 1330 (unless log-buffer 1331 (setq log-buffer (get-buffer-create "*lsp-log*")) 1332 (with-current-buffer log-buffer 1333 (buffer-disable-undo) 1334 (view-mode 1) 1335 (set (make-local-variable 'lsp--log-lines) 0))) 1336 (with-current-buffer log-buffer 1337 (save-excursion 1338 (let* ((message (apply 'format format args)) 1339 ;; Count newlines in message. 1340 (newlines (1+ (cl-loop with start = 0 1341 for count from 0 1342 while (string-match "\n" message start) 1343 do (setq start (match-end 0)) 1344 finally return count)))) 1345 (goto-char (point-max)) 1346 1347 ;; in case the buffer is not empty insert before last \n to preserve 1348 ;; the point position(in case it is in the end) 1349 (if (eq (point) (point-min)) 1350 (progn 1351 (insert "\n") 1352 (backward-char)) 1353 (backward-char) 1354 (insert "\n")) 1355 (insert message) 1356 1357 (setq lsp--log-lines (+ lsp--log-lines newlines)) 1358 1359 (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max)) 1360 (let ((to-delete (- lsp--log-lines lsp-log-max))) 1361 (goto-char (point-min)) 1362 (forward-line to-delete) 1363 (delete-region (point-min) (point)) 1364 (setq lsp--log-lines lsp-log-max))))))))) 1365 1366 (defalias 'lsp-message 'lsp-log) 1367 1368 (defalias 'lsp-ht 'ht) 1369 1370 (defalias 'lsp-file-local-name 'file-local-name) 1371 1372 (defun lsp-f-canonical (file-name) 1373 "Return the canonical FILE-NAME, without a trailing slash." 1374 (directory-file-name (expand-file-name file-name))) 1375 1376 (defalias 'lsp-canonical-file-name 'lsp-f-canonical) 1377 1378 (defun lsp-f-same? (path-a path-b) 1379 "Return t if PATH-A and PATH-B are references to the same file. 1380 Symlinks are not followed." 1381 (when (and (f-exists? path-a) 1382 (f-exists? path-b)) 1383 (equal 1384 (lsp-f-canonical (directory-file-name (f-expand path-a))) 1385 (lsp-f-canonical (directory-file-name (f-expand path-b)))))) 1386 1387 (defun lsp-f-parent (path) 1388 "Return the parent directory to PATH. 1389 Symlinks are not followed." 1390 (let ((parent (file-name-directory 1391 (directory-file-name (f-expand path default-directory))))) 1392 (unless (lsp-f-same? path parent) 1393 (if (f-relative? path) 1394 (f-relative parent) 1395 (directory-file-name parent))))) 1396 1397 (defun lsp-f-ancestor-of? (path-a path-b) 1398 "Return t if PATH-A is an ancestor of PATH-B. 1399 Symlinks are not followed." 1400 (unless (lsp-f-same? path-a path-b) 1401 (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator)) 1402 (lsp-f-canonical path-b)))) 1403 1404 ;; compat 1405 (if (version< emacs-version "29.1") 1406 ;; Undo macro probably introduced in 29.1 1407 (defmacro lsp-with-undo-amalgamate (&rest body) 1408 "Like `progn' but perform BODY with amalgamated undo barriers. 1409 1410 This allows multiple operations to be undone in a single step. 1411 When undo is disabled this behaves like `progn'." 1412 (declare (indent 0) (debug t)) 1413 (let ((handle (make-symbol "--change-group-handle--"))) 1414 `(let ((,handle (prepare-change-group)) 1415 ;; Don't truncate any undo data in the middle of this, 1416 ;; otherwise Emacs might truncate part of the resulting 1417 ;; undo step: we want to mimic the behavior we'd get if the 1418 ;; undo-boundaries were never added in the first place. 1419 (undo-outer-limit nil) 1420 (undo-limit most-positive-fixnum) 1421 (undo-strong-limit most-positive-fixnum)) 1422 (unwind-protect 1423 (progn 1424 (activate-change-group ,handle) 1425 ,@body) 1426 (progn 1427 (accept-change-group ,handle) 1428 (undo-amalgamate-change-group ,handle)))))) 1429 (defalias 'lsp-with-undo-amalgamate 'with-undo-amalgamate)) 1430 1431 (defun lsp--merge-results (results method) 1432 "Merge RESULTS by filtering the empty hash-tables and merging 1433 the lists according to METHOD." 1434 (pcase (--map (if (vectorp it) 1435 (append it nil) it) 1436 (-filter #'identity results)) 1437 (`() ()) 1438 ;; only one result - simply return it 1439 (`(,fst) fst) 1440 ;; multiple results merge it based on strategy 1441 (results 1442 (pcase method 1443 ("textDocument/hover" (pcase (seq-filter 1444 (-compose #'not #'lsp-empty?) 1445 results) 1446 (`(,hover) hover) 1447 (hovers (lsp-make-hover 1448 :contents 1449 (-mapcat 1450 (-lambda ((&Hover :contents)) 1451 (if (and (sequencep contents) 1452 (not (stringp contents))) 1453 (append contents ()) 1454 (list contents))) 1455 hovers))))) 1456 ("textDocument/completion" 1457 (lsp-make-completion-list 1458 :is-incomplete (seq-some 1459 #'lsp:completion-list-is-incomplete 1460 results) 1461 :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it) 1462 (lsp:completion-list-items it) 1463 it) 1464 nil)) 1465 results))) 1466 ("completionItem/resolve" 1467 (let ((item (cl-first results))) 1468 (when-let* ((details (seq-filter #'identity 1469 (seq-map #'lsp:completion-item-detail? results)))) 1470 (lsp:set-completion-item-detail? 1471 item 1472 (string-join details " "))) 1473 (when-let* ((docs (seq-filter #'identity 1474 (seq-map #'lsp:completion-item-documentation? results)))) 1475 (lsp:set-completion-item-documentation? 1476 item 1477 (lsp-make-markup-content 1478 :kind (or (seq-some (lambda (it) 1479 (when (equal (lsp:markup-content-kind it) 1480 lsp/markup-kind-markdown) 1481 lsp/markup-kind-markdown)) 1482 docs) 1483 lsp/markup-kind-plain-text) 1484 :value (string-join (seq-map (lambda (doc) 1485 (or (lsp:markup-content-value doc) 1486 (and (stringp doc) doc))) 1487 docs) 1488 "\n")))) 1489 (when-let* ((edits (seq-filter #'identity 1490 (seq-map #'lsp:completion-item-additional-text-edits? results)))) 1491 (lsp:set-completion-item-additional-text-edits? 1492 item 1493 (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits))) 1494 item)) 1495 (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results)))))) 1496 1497 (defun lsp--spinner-start () 1498 "Start spinner indication." 1499 (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error))) 1500 1501 (defun lsp--propertize (str type) 1502 "Propertize STR as per TYPE." 1503 (propertize str 'face (alist-get type lsp--message-type-face))) 1504 1505 (defun lsp-workspaces () 1506 "Return the lsp workspaces associated with the current project." 1507 (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces)) 1508 1509 (defun lsp--completing-read (prompt collection transform-fn &optional predicate 1510 require-match initial-input 1511 hist def inherit-input-method) 1512 "Wrap `completing-read' to provide transformation function and disable sort. 1513 1514 TRANSFORM-FN will be used to transform each of the items before displaying. 1515 1516 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF 1517 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes." 1518 (let* ((col (--map (cons (funcall transform-fn it) it) collection)) 1519 (completion (completing-read prompt 1520 (lambda (string pred action) 1521 (if (eq action 'metadata) 1522 `(metadata (display-sort-function . identity)) 1523 (complete-with-action action col string pred))) 1524 predicate require-match initial-input hist 1525 def inherit-input-method))) 1526 (cdr (assoc completion col)))) 1527 1528 (defconst lsp--system-arch (lambda () 1529 (setq lsp--system-arch 1530 (pcase system-type 1531 ('windows-nt 1532 (pcase system-configuration 1533 ((rx bol "x86_64-") 'x64) 1534 (_ 'x86))) 1535 ('darwin 1536 (pcase system-configuration 1537 ((rx "aarch64-") 'arm64) 1538 (_ 'x64))) 1539 ('gnu/linux 1540 (pcase system-configuration 1541 ((rx bol "aarch64-") 'arm64) 1542 ((rx bol "x86_64") 'x64) 1543 ((rx bol (| "i386" "i886")) 'x32))) 1544 (_ 1545 (pcase system-configuration 1546 ((rx bol "x86_64") 'x64) 1547 ((rx bol (| "i386" "i886")) 'x32)))))) 1548 "Return the system architecture of `Emacs'. 1549 Special values: 1550 `x64' 64bit 1551 `x32' 32bit 1552 `arm64' ARM 64bit") 1553 1554 (defmacro lsp-with-current-buffer (buffer-id &rest body) 1555 (declare (indent 1) (debug t)) 1556 `(if-let* ((wcb (plist-get ,buffer-id :with-current-buffer))) 1557 (with-lsp-workspaces (plist-get ,buffer-id :workspaces) 1558 (funcall wcb (lambda () ,@body))) 1559 (with-current-buffer ,buffer-id 1560 ,@body))) 1561 1562 (defvar lsp--throw-on-input nil 1563 "Make `lsp-*-while-no-input' throws `input' on interrupted.") 1564 1565 (defmacro lsp--catch (tag bodyform &rest handlers) 1566 "Catch TAG thrown in BODYFORM. 1567 The return value from TAG will be handled in HANDLERS by `pcase'." 1568 (declare (debug (form form &rest (pcase-PAT body))) (indent 2)) 1569 (let ((re-sym (make-symbol "re"))) 1570 `(let ((,re-sym (catch ,tag ,bodyform))) 1571 (pcase ,re-sym 1572 ,@handlers)))) 1573 1574 (defmacro lsp--while-no-input (&rest body) 1575 "Wrap BODY in `while-no-input' and respecting `non-essential'. 1576 If `lsp--throw-on-input' is set, will throw if input is pending, else 1577 return value of `body' or nil if interrupted." 1578 (declare (debug t) (indent 0)) 1579 `(if non-essential 1580 (let ((res (while-no-input ,@body))) 1581 (cond 1582 ((and lsp--throw-on-input (equal res t)) 1583 (throw 'input :interrupted)) 1584 ((booleanp res) nil) 1585 (t res))) 1586 ,@body)) 1587 1588 ;; A ‘lsp--client’ object describes the client-side behavior of a language 1589 ;; server. It is used to start individual server processes, each of which is 1590 ;; represented by a ‘lsp--workspace’ object. Client objects are normally 1591 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’. Each 1592 ;; workspace refers to exactly one client, but there can be multiple workspaces 1593 ;; for a single client. 1594 (cl-defstruct lsp--client 1595 ;; ‘language-id’ is a function that receives a buffer as a single argument 1596 ;; and should return the language identifier for that buffer. See 1597 ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem 1598 ;; for a list of language identifiers. Also consult the documentation for 1599 ;; the language server represented by this client to find out what language 1600 ;; identifiers it supports or expects. 1601 (language-id nil) 1602 1603 ;; ‘add-on?’ when set to t the server will be started no matter whether there 1604 ;; is another server handling the same mode. 1605 (add-on? nil) 1606 ;; ‘new-connection’ is a function that should start a language server process 1607 ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS). 1608 ;; COMMAND-PROCESS must be a process object representing the server process 1609 ;; just started. COMMUNICATION-PROCESS must be a process (including pipe and 1610 ;; network processes) that ‘lsp-mode’ uses to communicate with the language 1611 ;; server using the language server protocol. COMMAND-PROCESS and 1612 ;; COMMUNICATION-PROCESS may be the same process; in that case 1613 ;; ‘new-connection’ may also return that process as a single 1614 ;; object. ‘new-connection’ is called with two arguments, FILTER and 1615 ;; SENTINEL. FILTER should be used as process filter for 1616 ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for 1617 ;; COMMAND-PROCESS. 1618 (new-connection nil) 1619 1620 ;; ‘ignore-regexps’ is a list of regexps. When a data packet from the 1621 ;; language server matches any of these regexps, it will be ignored. This is 1622 ;; intended for dealing with language servers that output non-protocol data. 1623 (ignore-regexps nil) 1624 1625 ;; ‘ignore-messages’ is a list of regexps. When a message from the language 1626 ;; server matches any of these regexps, it will be ignored. This is useful 1627 ;; for filtering out unwanted messages; such as servers that send nonstandard 1628 ;; message types, or extraneous log messages. 1629 (ignore-messages nil) 1630 1631 ;; ‘notification-handlers’ is a hash table mapping notification method names 1632 ;; (strings) to functions handling the respective notifications. Upon 1633 ;; receiving a notification, ‘lsp-mode’ will call the associated handler 1634 ;; function passing two arguments, the ‘lsp--workspace’ object and the 1635 ;; deserialized notification parameters. 1636 (notification-handlers (make-hash-table :test 'equal)) 1637 1638 ;; ‘request-handlers’ is a hash table mapping request method names 1639 ;; (strings) to functions handling the respective notifications. Upon 1640 ;; receiving a request, ‘lsp-mode’ will call the associated handler function 1641 ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized 1642 ;; request parameters. 1643 (request-handlers (make-hash-table :test 'equal)) 1644 1645 ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request 1646 ;; identifiers for pending asynchronous requests to functions handling the 1647 ;; respective responses. Upon receiving a response from the language server, 1648 ;; ‘lsp-mode’ will call the associated response handler function with a 1649 ;; single argument, the deserialized response parameters. 1650 (response-handlers (make-hash-table :test 'eql)) 1651 1652 ;; ‘prefix-function’ is called for getting the prefix for completion. 1653 ;; The function takes no parameter and returns a cons (start . end) representing 1654 ;; the start and end bounds of the prefix. If it's not set, the client uses a 1655 ;; default prefix function." 1656 (prefix-function nil) 1657 1658 ;; Contains mapping of scheme to the function that is going to be used to load 1659 ;; the file. 1660 (uri-handlers (make-hash-table :test #'equal)) 1661 1662 ;; ‘action-handlers’ is a hash table mapping action to a handler function. It 1663 ;; can be used in `lsp-execute-code-action' to determine whether the action 1664 ;; current client is interested in executing the action instead of sending it 1665 ;; to the server. 1666 (action-handlers (make-hash-table :test 'equal)) 1667 1668 ;; `action-filter' can be set to a function that modifies any incoming 1669 ;; `CodeAction' in place before it is executed. The return value is ignored. 1670 ;; This can be used to patch up broken code action requests before they are 1671 ;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an 1672 ;; example of a function that can be useful here. 1673 (action-filter nil) 1674 1675 ;; major modes supported by the client. 1676 major-modes 1677 ;; Function that will be called to decide if this language client 1678 ;; should manage a particular buffer. The function will be passed 1679 ;; the file name and major mode to inform the decision. Setting 1680 ;; `activation-fn' will override `major-modes', if 1681 ;; present. 1682 activation-fn 1683 ;; Break the tie when major-mode is supported by multiple clients. 1684 (priority 0) 1685 ;; Unique identifier for representing the client object. 1686 server-id 1687 ;; defines whether the client supports multi root workspaces. 1688 multi-root 1689 ;; Initialization options or a function that returns initialization options. 1690 initialization-options 1691 ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or 1692 ;; completely replace, the faces used for semantic highlighting on a 1693 ;; client-by-client basis. 1694 ;; 1695 ;; It recognizes four members, all of which are optional: `:types’ and 1696 ;; `:modifiers’, respectively, should be face definition lists akin to 1697 ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be 1698 ;; merged with the default face definition list. 1699 ;; 1700 ;; Alternatively, if the plist members `:discard-default-types’ or 1701 ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers' 1702 ;; face definitions will be replaced entirely by their respective overrides. 1703 ;; 1704 ;; For example, setting `:semantic-tokens-faces-overrides' to 1705 ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from 1706 ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'. 1707 ;; 1708 ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))' 1709 ;; will also remap "macro", but on top of that associate the fictional token type 1710 ;; "not-quite-a-macro" with the face named `some-face'. 1711 ;; 1712 ;; `(:types (("macro" . font-lock-keyword-face)) 1713 ;; :modifiers (("declaration" . lsp-face-semhl-interface)) 1714 ;; :discard-default-types t 1715 ;; :discard-default-modifiers t)' 1716 ;; will discard all default face definitions, hence leaving the client with 1717 ;; only one token type "macro", mapped to `font-lock-keyword-face', and one 1718 ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'. 1719 semantic-tokens-faces-overrides 1720 ;; Provides support for registering LSP Server specific capabilities. 1721 custom-capabilities 1722 ;; Function which returns the folders that are considered to be not projects but library files. 1723 ;; The function accepts one parameter currently active workspace. 1724 ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225. 1725 library-folders-fn 1726 ;; function which will be called when opening file in the workspace to perform 1727 ;; client specific initialization. The function accepts one parameter 1728 ;; currently active workspace. 1729 before-file-open-fn 1730 ;; Function which will be called right after a workspace has been initialized. 1731 initialized-fn 1732 ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP. 1733 (remote? nil) 1734 1735 ;; ‘completion-in-comments?’ t if the client supports completion in comments. 1736 (completion-in-comments? nil) 1737 1738 ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client. 1739 (path->uri-fn nil) 1740 1741 ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client. 1742 (uri->path-fn nil) 1743 ;; Function that returns an environment structure that will be used 1744 ;; to set some environment variables when starting the language 1745 ;; server process. These environment variables enable some 1746 ;; additional features in the language server. The environment 1747 ;; structure is an alist of the form (KEY . VALUE), where KEY is a 1748 ;; string (regularly in all caps), and VALUE may be a string, a 1749 ;; boolean, or a sequence of strings. 1750 environment-fn 1751 1752 ;; ‘after-open-fn’ workspace after open specific hooks. 1753 (after-open-fn nil) 1754 1755 ;; ‘async-request-handlers’ is a hash table mapping request method names 1756 ;; (strings) to functions handling the respective requests that may take 1757 ;; time to finish. Upon receiving a request, ‘lsp-mode’ will call the 1758 ;; associated handler function passing three arguments, the ‘lsp--workspace’ 1759 ;; object, the deserialized request parameters and the callback which accept 1760 ;; result as its parameter. 1761 (async-request-handlers (make-hash-table :test 'equal)) 1762 download-server-fn 1763 download-in-progress? 1764 buffers 1765 synchronize-sections) 1766 1767 (defun lsp-clients-executable-find (find-command &rest args) 1768 "Finds an executable by invoking a search command. 1769 1770 FIND-COMMAND is the executable finder that searches for the 1771 actual language server executable. ARGS is a list of arguments to 1772 give to FIND-COMMAND to find the language server. Returns the 1773 output of FIND-COMMAND if it exits successfully, nil otherwise. 1774 1775 Typical uses include finding an executable by invoking `find' in 1776 a project, finding LLVM commands on macOS with `xcrun', or 1777 looking up project-specific language servers for projects written 1778 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv' 1779 etc." 1780 (when-let* ((find-command-path (executable-find find-command)) 1781 (executable-path 1782 (with-temp-buffer 1783 (when (zerop (apply 'call-process find-command-path nil t nil args)) 1784 (buffer-substring-no-properties (point-min) (point-max)))))) 1785 (string-trim executable-path))) 1786 1787 (defvar lsp--already-widened nil) 1788 1789 (defmacro lsp-save-restriction-and-excursion (&rest form) 1790 (declare (indent 0) (debug t)) 1791 `(if lsp--already-widened 1792 (save-excursion ,@form) 1793 (-let [lsp--already-widened t] 1794 (save-restriction 1795 (widen) 1796 (save-excursion ,@form))))) 1797 1798 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number 1799 (defun lsp--line-character-to-point (line character) 1800 "Return the point for character CHARACTER on line LINE." 1801 (or (lsp-virtual-buffer-call :line/character->point line character) 1802 (let ((inhibit-field-text-motion t)) 1803 (lsp-save-restriction-and-excursion 1804 (goto-char (point-min)) 1805 (forward-line line) 1806 ;; server may send character position beyond the current line and we 1807 ;; should fallback to line end. 1808 (-let [line-end (line-end-position)] 1809 (if (> character (- line-end (point))) 1810 line-end 1811 (forward-char character) 1812 (point))))))) 1813 1814 (lsp-defun lsp--position-to-point ((&Position :line :character)) 1815 "Convert `Position' object in PARAMS to a point." 1816 (lsp--line-character-to-point line character)) 1817 1818 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end)) 1819 (cons start end)) 1820 1821 (lsp-defun lsp--range-text ((&RangeToPoint :start :end)) 1822 (buffer-substring start end)) 1823 1824 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end))) 1825 (cond 1826 ((and 1827 (region-active-p) 1828 (<= start (region-beginning) end) 1829 (<= start (region-end) end) 1830 (or (not (= start (region-beginning))) 1831 (not (= end (region-end))))) 1832 (cons start end)) 1833 ((and (<= start (point) end) 1834 (not (region-active-p))) 1835 (cons start end)) 1836 (parent? (lsp--find-wrapping-range parent?)))) 1837 1838 (defun lsp--get-selection-range () 1839 (or 1840 (-when-let ((cache . cache-tick) lsp--document-selection-range-cache) 1841 (when (= cache-tick (buffer-modified-tick)) cache)) 1842 (let ((response (cl-first 1843 (lsp-request 1844 "textDocument/selectionRange" 1845 (list :textDocument (lsp--text-document-identifier) 1846 :positions (vector (lsp--cur-position))))))) 1847 (setq lsp--document-selection-range-cache 1848 (cons response (buffer-modified-tick))) 1849 response))) 1850 1851 (defun lsp-extend-selection () 1852 "Extend selection." 1853 (interactive) 1854 (unless (lsp-feature? "textDocument/selectionRange") 1855 (signal 'lsp-capability-not-supported (list "selectionRangeProvider"))) 1856 (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range))) 1857 (goto-char start) 1858 (set-mark (point)) 1859 (goto-char end) 1860 (exchange-point-and-mark))) 1861 1862 (defun lsp-warn (message &rest args) 1863 "Display a warning message made from (`format-message' MESSAGE ARGS...). 1864 This is equivalent to `display-warning', using `lsp-mode' as the type and 1865 `:warning' as the level." 1866 (display-warning 'lsp-mode (apply #'format-message message args))) 1867 1868 (defun lsp--get-uri-handler (scheme) 1869 "Get uri handler for SCHEME in the current workspace." 1870 (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it))) 1871 (or (lsp-workspaces) (lsp--session-workspaces (lsp-session))))) 1872 1873 (defun lsp--fix-path-casing (path) 1874 "On windows, downcases path because the windows file system is 1875 case-insensitive. 1876 1877 On other systems, returns path without change." 1878 (if (eq system-type 'windows-nt) (downcase path) path)) 1879 1880 (defun lsp--uri-to-path (uri) 1881 "Convert URI to a file path." 1882 (if-let* ((fn (->> (lsp-workspaces) 1883 (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client)) 1884 (cl-first)))) 1885 (funcall fn uri) 1886 (lsp--uri-to-path-1 uri))) 1887 1888 (defun lsp-remap-path-if-needed (file-name) 1889 (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings)) 1890 (propertize (buffer-local-value 'buffer-file-name buffer) 1891 'lsp-virtual-buffer virtual-buffer) 1892 file-name)) 1893 1894 (defun lsp--uri-to-path-1 (uri) 1895 "Convert URI to a file path." 1896 (let* ((url (url-generic-parse-url (url-unhex-string uri))) 1897 (type (url-type url)) 1898 (target (url-target url)) 1899 (file 1900 (concat (decode-coding-string (url-filename url) 1901 (or locale-coding-system 'utf-8)) 1902 (when (and target 1903 (not (s-match 1904 (rx "#" (group (1+ num)) (or "," "#") 1905 (group (1+ num)) 1906 string-end) 1907 uri))) 1908 (concat "#" target)))) 1909 (file-name (if (and type (not (string= type "file"))) 1910 (if-let* ((handler (lsp--get-uri-handler type))) 1911 (funcall handler uri) 1912 uri) 1913 ;; `url-generic-parse-url' is buggy on windows: 1914 ;; https://github.com/emacs-lsp/lsp-mode/pull/265 1915 (or (and (eq system-type 'windows-nt) 1916 (eq (elt file 0) ?\/) 1917 (substring file 1)) 1918 file)))) 1919 (->> file-name 1920 (concat (-some #'lsp--workspace-host-root (lsp-workspaces))) 1921 (lsp-remap-path-if-needed)))) 1922 1923 (defun lsp--buffer-uri () 1924 "Return URI of the current buffer." 1925 (or lsp-buffer-uri 1926 (plist-get lsp--virtual-buffer :buffer-uri) 1927 (lsp--path-to-uri 1928 (or (buffer-file-name) (buffer-file-name (buffer-base-buffer)))))) 1929 1930 (defun lsp-register-client-capabilities (&rest _args) 1931 "Implemented only to make `company-lsp' happy. 1932 DELETE when `lsp-mode.el' is deleted.") 1933 1934 (defconst lsp--url-path-allowed-chars 1935 (url--allowed-chars (append '(?/) url-unreserved-chars)) 1936 "`url-unreserved-chars' with additional delim ?/. 1937 This set of allowed chars is enough for hexifying local file paths.") 1938 1939 (defun lsp--path-to-uri-1 (path) 1940 (concat lsp--uri-file-prefix 1941 (--> path 1942 (expand-file-name it) 1943 (or (file-remote-p it 'localname t) it) 1944 (url-hexify-string it lsp--url-path-allowed-chars)))) 1945 1946 (defun lsp--path-to-uri (path) 1947 "Convert PATH to a uri." 1948 (if-let* ((uri-fn (->> (lsp-workspaces) 1949 (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client)) 1950 (cl-first)))) 1951 (funcall uri-fn path) 1952 (lsp--path-to-uri-1 path))) 1953 1954 (defun lsp--string-match-any (regex-list str) 1955 "Return the first regex, if any, within REGEX-LIST matching STR." 1956 (--first (string-match it str) regex-list)) 1957 1958 (cl-defstruct lsp-watch 1959 (descriptors (make-hash-table :test 'equal)) 1960 root-directory) 1961 1962 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories) 1963 (let ((file-name (cl-third event)) 1964 (event-type (cl-second event))) 1965 (cond 1966 ((and (file-directory-p file-name) 1967 (equal 'created event-type) 1968 (not (lsp--string-match-any ignored-directories file-name))) 1969 1970 (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch) 1971 1972 ;; process the files that are already present in 1973 ;; the directory. 1974 (->> (directory-files-recursively file-name ".*" t) 1975 (seq-do (lambda (f) 1976 (unless (file-directory-p f) 1977 (funcall callback (list nil 'created f))))))) 1978 ((and (memq event-type '(created deleted changed)) 1979 (not (file-directory-p file-name)) 1980 (not (lsp--string-match-any ignored-files file-name))) 1981 (funcall callback event)) 1982 ((and (memq event-type '(renamed)) 1983 (not (file-directory-p file-name)) 1984 (not (lsp--string-match-any ignored-files file-name))) 1985 (funcall callback `(,(cl-first event) deleted ,(cl-third event))) 1986 (funcall callback `(,(cl-first event) created ,(cl-fourth event))))))) 1987 1988 (defun lsp--ask-about-watching-big-repo (number-of-directories dir) 1989 "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR. 1990 This is useful when there is a lot of files in a repository, as 1991 that may slow Emacs down. Returns t if the user wants to watch 1992 the entire repository, nil otherwise." 1993 (prog1 1994 (yes-or-no-p 1995 (format 1996 "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down. 1997 Do you want to watch all files in %s? " 1998 dir 1999 number-of-directories 2000 dir)) 2001 (lsp--info 2002 (concat "You can configure this warning with the `lsp-enable-file-watchers' " 2003 "and `lsp-file-watch-threshold' variables")))) 2004 2005 2006 (defun lsp--path-is-watchable-directory (path dir ignored-directories) 2007 "Figure out whether PATH (inside of DIR) is meant to have a file watcher set. 2008 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't 2009 want to watch." 2010 (let 2011 ((full-path (f-join dir path))) 2012 (and (file-accessible-directory-p full-path) 2013 (not (equal path ".")) 2014 (not (equal path "..")) 2015 (not (lsp--string-match-any ignored-directories full-path))))) 2016 2017 2018 (defun lsp--all-watchable-directories (dir ignored-directories &optional visited) 2019 "Traverse DIR recursively returning a list of paths that should have watchers. 2020 IGNORED-DIRECTORIES will be used for exclusions. 2021 VISITED is used to track already-visited directories to avoid infinite loops." 2022 (let* ((dir (if (f-symlink? dir) 2023 (file-truename dir) 2024 dir)) 2025 ;; Initialize visited directories if not provided 2026 (visited (or visited (make-hash-table :test 'equal)))) 2027 (if (gethash dir visited) 2028 ;; If the directory has already been visited, skip it 2029 nil 2030 ;; Mark the current directory as visited 2031 (puthash dir t visited) 2032 (apply #'nconc 2033 ;; the directory itself is assumed to be part of the set 2034 (list dir) 2035 ;; collect all subdirectories that are watchable 2036 (-map 2037 (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories visited)) 2038 ;; but only look at subdirectories that are watchable 2039 (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories)) 2040 (directory-files dir))))))) 2041 2042 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?) 2043 "Create recursive file notification watch in DIR. 2044 CALLBACK will be called when there are changes in any of 2045 the monitored files. WATCHES is a hash table directory->file 2046 notification handle which contains all of the watch that 2047 already have been created. Watches will not be created for 2048 any directory that matches any regex in IGNORED-DIRECTORIES. 2049 Watches will not be created for any file that matches any 2050 regex in IGNORED-FILES." 2051 (let* ((dir (if (f-symlink? dir) 2052 (file-truename dir) 2053 dir)) 2054 (watch (or watch (make-lsp-watch :root-directory dir))) 2055 (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories))) 2056 (lsp-log "Creating watchers for following %s folders:\n %s" 2057 (length dirs-to-watch) 2058 (s-join "\n " dirs-to-watch)) 2059 (when (or 2060 (not warn-big-repo?) 2061 (not lsp-file-watch-threshold) 2062 (let ((number-of-directories (length dirs-to-watch))) 2063 (or 2064 (< number-of-directories lsp-file-watch-threshold) 2065 (condition-case nil 2066 (lsp--ask-about-watching-big-repo number-of-directories dir) 2067 (quit))))) 2068 (dolist (current-dir dirs-to-watch) 2069 (condition-case err 2070 (progn 2071 (puthash 2072 current-dir 2073 (file-notify-add-watch current-dir 2074 '(change) 2075 (lambda (event) 2076 (lsp--folder-watch-callback event callback watch ignored-files ignored-directories))) 2077 (lsp-watch-descriptors watch))) 2078 (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err))) 2079 (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))))) 2080 watch)) 2081 2082 (defun lsp-kill-watch (watch) 2083 "Delete WATCH." 2084 (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch)) 2085 (ht-clear! (lsp-watch-descriptors watch))) 2086 2087 (defun lsp-json-bool (val) 2088 "Convert VAL to JSON boolean." 2089 (if val t :json-false)) 2090 2091 (defmacro with-lsp-workspace (workspace &rest body) 2092 "Helper macro for invoking BODY in WORKSPACE context." 2093 (declare (debug (form body)) 2094 (indent 1)) 2095 `(let ((lsp--cur-workspace ,workspace)) ,@body)) 2096 2097 (defmacro with-lsp-workspaces (workspaces &rest body) 2098 "Helper macro for invoking BODY against multiple WORKSPACES." 2099 (declare (debug (form body)) 2100 (indent 1)) 2101 `(let ((lsp--buffer-workspaces ,workspaces)) ,@body)) 2102 2103 2104 2105 (defmacro lsp-consistency-check (package) 2106 `(defconst ,(intern (concat (symbol-name package) 2107 "-plist-value-when-compiled")) 2108 (eval-when-compile lsp-use-plists))) 2109 2110 2111 ;; loading code-workspace files 2112 2113 ;;;###autoload 2114 (defun lsp-load-vscode-workspace (file) 2115 "Load vscode workspace from FILE" 2116 (interactive "fSelect file to import: ") 2117 (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session))) 2118 2119 (let ((dir (f-dirname file))) 2120 (->> file 2121 (json-read-file) 2122 (alist-get 'folders) 2123 (-map (-lambda ((&alist 'path)) 2124 (lsp-workspace-folders-add (expand-file-name path dir))))))) 2125 2126 ;;;###autoload 2127 (defun lsp-save-vscode-workspace (file) 2128 "Save vscode workspace to FILE" 2129 (interactive "FSelect file to save to: ") 2130 2131 (let ((json-encoding-pretty-print t)) 2132 (f-write-text (json-encode 2133 `((folders . ,(->> (lsp-session) 2134 (lsp-session-folders) 2135 (--map `((path . ,it))))))) 2136 'utf-8 2137 file))) 2138 2139 2140 (defmacro lsp-foreach-workspace (&rest body) 2141 "Execute BODY for each of the current workspaces." 2142 (declare (debug (form body))) 2143 `(--map (with-lsp-workspace it ,@body) (lsp-workspaces))) 2144 2145 (defmacro when-lsp-workspace (workspace &rest body) 2146 "Helper macro for invoking BODY in WORKSPACE context if present." 2147 (declare (debug (form body)) 2148 (indent 1)) 2149 `(when-let* ((lsp--cur-workspace ,workspace)) ,@body)) 2150 2151 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items)) 2152 (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read)) 2153 (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label)) 2154 items)) 2155 (result (funcall-interactively 2156 selectfunc 2157 (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels)) 2158 (choices (if (listp result) 2159 (if (equal result '("*")) 2160 itemLabels 2161 result) 2162 (list result)))) 2163 (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data)) 2164 (if (member label choices) 2165 (lsp-make-quick-pick-item :label label :picked t :user-data user-data) 2166 nil)) 2167 items))))) 2168 2169 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?)) 2170 (read-string (format "%s: " prompt) (or value? ""))) 2171 2172 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type)) 2173 "Send the server's messages to log. 2174 PARAMS - the data sent from _WORKSPACE." 2175 (funcall (cl-case type 2176 (1 'lsp--error) 2177 (2 'lsp--warn) 2178 (t 'lsp--info)) 2179 "%s" 2180 message)) 2181 2182 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type)) 2183 "Send the server's messages to log. 2184 PARAMS - the data sent from WORKSPACE." 2185 (ignore 2186 (let ((client (lsp--workspace-client workspace))) 2187 (when (or (not client) 2188 (cl-notany (-rpartial #'string-match-p message) 2189 (lsp--client-ignore-messages client))) 2190 (lsp-log "%s" (lsp--propertize message type)))))) 2191 2192 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?)) 2193 "Display a message request to user sending the user selection back to server." 2194 (let* ((message (lsp--propertize message type)) 2195 (choices (seq-map #'lsp:message-action-item-title actions?))) 2196 (if choices 2197 (completing-read (concat message " ") (seq-into choices 'list) nil t) 2198 (lsp-log message)))) 2199 2200 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?)) 2201 "Show document URI in a buffer and go to SELECTION if any." 2202 (let ((path (lsp--uri-to-path uri))) 2203 (when (f-exists? path) 2204 (with-current-buffer (find-file path) 2205 (when selection? 2206 (goto-char (lsp--position-to-point (lsp:range-start selection?)))) 2207 t)))) 2208 2209 (defcustom lsp-progress-prefix "⌛ " 2210 "Progress prefix." 2211 :group 'lsp-mode 2212 :type 'string 2213 :package-version '(lsp-mode . "8.0.0")) 2214 2215 (defcustom lsp-progress-function #'lsp-on-progress-modeline 2216 "Function for handling the progress notifications." 2217 :group 'lsp-mode 2218 :type '(choice 2219 (const :tag "Use modeline" lsp-on-progress-modeline) 2220 (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')" 2221 lsp-on-progress-legacy) 2222 (const :tag "Ignore" ignore) 2223 (function :tag "Other function")) 2224 :package-version '(lsp-mode . "8.0.0")) 2225 2226 (defcustom lsp-request-while-no-input-may-block nil 2227 "Have `lsp-request-while-no-input` block unless `non-essential` is t." 2228 :group 'lsp-mode 2229 :type 'boolean) 2230 2231 (defun lsp--progress-status () 2232 "Returns the status of the progress for the current workspaces." 2233 (-let ((progress-status 2234 (s-join 2235 "|" 2236 (-keep 2237 (lambda (workspace) 2238 (let ((tokens (lsp--workspace-work-done-tokens workspace))) 2239 (unless (ht-empty? tokens) 2240 (mapconcat 2241 (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?)) 2242 (concat (if percentage? 2243 (if (numberp percentage?) 2244 (format "%.0f%%%% " percentage?) 2245 (format "%s%%%% " percentage?)) 2246 "") 2247 (or message? title))) 2248 (ht-values tokens) 2249 "|")))) 2250 (lsp-workspaces))))) 2251 (unless (s-blank? progress-status) 2252 (concat lsp-progress-prefix progress-status " ")))) 2253 2254 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value 2255 (value &as &WorkDoneProgress :kind))) 2256 "PARAMS contains the progress data. 2257 WORKSPACE is the workspace that contains the progress token." 2258 (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status)))) 2259 (pcase kind 2260 ("begin" (lsp-workspace-set-work-done-token token value workspace)) 2261 ("report" (lsp-workspace-set-work-done-token token value workspace)) 2262 ("end" (lsp-workspace-rem-work-done-token token workspace))) 2263 (force-mode-line-update)) 2264 2265 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value 2266 (value &as &WorkDoneProgress :kind))) 2267 "PARAMS contains the progress data. 2268 WORKSPACE is the workspace that contains the progress token." 2269 (pcase kind 2270 ("begin" 2271 (-let* (((&WorkDoneProgressBegin :title :percentage?) value) 2272 (reporter 2273 (if lsp-progress-via-spinner 2274 (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types)) 2275 ;; Set message as a tooltip for the spinner strings 2276 (propertized-strings 2277 (seq-map (lambda (string) (propertize string 'help-echo title)) 2278 spinner-strings)) 2279 (spinner-type (vconcat propertized-strings))) 2280 ;; The progress relates to the server as a whole, 2281 ;; display it on all buffers. 2282 (mapcar (lambda (buffer) 2283 (lsp-with-current-buffer buffer 2284 (spinner-start spinner-type)) 2285 buffer) 2286 (lsp--workspace-buffers workspace))) 2287 (if percentage? 2288 (make-progress-reporter title 0 100 percentage?) 2289 ;; No percentage, just progress 2290 (make-progress-reporter title nil nil))))) 2291 (lsp-workspace-set-work-done-token token reporter workspace))) 2292 ("report" 2293 (when-let* ((reporter (lsp-workspace-get-work-done-token token workspace))) 2294 (unless lsp-progress-via-spinner 2295 (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value))))) 2296 2297 ("end" 2298 (when-let* ((reporter (lsp-workspace-get-work-done-token token workspace))) 2299 (if lsp-progress-via-spinner 2300 (mapc (lambda (buffer) 2301 (when (lsp-buffer-live-p buffer) 2302 (lsp-with-current-buffer buffer 2303 (spinner-stop)))) 2304 reporter) 2305 (progress-reporter-done reporter)) 2306 (lsp-workspace-rem-work-done-token token workspace))))) 2307 2308 2309 ;; diagnostics 2310 2311 (defvar lsp-diagnostic-filter nil 2312 "A a function which will be called with 2313 `&PublishDiagnosticsParams' and `workspace' which can be used 2314 to filter out the diagnostics. The function should return 2315 `&PublishDiagnosticsParams'. 2316 2317 Common usecase are: 2318 1. Filter the diagnostics for a particular language server. 2319 2. Filter out the diagnostics under specific level.") 2320 2321 (defvar lsp-diagnostic-stats (ht)) 2322 2323 (defun lsp-diagnostics (&optional current-workspace?) 2324 "Return the diagnostics from all workspaces." 2325 (or (pcase (if current-workspace? 2326 (lsp-workspaces) 2327 (lsp--session-workspaces (lsp-session))) 2328 (`() ()) 2329 (`(,workspace) (lsp--workspace-diagnostics workspace)) 2330 (`,workspaces (let ((result (make-hash-table :test 'equal))) 2331 (mapc (lambda (workspace) 2332 (->> workspace 2333 (lsp--workspace-diagnostics) 2334 (maphash (lambda (file-name diagnostics) 2335 (puthash file-name 2336 (append (gethash file-name result) diagnostics) 2337 result))))) 2338 workspaces) 2339 result))) 2340 (ht))) 2341 2342 (defun lsp-diagnostics-stats-for (path) 2343 "Get diagnostics statistics for PATH. 2344 The result format is vector [_ errors warnings infos hints] or nil." 2345 (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats)) 2346 2347 (defun lsp-diagnostics--request-pull-diagnostics (workspace) 2348 "Request new diagnostics for the current file within WORKSPACE. 2349 This is only executed if the server supports pull diagnostics." 2350 (when (lsp-feature? "textDocument/diagnostic") 2351 (let ((path (lsp--fix-path-casing (buffer-file-name)))) 2352 (lsp-request-async "textDocument/diagnostic" 2353 (list :textDocument (lsp--text-document-identifier)) 2354 (-lambda ((&DocumentDiagnosticReport :kind :items?)) 2355 (lsp-diagnostics--apply-pull-diagnostics workspace path kind items?)) 2356 :mode 'tick)))) 2357 2358 (defun lsp-diagnostics--update-path (path new-stats) 2359 (let ((new-stats (copy-sequence new-stats)) 2360 (path (lsp--fix-path-casing (directory-file-name path)))) 2361 (if-let* ((old-data (gethash path lsp-diagnostic-stats))) 2362 (dotimes (idx 5) 2363 (cl-callf + (aref old-data idx) 2364 (aref new-stats idx))) 2365 (puthash path new-stats lsp-diagnostic-stats)))) 2366 2367 (defun lsp-diagnostics--convert-and-update-path-stats (workspace path diagnostics) 2368 (let ((path (lsp--fix-path-casing path)) 2369 (new-stats (make-vector 5 0))) 2370 (mapc (-lambda ((&Diagnostic :severity?)) 2371 (cl-incf (aref new-stats (or severity? 1)))) 2372 diagnostics) 2373 (when-let* ((old-diags (gethash path (lsp--workspace-diagnostics workspace)))) 2374 (mapc (-lambda ((&Diagnostic :severity?)) 2375 (cl-decf (aref new-stats (or severity? 1)))) 2376 old-diags)) 2377 (lsp-diagnostics--update-path path new-stats) 2378 (while (not (string= path (setf path (file-name-directory 2379 (directory-file-name path))))) 2380 (lsp-diagnostics--update-path path new-stats)))) 2381 2382 (lsp-defun lsp--on-diagnostics-update-stats (workspace 2383 (&PublishDiagnosticsParams :uri :diagnostics)) 2384 (lsp-diagnostics--convert-and-update-path-stats workspace (lsp--uri-to-path uri) diagnostics)) 2385 2386 (defun lsp-diagnostics--apply-pull-diagnostics (workspace path kind diagnostics?) 2387 "Update WORKSPACE diagnostics at PATH with DIAGNOSTICS?. 2388 Depends on KIND being a \\='full\\=' update." 2389 (cond 2390 ((equal kind "full") 2391 ;; TODO support `lsp-diagnostic-filter' 2392 ;; (the params types differ from the published diagnostics response) 2393 (lsp-diagnostics--convert-and-update-path-stats workspace path diagnostics?) 2394 (-let* ((lsp--virtual-buffer-mappings (ht)) 2395 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2396 (if (seq-empty-p diagnostics?) 2397 (remhash path workspace-diagnostics) 2398 (puthash path (append diagnostics? nil) workspace-diagnostics)) 2399 (run-hooks 'lsp-diagnostics-updated-hook))) 2400 ((equal kind "unchanged") t) 2401 (t (lsp--error "Unknown pull diagnostic result kind '%s'" kind)))) 2402 2403 (defun lsp--on-diagnostics (workspace params) 2404 "Callback for textDocument/publishDiagnostics. 2405 interface PublishDiagnosticsParams { 2406 uri: string; 2407 diagnostics: Diagnostic[]; 2408 } 2409 PARAMS contains the diagnostics data. 2410 WORKSPACE is the workspace that contains the diagnostics." 2411 (when lsp-diagnostic-filter 2412 (setf params (funcall lsp-diagnostic-filter params workspace))) 2413 2414 (lsp--on-diagnostics-update-stats workspace params) 2415 2416 (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params) 2417 (lsp--virtual-buffer-mappings (ht)) 2418 (file (lsp--fix-path-casing (lsp--uri-to-path uri))) 2419 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2420 2421 (if (seq-empty-p diagnostics) 2422 (remhash file workspace-diagnostics) 2423 (puthash file (append diagnostics nil) workspace-diagnostics)) 2424 2425 (run-hooks 'lsp-diagnostics-updated-hook))) 2426 2427 (defun lsp-diagnostics--workspace-cleanup (workspace) 2428 (->> workspace 2429 (lsp--workspace-diagnostics) 2430 (maphash (lambda (key _) 2431 (lsp--on-diagnostics-update-stats 2432 workspace 2433 (lsp-make-publish-diagnostics-params 2434 :uri (lsp--path-to-uri key) 2435 :diagnostics []))))) 2436 (clrhash (lsp--workspace-diagnostics workspace))) 2437 2438 2439 2440 ;; textDocument/foldingRange support 2441 2442 (cl-defstruct lsp--folding-range beg end kind children) 2443 2444 (defvar-local lsp--cached-folding-ranges nil) 2445 (defvar-local lsp--cached-nested-folding-ranges nil) 2446 2447 (defun lsp--folding-range-width (range) 2448 (- (lsp--folding-range-end range) 2449 (lsp--folding-range-beg range))) 2450 2451 (defun lsp--get-folding-ranges () 2452 "Get the folding ranges for the current buffer." 2453 (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges)) 2454 (let* ((ranges (lsp-request "textDocument/foldingRange" 2455 `(:textDocument ,(lsp--text-document-identifier)))) 2456 (sorted-line-col-pairs (->> ranges 2457 (cl-mapcan (-lambda ((&FoldingRange :start-line 2458 :start-character? 2459 :end-line 2460 :end-character?)) 2461 (list (cons start-line start-character?) 2462 (cons end-line end-character?)))) 2463 (-sort #'lsp--line-col-comparator))) 2464 (line-col-to-point-map (lsp--convert-line-col-to-points-batch 2465 sorted-line-col-pairs))) 2466 (setq lsp--cached-folding-ranges 2467 (cons (buffer-chars-modified-tick) 2468 (--> ranges 2469 (seq-map (-lambda ((range &as 2470 &FoldingRange :start-line 2471 :start-character? 2472 :end-line 2473 :end-character? 2474 :kind?)) 2475 (make-lsp--folding-range 2476 :beg (ht-get line-col-to-point-map 2477 (cons start-line start-character?)) 2478 :end (ht-get line-col-to-point-map 2479 (cons end-line end-character?)) 2480 :kind kind?)) 2481 it) 2482 (seq-filter (lambda (folding-range) 2483 (< (lsp--folding-range-beg folding-range) 2484 (lsp--folding-range-end folding-range))) 2485 it) 2486 (seq-into it 'list) 2487 (delete-dups it)))))) 2488 (cdr lsp--cached-folding-ranges)) 2489 2490 (defun lsp--get-nested-folding-ranges () 2491 "Get a list of nested folding ranges for the current buffer." 2492 (-let [(tick . _) lsp--cached-folding-ranges] 2493 (if (and (eq tick (buffer-chars-modified-tick)) 2494 lsp--cached-nested-folding-ranges) 2495 lsp--cached-nested-folding-ranges 2496 (setq lsp--cached-nested-folding-ranges 2497 (lsp--folding-range-build-trees (lsp--get-folding-ranges)))))) 2498 2499 (defun lsp--folding-range-build-trees (ranges) 2500 (setq ranges (seq-sort #'lsp--range-before-p ranges)) 2501 (let* ((dummy-node (make-lsp--folding-range 2502 :beg most-negative-fixnum 2503 :end most-positive-fixnum)) 2504 (stack (list dummy-node))) 2505 (dolist (range ranges) 2506 (while (not (lsp--range-inside-p range (car stack))) 2507 (pop stack)) 2508 (push range (lsp--folding-range-children (car stack))) 2509 (push range stack)) 2510 (lsp--folding-range-children dummy-node))) 2511 2512 (defun lsp--range-inside-p (r1 r2) 2513 "Return non-nil if folding range R1 lies inside R2" 2514 (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2)) 2515 (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2)))) 2516 2517 (defun lsp--range-before-p (r1 r2) 2518 "Return non-nil if folding range R1 ends before R2" 2519 ;; Ensure r1 comes before r2 2520 (or (< (lsp--folding-range-beg r1) 2521 (lsp--folding-range-beg r2)) 2522 ;; If beg(r1) == beg(r2) make sure r2 ends first 2523 (and (= (lsp--folding-range-beg r1) 2524 (lsp--folding-range-beg r2)) 2525 (< (lsp--folding-range-end r2) 2526 (lsp--folding-range-end r1))))) 2527 2528 (defun lsp--point-inside-range-p (point range) 2529 "Return non-nil if POINT lies inside folding range RANGE." 2530 (and (>= point (lsp--folding-range-beg range)) 2531 (<= point (lsp--folding-range-end range)))) 2532 2533 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point))) 2534 "Return the innermost folding range POINT lies in." 2535 (seq-reduce (lambda (innermost-range curr-range) 2536 (if (and (lsp--point-inside-range-p point curr-range) 2537 (or (null innermost-range) 2538 (lsp--range-inside-p curr-range innermost-range))) 2539 curr-range 2540 innermost-range)) 2541 (lsp--get-folding-ranges) 2542 nil)) 2543 2544 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point))) 2545 "Return the outermost folding range POINT lies in." 2546 (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range) 2547 (let ((curr-width (lsp--folding-range-width curr-range))) 2548 (if (and (lsp--point-inside-range-p point curr-range) 2549 (or (null best-pair) 2550 (> curr-width outermost-width))) 2551 (cons curr-width curr-range) 2552 best-pair))) 2553 (lsp--get-folding-ranges) 2554 nil))) 2555 2556 (defun lsp--folding-range-at-point-bounds () 2557 (when (and lsp-enable-folding 2558 (lsp-feature? "textDocument/foldingRange")) 2559 (if-let* ((range (lsp--get-current-innermost-folding-range))) 2560 (cons (lsp--folding-range-beg range) 2561 (lsp--folding-range-end range))))) 2562 (put 'lsp--folding-range 'bounds-of-thing-at-point 2563 #'lsp--folding-range-at-point-bounds) 2564 2565 (defun lsp--get-nearest-folding-range (&optional backward) 2566 (let ((point (point)) 2567 (found nil)) 2568 (while (not 2569 (or found 2570 (if backward 2571 (<= point (point-min)) 2572 (>= point (point-max))))) 2573 (if backward (cl-decf point) (cl-incf point)) 2574 (setq found (lsp--get-current-innermost-folding-range point))) 2575 found)) 2576 2577 (defun lsp--folding-range-at-point-forward-op (n) 2578 (when (and lsp-enable-folding 2579 (not (zerop n)) 2580 (lsp-feature? "textDocument/foldingRange")) 2581 (cl-block break 2582 (dotimes (_ (abs n)) 2583 (if-let* ((range (lsp--get-nearest-folding-range (< n 0)))) 2584 (goto-char (if (< n 0) 2585 (lsp--folding-range-beg range) 2586 (lsp--folding-range-end range))) 2587 (cl-return-from break)))))) 2588 (put 'lsp--folding-range 'forward-op 2589 #'lsp--folding-range-at-point-forward-op) 2590 2591 (defun lsp--folding-range-at-point-beginning-op () 2592 (goto-char (car (lsp--folding-range-at-point-bounds)))) 2593 (put 'lsp--folding-range 'beginning-op 2594 #'lsp--folding-range-at-point-beginning-op) 2595 2596 (defun lsp--folding-range-at-point-end-op () 2597 (goto-char (cdr (lsp--folding-range-at-point-bounds)))) 2598 (put 'lsp--folding-range 'end-op 2599 #'lsp--folding-range-at-point-end-op) 2600 2601 (defun lsp--range-at-point-bounds () 2602 (or (lsp--folding-range-at-point-bounds) 2603 (when-let* ((range (and 2604 (lsp-feature? "textDocument/hover") 2605 (->> (lsp--text-document-position-params) 2606 (lsp-request "textDocument/hover") 2607 (lsp:hover-range?))))) 2608 (lsp--range-to-region range)))) 2609 2610 ;; A more general purpose "thing", useful for applications like focus.el 2611 (put 'lsp--range 'bounds-of-thing-at-point 2612 #'lsp--range-at-point-bounds) 2613 2614 (defun lsp--log-io-p (method) 2615 "Return non nil if should log for METHOD." 2616 (and lsp-log-io 2617 (or (not lsp-log-io-allowlist-methods) 2618 (member method lsp-log-io-allowlist-methods)))) 2619 2620 2621 ;; toggles 2622 2623 (defun lsp-toggle-trace-io () 2624 "Toggle client-server protocol logging." 2625 (interactive) 2626 (setq lsp-log-io (not lsp-log-io)) 2627 (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled"))) 2628 2629 (defun lsp-toggle-signature-auto-activate () 2630 "Toggle signature auto activate." 2631 (interactive) 2632 (setq lsp-signature-auto-activate 2633 (unless lsp-signature-auto-activate '(:on-trigger-char))) 2634 (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled")) 2635 (lsp--update-signature-help-hook)) 2636 2637 (defun lsp-toggle-on-type-formatting () 2638 "Toggle on type formatting." 2639 (interactive) 2640 (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting)) 2641 (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled")) 2642 (lsp--update-on-type-formatting-hook)) 2643 2644 (defun lsp-toggle-symbol-highlight () 2645 "Toggle symbol highlighting." 2646 (interactive) 2647 (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting)) 2648 2649 (cond 2650 ((and lsp-enable-symbol-highlighting 2651 (lsp-feature? "textDocument/documentHighlight")) 2652 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t) 2653 (lsp--info "Symbol highlighting enabled in current buffer.")) 2654 ((not lsp-enable-symbol-highlighting) 2655 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 2656 (lsp--remove-overlays 'lsp-highlight) 2657 (lsp--info "Symbol highlighting disabled in current buffer.")))) 2658 2659 2660 ;; keybindings 2661 (defvar lsp--binding-descriptions nil 2662 "List of key binding/short description pair.") 2663 2664 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings) 2665 "In KEYMAP, define key sequence KEY as DEF conditionally. 2666 This is like `define-key', except the definition disappears 2667 whenever COND evaluates to nil. 2668 DESC is the short-description for the binding. 2669 BINDINGS is a list of (key def desc cond)." 2670 (declare (indent defun) 2671 (debug (form form form form form &rest sexp))) 2672 (->> (cl-list* key def desc cond bindings) 2673 (-partition 4) 2674 (-mapcat (-lambda ((key def desc cond)) 2675 `((define-key ,keymap ,key 2676 '(menu-item 2677 ,(format "maybe-%s" def) 2678 ,def 2679 :filter 2680 (lambda (item) 2681 (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer) 2682 lsp--describe-buffer) 2683 (current-buffer)) 2684 ,cond) 2685 item)))) 2686 (when (stringp ,key) 2687 (setq lsp--binding-descriptions 2688 (append lsp--binding-descriptions '(,key ,desc))))))) 2689 macroexp-progn)) 2690 2691 (defvar lsp--describe-buffer nil) 2692 2693 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus) 2694 (let ((lsp--describe-buffer buffer)) 2695 (funcall fn buffer prefix menus))) 2696 2697 (advice-add 'describe-buffer-bindings 2698 :around 2699 #'lsp-describe-buffer-bindings-advice) 2700 2701 (defun lsp--prepend-prefix (mappings) 2702 (->> mappings 2703 (-partition 2) 2704 (-mapcat (-lambda ((key description)) 2705 (list (concat lsp-keymap-prefix " " key) 2706 description))))) 2707 2708 (defvar lsp-command-map 2709 (-doto (make-sparse-keymap) 2710 (lsp-define-conditional-key 2711 ;; workspaces 2712 "wD" lsp-disconnect "disconnect" (lsp-workspaces) 2713 "wd" lsp-describe-session "describe session" t 2714 "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces) 2715 "wr" lsp-workspace-restart "restart server" (lsp-workspaces) 2716 "ws" lsp "start server" t 2717 2718 ;; formatting 2719 "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting") 2720 (lsp-feature? "textDocument/formatting")) 2721 "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting") 2722 2723 ;; folders 2724 "Fa" lsp-workspace-folders-add "add folder" t 2725 "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t 2726 "Fr" lsp-workspace-folders-remove "remove folder" t 2727 2728 ;; toggles 2729 "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature? 2730 "textDocument/publishDiagnostics") 2731 "TL" lsp-toggle-trace-io "toggle log io" t 2732 "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline) 2733 "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs) 2734 "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature? 2735 "textDocument/codeAction") 2736 "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature? 2737 "textDocument/documentSymbol") 2738 "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc) 2739 "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature? 2740 "textDocument/onTypeFormatting") 2741 "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight") 2742 "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens") 2743 "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp") 2744 2745 ;; goto 2746 "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol") 2747 "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration") 2748 "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list) 2749 "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition") 2750 "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls") 2751 (fboundp 'lsp-treemacs-call-hierarchy)) 2752 "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation") 2753 "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references") 2754 "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition") 2755 2756 ;; help 2757 "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc) 2758 (lsp-feature? "textDocument/hover")) 2759 "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover") 2760 "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp") 2761 2762 ;; refactoring 2763 "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction") 2764 "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename") 2765 2766 ;; actions 2767 "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction") 2768 "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight") 2769 "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy)) 2770 2771 ;; peeks 2772 "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition") 2773 (fboundp 'lsp-ui-peek-find-definitions)) 2774 "Gi" lsp-ui-peek-find-implementation "peek implementations" (and 2775 (fboundp 'lsp-ui-peek-find-implementation) 2776 (lsp-feature? "textDocument/implementation")) 2777 "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references) 2778 (lsp-feature? "textDocument/references")) 2779 "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp 2780 'lsp-ui-peek-find-workspace-symbol) 2781 (lsp-feature? "workspace/symbol"))))) 2782 2783 2784 ;; which-key integration 2785 2786 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key") 2787 (declare-function which-key-add-key-based-replacements "ext:which-key") 2788 2789 (defun lsp-enable-which-key-integration (&optional all-modes) 2790 "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current 2791 active `major-mode', or for all major modes when ALL-MODES is t." 2792 (cl-flet ((which-key-fn (if all-modes 2793 'which-key-add-key-based-replacements 2794 (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode)))) 2795 (apply 2796 #'which-key-fn 2797 (lsp--prepend-prefix 2798 (cl-list* 2799 "" "lsp" 2800 "w" "workspaces" 2801 "F" "folders" 2802 "=" "formatting" 2803 "T" "toggle" 2804 "g" "goto" 2805 "h" "help" 2806 "r" "refactor" 2807 "a" "code actions" 2808 "G" "peek" 2809 lsp--binding-descriptions))))) 2810 2811 2812 ;; Globbing syntax 2813 2814 ;; We port VSCode's glob-to-regexp code 2815 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts) 2816 ;; since the LSP globbing syntax seems to be the same as that of 2817 ;; VSCode. 2818 2819 (defconst lsp-globstar "**" 2820 "Globstar pattern.") 2821 2822 (defconst lsp-glob-split ?/ 2823 "The character by which we split path components in a glob 2824 pattern.") 2825 2826 (defconst lsp-path-regexp "[/\\\\]" 2827 "Forward or backslash to be used as a path separator in 2828 computed regexps.") 2829 2830 (defconst lsp-non-path-regexp "[^/\\\\]" 2831 "A regexp matching anything other than a slash.") 2832 2833 (defconst lsp-globstar-regexp 2834 (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?" 2835 lsp-path-regexp 2836 lsp-non-path-regexp lsp-path-regexp 2837 lsp-path-regexp lsp-non-path-regexp) 2838 "Globstar in regexp form.") 2839 2840 (defun lsp-split-glob-pattern (pattern split-char) 2841 "Split PATTERN at SPLIT-CHAR while respecting braces and brackets." 2842 (when pattern 2843 (let ((segments nil) 2844 (in-braces nil) 2845 (in-brackets nil) 2846 (current-segment "")) 2847 (dolist (char (string-to-list pattern)) 2848 (cl-block 'exit-point 2849 (if (eq char split-char) 2850 (when (and (null in-braces) 2851 (null in-brackets)) 2852 (push current-segment segments) 2853 (setq current-segment "") 2854 (cl-return-from 'exit-point)) 2855 (pcase char 2856 (?{ 2857 (setq in-braces t)) 2858 (?} 2859 (setq in-braces nil)) 2860 (?\[ 2861 (setq in-brackets t)) 2862 (?\] 2863 (setq in-brackets nil)))) 2864 (setq current-segment (concat current-segment 2865 (char-to-string char))))) 2866 (unless (string-empty-p current-segment) 2867 (push current-segment segments)) 2868 (nreverse segments)))) 2869 2870 (defun lsp--glob-to-regexp (pattern) 2871 "Helper function to convert a PATTERN from LSP's glob syntax to 2872 an Elisp regexp." 2873 (if (string-empty-p pattern) 2874 "" 2875 (let ((current-regexp "") 2876 (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split))) 2877 (if (-all? (lambda (segment) (eq segment lsp-globstar)) 2878 glob-segments) 2879 ".*" 2880 (let ((prev-segment-was-globstar nil)) 2881 (seq-do-indexed 2882 (lambda (segment index) 2883 (if (string-equal segment lsp-globstar) 2884 (unless prev-segment-was-globstar 2885 (setq current-regexp (concat current-regexp 2886 lsp-globstar-regexp)) 2887 (setq prev-segment-was-globstar t)) 2888 (let ((in-braces nil) 2889 (brace-val "") 2890 (in-brackets nil) 2891 (bracket-val "")) 2892 (dolist (char (string-to-list segment)) 2893 (cond 2894 ((and (not (char-equal char ?\})) 2895 in-braces) 2896 (setq brace-val (concat brace-val 2897 (char-to-string char)))) 2898 ((and in-brackets 2899 (or (not (char-equal char ?\])) 2900 (string-empty-p bracket-val))) 2901 (let ((curr (cond 2902 ((char-equal char ?-) 2903 "-") 2904 ;; NOTE: ?\^ and ?^ are different characters 2905 ((and (memq char '(?^ ?!)) 2906 (string-empty-p bracket-val)) 2907 "^") 2908 ((char-equal char lsp-glob-split) 2909 "") 2910 (t 2911 (regexp-quote (char-to-string char)))))) 2912 (setq bracket-val (concat bracket-val curr)))) 2913 (t 2914 (cl-case char 2915 (?{ 2916 (setq in-braces t)) 2917 (?\[ 2918 (setq in-brackets t)) 2919 (?} 2920 (let* ((choices (lsp-split-glob-pattern brace-val ?\,)) 2921 (brace-regexp (concat "\\(?:" 2922 (mapconcat #'lsp--glob-to-regexp choices "\\|") 2923 "\\)"))) 2924 (setq current-regexp (concat current-regexp 2925 brace-regexp)) 2926 (setq in-braces nil) 2927 (setq brace-val ""))) 2928 (?\] 2929 (setq current-regexp 2930 (concat current-regexp 2931 "[" bracket-val "]")) 2932 (setq in-brackets nil) 2933 (setq bracket-val "")) 2934 (?? 2935 (setq current-regexp 2936 (concat current-regexp 2937 lsp-non-path-regexp))) 2938 (?* 2939 (setq current-regexp 2940 (concat current-regexp 2941 lsp-non-path-regexp "*?"))) 2942 (t 2943 (setq current-regexp 2944 (concat current-regexp 2945 (regexp-quote (char-to-string char))))))))) 2946 (when (and (< index (1- (length glob-segments))) 2947 (or (not (string-equal (nth (1+ index) glob-segments) 2948 lsp-globstar)) 2949 (< (+ index 2) 2950 (length glob-segments)))) 2951 (setq current-regexp 2952 (concat current-regexp 2953 lsp-path-regexp))) 2954 (setq prev-segment-was-globstar nil)))) 2955 glob-segments) 2956 current-regexp))))) 2957 2958 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365 2959 (defun lsp-glob-unbrace-at-top-level (glob-pattern) 2960 "If GLOB-PATTERN does not start with a brace, return a singleton list 2961 containing GLOB-PATTERN. 2962 2963 If GLOB-PATTERN does start with a brace, return a list of the 2964 comma-separated globs within the top-level braces." 2965 (if (not (string-prefix-p "{" glob-pattern)) 2966 (list glob-pattern) 2967 (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,))) 2968 2969 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern) 2970 "Convert GLOB-PATTERN to a regexp wrapped with the beginning- 2971 and end-of-string meta-characters." 2972 (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'")) 2973 2974 (defun lsp-glob-to-regexps (glob-pattern) 2975 "Convert a GLOB-PATTERN to a list of Elisp regexps." 2976 (when-let* 2977 ((glob-pattern (cond ((hash-table-p glob-pattern) 2978 (ht-get glob-pattern "pattern")) 2979 ((stringp glob-pattern) glob-pattern) 2980 (t (error "Unknown glob-pattern type: %s" glob-pattern)))) 2981 (trimmed-pattern (string-trim glob-pattern)) 2982 (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern))) 2983 (seq-map #'lsp-glob-convert-to-wrapped-regexp 2984 top-level-unbraced-patterns))) 2985 2986 2987 2988 (defvar lsp-mode-menu) 2989 2990 (defun lsp-mouse-click (event) 2991 (interactive "e") 2992 (let* ((ec (event-start event)) 2993 (choice (x-popup-menu event lsp-mode-menu)) 2994 (action (lookup-key lsp-mode-menu (apply 'vector choice)))) 2995 2996 (select-window (posn-window ec)) 2997 2998 (unless (and (region-active-p) (eq action 'lsp-execute-code-action)) 2999 (goto-char (posn-point ec))) 3000 (run-with-idle-timer 3001 0.001 nil 3002 (lambda () 3003 (cl-labels ((check (value) (not (null value)))) 3004 (when choice 3005 (call-interactively action))))))) 3006 3007 (defvar lsp-mode-map 3008 (let ((map (make-sparse-keymap))) 3009 (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse) 3010 (define-key map (kbd "C-<mouse-1>") #'ignore) 3011 (define-key map (kbd "<mouse-3>") #'lsp-mouse-click) 3012 (define-key map (kbd "C-S-SPC") #'lsp-signature-activate) 3013 (when lsp-keymap-prefix 3014 (define-key map (kbd lsp-keymap-prefix) lsp-command-map)) 3015 map) 3016 "Keymap for `lsp-mode'.") 3017 3018 (define-minor-mode lsp-mode "Mode for LSP interaction." 3019 :keymap lsp-mode-map 3020 :lighter 3021 (" LSP[" 3022 (lsp--buffer-workspaces 3023 (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "][")) 3024 (:propertize "Disconnected" face warning)) 3025 "]") 3026 :group 'lsp-mode 3027 (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred)) 3028 ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp' 3029 (lsp))) 3030 3031 (defvar lsp-mode-menu 3032 (easy-menu-create-menu 3033 nil 3034 `(["Go to definition" lsp-find-definition 3035 :active (lsp-feature? "textDocument/definition")] 3036 ["Find references" lsp-find-references 3037 :active (lsp-feature? "textDocument/references")] 3038 ["Find implementations" lsp-find-implementation 3039 :active (lsp-feature? "textDocument/implementation")] 3040 ["Find declarations" lsp-find-declaration 3041 :active (lsp-feature? "textDocument/declaration")] 3042 ["Go to type declaration" lsp-find-type-definition 3043 :active (lsp-feature? "textDocument/typeDefinition")] 3044 "--" 3045 ["Describe" lsp-describe-thing-at-point] 3046 ["Code action" lsp-execute-code-action] 3047 ["Format" lsp-format-buffer] 3048 ["Highlight references" lsp-document-highlight] 3049 ["Type Hierarchy" lsp-java-type-hierarchy 3050 :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")] 3051 ["Type Hierarchy" lsp-treemacs-type-hierarchy 3052 :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")) 3053 (functionp 'lsp-treemacs-type-hierarchy) 3054 (lsp-feature? "textDocument/typeHierarchy"))] 3055 ["Call Hierarchy" lsp-treemacs-call-hierarchy 3056 :visible (and (functionp 'lsp-treemacs-call-hierarchy) 3057 (lsp-feature? "textDocument/callHierarchy"))] 3058 ["Rename" lsp-rename 3059 :active (lsp-feature? "textDocument/rename")] 3060 "--" 3061 ("Session" 3062 ["View logs" lsp-workspace-show-log] 3063 ["Describe" lsp-describe-session] 3064 ["Shutdown" lsp-shutdown-workspace] 3065 ["Restart" lsp-restart-workspace]) 3066 ("Workspace Folders" 3067 ["Add" lsp-workspace-folders-add] 3068 ["Remove" lsp-workspace-folders-remove] 3069 ["Open" lsp-workspace-folders-open]) 3070 ("Toggle features" 3071 ["Lenses" lsp-lens-mode] 3072 ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode] 3073 ["Modeline code actions" lsp-modeline-code-actions-mode] 3074 ["Modeline diagnostics" lsp-modeline-diagnostics-mode]) 3075 "---" 3076 ("Debug" 3077 :active (bound-and-true-p dap-ui-mode) 3078 :filter ,(lambda (_) 3079 (and (boundp 'dap-ui-menu-items) 3080 (nthcdr 3 dap-ui-menu-items)))))) 3081 "Menu for lsp-mode.") 3082 3083 (defalias 'make-lsp-client 'make-lsp--client) 3084 3085 (cl-defstruct lsp--registered-capability 3086 (id "") 3087 (method " ") 3088 (options nil)) 3089 3090 ;; A ‘lsp--workspace’ object represents exactly one language server process. 3091 (cl-defstruct lsp--workspace 3092 ;; the `ewoc' object for displaying I/O to and from the server 3093 (ewoc nil) 3094 3095 ;; ‘server-capabilities’ is a hash table of the language server capabilities. 3096 ;; It is the hash table representation of a LSP ServerCapabilities structure; 3097 ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize. 3098 (server-capabilities nil) 3099 3100 ;; ‘registered-server-capabilities’ is a list of hash tables that represent 3101 ;; dynamically-registered Registration objects. See 3102 ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability. 3103 (registered-server-capabilities nil) 3104 3105 ;; ‘root’ is a directory name or a directory file name for the workspace 3106 ;; root. ‘lsp-mode’ passes this directory to the ‘initialize’ method of the 3107 ;; language server; see 3108 ;; https://microsoft.github.io/language-server-protocol/specification#initialize. 3109 (root nil) 3110 3111 ;; ‘client’ is the ‘lsp--client’ object associated with this workspace. 3112 (client nil) 3113 3114 ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It 3115 ;; used to derive the file path in `lsp--uri-to-path' when using tramp 3116 ;; connection. 3117 (host-root nil) 3118 3119 ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or 3120 ;; a network connection. ‘lsp-mode’ communicates with ‘proc’ using the 3121 ;; language server protocol. ‘proc’ corresponds to the COMMUNICATION-PROCESS 3122 ;; element of the return value of the client’s ‘get-root’ field, which see. 3123 (proc nil) 3124 3125 ;; ‘proc’ is a process object; it must represent a regular process, not a 3126 ;; pipe or network process. It represents the actual server process that 3127 ;; corresponds to this workspace. ‘cmd-proc’ corresponds to the 3128 ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’ 3129 ;; field, which see. 3130 (cmd-proc nil) 3131 3132 ;; ‘buffers’ is a list of buffers associated with this workspace. 3133 (buffers nil) 3134 3135 ;; if semantic tokens is enabled, `semantic-tokens-faces' contains 3136 ;; one face (or nil) for each token type supported by the language server. 3137 (semantic-tokens-faces nil) 3138 3139 ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces' 3140 ;; contains one face (or nil) for each modifier type supported by the language 3141 ;; server 3142 (semantic-tokens-modifier-faces nil) 3143 3144 ;; Extra client capabilities provided by third-party packages using 3145 ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME 3146 ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name, 3147 ;; and CAPS is either a plist of the client capabilities, or a function that 3148 ;; takes no argument and returns a plist of the client capabilities or nil. 3149 (extra-client-capabilities nil) 3150 3151 ;; Workspace status 3152 (status nil) 3153 3154 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3155 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3156 (metadata (make-hash-table :test 'equal)) 3157 3158 ;; contains all the file notification watches that have been created for the 3159 ;; current workspace in format filePath->file notification handle. 3160 (watches (make-hash-table :test 'equal)) 3161 3162 ;; list of workspace folders 3163 (workspace-folders nil) 3164 3165 ;; ‘last-id’ the last request id for the current workspace. 3166 (last-id 0) 3167 3168 ;; ‘status-string’ allows extensions to specify custom status string based on 3169 ;; the Language Server specific messages. 3170 (status-string nil) 3171 3172 ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it 3173 ;; was stopped). 3174 shutdown-action 3175 3176 ;; ‘diagnostics’ a hashmap with workspace diagnostics. 3177 (diagnostics (make-hash-table :test 'equal)) 3178 3179 ;; contains all the workDone progress tokens that have been created 3180 ;; for the current workspace. 3181 (work-done-tokens (make-hash-table :test 'equal))) 3182 3183 3184 (cl-defstruct lsp-session 3185 ;; contains the folders that are part of the current session 3186 folders 3187 ;; contains the folders that must not be imported in the current workspace. 3188 folders-blocklist 3189 ;; contains the list of folders that must be imported in a project in case of 3190 ;; multi root LSP server. 3191 (server-id->folders (make-hash-table :test 'equal)) 3192 ;; folder to list of the servers that are associated with the folder. 3193 (folder->servers (make-hash-table :test 'equal)) 3194 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3195 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3196 (metadata (make-hash-table :test 'equal))) 3197 3198 (defun lsp-workspace-status (status-string &optional workspace) 3199 "Set current workspace status to STATUS-STRING. 3200 If WORKSPACE is not specified defaults to lsp--cur-workspace." 3201 (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string)))) 3202 (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string))) 3203 3204 (defun lsp-session-set-metadata (key value &optional _workspace) 3205 "Associate KEY with VALUE in the WORKSPACE metadata. 3206 If WORKSPACE is not provided current workspace will be used." 3207 (puthash key value (lsp-session-metadata (lsp-session)))) 3208 3209 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata) 3210 3211 (defun lsp-session-get-metadata (key &optional _workspace) 3212 "Lookup KEY in WORKSPACE metadata. 3213 If WORKSPACE is not provided current workspace will be used." 3214 (gethash key (lsp-session-metadata (lsp-session)))) 3215 3216 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata) 3217 3218 (defun lsp-workspace-set-work-done-token (token value workspace) 3219 "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens." 3220 (puthash token value (lsp--workspace-work-done-tokens workspace))) 3221 3222 (defun lsp-workspace-get-work-done-token (token workspace) 3223 "Lookup TOKEN in the WORKSPACE work-done-tokens." 3224 (gethash token (lsp--workspace-work-done-tokens workspace))) 3225 3226 (defun lsp-workspace-rem-work-done-token (token workspace) 3227 "Remove TOKEN from the WORKSPACE work-done-tokens." 3228 (remhash token (lsp--workspace-work-done-tokens workspace))) 3229 3230 3231 (defun lsp--make-notification (method &optional params) 3232 "Create notification body for method METHOD and parameters PARAMS." 3233 (list :jsonrpc "2.0" :method method :params params)) 3234 3235 (defalias 'lsp--make-request 'lsp--make-notification) 3236 (defalias 'lsp-make-request 'lsp--make-notification) 3237 3238 (defun lsp--make-response (id result) 3239 "Create response for REQUEST with RESULT." 3240 `(:jsonrpc "2.0" :id ,id :result ,result)) 3241 3242 (defun lsp-make-notification (method &optional params) 3243 "Create notification body for method METHOD and parameters PARAMS." 3244 (lsp--make-notification method params)) 3245 3246 (defmacro lsp--json-serialize (params) 3247 (if (progn 3248 (require 'json) 3249 (fboundp 'json-serialize)) 3250 `(json-serialize ,params 3251 :null-object nil 3252 :false-object :json-false) 3253 `(let ((json-false :json-false)) 3254 (json-encode ,params)))) 3255 3256 (defun lsp--make-message (params) 3257 "Create a LSP message from PARAMS, after encoding it to a JSON string." 3258 (let ((body (lsp--json-serialize params))) 3259 (concat "Content-Length: " 3260 (number-to-string (1+ (string-bytes body))) 3261 "\r\n\r\n" 3262 body 3263 "\n"))) 3264 3265 (cl-defstruct lsp--log-entry timestamp process-time type method id body) 3266 3267 (defun lsp--make-log-entry (method id body type &optional process-time) 3268 "Create an outgoing log object from BODY with method METHOD and id ID. 3269 If ID is non-nil, then the body is assumed to be a notification. 3270 TYPE can either be `incoming' or `outgoing'" 3271 (cl-assert (memq type '(incoming-req outgoing-req incoming-notif 3272 outgoing-notif incoming-resp 3273 outgoing-resp))) 3274 (make-lsp--log-entry 3275 :timestamp (format-time-string "%I:%M:%S %p") 3276 :process-time process-time 3277 :method method 3278 :id id 3279 :type type 3280 :body body)) 3281 3282 (defun lsp--log-font-lock-json (body) 3283 "Font lock JSON BODY." 3284 (with-temp-buffer 3285 (insert body) 3286 ;; We set the temp buffer file-name extension to .json and call `set-auto-mode' 3287 ;; so the users configured json mode is used which could be 3288 ;; `json-mode', `json-ts-mode', `jsonian-mode', etc. 3289 (let ((buffer-file-name "lsp-log.json")) 3290 (delay-mode-hooks 3291 (set-auto-mode) 3292 (if (fboundp 'font-lock-ensure) 3293 (font-lock-ensure) 3294 (with-no-warnings 3295 (font-lock-fontify-buffer))))) 3296 (buffer-string))) 3297 3298 (defun lsp--log-entry-pp (entry) 3299 (cl-assert (lsp--log-entry-p entry)) 3300 (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time 3301 body) 3302 entry) 3303 (json-false :json-false) 3304 (json-encoding-pretty-print t) 3305 (str nil)) 3306 (setq str 3307 (concat (format "[Trace - %s] " timestamp) 3308 (pcase type 3309 ('incoming-req (format "Received request '%s - (%s)." method id)) 3310 ('outgoing-req (format "Sending request '%s - (%s)'." method id)) 3311 3312 ('incoming-notif (format "Received notification '%s'." method)) 3313 ('outgoing-notif (format "Sending notification '%s'." method)) 3314 3315 ('incoming-resp (format "Received response '%s - (%s)' in %dms." 3316 method id process-time)) 3317 ('outgoing-resp 3318 (format 3319 "Sending response '%s - (%s)'. Processing request took %dms" 3320 method id process-time))) 3321 "\n" 3322 (if (memq type '(incoming-resp ougoing-resp)) 3323 "Result: " 3324 "Params: ") 3325 (lsp--log-font-lock-json (json-encode body)) 3326 "\n\n\n")) 3327 (setq str (propertize str 'mouse-face 'highlight 'read-only t)) 3328 (insert str))) 3329 3330 (defvar-local lsp--log-io-ewoc nil) 3331 3332 (defun lsp--get-create-io-ewoc (workspace) 3333 (if (and (lsp--workspace-ewoc workspace) 3334 (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace)))) 3335 (lsp--workspace-ewoc workspace) 3336 (with-current-buffer (lsp--get-log-buffer-create workspace) 3337 (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode)) 3338 (setq-local window-point-insertion-type t) 3339 (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t)) 3340 (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc)) 3341 (lsp--workspace-ewoc workspace))) 3342 3343 (defun lsp--ewoc-count (ewoc) 3344 (let* ((count 0) 3345 (count-fn (lambda (_) (setq count (1+ count))))) 3346 (ewoc-map count-fn ewoc) 3347 count)) 3348 3349 (defun lsp--log-entry-new (entry workspace) 3350 (let* ((ewoc (lsp--get-create-io-ewoc workspace)) 3351 (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc))) 3352 (node (if (or (eq lsp-io-messages-max t) 3353 (>= lsp-io-messages-max count)) 3354 nil 3355 (ewoc-nth ewoc (1- lsp-io-messages-max)))) 3356 (prev nil) 3357 (inhibit-read-only t)) 3358 (while node 3359 (setq prev (ewoc-prev ewoc node)) 3360 (ewoc-delete ewoc node) 3361 (setq node prev)) 3362 (ewoc-enter-last ewoc entry))) 3363 3364 (defun lsp--send-notification (body) 3365 "Send BODY as a notification to the language server." 3366 (lsp-foreach-workspace 3367 (when (lsp--log-io-p (plist-get body :method)) 3368 (lsp--log-entry-new (lsp--make-log-entry 3369 (plist-get body :method) 3370 nil (plist-get body :params) 'outgoing-notif) 3371 lsp--cur-workspace)) 3372 (lsp--send-no-wait body 3373 (lsp--workspace-proc lsp--cur-workspace)))) 3374 3375 (defalias 'lsp-send-notification 'lsp--send-notification) 3376 3377 (defun lsp-notify (method params) 3378 "Send notification METHOD with PARAMS." 3379 (lsp--send-notification (lsp--make-notification method params))) 3380 3381 (defun lsp--cur-workspace-check () 3382 "Check whether buffer lsp workspace(s) are set." 3383 (cl-assert (lsp-workspaces) nil 3384 "No language server(s) is associated with this buffer.")) 3385 3386 (defun lsp--send-request (body &optional no-wait no-merge) 3387 "Send BODY as a request to the language server, get the response. 3388 If NO-WAIT is non-nil, don't synchronously wait for a response. 3389 If NO-MERGE is non-nil, don't merge the results but return an 3390 alist mapping workspace->result." 3391 (lsp-request (plist-get body :method) 3392 (plist-get body :params) 3393 :no-wait no-wait 3394 :no-merge no-merge)) 3395 3396 (defalias 'lsp-send-request 'lsp--send-request 3397 "Send BODY as a request to the language server and return the response 3398 synchronously. 3399 \n(fn BODY)") 3400 3401 (cl-defun lsp-request (method params &key no-wait no-merge) 3402 "Send request METHOD with PARAMS. 3403 If NO-MERGE is non-nil, don't merge the results but return alist 3404 workspace->result. 3405 If NO-WAIT is non-nil send the request as notification." 3406 (if no-wait 3407 (lsp-notify method params) 3408 (let* ((send-time (float-time)) 3409 ;; max time by which we must get a response 3410 (expected-time 3411 (and 3412 lsp-response-timeout 3413 (+ send-time lsp-response-timeout))) 3414 resp-result resp-error done?) 3415 (unwind-protect 3416 (progn 3417 (lsp-request-async method params 3418 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3419 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3420 :no-merge no-merge 3421 :mode 'detached 3422 :cancel-token :sync-request) 3423 (while (not (or resp-error resp-result)) 3424 (if (functionp 'json-rpc-connection) 3425 (catch 'lsp-done (sit-for 0.01)) 3426 (catch 'lsp-done 3427 (accept-process-output 3428 nil 3429 (if expected-time (- expected-time send-time) 1)))) 3430 (setq send-time (float-time)) 3431 (when (and expected-time (< expected-time send-time)) 3432 (error "Timeout while waiting for response. Method: %s" method))) 3433 (setq done? t) 3434 (cond 3435 ((eq resp-result :finished) nil) 3436 (resp-result resp-result) 3437 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3438 ((lsp-json-error? (cl-first resp-error)) 3439 (error (lsp:json-error-message (cl-first resp-error)))))) 3440 (unless done? 3441 (lsp-cancel-request-by-token :sync-request)))))) 3442 3443 (cl-defun lsp-request-while-no-input (method params) 3444 "Send request METHOD with PARAMS and waits until there is no input. 3445 Return same value as `lsp--while-no-input' and respecting `non-essential'." 3446 (if (or non-essential (not lsp-request-while-no-input-may-block)) 3447 (let* ((send-time (float-time)) 3448 ;; max time by which we must get a response 3449 (expected-time 3450 (and 3451 lsp-response-timeout 3452 (+ send-time lsp-response-timeout))) 3453 resp-result resp-error done?) 3454 (unwind-protect 3455 (progn 3456 (lsp-request-async method params 3457 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3458 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3459 :mode 'detached 3460 :cancel-token :sync-request) 3461 (while (not (or resp-error resp-result (input-pending-p))) 3462 (catch 'lsp-done 3463 (sit-for 3464 (if expected-time (- expected-time send-time) 1))) 3465 (setq send-time (float-time)) 3466 (when (and expected-time (< expected-time send-time)) 3467 (error "Timeout while waiting for response. Method: %s" method))) 3468 (setq done? (or resp-error resp-result)) 3469 (cond 3470 ((eq resp-result :finished) nil) 3471 (resp-result resp-result) 3472 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3473 ((lsp-json-error? (cl-first resp-error)) 3474 (error (lsp:json-error-message (cl-first resp-error)))))) 3475 (unless done? 3476 (lsp-cancel-request-by-token :sync-request)) 3477 (when (and (input-pending-p) lsp--throw-on-input) 3478 (throw 'input :interrupted)))) 3479 (lsp-request method params))) 3480 3481 (defvar lsp--cancelable-requests (ht)) 3482 3483 (cl-defun lsp-request-async (method params callback 3484 &key mode error-handler cancel-handler no-merge cancel-token) 3485 "Send METHOD with PARAMS as a request to the language server. 3486 Call CALLBACK with the response received from the server 3487 asynchronously. 3488 MODE determines when the callback will be called depending on the 3489 condition of the original buffer. It could be: 3490 - `detached' which means that the callback will be executed no 3491 matter what has happened to the buffer. 3492 - `alive' - the callback will be executed only if the buffer from 3493 which the call was executed is still alive. 3494 - `current' the callback will be executed only if the original buffer 3495 is still selected. 3496 - `tick' - the callback will be executed only if the buffer was not modified. 3497 - `unchanged' - the callback will be executed only if the buffer hasn't 3498 changed and if the buffer is not modified. 3499 3500 ERROR-HANDLER will be called in case the request has failed. 3501 CANCEL-HANDLER will be called in case the request is being canceled. 3502 If NO-MERGE is non-nil, don't merge the results but return alist 3503 workspace->result. 3504 CANCEL-TOKEN is the token that can be used to cancel request." 3505 (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params) 3506 callback mode error-handler cancel-handler no-merge cancel-token)) 3507 3508 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback) 3509 (lambda (&rest _) 3510 (unless (and (equal 'post-command-hook hook) 3511 (equal (current-buffer) buf)) 3512 (lsp--request-cleanup-hooks id) 3513 (with-lsp-workspaces workspaces 3514 (lsp--cancel-request id) 3515 (when cancel-callback (funcall cancel-callback))) 3516 (lsp-log "Cancelling %s(%s) in hook %s" method id hook)))) 3517 3518 (defun lsp--create-async-callback 3519 (callback method no-merge workspaces) 3520 "Create async handler expecting COUNT results, merge them and call CALLBACK. 3521 MODE determines when the callback will be called depending on the 3522 condition of the original buffer. METHOD is the invoked method. 3523 If NO-MERGE is non-nil, don't merge the results but return alist 3524 workspace->result. ID is the request id." 3525 (let (results errors) 3526 (lambda (result) 3527 (push (cons lsp--cur-workspace result) 3528 (if (eq result :error) errors results)) 3529 (when (and (not (eq (length errors) (length workspaces))) 3530 (eq (+ (length errors) (length results)) (length workspaces))) 3531 (funcall callback 3532 (if no-merge 3533 results 3534 (lsp--merge-results (-map #'cl-rest results) method))))))) 3535 3536 (defcustom lsp-default-create-error-handler-fn nil 3537 "Default error handler customization. 3538 Handler should give METHOD as argument and return function of one argument 3539 ERROR." 3540 :type 'function 3541 :group 'lsp-mode 3542 :package-version '(lsp-mode . "9.0.0")) 3543 3544 (defun lsp--create-default-error-handler (method) 3545 "Default error handler. 3546 METHOD is the executed method." 3547 (if lsp-default-create-error-handler-fn 3548 (funcall lsp-default-create-error-handler-fn method) 3549 (lambda (error) 3550 (lsp--warn "%s" (or (lsp--error-string error) 3551 (format "%s Request has failed" method)))))) 3552 3553 (defvar lsp--request-cleanup-hooks (ht)) 3554 3555 (defun lsp--request-cleanup-hooks (request-id) 3556 (when-let* ((cleanup-function (gethash request-id lsp--request-cleanup-hooks))) 3557 (funcall cleanup-function) 3558 (remhash request-id lsp--request-cleanup-hooks))) 3559 3560 (defun lsp-cancel-request-by-token (cancel-token) 3561 "Cancel request using CANCEL-TOKEN." 3562 (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests)) 3563 (with-lsp-workspaces workspaces 3564 (lsp--cancel-request request-id)) 3565 (remhash cancel-token lsp--cancelable-requests) 3566 (lsp--request-cleanup-hooks request-id))) 3567 3568 (defun lsp--send-request-async (body callback 3569 &optional mode error-callback cancel-callback 3570 no-merge cancel-token) 3571 "Send BODY as a request to the language server. 3572 Call CALLBACK with the response received from the server 3573 asynchronously. 3574 MODE determines when the callback will be called depending on the 3575 condition of the original buffer. It could be: 3576 - `detached' which means that the callback will be executed no 3577 matter what has happened to the buffer. 3578 - `alive' - the callback will be executed only if the buffer from 3579 which the call was executed is still alive. 3580 - `current' the callback will be executed only if the original buffer 3581 is still selected. 3582 - `tick' - the callback will be executed only if the buffer was not modified. 3583 - `unchanged' - the callback will be executed only if the buffer hasn't 3584 changed and if the buffer is not modified. 3585 3586 ERROR-CALLBACK will be called in case the request has failed. 3587 CANCEL-CALLBACK will be called in case the request is being canceled. 3588 If NO-MERGE is non-nil, don't merge the results but return alist 3589 workspace->result. 3590 CANCEL-TOKEN is the token that can be used to cancel request." 3591 (when cancel-token 3592 (lsp-cancel-request-by-token cancel-token)) 3593 3594 (if-let* ((target-workspaces (lsp--find-workspaces-for body))) 3595 (let* ((start-time (current-time)) 3596 (method (plist-get body :method)) 3597 (id (cl-incf lsp-last-id)) 3598 (buf (current-buffer)) 3599 (cancel-callback (when cancel-callback 3600 (pcase mode 3601 ((or 'alive 'tick 'unchanged) 3602 (lambda () 3603 (with-current-buffer buf 3604 (funcall cancel-callback)))) 3605 (_ cancel-callback)))) 3606 ;; calculate what are the (hook . local) pairs which will cancel 3607 ;; the request 3608 (hooks (pcase mode 3609 ('alive '((kill-buffer-hook . t))) 3610 ('tick '((kill-buffer-hook . t) (after-change-functions . t))) 3611 ('unchanged '((after-change-functions . t) (post-command-hook . nil))) 3612 ('current '((post-command-hook . nil))))) 3613 ;; note: lambdas in emacs can be compared but we should make sure 3614 ;; that all of the captured arguments are the same - in our case 3615 ;; `lsp--create-request-cancel' will return the same lambda when 3616 ;; called with the same params. 3617 (cleanup-hooks 3618 (lambda () (mapc 3619 (-lambda ((hook . local)) 3620 (if local 3621 (when (buffer-live-p buf) 3622 (with-current-buffer buf 3623 (remove-hook hook 3624 (lsp--create-request-cancel 3625 id target-workspaces hook buf method cancel-callback) 3626 t))) 3627 (remove-hook hook (lsp--create-request-cancel 3628 id target-workspaces hook buf method cancel-callback)))) 3629 hooks) 3630 (remhash cancel-token lsp--cancelable-requests))) 3631 (callback (pcase mode 3632 ((or 'alive 'tick 'unchanged) (lambda (&rest args) 3633 (with-current-buffer buf 3634 (apply callback args)))) 3635 (_ callback))) 3636 (callback (lsp--create-async-callback callback 3637 method 3638 no-merge 3639 target-workspaces)) 3640 (callback (lambda (result) 3641 (lsp--request-cleanup-hooks id) 3642 (funcall callback result))) 3643 (error-callback (lsp--create-async-callback 3644 (or error-callback 3645 (lsp--create-default-error-handler method)) 3646 method 3647 nil 3648 target-workspaces)) 3649 (error-callback (lambda (error) 3650 (funcall callback :error) 3651 (lsp--request-cleanup-hooks id) 3652 (funcall error-callback error))) 3653 (body (plist-put body :id id))) 3654 3655 ;; cancel request in any of the hooks 3656 (mapc (-lambda ((hook . local)) 3657 (add-hook hook 3658 (lsp--create-request-cancel 3659 id target-workspaces hook buf method cancel-callback) 3660 nil local)) 3661 hooks) 3662 (puthash id cleanup-hooks lsp--request-cleanup-hooks) 3663 3664 (setq lsp--last-active-workspaces target-workspaces) 3665 3666 (when cancel-token 3667 (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests)) 3668 3669 (seq-doseq (workspace target-workspaces) 3670 (when (lsp--log-io-p method) 3671 (lsp--log-entry-new (lsp--make-log-entry method id 3672 (plist-get body :params) 3673 'outgoing-req) 3674 workspace)) 3675 (puthash id 3676 (list callback error-callback method start-time (current-time)) 3677 (-> workspace 3678 (lsp--workspace-client) 3679 (lsp--client-response-handlers))) 3680 (lsp--send-no-wait body (lsp--workspace-proc workspace))) 3681 body) 3682 (error "The connected server(s) does not support method %s. 3683 To find out what capabilities support your server use `M-x lsp-describe-session' 3684 and expand the capabilities section" 3685 (plist-get body :method)))) 3686 3687 ;; deprecated, use lsp-request-async. 3688 (defalias 'lsp-send-request-async 'lsp--send-request-async) 3689 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1") 3690 3691 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any 3692 ;; pending language servers. 3693 (add-hook 'kill-emacs-hook #'lsp--global-teardown) 3694 3695 (defun lsp--global-teardown () 3696 "Unload working workspaces." 3697 (lsp-foreach-workspace (lsp--shutdown-workspace))) 3698 3699 (defun lsp--shutdown-workspace (&optional restart) 3700 "Shut down the language server process for ‘lsp--cur-workspace’." 3701 (with-demoted-errors "LSP error: %S" 3702 (let ((lsp-response-timeout 0.5)) 3703 (condition-case err 3704 (lsp-request "shutdown" nil) 3705 (error (lsp--error "%s" err)))) 3706 (lsp-notify "exit" nil)) 3707 (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown)) 3708 (lsp--uninitialize-workspace)) 3709 3710 (defcustom lsp-inlay-hint-enable nil 3711 "If non-nil it will enable inlay hints." 3712 :type 'boolean 3713 :group 'lsp-mode 3714 :package-version '(lsp-mode . "9.0.0")) 3715 3716 (defun lsp--uninitialize-workspace () 3717 "Cleanup buffer state. 3718 When a workspace is shut down, by request or from just 3719 disappearing, unset all the variables related to it." 3720 (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace] 3721 (lsp-process-kill cmd-proc) 3722 (mapc (lambda (buf) 3723 (when (lsp-buffer-live-p buf) 3724 (lsp-with-current-buffer buf 3725 (lsp-managed-mode -1)))) 3726 buffers) 3727 (lsp-diagnostics--workspace-cleanup lsp--cur-workspace))) 3728 3729 (defun lsp--client-capabilities (&optional custom-capabilities) 3730 "Return the client capabilities appending CUSTOM-CAPABILITIES." 3731 (append 3732 `((general . ((positionEncodings . ["utf-32", "utf-16"]))) 3733 (workspace . ((workspaceEdit . ((documentChanges . t) 3734 (resourceOperations . ["create" "rename" "delete"]))) 3735 (applyEdit . t) 3736 (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))))) 3737 (executeCommand . ((dynamicRegistration . :json-false))) 3738 ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t))))) 3739 (workspaceFolders . t) 3740 (configuration . t) 3741 ,@(when lsp-semantic-tokens-enable 3742 `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests) 3743 lsp-semantic-tokens-honor-refresh-requests) 3744 :json-false)))))) 3745 ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t))))) 3746 ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false))))) 3747 (diagnostics . ((refreshSupport . :json-false))) 3748 (fileOperations . ((didCreate . :json-false) 3749 (willCreate . :json-false) 3750 (didRename . t) 3751 (willRename . t) 3752 (didDelete . :json-false) 3753 (willDelete . :json-false))))) 3754 (textDocument . ((declaration . ((dynamicRegistration . t) 3755 (linkSupport . t))) 3756 (definition . ((dynamicRegistration . t) 3757 (linkSupport . t))) 3758 (references . ((dynamicRegistration . t))) 3759 (implementation . ((dynamicRegistration . t) 3760 (linkSupport . t))) 3761 (typeDefinition . ((dynamicRegistration . t) 3762 (linkSupport . t))) 3763 (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t))) 3764 (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))) 3765 (hierarchicalDocumentSymbolSupport . t))) 3766 (formatting . ((dynamicRegistration . t))) 3767 (rangeFormatting . ((dynamicRegistration . t))) 3768 (onTypeFormatting . ((dynamicRegistration . t))) 3769 ,@(when (and lsp-semantic-tokens-enable 3770 (functionp 'lsp--semantic-tokens-capabilities)) 3771 (lsp--semantic-tokens-capabilities)) 3772 (rename . ((dynamicRegistration . t) (prepareSupport . t))) 3773 (codeAction . ((dynamicRegistration . t) 3774 (isPreferredSupport . t) 3775 (codeActionLiteralSupport . ((codeActionKind . ((valueSet . ["" 3776 "quickfix" 3777 "refactor" 3778 "refactor.extract" 3779 "refactor.inline" 3780 "refactor.rewrite" 3781 "source" 3782 "source.organizeImports"]))))) 3783 (resolveSupport . ((properties . ["edit" "command"]))) 3784 (dataSupport . t))) 3785 (completion . ((completionItem . ((snippetSupport . ,(cond 3786 ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode))) 3787 (lsp--warn (concat 3788 "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. " 3789 "You must either install yasnippet, or disable snippet support.")) 3790 :json-false) 3791 (lsp-enable-snippet t) 3792 (t :json-false))) 3793 (documentationFormat . ["markdown" "plaintext"]) 3794 ;; Remove this after jdtls support resolveSupport 3795 (resolveAdditionalTextEditsSupport . t) 3796 (insertReplaceSupport . t) 3797 (deprecatedSupport . t) 3798 (resolveSupport 3799 . ((properties . ["documentation" 3800 "detail" 3801 "additionalTextEdits" 3802 "command" 3803 "insertTextFormat" 3804 "insertTextMode"]))) 3805 (insertTextModeSupport . ((valueSet . [1 2]))))) 3806 (contextSupport . t) 3807 (dynamicRegistration . t))) 3808 (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t))))) 3809 (dynamicRegistration . t))) 3810 (documentLink . ((dynamicRegistration . t) 3811 (tooltipSupport . t))) 3812 (hover . ((contentFormat . ["markdown" "plaintext"]) 3813 (dynamicRegistration . t))) 3814 ,@(when lsp-enable-folding 3815 `((foldingRange . ((dynamicRegistration . t) 3816 ,@(when lsp-folding-range-limit 3817 `((rangeLimit . ,lsp-folding-range-limit))) 3818 ,@(when lsp-folding-line-folding-only 3819 `((lineFoldingOnly . t))))))) 3820 (selectionRange . ((dynamicRegistration . t))) 3821 (callHierarchy . ((dynamicRegistration . :json-false))) 3822 (typeHierarchy . ((dynamicRegistration . t))) 3823 (publishDiagnostics . ((relatedInformation . t) 3824 (tagSupport . ((valueSet . [1 2]))) 3825 (versionSupport . t))) 3826 (diagnostic . ((dynamicRegistration . :json-false) 3827 (relatedDocumentSupport . :json-false))) 3828 (linkedEditingRange . ((dynamicRegistration . t))))) 3829 (window . ((workDoneProgress . t) 3830 (showDocument . ((support . t)))))) 3831 custom-capabilities)) 3832 3833 (defun lsp-find-roots-for-workspace (workspace session) 3834 "Get all roots for the WORKSPACE." 3835 (-filter #'identity (ht-map (lambda (folder workspaces) 3836 (when (-contains? workspaces workspace) 3837 folder)) 3838 (lsp-session-folder->servers session)))) 3839 3840 (defun lsp-session-watches (&optional session) 3841 "Get watches created for SESSION." 3842 (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session)))) 3843 (-let [res (make-hash-table :test 'equal)] 3844 (puthash "__watches" res (lsp-session-metadata (or session (lsp-session)))) 3845 res))) 3846 3847 (defun lsp--file-process-event (session root-folder event) 3848 "Process file event." 3849 (let* ((changed-file (cl-third event)) 3850 (rel-changed-file (f-relative changed-file root-folder)) 3851 (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type)) 3852 (bit-position (1- event-numeric-kind)) 3853 (watch-bit (ash 1 bit-position))) 3854 (->> 3855 session 3856 lsp-session-folder->servers 3857 (gethash root-folder) 3858 (seq-do (lambda (workspace) 3859 (when (->> 3860 workspace 3861 lsp--workspace-registered-server-capabilities 3862 (-any? 3863 (lambda (capability) 3864 (and 3865 (equal (lsp--registered-capability-method capability) 3866 "workspace/didChangeWatchedFiles") 3867 (->> 3868 capability 3869 lsp--registered-capability-options 3870 (lsp:did-change-watched-files-registration-options-watchers) 3871 (seq-find 3872 (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp)) 3873 (when (or (null kind?) 3874 (> (logand kind? watch-bit) 0)) 3875 (-let [regexes (or cached-regexp 3876 (let ((regexp (lsp-glob-to-regexps glob-pattern))) 3877 (lsp-put fs-watcher :_cachedRegexp regexp) 3878 regexp))] 3879 (-any? (lambda (re) 3880 (or (string-match re changed-file) 3881 (string-match re rel-changed-file))) 3882 regexes)))))))))) 3883 (with-lsp-workspace workspace 3884 (lsp-notify 3885 "workspace/didChangeWatchedFiles" 3886 `((changes . [((type . ,event-numeric-kind) 3887 (uri . ,(lsp--path-to-uri changed-file)))])))))))))) 3888 3889 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?)) 3890 "Register capability REG." 3891 (when (and lsp-enable-file-watchers 3892 (equal method "workspace/didChangeWatchedFiles")) 3893 (-let* ((created-watches (lsp-session-watches (lsp-session))) 3894 (root-folders (cl-set-difference 3895 (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session)) 3896 (ht-keys created-watches)))) 3897 ;; create watch for each root folder without such 3898 (dolist (folder root-folders) 3899 (let* ((watch (make-lsp-watch :root-directory folder)) 3900 (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder)) 3901 (ignored-files-regex-list (car ignored-things)) 3902 (ignored-directories-regex-list (cadr ignored-things))) 3903 (puthash folder watch created-watches) 3904 (lsp-watch-root-folder (file-truename folder) 3905 (-partial #'lsp--file-process-event (lsp-session) folder) 3906 ignored-files-regex-list 3907 ignored-directories-regex-list 3908 watch 3909 t))))) 3910 3911 (push 3912 (make-lsp--registered-capability :id id :method method :options register-options?) 3913 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3914 3915 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body) 3916 "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to 3917 access dir-local variables." 3918 (declare (indent 1) (debug t)) 3919 `(with-temp-buffer 3920 ;; Set the buffer's name to something under the root so that we can hack the local variables 3921 ;; This file doesn't need to exist and will not be created due to this. 3922 (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root))) 3923 (hack-local-variables) 3924 (prog1 ,@body 3925 (setq-local buffer-file-name nil)))) 3926 3927 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root) 3928 "Return a list of the form 3929 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given 3930 WORKSPACE-ROOT." 3931 ;; The intent of this function is to provide per-root workspace-level customization of the 3932 ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables. 3933 (lsp--with-workspace-temp-buffer workspace-root 3934 (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories)))) 3935 3936 3937 (defun lsp--cleanup-hanging-watches () 3938 "Cleanup watches in case there are no more workspaces that are interested 3939 in that particular folder." 3940 (let* ((session (lsp-session)) 3941 (watches (lsp-session-watches session))) 3942 (dolist (watched-folder (ht-keys watches)) 3943 (when (-none? (lambda (workspace) 3944 (with-lsp-workspace workspace 3945 (lsp--registered-capability "workspace/didChangeWatchedFiles"))) 3946 (gethash watched-folder (lsp-session-folder->servers (lsp-session)))) 3947 (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder) 3948 (lsp-kill-watch (gethash watched-folder watches)) 3949 (remhash watched-folder watches))))) 3950 3951 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method)) 3952 "Unregister capability UNREG." 3953 (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace) 3954 (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id)) 3955 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3956 (when (equal method "workspace/didChangeWatchedFiles") 3957 (lsp--cleanup-hanging-watches))) 3958 3959 (defun lsp--server-capabilities () 3960 "Return the capabilities of the language server associated with the buffer." 3961 (->> (lsp-workspaces) 3962 (-keep #'lsp--workspace-server-capabilities) 3963 (apply #'lsp-merge))) 3964 3965 (defun lsp--send-open-close-p () 3966 "Return whether open and close notifications should be sent to the server." 3967 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3968 (or (memq sync '(1 2)) 3969 (lsp:text-document-sync-options-open-close? sync)))) 3970 3971 (defun lsp--send-will-save-p () 3972 "Return whether willSave notifications should be sent to the server." 3973 (-> (lsp--server-capabilities) 3974 (lsp:server-capabilities-text-document-sync?) 3975 (lsp:text-document-sync-options-will-save?))) 3976 3977 (defun lsp--send-will-save-wait-until-p () 3978 "Return whether willSaveWaitUntil notifications should be sent to the server." 3979 (-> (lsp--server-capabilities) 3980 (lsp:server-capabilities-text-document-sync?) 3981 (lsp:text-document-sync-options-will-save-wait-until?))) 3982 3983 (defun lsp--send-did-save-p () 3984 "Return whether didSave notifications should be sent to the server." 3985 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3986 (or (memq sync '(1 2)) 3987 (lsp:text-document-sync-options-save? sync)))) 3988 3989 (defun lsp--save-include-text-p () 3990 "Return whether save notifications should include the text document's contents." 3991 (->> (lsp--server-capabilities) 3992 (lsp:server-capabilities-text-document-sync?) 3993 (lsp:text-document-sync-options-save?) 3994 (lsp:text-document-save-registration-options-include-text?))) 3995 3996 (defun lsp--send-will-rename-files-p (path) 3997 "Return whether willRenameFiles request should be sent to the server. 3998 If any filters, checks if it applies for PATH." 3999 (let* ((will-rename (-> (lsp--server-capabilities) 4000 (lsp:server-capabilities-workspace?) 4001 (lsp:workspace-server-capabilities-file-operations?) 4002 (lsp:workspace-file-operations-will-rename?))) 4003 (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list))) 4004 (and will-rename 4005 (or (seq-empty-p filters) 4006 (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob))) 4007 (-let [regexes (lsp-glob-to-regexps glob)] 4008 (and (or (not scheme?) 4009 (string-prefix-p scheme? (lsp--path-to-uri path))) 4010 (-any? (lambda (re) 4011 (string-match re path)) 4012 regexes)))) 4013 filters))))) 4014 4015 (defun lsp--send-did-rename-files-p () 4016 "Return whether didRenameFiles notification should be sent to the server." 4017 (-> (lsp--server-capabilities) 4018 (lsp:server-capabilities-workspace?) 4019 (lsp:workspace-server-capabilities-file-operations?) 4020 (lsp:workspace-file-operations-did-rename?))) 4021 4022 (declare-function project-roots "ext:project" (project) t) 4023 (declare-function project-root "ext:project" (project) t) 4024 4025 (defun lsp--suggest-project-root () 4026 "Get project root." 4027 (or 4028 (when (fboundp 'projectile-project-root) 4029 (condition-case nil 4030 (projectile-project-root) 4031 (error nil))) 4032 (when (fboundp 'project-current) 4033 (when-let* ((project (project-current))) 4034 (if (fboundp 'project-root) 4035 (project-root project) 4036 (car (with-no-warnings 4037 (project-roots project)))))) 4038 default-directory)) 4039 4040 (defun lsp--read-from-file (file) 4041 "Read FILE content." 4042 (when (file-exists-p file) 4043 (cl-first (read-from-string (f-read-text file 'utf-8))))) 4044 4045 (defun lsp--persist (file-name to-persist) 4046 "Persist TO-PERSIST in FILE-NAME. 4047 4048 This function creates the parent directories if they don't exist 4049 yet." 4050 (let ((print-length nil) 4051 (print-level nil)) 4052 ;; Create all parent directories: 4053 (make-directory (f-parent file-name) t) 4054 (f-write-text (prin1-to-string to-persist) 'utf-8 file-name))) 4055 4056 (defun lsp-workspace-folders-add (project-root) 4057 "Add PROJECT-ROOT to the list of workspace folders." 4058 (interactive 4059 (list (read-directory-name "Select folder to add: " 4060 (or (lsp--suggest-project-root) default-directory) nil t))) 4061 (cl-pushnew (lsp-f-canonical project-root) 4062 (lsp-session-folders (lsp-session)) :test 'equal) 4063 (lsp--persist-session (lsp-session)) 4064 4065 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)) 4066 4067 (defun lsp-workspace-folders-remove (project-root) 4068 "Remove PROJECT-ROOT from the list of workspace folders." 4069 (interactive (list (completing-read "Select folder to remove: " 4070 (lsp-session-folders (lsp-session)) 4071 nil t nil nil 4072 (lsp-find-session-folder (lsp-session) default-directory)))) 4073 4074 (setq project-root (lsp-f-canonical project-root)) 4075 4076 ;; send remove folder to each multiroot workspace associated with the folder 4077 (dolist (wks (->> (lsp-session) 4078 (lsp-session-folder->servers) 4079 (gethash project-root) 4080 (--filter (lsp--client-multi-root (lsp--workspace-client it))))) 4081 (with-lsp-workspace wks 4082 (lsp-notify "workspace/didChangeWorkspaceFolders" 4083 (lsp-make-did-change-workspace-folders-params 4084 :event (lsp-make-workspace-folders-change-event 4085 :removed (vector (lsp-make-workspace-folder 4086 :uri (lsp--path-to-uri project-root) 4087 :name (f-filename project-root))) 4088 :added []))))) 4089 4090 ;; turn off servers in the removed directory 4091 (let* ((session (lsp-session)) 4092 (folder->servers (lsp-session-folder->servers session)) 4093 (server-id->folders (lsp-session-server-id->folders session)) 4094 (workspaces (gethash project-root folder->servers))) 4095 4096 (remhash project-root folder->servers) 4097 4098 ;; turn off the servers without root folders 4099 (dolist (workspace workspaces) 4100 (when (--none? (-contains? it workspace) (ht-values folder->servers)) 4101 (lsp--info "Shutdown %s since folder %s is removed..." 4102 (lsp--workspace-print workspace) project-root) 4103 (with-lsp-workspace workspace (lsp--shutdown-workspace)))) 4104 4105 (setf (lsp-session-folders session) 4106 (-remove-item project-root (lsp-session-folders session))) 4107 4108 (ht-aeach (puthash key 4109 (-remove-item project-root value) 4110 server-id->folders) 4111 server-id->folders) 4112 (lsp--persist-session (lsp-session))) 4113 4114 (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root))) 4115 4116 (defun lsp-workspace-blocklist-remove (project-root) 4117 "Remove PROJECT-ROOT from the workspace blocklist." 4118 (interactive (list (completing-read "Select folder to remove:" 4119 (lsp-session-folders-blocklist (lsp-session)) 4120 nil t))) 4121 (setf (lsp-session-folders-blocklist (lsp-session)) 4122 (delete project-root 4123 (lsp-session-folders-blocklist (lsp-session)))) 4124 (lsp--persist-session (lsp-session))) 4125 4126 (define-obsolete-function-alias 'lsp-workspace-folders-switch 4127 'lsp-workspace-folders-open "lsp-mode 6.1") 4128 4129 (defun lsp-workspace-folders-open (project-root) 4130 "Open the directory located at PROJECT-ROOT" 4131 (interactive (list (completing-read "Open folder: " 4132 (lsp-session-folders (lsp-session)) 4133 nil t))) 4134 (find-file project-root)) 4135 4136 (defun lsp--maybe-enable-signature-help (trigger-characters) 4137 (let ((ch last-command-event)) 4138 (when (cl-find ch trigger-characters :key #'string-to-char) 4139 (lsp-signature-activate)))) 4140 4141 (defun lsp--on-type-formatting-handler-create () 4142 (when-let* ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" ))) 4143 (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character? 4144 :first-trigger-character) provider] 4145 (lambda () 4146 (lsp--on-type-formatting first-trigger-character 4147 more-trigger-character?))))) 4148 4149 (defun lsp--update-on-type-formatting-hook (&optional cleanup?) 4150 (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create))) 4151 (cond 4152 ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?)) 4153 (add-hook 'post-self-insert-hook on-type-formatting-handler nil t)) 4154 ((or cleanup? 4155 (not lsp-enable-on-type-formatting)) 4156 (remove-hook 'post-self-insert-hook on-type-formatting-handler t))))) 4157 4158 (defun lsp--signature-help-handler-create () 4159 (-when-let ((&SignatureHelpOptions? :trigger-characters?) 4160 (lsp--capability-for-method "textDocument/signatureHelp")) 4161 (lambda () 4162 (lsp--maybe-enable-signature-help trigger-characters?)))) 4163 4164 (defun lsp--update-signature-help-hook (&optional cleanup?) 4165 (let ((signature-help-handler (lsp--signature-help-handler-create))) 4166 (cond 4167 ((and (or (equal lsp-signature-auto-activate t) 4168 (memq :on-trigger-char lsp-signature-auto-activate)) 4169 signature-help-handler 4170 (not cleanup?)) 4171 (add-hook 'post-self-insert-hook signature-help-handler nil t)) 4172 4173 ((or cleanup? 4174 (not (or (equal lsp-signature-auto-activate t) 4175 (memq :on-trigger-char lsp-signature-auto-activate)))) 4176 (remove-hook 'post-self-insert-hook signature-help-handler t))))) 4177 4178 (defun lsp--after-set-visited-file-name () 4179 (lsp-disconnect) 4180 (lsp)) 4181 4182 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27 4183 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099 4184 (defvar eldoc-documentation-default) ; CI 4185 (when (< emacs-major-version 28) 4186 (unless (boundp 'eldoc-documentation-functions) 4187 (load "eldoc" nil 'nomessage)) 4188 (when (memq (default-value 'eldoc-documentation-function) '(nil ignore)) 4189 ;; actually `eldoc-documentation-strategy', but CI was failing 4190 (setq-default eldoc-documentation-function 'eldoc-documentation-default))) 4191 4192 (define-minor-mode lsp-managed-mode 4193 "Mode for source buffers managed by lsp-mode." 4194 :lighter nil 4195 (cond 4196 (lsp-managed-mode 4197 (when (lsp-feature? "textDocument/hover") 4198 (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t) 4199 (eldoc-mode 1)) 4200 4201 (add-hook 'after-change-functions #'lsp-on-change nil t) 4202 (add-hook 'after-revert-hook #'lsp-on-revert nil t) 4203 (add-hook 'after-save-hook #'lsp-on-save nil t) 4204 (add-hook 'auto-save-hook #'lsp--on-auto-save nil t) 4205 (add-hook 'before-change-functions #'lsp-before-change nil t) 4206 (add-hook 'before-save-hook #'lsp--before-save nil t) 4207 (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t) 4208 (add-hook 'post-command-hook #'lsp--post-command nil t) 4209 4210 (lsp--update-on-type-formatting-hook) 4211 (lsp--update-signature-help-hook) 4212 4213 (when lsp-enable-xref 4214 (add-hook 'xref-backend-functions #'lsp--xref-backend nil t)) 4215 4216 (lsp-configure-buffer) 4217 4218 ;; make sure we turn off lsp-mode in case major mode changes, because major 4219 ;; mode change will wipe the buffer locals. 4220 (add-hook 'change-major-mode-hook #'lsp-disconnect nil t) 4221 (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t) 4222 4223 (let ((buffer (lsp-current-buffer))) 4224 (run-with-idle-timer 4225 0.0 nil 4226 (lambda () 4227 (when (lsp-buffer-live-p buffer) 4228 (lsp-with-current-buffer buffer 4229 (lsp--on-change-debounce buffer) 4230 (lsp--on-idle buffer))))))) 4231 (t 4232 (lsp-unconfig-buffer) 4233 4234 (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t) 4235 (remove-hook 'post-command-hook #'lsp--post-command t) 4236 (remove-hook 'after-change-functions #'lsp-on-change t) 4237 (remove-hook 'after-revert-hook #'lsp-on-revert t) 4238 (remove-hook 'after-save-hook #'lsp-on-save t) 4239 (remove-hook 'auto-save-hook #'lsp--on-auto-save t) 4240 (remove-hook 'before-change-functions #'lsp-before-change t) 4241 (remove-hook 'before-save-hook #'lsp--before-save t) 4242 (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t) 4243 4244 (lsp--update-on-type-formatting-hook :cleanup) 4245 (lsp--update-signature-help-hook :cleanup) 4246 4247 (when lsp--on-idle-timer 4248 (cancel-timer lsp--on-idle-timer) 4249 (setq lsp--on-idle-timer nil)) 4250 4251 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4252 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4253 4254 (lsp--remove-overlays 'lsp-highlight) 4255 (lsp--remove-overlays 'lsp-links) 4256 4257 (remove-hook 'xref-backend-functions #'lsp--xref-backend t) 4258 (remove-hook 'change-major-mode-hook #'lsp-disconnect t) 4259 (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t) 4260 (setq-local lsp-buffer-uri nil)))) 4261 4262 (defun lsp-configure-buffer () 4263 "Configure LSP features for current buffer." 4264 ;; make sure the core is running in the context of all available workspaces 4265 ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context 4266 (let ((lsp--buffer-workspaces (cond 4267 (lsp--buffer-workspaces) 4268 (lsp--cur-workspace (list lsp--cur-workspace)))) 4269 lsp--cur-workspace) 4270 (when lsp-auto-configure 4271 (lsp--auto-configure) 4272 4273 (when (and lsp-enable-text-document-color 4274 (lsp-feature? "textDocument/documentColor")) 4275 (add-hook 'lsp-on-change-hook #'lsp--document-color nil t)) 4276 4277 (when (and lsp-enable-imenu 4278 (lsp-feature? "textDocument/documentSymbol")) 4279 (lsp-enable-imenu)) 4280 4281 (when (and lsp-enable-indentation 4282 (lsp-feature? "textDocument/rangeFormatting")) 4283 (add-function :override (local 'indent-region-function) #'lsp-format-region)) 4284 4285 (when (and lsp-enable-symbol-highlighting 4286 (lsp-feature? "textDocument/documentHighlight")) 4287 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)) 4288 4289 (when (and lsp-enable-links 4290 (lsp-feature? "textDocument/documentLink")) 4291 (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t)) 4292 4293 (when (and lsp-inlay-hint-enable 4294 (lsp-feature? "textDocument/inlayHint")) 4295 (lsp-inlay-hints-mode)) 4296 4297 (when (and lsp-enable-dap-auto-configure 4298 (functionp 'dap-mode)) 4299 (dap-auto-configure-mode 1))) 4300 (run-hooks 'lsp-configure-hook))) 4301 4302 (defun lsp-unconfig-buffer () 4303 "Unconfigure LSP features for buffer." 4304 (lsp--remove-overlays 'lsp-color) 4305 4306 (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function) 4307 (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index) 4308 (setq-local imenu-menubar-modified-tick 0) 4309 (setq-local imenu--index-alist nil) 4310 (imenu--cleanup)) 4311 4312 (remove-function (local 'indent-region-function) #'lsp-format-region) 4313 4314 (remove-hook 'lsp-on-change-hook #'lsp--document-color t) 4315 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4316 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4317 4318 (when (and lsp-enable-dap-auto-configure 4319 (functionp 'dap-mode)) 4320 (dap-auto-configure-mode -1)) 4321 4322 (run-hooks 'lsp-unconfigure-hook)) 4323 4324 (defun lsp--buffer-content () 4325 (lsp-save-restriction-and-excursion 4326 (or (lsp-virtual-buffer-call :buffer-string) 4327 (buffer-substring-no-properties (point-min) 4328 (point-max))))) 4329 4330 (defun lsp--text-document-did-open () 4331 "`document/didOpen' event." 4332 (run-hooks 'lsp-before-open-hook) 4333 (when (and lsp-auto-touch-files 4334 (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri))))) 4335 (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri)) 4336 (save-buffer)) 4337 4338 (setq lsp--cur-version (or lsp--cur-version 0)) 4339 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 4340 (lsp-notify 4341 "textDocument/didOpen" 4342 (list :textDocument 4343 (list :uri (lsp--buffer-uri) 4344 :languageId (lsp-buffer-language) 4345 :version lsp--cur-version 4346 :text (lsp--buffer-content)))) 4347 4348 (lsp-managed-mode 1) 4349 4350 (lsp-diagnostics--request-pull-diagnostics lsp--cur-workspace) 4351 4352 (run-hooks 'lsp-after-open-hook) 4353 (when-let* ((client (-some-> lsp--cur-workspace (lsp--workspace-client)))) 4354 (-some-> (lsp--client-after-open-fn client) 4355 (funcall)) 4356 (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client)) 4357 (intern-soft) 4358 (run-hooks)))) 4359 4360 (defun lsp--text-document-identifier () 4361 "Make TextDocumentIdentifier." 4362 (list :uri (lsp--buffer-uri))) 4363 4364 (defun lsp--versioned-text-document-identifier () 4365 "Make VersionedTextDocumentIdentifier." 4366 (plist-put (lsp--text-document-identifier) :version lsp--cur-version)) 4367 4368 (defun lsp--cur-line (&optional point) 4369 (1- (line-number-at-pos point))) 4370 4371 (defun lsp--cur-position () 4372 "Make a Position object for the current point." 4373 (or (lsp-virtual-buffer-call :cur-position) 4374 (lsp-save-restriction-and-excursion 4375 (list :line (lsp--cur-line) 4376 :character (- (point) (line-beginning-position)))))) 4377 4378 (defun lsp--point-to-position (point) 4379 "Convert POINT to Position." 4380 (lsp-save-restriction-and-excursion 4381 (goto-char point) 4382 (lsp--cur-position))) 4383 4384 (defun lsp--range (start end) 4385 "Make Range body from START and END." 4386 ;; make sure start and end are Position objects 4387 (list :start start :end end)) 4388 4389 (defun lsp--region-to-range (start end) 4390 "Make Range object for the current region." 4391 (lsp--range (lsp--point-to-position start) 4392 (lsp--point-to-position end))) 4393 4394 (defun lsp--region-or-line () 4395 "The active region or the current line." 4396 (if (use-region-p) 4397 (lsp--region-to-range (region-beginning) (region-end)) 4398 (lsp--region-to-range (line-beginning-position) (line-end-position)))) 4399 4400 (defun lsp--check-document-changes-version (document-changes) 4401 "Verify that DOCUMENT-CHANGES have the proper version." 4402 (unless (seq-every-p 4403 (-lambda ((&TextDocumentEdit :text-document)) 4404 (or 4405 (not text-document) 4406 (let* ((filename (-> text-document 4407 lsp:versioned-text-document-identifier-uri 4408 lsp--uri-to-path)) 4409 (version (lsp:versioned-text-document-identifier-version? text-document))) 4410 (with-current-buffer (find-file-noselect filename) 4411 (or (null version) (zerop version) (= -1 version) 4412 (equal version lsp--cur-version)))))) 4413 document-changes) 4414 (error "Document changes cannot be applied due to different document version"))) 4415 4416 (defun lsp--apply-workspace-edit (workspace-edit &optional operation) 4417 "Apply the WorkspaceEdit object WORKSPACE-EDIT. 4418 OPERATION is symbol representing the source of this text edit." 4419 (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit)) 4420 (if-let* ((document-changes (seq-reverse document-changes?))) 4421 (progn 4422 (lsp--check-document-changes-version document-changes) 4423 (->> document-changes 4424 (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create"))) 4425 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4426 (->> document-changes 4427 (seq-filter (-lambda ((&CreateFile :kind)) 4428 (and (or (not kind) (equal kind "edit")) 4429 (not (equal kind "create"))))) 4430 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4431 (->> document-changes 4432 (seq-filter (-lambda ((&CreateFile :kind)) 4433 (and (not (or (not kind) (equal kind "edit"))) 4434 (not (equal kind "create"))))) 4435 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))) 4436 (lsp-map 4437 (lambda (uri text-edits) 4438 (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect) 4439 (lsp--apply-text-edits text-edits operation))) 4440 changes?)))) 4441 4442 (defmacro lsp-with-filename (file &rest body) 4443 "Execute BODY with FILE as a context. 4444 Need to handle the case when FILE indicates virtual buffer." 4445 (declare (indent 1) (debug t)) 4446 `(if-let* ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file))) 4447 (lsp-with-current-buffer lsp--virtual-buffer 4448 ,@body) 4449 ,@body)) 4450 4451 (defun lsp--apply-text-document-edit (edit &optional operation) 4452 "Apply the TextDocumentEdit object EDIT. 4453 OPERATION is symbol representing the source of this text edit. 4454 If the file is not being visited by any buffer, it is opened with 4455 `find-file-noselect'. 4456 Because lsp-mode does not store previous document versions, the edit is only 4457 applied if the version of the textDocument matches the version of the 4458 corresponding file. 4459 4460 interface TextDocumentEdit { 4461 textDocument: VersionedTextDocumentIdentifier; 4462 edits: TextEdit[]; 4463 }" 4464 (pcase (lsp:edit-kind edit) 4465 ("create" (-let* (((&CreateFile :uri :options?) edit) 4466 (file-name (lsp--uri-to-path uri))) 4467 (mkdir (f-dirname file-name) t) 4468 (f-touch file-name) 4469 (when (lsp:create-file-options-overwrite? options?) 4470 (f-write-text "" nil file-name)) 4471 (find-file-noselect file-name))) 4472 ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit)) 4473 (f-delete (lsp--uri-to-path uri) recursive?))) 4474 ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit) 4475 (old-file-name (lsp--uri-to-path old-uri)) 4476 (new-file-name (lsp--uri-to-path new-uri)) 4477 (buf (find-buffer-visiting old-file-name))) 4478 (when buf 4479 (lsp-with-current-buffer buf 4480 (save-buffer) 4481 (lsp--text-document-did-close))) 4482 (mkdir (f-dirname new-file-name) t) 4483 (rename-file old-file-name new-file-name overwrite?) 4484 (when buf 4485 (lsp-with-current-buffer buf 4486 (set-buffer-modified-p nil) 4487 (setq lsp-buffer-uri nil) 4488 (set-visited-file-name new-file-name) 4489 (lsp))))) 4490 (_ (let ((file-name (->> edit 4491 (lsp:text-document-edit-text-document) 4492 (lsp:versioned-text-document-identifier-uri) 4493 (lsp--uri-to-path)))) 4494 (lsp-with-current-buffer (find-buffer-visiting file-name) 4495 (lsp-with-filename file-name 4496 (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation))))))) 4497 4498 (lsp-defun lsp--position-compare ((&Position :line left-line 4499 :character left-character) 4500 (&Position :line right-line 4501 :character right-character)) 4502 "Return t if position LEFT is greater than RIGHT." 4503 (if (= left-line right-line) 4504 (> left-character right-character) 4505 (> left-line right-line))) 4506 4507 (lsp-defun lsp-point-in-range? (position (&Range :start :end)) 4508 "Returns if POINT is in RANGE." 4509 (not (or (lsp--position-compare start position) 4510 (lsp--position-compare position end)))) 4511 4512 (lsp-defun lsp--position-equal ((&Position :line left-line 4513 :character left-character) 4514 (&Position :line right-line 4515 :character right-character)) 4516 "Return whether LEFT and RIGHT positions are equal." 4517 (and (= left-line right-line) 4518 (= left-character right-character))) 4519 4520 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end)) 4521 (&TextEdit :range (&Range :start right-start :end right-end))) 4522 (if (lsp--position-equal left-start right-start) 4523 (lsp--position-compare left-end right-end) 4524 (lsp--position-compare left-start right-start))) 4525 4526 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text)) 4527 "Apply the edits described in the TextEdit object in TEXT-EDIT." 4528 (setq new-text (s-replace "\r" "" (or new-text ""))) 4529 (lsp:set-text-edit-new-text edit new-text) 4530 (goto-char start) 4531 (delete-region start end) 4532 (insert new-text)) 4533 4534 ;; WORKAROUND: typescript-language might send -1 when applying code actions. 4535 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582 4536 (lsp-defun lsp--fix-point ((point &as &Position :character :line)) 4537 (-doto point 4538 (lsp:set-position-line (max 0 line)) 4539 (lsp:set-position-character (max 0 character)))) 4540 4541 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as 4542 &TextEdit 4543 :range (&Range :start :end) 4544 :new-text)) 4545 "Apply the edits described in the TextEdit object in TEXT-EDIT. 4546 The method uses `replace-buffer-contents'." 4547 (setq new-text (s-replace "\r" "" (or new-text ""))) 4548 (lsp:set-text-edit-new-text edit new-text) 4549 (-let* ((source (current-buffer)) 4550 ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start) 4551 :end (lsp--fix-point end))))) 4552 (with-temp-buffer 4553 (insert new-text) 4554 (let ((temp (current-buffer))) 4555 (with-current-buffer source 4556 (save-excursion 4557 (save-restriction 4558 (narrow-to-region beg end) 4559 4560 ;; On emacs versions < 26.2, 4561 ;; `replace-buffer-contents' is buggy - it calls 4562 ;; change functions with invalid arguments - so we 4563 ;; manually call the change functions here. 4564 ;; 4565 ;; See emacs bugs #32237, #32278: 4566 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237 4567 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278 4568 (let ((inhibit-modification-hooks t) 4569 (length (- end beg))) 4570 (run-hook-with-args 'before-change-functions 4571 beg end) 4572 (replace-buffer-contents temp) 4573 (run-hook-with-args 'after-change-functions 4574 beg (+ beg (length new-text)) 4575 length))))))))) 4576 4577 (defun lsp--to-yasnippet-snippet (snippet) 4578 "Convert LSP SNIPPET to yasnippet snippet." 4579 ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it. 4580 (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`"))) 4581 (rx "\\" (backref 1)) 4582 snippet 4583 nil nil 1)) 4584 4585 (defvar-local lsp-enable-relative-indentation nil 4586 "Enable relative indentation when insert texts, snippets ... 4587 from language server.") 4588 4589 (defun lsp--expand-snippet (snippet &optional start end expand-env) 4590 "Wrapper of `yas-expand-snippet' with all of it arguments. 4591 The snippet will be convert to LSP style and indent according to 4592 LSP server result." 4593 (require 'yasnippet nil t) 4594 (let* ((inhibit-field-text-motion t) 4595 (yas-wrap-around-region nil) 4596 (yas-indent-line 'none) 4597 (yas-also-auto-indent-first-line nil)) 4598 (yas-expand-snippet 4599 (lsp--to-yasnippet-snippet snippet) 4600 start end expand-env))) 4601 4602 (defun lsp--indent-lines (start end &optional insert-text-mode?) 4603 "Indent from START to END based on INSERT-TEXT-MODE? value. 4604 - When INSERT-TEXT-MODE? is provided 4605 - if it's `lsp/insert-text-mode-as-it', do no editor indentation. 4606 - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading 4607 whitespaces to match the line where text is inserted. 4608 - When it's not provided, using `indent-line-function' for each line." 4609 (save-excursion 4610 (goto-char end) 4611 (let* ((end-line (line-number-at-pos)) 4612 (offset (save-excursion 4613 (goto-char start) 4614 (current-indentation))) 4615 (indent-line-function 4616 (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it) 4617 #'ignore) 4618 ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation) 4619 lsp-enable-relative-indentation 4620 ;; Indenting snippets is extremely slow in `org-mode' buffers 4621 ;; since it has to calculate indentation based on SRC block 4622 ;; position. Thus we use relative indentation as default. 4623 (derived-mode-p 'org-mode)) 4624 (lambda () (save-excursion 4625 (beginning-of-line) 4626 (indent-to-column offset)))) 4627 (t indent-line-function)))) 4628 (goto-char start) 4629 (forward-line) 4630 (while (and (not (eobp)) 4631 (<= (line-number-at-pos) end-line)) 4632 (funcall indent-line-function) 4633 (forward-line))))) 4634 4635 (defun lsp--apply-text-edits (edits &optional operation) 4636 "Apply the EDITS described in the TextEdit[] object. 4637 OPERATION is symbol representing the source of this text edit." 4638 (unless (seq-empty-p edits) 4639 (atomic-change-group 4640 (run-hooks 'lsp-before-apply-edits-hook) 4641 (let* ((change-group (prepare-change-group)) 4642 (howmany (length edits)) 4643 (message (format "Applying %s edits to `%s' ..." howmany (current-buffer))) 4644 (_ (lsp--info message)) 4645 (reporter (make-progress-reporter message 0 howmany)) 4646 (done 0) 4647 (apply-edit (if (not lsp--virtual-buffer) 4648 #'lsp--apply-text-edit-replace-buffer-contents 4649 #'lsp--apply-text-edit))) 4650 (unwind-protect 4651 (->> edits 4652 ;; We sort text edits so as to apply edits that modify latter 4653 ;; parts of the document first. Furthermore, because the LSP 4654 ;; spec dictates that: "If multiple inserts have the same 4655 ;; position, the order in the array defines which edit to 4656 ;; apply first." We reverse the initial list and sort stably 4657 ;; to make sure the order among edits with the same position 4658 ;; is preserved. 4659 (nreverse) 4660 (seq-sort #'lsp--text-edit-sort-predicate) 4661 (mapc (lambda (edit) 4662 (progress-reporter-update reporter (cl-incf done)) 4663 (funcall apply-edit edit) 4664 (when (lsp:snippet-text-edit-insert-text-format? edit) 4665 (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start) 4666 :insert-text-format? :new-text) edit) 4667 (when (eq insert-text-format? lsp/insert-text-format-snippet) 4668 ;; No `save-excursion' needed since expand snippet will change point anyway 4669 (goto-char (+ start (length new-text))) 4670 (lsp--indent-lines start (point)) 4671 (lsp--expand-snippet new-text start (point))))) 4672 (run-hook-with-args 'lsp-after-apply-edits-hook operation)))) 4673 (undo-amalgamate-change-group change-group) 4674 (progress-reporter-done reporter)))))) 4675 4676 (defun lsp--create-apply-text-edits-handlers () 4677 "Create (handler cleanup-fn) for applying text edits in async request. 4678 Only works when mode is `tick or `alive." 4679 (let* (first-edited 4680 (func (lambda (start &rest _) 4681 (setq first-edited (if first-edited 4682 (min start first-edited) 4683 start))))) 4684 (add-hook 'before-change-functions func nil t) 4685 (list 4686 (lambda (edits) 4687 (if (and first-edited 4688 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end))) 4689 ;; Text edit region is overlapped 4690 (> end first-edited)) 4691 edits)) 4692 (lsp--warn "TextEdits will not be applied since document has been modified before of them.") 4693 (lsp--apply-text-edits edits 'completion-cleanup))) 4694 (lambda () 4695 (remove-hook 'before-change-functions func t))))) 4696 4697 (defun lsp--capability (cap &optional capabilities) 4698 "Get the value of capability CAP. If CAPABILITIES is non-nil, use them instead." 4699 (when (stringp cap) 4700 (setq cap (intern (concat ":" cap)))) 4701 4702 (lsp-get (or capabilities 4703 (lsp--server-capabilities)) 4704 cap)) 4705 4706 (defun lsp--registered-capability (method) 4707 "Check whether there is workspace providing METHOD." 4708 (->> (lsp-workspaces) 4709 (--keep (seq-find (lambda (reg) 4710 (equal (lsp--registered-capability-method reg) method)) 4711 (lsp--workspace-registered-server-capabilities it))) 4712 cl-first)) 4713 4714 (defun lsp--capability-for-method (method) 4715 "Get the value of capability for METHOD." 4716 (-let* ((reqs (cdr (assoc method lsp-method-requirements))) 4717 ((&plist :capability) reqs)) 4718 (or (and capability (lsp--capability capability)) 4719 (-some-> (lsp--registered-capability method) 4720 (lsp--registered-capability-options))))) 4721 4722 (defvar-local lsp--before-change-vals nil 4723 "Store the positions from the `lsp-before-change' function call, for 4724 validation and use in the `lsp-on-change' function.") 4725 4726 (defun lsp--text-document-content-change-event (start end length) 4727 "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH." 4728 ;; So (47 54 0) means add 7 chars starting at pos 47 4729 ;; must become 4730 ;; {"range":{"start":{"line":5,"character":6} 4731 ;; ,"end" :{"line":5,"character":6}} 4732 ;; ,"rangeLength":0 4733 ;; ,"text":"\nbb = 5"} 4734 ;; 4735 ;; And (47 47 7) means delete 7 chars starting at pos 47 4736 ;; must become 4737 ;; {"range":{"start":{"line":6,"character":0} 4738 ;; ,"end" :{"line":7,"character":0}} 4739 ;; ,"rangeLength":7 4740 ;; ,"text":""} 4741 ;; 4742 ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with 4743 ;; 13 chars. So it must become 4744 ;; {"range":{"start":{"line":5,"character":8} 4745 ;; ,"end" :{"line":5,"character":11}} 4746 ;; ,"rangeLength":3 4747 ;; ,"text":"new-chars-xxx"} 4748 ;; 4749 4750 ;; Adding text: 4751 ;; lsp-before-change:(start,end)=(33,33) 4752 ;; lsp-on-change:(start,end,length)=(33,34,0) 4753 ;; 4754 ;; Changing text: 4755 ;; lsp-before-change:(start,end)=(208,211) 4756 ;; lsp-on-change:(start,end,length)=(208,221,3) 4757 ;; 4758 ;; Deleting text: 4759 ;; lsp-before-change:(start,end)=(19,27) 4760 ;; lsp-on-change:(start,end,length)=(19,19,8) 4761 (if (zerop length) 4762 ;; Adding something only, work from start only 4763 `( :range ,(lsp--range 4764 (lsp--point-to-position start) 4765 (lsp--point-to-position start)) 4766 :rangeLength 0 4767 :text ,(buffer-substring-no-properties start end)) 4768 4769 (if (eq start end) 4770 ;; Deleting something only 4771 (if (lsp--bracketed-change-p start length) 4772 ;; The before-change value is bracketed, use it 4773 `( :range ,(lsp--range 4774 (lsp--point-to-position start) 4775 (plist-get lsp--before-change-vals :end-pos)) 4776 :rangeLength ,length 4777 :text "") 4778 ;; If the change is not bracketed, send a full change event instead. 4779 (lsp--full-change-event)) 4780 4781 ;; Deleting some things, adding others 4782 (if (lsp--bracketed-change-p start length) 4783 ;; The before-change value is valid, use it 4784 `( :range ,(lsp--range 4785 (lsp--point-to-position start) 4786 (plist-get lsp--before-change-vals :end-pos)) 4787 :rangeLength ,length 4788 :text ,(buffer-substring-no-properties start end)) 4789 (lsp--full-change-event))))) 4790 4791 (defun lsp--bracketed-change-p (start length) 4792 "If the before and after positions are the same, and the length 4793 is the size of the start range, we are probably good." 4794 (-let [(&plist :end before-end :start before-start) lsp--before-change-vals] 4795 (and (eq start before-start) 4796 (eq length (- before-end before-start))))) 4797 4798 (defun lsp--full-change-event () 4799 `(:text ,(lsp--buffer-content))) 4800 4801 (defun lsp-before-change (start end) 4802 "Executed before a file is changed. 4803 Added to `before-change-functions'." 4804 ;; Note: 4805 ;; 4806 ;; This variable holds a list of functions to call when Emacs is about to 4807 ;; modify a buffer. Each function gets two arguments, the beginning and end of 4808 ;; the region that is about to change, represented as integers. The buffer 4809 ;; that is about to change is always the current buffer when the function is 4810 ;; called. 4811 ;; 4812 ;; WARNING: 4813 ;; 4814 ;; Do not expect the before-change hooks and the after-change hooks be called 4815 ;; in balanced pairs around each buffer change. Also don't expect the 4816 ;; before-change hooks to be called for every chunk of text Emacs is about to 4817 ;; delete. These hooks are provided on the assumption that Lisp programs will 4818 ;; use either before- or the after-change hooks, but not both, and the 4819 ;; boundaries of the region where the changes happen might include more than 4820 ;; just the actual changed text, or even lump together several changes done 4821 ;; piecemeal. 4822 (save-match-data 4823 (lsp-save-restriction-and-excursion 4824 (setq lsp--before-change-vals 4825 (list :start start 4826 :end end 4827 :end-pos (lsp--point-to-position end)))))) 4828 4829 (defun lsp--flush-delayed-changes () 4830 (let ((inhibit-quit t)) 4831 (when lsp--delay-timer 4832 (cancel-timer lsp--delay-timer)) 4833 (mapc (-lambda ((workspace buffer document change)) 4834 (with-current-buffer buffer 4835 (with-lsp-workspace workspace 4836 (lsp-notify "textDocument/didChange" 4837 (list :textDocument document 4838 :contentChanges (vector change)))))) 4839 (prog1 (nreverse lsp--delayed-requests) 4840 (setq lsp--delayed-requests nil))))) 4841 4842 (defun lsp--workspace-sync-method (workspace) 4843 (let ((sync (-> workspace 4844 (lsp--workspace-server-capabilities) 4845 (lsp:server-capabilities-text-document-sync?)))) 4846 (if (lsp-text-document-sync-options? sync) 4847 (lsp:text-document-sync-options-change? sync) 4848 sync))) 4849 4850 (defun lsp-on-change (start end length &optional content-change-event-fn) 4851 "Executed when a file is changed. 4852 Added to `after-change-functions'." 4853 ;; Note: 4854 ;; 4855 ;; Each function receives three arguments: the beginning and end of the region 4856 ;; just changed, and the length of the text that existed before the change. 4857 ;; All three arguments are integers. The buffer that has been changed is 4858 ;; always the current buffer when the function is called. 4859 ;; 4860 ;; The length of the old text is the difference between the buffer positions 4861 ;; before and after that text as it was before the change. As for the 4862 ;; changed text, its length is simply the difference between the first two 4863 ;; arguments. 4864 ;; 4865 ;; So (47 54 0) means add 7 chars starting at pos 47 4866 ;; So (47 47 7) means delete 7 chars starting at pos 47 4867 (save-match-data 4868 (let ((inhibit-quit t) 4869 ;; make sure that `lsp-on-change' is called in multi-workspace context 4870 ;; see #2901 4871 lsp--cur-workspace) 4872 ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done 4873 ;; by auto-revert-mode) will cause this handler to get called with a nil 4874 ;; buffer-file-name. We need the buffer-file-name to send notifications; 4875 ;; so we skip handling revert-buffer-caused changes and instead handle 4876 ;; reverts separately in lsp-on-revert 4877 (when (not revert-buffer-in-progress-p) 4878 (cl-incf lsp--cur-version) 4879 (mapc 4880 (lambda (workspace) 4881 (pcase (or lsp-document-sync-method 4882 (lsp--workspace-sync-method workspace)) 4883 (1 4884 (if lsp-debounce-full-sync-notifications 4885 (setq lsp--delayed-requests 4886 (->> lsp--delayed-requests 4887 (-remove (-lambda ((_ buffer)) 4888 (equal (current-buffer) buffer))) 4889 (cons (list workspace 4890 (current-buffer) 4891 (lsp--versioned-text-document-identifier) 4892 (lsp--full-change-event))))) 4893 (with-lsp-workspace workspace 4894 (lsp-notify "textDocument/didChange" 4895 (list :contentChanges (vector (lsp--full-change-event)) 4896 :textDocument (lsp--versioned-text-document-identifier))) 4897 (lsp-diagnostics--request-pull-diagnostics workspace)))) 4898 (2 4899 (with-lsp-workspace workspace 4900 (lsp-notify 4901 "textDocument/didChange" 4902 (list :textDocument (lsp--versioned-text-document-identifier) 4903 :contentChanges (vector 4904 (if content-change-event-fn 4905 (funcall content-change-event-fn start end length) 4906 (lsp--text-document-content-change-event 4907 start end length))))) 4908 (lsp-diagnostics--request-pull-diagnostics workspace))))) 4909 (lsp-workspaces)) 4910 (when lsp--delay-timer (cancel-timer lsp--delay-timer)) 4911 (setq lsp--delay-timer (run-with-idle-timer 4912 lsp-debounce-full-sync-notifications-interval 4913 nil 4914 #'lsp--flush-delayed-changes)) 4915 ;; force cleanup overlays after each change 4916 (lsp--remove-overlays 'lsp-highlight) 4917 (lsp--after-change (current-buffer)))))) 4918 4919 4920 4921 ;; facilities for on change hooks. We do not want to make lsp calls on each 4922 ;; change event so we add debounce to avoid flooding the server with events. 4923 ;; Additionally, we want to have a mechanism for stopping the server calls in 4924 ;; particular cases like, e. g. when performing completion. 4925 4926 (defvar lsp-inhibit-lsp-hooks nil 4927 "Flag to control.") 4928 4929 (defcustom lsp-on-change-hook nil 4930 "Hooks to run when buffer has changed." 4931 :type 'hook 4932 :group 'lsp-mode) 4933 4934 (defcustom lsp-idle-delay 0.500 4935 "Debounce interval for `after-change-functions'." 4936 :type 'number 4937 :group 'lsp-mode) 4938 4939 (defcustom lsp-on-idle-hook nil 4940 "Hooks to run after `lsp-idle-delay'." 4941 :type 'hook 4942 :group 'lsp-mode) 4943 4944 (defun lsp--idle-reschedule (buffer) 4945 (when lsp--on-idle-timer 4946 (cancel-timer lsp--on-idle-timer)) 4947 4948 (setq lsp--on-idle-timer (run-with-idle-timer 4949 lsp-idle-delay 4950 nil 4951 #'lsp--on-idle 4952 buffer))) 4953 4954 (defun lsp--post-command () 4955 (lsp--cleanup-highlights-if-needed) 4956 (lsp--idle-reschedule (current-buffer))) 4957 4958 (defun lsp--on-idle (buffer) 4959 "Start post command loop." 4960 (when (and (buffer-live-p buffer) 4961 (equal buffer (current-buffer)) 4962 (not lsp-inhibit-lsp-hooks) 4963 lsp-managed-mode) 4964 (run-hooks 'lsp-on-idle-hook))) 4965 4966 (defun lsp--on-change-debounce (buffer) 4967 (when (and (buffer-live-p buffer) 4968 (equal buffer (current-buffer)) 4969 (not lsp-inhibit-lsp-hooks) 4970 lsp-managed-mode) 4971 (run-hooks 'lsp-on-change-hook))) 4972 4973 4974 (defvar-local lsp--after-change-vals nil 4975 "plist that stores the buffer state when `lsp--after-change' has ben activated. Since the 4976 functions in `lsp-on-change-hook' are called with a timer, mouse 4977 movements may have changed the position") 4978 4979 (defun lsp--after-change (buffer) 4980 "Called after most textDocument/didChange events." 4981 (setq lsp--signature-last-index nil 4982 lsp--signature-last nil) 4983 4984 ;; cleanup diagnostics 4985 (when lsp-diagnostic-clean-after-change 4986 (dolist (workspace (lsp-workspaces)) 4987 (-let [diagnostics (lsp--workspace-diagnostics workspace)] 4988 (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics)))) 4989 4990 (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled) 4991 (lsp--semantic-tokens-refresh-if-enabled buffer)) 4992 (when lsp--on-change-timer 4993 (cancel-timer lsp--on-change-timer)) 4994 4995 (setq lsp--after-change-vals (list :point (point) 4996 :buffer (current-buffer))) 4997 (setq lsp--on-change-timer (run-with-idle-timer 4998 lsp-idle-delay 4999 nil 5000 #'lsp--on-change-debounce 5001 buffer)) 5002 (lsp--idle-reschedule buffer)) 5003 5004 5005 (defcustom lsp-trim-trailing-whitespace t 5006 "Trim trailing whitespace on a line." 5007 :group 'lsp-mode 5008 :type 'boolean) 5009 5010 (defcustom lsp-insert-final-newline t 5011 "Insert a newline character at the end of the file if one does not exist." 5012 :group 'lsp-mode 5013 :type 'boolean) 5014 5015 (defcustom lsp-trim-final-newlines t 5016 "Trim all newlines after the final newline at the end of the file." 5017 :group 'lsp-mode 5018 :type 'boolean) 5019 5020 5021 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters) 5022 "Self insert handling. 5023 Applies on type formatting." 5024 (let ((ch last-command-event)) 5025 (when (or (eq (string-to-char first-trigger-characters) ch) 5026 (cl-find ch more-trigger-characters :key #'string-to-char)) 5027 (lsp-request-async "textDocument/onTypeFormatting" 5028 (lsp-make-document-on-type-formatting-params 5029 :text-document (lsp--text-document-identifier) 5030 :options (lsp-make-formatting-options 5031 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 5032 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 5033 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 5034 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 5035 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)) 5036 :ch (char-to-string ch) 5037 :position (lsp--cur-position)) 5038 (lambda (data) (lsp--apply-text-edits data 'format)) 5039 :mode 'tick)))) 5040 5041 5042 ;; links 5043 (defun lsp--document-links () 5044 (when (lsp-feature? "textDocument/documentLink") 5045 (lsp-request-async 5046 "textDocument/documentLink" 5047 `(:textDocument ,(lsp--text-document-identifier)) 5048 (lambda (links) 5049 (lsp--remove-overlays 'lsp-link) 5050 (seq-do 5051 (-lambda ((link &as &DocumentLink :range (&Range :start :end))) 5052 (-doto (make-button (lsp--position-to-point start) 5053 (lsp--position-to-point end) 5054 'action (lsp--document-link-keymap link) 5055 'keymap (let ((map (make-sparse-keymap))) 5056 (define-key map [M-return] 'push-button) 5057 (define-key map [mouse-2] 'push-button) 5058 map) 5059 'help-echo "mouse-2, M-RET: Visit this link") 5060 (overlay-put 'lsp-link t))) 5061 links)) 5062 :mode 'unchanged))) 5063 5064 (defun lsp--document-link-handle-target (url) 5065 (let* ((parsed-url (url-generic-parse-url (url-unhex-string url))) 5066 (type (url-type parsed-url))) 5067 (pcase type 5068 ("file" 5069 (xref-push-marker-stack) 5070 (find-file (lsp--uri-to-path url)) 5071 (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url)) 5072 (goto-char (lsp--position-to-point 5073 (lsp-make-position :character (1- (string-to-number column)) 5074 :line (1- (string-to-number line))))))) 5075 ((or "http" "https") (browse-url url)) 5076 (type (if-let* ((handler (lsp--get-uri-handler type))) 5077 (funcall handler url) 5078 (signal 'lsp-file-scheme-not-supported (list url))))))) 5079 5080 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?)) 5081 (if target? 5082 (lambda (_) 5083 (interactive) 5084 (lsp--document-link-handle-target target?)) 5085 (lambda (_) 5086 (interactive) 5087 (when (lsp:document-link-registration-options-resolve-provider? 5088 (lsp--capability-for-method "textDocument/documentLink")) 5089 (lsp-request-async 5090 "documentLink/resolve" 5091 link 5092 (-lambda ((&DocumentLink :target?)) 5093 (lsp--document-link-handle-target target?))))))) 5094 5095 5096 5097 (defcustom lsp-warn-no-matched-clients t 5098 "Whether to show messages when there are no supported clients." 5099 :group 'lsp-mode 5100 :type 'boolean) 5101 5102 (defun lsp-buffer-language--configured-id () 5103 "Return nil when not registered." 5104 (->> lsp-language-id-configuration 5105 (-first 5106 (-lambda ((mode-or-pattern . language)) 5107 (cond 5108 ((and (stringp mode-or-pattern) 5109 (s-matches? mode-or-pattern (buffer-file-name))) 5110 language) 5111 ((eq mode-or-pattern major-mode) language)))) 5112 cl-rest)) 5113 5114 (defvar-local lsp--buffer-language nil 5115 "Locally cached returned value of `lsp-buffer-language'.") 5116 5117 (defun lsp-buffer-language () 5118 "Get language corresponding current buffer." 5119 (or lsp--buffer-language 5120 (let* ((configured-language (lsp-buffer-language--configured-id))) 5121 (setq lsp--buffer-language 5122 (or configured-language 5123 ;; ensure non-nil 5124 (string-remove-suffix "-mode" (symbol-name major-mode)))) 5125 (when (and lsp-warn-no-matched-clients 5126 (null configured-language)) 5127 (lsp-warn "Unable to calculate the languageId for buffer `%s'. \ 5128 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s" 5129 (buffer-name) 5130 major-mode)) 5131 lsp--buffer-language))) 5132 5133 (defun lsp-activate-on (&rest languages) 5134 "Returns language activation function. 5135 The function will return t when the `lsp-buffer-language' returns 5136 one of the LANGUAGES." 5137 (lambda (_file-name _mode) 5138 (-contains? languages (lsp-buffer-language)))) 5139 5140 (defun lsp-workspace-root (&optional path) 5141 "Find the workspace root for the current file or PATH." 5142 (-when-let* ((file-name (or path (buffer-file-name))) 5143 (file-name (lsp-f-canonical file-name))) 5144 (->> (lsp-session) 5145 (lsp-session-folders) 5146 (--filter (and (lsp--files-same-host it file-name) 5147 (or (lsp-f-ancestor-of? it file-name) 5148 (equal it file-name)))) 5149 (--max-by (> (length it) (length other)))))) 5150 5151 (defun lsp-on-revert () 5152 "Executed when a file is reverted. 5153 Added to `after-revert-hook'." 5154 (let ((n (buffer-size)) 5155 (revert-buffer-in-progress-p nil)) 5156 (lsp-on-change 0 n n))) 5157 5158 (defun lsp--text-document-did-close (&optional keep-workspace-alive) 5159 "Executed when the file is closed, added to `kill-buffer-hook'. 5160 5161 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace 5162 if it's closing the last buffer in the workspace." 5163 (lsp-foreach-workspace 5164 (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 5165 (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S" 5166 (lsp-notify "textDocument/didClose" 5167 `(:textDocument ,(lsp--text-document-identifier)))) 5168 (when (and (not lsp-keep-workspace-alive) 5169 (not keep-workspace-alive) 5170 (not (lsp--workspace-buffers lsp--cur-workspace))) 5171 (lsp--shutdown-workspace)))) 5172 5173 (defun lsp--will-save-text-document-params (reason) 5174 (list :textDocument (lsp--text-document-identifier) 5175 :reason reason)) 5176 5177 (defun lsp--before-save () 5178 "Before save handler." 5179 (with-demoted-errors "Error in ‘lsp--before-save’: %S" 5180 (let ((params (lsp--will-save-text-document-params 1))) 5181 (when (lsp--send-will-save-p) 5182 (lsp-notify "textDocument/willSave" params)) 5183 (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits) 5184 (let ((lsp-response-timeout 0.1)) 5185 (condition-case nil 5186 (lsp--apply-text-edits 5187 (lsp-request "textDocument/willSaveWaitUntil" 5188 params) 5189 'before-save) 5190 (error))))))) 5191 5192 (defun lsp--on-auto-save () 5193 "Handler for auto-save." 5194 (when (lsp--send-will-save-p) 5195 (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S" 5196 (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2))))) 5197 5198 (defun lsp--text-document-did-save () 5199 "Executed when the file is closed, added to `after-save-hook''." 5200 (when (lsp--send-did-save-p) 5201 (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’" 5202 (lsp-notify "textDocument/didSave" 5203 `( :textDocument ,(lsp--versioned-text-document-identifier) 5204 ,@(when (lsp--save-include-text-p) 5205 (list :text (lsp--buffer-content)))))))) 5206 5207 (defun lsp--text-document-position-params (&optional identifier position) 5208 "Make TextDocumentPositionParams for the current point in the current document. 5209 If IDENTIFIER and POSITION are non-nil, they will be used as the document 5210 identifier and the position respectively." 5211 (list :textDocument (or identifier (lsp--text-document-identifier)) 5212 :position (or position (lsp--cur-position)))) 5213 5214 (defun lsp--get-buffer-diagnostics () 5215 "Return buffer diagnostics." 5216 (gethash (or 5217 (plist-get lsp--virtual-buffer :buffer-file-name) 5218 (lsp--fix-path-casing (buffer-file-name))) 5219 (lsp-diagnostics t))) 5220 5221 (defun lsp-cur-line-diagnostics () 5222 "Return any diagnostics that apply to the current line." 5223 (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)] 5224 (cl-coerce (-filter 5225 (-lambda ((&Diagnostic :range (&Range :start (&Position :line)))) 5226 (and (>= line start) (<= line end))) 5227 (lsp--get-buffer-diagnostics)) 5228 'vector))) 5229 5230 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end) 5231 (right &as &Range :start right-start :end right-end)) 5232 (or (lsp-point-in-range? right-start left) 5233 (lsp-point-in-range? right-end left) 5234 (lsp-point-in-range? left-start right) 5235 (lsp-point-in-range? left-end right))) 5236 5237 (defun lsp-make-position-1 (position) 5238 (lsp-make-position :line (plist-get position :line) 5239 :character (plist-get position :character))) 5240 5241 (defun lsp-cur-possition-diagnostics () 5242 "Return any diagnostics that apply to the current line." 5243 (-let* ((start (if (use-region-p) (region-beginning) (point))) 5244 (end (if (use-region-p) (region-end) (point))) 5245 (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start)) 5246 :end (lsp-make-position-1 (lsp-point-to-position end))))) 5247 (->> (lsp--get-buffer-diagnostics) 5248 (-filter 5249 (-lambda ((&Diagnostic :range)) 5250 (lsp-range-overlapping? range current-range))) 5251 (apply 'vector)))) 5252 5253 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics) 5254 5255 (defun lsp--extract-line-from-buffer (pos) 5256 "Return the line pointed to by POS (a Position object) in the current buffer." 5257 (let* ((point (lsp--position-to-point pos)) 5258 (inhibit-field-text-motion t)) 5259 (save-excursion 5260 (goto-char point) 5261 (buffer-substring (line-beginning-position) (line-end-position))))) 5262 5263 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line) 5264 :end (end &as &Position :character end-char))) 5265 "Return a xref-item from a RANGE in FILENAME." 5266 (let* ((line (lsp--extract-line-from-buffer start)) 5267 (len (length line))) 5268 (add-face-text-property (max (min start-char len) 0) 5269 (max (min end-char len) 0) 5270 'xref-match t line) 5271 ;; LINE is nil when FILENAME is not being current visited by any buffer. 5272 (xref-make-match (or line filename) 5273 (xref-make-file-location 5274 filename 5275 (lsp-translate-line (1+ start-line)) 5276 (lsp-translate-column start-char)) 5277 (- end-char start-char)))) 5278 5279 (defun lsp--location-uri (loc) 5280 (if (lsp-location? loc) 5281 (lsp:location-uri loc) 5282 (lsp:location-link-target-uri loc))) 5283 5284 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start))) 5285 "Go to location." 5286 (let ((path (lsp--uri-to-path uri))) 5287 (if (f-exists? path) 5288 (with-current-buffer (find-file path) 5289 (goto-char (lsp--position-to-point start))) 5290 (error "There is no file %s" path)))) 5291 5292 (defun lsp--location-range (loc) 5293 (if (lsp-location? loc) 5294 (lsp:location-range loc) 5295 (lsp:location-link-target-selection-range loc))) 5296 5297 (defun lsp--locations-to-xref-items (locations) 5298 "Return a list of `xref-item' given LOCATIONS, which can be of 5299 type Location, LocationLink, Location[] or LocationLink[]." 5300 (setq locations 5301 (pcase locations 5302 ((seq (or (lsp-interface Location) 5303 (lsp-interface LocationLink))) 5304 (append locations nil)) 5305 ((or (lsp-interface Location) 5306 (lsp-interface LocationLink)) 5307 (list locations)))) 5308 5309 (cl-labels ((get-xrefs-in-file 5310 (file-locs) 5311 (-let [(filename . matches) file-locs] 5312 (condition-case err 5313 (let ((visiting (find-buffer-visiting filename)) 5314 (fn (lambda (loc) 5315 (lsp-with-filename filename 5316 (lsp--xref-make-item filename 5317 (lsp--location-range loc)))))) 5318 (if visiting 5319 (with-current-buffer visiting 5320 (seq-map fn matches)) 5321 (when (file-readable-p filename) 5322 (with-temp-buffer 5323 (insert-file-contents-literally filename) 5324 (seq-map fn matches))))) 5325 (error (lsp-warn "Failed to process xref entry for filename '%s': %s" 5326 filename (error-message-string err))) 5327 (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s" 5328 filename (error-message-string err))))))) 5329 5330 (->> locations 5331 (seq-sort #'lsp--location-before-p) 5332 (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri)) 5333 (seq-map #'get-xrefs-in-file) 5334 (apply #'nconc)))) 5335 5336 (defun lsp--location-before-p (left right) 5337 "Sort first by file, then by line, then by column." 5338 (let ((left-uri (lsp--location-uri left)) 5339 (right-uri (lsp--location-uri right))) 5340 (if (not (string= left-uri right-uri)) 5341 (string< left-uri right-uri) 5342 (-let (((&Range :start left-start) (lsp--location-range left)) 5343 ((&Range :start right-start) (lsp--location-range right))) 5344 (lsp--position-compare right-start left-start))))) 5345 5346 (defun lsp--make-reference-params (&optional td-position exclude-declaration) 5347 "Make a ReferenceParam object. 5348 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead. 5349 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." 5350 (let ((json-false :json-false)) 5351 (plist-put (or td-position (lsp--text-document-position-params)) 5352 :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration)))))) 5353 5354 (defun lsp--cancel-request (id) 5355 "Cancel request with ID in all workspaces." 5356 (lsp-foreach-workspace 5357 (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id)) 5358 (lsp-notify "$/cancelRequest" `(:id ,id)))) 5359 5360 (defvar-local lsp--hover-saved-bounds nil) 5361 5362 (defun lsp-eldoc-function (cb &rest _ignored) 5363 "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')." 5364 (if (and lsp--hover-saved-bounds 5365 (lsp--point-in-bounds-p lsp--hover-saved-bounds)) 5366 lsp--eldoc-saved-message 5367 (setq lsp--hover-saved-bounds nil 5368 lsp--eldoc-saved-message nil) 5369 (if (looking-at-p "[[:space:]\n]") 5370 (setq lsp--eldoc-saved-message nil) ; And returns nil. 5371 (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover")) 5372 (lsp-request-async 5373 "textDocument/hover" 5374 (lsp--text-document-position-params) 5375 (-lambda ((hover &as &Hover? :range? :contents)) 5376 (setq lsp--hover-saved-bounds (when range? 5377 (lsp--range-to-region range?))) 5378 (funcall cb (setq lsp--eldoc-saved-message 5379 (when contents 5380 (lsp--render-on-hover-content 5381 contents 5382 lsp-eldoc-render-all))))) 5383 :error-handler #'ignore 5384 :mode 'tick 5385 :cancel-token :eldoc-hover))))) 5386 5387 (defun lsp--point-on-highlight? () 5388 (-some? (lambda (overlay) 5389 (overlay-get overlay 'lsp-highlight)) 5390 (overlays-at (point)))) 5391 5392 (defun lsp--cleanup-highlights-if-needed () 5393 (when (and lsp-enable-symbol-highlighting 5394 lsp--have-document-highlights 5395 (not (lsp--point-on-highlight?))) 5396 (lsp--remove-overlays 'lsp-highlight) 5397 (setq lsp--have-document-highlights nil) 5398 (lsp-cancel-request-by-token :highlights))) 5399 5400 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil 5401 "The bounds of the symbol from which `lsp--document-highlight' 5402 most recently requested highlights.") 5403 5404 (defun lsp--document-highlight () 5405 (when (lsp-feature? "textDocument/documentHighlight") 5406 (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol))) 5407 (unless (or (looking-at-p "[[:space:]\n]") 5408 (not lsp-enable-symbol-highlighting) 5409 (and lsp--have-document-highlights 5410 curr-sym-bounds 5411 (equal curr-sym-bounds 5412 lsp--symbol-bounds-of-last-highlight-invocation))) 5413 (setq lsp--symbol-bounds-of-last-highlight-invocation 5414 curr-sym-bounds) 5415 (lsp-request-async "textDocument/documentHighlight" 5416 (lsp--text-document-position-params) 5417 #'lsp--document-highlight-callback 5418 :mode 'tick 5419 :cancel-token :highlights))))) 5420 5421 (defun lsp--help-open-link (&rest _) 5422 "Open markdown link at point via mouse or keyboard." 5423 (interactive "P") 5424 (let ((buffer-list-update-hook nil)) 5425 (-let [(buffer point) (if-let* ((valid (and (listp last-input-event) 5426 (eq (car last-input-event) 'mouse-2))) 5427 (event (cadr last-input-event)) 5428 (win (posn-window event)) 5429 (buffer (window-buffer win))) 5430 `(,buffer ,(posn-point event)) 5431 `(,(current-buffer) ,(point)))] 5432 (with-current-buffer buffer 5433 (when-let* ((face (get-text-property point 'face)) 5434 (url (or (and (eq face 'markdown-link-face) 5435 (get-text-property point 'help-echo)) 5436 (and (memq face '(markdown-url-face markdown-plain-url-face)) 5437 (nth 3 (markdown-link-at-pos point)))))) 5438 (lsp--document-link-handle-target url)))))) 5439 5440 (defvar lsp-help-mode-map 5441 (-doto (make-sparse-keymap) 5442 (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link)) 5443 "Keymap for `lsp-help-mode'.") 5444 5445 (define-derived-mode lsp-help-mode help-mode "LspHelp" 5446 "Major mode for displaying lsp help.") 5447 5448 (defun lsp-describe-thing-at-point () 5449 "Display the type signature and documentation of the thing at point." 5450 (interactive) 5451 (let ((contents (-some->> (lsp--text-document-position-params) 5452 (lsp--make-request "textDocument/hover") 5453 (lsp--send-request) 5454 (lsp:hover-contents)))) 5455 (if (and contents (not (equal contents ""))) 5456 (let ((lsp-help-buf-name "*lsp-help*")) 5457 (with-current-buffer (get-buffer-create lsp-help-buf-name) 5458 (delay-mode-hooks 5459 (lsp-help-mode) 5460 (with-help-window lsp-help-buf-name 5461 (insert 5462 (mapconcat 'string-trim-right 5463 (split-string (lsp--render-on-hover-content contents t) "\n") 5464 "\n")))) 5465 (run-mode-hooks))) 5466 (lsp--info "No content at point.")))) 5467 5468 (defun lsp--point-in-bounds-p (bounds) 5469 "Return whether the current point is within BOUNDS." 5470 (and (<= (car bounds) (point)) (< (point) (cdr bounds)))) 5471 5472 (defun lsp-get-renderer (language) 5473 "Get renderer for LANGUAGE." 5474 (lambda (str) 5475 (lsp--render-string str language))) 5476 5477 (defun lsp--setup-markdown (mode) 5478 "Setup the ‘markdown-mode’ in the frame. 5479 MODE is the mode used in the parent frame." 5480 (make-local-variable 'markdown-code-lang-modes) 5481 (dolist (mark (alist-get mode lsp-custom-markup-modes)) 5482 (add-to-list 'markdown-code-lang-modes (cons mark mode))) 5483 (setq-local markdown-fontify-code-blocks-natively t) 5484 (setq-local markdown-fontify-code-block-default-mode mode) 5485 (setq-local markdown-hide-markup t) 5486 5487 ;; Render some common HTML entities. 5488 ;; This should really happen in markdown-mode instead, 5489 ;; but it doesn't, so we do it here for now. 5490 (setq prettify-symbols-alist 5491 (cl-loop for i from 0 to 255 5492 collect (cons (format "&#x%02X;" i) i))) 5493 (push '("<" . ?<) prettify-symbols-alist) 5494 (push '(">" . ?>) prettify-symbols-alist) 5495 (push '("&" . ?&) prettify-symbols-alist) 5496 (push '(" " . ? ) prettify-symbols-alist) 5497 (setq prettify-symbols-compose-predicate 5498 (lambda (_start _end _match) t)) 5499 (prettify-symbols-mode 1)) 5500 5501 (defvar lsp-help-link-keymap 5502 (let ((map (make-sparse-keymap))) 5503 (define-key map [mouse-2] #'lsp--help-open-link) 5504 (define-key map "\r" #'lsp--help-open-link) 5505 map) 5506 "Keymap active on links in *lsp-help* mode.") 5507 5508 (defun lsp--fix-markdown-links () 5509 (let ((inhibit-read-only t) 5510 (inhibit-modification-hooks t) 5511 (prop)) 5512 (save-restriction 5513 (goto-char (point-min)) 5514 (while (setq prop (markdown-find-next-prop 'face)) 5515 (let ((end (or (next-single-property-change (car prop) 'face) 5516 (point-max)))) 5517 (when (memq (get-text-property (car prop) 'face) 5518 '(markdown-link-face 5519 markdown-url-face 5520 markdown-plain-url-face)) 5521 (add-text-properties (car prop) end 5522 (list 'button t 5523 'category 'lsp-help-link 5524 'follow-link t 5525 'keymap lsp-help-link-keymap))) 5526 (goto-char end)))))) 5527 5528 (defun lsp--buffer-string-visible () 5529 "Return visible buffer string. 5530 Stolen from `org-copy-visible'." 5531 (let ((temp (generate-new-buffer " *temp*")) 5532 (beg (point-min)) 5533 (end (point-max))) 5534 (while (/= beg end) 5535 (when (get-char-property beg 'invisible) 5536 (setq beg (next-single-char-property-change beg 'invisible nil end))) 5537 (let* ((next (next-single-char-property-change beg 'invisible nil end)) 5538 (substring (buffer-substring beg next))) 5539 (with-current-buffer temp (insert substring)) 5540 ;; (setq result (concat result substring)) 5541 (setq beg next))) 5542 (setq deactivate-mark t) 5543 (prog1 (with-current-buffer temp 5544 (s-chop-suffix "\n" (buffer-string))) 5545 (kill-buffer temp)))) 5546 5547 (defvar lsp-buffer-major-mode nil 5548 "Holds the major mode when fontification function is running. 5549 See #2588") 5550 5551 (defvar view-inhibit-help-message) 5552 5553 (defun lsp--render-markdown () 5554 "Render markdown." 5555 5556 (let ((markdown-enable-math nil)) 5557 (goto-char (point-min)) 5558 (while (re-search-forward 5559 (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/" 5560 "{" "}" "[" "]" "(" ")" 5561 "#" "+" "-" "." "!" "|")))) 5562 nil t) 5563 (replace-match (rx (backref 1)))) 5564 5565 ;; markdown-mode v2.3 does not yet provide gfm-view-mode 5566 (if (fboundp 'gfm-view-mode) 5567 (let ((view-inhibit-help-message t)) 5568 (gfm-view-mode)) 5569 (gfm-mode)) 5570 5571 (lsp--setup-markdown lsp-buffer-major-mode))) 5572 5573 (defvar lsp--display-inline-image-alist 5574 '((lsp--render-markdown 5575 (:regexp 5576 "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)" 5577 :sexp 5578 (create-image 5579 (base64-decode-string 5580 (buffer-substring-no-properties (match-beginning 1) (match-end 1))) 5581 nil t)))) 5582 "Replaced string regexp and function returning image. 5583 Each element should have the form (MODE . (PROPERTY-LIST...)). 5584 MODE (car) is function which is defined in `lsp-language-id-configuration'. 5585 Cdr should be list of PROPERTY-LIST. 5586 5587 Each PROPERTY-LIST should have properties: 5588 :regexp Regexp which determines what string is relpaced to image. 5589 You should also get information of image, by parenthesis constructs. 5590 By default, all matched string is replaced to image, but you can 5591 change index of replaced string by keyword :replaced-index. 5592 5593 :sexp Return image when evaluated. You can use information of regexp 5594 by using (match-beggining N), (match-end N) or (match-substring N). 5595 5596 In addition, each can have property: 5597 :replaced-index Determine index which is used to replace regexp to image. 5598 The value means first argument of `match-beginning' and 5599 `match-end'. If omitted, interpreted as index 0.") 5600 5601 (defcustom lsp-display-inline-image t 5602 "Showing inline image or not." 5603 :group 'lsp-mode 5604 :type 'boolean) 5605 5606 (defcustom lsp-enable-suggest-server-download t 5607 "When non-nil enable server downloading suggestions." 5608 :group 'lsp-mode 5609 :type 'boolean 5610 :package-version '(lsp-mode . "9.0.0")) 5611 5612 (defcustom lsp-auto-register-remote-clients t 5613 "When non-nil register remote when registering the local one." 5614 :group 'lsp-mode 5615 :type 'boolean 5616 :package-version '(lsp-mode . "9.0.0")) 5617 5618 (defun lsp--display-inline-image (mode) 5619 "Add image property if available." 5620 (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist)))) 5621 (when (and (display-images-p) lsp-display-inline-image) 5622 (cl-loop 5623 for plist in plist-list 5624 with regexp with replaced-index 5625 do 5626 (setq regexp (plist-get plist :regexp)) 5627 (setq replaced-index (or (plist-get plist :replaced-index) 0)) 5628 5629 (font-lock-remove-keywords nil (list regexp replaced-index)) 5630 (let ((inhibit-read-only t)) 5631 (save-excursion 5632 (goto-char (point-min)) 5633 (while (re-search-forward regexp nil t) 5634 (set-text-properties 5635 (match-beginning replaced-index) (match-end replaced-index) 5636 nil) 5637 (add-text-properties 5638 (match-beginning replaced-index) (match-end replaced-index) 5639 `(display ,(eval (plist-get plist :sexp))))))))))) 5640 5641 (defun lsp--fontlock-with-mode (str mode) 5642 "Fontlock STR with MODE." 5643 (let ((lsp-buffer-major-mode major-mode)) 5644 (with-temp-buffer 5645 (with-demoted-errors "Error during doc rendering: %s" 5646 (insert str) 5647 (delay-mode-hooks (funcall mode)) 5648 (cl-flet ((window-body-width () lsp-window-body-width)) 5649 ;; This can go wrong in some cases, and the fontification would 5650 ;; not work as expected. 5651 ;; 5652 ;; See #2984 5653 (ignore-errors (font-lock-ensure)) 5654 (lsp--display-inline-image mode) 5655 (when (eq mode 'lsp--render-markdown) 5656 (lsp--fix-markdown-links)))) 5657 (lsp--buffer-string-visible)))) 5658 5659 (defun lsp--render-string (str language) 5660 "Render STR using `major-mode' corresponding to LANGUAGE. 5661 When language is nil render as markup if `markdown-mode' is loaded." 5662 (setq str (s-replace "\r" "" (or str ""))) 5663 (if-let* ((modes (-keep (-lambda ((mode . lang)) 5664 (when (and (equal lang language) (functionp mode)) 5665 mode)) 5666 lsp-language-id-configuration)) 5667 (mode (car (or (member major-mode modes) modes)))) 5668 (lsp--fontlock-with-mode str mode) 5669 str)) 5670 5671 (defun lsp--render-element (content) 5672 "Render CONTENT element." 5673 (let ((inhibit-message t)) 5674 (or 5675 (pcase content 5676 ((lsp-interface MarkedString :value :language) 5677 (lsp--render-string value language)) 5678 ((lsp-interface MarkupContent :value :kind) 5679 (lsp--render-string value kind)) 5680 ;; plain string 5681 ((pred stringp) (lsp--render-string content "markdown")) 5682 ((pred null) "") 5683 (_ (error "Failed to handle %s" content))) 5684 ""))) 5685 5686 (defun lsp--create-unique-string-fn () 5687 (let (elements) 5688 (lambda (element) 5689 (let ((count (cl-count element elements :test #'string=))) 5690 (prog1 (if (zerop count) 5691 element 5692 (format "%s (%s)" element count)) 5693 (push element elements)))))) 5694 5695 (defun lsp--select-action (actions) 5696 "Select an action to execute from ACTIONS." 5697 (cond 5698 ((seq-empty-p actions) (signal 'lsp-no-code-actions nil)) 5699 ((and (eq (seq-length actions) 1) lsp-auto-execute-action) 5700 (lsp-seq-first actions)) 5701 (t (let ((completion-ignore-case t)) 5702 (lsp--completing-read "Select code action: " 5703 (seq-into actions 'list) 5704 (-compose (lsp--create-unique-string-fn) 5705 #'lsp:code-action-title) 5706 nil t))))) 5707 5708 (defun lsp--workspace-server-id (workspace) 5709 "Return the server ID of WORKSPACE." 5710 (-> workspace lsp--workspace-client lsp--client-server-id)) 5711 5712 (defun lsp--handle-rendered-for-echo-area (contents) 5713 "Return a single line from RENDERED, appropriate for display in the echo area." 5714 (pcase (lsp-workspaces) 5715 (`(,workspace) 5716 (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace))) 5717 ;; For projects with multiple active workspaces we also default to 5718 ;; render the first line. 5719 (_ (lsp-clients-extract-signature-on-hover contents nil)))) 5720 5721 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id) 5722 "Extract a representative line from CONTENTS, to show in the echo area." 5723 (car (s-lines (s-trim (lsp--render-element contents))))) 5724 5725 (defun lsp--render-on-hover-content (contents render-all) 5726 "Render the content received from `textDocument/hover' request. 5727 CONTENTS - MarkedString | MarkedString[] | MarkupContent 5728 RENDER-ALL - nil if only the signature should be rendered." 5729 (cond 5730 ((lsp-markup-content? contents) 5731 ;; MarkupContent. 5732 ;; It tends to be long and is not suitable to display fully in the echo area. 5733 ;; Just display the first line which is typically the signature. 5734 (if render-all 5735 (lsp--render-element contents) 5736 (lsp--handle-rendered-for-echo-area contents))) 5737 ((and (stringp contents) (not (string-match-p "\n" contents))) 5738 ;; If the contents is a single string containing a single line, 5739 ;; render it always. 5740 (lsp--render-element contents)) 5741 (t 5742 ;; MarkedString -> MarkedString[] 5743 (when (or (lsp-marked-string? contents) (stringp contents)) 5744 (setq contents (list contents))) 5745 ;; Consider the signature consisting of the elements who have a renderable 5746 ;; "language" property. When render-all is nil, ignore other elements. 5747 (string-join 5748 (seq-map 5749 #'lsp--render-element 5750 (if render-all 5751 contents 5752 ;; Only render contents that have an available renderer. 5753 (seq-take 5754 (seq-filter 5755 (-andfn #'lsp-marked-string? 5756 (-compose #'lsp-get-renderer #'lsp:marked-string-language)) 5757 contents) 5758 1))) 5759 (if (bound-and-true-p page-break-lines-mode) 5760 "\n\n" 5761 "\n"))))) 5762 5763 5764 5765 (defvar lsp-signature-mode-map 5766 (-doto (make-sparse-keymap) 5767 (define-key (kbd "M-n") #'lsp-signature-next) 5768 (define-key (kbd "M-p") #'lsp-signature-previous) 5769 (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs) 5770 (define-key (kbd "C-c C-k") #'lsp-signature-stop) 5771 (define-key (kbd "C-g") #'lsp-signature-stop)) 5772 "Keymap for `lsp-signature-mode'.") 5773 5774 (define-minor-mode lsp-signature-mode 5775 "Mode used to show signature popup." 5776 :keymap lsp-signature-mode-map 5777 :lighter "" 5778 :group 'lsp-mode) 5779 5780 (defun lsp-signature-stop () 5781 "Stop showing current signature help." 5782 (interactive) 5783 (lsp-cancel-request-by-token :signature) 5784 (remove-hook 'post-command-hook #'lsp-signature) 5785 (funcall lsp-signature-function nil) 5786 (lsp-signature-mode -1)) 5787 5788 (declare-function page-break-lines--update-display-tables "ext:page-break-lines") 5789 5790 (defun lsp--setup-page-break-mode-if-present () 5791 "Enable `page-break-lines-mode' in current buffer." 5792 (when (fboundp 'page-break-lines-mode) 5793 (page-break-lines-mode) 5794 ;; force page-break-lines-mode to update the display tables. 5795 (page-break-lines--update-display-tables))) 5796 5797 (defun lsp-lv-message (message) 5798 (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present) 5799 (if message 5800 (progn 5801 (setq lsp--signature-last-buffer (current-buffer)) 5802 (let ((lv-force-update t)) 5803 (lv-message "%s" message))) 5804 (lv-delete-window) 5805 (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present))) 5806 5807 (declare-function posframe-show "ext:posframe") 5808 (declare-function posframe-hide "ext:posframe") 5809 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe") 5810 5811 (defface lsp-signature-posframe 5812 '((t :inherit tooltip)) 5813 "Background and foreground for `lsp-signature-posframe'." 5814 :group 'lsp-mode) 5815 5816 (defvar lsp-signature-posframe-params 5817 (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward 5818 :height 10 5819 :width 60 5820 :border-width 1 5821 :min-width 60) 5822 "Params for signature and `posframe-show'.") 5823 5824 (defun lsp-signature-posframe (str) 5825 "Use posframe to show the STR signatureHelp string." 5826 (if str 5827 (apply #'posframe-show 5828 (with-current-buffer (get-buffer-create " *lsp-signature*") 5829 (erase-buffer) 5830 (insert str) 5831 (visual-line-mode 1) 5832 (lsp--setup-page-break-mode-if-present) 5833 (current-buffer)) 5834 (append 5835 lsp-signature-posframe-params 5836 (list :position (point) 5837 :background-color (face-attribute 'lsp-signature-posframe :background nil t) 5838 :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t) 5839 :border-color (face-attribute (if (facep 'child-frame-border) 5840 'child-frame-border 5841 'internal-border) 5842 :background nil t)))) 5843 (posframe-hide " *lsp-signature*"))) 5844 5845 (defun lsp--handle-signature-update (signature) 5846 (let ((message 5847 (if (lsp-signature-help? signature) 5848 (lsp--signature->message signature) 5849 (mapconcat #'lsp--signature->message signature "\n")))) 5850 (if (s-present? message) 5851 (funcall lsp-signature-function message) 5852 (lsp-signature-stop)))) 5853 5854 (defun lsp-signature-activate () 5855 "Activate signature help. 5856 It will show up only if current point has signature help." 5857 (interactive) 5858 (setq lsp--signature-last nil 5859 lsp--signature-last-index nil 5860 lsp--signature-last-buffer (current-buffer)) 5861 (add-hook 'post-command-hook #'lsp-signature) 5862 (lsp-signature-mode t)) 5863 5864 (defcustom lsp-signature-cycle t 5865 "Whether `lsp-signature-next' and prev should cycle." 5866 :type 'boolean 5867 :group 'lsp-mode) 5868 5869 (defun lsp-signature-next () 5870 "Show next signature." 5871 (interactive) 5872 (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last)))) 5873 (when (and lsp--signature-last-index 5874 lsp--signature-last 5875 (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs))) 5876 (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs)) 5877 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))) 5878 5879 (defun lsp-signature-previous () 5880 "Next signature." 5881 (interactive) 5882 (when (and lsp--signature-last-index 5883 lsp--signature-last 5884 (or lsp-signature-cycle (not (zerop lsp--signature-last-index)))) 5885 (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index) 5886 (length (lsp:signature-help-signatures lsp--signature-last)) 5887 lsp--signature-last-index))) 5888 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))) 5889 5890 (defun lsp-signature-toggle-full-docs () 5891 "Toggle full/partial signature documentation." 5892 (interactive) 5893 (let ((all? (not (numberp lsp-signature-doc-lines)))) 5894 (setq lsp-signature-doc-lines (if all? 5895 (or (car-safe lsp-signature-doc-lines) 5896 20) 5897 (list lsp-signature-doc-lines)))) 5898 (lsp-signature-activate)) 5899 5900 (defface lsp-signature-highlight-function-argument 5901 '((t :inherit eldoc-highlight-function-argument)) 5902 "The face to use to highlight function arguments in signatures." 5903 :group 'lsp-mode) 5904 5905 (defun lsp--signature->message (signature-help) 5906 "Generate eldoc message from SIGNATURE-HELP response." 5907 (setq lsp--signature-last signature-help) 5908 5909 (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help)))) 5910 (-let* (((&SignatureHelp :active-signature? 5911 :active-parameter? 5912 :signatures) signature-help) 5913 (active-signature? (or lsp--signature-last-index active-signature? 0)) 5914 (_ (setq lsp--signature-last-index active-signature?)) 5915 ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?)) 5916 (prefix (if (= (length signatures) 1) 5917 "" 5918 (concat (propertize (format " %s/%s" 5919 (1+ active-signature?) 5920 (length signatures)) 5921 'face 'success) 5922 " "))) 5923 (method-docs (when 5924 (and lsp-signature-render-documentation 5925 (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines))) 5926 (let ((docs (lsp--render-element 5927 (lsp:parameter-information-documentation? signature)))) 5928 (when (s-present? docs) 5929 (concat 5930 "\n" 5931 (if (fboundp 'page-break-lines-mode) 5932 "\n" 5933 "") 5934 (if (and (numberp lsp-signature-doc-lines) 5935 (> (length (s-lines docs)) lsp-signature-doc-lines)) 5936 (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs))) 5937 (propertize "\nTruncated..." 'face 'highlight)) 5938 docs))))))) 5939 (when (and active-parameter? (not (seq-empty-p parameters?))) 5940 (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?))) 5941 (seq-elt parameters? active-parameter?))) 5942 (selected-param-label (let ((label (lsp:parameter-information-label param))) 5943 (if (stringp label) label (append label nil)))) 5944 (start (if (stringp selected-param-label) 5945 (s-index-of selected-param-label label) 5946 (cl-first selected-param-label))) 5947 (end (if (stringp selected-param-label) 5948 (+ start (length selected-param-label)) 5949 (cl-second selected-param-label)))) 5950 (add-face-text-property start end 'lsp-signature-highlight-function-argument nil label))) 5951 (concat prefix label method-docs)))) 5952 5953 (defun lsp-signature () 5954 "Display signature info (based on `textDocument/signatureHelp')" 5955 (if (and lsp--signature-last-buffer 5956 (not (equal (current-buffer) lsp--signature-last-buffer))) 5957 (lsp-signature-stop) 5958 (lsp-request-async "textDocument/signatureHelp" 5959 (lsp--text-document-position-params) 5960 #'lsp--handle-signature-update 5961 :cancel-token :signature))) 5962 5963 5964 (defcustom lsp-overlay-document-color-char "■" 5965 "Display the char represent the document color in overlay" 5966 :type 'string 5967 :group 'lsp-mode) 5968 5969 ;; color presentation 5970 (defun lsp--color-create-interactive-command (color range) 5971 (lambda () 5972 (interactive) 5973 (-let [(&ColorPresentation? :text-edit? 5974 :additional-text-edits?) 5975 (lsp--completing-read 5976 "Select color presentation: " 5977 (lsp-request 5978 "textDocument/colorPresentation" 5979 `( :textDocument ,(lsp--text-document-identifier) 5980 :color ,color 5981 :range ,range)) 5982 #'lsp:color-presentation-label 5983 nil 5984 t)] 5985 (when text-edit? 5986 (lsp--apply-text-edit text-edit?)) 5987 (when additional-text-edits? 5988 (lsp--apply-text-edits additional-text-edits? 'color-presentation))))) 5989 5990 (defun lsp--number->color (number) 5991 (let ((result (format "%x" 5992 (round (* (or number 0) 255.0))))) 5993 (if (= 1 (length result)) 5994 (concat "0" result) 5995 result))) 5996 5997 (defun lsp--document-color () 5998 "Document color handler." 5999 (when (lsp-feature? "textDocument/documentColor") 6000 (lsp-request-async 6001 "textDocument/documentColor" 6002 `(:textDocument ,(lsp--text-document-identifier)) 6003 (lambda (result) 6004 (lsp--remove-overlays 'lsp-color) 6005 (seq-do 6006 (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue) 6007 :range)) 6008 (-let* (((beg . end) (lsp--range-to-region range)) 6009 (overlay (make-overlay beg end)) 6010 (command (lsp--color-create-interactive-command color range))) 6011 (overlay-put overlay 'lsp-color t) 6012 (overlay-put overlay 'evaporate t) 6013 (overlay-put overlay 6014 'before-string 6015 (propertize 6016 lsp-overlay-document-color-char 6017 'face `((:foreground ,(format 6018 "#%s%s%s" 6019 (lsp--number->color red) 6020 (lsp--number->color green) 6021 (lsp--number->color blue)))) 6022 'action command 6023 'mouse-face 'lsp-lens-mouse-face 6024 'local-map (-doto (make-sparse-keymap) 6025 (define-key [mouse-1] command)))))) 6026 result)) 6027 :mode 'unchanged 6028 :cancel-token :document-color-token))) 6029 6030 6031 6032 (defun lsp--action-trigger-parameter-hints (_command) 6033 "Handler for editor.action.triggerParameterHints." 6034 (when (member :on-server-request lsp-signature-auto-activate) 6035 (lsp-signature-activate))) 6036 6037 (defun lsp--action-trigger-suggest (_command) 6038 "Handler for editor.action.triggerSuggest." 6039 (cond 6040 ((and (bound-and-true-p company-mode) 6041 (fboundp 'company-auto-begin) 6042 (fboundp 'company-post-command)) 6043 (run-at-time 0 nil 6044 (lambda () 6045 (let ((this-command 'company-idle-begin) 6046 (company-minimum-prefix-length 0)) 6047 (company-auto-begin) 6048 (company-post-command))))) 6049 (t 6050 (completion-at-point)))) 6051 6052 (defconst lsp--default-action-handlers 6053 (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints) 6054 ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest)) 6055 "Default action handlers.") 6056 6057 (defun lsp--find-action-handler (command) 6058 "Find action handler for particular COMMAND." 6059 (or 6060 (--some (-some->> it 6061 (lsp--workspace-client) 6062 (lsp--client-action-handlers) 6063 (gethash command)) 6064 (lsp-workspaces)) 6065 (gethash command lsp--default-action-handlers))) 6066 6067 (defun lsp--text-document-code-action-params (&optional kind) 6068 "Code action params." 6069 (list :textDocument (lsp--text-document-identifier) 6070 :range (if (use-region-p) 6071 (lsp--region-to-range (region-beginning) (region-end)) 6072 (lsp--region-to-range (point) (point))) 6073 :context `( :diagnostics ,(lsp-cur-possition-diagnostics) 6074 ,@(when kind (list :only (vector kind)))))) 6075 6076 (defun lsp-code-actions-at-point (&optional kind) 6077 "Retrieve the code actions for the active region or the current line. 6078 It will filter by KIND if non nil." 6079 (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind))) 6080 6081 (defun lsp-execute-code-action-by-kind (command-kind) 6082 "Execute code action by COMMAND-KIND." 6083 (if-let* ((action (->> (lsp-get-or-calculate-code-actions command-kind) 6084 (-filter (-lambda ((&CodeAction :kind?)) 6085 (and kind? (s-prefix? command-kind kind?)))) 6086 lsp--select-action))) 6087 (lsp-execute-code-action action) 6088 (signal 'lsp-no-code-actions '(command-kind)))) 6089 6090 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point) 6091 6092 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?)) 6093 "Parse and execute a code ACTION represented as a Command LSP type." 6094 (let ((server-id (->> (lsp-workspaces) 6095 (cl-first) 6096 (or lsp--cur-workspace) 6097 (lsp--workspace-client) 6098 (lsp--client-server-id)))) 6099 (condition-case nil 6100 (with-no-warnings 6101 (lsp-execute-command server-id (intern command) arguments?)) 6102 (cl-no-applicable-method 6103 (if-let* ((action-handler (lsp--find-action-handler command))) 6104 (funcall action-handler action) 6105 (lsp-send-execute-command command arguments?)))))) 6106 6107 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?)) 6108 "Execute code action ACTION. For example, when text under the 6109 caret has a suggestion to apply a fix from an lsp-server, calling 6110 this function will do so. 6111 If ACTION is not set it will be selected from `lsp-code-actions-at-point'. 6112 Request codeAction/resolve for more info if server supports." 6113 (interactive (list (lsp--select-action (lsp-code-actions-at-point)))) 6114 (if (and (lsp-feature? "codeAction/resolve") 6115 (not command?) 6116 (not edit?)) 6117 (lsp--execute-code-action (lsp-request "codeAction/resolve" action)) 6118 (lsp--execute-code-action action))) 6119 6120 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?)) 6121 "Execute code action ACTION." 6122 (when edit? 6123 (lsp--apply-workspace-edit edit? 'code-action)) 6124 6125 (cond 6126 ((stringp command?) (lsp--execute-command action)) 6127 ((lsp-command? command?) (progn 6128 (when-let* ((action-filter (->> (lsp-workspaces) 6129 (cl-first) 6130 (or lsp--cur-workspace) 6131 (lsp--workspace-client) 6132 (lsp--client-action-filter)))) 6133 (funcall action-filter command?)) 6134 (lsp--execute-command command?))))) 6135 6136 (lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments) 6137 "Patch incorrect boolean argument values in the provided `CodeAction' command 6138 in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values 6139 in this list can be either symbols or lists of symbols that 6140 represent paths to boolean arguments in code actions: 6141 6142 > (lsp-fix-code-action-booleans command `(:foo :bar (:some :nested :boolean))) 6143 6144 When there are available code actions, the server sends 6145 `lsp-mode' a list of possible command names and arguments as 6146 JSON. `lsp-mode' parses all boolean false values as `nil'. As a 6147 result code action arguments containing falsy values don't 6148 roundtrip correctly because `lsp-mode' will end up sending null 6149 values back to the client. This list makes it possible to 6150 selectively transform `nil' values back into `:json-false'." 6151 (seq-doseq (path boolean-action-arguments) 6152 (seq-doseq (args arguments?) 6153 (lsp--fix-nested-boolean args (if (listp path) path (list path)))))) 6154 6155 (defun lsp--fix-nested-boolean (structure path) 6156 "Traverse STRUCTURE using the paths from the PATH list, changing the value to 6157 `:json-false' if it was `nil'. PATH should be a list containing 6158 one or more symbols, and STRUCTURE should be compatible with 6159 `lsp-member?', `lsp-get', and `lsp-put'." 6160 (let ((key (car path)) 6161 (rest (cdr path))) 6162 (if (null rest) 6163 ;; `lsp-put' returns `nil' both when the key doesn't exist and when the 6164 ;; value is `nil', so we need to explicitly check its presence here 6165 (when (and (lsp-member? structure key) (not (lsp-get structure key))) 6166 (lsp-put structure key :json-false)) 6167 ;; If `key' does not exist, then we'll silently ignore it 6168 (when-let* ((child (lsp-get structure key))) 6169 (lsp--fix-nested-boolean child rest))))) 6170 6171 (defvar lsp--formatting-indent-alist 6172 ;; Taken from `dtrt-indent-mode' 6173 '( 6174 (ada-mode . ada-indent) ; Ada 6175 (ada-ts-mode . ada-ts-mode-indent-offset) 6176 (c++-mode . c-basic-offset) ; C++ 6177 (c++-ts-mode . c-ts-mode-indent-offset) 6178 (c-mode . c-basic-offset) ; C 6179 (c-ts-mode . c-ts-mode-indent-offset) 6180 (cperl-mode . cperl-indent-level) ; Perl 6181 (crystal-mode . crystal-indent-level) ; Crystal (Ruby) 6182 (csharp-mode . c-basic-offset) ; C# 6183 (csharp-tree-sitter-mode . csharp-tree-sitter-indent-offset) ; C# 6184 (csharp-ts-mode . csharp-ts-mode-indent-offset) ; C# (tree-sitter, Emacs29) 6185 (css-mode . css-indent-offset) ; CSS 6186 (d-mode . c-basic-offset) ; D 6187 (enh-ruby-mode . enh-ruby-indent-level) ; Ruby 6188 (erlang-mode . erlang-indent-level) ; Erlang 6189 (ess-mode . ess-indent-offset) ; ESS (R) 6190 (go-ts-mode . go-ts-mode-indent-offset) 6191 (gpr-mode . gpr-indent-offset) ; GNAT Project 6192 (gpr-ts-mode . gpr-ts-mode-indent-offset) 6193 (hack-mode . hack-indent-offset) ; Hack 6194 (java-mode . c-basic-offset) ; Java 6195 (java-ts-mode . java-ts-mode-indent-offset) 6196 (jde-mode . c-basic-offset) ; Java (JDE) 6197 (js-mode . js-indent-level) ; JavaScript 6198 (js-ts-mode . js-indent-level) 6199 (js2-mode . js2-basic-offset) ; JavaScript-IDE 6200 (js3-mode . js3-indent-level) ; JavaScript-IDE 6201 (json-mode . js-indent-level) ; JSON 6202 (json-ts-mode . json-ts-mode-indent-offset) 6203 (lua-mode . lua-indent-level) ; Lua 6204 (lua-ts-mode . lua-ts-indent-offset) 6205 (nxml-mode . nxml-child-indent) ; XML 6206 (objc-mode . c-basic-offset) ; Objective C 6207 (pascal-mode . pascal-indent-level) ; Pascal 6208 (perl-mode . perl-indent-level) ; Perl 6209 (php-mode . c-basic-offset) ; PHP 6210 (php-ts-mode . php-ts-mode-indent-offset) ; PHP 6211 (powershell-mode . powershell-indent) ; PowerShell 6212 (powershell-ts-mode . powershell-ts-mode-indent-offset) ; PowerShell 6213 (raku-mode . raku-indent-offset) ; Perl6/Raku 6214 (ruby-mode . ruby-indent-level) ; Ruby 6215 (rust-mode . rust-indent-offset) ; Rust 6216 (rust-ts-mode . rust-ts-mode-indent-offset) 6217 (rustic-mode . rustic-indent-offset) ; Rust 6218 (scala-mode . scala-indent:step) ; Scala 6219 (sgml-mode . sgml-basic-offset) ; SGML 6220 (sh-mode . sh-basic-offset) ; Shell Script 6221 (toml-ts-mode . toml-ts-mode-indent-offset) 6222 (typescript-mode . typescript-indent-level) ; Typescript 6223 (typescript-ts-mode . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29) 6224 (yaml-mode . yaml-indent-offset) ; YAML 6225 (yang-mode . c-basic-offset) ; YANG (yang-mode) 6226 6227 (default . standard-indent)) ; default fallback 6228 "A mapping from `major-mode' to its indent variable.") 6229 6230 (defun lsp--get-indent-width (mode) 6231 "Get indentation offset for MODE." 6232 (or (alist-get mode lsp--formatting-indent-alist) 6233 (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default)))) 6234 6235 (defun lsp--make-document-formatting-params () 6236 "Create document formatting params." 6237 (lsp-make-document-formatting-params 6238 :text-document (lsp--text-document-identifier) 6239 :options (lsp-make-formatting-options 6240 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 6241 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 6242 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 6243 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 6244 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)))) 6245 6246 (defun lsp-format-buffer () 6247 "Ask the server to format this document." 6248 (interactive "*") 6249 (cond ((lsp-feature? "textDocument/formatting") 6250 (let ((edits (lsp-request "textDocument/formatting" 6251 (lsp--make-document-formatting-params)))) 6252 (if (seq-empty-p edits) 6253 (lsp--info "No formatting changes provided") 6254 (lsp--apply-text-edits edits 'format)))) 6255 ((lsp-feature? "textDocument/rangeFormatting") 6256 (save-restriction 6257 (widen) 6258 (lsp-format-region (point-min) (point-max)))) 6259 (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider"))))) 6260 6261 (defun lsp-format-region (s e) 6262 "Ask the server to format the region, or if none is selected, the current line." 6263 (interactive "r") 6264 (let ((edits (lsp-request 6265 "textDocument/rangeFormatting" 6266 (lsp--make-document-range-formatting-params s e)))) 6267 (if (seq-empty-p edits) 6268 (lsp--info "No formatting changes provided") 6269 (lsp--apply-text-edits edits 'format)))) 6270 6271 (defmacro lsp-make-interactive-code-action (func-name code-action-kind) 6272 "Define an interactive function FUNC-NAME that attempts to 6273 execute a CODE-ACTION-KIND action." 6274 `(defun ,(intern (concat "lsp-" (symbol-name func-name))) () 6275 ,(format "Perform the %s code action, if available." code-action-kind) 6276 (interactive) 6277 ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to 6278 ;; auto-execute here: the user has specified exactly what they want. 6279 (let ((lsp-auto-execute-action t)) 6280 (condition-case nil 6281 (lsp-execute-code-action-by-kind ,code-action-kind) 6282 (lsp-no-code-actions 6283 (when (called-interactively-p 'any) 6284 (lsp--info ,(format "%s action not available" code-action-kind)))))))) 6285 6286 (lsp-make-interactive-code-action organize-imports "source.organizeImports") 6287 6288 (defun lsp--make-document-range-formatting-params (start end) 6289 "Make DocumentRangeFormattingParams for selected region." 6290 (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params) 6291 (lsp--region-to-range start end))) 6292 6293 (defconst lsp--highlight-kind-face 6294 '((1 . lsp-face-highlight-textual) 6295 (2 . lsp-face-highlight-read) 6296 (3 . lsp-face-highlight-write))) 6297 6298 (defun lsp--remove-overlays (name) 6299 (save-restriction 6300 (widen) 6301 (remove-overlays (point-min) (point-max) name t))) 6302 6303 (defun lsp-document-highlight () 6304 "Highlight all relevant references to the symbol under point." 6305 (interactive) 6306 (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights 6307 (setq lsp--have-document-highlights nil 6308 lsp--symbol-bounds-of-last-highlight-invocation nil) 6309 (let ((lsp-enable-symbol-highlighting t)) 6310 (lsp--document-highlight))) 6311 6312 (defun lsp--document-highlight-callback (highlights) 6313 "Create a callback to process the reply of a 6314 `textDocument/documentHighlight' message for the buffer BUF. 6315 A reference is highlighted only if it is visible in a window." 6316 (lsp--remove-overlays 'lsp-highlight) 6317 6318 (let* ((wins-visible-pos (-map (lambda (win) 6319 (cons (1- (line-number-at-pos (window-start win) t)) 6320 (1+ (line-number-at-pos (min (window-end win) 6321 (with-current-buffer (window-buffer win) 6322 (buffer-end +1))) 6323 t)))) 6324 (get-buffer-window-list nil nil 'visible)))) 6325 (setq lsp--have-document-highlights t) 6326 (-map 6327 (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line) 6328 :end (end &as &Position :line end-line)) 6329 :kind?)) 6330 (-map 6331 (-lambda ((start-window . end-window)) 6332 ;; Make the overlay only if the reference is visible 6333 (when (and (> (1+ start-line) start-window) 6334 (< (1+ end-line) end-window)) 6335 (let ((start-point (lsp--position-to-point start)) 6336 (end-point (lsp--position-to-point end))) 6337 (when (not (and lsp-symbol-highlighting-skip-current 6338 (<= start-point (point) end-point))) 6339 (-doto (make-overlay start-point end-point) 6340 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face))) 6341 (overlay-put 'lsp-highlight t)))))) 6342 wins-visible-pos)) 6343 highlights))) 6344 6345 (defcustom lsp-symbol-kinds 6346 '((1 . "File") 6347 (2 . "Module") 6348 (3 . "Namespace") 6349 (4 . "Package") 6350 (5 . "Class") 6351 (6 . "Method") 6352 (7 . "Property") 6353 (8 . "Field") 6354 (9 . "Constructor") 6355 (10 . "Enum") 6356 (11 . "Interface") 6357 (12 . "Function") 6358 (13 . "Variable") 6359 (14 . "Constant") 6360 (15 . "String") 6361 (16 . "Number") 6362 (17 . "Boolean") 6363 (18 . "Array") 6364 (19 . "Object") 6365 (20 . "Key") 6366 (21 . "Null") 6367 (22 . "Enum Member") 6368 (23 . "Struct") 6369 (24 . "Event") 6370 (25 . "Operator") 6371 (26 . "Type Parameter")) 6372 "Alist mapping SymbolKinds to human-readable strings. 6373 Various Symbol objects in the LSP protocol have an integral type, 6374 specifying what they are. This alist maps such type integrals to 6375 readable representations of them. See 6376 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/', 6377 namespace SymbolKind." 6378 :group 'lsp-mode 6379 :type '(alist :key-type integer :value-type string)) 6380 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds) 6381 6382 (lsp-defun lsp--symbol-information-to-xref 6383 ((&SymbolInformation :kind :name 6384 :location (&Location :uri :range (&Range :start 6385 (&Position :line :character))))) 6386 "Return a `xref-item' from SYMBOL information." 6387 (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name) 6388 (xref-make-file-location (lsp--uri-to-path uri) 6389 line 6390 character))) 6391 6392 (defun lsp--get-document-symbols () 6393 "Get document symbols. 6394 6395 If the buffer has not been modified since symbols were last 6396 retrieved, simply return the latest result. 6397 6398 Else, if the request was initiated by Imenu updating its menu-bar 6399 entry, perform it asynchronously; i.e., give Imenu the latest 6400 result and then force a refresh when a new one is available. 6401 6402 Else (e.g., due to interactive use of `imenu' or `xref'), 6403 perform the request synchronously." 6404 (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick) 6405 lsp--document-symbols 6406 (let ((method "textDocument/documentSymbol") 6407 (params `(:textDocument ,(lsp--text-document-identifier))) 6408 (tick (buffer-chars-modified-tick))) 6409 (if (not lsp--document-symbols-request-async) 6410 (prog1 6411 (setq lsp--document-symbols (lsp-request method params)) 6412 (setq lsp--document-symbols-tick tick)) 6413 (lsp-request-async method params 6414 (lambda (document-symbols) 6415 (setq lsp--document-symbols document-symbols 6416 lsp--document-symbols-tick tick) 6417 (lsp--imenu-refresh)) 6418 :mode 'alive 6419 :cancel-token :document-symbols) 6420 lsp--document-symbols)))) 6421 6422 (advice-add 'imenu-update-menubar :around 6423 (lambda (oldfun &rest r) 6424 (let ((lsp--document-symbols-request-async t)) 6425 (apply oldfun r)))) 6426 6427 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position) 6428 "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION." 6429 (-let (((symbol &as &DocumentSymbol? :children?) 6430 (seq-find (-lambda ((&DocumentSymbol :range)) 6431 (lsp-point-in-range? current-position range)) 6432 document-symbols))) 6433 (if children? 6434 (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position)) 6435 (when symbol 6436 (list symbol))))) 6437 6438 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?)) 6439 "Convert a SymbolInformation to a DocumentInformation" 6440 (lsp-make-document-symbol :name name 6441 :kind kind 6442 :range (lsp:location-range location) 6443 :children? nil 6444 :deprecated? deprecated? 6445 :selection-range (lsp:location-range location) 6446 :detail? container-name?)) 6447 6448 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position) 6449 "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION." 6450 (--> symbols-informations 6451 (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range))) 6452 (when (lsp-point-in-range? current-position range) 6453 (lsp--symbol-information->document-symbol symbol))) 6454 it) 6455 (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position)) 6456 (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position))) 6457 (and (lsp--position-compare b-start-position a-start-position) 6458 (lsp--position-compare a-end-position b-end-position)))))) 6459 6460 (defun lsp--symbols->document-symbols-hierarchy (symbols) 6461 "Convert SYMBOLS to symbols-hierarchy." 6462 (when-let* ((first-symbol (lsp-seq-first symbols))) 6463 (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line) 6464 :character (plist-get (lsp--cur-position) :character)))) 6465 (if (lsp-symbol-information? first-symbol) 6466 (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position) 6467 (lsp--document-symbols->document-symbols-hierarchy symbols cur-position))))) 6468 6469 (defun lsp--xref-backend () 'xref-lsp) 6470 6471 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp))) 6472 (propertize (or (thing-at-point 'symbol) "") 6473 'identifier-at-point t)) 6474 6475 (defun lsp--xref-elements-index (symbols path) 6476 (-mapcat 6477 (-lambda (sym) 6478 (pcase-exhaustive sym 6479 ((lsp-interface DocumentSymbol :name :children? :selection-range (lsp-interface Range :start)) 6480 (cons (cons (concat path name) 6481 (lsp--position-to-point start)) 6482 (lsp--xref-elements-index children? (concat path name " / ")))) 6483 ((lsp-interface SymbolInformation :name :location (lsp-interface Location :range (lsp-interface Range :start))) 6484 (list (cons (concat path name) 6485 (lsp--position-to-point start)))))) 6486 symbols)) 6487 6488 (defvar-local lsp--symbols-cache nil) 6489 6490 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp))) 6491 (if (lsp--find-workspaces-for "textDocument/documentSymbol") 6492 (progn 6493 (setq lsp--symbols-cache (lsp--xref-elements-index 6494 (lsp--get-document-symbols) nil)) 6495 lsp--symbols-cache) 6496 (list (propertize (or (thing-at-point 'symbol) "") 6497 'identifier-at-point t)))) 6498 6499 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier) 6500 (save-excursion 6501 (unless (get-text-property 0 'identifier-at-point identifier) 6502 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6503 (user-error "Unable to find symbol %s in current document" identifier))))) 6504 (lsp--locations-to-xref-items (lsp-request "textDocument/definition" 6505 (lsp--text-document-position-params))))) 6506 6507 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier) 6508 (save-excursion 6509 (unless (get-text-property 0 'identifier-at-point identifier) 6510 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6511 (user-error "Unable to find symbol %s" identifier))))) 6512 (lsp--locations-to-xref-items (lsp-request "textDocument/references" 6513 (lsp--make-reference-params nil lsp-references-exclude-declaration))))) 6514 6515 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern) 6516 (seq-map #'lsp--symbol-information-to-xref 6517 (lsp-request "workspace/symbol" `(:query ,pattern)))) 6518 6519 (defcustom lsp-rename-use-prepare t 6520 "Whether `lsp-rename' should do a prepareRename first. 6521 For some language servers, textDocument/prepareRename might be 6522 too slow, in which case this variable may be set to nil. 6523 `lsp-rename' will then use `thing-at-point' `symbol' to determine 6524 the symbol to rename at point." 6525 :group 'lsp-mode 6526 :type 'boolean) 6527 6528 (defun lsp--get-symbol-to-rename () 6529 "Get a symbol to rename and placeholder at point. 6530 Returns a cons ((START . END) . PLACEHOLDER?), and nil if 6531 renaming is generally supported but cannot be done at point. 6532 START and END are the bounds of the identifiers being renamed, 6533 while PLACEHOLDER?, is either nil or a string suggested by the 6534 language server as the initial input of a new-name prompt." 6535 (unless (lsp-feature? "textDocument/rename") 6536 (error "The connected server(s) doesn't support renaming")) 6537 (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename")) 6538 (when-let* ((response 6539 (lsp-request "textDocument/prepareRename" 6540 (lsp--text-document-position-params)))) 6541 (let* ((bounds (lsp--range-to-region 6542 (if (lsp-range? response) 6543 response 6544 (lsp:prepare-rename-result-range response)))) 6545 (placeholder 6546 (and (not (lsp-range? response)) 6547 (lsp:prepare-rename-result-placeholder response)))) 6548 (cons bounds placeholder))) 6549 (when-let* ((bounds (bounds-of-thing-at-point 'symbol))) 6550 (cons bounds nil)))) 6551 6552 (defface lsp-face-rename '((t :underline t)) 6553 "Face used to highlight the identifier being renamed. 6554 Renaming can be done using `lsp-rename'." 6555 :group 'lsp-mode) 6556 6557 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face)) 6558 "Face used to display the rename placeholder in. 6559 When calling `lsp-rename' interactively, this will be the face of 6560 the new name." 6561 :group 'lsp-mode) 6562 6563 (defvar lsp-rename-history '() 6564 "History for `lsp--read-rename'.") 6565 6566 (defun lsp--read-rename (at-point) 6567 "Read a new name for a `lsp-rename' at `point' from the user. 6568 AT-POINT shall be a structure as returned by 6569 `lsp--get-symbol-to-rename'. 6570 6571 Returns a string, which should be the new name for the identifier 6572 at point. If renaming cannot be done at point (as determined from 6573 AT-POINT), throw a `user-error'. 6574 6575 This function is for use in `lsp-rename' only, and shall not be 6576 relied upon." 6577 (unless at-point 6578 (user-error "`lsp-rename' is invalid here")) 6579 (-let* ((((start . end) . placeholder?) at-point) 6580 ;; Do the `buffer-substring' first to not include `lsp-face-rename' 6581 (rename-me (buffer-substring start end)) 6582 (placeholder (or placeholder? rename-me)) 6583 (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face)) 6584 6585 overlay) 6586 ;; We need unwind protect, as the user might cancel here, causing the 6587 ;; overlay to linger. 6588 (unwind-protect 6589 (progn 6590 (setq overlay (make-overlay start end)) 6591 (overlay-put overlay 'face 'lsp-face-rename) 6592 6593 (read-string (format "Rename %s to: " rename-me) placeholder 6594 'lsp-rename-history)) 6595 (and overlay (delete-overlay overlay))))) 6596 6597 (defun lsp-rename (newname) 6598 "Rename the symbol (and all references to it) under point to NEWNAME." 6599 (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename)))) 6600 (when-let* ((edits (lsp-request "textDocument/rename" 6601 `( :textDocument ,(lsp--text-document-identifier) 6602 :position ,(lsp--cur-position) 6603 :newName ,newname)))) 6604 (lsp--apply-workspace-edit edits 'rename))) 6605 6606 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?) 6607 "Advice around function `rename-file'. 6608 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?. 6609 6610 This advice sends workspace/willRenameFiles before renaming file 6611 to check if server wants to apply any workspaceEdits after renamed." 6612 (if (and lsp-apply-edits-after-file-operations 6613 (lsp--send-will-rename-files-p old-name)) 6614 (let ((params (lsp-make-rename-files-params 6615 :files (vector (lsp-make-file-rename 6616 :oldUri (lsp--path-to-uri old-name) 6617 :newUri (lsp--path-to-uri new-name)))))) 6618 (when-let* ((edits (lsp-request "workspace/willRenameFiles" params))) 6619 (lsp--apply-workspace-edit edits 'rename-file) 6620 (funcall old-func old-name new-name ok-if-already-exists?) 6621 (when (lsp--send-did-rename-files-p) 6622 (lsp-notify "workspace/didRenameFiles" params)))) 6623 (funcall old-func old-name new-name ok-if-already-exists?))) 6624 6625 (advice-add 'rename-file :around #'lsp--on-rename-file) 6626 6627 (defcustom lsp-xref-force-references nil 6628 "If non-nil threat everything as references(e. g. jump if only one item.)" 6629 :group 'lsp-mode 6630 :type 'boolean) 6631 6632 (defun lsp-show-xrefs (xrefs display-action references?) 6633 (unless (region-active-p) (push-mark nil t)) 6634 (if (boundp 'xref-show-definitions-function) 6635 (with-no-warnings 6636 (xref-push-marker-stack) 6637 (funcall (if (and references? (not lsp-xref-force-references)) 6638 xref-show-xrefs-function 6639 xref-show-definitions-function) 6640 (-const xrefs) 6641 `((window . ,(selected-window)) 6642 (display-action . ,display-action) 6643 ,(if (and references? (not lsp-xref-force-references)) 6644 `(auto-jump . ,xref-auto-jump-to-first-xref) 6645 `(auto-jump . ,xref-auto-jump-to-first-definition))))) 6646 (xref--show-xrefs xrefs display-action))) 6647 6648 (cl-defmethod seq-empty-p ((ht hash-table)) 6649 "Function `seq-empty-p' for hash-table." 6650 (hash-table-empty-p ht)) 6651 6652 (cl-defun lsp-find-locations (method &optional extra &key display-action references?) 6653 "Send request named METHOD and get cross references of the symbol under point. 6654 EXTRA is a plist of extra parameters. 6655 REFERENCES? t when METHOD returns references." 6656 (let ((loc (lsp-request method 6657 (append (lsp--text-document-position-params) extra)))) 6658 (if (seq-empty-p loc) 6659 (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) "")) 6660 (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?)))) 6661 6662 (cl-defun lsp-find-declaration (&key display-action) 6663 "Find declarations of the symbol under point." 6664 (interactive) 6665 (lsp-find-locations "textDocument/declaration" nil :display-action display-action)) 6666 6667 (cl-defun lsp-find-definition (&key display-action) 6668 "Find definitions of the symbol under point." 6669 (interactive) 6670 (lsp-find-locations "textDocument/definition" nil :display-action display-action)) 6671 6672 (defun lsp-find-definition-mouse (click) 6673 "Click to start `lsp-find-definition' at clicked point." 6674 (interactive "e") 6675 (let* ((ec (event-start click)) 6676 (p1 (posn-point ec)) 6677 (w1 (posn-window ec))) 6678 (select-window w1) 6679 (goto-char p1) 6680 (lsp-find-definition))) 6681 6682 (cl-defun lsp-find-implementation (&key display-action) 6683 "Find implementations of the symbol under point." 6684 (interactive) 6685 (lsp-find-locations "textDocument/implementation" 6686 nil 6687 :display-action display-action 6688 :references? t)) 6689 6690 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action) 6691 "Find references of the symbol under point." 6692 (interactive "P") 6693 (lsp-find-locations "textDocument/references" 6694 (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-declaration))))) 6695 :display-action display-action 6696 :references? t)) 6697 6698 (cl-defun lsp-find-type-definition (&key display-action) 6699 "Find type definitions of the symbol under point." 6700 (interactive) 6701 (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action)) 6702 6703 (defalias 'lsp-find-custom #'lsp-find-locations) 6704 (defalias 'lsp-goto-implementation #'lsp-find-implementation) 6705 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition) 6706 6707 (with-eval-after-load 'evil 6708 (evil-set-command-property 'lsp-find-definition :jump t) 6709 (evil-set-command-property 'lsp-find-implementation :jump t) 6710 (evil-set-command-property 'lsp-find-references :jump t) 6711 (evil-set-command-property 'lsp-find-type-definition :jump t)) 6712 6713 (defun lsp--workspace-method-supported? (check-command method capability workspace) 6714 (with-lsp-workspace workspace 6715 (if check-command 6716 (funcall check-command workspace) 6717 (or 6718 (when capability (lsp--capability capability)) 6719 (lsp--registered-capability method) 6720 (and (not capability) (not check-command)))))) 6721 6722 (defun lsp-disable-method-for-server (method server-id) 6723 "Disable METHOD for SERVER-ID." 6724 (cl-callf 6725 (lambda (reqs) 6726 (-let (((&plist :check-command :capability) reqs)) 6727 (list :check-command 6728 (lambda (workspace) 6729 (unless (-> workspace 6730 lsp--workspace-client 6731 lsp--client-server-id 6732 (eq server-id)) 6733 (lsp--workspace-method-supported? check-command 6734 method 6735 capability 6736 workspace)))))) 6737 (alist-get method lsp-method-requirements nil nil 'string=))) 6738 6739 (defun lsp--find-workspaces-for (msg-or-method) 6740 "Find all workspaces in the current project that can handle MSG." 6741 (let ((method (if (stringp msg-or-method) 6742 msg-or-method 6743 (plist-get msg-or-method :method)))) 6744 (-if-let (reqs (cdr (assoc method lsp-method-requirements))) 6745 (-let (((&plist :capability :check-command) reqs)) 6746 (-filter 6747 (-partial #'lsp--workspace-method-supported? 6748 check-command method capability) 6749 (lsp-workspaces))) 6750 (lsp-workspaces)))) 6751 6752 (defun lsp-can-execute-command? (command-name) 6753 "Returns non-nil if current language server(s) can execute COMMAND-NAME. 6754 The command is executed via `workspace/executeCommand'" 6755 (cl-position 6756 command-name 6757 (lsp:execute-command-options-commands 6758 (lsp:server-capabilities-execute-command-provider? 6759 (lsp--server-capabilities))) 6760 :test #'equal)) 6761 6762 (defalias 'lsp-feature? 'lsp--find-workspaces-for) 6763 6764 (cl-defmethod lsp-execute-command (_server _command _arguments) 6765 "Dispatch COMMAND execution." 6766 (signal 'cl-no-applicable-method nil)) 6767 6768 (defun lsp-workspace-command-execute (command &optional args) 6769 "Execute workspace COMMAND with ARGS." 6770 (condition-case-unless-debug err 6771 (let ((params (if args 6772 (list :command command :arguments args) 6773 (list :command command)))) 6774 (lsp-request "workspace/executeCommand" params)) 6775 (error 6776 (error "`workspace/executeCommand' with `%s' failed.\n\n%S" 6777 command err)))) 6778 6779 (defun lsp-send-execute-command (command &optional args) 6780 "Create and send a `workspace/executeCommand' message having command COMMAND 6781 and optional ARGS." 6782 (lsp-workspace-command-execute command args)) 6783 6784 (defalias 'lsp-point-to-position #'lsp--point-to-position) 6785 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier) 6786 (defalias 'lsp--send-execute-command #'lsp-send-execute-command) 6787 (defalias 'lsp-on-open #'lsp--text-document-did-open) 6788 (defalias 'lsp-on-save #'lsp--text-document-did-save) 6789 6790 (defun lsp--set-configuration (settings) 6791 "Set the SETTINGS for the lsp server." 6792 (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings))) 6793 6794 (defun lsp-current-buffer () 6795 (or lsp--virtual-buffer 6796 (current-buffer))) 6797 6798 (defun lsp-buffer-live-p (buffer-id) 6799 (if-let* ((buffer-live (plist-get buffer-id :buffer-live?))) 6800 (funcall buffer-live buffer-id) 6801 (buffer-live-p buffer-id))) 6802 6803 (defun lsp--on-set-visited-file-name (old-func &rest args) 6804 "Advice around function `set-visited-file-name'. 6805 6806 This advice sends textDocument/didClose for the old file and 6807 textDocument/didOpen for the new file." 6808 (when lsp--cur-workspace 6809 (lsp--text-document-did-close t)) 6810 (prog1 (apply old-func args) 6811 (when lsp--cur-workspace 6812 (lsp--text-document-did-open)))) 6813 6814 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name) 6815 6816 (defcustom lsp-flush-delayed-changes-before-next-message t 6817 "If non-nil send the document changes update before sending other messages. 6818 6819 If nil, and `lsp-debounce-full-sync-notifications' is non-nil, 6820 change notifications will be throttled by 6821 `lsp-debounce-full-sync-notifications-interval' regardless of 6822 other messages." 6823 :group 'lsp-mode 6824 :type 'boolean) 6825 6826 (defvar lsp--not-flushing-delayed-changes t) 6827 6828 (defun lsp--send-no-wait (message proc) 6829 "Send MESSAGE to PROC without waiting for further output." 6830 6831 (when (and lsp--not-flushing-delayed-changes 6832 lsp-flush-delayed-changes-before-next-message) 6833 (let ((lsp--not-flushing-delayed-changes nil)) 6834 (lsp--flush-delayed-changes))) 6835 (lsp-process-send proc message)) 6836 6837 (define-error 'lsp-parse-error 6838 "Error parsing message from language server" 'lsp-error) 6839 (define-error 'lsp-unknown-message-type 6840 "Unknown message type" '(lsp-error lsp-parse-error)) 6841 (define-error 'lsp-unknown-json-rpc-version 6842 "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error)) 6843 (define-error 'lsp-no-content-length 6844 "Content-Length header missing in message" '(lsp-error lsp-parse-error)) 6845 (define-error 'lsp-invalid-header-name 6846 "Invalid header name" '(lsp-error lsp-parse-error)) 6847 6848 ;; id method 6849 ;; x x request 6850 ;; x . response 6851 ;; . x notification 6852 (defun lsp--get-message-type (json-data) 6853 "Get the message type from JSON-DATA." 6854 (if (lsp:json-message-id? json-data) 6855 (if (lsp:json-message-error? json-data) 6856 'response-error 6857 (if (lsp:json-message-method? json-data) 6858 'request 6859 'response)) 6860 'notification)) 6861 6862 (defconst lsp--default-notification-handlers 6863 (ht ("window/showMessage" #'lsp--window-show-message) 6864 ("window/logMessage" #'lsp--window-log-message) 6865 ("window/showInputBox" #'lsp--window-show-input-box) 6866 ("window/showQuickPick" #'lsp--window-show-quick-pick) 6867 ("textDocument/publishDiagnostics" #'lsp--on-diagnostics) 6868 ("textDocument/diagnosticsEnd" #'ignore) 6869 ("textDocument/diagnosticsBegin" #'ignore) 6870 ("telemetry/event" #'ignore) 6871 ("$/progress" (lambda (workspace params) 6872 (funcall lsp-progress-function workspace params))))) 6873 6874 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method)) 6875 "Call the appropriate handler for NOTIFICATION." 6876 (-let ((client (lsp--workspace-client workspace))) 6877 (when (lsp--log-io-p method) 6878 (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif) 6879 lsp--cur-workspace)) 6880 (if-let* ((handler (or (gethash method (lsp--client-notification-handlers client)) 6881 (gethash method lsp--default-notification-handlers)))) 6882 (funcall handler workspace params) 6883 (when (and method (not (string-prefix-p "$" method))) 6884 (lsp-warn "Unknown notification: %s" method))))) 6885 6886 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items)) 6887 "Get section configuration. 6888 PARAMS are the `workspace/configuration' request params" 6889 (->> items 6890 (-map (-lambda ((&ConfigurationItem :section?)) 6891 (-let* ((path-parts (split-string section? "\\.")) 6892 (path-without-last (s-join "." (-slice path-parts 0 -1))) 6893 (path-parts-len (length path-parts))) 6894 (cond 6895 ((<= path-parts-len 1) 6896 (ht-get (lsp-configuration-section section?) 6897 (car-safe path-parts) 6898 (ht-create))) 6899 ((> path-parts-len 1) 6900 (when-let* ((section (lsp-configuration-section path-without-last)) 6901 (keys path-parts)) 6902 (while (and keys section) 6903 (setf section (ht-get section (pop keys)))) 6904 section)))))) 6905 (apply #'vector))) 6906 6907 (defun lsp--ms-since (timestamp) 6908 "Integer number of milliseconds since TIMESTAMP. Fractions discarded." 6909 (floor (* 1000 (float-time (time-since timestamp))))) 6910 6911 (defun lsp--send-request-response (workspace recv-time request response) 6912 "Send the RESPONSE for REQUEST in WORKSPACE and log if needed." 6913 (-let* (((&JSONResponse :params :method :id) request) 6914 (process (lsp--workspace-proc workspace)) 6915 (response (lsp--make-response id response)) 6916 (req-entry (and lsp-log-io 6917 (lsp--make-log-entry method id params 'incoming-req))) 6918 (resp-entry (and lsp-log-io 6919 (lsp--make-log-entry method id response 'outgoing-resp 6920 (lsp--ms-since recv-time))))) 6921 ;; Send response to the server. 6922 (when (lsp--log-io-p method) 6923 (lsp--log-entry-new req-entry workspace) 6924 (lsp--log-entry-new resp-entry workspace)) 6925 (lsp--send-no-wait response process))) 6926 6927 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method)) 6928 "Call the appropriate handler for REQUEST, and send the return value to the 6929 server. WORKSPACE is the active workspace." 6930 (-let* ((recv-time (current-time)) 6931 (client (lsp--workspace-client workspace)) 6932 (buffers (lsp--workspace-buffers workspace)) 6933 handler 6934 (response (cond 6935 ((setq handler (gethash method (lsp--client-request-handlers client) nil)) 6936 (funcall handler workspace params)) 6937 ((setq handler (gethash method (lsp--client-async-request-handlers client) nil)) 6938 (funcall handler workspace params 6939 (-partial #'lsp--send-request-response 6940 workspace recv-time request)) 6941 'delay-response) 6942 ((equal method "client/registerCapability") 6943 (mapc #'lsp--server-register-capability 6944 (lsp:registration-params-registrations params)) 6945 (mapc (lambda (buf) 6946 (when (lsp-buffer-live-p buf) 6947 (lsp-with-current-buffer buf 6948 (lsp-unconfig-buffer) 6949 (lsp-configure-buffer)))) 6950 buffers) 6951 nil) 6952 ((equal method "window/showMessageRequest") 6953 (let ((choice (lsp--window-log-message-request params))) 6954 `(:title ,choice))) 6955 ((equal method "window/showDocument") 6956 (let ((success? (lsp--window-show-document params))) 6957 (lsp-make-show-document-result :success (or success? 6958 :json-false)))) 6959 ((equal method "client/unregisterCapability") 6960 (mapc #'lsp--server-unregister-capability 6961 (lsp:unregistration-params-unregisterations params)) 6962 (mapc (lambda (buf) 6963 (when (lsp-buffer-live-p buf) 6964 (lsp-with-current-buffer buf 6965 (lsp-unconfig-buffer) 6966 (lsp-configure-buffer)))) 6967 buffers) 6968 nil) 6969 ((equal method "workspace/applyEdit") 6970 (list :applied (condition-case err 6971 (prog1 t 6972 (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested)) 6973 (error 6974 (lsp--error "Failed to apply edits with message %s" 6975 (error-message-string err)) 6976 :json-false)))) 6977 ((equal method "workspace/configuration") 6978 (with-lsp-workspace workspace 6979 (if-let* ((buf (car buffers))) 6980 (lsp-with-current-buffer buf 6981 (lsp--build-workspace-configuration-response params)) 6982 (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace) 6983 (lsp--build-workspace-configuration-response params))))) 6984 ((equal method "workspace/workspaceFolders") 6985 (let ((folders (or (-> workspace 6986 (lsp--workspace-client) 6987 (lsp--client-server-id) 6988 (gethash (lsp-session-server-id->folders (lsp-session)))) 6989 (lsp-session-folders (lsp-session))))) 6990 (->> folders 6991 (-distinct) 6992 (-map (lambda (folder) 6993 (list :uri (lsp--path-to-uri folder)))) 6994 (apply #'vector)))) 6995 ((equal method "window/workDoneProgress/create") 6996 nil ;; no specific reply, no processing required 6997 ) 6998 ((equal method "workspace/semanticTokens/refresh") 6999 (when (and lsp-semantic-tokens-enable 7000 (fboundp 'lsp--semantic-tokens-on-refresh)) 7001 (lsp--semantic-tokens-on-refresh workspace)) 7002 nil) 7003 ((equal method "workspace/codeLens/refresh") 7004 (when (and lsp-lens-enable 7005 (fboundp 'lsp--lens-on-refresh)) 7006 (lsp--lens-on-refresh workspace)) 7007 nil) 7008 ((equal method "workspace/diagnostic/refresh") 7009 nil) 7010 (t (lsp-warn "Unknown request method: %s" method) nil)))) 7011 ;; Send response to the server. 7012 (unless (eq response 'delay-response) 7013 (lsp--send-request-response workspace recv-time request response)))) 7014 7015 (lsp-defun lsp--error-string ((&JSONError :message :code)) 7016 "Format ERR as a user friendly string." 7017 (format "Error from the Language Server: %s (%s)" 7018 message 7019 (or (car (alist-get code lsp--errors)) "Unknown error"))) 7020 7021 (defun lsp--get-body-length (headers) 7022 (let ((content-length (cdr (assoc "Content-Length" headers)))) 7023 (if content-length 7024 (string-to-number content-length) 7025 7026 ;; This usually means either the server or our parser is 7027 ;; screwed up with a previous Content-Length 7028 (error "No Content-Length header")))) 7029 7030 (defun lsp--parse-header (s) 7031 "Parse string S as a LSP (KEY . VAL) header." 7032 (let ((pos (string-match "\:" s)) 7033 key val) 7034 (unless pos 7035 (signal 'lsp-invalid-header-name (list s))) 7036 (setq key (substring s 0 pos) 7037 val (s-trim-left (substring s (+ 1 pos)))) 7038 (when (equal key "Content-Length") 7039 (cl-assert (cl-loop for c across val 7040 when (or (> c ?9) (< c ?0)) return nil 7041 finally return t) 7042 nil (format "Invalid Content-Length value: %s" val))) 7043 (cons key val))) 7044 7045 (defmacro lsp--read-json (str) 7046 "Read json string STR." 7047 (if (progn 7048 (require 'json) 7049 (fboundp 'json-parse-string)) 7050 `(json-parse-string ,str 7051 :object-type (if lsp-use-plists 7052 'plist 7053 'hash-table) 7054 :null-object nil 7055 :false-object nil) 7056 `(let ((json-array-type 'vector) 7057 (json-object-type (if lsp-use-plists 7058 'plist 7059 'hash-table)) 7060 (json-false nil)) 7061 (json-read-from-string ,str)))) 7062 7063 (defmacro lsp-json-read-buffer () 7064 "Read json from the current buffer." 7065 (if (progn 7066 (require 'json) 7067 (fboundp 'json-parse-buffer)) 7068 `(json-parse-buffer :object-type (if lsp-use-plists 7069 'plist 7070 'hash-table) 7071 :null-object nil 7072 :false-object nil) 7073 `(let ((json-array-type 'vector) 7074 (json-object-type (if lsp-use-plists 7075 'plist 7076 'hash-table)) 7077 (json-false nil)) 7078 (json-read)))) 7079 7080 (defun lsp--read-json-file (file-path) 7081 "Read json file." 7082 (-> file-path 7083 (f-read-text) 7084 (lsp--read-json))) 7085 7086 (defun lsp--parser-on-message (json-data workspace) 7087 "Called when the parser P read a complete MSG from the server." 7088 (with-demoted-errors "Error processing message %S." 7089 (with-lsp-workspace workspace 7090 (let* ((client (lsp--workspace-client workspace)) 7091 (id (--when-let (lsp:json-response-id json-data) 7092 (if (stringp it) (string-to-number it) it))) 7093 (data (lsp:json-response-result json-data))) 7094 (pcase (lsp--get-message-type json-data) 7095 ('response 7096 (cl-assert id) 7097 (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))] 7098 (when (lsp--log-io-p method) 7099 (lsp--log-entry-new 7100 (lsp--make-log-entry method id data 'incoming-resp 7101 (lsp--ms-since before-send)) 7102 workspace)) 7103 (when callback 7104 (remhash id (lsp--client-response-handlers client)) 7105 (funcall callback (lsp:json-response-result json-data))))) 7106 ('response-error 7107 (cl-assert id) 7108 (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))] 7109 (when (lsp--log-io-p method) 7110 (lsp--log-entry-new 7111 (lsp--make-log-entry method id (lsp:json-response-error-error json-data) 7112 'incoming-resp (lsp--ms-since before-send)) 7113 workspace)) 7114 (when callback 7115 (remhash id (lsp--client-response-handlers client)) 7116 (funcall callback (lsp:json-response-error-error json-data))))) 7117 ('notification 7118 (lsp--on-notification workspace json-data)) 7119 ('request (lsp--on-request workspace json-data))))))) 7120 7121 (defun lsp--create-filter-function (workspace) 7122 "Make filter for the workspace." 7123 (let ((body-received 0) 7124 leftovers body-length body chunk) 7125 (lambda (_proc input) 7126 (setf chunk (if (s-blank? leftovers) 7127 (encode-coding-string input 'utf-8-unix t) 7128 (concat leftovers (encode-coding-string input 'utf-8-unix t)))) 7129 7130 (let (messages) 7131 (while (not (s-blank? chunk)) 7132 (if (not body-length) 7133 ;; Read headers 7134 (if-let* ((body-sep-pos (string-match-p "\r\n\r\n" chunk))) 7135 ;; We've got all the headers, handle them all at once: 7136 (setf body-length (lsp--get-body-length 7137 (mapcar #'lsp--parse-header 7138 (split-string 7139 (substring-no-properties chunk 7140 (or (string-match-p "Content-Length" chunk) 7141 (error "Unable to find Content-Length header.")) 7142 body-sep-pos) 7143 "\r\n"))) 7144 body-received 0 7145 leftovers nil 7146 chunk (substring-no-properties chunk (+ body-sep-pos 4))) 7147 7148 ;; Haven't found the end of the headers yet. Save everything 7149 ;; for when the next chunk arrives and await further input. 7150 (setf leftovers chunk 7151 chunk nil)) 7152 (let* ((chunk-length (string-bytes chunk)) 7153 (left-to-receive (- body-length body-received)) 7154 (this-body (if (< left-to-receive chunk-length) 7155 (prog1 (substring-no-properties chunk 0 left-to-receive) 7156 (setf chunk (substring-no-properties chunk left-to-receive))) 7157 (prog1 chunk 7158 (setf chunk nil)))) 7159 (body-bytes (string-bytes this-body))) 7160 (push this-body body) 7161 (setf body-received (+ body-received body-bytes)) 7162 (when (>= chunk-length left-to-receive) 7163 (condition-case err 7164 (with-temp-buffer 7165 (apply #'insert 7166 (nreverse 7167 (prog1 body 7168 (setf leftovers nil 7169 body-length nil 7170 body-received nil 7171 body nil)))) 7172 (decode-coding-region (point-min) 7173 (point-max) 7174 'utf-8) 7175 (goto-char (point-min)) 7176 (push (lsp-json-read-buffer) messages)) 7177 7178 (error 7179 (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s" 7180 (concat leftovers input) 7181 err))))))) 7182 (mapc (lambda (msg) 7183 (lsp--parser-on-message msg workspace)) 7184 (nreverse messages)))))) 7185 7186 (defvar-local lsp--line-col-to-point-hash-table nil 7187 "Hash table with keys (line . col) and values that are either point positions 7188 or markers.") 7189 7190 (defcustom lsp-imenu-detailed-outline t 7191 "Whether `lsp-imenu' should include signatures. 7192 This will be ignored if the server doesn't provide the necessary 7193 information, for example if it doesn't support DocumentSymbols." 7194 :group 'lsp-imenu 7195 :type 'boolean) 7196 7197 (defcustom lsp-imenu-hide-parent-details t 7198 "Whether `lsp-imenu' should hide signatures of parent nodes." 7199 :group 'lsp-imenu 7200 :type 'boolean) 7201 7202 (defface lsp-details-face '((t :height 0.8 :inherit shadow)) 7203 "Used to display additional information throughout `lsp'. 7204 Things like line numbers, signatures, ... are considered 7205 additional information. Often, additional faces are defined that 7206 inherit from this face by default, like `lsp-signature-face', and 7207 they may be customized for finer control." 7208 :group 'lsp-mode) 7209 7210 (defface lsp-signature-face '((t :inherit lsp-details-face)) 7211 "Used to display signatures in `imenu', ...." 7212 :group 'lsp-mode) 7213 7214 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?) 7215 show-detail?) 7216 "Render INPUT0, an `&DocumentSymbol', to a string. 7217 If SHOW-DETAIL? is set, make use of its `:detail?' field (often 7218 the signature)." 7219 (let ((detail (and show-detail? (s-present? detail?) 7220 (propertize (concat " " (s-trim-left detail?)) 7221 'face 'lsp-signature-face))) 7222 (name (if deprecated? 7223 (propertize name 'face 'lsp-face-semhl-deprecated) name))) 7224 (concat name detail))) 7225 7226 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?) 7227 separator) 7228 "Render a piece of SymbolInformation. 7229 Handle :deprecated?. If SEPARATOR is non-nil, the 7230 symbol's (optional) parent, SEPARATOR and the symbol itself are 7231 concatenated." 7232 (when (and separator container-name? (not (string-empty-p container-name?))) 7233 (setq name (concat name separator container-name?))) 7234 (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name)) 7235 7236 (defun lsp--symbol-to-imenu-elem (sym) 7237 "Convert SYM to imenu element. 7238 7239 SYM is a SymbolInformation message. 7240 7241 Return a cons cell (full-name . start-point)." 7242 (let ((start-point (ht-get lsp--line-col-to-point-hash-table 7243 (lsp--get-line-and-col sym)))) 7244 (cons (lsp-render-symbol-information 7245 sym (and lsp-imenu-show-container-name 7246 lsp-imenu-container-name-separator)) 7247 start-point))) 7248 7249 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?)) 7250 "Convert SYM to hierarchical imenu elements. 7251 7252 SYM is a DocumentSymbol message. 7253 7254 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if 7255 SYM doesn't have any children. Otherwise return a cons cell with 7256 an alist 7257 7258 (\"symbol-name\" . ((\"(symbol-kind)\" . start-point) 7259 cons-cells-from-children))" 7260 (let ((filtered-children (lsp--imenu-filter-symbols children?)) 7261 (signature (lsp-render-symbol sym lsp-imenu-detailed-outline))) 7262 (if (seq-empty-p filtered-children) 7263 (cons signature 7264 (ht-get lsp--line-col-to-point-hash-table 7265 (lsp--get-line-and-col sym))) 7266 (cons signature 7267 (lsp--imenu-create-hierarchical-index filtered-children))))) 7268 7269 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind)) 7270 "Determine if SYM is for the current document and is to be shown." 7271 ;; It's a SymbolInformation or DocumentSymbol, which is always in the 7272 ;; current buffer file. 7273 (and lsp-imenu-index-symbol-kinds 7274 (numberp kind) 7275 (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup)) 7276 kind 7277 0))) 7278 (not (memql (aref lsp/symbol-kind-lookup clamped-kind) 7279 lsp-imenu-index-symbol-kinds))))) 7280 7281 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind)) 7282 "The string name of the kind of SYM." 7283 (alist-get kind lsp-symbol-kinds "Other")) 7284 7285 (defun lsp--get-line-and-col (sym) 7286 "Obtain the line and column corresponding to SYM." 7287 (-let* ((location (lsp:symbol-information-location sym)) 7288 (name-range (or (and location (lsp:location-range location)) 7289 (lsp:document-symbol-selection-range sym))) 7290 ((&Range :start (&Position :line :character)) name-range)) 7291 (cons line character))) 7292 7293 (defun lsp--collect-lines-and-cols (symbols) 7294 "Return a sorted list ((line . col) ...) of the locations of SYMBOLS." 7295 (let ((stack (mapcar 'identity symbols)) 7296 line-col-list) 7297 (while stack 7298 (let ((sym (pop stack))) 7299 (push (lsp--get-line-and-col sym) line-col-list) 7300 (unless (seq-empty-p (lsp:document-symbol-children? sym)) 7301 (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack))))) 7302 (-sort #'lsp--line-col-comparator line-col-list))) 7303 7304 (defun lsp--convert-line-col-to-points-batch (line-col-list) 7305 "Convert a sorted list of positions from line-column 7306 representation to point representation." 7307 (let ((line-col-to-point-map (ht-create)) 7308 (inhibit-field-text-motion t) 7309 (curr-line 0)) 7310 (lsp-save-restriction-and-excursion 7311 (goto-char (point-min)) 7312 (cl-loop for (line . col) in line-col-list do 7313 (forward-line (- line curr-line)) 7314 (setq curr-line line) 7315 (let ((line-end (line-end-position))) 7316 (if (or (not col) (> col (- line-end (point)))) 7317 (goto-char line-end) 7318 (forward-char col))) 7319 (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers 7320 (point-marker) 7321 (point))))) 7322 line-col-to-point-map)) 7323 7324 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2)) 7325 (or (< l1 l2) 7326 (and (= l1 l2) 7327 (cond ((and c1 c2) 7328 (< c1 c2)) 7329 (c1 t))))) 7330 7331 (defun lsp-imenu-create-uncategorized-index (symbols) 7332 "Create imenu index from document SYMBOLS. 7333 This function, unlike `lsp-imenu-create-categorized-index', does 7334 not categorize by type, but instead returns an `imenu' index 7335 corresponding to the symbol hierarchy returned by the server 7336 directly." 7337 (let* ((lsp--line-col-to-point-hash-table (-> symbols 7338 lsp--collect-lines-and-cols 7339 lsp--convert-line-col-to-points-batch))) 7340 (if (lsp--imenu-hierarchical-p symbols) 7341 (lsp--imenu-create-hierarchical-index symbols) 7342 (lsp--imenu-create-non-hierarchical-index symbols)))) 7343 7344 (defcustom lsp-imenu-symbol-kinds 7345 '((1 . "Files") 7346 (2 . "Modules") 7347 (3 . "Namespaces") 7348 (4 . "Packages") 7349 (5 . "Classes") 7350 (6 . "Methods") 7351 (7 . "Properties") 7352 (8 . "Fields") 7353 (9 . "Constructors") 7354 (10 . "Enums") 7355 (11 . "Interfaces") 7356 (12 . "Functions") 7357 (13 . "Variables") 7358 (14 . "Constants") 7359 (15 . "Strings") 7360 (16 . "Numbers") 7361 (17 . "Booleans") 7362 (18 . "Arrays") 7363 (19 . "Objects") 7364 (20 . "Keys") 7365 (21 . "Nulls") 7366 (22 . "Enum Members") 7367 (23 . "Structs") 7368 (24 . "Events") 7369 (25 . "Operators") 7370 (26 . "Type Parameters")) 7371 "`lsp-symbol-kinds', but only used by `imenu'. 7372 A new variable is needed, as it is `imenu' convention to use 7373 pluralized categories, which `lsp-symbol-kinds' doesn't. If the 7374 non-pluralized names are preferred, this can be set to 7375 `lsp-symbol-kinds'." 7376 :type '(alist :key-type integer :value-type string)) 7377 7378 (defun lsp--imenu-kind->name (kind) 7379 (alist-get kind lsp-imenu-symbol-kinds "?")) 7380 7381 (defun lsp-imenu-create-top-level-categorized-index (symbols) 7382 "Create an `imenu' index categorizing SYMBOLS by type. 7383 Only root symbols are categorized. 7384 7385 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS 7386 shall be a list of DocumentSymbols or SymbolInformation." 7387 (mapcan 7388 (-lambda ((type . symbols)) 7389 (let ((cat (lsp--imenu-kind->name type)) 7390 (symbols (lsp-imenu-create-uncategorized-index symbols))) 7391 ;; If there is no :kind (this is being defensive), or we couldn't look it 7392 ;; up, just display the symbols inline, without categories. 7393 (if cat (list (cons cat symbols)) symbols))) 7394 (sort (seq-group-by #'lsp:document-symbol-kind symbols) 7395 (-lambda ((kinda) (kindb)) (< kinda kindb))))) 7396 7397 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start))) 7398 "Convert an `&DocumentSymbol' to an `imenu' entry." 7399 (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start)) 7400 7401 (defun lsp--imenu-create-categorized-index-1 (symbols) 7402 "Returns an `imenu' index from SYMBOLS categorized by type. 7403 The result looks like this: ((\"Variables\" . (...)))." 7404 (->> 7405 symbols 7406 (mapcan 7407 (-lambda ((sym &as &DocumentSymbol :kind :children?)) 7408 (if (seq-empty-p children?) 7409 (list (list kind (lsp--symbol->imenu sym))) 7410 (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline 7411 (not lsp-imenu-hide-parent-details))))) 7412 (cons 7413 (list kind (lsp--symbol->imenu sym)) 7414 (mapcar (-lambda ((type . imenu-items)) 7415 (list type (cons parent (mapcan #'cdr imenu-items)))) 7416 (-group-by #'car (lsp--imenu-create-categorized-index-1 children?)))))))) 7417 (-group-by #'car) 7418 (mapcar 7419 (-lambda ((kind . syms)) 7420 (cons kind (mapcan #'cdr syms)))))) 7421 7422 (defun lsp--imenu-create-categorized-index (symbols) 7423 (let ((syms (lsp--imenu-create-categorized-index-1 symbols))) 7424 (dolist (sym syms) 7425 (setcar sym (lsp--imenu-kind->name (car sym)))) 7426 syms)) 7427 7428 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start)))) 7429 (cons (lsp-render-symbol-information sym nil) start)) 7430 7431 (defun lsp--imenu-create-categorized-index-flat (symbols) 7432 "Create a kind-categorized index for SymbolInformation." 7433 (mapcar (-lambda ((kind . syms)) 7434 (cons (lsp--imenu-kind->name kind) 7435 (mapcan (-lambda ((parent . children)) 7436 (let ((children (mapcar #'lsp--symbol-information->imenu children))) 7437 (if parent (list (cons parent children)) children))) 7438 (-group-by #'lsp:symbol-information-container-name? syms)))) 7439 (seq-group-by #'lsp:symbol-information-kind symbols))) 7440 7441 (defun lsp-imenu-create-categorized-index (symbols) 7442 (if (lsp--imenu-hierarchical-p symbols) 7443 (lsp--imenu-create-categorized-index symbols) 7444 (lsp--imenu-create-categorized-index-flat symbols))) 7445 7446 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index 7447 "Function that should create an `imenu' index. 7448 It will be called with a list of SymbolInformation or 7449 DocumentSymbols, whose first level is already filtered. It shall 7450 then return an appropriate `imenu' index (see 7451 `imenu-create-index-function'). 7452 7453 Note that this interface is not stable, and subject to change any 7454 time." 7455 :group 'lsp-imenu 7456 :type '(radio 7457 (const :tag "Categorize by type" 7458 lsp-imenu-create-categorized-index) 7459 (const :tag "Categorize root symbols by type" 7460 lsp-imenu-create-top-level-categorized-index) 7461 (const :tag "Uncategorized, inline entries" 7462 lsp-imenu-create-uncategorized-index) 7463 (function :tag "Custom function"))) 7464 7465 (defun lsp--imenu-create-index () 7466 "Create an `imenu' index based on the language server. 7467 Respects `lsp-imenu-index-function'." 7468 (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols)))) 7469 (funcall lsp-imenu-index-function symbols))) 7470 7471 (defun lsp--imenu-filter-symbols (symbols) 7472 "Filter out unsupported symbols from SYMBOLS." 7473 (seq-remove #'lsp--symbol-ignore symbols)) 7474 7475 (defun lsp--imenu-hierarchical-p (symbols) 7476 "Determine whether any element in SYMBOLS has children." 7477 (seq-some #'lsp-document-symbol? symbols)) 7478 7479 (defun lsp--imenu-create-non-hierarchical-index (symbols) 7480 "Create imenu index for non-hierarchical SYMBOLS. 7481 7482 SYMBOLS are a list of DocumentSymbol messages. 7483 7484 Return a nested alist keyed by symbol names. e.g. 7485 7486 ((\"SomeClass\" (\"(Class)\" . 10) 7487 (\"someField (Field)\" . 20) 7488 (\"someFunction (Function)\" . 25) 7489 (\"SomeSubClass\" (\"(Class)\" . 30) 7490 (\"someSubField (Field)\" . 35)) 7491 (\"someFunction (Function)\" . 40))" 7492 (seq-map (lambda (nested-alist) 7493 (cons (car nested-alist) 7494 (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist)))) 7495 (seq-group-by #'lsp--get-symbol-type symbols))) 7496 7497 (defun lsp--imenu-create-hierarchical-index (symbols) 7498 "Create imenu index for hierarchical SYMBOLS. 7499 7500 SYMBOLS are a list of DocumentSymbol messages. 7501 7502 Return a nested alist keyed by symbol names. e.g. 7503 7504 ((\"SomeClass\" (\"(Class)\" . 10) 7505 (\"someField (Field)\" . 20) 7506 (\"someFunction (Function)\" . 25) 7507 (\"SomeSubClass\" (\"(Class)\" . 30) 7508 (\"someSubField (Field)\" . 35)) 7509 (\"someFunction (Function)\" . 40))" 7510 (seq-map #'lsp--symbol-to-hierarchical-imenu-elem 7511 (seq-sort #'lsp--imenu-symbol-lessp symbols))) 7512 7513 (defun lsp--imenu-symbol-lessp (sym1 sym2) 7514 (let* ((compare-results (mapcar (lambda (method) 7515 (funcall (alist-get method lsp--imenu-compare-function-alist) 7516 sym1 sym2)) 7517 lsp-imenu-sort-methods)) 7518 (result (seq-find (lambda (result) 7519 (not (= result 0))) 7520 compare-results 7521 0))) 7522 (and (numberp result) (< result 0)))) 7523 7524 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left) 7525 (&SymbolInformation :kind right)) 7526 "Compare SYM1 and SYM2 by kind." 7527 (- left right)) 7528 7529 (defun lsp--imenu-compare-line-col (sym1 sym2) 7530 (if (lsp--line-col-comparator 7531 (lsp--get-line-and-col sym1) 7532 (lsp--get-line-and-col sym2)) 7533 -1 7534 1)) 7535 7536 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1) 7537 (&SymbolInformation :name name2)) 7538 "Compare SYM1 and SYM2 by name." 7539 (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2)))) 7540 (if (numberp result) result 0))) 7541 7542 (defun lsp--imenu-refresh () 7543 "Force Imenu to refresh itself." 7544 (imenu--menubar-select imenu--rescan-item)) 7545 7546 (defun lsp-enable-imenu () 7547 "Use lsp-imenu for the current buffer." 7548 (imenu--cleanup) 7549 (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index) 7550 (setq-local imenu-menubar-modified-tick -1) 7551 (setq-local imenu--index-alist nil) 7552 (when menu-bar-mode 7553 (lsp--imenu-refresh))) 7554 7555 (defun lsp-resolve-final-command (command &optional test?) 7556 "Resolve final function COMMAND." 7557 (let* ((command (lsp-resolve-value command)) 7558 (command (cl-etypecase command 7559 (list 7560 (cl-assert (seq-every-p (apply-partially #'stringp) command) nil 7561 "Invalid command list") 7562 command) 7563 (string (list command))))) 7564 (if (and (file-remote-p default-directory) (not test?)) 7565 (list shell-file-name "-c" 7566 (string-join (cons "stty raw > /dev/null;" 7567 (mapcar #'shell-quote-argument command)) 7568 " ")) 7569 command))) 7570 7571 (defun lsp-server-present? (final-command) 7572 "Check whether FINAL-COMMAND is present." 7573 (let ((binary-found? (executable-find (cl-first final-command) t))) 7574 (if binary-found? 7575 (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command)) 7576 (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command))) 7577 binary-found?)) 7578 7579 (defun lsp--value-to-string (value) 7580 "Convert VALUE to a string that can be set as value in an environment 7581 variable." 7582 (cond 7583 ((stringp value) value) 7584 ((booleanp value) (if value 7585 "1" 7586 "0")) 7587 ((and (sequencep value) 7588 (seq-every-p #'stringp value)) (string-join value ":")) 7589 (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables")))) 7590 7591 (defun lsp--compute-process-environment (environment-fn) 7592 "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'. 7593 Ignore non-boolean keys whose value is nil." 7594 (let ((environment (if environment-fn 7595 (funcall environment-fn) 7596 nil))) 7597 (-flatten (cons (cl-loop for (key . value) in environment 7598 if (or (eval value) 7599 (eq (get value 'custom-type) 'boolean)) 7600 collect (concat key "=" (lsp--value-to-string 7601 (eval value)))) 7602 process-environment)))) 7603 7604 (defun lsp--default-directory-for-connection (&optional path) 7605 "Return path to be used for the working directory of a LSP process. 7606 7607 If `lsp-use-workspace-root-for-server-default-directory' is 7608 non-nil, uses `lsp-workspace-root' to find the directory 7609 corresponding to PATH, else returns `default-directory'." 7610 (if lsp-use-workspace-root-for-server-default-directory 7611 (lsp-workspace-root path) 7612 default-directory)) 7613 7614 (defun lsp--fix-remote-cmd (program) 7615 "Helper for `lsp-stdio-connection'. 7616 Originally coppied from eglot." 7617 7618 (if (file-remote-p default-directory) 7619 (list shell-file-name "-c" 7620 (string-join (cons "stty raw > /dev/null;" 7621 (mapcar #'shell-quote-argument program)) 7622 " ")) 7623 program)) 7624 7625 (defvar tramp-use-ssh-controlmaster-options) 7626 (defvar tramp-ssh-controlmaster-options) 7627 7628 (defun lsp-stdio-connection (command &optional test-command) 7629 "Returns a connection property list using COMMAND. 7630 COMMAND can be: A string, denoting the command to launch the 7631 language server. A list of strings, denoting an executable with 7632 its command line arguments. A function, that either returns a 7633 string or a list of strings. In all cases, the launched language 7634 server should send and receive messages on standard I/O. 7635 TEST-COMMAND is a function with no arguments which returns 7636 whether the command is present or not. When not specified 7637 `lsp-mode' will check whether the first element of the list 7638 returned by COMMAND is available via `executable-find'" 7639 (cl-check-type command (or string 7640 function 7641 (and list 7642 (satisfies (lambda (l) 7643 (seq-every-p (lambda (el) 7644 (stringp el)) 7645 l)))))) 7646 (list :connect (lambda (filter sentinel name environment-fn workspace) 7647 (if (and (functionp 'json-rpc-connection) 7648 (not (file-remote-p default-directory))) 7649 (lsp-json-rpc-connection workspace (lsp-resolve-final-command command)) 7650 (let ((final-command (lsp-resolve-final-command command)) 7651 (process-name (generate-new-buffer-name name)) 7652 (process-environment 7653 (lsp--compute-process-environment environment-fn))) 7654 (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name))) 7655 (default-directory (lsp--default-directory-for-connection)) 7656 (tramp-use-ssh-controlmaster-options 'suppress) 7657 (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none") 7658 (proc (make-process 7659 :name process-name 7660 :connection-type 'pipe 7661 :buffer (format "*%s*" process-name) 7662 :coding 'no-conversion 7663 :command final-command 7664 :filter filter 7665 :sentinel sentinel 7666 :stderr stderr-buf 7667 :noquery t 7668 :file-handler t))) 7669 (set-process-query-on-exit-flag proc nil) 7670 (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) 7671 (with-current-buffer (get-buffer stderr-buf) 7672 ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc. 7673 (special-mode)) 7674 (cons proc proc))))) 7675 :test? (or 7676 test-command 7677 (lambda () 7678 (lsp-server-present? (lsp-resolve-final-command command t)))))) 7679 7680 (defun lsp--open-network-stream (host port name) 7681 "Open network stream to HOST:PORT. 7682 NAME will be passed to `open-network-stream'. 7683 RETRY-COUNT is the number of the retries. 7684 SLEEP-INTERVAL is the sleep interval between each retry." 7685 (let* ((retries 0) 7686 (sleep-interval 0.01) 7687 (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval)) 7688 connection) 7689 (while (and (not connection) (< retries number-of-retries)) 7690 (condition-case err 7691 (setq connection (open-network-stream name nil host port 7692 :type 'plain 7693 :coding 'no-conversion)) 7694 (file-error 7695 (let ((inhibit-message t)) 7696 (lsp--warn "Failed to connect to %s:%s with error message %s" 7697 host 7698 port 7699 (error-message-string err)) 7700 (sleep-for sleep-interval) 7701 (cl-incf retries))))) 7702 (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port)))) 7703 7704 (defun lsp--port-available (host port) 7705 "Return non-nil if HOST and PORT are available." 7706 (condition-case _err 7707 (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain)) 7708 (file-error t))) 7709 7710 (defun lsp--find-available-port (host starting-port) 7711 "Find available port on HOST starting from STARTING-PORT." 7712 (let ((port starting-port)) 7713 (while (not (lsp--port-available host port)) 7714 (cl-incf port)) 7715 port)) 7716 7717 (defun lsp-tcp-connection (command-fn) 7718 "Returns a connection property list similar to `lsp-stdio-connection'. 7719 COMMAND-FN can only be a function that takes a single argument, a 7720 port number. It should return a command for launches a language server 7721 process listening for TCP connections on the provided port." 7722 (cl-check-type command-fn function) 7723 (list 7724 :connect (lambda (filter sentinel name environment-fn _workspace) 7725 (let* ((host "localhost") 7726 (port (lsp--find-available-port host (cl-incf lsp--tcp-port))) 7727 (command (funcall command-fn port)) 7728 (final-command (if (consp command) command (list command))) 7729 (_ (unless (lsp-server-present? final-command) 7730 (user-error (format "Couldn't find executable %s" (cl-first final-command))))) 7731 (process-environment 7732 (lsp--compute-process-environment environment-fn)) 7733 (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion 7734 :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t)) 7735 (tcp-proc (lsp--open-network-stream host port (concat name "::tcp")))) 7736 7737 ;; TODO: Same :noquery issue (see above) 7738 (set-process-query-on-exit-flag proc nil) 7739 (set-process-query-on-exit-flag tcp-proc nil) 7740 (set-process-filter tcp-proc filter) 7741 (cons tcp-proc proc))) 7742 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7743 7744 (defalias 'lsp-tcp-server 'lsp-tcp-server-command) 7745 7746 (defun lsp-tcp-server-command (command-fn) 7747 "Create tcp server connection. 7748 In this mode Emacs is TCP server and the language server connects 7749 to it. COMMAND is function with one parameter(the port) and it 7750 should return the command to start the LS server." 7751 (cl-check-type command-fn function) 7752 (list 7753 :connect (lambda (filter sentinel name environment-fn _workspace) 7754 (let* (tcp-client-connection 7755 (tcp-server (make-network-process :name (format "*tcp-server-%s*" name) 7756 :buffer (format "*tcp-server-%s*" name) 7757 :family 'ipv4 7758 :service lsp--tcp-server-port 7759 :sentinel (lambda (proc _string) 7760 (lsp-log "Language server %s is connected." name) 7761 (setf tcp-client-connection proc)) 7762 :server 't)) 7763 (port (process-contact tcp-server :service)) 7764 (final-command (funcall command-fn port)) 7765 (process-environment 7766 (lsp--compute-process-environment environment-fn)) 7767 (cmd-proc (make-process :name name 7768 :connection-type 'pipe 7769 :coding 'no-conversion 7770 :command final-command 7771 :stderr (format "*tcp-server-%s*::stderr" name) 7772 :noquery t))) 7773 (let ((retries 0)) 7774 ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds) 7775 (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds))) 7776 (lsp--info "Waiting for connection for %s, retries: %s" name retries) 7777 (sit-for 0.500) 7778 (cl-incf retries))) 7779 7780 (unless tcp-client-connection 7781 (condition-case nil (delete-process tcp-server) (error)) 7782 (condition-case nil (delete-process cmd-proc) (error)) 7783 (error "Failed to create connection to %s on port %s" name port)) 7784 (lsp--info "Successfully connected to %s" name) 7785 7786 (set-process-query-on-exit-flag cmd-proc nil) 7787 (set-process-query-on-exit-flag tcp-client-connection nil) 7788 (set-process-query-on-exit-flag tcp-server nil) 7789 7790 (set-process-filter tcp-client-connection filter) 7791 (set-process-sentinel tcp-client-connection sentinel) 7792 (cons tcp-client-connection cmd-proc))) 7793 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7794 7795 (defalias 'lsp-tramp-connection 'lsp-stdio-connection) 7796 7797 (defun lsp--auto-configure () 7798 "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed." 7799 (when (functionp 'lsp-ui-mode) 7800 (lsp-ui-mode)) 7801 7802 (if lsp-headerline-breadcrumb-enable 7803 (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode) 7804 (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)) 7805 (if lsp-modeline-code-actions-enable 7806 (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode) 7807 (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)) 7808 (if lsp-modeline-diagnostics-enable 7809 (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode) 7810 (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)) 7811 (if lsp-modeline-workspace-status-enable 7812 (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode) 7813 (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)) 7814 (if lsp-lens-enable 7815 (add-hook 'lsp-configure-hook 'lsp-lens--enable) 7816 (remove-hook 'lsp-configure-hook 'lsp-lens--enable)) 7817 (if lsp-semantic-tokens-enable 7818 (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable) 7819 (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)) 7820 7821 ;; yas-snippet config 7822 (setq-local yas-inhibit-overlay-modification-protection t)) 7823 7824 (defun lsp--restart-if-needed (workspace) 7825 "Handler restart for WORKSPACE." 7826 (when (or (eq lsp-restart 'auto-restart) 7827 (eq (lsp--workspace-shutdown-action workspace) 'restart) 7828 (and (eq lsp-restart 'interactive) 7829 (let ((query (format 7830 "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?" 7831 (lsp--workspace-print workspace)))) 7832 (y-or-n-p query)))) 7833 (--each (lsp--workspace-buffers workspace) 7834 (when (lsp-buffer-live-p it) 7835 (lsp-with-current-buffer it 7836 (if lsp--buffer-deferred 7837 (lsp-deferred) 7838 (lsp--info "Restarting LSP in buffer %s" (buffer-name)) 7839 (lsp))))))) 7840 7841 (defun lsp--update-key (table key fn) 7842 "Apply FN on value corresponding to KEY in TABLE." 7843 (let ((existing-value (gethash key table))) 7844 (if-let* ((new-value (funcall fn existing-value))) 7845 (puthash key new-value table) 7846 (remhash key table)))) 7847 7848 (defun lsp--process-sentinel (workspace process exit-str) 7849 "Create the sentinel for WORKSPACE." 7850 (unless (process-live-p process) 7851 (lsp--handle-process-exit workspace exit-str))) 7852 7853 (defun lsp--handle-process-exit (workspace exit-str) 7854 (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session))) 7855 (proc (lsp--workspace-proc workspace))) 7856 (lsp--warn "%s has exited (%s)" 7857 (lsp-process-name proc) 7858 (string-trim-right (or exit-str ""))) 7859 (with-lsp-workspace workspace 7860 ;; Clean workspace related data in each of the buffers 7861 ;; in the workspace. 7862 (--each (lsp--workspace-buffers workspace) 7863 (when (lsp-buffer-live-p it) 7864 (lsp-with-current-buffer it 7865 (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces)) 7866 (lsp--uninitialize-workspace) 7867 (lsp--spinner-stop) 7868 (lsp--remove-overlays 'lsp-highlight)))) 7869 7870 ;; Cleanup session from references to the closed workspace. 7871 (--each (hash-table-keys folder->workspaces) 7872 (lsp--update-key folder->workspaces it (apply-partially 'delete workspace))) 7873 7874 (lsp-process-cleanup proc)) 7875 7876 (run-hook-with-args 'lsp-after-uninitialized-functions workspace) 7877 7878 (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown) 7879 (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace)) 7880 (lsp--restart-if-needed workspace)) 7881 (lsp--cleanup-hanging-watches))) 7882 7883 (defun lsp-workspace-folders (workspace) 7884 "Return all folders associated with WORKSPACE." 7885 (let (result) 7886 (->> (lsp-session) 7887 (lsp-session-folder->servers) 7888 (maphash (lambda (folder workspaces) 7889 (when (-contains? workspaces workspace) 7890 (push folder result))))) 7891 result)) 7892 7893 (defun lsp--start-workspace (session client-template root &optional initialization-options) 7894 "Create new workspace for CLIENT-TEMPLATE with project root ROOT. 7895 INITIALIZATION-OPTIONS are passed to initialize function. 7896 SESSION is the active session." 7897 (lsp--spinner-start) 7898 (-let* ((default-directory root) 7899 (client (copy-lsp--client client-template)) 7900 (workspace (make-lsp--workspace 7901 :root root 7902 :client client 7903 :status 'starting 7904 :buffers (list (lsp-current-buffer)) 7905 :host-root (file-remote-p root))) 7906 ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities 7907 'multi-root 'initialized-fn) client) 7908 ((proc . cmd-proc) (funcall 7909 (or (plist-get new-connection :connect) 7910 (user-error "Client %s is configured incorrectly" client)) 7911 (lsp--create-filter-function workspace) 7912 (apply-partially #'lsp--process-sentinel workspace) 7913 (format "%s" server-id) 7914 environment-fn 7915 workspace)) 7916 (workspace-folders (gethash server-id (lsp-session-server-id->folders session)))) 7917 (setf (lsp--workspace-proc workspace) proc 7918 (lsp--workspace-cmd-proc workspace) cmd-proc) 7919 7920 ;; update (lsp-session-folder->servers) depending on whether we are starting 7921 ;; multi/single folder workspace 7922 (mapc (lambda (project-root) 7923 (->> session 7924 (lsp-session-folder->servers) 7925 (gethash project-root) 7926 (cl-pushnew workspace))) 7927 (or workspace-folders (list root))) 7928 7929 (with-lsp-workspace workspace 7930 (run-hooks 'lsp-before-initialize-hook) 7931 (lsp-request-async 7932 "initialize" 7933 (append 7934 (list :processId (unless (file-remote-p (buffer-file-name)) 7935 (emacs-pid)) 7936 :rootPath (lsp-file-local-name (expand-file-name root)) 7937 :clientInfo (list :name "emacs" 7938 :version (emacs-version)) 7939 :rootUri (lsp--path-to-uri root) 7940 :capabilities (lsp--client-capabilities custom-capabilities) 7941 :initializationOptions initialization-options 7942 :workDoneToken "1") 7943 (when lsp-server-trace 7944 (list :trace lsp-server-trace)) 7945 (when multi-root 7946 (->> workspace-folders 7947 (-distinct) 7948 (-map (lambda (folder) 7949 (list :uri (lsp--path-to-uri folder) 7950 :name (f-filename folder)))) 7951 (apply 'vector) 7952 (list :workspaceFolders)))) 7953 (-lambda ((&InitializeResult :capabilities)) 7954 ;; we know that Rust Analyzer will send {} which will be parsed as null 7955 ;; when using plists 7956 (when (equal 'rust-analyzer server-id) 7957 (-> capabilities 7958 (lsp:server-capabilities-text-document-sync?) 7959 (lsp:set-text-document-sync-options-save? t))) 7960 7961 (setf (lsp--workspace-server-capabilities workspace) capabilities 7962 (lsp--workspace-status workspace) 'initialized) 7963 7964 (with-lsp-workspace workspace 7965 (lsp-notify "initialized" lsp--empty-ht)) 7966 7967 (when initialized-fn (funcall initialized-fn workspace)) 7968 7969 (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace)) 7970 (->> workspace 7971 (lsp--workspace-buffers) 7972 (mapc (lambda (buffer) 7973 (lsp-with-current-buffer buffer 7974 (lsp--open-in-workspace workspace))))) 7975 7976 (with-lsp-workspace workspace 7977 (run-hooks 'lsp-after-initialize-hook)) 7978 (lsp--info "%s initialized successfully in folders: %s" 7979 (lsp--workspace-print workspace) 7980 (lsp-workspace-folders workspace))) 7981 :mode 'detached)) 7982 workspace)) 7983 7984 (defun lsp--load-default-session () 7985 "Load default session." 7986 (setq lsp--session (or (condition-case err 7987 (lsp--read-from-file lsp-session-file) 7988 (error (lsp--error "Failed to parse the session %s, starting with clean one." 7989 (error-message-string err)) 7990 nil)) 7991 (make-lsp-session)))) 7992 7993 (defun lsp-session () 7994 "Get the session associated with the current buffer." 7995 (or lsp--session (setq lsp--session (lsp--load-default-session)))) 7996 7997 (defun lsp--client-disabled-p (buffer-major-mode client) 7998 (seq-some 7999 (lambda (entry) 8000 (pcase entry 8001 ((pred symbolp) (eq entry client)) 8002 (`(,mode . ,client-or-list) 8003 (and (eq mode buffer-major-mode) 8004 (if (listp client-or-list) 8005 (memq client client-or-list) 8006 (eq client client-or-list)))))) 8007 lsp-disabled-clients)) 8008 8009 8010 ;; download server 8011 8012 (defcustom lsp-server-install-dir (expand-file-name 8013 (locate-user-emacs-file (f-join ".cache" "lsp"))) 8014 "Directory in which the servers will be installed." 8015 :risky t 8016 :type 'directory 8017 :package-version '(lsp-mode . "6.3") 8018 :group 'lsp-mode) 8019 8020 (defcustom lsp-verify-signature t 8021 "Whether to check GPG signatures of downloaded files." 8022 :type 'boolean 8023 :package-version '(lsp-mode . "8.0.0") 8024 :group 'lsp-mode) 8025 8026 (defvar lsp--dependencies (ht)) 8027 8028 (defun lsp-dependency (name &rest definitions) 8029 "Used to specify a language server DEPENDENCY, the server 8030 executable or other required file path. Typically, the 8031 DEPENDENCY is found by locating it on the system path using 8032 `executable-find'. 8033 8034 You can explicitly call lsp-dependency in your environment to 8035 specify the absolute path to the DEPENDENCY. For example, the 8036 typescript-language-server requires both the server and the 8037 typescript compiler. If you have installed them in a team shared 8038 read-only location, you can instruct lsp-mode to use them via 8039 8040 (eval-after-load `lsp-mode 8041 `(progn 8042 (require lsp-javascript) 8043 (lsp-dependency typescript-language-server (:system ,tls-exe)) 8044 (lsp-dependency typescript (:system ,ts-js)))) 8045 8046 where tls-exe is the absolute path to the typescript-language-server 8047 executable and ts-js is the absolute path to the typescript compiler 8048 JavaScript file, tsserver.js (the *.js is required for Windows)." 8049 (ht-set lsp--dependencies name definitions)) 8050 8051 (defun lsp--server-binary-present? (client) 8052 (unless (equal (lsp--client-server-id client) 'lsp-pwsh) 8053 (condition-case () 8054 (-some-> client lsp--client-new-connection (plist-get :test?) funcall) 8055 (error nil) 8056 (args-out-of-range nil)))) 8057 8058 (define-minor-mode lsp-installation-buffer-mode 8059 "Mode used in *lsp-installation* buffers. 8060 It can be used to set-up keybindings, etc. Disabling this mode 8061 detaches the installation buffer from commands like 8062 `lsp-select-installation-buffer'." 8063 :init-value nil 8064 :lighter nil) 8065 8066 (defface lsp-installation-finished-buffer-face '((t :foreground "orange")) 8067 "Face used for finished installation buffers. 8068 Used in `lsp-select-installation-buffer'." 8069 :group 'lsp-mode) 8070 8071 (defface lsp-installation-buffer-face '((t :foreground "green")) 8072 "Face used for installation buffers still in progress. 8073 Used in `lsp-select-installation-buffer'." 8074 :group 'lsp-mode) 8075 8076 (defun lsp--installation-buffer? (buf) 8077 "Check whether BUF is an `lsp-async-start-process' buffer." 8078 (buffer-local-value 'lsp-installation-buffer-mode buf)) 8079 8080 (defun lsp-select-installation-buffer (&optional show-finished) 8081 "Interactively choose an installation buffer. 8082 If SHOW-FINISHED is set, leftover (finished) installation buffers 8083 are still shown." 8084 (interactive "P") 8085 (let ((bufs (--filter (and (lsp--installation-buffer? it) 8086 (or show-finished (get-buffer-process it))) 8087 (buffer-list)))) 8088 (pcase bufs 8089 (`nil (user-error "No installation buffers")) 8090 (`(,buf) (pop-to-buffer buf)) 8091 (bufs (pop-to-buffer (completing-read "Select installation buffer: " 8092 (--map (propertize (buffer-name it) 'face 8093 (if (get-buffer-process it) 8094 'lsp-installation-buffer-face 8095 'lsp-installation-finished-buffer-face)) 8096 bufs))))))) 8097 8098 (defun lsp-cleanup-installation-buffers () 8099 "Delete finished *lsp-installation* buffers." 8100 (interactive) 8101 (dolist (buf (buffer-list)) 8102 (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf))) 8103 (kill-buffer buf)))) 8104 8105 (defun lsp--download-status () 8106 (-some--> #'lsp--client-download-in-progress? 8107 (lsp--filter-clients it) 8108 (-map (-compose #'symbol-name #'lsp--client-server-id) it) 8109 (format "%s" it) 8110 (propertize it 'face 'success) 8111 (format " Installing following servers: %s" it) 8112 (propertize it 8113 'local-map (make-mode-line-mouse-map 8114 'mouse-1 #'lsp-select-installation-buffer) 8115 'mouse-face 'highlight))) 8116 8117 (defun lsp--install-server-internal (client &optional update?) 8118 (unless (lsp--client-download-server-fn client) 8119 (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation." 8120 (lsp--client-server-id client))) 8121 8122 (setf (lsp--client-download-in-progress? client) t) 8123 (add-to-list 'global-mode-string '(t (:eval (lsp--download-status)))) 8124 (cl-flet ((done 8125 (success? &optional error-message) 8126 ;; run with idle timer to make sure the lsp command is executed in 8127 ;; the main thread, see #2739. 8128 (run-with-timer 8129 0.0 8130 nil 8131 (lambda () 8132 (-let [(&lsp-cln 'server-id 'buffers) client] 8133 (setf (lsp--client-download-in-progress? client) nil 8134 (lsp--client-buffers client) nil) 8135 (if success? 8136 (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id 8137 (length buffers)) 8138 (lsp--error "Server %s install process failed with the following error message: %s. 8139 Check `*lsp-install*' and `*lsp-log*' buffer." 8140 server-id 8141 error-message)) 8142 (seq-do 8143 (lambda (buffer) 8144 (when (lsp-buffer-live-p buffer) 8145 (lsp-with-current-buffer buffer 8146 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8147 global-mode-string) 8148 (when success? (lsp))))) 8149 buffers) 8150 (unless (lsp--filter-clients #'lsp--client-download-in-progress?) 8151 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8152 global-mode-string))))))) 8153 (lsp--info "Download %s started." (lsp--client-server-id client)) 8154 (condition-case err 8155 (funcall 8156 (lsp--client-download-server-fn client) 8157 client 8158 (lambda () (done t)) 8159 (lambda (msg) (done nil msg)) 8160 update?) 8161 (error 8162 (done nil (error-message-string err)))))) 8163 8164 (defun lsp--require-packages () 8165 "Load `lsp-client-packages' if needed." 8166 (when (and lsp-auto-configure (not lsp--client-packages-required)) 8167 (seq-do (lambda (package) 8168 ;; loading client is slow and `lsp' can be called repeatedly 8169 (unless (featurep package) 8170 (require package nil t))) 8171 lsp-client-packages) 8172 (setq lsp--client-packages-required t))) 8173 8174 ;;;###autoload 8175 (defun lsp-install-server (update? &optional server-id) 8176 "Interactively install or re-install server. 8177 When prefix UPDATE? is t force installation even if the server is present." 8178 (interactive "P") 8179 (lsp--require-packages) 8180 (let* ((chosen-client (or (gethash server-id lsp-clients) 8181 (lsp--completing-read 8182 "Select server to install/re-install: " 8183 (or (->> lsp-clients 8184 (ht-values) 8185 (-filter (-andfn 8186 (-not #'lsp--client-download-in-progress?) 8187 #'lsp--client-download-server-fn))) 8188 (user-error "There are no servers with automatic installation")) 8189 (lambda (client) 8190 (let ((server-name (-> client lsp--client-server-id symbol-name))) 8191 (if (lsp--server-binary-present? client) 8192 (concat server-name " (Already installed)") 8193 server-name))) 8194 nil 8195 t))) 8196 (update? (or update? 8197 (and (not (lsp--client-download-in-progress? chosen-client)) 8198 (lsp--server-binary-present? chosen-client))))) 8199 (lsp--install-server-internal chosen-client update?))) 8200 8201 ;;;###autoload 8202 (defun lsp-uninstall-server (dir) 8203 "Delete a LSP server from `lsp-server-install-dir'." 8204 (interactive 8205 (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir)))) 8206 (unless (file-directory-p dir) 8207 (user-error "Couldn't find %s directory" dir)) 8208 (delete-directory dir 'recursive) 8209 (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir)))) 8210 8211 ;;;###autoload 8212 (defun lsp-uninstall-servers () 8213 "Uninstall all installed servers." 8214 (interactive) 8215 (let* ((dir lsp-server-install-dir) 8216 (servers (ignore-errors 8217 (directory-files dir t 8218 directory-files-no-dot-files-regexp)))) 8219 (if (or (not (file-directory-p dir)) (zerop (length servers))) 8220 (user-error "No servers to uninstall") 8221 (when (yes-or-no-p 8222 (format "Servers to uninstall: %d (%s), proceed? " 8223 (length servers) 8224 (mapconcat (lambda (server) 8225 (file-name-nondirectory (directory-file-name server))) 8226 servers " "))) 8227 (mapc #'lsp-uninstall-server servers) 8228 (message "All servers uninstalled"))))) 8229 8230 ;;;###autoload 8231 (defun lsp-update-server (&optional server-id) 8232 "Interactively update (reinstall) a server." 8233 (interactive) 8234 (lsp--require-packages) 8235 (let ((chosen-client (or (gethash server-id lsp-clients) 8236 (lsp--completing-read 8237 "Select server to update (if not on the list, probably you need to `lsp-install-server`): " 8238 (or (->> lsp-clients 8239 (ht-values) 8240 (-filter (-andfn 8241 (-not #'lsp--client-download-in-progress?) 8242 #'lsp--client-download-server-fn 8243 #'lsp--server-binary-present?))) 8244 (user-error "There are no servers to update")) 8245 (lambda (client) 8246 (-> client lsp--client-server-id symbol-name)) 8247 nil 8248 t)))) 8249 (lsp--install-server-internal chosen-client t))) 8250 8251 ;;;###autoload 8252 (defun lsp-update-servers () 8253 "Update (reinstall) all installed servers." 8254 (interactive) 8255 (lsp--require-packages) 8256 (mapc (lambda (client) (lsp--install-server-internal client t)) 8257 (-filter (-andfn 8258 (-not #'lsp--client-download-in-progress?) 8259 #'lsp--client-download-server-fn 8260 #'lsp--server-binary-present?) (hash-table-values lsp-clients)))) 8261 8262 ;;;###autoload 8263 (defun lsp-ensure-server (server-id) 8264 "Ensure server SERVER-ID" 8265 (lsp--require-packages) 8266 (if-let* ((client (gethash server-id lsp-clients))) 8267 (unless (lsp--server-binary-present? client) 8268 (lsp--info "Server `%s' is not preset, installing..." server-id) 8269 (lsp-install-server nil server-id)) 8270 (warn "Unable to find server registration with id %s" server-id))) 8271 8272 (defun lsp-async-start-process (callback error-callback &rest command) 8273 "Start async process COMMAND with CALLBACK and ERROR-CALLBACK." 8274 (let ((name (cl-first command))) 8275 (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd) 8276 (not (null cmd))) 8277 command) 8278 " ") t 8279 (lambda (&rest _) 8280 (generate-new-buffer-name (format "*lsp-install: %s*" name)))) 8281 (lsp-installation-buffer-mode +1) 8282 (view-mode +1) 8283 (add-hook 8284 'compilation-finish-functions 8285 (lambda (_buf status) 8286 (if (string= "finished\n" status) 8287 (condition-case err 8288 (funcall callback) 8289 (error 8290 (funcall error-callback (error-message-string err)))) 8291 (funcall error-callback (s-trim-right status)))) 8292 nil t)))) 8293 8294 (defun lsp-resolve-value (value) 8295 "Resolve VALUE's value. 8296 If it is function - call it. 8297 If it is a variable - return it's value 8298 Otherwise returns value itself." 8299 (cond 8300 ((functionp value) (funcall value)) 8301 ((and (symbolp value) (boundp value)) (symbol-value value)) 8302 (value))) 8303 8304 (defvar lsp-deps-providers 8305 (list :npm (list :path #'lsp--npm-dependency-path 8306 :install #'lsp--npm-dependency-install) 8307 :cargo (list :path #'lsp--cargo-dependency-path 8308 :install #'lsp--cargo-dependency-install) 8309 :system (list :path #'lsp--system-path) 8310 :download (list :path #'lsp-download-path 8311 :install #'lsp-download-install))) 8312 8313 (defun lsp--system-path (path) 8314 "If PATH is absolute and exists return it as is. Otherwise, 8315 return the absolute path to the executable defined by PATH or 8316 nil." 8317 ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the 8318 ;; typescript-language-server. When lsp invokes the server, lsp needs to 8319 ;; supply the path to the typescript compiler, tsserver.js, as an argument. To 8320 ;; make code platform independent, one must pass the absolute path to the 8321 ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript 8322 ;; child process spawn command that is invoked by the 8323 ;; typescript-language-server). This is why we check for existence and not 8324 ;; that the path is executable. 8325 (let ((path (lsp-resolve-value path))) 8326 (cond 8327 ((and (f-absolute? path) 8328 (f-exists? path)) 8329 path) 8330 ((executable-find path t) path)))) 8331 8332 (defun lsp-package-path (dependency) 8333 "Path to the DEPENDENCY each of the registered providers." 8334 (let (path) 8335 (--first (-let [(provider . rest) it] 8336 (setq path (-some-> lsp-deps-providers 8337 (plist-get provider) 8338 (plist-get :path) 8339 (apply rest)))) 8340 (gethash dependency lsp--dependencies)) 8341 path)) 8342 8343 (defun lsp-package-ensure (dependency callback error-callback) 8344 "Asynchronously ensure a package." 8345 (or (-first (-lambda ((provider . rest)) 8346 (-some-> lsp-deps-providers 8347 (plist-get provider) 8348 (plist-get :install) 8349 (apply (cl-list* callback error-callback rest)))) 8350 (gethash dependency lsp--dependencies)) 8351 (funcall error-callback (format "Unable to find a way to install %s" dependency)))) 8352 8353 8354 ;; npm handling 8355 8356 ;; https://docs.npmjs.com/files/folders#executables 8357 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys) 8358 "Return npm dependency PATH for PACKAGE." 8359 (let ((path (executable-find 8360 (f-join lsp-server-install-dir "npm" package 8361 (cond ((eq system-type 'windows-nt) "") 8362 (t "bin")) 8363 path) 8364 t))) 8365 (unless (and path (f-exists? path)) 8366 (error "The package %s is not installed. Unable to find %s" package path)) 8367 path)) 8368 8369 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys) 8370 (if-let* ((npm-binary (executable-find "npm"))) 8371 (progn 8372 ;; Explicitly `make-directory' to work around NPM bug in 8373 ;; versions 7.0.0 through 7.4.1. See 8374 ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for 8375 ;; discussion. 8376 (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents) 8377 (lsp-async-start-process (lambda () 8378 (if (string-empty-p 8379 (string-trim (shell-command-to-string 8380 (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " ")))) 8381 (funcall callback) 8382 (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json"))))) 8383 (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx 8384 (when (f-dir-p default-directory) 8385 (lsp-async-start-process callback 8386 error-callback 8387 (executable-find "npx") 8388 "npm-install-peers"))))) 8389 error-callback 8390 npm-binary 8391 "-g" 8392 "--prefix" 8393 (f-join lsp-server-install-dir "npm" package) 8394 "install" 8395 package)) 8396 (lsp-log "Unable to install %s via `npm' because it is not present" package) 8397 nil)) 8398 8399 8400 ;; Cargo dependency handling 8401 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys) 8402 (let ((path (executable-find 8403 (f-join lsp-server-install-dir 8404 "cargo" 8405 package 8406 "bin" 8407 path) 8408 t))) 8409 (unless (and path (f-exists? path)) 8410 (error "The package %s is not installed. Unable to find %s" package path)) 8411 path)) 8412 8413 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys) 8414 (if-let* ((cargo-binary (executable-find "cargo"))) 8415 (lsp-async-start-process 8416 callback 8417 error-callback 8418 cargo-binary 8419 "install" 8420 package 8421 (when git 8422 "--git") 8423 git 8424 "--root" 8425 (f-join lsp-server-install-dir "cargo" package)) 8426 (lsp-log "Unable to install %s via `cargo' because it is not present" package) 8427 nil)) 8428 8429 8430 8431 ;; Download URL handling 8432 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys) 8433 (let* ((url (lsp-resolve-value url)) 8434 (store-path (lsp-resolve-value store-path)) 8435 ;; (decompress (lsp-resolve-value decompress)) 8436 (download-path 8437 (pcase decompress 8438 (:gzip (concat store-path ".gz")) 8439 (:zip (concat store-path ".zip")) 8440 (:targz (concat store-path ".tar.gz")) 8441 (`nil store-path) 8442 (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'"))))) 8443 (make-thread 8444 (lambda () 8445 (condition-case err 8446 (progn 8447 (when (f-exists? download-path) 8448 (f-delete download-path)) 8449 (when (f-exists? store-path) 8450 (f-delete store-path)) 8451 (lsp--info "Starting to download %s to %s..." url download-path) 8452 (mkdir (f-parent download-path) t) 8453 (url-copy-file url download-path) 8454 (lsp--info "Finished downloading %s..." download-path) 8455 (when (and lsp-verify-signature asc-url pgp-key) 8456 (if (executable-find epg-gpg-program) 8457 (let ((asc-download-path (concat download-path ".asc")) 8458 (context (epg-make-context)) 8459 (fingerprint) 8460 (signature)) 8461 (when (f-exists? asc-download-path) 8462 (f-delete asc-download-path)) 8463 (lsp--info "Starting to download %s to %s..." asc-url asc-download-path) 8464 (url-copy-file asc-url asc-download-path) 8465 (lsp--info "Finished downloading %s..." asc-download-path) 8466 (epg-import-keys-from-string context pgp-key) 8467 (setq fingerprint (epg-import-status-fingerprint 8468 (car 8469 (epg-import-result-imports 8470 (epg-context-result-for context 'import))))) 8471 (lsp--info "Verifying signature %s..." asc-download-path) 8472 (epg-verify-file context asc-download-path download-path) 8473 (setq signature (car (epg-context-result-for context 'verify))) 8474 (unless (and 8475 (eq (epg-signature-status signature) 'good) 8476 (equal (epg-signature-fingerprint signature) fingerprint)) 8477 (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature)))) 8478 (lsp--warn "GPG is not installed, skipping the signature check."))) 8479 (when decompress 8480 (lsp--info "Decompressing %s..." download-path) 8481 (pcase decompress 8482 (:gzip 8483 (lsp-gunzip download-path)) 8484 (:zip (lsp-unzip download-path (f-parent store-path))) 8485 (:targz (lsp-tar-gz-decompress download-path (f-parent store-path)))) 8486 (lsp--info "Decompressed %s..." store-path)) 8487 (funcall callback)) 8488 (error (funcall error-callback err))))))) 8489 8490 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys) 8491 "Download URL and store it into STORE-PATH. 8492 8493 SET-EXECUTABLE? when non-nil change the executable flags of 8494 STORE-PATH to make it executable. BINARY-PATH can be specified 8495 when the binary to start does not match the name of the 8496 archive (e.g. when the archive has multiple files)" 8497 (let ((store-path (or (lsp-resolve-value binary-path) 8498 (lsp-resolve-value store-path)))) 8499 (cond 8500 ((executable-find store-path) store-path) 8501 ((and set-executable? (f-exists? store-path)) 8502 (set-file-modes store-path #o0700) 8503 store-path) 8504 ((f-exists? store-path) store-path)))) 8505 8506 (defun lsp--find-latest-gh-release-url (url regex) 8507 "Fetch the latest version in the releases given by URL by using REGEX." 8508 (let ((url-request-method "GET")) 8509 (with-current-buffer (url-retrieve-synchronously url) 8510 (goto-char (point-min)) 8511 (re-search-forward "\n\n" nil 'noerror) 8512 (delete-region (point-min) (point)) 8513 (let* ((json-result (lsp-json-read-buffer))) 8514 (message "Latest version found: %s" (lsp-get json-result :tag_name)) 8515 (--> json-result 8516 (lsp-get it :assets) 8517 (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it) 8518 (lsp-get it :browser_download_url)))))) 8519 8520 ;; unzip 8521 8522 (defconst lsp-ext-pwsh-script "pwsh -noprofile -noninteractive \ 8523 -nologo -ex bypass -c Expand-Archive -Path '%s' -DestinationPath '%s'" 8524 "Pwsh script to unzip file.") 8525 8526 (defconst lsp-ext-powershell-script "powershell -noprofile -noninteractive \ 8527 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'" 8528 "Powershell script to unzip file.") 8529 8530 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'" 8531 "Unzip script to unzip file.") 8532 8533 (defcustom lsp-unzip-script (lambda () 8534 (cond ((and (eq system-type 'windows-nt) 8535 (executable-find "pwsh")) 8536 lsp-ext-pwsh-script) 8537 ((and (eq system-type 'windows-nt) 8538 (executable-find "powershell")) 8539 lsp-ext-powershell-script) 8540 ((executable-find "unzip") lsp-ext-unzip-script) 8541 ((executable-find "pwsh") lsp-ext-pwsh-script) 8542 (t nil))) 8543 "The script to unzip." 8544 :group 'lsp-mode 8545 :type 'string 8546 :package-version '(lsp-mode . "8.0.0")) 8547 8548 (defun lsp-unzip (zip-file dest) 8549 "Unzip ZIP-FILE to DEST." 8550 (unless lsp-unzip-script 8551 (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'")) 8552 (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest))) 8553 8554 ;; gunzip 8555 8556 (defconst lsp-ext-gunzip-script "gzip -d %1$s" 8557 "Script to decompress a gzippped file with gzip.") 8558 8559 (defcustom lsp-gunzip-script (lambda () 8560 (cond ((executable-find "gzip") lsp-ext-gunzip-script) 8561 (t nil))) 8562 "The script to decompress a gzipped file. 8563 Should be a format string with one argument for the file to be decompressed 8564 in place." 8565 :group 'lsp-mode 8566 :type 'string 8567 :package-version '(lsp-mode . "8.0.0")) 8568 8569 (defun lsp-gunzip (gz-file) 8570 "Decompress GZ-FILE in place." 8571 (unless lsp-gunzip-script 8572 (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file)) 8573 (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file))) 8574 8575 ;; tar.gz decompression 8576 8577 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'" 8578 "Script to decompress a .tar.gz file.") 8579 8580 (defcustom lsp-tar-script (lambda () 8581 (cond ((executable-find "tar") lsp-ext-tar-script) 8582 (t nil))) 8583 "The script to decompress a .tar.gz file. 8584 Should be a format string with one argument for the file to be decompressed 8585 in place." 8586 :group 'lsp-mode 8587 :type 'string) 8588 8589 (defun lsp-tar-gz-decompress (targz-file dest) 8590 "Decompress TARGZ-FILE in DEST." 8591 (unless lsp-tar-script 8592 (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file)) 8593 (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest))) 8594 8595 8596 ;; VSCode marketplace 8597 8598 (defcustom lsp-vscode-ext-url 8599 "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s" 8600 "Vscode extension template url." 8601 :group 'lsp-mode 8602 :type 'string 8603 :package-version '(lsp-mode . "8.0.0")) 8604 8605 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform) 8606 "Return the URL to vscode extension. 8607 PUBLISHER is the extension publisher. 8608 NAME is the name of the extension. 8609 VERSION is the version of the extension. 8610 TARGETPLATFORM is the targetPlatform of the extension." 8611 (format lsp-vscode-ext-url publisher name version (or targetPlatform ""))) 8612 8613 8614 8615 ;; Queueing prompts 8616 8617 (defvar lsp--question-queue nil 8618 "List of questions yet to be asked by `lsp-ask-question'.") 8619 8620 (defun lsp-ask-question (question options callback) 8621 "Prompt the user to answer the QUESTION with one of the OPTIONS from the 8622 minibuffer. Once the user selects an option, the CALLBACK function will be 8623 called, passing the selected option to it. 8624 8625 If the user is currently being shown a question, the question will be stored in 8626 `lsp--question-queue', and will be asked once the user has answered the current 8627 question." 8628 (add-to-list 'lsp--question-queue `(("question" . ,question) 8629 ("options" . ,options) 8630 ("callback" . ,callback)) t) 8631 (when (eq (length lsp--question-queue) 1) 8632 (lsp--process-question-queue))) 8633 8634 (defun lsp--process-question-queue () 8635 "Take the first question from `lsp--question-queue', process it, then process 8636 the next question until the queue is empty." 8637 (-let* (((&alist "question" "options" "callback") (car lsp--question-queue)) 8638 (answer (completing-read question options nil t))) 8639 (pop lsp--question-queue) 8640 (funcall callback answer) 8641 (when lsp--question-queue 8642 (lsp--process-question-queue)))) 8643 8644 (defun lsp--supports-buffer? (client) 8645 (and 8646 ;; both file and client remote or both local 8647 (eq (---truthy? (file-remote-p (buffer-file-name))) 8648 (---truthy? (lsp--client-remote? client))) 8649 8650 ;; activation function or major-mode match. 8651 (if-let* ((activation-fn (lsp--client-activation-fn client))) 8652 (funcall activation-fn (buffer-file-name) major-mode) 8653 (-contains? (lsp--client-major-modes client) major-mode)) 8654 8655 ;; check whether it is enabled if `lsp-enabled-clients' is not null 8656 (or (null lsp-enabled-clients) 8657 (or (member (lsp--client-server-id client) lsp-enabled-clients) 8658 (ignore (lsp--info "Client %s is not in lsp-enabled-clients" 8659 (lsp--client-server-id client))))) 8660 8661 ;; check whether it is not disabled. 8662 (not (lsp--client-disabled-p major-mode (lsp--client-server-id client))))) 8663 8664 (defun lsp--filter-clients (pred) 8665 (->> lsp-clients hash-table-values (-filter pred))) 8666 8667 (defun lsp--find-clients () 8668 "Find clients which can handle current buffer." 8669 (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 8670 #'lsp--server-binary-present?))) 8671 (lsp-log "Found the following clients for %s: %s" 8672 (buffer-file-name) 8673 (s-join ", " 8674 (-map (lambda (client) 8675 (format "(server-id %s, priority %s)" 8676 (lsp--client-server-id client) 8677 (lsp--client-priority client))) 8678 matching-clients))) 8679 (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients)) 8680 (selected-clients (if-let* ((main-client (and main-clients 8681 (--max-by (> (lsp--client-priority it) 8682 (lsp--client-priority other)) 8683 main-clients)))) 8684 (cons main-client add-on-clients) 8685 add-on-clients))) 8686 (lsp-log "The following clients were selected based on priority: %s" 8687 (s-join ", " 8688 (-map (lambda (client) 8689 (format "(server-id %s, priority %s)" 8690 (lsp--client-server-id client) 8691 (lsp--client-priority client))) 8692 selected-clients))) 8693 selected-clients))) 8694 8695 (defun lsp-workspace-remove-all-folders() 8696 "Delete all lsp tracked folders." 8697 (interactive) 8698 (--each (lsp-session-folders (lsp-session)) 8699 (lsp-workspace-folders-remove it))) 8700 8701 (defun lsp-register-client (client) 8702 "Registers LSP client CLIENT." 8703 (let ((client-id (lsp--client-server-id client))) 8704 (puthash client-id client lsp-clients) 8705 (setplist (intern (format "lsp-%s-after-open-hook" client-id)) 8706 `( standard-value (nil) custom-type hook 8707 custom-package-version (lsp-mode . "7.0.1") 8708 variable-documentation ,(format "Hooks to run after `%s' server is run." client-id) 8709 custom-requests nil))) 8710 (when (and lsp-auto-register-remote-clients 8711 (not (lsp--client-remote? client))) 8712 (let ((remote-client (copy-lsp--client client))) 8713 (setf (lsp--client-remote? remote-client) t 8714 (lsp--client-server-id remote-client) (intern 8715 (format "%s-tramp" 8716 (lsp--client-server-id client))) 8717 ;; disable automatic download 8718 (lsp--client-download-server-fn remote-client) nil) 8719 (lsp-register-client remote-client)))) 8720 8721 (defun lsp--create-initialization-options (_session client) 8722 "Create initialization-options from SESSION and CLIENT. 8723 Add workspace folders depending on server being multiroot and 8724 session workspace folder configuration for the server." 8725 (let* ((initialization-options-or-fn (lsp--client-initialization-options client))) 8726 (if (functionp initialization-options-or-fn) 8727 (funcall initialization-options-or-fn) 8728 initialization-options-or-fn))) 8729 8730 (defvar lsp-client-settings (make-hash-table :test 'equal) 8731 "For internal use, any external users please use 8732 `lsp-register-custom-settings' function instead") 8733 8734 (defun lsp-register-custom-settings (props) 8735 "Register PROPS. 8736 PROPS is list of triple (path value boolean?) where PATH is the path to the 8737 property; VALUE can be a literal value, symbol to be evaluated, or either a 8738 function or lambda function to be called without arguments; BOOLEAN? is an 8739 optional flag that should be non-nil for boolean settings, when it is nil the 8740 property will be ignored if the VALUE is nil. 8741 8742 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))' 8743 \(note the double parentheses)" 8744 (mapc 8745 (-lambda ((path . rest)) 8746 (puthash path rest lsp-client-settings)) 8747 props)) 8748 8749 (defun lsp-region-text (region) 8750 "Get the text for REGION in current buffer." 8751 (-let (((start . end) (lsp--range-to-region region))) 8752 (buffer-substring-no-properties start end))) 8753 8754 (defun lsp-ht-set (tbl paths value) 8755 "Set nested hash table value. 8756 TBL - a hash table, PATHS is the path to the nested VALUE." 8757 (pcase paths 8758 (`(,path) (ht-set! tbl path value)) 8759 (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl) 8760 (let ((temp-tbl (ht))) 8761 (ht-set! tbl path temp-tbl) 8762 temp-tbl)))) 8763 (lsp-ht-set nested-tbl rst value))))) 8764 8765 ;; sections 8766 8767 (defalias 'defcustom-lsp 'lsp-defcustom) 8768 8769 (defmacro lsp-defcustom (symbol standard doc &rest args) 8770 "Defines `lsp-mode' server property." 8771 (declare (doc-string 3) (debug (name body)) 8772 (indent defun)) 8773 (let ((path (plist-get args :lsp-path)) 8774 (setter (intern (concat (symbol-name symbol) "--set")))) 8775 (cl-remf args :lsp-path) 8776 `(progn 8777 (lsp-register-custom-settings 8778 (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type)))))) 8779 8780 (defcustom ,symbol ,standard ,doc ,@args) 8781 8782 ;; Use a variable watcher instead of registering a `defcustom' 8783 ;; setter since `hack-local-variables' is not aware of custom 8784 ;; setters and won't invoke them. 8785 8786 (defun ,setter (sym val op _where) 8787 (when (eq op 'set) 8788 (lsp--set-custom-property sym val ,path))) 8789 8790 (add-variable-watcher ',symbol #',setter)))) 8791 8792 (defun lsp--set-custom-property (sym val path) 8793 (set sym val) 8794 (let ((section (cl-first (s-split "\\." path)))) 8795 (mapc (lambda (workspace) 8796 (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace)) 8797 section) 8798 (with-lsp-workspace workspace 8799 (lsp--set-configuration (lsp-configuration-section section))))) 8800 (lsp--session-workspaces (lsp-session))))) 8801 8802 (defun lsp-configuration-section (section) 8803 "Get settings for SECTION." 8804 (let ((ret (ht-create))) 8805 (maphash (-lambda (path (variable boolean?)) 8806 (when (s-matches? (concat (regexp-quote section) "\\..*") path) 8807 (let* ((symbol-value (-> variable 8808 lsp-resolve-value 8809 lsp-resolve-value)) 8810 (value (if (and boolean? (not symbol-value)) 8811 :json-false 8812 symbol-value))) 8813 (when (or boolean? value) 8814 (lsp-ht-set ret (s-split "\\." path) value))))) 8815 lsp-client-settings) 8816 ret)) 8817 8818 8819 (defun lsp--start-connection (session client project-root) 8820 "Initiates connection created from CLIENT for PROJECT-ROOT. 8821 SESSION is the active session." 8822 (when (lsp--client-multi-root client) 8823 (cl-pushnew project-root (gethash (lsp--client-server-id client) 8824 (lsp-session-server-id->folders session)))) 8825 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil) 8826 8827 (unwind-protect 8828 (lsp--start-workspace session client project-root (lsp--create-initialization-options session client)) 8829 (lsp--spinner-stop))) 8830 8831 ;; lsp-log-io-mode 8832 8833 (defvar lsp-log-io-mode-map 8834 (let ((map (make-sparse-keymap))) 8835 (define-key map (kbd "M-n") #'lsp-log-io-next) 8836 (define-key map (kbd "M-p") #'lsp-log-io-prev) 8837 (define-key map (kbd "k") #'lsp--erase-log-buffer) 8838 (define-key map (kbd "K") #'lsp--erase-session-log-buffers) 8839 map) 8840 "Keymap for lsp log buffer mode.") 8841 8842 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo" 8843 "Special mode for viewing IO logs.") 8844 8845 (defun lsp-workspace-show-log (workspace) 8846 "Display the log buffer of WORKSPACE." 8847 (interactive 8848 (list (if lsp-log-io 8849 (if (eq (length (lsp-workspaces)) 1) 8850 (cl-first (lsp-workspaces)) 8851 (lsp--completing-read "Workspace: " (lsp-workspaces) 8852 #'lsp--workspace-print nil t)) 8853 (user-error "IO logging is disabled")))) 8854 (pop-to-buffer (lsp--get-log-buffer-create workspace))) 8855 8856 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log) 8857 8858 (defun lsp--get-log-buffer-create (workspace) 8859 "Return the lsp log buffer of WORKSPACE, creating a new one if needed." 8860 (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8861 (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id))) 8862 (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid)))) 8863 8864 (defun lsp--erase-log-buffer (&optional all) 8865 "Delete contents of current lsp log buffer. 8866 When ALL is t, erase all log buffers of the running session." 8867 (interactive) 8868 (let* ((workspaces (lsp--session-workspaces (lsp-session))) 8869 (current-log-buffer (current-buffer))) 8870 (dolist (w workspaces) 8871 (let ((b (lsp--get-log-buffer-create w))) 8872 (when (or all (eq b current-log-buffer)) 8873 (with-current-buffer b 8874 (let ((inhibit-read-only t)) 8875 (erase-buffer)))))))) 8876 8877 (defun lsp--erase-session-log-buffers () 8878 "Erase log buffers of the running session." 8879 (interactive) 8880 (lsp--erase-log-buffer t)) 8881 8882 (defun lsp-log-io-next (arg) 8883 "Move to next log entry." 8884 (interactive "P") 8885 (ewoc-goto-next lsp--log-io-ewoc (or arg 1))) 8886 8887 (defun lsp-log-io-prev (arg) 8888 "Move to previous log entry." 8889 (interactive "P") 8890 (ewoc-goto-prev lsp--log-io-ewoc (or arg 1))) 8891 8892 8893 8894 (cl-defmethod lsp-process-id ((process process)) 8895 (process-id process)) 8896 8897 (cl-defmethod lsp-process-name ((process process)) (process-name process)) 8898 8899 (cl-defmethod lsp-process-status ((process process)) (process-status process)) 8900 8901 (cl-defmethod lsp-process-kill ((process process)) 8902 (when (process-live-p process) 8903 (kill-process process))) 8904 8905 (cl-defmethod lsp-process-send ((process process) message) 8906 (condition-case err 8907 (process-send-string process (lsp--make-message message)) 8908 (error (lsp--error "Sending to process failed with the following error: %s" 8909 (error-message-string err))))) 8910 8911 (cl-defmethod lsp-process-cleanup (process) 8912 ;; Kill standard error buffer only if the process exited normally. 8913 ;; Leave it intact otherwise for debugging purposes. 8914 (let ((buffer (-> process process-name get-buffer))) 8915 (when (and (eq (process-status process) 'exit) 8916 (zerop (process-exit-status process)) 8917 (buffer-live-p buffer)) 8918 (kill-buffer buffer)))) 8919 8920 8921 ;; native JSONRPC 8922 8923 (declare-function json-rpc "ext:json") 8924 (declare-function json-rpc-connection "ext:json") 8925 (declare-function json-rpc-send "ext:json") 8926 (declare-function json-rpc-shutdown "ext:json") 8927 (declare-function json-rpc-stderr "ext:json") 8928 (declare-function json-rpc-pid "ext:json") 8929 8930 (defvar lsp-json-rpc-thread nil) 8931 (defvar lsp-json-rpc-queue nil) 8932 (defvar lsp-json-rpc-done nil) 8933 (defvar lsp-json-rpc-mutex (make-mutex)) 8934 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex)) 8935 8936 (defun lsp-json-rpc-process-queue () 8937 (while (not lsp-json-rpc-done) 8938 (while lsp-json-rpc-queue 8939 (-let (((proc . message) (pop lsp-json-rpc-queue))) 8940 (json-rpc-send 8941 proc message 8942 :null-object nil 8943 :false-object :json-false))) 8944 (with-mutex lsp-json-rpc-mutex 8945 (condition-wait lsp-json-rpc-condition)))) 8946 8947 (cl-defmethod lsp-process-id (process) (json-rpc-pid process)) 8948 8949 (cl-defmethod lsp-process-name (_process) "TBD") 8950 8951 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process)) 8952 8953 (cl-defmethod lsp-process-send (proc message) 8954 (unless lsp-json-rpc-thread 8955 (with-current-buffer (get-buffer-create " *json-rpc*") 8956 (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*")))) 8957 8958 (with-mutex lsp-json-rpc-mutex 8959 (setq lsp-json-rpc-queue (append lsp-json-rpc-queue 8960 (list (cons proc message)))) 8961 (condition-notify lsp-json-rpc-condition))) 8962 8963 (cl-defmethod lsp-process-cleanup (_proc)) 8964 8965 (defun lsp-json-rpc-connection (workspace command) 8966 (let ((con (apply #'json-rpc-connection command)) 8967 (object-type (if lsp-use-plists 'plist 'hash-table))) 8968 (with-current-buffer (get-buffer-create " *json-rpc*") 8969 (make-thread 8970 (lambda () 8971 (json-rpc 8972 con 8973 (lambda (result err done) 8974 (run-with-timer 8975 0.0 8976 nil 8977 (lambda () 8978 (cond 8979 (result (lsp--parser-on-message result workspace)) 8980 (err (warn "Json parsing failed with the following error: %s" err)) 8981 (done (lsp--handle-process-exit workspace "")))))) 8982 :object-type object-type 8983 :null-object nil 8984 :false-object nil)) 8985 "*json-rpc-connection*")) 8986 (cons con con))) 8987 8988 (defun lsp-json-rpc-stderr () 8989 (interactive) 8990 (--when-let (pcase (lsp-workspaces) 8991 (`nil (user-error "There are no active servers in the current buffer")) 8992 (`(,workspace) workspace) 8993 (workspaces (lsp--completing-read "Select server: " 8994 workspaces 8995 'lsp--workspace-print nil t))) 8996 (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it))) 8997 (buffer (format "*stderr-%s*" (lsp--workspace-print it)) )) 8998 (with-current-buffer (get-buffer-create buffer) 8999 (with-help-window buffer 9000 (insert content)))))) 9001 9002 9003 (defun lsp--workspace-print (workspace) 9004 "Visual representation WORKSPACE." 9005 (let* ((proc (lsp--workspace-cmd-proc workspace)) 9006 (status (lsp--workspace-status workspace)) 9007 (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 9008 (pid (lsp-process-id proc))) 9009 9010 (if (eq 'initialized status) 9011 (format "%s:%s" server-id pid) 9012 (format "%s:%s/%s" server-id pid status)))) 9013 9014 (defun lsp--map-tree-widget (m) 9015 "Build `tree-widget' from a hash-table or plist M." 9016 (when (lsp-structure-p m) 9017 (let (nodes) 9018 (lsp-map (lambda (k v) 9019 (push `(tree-widget 9020 :tag ,(if (lsp-structure-p v) 9021 (format "%s:" k) 9022 (format "%s: %s" k 9023 (propertize (format "%s" v) 9024 'face 9025 'font-lock-string-face))) 9026 :open t 9027 ,@(lsp--map-tree-widget v)) 9028 nodes)) 9029 m) 9030 nodes))) 9031 9032 (defun lsp-buffer-name (buffer-id) 9033 (if-let* ((buffer-name (plist-get buffer-id :buffer-name))) 9034 (funcall buffer-name buffer-id) 9035 (buffer-name buffer-id))) 9036 9037 (defun lsp--render-workspace (workspace) 9038 "Tree node representation of WORKSPACE." 9039 `(tree-widget :tag ,(lsp--workspace-print workspace) 9040 :open t 9041 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face) 9042 :open t 9043 ,@(->> workspace 9044 (lsp--workspace-buffers) 9045 (--map `(tree-widget 9046 :tag ,(when (lsp-buffer-live-p it) 9047 (let ((buffer-name (lsp-buffer-name it))) 9048 (if (lsp-with-current-buffer it buffer-read-only) 9049 (propertize buffer-name 'face 'font-lock-constant-face) 9050 buffer-name))))))) 9051 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face) 9052 ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget)))) 9053 9054 (define-derived-mode lsp-browser-mode special-mode "LspBrowser" 9055 "Define mode for displaying lsp sessions." 9056 (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t))))) 9057 9058 (defun lsp-describe-session () 9059 "Describes current `lsp-session'." 9060 (interactive) 9061 (let ((session (lsp-session)) 9062 (buf (get-buffer-create "*lsp session*")) 9063 (root (lsp-workspace-root))) 9064 (with-current-buffer buf 9065 (lsp-browser-mode) 9066 (let ((inhibit-read-only t)) 9067 (erase-buffer) 9068 (--each (lsp-session-folders session) 9069 (widget-create 9070 `(tree-widget 9071 :tag ,(propertize it 'face 'font-lock-keyword-face) 9072 :open t 9073 ,@(->> session 9074 (lsp-session-folder->servers) 9075 (gethash it) 9076 (-map 'lsp--render-workspace))))))) 9077 (pop-to-buffer buf) 9078 (goto-char (point-min)) 9079 (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag) 9080 until (or (and root (string= tag root)) (eobp)) 9081 do (goto-char (next-overlay-change (point)))))) 9082 9083 (defun lsp--session-workspaces (session) 9084 "Get all workspaces that are part of the SESSION." 9085 (-> session lsp-session-folder->servers hash-table-values -flatten -uniq)) 9086 9087 (defun lsp--find-multiroot-workspace (session client project-root) 9088 "Look for a multiroot connection in SESSION created from CLIENT for 9089 PROJECT-ROOT and BUFFER-MAJOR-MODE." 9090 (when (lsp--client-multi-root client) 9091 (-when-let (multi-root-workspace (->> session 9092 (lsp--session-workspaces) 9093 (--first (eq (-> it lsp--workspace-client lsp--client-server-id) 9094 (lsp--client-server-id client))))) 9095 (with-lsp-workspace multi-root-workspace 9096 (lsp-notify "workspace/didChangeWorkspaceFolders" 9097 (lsp-make-did-change-workspace-folders-params 9098 :event (lsp-make-workspace-folders-change-event 9099 :added (vector (lsp-make-workspace-folder 9100 :uri (lsp--path-to-uri project-root) 9101 :name (f-filename project-root))) 9102 :removed [])))) 9103 9104 (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace)) 9105 (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root)) 9106 9107 (lsp--persist-session session) 9108 9109 (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace)) 9110 (lsp--open-in-workspace multi-root-workspace) 9111 9112 multi-root-workspace))) 9113 9114 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder) 9115 "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT. 9116 IGNORE-MULTI-FOLDER to ignore multi folder server." 9117 (-map (lambda (client) 9118 (or 9119 (lsp--find-workspace session client project-root) 9120 (unless ignore-multi-folder 9121 (lsp--find-multiroot-workspace session client project-root)) 9122 (lsp--start-connection session client project-root))) 9123 clients)) 9124 9125 (defun lsp--spinner-stop () 9126 "Stop the spinner in case all of the workspaces are started." 9127 (when (--all? (eq (lsp--workspace-status it) 'initialized) 9128 lsp--buffer-workspaces) 9129 (spinner-stop))) 9130 9131 (defun lsp--open-in-workspace (workspace) 9132 "Open in existing WORKSPACE." 9133 (if (eq 'initialized (lsp--workspace-status workspace)) 9134 ;; when workspace is initialized just call document did open. 9135 (progn 9136 (with-lsp-workspace workspace 9137 (when-let* ((before-document-open-fn (-> workspace 9138 lsp--workspace-client 9139 lsp--client-before-file-open-fn))) 9140 (funcall before-document-open-fn workspace)) 9141 (lsp--text-document-did-open)) 9142 (lsp--spinner-stop)) 9143 ;; when it is not initialized 9144 (lsp--spinner-start) 9145 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace)))) 9146 9147 (defun lsp--find-workspace (session client project-root) 9148 "Find server connection created with CLIENT in SESSION for PROJECT-ROOT." 9149 (when-let* ((workspace (->> session 9150 (lsp-session-folder->servers) 9151 (gethash project-root) 9152 (--first (eql (-> it lsp--workspace-client lsp--client-server-id) 9153 (lsp--client-server-id client)))))) 9154 (lsp--open-in-workspace workspace) 9155 workspace)) 9156 9157 (defun lsp--read-char (prompt &optional options) 9158 "Wrapper for `read-char-from-minibuffer' if Emacs +27. 9159 Fallback to `read-key' otherwise. 9160 PROMPT is the message and OPTIONS the available options." 9161 (if (fboundp 'read-char-from-minibuffer) 9162 (read-char-from-minibuffer prompt options) 9163 (read-key prompt))) 9164 9165 (defun lsp--find-root-interactively (session) 9166 "Find project interactively. 9167 Returns nil if the project should not be added to the current SESSION." 9168 (condition-case nil 9169 (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory)) 9170 (action (lsp--read-char 9171 (format 9172 "%s is not part of any project. 9173 9174 %s ==> Import project root %s 9175 %s ==> Import project by selecting root directory interactively 9176 %s ==> Import project at current directory %s 9177 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist 9178 %s ==> Do not ask again for the current project by selecting ignore path interactively 9179 %s ==> Do nothing: ask again when opening other files from the current project 9180 9181 Select action: " 9182 (propertize (buffer-name) 'face 'bold) 9183 (propertize "i" 'face 'success) 9184 (propertize project-root-suggestion 'face 'bold) 9185 (propertize "I" 'face 'success) 9186 (propertize "." 'face 'success) 9187 (propertize default-directory 'face 'bold) 9188 (propertize "d" 'face 'warning) 9189 (propertize project-root-suggestion 'face 'bold) 9190 (propertize "D" 'face 'warning) 9191 (propertize "n" 'face 'warning)) 9192 '(?i ?\r ?I ?. ?d ?D ?n)))) 9193 (cl-case action 9194 (?i project-root-suggestion) 9195 (?\r project-root-suggestion) 9196 (?I (read-directory-name "Select workspace folder to add: " 9197 (or project-root-suggestion default-directory) 9198 nil 9199 t)) 9200 (?. default-directory) 9201 (?d (push project-root-suggestion (lsp-session-folders-blocklist session)) 9202 (lsp--persist-session session) 9203 nil) 9204 (?D (push (read-directory-name "Select folder to blocklist: " 9205 (or project-root-suggestion default-directory) 9206 nil 9207 t) 9208 (lsp-session-folders-blocklist session)) 9209 (lsp--persist-session session) 9210 nil) 9211 (t nil))) 9212 (quit))) 9213 9214 (declare-function tramp-file-name-host "ext:tramp" (file) t) 9215 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault)) 9216 9217 (defun lsp--files-same-host (f1 f2) 9218 "Predicate on whether or not two files are on the same host." 9219 (or (not (or (file-remote-p f1) (file-remote-p f2))) 9220 (and (file-remote-p f1) 9221 (file-remote-p f2) 9222 (progn (require 'tramp) 9223 (equal (tramp-file-name-host (tramp-dissect-file-name f1)) 9224 (tramp-file-name-host (tramp-dissect-file-name f2))))))) 9225 9226 (defun lsp-find-session-folder (session file-name) 9227 "Look in the current SESSION for folder containing FILE-NAME." 9228 (let ((file-name-canonical (lsp-f-canonical file-name))) 9229 (->> session 9230 (lsp-session-folders) 9231 (--filter (and (lsp--files-same-host it file-name-canonical) 9232 (or (lsp-f-same? it file-name-canonical) 9233 (and (f-dir? it) 9234 (lsp-f-ancestor-of? it file-name-canonical))))) 9235 (--max-by (> (length it) 9236 (length other)))))) 9237 9238 (defun lsp-find-workspace (server-id &optional file-name) 9239 "Find workspace for SERVER-ID for FILE-NAME." 9240 (-when-let* ((session (lsp-session)) 9241 (folder->servers (lsp-session-folder->servers session)) 9242 (workspaces (if file-name 9243 (gethash (lsp-find-session-folder session file-name) folder->servers) 9244 (lsp--session-workspaces session)))) 9245 9246 (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces))) 9247 9248 (defun lsp--calculate-root (session file-name) 9249 "Calculate project root for FILE-NAME in SESSION." 9250 (and 9251 (->> session 9252 (lsp-session-folders-blocklist) 9253 (--first (and (lsp--files-same-host it file-name) 9254 (lsp-f-ancestor-of? it file-name) 9255 (prog1 t 9256 (lsp--info "File %s is in blocklisted directory %s" file-name it)))) 9257 not) 9258 (or 9259 (when lsp-auto-guess-root 9260 (lsp--suggest-project-root)) 9261 (unless lsp-guess-root-without-session 9262 (lsp-find-session-folder session file-name)) 9263 (unless lsp-auto-guess-root 9264 (when-let* ((root-folder (lsp--find-root-interactively session))) 9265 (if (or (not (f-equal? root-folder (expand-file-name "~/"))) 9266 (yes-or-no-p 9267 (concat 9268 (propertize "[WARNING] " 'face 'warning) 9269 "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: 9270 9271 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/'). 9272 2. If your file is under `~/' then create a subfolder and move that file in this folder. 9273 9274 Type `No' to go back to project selection. 9275 Type `Yes' to confirm `HOME' as project root. 9276 Type `C-g' to cancel project import process and stop `lsp'"))) 9277 root-folder 9278 (lsp--calculate-root session file-name))))))) 9279 9280 (defun lsp--try-open-in-library-workspace () 9281 "Try opening current file as library file in any of the active workspace. 9282 The library folders are defined by each client for each of the active workspace." 9283 (when-let* ((workspace (->> (lsp-session) 9284 (lsp--session-workspaces) 9285 ;; Sort the last active workspaces first as they are more likely to be 9286 ;; the correct ones, especially when jumping to a definition. 9287 (-sort (lambda (a _b) 9288 (-contains? lsp--last-active-workspaces a))) 9289 (--first 9290 (and (-> it lsp--workspace-client lsp--supports-buffer?) 9291 (when-let* ((library-folders-fn 9292 (-> it lsp--workspace-client lsp--client-library-folders-fn))) 9293 (-first (lambda (library-folder) 9294 (lsp-f-ancestor-of? library-folder (buffer-file-name))) 9295 (funcall library-folders-fn it)))))))) 9296 (lsp--open-in-workspace workspace) 9297 (view-mode t) 9298 (lsp--info "Opening read-only library file %s." (buffer-file-name)) 9299 (list workspace))) 9300 9301 (defun lsp--persist-session (session) 9302 "Persist SESSION to `lsp-session-file'." 9303 (lsp--persist lsp-session-file (make-lsp-session 9304 :folders (lsp-session-folders session) 9305 :folders-blocklist (lsp-session-folders-blocklist session) 9306 :server-id->folders (lsp-session-server-id->folders session)))) 9307 9308 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder) 9309 "Try create opening file as a project file. 9310 When IGNORE-MULTI-FOLDER is t the lsp mode will start new 9311 language server even if there is language server which can handle 9312 current language. When IGNORE-MULTI-FOLDER is nil current file 9313 will be opened in multi folder language server if there is 9314 such." 9315 (-let ((session (lsp-session))) 9316 (-if-let (clients (if ask-for-client 9317 (list (lsp--completing-read "Select server to start: " 9318 (ht-values lsp-clients) 9319 (-compose 'symbol-name 'lsp--client-server-id) nil t)) 9320 (lsp--find-clients))) 9321 (-if-let (project-root (-some-> session 9322 (lsp--calculate-root (buffer-file-name)) 9323 (lsp-f-canonical))) 9324 (progn 9325 ;; update project roots if needed and persist the lsp session 9326 (unless (-contains? (lsp-session-folders session) project-root) 9327 (cl-pushnew project-root (lsp-session-folders session)) 9328 (lsp--persist-session session)) 9329 (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder)) 9330 (lsp--warn "%s not in project or it is blocklisted." (buffer-name)) 9331 nil) 9332 (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode) 9333 nil))) 9334 9335 (defun lsp-shutdown-workspace () 9336 "Shutdown language server." 9337 (interactive) 9338 (--when-let (pcase (lsp-workspaces) 9339 (`nil (user-error "There are no active servers in the current buffer")) 9340 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?" 9341 (lsp--workspace-print workspace))) 9342 workspace)) 9343 (workspaces (lsp--completing-read "Select server: " 9344 workspaces 9345 'lsp--workspace-print nil t))) 9346 (lsp-workspace-shutdown it))) 9347 9348 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1") 9349 9350 (defcustom lsp-auto-select-workspace t 9351 "Shutdown or restart a single workspace. 9352 If set and the current buffer has only a single workspace 9353 associated with it, `lsp-shutdown-workspace' and 9354 `lsp-restart-workspace' will act on it without asking." 9355 :type 'boolean 9356 :group 'lsp-mode) 9357 9358 (defun lsp--read-workspace () 9359 "Ask the user to select a workspace. 9360 Errors if there are none." 9361 (pcase (lsp-workspaces) 9362 (`nil (error "No workspaces associated with the current buffer")) 9363 ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace) 9364 (workspaces (lsp--completing-read "Select workspace: " workspaces 9365 #'lsp--workspace-print nil t)))) 9366 9367 (defun lsp-workspace-shutdown (workspace) 9368 "Shut the workspace WORKSPACE and the language server associated with it" 9369 (interactive (list (lsp--read-workspace))) 9370 (lsp--warn "Stopping %s" (lsp--workspace-print workspace)) 9371 (with-lsp-workspace workspace (lsp--shutdown-workspace))) 9372 9373 (defun lsp-disconnect () 9374 "Disconnect the buffer from the language server." 9375 (interactive) 9376 (lsp--text-document-did-close t) 9377 (lsp-managed-mode -1) 9378 (lsp-mode -1) 9379 (setq lsp--buffer-workspaces nil) 9380 (lsp--info "Disconnected")) 9381 9382 (defun lsp-restart-workspace () 9383 (interactive) 9384 (--when-let (pcase (lsp-workspaces) 9385 (`nil (user-error "There are no active servers in the current buffer")) 9386 (`(,workspace) workspace) 9387 (workspaces (lsp--completing-read "Select server: " 9388 workspaces 9389 'lsp--workspace-print nil t))) 9390 (lsp-workspace-restart it))) 9391 9392 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1") 9393 9394 (defun lsp-workspace-restart (workspace) 9395 "Restart the workspace WORKSPACE and the language server associated with it" 9396 (interactive (list (lsp--read-workspace))) 9397 (lsp--warn "Restarting %s" (lsp--workspace-print workspace)) 9398 (with-lsp-workspace workspace (lsp--shutdown-workspace t))) 9399 9400 ;;;###autoload 9401 (defun lsp (&optional arg) 9402 "Entry point for the server startup. 9403 When ARG is t the lsp mode will start new language server even if 9404 there is language server which can handle current language. When 9405 ARG is nil current file will be opened in multi folder language 9406 server if there is such. When `lsp' is called with prefix 9407 argument ask the user to select which language server to start." 9408 (interactive "P") 9409 9410 (lsp--require-packages) 9411 9412 (when (buffer-file-name) 9413 (let (clients 9414 (matching-clients (lsp--filter-clients 9415 (-andfn #'lsp--supports-buffer? 9416 #'lsp--server-binary-present?)))) 9417 (cond 9418 (matching-clients 9419 (when (setq lsp--buffer-workspaces 9420 (or (and 9421 ;; Don't open as library file if file is part of a project. 9422 (not (lsp-find-session-folder (lsp-session) (buffer-file-name))) 9423 (lsp--try-open-in-library-workspace)) 9424 (lsp--try-project-root-workspaces (equal arg '(4)) 9425 (and arg (not (equal arg 1)))))) 9426 (lsp-mode 1) 9427 (when lsp-auto-configure (lsp--auto-configure)) 9428 (setq lsp-buffer-uri (lsp--buffer-uri)) 9429 (lsp--info "Connected to %s." 9430 (apply 'concat (--map (format "[%s %s]" 9431 (lsp--workspace-print it) 9432 (lsp--workspace-root it)) 9433 lsp--buffer-workspaces))))) 9434 ;; look for servers which are currently being downloaded. 9435 ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9436 #'lsp--client-download-in-progress?))) 9437 (lsp--info "There are language server(%s) installation in progress. 9438 The server(s) will be started in the buffer when it has finished." 9439 (-map #'lsp--client-server-id clients)) 9440 (seq-do (lambda (client) 9441 (cl-pushnew (current-buffer) (lsp--client-buffers client))) 9442 clients)) 9443 ;; look for servers to install 9444 ((setq clients (lsp--filter-clients 9445 (-andfn #'lsp--supports-buffer? 9446 (-const lsp-enable-suggest-server-download) 9447 #'lsp--client-download-server-fn 9448 (-not #'lsp--client-download-in-progress?)))) 9449 (let ((client (lsp--completing-read 9450 (concat "Unable to find installed server supporting this file. " 9451 "The following servers could be installed automatically: ") 9452 clients 9453 (-compose #'symbol-name #'lsp--client-server-id) 9454 nil 9455 t))) 9456 (cl-pushnew (current-buffer) (lsp--client-buffers client)) 9457 (lsp--install-server-internal client))) 9458 ;; ignore other warnings 9459 ((not lsp-warn-no-matched-clients) 9460 nil) 9461 ;; automatic installation disabled 9462 ((setq clients (unless matching-clients 9463 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9464 #'lsp--client-download-server-fn 9465 (-not (-const lsp-enable-suggest-server-download)) 9466 (-not #'lsp--server-binary-present?))))) 9467 (lsp--warn "The following servers support current file but automatic download is disabled: %s 9468 \(If you have already installed the server check *lsp-log*)." 9469 (mapconcat (lambda (client) 9470 (symbol-name (lsp--client-server-id client))) 9471 clients 9472 " "))) 9473 ;; no clients present 9474 ((setq clients (unless matching-clients 9475 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9476 (-not #'lsp--server-binary-present?))))) 9477 (lsp--warn "The following servers support current file but do not have automatic installation: %s 9478 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages. 9479 \(If you have already installed the server check *lsp-log*)." 9480 (mapconcat (lambda (client) 9481 (symbol-name (lsp--client-server-id client))) 9482 clients 9483 " "))) 9484 ;; no matches 9485 ((-> #'lsp--supports-buffer? lsp--filter-clients not) 9486 (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'. 9487 This issue might be caused by: 9488 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'. 9489 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'. 9490 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/ . 9491 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/. 9492 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients'). 9493 You can customize `lsp-warn-no-matched-clients' to disable this message." 9494 major-mode major-mode major-mode)))))) 9495 9496 (defun lsp--buffer-visible-p () 9497 "Return non nil if current buffer is visible." 9498 (or (buffer-modified-p) (get-buffer-window nil t))) 9499 9500 (defun lsp--init-if-visible () 9501 "Run `lsp' for the current buffer if the buffer is visible. 9502 Returns non nil if `lsp' was run for the buffer." 9503 (when (lsp--buffer-visible-p) 9504 (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t) 9505 (lsp) 9506 t)) 9507 9508 ;;;###autoload 9509 (defun lsp-deferred () 9510 "Entry point that defers server startup until buffer is visible. 9511 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'. 9512 This avoids overloading the server with many files when starting Emacs." 9513 ;; Workspace may not be initialized yet. Use a buffer local variable to 9514 ;; remember that we deferred loading of this buffer. 9515 (setq lsp--buffer-deferred t) 9516 (let ((buffer (current-buffer))) 9517 ;; Avoid false positives as desktop-mode restores buffers by deferring 9518 ;; visibility check until the stack clears. 9519 (run-with-idle-timer 0 nil (lambda () 9520 (when (buffer-live-p buffer) 9521 (with-current-buffer buffer 9522 (unless (lsp--init-if-visible) 9523 (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t)))))))) 9524 9525 9526 9527 (defvar lsp-file-truename-cache (ht)) 9528 9529 (defmacro lsp-with-cached-filetrue-name (&rest body) 9530 "Executes BODY caching the `file-truename' calls." 9531 `(let ((old-fn (symbol-function 'file-truename))) 9532 (unwind-protect 9533 (progn 9534 (fset 'file-truename 9535 (lambda (file-name &optional counter prev-dirs) 9536 (or (gethash file-name lsp-file-truename-cache) 9537 (puthash file-name (apply old-fn (list file-name counter prev-dirs)) 9538 lsp-file-truename-cache)))) 9539 ,@body) 9540 (fset 'file-truename old-fn)))) 9541 9542 9543 (defun lsp-virtual-buffer-call (key &rest args) 9544 (when lsp--virtual-buffer 9545 (when-let* ((fn (plist-get lsp--virtual-buffer key))) 9546 (apply fn args)))) 9547 9548 (defun lsp-translate-column (column) 9549 "Translate COLUMN taking into account virtual buffers." 9550 (or (lsp-virtual-buffer-call :real->virtual-char column) 9551 column)) 9552 9553 (defun lsp-translate-line (line) 9554 "Translate LINE taking into account virtual buffers." 9555 (or (lsp-virtual-buffer-call :real->virtual-line line) 9556 line)) 9557 9558 9559 ;; lsp internal validation. 9560 9561 (defmacro lsp--doctor (&rest checks) 9562 `(-let [buf (current-buffer)] 9563 (with-current-buffer (get-buffer-create "*lsp-performance*") 9564 (with-help-window (current-buffer) 9565 ,@(-map (-lambda ((msg form)) 9566 `(insert (format "%s: %s\n" ,msg 9567 (let ((res (with-current-buffer buf 9568 ,form))) 9569 (cond 9570 ((eq res :optional) (propertize "OPTIONAL" 'face 'warning)) 9571 (res (propertize "OK" 'face 'success)) 9572 (t (propertize "ERROR" 'face 'error))))))) 9573 (-partition 2 checks)))))) 9574 9575 (define-obsolete-function-alias 'lsp-diagnose 9576 'lsp-doctor "lsp-mode 8.0.0") 9577 9578 (defun lsp-doctor () 9579 "Validate performance settings." 9580 (interactive) 9581 (lsp--doctor 9582 "Checking for Native JSON support" (functionp 'json-serialize) 9583 "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max) 9584 "Check `read-process-output-max' default has been changed from 4k" 9585 (and (boundp 'read-process-output-max) 9586 (> read-process-output-max 4096)) 9587 "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)" 9588 (condition-case _err 9589 (progn (lsp--make-message (list "a" "b")) 9590 nil) 9591 (error t)) 9592 "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000) 9593 "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) 9594 "Using emacs 28+ with native compilation?" 9595 (or (and (fboundp 'native-comp-available-p) 9596 (native-comp-available-p)) 9597 :optional))) 9598 9599 (declare-function package-version-join "ext:package") 9600 (declare-function package-desc-version "ext:package") 9601 (declare-function package--alist "ext:package") 9602 9603 (defun lsp-version () 9604 "Return string describing current version of `lsp-mode'." 9605 (interactive) 9606 (unless (featurep 'package) 9607 (require 'package)) 9608 (let ((ver (format "lsp-mode %s, Emacs %s, %s" 9609 (package-version-join 9610 (package-desc-version 9611 (car (alist-get 'lsp-mode (package--alist))))) 9612 emacs-version 9613 system-type))) 9614 (if (called-interactively-p 'interactive) 9615 (lsp--info "%s" ver) 9616 ver))) 9617 9618 9619 9620 ;; org-mode/virtual-buffer 9621 9622 (declare-function org-babel-get-src-block-info "ext:ob-core") 9623 (declare-function org-do-remove-indentation "ext:org-macs") 9624 (declare-function org-src-get-lang-mode "ext:org-src") 9625 (declare-function org-element-context "ext:org-element") 9626 9627 (defun lsp--virtual-buffer-update-position () 9628 (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range)) 9629 (funcall in-range)) 9630 lsp--virtual-buffer-connections)) 9631 (unless (equal virtual-buffer lsp--virtual-buffer) 9632 (lsp-org)) 9633 (when lsp-managed-mode 9634 (lsp-managed-mode -1) 9635 (lsp-mode -1) 9636 (setq lsp--buffer-workspaces nil) 9637 (setq lsp--virtual-buffer nil) 9638 (setq lsp-buffer-uri nil) 9639 9640 ;; force refresh of diagnostics 9641 (run-hooks 'lsp-after-diagnostics-hook)))) 9642 9643 (defun lsp-virtual-buffer-on-change (start end length) 9644 "Adjust on change event to be executed against the proper language server." 9645 (let ((max-point (max end 9646 (or (plist-get lsp--before-change-vals :end) 0) 9647 (+ start length)))) 9648 (when-let* ((virtual-buffer (-first (lambda (vb) 9649 (let ((lsp--virtual-buffer vb)) 9650 (and (lsp-virtual-buffer-call :in-range start) 9651 (lsp-virtual-buffer-call :in-range max-point)))) 9652 lsp--virtual-buffer-connections))) 9653 (lsp-with-current-buffer virtual-buffer 9654 (lsp-on-change start end length 9655 (lambda (&rest _) 9656 (list :range (lsp--range (list :character 0 :line 0) 9657 lsp--virtual-buffer-point-max) 9658 :text (lsp--buffer-content)))))))) 9659 9660 (defun lsp-virtual-buffer-before-change (start _end) 9661 (when-let* ((virtual-buffer (-first (lambda (vb) 9662 (lsp-with-current-buffer vb 9663 (lsp-virtual-buffer-call :in-range start))) 9664 lsp--virtual-buffer-connections))) 9665 (lsp-with-current-buffer virtual-buffer 9666 (setq lsp--virtual-buffer-point-max 9667 (lsp--point-to-position (lsp-virtual-buffer-call :last-point)))))) 9668 9669 (defun lsp-patch-on-change-event () 9670 (remove-hook 'after-change-functions #'lsp-on-change t) 9671 (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t) 9672 (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t)) 9673 9674 (defun lsp-kill-virtual-buffers () 9675 (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections)) 9676 9677 (defun lsp--move-point-in-indentation (point indentation) 9678 (save-excursion 9679 (goto-char point) 9680 (if (<= point (+ (line-beginning-position) indentation)) 9681 (line-beginning-position) 9682 point))) 9683 9684 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck") 9685 (declare-function flycheck-add-mode "ext:flycheck") 9686 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics") 9687 9688 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn) 9689 9690 (defun lsp-flycheck-add-mode (mode) 9691 "Register flycheck support for MODE." 9692 (lsp-diagnostics-lsp-checker-if-needed) 9693 (unless (flycheck-checker-supports-major-mode-p 'lsp mode) 9694 (flycheck-add-mode 'lsp mode))) 9695 9696 (defun lsp-progress-spinner-type () 9697 "Retrieve the spinner type value, if value is not a symbol of `spinner-types 9698 defaults to `progress-bar." 9699 (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar)) 9700 9701 (defun lsp-org () 9702 (interactive) 9703 (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range)) 9704 (funcall in-range)) 9705 lsp--virtual-buffer-connections)) 9706 (unless (equal lsp--virtual-buffer virtual-buffer) 9707 (setq lsp--buffer-workspaces workspaces) 9708 (setq lsp--virtual-buffer virtual-buffer) 9709 (setq lsp-buffer-uri nil) 9710 (lsp-mode 1) 9711 (lsp-managed-mode 1) 9712 (lsp-patch-on-change-event)) 9713 9714 (save-excursion 9715 (-let* (virtual-buffer 9716 (wcb (lambda (f) 9717 (with-current-buffer (plist-get virtual-buffer :buffer) 9718 (-let* (((&plist :major-mode :buffer-file-name 9719 :goto-buffer :workspaces) virtual-buffer) 9720 (lsp--virtual-buffer virtual-buffer) 9721 (lsp--buffer-workspaces workspaces)) 9722 (save-excursion 9723 (funcall goto-buffer) 9724 (funcall f)))))) 9725 ((&plist :begin :end :post-blank :language) (cl-second (org-element-context))) 9726 ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light))) 9727 9728 (file-name (if file-name 9729 (f-expand file-name) 9730 (user-error "You should specify file name in the src block header."))) 9731 (begin-marker (progn 9732 (goto-char begin) 9733 (forward-line) 9734 (set-marker (make-marker) (point)))) 9735 (end-marker (progn 9736 (goto-char end) 9737 (forward-line (1- (- post-blank))) 9738 (set-marker (make-marker) (1+ (point))))) 9739 (buf (current-buffer)) 9740 (src-block (buffer-substring-no-properties begin-marker 9741 (1- end-marker))) 9742 (indentation (with-temp-buffer 9743 (insert src-block) 9744 9745 (goto-char (point-min)) 9746 (let ((indentation (current-indentation))) 9747 (plist-put lsp--virtual-buffer :indentation indentation) 9748 (org-do-remove-indentation) 9749 (goto-char (point-min)) 9750 (- indentation (current-indentation)))))) 9751 (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t) 9752 9753 (when (fboundp 'flycheck-add-mode) 9754 (lsp-flycheck-add-mode 'org-mode)) 9755 9756 (setq lsp--virtual-buffer 9757 (list 9758 :in-range (lambda (&optional point) 9759 (<= begin-marker (or point (point)) (1- end-marker))) 9760 :goto-buffer (lambda () (goto-char begin-marker)) 9761 :buffer-string 9762 (lambda () 9763 (let ((src-block (buffer-substring-no-properties 9764 begin-marker 9765 (1- end-marker)))) 9766 (with-temp-buffer 9767 (insert src-block) 9768 9769 (goto-char (point-min)) 9770 (while (not (eobp)) 9771 (delete-region (point) (if (> (+ (point) indentation) (line-end-position)) 9772 (line-end-position) 9773 (+ (point) indentation))) 9774 (forward-line)) 9775 (buffer-substring-no-properties (point-min) 9776 (point-max))))) 9777 :buffer buf 9778 :begin begin-marker 9779 :end end-marker 9780 :indentation indentation 9781 :last-point (lambda () (1- end-marker)) 9782 :cur-position (lambda () 9783 (lsp-save-restriction-and-excursion 9784 (list :line (- (lsp--cur-line) 9785 (lsp--cur-line begin-marker)) 9786 :character (let ((character (- (point) 9787 (line-beginning-position) 9788 indentation))) 9789 (if (< character 0) 9790 0 9791 character))))) 9792 :line/character->point (-lambda (line character) 9793 (-let [inhibit-field-text-motion t] 9794 (+ indentation 9795 (lsp-save-restriction-and-excursion 9796 (goto-char begin-marker) 9797 (forward-line line) 9798 (-let [line-end (line-end-position)] 9799 (if (> character (- line-end (point))) 9800 line-end 9801 (forward-char character) 9802 (point))))))) 9803 :major-mode (org-src-get-lang-mode language) 9804 :buffer-file-name file-name 9805 :buffer-uri (lsp--path-to-uri file-name) 9806 :with-current-buffer wcb 9807 :buffer-live? (lambda (_) (buffer-live-p buf)) 9808 :buffer-name (lambda (_) 9809 (propertize (format "%s(%s:%s)%s" 9810 (buffer-name buf) 9811 begin-marker 9812 end-marker 9813 language) 9814 'face 'italic)) 9815 :real->virtual-line (lambda (line) 9816 (+ line (line-number-at-pos begin-marker) -1)) 9817 :real->virtual-char (lambda (char) (+ char indentation)) 9818 :cleanup (lambda () 9819 (set-marker begin-marker nil) 9820 (set-marker end-marker nil)))) 9821 (setf virtual-buffer lsp--virtual-buffer) 9822 (puthash file-name virtual-buffer lsp--virtual-buffer-mappings) 9823 (push virtual-buffer lsp--virtual-buffer-connections) 9824 9825 ;; TODO: tangle only connected sections 9826 (add-hook 'after-save-hook 'org-babel-tangle nil t) 9827 (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t) 9828 (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t) 9829 9830 (setq lsp--buffer-workspaces 9831 (lsp-with-current-buffer virtual-buffer 9832 (lsp) 9833 (plist-put virtual-buffer :workspaces (lsp-workspaces)) 9834 (lsp-workspaces))))))) 9835 9836 (defun lsp-virtual-buffer-disconnect (virtual-buffer) 9837 (interactive (list (or 9838 lsp--virtual-buffer 9839 (when lsp--virtual-buffer-connections 9840 (lsp--completing-read "Select virtual buffer to disconnect: " 9841 lsp--virtual-buffer-connections 9842 (-lambda ((&plist :buffer-file-name)) 9843 buffer-file-name)))))) 9844 (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer) 9845 (progn 9846 (lsp-with-current-buffer virtual-buffer 9847 (lsp--text-document-did-close)) 9848 (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections)) 9849 (when (eq virtual-buffer lsp--virtual-buffer) 9850 (setf lsp--virtual-buffer nil)) 9851 (when cleanup (funcall cleanup)) 9852 (remhash file-name lsp--virtual-buffer-mappings) 9853 9854 (lsp--virtual-buffer-update-position) 9855 (lsp--info "Disconnected from buffer %s" file-name)) 9856 (lsp--error "Nothing to disconnect from?"))) 9857 9858 9859 ;; inlay hints 9860 9861 (defface lsp-inlay-hint-face 9862 '((t :inherit font-lock-comment-face)) 9863 "The face to use for the JavaScript inlays." 9864 :group 'lsp-mode 9865 :package-version '(lsp-mode . "9.0.0")) 9866 9867 (defface lsp-inlay-hint-type-face 9868 '((t :inherit lsp-inlay-hint-face)) 9869 "Face for inlay type hints (e.g. inferred variable types)." 9870 :group 'lsp-mode 9871 :package-version '(lsp-mode . "9.0.0")) 9872 9873 (defcustom lsp-inlay-hint-type-format "%s" 9874 "Format string for variable inlays (part of the inlay face)." 9875 :type '(string :tag "String") 9876 :group 'lsp-mode 9877 :package-version '(lsp-mode . "9.0.0")) 9878 9879 (defface lsp-inlay-hint-parameter-face 9880 '((t :inherit lsp-inlay-hint-face)) 9881 "Face for inlay parameter hints (e.g. function parameter names at 9882 call-site)." 9883 :group 'lsp-mode 9884 :package-version '(lsp-mode . "9.0.0")) 9885 9886 (defcustom lsp-inlay-hint-param-format "%s" 9887 "Format string for parameter inlays (part of the inlay face)." 9888 :type '(string :tag "String") 9889 :group 'lsp-mode 9890 :package-version '(lsp-mode . "9.0.0")) 9891 9892 (defcustom lsp-update-inlay-hints-on-scroll t 9893 "If non-nil update inlay hints immediately when scrolling or 9894 modifying window sizes." 9895 :type 'boolean 9896 :package-version '(lsp-mode . "9.0.0")) 9897 9898 (defun lsp--format-inlay (text kind) 9899 (cond 9900 ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text)) 9901 ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text)) 9902 (t text))) 9903 9904 (defun lsp--face-for-inlay (kind) 9905 (cond 9906 ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face) 9907 ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face) 9908 (t 'lsp-inlay-hint-face))) 9909 9910 (defun lsp--update-inlay-hints-scroll-function (window start) 9911 (lsp-update-inlay-hints start (window-end window t))) 9912 9913 (defun lsp--update-inlay-hints () 9914 (lsp-update-inlay-hints (window-start) (window-end nil t))) 9915 9916 (defun lsp--label-from-inlay-hints-response (label) 9917 "Returns a string label built from an array of 9918 InlayHintLabelParts or the argument itself if it's already a 9919 string." 9920 (cl-typecase label 9921 (string label) 9922 (vector 9923 (string-join (mapcar (lambda (part) 9924 (-let (((&InlayHintLabelPart :value) part)) 9925 value)) 9926 label))))) 9927 9928 (defun lsp-update-inlay-hints (start end) 9929 (lsp-request-async 9930 "textDocument/inlayHint" 9931 (lsp-make-inlay-hints-params 9932 :text-document (lsp--text-document-identifier) 9933 :range (lsp-make-range :start 9934 (lsp-point-to-position start) 9935 :end 9936 (lsp-point-to-position end))) 9937 (lambda (res) 9938 (lsp--remove-overlays 'lsp-inlay-hint) 9939 (dolist (hint res) 9940 (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint) 9941 (kind (or kind? lsp/inlay-hint-kind-type-hint)) 9942 (label (lsp--label-from-inlay-hints-response label)) 9943 (pos (lsp--position-to-point position)) 9944 (overlay (make-overlay pos pos nil 'front-advance 'end-advance))) 9945 (when (stringp label) 9946 (overlay-put overlay 'lsp-inlay-hint t) 9947 (overlay-put overlay 'before-string 9948 (format "%s%s%s" 9949 (if padding-left? " " "") 9950 (propertize (lsp--format-inlay label kind) 9951 'font-lock-face (lsp--face-for-inlay kind)) 9952 (if padding-right? " " ""))))))) 9953 :mode 'tick)) 9954 9955 (define-minor-mode lsp-inlay-hints-mode 9956 "Mode for displaying inlay hints." 9957 :lighter nil 9958 (cond 9959 ((and lsp-inlay-hints-mode lsp--buffer-workspaces) 9960 (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t) 9961 (when lsp-update-inlay-hints-on-scroll 9962 (add-to-list (make-local-variable 'window-scroll-functions) 9963 #'lsp--update-inlay-hints-scroll-function))) 9964 (t 9965 (lsp--remove-overlays 'lsp-inlay-hint) 9966 (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t) 9967 (setf window-scroll-functions 9968 (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions))))) 9969 9970 9971 9972 ;;;###autoload 9973 (defun lsp-start-plain () 9974 "Start `lsp-mode' using minimal configuration using the latest `melpa' version 9975 of the packages. 9976 9977 In case the major-mode that you are using for " 9978 (interactive) 9979 (let ((start-plain (make-temp-file "plain" nil ".el"))) 9980 (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el" 9981 start-plain t) 9982 (start-process "lsp-start-plain" 9983 (generate-new-buffer " *lsp-start-plain*") 9984 (expand-file-name invocation-name invocation-directory) 9985 "-q" "-l" start-plain (or (buffer-file-name) "")))) 9986 9987 9988 9989 (provide 'lsp-mode) 9990 ;;; lsp-mode.el ends here