lsp-mode.el (436272B)
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: 20241123.739 9 ;; Package-Revision: ae4907082b42 10 11 ;; URL: https://github.com/emacs-lsp/lsp-mode 12 ;; This program is free software; you can redistribute it and/or modify 13 ;; it under the terms of the GNU General Public License as published by 14 ;; the Free Software Foundation, either version 3 of the License, or 15 ;; (at your option) any later version. 16 17 ;; This program is distributed in the hope that it will be useful, 18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 ;; GNU General Public License for more details. 21 22 ;; You should have received a copy of the GNU General Public License 23 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 24 25 ;;; Commentary: 26 27 ;; Emacs client/library for the Language Server Protocol 28 29 ;;; Code: 30 31 (require 'cl-generic) 32 (require 'cl-lib) 33 (require 'compile) 34 (require 'dash) 35 (require 'epg) 36 (require 'ewoc) 37 (require 'f) 38 (require 'filenotify) 39 (require 'files) 40 (require 'ht) 41 (require 'imenu) 42 (require 'inline) 43 (require 'json) 44 (require 'lv) 45 (require 'markdown-mode) 46 (require 'network-stream) 47 (require 'pcase) 48 (require 'rx) 49 (require 's) 50 (require 'seq) 51 (require 'spinner) 52 (require 'subr-x) 53 (require 'tree-widget) 54 (require 'url-parse) 55 (require 'url-util) 56 (require 'widget) 57 (require 'xref) 58 (require 'minibuffer) 59 (require 'help-mode) 60 (require 'lsp-protocol) 61 62 (defgroup lsp-mode nil 63 "Language Server Protocol client." 64 :group 'tools 65 :tag "Language Server (lsp-mode)") 66 67 (declare-function evil-set-command-property "ext:evil-common") 68 (declare-function projectile-project-root "ext:projectile") 69 (declare-function yas-expand-snippet "ext:yasnippet") 70 (declare-function dap-mode "ext:dap-mode") 71 (declare-function dap-auto-configure-mode "ext:dap-mode") 72 73 (defvar yas-inhibit-overlay-modification-protection) 74 (defvar yas-indent-line) 75 (defvar yas-wrap-around-region) 76 (defvar yas-also-auto-indent-first-line) 77 (defvar dap-auto-configure-mode) 78 (defvar dap-ui-menu-items) 79 (defvar company-minimum-prefix-length) 80 81 (defconst lsp--message-type-face 82 `((1 . ,compilation-error-face) 83 (2 . ,compilation-warning-face) 84 (3 . ,compilation-message-face) 85 (4 . ,compilation-info-face))) 86 87 (defconst lsp--errors 88 '((-32700 "Parse Error") 89 (-32600 "Invalid Request") 90 (-32601 "Method not Found") 91 (-32602 "Invalid Parameters") 92 (-32603 "Internal Error") 93 (-32099 "Server Start Error") 94 (-32000 "Server End Error") 95 (-32002 "Server Not Initialized") 96 (-32001 "Unknown Error Code") 97 (-32800 "Request Cancelled")) 98 "Alist of error codes to user friendly strings.") 99 100 (defconst lsp--empty-ht (make-hash-table)) 101 102 (eval-and-compile 103 (defun dash-expand:&lsp-wks (key source) 104 `(,(intern-soft (format "lsp--workspace-%s" (eval key))) ,source)) 105 106 (defun dash-expand:&lsp-cln (key source) 107 `(,(intern-soft (format "lsp--client-%s" (eval key))) ,source))) 108 109 (define-obsolete-variable-alias 'lsp-print-io 'lsp-log-io "lsp-mode 6.1") 110 111 (defcustom lsp-log-io nil 112 "If non-nil, log all messages from the language server to a *lsp-log* buffer." 113 :group 'lsp-mode 114 :type 'boolean) 115 116 (defcustom lsp-log-io-allowlist-methods '() 117 "The methods to filter before print to lsp-log-io." 118 :group 'lsp-mode 119 :type '(repeat string) 120 :package-version '(lsp-mode . "9.0.0")) 121 122 (defcustom lsp-log-max message-log-max 123 "Maximum number of lines to keep in the log buffer. 124 If nil, disable message logging. If t, log messages but don’t truncate 125 the buffer when it becomes large." 126 :group 'lsp-mode 127 :type '(choice (const :tag "Disable" nil) 128 (integer :tag "lines") 129 (const :tag "Unlimited" t)) 130 :package-version '(lsp-mode . "6.1")) 131 132 (defcustom lsp-io-messages-max t 133 "Maximum number of messages that can be locked in a `lsp-io' buffer." 134 :group 'lsp-mode 135 :type '(choice (const :tag "Unlimited" t) 136 (integer :tag "Messages")) 137 :package-version '(lsp-mode . "6.1")) 138 139 (defcustom lsp-keep-workspace-alive t 140 "If non nil keep workspace alive when the last workspace buffer is closed." 141 :group 'lsp-mode 142 :type 'boolean) 143 144 (defcustom lsp-enable-snippet t 145 "Enable/disable snippet completion support." 146 :group 'lsp-completion 147 :type 'boolean) 148 149 (defcustom lsp-enable-folding t 150 "Enable/disable code folding support." 151 :group 'lsp-mode 152 :type 'boolean 153 :package-version '(lsp-mode . "6.1")) 154 155 (define-obsolete-variable-alias 'lsp-enable-semantic-highlighting 'lsp-semantic-tokens-enable "lsp-mode 8.0.0") 156 157 (defcustom lsp-semantic-tokens-enable nil 158 "Enable/disable support for semantic tokens. 159 As defined by the Language Server Protocol 3.16." 160 :group 'lsp-semantic-tokens 161 :type 'boolean) 162 163 (defcustom lsp-folding-range-limit nil 164 "The maximum number of folding ranges to receive from the language server." 165 :group 'lsp-mode 166 :type '(choice (const :tag "No limit." nil) 167 (integer :tag "Number of lines.")) 168 :package-version '(lsp-mode . "6.1")) 169 170 (defcustom lsp-folding-line-folding-only nil 171 "If non-nil, only fold complete lines." 172 :group 'lsp-mode 173 :type 'boolean 174 :package-version '(lsp-mode . "6.1")) 175 176 (defcustom lsp-client-packages 177 '( ccls lsp-actionscript lsp-ada lsp-angular lsp-ansible lsp-asm lsp-astro 178 lsp-autotools lsp-awk lsp-bash lsp-beancount lsp-bufls lsp-clangd 179 lsp-clojure lsp-cmake lsp-cobol lsp-credo lsp-crystal lsp-csharp lsp-css 180 lsp-cucumber lsp-cypher lsp-d lsp-dart lsp-dhall lsp-docker lsp-dockerfile 181 lsp-earthly lsp-elixir lsp-elm lsp-emmet lsp-erlang lsp-eslint lsp-fortran lsp-futhark 182 lsp-fsharp lsp-gdscript lsp-gleam lsp-glsl lsp-go lsp-golangci-lint lsp-grammarly 183 lsp-graphql lsp-groovy lsp-hack lsp-haskell lsp-haxe lsp-idris lsp-java 184 lsp-javascript lsp-jq lsp-json lsp-kotlin lsp-latex lsp-lisp lsp-ltex 185 lsp-lua lsp-fennel lsp-magik lsp-markdown lsp-marksman lsp-mdx lsp-meson lsp-metals lsp-mint 186 lsp-mojo lsp-move lsp-mssql lsp-nextflow lsp-nginx lsp-nim lsp-nix lsp-nushell lsp-ocaml 187 lsp-openscad lsp-pascal lsp-perl lsp-perlnavigator lsp-php lsp-pls 188 lsp-purescript lsp-pwsh lsp-pyls lsp-pylsp lsp-pyright lsp-python-ms 189 lsp-qml lsp-r lsp-racket lsp-remark lsp-rf lsp-roslyn lsp-rubocop lsp-ruby-lsp 190 lsp-ruby-syntax-tree lsp-ruff lsp-rust lsp-semgrep lsp-shader 191 lsp-solargraph lsp-solidity lsp-sonarlint lsp-sorbet lsp-sourcekit 192 lsp-sql lsp-sqls lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform 193 lsp-tex lsp-tilt lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-typespec lsp-v 194 lsp-vala lsp-verilog lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl 195 lsp-xml lsp-yaml lsp-yang lsp-zig) 196 "List of the clients to be automatically required." 197 :group 'lsp-mode 198 :type '(repeat symbol)) 199 200 (defcustom lsp-progress-via-spinner t 201 "If non-nil, display LSP $/progress reports via a spinner in the modeline." 202 :group 'lsp-mode 203 :type 'boolean) 204 205 (defcustom lsp-progress-spinner-type 'progress-bar 206 "Holds the type of spinner to be used in the mode-line. 207 Takes a value accepted by `spinner-start'." 208 :group 'lsp-mode 209 :type `(choice :tag "Choose a spinner by name" 210 ,@(mapcar (lambda (c) (list 'const (car c))) 211 spinner-types))) 212 213 (defvar-local lsp-use-workspace-root-for-server-default-directory nil 214 "Use `lsp-workspace-root' for `default-directory' when starting LSP process.") 215 216 (defvar-local lsp--cur-workspace nil) 217 218 (defvar-local lsp--cur-version 0) 219 (defvar-local lsp--virtual-buffer-connections nil) 220 (defvar-local lsp--virtual-buffer nil) 221 (defvar lsp--virtual-buffer-mappings (ht)) 222 223 (defvar lsp--uri-file-prefix (pcase system-type 224 (`windows-nt "file:///") 225 (_ "file://")) 226 "Prefix for a file-uri.") 227 228 (defvar-local lsp-buffer-uri nil 229 "If set, return it instead of calculating it using `buffer-file-name'.") 230 231 (define-error 'lsp-error "Unknown lsp-mode error") 232 (define-error 'lsp-empty-response-error 233 "Empty response from the language server" 'lsp-error) 234 (define-error 'lsp-timed-out-error 235 "Timed out while waiting for a response from the language server" 'lsp-error) 236 (define-error 'lsp-capability-not-supported 237 "Capability not supported by the language server" 'lsp-error) 238 (define-error 'lsp-file-scheme-not-supported 239 "Unsupported file scheme" 'lsp-error) 240 (define-error 'lsp-client-already-exists-error 241 "A client with this server-id already exists" 'lsp-error) 242 (define-error 'lsp-no-code-actions 243 "No code actions" 'lsp-error) 244 245 (defcustom lsp-auto-guess-root nil 246 "Automatically guess the project root using projectile/project. 247 Do *not* use this setting unless you are familiar with `lsp-mode' 248 internals and you are sure that all of your projects are 249 following `projectile'/`project.el' conventions." 250 :group 'lsp-mode 251 :type 'boolean) 252 253 (defcustom lsp-guess-root-without-session nil 254 "Ignore the session file when calculating the project root. 255 You almost always want to set lsp-auto-guess-root too. 256 Do *not* use this setting unless you are familiar with `lsp-mode' 257 internals and you are sure that all of your projects are 258 following `projectile'/`project.el' conventions." 259 :group 'lsp-mode 260 :type 'boolean) 261 262 (defcustom lsp-restart 'interactive 263 "Defines how server-exited events must be handled." 264 :group 'lsp-mode 265 :type '(choice (const interactive) 266 (const auto-restart) 267 (const ignore))) 268 269 (defcustom lsp-session-file (expand-file-name (locate-user-emacs-file ".lsp-session-v1")) 270 "File where session information is stored." 271 :group 'lsp-mode 272 :type 'file) 273 274 (defcustom lsp-auto-configure t 275 "Auto configure `lsp-mode' main features. 276 When set to t `lsp-mode' will auto-configure completion, 277 code-actions, breadcrumb, `flycheck', `flymake', `imenu', symbol highlighting, 278 lenses, links, and so on. 279 280 For finer granularity you may use `lsp-enable-*' properties." 281 :group 'lsp-mode 282 :type 'boolean 283 :package-version '(lsp-mode . "6.1")) 284 285 (defcustom lsp-disabled-clients nil 286 "A list of disabled/blocklisted clients. 287 Each entry in the list can be either: 288 a symbol, the server-id for the LSP client, or 289 a cons pair (MAJOR-MODE . CLIENTS), where MAJOR-MODE is the major-mode, 290 and CLIENTS is either a client or a list of clients. 291 292 This option can also be used as a file- or directory-local variable to 293 disable a language server for individual files or directories/projects 294 respectively." 295 :group 'lsp-mode 296 :type '(repeat (symbol)) 297 :safe 'listp 298 :package-version '(lsp-mode . "6.1")) 299 300 (defvar lsp-clients (make-hash-table :test 'eql) 301 "Hash table server-id -> client. 302 It contains all of the clients that are currently registered.") 303 304 (defvar lsp-enabled-clients nil 305 "List of clients allowed to be used for projects. 306 When nil, all registered clients are considered candidates.") 307 308 (defvar lsp-last-id 0 309 "Last request id.") 310 311 (defcustom lsp-before-initialize-hook nil 312 "List of functions to be called before a Language Server has been initialized 313 for a new workspace." 314 :type 'hook 315 :group 'lsp-mode) 316 317 (defcustom lsp-after-initialize-hook nil 318 "List of functions to be called after a Language Server has been initialized 319 for a new workspace." 320 :type 'hook 321 :group 'lsp-mode) 322 323 (defcustom lsp-before-open-hook nil 324 "List of functions to be called before a new file with LSP support is opened." 325 :type 'hook 326 :group 'lsp-mode) 327 328 (defcustom lsp-after-open-hook nil 329 "List of functions to be called after a new file with LSP support is opened." 330 :type 'hook 331 :group 'lsp-mode) 332 333 (defcustom lsp-enable-file-watchers t 334 "If non-nil lsp-mode will watch the files in the workspace if 335 the server has requested that." 336 :type 'boolean 337 :group 'lsp-mode 338 :package-version '(lsp-mode . "6.1")) 339 ;;;###autoload(put 'lsp-enable-file-watchers 'safe-local-variable #'booleanp) 340 341 (define-obsolete-variable-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "8.0.0") 342 343 (defcustom lsp-file-watch-ignored-directories 344 '(; SCM tools 345 "[/\\\\]\\.git\\'" 346 "[/\\\\]\\.github\\'" 347 "[/\\\\]\\.gitlab\\'" 348 "[/\\\\]\\.circleci\\'" 349 "[/\\\\]\\.hg\\'" 350 "[/\\\\]\\.bzr\\'" 351 "[/\\\\]_darcs\\'" 352 "[/\\\\]\\.svn\\'" 353 "[/\\\\]_FOSSIL_\\'" 354 ;; IDE or build tools 355 "[/\\\\]\\.idea\\'" 356 "[/\\\\]\\.ensime_cache\\'" 357 "[/\\\\]\\.eunit\\'" 358 "[/\\\\]node_modules" 359 "[/\\\\]\\.yarn\\'" 360 "[/\\\\]\\.fslckout\\'" 361 "[/\\\\]\\.tox\\'" 362 "[/\\\\]\\.nox\\'" 363 "[/\\\\]dist\\'" 364 "[/\\\\]dist-newstyle\\'" 365 "[/\\\\]\\.stack-work\\'" 366 "[/\\\\]\\.bloop\\'" 367 "[/\\\\]\\.metals\\'" 368 "[/\\\\]target\\'" 369 "[/\\\\]\\.ccls-cache\\'" 370 "[/\\\\]\\.vs\\'" 371 "[/\\\\]\\.vscode\\'" 372 "[/\\\\]\\.venv\\'" 373 "[/\\\\]\\.mypy_cache\\'" 374 "[/\\\\]\\.pytest_cache\\'" 375 ;; Swift Package Manager 376 "[/\\\\]\\.build\\'" 377 ;; Python 378 "[/\\\\]__pycache__\\'" 379 "[/\\\\]site-packages\\'" 380 "[/\\\\].pyenv\\'" 381 ;; Autotools output 382 "[/\\\\]\\.deps\\'" 383 "[/\\\\]build-aux\\'" 384 "[/\\\\]autom4te.cache\\'" 385 "[/\\\\]\\.reference\\'" 386 ;; Bazel 387 "[/\\\\]bazel-[^/\\\\]+\\'" 388 ;; CSharp 389 "[/\\\\]\\.cache[/\\\\]lsp-csharp\\'" 390 "[/\\\\]\\.meta\\'" 391 "[/\\\\]\\.nuget\\'" 392 ;; Unity 393 "[/\\\\]Library\\'" 394 ;; Clojure 395 "[/\\\\]\\.lsp\\'" 396 "[/\\\\]\\.clj-kondo\\'" 397 "[/\\\\]\\.shadow-cljs\\'" 398 "[/\\\\]\\.babel_cache\\'" 399 "[/\\\\]\\.cpcache\\'" 400 "[/\\\\]\\checkouts\\'" 401 ;; Gradle 402 "[/\\\\]\\.gradle\\'" 403 ;; Maven 404 "[/\\\\]\\.m2\\'" 405 ;; .Net Core build-output 406 "[/\\\\]bin/Debug\\'" 407 "[/\\\\]obj\\'" 408 ;; OCaml and Dune 409 "[/\\\\]_opam\\'" 410 "[/\\\\]_build\\'" 411 ;; Elixir 412 "[/\\\\]\\.elixir_ls\\'" 413 ;; Elixir Credo 414 "[/\\\\]\\.elixir-tools\\'" 415 ;; terraform and terragrunt 416 "[/\\\\]\\.terraform\\'" 417 "[/\\\\]\\.terragrunt-cache\\'" 418 ;; nix-direnv 419 "[/\\\\]\\result" 420 "[/\\\\]\\result-bin" 421 "[/\\\\]\\.direnv\\'") 422 "List of regexps matching directory paths which won't be monitored when 423 creating file watches. Customization of this variable is only honored at 424 the global level or at a root of an lsp workspace." 425 :group 'lsp-mode 426 :type '(repeat string) 427 :package-version '(lsp-mode . "8.0.0")) 428 429 (define-obsolete-function-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "7.0.1") 430 431 (defun lsp-file-watch-ignored-directories () 432 lsp-file-watch-ignored-directories) 433 434 ;; Allow lsp-file-watch-ignored-directories as a file or directory-local variable 435 ;;;###autoload(put 'lsp-file-watch-ignored-directories 'safe-local-variable 'lsp--string-listp) 436 437 (defcustom lsp-file-watch-ignored-files 438 '( 439 ;; Flycheck tempfiles 440 "[/\\\\]flycheck_[^/\\\\]+\\'" 441 ;; lockfiles 442 "[/\\\\]\\.#[^/\\\\]+\\'" 443 ;; backup files 444 "[/\\\\][^/\\\\]+~\\'" ) 445 "List of regexps matching files for which change events will 446 not be sent to the server. 447 448 This setting has no impact on whether a file-watch is created for 449 a directory; it merely prevents notifications pertaining to 450 matched files from being sent to the server. To prevent a 451 file-watch from being created for a directory, customize 452 `lsp-file-watch-ignored-directories' 453 454 Customization of this variable is only honored at the global 455 level or at a root of an lsp workspace." 456 :group 'lsp-mode 457 :type '(repeat string) 458 :package-version '(lsp-mode . "8.0.0")) 459 460 ;; Allow lsp-file-watch-ignored-files as a file or directory-local variable 461 ;;;###autoload(put 'lsp-file-watch-ignored-files 'safe-local-variable 'lsp--string-listp) 462 463 (defcustom lsp-after-uninitialized-functions nil 464 "List of functions to be called after a Language Server has been uninitialized." 465 :type 'hook 466 :group 'lsp-mode 467 :package-version '(lsp-mode . "6.3")) 468 469 (defconst lsp--sync-full 1) 470 (defconst lsp--sync-incremental 2) 471 472 (defcustom lsp-debounce-full-sync-notifications t 473 "If non-nil debounce full sync events. 474 This flag affects only servers which do not support incremental updates." 475 :type 'boolean 476 :group 'lsp-mode 477 :package-version '(lsp-mode . "6.1")) 478 479 (defcustom lsp-debounce-full-sync-notifications-interval 1.0 480 "Time to wait before sending full sync synchronization after buffer modification." 481 :type 'float 482 :group 'lsp-mode 483 :package-version '(lsp-mode . "6.1")) 484 485 (defvar lsp--stderr-index 0) 486 487 (defvar lsp--delayed-requests nil) 488 (defvar lsp--delay-timer nil) 489 490 (defcustom lsp-document-sync-method nil 491 "How to sync the document with the language server." 492 :type '(choice (const :tag "Documents are synced by always sending the full content of the document." lsp--sync-full) 493 (const :tag "Documents are synced by always sending incremental changes to the document." lsp--sync-incremental) 494 (const :tag "Use the method recommended by the language server." nil)) 495 :group 'lsp-mode) 496 497 (defcustom lsp-auto-execute-action t 498 "Auto-execute single action." 499 :type 'boolean 500 :group 'lsp-mode) 501 502 (defcustom lsp-enable-links t 503 "If non-nil, all references to links in a file will be made clickable, if 504 supported by the language server." 505 :type 'boolean 506 :group 'lsp-mode 507 :package-version '(lsp-mode . "6.1")) 508 509 (defcustom lsp-enable-imenu t 510 "If non-nil, automatically enable `imenu' integration when server provides 511 `textDocument/documentSymbol'." 512 :type 'boolean 513 :group 'lsp-mode 514 :package-version '(lsp-mode . "6.2")) 515 516 (defcustom lsp-enable-dap-auto-configure t 517 "If non-nil, enable `dap-auto-configure-mode`." 518 :type 'boolean 519 :group 'lsp-mode 520 :package-version '(lsp-mode . "7.0")) 521 522 (defcustom lsp-eldoc-enable-hover t 523 "If non-nil, `eldoc' will display hover info when it is present." 524 :type 'boolean 525 :group 'lsp-mode) 526 527 (defcustom lsp-eldoc-render-all nil 528 "Display all of the info returned by document/onHover. 529 If this is set to nil, `eldoc' will show only the symbol information." 530 :type 'boolean 531 :group 'lsp-mode) 532 533 (define-obsolete-variable-alias 'lsp-enable-completion-at-point 534 'lsp-completion-enable "lsp-mode 7.0.1") 535 536 (defcustom lsp-completion-enable t 537 "Enable `completion-at-point' integration." 538 :type 'boolean 539 :group 'lsp-completion) 540 541 (defcustom lsp-enable-symbol-highlighting t 542 "Highlight references of the symbol at point." 543 :type 'boolean 544 :group 'lsp-mode) 545 546 (defcustom lsp-enable-xref t 547 "Enable xref integration." 548 :type 'boolean 549 :group 'lsp-mode) 550 551 (define-obsolete-variable-alias 552 'lsp-references-exclude-definition 553 'lsp-references-exclude-declaration 554 "9.0.1") 555 556 (defcustom lsp-references-exclude-declaration nil 557 "If non-nil, exclude declarations when finding references." 558 :type 'boolean 559 :group 'lsp-mode) 560 561 (defcustom lsp-enable-indentation t 562 "Indent regions using the file formatting functionality provided by the 563 language server." 564 :type 'boolean 565 :group 'lsp-mode) 566 567 (defcustom lsp-enable-on-type-formatting t 568 "Enable `textDocument/onTypeFormatting' integration." 569 :type 'boolean 570 :group 'lsp-mode) 571 572 (defcustom lsp-enable-text-document-color t 573 "Enable `textDocument/documentColor' integration." 574 :type 'boolean 575 :group 'lsp-mode) 576 577 (defcustom lsp-before-save-edits t 578 "If non-nil, `lsp-mode' will apply edits suggested by the language server 579 before saving a document." 580 :type 'boolean 581 :group 'lsp-mode) 582 583 (defcustom lsp-after-apply-edits-hook nil 584 "Hooks to run when text edit is applied. 585 It contains the operation source." 586 :type 'hook 587 :group 'lsp-mode 588 :package-version '(lsp-mode . "8.0.0")) 589 590 (defcustom lsp-apply-edits-after-file-operations t 591 "Whether to apply edits returned by server after file operations if any. 592 Applicable only if server supports workspace.fileOperations for operations: 593 `workspace/willRenameFiles', `workspace/willCreateFiles' and 594 `workspace/willDeleteFiles'." 595 :group 'lsp-mode 596 :type 'boolean) 597 598 (defcustom lsp-modeline-code-actions-enable t 599 "Whether to show code actions on modeline." 600 :type 'boolean 601 :group 'lsp-modeline) 602 603 (defcustom lsp-modeline-diagnostics-enable t 604 "Whether to show diagnostics on modeline." 605 :type 'boolean 606 :group 'lsp-modeline) 607 608 (defcustom lsp-modeline-workspace-status-enable t 609 "Whether to show workspace status on modeline." 610 :type 'boolean 611 :group 'lsp-modeline 612 :package-version '(lsp-mode . "8.0.0")) 613 614 (defcustom lsp-headerline-breadcrumb-enable t 615 "Whether to enable breadcrumb on headerline." 616 :type 'boolean 617 :group 'lsp-headerline) 618 619 (defcustom lsp-configure-hook nil 620 "Hooks to run when `lsp-configure-buffer' is called." 621 :type 'hook 622 :group 'lsp-mode) 623 624 (defcustom lsp-unconfigure-hook nil 625 "Hooks to run when `lsp-unconfig-buffer' is called." 626 :type 'hook 627 :group 'lsp-mode) 628 629 (defcustom lsp-after-diagnostics-hook nil 630 "Hooks to run after diagnostics are received. 631 Note: it runs only if the receiving buffer is open. Use 632 `lsp-diagnostics-updated-hook'if you want to be notified when 633 diagnostics have changed." 634 :type 'hook 635 :group 'lsp-mode) 636 637 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook 638 'lsp-diagnostics-updated-hook "lsp-mode 6.4") 639 640 (defcustom lsp-diagnostics-updated-hook nil 641 "Hooks to run after diagnostics are received." 642 :type 'hook 643 :group 'lsp-mode) 644 645 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook 646 'lsp-workspace-folders-changed-functions "lsp-mode 6.3") 647 648 (defcustom lsp-workspace-folders-changed-functions nil 649 "Hooks to run after the folders has changed. 650 The hook will receive two parameters list of added and removed folders." 651 :type 'hook 652 :group 'lsp-mode) 653 654 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0") 655 656 (defcustom lsp-before-apply-edits-hook nil 657 "Hooks to run before applying edits." 658 :type 'hook 659 :group 'lsp-mode) 660 661 (defgroup lsp-imenu nil 662 "LSP Imenu." 663 :group 'lsp-mode 664 :tag "LSP Imenu") 665 666 (defcustom lsp-imenu-show-container-name t 667 "Display the symbol's container name in an imenu entry." 668 :type 'boolean 669 :group 'lsp-imenu) 670 671 (defcustom lsp-imenu-container-name-separator "/" 672 "Separator string to use to separate the container name from the symbol while 673 displaying imenu entries." 674 :type 'string 675 :group 'lsp-imenu) 676 677 (defcustom lsp-imenu-sort-methods '(kind name) 678 "How to sort the imenu items. 679 680 The value is a list of `kind' `name' or `position'. Priorities 681 are determined by the index of the element." 682 :type '(repeat (choice (const name) 683 (const position) 684 (const kind))) 685 :group 'lsp-imenu) 686 687 (defcustom lsp-imenu-index-symbol-kinds nil 688 "Which symbol kinds to show in imenu." 689 :type '(repeat (choice (const :tag "Miscellaneous" nil) 690 (const :tag "File" File) 691 (const :tag "Module" Module) 692 (const :tag "Namespace" Namespace) 693 (const :tag "Package" Package) 694 (const :tag "Class" Class) 695 (const :tag "Method" Method) 696 (const :tag "Property" Property) 697 (const :tag "Field" Field) 698 (const :tag "Constructor" Constructor) 699 (const :tag "Enum" Enum) 700 (const :tag "Interface" Interface) 701 (const :tag "Function" Function) 702 (const :tag "Variable" Variable) 703 (const :tag "Constant" Constant) 704 (const :tag "String" String) 705 (const :tag "Number" Number) 706 (const :tag "Boolean" Boolean) 707 (const :tag "Array" Array) 708 (const :tag "Object" Object) 709 (const :tag "Key" Key) 710 (const :tag "Null" Null) 711 (const :tag "Enum Member" EnumMember) 712 (const :tag "Struct" Struct) 713 (const :tag "Event" Event) 714 (const :tag "Operator" Operator) 715 (const :tag "Type Parameter" TypeParameter))) 716 :group 'lsp-imenu) 717 718 ;; vibhavp: Should we use a lower value (5)? 719 (defcustom lsp-response-timeout 10 720 "Number of seconds to wait for a response from the language server before 721 timing out. Nil if no timeout." 722 :type '(choice 723 (number :tag "Seconds") 724 (const :tag "No timeout" nil)) 725 :group 'lsp-mode) 726 727 (defcustom lsp-tcp-connection-timeout 2 728 "The timeout for tcp connection in seconds." 729 :type 'number 730 :group 'lsp-mode 731 :package-version '(lsp-mode . "6.2")) 732 733 (defconst lsp--imenu-compare-function-alist 734 (list (cons 'name #'lsp--imenu-compare-name) 735 (cons 'kind #'lsp--imenu-compare-kind) 736 (cons 'position #'lsp--imenu-compare-line-col)) 737 "An alist of (METHOD . FUNCTION). 738 METHOD is one of the symbols accepted by 739 `lsp-imenu-sort-methods'. 740 741 FUNCTION takes two hash tables representing DocumentSymbol. It 742 returns a negative number, 0, or a positive number indicating 743 whether the first parameter is less than, equal to, or greater 744 than the second parameter.") 745 746 (defcustom lsp-diagnostic-clean-after-change nil 747 "When non-nil, clean the diagnostics on change. 748 749 Note that when that setting is nil, `lsp-mode' will show stale 750 diagnostics until server publishes the new set of diagnostics" 751 :type 'boolean 752 :group 'lsp-diagnostics 753 :package-version '(lsp-mode . "7.0.1")) 754 755 (defcustom lsp-server-trace nil 756 "Request tracing on the server side. 757 The actual trace output at each level depends on the language server in use. 758 Changes take effect only when a new session is started." 759 :type '(choice (const :tag "Disabled" "off") 760 (const :tag "Messages only" "messages") 761 (const :tag "Verbose" "verbose") 762 (const :tag "Default (disabled)" nil)) 763 :group 'lsp-mode 764 :package-version '(lsp-mode . "6.1")) 765 766 (defcustom lsp-auto-touch-files t 767 "If non-nil ensure the files exist before sending 768 `textDocument/didOpen' notification." 769 :type 'boolean 770 :group 'lsp-mode 771 :package-version '(lsp-mode . "9.0.0")) 772 773 (defvar lsp-language-id-configuration 774 '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake") 775 ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile") 776 ("\\.astro$" . "astro") 777 ("\\.cs\\'" . "csharp") 778 ("\\.css$" . "css") 779 ("\\.cypher$" . "cypher") 780 ("Earthfile" . "earthfile") 781 ("\\.ebuild$" . "shellscript") 782 ("\\.go\\'" . "go") 783 ("\\.html$" . "html") 784 ("\\.hx$" . "haxe") 785 ("\\.hy$" . "hy") 786 ("\\.java\\'" . "java") 787 ("\\.jq$" . "jq") 788 ("\\.js$" . "javascript") 789 ("\\.json$" . "json") 790 ("\\.jsonc$" . "jsonc") 791 ("\\.jsonnet$" . "jsonnet") 792 ("\\.jsx$" . "javascriptreact") 793 ("\\.lua$" . "lua") 794 ("\\.fnl$" . "fennel") 795 ("\\.mdx\\'" . "mdx") 796 ("\\.nu$" . "nushell") 797 ("\\.php$" . "php") 798 ("\\.ps[dm]?1\\'" . "powershell") 799 ("\\.rs\\'" . "rust") 800 ("\\.spec\\'" . "rpm-spec") 801 ("\\.sql$" . "sql") 802 ("\\.svelte$" . "svelte") 803 ("\\.toml\\'" . "toml") 804 ("\\.ts$" . "typescript") 805 ("\\.tsp$" . "typespec") 806 ("\\.tsx$" . "typescriptreact") 807 ("\\.ttcn3$" . "ttcn3") 808 ("\\.vue$" . "vue") 809 ("\\.xml$" . "xml") 810 ("\\ya?ml$" . "yaml") 811 ("^PKGBUILD$" . "shellscript") 812 ("^go\\.mod\\'" . "go.mod") 813 ("^settings\\.json$" . "jsonc") 814 ("^yang\\.settings$" . "jsonc") 815 ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson") 816 (ada-mode . "ada") 817 (ada-ts-mode . "ada") 818 (gpr-mode . "gpr") 819 (gpr-ts-mode . "gpr") 820 (awk-mode . "awk") 821 (awk-ts-mode . "awk") 822 (nxml-mode . "xml") 823 (sql-mode . "sql") 824 (vimrc-mode . "vim") 825 (vimscript-ts-mode . "vim") 826 (sh-mode . "shellscript") 827 (bash-ts-mode . "shellscript") 828 (ebuild-mode . "shellscript") 829 (pkgbuild-mode . "shellscript") 830 (envrc-file-mode . "shellscript") 831 (scala-mode . "scala") 832 (scala-ts-mode . "scala") 833 (julia-mode . "julia") 834 (julia-ts-mode . "julia") 835 (clojure-mode . "clojure") 836 (clojurec-mode . "clojure") 837 (clojurescript-mode . "clojurescript") 838 (clojure-ts-mode . "clojure") 839 (clojure-ts-clojurec-mode . "clojure") 840 (clojure-ts-clojurescript-mode . "clojurescript") 841 (java-mode . "java") 842 (java-ts-mode . "java") 843 (jdee-mode . "java") 844 (groovy-mode . "groovy") 845 (nextflow-mode . "nextflow") 846 (python-mode . "python") 847 (python-ts-mode . "python") 848 (cython-mode . "python") 849 ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo") 850 (lsp--render-markdown . "markdown") 851 (move-mode . "move") 852 (rust-mode . "rust") 853 (rust-ts-mode . "rust") 854 (rustic-mode . "rust") 855 (kotlin-mode . "kotlin") 856 (kotlin-ts-mode . "kotlin") 857 (css-mode . "css") 858 (css-ts-mode . "css") 859 (less-mode . "less") 860 (less-css-mode . "less") 861 (lua-mode . "lua") 862 (lua-ts-mode . "lua") 863 (sass-mode . "sass") 864 (ssass-mode . "sass") 865 (scss-mode . "scss") 866 (scad-mode . "openscad") 867 (xml-mode . "xml") 868 (c-mode . "c") 869 (c-ts-mode . "c") 870 (c++-mode . "cpp") 871 (c++-ts-mode . "cpp") 872 (cuda-mode . "cuda") 873 (objc-mode . "objective-c") 874 (html-mode . "html") 875 (html-ts-mode . "html") 876 (sgml-mode . "html") 877 (mhtml-mode . "html") 878 (mint-mode . "mint") 879 (go-dot-mod-mode . "go.mod") 880 (go-mod-ts-mode . "go.mod") 881 (go-mode . "go") 882 (go-ts-mode . "go") 883 (graphql-mode . "graphql") 884 (haskell-mode . "haskell") 885 (haskell-ts-mode . "haskell") 886 (hack-mode . "hack") 887 (php-mode . "php") 888 (php-ts-mode . "php") 889 (powershell-mode . "powershell") 890 (powershell-mode . "PowerShell") 891 (powershell-ts-mode . "powershell") 892 (json-mode . "json") 893 (json-ts-mode . "json") 894 (jsonc-mode . "jsonc") 895 (rjsx-mode . "javascript") 896 (js2-mode . "javascript") 897 (js-mode . "javascript") 898 (js-ts-mode . "javascript") 899 (typescript-mode . "typescript") 900 (typescript-ts-mode . "typescript") 901 (typespec-mode . "typespec") 902 (tsx-ts-mode . "typescriptreact") 903 (svelte-mode . "svelte") 904 (fsharp-mode . "fsharp") 905 (reason-mode . "reason") 906 (caml-mode . "ocaml") 907 (tuareg-mode . "ocaml") 908 (futhark-mode . "futhark") 909 (swift-mode . "swift") 910 (elixir-mode . "elixir") 911 (elixir-ts-mode . "elixir") 912 (heex-ts-mode . "elixir") 913 (conf-javaprop-mode . "spring-boot-properties") 914 (yaml-mode . "yaml") 915 (yaml-ts-mode . "yaml") 916 (ruby-mode . "ruby") 917 (enh-ruby-mode . "ruby") 918 (ruby-ts-mode . "ruby") 919 (feature-mode . "cucumber") 920 (fortran-mode . "fortran") 921 (f90-mode . "fortran") 922 (elm-mode . "elm") 923 (dart-mode . "dart") 924 (erlang-mode . "erlang") 925 (dockerfile-mode . "dockerfile") 926 (dockerfile-ts-mode . "dockerfile") 927 (csharp-mode . "csharp") 928 (csharp-tree-sitter-mode . "csharp") 929 (csharp-ts-mode . "csharp") 930 (plain-tex-mode . "plaintex") 931 (context-mode . "context") 932 (cypher-mode . "cypher") 933 (latex-mode . "latex") 934 (LaTeX-mode . "latex") 935 (v-mode . "v") 936 (vhdl-mode . "vhdl") 937 (vhdl-ts-mode . "vhdl") 938 (verilog-mode . "verilog") 939 (terraform-mode . "terraform") 940 (ess-julia-mode . "julia") 941 (ess-r-mode . "r") 942 (crystal-mode . "crystal") 943 (nim-mode . "nim") 944 (dhall-mode . "dhall") 945 (cmake-mode . "cmake") 946 (cmake-ts-mode . "cmake") 947 (purescript-mode . "purescript") 948 (gdscript-mode . "gdscript") 949 (gdscript-ts-mode . "gdscript") 950 (perl-mode . "perl") 951 (cperl-mode . "perl") 952 (robot-mode . "robot") 953 (racket-mode . "racket") 954 (nix-mode . "nix") 955 (nix-ts-mode . "nix") 956 (prolog-mode . "prolog") 957 (vala-mode . "vala") 958 (actionscript-mode . "actionscript") 959 (d-mode . "d") 960 (zig-mode . "zig") 961 (zig-ts-mode . "zig") 962 (text-mode . "plaintext") 963 (markdown-mode . "markdown") 964 (gfm-mode . "markdown") 965 (beancount-mode . "beancount") 966 (conf-toml-mode . "toml") 967 (toml-ts-mode . "toml") 968 (org-mode . "org") 969 (org-journal-mode . "org") 970 (nginx-mode . "nginx") 971 (magik-mode . "magik") 972 (magik-ts-mode . "magik") 973 (idris-mode . "idris") 974 (idris2-mode . "idris2") 975 (gleam-mode . "gleam") 976 (gleam-ts-mode . "gleam") 977 (graphviz-dot-mode . "dot") 978 (tiltfile-mode . "tiltfile") 979 (solidity-mode . "solidity") 980 (bibtex-mode . "bibtex") 981 (rst-mode . "restructuredtext") 982 (glsl-mode . "glsl") 983 (shader-mode . "shaderlab") 984 (wgsl-mode . "wgsl") 985 (jq-mode . "jq") 986 (jq-ts-mode . "jq") 987 (protobuf-mode . "protobuf") 988 (nushell-mode . "nushell") 989 (nushell-ts-mode . "nushell") 990 (meson-mode . "meson") 991 (yang-mode . "yang")) 992 "Language id configuration.") 993 994 (defvar lsp--last-active-workspaces nil 995 "Keep track of last active workspace. 996 We want to try the last workspace first when jumping into a library 997 directory") 998 999 (defvar lsp-method-requirements 1000 '(("textDocument/callHierarchy" :capability :callHierarchyProvider) 1001 ("textDocument/codeAction" :capability :codeActionProvider) 1002 ("codeAction/resolve" 1003 :check-command (lambda (workspace) 1004 (with-lsp-workspace workspace 1005 (lsp:code-action-options-resolve-provider? 1006 (lsp--capability-for-method "textDocument/codeAction"))))) 1007 ("textDocument/codeLens" :capability :codeLensProvider) 1008 ("textDocument/completion" :capability :completionProvider) 1009 ("completionItem/resolve" 1010 :check-command (lambda (wk) 1011 (with-lsp-workspace wk 1012 (lsp:completion-options-resolve-provider? 1013 (lsp--capability-for-method "textDocument/completion"))))) 1014 ("textDocument/declaration" :capability :declarationProvider) 1015 ("textDocument/definition" :capability :definitionProvider) 1016 ("textDocument/documentColor" :capability :colorProvider) 1017 ("textDocument/documentLink" :capability :documentLinkProvider) 1018 ("textDocument/inlayHint" :capability :inlayHintProvider) 1019 ("textDocument/documentHighlight" :capability :documentHighlightProvider) 1020 ("textDocument/documentSymbol" :capability :documentSymbolProvider) 1021 ("textDocument/foldingRange" :capability :foldingRangeProvider) 1022 ("textDocument/formatting" :capability :documentFormattingProvider) 1023 ("textDocument/hover" :capability :hoverProvider) 1024 ("textDocument/implementation" :capability :implementationProvider) 1025 ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider) 1026 ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider) 1027 ("textDocument/prepareRename" 1028 :check-command (lambda (workspace) 1029 (with-lsp-workspace workspace 1030 (lsp:rename-options-prepare-provider? 1031 (lsp--capability-for-method "textDocument/rename"))))) 1032 ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider) 1033 ("textDocument/references" :capability :referencesProvider) 1034 ("textDocument/rename" :capability :renameProvider) 1035 ("textDocument/selectionRange" :capability :selectionRangeProvider) 1036 ("textDocument/semanticTokens" :capability :semanticTokensProvider) 1037 ("textDocument/semanticTokensFull" 1038 :check-command (lambda (workspace) 1039 (with-lsp-workspace workspace 1040 (lsp-get (lsp--capability :semanticTokensProvider) :full)))) 1041 ("textDocument/semanticTokensFull/Delta" 1042 :check-command (lambda (workspace) 1043 (with-lsp-workspace workspace 1044 (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full))) 1045 (and (not (booleanp capFull)) (lsp-get capFull :delta)))))) 1046 ("textDocument/semanticTokensRangeProvider" 1047 :check-command (lambda (workspace) 1048 (with-lsp-workspace workspace 1049 (lsp-get (lsp--capability :semanticTokensProvider) :range)))) 1050 ("textDocument/signatureHelp" :capability :signatureHelpProvider) 1051 ("textDocument/typeDefinition" :capability :typeDefinitionProvider) 1052 ("textDocument/typeHierarchy" :capability :typeHierarchyProvider) 1053 ("textDocument/diagnostic" :capability :diagnosticProvider) 1054 ("workspace/executeCommand" :capability :executeCommandProvider) 1055 ("workspace/symbol" :capability :workspaceSymbolProvider)) 1056 1057 "Map methods to requirements. 1058 It is used by request-sending functions to determine which server 1059 must be used for handling a particular message.") 1060 1061 (defconst lsp--file-change-type 1062 `((created . 1) 1063 (changed . 2) 1064 (deleted . 3))) 1065 1066 (defconst lsp--watch-kind 1067 `((create . 1) 1068 (change . 2) 1069 (delete . 4))) 1070 1071 (defvar lsp-window-body-width 40 1072 "Window body width when rendering doc.") 1073 1074 (defface lsp-face-highlight-textual 1075 '((t :inherit highlight)) 1076 "Face used for textual occurrences of symbols." 1077 :group 'lsp-mode) 1078 1079 (defface lsp-face-highlight-read 1080 '((t :inherit highlight :underline t)) 1081 "Face used for highlighting symbols being read." 1082 :group 'lsp-mode) 1083 1084 (defface lsp-face-highlight-write 1085 '((t :inherit highlight :weight bold)) 1086 "Face used for highlighting symbols being written to." 1087 :group 'lsp-mode) 1088 1089 (define-obsolete-variable-alias 'lsp-lens-auto-enable 1090 'lsp-lens-enable "lsp-mode 7.0.1") 1091 1092 (defcustom lsp-lens-enable t 1093 "Auto enable lenses if server supports." 1094 :group 'lsp-lens 1095 :type 'boolean 1096 :package-version '(lsp-mode . "6.3")) 1097 1098 (defcustom lsp-symbol-highlighting-skip-current nil 1099 "If non-nil skip current symbol when setting symbol highlights." 1100 :group 'lsp-mode 1101 :type 'boolean) 1102 1103 (defcustom lsp-file-watch-threshold 1000 1104 "Show warning if the files to watch are more than. 1105 Set to nil to disable the warning." 1106 :type 'number 1107 :group 'lsp-mode) 1108 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i)))) 1109 1110 (defvar lsp-custom-markup-modes 1111 '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic")) 1112 "Mode to uses with markdown code blocks. 1113 They are added to `markdown-code-lang-modes'") 1114 1115 (defcustom lsp-signature-render-documentation t 1116 "Display signature documentation in `eldoc'." 1117 :type 'boolean 1118 :group 'lsp-mode 1119 :package-version '(lsp-mode . "6.2")) 1120 1121 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request) 1122 "Auto activate signature conditions." 1123 :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char) 1124 (const :tag "After selected completion." :after-completion) 1125 (const :tag "When the server has sent show signature help." :on-server-request))) 1126 :group 'lsp-mode 1127 :package-version '(lsp-mode . "6.2")) 1128 1129 (defcustom lsp-signature-doc-lines 20 1130 "If number, limit the number of lines to show in the docs." 1131 :type 'number 1132 :group 'lsp-mode 1133 :package-version '(lsp-mode . "6.3")) 1134 1135 (defcustom lsp-signature-function 'lsp-lv-message 1136 "The function used for displaying signature info. 1137 It will be called with one param - the signature info. When 1138 called with nil the signature info must be cleared." 1139 :type 'function 1140 :group 'lsp-mode 1141 :package-version '(lsp-mode . "6.3")) 1142 1143 (defcustom lsp-keymap-prefix "s-l" 1144 "LSP-mode keymap prefix." 1145 :group 'lsp-mode 1146 :type 'string 1147 :package-version '(lsp-mode . "6.3")) 1148 1149 (defvar-local lsp--buffer-workspaces () 1150 "List of the buffer workspaces.") 1151 1152 (defvar-local lsp--buffer-deferred nil 1153 "Whether buffer was loaded via `lsp-deferred'.") 1154 1155 (defvar lsp--session nil 1156 "Contain the `lsp-session' for the current Emacs instance.") 1157 1158 (defvar lsp--tcp-port 10000) 1159 1160 (defvar lsp--client-packages-required nil 1161 "If nil, `lsp-client-packages' are yet to be required.") 1162 1163 (defvar lsp--tcp-server-port 0 1164 "The server socket which is opened when using `lsp-tcp-server' (a server 1165 socket is opened in Emacs and the language server connects to it). The 1166 default value of 0 ensures that a random high port is used. Set it to a positive 1167 integer to use a specific port.") 1168 1169 (defvar lsp--tcp-server-wait-seconds 10 1170 "Wait this amount of time for the client to connect to our server socket 1171 when using `lsp-tcp-server'.") 1172 1173 (defvar-local lsp--document-symbols nil 1174 "The latest document symbols.") 1175 1176 (defvar-local lsp--document-selection-range-cache nil 1177 "The document selection cache.") 1178 1179 (defvar-local lsp--document-symbols-request-async nil 1180 "If non-nil, request document symbols asynchronously.") 1181 1182 (defvar-local lsp--document-symbols-tick -1 1183 "The value of `buffer-chars-modified-tick' when document 1184 symbols were last retrieved.") 1185 1186 (defvar-local lsp--have-document-highlights nil 1187 "Set to `t' on symbol highlighting, cleared on 1188 `lsp--cleanup-highlights-if-needed'. Checking a separately 1189 defined flag is substantially faster than unconditionally 1190 calling `remove-overlays'.") 1191 1192 ;; Buffer local variable for storing number of lines. 1193 (defvar lsp--log-lines) 1194 1195 (defvar-local lsp--eldoc-saved-message nil) 1196 1197 (defvar lsp--on-change-timer nil) 1198 (defvar lsp--on-idle-timer nil) 1199 1200 (defvar-local lsp--signature-last nil) 1201 (defvar-local lsp--signature-last-index nil) 1202 (defvar lsp--signature-last-buffer nil) 1203 1204 (defvar-local lsp--virtual-buffer-point-max nil) 1205 1206 (cl-defmethod lsp-execute-command (_server _command _arguments) 1207 "Ask SERVER to execute COMMAND with ARGUMENTS.") 1208 1209 (defun lsp-elt (sequence n) 1210 "Return Nth element of SEQUENCE or nil if N is out of range." 1211 (cond 1212 ((listp sequence) (elt sequence n)) 1213 ((arrayp sequence) 1214 (and (> (length sequence) n) (aref sequence n))) 1215 (t (and (> (length sequence) n) (elt sequence n))))) 1216 1217 ;; define seq-first and seq-rest for older emacs 1218 (defun lsp-seq-first (sequence) 1219 "Return the first element of SEQUENCE." 1220 (lsp-elt sequence 0)) 1221 1222 (defun lsp-seq-rest (sequence) 1223 "Return a sequence of the elements of SEQUENCE except the first one." 1224 (seq-drop sequence 1)) 1225 1226 ;;;###autoload 1227 (defun lsp--string-listp (sequence) 1228 "Return t if all elements of SEQUENCE are strings, else nil." 1229 (not (seq-find (lambda (x) (not (stringp x))) sequence))) 1230 1231 (defun lsp--string-vector-p (candidate) 1232 "Returns true if CANDIDATE is a vector data structure and 1233 every element of it is of type string, else nil." 1234 (and 1235 (vectorp candidate) 1236 (seq-every-p #'stringp candidate))) 1237 1238 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0") 1239 1240 (defun lsp--editable-vector-match (widget value) 1241 "Function for `lsp-editable-vector' :match." 1242 ;; Value must be a list or a vector and all the members must match the type. 1243 (and (or (listp value) (vectorp value)) 1244 (length (cdr (lsp--editable-vector-match-inline widget value))))) 1245 1246 (defun lsp--editable-vector-match-inline (widget value) 1247 "Value for `lsp-editable-vector' :match-inline." 1248 (let ((type (nth 0 (widget-get widget :args))) 1249 (ok t) 1250 found) 1251 (while (and value ok) 1252 (let ((answer (widget-match-inline type value))) 1253 (if answer 1254 (let ((head (if (vectorp answer) (aref answer 0) (car answer))) 1255 (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer)))) 1256 (setq found (append found head) 1257 value tail)) 1258 (setq ok nil)))) 1259 (cons found value))) 1260 1261 (defun lsp--editable-vector-value-to-external (_widget internal-value) 1262 "Convert the internal list value to a vector." 1263 (if (listp internal-value) 1264 (apply 'vector internal-value) 1265 internal-value)) 1266 1267 (defun lsp--editable-vector-value-to-internal (_widget external-value) 1268 "Convert the external vector value to a list." 1269 (if (vectorp external-value) 1270 (append external-value nil) 1271 external-value)) 1272 1273 (define-widget 'lsp--editable-vector 'editable-list 1274 "A subclass of `editable-list' that accepts and returns a 1275 vector instead of a list." 1276 :value-to-external 'lsp--editable-vector-value-to-external 1277 :value-to-internal 'lsp--editable-vector-value-to-internal 1278 :match 'lsp--editable-vector-match 1279 :match-inline 'lsp--editable-vector-match-inline) 1280 1281 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector 1282 "A variable length homogeneous vector." 1283 :tag "Repeat" 1284 :format "%{%t%}:\n%v%i\n") 1285 1286 (define-widget 'lsp-string-vector 'lazy 1287 "A vector of zero or more elements, every element of which is a string. 1288 Appropriate for any language-specific `defcustom' that needs to 1289 serialize as a JSON array of strings. 1290 1291 Deprecated. Use `lsp-repeatable-vector' instead. " 1292 :offset 4 1293 :tag "Vector" 1294 :type '(lsp-repeatable-vector string)) 1295 1296 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0") 1297 1298 (defvar lsp--show-message t 1299 "If non-nil, show debug message from `lsp-mode'.") 1300 1301 (defun lsp--message (format &rest args) 1302 "Wrapper for `message' 1303 1304 We `inhibit-message' the message when the cursor is in the 1305 minibuffer and when emacs version is before emacs 27 due to the 1306 fact that we often use `lsp--info', `lsp--warn' and `lsp--error' 1307 in async context and the call to these function is removing the 1308 minibuffer prompt. The issue with async messages is already fixed 1309 in emacs 27. 1310 1311 See #2049" 1312 (when lsp--show-message 1313 (let ((inhibit-message (or inhibit-message 1314 (and (minibufferp) 1315 (version< emacs-version "27.0"))))) 1316 (apply #'message format args)))) 1317 1318 (defun lsp--info (format &rest args) 1319 "Display lsp info message with FORMAT with ARGS." 1320 (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args))) 1321 1322 (defun lsp--warn (format &rest args) 1323 "Display lsp warn message with FORMAT with ARGS." 1324 (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args))) 1325 1326 (defun lsp--error (format &rest args) 1327 "Display lsp error message with FORMAT with ARGS." 1328 (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args))) 1329 1330 (defun lsp-log (format &rest args) 1331 "Log message to the ’*lsp-log*’ buffer. 1332 1333 FORMAT and ARGS i the same as for `message'." 1334 (when lsp-log-max 1335 (let ((log-buffer (get-buffer "*lsp-log*")) 1336 (inhibit-read-only t)) 1337 (unless log-buffer 1338 (setq log-buffer (get-buffer-create "*lsp-log*")) 1339 (with-current-buffer log-buffer 1340 (buffer-disable-undo) 1341 (view-mode 1) 1342 (set (make-local-variable 'lsp--log-lines) 0))) 1343 (with-current-buffer log-buffer 1344 (save-excursion 1345 (let* ((message (apply 'format format args)) 1346 ;; Count newlines in message. 1347 (newlines (1+ (cl-loop with start = 0 1348 for count from 0 1349 while (string-match "\n" message start) 1350 do (setq start (match-end 0)) 1351 finally return count)))) 1352 (goto-char (point-max)) 1353 1354 ;; in case the buffer is not empty insert before last \n to preserve 1355 ;; the point position(in case it is in the end) 1356 (if (eq (point) (point-min)) 1357 (progn 1358 (insert "\n") 1359 (backward-char)) 1360 (backward-char) 1361 (insert "\n")) 1362 (insert message) 1363 1364 (setq lsp--log-lines (+ lsp--log-lines newlines)) 1365 1366 (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max)) 1367 (let ((to-delete (- lsp--log-lines lsp-log-max))) 1368 (goto-char (point-min)) 1369 (forward-line to-delete) 1370 (delete-region (point-min) (point)) 1371 (setq lsp--log-lines lsp-log-max))))))))) 1372 1373 (defalias 'lsp-message 'lsp-log) 1374 1375 (defalias 'lsp-ht 'ht) 1376 1377 (defalias 'lsp-file-local-name 'file-local-name) 1378 1379 (defun lsp-f-canonical (file-name) 1380 "Return the canonical FILE-NAME, without a trailing slash." 1381 (directory-file-name (expand-file-name file-name))) 1382 1383 (defalias 'lsp-canonical-file-name 'lsp-f-canonical) 1384 1385 (defun lsp-f-same? (path-a path-b) 1386 "Return t if PATH-A and PATH-B are references to the same file. 1387 Symlinks are not followed." 1388 (when (and (f-exists? path-a) 1389 (f-exists? path-b)) 1390 (equal 1391 (lsp-f-canonical (directory-file-name (f-expand path-a))) 1392 (lsp-f-canonical (directory-file-name (f-expand path-b)))))) 1393 1394 (defun lsp-f-parent (path) 1395 "Return the parent directory to PATH. 1396 Symlinks are not followed." 1397 (let ((parent (file-name-directory 1398 (directory-file-name (f-expand path default-directory))))) 1399 (unless (lsp-f-same? path parent) 1400 (if (f-relative? path) 1401 (f-relative parent) 1402 (directory-file-name parent))))) 1403 1404 (defun lsp-f-ancestor-of? (path-a path-b) 1405 "Return t if PATH-A is an ancestor of PATH-B. 1406 Symlinks are not followed." 1407 (unless (lsp-f-same? path-a path-b) 1408 (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator)) 1409 (lsp-f-canonical path-b)))) 1410 1411 (defun lsp--merge-results (results method) 1412 "Merge RESULTS by filtering the empty hash-tables and merging 1413 the lists according to METHOD." 1414 (pcase (--map (if (vectorp it) 1415 (append it nil) it) 1416 (-filter #'identity results)) 1417 (`() ()) 1418 ;; only one result - simply return it 1419 (`(,fst) fst) 1420 ;; multiple results merge it based on strategy 1421 (results 1422 (pcase method 1423 ("textDocument/hover" (pcase (seq-filter 1424 (-compose #'not #'lsp-empty?) 1425 results) 1426 (`(,hover) hover) 1427 (hovers (lsp-make-hover 1428 :contents 1429 (-mapcat 1430 (-lambda ((&Hover :contents)) 1431 (if (and (sequencep contents) 1432 (not (stringp contents))) 1433 (append contents ()) 1434 (list contents))) 1435 hovers))))) 1436 ("textDocument/completion" 1437 (lsp-make-completion-list 1438 :is-incomplete (seq-some 1439 #'lsp:completion-list-is-incomplete 1440 results) 1441 :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it) 1442 (lsp:completion-list-items it) 1443 it) 1444 nil)) 1445 results))) 1446 ("completionItem/resolve" 1447 (let ((item (cl-first results))) 1448 (when-let* ((details (seq-filter #'identity 1449 (seq-map #'lsp:completion-item-detail? results)))) 1450 (lsp:set-completion-item-detail? 1451 item 1452 (string-join details " "))) 1453 (when-let* ((docs (seq-filter #'identity 1454 (seq-map #'lsp:completion-item-documentation? results)))) 1455 (lsp:set-completion-item-documentation? 1456 item 1457 (lsp-make-markup-content 1458 :kind (or (seq-some (lambda (it) 1459 (when (equal (lsp:markup-content-kind it) 1460 lsp/markup-kind-markdown) 1461 lsp/markup-kind-markdown)) 1462 docs) 1463 lsp/markup-kind-plain-text) 1464 :value (string-join (seq-map (lambda (doc) 1465 (or (lsp:markup-content-value doc) 1466 (and (stringp doc) doc))) 1467 docs) 1468 "\n")))) 1469 (when-let* ((edits (seq-filter #'identity 1470 (seq-map #'lsp:completion-item-additional-text-edits? results)))) 1471 (lsp:set-completion-item-additional-text-edits? 1472 item 1473 (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits))) 1474 item)) 1475 (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results)))))) 1476 1477 (defun lsp--spinner-start () 1478 "Start spinner indication." 1479 (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error))) 1480 1481 (defun lsp--propertize (str type) 1482 "Propertize STR as per TYPE." 1483 (propertize str 'face (alist-get type lsp--message-type-face))) 1484 1485 (defun lsp-workspaces () 1486 "Return the lsp workspaces associated with the current project." 1487 (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces)) 1488 1489 (defun lsp--completing-read (prompt collection transform-fn &optional predicate 1490 require-match initial-input 1491 hist def inherit-input-method) 1492 "Wrap `completing-read' to provide transformation function and disable sort. 1493 1494 TRANSFORM-FN will be used to transform each of the items before displaying. 1495 1496 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF 1497 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes." 1498 (let* ((col (--map (cons (funcall transform-fn it) it) collection)) 1499 (completion (completing-read prompt 1500 (lambda (string pred action) 1501 (if (eq action 'metadata) 1502 `(metadata (display-sort-function . identity)) 1503 (complete-with-action action col string pred))) 1504 predicate require-match initial-input hist 1505 def inherit-input-method))) 1506 (cdr (assoc completion col)))) 1507 1508 (defconst lsp--system-arch (lambda () 1509 (setq lsp--system-arch 1510 (pcase system-type 1511 ('windows-nt 1512 (pcase system-configuration 1513 ((rx bol "x86_64-") 'x64) 1514 (_ 'x86))) 1515 ('darwin 1516 (pcase system-configuration 1517 ((rx "aarch64-") 'arm64) 1518 (_ 'x64))) 1519 ('gnu/linux 1520 (pcase system-configuration 1521 ((rx bol "aarch64-") 'arm64) 1522 ((rx bol "x86_64") 'x64) 1523 ((rx bol (| "i386" "i886")) 'x32))) 1524 (_ 1525 (pcase system-configuration 1526 ((rx bol "x86_64") 'x64) 1527 ((rx bol (| "i386" "i886")) 'x32)))))) 1528 "Return the system architecture of `Emacs'. 1529 Special values: 1530 `x64' 64bit 1531 `x32' 32bit 1532 `arm64' ARM 64bit") 1533 1534 (defmacro lsp-with-current-buffer (buffer-id &rest body) 1535 (declare (indent 1) (debug t)) 1536 `(if-let* ((wcb (plist-get ,buffer-id :with-current-buffer))) 1537 (with-lsp-workspaces (plist-get ,buffer-id :workspaces) 1538 (funcall wcb (lambda () ,@body))) 1539 (with-current-buffer ,buffer-id 1540 ,@body))) 1541 1542 (defvar lsp--throw-on-input nil 1543 "Make `lsp-*-while-no-input' throws `input' on interrupted.") 1544 1545 (defmacro lsp--catch (tag bodyform &rest handlers) 1546 "Catch TAG thrown in BODYFORM. 1547 The return value from TAG will be handled in HANDLERS by `pcase'." 1548 (declare (debug (form form &rest (pcase-PAT body))) (indent 2)) 1549 (let ((re-sym (make-symbol "re"))) 1550 `(let ((,re-sym (catch ,tag ,bodyform))) 1551 (pcase ,re-sym 1552 ,@handlers)))) 1553 1554 (defmacro lsp--while-no-input (&rest body) 1555 "Wrap BODY in `while-no-input' and respecting `non-essential'. 1556 If `lsp--throw-on-input' is set, will throw if input is pending, else 1557 return value of `body' or nil if interrupted." 1558 (declare (debug t) (indent 0)) 1559 `(if non-essential 1560 (let ((res (while-no-input ,@body))) 1561 (cond 1562 ((and lsp--throw-on-input (equal res t)) 1563 (throw 'input :interrupted)) 1564 ((booleanp res) nil) 1565 (t res))) 1566 ,@body)) 1567 1568 ;; A ‘lsp--client’ object describes the client-side behavior of a language 1569 ;; server. It is used to start individual server processes, each of which is 1570 ;; represented by a ‘lsp--workspace’ object. Client objects are normally 1571 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’. Each 1572 ;; workspace refers to exactly one client, but there can be multiple workspaces 1573 ;; for a single client. 1574 (cl-defstruct lsp--client 1575 ;; ‘language-id’ is a function that receives a buffer as a single argument 1576 ;; and should return the language identifier for that buffer. See 1577 ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem 1578 ;; for a list of language identifiers. Also consult the documentation for 1579 ;; the language server represented by this client to find out what language 1580 ;; identifiers it supports or expects. 1581 (language-id nil) 1582 1583 ;; ‘add-on?’ when set to t the server will be started no matter whether there 1584 ;; is another server handling the same mode. 1585 (add-on? nil) 1586 ;; ‘new-connection’ is a function that should start a language server process 1587 ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS). 1588 ;; COMMAND-PROCESS must be a process object representing the server process 1589 ;; just started. COMMUNICATION-PROCESS must be a process (including pipe and 1590 ;; network processes) that ‘lsp-mode’ uses to communicate with the language 1591 ;; server using the language server protocol. COMMAND-PROCESS and 1592 ;; COMMUNICATION-PROCESS may be the same process; in that case 1593 ;; ‘new-connection’ may also return that process as a single 1594 ;; object. ‘new-connection’ is called with two arguments, FILTER and 1595 ;; SENTINEL. FILTER should be used as process filter for 1596 ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for 1597 ;; COMMAND-PROCESS. 1598 (new-connection nil) 1599 1600 ;; ‘ignore-regexps’ is a list of regexps. When a data packet from the 1601 ;; language server matches any of these regexps, it will be ignored. This is 1602 ;; intended for dealing with language servers that output non-protocol data. 1603 (ignore-regexps nil) 1604 1605 ;; ‘ignore-messages’ is a list of regexps. When a message from the language 1606 ;; server matches any of these regexps, it will be ignored. This is useful 1607 ;; for filtering out unwanted messages; such as servers that send nonstandard 1608 ;; message types, or extraneous log messages. 1609 (ignore-messages nil) 1610 1611 ;; ‘notification-handlers’ is a hash table mapping notification method names 1612 ;; (strings) to functions handling the respective notifications. Upon 1613 ;; receiving a notification, ‘lsp-mode’ will call the associated handler 1614 ;; function passing two arguments, the ‘lsp--workspace’ object and the 1615 ;; deserialized notification parameters. 1616 (notification-handlers (make-hash-table :test 'equal)) 1617 1618 ;; ‘request-handlers’ is a hash table mapping request method names 1619 ;; (strings) to functions handling the respective notifications. Upon 1620 ;; receiving a request, ‘lsp-mode’ will call the associated handler function 1621 ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized 1622 ;; request parameters. 1623 (request-handlers (make-hash-table :test 'equal)) 1624 1625 ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request 1626 ;; identifiers for pending asynchronous requests to functions handling the 1627 ;; respective responses. Upon receiving a response from the language server, 1628 ;; ‘lsp-mode’ will call the associated response handler function with a 1629 ;; single argument, the deserialized response parameters. 1630 (response-handlers (make-hash-table :test 'eql)) 1631 1632 ;; ‘prefix-function’ is called for getting the prefix for completion. 1633 ;; The function takes no parameter and returns a cons (start . end) representing 1634 ;; the start and end bounds of the prefix. If it's not set, the client uses a 1635 ;; default prefix function." 1636 (prefix-function nil) 1637 1638 ;; Contains mapping of scheme to the function that is going to be used to load 1639 ;; the file. 1640 (uri-handlers (make-hash-table :test #'equal)) 1641 1642 ;; ‘action-handlers’ is a hash table mapping action to a handler function. It 1643 ;; can be used in `lsp-execute-code-action' to determine whether the action 1644 ;; current client is interested in executing the action instead of sending it 1645 ;; to the server. 1646 (action-handlers (make-hash-table :test 'equal)) 1647 1648 ;; `action-filter' can be set to a function that modifies any incoming 1649 ;; `CodeAction' in place before it is executed. The return value is ignored. 1650 ;; This can be used to patch up broken code action requests before they are 1651 ;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an 1652 ;; example of a function that can be useful here. 1653 (action-filter nil) 1654 1655 ;; major modes supported by the client. 1656 major-modes 1657 ;; Function that will be called to decide if this language client 1658 ;; should manage a particular buffer. The function will be passed 1659 ;; the file name and major mode to inform the decision. Setting 1660 ;; `activation-fn' will override `major-modes', if 1661 ;; present. 1662 activation-fn 1663 ;; Break the tie when major-mode is supported by multiple clients. 1664 (priority 0) 1665 ;; Unique identifier for representing the client object. 1666 server-id 1667 ;; defines whether the client supports multi root workspaces. 1668 multi-root 1669 ;; Initialization options or a function that returns initialization options. 1670 initialization-options 1671 ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or 1672 ;; completely replace, the faces used for semantic highlighting on a 1673 ;; client-by-client basis. 1674 ;; 1675 ;; It recognizes four members, all of which are optional: `:types’ and 1676 ;; `:modifiers’, respectively, should be face definition lists akin to 1677 ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be 1678 ;; merged with the default face definition list. 1679 ;; 1680 ;; Alternatively, if the plist members `:discard-default-types’ or 1681 ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers' 1682 ;; face definitions will be replaced entirely by their respective overrides. 1683 ;; 1684 ;; For example, setting `:semantic-tokens-faces-overrides' to 1685 ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from 1686 ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'. 1687 ;; 1688 ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))' 1689 ;; will also remap "macro", but on top of that associate the fictional token type 1690 ;; "not-quite-a-macro" with the face named `some-face'. 1691 ;; 1692 ;; `(:types (("macro" . font-lock-keyword-face)) 1693 ;; :modifiers (("declaration" . lsp-face-semhl-interface)) 1694 ;; :discard-default-types t 1695 ;; :discard-default-modifiers t)' 1696 ;; will discard all default face definitions, hence leaving the client with 1697 ;; only one token type "macro", mapped to `font-lock-keyword-face', and one 1698 ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'. 1699 semantic-tokens-faces-overrides 1700 ;; Provides support for registering LSP Server specific capabilities. 1701 custom-capabilities 1702 ;; Function which returns the folders that are considered to be not projects but library files. 1703 ;; The function accepts one parameter currently active workspace. 1704 ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225. 1705 library-folders-fn 1706 ;; function which will be called when opening file in the workspace to perform 1707 ;; client specific initialization. The function accepts one parameter 1708 ;; currently active workspace. 1709 before-file-open-fn 1710 ;; Function which will be called right after a workspace has been initialized. 1711 initialized-fn 1712 ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP. 1713 (remote? nil) 1714 1715 ;; ‘completion-in-comments?’ t if the client supports completion in comments. 1716 (completion-in-comments? nil) 1717 1718 ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client. 1719 (path->uri-fn nil) 1720 1721 ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client. 1722 (uri->path-fn nil) 1723 ;; Function that returns an environment structure that will be used 1724 ;; to set some environment variables when starting the language 1725 ;; server process. These environment variables enable some 1726 ;; additional features in the language server. The environment 1727 ;; structure is an alist of the form (KEY . VALUE), where KEY is a 1728 ;; string (regularly in all caps), and VALUE may be a string, a 1729 ;; boolean, or a sequence of strings. 1730 environment-fn 1731 1732 ;; ‘after-open-fn’ workspace after open specific hooks. 1733 (after-open-fn nil) 1734 1735 ;; ‘async-request-handlers’ is a hash table mapping request method names 1736 ;; (strings) to functions handling the respective requests that may take 1737 ;; time to finish. Upon receiving a request, ‘lsp-mode’ will call the 1738 ;; associated handler function passing three arguments, the ‘lsp--workspace’ 1739 ;; object, the deserialized request parameters and the callback which accept 1740 ;; result as its parameter. 1741 (async-request-handlers (make-hash-table :test 'equal)) 1742 download-server-fn 1743 download-in-progress? 1744 buffers 1745 synchronize-sections) 1746 1747 (defun lsp-clients-executable-find (find-command &rest args) 1748 "Finds an executable by invoking a search command. 1749 1750 FIND-COMMAND is the executable finder that searches for the 1751 actual language server executable. ARGS is a list of arguments to 1752 give to FIND-COMMAND to find the language server. Returns the 1753 output of FIND-COMMAND if it exits successfully, nil otherwise. 1754 1755 Typical uses include finding an executable by invoking `find' in 1756 a project, finding LLVM commands on macOS with `xcrun', or 1757 looking up project-specific language servers for projects written 1758 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv' 1759 etc." 1760 (when-let* ((find-command-path (executable-find find-command)) 1761 (executable-path 1762 (with-temp-buffer 1763 (when (zerop (apply 'call-process find-command-path nil t nil args)) 1764 (buffer-substring-no-properties (point-min) (point-max)))))) 1765 (string-trim executable-path))) 1766 1767 (defvar lsp--already-widened nil) 1768 1769 (defmacro lsp-save-restriction-and-excursion (&rest form) 1770 (declare (indent 0) (debug t)) 1771 `(if lsp--already-widened 1772 (save-excursion ,@form) 1773 (-let [lsp--already-widened t] 1774 (save-restriction 1775 (widen) 1776 (save-excursion ,@form))))) 1777 1778 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number 1779 (defun lsp--line-character-to-point (line character) 1780 "Return the point for character CHARACTER on line LINE." 1781 (or (lsp-virtual-buffer-call :line/character->point line character) 1782 (let ((inhibit-field-text-motion t)) 1783 (lsp-save-restriction-and-excursion 1784 (goto-char (point-min)) 1785 (forward-line line) 1786 ;; server may send character position beyond the current line and we 1787 ;; should fallback to line end. 1788 (-let [line-end (line-end-position)] 1789 (if (> character (- line-end (point))) 1790 line-end 1791 (forward-char character) 1792 (point))))))) 1793 1794 (lsp-defun lsp--position-to-point ((&Position :line :character)) 1795 "Convert `Position' object in PARAMS to a point." 1796 (lsp--line-character-to-point line character)) 1797 1798 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end)) 1799 (cons start end)) 1800 1801 (lsp-defun lsp--range-text ((&RangeToPoint :start :end)) 1802 (buffer-substring start end)) 1803 1804 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end))) 1805 (cond 1806 ((and 1807 (region-active-p) 1808 (<= start (region-beginning) end) 1809 (<= start (region-end) end) 1810 (or (not (= start (region-beginning))) 1811 (not (= end (region-end))))) 1812 (cons start end)) 1813 ((and (<= start (point) end) 1814 (not (region-active-p))) 1815 (cons start end)) 1816 (parent? (lsp--find-wrapping-range parent?)))) 1817 1818 (defun lsp--get-selection-range () 1819 (or 1820 (-when-let ((cache . cache-tick) lsp--document-selection-range-cache) 1821 (when (= cache-tick (buffer-modified-tick)) cache)) 1822 (let ((response (cl-first 1823 (lsp-request 1824 "textDocument/selectionRange" 1825 (list :textDocument (lsp--text-document-identifier) 1826 :positions (vector (lsp--cur-position))))))) 1827 (setq lsp--document-selection-range-cache 1828 (cons response (buffer-modified-tick))) 1829 response))) 1830 1831 (defun lsp-extend-selection () 1832 "Extend selection." 1833 (interactive) 1834 (unless (lsp-feature? "textDocument/selectionRange") 1835 (signal 'lsp-capability-not-supported (list "selectionRangeProvider"))) 1836 (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range))) 1837 (goto-char start) 1838 (set-mark (point)) 1839 (goto-char end) 1840 (exchange-point-and-mark))) 1841 1842 (defun lsp-warn (message &rest args) 1843 "Display a warning message made from (`format-message' MESSAGE ARGS...). 1844 This is equivalent to `display-warning', using `lsp-mode' as the type and 1845 `:warning' as the level." 1846 (display-warning 'lsp-mode (apply #'format-message message args))) 1847 1848 (defun lsp--get-uri-handler (scheme) 1849 "Get uri handler for SCHEME in the current workspace." 1850 (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it))) 1851 (or (lsp-workspaces) (lsp--session-workspaces (lsp-session))))) 1852 1853 (defun lsp--fix-path-casing (path) 1854 "On windows, downcases path because the windows file system is 1855 case-insensitive. 1856 1857 On other systems, returns path without change." 1858 (if (eq system-type 'windows-nt) (downcase path) path)) 1859 1860 (defun lsp--uri-to-path (uri) 1861 "Convert URI to a file path." 1862 (if-let* ((fn (->> (lsp-workspaces) 1863 (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client)) 1864 (cl-first)))) 1865 (funcall fn uri) 1866 (lsp--uri-to-path-1 uri))) 1867 1868 (defun lsp-remap-path-if-needed (file-name) 1869 (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings)) 1870 (propertize (buffer-local-value 'buffer-file-name buffer) 1871 'lsp-virtual-buffer virtual-buffer) 1872 file-name)) 1873 1874 (defun lsp--uri-to-path-1 (uri) 1875 "Convert URI to a file path." 1876 (let* ((url (url-generic-parse-url (url-unhex-string uri))) 1877 (type (url-type url)) 1878 (target (url-target url)) 1879 (file 1880 (concat (decode-coding-string (url-filename url) 1881 (or locale-coding-system 'utf-8)) 1882 (when (and target 1883 (not (s-match 1884 (rx "#" (group (1+ num)) (or "," "#") 1885 (group (1+ num)) 1886 string-end) 1887 uri))) 1888 (concat "#" target)))) 1889 (file-name (if (and type (not (string= type "file"))) 1890 (if-let* ((handler (lsp--get-uri-handler type))) 1891 (funcall handler uri) 1892 uri) 1893 ;; `url-generic-parse-url' is buggy on windows: 1894 ;; https://github.com/emacs-lsp/lsp-mode/pull/265 1895 (or (and (eq system-type 'windows-nt) 1896 (eq (elt file 0) ?\/) 1897 (substring file 1)) 1898 file)))) 1899 (->> file-name 1900 (concat (-some #'lsp--workspace-host-root (lsp-workspaces))) 1901 (lsp-remap-path-if-needed)))) 1902 1903 (defun lsp--buffer-uri () 1904 "Return URI of the current buffer." 1905 (or lsp-buffer-uri 1906 (plist-get lsp--virtual-buffer :buffer-uri) 1907 (lsp--path-to-uri 1908 (or (buffer-file-name) (buffer-file-name (buffer-base-buffer)))))) 1909 1910 (defun lsp-register-client-capabilities (&rest _args) 1911 "Implemented only to make `company-lsp' happy. 1912 DELETE when `lsp-mode.el' is deleted.") 1913 1914 (defconst lsp--url-path-allowed-chars 1915 (url--allowed-chars (append '(?/) url-unreserved-chars)) 1916 "`url-unreserved-chars' with additional delim ?/. 1917 This set of allowed chars is enough for hexifying local file paths.") 1918 1919 (defun lsp--path-to-uri-1 (path) 1920 (concat lsp--uri-file-prefix 1921 (--> path 1922 (expand-file-name it) 1923 (or (file-remote-p it 'localname t) it) 1924 (url-hexify-string it lsp--url-path-allowed-chars)))) 1925 1926 (defun lsp--path-to-uri (path) 1927 "Convert PATH to a uri." 1928 (if-let* ((uri-fn (->> (lsp-workspaces) 1929 (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client)) 1930 (cl-first)))) 1931 (funcall uri-fn path) 1932 (lsp--path-to-uri-1 path))) 1933 1934 (defun lsp--string-match-any (regex-list str) 1935 "Return the first regex, if any, within REGEX-LIST matching STR." 1936 (--first (string-match it str) regex-list)) 1937 1938 (cl-defstruct lsp-watch 1939 (descriptors (make-hash-table :test 'equal)) 1940 root-directory) 1941 1942 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories) 1943 (let ((file-name (cl-third event)) 1944 (event-type (cl-second event))) 1945 (cond 1946 ((and (file-directory-p file-name) 1947 (equal 'created event-type) 1948 (not (lsp--string-match-any ignored-directories file-name))) 1949 1950 (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch) 1951 1952 ;; process the files that are already present in 1953 ;; the directory. 1954 (->> (directory-files-recursively file-name ".*" t) 1955 (seq-do (lambda (f) 1956 (unless (file-directory-p f) 1957 (funcall callback (list nil 'created f))))))) 1958 ((and (memq event-type '(created deleted changed)) 1959 (not (file-directory-p file-name)) 1960 (not (lsp--string-match-any ignored-files file-name))) 1961 (funcall callback event)) 1962 ((and (memq event-type '(renamed)) 1963 (not (file-directory-p file-name)) 1964 (not (lsp--string-match-any ignored-files file-name))) 1965 (funcall callback `(,(cl-first event) deleted ,(cl-third event))) 1966 (funcall callback `(,(cl-first event) created ,(cl-fourth event))))))) 1967 1968 (defun lsp--ask-about-watching-big-repo (number-of-directories dir) 1969 "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR. 1970 This is useful when there is a lot of files in a repository, as 1971 that may slow Emacs down. Returns t if the user wants to watch 1972 the entire repository, nil otherwise." 1973 (prog1 1974 (yes-or-no-p 1975 (format 1976 "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down. 1977 Do you want to watch all files in %s? " 1978 dir 1979 number-of-directories 1980 dir)) 1981 (lsp--info 1982 (concat "You can configure this warning with the `lsp-enable-file-watchers' " 1983 "and `lsp-file-watch-threshold' variables")))) 1984 1985 1986 (defun lsp--path-is-watchable-directory (path dir ignored-directories) 1987 "Figure out whether PATH (inside of DIR) is meant to have a file watcher set. 1988 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't 1989 want to watch." 1990 (let 1991 ((full-path (f-join dir path))) 1992 (and (file-accessible-directory-p full-path) 1993 (not (equal path ".")) 1994 (not (equal path "..")) 1995 (not (lsp--string-match-any ignored-directories full-path))))) 1996 1997 1998 (defun lsp--all-watchable-directories (dir ignored-directories &optional visited) 1999 "Traverse DIR recursively returning a list of paths that should have watchers. 2000 IGNORED-DIRECTORIES will be used for exclusions. 2001 VISITED is used to track already-visited directories to avoid infinite loops." 2002 (let* ((dir (if (f-symlink? dir) 2003 (file-truename dir) 2004 dir)) 2005 ;; Initialize visited directories if not provided 2006 (visited (or visited (make-hash-table :test 'equal)))) 2007 (if (gethash dir visited) 2008 ;; If the directory has already been visited, skip it 2009 nil 2010 ;; Mark the current directory as visited 2011 (puthash dir t visited) 2012 (apply #'nconc 2013 ;; the directory itself is assumed to be part of the set 2014 (list dir) 2015 ;; collect all subdirectories that are watchable 2016 (-map 2017 (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories visited)) 2018 ;; but only look at subdirectories that are watchable 2019 (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories)) 2020 (directory-files dir))))))) 2021 2022 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?) 2023 "Create recursive file notification watch in DIR. 2024 CALLBACK will be called when there are changes in any of 2025 the monitored files. WATCHES is a hash table directory->file 2026 notification handle which contains all of the watch that 2027 already have been created. Watches will not be created for 2028 any directory that matches any regex in IGNORED-DIRECTORIES. 2029 Watches will not be created for any file that matches any 2030 regex in IGNORED-FILES." 2031 (let* ((dir (if (f-symlink? dir) 2032 (file-truename dir) 2033 dir)) 2034 (watch (or watch (make-lsp-watch :root-directory dir))) 2035 (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories))) 2036 (lsp-log "Creating watchers for following %s folders:\n %s" 2037 (length dirs-to-watch) 2038 (s-join "\n " dirs-to-watch)) 2039 (when (or 2040 (not warn-big-repo?) 2041 (not lsp-file-watch-threshold) 2042 (let ((number-of-directories (length dirs-to-watch))) 2043 (or 2044 (< number-of-directories lsp-file-watch-threshold) 2045 (condition-case nil 2046 (lsp--ask-about-watching-big-repo number-of-directories dir) 2047 (quit))))) 2048 (dolist (current-dir dirs-to-watch) 2049 (condition-case err 2050 (progn 2051 (puthash 2052 current-dir 2053 (file-notify-add-watch current-dir 2054 '(change) 2055 (lambda (event) 2056 (lsp--folder-watch-callback event callback watch ignored-files ignored-directories))) 2057 (lsp-watch-descriptors watch))) 2058 (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err))) 2059 (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))))) 2060 watch)) 2061 2062 (defun lsp-kill-watch (watch) 2063 "Delete WATCH." 2064 (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch)) 2065 (ht-clear! (lsp-watch-descriptors watch))) 2066 2067 (defun lsp-json-bool (val) 2068 "Convert VAL to JSON boolean." 2069 (if val t :json-false)) 2070 2071 (defmacro with-lsp-workspace (workspace &rest body) 2072 "Helper macro for invoking BODY in WORKSPACE context." 2073 (declare (debug (form body)) 2074 (indent 1)) 2075 `(let ((lsp--cur-workspace ,workspace)) ,@body)) 2076 2077 (defmacro with-lsp-workspaces (workspaces &rest body) 2078 "Helper macro for invoking BODY against multiple WORKSPACES." 2079 (declare (debug (form body)) 2080 (indent 1)) 2081 `(let ((lsp--buffer-workspaces ,workspaces)) ,@body)) 2082 2083 2084 2085 (defmacro lsp-consistency-check (package) 2086 `(defconst ,(intern (concat (symbol-name package) 2087 "-plist-value-when-compiled")) 2088 (eval-when-compile lsp-use-plists))) 2089 2090 2091 ;; loading code-workspace files 2092 2093 ;;;###autoload 2094 (defun lsp-load-vscode-workspace (file) 2095 "Load vscode workspace from FILE" 2096 (interactive "fSelect file to import: ") 2097 (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session))) 2098 2099 (let ((dir (f-dirname file))) 2100 (->> file 2101 (json-read-file) 2102 (alist-get 'folders) 2103 (-map (-lambda ((&alist 'path)) 2104 (lsp-workspace-folders-add (expand-file-name path dir))))))) 2105 2106 ;;;###autoload 2107 (defun lsp-save-vscode-workspace (file) 2108 "Save vscode workspace to FILE" 2109 (interactive "FSelect file to save to: ") 2110 2111 (let ((json-encoding-pretty-print t)) 2112 (f-write-text (json-encode 2113 `((folders . ,(->> (lsp-session) 2114 (lsp-session-folders) 2115 (--map `((path . ,it))))))) 2116 'utf-8 2117 file))) 2118 2119 2120 (defmacro lsp-foreach-workspace (&rest body) 2121 "Execute BODY for each of the current workspaces." 2122 (declare (debug (form body))) 2123 `(--map (with-lsp-workspace it ,@body) (lsp-workspaces))) 2124 2125 (defmacro when-lsp-workspace (workspace &rest body) 2126 "Helper macro for invoking BODY in WORKSPACE context if present." 2127 (declare (debug (form body)) 2128 (indent 1)) 2129 `(when-let* ((lsp--cur-workspace ,workspace)) ,@body)) 2130 2131 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items)) 2132 (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read)) 2133 (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label)) 2134 items)) 2135 (result (funcall-interactively 2136 selectfunc 2137 (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels)) 2138 (choices (if (listp result) 2139 (if (equal result '("*")) 2140 itemLabels 2141 result) 2142 (list result)))) 2143 (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data)) 2144 (if (member label choices) 2145 (lsp-make-quick-pick-item :label label :picked t :user-data user-data) 2146 nil)) 2147 items))))) 2148 2149 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?)) 2150 (read-string (format "%s: " prompt) (or value? ""))) 2151 2152 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type)) 2153 "Send the server's messages to log. 2154 PARAMS - the data sent from _WORKSPACE." 2155 (funcall (cl-case type 2156 (1 'lsp--error) 2157 (2 'lsp--warn) 2158 (t 'lsp--info)) 2159 "%s" 2160 message)) 2161 2162 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type)) 2163 "Send the server's messages to log. 2164 PARAMS - the data sent from WORKSPACE." 2165 (ignore 2166 (let ((client (lsp--workspace-client workspace))) 2167 (when (or (not client) 2168 (cl-notany (-rpartial #'string-match-p message) 2169 (lsp--client-ignore-messages client))) 2170 (lsp-log "%s" (lsp--propertize message type)))))) 2171 2172 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?)) 2173 "Display a message request to user sending the user selection back to server." 2174 (let* ((message (lsp--propertize message type)) 2175 (choices (seq-map #'lsp:message-action-item-title actions?))) 2176 (if choices 2177 (completing-read (concat message " ") (seq-into choices 'list) nil t) 2178 (lsp-log message)))) 2179 2180 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?)) 2181 "Show document URI in a buffer and go to SELECTION if any." 2182 (let ((path (lsp--uri-to-path uri))) 2183 (when (f-exists? path) 2184 (with-current-buffer (find-file path) 2185 (when selection? 2186 (goto-char (lsp--position-to-point (lsp:range-start selection?)))) 2187 t)))) 2188 2189 (defcustom lsp-progress-prefix "⌛ " 2190 "Progress prefix." 2191 :group 'lsp-mode 2192 :type 'string 2193 :package-version '(lsp-mode . "8.0.0")) 2194 2195 (defcustom lsp-progress-function #'lsp-on-progress-modeline 2196 "Function for handling the progress notifications." 2197 :group 'lsp-mode 2198 :type '(choice 2199 (const :tag "Use modeline" lsp-on-progress-modeline) 2200 (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')" 2201 lsp-on-progress-legacy) 2202 (const :tag "Ignore" ignore) 2203 (function :tag "Other function")) 2204 :package-version '(lsp-mode . "8.0.0")) 2205 2206 (defcustom lsp-request-while-no-input-may-block nil 2207 "Have `lsp-request-while-no-input` block unless `non-essential` is t." 2208 :group 'lsp-mode 2209 :type 'boolean) 2210 2211 (defun lsp--progress-status () 2212 "Returns the status of the progress for the current workspaces." 2213 (-let ((progress-status 2214 (s-join 2215 "|" 2216 (-keep 2217 (lambda (workspace) 2218 (let ((tokens (lsp--workspace-work-done-tokens workspace))) 2219 (unless (ht-empty? tokens) 2220 (mapconcat 2221 (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?)) 2222 (concat (if percentage? 2223 (if (numberp percentage?) 2224 (format "%.0f%%%% " percentage?) 2225 (format "%s%%%% " percentage?)) 2226 "") 2227 (or message? title))) 2228 (ht-values tokens) 2229 "|")))) 2230 (lsp-workspaces))))) 2231 (unless (s-blank? progress-status) 2232 (concat lsp-progress-prefix progress-status " ")))) 2233 2234 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value 2235 (value &as &WorkDoneProgress :kind))) 2236 "PARAMS contains the progress data. 2237 WORKSPACE is the workspace that contains the progress token." 2238 (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status)))) 2239 (pcase kind 2240 ("begin" (lsp-workspace-set-work-done-token token value workspace)) 2241 ("report" (lsp-workspace-set-work-done-token token value workspace)) 2242 ("end" (lsp-workspace-rem-work-done-token token workspace))) 2243 (force-mode-line-update)) 2244 2245 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value 2246 (value &as &WorkDoneProgress :kind))) 2247 "PARAMS contains the progress data. 2248 WORKSPACE is the workspace that contains the progress token." 2249 (pcase kind 2250 ("begin" 2251 (-let* (((&WorkDoneProgressBegin :title :percentage?) value) 2252 (reporter 2253 (if lsp-progress-via-spinner 2254 (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types)) 2255 ;; Set message as a tooltip for the spinner strings 2256 (propertized-strings 2257 (seq-map (lambda (string) (propertize string 'help-echo title)) 2258 spinner-strings)) 2259 (spinner-type (vconcat propertized-strings))) 2260 ;; The progress relates to the server as a whole, 2261 ;; display it on all buffers. 2262 (mapcar (lambda (buffer) 2263 (lsp-with-current-buffer buffer 2264 (spinner-start spinner-type)) 2265 buffer) 2266 (lsp--workspace-buffers workspace))) 2267 (if percentage? 2268 (make-progress-reporter title 0 100 percentage?) 2269 ;; No percentage, just progress 2270 (make-progress-reporter title nil nil))))) 2271 (lsp-workspace-set-work-done-token token reporter workspace))) 2272 ("report" 2273 (when-let* ((reporter (lsp-workspace-get-work-done-token token workspace))) 2274 (unless lsp-progress-via-spinner 2275 (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value))))) 2276 2277 ("end" 2278 (when-let* ((reporter (lsp-workspace-get-work-done-token token workspace))) 2279 (if lsp-progress-via-spinner 2280 (mapc (lambda (buffer) 2281 (when (lsp-buffer-live-p buffer) 2282 (lsp-with-current-buffer buffer 2283 (spinner-stop)))) 2284 reporter) 2285 (progress-reporter-done reporter)) 2286 (lsp-workspace-rem-work-done-token token workspace))))) 2287 2288 2289 ;; diagnostics 2290 2291 (defvar lsp-diagnostic-filter nil 2292 "A a function which will be called with 2293 `&PublishDiagnosticsParams' and `workspace' which can be used 2294 to filter out the diagnostics. The function should return 2295 `&PublishDiagnosticsParams'. 2296 2297 Common usecase are: 2298 1. Filter the diagnostics for a particular language server. 2299 2. Filter out the diagnostics under specific level.") 2300 2301 (defvar lsp-diagnostic-stats (ht)) 2302 2303 (defun lsp-diagnostics (&optional current-workspace?) 2304 "Return the diagnostics from all workspaces." 2305 (or (pcase (if current-workspace? 2306 (lsp-workspaces) 2307 (lsp--session-workspaces (lsp-session))) 2308 (`() ()) 2309 (`(,workspace) (lsp--workspace-diagnostics workspace)) 2310 (`,workspaces (let ((result (make-hash-table :test 'equal))) 2311 (mapc (lambda (workspace) 2312 (->> workspace 2313 (lsp--workspace-diagnostics) 2314 (maphash (lambda (file-name diagnostics) 2315 (puthash file-name 2316 (append (gethash file-name result) diagnostics) 2317 result))))) 2318 workspaces) 2319 result))) 2320 (ht))) 2321 2322 (defun lsp-diagnostics-stats-for (path) 2323 "Get diagnostics statistics for PATH. 2324 The result format is vector [_ errors warnings infos hints] or nil." 2325 (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats)) 2326 2327 (defun lsp-diagnostics--request-pull-diagnostics (workspace) 2328 "Request new diagnostics for the current file within WORKSPACE. 2329 This is only executed if the server supports pull diagnostics." 2330 (when (lsp-feature? "textDocument/diagnostic") 2331 (let ((path (lsp--fix-path-casing (buffer-file-name)))) 2332 (lsp-request-async "textDocument/diagnostic" 2333 (list :textDocument (lsp--text-document-identifier)) 2334 (-lambda ((&DocumentDiagnosticReport :kind :items?)) 2335 (lsp-diagnostics--apply-pull-diagnostics workspace path kind items?)) 2336 :mode 'tick)))) 2337 2338 (defun lsp-diagnostics--update-path (path new-stats) 2339 (let ((new-stats (copy-sequence new-stats)) 2340 (path (lsp--fix-path-casing (directory-file-name path)))) 2341 (if-let* ((old-data (gethash path lsp-diagnostic-stats))) 2342 (dotimes (idx 5) 2343 (cl-callf + (aref old-data idx) 2344 (aref new-stats idx))) 2345 (puthash path new-stats lsp-diagnostic-stats)))) 2346 2347 (defun lsp-diagnostics--convert-and-update-path-stats (workspace path diagnostics) 2348 (let ((path (lsp--fix-path-casing path)) 2349 (new-stats (make-vector 5 0))) 2350 (mapc (-lambda ((&Diagnostic :severity?)) 2351 (cl-incf (aref new-stats (or severity? 1)))) 2352 diagnostics) 2353 (when-let* ((old-diags (gethash path (lsp--workspace-diagnostics workspace)))) 2354 (mapc (-lambda ((&Diagnostic :severity?)) 2355 (cl-decf (aref new-stats (or severity? 1)))) 2356 old-diags)) 2357 (lsp-diagnostics--update-path path new-stats) 2358 (while (not (string= path (setf path (file-name-directory 2359 (directory-file-name path))))) 2360 (lsp-diagnostics--update-path path new-stats)))) 2361 2362 (lsp-defun lsp--on-diagnostics-update-stats (workspace 2363 (&PublishDiagnosticsParams :uri :diagnostics)) 2364 (lsp-diagnostics--convert-and-update-path-stats workspace (lsp--uri-to-path uri) diagnostics)) 2365 2366 (defun lsp-diagnostics--apply-pull-diagnostics (workspace path kind diagnostics?) 2367 "Update WORKSPACE diagnostics at PATH with DIAGNOSTICS?. 2368 Depends on KIND being a \\='full\\=' update." 2369 (cond 2370 ((equal kind "full") 2371 ;; TODO support `lsp-diagnostic-filter' 2372 ;; (the params types differ from the published diagnostics response) 2373 (lsp-diagnostics--convert-and-update-path-stats workspace path diagnostics?) 2374 (-let* ((lsp--virtual-buffer-mappings (ht)) 2375 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2376 (if (seq-empty-p diagnostics?) 2377 (remhash path workspace-diagnostics) 2378 (puthash path (append diagnostics? nil) workspace-diagnostics)) 2379 (run-hooks 'lsp-diagnostics-updated-hook))) 2380 ((equal kind "unchanged") t) 2381 (t (lsp--error "Unknown pull diagnostic result kind '%s'" kind)))) 2382 2383 (defun lsp--on-diagnostics (workspace params) 2384 "Callback for textDocument/publishDiagnostics. 2385 interface PublishDiagnosticsParams { 2386 uri: string; 2387 diagnostics: Diagnostic[]; 2388 } 2389 PARAMS contains the diagnostics data. 2390 WORKSPACE is the workspace that contains the diagnostics." 2391 (when lsp-diagnostic-filter 2392 (setf params (funcall lsp-diagnostic-filter params workspace))) 2393 2394 (lsp--on-diagnostics-update-stats workspace params) 2395 2396 (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params) 2397 (lsp--virtual-buffer-mappings (ht)) 2398 (file (lsp--fix-path-casing (lsp--uri-to-path uri))) 2399 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2400 2401 (if (seq-empty-p diagnostics) 2402 (remhash file workspace-diagnostics) 2403 (puthash file (append diagnostics nil) workspace-diagnostics)) 2404 2405 (run-hooks 'lsp-diagnostics-updated-hook))) 2406 2407 (defun lsp-diagnostics--workspace-cleanup (workspace) 2408 (->> workspace 2409 (lsp--workspace-diagnostics) 2410 (maphash (lambda (key _) 2411 (lsp--on-diagnostics-update-stats 2412 workspace 2413 (lsp-make-publish-diagnostics-params 2414 :uri (lsp--path-to-uri key) 2415 :diagnostics []))))) 2416 (clrhash (lsp--workspace-diagnostics workspace))) 2417 2418 2419 2420 ;; textDocument/foldingRange support 2421 2422 (cl-defstruct lsp--folding-range beg end kind children) 2423 2424 (defvar-local lsp--cached-folding-ranges nil) 2425 (defvar-local lsp--cached-nested-folding-ranges nil) 2426 2427 (defun lsp--folding-range-width (range) 2428 (- (lsp--folding-range-end range) 2429 (lsp--folding-range-beg range))) 2430 2431 (defun lsp--get-folding-ranges () 2432 "Get the folding ranges for the current buffer." 2433 (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges)) 2434 (let* ((ranges (lsp-request "textDocument/foldingRange" 2435 `(:textDocument ,(lsp--text-document-identifier)))) 2436 (sorted-line-col-pairs (->> ranges 2437 (cl-mapcan (-lambda ((&FoldingRange :start-line 2438 :start-character? 2439 :end-line 2440 :end-character?)) 2441 (list (cons start-line start-character?) 2442 (cons end-line end-character?)))) 2443 (-sort #'lsp--line-col-comparator))) 2444 (line-col-to-point-map (lsp--convert-line-col-to-points-batch 2445 sorted-line-col-pairs))) 2446 (setq lsp--cached-folding-ranges 2447 (cons (buffer-chars-modified-tick) 2448 (--> ranges 2449 (seq-map (-lambda ((range &as 2450 &FoldingRange :start-line 2451 :start-character? 2452 :end-line 2453 :end-character? 2454 :kind?)) 2455 (make-lsp--folding-range 2456 :beg (ht-get line-col-to-point-map 2457 (cons start-line start-character?)) 2458 :end (ht-get line-col-to-point-map 2459 (cons end-line end-character?)) 2460 :kind kind?)) 2461 it) 2462 (seq-filter (lambda (folding-range) 2463 (< (lsp--folding-range-beg folding-range) 2464 (lsp--folding-range-end folding-range))) 2465 it) 2466 (seq-into it 'list) 2467 (delete-dups it)))))) 2468 (cdr lsp--cached-folding-ranges)) 2469 2470 (defun lsp--get-nested-folding-ranges () 2471 "Get a list of nested folding ranges for the current buffer." 2472 (-let [(tick . _) lsp--cached-folding-ranges] 2473 (if (and (eq tick (buffer-chars-modified-tick)) 2474 lsp--cached-nested-folding-ranges) 2475 lsp--cached-nested-folding-ranges 2476 (setq lsp--cached-nested-folding-ranges 2477 (lsp--folding-range-build-trees (lsp--get-folding-ranges)))))) 2478 2479 (defun lsp--folding-range-build-trees (ranges) 2480 (setq ranges (seq-sort #'lsp--range-before-p ranges)) 2481 (let* ((dummy-node (make-lsp--folding-range 2482 :beg most-negative-fixnum 2483 :end most-positive-fixnum)) 2484 (stack (list dummy-node))) 2485 (dolist (range ranges) 2486 (while (not (lsp--range-inside-p range (car stack))) 2487 (pop stack)) 2488 (push range (lsp--folding-range-children (car stack))) 2489 (push range stack)) 2490 (lsp--folding-range-children dummy-node))) 2491 2492 (defun lsp--range-inside-p (r1 r2) 2493 "Return non-nil if folding range R1 lies inside R2" 2494 (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2)) 2495 (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2)))) 2496 2497 (defun lsp--range-before-p (r1 r2) 2498 "Return non-nil if folding range R1 ends before R2" 2499 ;; Ensure r1 comes before r2 2500 (or (< (lsp--folding-range-beg r1) 2501 (lsp--folding-range-beg r2)) 2502 ;; If beg(r1) == beg(r2) make sure r2 ends first 2503 (and (= (lsp--folding-range-beg r1) 2504 (lsp--folding-range-beg r2)) 2505 (< (lsp--folding-range-end r2) 2506 (lsp--folding-range-end r1))))) 2507 2508 (defun lsp--point-inside-range-p (point range) 2509 "Return non-nil if POINT lies inside folding range RANGE." 2510 (and (>= point (lsp--folding-range-beg range)) 2511 (<= point (lsp--folding-range-end range)))) 2512 2513 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point))) 2514 "Return the innermost folding range POINT lies in." 2515 (seq-reduce (lambda (innermost-range curr-range) 2516 (if (and (lsp--point-inside-range-p point curr-range) 2517 (or (null innermost-range) 2518 (lsp--range-inside-p curr-range innermost-range))) 2519 curr-range 2520 innermost-range)) 2521 (lsp--get-folding-ranges) 2522 nil)) 2523 2524 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point))) 2525 "Return the outermost folding range POINT lies in." 2526 (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range) 2527 (let ((curr-width (lsp--folding-range-width curr-range))) 2528 (if (and (lsp--point-inside-range-p point curr-range) 2529 (or (null best-pair) 2530 (> curr-width outermost-width))) 2531 (cons curr-width curr-range) 2532 best-pair))) 2533 (lsp--get-folding-ranges) 2534 nil))) 2535 2536 (defun lsp--folding-range-at-point-bounds () 2537 (when (and lsp-enable-folding 2538 (lsp-feature? "textDocument/foldingRange")) 2539 (if-let* ((range (lsp--get-current-innermost-folding-range))) 2540 (cons (lsp--folding-range-beg range) 2541 (lsp--folding-range-end range))))) 2542 (put 'lsp--folding-range 'bounds-of-thing-at-point 2543 #'lsp--folding-range-at-point-bounds) 2544 2545 (defun lsp--get-nearest-folding-range (&optional backward) 2546 (let ((point (point)) 2547 (found nil)) 2548 (while (not 2549 (or found 2550 (if backward 2551 (<= point (point-min)) 2552 (>= point (point-max))))) 2553 (if backward (cl-decf point) (cl-incf point)) 2554 (setq found (lsp--get-current-innermost-folding-range point))) 2555 found)) 2556 2557 (defun lsp--folding-range-at-point-forward-op (n) 2558 (when (and lsp-enable-folding 2559 (not (zerop n)) 2560 (lsp-feature? "textDocument/foldingRange")) 2561 (cl-block break 2562 (dotimes (_ (abs n)) 2563 (if-let* ((range (lsp--get-nearest-folding-range (< n 0)))) 2564 (goto-char (if (< n 0) 2565 (lsp--folding-range-beg range) 2566 (lsp--folding-range-end range))) 2567 (cl-return-from break)))))) 2568 (put 'lsp--folding-range 'forward-op 2569 #'lsp--folding-range-at-point-forward-op) 2570 2571 (defun lsp--folding-range-at-point-beginning-op () 2572 (goto-char (car (lsp--folding-range-at-point-bounds)))) 2573 (put 'lsp--folding-range 'beginning-op 2574 #'lsp--folding-range-at-point-beginning-op) 2575 2576 (defun lsp--folding-range-at-point-end-op () 2577 (goto-char (cdr (lsp--folding-range-at-point-bounds)))) 2578 (put 'lsp--folding-range 'end-op 2579 #'lsp--folding-range-at-point-end-op) 2580 2581 (defun lsp--range-at-point-bounds () 2582 (or (lsp--folding-range-at-point-bounds) 2583 (when-let* ((range (and 2584 (lsp-feature? "textDocument/hover") 2585 (->> (lsp--text-document-position-params) 2586 (lsp-request "textDocument/hover") 2587 (lsp:hover-range?))))) 2588 (lsp--range-to-region range)))) 2589 2590 ;; A more general purpose "thing", useful for applications like focus.el 2591 (put 'lsp--range 'bounds-of-thing-at-point 2592 #'lsp--range-at-point-bounds) 2593 2594 (defun lsp--log-io-p (method) 2595 "Return non nil if should log for METHOD." 2596 (and lsp-log-io 2597 (or (not lsp-log-io-allowlist-methods) 2598 (member method lsp-log-io-allowlist-methods)))) 2599 2600 2601 ;; toggles 2602 2603 (defun lsp-toggle-trace-io () 2604 "Toggle client-server protocol logging." 2605 (interactive) 2606 (setq lsp-log-io (not lsp-log-io)) 2607 (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled"))) 2608 2609 (defun lsp-toggle-signature-auto-activate () 2610 "Toggle signature auto activate." 2611 (interactive) 2612 (setq lsp-signature-auto-activate 2613 (unless lsp-signature-auto-activate '(:on-trigger-char))) 2614 (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled")) 2615 (lsp--update-signature-help-hook)) 2616 2617 (defun lsp-toggle-on-type-formatting () 2618 "Toggle on type formatting." 2619 (interactive) 2620 (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting)) 2621 (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled")) 2622 (lsp--update-on-type-formatting-hook)) 2623 2624 (defun lsp-toggle-symbol-highlight () 2625 "Toggle symbol highlighting." 2626 (interactive) 2627 (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting)) 2628 2629 (cond 2630 ((and lsp-enable-symbol-highlighting 2631 (lsp-feature? "textDocument/documentHighlight")) 2632 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t) 2633 (lsp--info "Symbol highlighting enabled in current buffer.")) 2634 ((not lsp-enable-symbol-highlighting) 2635 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 2636 (lsp--remove-overlays 'lsp-highlight) 2637 (lsp--info "Symbol highlighting disabled in current buffer.")))) 2638 2639 2640 ;; keybindings 2641 (defvar lsp--binding-descriptions nil 2642 "List of key binding/short description pair.") 2643 2644 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings) 2645 "In KEYMAP, define key sequence KEY as DEF conditionally. 2646 This is like `define-key', except the definition disappears 2647 whenever COND evaluates to nil. 2648 DESC is the short-description for the binding. 2649 BINDINGS is a list of (key def desc cond)." 2650 (declare (indent defun) 2651 (debug (form form form form form &rest sexp))) 2652 (->> (cl-list* key def desc cond bindings) 2653 (-partition 4) 2654 (-mapcat (-lambda ((key def desc cond)) 2655 `((define-key ,keymap ,key 2656 '(menu-item 2657 ,(format "maybe-%s" def) 2658 ,def 2659 :filter 2660 (lambda (item) 2661 (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer) 2662 lsp--describe-buffer) 2663 (current-buffer)) 2664 ,cond) 2665 item)))) 2666 (when (stringp ,key) 2667 (setq lsp--binding-descriptions 2668 (append lsp--binding-descriptions '(,key ,desc))))))) 2669 macroexp-progn)) 2670 2671 (defvar lsp--describe-buffer nil) 2672 2673 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus) 2674 (let ((lsp--describe-buffer buffer)) 2675 (funcall fn buffer prefix menus))) 2676 2677 (advice-add 'describe-buffer-bindings 2678 :around 2679 #'lsp-describe-buffer-bindings-advice) 2680 2681 (defun lsp--prepend-prefix (mappings) 2682 (->> mappings 2683 (-partition 2) 2684 (-mapcat (-lambda ((key description)) 2685 (list (concat lsp-keymap-prefix " " key) 2686 description))))) 2687 2688 (defvar lsp-command-map 2689 (-doto (make-sparse-keymap) 2690 (lsp-define-conditional-key 2691 ;; workspaces 2692 "wD" lsp-disconnect "disconnect" (lsp-workspaces) 2693 "wd" lsp-describe-session "describe session" t 2694 "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces) 2695 "wr" lsp-workspace-restart "restart server" (lsp-workspaces) 2696 "ws" lsp "start server" t 2697 2698 ;; formatting 2699 "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting") 2700 (lsp-feature? "textDocument/formatting")) 2701 "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting") 2702 2703 ;; folders 2704 "Fa" lsp-workspace-folders-add "add folder" t 2705 "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t 2706 "Fr" lsp-workspace-folders-remove "remove folder" t 2707 2708 ;; toggles 2709 "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature? 2710 "textDocument/publishDiagnostics") 2711 "TL" lsp-toggle-trace-io "toggle log io" t 2712 "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline) 2713 "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs) 2714 "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature? 2715 "textDocument/codeAction") 2716 "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature? 2717 "textDocument/documentSymbol") 2718 "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc) 2719 "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature? 2720 "textDocument/onTypeFormatting") 2721 "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight") 2722 "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens") 2723 "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp") 2724 2725 ;; goto 2726 "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol") 2727 "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration") 2728 "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list) 2729 "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition") 2730 "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls") 2731 (fboundp 'lsp-treemacs-call-hierarchy)) 2732 "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation") 2733 "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references") 2734 "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition") 2735 2736 ;; help 2737 "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc) 2738 (lsp-feature? "textDocument/hover")) 2739 "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover") 2740 "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp") 2741 2742 ;; refactoring 2743 "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction") 2744 "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename") 2745 2746 ;; actions 2747 "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction") 2748 "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight") 2749 "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy)) 2750 2751 ;; peeks 2752 "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition") 2753 (fboundp 'lsp-ui-peek-find-definitions)) 2754 "Gi" lsp-ui-peek-find-implementation "peek implementations" (and 2755 (fboundp 'lsp-ui-peek-find-implementation) 2756 (lsp-feature? "textDocument/implementation")) 2757 "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references) 2758 (lsp-feature? "textDocument/references")) 2759 "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp 2760 'lsp-ui-peek-find-workspace-symbol) 2761 (lsp-feature? "workspace/symbol"))))) 2762 2763 2764 ;; which-key integration 2765 2766 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key") 2767 (declare-function which-key-add-key-based-replacements "ext:which-key") 2768 2769 (defun lsp-enable-which-key-integration (&optional all-modes) 2770 "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current 2771 active `major-mode', or for all major modes when ALL-MODES is t." 2772 (cl-flet ((which-key-fn (if all-modes 2773 'which-key-add-key-based-replacements 2774 (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode)))) 2775 (apply 2776 #'which-key-fn 2777 (lsp--prepend-prefix 2778 (cl-list* 2779 "" "lsp" 2780 "w" "workspaces" 2781 "F" "folders" 2782 "=" "formatting" 2783 "T" "toggle" 2784 "g" "goto" 2785 "h" "help" 2786 "r" "refactor" 2787 "a" "code actions" 2788 "G" "peek" 2789 lsp--binding-descriptions))))) 2790 2791 2792 ;; Globbing syntax 2793 2794 ;; We port VSCode's glob-to-regexp code 2795 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts) 2796 ;; since the LSP globbing syntax seems to be the same as that of 2797 ;; VSCode. 2798 2799 (defconst lsp-globstar "**" 2800 "Globstar pattern.") 2801 2802 (defconst lsp-glob-split ?/ 2803 "The character by which we split path components in a glob 2804 pattern.") 2805 2806 (defconst lsp-path-regexp "[/\\\\]" 2807 "Forward or backslash to be used as a path separator in 2808 computed regexps.") 2809 2810 (defconst lsp-non-path-regexp "[^/\\\\]" 2811 "A regexp matching anything other than a slash.") 2812 2813 (defconst lsp-globstar-regexp 2814 (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?" 2815 lsp-path-regexp 2816 lsp-non-path-regexp lsp-path-regexp 2817 lsp-path-regexp lsp-non-path-regexp) 2818 "Globstar in regexp form.") 2819 2820 (defun lsp-split-glob-pattern (pattern split-char) 2821 "Split PATTERN at SPLIT-CHAR while respecting braces and brackets." 2822 (when pattern 2823 (let ((segments nil) 2824 (in-braces nil) 2825 (in-brackets nil) 2826 (current-segment "")) 2827 (dolist (char (string-to-list pattern)) 2828 (cl-block 'exit-point 2829 (if (eq char split-char) 2830 (when (and (null in-braces) 2831 (null in-brackets)) 2832 (push current-segment segments) 2833 (setq current-segment "") 2834 (cl-return-from 'exit-point)) 2835 (pcase char 2836 (?{ 2837 (setq in-braces t)) 2838 (?} 2839 (setq in-braces nil)) 2840 (?\[ 2841 (setq in-brackets t)) 2842 (?\] 2843 (setq in-brackets nil)))) 2844 (setq current-segment (concat current-segment 2845 (char-to-string char))))) 2846 (unless (string-empty-p current-segment) 2847 (push current-segment segments)) 2848 (nreverse segments)))) 2849 2850 (defun lsp--glob-to-regexp (pattern) 2851 "Helper function to convert a PATTERN from LSP's glob syntax to 2852 an Elisp regexp." 2853 (if (string-empty-p pattern) 2854 "" 2855 (let ((current-regexp "") 2856 (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split))) 2857 (if (-all? (lambda (segment) (eq segment lsp-globstar)) 2858 glob-segments) 2859 ".*" 2860 (let ((prev-segment-was-globstar nil)) 2861 (seq-do-indexed 2862 (lambda (segment index) 2863 (if (string-equal segment lsp-globstar) 2864 (unless prev-segment-was-globstar 2865 (setq current-regexp (concat current-regexp 2866 lsp-globstar-regexp)) 2867 (setq prev-segment-was-globstar t)) 2868 (let ((in-braces nil) 2869 (brace-val "") 2870 (in-brackets nil) 2871 (bracket-val "")) 2872 (dolist (char (string-to-list segment)) 2873 (cond 2874 ((and (not (char-equal char ?\})) 2875 in-braces) 2876 (setq brace-val (concat brace-val 2877 (char-to-string char)))) 2878 ((and in-brackets 2879 (or (not (char-equal char ?\])) 2880 (string-empty-p bracket-val))) 2881 (let ((curr (cond 2882 ((char-equal char ?-) 2883 "-") 2884 ;; NOTE: ?\^ and ?^ are different characters 2885 ((and (memq char '(?^ ?!)) 2886 (string-empty-p bracket-val)) 2887 "^") 2888 ((char-equal char lsp-glob-split) 2889 "") 2890 (t 2891 (regexp-quote (char-to-string char)))))) 2892 (setq bracket-val (concat bracket-val curr)))) 2893 (t 2894 (cl-case char 2895 (?{ 2896 (setq in-braces t)) 2897 (?\[ 2898 (setq in-brackets t)) 2899 (?} 2900 (let* ((choices (lsp-split-glob-pattern brace-val ?\,)) 2901 (brace-regexp (concat "\\(?:" 2902 (mapconcat #'lsp--glob-to-regexp choices "\\|") 2903 "\\)"))) 2904 (setq current-regexp (concat current-regexp 2905 brace-regexp)) 2906 (setq in-braces nil) 2907 (setq brace-val ""))) 2908 (?\] 2909 (setq current-regexp 2910 (concat current-regexp 2911 "[" bracket-val "]")) 2912 (setq in-brackets nil) 2913 (setq bracket-val "")) 2914 (?? 2915 (setq current-regexp 2916 (concat current-regexp 2917 lsp-non-path-regexp))) 2918 (?* 2919 (setq current-regexp 2920 (concat current-regexp 2921 lsp-non-path-regexp "*?"))) 2922 (t 2923 (setq current-regexp 2924 (concat current-regexp 2925 (regexp-quote (char-to-string char))))))))) 2926 (when (and (< index (1- (length glob-segments))) 2927 (or (not (string-equal (nth (1+ index) glob-segments) 2928 lsp-globstar)) 2929 (< (+ index 2) 2930 (length glob-segments)))) 2931 (setq current-regexp 2932 (concat current-regexp 2933 lsp-path-regexp))) 2934 (setq prev-segment-was-globstar nil)))) 2935 glob-segments) 2936 current-regexp))))) 2937 2938 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365 2939 (defun lsp-glob-unbrace-at-top-level (glob-pattern) 2940 "If GLOB-PATTERN does not start with a brace, return a singleton list 2941 containing GLOB-PATTERN. 2942 2943 If GLOB-PATTERN does start with a brace, return a list of the 2944 comma-separated globs within the top-level braces." 2945 (if (not (string-prefix-p "{" glob-pattern)) 2946 (list glob-pattern) 2947 (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,))) 2948 2949 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern) 2950 "Convert GLOB-PATTERN to a regexp wrapped with the beginning- 2951 and end-of-string meta-characters." 2952 (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'")) 2953 2954 (defun lsp-glob-to-regexps (glob-pattern) 2955 "Convert a GLOB-PATTERN to a list of Elisp regexps." 2956 (when-let* 2957 ((glob-pattern (cond ((hash-table-p glob-pattern) 2958 (ht-get glob-pattern "pattern")) 2959 ((stringp glob-pattern) glob-pattern) 2960 (t (error "Unknown glob-pattern type: %s" glob-pattern)))) 2961 (trimmed-pattern (string-trim glob-pattern)) 2962 (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern))) 2963 (seq-map #'lsp-glob-convert-to-wrapped-regexp 2964 top-level-unbraced-patterns))) 2965 2966 2967 2968 (defvar lsp-mode-menu) 2969 2970 (defun lsp-mouse-click (event) 2971 (interactive "e") 2972 (let* ((ec (event-start event)) 2973 (choice (x-popup-menu event lsp-mode-menu)) 2974 (action (lookup-key lsp-mode-menu (apply 'vector choice)))) 2975 2976 (select-window (posn-window ec)) 2977 2978 (unless (and (region-active-p) (eq action 'lsp-execute-code-action)) 2979 (goto-char (posn-point ec))) 2980 (run-with-idle-timer 2981 0.001 nil 2982 (lambda () 2983 (cl-labels ((check (value) (not (null value)))) 2984 (when choice 2985 (call-interactively action))))))) 2986 2987 (defvar lsp-mode-map 2988 (let ((map (make-sparse-keymap))) 2989 (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse) 2990 (define-key map (kbd "C-<mouse-1>") #'ignore) 2991 (define-key map (kbd "<mouse-3>") #'lsp-mouse-click) 2992 (define-key map (kbd "C-S-SPC") #'lsp-signature-activate) 2993 (when lsp-keymap-prefix 2994 (define-key map (kbd lsp-keymap-prefix) lsp-command-map)) 2995 map) 2996 "Keymap for `lsp-mode'.") 2997 2998 (define-minor-mode lsp-mode "Mode for LSP interaction." 2999 :keymap lsp-mode-map 3000 :lighter 3001 (" LSP[" 3002 (lsp--buffer-workspaces 3003 (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "][")) 3004 (:propertize "Disconnected" face warning)) 3005 "]") 3006 :group 'lsp-mode 3007 (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred)) 3008 ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp' 3009 (lsp))) 3010 3011 (defvar lsp-mode-menu 3012 (easy-menu-create-menu 3013 nil 3014 `(["Go to definition" lsp-find-definition 3015 :active (lsp-feature? "textDocument/definition")] 3016 ["Find references" lsp-find-references 3017 :active (lsp-feature? "textDocument/references")] 3018 ["Find implementations" lsp-find-implementation 3019 :active (lsp-feature? "textDocument/implementation")] 3020 ["Find declarations" lsp-find-declaration 3021 :active (lsp-feature? "textDocument/declaration")] 3022 ["Go to type declaration" lsp-find-type-definition 3023 :active (lsp-feature? "textDocument/typeDefinition")] 3024 "--" 3025 ["Describe" lsp-describe-thing-at-point] 3026 ["Code action" lsp-execute-code-action] 3027 ["Format" lsp-format-buffer] 3028 ["Highlight references" lsp-document-highlight] 3029 ["Type Hierarchy" lsp-java-type-hierarchy 3030 :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")] 3031 ["Type Hierarchy" lsp-treemacs-type-hierarchy 3032 :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")) 3033 (functionp 'lsp-treemacs-type-hierarchy) 3034 (lsp-feature? "textDocument/typeHierarchy"))] 3035 ["Call Hierarchy" lsp-treemacs-call-hierarchy 3036 :visible (and (functionp 'lsp-treemacs-call-hierarchy) 3037 (lsp-feature? "textDocument/callHierarchy"))] 3038 ["Rename" lsp-rename 3039 :active (lsp-feature? "textDocument/rename")] 3040 "--" 3041 ("Session" 3042 ["View logs" lsp-workspace-show-log] 3043 ["Describe" lsp-describe-session] 3044 ["Shutdown" lsp-shutdown-workspace] 3045 ["Restart" lsp-restart-workspace]) 3046 ("Workspace Folders" 3047 ["Add" lsp-workspace-folders-add] 3048 ["Remove" lsp-workspace-folders-remove] 3049 ["Open" lsp-workspace-folders-open]) 3050 ("Toggle features" 3051 ["Lenses" lsp-lens-mode] 3052 ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode] 3053 ["Modeline code actions" lsp-modeline-code-actions-mode] 3054 ["Modeline diagnostics" lsp-modeline-diagnostics-mode]) 3055 "---" 3056 ("Debug" 3057 :active (bound-and-true-p dap-ui-mode) 3058 :filter ,(lambda (_) 3059 (and (boundp 'dap-ui-menu-items) 3060 (nthcdr 3 dap-ui-menu-items)))))) 3061 "Menu for lsp-mode.") 3062 3063 (defalias 'make-lsp-client 'make-lsp--client) 3064 3065 (cl-defstruct lsp--registered-capability 3066 (id "") 3067 (method " ") 3068 (options nil)) 3069 3070 ;; A ‘lsp--workspace’ object represents exactly one language server process. 3071 (cl-defstruct lsp--workspace 3072 ;; the `ewoc' object for displaying I/O to and from the server 3073 (ewoc nil) 3074 3075 ;; ‘server-capabilities’ is a hash table of the language server capabilities. 3076 ;; It is the hash table representation of a LSP ServerCapabilities structure; 3077 ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize. 3078 (server-capabilities nil) 3079 3080 ;; ‘registered-server-capabilities’ is a list of hash tables that represent 3081 ;; dynamically-registered Registration objects. See 3082 ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability. 3083 (registered-server-capabilities nil) 3084 3085 ;; ‘root’ is a directory name or a directory file name for the workspace 3086 ;; root. ‘lsp-mode’ passes this directory to the ‘initialize’ method of the 3087 ;; language server; see 3088 ;; https://microsoft.github.io/language-server-protocol/specification#initialize. 3089 (root nil) 3090 3091 ;; ‘client’ is the ‘lsp--client’ object associated with this workspace. 3092 (client nil) 3093 3094 ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It 3095 ;; used to derive the file path in `lsp--uri-to-path' when using tramp 3096 ;; connection. 3097 (host-root nil) 3098 3099 ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or 3100 ;; a network connection. ‘lsp-mode’ communicates with ‘proc’ using the 3101 ;; language server protocol. ‘proc’ corresponds to the COMMUNICATION-PROCESS 3102 ;; element of the return value of the client’s ‘get-root’ field, which see. 3103 (proc nil) 3104 3105 ;; ‘proc’ is a process object; it must represent a regular process, not a 3106 ;; pipe or network process. It represents the actual server process that 3107 ;; corresponds to this workspace. ‘cmd-proc’ corresponds to the 3108 ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’ 3109 ;; field, which see. 3110 (cmd-proc nil) 3111 3112 ;; ‘buffers’ is a list of buffers associated with this workspace. 3113 (buffers nil) 3114 3115 ;; if semantic tokens is enabled, `semantic-tokens-faces' contains 3116 ;; one face (or nil) for each token type supported by the language server. 3117 (semantic-tokens-faces nil) 3118 3119 ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces' 3120 ;; contains one face (or nil) for each modifier type supported by the language 3121 ;; server 3122 (semantic-tokens-modifier-faces nil) 3123 3124 ;; Extra client capabilities provided by third-party packages using 3125 ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME 3126 ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name, 3127 ;; and CAPS is either a plist of the client capabilities, or a function that 3128 ;; takes no argument and returns a plist of the client capabilities or nil. 3129 (extra-client-capabilities nil) 3130 3131 ;; Workspace status 3132 (status nil) 3133 3134 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3135 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3136 (metadata (make-hash-table :test 'equal)) 3137 3138 ;; contains all the file notification watches that have been created for the 3139 ;; current workspace in format filePath->file notification handle. 3140 (watches (make-hash-table :test 'equal)) 3141 3142 ;; list of workspace folders 3143 (workspace-folders nil) 3144 3145 ;; ‘last-id’ the last request id for the current workspace. 3146 (last-id 0) 3147 3148 ;; ‘status-string’ allows extensions to specify custom status string based on 3149 ;; the Language Server specific messages. 3150 (status-string nil) 3151 3152 ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it 3153 ;; was stopped). 3154 shutdown-action 3155 3156 ;; ‘diagnostics’ a hashmap with workspace diagnostics. 3157 (diagnostics (make-hash-table :test 'equal)) 3158 3159 ;; contains all the workDone progress tokens that have been created 3160 ;; for the current workspace. 3161 (work-done-tokens (make-hash-table :test 'equal))) 3162 3163 3164 (cl-defstruct lsp-session 3165 ;; contains the folders that are part of the current session 3166 folders 3167 ;; contains the folders that must not be imported in the current workspace. 3168 folders-blocklist 3169 ;; contains the list of folders that must be imported in a project in case of 3170 ;; multi root LSP server. 3171 (server-id->folders (make-hash-table :test 'equal)) 3172 ;; folder to list of the servers that are associated with the folder. 3173 (folder->servers (make-hash-table :test 'equal)) 3174 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3175 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3176 (metadata (make-hash-table :test 'equal))) 3177 3178 (defun lsp-workspace-status (status-string &optional workspace) 3179 "Set current workspace status to STATUS-STRING. 3180 If WORKSPACE is not specified defaults to lsp--cur-workspace." 3181 (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string)))) 3182 (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string))) 3183 3184 (defun lsp-session-set-metadata (key value &optional _workspace) 3185 "Associate KEY with VALUE in the WORKSPACE metadata. 3186 If WORKSPACE is not provided current workspace will be used." 3187 (puthash key value (lsp-session-metadata (lsp-session)))) 3188 3189 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata) 3190 3191 (defun lsp-session-get-metadata (key &optional _workspace) 3192 "Lookup KEY in WORKSPACE metadata. 3193 If WORKSPACE is not provided current workspace will be used." 3194 (gethash key (lsp-session-metadata (lsp-session)))) 3195 3196 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata) 3197 3198 (defun lsp-workspace-set-work-done-token (token value workspace) 3199 "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens." 3200 (puthash token value (lsp--workspace-work-done-tokens workspace))) 3201 3202 (defun lsp-workspace-get-work-done-token (token workspace) 3203 "Lookup TOKEN in the WORKSPACE work-done-tokens." 3204 (gethash token (lsp--workspace-work-done-tokens workspace))) 3205 3206 (defun lsp-workspace-rem-work-done-token (token workspace) 3207 "Remove TOKEN from the WORKSPACE work-done-tokens." 3208 (remhash token (lsp--workspace-work-done-tokens workspace))) 3209 3210 3211 (defun lsp--make-notification (method &optional params) 3212 "Create notification body for method METHOD and parameters PARAMS." 3213 (list :jsonrpc "2.0" :method method :params params)) 3214 3215 (defalias 'lsp--make-request 'lsp--make-notification) 3216 (defalias 'lsp-make-request 'lsp--make-notification) 3217 3218 (defun lsp--make-response (id result) 3219 "Create response for REQUEST with RESULT." 3220 `(:jsonrpc "2.0" :id ,id :result ,result)) 3221 3222 (defun lsp-make-notification (method &optional params) 3223 "Create notification body for method METHOD and parameters PARAMS." 3224 (lsp--make-notification method params)) 3225 3226 (defmacro lsp--json-serialize (params) 3227 (if (progn 3228 (require 'json) 3229 (fboundp 'json-serialize)) 3230 `(json-serialize ,params 3231 :null-object nil 3232 :false-object :json-false) 3233 `(let ((json-false :json-false)) 3234 (json-encode ,params)))) 3235 3236 (defun lsp--make-message (params) 3237 "Create a LSP message from PARAMS, after encoding it to a JSON string." 3238 (let ((body (lsp--json-serialize params))) 3239 (concat "Content-Length: " 3240 (number-to-string (1+ (string-bytes body))) 3241 "\r\n\r\n" 3242 body 3243 "\n"))) 3244 3245 (cl-defstruct lsp--log-entry timestamp process-time type method id body) 3246 3247 (defun lsp--make-log-entry (method id body type &optional process-time) 3248 "Create an outgoing log object from BODY with method METHOD and id ID. 3249 If ID is non-nil, then the body is assumed to be a notification. 3250 TYPE can either be `incoming' or `outgoing'" 3251 (cl-assert (memq type '(incoming-req outgoing-req incoming-notif 3252 outgoing-notif incoming-resp 3253 outgoing-resp))) 3254 (make-lsp--log-entry 3255 :timestamp (format-time-string "%I:%M:%S %p") 3256 :process-time process-time 3257 :method method 3258 :id id 3259 :type type 3260 :body body)) 3261 3262 (defun lsp--log-font-lock-json (body) 3263 "Font lock JSON BODY." 3264 (with-temp-buffer 3265 (insert body) 3266 ;; We set the temp buffer file-name extension to .json and call `set-auto-mode' 3267 ;; so the users configured json mode is used which could be 3268 ;; `json-mode', `json-ts-mode', `jsonian-mode', etc. 3269 (let ((buffer-file-name "lsp-log.json")) 3270 (delay-mode-hooks 3271 (set-auto-mode) 3272 (if (fboundp 'font-lock-ensure) 3273 (font-lock-ensure) 3274 (with-no-warnings 3275 (font-lock-fontify-buffer))))) 3276 (buffer-string))) 3277 3278 (defun lsp--log-entry-pp (entry) 3279 (cl-assert (lsp--log-entry-p entry)) 3280 (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time 3281 body) 3282 entry) 3283 (json-false :json-false) 3284 (json-encoding-pretty-print t) 3285 (str nil)) 3286 (setq str 3287 (concat (format "[Trace - %s] " timestamp) 3288 (pcase type 3289 ('incoming-req (format "Received request '%s - (%s)." method id)) 3290 ('outgoing-req (format "Sending request '%s - (%s)'." method id)) 3291 3292 ('incoming-notif (format "Received notification '%s'." method)) 3293 ('outgoing-notif (format "Sending notification '%s'." method)) 3294 3295 ('incoming-resp (format "Received response '%s - (%s)' in %dms." 3296 method id process-time)) 3297 ('outgoing-resp 3298 (format 3299 "Sending response '%s - (%s)'. Processing request took %dms" 3300 method id process-time))) 3301 "\n" 3302 (if (memq type '(incoming-resp ougoing-resp)) 3303 "Result: " 3304 "Params: ") 3305 (lsp--log-font-lock-json (json-encode body)) 3306 "\n\n\n")) 3307 (setq str (propertize str 'mouse-face 'highlight 'read-only t)) 3308 (insert str))) 3309 3310 (defvar-local lsp--log-io-ewoc nil) 3311 3312 (defun lsp--get-create-io-ewoc (workspace) 3313 (if (and (lsp--workspace-ewoc workspace) 3314 (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace)))) 3315 (lsp--workspace-ewoc workspace) 3316 (with-current-buffer (lsp--get-log-buffer-create workspace) 3317 (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode)) 3318 (setq-local window-point-insertion-type t) 3319 (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t)) 3320 (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc)) 3321 (lsp--workspace-ewoc workspace))) 3322 3323 (defun lsp--ewoc-count (ewoc) 3324 (let* ((count 0) 3325 (count-fn (lambda (_) (setq count (1+ count))))) 3326 (ewoc-map count-fn ewoc) 3327 count)) 3328 3329 (defun lsp--log-entry-new (entry workspace) 3330 (let* ((ewoc (lsp--get-create-io-ewoc workspace)) 3331 (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc))) 3332 (node (if (or (eq lsp-io-messages-max t) 3333 (>= lsp-io-messages-max count)) 3334 nil 3335 (ewoc-nth ewoc (1- lsp-io-messages-max)))) 3336 (prev nil) 3337 (inhibit-read-only t)) 3338 (while node 3339 (setq prev (ewoc-prev ewoc node)) 3340 (ewoc-delete ewoc node) 3341 (setq node prev)) 3342 (ewoc-enter-last ewoc entry))) 3343 3344 (defun lsp--send-notification (body) 3345 "Send BODY as a notification to the language server." 3346 (lsp-foreach-workspace 3347 (when (lsp--log-io-p (plist-get body :method)) 3348 (lsp--log-entry-new (lsp--make-log-entry 3349 (plist-get body :method) 3350 nil (plist-get body :params) 'outgoing-notif) 3351 lsp--cur-workspace)) 3352 (lsp--send-no-wait body 3353 (lsp--workspace-proc lsp--cur-workspace)))) 3354 3355 (defalias 'lsp-send-notification 'lsp--send-notification) 3356 3357 (defun lsp-notify (method params) 3358 "Send notification METHOD with PARAMS." 3359 (lsp--send-notification (lsp--make-notification method params))) 3360 3361 (defun lsp--cur-workspace-check () 3362 "Check whether buffer lsp workspace(s) are set." 3363 (cl-assert (lsp-workspaces) nil 3364 "No language server(s) is associated with this buffer.")) 3365 3366 (defun lsp--send-request (body &optional no-wait no-merge) 3367 "Send BODY as a request to the language server, get the response. 3368 If NO-WAIT is non-nil, don't synchronously wait for a response. 3369 If NO-MERGE is non-nil, don't merge the results but return an 3370 alist mapping workspace->result." 3371 (lsp-request (plist-get body :method) 3372 (plist-get body :params) 3373 :no-wait no-wait 3374 :no-merge no-merge)) 3375 3376 (defalias 'lsp-send-request 'lsp--send-request 3377 "Send BODY as a request to the language server and return the response 3378 synchronously. 3379 \n(fn BODY)") 3380 3381 (cl-defun lsp-request (method params &key no-wait no-merge) 3382 "Send request METHOD with PARAMS. 3383 If NO-MERGE is non-nil, don't merge the results but return alist 3384 workspace->result. 3385 If NO-WAIT is non-nil send the request as notification." 3386 (if no-wait 3387 (lsp-notify method params) 3388 (let* ((send-time (float-time)) 3389 ;; max time by which we must get a response 3390 (expected-time 3391 (and 3392 lsp-response-timeout 3393 (+ send-time lsp-response-timeout))) 3394 resp-result resp-error done?) 3395 (unwind-protect 3396 (progn 3397 (lsp-request-async method params 3398 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3399 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3400 :no-merge no-merge 3401 :mode 'detached 3402 :cancel-token :sync-request) 3403 (while (not (or resp-error resp-result)) 3404 (if (functionp 'json-rpc-connection) 3405 (catch 'lsp-done (sit-for 0.01)) 3406 (catch 'lsp-done 3407 (accept-process-output 3408 nil 3409 (if expected-time (- expected-time send-time) 1)))) 3410 (setq send-time (float-time)) 3411 (when (and expected-time (< expected-time send-time)) 3412 (error "Timeout while waiting for response. Method: %s" method))) 3413 (setq done? t) 3414 (cond 3415 ((eq resp-result :finished) nil) 3416 (resp-result resp-result) 3417 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3418 ((lsp-json-error? (cl-first resp-error)) 3419 (error (lsp:json-error-message (cl-first resp-error)))))) 3420 (unless done? 3421 (lsp-cancel-request-by-token :sync-request)))))) 3422 3423 (cl-defun lsp-request-while-no-input (method params) 3424 "Send request METHOD with PARAMS and waits until there is no input. 3425 Return same value as `lsp--while-no-input' and respecting `non-essential'." 3426 (if (or non-essential (not lsp-request-while-no-input-may-block)) 3427 (let* ((send-time (float-time)) 3428 ;; max time by which we must get a response 3429 (expected-time 3430 (and 3431 lsp-response-timeout 3432 (+ send-time lsp-response-timeout))) 3433 resp-result resp-error done?) 3434 (unwind-protect 3435 (progn 3436 (lsp-request-async method params 3437 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3438 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3439 :mode 'detached 3440 :cancel-token :sync-request) 3441 (while (not (or resp-error resp-result (input-pending-p))) 3442 (catch 'lsp-done 3443 (sit-for 3444 (if expected-time (- expected-time send-time) 1))) 3445 (setq send-time (float-time)) 3446 (when (and expected-time (< expected-time send-time)) 3447 (error "Timeout while waiting for response. Method: %s" method))) 3448 (setq done? (or resp-error resp-result)) 3449 (cond 3450 ((eq resp-result :finished) nil) 3451 (resp-result resp-result) 3452 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3453 ((lsp-json-error? (cl-first resp-error)) 3454 (error (lsp:json-error-message (cl-first resp-error)))))) 3455 (unless done? 3456 (lsp-cancel-request-by-token :sync-request)) 3457 (when (and (input-pending-p) lsp--throw-on-input) 3458 (throw 'input :interrupted)))) 3459 (lsp-request method params))) 3460 3461 (defvar lsp--cancelable-requests (ht)) 3462 3463 (cl-defun lsp-request-async (method params callback 3464 &key mode error-handler cancel-handler no-merge cancel-token) 3465 "Send METHOD with PARAMS as a request to the language server. 3466 Call CALLBACK with the response received from the server 3467 asynchronously. 3468 MODE determines when the callback will be called depending on the 3469 condition of the original buffer. It could be: 3470 - `detached' which means that the callback will be executed no 3471 matter what has happened to the buffer. 3472 - `alive' - the callback will be executed only if the buffer from 3473 which the call was executed is still alive. 3474 - `current' the callback will be executed only if the original buffer 3475 is still selected. 3476 - `tick' - the callback will be executed only if the buffer was not modified. 3477 - `unchanged' - the callback will be executed only if the buffer hasn't 3478 changed and if the buffer is not modified. 3479 3480 ERROR-HANDLER will be called in case the request has failed. 3481 CANCEL-HANDLER will be called in case the request is being canceled. 3482 If NO-MERGE is non-nil, don't merge the results but return alist 3483 workspace->result. 3484 CANCEL-TOKEN is the token that can be used to cancel request." 3485 (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params) 3486 callback mode error-handler cancel-handler no-merge cancel-token)) 3487 3488 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback) 3489 (lambda (&rest _) 3490 (unless (and (equal 'post-command-hook hook) 3491 (equal (current-buffer) buf)) 3492 (lsp--request-cleanup-hooks id) 3493 (with-lsp-workspaces workspaces 3494 (lsp--cancel-request id) 3495 (when cancel-callback (funcall cancel-callback))) 3496 (lsp-log "Cancelling %s(%s) in hook %s" method id hook)))) 3497 3498 (defun lsp--create-async-callback 3499 (callback method no-merge workspaces) 3500 "Create async handler expecting COUNT results, merge them and call CALLBACK. 3501 MODE determines when the callback will be called depending on the 3502 condition of the original buffer. METHOD is the invoked method. 3503 If NO-MERGE is non-nil, don't merge the results but return alist 3504 workspace->result. ID is the request id." 3505 (let (results errors) 3506 (lambda (result) 3507 (push (cons lsp--cur-workspace result) 3508 (if (eq result :error) errors results)) 3509 (when (and (not (eq (length errors) (length workspaces))) 3510 (eq (+ (length errors) (length results)) (length workspaces))) 3511 (funcall callback 3512 (if no-merge 3513 results 3514 (lsp--merge-results (-map #'cl-rest results) method))))))) 3515 3516 (defcustom lsp-default-create-error-handler-fn nil 3517 "Default error handler customization. 3518 Handler should give METHOD as argument and return function of one argument 3519 ERROR." 3520 :type 'function 3521 :group 'lsp-mode 3522 :package-version '(lsp-mode . "9.0.0")) 3523 3524 (defun lsp--create-default-error-handler (method) 3525 "Default error handler. 3526 METHOD is the executed method." 3527 (if lsp-default-create-error-handler-fn 3528 (funcall lsp-default-create-error-handler-fn method) 3529 (lambda (error) 3530 (lsp--warn "%s" (or (lsp--error-string error) 3531 (format "%s Request has failed" method)))))) 3532 3533 (defvar lsp--request-cleanup-hooks (ht)) 3534 3535 (defun lsp--request-cleanup-hooks (request-id) 3536 (when-let* ((cleanup-function (gethash request-id lsp--request-cleanup-hooks))) 3537 (funcall cleanup-function) 3538 (remhash request-id lsp--request-cleanup-hooks))) 3539 3540 (defun lsp-cancel-request-by-token (cancel-token) 3541 "Cancel request using CANCEL-TOKEN." 3542 (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests)) 3543 (with-lsp-workspaces workspaces 3544 (lsp--cancel-request request-id)) 3545 (remhash cancel-token lsp--cancelable-requests) 3546 (lsp--request-cleanup-hooks request-id))) 3547 3548 (defun lsp--send-request-async (body callback 3549 &optional mode error-callback cancel-callback 3550 no-merge cancel-token) 3551 "Send BODY as a request to the language server. 3552 Call CALLBACK with the response received from the server 3553 asynchronously. 3554 MODE determines when the callback will be called depending on the 3555 condition of the original buffer. It could be: 3556 - `detached' which means that the callback will be executed no 3557 matter what has happened to the buffer. 3558 - `alive' - the callback will be executed only if the buffer from 3559 which the call was executed is still alive. 3560 - `current' the callback will be executed only if the original buffer 3561 is still selected. 3562 - `tick' - the callback will be executed only if the buffer was not modified. 3563 - `unchanged' - the callback will be executed only if the buffer hasn't 3564 changed and if the buffer is not modified. 3565 3566 ERROR-CALLBACK will be called in case the request has failed. 3567 CANCEL-CALLBACK will be called in case the request is being canceled. 3568 If NO-MERGE is non-nil, don't merge the results but return alist 3569 workspace->result. 3570 CANCEL-TOKEN is the token that can be used to cancel request." 3571 (when cancel-token 3572 (lsp-cancel-request-by-token cancel-token)) 3573 3574 (if-let* ((target-workspaces (lsp--find-workspaces-for body))) 3575 (let* ((start-time (current-time)) 3576 (method (plist-get body :method)) 3577 (id (cl-incf lsp-last-id)) 3578 (buf (current-buffer)) 3579 (cancel-callback (when cancel-callback 3580 (pcase mode 3581 ((or 'alive 'tick 'unchanged) 3582 (lambda () 3583 (with-current-buffer buf 3584 (funcall cancel-callback)))) 3585 (_ cancel-callback)))) 3586 ;; calculate what are the (hook . local) pairs which will cancel 3587 ;; the request 3588 (hooks (pcase mode 3589 ('alive '((kill-buffer-hook . t))) 3590 ('tick '((kill-buffer-hook . t) (after-change-functions . t))) 3591 ('unchanged '((after-change-functions . t) (post-command-hook . nil))) 3592 ('current '((post-command-hook . nil))))) 3593 ;; note: lambdas in emacs can be compared but we should make sure 3594 ;; that all of the captured arguments are the same - in our case 3595 ;; `lsp--create-request-cancel' will return the same lambda when 3596 ;; called with the same params. 3597 (cleanup-hooks 3598 (lambda () (mapc 3599 (-lambda ((hook . local)) 3600 (if local 3601 (when (buffer-live-p buf) 3602 (with-current-buffer buf 3603 (remove-hook hook 3604 (lsp--create-request-cancel 3605 id target-workspaces hook buf method cancel-callback) 3606 t))) 3607 (remove-hook hook (lsp--create-request-cancel 3608 id target-workspaces hook buf method cancel-callback)))) 3609 hooks) 3610 (remhash cancel-token lsp--cancelable-requests))) 3611 (callback (pcase mode 3612 ((or 'alive 'tick 'unchanged) (lambda (&rest args) 3613 (with-current-buffer buf 3614 (apply callback args)))) 3615 (_ callback))) 3616 (callback (lsp--create-async-callback callback 3617 method 3618 no-merge 3619 target-workspaces)) 3620 (callback (lambda (result) 3621 (lsp--request-cleanup-hooks id) 3622 (funcall callback result))) 3623 (error-callback (lsp--create-async-callback 3624 (or error-callback 3625 (lsp--create-default-error-handler method)) 3626 method 3627 nil 3628 target-workspaces)) 3629 (error-callback (lambda (error) 3630 (funcall callback :error) 3631 (lsp--request-cleanup-hooks id) 3632 (funcall error-callback error))) 3633 (body (plist-put body :id id))) 3634 3635 ;; cancel request in any of the hooks 3636 (mapc (-lambda ((hook . local)) 3637 (add-hook hook 3638 (lsp--create-request-cancel 3639 id target-workspaces hook buf method cancel-callback) 3640 nil local)) 3641 hooks) 3642 (puthash id cleanup-hooks lsp--request-cleanup-hooks) 3643 3644 (setq lsp--last-active-workspaces target-workspaces) 3645 3646 (when cancel-token 3647 (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests)) 3648 3649 (seq-doseq (workspace target-workspaces) 3650 (when (lsp--log-io-p method) 3651 (lsp--log-entry-new (lsp--make-log-entry method id 3652 (plist-get body :params) 3653 'outgoing-req) 3654 workspace)) 3655 (puthash id 3656 (list callback error-callback method start-time (current-time)) 3657 (-> workspace 3658 (lsp--workspace-client) 3659 (lsp--client-response-handlers))) 3660 (lsp--send-no-wait body (lsp--workspace-proc workspace))) 3661 body) 3662 (error "The connected server(s) does not support method %s. 3663 To find out what capabilities support your server use `M-x lsp-describe-session' 3664 and expand the capabilities section" 3665 (plist-get body :method)))) 3666 3667 ;; deprecated, use lsp-request-async. 3668 (defalias 'lsp-send-request-async 'lsp--send-request-async) 3669 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1") 3670 3671 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any 3672 ;; pending language servers. 3673 (add-hook 'kill-emacs-hook #'lsp--global-teardown) 3674 3675 (defun lsp--global-teardown () 3676 "Unload working workspaces." 3677 (lsp-foreach-workspace (lsp--shutdown-workspace))) 3678 3679 (defun lsp--shutdown-workspace (&optional restart) 3680 "Shut down the language server process for ‘lsp--cur-workspace’." 3681 (with-demoted-errors "LSP error: %S" 3682 (let ((lsp-response-timeout 0.5)) 3683 (condition-case err 3684 (lsp-request "shutdown" nil) 3685 (error (lsp--error "%s" err)))) 3686 (lsp-notify "exit" nil)) 3687 (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown)) 3688 (lsp--uninitialize-workspace)) 3689 3690 (defcustom lsp-inlay-hint-enable nil 3691 "If non-nil it will enable inlay hints." 3692 :type 'boolean 3693 :group 'lsp-mode 3694 :package-version '(lsp-mode . "9.0.0")) 3695 3696 (defun lsp--uninitialize-workspace () 3697 "Cleanup buffer state. 3698 When a workspace is shut down, by request or from just 3699 disappearing, unset all the variables related to it." 3700 (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace] 3701 (lsp-process-kill cmd-proc) 3702 (mapc (lambda (buf) 3703 (when (lsp-buffer-live-p buf) 3704 (lsp-with-current-buffer buf 3705 (lsp-managed-mode -1)))) 3706 buffers) 3707 (lsp-diagnostics--workspace-cleanup lsp--cur-workspace))) 3708 3709 (defun lsp--client-capabilities (&optional custom-capabilities) 3710 "Return the client capabilities appending CUSTOM-CAPABILITIES." 3711 (append 3712 `((general . ((positionEncodings . ["utf-32", "utf-16"]))) 3713 (workspace . ((workspaceEdit . ((documentChanges . t) 3714 (resourceOperations . ["create" "rename" "delete"]))) 3715 (applyEdit . t) 3716 (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))))) 3717 (executeCommand . ((dynamicRegistration . :json-false))) 3718 ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t))))) 3719 (workspaceFolders . t) 3720 (configuration . t) 3721 ,@(when lsp-semantic-tokens-enable 3722 `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests) 3723 lsp-semantic-tokens-honor-refresh-requests) 3724 :json-false)))))) 3725 ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t))))) 3726 ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false))))) 3727 (diagnostics . ((refreshSupport . :json-false))) 3728 (fileOperations . ((didCreate . :json-false) 3729 (willCreate . :json-false) 3730 (didRename . t) 3731 (willRename . t) 3732 (didDelete . :json-false) 3733 (willDelete . :json-false))))) 3734 (textDocument . ((declaration . ((dynamicRegistration . t) 3735 (linkSupport . t))) 3736 (definition . ((dynamicRegistration . t) 3737 (linkSupport . t))) 3738 (references . ((dynamicRegistration . t))) 3739 (implementation . ((dynamicRegistration . t) 3740 (linkSupport . t))) 3741 (typeDefinition . ((dynamicRegistration . t) 3742 (linkSupport . t))) 3743 (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t))) 3744 (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))) 3745 (hierarchicalDocumentSymbolSupport . t))) 3746 (formatting . ((dynamicRegistration . t))) 3747 (rangeFormatting . ((dynamicRegistration . t))) 3748 (onTypeFormatting . ((dynamicRegistration . t))) 3749 ,@(when (and lsp-semantic-tokens-enable 3750 (functionp 'lsp--semantic-tokens-capabilities)) 3751 (lsp--semantic-tokens-capabilities)) 3752 (rename . ((dynamicRegistration . t) (prepareSupport . t))) 3753 (codeAction . ((dynamicRegistration . t) 3754 (isPreferredSupport . t) 3755 (codeActionLiteralSupport . ((codeActionKind . ((valueSet . ["" 3756 "quickfix" 3757 "refactor" 3758 "refactor.extract" 3759 "refactor.inline" 3760 "refactor.rewrite" 3761 "source" 3762 "source.organizeImports"]))))) 3763 (resolveSupport . ((properties . ["edit" "command"]))) 3764 (dataSupport . t))) 3765 (completion . ((completionItem . ((snippetSupport . ,(cond 3766 ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode))) 3767 (lsp--warn (concat 3768 "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. " 3769 "You must either install yasnippet, or disable snippet support.")) 3770 :json-false) 3771 (lsp-enable-snippet t) 3772 (t :json-false))) 3773 (documentationFormat . ["markdown" "plaintext"]) 3774 ;; Remove this after jdtls support resolveSupport 3775 (resolveAdditionalTextEditsSupport . t) 3776 (insertReplaceSupport . t) 3777 (deprecatedSupport . t) 3778 (resolveSupport 3779 . ((properties . ["documentation" 3780 "detail" 3781 "additionalTextEdits" 3782 "command" 3783 "insertTextFormat" 3784 "insertTextMode"]))) 3785 (insertTextModeSupport . ((valueSet . [1 2]))))) 3786 (contextSupport . t) 3787 (dynamicRegistration . t))) 3788 (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t))))) 3789 (dynamicRegistration . t))) 3790 (documentLink . ((dynamicRegistration . t) 3791 (tooltipSupport . t))) 3792 (hover . ((contentFormat . ["markdown" "plaintext"]) 3793 (dynamicRegistration . t))) 3794 ,@(when lsp-enable-folding 3795 `((foldingRange . ((dynamicRegistration . t) 3796 ,@(when lsp-folding-range-limit 3797 `((rangeLimit . ,lsp-folding-range-limit))) 3798 ,@(when lsp-folding-line-folding-only 3799 `((lineFoldingOnly . t))))))) 3800 (selectionRange . ((dynamicRegistration . t))) 3801 (callHierarchy . ((dynamicRegistration . :json-false))) 3802 (typeHierarchy . ((dynamicRegistration . t))) 3803 (publishDiagnostics . ((relatedInformation . t) 3804 (tagSupport . ((valueSet . [1 2]))) 3805 (versionSupport . t))) 3806 (diagnostic . ((dynamicRegistration . :json-false) 3807 (relatedDocumentSupport . :json-false))) 3808 (linkedEditingRange . ((dynamicRegistration . t))))) 3809 (window . ((workDoneProgress . t) 3810 (showDocument . ((support . t)))))) 3811 custom-capabilities)) 3812 3813 (defun lsp-find-roots-for-workspace (workspace session) 3814 "Get all roots for the WORKSPACE." 3815 (-filter #'identity (ht-map (lambda (folder workspaces) 3816 (when (-contains? workspaces workspace) 3817 folder)) 3818 (lsp-session-folder->servers session)))) 3819 3820 (defun lsp-session-watches (&optional session) 3821 "Get watches created for SESSION." 3822 (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session)))) 3823 (-let [res (make-hash-table :test 'equal)] 3824 (puthash "__watches" res (lsp-session-metadata (or session (lsp-session)))) 3825 res))) 3826 3827 (defun lsp--file-process-event (session root-folder event) 3828 "Process file event." 3829 (let* ((changed-file (cl-third event)) 3830 (rel-changed-file (f-relative changed-file root-folder)) 3831 (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type)) 3832 (bit-position (1- event-numeric-kind)) 3833 (watch-bit (ash 1 bit-position))) 3834 (->> 3835 session 3836 lsp-session-folder->servers 3837 (gethash root-folder) 3838 (seq-do (lambda (workspace) 3839 (when (->> 3840 workspace 3841 lsp--workspace-registered-server-capabilities 3842 (-any? 3843 (lambda (capability) 3844 (and 3845 (equal (lsp--registered-capability-method capability) 3846 "workspace/didChangeWatchedFiles") 3847 (->> 3848 capability 3849 lsp--registered-capability-options 3850 (lsp:did-change-watched-files-registration-options-watchers) 3851 (seq-find 3852 (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp)) 3853 (when (or (null kind?) 3854 (> (logand kind? watch-bit) 0)) 3855 (-let [regexes (or cached-regexp 3856 (let ((regexp (lsp-glob-to-regexps glob-pattern))) 3857 (lsp-put fs-watcher :_cachedRegexp regexp) 3858 regexp))] 3859 (-any? (lambda (re) 3860 (or (string-match re changed-file) 3861 (string-match re rel-changed-file))) 3862 regexes)))))))))) 3863 (with-lsp-workspace workspace 3864 (lsp-notify 3865 "workspace/didChangeWatchedFiles" 3866 `((changes . [((type . ,event-numeric-kind) 3867 (uri . ,(lsp--path-to-uri changed-file)))])))))))))) 3868 3869 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?)) 3870 "Register capability REG." 3871 (when (and lsp-enable-file-watchers 3872 (equal method "workspace/didChangeWatchedFiles")) 3873 (-let* ((created-watches (lsp-session-watches (lsp-session))) 3874 (root-folders (cl-set-difference 3875 (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session)) 3876 (ht-keys created-watches)))) 3877 ;; create watch for each root folder without such 3878 (dolist (folder root-folders) 3879 (let* ((watch (make-lsp-watch :root-directory folder)) 3880 (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder)) 3881 (ignored-files-regex-list (car ignored-things)) 3882 (ignored-directories-regex-list (cadr ignored-things))) 3883 (puthash folder watch created-watches) 3884 (lsp-watch-root-folder (file-truename folder) 3885 (-partial #'lsp--file-process-event (lsp-session) folder) 3886 ignored-files-regex-list 3887 ignored-directories-regex-list 3888 watch 3889 t))))) 3890 3891 (push 3892 (make-lsp--registered-capability :id id :method method :options register-options?) 3893 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3894 3895 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body) 3896 "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to 3897 access dir-local variables." 3898 (declare (indent 1) (debug t)) 3899 `(with-temp-buffer 3900 ;; Set the buffer's name to something under the root so that we can hack the local variables 3901 ;; This file doesn't need to exist and will not be created due to this. 3902 (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root))) 3903 (hack-local-variables) 3904 (prog1 ,@body 3905 (setq-local buffer-file-name nil)))) 3906 3907 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root) 3908 "Return a list of the form 3909 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given 3910 WORKSPACE-ROOT." 3911 ;; The intent of this function is to provide per-root workspace-level customization of the 3912 ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables. 3913 (lsp--with-workspace-temp-buffer workspace-root 3914 (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories)))) 3915 3916 3917 (defun lsp--cleanup-hanging-watches () 3918 "Cleanup watches in case there are no more workspaces that are interested 3919 in that particular folder." 3920 (let* ((session (lsp-session)) 3921 (watches (lsp-session-watches session))) 3922 (dolist (watched-folder (ht-keys watches)) 3923 (when (-none? (lambda (workspace) 3924 (with-lsp-workspace workspace 3925 (lsp--registered-capability "workspace/didChangeWatchedFiles"))) 3926 (gethash watched-folder (lsp-session-folder->servers (lsp-session)))) 3927 (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder) 3928 (lsp-kill-watch (gethash watched-folder watches)) 3929 (remhash watched-folder watches))))) 3930 3931 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method)) 3932 "Unregister capability UNREG." 3933 (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace) 3934 (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id)) 3935 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3936 (when (equal method "workspace/didChangeWatchedFiles") 3937 (lsp--cleanup-hanging-watches))) 3938 3939 (defun lsp--server-capabilities () 3940 "Return the capabilities of the language server associated with the buffer." 3941 (->> (lsp-workspaces) 3942 (-keep #'lsp--workspace-server-capabilities) 3943 (apply #'lsp-merge))) 3944 3945 (defun lsp--send-open-close-p () 3946 "Return whether open and close notifications should be sent to the server." 3947 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3948 (or (memq sync '(1 2)) 3949 (lsp:text-document-sync-options-open-close? sync)))) 3950 3951 (defun lsp--send-will-save-p () 3952 "Return whether willSave notifications should be sent to the server." 3953 (-> (lsp--server-capabilities) 3954 (lsp:server-capabilities-text-document-sync?) 3955 (lsp:text-document-sync-options-will-save?))) 3956 3957 (defun lsp--send-will-save-wait-until-p () 3958 "Return whether willSaveWaitUntil notifications should be sent to the server." 3959 (-> (lsp--server-capabilities) 3960 (lsp:server-capabilities-text-document-sync?) 3961 (lsp:text-document-sync-options-will-save-wait-until?))) 3962 3963 (defun lsp--send-did-save-p () 3964 "Return whether didSave notifications should be sent to the server." 3965 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3966 (or (memq sync '(1 2)) 3967 (lsp:text-document-sync-options-save? sync)))) 3968 3969 (defun lsp--save-include-text-p () 3970 "Return whether save notifications should include the text document's contents." 3971 (->> (lsp--server-capabilities) 3972 (lsp:server-capabilities-text-document-sync?) 3973 (lsp:text-document-sync-options-save?) 3974 (lsp:text-document-save-registration-options-include-text?))) 3975 3976 (defun lsp--send-will-rename-files-p (path) 3977 "Return whether willRenameFiles request should be sent to the server. 3978 If any filters, checks if it applies for PATH." 3979 (let* ((will-rename (-> (lsp--server-capabilities) 3980 (lsp:server-capabilities-workspace?) 3981 (lsp:workspace-server-capabilities-file-operations?) 3982 (lsp:workspace-file-operations-will-rename?))) 3983 (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list))) 3984 (and will-rename 3985 (or (seq-empty-p filters) 3986 (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob))) 3987 (-let [regexes (lsp-glob-to-regexps glob)] 3988 (and (or (not scheme?) 3989 (string-prefix-p scheme? (lsp--path-to-uri path))) 3990 (-any? (lambda (re) 3991 (string-match re path)) 3992 regexes)))) 3993 filters))))) 3994 3995 (defun lsp--send-did-rename-files-p () 3996 "Return whether didRenameFiles notification should be sent to the server." 3997 (-> (lsp--server-capabilities) 3998 (lsp:server-capabilities-workspace?) 3999 (lsp:workspace-server-capabilities-file-operations?) 4000 (lsp:workspace-file-operations-did-rename?))) 4001 4002 (declare-function project-roots "ext:project" (project) t) 4003 (declare-function project-root "ext:project" (project) t) 4004 4005 (defun lsp--suggest-project-root () 4006 "Get project root." 4007 (or 4008 (when (fboundp 'projectile-project-root) 4009 (condition-case nil 4010 (projectile-project-root) 4011 (error nil))) 4012 (when (fboundp 'project-current) 4013 (when-let* ((project (project-current))) 4014 (if (fboundp 'project-root) 4015 (project-root project) 4016 (car (with-no-warnings 4017 (project-roots project)))))) 4018 default-directory)) 4019 4020 (defun lsp--read-from-file (file) 4021 "Read FILE content." 4022 (when (file-exists-p file) 4023 (cl-first (read-from-string (f-read-text file 'utf-8))))) 4024 4025 (defun lsp--persist (file-name to-persist) 4026 "Persist TO-PERSIST in FILE-NAME. 4027 4028 This function creates the parent directories if they don't exist 4029 yet." 4030 (let ((print-length nil) 4031 (print-level nil)) 4032 ;; Create all parent directories: 4033 (make-directory (f-parent file-name) t) 4034 (f-write-text (prin1-to-string to-persist) 'utf-8 file-name))) 4035 4036 (defun lsp-workspace-folders-add (project-root) 4037 "Add PROJECT-ROOT to the list of workspace folders." 4038 (interactive 4039 (list (read-directory-name "Select folder to add: " 4040 (or (lsp--suggest-project-root) default-directory) nil t))) 4041 (cl-pushnew (lsp-f-canonical project-root) 4042 (lsp-session-folders (lsp-session)) :test 'equal) 4043 (lsp--persist-session (lsp-session)) 4044 4045 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)) 4046 4047 (defun lsp-workspace-folders-remove (project-root) 4048 "Remove PROJECT-ROOT from the list of workspace folders." 4049 (interactive (list (completing-read "Select folder to remove: " 4050 (lsp-session-folders (lsp-session)) 4051 nil t nil nil 4052 (lsp-find-session-folder (lsp-session) default-directory)))) 4053 4054 (setq project-root (lsp-f-canonical project-root)) 4055 4056 ;; send remove folder to each multiroot workspace associated with the folder 4057 (dolist (wks (->> (lsp-session) 4058 (lsp-session-folder->servers) 4059 (gethash project-root) 4060 (--filter (lsp--client-multi-root (lsp--workspace-client it))))) 4061 (with-lsp-workspace wks 4062 (lsp-notify "workspace/didChangeWorkspaceFolders" 4063 (lsp-make-did-change-workspace-folders-params 4064 :event (lsp-make-workspace-folders-change-event 4065 :removed (vector (lsp-make-workspace-folder 4066 :uri (lsp--path-to-uri project-root) 4067 :name (f-filename project-root))) 4068 :added []))))) 4069 4070 ;; turn off servers in the removed directory 4071 (let* ((session (lsp-session)) 4072 (folder->servers (lsp-session-folder->servers session)) 4073 (server-id->folders (lsp-session-server-id->folders session)) 4074 (workspaces (gethash project-root folder->servers))) 4075 4076 (remhash project-root folder->servers) 4077 4078 ;; turn off the servers without root folders 4079 (dolist (workspace workspaces) 4080 (when (--none? (-contains? it workspace) (ht-values folder->servers)) 4081 (lsp--info "Shutdown %s since folder %s is removed..." 4082 (lsp--workspace-print workspace) project-root) 4083 (with-lsp-workspace workspace (lsp--shutdown-workspace)))) 4084 4085 (setf (lsp-session-folders session) 4086 (-remove-item project-root (lsp-session-folders session))) 4087 4088 (ht-aeach (puthash key 4089 (-remove-item project-root value) 4090 server-id->folders) 4091 server-id->folders) 4092 (lsp--persist-session (lsp-session))) 4093 4094 (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root))) 4095 4096 (defun lsp-workspace-blocklist-remove (project-root) 4097 "Remove PROJECT-ROOT from the workspace blocklist." 4098 (interactive (list (completing-read "Select folder to remove:" 4099 (lsp-session-folders-blocklist (lsp-session)) 4100 nil t))) 4101 (setf (lsp-session-folders-blocklist (lsp-session)) 4102 (delete project-root 4103 (lsp-session-folders-blocklist (lsp-session)))) 4104 (lsp--persist-session (lsp-session))) 4105 4106 (define-obsolete-function-alias 'lsp-workspace-folders-switch 4107 'lsp-workspace-folders-open "lsp-mode 6.1") 4108 4109 (defun lsp-workspace-folders-open (project-root) 4110 "Open the directory located at PROJECT-ROOT" 4111 (interactive (list (completing-read "Open folder: " 4112 (lsp-session-folders (lsp-session)) 4113 nil t))) 4114 (find-file project-root)) 4115 4116 (defun lsp--maybe-enable-signature-help (trigger-characters) 4117 (let ((ch last-command-event)) 4118 (when (cl-find ch trigger-characters :key #'string-to-char) 4119 (lsp-signature-activate)))) 4120 4121 (defun lsp--on-type-formatting-handler-create () 4122 (when-let* ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" ))) 4123 (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character? 4124 :first-trigger-character) provider] 4125 (lambda () 4126 (lsp--on-type-formatting first-trigger-character 4127 more-trigger-character?))))) 4128 4129 (defun lsp--update-on-type-formatting-hook (&optional cleanup?) 4130 (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create))) 4131 (cond 4132 ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?)) 4133 (add-hook 'post-self-insert-hook on-type-formatting-handler nil t)) 4134 ((or cleanup? 4135 (not lsp-enable-on-type-formatting)) 4136 (remove-hook 'post-self-insert-hook on-type-formatting-handler t))))) 4137 4138 (defun lsp--signature-help-handler-create () 4139 (-when-let ((&SignatureHelpOptions? :trigger-characters?) 4140 (lsp--capability-for-method "textDocument/signatureHelp")) 4141 (lambda () 4142 (lsp--maybe-enable-signature-help trigger-characters?)))) 4143 4144 (defun lsp--update-signature-help-hook (&optional cleanup?) 4145 (let ((signature-help-handler (lsp--signature-help-handler-create))) 4146 (cond 4147 ((and (or (equal lsp-signature-auto-activate t) 4148 (memq :on-trigger-char lsp-signature-auto-activate)) 4149 signature-help-handler 4150 (not cleanup?)) 4151 (add-hook 'post-self-insert-hook signature-help-handler nil t)) 4152 4153 ((or cleanup? 4154 (not (or (equal lsp-signature-auto-activate t) 4155 (memq :on-trigger-char lsp-signature-auto-activate)))) 4156 (remove-hook 'post-self-insert-hook signature-help-handler t))))) 4157 4158 (defun lsp--after-set-visited-file-name () 4159 (lsp-disconnect) 4160 (lsp)) 4161 4162 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27 4163 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099 4164 (defvar eldoc-documentation-default) ; CI 4165 (when (< emacs-major-version 28) 4166 (unless (boundp 'eldoc-documentation-functions) 4167 (load "eldoc" nil 'nomessage)) 4168 (when (memq (default-value 'eldoc-documentation-function) '(nil ignore)) 4169 ;; actually `eldoc-documentation-strategy', but CI was failing 4170 (setq-default eldoc-documentation-function 'eldoc-documentation-default))) 4171 4172 (define-minor-mode lsp-managed-mode 4173 "Mode for source buffers managed by lsp-mode." 4174 :lighter nil 4175 (cond 4176 (lsp-managed-mode 4177 (when (lsp-feature? "textDocument/hover") 4178 (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t) 4179 (eldoc-mode 1)) 4180 4181 (add-hook 'after-change-functions #'lsp-on-change nil t) 4182 (add-hook 'after-revert-hook #'lsp-on-revert nil t) 4183 (add-hook 'after-save-hook #'lsp-on-save nil t) 4184 (add-hook 'auto-save-hook #'lsp--on-auto-save nil t) 4185 (add-hook 'before-change-functions #'lsp-before-change nil t) 4186 (add-hook 'before-save-hook #'lsp--before-save nil t) 4187 (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t) 4188 (add-hook 'post-command-hook #'lsp--post-command nil t) 4189 4190 (lsp--update-on-type-formatting-hook) 4191 (lsp--update-signature-help-hook) 4192 4193 (when lsp-enable-xref 4194 (add-hook 'xref-backend-functions #'lsp--xref-backend nil t)) 4195 4196 (lsp-configure-buffer) 4197 4198 ;; make sure we turn off lsp-mode in case major mode changes, because major 4199 ;; mode change will wipe the buffer locals. 4200 (add-hook 'change-major-mode-hook #'lsp-disconnect nil t) 4201 (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t) 4202 4203 (let ((buffer (lsp-current-buffer))) 4204 (run-with-idle-timer 4205 0.0 nil 4206 (lambda () 4207 (when (lsp-buffer-live-p buffer) 4208 (lsp-with-current-buffer buffer 4209 (lsp--on-change-debounce buffer) 4210 (lsp--on-idle buffer))))))) 4211 (t 4212 (lsp-unconfig-buffer) 4213 4214 (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t) 4215 (remove-hook 'post-command-hook #'lsp--post-command t) 4216 (remove-hook 'after-change-functions #'lsp-on-change t) 4217 (remove-hook 'after-revert-hook #'lsp-on-revert t) 4218 (remove-hook 'after-save-hook #'lsp-on-save t) 4219 (remove-hook 'auto-save-hook #'lsp--on-auto-save t) 4220 (remove-hook 'before-change-functions #'lsp-before-change t) 4221 (remove-hook 'before-save-hook #'lsp--before-save t) 4222 (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t) 4223 4224 (lsp--update-on-type-formatting-hook :cleanup) 4225 (lsp--update-signature-help-hook :cleanup) 4226 4227 (when lsp--on-idle-timer 4228 (cancel-timer lsp--on-idle-timer) 4229 (setq lsp--on-idle-timer nil)) 4230 4231 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4232 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4233 4234 (lsp--remove-overlays 'lsp-highlight) 4235 (lsp--remove-overlays 'lsp-links) 4236 4237 (remove-hook 'xref-backend-functions #'lsp--xref-backend t) 4238 (remove-hook 'change-major-mode-hook #'lsp-disconnect t) 4239 (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t) 4240 (setq-local lsp-buffer-uri nil)))) 4241 4242 (defun lsp-configure-buffer () 4243 "Configure LSP features for current buffer." 4244 ;; make sure the core is running in the context of all available workspaces 4245 ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context 4246 (let ((lsp--buffer-workspaces (cond 4247 (lsp--buffer-workspaces) 4248 (lsp--cur-workspace (list lsp--cur-workspace)))) 4249 lsp--cur-workspace) 4250 (when lsp-auto-configure 4251 (lsp--auto-configure) 4252 4253 (when (and lsp-enable-text-document-color 4254 (lsp-feature? "textDocument/documentColor")) 4255 (add-hook 'lsp-on-change-hook #'lsp--document-color nil t)) 4256 4257 (when (and lsp-enable-imenu 4258 (lsp-feature? "textDocument/documentSymbol")) 4259 (lsp-enable-imenu)) 4260 4261 (when (and lsp-enable-indentation 4262 (lsp-feature? "textDocument/rangeFormatting")) 4263 (add-function :override (local 'indent-region-function) #'lsp-format-region)) 4264 4265 (when (and lsp-enable-symbol-highlighting 4266 (lsp-feature? "textDocument/documentHighlight")) 4267 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)) 4268 4269 (when (and lsp-enable-links 4270 (lsp-feature? "textDocument/documentLink")) 4271 (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t)) 4272 4273 (when (and lsp-inlay-hint-enable 4274 (lsp-feature? "textDocument/inlayHint")) 4275 (lsp-inlay-hints-mode)) 4276 4277 (when (and lsp-enable-dap-auto-configure 4278 (functionp 'dap-mode)) 4279 (dap-auto-configure-mode 1))) 4280 (run-hooks 'lsp-configure-hook))) 4281 4282 (defun lsp-unconfig-buffer () 4283 "Unconfigure LSP features for buffer." 4284 (lsp--remove-overlays 'lsp-color) 4285 4286 (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function) 4287 (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index) 4288 (setq-local imenu-menubar-modified-tick 0) 4289 (setq-local imenu--index-alist nil) 4290 (imenu--cleanup)) 4291 4292 (remove-function (local 'indent-region-function) #'lsp-format-region) 4293 4294 (remove-hook 'lsp-on-change-hook #'lsp--document-color t) 4295 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4296 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4297 4298 (when (and lsp-enable-dap-auto-configure 4299 (functionp 'dap-mode)) 4300 (dap-auto-configure-mode -1)) 4301 4302 (run-hooks 'lsp-unconfigure-hook)) 4303 4304 (defun lsp--buffer-content () 4305 (lsp-save-restriction-and-excursion 4306 (or (lsp-virtual-buffer-call :buffer-string) 4307 (buffer-substring-no-properties (point-min) 4308 (point-max))))) 4309 4310 (defun lsp--text-document-did-open () 4311 "`document/didOpen' event." 4312 (run-hooks 'lsp-before-open-hook) 4313 (when (and lsp-auto-touch-files 4314 (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri))))) 4315 (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri)) 4316 (save-buffer)) 4317 4318 (setq lsp--cur-version (or lsp--cur-version 0)) 4319 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 4320 (lsp-notify 4321 "textDocument/didOpen" 4322 (list :textDocument 4323 (list :uri (lsp--buffer-uri) 4324 :languageId (lsp-buffer-language) 4325 :version lsp--cur-version 4326 :text (lsp--buffer-content)))) 4327 4328 (lsp-managed-mode 1) 4329 4330 (lsp-diagnostics--request-pull-diagnostics lsp--cur-workspace) 4331 4332 (run-hooks 'lsp-after-open-hook) 4333 (when-let* ((client (-some-> lsp--cur-workspace (lsp--workspace-client)))) 4334 (-some-> (lsp--client-after-open-fn client) 4335 (funcall)) 4336 (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client)) 4337 (intern-soft) 4338 (run-hooks)))) 4339 4340 (defun lsp--text-document-identifier () 4341 "Make TextDocumentIdentifier." 4342 (list :uri (lsp--buffer-uri))) 4343 4344 (defun lsp--versioned-text-document-identifier () 4345 "Make VersionedTextDocumentIdentifier." 4346 (plist-put (lsp--text-document-identifier) :version lsp--cur-version)) 4347 4348 (defun lsp--cur-line (&optional point) 4349 (1- (line-number-at-pos point))) 4350 4351 (defun lsp--cur-position () 4352 "Make a Position object for the current point." 4353 (or (lsp-virtual-buffer-call :cur-position) 4354 (lsp-save-restriction-and-excursion 4355 (list :line (lsp--cur-line) 4356 :character (- (point) (line-beginning-position)))))) 4357 4358 (defun lsp--point-to-position (point) 4359 "Convert POINT to Position." 4360 (lsp-save-restriction-and-excursion 4361 (goto-char point) 4362 (lsp--cur-position))) 4363 4364 (defun lsp--range (start end) 4365 "Make Range body from START and END." 4366 ;; make sure start and end are Position objects 4367 (list :start start :end end)) 4368 4369 (defun lsp--region-to-range (start end) 4370 "Make Range object for the current region." 4371 (lsp--range (lsp--point-to-position start) 4372 (lsp--point-to-position end))) 4373 4374 (defun lsp--region-or-line () 4375 "The active region or the current line." 4376 (if (use-region-p) 4377 (lsp--region-to-range (region-beginning) (region-end)) 4378 (lsp--region-to-range (line-beginning-position) (line-end-position)))) 4379 4380 (defun lsp--check-document-changes-version (document-changes) 4381 "Verify that DOCUMENT-CHANGES have the proper version." 4382 (unless (seq-every-p 4383 (-lambda ((&TextDocumentEdit :text-document)) 4384 (or 4385 (not text-document) 4386 (let* ((filename (-> text-document 4387 lsp:versioned-text-document-identifier-uri 4388 lsp--uri-to-path)) 4389 (version (lsp:versioned-text-document-identifier-version? text-document))) 4390 (with-current-buffer (find-file-noselect filename) 4391 (or (null version) (zerop version) (= -1 version) 4392 (equal version lsp--cur-version)))))) 4393 document-changes) 4394 (error "Document changes cannot be applied due to different document version"))) 4395 4396 (defun lsp--apply-workspace-edit (workspace-edit &optional operation) 4397 "Apply the WorkspaceEdit object WORKSPACE-EDIT. 4398 OPERATION is symbol representing the source of this text edit." 4399 (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit)) 4400 (if-let* ((document-changes (seq-reverse document-changes?))) 4401 (progn 4402 (lsp--check-document-changes-version document-changes) 4403 (->> document-changes 4404 (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create"))) 4405 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4406 (->> document-changes 4407 (seq-filter (-lambda ((&CreateFile :kind)) 4408 (and (or (not kind) (equal kind "edit")) 4409 (not (equal kind "create"))))) 4410 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4411 (->> document-changes 4412 (seq-filter (-lambda ((&CreateFile :kind)) 4413 (and (not (or (not kind) (equal kind "edit"))) 4414 (not (equal kind "create"))))) 4415 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))) 4416 (lsp-map 4417 (lambda (uri text-edits) 4418 (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect) 4419 (lsp--apply-text-edits text-edits operation))) 4420 changes?)))) 4421 4422 (defmacro lsp-with-filename (file &rest body) 4423 "Execute BODY with FILE as a context. 4424 Need to handle the case when FILE indicates virtual buffer." 4425 (declare (indent 1) (debug t)) 4426 `(if-let* ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file))) 4427 (lsp-with-current-buffer lsp--virtual-buffer 4428 ,@body) 4429 ,@body)) 4430 4431 (defun lsp--apply-text-document-edit (edit &optional operation) 4432 "Apply the TextDocumentEdit object EDIT. 4433 OPERATION is symbol representing the source of this text edit. 4434 If the file is not being visited by any buffer, it is opened with 4435 `find-file-noselect'. 4436 Because lsp-mode does not store previous document versions, the edit is only 4437 applied if the version of the textDocument matches the version of the 4438 corresponding file. 4439 4440 interface TextDocumentEdit { 4441 textDocument: VersionedTextDocumentIdentifier; 4442 edits: TextEdit[]; 4443 }" 4444 (pcase (lsp:edit-kind edit) 4445 ("create" (-let* (((&CreateFile :uri :options?) edit) 4446 (file-name (lsp--uri-to-path uri))) 4447 (mkdir (f-dirname file-name) t) 4448 (f-touch file-name) 4449 (when (lsp:create-file-options-overwrite? options?) 4450 (f-write-text "" nil file-name)) 4451 (find-file-noselect file-name))) 4452 ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit)) 4453 (f-delete (lsp--uri-to-path uri) recursive?))) 4454 ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit) 4455 (old-file-name (lsp--uri-to-path old-uri)) 4456 (new-file-name (lsp--uri-to-path new-uri)) 4457 (buf (find-buffer-visiting old-file-name))) 4458 (when buf 4459 (lsp-with-current-buffer buf 4460 (save-buffer) 4461 (lsp--text-document-did-close))) 4462 (mkdir (f-dirname new-file-name) t) 4463 (rename-file old-file-name new-file-name overwrite?) 4464 (when buf 4465 (lsp-with-current-buffer buf 4466 (set-buffer-modified-p nil) 4467 (setq lsp-buffer-uri nil) 4468 (set-visited-file-name new-file-name) 4469 (lsp))))) 4470 (_ (let ((file-name (->> edit 4471 (lsp:text-document-edit-text-document) 4472 (lsp:versioned-text-document-identifier-uri) 4473 (lsp--uri-to-path)))) 4474 (lsp-with-current-buffer (find-buffer-visiting file-name) 4475 (lsp-with-filename file-name 4476 (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation))))))) 4477 4478 (lsp-defun lsp--position-compare ((&Position :line left-line 4479 :character left-character) 4480 (&Position :line right-line 4481 :character right-character)) 4482 "Return t if position LEFT is greater than RIGHT." 4483 (if (= left-line right-line) 4484 (> left-character right-character) 4485 (> left-line right-line))) 4486 4487 (lsp-defun lsp-point-in-range? (position (&Range :start :end)) 4488 "Returns if POINT is in RANGE." 4489 (not (or (lsp--position-compare start position) 4490 (lsp--position-compare position end)))) 4491 4492 (lsp-defun lsp--position-equal ((&Position :line left-line 4493 :character left-character) 4494 (&Position :line right-line 4495 :character right-character)) 4496 "Return whether LEFT and RIGHT positions are equal." 4497 (and (= left-line right-line) 4498 (= left-character right-character))) 4499 4500 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end)) 4501 (&TextEdit :range (&Range :start right-start :end right-end))) 4502 (if (lsp--position-equal left-start right-start) 4503 (lsp--position-compare left-end right-end) 4504 (lsp--position-compare left-start right-start))) 4505 4506 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text)) 4507 "Apply the edits described in the TextEdit object in TEXT-EDIT." 4508 (setq new-text (s-replace "\r" "" (or new-text ""))) 4509 (lsp:set-text-edit-new-text edit new-text) 4510 (goto-char start) 4511 (delete-region start end) 4512 (insert new-text)) 4513 4514 ;; WORKAROUND: typescript-language might send -1 when applying code actions. 4515 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582 4516 (lsp-defun lsp--fix-point ((point &as &Position :character :line)) 4517 (-doto point 4518 (lsp:set-position-line (max 0 line)) 4519 (lsp:set-position-character (max 0 character)))) 4520 4521 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as 4522 &TextEdit 4523 :range (&Range :start :end) 4524 :new-text)) 4525 "Apply the edits described in the TextEdit object in TEXT-EDIT. 4526 The method uses `replace-buffer-contents'." 4527 (setq new-text (s-replace "\r" "" (or new-text ""))) 4528 (lsp:set-text-edit-new-text edit new-text) 4529 (-let* ((source (current-buffer)) 4530 ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start) 4531 :end (lsp--fix-point end))))) 4532 (with-temp-buffer 4533 (insert new-text) 4534 (let ((temp (current-buffer))) 4535 (with-current-buffer source 4536 (save-excursion 4537 (save-restriction 4538 (narrow-to-region beg end) 4539 4540 ;; On emacs versions < 26.2, 4541 ;; `replace-buffer-contents' is buggy - it calls 4542 ;; change functions with invalid arguments - so we 4543 ;; manually call the change functions here. 4544 ;; 4545 ;; See emacs bugs #32237, #32278: 4546 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237 4547 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278 4548 (let ((inhibit-modification-hooks t) 4549 (length (- end beg))) 4550 (run-hook-with-args 'before-change-functions 4551 beg end) 4552 (replace-buffer-contents temp) 4553 (run-hook-with-args 'after-change-functions 4554 beg (+ beg (length new-text)) 4555 length))))))))) 4556 4557 (defun lsp--to-yasnippet-snippet (snippet) 4558 "Convert LSP SNIPPET to yasnippet snippet." 4559 ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it. 4560 (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`"))) 4561 (rx "\\" (backref 1)) 4562 snippet 4563 nil nil 1)) 4564 4565 (defvar-local lsp-enable-relative-indentation nil 4566 "Enable relative indentation when insert texts, snippets ... 4567 from language server.") 4568 4569 (defun lsp--expand-snippet (snippet &optional start end expand-env) 4570 "Wrapper of `yas-expand-snippet' with all of it arguments. 4571 The snippet will be convert to LSP style and indent according to 4572 LSP server result." 4573 (require 'yasnippet nil t) 4574 (let* ((inhibit-field-text-motion t) 4575 (yas-wrap-around-region nil) 4576 (yas-indent-line 'none) 4577 (yas-also-auto-indent-first-line nil)) 4578 (yas-expand-snippet 4579 (lsp--to-yasnippet-snippet snippet) 4580 start end expand-env))) 4581 4582 (defun lsp--indent-lines (start end &optional insert-text-mode?) 4583 "Indent from START to END based on INSERT-TEXT-MODE? value. 4584 - When INSERT-TEXT-MODE? is provided 4585 - if it's `lsp/insert-text-mode-as-it', do no editor indentation. 4586 - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading 4587 whitespaces to match the line where text is inserted. 4588 - When it's not provided, using `indent-line-function' for each line." 4589 (save-excursion 4590 (goto-char end) 4591 (let* ((end-line (line-number-at-pos)) 4592 (offset (save-excursion 4593 (goto-char start) 4594 (current-indentation))) 4595 (indent-line-function 4596 (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it) 4597 #'ignore) 4598 ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation) 4599 lsp-enable-relative-indentation 4600 ;; Indenting snippets is extremely slow in `org-mode' buffers 4601 ;; since it has to calculate indentation based on SRC block 4602 ;; position. Thus we use relative indentation as default. 4603 (derived-mode-p 'org-mode)) 4604 (lambda () (save-excursion 4605 (beginning-of-line) 4606 (indent-to-column offset)))) 4607 (t indent-line-function)))) 4608 (goto-char start) 4609 (forward-line) 4610 (while (and (not (eobp)) 4611 (<= (line-number-at-pos) end-line)) 4612 (funcall indent-line-function) 4613 (forward-line))))) 4614 4615 (defun lsp--apply-text-edits (edits &optional operation) 4616 "Apply the EDITS described in the TextEdit[] object. 4617 OPERATION is symbol representing the source of this text edit." 4618 (unless (seq-empty-p edits) 4619 (atomic-change-group 4620 (run-hooks 'lsp-before-apply-edits-hook) 4621 (let* ((change-group (prepare-change-group)) 4622 (howmany (length edits)) 4623 (message (format "Applying %s edits to `%s' ..." howmany (current-buffer))) 4624 (_ (lsp--info message)) 4625 (reporter (make-progress-reporter message 0 howmany)) 4626 (done 0) 4627 (apply-edit (if (not lsp--virtual-buffer) 4628 #'lsp--apply-text-edit-replace-buffer-contents 4629 #'lsp--apply-text-edit))) 4630 (unwind-protect 4631 (->> edits 4632 ;; We sort text edits so as to apply edits that modify latter 4633 ;; parts of the document first. Furthermore, because the LSP 4634 ;; spec dictates that: "If multiple inserts have the same 4635 ;; position, the order in the array defines which edit to 4636 ;; apply first." We reverse the initial list and sort stably 4637 ;; to make sure the order among edits with the same position 4638 ;; is preserved. 4639 (nreverse) 4640 (seq-sort #'lsp--text-edit-sort-predicate) 4641 (mapc (lambda (edit) 4642 (progress-reporter-update reporter (cl-incf done)) 4643 (funcall apply-edit edit) 4644 (when (lsp:snippet-text-edit-insert-text-format? edit) 4645 (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start) 4646 :insert-text-format? :new-text) edit) 4647 (when (eq insert-text-format? lsp/insert-text-format-snippet) 4648 ;; No `save-excursion' needed since expand snippet will change point anyway 4649 (goto-char (+ start (length new-text))) 4650 (lsp--indent-lines start (point)) 4651 (lsp--expand-snippet new-text start (point))))) 4652 (run-hook-with-args 'lsp-after-apply-edits-hook operation)))) 4653 (undo-amalgamate-change-group change-group) 4654 (progress-reporter-done reporter)))))) 4655 4656 (defun lsp--create-apply-text-edits-handlers () 4657 "Create (handler cleanup-fn) for applying text edits in async request. 4658 Only works when mode is `tick or `alive." 4659 (let* (first-edited 4660 (func (lambda (start &rest _) 4661 (setq first-edited (if first-edited 4662 (min start first-edited) 4663 start))))) 4664 (add-hook 'before-change-functions func nil t) 4665 (list 4666 (lambda (edits) 4667 (if (and first-edited 4668 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end))) 4669 ;; Text edit region is overlapped 4670 (> end first-edited)) 4671 edits)) 4672 (lsp--warn "TextEdits will not be applied since document has been modified before of them.") 4673 (lsp--apply-text-edits edits 'completion-cleanup))) 4674 (lambda () 4675 (remove-hook 'before-change-functions func t))))) 4676 4677 (defun lsp--capability (cap &optional capabilities) 4678 "Get the value of capability CAP. If CAPABILITIES is non-nil, use them instead." 4679 (when (stringp cap) 4680 (setq cap (intern (concat ":" cap)))) 4681 4682 (lsp-get (or capabilities 4683 (lsp--server-capabilities)) 4684 cap)) 4685 4686 (defun lsp--registered-capability (method) 4687 "Check whether there is workspace providing METHOD." 4688 (->> (lsp-workspaces) 4689 (--keep (seq-find (lambda (reg) 4690 (equal (lsp--registered-capability-method reg) method)) 4691 (lsp--workspace-registered-server-capabilities it))) 4692 cl-first)) 4693 4694 (defun lsp--capability-for-method (method) 4695 "Get the value of capability for METHOD." 4696 (-let* ((reqs (cdr (assoc method lsp-method-requirements))) 4697 ((&plist :capability) reqs)) 4698 (or (and capability (lsp--capability capability)) 4699 (-some-> (lsp--registered-capability method) 4700 (lsp--registered-capability-options))))) 4701 4702 (defvar-local lsp--before-change-vals nil 4703 "Store the positions from the `lsp-before-change' function call, for 4704 validation and use in the `lsp-on-change' function.") 4705 4706 (defun lsp--text-document-content-change-event (start end length) 4707 "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH." 4708 ;; So (47 54 0) means add 7 chars starting at pos 47 4709 ;; must become 4710 ;; {"range":{"start":{"line":5,"character":6} 4711 ;; ,"end" :{"line":5,"character":6}} 4712 ;; ,"rangeLength":0 4713 ;; ,"text":"\nbb = 5"} 4714 ;; 4715 ;; And (47 47 7) means delete 7 chars starting at pos 47 4716 ;; must become 4717 ;; {"range":{"start":{"line":6,"character":0} 4718 ;; ,"end" :{"line":7,"character":0}} 4719 ;; ,"rangeLength":7 4720 ;; ,"text":""} 4721 ;; 4722 ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with 4723 ;; 13 chars. So it must become 4724 ;; {"range":{"start":{"line":5,"character":8} 4725 ;; ,"end" :{"line":5,"character":11}} 4726 ;; ,"rangeLength":3 4727 ;; ,"text":"new-chars-xxx"} 4728 ;; 4729 4730 ;; Adding text: 4731 ;; lsp-before-change:(start,end)=(33,33) 4732 ;; lsp-on-change:(start,end,length)=(33,34,0) 4733 ;; 4734 ;; Changing text: 4735 ;; lsp-before-change:(start,end)=(208,211) 4736 ;; lsp-on-change:(start,end,length)=(208,221,3) 4737 ;; 4738 ;; Deleting text: 4739 ;; lsp-before-change:(start,end)=(19,27) 4740 ;; lsp-on-change:(start,end,length)=(19,19,8) 4741 (if (zerop length) 4742 ;; Adding something only, work from start only 4743 `( :range ,(lsp--range 4744 (lsp--point-to-position start) 4745 (lsp--point-to-position start)) 4746 :rangeLength 0 4747 :text ,(buffer-substring-no-properties start end)) 4748 4749 (if (eq start end) 4750 ;; Deleting something only 4751 (if (lsp--bracketed-change-p start length) 4752 ;; The before-change value is bracketed, use it 4753 `( :range ,(lsp--range 4754 (lsp--point-to-position start) 4755 (plist-get lsp--before-change-vals :end-pos)) 4756 :rangeLength ,length 4757 :text "") 4758 ;; If the change is not bracketed, send a full change event instead. 4759 (lsp--full-change-event)) 4760 4761 ;; Deleting some things, adding others 4762 (if (lsp--bracketed-change-p start length) 4763 ;; The before-change value is valid, use it 4764 `( :range ,(lsp--range 4765 (lsp--point-to-position start) 4766 (plist-get lsp--before-change-vals :end-pos)) 4767 :rangeLength ,length 4768 :text ,(buffer-substring-no-properties start end)) 4769 (lsp--full-change-event))))) 4770 4771 (defun lsp--bracketed-change-p (start length) 4772 "If the before and after positions are the same, and the length 4773 is the size of the start range, we are probably good." 4774 (-let [(&plist :end before-end :start before-start) lsp--before-change-vals] 4775 (and (eq start before-start) 4776 (eq length (- before-end before-start))))) 4777 4778 (defun lsp--full-change-event () 4779 `(:text ,(lsp--buffer-content))) 4780 4781 (defun lsp-before-change (start end) 4782 "Executed before a file is changed. 4783 Added to `before-change-functions'." 4784 ;; Note: 4785 ;; 4786 ;; This variable holds a list of functions to call when Emacs is about to 4787 ;; modify a buffer. Each function gets two arguments, the beginning and end of 4788 ;; the region that is about to change, represented as integers. The buffer 4789 ;; that is about to change is always the current buffer when the function is 4790 ;; called. 4791 ;; 4792 ;; WARNING: 4793 ;; 4794 ;; Do not expect the before-change hooks and the after-change hooks be called 4795 ;; in balanced pairs around each buffer change. Also don't expect the 4796 ;; before-change hooks to be called for every chunk of text Emacs is about to 4797 ;; delete. These hooks are provided on the assumption that Lisp programs will 4798 ;; use either before- or the after-change hooks, but not both, and the 4799 ;; boundaries of the region where the changes happen might include more than 4800 ;; just the actual changed text, or even lump together several changes done 4801 ;; piecemeal. 4802 (save-match-data 4803 (lsp-save-restriction-and-excursion 4804 (setq lsp--before-change-vals 4805 (list :start start 4806 :end end 4807 :end-pos (lsp--point-to-position end)))))) 4808 4809 (defun lsp--flush-delayed-changes () 4810 (let ((inhibit-quit t)) 4811 (when lsp--delay-timer 4812 (cancel-timer lsp--delay-timer)) 4813 (mapc (-lambda ((workspace buffer document change)) 4814 (with-current-buffer buffer 4815 (with-lsp-workspace workspace 4816 (lsp-notify "textDocument/didChange" 4817 (list :textDocument document 4818 :contentChanges (vector change)))))) 4819 (prog1 (nreverse lsp--delayed-requests) 4820 (setq lsp--delayed-requests nil))))) 4821 4822 (defun lsp--workspace-sync-method (workspace) 4823 (let ((sync (-> workspace 4824 (lsp--workspace-server-capabilities) 4825 (lsp:server-capabilities-text-document-sync?)))) 4826 (if (lsp-text-document-sync-options? sync) 4827 (lsp:text-document-sync-options-change? sync) 4828 sync))) 4829 4830 (defun lsp-on-change (start end length &optional content-change-event-fn) 4831 "Executed when a file is changed. 4832 Added to `after-change-functions'." 4833 ;; Note: 4834 ;; 4835 ;; Each function receives three arguments: the beginning and end of the region 4836 ;; just changed, and the length of the text that existed before the change. 4837 ;; All three arguments are integers. The buffer that has been changed is 4838 ;; always the current buffer when the function is called. 4839 ;; 4840 ;; The length of the old text is the difference between the buffer positions 4841 ;; before and after that text as it was before the change. As for the 4842 ;; changed text, its length is simply the difference between the first two 4843 ;; arguments. 4844 ;; 4845 ;; So (47 54 0) means add 7 chars starting at pos 47 4846 ;; So (47 47 7) means delete 7 chars starting at pos 47 4847 (save-match-data 4848 (let ((inhibit-quit t) 4849 ;; make sure that `lsp-on-change' is called in multi-workspace context 4850 ;; see #2901 4851 lsp--cur-workspace) 4852 ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done 4853 ;; by auto-revert-mode) will cause this handler to get called with a nil 4854 ;; buffer-file-name. We need the buffer-file-name to send notifications; 4855 ;; so we skip handling revert-buffer-caused changes and instead handle 4856 ;; reverts separately in lsp-on-revert 4857 (when (not revert-buffer-in-progress-p) 4858 (cl-incf lsp--cur-version) 4859 (mapc 4860 (lambda (workspace) 4861 (pcase (or lsp-document-sync-method 4862 (lsp--workspace-sync-method workspace)) 4863 (1 4864 (if lsp-debounce-full-sync-notifications 4865 (setq lsp--delayed-requests 4866 (->> lsp--delayed-requests 4867 (-remove (-lambda ((_ buffer)) 4868 (equal (current-buffer) buffer))) 4869 (cons (list workspace 4870 (current-buffer) 4871 (lsp--versioned-text-document-identifier) 4872 (lsp--full-change-event))))) 4873 (with-lsp-workspace workspace 4874 (lsp-notify "textDocument/didChange" 4875 (list :contentChanges (vector (lsp--full-change-event)) 4876 :textDocument (lsp--versioned-text-document-identifier))) 4877 (lsp-diagnostics--request-pull-diagnostics workspace)))) 4878 (2 4879 (with-lsp-workspace workspace 4880 (lsp-notify 4881 "textDocument/didChange" 4882 (list :textDocument (lsp--versioned-text-document-identifier) 4883 :contentChanges (vector 4884 (if content-change-event-fn 4885 (funcall content-change-event-fn start end length) 4886 (lsp--text-document-content-change-event 4887 start end length))))) 4888 (lsp-diagnostics--request-pull-diagnostics workspace))))) 4889 (lsp-workspaces)) 4890 (when lsp--delay-timer (cancel-timer lsp--delay-timer)) 4891 (setq lsp--delay-timer (run-with-idle-timer 4892 lsp-debounce-full-sync-notifications-interval 4893 nil 4894 #'lsp--flush-delayed-changes)) 4895 ;; force cleanup overlays after each change 4896 (lsp--remove-overlays 'lsp-highlight) 4897 (lsp--after-change (current-buffer)))))) 4898 4899 4900 4901 ;; facilities for on change hooks. We do not want to make lsp calls on each 4902 ;; change event so we add debounce to avoid flooding the server with events. 4903 ;; Additionally, we want to have a mechanism for stopping the server calls in 4904 ;; particular cases like, e. g. when performing completion. 4905 4906 (defvar lsp-inhibit-lsp-hooks nil 4907 "Flag to control.") 4908 4909 (defcustom lsp-on-change-hook nil 4910 "Hooks to run when buffer has changed." 4911 :type 'hook 4912 :group 'lsp-mode) 4913 4914 (defcustom lsp-idle-delay 0.500 4915 "Debounce interval for `after-change-functions'." 4916 :type 'number 4917 :group 'lsp-mode) 4918 4919 (defcustom lsp-on-idle-hook nil 4920 "Hooks to run after `lsp-idle-delay'." 4921 :type 'hook 4922 :group 'lsp-mode) 4923 4924 (defun lsp--idle-reschedule (buffer) 4925 (when lsp--on-idle-timer 4926 (cancel-timer lsp--on-idle-timer)) 4927 4928 (setq lsp--on-idle-timer (run-with-idle-timer 4929 lsp-idle-delay 4930 nil 4931 #'lsp--on-idle 4932 buffer))) 4933 4934 (defun lsp--post-command () 4935 (lsp--cleanup-highlights-if-needed) 4936 (lsp--idle-reschedule (current-buffer))) 4937 4938 (defun lsp--on-idle (buffer) 4939 "Start post command loop." 4940 (when (and (buffer-live-p buffer) 4941 (equal buffer (current-buffer)) 4942 (not lsp-inhibit-lsp-hooks) 4943 lsp-managed-mode) 4944 (run-hooks 'lsp-on-idle-hook))) 4945 4946 (defun lsp--on-change-debounce (buffer) 4947 (when (and (buffer-live-p buffer) 4948 (equal buffer (current-buffer)) 4949 (not lsp-inhibit-lsp-hooks) 4950 lsp-managed-mode) 4951 (run-hooks 'lsp-on-change-hook))) 4952 4953 (defun lsp--after-change (buffer) 4954 "Called after most textDocument/didChange events." 4955 (setq lsp--signature-last-index nil 4956 lsp--signature-last nil) 4957 4958 ;; cleanup diagnostics 4959 (when lsp-diagnostic-clean-after-change 4960 (dolist (workspace (lsp-workspaces)) 4961 (-let [diagnostics (lsp--workspace-diagnostics workspace)] 4962 (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics)))) 4963 4964 (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled) 4965 (lsp--semantic-tokens-refresh-if-enabled buffer)) 4966 (when lsp--on-change-timer 4967 (cancel-timer lsp--on-change-timer)) 4968 (setq lsp--on-change-timer (run-with-idle-timer 4969 lsp-idle-delay 4970 nil 4971 #'lsp--on-change-debounce 4972 buffer)) 4973 (lsp--idle-reschedule buffer)) 4974 4975 4976 (defcustom lsp-trim-trailing-whitespace t 4977 "Trim trailing whitespace on a line." 4978 :group 'lsp-mode 4979 :type 'boolean) 4980 4981 (defcustom lsp-insert-final-newline t 4982 "Insert a newline character at the end of the file if one does not exist." 4983 :group 'lsp-mode 4984 :type 'boolean) 4985 4986 (defcustom lsp-trim-final-newlines t 4987 "Trim all newlines after the final newline at the end of the file." 4988 :group 'lsp-mode 4989 :type 'boolean) 4990 4991 4992 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters) 4993 "Self insert handling. 4994 Applies on type formatting." 4995 (let ((ch last-command-event)) 4996 (when (or (eq (string-to-char first-trigger-characters) ch) 4997 (cl-find ch more-trigger-characters :key #'string-to-char)) 4998 (lsp-request-async "textDocument/onTypeFormatting" 4999 (lsp-make-document-on-type-formatting-params 5000 :text-document (lsp--text-document-identifier) 5001 :options (lsp-make-formatting-options 5002 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 5003 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 5004 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 5005 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 5006 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)) 5007 :ch (char-to-string ch) 5008 :position (lsp--cur-position)) 5009 (lambda (data) (lsp--apply-text-edits data 'format)) 5010 :mode 'tick)))) 5011 5012 5013 ;; links 5014 (defun lsp--document-links () 5015 (when (lsp-feature? "textDocument/documentLink") 5016 (lsp-request-async 5017 "textDocument/documentLink" 5018 `(:textDocument ,(lsp--text-document-identifier)) 5019 (lambda (links) 5020 (lsp--remove-overlays 'lsp-link) 5021 (seq-do 5022 (-lambda ((link &as &DocumentLink :range (&Range :start :end))) 5023 (-doto (make-button (lsp--position-to-point start) 5024 (lsp--position-to-point end) 5025 'action (lsp--document-link-keymap link) 5026 'keymap (let ((map (make-sparse-keymap))) 5027 (define-key map [M-return] 'push-button) 5028 (define-key map [mouse-2] 'push-button) 5029 map) 5030 'help-echo "mouse-2, M-RET: Visit this link") 5031 (overlay-put 'lsp-link t))) 5032 links)) 5033 :mode 'unchanged))) 5034 5035 (defun lsp--document-link-handle-target (url) 5036 (let* ((parsed-url (url-generic-parse-url (url-unhex-string url))) 5037 (type (url-type parsed-url))) 5038 (pcase type 5039 ("file" 5040 (xref-push-marker-stack) 5041 (find-file (lsp--uri-to-path url)) 5042 (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url)) 5043 (goto-char (lsp--position-to-point 5044 (lsp-make-position :character (1- (string-to-number column)) 5045 :line (1- (string-to-number line))))))) 5046 ((or "http" "https") (browse-url url)) 5047 (type (if-let* ((handler (lsp--get-uri-handler type))) 5048 (funcall handler url) 5049 (signal 'lsp-file-scheme-not-supported (list url))))))) 5050 5051 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?)) 5052 (if target? 5053 (lambda (_) 5054 (interactive) 5055 (lsp--document-link-handle-target target?)) 5056 (lambda (_) 5057 (interactive) 5058 (when (lsp:document-link-registration-options-resolve-provider? 5059 (lsp--capability-for-method "textDocument/documentLink")) 5060 (lsp-request-async 5061 "documentLink/resolve" 5062 link 5063 (-lambda ((&DocumentLink :target?)) 5064 (lsp--document-link-handle-target target?))))))) 5065 5066 5067 5068 (defcustom lsp-warn-no-matched-clients t 5069 "Whether to show messages when there are no supported clients." 5070 :group 'lsp-mode 5071 :type 'boolean) 5072 5073 (defun lsp-buffer-language--configured-id () 5074 "Return nil when not registered." 5075 (->> lsp-language-id-configuration 5076 (-first 5077 (-lambda ((mode-or-pattern . language)) 5078 (cond 5079 ((and (stringp mode-or-pattern) 5080 (s-matches? mode-or-pattern (buffer-file-name))) 5081 language) 5082 ((eq mode-or-pattern major-mode) language)))) 5083 cl-rest)) 5084 5085 (defvar-local lsp--buffer-language nil 5086 "Locally cached returned value of `lsp-buffer-language'.") 5087 5088 (defun lsp-buffer-language () 5089 "Get language corresponding current buffer." 5090 (or lsp--buffer-language 5091 (let* ((configured-language (lsp-buffer-language--configured-id))) 5092 (setq lsp--buffer-language 5093 (or configured-language 5094 ;; ensure non-nil 5095 (string-remove-suffix "-mode" (symbol-name major-mode)))) 5096 (when (and lsp-warn-no-matched-clients 5097 (null configured-language)) 5098 (lsp-warn "Unable to calculate the languageId for buffer `%s'. \ 5099 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s" 5100 (buffer-name) 5101 major-mode)) 5102 lsp--buffer-language))) 5103 5104 (defun lsp-activate-on (&rest languages) 5105 "Returns language activation function. 5106 The function will return t when the `lsp-buffer-language' returns 5107 one of the LANGUAGES." 5108 (lambda (_file-name _mode) 5109 (-contains? languages (lsp-buffer-language)))) 5110 5111 (defun lsp-workspace-root (&optional path) 5112 "Find the workspace root for the current file or PATH." 5113 (-when-let* ((file-name (or path (buffer-file-name))) 5114 (file-name (lsp-f-canonical file-name))) 5115 (->> (lsp-session) 5116 (lsp-session-folders) 5117 (--filter (and (lsp--files-same-host it file-name) 5118 (or (lsp-f-ancestor-of? it file-name) 5119 (equal it file-name)))) 5120 (--max-by (> (length it) (length other)))))) 5121 5122 (defun lsp-on-revert () 5123 "Executed when a file is reverted. 5124 Added to `after-revert-hook'." 5125 (let ((n (buffer-size)) 5126 (revert-buffer-in-progress-p nil)) 5127 (lsp-on-change 0 n n))) 5128 5129 (defun lsp--text-document-did-close (&optional keep-workspace-alive) 5130 "Executed when the file is closed, added to `kill-buffer-hook'. 5131 5132 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace 5133 if it's closing the last buffer in the workspace." 5134 (lsp-foreach-workspace 5135 (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 5136 (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S" 5137 (lsp-notify "textDocument/didClose" 5138 `(:textDocument ,(lsp--text-document-identifier)))) 5139 (when (and (not lsp-keep-workspace-alive) 5140 (not keep-workspace-alive) 5141 (not (lsp--workspace-buffers lsp--cur-workspace))) 5142 (lsp--shutdown-workspace)))) 5143 5144 (defun lsp--will-save-text-document-params (reason) 5145 (list :textDocument (lsp--text-document-identifier) 5146 :reason reason)) 5147 5148 (defun lsp--before-save () 5149 "Before save handler." 5150 (with-demoted-errors "Error in ‘lsp--before-save’: %S" 5151 (let ((params (lsp--will-save-text-document-params 1))) 5152 (when (lsp--send-will-save-p) 5153 (lsp-notify "textDocument/willSave" params)) 5154 (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits) 5155 (let ((lsp-response-timeout 0.1)) 5156 (condition-case nil 5157 (lsp--apply-text-edits 5158 (lsp-request "textDocument/willSaveWaitUntil" 5159 params) 5160 'before-save) 5161 (error))))))) 5162 5163 (defun lsp--on-auto-save () 5164 "Handler for auto-save." 5165 (when (lsp--send-will-save-p) 5166 (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S" 5167 (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2))))) 5168 5169 (defun lsp--text-document-did-save () 5170 "Executed when the file is closed, added to `after-save-hook''." 5171 (when (lsp--send-did-save-p) 5172 (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’" 5173 (lsp-notify "textDocument/didSave" 5174 `( :textDocument ,(lsp--versioned-text-document-identifier) 5175 ,@(when (lsp--save-include-text-p) 5176 (list :text (lsp--buffer-content)))))))) 5177 5178 (defun lsp--text-document-position-params (&optional identifier position) 5179 "Make TextDocumentPositionParams for the current point in the current document. 5180 If IDENTIFIER and POSITION are non-nil, they will be used as the document 5181 identifier and the position respectively." 5182 (list :textDocument (or identifier (lsp--text-document-identifier)) 5183 :position (or position (lsp--cur-position)))) 5184 5185 (defun lsp--get-buffer-diagnostics () 5186 "Return buffer diagnostics." 5187 (gethash (or 5188 (plist-get lsp--virtual-buffer :buffer-file-name) 5189 (lsp--fix-path-casing (buffer-file-name))) 5190 (lsp-diagnostics t))) 5191 5192 (defun lsp-cur-line-diagnostics () 5193 "Return any diagnostics that apply to the current line." 5194 (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)] 5195 (cl-coerce (-filter 5196 (-lambda ((&Diagnostic :range (&Range :start (&Position :line)))) 5197 (and (>= line start) (<= line end))) 5198 (lsp--get-buffer-diagnostics)) 5199 'vector))) 5200 5201 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end) 5202 (right &as &Range :start right-start :end right-end)) 5203 (or (lsp-point-in-range? right-start left) 5204 (lsp-point-in-range? right-end left) 5205 (lsp-point-in-range? left-start right) 5206 (lsp-point-in-range? left-end right))) 5207 5208 (defun lsp-make-position-1 (position) 5209 (lsp-make-position :line (plist-get position :line) 5210 :character (plist-get position :character))) 5211 5212 (defun lsp-cur-possition-diagnostics () 5213 "Return any diagnostics that apply to the current line." 5214 (-let* ((start (if (use-region-p) (region-beginning) (point))) 5215 (end (if (use-region-p) (region-end) (point))) 5216 (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start)) 5217 :end (lsp-make-position-1 (lsp-point-to-position end))))) 5218 (->> (lsp--get-buffer-diagnostics) 5219 (-filter 5220 (-lambda ((&Diagnostic :range)) 5221 (lsp-range-overlapping? range current-range))) 5222 (apply 'vector)))) 5223 5224 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics) 5225 5226 (defun lsp--extract-line-from-buffer (pos) 5227 "Return the line pointed to by POS (a Position object) in the current buffer." 5228 (let* ((point (lsp--position-to-point pos)) 5229 (inhibit-field-text-motion t)) 5230 (save-excursion 5231 (goto-char point) 5232 (buffer-substring (line-beginning-position) (line-end-position))))) 5233 5234 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line) 5235 :end (end &as &Position :character end-char))) 5236 "Return a xref-item from a RANGE in FILENAME." 5237 (let* ((line (lsp--extract-line-from-buffer start)) 5238 (len (length line))) 5239 (add-face-text-property (max (min start-char len) 0) 5240 (max (min end-char len) 0) 5241 'xref-match t line) 5242 ;; LINE is nil when FILENAME is not being current visited by any buffer. 5243 (xref-make-match (or line filename) 5244 (xref-make-file-location 5245 filename 5246 (lsp-translate-line (1+ start-line)) 5247 (lsp-translate-column start-char)) 5248 (- end-char start-char)))) 5249 5250 (defun lsp--location-uri (loc) 5251 (if (lsp-location? loc) 5252 (lsp:location-uri loc) 5253 (lsp:location-link-target-uri loc))) 5254 5255 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start))) 5256 "Go to location." 5257 (let ((path (lsp--uri-to-path uri))) 5258 (if (f-exists? path) 5259 (with-current-buffer (find-file path) 5260 (goto-char (lsp--position-to-point start))) 5261 (error "There is no file %s" path)))) 5262 5263 (defun lsp--location-range (loc) 5264 (if (lsp-location? loc) 5265 (lsp:location-range loc) 5266 (lsp:location-link-target-selection-range loc))) 5267 5268 (defun lsp--locations-to-xref-items (locations) 5269 "Return a list of `xref-item' given LOCATIONS, which can be of 5270 type Location, LocationLink, Location[] or LocationLink[]." 5271 (setq locations 5272 (pcase locations 5273 ((seq (or (lsp-interface Location) 5274 (lsp-interface LocationLink))) 5275 (append locations nil)) 5276 ((or (lsp-interface Location) 5277 (lsp-interface LocationLink)) 5278 (list locations)))) 5279 5280 (cl-labels ((get-xrefs-in-file 5281 (file-locs) 5282 (-let [(filename . matches) file-locs] 5283 (condition-case err 5284 (let ((visiting (find-buffer-visiting filename)) 5285 (fn (lambda (loc) 5286 (lsp-with-filename filename 5287 (lsp--xref-make-item filename 5288 (lsp--location-range loc)))))) 5289 (if visiting 5290 (with-current-buffer visiting 5291 (seq-map fn matches)) 5292 (when (file-readable-p filename) 5293 (with-temp-buffer 5294 (insert-file-contents-literally filename) 5295 (seq-map fn matches))))) 5296 (error (lsp-warn "Failed to process xref entry for filename '%s': %s" 5297 filename (error-message-string err))) 5298 (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s" 5299 filename (error-message-string err))))))) 5300 5301 (->> locations 5302 (seq-sort #'lsp--location-before-p) 5303 (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri)) 5304 (seq-map #'get-xrefs-in-file) 5305 (apply #'nconc)))) 5306 5307 (defun lsp--location-before-p (left right) 5308 "Sort first by file, then by line, then by column." 5309 (let ((left-uri (lsp--location-uri left)) 5310 (right-uri (lsp--location-uri right))) 5311 (if (not (string= left-uri right-uri)) 5312 (string< left-uri right-uri) 5313 (-let (((&Range :start left-start) (lsp--location-range left)) 5314 ((&Range :start right-start) (lsp--location-range right))) 5315 (lsp--position-compare right-start left-start))))) 5316 5317 (defun lsp--make-reference-params (&optional td-position exclude-declaration) 5318 "Make a ReferenceParam object. 5319 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead. 5320 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." 5321 (let ((json-false :json-false)) 5322 (plist-put (or td-position (lsp--text-document-position-params)) 5323 :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration)))))) 5324 5325 (defun lsp--cancel-request (id) 5326 "Cancel request with ID in all workspaces." 5327 (lsp-foreach-workspace 5328 (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id)) 5329 (lsp-notify "$/cancelRequest" `(:id ,id)))) 5330 5331 (defvar-local lsp--hover-saved-bounds nil) 5332 5333 (defun lsp-eldoc-function (cb &rest _ignored) 5334 "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')." 5335 (if (and lsp--hover-saved-bounds 5336 (lsp--point-in-bounds-p lsp--hover-saved-bounds)) 5337 lsp--eldoc-saved-message 5338 (setq lsp--hover-saved-bounds nil 5339 lsp--eldoc-saved-message nil) 5340 (if (looking-at-p "[[:space:]\n]") 5341 (setq lsp--eldoc-saved-message nil) ; And returns nil. 5342 (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover")) 5343 (lsp-request-async 5344 "textDocument/hover" 5345 (lsp--text-document-position-params) 5346 (-lambda ((hover &as &Hover? :range? :contents)) 5347 (setq lsp--hover-saved-bounds (when range? 5348 (lsp--range-to-region range?))) 5349 (funcall cb (setq lsp--eldoc-saved-message 5350 (when contents 5351 (lsp--render-on-hover-content 5352 contents 5353 lsp-eldoc-render-all))))) 5354 :error-handler #'ignore 5355 :mode 'tick 5356 :cancel-token :eldoc-hover))))) 5357 5358 (defun lsp--point-on-highlight? () 5359 (-some? (lambda (overlay) 5360 (overlay-get overlay 'lsp-highlight)) 5361 (overlays-at (point)))) 5362 5363 (defun lsp--cleanup-highlights-if-needed () 5364 (when (and lsp-enable-symbol-highlighting 5365 lsp--have-document-highlights 5366 (not (lsp--point-on-highlight?))) 5367 (lsp--remove-overlays 'lsp-highlight) 5368 (setq lsp--have-document-highlights nil) 5369 (lsp-cancel-request-by-token :highlights))) 5370 5371 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil 5372 "The bounds of the symbol from which `lsp--document-highlight' 5373 most recently requested highlights.") 5374 5375 (defun lsp--document-highlight () 5376 (when (lsp-feature? "textDocument/documentHighlight") 5377 (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol))) 5378 (unless (or (looking-at-p "[[:space:]\n]") 5379 (not lsp-enable-symbol-highlighting) 5380 (and lsp--have-document-highlights 5381 curr-sym-bounds 5382 (equal curr-sym-bounds 5383 lsp--symbol-bounds-of-last-highlight-invocation))) 5384 (setq lsp--symbol-bounds-of-last-highlight-invocation 5385 curr-sym-bounds) 5386 (lsp-request-async "textDocument/documentHighlight" 5387 (lsp--text-document-position-params) 5388 #'lsp--document-highlight-callback 5389 :mode 'tick 5390 :cancel-token :highlights))))) 5391 5392 (defun lsp--help-open-link (&rest _) 5393 "Open markdown link at point via mouse or keyboard." 5394 (interactive "P") 5395 (let ((buffer-list-update-hook nil)) 5396 (-let [(buffer point) (if-let* ((valid (and (listp last-input-event) 5397 (eq (car last-input-event) 'mouse-2))) 5398 (event (cadr last-input-event)) 5399 (win (posn-window event)) 5400 (buffer (window-buffer win))) 5401 `(,buffer ,(posn-point event)) 5402 `(,(current-buffer) ,(point)))] 5403 (with-current-buffer buffer 5404 (when-let* ((face (get-text-property point 'face)) 5405 (url (or (and (eq face 'markdown-link-face) 5406 (get-text-property point 'help-echo)) 5407 (and (memq face '(markdown-url-face markdown-plain-url-face)) 5408 (nth 3 (markdown-link-at-pos point)))))) 5409 (lsp--document-link-handle-target url)))))) 5410 5411 (defvar lsp-help-mode-map 5412 (-doto (make-sparse-keymap) 5413 (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link)) 5414 "Keymap for `lsp-help-mode'.") 5415 5416 (define-derived-mode lsp-help-mode help-mode "LspHelp" 5417 "Major mode for displaying lsp help.") 5418 5419 (defun lsp-describe-thing-at-point () 5420 "Display the type signature and documentation of the thing at point." 5421 (interactive) 5422 (let ((contents (-some->> (lsp--text-document-position-params) 5423 (lsp--make-request "textDocument/hover") 5424 (lsp--send-request) 5425 (lsp:hover-contents)))) 5426 (if (and contents (not (equal contents ""))) 5427 (let ((lsp-help-buf-name "*lsp-help*")) 5428 (with-current-buffer (get-buffer-create lsp-help-buf-name) 5429 (delay-mode-hooks 5430 (lsp-help-mode) 5431 (with-help-window lsp-help-buf-name 5432 (insert 5433 (mapconcat 'string-trim-right 5434 (split-string (lsp--render-on-hover-content contents t) "\n") 5435 "\n")))) 5436 (run-mode-hooks))) 5437 (lsp--info "No content at point.")))) 5438 5439 (defun lsp--point-in-bounds-p (bounds) 5440 "Return whether the current point is within BOUNDS." 5441 (and (<= (car bounds) (point)) (< (point) (cdr bounds)))) 5442 5443 (defun lsp-get-renderer (language) 5444 "Get renderer for LANGUAGE." 5445 (lambda (str) 5446 (lsp--render-string str language))) 5447 5448 (defun lsp--setup-markdown (mode) 5449 "Setup the ‘markdown-mode’ in the frame. 5450 MODE is the mode used in the parent frame." 5451 (make-local-variable 'markdown-code-lang-modes) 5452 (dolist (mark (alist-get mode lsp-custom-markup-modes)) 5453 (add-to-list 'markdown-code-lang-modes (cons mark mode))) 5454 (setq-local markdown-fontify-code-blocks-natively t) 5455 (setq-local markdown-fontify-code-block-default-mode mode) 5456 (setq-local markdown-hide-markup t) 5457 5458 ;; Render some common HTML entities. 5459 ;; This should really happen in markdown-mode instead, 5460 ;; but it doesn't, so we do it here for now. 5461 (setq prettify-symbols-alist 5462 (cl-loop for i from 0 to 255 5463 collect (cons (format "&#x%02X;" i) i))) 5464 (push '("<" . ?<) prettify-symbols-alist) 5465 (push '(">" . ?>) prettify-symbols-alist) 5466 (push '("&" . ?&) prettify-symbols-alist) 5467 (push '(" " . ? ) prettify-symbols-alist) 5468 (setq prettify-symbols-compose-predicate 5469 (lambda (_start _end _match) t)) 5470 (prettify-symbols-mode 1)) 5471 5472 (defvar lsp-help-link-keymap 5473 (let ((map (make-sparse-keymap))) 5474 (define-key map [mouse-2] #'lsp--help-open-link) 5475 (define-key map "\r" #'lsp--help-open-link) 5476 map) 5477 "Keymap active on links in *lsp-help* mode.") 5478 5479 (defun lsp--fix-markdown-links () 5480 (let ((inhibit-read-only t) 5481 (inhibit-modification-hooks t) 5482 (prop)) 5483 (save-restriction 5484 (goto-char (point-min)) 5485 (while (setq prop (markdown-find-next-prop 'face)) 5486 (let ((end (or (next-single-property-change (car prop) 'face) 5487 (point-max)))) 5488 (when (memq (get-text-property (car prop) 'face) 5489 '(markdown-link-face 5490 markdown-url-face 5491 markdown-plain-url-face)) 5492 (add-text-properties (car prop) end 5493 (list 'button t 5494 'category 'lsp-help-link 5495 'follow-link t 5496 'keymap lsp-help-link-keymap))) 5497 (goto-char end)))))) 5498 5499 (defun lsp--buffer-string-visible () 5500 "Return visible buffer string. 5501 Stolen from `org-copy-visible'." 5502 (let ((temp (generate-new-buffer " *temp*")) 5503 (beg (point-min)) 5504 (end (point-max))) 5505 (while (/= beg end) 5506 (when (get-char-property beg 'invisible) 5507 (setq beg (next-single-char-property-change beg 'invisible nil end))) 5508 (let* ((next (next-single-char-property-change beg 'invisible nil end)) 5509 (substring (buffer-substring beg next))) 5510 (with-current-buffer temp (insert substring)) 5511 ;; (setq result (concat result substring)) 5512 (setq beg next))) 5513 (setq deactivate-mark t) 5514 (prog1 (with-current-buffer temp 5515 (s-chop-suffix "\n" (buffer-string))) 5516 (kill-buffer temp)))) 5517 5518 (defvar lsp-buffer-major-mode nil 5519 "Holds the major mode when fontification function is running. 5520 See #2588") 5521 5522 (defvar view-inhibit-help-message) 5523 5524 (defun lsp--render-markdown () 5525 "Render markdown." 5526 5527 (let ((markdown-enable-math nil)) 5528 (goto-char (point-min)) 5529 (while (re-search-forward 5530 (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/" 5531 "{" "}" "[" "]" "(" ")" 5532 "#" "+" "-" "." "!" "|")))) 5533 nil t) 5534 (replace-match (rx (backref 1)))) 5535 5536 ;; markdown-mode v2.3 does not yet provide gfm-view-mode 5537 (if (fboundp 'gfm-view-mode) 5538 (let ((view-inhibit-help-message t)) 5539 (gfm-view-mode)) 5540 (gfm-mode)) 5541 5542 (lsp--setup-markdown lsp-buffer-major-mode))) 5543 5544 (defvar lsp--display-inline-image-alist 5545 '((lsp--render-markdown 5546 (:regexp 5547 "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)" 5548 :sexp 5549 (create-image 5550 (base64-decode-string 5551 (buffer-substring-no-properties (match-beginning 1) (match-end 1))) 5552 nil t)))) 5553 "Replaced string regexp and function returning image. 5554 Each element should have the form (MODE . (PROPERTY-LIST...)). 5555 MODE (car) is function which is defined in `lsp-language-id-configuration'. 5556 Cdr should be list of PROPERTY-LIST. 5557 5558 Each PROPERTY-LIST should have properties: 5559 :regexp Regexp which determines what string is relpaced to image. 5560 You should also get information of image, by parenthesis constructs. 5561 By default, all matched string is replaced to image, but you can 5562 change index of replaced string by keyword :replaced-index. 5563 5564 :sexp Return image when evaluated. You can use information of regexp 5565 by using (match-beggining N), (match-end N) or (match-substring N). 5566 5567 In addition, each can have property: 5568 :replaced-index Determine index which is used to replace regexp to image. 5569 The value means first argument of `match-beginning' and 5570 `match-end'. If omitted, interpreted as index 0.") 5571 5572 (defcustom lsp-display-inline-image t 5573 "Showing inline image or not." 5574 :group 'lsp-mode 5575 :type 'boolean) 5576 5577 (defcustom lsp-enable-suggest-server-download t 5578 "When non-nil enable server downloading suggestions." 5579 :group 'lsp-mode 5580 :type 'boolean 5581 :package-version '(lsp-mode . "9.0.0")) 5582 5583 (defcustom lsp-auto-register-remote-clients t 5584 "When non-nil register remote when registering the local one." 5585 :group 'lsp-mode 5586 :type 'boolean 5587 :package-version '(lsp-mode . "9.0.0")) 5588 5589 (defun lsp--display-inline-image (mode) 5590 "Add image property if available." 5591 (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist)))) 5592 (when (and (display-images-p) lsp-display-inline-image) 5593 (cl-loop 5594 for plist in plist-list 5595 with regexp with replaced-index 5596 do 5597 (setq regexp (plist-get plist :regexp)) 5598 (setq replaced-index (or (plist-get plist :replaced-index) 0)) 5599 5600 (font-lock-remove-keywords nil (list regexp replaced-index)) 5601 (let ((inhibit-read-only t)) 5602 (save-excursion 5603 (goto-char (point-min)) 5604 (while (re-search-forward regexp nil t) 5605 (set-text-properties 5606 (match-beginning replaced-index) (match-end replaced-index) 5607 nil) 5608 (add-text-properties 5609 (match-beginning replaced-index) (match-end replaced-index) 5610 `(display ,(eval (plist-get plist :sexp))))))))))) 5611 5612 (defun lsp--fontlock-with-mode (str mode) 5613 "Fontlock STR with MODE." 5614 (let ((lsp-buffer-major-mode major-mode)) 5615 (with-temp-buffer 5616 (with-demoted-errors "Error during doc rendering: %s" 5617 (insert str) 5618 (delay-mode-hooks (funcall mode)) 5619 (cl-flet ((window-body-width () lsp-window-body-width)) 5620 ;; This can go wrong in some cases, and the fontification would 5621 ;; not work as expected. 5622 ;; 5623 ;; See #2984 5624 (ignore-errors (font-lock-ensure)) 5625 (lsp--display-inline-image mode) 5626 (when (eq mode 'lsp--render-markdown) 5627 (lsp--fix-markdown-links)))) 5628 (lsp--buffer-string-visible)))) 5629 5630 (defun lsp--render-string (str language) 5631 "Render STR using `major-mode' corresponding to LANGUAGE. 5632 When language is nil render as markup if `markdown-mode' is loaded." 5633 (setq str (s-replace "\r" "" (or str ""))) 5634 (if-let* ((modes (-keep (-lambda ((mode . lang)) 5635 (when (and (equal lang language) (functionp mode)) 5636 mode)) 5637 lsp-language-id-configuration)) 5638 (mode (car (or (member major-mode modes) modes)))) 5639 (lsp--fontlock-with-mode str mode) 5640 str)) 5641 5642 (defun lsp--render-element (content) 5643 "Render CONTENT element." 5644 (let ((inhibit-message t)) 5645 (or 5646 (pcase content 5647 ((lsp-interface MarkedString :value :language) 5648 (lsp--render-string value language)) 5649 ((lsp-interface MarkupContent :value :kind) 5650 (lsp--render-string value kind)) 5651 ;; plain string 5652 ((pred stringp) (lsp--render-string content "markdown")) 5653 ((pred null) "") 5654 (_ (error "Failed to handle %s" content))) 5655 ""))) 5656 5657 (defun lsp--create-unique-string-fn () 5658 (let (elements) 5659 (lambda (element) 5660 (let ((count (cl-count element elements :test #'string=))) 5661 (prog1 (if (zerop count) 5662 element 5663 (format "%s (%s)" element count)) 5664 (push element elements)))))) 5665 5666 (defun lsp--select-action (actions) 5667 "Select an action to execute from ACTIONS." 5668 (cond 5669 ((seq-empty-p actions) (signal 'lsp-no-code-actions nil)) 5670 ((and (eq (seq-length actions) 1) lsp-auto-execute-action) 5671 (lsp-seq-first actions)) 5672 (t (let ((completion-ignore-case t)) 5673 (lsp--completing-read "Select code action: " 5674 (seq-into actions 'list) 5675 (-compose (lsp--create-unique-string-fn) 5676 #'lsp:code-action-title) 5677 nil t))))) 5678 5679 (defun lsp--workspace-server-id (workspace) 5680 "Return the server ID of WORKSPACE." 5681 (-> workspace lsp--workspace-client lsp--client-server-id)) 5682 5683 (defun lsp--handle-rendered-for-echo-area (contents) 5684 "Return a single line from RENDERED, appropriate for display in the echo area." 5685 (pcase (lsp-workspaces) 5686 (`(,workspace) 5687 (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace))) 5688 ;; For projects with multiple active workspaces we also default to 5689 ;; render the first line. 5690 (_ (lsp-clients-extract-signature-on-hover contents nil)))) 5691 5692 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id) 5693 "Extract a representative line from CONTENTS, to show in the echo area." 5694 (car (s-lines (s-trim (lsp--render-element contents))))) 5695 5696 (defun lsp--render-on-hover-content (contents render-all) 5697 "Render the content received from `document/onHover' request. 5698 CONTENTS - MarkedString | MarkedString[] | MarkupContent 5699 RENDER-ALL - nil if only the signature should be rendered." 5700 (cond 5701 ((lsp-markup-content? contents) 5702 ;; MarkupContent. 5703 ;; It tends to be long and is not suitable to display fully in the echo area. 5704 ;; Just display the first line which is typically the signature. 5705 (if render-all 5706 (lsp--render-element contents) 5707 (lsp--handle-rendered-for-echo-area contents))) 5708 ((and (stringp contents) (not (string-match-p "\n" contents))) 5709 ;; If the contents is a single string containing a single line, 5710 ;; render it always. 5711 (lsp--render-element contents)) 5712 (t 5713 ;; MarkedString -> MarkedString[] 5714 (when (or (lsp-marked-string? contents) (stringp contents)) 5715 (setq contents (list contents))) 5716 ;; Consider the signature consisting of the elements who have a renderable 5717 ;; "language" property. When render-all is nil, ignore other elements. 5718 (string-join 5719 (seq-map 5720 #'lsp--render-element 5721 (if render-all 5722 contents 5723 ;; Only render contents that have an available renderer. 5724 (seq-take 5725 (seq-filter 5726 (-andfn #'lsp-marked-string? 5727 (-compose #'lsp-get-renderer #'lsp:marked-string-language)) 5728 contents) 5729 1))) 5730 (if (bound-and-true-p page-break-lines-mode) 5731 "\n\n" 5732 "\n"))))) 5733 5734 5735 5736 (defvar lsp-signature-mode-map 5737 (-doto (make-sparse-keymap) 5738 (define-key (kbd "M-n") #'lsp-signature-next) 5739 (define-key (kbd "M-p") #'lsp-signature-previous) 5740 (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs) 5741 (define-key (kbd "C-c C-k") #'lsp-signature-stop) 5742 (define-key (kbd "C-g") #'lsp-signature-stop)) 5743 "Keymap for `lsp-signature-mode'.") 5744 5745 (define-minor-mode lsp-signature-mode 5746 "Mode used to show signature popup." 5747 :keymap lsp-signature-mode-map 5748 :lighter "" 5749 :group 'lsp-mode) 5750 5751 (defun lsp-signature-stop () 5752 "Stop showing current signature help." 5753 (interactive) 5754 (lsp-cancel-request-by-token :signature) 5755 (remove-hook 'post-command-hook #'lsp-signature) 5756 (funcall lsp-signature-function nil) 5757 (lsp-signature-mode -1)) 5758 5759 (declare-function page-break-lines--update-display-tables "ext:page-break-lines") 5760 5761 (defun lsp--setup-page-break-mode-if-present () 5762 "Enable `page-break-lines-mode' in current buffer." 5763 (when (fboundp 'page-break-lines-mode) 5764 (page-break-lines-mode) 5765 ;; force page-break-lines-mode to update the display tables. 5766 (page-break-lines--update-display-tables))) 5767 5768 (defun lsp-lv-message (message) 5769 (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present) 5770 (if message 5771 (progn 5772 (setq lsp--signature-last-buffer (current-buffer)) 5773 (let ((lv-force-update t)) 5774 (lv-message "%s" message))) 5775 (lv-delete-window) 5776 (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present))) 5777 5778 (declare-function posframe-show "ext:posframe") 5779 (declare-function posframe-hide "ext:posframe") 5780 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe") 5781 5782 (defface lsp-signature-posframe 5783 '((t :inherit tooltip)) 5784 "Background and foreground for `lsp-signature-posframe'." 5785 :group 'lsp-mode) 5786 5787 (defvar lsp-signature-posframe-params 5788 (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward 5789 :height 10 5790 :width 60 5791 :border-width 1 5792 :min-width 60) 5793 "Params for signature and `posframe-show'.") 5794 5795 (defun lsp-signature-posframe (str) 5796 "Use posframe to show the STR signatureHelp string." 5797 (if str 5798 (apply #'posframe-show 5799 (with-current-buffer (get-buffer-create " *lsp-signature*") 5800 (erase-buffer) 5801 (insert str) 5802 (visual-line-mode 1) 5803 (lsp--setup-page-break-mode-if-present) 5804 (current-buffer)) 5805 (append 5806 lsp-signature-posframe-params 5807 (list :position (point) 5808 :background-color (face-attribute 'lsp-signature-posframe :background nil t) 5809 :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t) 5810 :border-color (face-attribute (if (facep 'child-frame-border) 5811 'child-frame-border 5812 'internal-border) 5813 :background nil t)))) 5814 (posframe-hide " *lsp-signature*"))) 5815 5816 (defun lsp--handle-signature-update (signature) 5817 (let ((message 5818 (if (lsp-signature-help? signature) 5819 (lsp--signature->message signature) 5820 (mapconcat #'lsp--signature->message signature "\n")))) 5821 (if (s-present? message) 5822 (funcall lsp-signature-function message) 5823 (lsp-signature-stop)))) 5824 5825 (defun lsp-signature-activate () 5826 "Activate signature help. 5827 It will show up only if current point has signature help." 5828 (interactive) 5829 (setq lsp--signature-last nil 5830 lsp--signature-last-index nil 5831 lsp--signature-last-buffer (current-buffer)) 5832 (add-hook 'post-command-hook #'lsp-signature) 5833 (lsp-signature-mode t)) 5834 5835 (defcustom lsp-signature-cycle t 5836 "Whether `lsp-signature-next' and prev should cycle." 5837 :type 'boolean 5838 :group 'lsp-mode) 5839 5840 (defun lsp-signature-next () 5841 "Show next signature." 5842 (interactive) 5843 (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last)))) 5844 (when (and lsp--signature-last-index 5845 lsp--signature-last 5846 (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs))) 5847 (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs)) 5848 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))) 5849 5850 (defun lsp-signature-previous () 5851 "Next signature." 5852 (interactive) 5853 (when (and lsp--signature-last-index 5854 lsp--signature-last 5855 (or lsp-signature-cycle (not (zerop lsp--signature-last-index)))) 5856 (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index) 5857 (length (lsp:signature-help-signatures lsp--signature-last)) 5858 lsp--signature-last-index))) 5859 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))) 5860 5861 (defun lsp-signature-toggle-full-docs () 5862 "Toggle full/partial signature documentation." 5863 (interactive) 5864 (let ((all? (not (numberp lsp-signature-doc-lines)))) 5865 (setq lsp-signature-doc-lines (if all? 5866 (or (car-safe lsp-signature-doc-lines) 5867 20) 5868 (list lsp-signature-doc-lines)))) 5869 (lsp-signature-activate)) 5870 5871 (defface lsp-signature-highlight-function-argument 5872 '((t :inherit eldoc-highlight-function-argument)) 5873 "The face to use to highlight function arguments in signatures." 5874 :group 'lsp-mode) 5875 5876 (defun lsp--signature->message (signature-help) 5877 "Generate eldoc message from SIGNATURE-HELP response." 5878 (setq lsp--signature-last signature-help) 5879 5880 (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help)))) 5881 (-let* (((&SignatureHelp :active-signature? 5882 :active-parameter? 5883 :signatures) signature-help) 5884 (active-signature? (or lsp--signature-last-index active-signature? 0)) 5885 (_ (setq lsp--signature-last-index active-signature?)) 5886 ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?)) 5887 (prefix (if (= (length signatures) 1) 5888 "" 5889 (concat (propertize (format " %s/%s" 5890 (1+ active-signature?) 5891 (length signatures)) 5892 'face 'success) 5893 " "))) 5894 (method-docs (when 5895 (and lsp-signature-render-documentation 5896 (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines))) 5897 (let ((docs (lsp--render-element 5898 (lsp:parameter-information-documentation? signature)))) 5899 (when (s-present? docs) 5900 (concat 5901 "\n" 5902 (if (fboundp 'page-break-lines-mode) 5903 "\n" 5904 "") 5905 (if (and (numberp lsp-signature-doc-lines) 5906 (> (length (s-lines docs)) lsp-signature-doc-lines)) 5907 (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs))) 5908 (propertize "\nTruncated..." 'face 'highlight)) 5909 docs))))))) 5910 (when (and active-parameter? (not (seq-empty-p parameters?))) 5911 (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?))) 5912 (seq-elt parameters? active-parameter?))) 5913 (selected-param-label (let ((label (lsp:parameter-information-label param))) 5914 (if (stringp label) label (append label nil)))) 5915 (start (if (stringp selected-param-label) 5916 (s-index-of selected-param-label label) 5917 (cl-first selected-param-label))) 5918 (end (if (stringp selected-param-label) 5919 (+ start (length selected-param-label)) 5920 (cl-second selected-param-label)))) 5921 (add-face-text-property start end 'lsp-signature-highlight-function-argument nil label))) 5922 (concat prefix label method-docs)))) 5923 5924 (defun lsp-signature () 5925 "Display signature info (based on `textDocument/signatureHelp')" 5926 (if (and lsp--signature-last-buffer 5927 (not (equal (current-buffer) lsp--signature-last-buffer))) 5928 (lsp-signature-stop) 5929 (lsp-request-async "textDocument/signatureHelp" 5930 (lsp--text-document-position-params) 5931 #'lsp--handle-signature-update 5932 :cancel-token :signature))) 5933 5934 5935 (defcustom lsp-overlay-document-color-char "■" 5936 "Display the char represent the document color in overlay" 5937 :type 'string 5938 :group 'lsp-mode) 5939 5940 ;; color presentation 5941 (defun lsp--color-create-interactive-command (color range) 5942 (lambda () 5943 (interactive) 5944 (-let [(&ColorPresentation? :text-edit? 5945 :additional-text-edits?) 5946 (lsp--completing-read 5947 "Select color presentation: " 5948 (lsp-request 5949 "textDocument/colorPresentation" 5950 `( :textDocument ,(lsp--text-document-identifier) 5951 :color ,color 5952 :range ,range)) 5953 #'lsp:color-presentation-label 5954 nil 5955 t)] 5956 (when text-edit? 5957 (lsp--apply-text-edit text-edit?)) 5958 (when additional-text-edits? 5959 (lsp--apply-text-edits additional-text-edits? 'color-presentation))))) 5960 5961 (defun lsp--number->color (number) 5962 (let ((result (format "%x" 5963 (round (* (or number 0) 255.0))))) 5964 (if (= 1 (length result)) 5965 (concat "0" result) 5966 result))) 5967 5968 (defun lsp--document-color () 5969 "Document color handler." 5970 (when (lsp-feature? "textDocument/documentColor") 5971 (lsp-request-async 5972 "textDocument/documentColor" 5973 `(:textDocument ,(lsp--text-document-identifier)) 5974 (lambda (result) 5975 (lsp--remove-overlays 'lsp-color) 5976 (seq-do 5977 (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue) 5978 :range)) 5979 (-let* (((beg . end) (lsp--range-to-region range)) 5980 (overlay (make-overlay beg end)) 5981 (command (lsp--color-create-interactive-command color range))) 5982 (overlay-put overlay 'lsp-color t) 5983 (overlay-put overlay 'evaporate t) 5984 (overlay-put overlay 5985 'before-string 5986 (propertize 5987 lsp-overlay-document-color-char 5988 'face `((:foreground ,(format 5989 "#%s%s%s" 5990 (lsp--number->color red) 5991 (lsp--number->color green) 5992 (lsp--number->color blue)))) 5993 'action command 5994 'mouse-face 'lsp-lens-mouse-face 5995 'local-map (-doto (make-sparse-keymap) 5996 (define-key [mouse-1] command)))))) 5997 result)) 5998 :mode 'unchanged 5999 :cancel-token :document-color-token))) 6000 6001 6002 6003 (defun lsp--action-trigger-parameter-hints (_command) 6004 "Handler for editor.action.triggerParameterHints." 6005 (when (member :on-server-request lsp-signature-auto-activate) 6006 (lsp-signature-activate))) 6007 6008 (defun lsp--action-trigger-suggest (_command) 6009 "Handler for editor.action.triggerSuggest." 6010 (cond 6011 ((and (bound-and-true-p company-mode) 6012 (fboundp 'company-auto-begin) 6013 (fboundp 'company-post-command)) 6014 (run-at-time 0 nil 6015 (lambda () 6016 (let ((this-command 'company-idle-begin) 6017 (company-minimum-prefix-length 0)) 6018 (company-auto-begin) 6019 (company-post-command))))) 6020 (t 6021 (completion-at-point)))) 6022 6023 (defconst lsp--default-action-handlers 6024 (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints) 6025 ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest)) 6026 "Default action handlers.") 6027 6028 (defun lsp--find-action-handler (command) 6029 "Find action handler for particular COMMAND." 6030 (or 6031 (--some (-some->> it 6032 (lsp--workspace-client) 6033 (lsp--client-action-handlers) 6034 (gethash command)) 6035 (lsp-workspaces)) 6036 (gethash command lsp--default-action-handlers))) 6037 6038 (defun lsp--text-document-code-action-params (&optional kind) 6039 "Code action params." 6040 (list :textDocument (lsp--text-document-identifier) 6041 :range (if (use-region-p) 6042 (lsp--region-to-range (region-beginning) (region-end)) 6043 (lsp--region-to-range (point) (point))) 6044 :context `( :diagnostics ,(lsp-cur-possition-diagnostics) 6045 ,@(when kind (list :only (vector kind)))))) 6046 6047 (defun lsp-code-actions-at-point (&optional kind) 6048 "Retrieve the code actions for the active region or the current line. 6049 It will filter by KIND if non nil." 6050 (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind))) 6051 6052 (defun lsp-execute-code-action-by-kind (command-kind) 6053 "Execute code action by COMMAND-KIND." 6054 (if-let* ((action (->> (lsp-get-or-calculate-code-actions command-kind) 6055 (-filter (-lambda ((&CodeAction :kind?)) 6056 (and kind? (s-prefix? command-kind kind?)))) 6057 lsp--select-action))) 6058 (lsp-execute-code-action action) 6059 (signal 'lsp-no-code-actions '(command-kind)))) 6060 6061 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point) 6062 6063 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?)) 6064 "Parse and execute a code ACTION represented as a Command LSP type." 6065 (let ((server-id (->> (lsp-workspaces) 6066 (cl-first) 6067 (or lsp--cur-workspace) 6068 (lsp--workspace-client) 6069 (lsp--client-server-id)))) 6070 (condition-case nil 6071 (with-no-warnings 6072 (lsp-execute-command server-id (intern command) arguments?)) 6073 (cl-no-applicable-method 6074 (if-let* ((action-handler (lsp--find-action-handler command))) 6075 (funcall action-handler action) 6076 (lsp-send-execute-command command arguments?)))))) 6077 6078 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?)) 6079 "Execute code action ACTION. For example, when text under the 6080 caret has a suggestion to apply a fix from an lsp-server, calling 6081 this function will do so. 6082 If ACTION is not set it will be selected from `lsp-code-actions-at-point'. 6083 Request codeAction/resolve for more info if server supports." 6084 (interactive (list (lsp--select-action (lsp-code-actions-at-point)))) 6085 (if (and (lsp-feature? "codeAction/resolve") 6086 (not command?) 6087 (not edit?)) 6088 (lsp--execute-code-action (lsp-request "codeAction/resolve" action)) 6089 (lsp--execute-code-action action))) 6090 6091 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?)) 6092 "Execute code action ACTION." 6093 (when edit? 6094 (lsp--apply-workspace-edit edit? 'code-action)) 6095 6096 (cond 6097 ((stringp command?) (lsp--execute-command action)) 6098 ((lsp-command? command?) (progn 6099 (when-let* ((action-filter (->> (lsp-workspaces) 6100 (cl-first) 6101 (or lsp--cur-workspace) 6102 (lsp--workspace-client) 6103 (lsp--client-action-filter)))) 6104 (funcall action-filter command?)) 6105 (lsp--execute-command command?))))) 6106 6107 (lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments) 6108 "Patch incorrect boolean argument values in the provided `CodeAction' command 6109 in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values 6110 in this list can be either symbols or lists of symbols that 6111 represent paths to boolean arguments in code actions: 6112 6113 > (lsp-fix-code-action-booleans command `(:foo :bar (:some :nested :boolean))) 6114 6115 When there are available code actions, the server sends 6116 `lsp-mode' a list of possible command names and arguments as 6117 JSON. `lsp-mode' parses all boolean false values as `nil'. As a 6118 result code action arguments containing falsy values don't 6119 roundtrip correctly because `lsp-mode' will end up sending null 6120 values back to the client. This list makes it possible to 6121 selectively transform `nil' values back into `:json-false'." 6122 (seq-doseq (path boolean-action-arguments) 6123 (seq-doseq (args arguments?) 6124 (lsp--fix-nested-boolean args (if (listp path) path (list path)))))) 6125 6126 (defun lsp--fix-nested-boolean (structure path) 6127 "Traverse STRUCTURE using the paths from the PATH list, changing the value to 6128 `:json-false' if it was `nil'. PATH should be a list containing 6129 one or more symbols, and STRUCTURE should be compatible with 6130 `lsp-member?', `lsp-get', and `lsp-put'." 6131 (let ((key (car path)) 6132 (rest (cdr path))) 6133 (if (null rest) 6134 ;; `lsp-put' returns `nil' both when the key doesn't exist and when the 6135 ;; value is `nil', so we need to explicitly check its presence here 6136 (when (and (lsp-member? structure key) (not (lsp-get structure key))) 6137 (lsp-put structure key :json-false)) 6138 ;; If `key' does not exist, then we'll silently ignore it 6139 (when-let* ((child (lsp-get structure key))) 6140 (lsp--fix-nested-boolean child rest))))) 6141 6142 (defvar lsp--formatting-indent-alist 6143 ;; Taken from `dtrt-indent-mode' 6144 '( 6145 (ada-mode . ada-indent) ; Ada 6146 (ada-ts-mode . ada-ts-mode-indent-offset) 6147 (c++-mode . c-basic-offset) ; C++ 6148 (c++-ts-mode . c-ts-mode-indent-offset) 6149 (c-mode . c-basic-offset) ; C 6150 (c-ts-mode . c-ts-mode-indent-offset) 6151 (cperl-mode . cperl-indent-level) ; Perl 6152 (crystal-mode . crystal-indent-level) ; Crystal (Ruby) 6153 (csharp-mode . c-basic-offset) ; C# 6154 (csharp-tree-sitter-mode . csharp-tree-sitter-indent-offset) ; C# 6155 (csharp-ts-mode . csharp-ts-mode-indent-offset) ; C# (tree-sitter, Emacs29) 6156 (css-mode . css-indent-offset) ; CSS 6157 (d-mode . c-basic-offset) ; D 6158 (enh-ruby-mode . enh-ruby-indent-level) ; Ruby 6159 (erlang-mode . erlang-indent-level) ; Erlang 6160 (ess-mode . ess-indent-offset) ; ESS (R) 6161 (go-ts-mode . go-ts-mode-indent-offset) 6162 (gpr-mode . gpr-indent-offset) ; GNAT Project 6163 (gpr-ts-mode . gpr-ts-mode-indent-offset) 6164 (hack-mode . hack-indent-offset) ; Hack 6165 (java-mode . c-basic-offset) ; Java 6166 (java-ts-mode . java-ts-mode-indent-offset) 6167 (jde-mode . c-basic-offset) ; Java (JDE) 6168 (js-mode . js-indent-level) ; JavaScript 6169 (js-ts-mode . js-indent-level) 6170 (js2-mode . js2-basic-offset) ; JavaScript-IDE 6171 (js3-mode . js3-indent-level) ; JavaScript-IDE 6172 (json-mode . js-indent-level) ; JSON 6173 (json-ts-mode . json-ts-mode-indent-offset) 6174 (lua-mode . lua-indent-level) ; Lua 6175 (lua-ts-mode . lua-ts-indent-offset) 6176 (nxml-mode . nxml-child-indent) ; XML 6177 (objc-mode . c-basic-offset) ; Objective C 6178 (pascal-mode . pascal-indent-level) ; Pascal 6179 (perl-mode . perl-indent-level) ; Perl 6180 (php-mode . c-basic-offset) ; PHP 6181 (php-ts-mode . php-ts-mode-indent-offset) ; PHP 6182 (powershell-mode . powershell-indent) ; PowerShell 6183 (powershell-ts-mode . powershell-ts-mode-indent-offset) ; PowerShell 6184 (raku-mode . raku-indent-offset) ; Perl6/Raku 6185 (ruby-mode . ruby-indent-level) ; Ruby 6186 (rust-mode . rust-indent-offset) ; Rust 6187 (rust-ts-mode . rust-ts-mode-indent-offset) 6188 (rustic-mode . rustic-indent-offset) ; Rust 6189 (scala-mode . scala-indent:step) ; Scala 6190 (sgml-mode . sgml-basic-offset) ; SGML 6191 (sh-mode . sh-basic-offset) ; Shell Script 6192 (toml-ts-mode . toml-ts-mode-indent-offset) 6193 (typescript-mode . typescript-indent-level) ; Typescript 6194 (typescript-ts-mode . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29) 6195 (yaml-mode . yaml-indent-offset) ; YAML 6196 (yang-mode . c-basic-offset) ; YANG (yang-mode) 6197 6198 (default . standard-indent)) ; default fallback 6199 "A mapping from `major-mode' to its indent variable.") 6200 6201 (defun lsp--get-indent-width (mode) 6202 "Get indentation offset for MODE." 6203 (or (alist-get mode lsp--formatting-indent-alist) 6204 (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default)))) 6205 6206 (defun lsp--make-document-formatting-params () 6207 "Create document formatting params." 6208 (lsp-make-document-formatting-params 6209 :text-document (lsp--text-document-identifier) 6210 :options (lsp-make-formatting-options 6211 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 6212 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 6213 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 6214 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 6215 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)))) 6216 6217 (defun lsp-format-buffer () 6218 "Ask the server to format this document." 6219 (interactive "*") 6220 (cond ((lsp-feature? "textDocument/formatting") 6221 (let ((edits (lsp-request "textDocument/formatting" 6222 (lsp--make-document-formatting-params)))) 6223 (if (seq-empty-p edits) 6224 (lsp--info "No formatting changes provided") 6225 (lsp--apply-text-edits edits 'format)))) 6226 ((lsp-feature? "textDocument/rangeFormatting") 6227 (save-restriction 6228 (widen) 6229 (lsp-format-region (point-min) (point-max)))) 6230 (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider"))))) 6231 6232 (defun lsp-format-region (s e) 6233 "Ask the server to format the region, or if none is selected, the current line." 6234 (interactive "r") 6235 (let ((edits (lsp-request 6236 "textDocument/rangeFormatting" 6237 (lsp--make-document-range-formatting-params s e)))) 6238 (if (seq-empty-p edits) 6239 (lsp--info "No formatting changes provided") 6240 (lsp--apply-text-edits edits 'format)))) 6241 6242 (defmacro lsp-make-interactive-code-action (func-name code-action-kind) 6243 "Define an interactive function FUNC-NAME that attempts to 6244 execute a CODE-ACTION-KIND action." 6245 `(defun ,(intern (concat "lsp-" (symbol-name func-name))) () 6246 ,(format "Perform the %s code action, if available." code-action-kind) 6247 (interactive) 6248 ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to 6249 ;; auto-execute here: the user has specified exactly what they want. 6250 (let ((lsp-auto-execute-action t)) 6251 (condition-case nil 6252 (lsp-execute-code-action-by-kind ,code-action-kind) 6253 (lsp-no-code-actions 6254 (when (called-interactively-p 'any) 6255 (lsp--info ,(format "%s action not available" code-action-kind)))))))) 6256 6257 (lsp-make-interactive-code-action organize-imports "source.organizeImports") 6258 6259 (defun lsp--make-document-range-formatting-params (start end) 6260 "Make DocumentRangeFormattingParams for selected region." 6261 (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params) 6262 (lsp--region-to-range start end))) 6263 6264 (defconst lsp--highlight-kind-face 6265 '((1 . lsp-face-highlight-textual) 6266 (2 . lsp-face-highlight-read) 6267 (3 . lsp-face-highlight-write))) 6268 6269 (defun lsp--remove-overlays (name) 6270 (save-restriction 6271 (widen) 6272 (remove-overlays (point-min) (point-max) name t))) 6273 6274 (defun lsp-document-highlight () 6275 "Highlight all relevant references to the symbol under point." 6276 (interactive) 6277 (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights 6278 (setq lsp--have-document-highlights nil 6279 lsp--symbol-bounds-of-last-highlight-invocation nil) 6280 (let ((lsp-enable-symbol-highlighting t)) 6281 (lsp--document-highlight))) 6282 6283 (defun lsp--document-highlight-callback (highlights) 6284 "Create a callback to process the reply of a 6285 `textDocument/documentHighlight' message for the buffer BUF. 6286 A reference is highlighted only if it is visible in a window." 6287 (lsp--remove-overlays 'lsp-highlight) 6288 6289 (let* ((wins-visible-pos (-map (lambda (win) 6290 (cons (1- (line-number-at-pos (window-start win) t)) 6291 (1+ (line-number-at-pos (window-end win) t)))) 6292 (get-buffer-window-list nil nil 'visible)))) 6293 (setq lsp--have-document-highlights t) 6294 (-map 6295 (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line) 6296 :end (end &as &Position :line end-line)) 6297 :kind?)) 6298 (-map 6299 (-lambda ((start-window . end-window)) 6300 ;; Make the overlay only if the reference is visible 6301 (when (and (> (1+ start-line) start-window) 6302 (< (1+ end-line) end-window)) 6303 (let ((start-point (lsp--position-to-point start)) 6304 (end-point (lsp--position-to-point end))) 6305 (when (not (and lsp-symbol-highlighting-skip-current 6306 (<= start-point (point) end-point))) 6307 (-doto (make-overlay start-point end-point) 6308 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face))) 6309 (overlay-put 'lsp-highlight t)))))) 6310 wins-visible-pos)) 6311 highlights))) 6312 6313 (defcustom lsp-symbol-kinds 6314 '((1 . "File") 6315 (2 . "Module") 6316 (3 . "Namespace") 6317 (4 . "Package") 6318 (5 . "Class") 6319 (6 . "Method") 6320 (7 . "Property") 6321 (8 . "Field") 6322 (9 . "Constructor") 6323 (10 . "Enum") 6324 (11 . "Interface") 6325 (12 . "Function") 6326 (13 . "Variable") 6327 (14 . "Constant") 6328 (15 . "String") 6329 (16 . "Number") 6330 (17 . "Boolean") 6331 (18 . "Array") 6332 (19 . "Object") 6333 (20 . "Key") 6334 (21 . "Null") 6335 (22 . "Enum Member") 6336 (23 . "Struct") 6337 (24 . "Event") 6338 (25 . "Operator") 6339 (26 . "Type Parameter")) 6340 "Alist mapping SymbolKinds to human-readable strings. 6341 Various Symbol objects in the LSP protocol have an integral type, 6342 specifying what they are. This alist maps such type integrals to 6343 readable representations of them. See 6344 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/', 6345 namespace SymbolKind." 6346 :group 'lsp-mode 6347 :type '(alist :key-type integer :value-type string)) 6348 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds) 6349 6350 (lsp-defun lsp--symbol-information-to-xref 6351 ((&SymbolInformation :kind :name 6352 :location (&Location :uri :range (&Range :start 6353 (&Position :line :character))))) 6354 "Return a `xref-item' from SYMBOL information." 6355 (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name) 6356 (xref-make-file-location (lsp--uri-to-path uri) 6357 line 6358 character))) 6359 6360 (defun lsp--get-document-symbols () 6361 "Get document symbols. 6362 6363 If the buffer has not been modified since symbols were last 6364 retrieved, simply return the latest result. 6365 6366 Else, if the request was initiated by Imenu updating its menu-bar 6367 entry, perform it asynchronously; i.e., give Imenu the latest 6368 result and then force a refresh when a new one is available. 6369 6370 Else (e.g., due to interactive use of `imenu' or `xref'), 6371 perform the request synchronously." 6372 (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick) 6373 lsp--document-symbols 6374 (let ((method "textDocument/documentSymbol") 6375 (params `(:textDocument ,(lsp--text-document-identifier))) 6376 (tick (buffer-chars-modified-tick))) 6377 (if (not lsp--document-symbols-request-async) 6378 (prog1 6379 (setq lsp--document-symbols (lsp-request method params)) 6380 (setq lsp--document-symbols-tick tick)) 6381 (lsp-request-async method params 6382 (lambda (document-symbols) 6383 (setq lsp--document-symbols document-symbols 6384 lsp--document-symbols-tick tick) 6385 (lsp--imenu-refresh)) 6386 :mode 'alive 6387 :cancel-token :document-symbols) 6388 lsp--document-symbols)))) 6389 6390 (advice-add 'imenu-update-menubar :around 6391 (lambda (oldfun &rest r) 6392 (let ((lsp--document-symbols-request-async t)) 6393 (apply oldfun r)))) 6394 6395 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position) 6396 "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION." 6397 (-let (((symbol &as &DocumentSymbol? :children?) 6398 (seq-find (-lambda ((&DocumentSymbol :range)) 6399 (lsp-point-in-range? current-position range)) 6400 document-symbols))) 6401 (if children? 6402 (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position)) 6403 (when symbol 6404 (list symbol))))) 6405 6406 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?)) 6407 "Convert a SymbolInformation to a DocumentInformation" 6408 (lsp-make-document-symbol :name name 6409 :kind kind 6410 :range (lsp:location-range location) 6411 :children? nil 6412 :deprecated? deprecated? 6413 :selection-range (lsp:location-range location) 6414 :detail? container-name?)) 6415 6416 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position) 6417 "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION." 6418 (--> symbols-informations 6419 (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range))) 6420 (when (lsp-point-in-range? current-position range) 6421 (lsp--symbol-information->document-symbol symbol))) 6422 it) 6423 (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position)) 6424 (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position))) 6425 (and (lsp--position-compare b-start-position a-start-position) 6426 (lsp--position-compare a-end-position b-end-position)))))) 6427 6428 (defun lsp--symbols->document-symbols-hierarchy (symbols) 6429 "Convert SYMBOLS to symbols-hierarchy." 6430 (when-let* ((first-symbol (lsp-seq-first symbols))) 6431 (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line) 6432 :character (plist-get (lsp--cur-position) :character)))) 6433 (if (lsp-symbol-information? first-symbol) 6434 (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position) 6435 (lsp--document-symbols->document-symbols-hierarchy symbols cur-position))))) 6436 6437 (defun lsp--xref-backend () 'xref-lsp) 6438 6439 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp))) 6440 (propertize (or (thing-at-point 'symbol) "") 6441 'identifier-at-point t)) 6442 6443 (defun lsp--xref-elements-index (symbols path) 6444 (-mapcat 6445 (-lambda (sym) 6446 (pcase-exhaustive sym 6447 ((lsp-interface DocumentSymbol :name :children? :selection-range (lsp-interface Range :start)) 6448 (cons (cons (concat path name) 6449 (lsp--position-to-point start)) 6450 (lsp--xref-elements-index children? (concat path name " / ")))) 6451 ((lsp-interface SymbolInformation :name :location (lsp-interface Location :range (lsp-interface Range :start))) 6452 (list (cons (concat path name) 6453 (lsp--position-to-point start)))))) 6454 symbols)) 6455 6456 (defvar-local lsp--symbols-cache nil) 6457 6458 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp))) 6459 (if (lsp--find-workspaces-for "textDocument/documentSymbol") 6460 (progn 6461 (setq lsp--symbols-cache (lsp--xref-elements-index 6462 (lsp--get-document-symbols) nil)) 6463 lsp--symbols-cache) 6464 (list (propertize (or (thing-at-point 'symbol) "") 6465 'identifier-at-point t)))) 6466 6467 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier) 6468 (save-excursion 6469 (unless (get-text-property 0 'identifier-at-point identifier) 6470 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6471 (user-error "Unable to find symbol %s in current document" identifier))))) 6472 (lsp--locations-to-xref-items (lsp-request "textDocument/definition" 6473 (lsp--text-document-position-params))))) 6474 6475 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier) 6476 (save-excursion 6477 (unless (get-text-property 0 'identifier-at-point identifier) 6478 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6479 (user-error "Unable to find symbol %s" identifier))))) 6480 (lsp--locations-to-xref-items (lsp-request "textDocument/references" 6481 (lsp--make-reference-params nil lsp-references-exclude-declaration))))) 6482 6483 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern) 6484 (seq-map #'lsp--symbol-information-to-xref 6485 (lsp-request "workspace/symbol" `(:query ,pattern)))) 6486 6487 (defcustom lsp-rename-use-prepare t 6488 "Whether `lsp-rename' should do a prepareRename first. 6489 For some language servers, textDocument/prepareRename might be 6490 too slow, in which case this variable may be set to nil. 6491 `lsp-rename' will then use `thing-at-point' `symbol' to determine 6492 the symbol to rename at point." 6493 :group 'lsp-mode 6494 :type 'boolean) 6495 6496 (defun lsp--get-symbol-to-rename () 6497 "Get a symbol to rename and placeholder at point. 6498 Returns a cons ((START . END) . PLACEHOLDER?), and nil if 6499 renaming is generally supported but cannot be done at point. 6500 START and END are the bounds of the identifiers being renamed, 6501 while PLACEHOLDER?, is either nil or a string suggested by the 6502 language server as the initial input of a new-name prompt." 6503 (unless (lsp-feature? "textDocument/rename") 6504 (error "The connected server(s) doesn't support renaming")) 6505 (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename")) 6506 (when-let* ((response 6507 (lsp-request "textDocument/prepareRename" 6508 (lsp--text-document-position-params)))) 6509 (let* ((bounds (lsp--range-to-region 6510 (if (lsp-range? response) 6511 response 6512 (lsp:prepare-rename-result-range response)))) 6513 (placeholder 6514 (and (not (lsp-range? response)) 6515 (lsp:prepare-rename-result-placeholder response)))) 6516 (cons bounds placeholder))) 6517 (when-let* ((bounds (bounds-of-thing-at-point 'symbol))) 6518 (cons bounds nil)))) 6519 6520 (defface lsp-face-rename '((t :underline t)) 6521 "Face used to highlight the identifier being renamed. 6522 Renaming can be done using `lsp-rename'." 6523 :group 'lsp-mode) 6524 6525 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face)) 6526 "Face used to display the rename placeholder in. 6527 When calling `lsp-rename' interactively, this will be the face of 6528 the new name." 6529 :group 'lsp-mode) 6530 6531 (defvar lsp-rename-history '() 6532 "History for `lsp--read-rename'.") 6533 6534 (defun lsp--read-rename (at-point) 6535 "Read a new name for a `lsp-rename' at `point' from the user. 6536 AT-POINT shall be a structure as returned by 6537 `lsp--get-symbol-to-rename'. 6538 6539 Returns a string, which should be the new name for the identifier 6540 at point. If renaming cannot be done at point (as determined from 6541 AT-POINT), throw a `user-error'. 6542 6543 This function is for use in `lsp-rename' only, and shall not be 6544 relied upon." 6545 (unless at-point 6546 (user-error "`lsp-rename' is invalid here")) 6547 (-let* ((((start . end) . placeholder?) at-point) 6548 ;; Do the `buffer-substring' first to not include `lsp-face-rename' 6549 (rename-me (buffer-substring start end)) 6550 (placeholder (or placeholder? rename-me)) 6551 (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face)) 6552 6553 overlay) 6554 ;; We need unwind protect, as the user might cancel here, causing the 6555 ;; overlay to linger. 6556 (unwind-protect 6557 (progn 6558 (setq overlay (make-overlay start end)) 6559 (overlay-put overlay 'face 'lsp-face-rename) 6560 6561 (read-string (format "Rename %s to: " rename-me) placeholder 6562 'lsp-rename-history)) 6563 (and overlay (delete-overlay overlay))))) 6564 6565 (defun lsp-rename (newname) 6566 "Rename the symbol (and all references to it) under point to NEWNAME." 6567 (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename)))) 6568 (when-let* ((edits (lsp-request "textDocument/rename" 6569 `( :textDocument ,(lsp--text-document-identifier) 6570 :position ,(lsp--cur-position) 6571 :newName ,newname)))) 6572 (lsp--apply-workspace-edit edits 'rename))) 6573 6574 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?) 6575 "Advice around function `rename-file'. 6576 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?. 6577 6578 This advice sends workspace/willRenameFiles before renaming file 6579 to check if server wants to apply any workspaceEdits after renamed." 6580 (if (and lsp-apply-edits-after-file-operations 6581 (lsp--send-will-rename-files-p old-name)) 6582 (let ((params (lsp-make-rename-files-params 6583 :files (vector (lsp-make-file-rename 6584 :oldUri (lsp--path-to-uri old-name) 6585 :newUri (lsp--path-to-uri new-name)))))) 6586 (when-let* ((edits (lsp-request "workspace/willRenameFiles" params))) 6587 (lsp--apply-workspace-edit edits 'rename-file) 6588 (funcall old-func old-name new-name ok-if-already-exists?) 6589 (when (lsp--send-did-rename-files-p) 6590 (lsp-notify "workspace/didRenameFiles" params)))) 6591 (funcall old-func old-name new-name ok-if-already-exists?))) 6592 6593 (advice-add 'rename-file :around #'lsp--on-rename-file) 6594 6595 (defcustom lsp-xref-force-references nil 6596 "If non-nil threat everything as references(e. g. jump if only one item.)" 6597 :group 'lsp-mode 6598 :type 'boolean) 6599 6600 (defun lsp-show-xrefs (xrefs display-action references?) 6601 (unless (region-active-p) (push-mark nil t)) 6602 (if (boundp 'xref-show-definitions-function) 6603 (with-no-warnings 6604 (xref-push-marker-stack) 6605 (funcall (if (and references? (not lsp-xref-force-references)) 6606 xref-show-xrefs-function 6607 xref-show-definitions-function) 6608 (-const xrefs) 6609 `((window . ,(selected-window)) 6610 (display-action . ,display-action) 6611 ,(if (and references? (not lsp-xref-force-references)) 6612 `(auto-jump . ,xref-auto-jump-to-first-xref) 6613 `(auto-jump . ,xref-auto-jump-to-first-definition))))) 6614 (xref--show-xrefs xrefs display-action))) 6615 6616 (cl-defmethod seq-empty-p ((ht hash-table)) 6617 "Function `seq-empty-p' for hash-table." 6618 (hash-table-empty-p ht)) 6619 6620 (cl-defun lsp-find-locations (method &optional extra &key display-action references?) 6621 "Send request named METHOD and get cross references of the symbol under point. 6622 EXTRA is a plist of extra parameters. 6623 REFERENCES? t when METHOD returns references." 6624 (let ((loc (lsp-request method 6625 (append (lsp--text-document-position-params) extra)))) 6626 (if (seq-empty-p loc) 6627 (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) "")) 6628 (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?)))) 6629 6630 (cl-defun lsp-find-declaration (&key display-action) 6631 "Find declarations of the symbol under point." 6632 (interactive) 6633 (lsp-find-locations "textDocument/declaration" nil :display-action display-action)) 6634 6635 (cl-defun lsp-find-definition (&key display-action) 6636 "Find definitions of the symbol under point." 6637 (interactive) 6638 (lsp-find-locations "textDocument/definition" nil :display-action display-action)) 6639 6640 (defun lsp-find-definition-mouse (click) 6641 "Click to start `lsp-find-definition' at clicked point." 6642 (interactive "e") 6643 (let* ((ec (event-start click)) 6644 (p1 (posn-point ec)) 6645 (w1 (posn-window ec))) 6646 (select-window w1) 6647 (goto-char p1) 6648 (lsp-find-definition))) 6649 6650 (cl-defun lsp-find-implementation (&key display-action) 6651 "Find implementations of the symbol under point." 6652 (interactive) 6653 (lsp-find-locations "textDocument/implementation" 6654 nil 6655 :display-action display-action 6656 :references? t)) 6657 6658 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action) 6659 "Find references of the symbol under point." 6660 (interactive "P") 6661 (lsp-find-locations "textDocument/references" 6662 (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-declaration))))) 6663 :display-action display-action 6664 :references? t)) 6665 6666 (cl-defun lsp-find-type-definition (&key display-action) 6667 "Find type definitions of the symbol under point." 6668 (interactive) 6669 (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action)) 6670 6671 (defalias 'lsp-find-custom #'lsp-find-locations) 6672 (defalias 'lsp-goto-implementation #'lsp-find-implementation) 6673 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition) 6674 6675 (with-eval-after-load 'evil 6676 (evil-set-command-property 'lsp-find-definition :jump t) 6677 (evil-set-command-property 'lsp-find-implementation :jump t) 6678 (evil-set-command-property 'lsp-find-references :jump t) 6679 (evil-set-command-property 'lsp-find-type-definition :jump t)) 6680 6681 (defun lsp--workspace-method-supported? (check-command method capability workspace) 6682 (with-lsp-workspace workspace 6683 (if check-command 6684 (funcall check-command workspace) 6685 (or 6686 (when capability (lsp--capability capability)) 6687 (lsp--registered-capability method) 6688 (and (not capability) (not check-command)))))) 6689 6690 (defun lsp-disable-method-for-server (method server-id) 6691 "Disable METHOD for SERVER-ID." 6692 (cl-callf 6693 (lambda (reqs) 6694 (-let (((&plist :check-command :capability) reqs)) 6695 (list :check-command 6696 (lambda (workspace) 6697 (unless (-> workspace 6698 lsp--workspace-client 6699 lsp--client-server-id 6700 (eq server-id)) 6701 (lsp--workspace-method-supported? check-command 6702 method 6703 capability 6704 workspace)))))) 6705 (alist-get method lsp-method-requirements nil nil 'string=))) 6706 6707 (defun lsp--find-workspaces-for (msg-or-method) 6708 "Find all workspaces in the current project that can handle MSG." 6709 (let ((method (if (stringp msg-or-method) 6710 msg-or-method 6711 (plist-get msg-or-method :method)))) 6712 (-if-let (reqs (cdr (assoc method lsp-method-requirements))) 6713 (-let (((&plist :capability :check-command) reqs)) 6714 (-filter 6715 (-partial #'lsp--workspace-method-supported? 6716 check-command method capability) 6717 (lsp-workspaces))) 6718 (lsp-workspaces)))) 6719 6720 (defun lsp-can-execute-command? (command-name) 6721 "Returns non-nil if current language server(s) can execute COMMAND-NAME. 6722 The command is executed via `workspace/executeCommand'" 6723 (cl-position 6724 command-name 6725 (lsp:execute-command-options-commands 6726 (lsp:server-capabilities-execute-command-provider? 6727 (lsp--server-capabilities))) 6728 :test #'equal)) 6729 6730 (defalias 'lsp-feature? 'lsp--find-workspaces-for) 6731 6732 (cl-defmethod lsp-execute-command (_server _command _arguments) 6733 "Dispatch COMMAND execution." 6734 (signal 'cl-no-applicable-method nil)) 6735 6736 (defun lsp-workspace-command-execute (command &optional args) 6737 "Execute workspace COMMAND with ARGS." 6738 (condition-case-unless-debug err 6739 (let ((params (if args 6740 (list :command command :arguments args) 6741 (list :command command)))) 6742 (lsp-request "workspace/executeCommand" params)) 6743 (error 6744 (error "`workspace/executeCommand' with `%s' failed.\n\n%S" 6745 command err)))) 6746 6747 (defun lsp-send-execute-command (command &optional args) 6748 "Create and send a `workspace/executeCommand' message having command COMMAND 6749 and optional ARGS." 6750 (lsp-workspace-command-execute command args)) 6751 6752 (defalias 'lsp-point-to-position #'lsp--point-to-position) 6753 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier) 6754 (defalias 'lsp--send-execute-command #'lsp-send-execute-command) 6755 (defalias 'lsp-on-open #'lsp--text-document-did-open) 6756 (defalias 'lsp-on-save #'lsp--text-document-did-save) 6757 6758 (defun lsp--set-configuration (settings) 6759 "Set the SETTINGS for the lsp server." 6760 (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings))) 6761 6762 (defun lsp-current-buffer () 6763 (or lsp--virtual-buffer 6764 (current-buffer))) 6765 6766 (defun lsp-buffer-live-p (buffer-id) 6767 (if-let* ((buffer-live (plist-get buffer-id :buffer-live?))) 6768 (funcall buffer-live buffer-id) 6769 (buffer-live-p buffer-id))) 6770 6771 (defun lsp--on-set-visited-file-name (old-func &rest args) 6772 "Advice around function `set-visited-file-name'. 6773 6774 This advice sends textDocument/didClose for the old file and 6775 textDocument/didOpen for the new file." 6776 (when lsp--cur-workspace 6777 (lsp--text-document-did-close t)) 6778 (prog1 (apply old-func args) 6779 (when lsp--cur-workspace 6780 (lsp--text-document-did-open)))) 6781 6782 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name) 6783 6784 (defcustom lsp-flush-delayed-changes-before-next-message t 6785 "If non-nil send the document changes update before sending other messages. 6786 6787 If nil, and `lsp-debounce-full-sync-notifications' is non-nil, 6788 change notifications will be throttled by 6789 `lsp-debounce-full-sync-notifications-interval' regardless of 6790 other messages." 6791 :group 'lsp-mode 6792 :type 'boolean) 6793 6794 (defvar lsp--not-flushing-delayed-changes t) 6795 6796 (defun lsp--send-no-wait (message proc) 6797 "Send MESSAGE to PROC without waiting for further output." 6798 6799 (when (and lsp--not-flushing-delayed-changes 6800 lsp-flush-delayed-changes-before-next-message) 6801 (let ((lsp--not-flushing-delayed-changes nil)) 6802 (lsp--flush-delayed-changes))) 6803 (lsp-process-send proc message)) 6804 6805 (define-error 'lsp-parse-error 6806 "Error parsing message from language server" 'lsp-error) 6807 (define-error 'lsp-unknown-message-type 6808 "Unknown message type" '(lsp-error lsp-parse-error)) 6809 (define-error 'lsp-unknown-json-rpc-version 6810 "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error)) 6811 (define-error 'lsp-no-content-length 6812 "Content-Length header missing in message" '(lsp-error lsp-parse-error)) 6813 (define-error 'lsp-invalid-header-name 6814 "Invalid header name" '(lsp-error lsp-parse-error)) 6815 6816 ;; id method 6817 ;; x x request 6818 ;; x . response 6819 ;; . x notification 6820 (defun lsp--get-message-type (json-data) 6821 "Get the message type from JSON-DATA." 6822 (if (lsp:json-message-id? json-data) 6823 (if (lsp:json-message-error? json-data) 6824 'response-error 6825 (if (lsp:json-message-method? json-data) 6826 'request 6827 'response)) 6828 'notification)) 6829 6830 (defconst lsp--default-notification-handlers 6831 (ht ("window/showMessage" #'lsp--window-show-message) 6832 ("window/logMessage" #'lsp--window-log-message) 6833 ("window/showInputBox" #'lsp--window-show-input-box) 6834 ("window/showQuickPick" #'lsp--window-show-quick-pick) 6835 ("textDocument/publishDiagnostics" #'lsp--on-diagnostics) 6836 ("textDocument/diagnosticsEnd" #'ignore) 6837 ("textDocument/diagnosticsBegin" #'ignore) 6838 ("telemetry/event" #'ignore) 6839 ("$/progress" (lambda (workspace params) 6840 (funcall lsp-progress-function workspace params))))) 6841 6842 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method)) 6843 "Call the appropriate handler for NOTIFICATION." 6844 (-let ((client (lsp--workspace-client workspace))) 6845 (when (lsp--log-io-p method) 6846 (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif) 6847 lsp--cur-workspace)) 6848 (if-let* ((handler (or (gethash method (lsp--client-notification-handlers client)) 6849 (gethash method lsp--default-notification-handlers)))) 6850 (funcall handler workspace params) 6851 (when (and method (not (string-prefix-p "$" method))) 6852 (lsp-warn "Unknown notification: %s" method))))) 6853 6854 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items)) 6855 "Get section configuration. 6856 PARAMS are the `workspace/configuration' request params" 6857 (->> items 6858 (-map (-lambda ((&ConfigurationItem :section?)) 6859 (-let* ((path-parts (split-string section? "\\.")) 6860 (path-without-last (s-join "." (-slice path-parts 0 -1))) 6861 (path-parts-len (length path-parts))) 6862 (cond 6863 ((<= path-parts-len 1) 6864 (ht-get (lsp-configuration-section section?) 6865 (car-safe path-parts) 6866 (ht-create))) 6867 ((> path-parts-len 1) 6868 (when-let* ((section (lsp-configuration-section path-without-last)) 6869 (keys path-parts)) 6870 (while (and keys section) 6871 (setf section (ht-get section (pop keys)))) 6872 section)))))) 6873 (apply #'vector))) 6874 6875 (defun lsp--ms-since (timestamp) 6876 "Integer number of milliseconds since TIMESTAMP. Fractions discarded." 6877 (floor (* 1000 (float-time (time-since timestamp))))) 6878 6879 (defun lsp--send-request-response (workspace recv-time request response) 6880 "Send the RESPONSE for REQUEST in WORKSPACE and log if needed." 6881 (-let* (((&JSONResponse :params :method :id) request) 6882 (process (lsp--workspace-proc workspace)) 6883 (response (lsp--make-response id response)) 6884 (req-entry (and lsp-log-io 6885 (lsp--make-log-entry method id params 'incoming-req))) 6886 (resp-entry (and lsp-log-io 6887 (lsp--make-log-entry method id response 'outgoing-resp 6888 (lsp--ms-since recv-time))))) 6889 ;; Send response to the server. 6890 (when (lsp--log-io-p method) 6891 (lsp--log-entry-new req-entry workspace) 6892 (lsp--log-entry-new resp-entry workspace)) 6893 (lsp--send-no-wait response process))) 6894 6895 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method)) 6896 "Call the appropriate handler for REQUEST, and send the return value to the 6897 server. WORKSPACE is the active workspace." 6898 (-let* ((recv-time (current-time)) 6899 (client (lsp--workspace-client workspace)) 6900 (buffers (lsp--workspace-buffers workspace)) 6901 handler 6902 (response (cond 6903 ((setq handler (gethash method (lsp--client-request-handlers client) nil)) 6904 (funcall handler workspace params)) 6905 ((setq handler (gethash method (lsp--client-async-request-handlers client) nil)) 6906 (funcall handler workspace params 6907 (-partial #'lsp--send-request-response 6908 workspace recv-time request)) 6909 'delay-response) 6910 ((equal method "client/registerCapability") 6911 (mapc #'lsp--server-register-capability 6912 (lsp:registration-params-registrations params)) 6913 (mapc (lambda (buf) 6914 (when (lsp-buffer-live-p buf) 6915 (lsp-with-current-buffer buf 6916 (lsp-unconfig-buffer) 6917 (lsp-configure-buffer)))) 6918 buffers) 6919 nil) 6920 ((equal method "window/showMessageRequest") 6921 (let ((choice (lsp--window-log-message-request params))) 6922 `(:title ,choice))) 6923 ((equal method "window/showDocument") 6924 (let ((success? (lsp--window-show-document params))) 6925 (lsp-make-show-document-result :success (or success? 6926 :json-false)))) 6927 ((equal method "client/unregisterCapability") 6928 (mapc #'lsp--server-unregister-capability 6929 (lsp:unregistration-params-unregisterations params)) 6930 (mapc (lambda (buf) 6931 (when (lsp-buffer-live-p buf) 6932 (lsp-with-current-buffer buf 6933 (lsp-unconfig-buffer) 6934 (lsp-configure-buffer)))) 6935 buffers) 6936 nil) 6937 ((equal method "workspace/applyEdit") 6938 (list :applied (condition-case err 6939 (prog1 t 6940 (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested)) 6941 (error 6942 (lsp--error "Failed to apply edits with message %s" 6943 (error-message-string err)) 6944 :json-false)))) 6945 ((equal method "workspace/configuration") 6946 (with-lsp-workspace workspace 6947 (if-let* ((buf (car buffers))) 6948 (lsp-with-current-buffer buf 6949 (lsp--build-workspace-configuration-response params)) 6950 (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace) 6951 (lsp--build-workspace-configuration-response params))))) 6952 ((equal method "workspace/workspaceFolders") 6953 (let ((folders (or (-> workspace 6954 (lsp--workspace-client) 6955 (lsp--client-server-id) 6956 (gethash (lsp-session-server-id->folders (lsp-session)))) 6957 (lsp-session-folders (lsp-session))))) 6958 (->> folders 6959 (-distinct) 6960 (-map (lambda (folder) 6961 (list :uri (lsp--path-to-uri folder)))) 6962 (apply #'vector)))) 6963 ((equal method "window/workDoneProgress/create") 6964 nil ;; no specific reply, no processing required 6965 ) 6966 ((equal method "workspace/semanticTokens/refresh") 6967 (when (and lsp-semantic-tokens-enable 6968 (fboundp 'lsp--semantic-tokens-on-refresh)) 6969 (lsp--semantic-tokens-on-refresh workspace)) 6970 nil) 6971 ((equal method "workspace/codeLens/refresh") 6972 (when (and lsp-lens-enable 6973 (fboundp 'lsp--lens-on-refresh)) 6974 (lsp--lens-on-refresh workspace)) 6975 nil) 6976 ((equal method "workspace/diagnostic/refresh") 6977 nil) 6978 (t (lsp-warn "Unknown request method: %s" method) nil)))) 6979 ;; Send response to the server. 6980 (unless (eq response 'delay-response) 6981 (lsp--send-request-response workspace recv-time request response)))) 6982 6983 (lsp-defun lsp--error-string ((&JSONError :message :code)) 6984 "Format ERR as a user friendly string." 6985 (format "Error from the Language Server: %s (%s)" 6986 message 6987 (or (car (alist-get code lsp--errors)) "Unknown error"))) 6988 6989 (defun lsp--get-body-length (headers) 6990 (let ((content-length (cdr (assoc "Content-Length" headers)))) 6991 (if content-length 6992 (string-to-number content-length) 6993 6994 ;; This usually means either the server or our parser is 6995 ;; screwed up with a previous Content-Length 6996 (error "No Content-Length header")))) 6997 6998 (defun lsp--parse-header (s) 6999 "Parse string S as a LSP (KEY . VAL) header." 7000 (let ((pos (string-match "\:" s)) 7001 key val) 7002 (unless pos 7003 (signal 'lsp-invalid-header-name (list s))) 7004 (setq key (substring s 0 pos) 7005 val (s-trim-left (substring s (+ 1 pos)))) 7006 (when (equal key "Content-Length") 7007 (cl-assert (cl-loop for c across val 7008 when (or (> c ?9) (< c ?0)) return nil 7009 finally return t) 7010 nil (format "Invalid Content-Length value: %s" val))) 7011 (cons key val))) 7012 7013 (defmacro lsp--read-json (str) 7014 "Read json string STR." 7015 (if (progn 7016 (require 'json) 7017 (fboundp 'json-parse-string)) 7018 `(json-parse-string ,str 7019 :object-type (if lsp-use-plists 7020 'plist 7021 'hash-table) 7022 :null-object nil 7023 :false-object nil) 7024 `(let ((json-array-type 'vector) 7025 (json-object-type (if lsp-use-plists 7026 'plist 7027 'hash-table)) 7028 (json-false nil)) 7029 (json-read-from-string ,str)))) 7030 7031 (defmacro lsp-json-read-buffer () 7032 "Read json from the current buffer." 7033 (if (progn 7034 (require 'json) 7035 (fboundp 'json-parse-buffer)) 7036 `(json-parse-buffer :object-type (if lsp-use-plists 7037 'plist 7038 'hash-table) 7039 :null-object nil 7040 :false-object nil) 7041 `(let ((json-array-type 'vector) 7042 (json-object-type (if lsp-use-plists 7043 'plist 7044 'hash-table)) 7045 (json-false nil)) 7046 (json-read)))) 7047 7048 (defun lsp--read-json-file (file-path) 7049 "Read json file." 7050 (-> file-path 7051 (f-read-text) 7052 (lsp--read-json))) 7053 7054 (defun lsp--parser-on-message (json-data workspace) 7055 "Called when the parser P read a complete MSG from the server." 7056 (with-demoted-errors "Error processing message %S." 7057 (with-lsp-workspace workspace 7058 (let* ((client (lsp--workspace-client workspace)) 7059 (id (--when-let (lsp:json-response-id json-data) 7060 (if (stringp it) (string-to-number it) it))) 7061 (data (lsp:json-response-result json-data))) 7062 (pcase (lsp--get-message-type json-data) 7063 ('response 7064 (cl-assert id) 7065 (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))] 7066 (when (lsp--log-io-p method) 7067 (lsp--log-entry-new 7068 (lsp--make-log-entry method id data 'incoming-resp 7069 (lsp--ms-since before-send)) 7070 workspace)) 7071 (when callback 7072 (remhash id (lsp--client-response-handlers client)) 7073 (funcall callback (lsp:json-response-result json-data))))) 7074 ('response-error 7075 (cl-assert id) 7076 (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))] 7077 (when (lsp--log-io-p method) 7078 (lsp--log-entry-new 7079 (lsp--make-log-entry method id (lsp:json-response-error-error json-data) 7080 'incoming-resp (lsp--ms-since before-send)) 7081 workspace)) 7082 (when callback 7083 (remhash id (lsp--client-response-handlers client)) 7084 (funcall callback (lsp:json-response-error-error json-data))))) 7085 ('notification 7086 (lsp--on-notification workspace json-data)) 7087 ('request (lsp--on-request workspace json-data))))))) 7088 7089 (defun lsp--create-filter-function (workspace) 7090 "Make filter for the workspace." 7091 (let ((body-received 0) 7092 leftovers body-length body chunk) 7093 (lambda (_proc input) 7094 (setf chunk (if (s-blank? leftovers) 7095 (encode-coding-string input 'utf-8-unix t) 7096 (concat leftovers (encode-coding-string input 'utf-8-unix t)))) 7097 7098 (let (messages) 7099 (while (not (s-blank? chunk)) 7100 (if (not body-length) 7101 ;; Read headers 7102 (if-let* ((body-sep-pos (string-match-p "\r\n\r\n" chunk))) 7103 ;; We've got all the headers, handle them all at once: 7104 (setf body-length (lsp--get-body-length 7105 (mapcar #'lsp--parse-header 7106 (split-string 7107 (substring-no-properties chunk 7108 (or (string-match-p "Content-Length" chunk) 7109 (error "Unable to find Content-Length header.")) 7110 body-sep-pos) 7111 "\r\n"))) 7112 body-received 0 7113 leftovers nil 7114 chunk (substring-no-properties chunk (+ body-sep-pos 4))) 7115 7116 ;; Haven't found the end of the headers yet. Save everything 7117 ;; for when the next chunk arrives and await further input. 7118 (setf leftovers chunk 7119 chunk nil)) 7120 (let* ((chunk-length (string-bytes chunk)) 7121 (left-to-receive (- body-length body-received)) 7122 (this-body (if (< left-to-receive chunk-length) 7123 (prog1 (substring-no-properties chunk 0 left-to-receive) 7124 (setf chunk (substring-no-properties chunk left-to-receive))) 7125 (prog1 chunk 7126 (setf chunk nil)))) 7127 (body-bytes (string-bytes this-body))) 7128 (push this-body body) 7129 (setf body-received (+ body-received body-bytes)) 7130 (when (>= chunk-length left-to-receive) 7131 (condition-case err 7132 (with-temp-buffer 7133 (apply #'insert 7134 (nreverse 7135 (prog1 body 7136 (setf leftovers nil 7137 body-length nil 7138 body-received nil 7139 body nil)))) 7140 (decode-coding-region (point-min) 7141 (point-max) 7142 'utf-8) 7143 (goto-char (point-min)) 7144 (push (lsp-json-read-buffer) messages)) 7145 7146 (error 7147 (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s" 7148 (concat leftovers input) 7149 err))))))) 7150 (mapc (lambda (msg) 7151 (lsp--parser-on-message msg workspace)) 7152 (nreverse messages)))))) 7153 7154 (defvar-local lsp--line-col-to-point-hash-table nil 7155 "Hash table with keys (line . col) and values that are either point positions 7156 or markers.") 7157 7158 (defcustom lsp-imenu-detailed-outline t 7159 "Whether `lsp-imenu' should include signatures. 7160 This will be ignored if the server doesn't provide the necessary 7161 information, for example if it doesn't support DocumentSymbols." 7162 :group 'lsp-imenu 7163 :type 'boolean) 7164 7165 (defcustom lsp-imenu-hide-parent-details t 7166 "Whether `lsp-imenu' should hide signatures of parent nodes." 7167 :group 'lsp-imenu 7168 :type 'boolean) 7169 7170 (defface lsp-details-face '((t :height 0.8 :inherit shadow)) 7171 "Used to display additional information throughout `lsp'. 7172 Things like line numbers, signatures, ... are considered 7173 additional information. Often, additional faces are defined that 7174 inherit from this face by default, like `lsp-signature-face', and 7175 they may be customized for finer control." 7176 :group 'lsp-mode) 7177 7178 (defface lsp-signature-face '((t :inherit lsp-details-face)) 7179 "Used to display signatures in `imenu', ...." 7180 :group 'lsp-mode) 7181 7182 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?) 7183 show-detail?) 7184 "Render INPUT0, an `&DocumentSymbol', to a string. 7185 If SHOW-DETAIL? is set, make use of its `:detail?' field (often 7186 the signature)." 7187 (let ((detail (and show-detail? (s-present? detail?) 7188 (propertize (concat " " (s-trim-left detail?)) 7189 'face 'lsp-signature-face))) 7190 (name (if deprecated? 7191 (propertize name 'face 'lsp-face-semhl-deprecated) name))) 7192 (concat name detail))) 7193 7194 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?) 7195 separator) 7196 "Render a piece of SymbolInformation. 7197 Handle :deprecated?. If SEPARATOR is non-nil, the 7198 symbol's (optional) parent, SEPARATOR and the symbol itself are 7199 concatenated." 7200 (when (and separator container-name? (not (string-empty-p container-name?))) 7201 (setq name (concat name separator container-name?))) 7202 (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name)) 7203 7204 (defun lsp--symbol-to-imenu-elem (sym) 7205 "Convert SYM to imenu element. 7206 7207 SYM is a SymbolInformation message. 7208 7209 Return a cons cell (full-name . start-point)." 7210 (let ((start-point (ht-get lsp--line-col-to-point-hash-table 7211 (lsp--get-line-and-col sym)))) 7212 (cons (lsp-render-symbol-information 7213 sym (and lsp-imenu-show-container-name 7214 lsp-imenu-container-name-separator)) 7215 start-point))) 7216 7217 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?)) 7218 "Convert SYM to hierarchical imenu elements. 7219 7220 SYM is a DocumentSymbol message. 7221 7222 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if 7223 SYM doesn't have any children. Otherwise return a cons cell with 7224 an alist 7225 7226 (\"symbol-name\" . ((\"(symbol-kind)\" . start-point) 7227 cons-cells-from-children))" 7228 (let ((filtered-children (lsp--imenu-filter-symbols children?)) 7229 (signature (lsp-render-symbol sym lsp-imenu-detailed-outline))) 7230 (if (seq-empty-p filtered-children) 7231 (cons signature 7232 (ht-get lsp--line-col-to-point-hash-table 7233 (lsp--get-line-and-col sym))) 7234 (cons signature 7235 (lsp--imenu-create-hierarchical-index filtered-children))))) 7236 7237 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind)) 7238 "Determine if SYM is for the current document and is to be shown." 7239 ;; It's a SymbolInformation or DocumentSymbol, which is always in the 7240 ;; current buffer file. 7241 (and lsp-imenu-index-symbol-kinds 7242 (numberp kind) 7243 (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup)) 7244 kind 7245 0))) 7246 (not (memql (aref lsp/symbol-kind-lookup clamped-kind) 7247 lsp-imenu-index-symbol-kinds))))) 7248 7249 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind)) 7250 "The string name of the kind of SYM." 7251 (alist-get kind lsp-symbol-kinds "Other")) 7252 7253 (defun lsp--get-line-and-col (sym) 7254 "Obtain the line and column corresponding to SYM." 7255 (-let* ((location (lsp:symbol-information-location sym)) 7256 (name-range (or (and location (lsp:location-range location)) 7257 (lsp:document-symbol-selection-range sym))) 7258 ((&Range :start (&Position :line :character)) name-range)) 7259 (cons line character))) 7260 7261 (defun lsp--collect-lines-and-cols (symbols) 7262 "Return a sorted list ((line . col) ...) of the locations of SYMBOLS." 7263 (let ((stack (mapcar 'identity symbols)) 7264 line-col-list) 7265 (while stack 7266 (let ((sym (pop stack))) 7267 (push (lsp--get-line-and-col sym) line-col-list) 7268 (unless (seq-empty-p (lsp:document-symbol-children? sym)) 7269 (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack))))) 7270 (-sort #'lsp--line-col-comparator line-col-list))) 7271 7272 (defun lsp--convert-line-col-to-points-batch (line-col-list) 7273 "Convert a sorted list of positions from line-column 7274 representation to point representation." 7275 (let ((line-col-to-point-map (ht-create)) 7276 (inhibit-field-text-motion t) 7277 (curr-line 0)) 7278 (lsp-save-restriction-and-excursion 7279 (goto-char (point-min)) 7280 (cl-loop for (line . col) in line-col-list do 7281 (forward-line (- line curr-line)) 7282 (setq curr-line line) 7283 (let ((line-end (line-end-position))) 7284 (if (or (not col) (> col (- line-end (point)))) 7285 (goto-char line-end) 7286 (forward-char col))) 7287 (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers 7288 (point-marker) 7289 (point))))) 7290 line-col-to-point-map)) 7291 7292 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2)) 7293 (or (< l1 l2) 7294 (and (= l1 l2) 7295 (cond ((and c1 c2) 7296 (< c1 c2)) 7297 (c1 t))))) 7298 7299 (defun lsp-imenu-create-uncategorized-index (symbols) 7300 "Create imenu index from document SYMBOLS. 7301 This function, unlike `lsp-imenu-create-categorized-index', does 7302 not categorize by type, but instead returns an `imenu' index 7303 corresponding to the symbol hierarchy returned by the server 7304 directly." 7305 (let* ((lsp--line-col-to-point-hash-table (-> symbols 7306 lsp--collect-lines-and-cols 7307 lsp--convert-line-col-to-points-batch))) 7308 (if (lsp--imenu-hierarchical-p symbols) 7309 (lsp--imenu-create-hierarchical-index symbols) 7310 (lsp--imenu-create-non-hierarchical-index symbols)))) 7311 7312 (defcustom lsp-imenu-symbol-kinds 7313 '((1 . "Files") 7314 (2 . "Modules") 7315 (3 . "Namespaces") 7316 (4 . "Packages") 7317 (5 . "Classes") 7318 (6 . "Methods") 7319 (7 . "Properties") 7320 (8 . "Fields") 7321 (9 . "Constructors") 7322 (10 . "Enums") 7323 (11 . "Interfaces") 7324 (12 . "Functions") 7325 (13 . "Variables") 7326 (14 . "Constants") 7327 (15 . "Strings") 7328 (16 . "Numbers") 7329 (17 . "Booleans") 7330 (18 . "Arrays") 7331 (19 . "Objects") 7332 (20 . "Keys") 7333 (21 . "Nulls") 7334 (22 . "Enum Members") 7335 (23 . "Structs") 7336 (24 . "Events") 7337 (25 . "Operators") 7338 (26 . "Type Parameters")) 7339 "`lsp-symbol-kinds', but only used by `imenu'. 7340 A new variable is needed, as it is `imenu' convention to use 7341 pluralized categories, which `lsp-symbol-kinds' doesn't. If the 7342 non-pluralized names are preferred, this can be set to 7343 `lsp-symbol-kinds'." 7344 :type '(alist :key-type integer :value-type string)) 7345 7346 (defun lsp--imenu-kind->name (kind) 7347 (alist-get kind lsp-imenu-symbol-kinds "?")) 7348 7349 (defun lsp-imenu-create-top-level-categorized-index (symbols) 7350 "Create an `imenu' index categorizing SYMBOLS by type. 7351 Only root symbols are categorized. 7352 7353 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS 7354 shall be a list of DocumentSymbols or SymbolInformation." 7355 (mapcan 7356 (-lambda ((type . symbols)) 7357 (let ((cat (lsp--imenu-kind->name type)) 7358 (symbols (lsp-imenu-create-uncategorized-index symbols))) 7359 ;; If there is no :kind (this is being defensive), or we couldn't look it 7360 ;; up, just display the symbols inline, without categories. 7361 (if cat (list (cons cat symbols)) symbols))) 7362 (sort (seq-group-by #'lsp:document-symbol-kind symbols) 7363 (-lambda ((kinda) (kindb)) (< kinda kindb))))) 7364 7365 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start))) 7366 "Convert an `&DocumentSymbol' to an `imenu' entry." 7367 (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start)) 7368 7369 (defun lsp--imenu-create-categorized-index-1 (symbols) 7370 "Returns an `imenu' index from SYMBOLS categorized by type. 7371 The result looks like this: ((\"Variables\" . (...)))." 7372 (->> 7373 symbols 7374 (mapcan 7375 (-lambda ((sym &as &DocumentSymbol :kind :children?)) 7376 (if (seq-empty-p children?) 7377 (list (list kind (lsp--symbol->imenu sym))) 7378 (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline 7379 (not lsp-imenu-hide-parent-details))))) 7380 (cons 7381 (list kind (lsp--symbol->imenu sym)) 7382 (mapcar (-lambda ((type . imenu-items)) 7383 (list type (cons parent (mapcan #'cdr imenu-items)))) 7384 (-group-by #'car (lsp--imenu-create-categorized-index-1 children?)))))))) 7385 (-group-by #'car) 7386 (mapcar 7387 (-lambda ((kind . syms)) 7388 (cons kind (mapcan #'cdr syms)))))) 7389 7390 (defun lsp--imenu-create-categorized-index (symbols) 7391 (let ((syms (lsp--imenu-create-categorized-index-1 symbols))) 7392 (dolist (sym syms) 7393 (setcar sym (lsp--imenu-kind->name (car sym)))) 7394 syms)) 7395 7396 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start)))) 7397 (cons (lsp-render-symbol-information sym nil) start)) 7398 7399 (defun lsp--imenu-create-categorized-index-flat (symbols) 7400 "Create a kind-categorized index for SymbolInformation." 7401 (mapcar (-lambda ((kind . syms)) 7402 (cons (lsp--imenu-kind->name kind) 7403 (mapcan (-lambda ((parent . children)) 7404 (let ((children (mapcar #'lsp--symbol-information->imenu children))) 7405 (if parent (list (cons parent children)) children))) 7406 (-group-by #'lsp:symbol-information-container-name? syms)))) 7407 (seq-group-by #'lsp:symbol-information-kind symbols))) 7408 7409 (defun lsp-imenu-create-categorized-index (symbols) 7410 (if (lsp--imenu-hierarchical-p symbols) 7411 (lsp--imenu-create-categorized-index symbols) 7412 (lsp--imenu-create-categorized-index-flat symbols))) 7413 7414 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index 7415 "Function that should create an `imenu' index. 7416 It will be called with a list of SymbolInformation or 7417 DocumentSymbols, whose first level is already filtered. It shall 7418 then return an appropriate `imenu' index (see 7419 `imenu-create-index-function'). 7420 7421 Note that this interface is not stable, and subject to change any 7422 time." 7423 :group 'lsp-imenu 7424 :type '(radio 7425 (const :tag "Categorize by type" 7426 lsp-imenu-create-categorized-index) 7427 (const :tag "Categorize root symbols by type" 7428 lsp-imenu-create-top-level-categorized-index) 7429 (const :tag "Uncategorized, inline entries" 7430 lsp-imenu-create-uncategorized-index) 7431 (function :tag "Custom function"))) 7432 7433 (defun lsp--imenu-create-index () 7434 "Create an `imenu' index based on the language server. 7435 Respects `lsp-imenu-index-function'." 7436 (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols)))) 7437 (funcall lsp-imenu-index-function symbols))) 7438 7439 (defun lsp--imenu-filter-symbols (symbols) 7440 "Filter out unsupported symbols from SYMBOLS." 7441 (seq-remove #'lsp--symbol-ignore symbols)) 7442 7443 (defun lsp--imenu-hierarchical-p (symbols) 7444 "Determine whether any element in SYMBOLS has children." 7445 (seq-some #'lsp-document-symbol? symbols)) 7446 7447 (defun lsp--imenu-create-non-hierarchical-index (symbols) 7448 "Create imenu index for non-hierarchical SYMBOLS. 7449 7450 SYMBOLS are a list of DocumentSymbol messages. 7451 7452 Return a nested alist keyed by symbol names. e.g. 7453 7454 ((\"SomeClass\" (\"(Class)\" . 10) 7455 (\"someField (Field)\" . 20) 7456 (\"someFunction (Function)\" . 25) 7457 (\"SomeSubClass\" (\"(Class)\" . 30) 7458 (\"someSubField (Field)\" . 35)) 7459 (\"someFunction (Function)\" . 40))" 7460 (seq-map (lambda (nested-alist) 7461 (cons (car nested-alist) 7462 (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist)))) 7463 (seq-group-by #'lsp--get-symbol-type symbols))) 7464 7465 (defun lsp--imenu-create-hierarchical-index (symbols) 7466 "Create imenu index for hierarchical SYMBOLS. 7467 7468 SYMBOLS are a list of DocumentSymbol messages. 7469 7470 Return a nested alist keyed by symbol names. e.g. 7471 7472 ((\"SomeClass\" (\"(Class)\" . 10) 7473 (\"someField (Field)\" . 20) 7474 (\"someFunction (Function)\" . 25) 7475 (\"SomeSubClass\" (\"(Class)\" . 30) 7476 (\"someSubField (Field)\" . 35)) 7477 (\"someFunction (Function)\" . 40))" 7478 (seq-map #'lsp--symbol-to-hierarchical-imenu-elem 7479 (seq-sort #'lsp--imenu-symbol-lessp symbols))) 7480 7481 (defun lsp--imenu-symbol-lessp (sym1 sym2) 7482 (let* ((compare-results (mapcar (lambda (method) 7483 (funcall (alist-get method lsp--imenu-compare-function-alist) 7484 sym1 sym2)) 7485 lsp-imenu-sort-methods)) 7486 (result (seq-find (lambda (result) 7487 (not (= result 0))) 7488 compare-results 7489 0))) 7490 (and (numberp result) (< result 0)))) 7491 7492 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left) 7493 (&SymbolInformation :kind right)) 7494 "Compare SYM1 and SYM2 by kind." 7495 (- left right)) 7496 7497 (defun lsp--imenu-compare-line-col (sym1 sym2) 7498 (if (lsp--line-col-comparator 7499 (lsp--get-line-and-col sym1) 7500 (lsp--get-line-and-col sym2)) 7501 -1 7502 1)) 7503 7504 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1) 7505 (&SymbolInformation :name name2)) 7506 "Compare SYM1 and SYM2 by name." 7507 (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2)))) 7508 (if (numberp result) result 0))) 7509 7510 (defun lsp--imenu-refresh () 7511 "Force Imenu to refresh itself." 7512 (imenu--menubar-select imenu--rescan-item)) 7513 7514 (defun lsp-enable-imenu () 7515 "Use lsp-imenu for the current buffer." 7516 (imenu--cleanup) 7517 (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index) 7518 (setq-local imenu-menubar-modified-tick -1) 7519 (setq-local imenu--index-alist nil) 7520 (when menu-bar-mode 7521 (lsp--imenu-refresh))) 7522 7523 (defun lsp-resolve-final-command (command &optional test?) 7524 "Resolve final function COMMAND." 7525 (let* ((command (lsp-resolve-value command)) 7526 (command (cl-etypecase command 7527 (list 7528 (cl-assert (seq-every-p (apply-partially #'stringp) command) nil 7529 "Invalid command list") 7530 command) 7531 (string (list command))))) 7532 (if (and (file-remote-p default-directory) (not test?)) 7533 (list shell-file-name "-c" 7534 (string-join (cons "stty raw > /dev/null;" 7535 (mapcar #'shell-quote-argument command)) 7536 " ")) 7537 command))) 7538 7539 (defun lsp-server-present? (final-command) 7540 "Check whether FINAL-COMMAND is present." 7541 (let ((binary-found? (executable-find (cl-first final-command) t))) 7542 (if binary-found? 7543 (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command)) 7544 (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command))) 7545 binary-found?)) 7546 7547 (defun lsp--value-to-string (value) 7548 "Convert VALUE to a string that can be set as value in an environment 7549 variable." 7550 (cond 7551 ((stringp value) value) 7552 ((booleanp value) (if value 7553 "1" 7554 "0")) 7555 ((and (sequencep value) 7556 (seq-every-p #'stringp value)) (string-join value ":")) 7557 (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables")))) 7558 7559 (defun lsp--compute-process-environment (environment-fn) 7560 "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'. 7561 Ignore non-boolean keys whose value is nil." 7562 (let ((environment (if environment-fn 7563 (funcall environment-fn) 7564 nil))) 7565 (-flatten (cons (cl-loop for (key . value) in environment 7566 if (or (eval value) 7567 (eq (get value 'custom-type) 'boolean)) 7568 collect (concat key "=" (lsp--value-to-string 7569 (eval value)))) 7570 process-environment)))) 7571 7572 (defun lsp--default-directory-for-connection (&optional path) 7573 "Return path to be used for the working directory of a LSP process. 7574 7575 If `lsp-use-workspace-root-for-server-default-directory' is 7576 non-nil, uses `lsp-workspace-root' to find the directory 7577 corresponding to PATH, else returns `default-directory'." 7578 (if lsp-use-workspace-root-for-server-default-directory 7579 (lsp-workspace-root path) 7580 default-directory)) 7581 7582 (defun lsp--fix-remote-cmd (program) 7583 "Helper for `lsp-stdio-connection'. 7584 Originally coppied from eglot." 7585 7586 (if (file-remote-p default-directory) 7587 (list shell-file-name "-c" 7588 (string-join (cons "stty raw > /dev/null;" 7589 (mapcar #'shell-quote-argument program)) 7590 " ")) 7591 program)) 7592 7593 (defvar tramp-use-ssh-controlmaster-options) 7594 (defvar tramp-ssh-controlmaster-options) 7595 7596 (defun lsp-stdio-connection (command &optional test-command) 7597 "Returns a connection property list using COMMAND. 7598 COMMAND can be: A string, denoting the command to launch the 7599 language server. A list of strings, denoting an executable with 7600 its command line arguments. A function, that either returns a 7601 string or a list of strings. In all cases, the launched language 7602 server should send and receive messages on standard I/O. 7603 TEST-COMMAND is a function with no arguments which returns 7604 whether the command is present or not. When not specified 7605 `lsp-mode' will check whether the first element of the list 7606 returned by COMMAND is available via `executable-find'" 7607 (cl-check-type command (or string 7608 function 7609 (and list 7610 (satisfies (lambda (l) 7611 (seq-every-p (lambda (el) 7612 (stringp el)) 7613 l)))))) 7614 (list :connect (lambda (filter sentinel name environment-fn workspace) 7615 (if (and (functionp 'json-rpc-connection) 7616 (not (file-remote-p default-directory))) 7617 (lsp-json-rpc-connection workspace (lsp-resolve-final-command command)) 7618 (let ((final-command (lsp-resolve-final-command command)) 7619 (process-name (generate-new-buffer-name name)) 7620 (process-environment 7621 (lsp--compute-process-environment environment-fn))) 7622 (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name))) 7623 (default-directory (lsp--default-directory-for-connection)) 7624 (tramp-use-ssh-controlmaster-options 'suppress) 7625 (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none") 7626 (proc (make-process 7627 :name process-name 7628 :connection-type 'pipe 7629 :buffer (format "*%s*" process-name) 7630 :coding 'no-conversion 7631 :command final-command 7632 :filter filter 7633 :sentinel sentinel 7634 :stderr stderr-buf 7635 :noquery t 7636 :file-handler t))) 7637 (set-process-query-on-exit-flag proc nil) 7638 (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) 7639 (with-current-buffer (get-buffer stderr-buf) 7640 ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc. 7641 (special-mode)) 7642 (cons proc proc))))) 7643 :test? (or 7644 test-command 7645 (lambda () 7646 (lsp-server-present? (lsp-resolve-final-command command t)))))) 7647 7648 (defun lsp--open-network-stream (host port name) 7649 "Open network stream to HOST:PORT. 7650 NAME will be passed to `open-network-stream'. 7651 RETRY-COUNT is the number of the retries. 7652 SLEEP-INTERVAL is the sleep interval between each retry." 7653 (let* ((retries 0) 7654 (sleep-interval 0.01) 7655 (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval)) 7656 connection) 7657 (while (and (not connection) (< retries number-of-retries)) 7658 (condition-case err 7659 (setq connection (open-network-stream name nil host port 7660 :type 'plain 7661 :coding 'no-conversion)) 7662 (file-error 7663 (let ((inhibit-message t)) 7664 (lsp--warn "Failed to connect to %s:%s with error message %s" 7665 host 7666 port 7667 (error-message-string err)) 7668 (sleep-for sleep-interval) 7669 (cl-incf retries))))) 7670 (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port)))) 7671 7672 (defun lsp--port-available (host port) 7673 "Return non-nil if HOST and PORT are available." 7674 (condition-case _err 7675 (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain)) 7676 (file-error t))) 7677 7678 (defun lsp--find-available-port (host starting-port) 7679 "Find available port on HOST starting from STARTING-PORT." 7680 (let ((port starting-port)) 7681 (while (not (lsp--port-available host port)) 7682 (cl-incf port)) 7683 port)) 7684 7685 (defun lsp-tcp-connection (command-fn) 7686 "Returns a connection property list similar to `lsp-stdio-connection'. 7687 COMMAND-FN can only be a function that takes a single argument, a 7688 port number. It should return a command for launches a language server 7689 process listening for TCP connections on the provided port." 7690 (cl-check-type command-fn function) 7691 (list 7692 :connect (lambda (filter sentinel name environment-fn _workspace) 7693 (let* ((host "localhost") 7694 (port (lsp--find-available-port host (cl-incf lsp--tcp-port))) 7695 (command (funcall command-fn port)) 7696 (final-command (if (consp command) command (list command))) 7697 (_ (unless (lsp-server-present? final-command) 7698 (user-error (format "Couldn't find executable %s" (cl-first final-command))))) 7699 (process-environment 7700 (lsp--compute-process-environment environment-fn)) 7701 (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion 7702 :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t)) 7703 (tcp-proc (lsp--open-network-stream host port (concat name "::tcp")))) 7704 7705 ;; TODO: Same :noquery issue (see above) 7706 (set-process-query-on-exit-flag proc nil) 7707 (set-process-query-on-exit-flag tcp-proc nil) 7708 (set-process-filter tcp-proc filter) 7709 (cons tcp-proc proc))) 7710 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7711 7712 (defalias 'lsp-tcp-server 'lsp-tcp-server-command) 7713 7714 (defun lsp-tcp-server-command (command-fn) 7715 "Create tcp server connection. 7716 In this mode Emacs is TCP server and the language server connects 7717 to it. COMMAND is function with one parameter(the port) and it 7718 should return the command to start the LS server." 7719 (cl-check-type command-fn function) 7720 (list 7721 :connect (lambda (filter sentinel name environment-fn _workspace) 7722 (let* (tcp-client-connection 7723 (tcp-server (make-network-process :name (format "*tcp-server-%s*" name) 7724 :buffer (format "*tcp-server-%s*" name) 7725 :family 'ipv4 7726 :service lsp--tcp-server-port 7727 :sentinel (lambda (proc _string) 7728 (lsp-log "Language server %s is connected." name) 7729 (setf tcp-client-connection proc)) 7730 :server 't)) 7731 (port (process-contact tcp-server :service)) 7732 (final-command (funcall command-fn port)) 7733 (process-environment 7734 (lsp--compute-process-environment environment-fn)) 7735 (cmd-proc (make-process :name name 7736 :connection-type 'pipe 7737 :coding 'no-conversion 7738 :command final-command 7739 :stderr (format "*tcp-server-%s*::stderr" name) 7740 :noquery t))) 7741 (let ((retries 0)) 7742 ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds) 7743 (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds))) 7744 (lsp--info "Waiting for connection for %s, retries: %s" name retries) 7745 (sit-for 0.500) 7746 (cl-incf retries))) 7747 7748 (unless tcp-client-connection 7749 (condition-case nil (delete-process tcp-server) (error)) 7750 (condition-case nil (delete-process cmd-proc) (error)) 7751 (error "Failed to create connection to %s on port %s" name port)) 7752 (lsp--info "Successfully connected to %s" name) 7753 7754 (set-process-query-on-exit-flag cmd-proc nil) 7755 (set-process-query-on-exit-flag tcp-client-connection nil) 7756 (set-process-query-on-exit-flag tcp-server nil) 7757 7758 (set-process-filter tcp-client-connection filter) 7759 (set-process-sentinel tcp-client-connection sentinel) 7760 (cons tcp-client-connection cmd-proc))) 7761 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7762 7763 (defalias 'lsp-tramp-connection 'lsp-stdio-connection) 7764 7765 (defun lsp--auto-configure () 7766 "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed." 7767 (when (functionp 'lsp-ui-mode) 7768 (lsp-ui-mode)) 7769 7770 (if lsp-headerline-breadcrumb-enable 7771 (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode) 7772 (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)) 7773 (if lsp-modeline-code-actions-enable 7774 (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode) 7775 (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)) 7776 (if lsp-modeline-diagnostics-enable 7777 (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode) 7778 (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)) 7779 (if lsp-modeline-workspace-status-enable 7780 (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode) 7781 (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)) 7782 (if lsp-lens-enable 7783 (add-hook 'lsp-configure-hook 'lsp-lens--enable) 7784 (remove-hook 'lsp-configure-hook 'lsp-lens--enable)) 7785 (if lsp-semantic-tokens-enable 7786 (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable) 7787 (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)) 7788 7789 ;; yas-snippet config 7790 (setq-local yas-inhibit-overlay-modification-protection t)) 7791 7792 (defun lsp--restart-if-needed (workspace) 7793 "Handler restart for WORKSPACE." 7794 (when (or (eq lsp-restart 'auto-restart) 7795 (eq (lsp--workspace-shutdown-action workspace) 'restart) 7796 (and (eq lsp-restart 'interactive) 7797 (let ((query (format 7798 "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?" 7799 (lsp--workspace-print workspace)))) 7800 (y-or-n-p query)))) 7801 (--each (lsp--workspace-buffers workspace) 7802 (when (lsp-buffer-live-p it) 7803 (lsp-with-current-buffer it 7804 (if lsp--buffer-deferred 7805 (lsp-deferred) 7806 (lsp--info "Restarting LSP in buffer %s" (buffer-name)) 7807 (lsp))))))) 7808 7809 (defun lsp--update-key (table key fn) 7810 "Apply FN on value corresponding to KEY in TABLE." 7811 (let ((existing-value (gethash key table))) 7812 (if-let* ((new-value (funcall fn existing-value))) 7813 (puthash key new-value table) 7814 (remhash key table)))) 7815 7816 (defun lsp--process-sentinel (workspace process exit-str) 7817 "Create the sentinel for WORKSPACE." 7818 (unless (process-live-p process) 7819 (lsp--handle-process-exit workspace exit-str))) 7820 7821 (defun lsp--handle-process-exit (workspace exit-str) 7822 (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session))) 7823 (proc (lsp--workspace-proc workspace))) 7824 (lsp--warn "%s has exited (%s)" 7825 (lsp-process-name proc) 7826 (string-trim-right (or exit-str ""))) 7827 (with-lsp-workspace workspace 7828 ;; Clean workspace related data in each of the buffers 7829 ;; in the workspace. 7830 (--each (lsp--workspace-buffers workspace) 7831 (when (lsp-buffer-live-p it) 7832 (lsp-with-current-buffer it 7833 (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces)) 7834 (lsp--uninitialize-workspace) 7835 (lsp--spinner-stop) 7836 (lsp--remove-overlays 'lsp-highlight)))) 7837 7838 ;; Cleanup session from references to the closed workspace. 7839 (--each (hash-table-keys folder->workspaces) 7840 (lsp--update-key folder->workspaces it (apply-partially 'delete workspace))) 7841 7842 (lsp-process-cleanup proc)) 7843 7844 (run-hook-with-args 'lsp-after-uninitialized-functions workspace) 7845 7846 (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown) 7847 (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace)) 7848 (lsp--restart-if-needed workspace)) 7849 (lsp--cleanup-hanging-watches))) 7850 7851 (defun lsp-workspace-folders (workspace) 7852 "Return all folders associated with WORKSPACE." 7853 (let (result) 7854 (->> (lsp-session) 7855 (lsp-session-folder->servers) 7856 (maphash (lambda (folder workspaces) 7857 (when (-contains? workspaces workspace) 7858 (push folder result))))) 7859 result)) 7860 7861 (defun lsp--start-workspace (session client-template root &optional initialization-options) 7862 "Create new workspace for CLIENT-TEMPLATE with project root ROOT. 7863 INITIALIZATION-OPTIONS are passed to initialize function. 7864 SESSION is the active session." 7865 (lsp--spinner-start) 7866 (-let* ((default-directory root) 7867 (client (copy-lsp--client client-template)) 7868 (workspace (make-lsp--workspace 7869 :root root 7870 :client client 7871 :status 'starting 7872 :buffers (list (lsp-current-buffer)) 7873 :host-root (file-remote-p root))) 7874 ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities 7875 'multi-root 'initialized-fn) client) 7876 ((proc . cmd-proc) (funcall 7877 (or (plist-get new-connection :connect) 7878 (user-error "Client %s is configured incorrectly" client)) 7879 (lsp--create-filter-function workspace) 7880 (apply-partially #'lsp--process-sentinel workspace) 7881 (format "%s" server-id) 7882 environment-fn 7883 workspace)) 7884 (workspace-folders (gethash server-id (lsp-session-server-id->folders session)))) 7885 (setf (lsp--workspace-proc workspace) proc 7886 (lsp--workspace-cmd-proc workspace) cmd-proc) 7887 7888 ;; update (lsp-session-folder->servers) depending on whether we are starting 7889 ;; multi/single folder workspace 7890 (mapc (lambda (project-root) 7891 (->> session 7892 (lsp-session-folder->servers) 7893 (gethash project-root) 7894 (cl-pushnew workspace))) 7895 (or workspace-folders (list root))) 7896 7897 (with-lsp-workspace workspace 7898 (run-hooks 'lsp-before-initialize-hook) 7899 (lsp-request-async 7900 "initialize" 7901 (append 7902 (list :processId (unless (file-remote-p (buffer-file-name)) 7903 (emacs-pid)) 7904 :rootPath (lsp-file-local-name (expand-file-name root)) 7905 :clientInfo (list :name "emacs" 7906 :version (emacs-version)) 7907 :rootUri (lsp--path-to-uri root) 7908 :capabilities (lsp--client-capabilities custom-capabilities) 7909 :initializationOptions initialization-options 7910 :workDoneToken "1") 7911 (when lsp-server-trace 7912 (list :trace lsp-server-trace)) 7913 (when multi-root 7914 (->> workspace-folders 7915 (-distinct) 7916 (-map (lambda (folder) 7917 (list :uri (lsp--path-to-uri folder) 7918 :name (f-filename folder)))) 7919 (apply 'vector) 7920 (list :workspaceFolders)))) 7921 (-lambda ((&InitializeResult :capabilities)) 7922 ;; we know that Rust Analyzer will send {} which will be parsed as null 7923 ;; when using plists 7924 (when (equal 'rust-analyzer server-id) 7925 (-> capabilities 7926 (lsp:server-capabilities-text-document-sync?) 7927 (lsp:set-text-document-sync-options-save? t))) 7928 7929 (setf (lsp--workspace-server-capabilities workspace) capabilities 7930 (lsp--workspace-status workspace) 'initialized) 7931 7932 (with-lsp-workspace workspace 7933 (lsp-notify "initialized" lsp--empty-ht)) 7934 7935 (when initialized-fn (funcall initialized-fn workspace)) 7936 7937 (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace)) 7938 (->> workspace 7939 (lsp--workspace-buffers) 7940 (mapc (lambda (buffer) 7941 (lsp-with-current-buffer buffer 7942 (lsp--open-in-workspace workspace))))) 7943 7944 (with-lsp-workspace workspace 7945 (run-hooks 'lsp-after-initialize-hook)) 7946 (lsp--info "%s initialized successfully in folders: %s" 7947 (lsp--workspace-print workspace) 7948 (lsp-workspace-folders workspace))) 7949 :mode 'detached)) 7950 workspace)) 7951 7952 (defun lsp--load-default-session () 7953 "Load default session." 7954 (setq lsp--session (or (condition-case err 7955 (lsp--read-from-file lsp-session-file) 7956 (error (lsp--error "Failed to parse the session %s, starting with clean one." 7957 (error-message-string err)) 7958 nil)) 7959 (make-lsp-session)))) 7960 7961 (defun lsp-session () 7962 "Get the session associated with the current buffer." 7963 (or lsp--session (setq lsp--session (lsp--load-default-session)))) 7964 7965 (defun lsp--client-disabled-p (buffer-major-mode client) 7966 (seq-some 7967 (lambda (entry) 7968 (pcase entry 7969 ((pred symbolp) (eq entry client)) 7970 (`(,mode . ,client-or-list) 7971 (and (eq mode buffer-major-mode) 7972 (if (listp client-or-list) 7973 (memq client client-or-list) 7974 (eq client client-or-list)))))) 7975 lsp-disabled-clients)) 7976 7977 7978 ;; download server 7979 7980 (defcustom lsp-server-install-dir (expand-file-name 7981 (locate-user-emacs-file (f-join ".cache" "lsp"))) 7982 "Directory in which the servers will be installed." 7983 :risky t 7984 :type 'directory 7985 :package-version '(lsp-mode . "6.3") 7986 :group 'lsp-mode) 7987 7988 (defcustom lsp-verify-signature t 7989 "Whether to check GPG signatures of downloaded files." 7990 :type 'boolean 7991 :package-version '(lsp-mode . "8.0.0") 7992 :group 'lsp-mode) 7993 7994 (defvar lsp--dependencies (ht)) 7995 7996 (defun lsp-dependency (name &rest definitions) 7997 "Used to specify a language server DEPENDENCY, the server 7998 executable or other required file path. Typically, the 7999 DEPENDENCY is found by locating it on the system path using 8000 `executable-find'. 8001 8002 You can explicitly call lsp-dependency in your environment to 8003 specify the absolute path to the DEPENDENCY. For example, the 8004 typescript-language-server requires both the server and the 8005 typescript compiler. If you have installed them in a team shared 8006 read-only location, you can instruct lsp-mode to use them via 8007 8008 (eval-after-load `lsp-mode 8009 `(progn 8010 (require lsp-javascript) 8011 (lsp-dependency typescript-language-server (:system ,tls-exe)) 8012 (lsp-dependency typescript (:system ,ts-js)))) 8013 8014 where tls-exe is the absolute path to the typescript-language-server 8015 executable and ts-js is the absolute path to the typescript compiler 8016 JavaScript file, tsserver.js (the *.js is required for Windows)." 8017 (ht-set lsp--dependencies name definitions)) 8018 8019 (defun lsp--server-binary-present? (client) 8020 (unless (equal (lsp--client-server-id client) 'lsp-pwsh) 8021 (condition-case () 8022 (-some-> client lsp--client-new-connection (plist-get :test?) funcall) 8023 (error nil) 8024 (args-out-of-range nil)))) 8025 8026 (define-minor-mode lsp-installation-buffer-mode 8027 "Mode used in *lsp-installation* buffers. 8028 It can be used to set-up keybindings, etc. Disabling this mode 8029 detaches the installation buffer from commands like 8030 `lsp-select-installation-buffer'." 8031 :init-value nil 8032 :lighter nil) 8033 8034 (defface lsp-installation-finished-buffer-face '((t :foreground "orange")) 8035 "Face used for finished installation buffers. 8036 Used in `lsp-select-installation-buffer'." 8037 :group 'lsp-mode) 8038 8039 (defface lsp-installation-buffer-face '((t :foreground "green")) 8040 "Face used for installation buffers still in progress. 8041 Used in `lsp-select-installation-buffer'." 8042 :group 'lsp-mode) 8043 8044 (defun lsp--installation-buffer? (buf) 8045 "Check whether BUF is an `lsp-async-start-process' buffer." 8046 (buffer-local-value 'lsp-installation-buffer-mode buf)) 8047 8048 (defun lsp-select-installation-buffer (&optional show-finished) 8049 "Interactively choose an installation buffer. 8050 If SHOW-FINISHED is set, leftover (finished) installation buffers 8051 are still shown." 8052 (interactive "P") 8053 (let ((bufs (--filter (and (lsp--installation-buffer? it) 8054 (or show-finished (get-buffer-process it))) 8055 (buffer-list)))) 8056 (pcase bufs 8057 (`nil (user-error "No installation buffers")) 8058 (`(,buf) (pop-to-buffer buf)) 8059 (bufs (pop-to-buffer (completing-read "Select installation buffer: " 8060 (--map (propertize (buffer-name it) 'face 8061 (if (get-buffer-process it) 8062 'lsp-installation-buffer-face 8063 'lsp-installation-finished-buffer-face)) 8064 bufs))))))) 8065 8066 (defun lsp-cleanup-installation-buffers () 8067 "Delete finished *lsp-installation* buffers." 8068 (interactive) 8069 (dolist (buf (buffer-list)) 8070 (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf))) 8071 (kill-buffer buf)))) 8072 8073 (defun lsp--download-status () 8074 (-some--> #'lsp--client-download-in-progress? 8075 (lsp--filter-clients it) 8076 (-map (-compose #'symbol-name #'lsp--client-server-id) it) 8077 (format "%s" it) 8078 (propertize it 'face 'success) 8079 (format " Installing following servers: %s" it) 8080 (propertize it 8081 'local-map (make-mode-line-mouse-map 8082 'mouse-1 #'lsp-select-installation-buffer) 8083 'mouse-face 'highlight))) 8084 8085 (defun lsp--install-server-internal (client &optional update?) 8086 (unless (lsp--client-download-server-fn client) 8087 (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation." 8088 (lsp--client-server-id client))) 8089 8090 (setf (lsp--client-download-in-progress? client) t) 8091 (add-to-list 'global-mode-string '(t (:eval (lsp--download-status)))) 8092 (cl-flet ((done 8093 (success? &optional error-message) 8094 ;; run with idle timer to make sure the lsp command is executed in 8095 ;; the main thread, see #2739. 8096 (run-with-timer 8097 0.0 8098 nil 8099 (lambda () 8100 (-let [(&lsp-cln 'server-id 'buffers) client] 8101 (setf (lsp--client-download-in-progress? client) nil 8102 (lsp--client-buffers client) nil) 8103 (if success? 8104 (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id 8105 (length buffers)) 8106 (lsp--error "Server %s install process failed with the following error message: %s. 8107 Check `*lsp-install*' and `*lsp-log*' buffer." 8108 server-id 8109 error-message)) 8110 (seq-do 8111 (lambda (buffer) 8112 (when (lsp-buffer-live-p buffer) 8113 (lsp-with-current-buffer buffer 8114 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8115 global-mode-string) 8116 (when success? (lsp))))) 8117 buffers) 8118 (unless (lsp--filter-clients #'lsp--client-download-in-progress?) 8119 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8120 global-mode-string))))))) 8121 (lsp--info "Download %s started." (lsp--client-server-id client)) 8122 (condition-case err 8123 (funcall 8124 (lsp--client-download-server-fn client) 8125 client 8126 (lambda () (done t)) 8127 (lambda (msg) (done nil msg)) 8128 update?) 8129 (error 8130 (done nil (error-message-string err)))))) 8131 8132 (defun lsp--require-packages () 8133 "Load `lsp-client-packages' if needed." 8134 (when (and lsp-auto-configure (not lsp--client-packages-required)) 8135 (seq-do (lambda (package) 8136 ;; loading client is slow and `lsp' can be called repeatedly 8137 (unless (featurep package) 8138 (require package nil t))) 8139 lsp-client-packages) 8140 (setq lsp--client-packages-required t))) 8141 8142 ;;;###autoload 8143 (defun lsp-install-server (update? &optional server-id) 8144 "Interactively install or re-install server. 8145 When prefix UPDATE? is t force installation even if the server is present." 8146 (interactive "P") 8147 (lsp--require-packages) 8148 (let* ((chosen-client (or (gethash server-id lsp-clients) 8149 (lsp--completing-read 8150 "Select server to install/re-install: " 8151 (or (->> lsp-clients 8152 (ht-values) 8153 (-filter (-andfn 8154 (-not #'lsp--client-download-in-progress?) 8155 #'lsp--client-download-server-fn))) 8156 (user-error "There are no servers with automatic installation")) 8157 (lambda (client) 8158 (let ((server-name (-> client lsp--client-server-id symbol-name))) 8159 (if (lsp--server-binary-present? client) 8160 (concat server-name " (Already installed)") 8161 server-name))) 8162 nil 8163 t))) 8164 (update? (or update? 8165 (and (not (lsp--client-download-in-progress? chosen-client)) 8166 (lsp--server-binary-present? chosen-client))))) 8167 (lsp--install-server-internal chosen-client update?))) 8168 8169 ;;;###autoload 8170 (defun lsp-uninstall-server (dir) 8171 "Delete a LSP server from `lsp-server-install-dir'." 8172 (interactive 8173 (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir)))) 8174 (unless (file-directory-p dir) 8175 (user-error "Couldn't find %s directory" dir)) 8176 (delete-directory dir 'recursive) 8177 (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir)))) 8178 8179 ;;;###autoload 8180 (defun lsp-uninstall-servers () 8181 "Uninstall all installed servers." 8182 (interactive) 8183 (let* ((dir lsp-server-install-dir) 8184 (servers (ignore-errors 8185 (directory-files dir t 8186 directory-files-no-dot-files-regexp)))) 8187 (if (or (not (file-directory-p dir)) (zerop (length servers))) 8188 (user-error "No servers to uninstall") 8189 (when (yes-or-no-p 8190 (format "Servers to uninstall: %d (%s), proceed? " 8191 (length servers) 8192 (mapconcat (lambda (server) 8193 (file-name-nondirectory (directory-file-name server))) 8194 servers " "))) 8195 (mapc #'lsp-uninstall-server servers) 8196 (message "All servers uninstalled"))))) 8197 8198 ;;;###autoload 8199 (defun lsp-update-server (&optional server-id) 8200 "Interactively update (reinstall) a server." 8201 (interactive) 8202 (lsp--require-packages) 8203 (let ((chosen-client (or (gethash server-id lsp-clients) 8204 (lsp--completing-read 8205 "Select server to update (if not on the list, probably you need to `lsp-install-server`): " 8206 (or (->> lsp-clients 8207 (ht-values) 8208 (-filter (-andfn 8209 (-not #'lsp--client-download-in-progress?) 8210 #'lsp--client-download-server-fn 8211 #'lsp--server-binary-present?))) 8212 (user-error "There are no servers to update")) 8213 (lambda (client) 8214 (-> client lsp--client-server-id symbol-name)) 8215 nil 8216 t)))) 8217 (lsp--install-server-internal chosen-client t))) 8218 8219 ;;;###autoload 8220 (defun lsp-update-servers () 8221 "Update (reinstall) all installed servers." 8222 (interactive) 8223 (lsp--require-packages) 8224 (mapc (lambda (client) (lsp--install-server-internal client t)) 8225 (-filter (-andfn 8226 (-not #'lsp--client-download-in-progress?) 8227 #'lsp--client-download-server-fn 8228 #'lsp--server-binary-present?) (hash-table-values lsp-clients)))) 8229 8230 ;;;###autoload 8231 (defun lsp-ensure-server (server-id) 8232 "Ensure server SERVER-ID" 8233 (lsp--require-packages) 8234 (if-let* ((client (gethash server-id lsp-clients))) 8235 (unless (lsp--server-binary-present? client) 8236 (lsp--info "Server `%s' is not preset, installing..." server-id) 8237 (lsp-install-server nil server-id)) 8238 (warn "Unable to find server registration with id %s" server-id))) 8239 8240 (defun lsp-async-start-process (callback error-callback &rest command) 8241 "Start async process COMMAND with CALLBACK and ERROR-CALLBACK." 8242 (let ((name (cl-first command))) 8243 (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd) 8244 (not (null cmd))) 8245 command) 8246 " ") t 8247 (lambda (&rest _) 8248 (generate-new-buffer-name (format "*lsp-install: %s*" name)))) 8249 (lsp-installation-buffer-mode +1) 8250 (view-mode +1) 8251 (add-hook 8252 'compilation-finish-functions 8253 (lambda (_buf status) 8254 (if (string= "finished\n" status) 8255 (condition-case err 8256 (funcall callback) 8257 (error 8258 (funcall error-callback (error-message-string err)))) 8259 (funcall error-callback (s-trim-right status)))) 8260 nil t)))) 8261 8262 (defun lsp-resolve-value (value) 8263 "Resolve VALUE's value. 8264 If it is function - call it. 8265 If it is a variable - return it's value 8266 Otherwise returns value itself." 8267 (cond 8268 ((functionp value) (funcall value)) 8269 ((and (symbolp value) (boundp value)) (symbol-value value)) 8270 (value))) 8271 8272 (defvar lsp-deps-providers 8273 (list :npm (list :path #'lsp--npm-dependency-path 8274 :install #'lsp--npm-dependency-install) 8275 :cargo (list :path #'lsp--cargo-dependency-path 8276 :install #'lsp--cargo-dependency-install) 8277 :system (list :path #'lsp--system-path) 8278 :download (list :path #'lsp-download-path 8279 :install #'lsp-download-install))) 8280 8281 (defun lsp--system-path (path) 8282 "If PATH is absolute and exists return it as is. Otherwise, 8283 return the absolute path to the executable defined by PATH or 8284 nil." 8285 ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the 8286 ;; typescript-language-server. When lsp invokes the server, lsp needs to 8287 ;; supply the path to the typescript compiler, tsserver.js, as an argument. To 8288 ;; make code platform independent, one must pass the absolute path to the 8289 ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript 8290 ;; child process spawn command that is invoked by the 8291 ;; typescript-language-server). This is why we check for existence and not 8292 ;; that the path is executable. 8293 (let ((path (lsp-resolve-value path))) 8294 (cond 8295 ((and (f-absolute? path) 8296 (f-exists? path)) 8297 path) 8298 ((executable-find path t) path)))) 8299 8300 (defun lsp-package-path (dependency) 8301 "Path to the DEPENDENCY each of the registered providers." 8302 (let (path) 8303 (--first (-let [(provider . rest) it] 8304 (setq path (-some-> lsp-deps-providers 8305 (plist-get provider) 8306 (plist-get :path) 8307 (apply rest)))) 8308 (gethash dependency lsp--dependencies)) 8309 path)) 8310 8311 (defun lsp-package-ensure (dependency callback error-callback) 8312 "Asynchronously ensure a package." 8313 (or (-first (-lambda ((provider . rest)) 8314 (-some-> lsp-deps-providers 8315 (plist-get provider) 8316 (plist-get :install) 8317 (apply (cl-list* callback error-callback rest)))) 8318 (gethash dependency lsp--dependencies)) 8319 (funcall error-callback (format "Unable to find a way to install %s" dependency)))) 8320 8321 8322 ;; npm handling 8323 8324 ;; https://docs.npmjs.com/files/folders#executables 8325 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys) 8326 "Return npm dependency PATH for PACKAGE." 8327 (let ((path (executable-find 8328 (f-join lsp-server-install-dir "npm" package 8329 (cond ((eq system-type 'windows-nt) "") 8330 (t "bin")) 8331 path) 8332 t))) 8333 (unless (and path (f-exists? path)) 8334 (error "The package %s is not installed. Unable to find %s" package path)) 8335 path)) 8336 8337 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys) 8338 (if-let* ((npm-binary (executable-find "npm"))) 8339 (progn 8340 ;; Explicitly `make-directory' to work around NPM bug in 8341 ;; versions 7.0.0 through 7.4.1. See 8342 ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for 8343 ;; discussion. 8344 (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents) 8345 (lsp-async-start-process (lambda () 8346 (if (string-empty-p 8347 (string-trim (shell-command-to-string 8348 (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " ")))) 8349 (funcall callback) 8350 (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json"))))) 8351 (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx 8352 (when (f-dir-p default-directory) 8353 (lsp-async-start-process callback 8354 error-callback 8355 (executable-find "npx") 8356 "npm-install-peers"))))) 8357 error-callback 8358 npm-binary 8359 "-g" 8360 "--prefix" 8361 (f-join lsp-server-install-dir "npm" package) 8362 "install" 8363 package)) 8364 (lsp-log "Unable to install %s via `npm' because it is not present" package) 8365 nil)) 8366 8367 8368 ;; Cargo dependency handling 8369 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys) 8370 (let ((path (executable-find 8371 (f-join lsp-server-install-dir 8372 "cargo" 8373 package 8374 "bin" 8375 path) 8376 t))) 8377 (unless (and path (f-exists? path)) 8378 (error "The package %s is not installed. Unable to find %s" package path)) 8379 path)) 8380 8381 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys) 8382 (if-let* ((cargo-binary (executable-find "cargo"))) 8383 (lsp-async-start-process 8384 callback 8385 error-callback 8386 cargo-binary 8387 "install" 8388 package 8389 (when git 8390 "--git") 8391 git 8392 "--root" 8393 (f-join lsp-server-install-dir "cargo" package)) 8394 (lsp-log "Unable to install %s via `cargo' because it is not present" package) 8395 nil)) 8396 8397 8398 8399 ;; Download URL handling 8400 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys) 8401 (let* ((url (lsp-resolve-value url)) 8402 (store-path (lsp-resolve-value store-path)) 8403 ;; (decompress (lsp-resolve-value decompress)) 8404 (download-path 8405 (pcase decompress 8406 (:gzip (concat store-path ".gz")) 8407 (:zip (concat store-path ".zip")) 8408 (:targz (concat store-path ".tar.gz")) 8409 (`nil store-path) 8410 (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'"))))) 8411 (make-thread 8412 (lambda () 8413 (condition-case err 8414 (progn 8415 (when (f-exists? download-path) 8416 (f-delete download-path)) 8417 (when (f-exists? store-path) 8418 (f-delete store-path)) 8419 (lsp--info "Starting to download %s to %s..." url download-path) 8420 (mkdir (f-parent download-path) t) 8421 (url-copy-file url download-path) 8422 (lsp--info "Finished downloading %s..." download-path) 8423 (when (and lsp-verify-signature asc-url pgp-key) 8424 (if (executable-find epg-gpg-program) 8425 (let ((asc-download-path (concat download-path ".asc")) 8426 (context (epg-make-context)) 8427 (fingerprint) 8428 (signature)) 8429 (when (f-exists? asc-download-path) 8430 (f-delete asc-download-path)) 8431 (lsp--info "Starting to download %s to %s..." asc-url asc-download-path) 8432 (url-copy-file asc-url asc-download-path) 8433 (lsp--info "Finished downloading %s..." asc-download-path) 8434 (epg-import-keys-from-string context pgp-key) 8435 (setq fingerprint (epg-import-status-fingerprint 8436 (car 8437 (epg-import-result-imports 8438 (epg-context-result-for context 'import))))) 8439 (lsp--info "Verifying signature %s..." asc-download-path) 8440 (epg-verify-file context asc-download-path download-path) 8441 (setq signature (car (epg-context-result-for context 'verify))) 8442 (unless (and 8443 (eq (epg-signature-status signature) 'good) 8444 (equal (epg-signature-fingerprint signature) fingerprint)) 8445 (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature)))) 8446 (lsp--warn "GPG is not installed, skipping the signature check."))) 8447 (when decompress 8448 (lsp--info "Decompressing %s..." download-path) 8449 (pcase decompress 8450 (:gzip 8451 (lsp-gunzip download-path)) 8452 (:zip (lsp-unzip download-path (f-parent store-path))) 8453 (:targz (lsp-tar-gz-decompress download-path (f-parent store-path)))) 8454 (lsp--info "Decompressed %s..." store-path)) 8455 (funcall callback)) 8456 (error (funcall error-callback err))))))) 8457 8458 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys) 8459 "Download URL and store it into STORE-PATH. 8460 8461 SET-EXECUTABLE? when non-nil change the executable flags of 8462 STORE-PATH to make it executable. BINARY-PATH can be specified 8463 when the binary to start does not match the name of the 8464 archive (e.g. when the archive has multiple files)" 8465 (let ((store-path (or (lsp-resolve-value binary-path) 8466 (lsp-resolve-value store-path)))) 8467 (cond 8468 ((executable-find store-path) store-path) 8469 ((and set-executable? (f-exists? store-path)) 8470 (set-file-modes store-path #o0700) 8471 store-path) 8472 ((f-exists? store-path) store-path)))) 8473 8474 (defun lsp--find-latest-gh-release-url (url regex) 8475 "Fetch the latest version in the releases given by URL by using REGEX." 8476 (let ((url-request-method "GET")) 8477 (with-current-buffer (url-retrieve-synchronously url) 8478 (goto-char (point-min)) 8479 (re-search-forward "\n\n" nil 'noerror) 8480 (delete-region (point-min) (point)) 8481 (let* ((json-result (lsp-json-read-buffer))) 8482 (message "Latest version found: %s" (lsp-get json-result :tag_name)) 8483 (--> json-result 8484 (lsp-get it :assets) 8485 (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it) 8486 (lsp-get it :browser_download_url)))))) 8487 8488 ;; unzip 8489 8490 (defconst lsp-ext-pwsh-script "pwsh -noprofile -noninteractive \ 8491 -nologo -ex bypass -c Expand-Archive -Path '%s' -DestinationPath '%s'" 8492 "Pwsh script to unzip file.") 8493 8494 (defconst lsp-ext-powershell-script "powershell -noprofile -noninteractive \ 8495 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'" 8496 "Powershell script to unzip file.") 8497 8498 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'" 8499 "Unzip script to unzip file.") 8500 8501 (defcustom lsp-unzip-script (lambda () 8502 (cond ((and (eq system-type 'windows-nt) 8503 (executable-find "pwsh")) 8504 lsp-ext-pwsh-script) 8505 ((and (eq system-type 'windows-nt) 8506 (executable-find "powershell")) 8507 lsp-ext-powershell-script) 8508 ((executable-find "unzip") lsp-ext-unzip-script) 8509 ((executable-find "pwsh") lsp-ext-pwsh-script) 8510 (t nil))) 8511 "The script to unzip." 8512 :group 'lsp-mode 8513 :type 'string 8514 :package-version '(lsp-mode . "8.0.0")) 8515 8516 (defun lsp-unzip (zip-file dest) 8517 "Unzip ZIP-FILE to DEST." 8518 (unless lsp-unzip-script 8519 (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'")) 8520 (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest))) 8521 8522 ;; gunzip 8523 8524 (defconst lsp-ext-gunzip-script "gzip -d %1$s" 8525 "Script to decompress a gzippped file with gzip.") 8526 8527 (defcustom lsp-gunzip-script (lambda () 8528 (cond ((executable-find "gzip") lsp-ext-gunzip-script) 8529 (t nil))) 8530 "The script to decompress a gzipped file. 8531 Should be a format string with one argument for the file to be decompressed 8532 in place." 8533 :group 'lsp-mode 8534 :type 'string 8535 :package-version '(lsp-mode . "8.0.0")) 8536 8537 (defun lsp-gunzip (gz-file) 8538 "Decompress GZ-FILE in place." 8539 (unless lsp-gunzip-script 8540 (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file)) 8541 (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file))) 8542 8543 ;; tar.gz decompression 8544 8545 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'" 8546 "Script to decompress a .tar.gz file.") 8547 8548 (defcustom lsp-tar-script (lambda () 8549 (cond ((executable-find "tar") lsp-ext-tar-script) 8550 (t nil))) 8551 "The script to decompress a .tar.gz file. 8552 Should be a format string with one argument for the file to be decompressed 8553 in place." 8554 :group 'lsp-mode 8555 :type 'string) 8556 8557 (defun lsp-tar-gz-decompress (targz-file dest) 8558 "Decompress TARGZ-FILE in DEST." 8559 (unless lsp-tar-script 8560 (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file)) 8561 (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest))) 8562 8563 8564 ;; VSCode marketplace 8565 8566 (defcustom lsp-vscode-ext-url 8567 "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s" 8568 "Vscode extension template url." 8569 :group 'lsp-mode 8570 :type 'string 8571 :package-version '(lsp-mode . "8.0.0")) 8572 8573 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform) 8574 "Return the URL to vscode extension. 8575 PUBLISHER is the extension publisher. 8576 NAME is the name of the extension. 8577 VERSION is the version of the extension. 8578 TARGETPLATFORM is the targetPlatform of the extension." 8579 (format lsp-vscode-ext-url publisher name version (or targetPlatform ""))) 8580 8581 8582 8583 ;; Queueing prompts 8584 8585 (defvar lsp--question-queue nil 8586 "List of questions yet to be asked by `lsp-ask-question'.") 8587 8588 (defun lsp-ask-question (question options callback) 8589 "Prompt the user to answer the QUESTION with one of the OPTIONS from the 8590 minibuffer. Once the user selects an option, the CALLBACK function will be 8591 called, passing the selected option to it. 8592 8593 If the user is currently being shown a question, the question will be stored in 8594 `lsp--question-queue', and will be asked once the user has answered the current 8595 question." 8596 (add-to-list 'lsp--question-queue `(("question" . ,question) 8597 ("options" . ,options) 8598 ("callback" . ,callback)) t) 8599 (when (eq (length lsp--question-queue) 1) 8600 (lsp--process-question-queue))) 8601 8602 (defun lsp--process-question-queue () 8603 "Take the first question from `lsp--question-queue', process it, then process 8604 the next question until the queue is empty." 8605 (-let* (((&alist "question" "options" "callback") (car lsp--question-queue)) 8606 (answer (completing-read question options nil t))) 8607 (pop lsp--question-queue) 8608 (funcall callback answer) 8609 (when lsp--question-queue 8610 (lsp--process-question-queue)))) 8611 8612 (defun lsp--supports-buffer? (client) 8613 (and 8614 ;; both file and client remote or both local 8615 (eq (---truthy? (file-remote-p (buffer-file-name))) 8616 (---truthy? (lsp--client-remote? client))) 8617 8618 ;; activation function or major-mode match. 8619 (if-let* ((activation-fn (lsp--client-activation-fn client))) 8620 (funcall activation-fn (buffer-file-name) major-mode) 8621 (-contains? (lsp--client-major-modes client) major-mode)) 8622 8623 ;; check whether it is enabled if `lsp-enabled-clients' is not null 8624 (or (null lsp-enabled-clients) 8625 (or (member (lsp--client-server-id client) lsp-enabled-clients) 8626 (ignore (lsp--info "Client %s is not in lsp-enabled-clients" 8627 (lsp--client-server-id client))))) 8628 8629 ;; check whether it is not disabled. 8630 (not (lsp--client-disabled-p major-mode (lsp--client-server-id client))))) 8631 8632 (defun lsp--filter-clients (pred) 8633 (->> lsp-clients hash-table-values (-filter pred))) 8634 8635 (defun lsp--find-clients () 8636 "Find clients which can handle current buffer." 8637 (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 8638 #'lsp--server-binary-present?))) 8639 (lsp-log "Found the following clients for %s: %s" 8640 (buffer-file-name) 8641 (s-join ", " 8642 (-map (lambda (client) 8643 (format "(server-id %s, priority %s)" 8644 (lsp--client-server-id client) 8645 (lsp--client-priority client))) 8646 matching-clients))) 8647 (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients)) 8648 (selected-clients (if-let* ((main-client (and main-clients 8649 (--max-by (> (lsp--client-priority it) 8650 (lsp--client-priority other)) 8651 main-clients)))) 8652 (cons main-client add-on-clients) 8653 add-on-clients))) 8654 (lsp-log "The following clients were selected based on priority: %s" 8655 (s-join ", " 8656 (-map (lambda (client) 8657 (format "(server-id %s, priority %s)" 8658 (lsp--client-server-id client) 8659 (lsp--client-priority client))) 8660 selected-clients))) 8661 selected-clients))) 8662 8663 (defun lsp-workspace-remove-all-folders() 8664 "Delete all lsp tracked folders." 8665 (interactive) 8666 (--each (lsp-session-folders (lsp-session)) 8667 (lsp-workspace-folders-remove it))) 8668 8669 (defun lsp-register-client (client) 8670 "Registers LSP client CLIENT." 8671 (let ((client-id (lsp--client-server-id client))) 8672 (puthash client-id client lsp-clients) 8673 (setplist (intern (format "lsp-%s-after-open-hook" client-id)) 8674 `( standard-value (nil) custom-type hook 8675 custom-package-version (lsp-mode . "7.0.1") 8676 variable-documentation ,(format "Hooks to run after `%s' server is run." client-id) 8677 custom-requests nil))) 8678 (when (and lsp-auto-register-remote-clients 8679 (not (lsp--client-remote? client))) 8680 (let ((remote-client (copy-lsp--client client))) 8681 (setf (lsp--client-remote? remote-client) t 8682 (lsp--client-server-id remote-client) (intern 8683 (format "%s-tramp" 8684 (lsp--client-server-id client))) 8685 ;; disable automatic download 8686 (lsp--client-download-server-fn remote-client) nil) 8687 (lsp-register-client remote-client)))) 8688 8689 (defun lsp--create-initialization-options (_session client) 8690 "Create initialization-options from SESSION and CLIENT. 8691 Add workspace folders depending on server being multiroot and 8692 session workspace folder configuration for the server." 8693 (let* ((initialization-options-or-fn (lsp--client-initialization-options client))) 8694 (if (functionp initialization-options-or-fn) 8695 (funcall initialization-options-or-fn) 8696 initialization-options-or-fn))) 8697 8698 (defvar lsp-client-settings (make-hash-table :test 'equal) 8699 "For internal use, any external users please use 8700 `lsp-register-custom-settings' function instead") 8701 8702 (defun lsp-register-custom-settings (props) 8703 "Register PROPS. 8704 PROPS is list of triple (path value boolean?) where PATH is the path to the 8705 property; VALUE can be a literal value, symbol to be evaluated, or either a 8706 function or lambda function to be called without arguments; BOOLEAN? is an 8707 optional flag that should be non-nil for boolean settings, when it is nil the 8708 property will be ignored if the VALUE is nil. 8709 8710 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))' 8711 \(note the double parentheses)" 8712 (mapc 8713 (-lambda ((path . rest)) 8714 (puthash path rest lsp-client-settings)) 8715 props)) 8716 8717 (defun lsp-region-text (region) 8718 "Get the text for REGION in current buffer." 8719 (-let (((start . end) (lsp--range-to-region region))) 8720 (buffer-substring-no-properties start end))) 8721 8722 (defun lsp-ht-set (tbl paths value) 8723 "Set nested hash table value. 8724 TBL - a hash table, PATHS is the path to the nested VALUE." 8725 (pcase paths 8726 (`(,path) (ht-set! tbl path value)) 8727 (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl) 8728 (let ((temp-tbl (ht))) 8729 (ht-set! tbl path temp-tbl) 8730 temp-tbl)))) 8731 (lsp-ht-set nested-tbl rst value))))) 8732 8733 ;; sections 8734 8735 (defalias 'defcustom-lsp 'lsp-defcustom) 8736 8737 (defmacro lsp-defcustom (symbol standard doc &rest args) 8738 "Defines `lsp-mode' server property." 8739 (declare (doc-string 3) (debug (name body)) 8740 (indent defun)) 8741 (let ((path (plist-get args :lsp-path)) 8742 (setter (intern (concat (symbol-name symbol) "--set")))) 8743 (cl-remf args :lsp-path) 8744 `(progn 8745 (lsp-register-custom-settings 8746 (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type)))))) 8747 8748 (defcustom ,symbol ,standard ,doc ,@args) 8749 8750 ;; Use a variable watcher instead of registering a `defcustom' 8751 ;; setter since `hack-local-variables' is not aware of custom 8752 ;; setters and won't invoke them. 8753 8754 (defun ,setter (sym val op _where) 8755 (when (eq op 'set) 8756 (lsp--set-custom-property sym val ,path))) 8757 8758 (add-variable-watcher ',symbol #',setter)))) 8759 8760 (defun lsp--set-custom-property (sym val path) 8761 (set sym val) 8762 (let ((section (cl-first (s-split "\\." path)))) 8763 (mapc (lambda (workspace) 8764 (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace)) 8765 section) 8766 (with-lsp-workspace workspace 8767 (lsp--set-configuration (lsp-configuration-section section))))) 8768 (lsp--session-workspaces (lsp-session))))) 8769 8770 (defun lsp-configuration-section (section) 8771 "Get settings for SECTION." 8772 (let ((ret (ht-create))) 8773 (maphash (-lambda (path (variable boolean?)) 8774 (when (s-matches? (concat (regexp-quote section) "\\..*") path) 8775 (let* ((symbol-value (-> variable 8776 lsp-resolve-value 8777 lsp-resolve-value)) 8778 (value (if (and boolean? (not symbol-value)) 8779 :json-false 8780 symbol-value))) 8781 (when (or boolean? value) 8782 (lsp-ht-set ret (s-split "\\." path) value))))) 8783 lsp-client-settings) 8784 ret)) 8785 8786 8787 (defun lsp--start-connection (session client project-root) 8788 "Initiates connection created from CLIENT for PROJECT-ROOT. 8789 SESSION is the active session." 8790 (when (lsp--client-multi-root client) 8791 (cl-pushnew project-root (gethash (lsp--client-server-id client) 8792 (lsp-session-server-id->folders session)))) 8793 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil) 8794 8795 (unwind-protect 8796 (lsp--start-workspace session client project-root (lsp--create-initialization-options session client)) 8797 (lsp--spinner-stop))) 8798 8799 ;; lsp-log-io-mode 8800 8801 (defvar lsp-log-io-mode-map 8802 (let ((map (make-sparse-keymap))) 8803 (define-key map (kbd "M-n") #'lsp-log-io-next) 8804 (define-key map (kbd "M-p") #'lsp-log-io-prev) 8805 (define-key map (kbd "k") #'lsp--erase-log-buffer) 8806 (define-key map (kbd "K") #'lsp--erase-session-log-buffers) 8807 map) 8808 "Keymap for lsp log buffer mode.") 8809 8810 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo" 8811 "Special mode for viewing IO logs.") 8812 8813 (defun lsp-workspace-show-log (workspace) 8814 "Display the log buffer of WORKSPACE." 8815 (interactive 8816 (list (if lsp-log-io 8817 (if (eq (length (lsp-workspaces)) 1) 8818 (cl-first (lsp-workspaces)) 8819 (lsp--completing-read "Workspace: " (lsp-workspaces) 8820 #'lsp--workspace-print nil t)) 8821 (user-error "IO logging is disabled")))) 8822 (pop-to-buffer (lsp--get-log-buffer-create workspace))) 8823 8824 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log) 8825 8826 (defun lsp--get-log-buffer-create (workspace) 8827 "Return the lsp log buffer of WORKSPACE, creating a new one if needed." 8828 (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8829 (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id))) 8830 (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid)))) 8831 8832 (defun lsp--erase-log-buffer (&optional all) 8833 "Delete contents of current lsp log buffer. 8834 When ALL is t, erase all log buffers of the running session." 8835 (interactive) 8836 (let* ((workspaces (lsp--session-workspaces (lsp-session))) 8837 (current-log-buffer (current-buffer))) 8838 (dolist (w workspaces) 8839 (let ((b (lsp--get-log-buffer-create w))) 8840 (when (or all (eq b current-log-buffer)) 8841 (with-current-buffer b 8842 (let ((inhibit-read-only t)) 8843 (erase-buffer)))))))) 8844 8845 (defun lsp--erase-session-log-buffers () 8846 "Erase log buffers of the running session." 8847 (interactive) 8848 (lsp--erase-log-buffer t)) 8849 8850 (defun lsp-log-io-next (arg) 8851 "Move to next log entry." 8852 (interactive "P") 8853 (ewoc-goto-next lsp--log-io-ewoc (or arg 1))) 8854 8855 (defun lsp-log-io-prev (arg) 8856 "Move to previous log entry." 8857 (interactive "P") 8858 (ewoc-goto-prev lsp--log-io-ewoc (or arg 1))) 8859 8860 8861 8862 (cl-defmethod lsp-process-id ((process process)) 8863 (process-id process)) 8864 8865 (cl-defmethod lsp-process-name ((process process)) (process-name process)) 8866 8867 (cl-defmethod lsp-process-status ((process process)) (process-status process)) 8868 8869 (cl-defmethod lsp-process-kill ((process process)) 8870 (when (process-live-p process) 8871 (kill-process process))) 8872 8873 (cl-defmethod lsp-process-send ((process process) message) 8874 (condition-case err 8875 (process-send-string process (lsp--make-message message)) 8876 (error (lsp--error "Sending to process failed with the following error: %s" 8877 (error-message-string err))))) 8878 8879 (cl-defmethod lsp-process-cleanup (process) 8880 ;; Kill standard error buffer only if the process exited normally. 8881 ;; Leave it intact otherwise for debugging purposes. 8882 (let ((buffer (-> process process-name get-buffer))) 8883 (when (and (eq (process-status process) 'exit) 8884 (zerop (process-exit-status process)) 8885 (buffer-live-p buffer)) 8886 (kill-buffer buffer)))) 8887 8888 8889 ;; native JSONRPC 8890 8891 (declare-function json-rpc "ext:json") 8892 (declare-function json-rpc-connection "ext:json") 8893 (declare-function json-rpc-send "ext:json") 8894 (declare-function json-rpc-shutdown "ext:json") 8895 (declare-function json-rpc-stderr "ext:json") 8896 (declare-function json-rpc-pid "ext:json") 8897 8898 (defvar lsp-json-rpc-thread nil) 8899 (defvar lsp-json-rpc-queue nil) 8900 (defvar lsp-json-rpc-done nil) 8901 (defvar lsp-json-rpc-mutex (make-mutex)) 8902 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex)) 8903 8904 (defun lsp-json-rpc-process-queue () 8905 (while (not lsp-json-rpc-done) 8906 (while lsp-json-rpc-queue 8907 (-let (((proc . message) (pop lsp-json-rpc-queue))) 8908 (json-rpc-send 8909 proc message 8910 :null-object nil 8911 :false-object :json-false))) 8912 (with-mutex lsp-json-rpc-mutex 8913 (condition-wait lsp-json-rpc-condition)))) 8914 8915 (cl-defmethod lsp-process-id (process) (json-rpc-pid process)) 8916 8917 (cl-defmethod lsp-process-name (_process) "TBD") 8918 8919 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process)) 8920 8921 (cl-defmethod lsp-process-send (proc message) 8922 (unless lsp-json-rpc-thread 8923 (with-current-buffer (get-buffer-create " *json-rpc*") 8924 (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*")))) 8925 8926 (with-mutex lsp-json-rpc-mutex 8927 (setq lsp-json-rpc-queue (append lsp-json-rpc-queue 8928 (list (cons proc message)))) 8929 (condition-notify lsp-json-rpc-condition))) 8930 8931 (cl-defmethod lsp-process-cleanup (_proc)) 8932 8933 (defun lsp-json-rpc-connection (workspace command) 8934 (let ((con (apply #'json-rpc-connection command)) 8935 (object-type (if lsp-use-plists 'plist 'hash-table))) 8936 (with-current-buffer (get-buffer-create " *json-rpc*") 8937 (make-thread 8938 (lambda () 8939 (json-rpc 8940 con 8941 (lambda (result err done) 8942 (run-with-timer 8943 0.0 8944 nil 8945 (lambda () 8946 (cond 8947 (result (lsp--parser-on-message result workspace)) 8948 (err (warn "Json parsing failed with the following error: %s" err)) 8949 (done (lsp--handle-process-exit workspace "")))))) 8950 :object-type object-type 8951 :null-object nil 8952 :false-object nil)) 8953 "*json-rpc-connection*")) 8954 (cons con con))) 8955 8956 (defun lsp-json-rpc-stderr () 8957 (interactive) 8958 (--when-let (pcase (lsp-workspaces) 8959 (`nil (user-error "There are no active servers in the current buffer")) 8960 (`(,workspace) workspace) 8961 (workspaces (lsp--completing-read "Select server: " 8962 workspaces 8963 'lsp--workspace-print nil t))) 8964 (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it))) 8965 (buffer (format "*stderr-%s*" (lsp--workspace-print it)) )) 8966 (with-current-buffer (get-buffer-create buffer) 8967 (with-help-window buffer 8968 (insert content)))))) 8969 8970 8971 (defun lsp--workspace-print (workspace) 8972 "Visual representation WORKSPACE." 8973 (let* ((proc (lsp--workspace-cmd-proc workspace)) 8974 (status (lsp--workspace-status workspace)) 8975 (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8976 (pid (lsp-process-id proc))) 8977 8978 (if (eq 'initialized status) 8979 (format "%s:%s" server-id pid) 8980 (format "%s:%s/%s" server-id pid status)))) 8981 8982 (defun lsp--map-tree-widget (m) 8983 "Build `tree-widget' from a hash-table or plist M." 8984 (when (lsp-structure-p m) 8985 (let (nodes) 8986 (lsp-map (lambda (k v) 8987 (push `(tree-widget 8988 :tag ,(if (lsp-structure-p v) 8989 (format "%s:" k) 8990 (format "%s: %s" k 8991 (propertize (format "%s" v) 8992 'face 8993 'font-lock-string-face))) 8994 :open t 8995 ,@(lsp--map-tree-widget v)) 8996 nodes)) 8997 m) 8998 nodes))) 8999 9000 (defun lsp-buffer-name (buffer-id) 9001 (if-let* ((buffer-name (plist-get buffer-id :buffer-name))) 9002 (funcall buffer-name buffer-id) 9003 (buffer-name buffer-id))) 9004 9005 (defun lsp--render-workspace (workspace) 9006 "Tree node representation of WORKSPACE." 9007 `(tree-widget :tag ,(lsp--workspace-print workspace) 9008 :open t 9009 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face) 9010 :open t 9011 ,@(->> workspace 9012 (lsp--workspace-buffers) 9013 (--map `(tree-widget 9014 :tag ,(when (lsp-buffer-live-p it) 9015 (let ((buffer-name (lsp-buffer-name it))) 9016 (if (lsp-with-current-buffer it buffer-read-only) 9017 (propertize buffer-name 'face 'font-lock-constant-face) 9018 buffer-name))))))) 9019 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face) 9020 ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget)))) 9021 9022 (define-derived-mode lsp-browser-mode special-mode "LspBrowser" 9023 "Define mode for displaying lsp sessions." 9024 (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t))))) 9025 9026 (defun lsp-describe-session () 9027 "Describes current `lsp-session'." 9028 (interactive) 9029 (let ((session (lsp-session)) 9030 (buf (get-buffer-create "*lsp session*")) 9031 (root (lsp-workspace-root))) 9032 (with-current-buffer buf 9033 (lsp-browser-mode) 9034 (let ((inhibit-read-only t)) 9035 (erase-buffer) 9036 (--each (lsp-session-folders session) 9037 (widget-create 9038 `(tree-widget 9039 :tag ,(propertize it 'face 'font-lock-keyword-face) 9040 :open t 9041 ,@(->> session 9042 (lsp-session-folder->servers) 9043 (gethash it) 9044 (-map 'lsp--render-workspace))))))) 9045 (pop-to-buffer buf) 9046 (goto-char (point-min)) 9047 (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag) 9048 until (or (and root (string= tag root)) (eobp)) 9049 do (goto-char (next-overlay-change (point)))))) 9050 9051 (defun lsp--session-workspaces (session) 9052 "Get all workspaces that are part of the SESSION." 9053 (-> session lsp-session-folder->servers hash-table-values -flatten -uniq)) 9054 9055 (defun lsp--find-multiroot-workspace (session client project-root) 9056 "Look for a multiroot connection in SESSION created from CLIENT for 9057 PROJECT-ROOT and BUFFER-MAJOR-MODE." 9058 (when (lsp--client-multi-root client) 9059 (-when-let (multi-root-workspace (->> session 9060 (lsp--session-workspaces) 9061 (--first (eq (-> it lsp--workspace-client lsp--client-server-id) 9062 (lsp--client-server-id client))))) 9063 (with-lsp-workspace multi-root-workspace 9064 (lsp-notify "workspace/didChangeWorkspaceFolders" 9065 (lsp-make-did-change-workspace-folders-params 9066 :event (lsp-make-workspace-folders-change-event 9067 :added (vector (lsp-make-workspace-folder 9068 :uri (lsp--path-to-uri project-root) 9069 :name (f-filename project-root))) 9070 :removed [])))) 9071 9072 (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace)) 9073 (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root)) 9074 9075 (lsp--persist-session session) 9076 9077 (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace)) 9078 (lsp--open-in-workspace multi-root-workspace) 9079 9080 multi-root-workspace))) 9081 9082 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder) 9083 "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT. 9084 IGNORE-MULTI-FOLDER to ignore multi folder server." 9085 (-map (lambda (client) 9086 (or 9087 (lsp--find-workspace session client project-root) 9088 (unless ignore-multi-folder 9089 (lsp--find-multiroot-workspace session client project-root)) 9090 (lsp--start-connection session client project-root))) 9091 clients)) 9092 9093 (defun lsp--spinner-stop () 9094 "Stop the spinner in case all of the workspaces are started." 9095 (when (--all? (eq (lsp--workspace-status it) 'initialized) 9096 lsp--buffer-workspaces) 9097 (spinner-stop))) 9098 9099 (defun lsp--open-in-workspace (workspace) 9100 "Open in existing WORKSPACE." 9101 (if (eq 'initialized (lsp--workspace-status workspace)) 9102 ;; when workspace is initialized just call document did open. 9103 (progn 9104 (with-lsp-workspace workspace 9105 (when-let* ((before-document-open-fn (-> workspace 9106 lsp--workspace-client 9107 lsp--client-before-file-open-fn))) 9108 (funcall before-document-open-fn workspace)) 9109 (lsp--text-document-did-open)) 9110 (lsp--spinner-stop)) 9111 ;; when it is not initialized 9112 (lsp--spinner-start) 9113 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace)))) 9114 9115 (defun lsp--find-workspace (session client project-root) 9116 "Find server connection created with CLIENT in SESSION for PROJECT-ROOT." 9117 (when-let* ((workspace (->> session 9118 (lsp-session-folder->servers) 9119 (gethash project-root) 9120 (--first (eql (-> it lsp--workspace-client lsp--client-server-id) 9121 (lsp--client-server-id client)))))) 9122 (lsp--open-in-workspace workspace) 9123 workspace)) 9124 9125 (defun lsp--read-char (prompt &optional options) 9126 "Wrapper for `read-char-from-minibuffer' if Emacs +27. 9127 Fallback to `read-key' otherwise. 9128 PROMPT is the message and OPTIONS the available options." 9129 (if (fboundp 'read-char-from-minibuffer) 9130 (read-char-from-minibuffer prompt options) 9131 (read-key prompt))) 9132 9133 (defun lsp--find-root-interactively (session) 9134 "Find project interactively. 9135 Returns nil if the project should not be added to the current SESSION." 9136 (condition-case nil 9137 (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory)) 9138 (action (lsp--read-char 9139 (format 9140 "%s is not part of any project. 9141 9142 %s ==> Import project root %s 9143 %s ==> Import project by selecting root directory interactively 9144 %s ==> Import project at current directory %s 9145 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist 9146 %s ==> Do not ask again for the current project by selecting ignore path interactively 9147 %s ==> Do nothing: ask again when opening other files from the current project 9148 9149 Select action: " 9150 (propertize (buffer-name) 'face 'bold) 9151 (propertize "i" 'face 'success) 9152 (propertize project-root-suggestion 'face 'bold) 9153 (propertize "I" 'face 'success) 9154 (propertize "." 'face 'success) 9155 (propertize default-directory 'face 'bold) 9156 (propertize "d" 'face 'warning) 9157 (propertize project-root-suggestion 'face 'bold) 9158 (propertize "D" 'face 'warning) 9159 (propertize "n" 'face 'warning)) 9160 '(?i ?\r ?I ?. ?d ?D ?n)))) 9161 (cl-case action 9162 (?i project-root-suggestion) 9163 (?\r project-root-suggestion) 9164 (?I (read-directory-name "Select workspace folder to add: " 9165 (or project-root-suggestion default-directory) 9166 nil 9167 t)) 9168 (?. default-directory) 9169 (?d (push project-root-suggestion (lsp-session-folders-blocklist session)) 9170 (lsp--persist-session session) 9171 nil) 9172 (?D (push (read-directory-name "Select folder to blocklist: " 9173 (or project-root-suggestion default-directory) 9174 nil 9175 t) 9176 (lsp-session-folders-blocklist session)) 9177 (lsp--persist-session session) 9178 nil) 9179 (t nil))) 9180 (quit))) 9181 9182 (declare-function tramp-file-name-host "ext:tramp" (file) t) 9183 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault)) 9184 9185 (defun lsp--files-same-host (f1 f2) 9186 "Predicate on whether or not two files are on the same host." 9187 (or (not (or (file-remote-p f1) (file-remote-p f2))) 9188 (and (file-remote-p f1) 9189 (file-remote-p f2) 9190 (progn (require 'tramp) 9191 (equal (tramp-file-name-host (tramp-dissect-file-name f1)) 9192 (tramp-file-name-host (tramp-dissect-file-name f2))))))) 9193 9194 (defun lsp-find-session-folder (session file-name) 9195 "Look in the current SESSION for folder containing FILE-NAME." 9196 (let ((file-name-canonical (lsp-f-canonical file-name))) 9197 (->> session 9198 (lsp-session-folders) 9199 (--filter (and (lsp--files-same-host it file-name-canonical) 9200 (or (lsp-f-same? it file-name-canonical) 9201 (and (f-dir? it) 9202 (lsp-f-ancestor-of? it file-name-canonical))))) 9203 (--max-by (> (length it) 9204 (length other)))))) 9205 9206 (defun lsp-find-workspace (server-id &optional file-name) 9207 "Find workspace for SERVER-ID for FILE-NAME." 9208 (-when-let* ((session (lsp-session)) 9209 (folder->servers (lsp-session-folder->servers session)) 9210 (workspaces (if file-name 9211 (gethash (lsp-find-session-folder session file-name) folder->servers) 9212 (lsp--session-workspaces session)))) 9213 9214 (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces))) 9215 9216 (defun lsp--calculate-root (session file-name) 9217 "Calculate project root for FILE-NAME in SESSION." 9218 (and 9219 (->> session 9220 (lsp-session-folders-blocklist) 9221 (--first (and (lsp--files-same-host it file-name) 9222 (lsp-f-ancestor-of? it file-name) 9223 (prog1 t 9224 (lsp--info "File %s is in blocklisted directory %s" file-name it)))) 9225 not) 9226 (or 9227 (when lsp-auto-guess-root 9228 (lsp--suggest-project-root)) 9229 (unless lsp-guess-root-without-session 9230 (lsp-find-session-folder session file-name)) 9231 (unless lsp-auto-guess-root 9232 (when-let* ((root-folder (lsp--find-root-interactively session))) 9233 (if (or (not (f-equal? root-folder (expand-file-name "~/"))) 9234 (yes-or-no-p 9235 (concat 9236 (propertize "[WARNING] " 'face 'warning) 9237 "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: 9238 9239 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/'). 9240 2. If your file is under `~/' then create a subfolder and move that file in this folder. 9241 9242 Type `No' to go back to project selection. 9243 Type `Yes' to confirm `HOME' as project root. 9244 Type `C-g' to cancel project import process and stop `lsp'"))) 9245 root-folder 9246 (lsp--calculate-root session file-name))))))) 9247 9248 (defun lsp--try-open-in-library-workspace () 9249 "Try opening current file as library file in any of the active workspace. 9250 The library folders are defined by each client for each of the active workspace." 9251 (when-let* ((workspace (->> (lsp-session) 9252 (lsp--session-workspaces) 9253 ;; Sort the last active workspaces first as they are more likely to be 9254 ;; the correct ones, especially when jumping to a definition. 9255 (-sort (lambda (a _b) 9256 (-contains? lsp--last-active-workspaces a))) 9257 (--first 9258 (and (-> it lsp--workspace-client lsp--supports-buffer?) 9259 (when-let* ((library-folders-fn 9260 (-> it lsp--workspace-client lsp--client-library-folders-fn))) 9261 (-first (lambda (library-folder) 9262 (lsp-f-ancestor-of? library-folder (buffer-file-name))) 9263 (funcall library-folders-fn it)))))))) 9264 (lsp--open-in-workspace workspace) 9265 (view-mode t) 9266 (lsp--info "Opening read-only library file %s." (buffer-file-name)) 9267 (list workspace))) 9268 9269 (defun lsp--persist-session (session) 9270 "Persist SESSION to `lsp-session-file'." 9271 (lsp--persist lsp-session-file (make-lsp-session 9272 :folders (lsp-session-folders session) 9273 :folders-blocklist (lsp-session-folders-blocklist session) 9274 :server-id->folders (lsp-session-server-id->folders session)))) 9275 9276 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder) 9277 "Try create opening file as a project file. 9278 When IGNORE-MULTI-FOLDER is t the lsp mode will start new 9279 language server even if there is language server which can handle 9280 current language. When IGNORE-MULTI-FOLDER is nil current file 9281 will be opened in multi folder language server if there is 9282 such." 9283 (-let ((session (lsp-session))) 9284 (-if-let (clients (if ask-for-client 9285 (list (lsp--completing-read "Select server to start: " 9286 (ht-values lsp-clients) 9287 (-compose 'symbol-name 'lsp--client-server-id) nil t)) 9288 (lsp--find-clients))) 9289 (-if-let (project-root (-some-> session 9290 (lsp--calculate-root (buffer-file-name)) 9291 (lsp-f-canonical))) 9292 (progn 9293 ;; update project roots if needed and persist the lsp session 9294 (unless (-contains? (lsp-session-folders session) project-root) 9295 (cl-pushnew project-root (lsp-session-folders session)) 9296 (lsp--persist-session session)) 9297 (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder)) 9298 (lsp--warn "%s not in project or it is blocklisted." (buffer-name)) 9299 nil) 9300 (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode) 9301 nil))) 9302 9303 (defun lsp-shutdown-workspace () 9304 "Shutdown language server." 9305 (interactive) 9306 (--when-let (pcase (lsp-workspaces) 9307 (`nil (user-error "There are no active servers in the current buffer")) 9308 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?" 9309 (lsp--workspace-print workspace))) 9310 workspace)) 9311 (workspaces (lsp--completing-read "Select server: " 9312 workspaces 9313 'lsp--workspace-print nil t))) 9314 (lsp-workspace-shutdown it))) 9315 9316 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1") 9317 9318 (defcustom lsp-auto-select-workspace t 9319 "Shutdown or restart a single workspace. 9320 If set and the current buffer has only a single workspace 9321 associated with it, `lsp-shutdown-workspace' and 9322 `lsp-restart-workspace' will act on it without asking." 9323 :type 'boolean 9324 :group 'lsp-mode) 9325 9326 (defun lsp--read-workspace () 9327 "Ask the user to select a workspace. 9328 Errors if there are none." 9329 (pcase (lsp-workspaces) 9330 (`nil (error "No workspaces associated with the current buffer")) 9331 ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace) 9332 (workspaces (lsp--completing-read "Select workspace: " workspaces 9333 #'lsp--workspace-print nil t)))) 9334 9335 (defun lsp-workspace-shutdown (workspace) 9336 "Shut the workspace WORKSPACE and the language server associated with it" 9337 (interactive (list (lsp--read-workspace))) 9338 (lsp--warn "Stopping %s" (lsp--workspace-print workspace)) 9339 (with-lsp-workspace workspace (lsp--shutdown-workspace))) 9340 9341 (defun lsp-disconnect () 9342 "Disconnect the buffer from the language server." 9343 (interactive) 9344 (lsp--text-document-did-close t) 9345 (lsp-managed-mode -1) 9346 (lsp-mode -1) 9347 (setq lsp--buffer-workspaces nil) 9348 (lsp--info "Disconnected")) 9349 9350 (defun lsp-restart-workspace () 9351 (interactive) 9352 (--when-let (pcase (lsp-workspaces) 9353 (`nil (user-error "There are no active servers in the current buffer")) 9354 (`(,workspace) workspace) 9355 (workspaces (lsp--completing-read "Select server: " 9356 workspaces 9357 'lsp--workspace-print nil t))) 9358 (lsp-workspace-restart it))) 9359 9360 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1") 9361 9362 (defun lsp-workspace-restart (workspace) 9363 "Restart the workspace WORKSPACE and the language server associated with it" 9364 (interactive (list (lsp--read-workspace))) 9365 (lsp--warn "Restarting %s" (lsp--workspace-print workspace)) 9366 (with-lsp-workspace workspace (lsp--shutdown-workspace t))) 9367 9368 ;;;###autoload 9369 (defun lsp (&optional arg) 9370 "Entry point for the server startup. 9371 When ARG is t the lsp mode will start new language server even if 9372 there is language server which can handle current language. When 9373 ARG is nil current file will be opened in multi folder language 9374 server if there is such. When `lsp' is called with prefix 9375 argument ask the user to select which language server to start." 9376 (interactive "P") 9377 9378 (lsp--require-packages) 9379 9380 (when (buffer-file-name) 9381 (let (clients 9382 (matching-clients (lsp--filter-clients 9383 (-andfn #'lsp--supports-buffer? 9384 #'lsp--server-binary-present?)))) 9385 (cond 9386 (matching-clients 9387 (when (setq lsp--buffer-workspaces 9388 (or (and 9389 ;; Don't open as library file if file is part of a project. 9390 (not (lsp-find-session-folder (lsp-session) (buffer-file-name))) 9391 (lsp--try-open-in-library-workspace)) 9392 (lsp--try-project-root-workspaces (equal arg '(4)) 9393 (and arg (not (equal arg 1)))))) 9394 (lsp-mode 1) 9395 (when lsp-auto-configure (lsp--auto-configure)) 9396 (setq lsp-buffer-uri (lsp--buffer-uri)) 9397 (lsp--info "Connected to %s." 9398 (apply 'concat (--map (format "[%s %s]" 9399 (lsp--workspace-print it) 9400 (lsp--workspace-root it)) 9401 lsp--buffer-workspaces))))) 9402 ;; look for servers which are currently being downloaded. 9403 ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9404 #'lsp--client-download-in-progress?))) 9405 (lsp--info "There are language server(%s) installation in progress. 9406 The server(s) will be started in the buffer when it has finished." 9407 (-map #'lsp--client-server-id clients)) 9408 (seq-do (lambda (client) 9409 (cl-pushnew (current-buffer) (lsp--client-buffers client))) 9410 clients)) 9411 ;; look for servers to install 9412 ((setq clients (lsp--filter-clients 9413 (-andfn #'lsp--supports-buffer? 9414 (-const lsp-enable-suggest-server-download) 9415 #'lsp--client-download-server-fn 9416 (-not #'lsp--client-download-in-progress?)))) 9417 (let ((client (lsp--completing-read 9418 (concat "Unable to find installed server supporting this file. " 9419 "The following servers could be installed automatically: ") 9420 clients 9421 (-compose #'symbol-name #'lsp--client-server-id) 9422 nil 9423 t))) 9424 (cl-pushnew (current-buffer) (lsp--client-buffers client)) 9425 (lsp--install-server-internal client))) 9426 ;; ignore other warnings 9427 ((not lsp-warn-no-matched-clients) 9428 nil) 9429 ;; automatic installation disabled 9430 ((setq clients (unless matching-clients 9431 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9432 #'lsp--client-download-server-fn 9433 (-not (-const lsp-enable-suggest-server-download)) 9434 (-not #'lsp--server-binary-present?))))) 9435 (lsp--warn "The following servers support current file but automatic download is disabled: %s 9436 \(If you have already installed the server check *lsp-log*)." 9437 (mapconcat (lambda (client) 9438 (symbol-name (lsp--client-server-id client))) 9439 clients 9440 " "))) 9441 ;; no clients present 9442 ((setq clients (unless matching-clients 9443 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9444 (-not #'lsp--server-binary-present?))))) 9445 (lsp--warn "The following servers support current file but do not have automatic installation: %s 9446 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages. 9447 \(If you have already installed the server check *lsp-log*)." 9448 (mapconcat (lambda (client) 9449 (symbol-name (lsp--client-server-id client))) 9450 clients 9451 " "))) 9452 ;; no matches 9453 ((-> #'lsp--supports-buffer? lsp--filter-clients not) 9454 (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'. 9455 This issue might be caused by: 9456 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'. 9457 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'. 9458 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/ . 9459 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/. 9460 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients'). 9461 You can customize `lsp-warn-no-matched-clients' to disable this message." 9462 major-mode major-mode major-mode)))))) 9463 9464 (defun lsp--buffer-visible-p () 9465 "Return non nil if current buffer is visible." 9466 (or (buffer-modified-p) (get-buffer-window nil t))) 9467 9468 (defun lsp--init-if-visible () 9469 "Run `lsp' for the current buffer if the buffer is visible. 9470 Returns non nil if `lsp' was run for the buffer." 9471 (when (lsp--buffer-visible-p) 9472 (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t) 9473 (lsp) 9474 t)) 9475 9476 ;;;###autoload 9477 (defun lsp-deferred () 9478 "Entry point that defers server startup until buffer is visible. 9479 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'. 9480 This avoids overloading the server with many files when starting Emacs." 9481 ;; Workspace may not be initialized yet. Use a buffer local variable to 9482 ;; remember that we deferred loading of this buffer. 9483 (setq lsp--buffer-deferred t) 9484 (let ((buffer (current-buffer))) 9485 ;; Avoid false positives as desktop-mode restores buffers by deferring 9486 ;; visibility check until the stack clears. 9487 (run-with-idle-timer 0 nil (lambda () 9488 (when (buffer-live-p buffer) 9489 (with-current-buffer buffer 9490 (unless (lsp--init-if-visible) 9491 (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t)))))))) 9492 9493 9494 9495 (defvar lsp-file-truename-cache (ht)) 9496 9497 (defmacro lsp-with-cached-filetrue-name (&rest body) 9498 "Executes BODY caching the `file-truename' calls." 9499 `(let ((old-fn (symbol-function 'file-truename))) 9500 (unwind-protect 9501 (progn 9502 (fset 'file-truename 9503 (lambda (file-name &optional counter prev-dirs) 9504 (or (gethash file-name lsp-file-truename-cache) 9505 (puthash file-name (apply old-fn (list file-name counter prev-dirs)) 9506 lsp-file-truename-cache)))) 9507 ,@body) 9508 (fset 'file-truename old-fn)))) 9509 9510 9511 (defun lsp-virtual-buffer-call (key &rest args) 9512 (when lsp--virtual-buffer 9513 (when-let* ((fn (plist-get lsp--virtual-buffer key))) 9514 (apply fn args)))) 9515 9516 (defun lsp-translate-column (column) 9517 "Translate COLUMN taking into account virtual buffers." 9518 (or (lsp-virtual-buffer-call :real->virtual-char column) 9519 column)) 9520 9521 (defun lsp-translate-line (line) 9522 "Translate LINE taking into account virtual buffers." 9523 (or (lsp-virtual-buffer-call :real->virtual-line line) 9524 line)) 9525 9526 9527 ;; lsp internal validation. 9528 9529 (defmacro lsp--doctor (&rest checks) 9530 `(-let [buf (current-buffer)] 9531 (with-current-buffer (get-buffer-create "*lsp-performance*") 9532 (with-help-window (current-buffer) 9533 ,@(-map (-lambda ((msg form)) 9534 `(insert (format "%s: %s\n" ,msg 9535 (let ((res (with-current-buffer buf 9536 ,form))) 9537 (cond 9538 ((eq res :optional) (propertize "OPTIONAL" 'face 'warning)) 9539 (res (propertize "OK" 'face 'success)) 9540 (t (propertize "ERROR" 'face 'error))))))) 9541 (-partition 2 checks)))))) 9542 9543 (define-obsolete-function-alias 'lsp-diagnose 9544 'lsp-doctor "lsp-mode 8.0.0") 9545 9546 (defun lsp-doctor () 9547 "Validate performance settings." 9548 (interactive) 9549 (lsp--doctor 9550 "Checking for Native JSON support" (functionp 'json-serialize) 9551 "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max) 9552 "Check `read-process-output-max' default has been changed from 4k" 9553 (and (boundp 'read-process-output-max) 9554 (> read-process-output-max 4096)) 9555 "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)" 9556 (condition-case _err 9557 (progn (lsp--make-message (list "a" "b")) 9558 nil) 9559 (error t)) 9560 "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000) 9561 "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) 9562 "Using emacs 28+ with native compilation?" 9563 (or (and (fboundp 'native-comp-available-p) 9564 (native-comp-available-p)) 9565 :optional))) 9566 9567 (declare-function package-version-join "ext:package") 9568 (declare-function package-desc-version "ext:package") 9569 (declare-function package--alist "ext:package") 9570 9571 (defun lsp-version () 9572 "Return string describing current version of `lsp-mode'." 9573 (interactive) 9574 (unless (featurep 'package) 9575 (require 'package)) 9576 (let ((ver (format "lsp-mode %s, Emacs %s, %s" 9577 (package-version-join 9578 (package-desc-version 9579 (car (alist-get 'lsp-mode (package--alist))))) 9580 emacs-version 9581 system-type))) 9582 (if (called-interactively-p 'interactive) 9583 (lsp--info "%s" ver) 9584 ver))) 9585 9586 9587 9588 ;; org-mode/virtual-buffer 9589 9590 (declare-function org-babel-get-src-block-info "ext:ob-core") 9591 (declare-function org-do-remove-indentation "ext:org-macs") 9592 (declare-function org-src-get-lang-mode "ext:org-src") 9593 (declare-function org-element-context "ext:org-element") 9594 9595 (defun lsp--virtual-buffer-update-position () 9596 (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range)) 9597 (funcall in-range)) 9598 lsp--virtual-buffer-connections)) 9599 (unless (equal virtual-buffer lsp--virtual-buffer) 9600 (lsp-org)) 9601 (when lsp-managed-mode 9602 (lsp-managed-mode -1) 9603 (lsp-mode -1) 9604 (setq lsp--buffer-workspaces nil) 9605 (setq lsp--virtual-buffer nil) 9606 (setq lsp-buffer-uri nil) 9607 9608 ;; force refresh of diagnostics 9609 (run-hooks 'lsp-after-diagnostics-hook)))) 9610 9611 (defun lsp-virtual-buffer-on-change (start end length) 9612 "Adjust on change event to be executed against the proper language server." 9613 (let ((max-point (max end 9614 (or (plist-get lsp--before-change-vals :end) 0) 9615 (+ start length)))) 9616 (when-let* ((virtual-buffer (-first (lambda (vb) 9617 (let ((lsp--virtual-buffer vb)) 9618 (and (lsp-virtual-buffer-call :in-range start) 9619 (lsp-virtual-buffer-call :in-range max-point)))) 9620 lsp--virtual-buffer-connections))) 9621 (lsp-with-current-buffer virtual-buffer 9622 (lsp-on-change start end length 9623 (lambda (&rest _) 9624 (list :range (lsp--range (list :character 0 :line 0) 9625 lsp--virtual-buffer-point-max) 9626 :text (lsp--buffer-content)))))))) 9627 9628 (defun lsp-virtual-buffer-before-change (start _end) 9629 (when-let* ((virtual-buffer (-first (lambda (vb) 9630 (lsp-with-current-buffer vb 9631 (lsp-virtual-buffer-call :in-range start))) 9632 lsp--virtual-buffer-connections))) 9633 (lsp-with-current-buffer virtual-buffer 9634 (setq lsp--virtual-buffer-point-max 9635 (lsp--point-to-position (lsp-virtual-buffer-call :last-point)))))) 9636 9637 (defun lsp-patch-on-change-event () 9638 (remove-hook 'after-change-functions #'lsp-on-change t) 9639 (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t) 9640 (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t)) 9641 9642 (defun lsp-kill-virtual-buffers () 9643 (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections)) 9644 9645 (defun lsp--move-point-in-indentation (point indentation) 9646 (save-excursion 9647 (goto-char point) 9648 (if (<= point (+ (line-beginning-position) indentation)) 9649 (line-beginning-position) 9650 point))) 9651 9652 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck") 9653 (declare-function flycheck-add-mode "ext:flycheck") 9654 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics") 9655 9656 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn) 9657 9658 (defun lsp-flycheck-add-mode (mode) 9659 "Register flycheck support for MODE." 9660 (lsp-diagnostics-lsp-checker-if-needed) 9661 (unless (flycheck-checker-supports-major-mode-p 'lsp mode) 9662 (flycheck-add-mode 'lsp mode))) 9663 9664 (defun lsp-progress-spinner-type () 9665 "Retrieve the spinner type value, if value is not a symbol of `spinner-types 9666 defaults to `progress-bar." 9667 (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar)) 9668 9669 (defun lsp-org () 9670 (interactive) 9671 (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range)) 9672 (funcall in-range)) 9673 lsp--virtual-buffer-connections)) 9674 (unless (equal lsp--virtual-buffer virtual-buffer) 9675 (setq lsp--buffer-workspaces workspaces) 9676 (setq lsp--virtual-buffer virtual-buffer) 9677 (setq lsp-buffer-uri nil) 9678 (lsp-mode 1) 9679 (lsp-managed-mode 1) 9680 (lsp-patch-on-change-event)) 9681 9682 (save-excursion 9683 (-let* (virtual-buffer 9684 (wcb (lambda (f) 9685 (with-current-buffer (plist-get virtual-buffer :buffer) 9686 (-let* (((&plist :major-mode :buffer-file-name 9687 :goto-buffer :workspaces) virtual-buffer) 9688 (lsp--virtual-buffer virtual-buffer) 9689 (lsp--buffer-workspaces workspaces)) 9690 (save-excursion 9691 (funcall goto-buffer) 9692 (funcall f)))))) 9693 ((&plist :begin :end :post-blank :language) (cl-second (org-element-context))) 9694 ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light))) 9695 9696 (file-name (if file-name 9697 (f-expand file-name) 9698 (user-error "You should specify file name in the src block header."))) 9699 (begin-marker (progn 9700 (goto-char begin) 9701 (forward-line) 9702 (set-marker (make-marker) (point)))) 9703 (end-marker (progn 9704 (goto-char end) 9705 (forward-line (1- (- post-blank))) 9706 (set-marker (make-marker) (1+ (point))))) 9707 (buf (current-buffer)) 9708 (src-block (buffer-substring-no-properties begin-marker 9709 (1- end-marker))) 9710 (indentation (with-temp-buffer 9711 (insert src-block) 9712 9713 (goto-char (point-min)) 9714 (let ((indentation (current-indentation))) 9715 (plist-put lsp--virtual-buffer :indentation indentation) 9716 (org-do-remove-indentation) 9717 (goto-char (point-min)) 9718 (- indentation (current-indentation)))))) 9719 (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t) 9720 9721 (when (fboundp 'flycheck-add-mode) 9722 (lsp-flycheck-add-mode 'org-mode)) 9723 9724 (setq lsp--virtual-buffer 9725 (list 9726 :in-range (lambda (&optional point) 9727 (<= begin-marker (or point (point)) (1- end-marker))) 9728 :goto-buffer (lambda () (goto-char begin-marker)) 9729 :buffer-string 9730 (lambda () 9731 (let ((src-block (buffer-substring-no-properties 9732 begin-marker 9733 (1- end-marker)))) 9734 (with-temp-buffer 9735 (insert src-block) 9736 9737 (goto-char (point-min)) 9738 (while (not (eobp)) 9739 (delete-region (point) (if (> (+ (point) indentation) (line-end-position)) 9740 (line-end-position) 9741 (+ (point) indentation))) 9742 (forward-line)) 9743 (buffer-substring-no-properties (point-min) 9744 (point-max))))) 9745 :buffer buf 9746 :begin begin-marker 9747 :end end-marker 9748 :indentation indentation 9749 :last-point (lambda () (1- end-marker)) 9750 :cur-position (lambda () 9751 (lsp-save-restriction-and-excursion 9752 (list :line (- (lsp--cur-line) 9753 (lsp--cur-line begin-marker)) 9754 :character (let ((character (- (point) 9755 (line-beginning-position) 9756 indentation))) 9757 (if (< character 0) 9758 0 9759 character))))) 9760 :line/character->point (-lambda (line character) 9761 (-let [inhibit-field-text-motion t] 9762 (+ indentation 9763 (lsp-save-restriction-and-excursion 9764 (goto-char begin-marker) 9765 (forward-line line) 9766 (-let [line-end (line-end-position)] 9767 (if (> character (- line-end (point))) 9768 line-end 9769 (forward-char character) 9770 (point))))))) 9771 :major-mode (org-src-get-lang-mode language) 9772 :buffer-file-name file-name 9773 :buffer-uri (lsp--path-to-uri file-name) 9774 :with-current-buffer wcb 9775 :buffer-live? (lambda (_) (buffer-live-p buf)) 9776 :buffer-name (lambda (_) 9777 (propertize (format "%s(%s:%s)%s" 9778 (buffer-name buf) 9779 begin-marker 9780 end-marker 9781 language) 9782 'face 'italic)) 9783 :real->virtual-line (lambda (line) 9784 (+ line (line-number-at-pos begin-marker) -1)) 9785 :real->virtual-char (lambda (char) (+ char indentation)) 9786 :cleanup (lambda () 9787 (set-marker begin-marker nil) 9788 (set-marker end-marker nil)))) 9789 (setf virtual-buffer lsp--virtual-buffer) 9790 (puthash file-name virtual-buffer lsp--virtual-buffer-mappings) 9791 (push virtual-buffer lsp--virtual-buffer-connections) 9792 9793 ;; TODO: tangle only connected sections 9794 (add-hook 'after-save-hook 'org-babel-tangle nil t) 9795 (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t) 9796 (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t) 9797 9798 (setq lsp--buffer-workspaces 9799 (lsp-with-current-buffer virtual-buffer 9800 (lsp) 9801 (plist-put virtual-buffer :workspaces (lsp-workspaces)) 9802 (lsp-workspaces))))))) 9803 9804 (defun lsp-virtual-buffer-disconnect (virtual-buffer) 9805 (interactive (list (or 9806 lsp--virtual-buffer 9807 (when lsp--virtual-buffer-connections 9808 (lsp--completing-read "Select virtual buffer to disconnect: " 9809 lsp--virtual-buffer-connections 9810 (-lambda ((&plist :buffer-file-name)) 9811 buffer-file-name)))))) 9812 (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer) 9813 (progn 9814 (lsp-with-current-buffer virtual-buffer 9815 (lsp--text-document-did-close)) 9816 (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections)) 9817 (when (eq virtual-buffer lsp--virtual-buffer) 9818 (setf lsp--virtual-buffer nil)) 9819 (when cleanup (funcall cleanup)) 9820 (remhash file-name lsp--virtual-buffer-mappings) 9821 9822 (lsp--virtual-buffer-update-position) 9823 (lsp--info "Disconnected from buffer %s" file-name)) 9824 (lsp--error "Nothing to disconnect from?"))) 9825 9826 9827 ;; inlay hints 9828 9829 (defface lsp-inlay-hint-face 9830 '((t :inherit font-lock-comment-face)) 9831 "The face to use for the JavaScript inlays." 9832 :group 'lsp-mode 9833 :package-version '(lsp-mode . "9.0.0")) 9834 9835 (defface lsp-inlay-hint-type-face 9836 '((t :inherit lsp-inlay-hint-face)) 9837 "Face for inlay type hints (e.g. inferred variable types)." 9838 :group 'lsp-mode 9839 :package-version '(lsp-mode . "9.0.0")) 9840 9841 (defcustom lsp-inlay-hint-type-format "%s" 9842 "Format string for variable inlays (part of the inlay face)." 9843 :type '(string :tag "String") 9844 :group 'lsp-mode 9845 :package-version '(lsp-mode . "9.0.0")) 9846 9847 (defface lsp-inlay-hint-parameter-face 9848 '((t :inherit lsp-inlay-hint-face)) 9849 "Face for inlay parameter hints (e.g. function parameter names at 9850 call-site)." 9851 :group 'lsp-mode 9852 :package-version '(lsp-mode . "9.0.0")) 9853 9854 (defcustom lsp-inlay-hint-param-format "%s" 9855 "Format string for parameter inlays (part of the inlay face)." 9856 :type '(string :tag "String") 9857 :group 'lsp-mode 9858 :package-version '(lsp-mode . "9.0.0")) 9859 9860 (defcustom lsp-update-inlay-hints-on-scroll t 9861 "If non-nil update inlay hints immediately when scrolling or 9862 modifying window sizes." 9863 :type 'boolean 9864 :package-version '(lsp-mode . "9.0.0")) 9865 9866 (defun lsp--format-inlay (text kind) 9867 (cond 9868 ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text)) 9869 ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text)) 9870 (t text))) 9871 9872 (defun lsp--face-for-inlay (kind) 9873 (cond 9874 ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face) 9875 ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face) 9876 (t 'lsp-inlay-hint-face))) 9877 9878 (defun lsp--update-inlay-hints-scroll-function (window start) 9879 (lsp-update-inlay-hints start (window-end window t))) 9880 9881 (defun lsp--update-inlay-hints () 9882 (lsp-update-inlay-hints (window-start) (window-end nil t))) 9883 9884 (defun lsp--label-from-inlay-hints-response (label) 9885 "Returns a string label built from an array of 9886 InlayHintLabelParts or the argument itself if it's already a 9887 string." 9888 (cl-typecase label 9889 (string label) 9890 (vector 9891 (string-join (mapcar (lambda (part) 9892 (-let (((&InlayHintLabelPart :value) part)) 9893 value)) 9894 label))))) 9895 9896 (defun lsp-update-inlay-hints (start end) 9897 (lsp-request-async 9898 "textDocument/inlayHint" 9899 (lsp-make-inlay-hints-params 9900 :text-document (lsp--text-document-identifier) 9901 :range (lsp-make-range :start 9902 (lsp-point-to-position start) 9903 :end 9904 (lsp-point-to-position end))) 9905 (lambda (res) 9906 (lsp--remove-overlays 'lsp-inlay-hint) 9907 (dolist (hint res) 9908 (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint) 9909 (kind (or kind? lsp/inlay-hint-kind-type-hint)) 9910 (label (lsp--label-from-inlay-hints-response label)) 9911 (pos (lsp--position-to-point position)) 9912 (overlay (make-overlay pos pos nil 'front-advance 'end-advance))) 9913 (when (stringp label) 9914 (overlay-put overlay 'lsp-inlay-hint t) 9915 (overlay-put overlay 'before-string 9916 (format "%s%s%s" 9917 (if padding-left? " " "") 9918 (propertize (lsp--format-inlay label kind) 9919 'font-lock-face (lsp--face-for-inlay kind)) 9920 (if padding-right? " " ""))))))) 9921 :mode 'tick)) 9922 9923 (define-minor-mode lsp-inlay-hints-mode 9924 "Mode for displaying inlay hints." 9925 :lighter nil 9926 (cond 9927 ((and lsp-inlay-hints-mode lsp--buffer-workspaces) 9928 (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t) 9929 (when lsp-update-inlay-hints-on-scroll 9930 (add-to-list (make-local-variable 'window-scroll-functions) 9931 #'lsp--update-inlay-hints-scroll-function))) 9932 (t 9933 (lsp--remove-overlays 'lsp-inlay-hint) 9934 (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t) 9935 (setf window-scroll-functions 9936 (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions))))) 9937 9938 9939 9940 ;;;###autoload 9941 (defun lsp-start-plain () 9942 "Start `lsp-mode' using minimal configuration using the latest `melpa' version 9943 of the packages. 9944 9945 In case the major-mode that you are using for " 9946 (interactive) 9947 (let ((start-plain (make-temp-file "plain" nil ".el"))) 9948 (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el" 9949 start-plain t) 9950 (start-process "lsp-start-plain" 9951 (generate-new-buffer " *lsp-start-plain*") 9952 (expand-file-name invocation-name invocation-directory) 9953 "-q" "-l" start-plain (or (buffer-file-name) "")))) 9954 9955 9956 9957 (provide 'lsp-mode) 9958 ;;; lsp-mode.el ends here