lsp-mode.el (434178B)
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: 20240930.1803 9 ;; Package-Revision: 532d38b3472c 10 11 ;; URL: https://github.com/emacs-lsp/lsp-mode 12 ;; This program is free software; you can redistribute it and/or modify 13 ;; it under the terms of the GNU General Public License as published by 14 ;; the Free Software Foundation, either version 3 of the License, or 15 ;; (at your option) any later version. 16 17 ;; This program is distributed in the hope that it will be useful, 18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 ;; GNU General Public License for more details. 21 22 ;; You should have received a copy of the GNU General Public License 23 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 24 25 ;;; Commentary: 26 27 ;; Emacs client/library for the Language Server Protocol 28 29 ;;; Code: 30 31 (require 'cl-generic) 32 (require 'cl-lib) 33 (require 'compile) 34 (require 'dash) 35 (require 'epg) 36 (require 'ewoc) 37 (require 'f) 38 (require 'filenotify) 39 (require 'files) 40 (require 'ht) 41 (require 'imenu) 42 (require 'inline) 43 (require 'json) 44 (require 'lv) 45 (require 'markdown-mode) 46 (require 'network-stream) 47 (require 'pcase) 48 (require 'rx) 49 (require 's) 50 (require 'seq) 51 (require 'spinner) 52 (require 'subr-x) 53 (require 'tree-widget) 54 (require 'url-parse) 55 (require 'url-util) 56 (require 'widget) 57 (require 'xref) 58 (require 'minibuffer) 59 (require 'help-mode) 60 (require 'lsp-protocol) 61 62 (defgroup lsp-mode nil 63 "Language Server Protocol client." 64 :group 'tools 65 :tag "Language Server (lsp-mode)") 66 67 (declare-function evil-set-command-property "ext:evil-common") 68 (declare-function projectile-project-root "ext:projectile") 69 (declare-function yas-expand-snippet "ext:yasnippet") 70 (declare-function dap-mode "ext:dap-mode") 71 (declare-function dap-auto-configure-mode "ext:dap-mode") 72 73 (defvar yas-inhibit-overlay-modification-protection) 74 (defvar yas-indent-line) 75 (defvar yas-wrap-around-region) 76 (defvar yas-also-auto-indent-first-line) 77 (defvar dap-auto-configure-mode) 78 (defvar dap-ui-menu-items) 79 (defvar company-minimum-prefix-length) 80 81 (defconst lsp--message-type-face 82 `((1 . ,compilation-error-face) 83 (2 . ,compilation-warning-face) 84 (3 . ,compilation-message-face) 85 (4 . ,compilation-info-face))) 86 87 (defconst lsp--errors 88 '((-32700 "Parse Error") 89 (-32600 "Invalid Request") 90 (-32601 "Method not Found") 91 (-32602 "Invalid Parameters") 92 (-32603 "Internal Error") 93 (-32099 "Server Start Error") 94 (-32000 "Server End Error") 95 (-32002 "Server Not Initialized") 96 (-32001 "Unknown Error Code") 97 (-32800 "Request Cancelled")) 98 "Alist of error codes to user friendly strings.") 99 100 (defconst lsp--empty-ht (make-hash-table)) 101 102 (eval-and-compile 103 (defun dash-expand:&lsp-wks (key source) 104 `(,(intern-soft (format "lsp--workspace-%s" (eval key))) ,source)) 105 106 (defun dash-expand:&lsp-cln (key source) 107 `(,(intern-soft (format "lsp--client-%s" (eval key))) ,source))) 108 109 (define-obsolete-variable-alias 'lsp-print-io 'lsp-log-io "lsp-mode 6.1") 110 111 (defcustom lsp-log-io nil 112 "If non-nil, log all messages from the language server to a *lsp-log* buffer." 113 :group 'lsp-mode 114 :type 'boolean) 115 116 (defcustom lsp-log-io-allowlist-methods '() 117 "The methods to filter before print to lsp-log-io." 118 :group 'lsp-mode 119 :type '(repeat string) 120 :package-version '(lsp-mode . "9.0.0")) 121 122 (defcustom lsp-log-max message-log-max 123 "Maximum number of lines to keep in the log buffer. 124 If nil, disable message logging. If t, log messages but don’t truncate 125 the buffer when it becomes large." 126 :group 'lsp-mode 127 :type '(choice (const :tag "Disable" nil) 128 (integer :tag "lines") 129 (const :tag "Unlimited" t)) 130 :package-version '(lsp-mode . "6.1")) 131 132 (defcustom lsp-io-messages-max t 133 "Maximum number of messages that can be locked in a `lsp-io' buffer." 134 :group 'lsp-mode 135 :type '(choice (const :tag "Unlimited" t) 136 (integer :tag "Messages")) 137 :package-version '(lsp-mode . "6.1")) 138 139 (defcustom lsp-keep-workspace-alive t 140 "If non nil keep workspace alive when the last workspace buffer is closed." 141 :group 'lsp-mode 142 :type 'boolean) 143 144 (defcustom lsp-enable-snippet t 145 "Enable/disable snippet completion support." 146 :group 'lsp-completion 147 :type 'boolean) 148 149 (defcustom lsp-enable-folding t 150 "Enable/disable code folding support." 151 :group 'lsp-mode 152 :type 'boolean 153 :package-version '(lsp-mode . "6.1")) 154 155 (define-obsolete-variable-alias 'lsp-enable-semantic-highlighting 'lsp-semantic-tokens-enable "lsp-mode 8.0.0") 156 157 (defcustom lsp-semantic-tokens-enable nil 158 "Enable/disable support for semantic tokens. 159 As defined by the Language Server Protocol 3.16." 160 :group 'lsp-semantic-tokens 161 :type 'boolean) 162 163 (defcustom lsp-folding-range-limit nil 164 "The maximum number of folding ranges to receive from the language server." 165 :group 'lsp-mode 166 :type '(choice (const :tag "No limit." nil) 167 (integer :tag "Number of lines.")) 168 :package-version '(lsp-mode . "6.1")) 169 170 (defcustom lsp-folding-line-folding-only nil 171 "If non-nil, only fold complete lines." 172 :group 'lsp-mode 173 :type 'boolean 174 :package-version '(lsp-mode . "6.1")) 175 176 (defcustom lsp-client-packages 177 '( ccls lsp-actionscript lsp-ada lsp-angular lsp-ansible lsp-asm lsp-astro 178 lsp-autotools lsp-awk lsp-bash lsp-beancount lsp-bufls lsp-clangd 179 lsp-clojure lsp-cmake lsp-cobol lsp-credo lsp-crystal lsp-csharp lsp-css 180 lsp-cucumber lsp-cypher lsp-d lsp-dart lsp-dhall lsp-docker lsp-dockerfile 181 lsp-earthly lsp-elixir lsp-elm lsp-emmet lsp-erlang lsp-eslint lsp-fortran lsp-futhark 182 lsp-fsharp lsp-gdscript lsp-gleam lsp-glsl lsp-go lsp-golangci-lint lsp-grammarly 183 lsp-graphql lsp-groovy lsp-hack lsp-haskell lsp-haxe lsp-idris lsp-java 184 lsp-javascript lsp-jq lsp-json lsp-kotlin lsp-latex lsp-lisp lsp-ltex 185 lsp-lua lsp-magik lsp-markdown lsp-marksman lsp-mdx lsp-meson lsp-metals lsp-mint 186 lsp-mojo lsp-move lsp-mssql lsp-nginx lsp-nim lsp-nix lsp-nushell lsp-ocaml 187 lsp-openscad lsp-pascal lsp-perl lsp-perlnavigator lsp-php lsp-pls 188 lsp-purescript lsp-pwsh lsp-pyls lsp-pylsp lsp-pyright lsp-python-ms 189 lsp-qml lsp-r lsp-racket lsp-remark lsp-rf lsp-roslyn lsp-rubocop lsp-ruby-lsp 190 lsp-ruby-syntax-tree lsp-ruff lsp-rust lsp-semgrep lsp-shader 191 lsp-solargraph lsp-solidity lsp-sonarlint lsp-sorbet lsp-sourcekit 192 lsp-sql lsp-sqls lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform 193 lsp-tex lsp-tilt lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-v 194 lsp-vala lsp-verilog lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl 195 lsp-xml lsp-yaml lsp-yang lsp-zig) 196 "List of the clients to be automatically required." 197 :group 'lsp-mode 198 :type '(repeat symbol)) 199 200 (defcustom lsp-progress-via-spinner t 201 "If non-nil, display LSP $/progress reports via a spinner in the modeline." 202 :group 'lsp-mode 203 :type 'boolean) 204 205 (defcustom lsp-progress-spinner-type 'progress-bar 206 "Holds the type of spinner to be used in the mode-line. 207 Takes a value accepted by `spinner-start'." 208 :group 'lsp-mode 209 :type `(choice :tag "Choose a spinner by name" 210 ,@(mapcar (lambda (c) (list 'const (car c))) 211 spinner-types))) 212 213 (defvar-local lsp-use-workspace-root-for-server-default-directory nil 214 "Use `lsp-workspace-root' for `default-directory' when starting LSP process.") 215 216 (defvar-local lsp--cur-workspace nil) 217 218 (defvar-local lsp--cur-version 0) 219 (defvar-local lsp--virtual-buffer-connections nil) 220 (defvar-local lsp--virtual-buffer nil) 221 (defvar lsp--virtual-buffer-mappings (ht)) 222 223 (defvar lsp--uri-file-prefix (pcase system-type 224 (`windows-nt "file:///") 225 (_ "file://")) 226 "Prefix for a file-uri.") 227 228 (defvar-local lsp-buffer-uri nil 229 "If set, return it instead of calculating it using `buffer-file-name'.") 230 231 (define-error 'lsp-error "Unknown lsp-mode error") 232 (define-error 'lsp-empty-response-error 233 "Empty response from the language server" 'lsp-error) 234 (define-error 'lsp-timed-out-error 235 "Timed out while waiting for a response from the language server" 'lsp-error) 236 (define-error 'lsp-capability-not-supported 237 "Capability not supported by the language server" 'lsp-error) 238 (define-error 'lsp-file-scheme-not-supported 239 "Unsupported file scheme" 'lsp-error) 240 (define-error 'lsp-client-already-exists-error 241 "A client with this server-id already exists" 'lsp-error) 242 (define-error 'lsp-no-code-actions 243 "No code actions" 'lsp-error) 244 245 (defcustom lsp-auto-guess-root nil 246 "Automatically guess the project root using projectile/project. 247 Do *not* use this setting unless you are familiar with `lsp-mode' 248 internals and you are sure that all of your projects are 249 following `projectile'/`project.el' conventions." 250 :group 'lsp-mode 251 :type 'boolean) 252 253 (defcustom lsp-guess-root-without-session nil 254 "Ignore the session file when calculating the project root. 255 You almost always want to set lsp-auto-guess-root too. 256 Do *not* use this setting unless you are familiar with `lsp-mode' 257 internals and you are sure that all of your projects are 258 following `projectile'/`project.el' conventions." 259 :group 'lsp-mode 260 :type 'boolean) 261 262 (defcustom lsp-restart 'interactive 263 "Defines how server-exited events must be handled." 264 :group 'lsp-mode 265 :type '(choice (const interactive) 266 (const auto-restart) 267 (const ignore))) 268 269 (defcustom lsp-session-file (expand-file-name (locate-user-emacs-file ".lsp-session-v1")) 270 "File where session information is stored." 271 :group 'lsp-mode 272 :type 'file) 273 274 (defcustom lsp-auto-configure t 275 "Auto configure `lsp-mode' main features. 276 When set to t `lsp-mode' will auto-configure completion, 277 code-actions, breadcrumb, `flycheck', `flymake', `imenu', symbol highlighting, 278 lenses, links, and so on. 279 280 For finer granularity you may use `lsp-enable-*' properties." 281 :group 'lsp-mode 282 :type 'boolean 283 :package-version '(lsp-mode . "6.1")) 284 285 (defcustom lsp-disabled-clients nil 286 "A list of disabled/blocklisted clients. 287 Each entry in the list can be either: 288 a symbol, the server-id for the LSP client, or 289 a cons pair (MAJOR-MODE . CLIENTS), where MAJOR-MODE is the major-mode, 290 and CLIENTS is either a client or a list of clients. 291 292 This option can also be used as a file- or directory-local variable to 293 disable a language server for individual files or directories/projects 294 respectively." 295 :group 'lsp-mode 296 :type '(repeat (symbol)) 297 :safe 'listp 298 :package-version '(lsp-mode . "6.1")) 299 300 (defvar lsp-clients (make-hash-table :test 'eql) 301 "Hash table server-id -> client. 302 It contains all of the clients that are currently registered.") 303 304 (defvar lsp-enabled-clients nil 305 "List of clients allowed to be used for projects. 306 When nil, all registered clients are considered candidates.") 307 308 (defvar lsp-last-id 0 309 "Last request id.") 310 311 (defcustom lsp-before-initialize-hook nil 312 "List of functions to be called before a Language Server has been initialized 313 for a new workspace." 314 :type 'hook 315 :group 'lsp-mode) 316 317 (defcustom lsp-after-initialize-hook nil 318 "List of functions to be called after a Language Server has been initialized 319 for a new workspace." 320 :type 'hook 321 :group 'lsp-mode) 322 323 (defcustom lsp-before-open-hook nil 324 "List of functions to be called before a new file with LSP support is opened." 325 :type 'hook 326 :group 'lsp-mode) 327 328 (defcustom lsp-after-open-hook nil 329 "List of functions to be called after a new file with LSP support is opened." 330 :type 'hook 331 :group 'lsp-mode) 332 333 (defcustom lsp-enable-file-watchers t 334 "If non-nil lsp-mode will watch the files in the workspace if 335 the server has requested that." 336 :type 'boolean 337 :group 'lsp-mode 338 :package-version '(lsp-mode . "6.1")) 339 ;;;###autoload(put 'lsp-enable-file-watchers 'safe-local-variable #'booleanp) 340 341 (define-obsolete-variable-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "8.0.0") 342 343 (defcustom lsp-file-watch-ignored-directories 344 '(; SCM tools 345 "[/\\\\]\\.git\\'" 346 "[/\\\\]\\.github\\'" 347 "[/\\\\]\\.gitlab\\'" 348 "[/\\\\]\\.circleci\\'" 349 "[/\\\\]\\.hg\\'" 350 "[/\\\\]\\.bzr\\'" 351 "[/\\\\]_darcs\\'" 352 "[/\\\\]\\.svn\\'" 353 "[/\\\\]_FOSSIL_\\'" 354 ;; IDE or build tools 355 "[/\\\\]\\.idea\\'" 356 "[/\\\\]\\.ensime_cache\\'" 357 "[/\\\\]\\.eunit\\'" 358 "[/\\\\]node_modules" 359 "[/\\\\]\\.yarn\\'" 360 "[/\\\\]\\.fslckout\\'" 361 "[/\\\\]\\.tox\\'" 362 "[/\\\\]\\.nox\\'" 363 "[/\\\\]dist\\'" 364 "[/\\\\]dist-newstyle\\'" 365 "[/\\\\]\\.stack-work\\'" 366 "[/\\\\]\\.bloop\\'" 367 "[/\\\\]\\.metals\\'" 368 "[/\\\\]target\\'" 369 "[/\\\\]\\.ccls-cache\\'" 370 "[/\\\\]\\.vs\\'" 371 "[/\\\\]\\.vscode\\'" 372 "[/\\\\]\\.venv\\'" 373 "[/\\\\]\\.mypy_cache\\'" 374 "[/\\\\]\\.pytest_cache\\'" 375 ;; Swift Package Manager 376 "[/\\\\]\\.build\\'" 377 ;; Python 378 "[/\\\\]__pycache__\\'" 379 ;; Autotools output 380 "[/\\\\]\\.deps\\'" 381 "[/\\\\]build-aux\\'" 382 "[/\\\\]autom4te.cache\\'" 383 "[/\\\\]\\.reference\\'" 384 ;; Bazel 385 "[/\\\\]bazel-[^/\\\\]+\\'" 386 ;; CSharp 387 "[/\\\\]\\.cache[/\\\\]lsp-csharp\\'" 388 "[/\\\\]\\.meta\\'" 389 "[/\\\\]\\.nuget\\'" 390 ;; Unity 391 "[/\\\\]Library\\'" 392 ;; Clojure 393 "[/\\\\]\\.lsp\\'" 394 "[/\\\\]\\.clj-kondo\\'" 395 "[/\\\\]\\.shadow-cljs\\'" 396 "[/\\\\]\\.babel_cache\\'" 397 "[/\\\\]\\.cpcache\\'" 398 "[/\\\\]\\checkouts\\'" 399 ;; Gradle 400 "[/\\\\]\\.gradle\\'" 401 ;; Maven 402 "[/\\\\]\\.m2\\'" 403 ;; .Net Core build-output 404 "[/\\\\]bin/Debug\\'" 405 "[/\\\\]obj\\'" 406 ;; OCaml and Dune 407 "[/\\\\]_opam\\'" 408 "[/\\\\]_build\\'" 409 ;; Elixir 410 "[/\\\\]\\.elixir_ls\\'" 411 ;; Elixir Credo 412 "[/\\\\]\\.elixir-tools\\'" 413 ;; terraform and terragrunt 414 "[/\\\\]\\.terraform\\'" 415 "[/\\\\]\\.terragrunt-cache\\'" 416 ;; nix-direnv 417 "[/\\\\]\\result" 418 "[/\\\\]\\result-bin" 419 "[/\\\\]\\.direnv\\'") 420 "List of regexps matching directory paths which won't be monitored when 421 creating file watches. Customization of this variable is only honored at 422 the global level or at a root of an lsp workspace." 423 :group 'lsp-mode 424 :type '(repeat string) 425 :package-version '(lsp-mode . "8.0.0")) 426 427 (define-obsolete-function-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "7.0.1") 428 429 (defun lsp-file-watch-ignored-directories () 430 lsp-file-watch-ignored-directories) 431 432 ;; Allow lsp-file-watch-ignored-directories as a file or directory-local variable 433 ;;;###autoload(put 'lsp-file-watch-ignored-directories 'safe-local-variable 'lsp--string-listp) 434 435 (defcustom lsp-file-watch-ignored-files 436 '( 437 ;; Flycheck tempfiles 438 "[/\\\\]flycheck_[^/\\\\]+\\'" 439 ;; lockfiles 440 "[/\\\\]\\.#[^/\\\\]+\\'" 441 ;; backup files 442 "[/\\\\][^/\\\\]+~\\'" ) 443 "List of regexps matching files for which change events will 444 not be sent to the server. 445 446 This setting has no impact on whether a file-watch is created for 447 a directory; it merely prevents notifications pertaining to 448 matched files from being sent to the server. To prevent a 449 file-watch from being created for a directory, customize 450 `lsp-file-watch-ignored-directories' 451 452 Customization of this variable is only honored at the global 453 level or at a root of an lsp workspace." 454 :group 'lsp-mode 455 :type '(repeat string) 456 :package-version '(lsp-mode . "8.0.0")) 457 458 ;; Allow lsp-file-watch-ignored-files as a file or directory-local variable 459 ;;;###autoload(put 'lsp-file-watch-ignored-files 'safe-local-variable 'lsp--string-listp) 460 461 (defcustom lsp-after-uninitialized-functions nil 462 "List of functions to be called after a Language Server has been uninitialized." 463 :type 'hook 464 :group 'lsp-mode 465 :package-version '(lsp-mode . "6.3")) 466 467 (defconst lsp--sync-full 1) 468 (defconst lsp--sync-incremental 2) 469 470 (defcustom lsp-debounce-full-sync-notifications t 471 "If non-nil debounce full sync events. 472 This flag affects only servers which do not support incremental updates." 473 :type 'boolean 474 :group 'lsp-mode 475 :package-version '(lsp-mode . "6.1")) 476 477 (defcustom lsp-debounce-full-sync-notifications-interval 1.0 478 "Time to wait before sending full sync synchronization after buffer modification." 479 :type 'float 480 :group 'lsp-mode 481 :package-version '(lsp-mode . "6.1")) 482 483 (defvar lsp--stderr-index 0) 484 485 (defvar lsp--delayed-requests nil) 486 (defvar lsp--delay-timer nil) 487 488 (defcustom lsp-document-sync-method nil 489 "How to sync the document with the language server." 490 :type '(choice (const :tag "Documents are synced by always sending the full content of the document." lsp--sync-full) 491 (const :tag "Documents are synced by always sending incremental changes to the document." lsp--sync-incremental) 492 (const :tag "Use the method recommended by the language server." nil)) 493 :group 'lsp-mode) 494 495 (defcustom lsp-auto-execute-action t 496 "Auto-execute single action." 497 :type 'boolean 498 :group 'lsp-mode) 499 500 (defcustom lsp-enable-links t 501 "If non-nil, all references to links in a file will be made clickable, if 502 supported by the language server." 503 :type 'boolean 504 :group 'lsp-mode 505 :package-version '(lsp-mode . "6.1")) 506 507 (defcustom lsp-enable-imenu t 508 "If non-nil, automatically enable `imenu' integration when server provides 509 `textDocument/documentSymbol'." 510 :type 'boolean 511 :group 'lsp-mode 512 :package-version '(lsp-mode . "6.2")) 513 514 (defcustom lsp-enable-dap-auto-configure t 515 "If non-nil, enable `dap-auto-configure-mode`." 516 :type 'boolean 517 :group 'lsp-mode 518 :package-version '(lsp-mode . "7.0")) 519 520 (defcustom lsp-eldoc-enable-hover t 521 "If non-nil, `eldoc' will display hover info when it is present." 522 :type 'boolean 523 :group 'lsp-mode) 524 525 (defcustom lsp-eldoc-render-all nil 526 "Display all of the info returned by document/onHover. 527 If this is set to nil, `eldoc' will show only the symbol information." 528 :type 'boolean 529 :group 'lsp-mode) 530 531 (define-obsolete-variable-alias 'lsp-enable-completion-at-point 532 'lsp-completion-enable "lsp-mode 7.0.1") 533 534 (defcustom lsp-completion-enable t 535 "Enable `completion-at-point' integration." 536 :type 'boolean 537 :group 'lsp-completion) 538 539 (defcustom lsp-enable-symbol-highlighting t 540 "Highlight references of the symbol at point." 541 :type 'boolean 542 :group 'lsp-mode) 543 544 (defcustom lsp-enable-xref t 545 "Enable xref integration." 546 :type 'boolean 547 :group 'lsp-mode) 548 549 (defcustom lsp-references-exclude-definition nil 550 "If non-nil, exclude declarations when finding references." 551 :type 'boolean 552 :group 'lsp-mode) 553 554 (defcustom lsp-enable-indentation t 555 "Indent regions using the file formatting functionality provided by the 556 language server." 557 :type 'boolean 558 :group 'lsp-mode) 559 560 (defcustom lsp-enable-on-type-formatting t 561 "Enable `textDocument/onTypeFormatting' integration." 562 :type 'boolean 563 :group 'lsp-mode) 564 565 (defcustom lsp-enable-text-document-color t 566 "Enable `textDocument/documentColor' integration." 567 :type 'boolean 568 :group 'lsp-mode) 569 570 (defcustom lsp-before-save-edits t 571 "If non-nil, `lsp-mode' will apply edits suggested by the language server 572 before saving a document." 573 :type 'boolean 574 :group 'lsp-mode) 575 576 (defcustom lsp-after-apply-edits-hook nil 577 "Hooks to run when text edit is applied. 578 It contains the operation source." 579 :type 'hook 580 :group 'lsp-mode 581 :package-version '(lsp-mode . "8.0.0")) 582 583 (defcustom lsp-apply-edits-after-file-operations t 584 "Whether to apply edits returned by server after file operations if any. 585 Applicable only if server supports workspace.fileOperations for operations: 586 `workspace/willRenameFiles', `workspace/willCreateFiles' and 587 `workspace/willDeleteFiles'." 588 :group 'lsp-mode 589 :type 'boolean) 590 591 (defcustom lsp-modeline-code-actions-enable t 592 "Whether to show code actions on modeline." 593 :type 'boolean 594 :group 'lsp-modeline) 595 596 (defcustom lsp-modeline-diagnostics-enable t 597 "Whether to show diagnostics on modeline." 598 :type 'boolean 599 :group 'lsp-modeline) 600 601 (defcustom lsp-modeline-workspace-status-enable t 602 "Whether to show workspace status on modeline." 603 :type 'boolean 604 :group 'lsp-modeline 605 :package-version '(lsp-mode . "8.0.0")) 606 607 (defcustom lsp-headerline-breadcrumb-enable t 608 "Whether to enable breadcrumb on headerline." 609 :type 'boolean 610 :group 'lsp-headerline) 611 612 (defcustom lsp-configure-hook nil 613 "Hooks to run when `lsp-configure-buffer' is called." 614 :type 'hook 615 :group 'lsp-mode) 616 617 (defcustom lsp-unconfigure-hook nil 618 "Hooks to run when `lsp-unconfig-buffer' is called." 619 :type 'hook 620 :group 'lsp-mode) 621 622 (defcustom lsp-after-diagnostics-hook nil 623 "Hooks to run after diagnostics are received. 624 Note: it runs only if the receiving buffer is open. Use 625 `lsp-diagnostics-updated-hook'if you want to be notified when 626 diagnostics have changed." 627 :type 'hook 628 :group 'lsp-mode) 629 630 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook 631 'lsp-diagnostics-updated-hook "lsp-mode 6.4") 632 633 (defcustom lsp-diagnostics-updated-hook nil 634 "Hooks to run after diagnostics are received." 635 :type 'hook 636 :group 'lsp-mode) 637 638 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook 639 'lsp-workspace-folders-changed-functions "lsp-mode 6.3") 640 641 (defcustom lsp-workspace-folders-changed-functions nil 642 "Hooks to run after the folders has changed. 643 The hook will receive two parameters list of added and removed folders." 644 :type 'hook 645 :group 'lsp-mode) 646 647 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0") 648 649 (defcustom lsp-before-apply-edits-hook nil 650 "Hooks to run before applying edits." 651 :type 'hook 652 :group 'lsp-mode) 653 654 (defgroup lsp-imenu nil 655 "LSP Imenu." 656 :group 'lsp-mode 657 :tag "LSP Imenu") 658 659 (defcustom lsp-imenu-show-container-name t 660 "Display the symbol's container name in an imenu entry." 661 :type 'boolean 662 :group 'lsp-imenu) 663 664 (defcustom lsp-imenu-container-name-separator "/" 665 "Separator string to use to separate the container name from the symbol while 666 displaying imenu entries." 667 :type 'string 668 :group 'lsp-imenu) 669 670 (defcustom lsp-imenu-sort-methods '(kind name) 671 "How to sort the imenu items. 672 673 The value is a list of `kind' `name' or `position'. Priorities 674 are determined by the index of the element." 675 :type '(repeat (choice (const name) 676 (const position) 677 (const kind))) 678 :group 'lsp-imenu) 679 680 (defcustom lsp-imenu-index-symbol-kinds nil 681 "Which symbol kinds to show in imenu." 682 :type '(repeat (choice (const :tag "Miscellaneous" nil) 683 (const :tag "File" File) 684 (const :tag "Module" Module) 685 (const :tag "Namespace" Namespace) 686 (const :tag "Package" Package) 687 (const :tag "Class" Class) 688 (const :tag "Method" Method) 689 (const :tag "Property" Property) 690 (const :tag "Field" Field) 691 (const :tag "Constructor" Constructor) 692 (const :tag "Enum" Enum) 693 (const :tag "Interface" Interface) 694 (const :tag "Function" Function) 695 (const :tag "Variable" Variable) 696 (const :tag "Constant" Constant) 697 (const :tag "String" String) 698 (const :tag "Number" Number) 699 (const :tag "Boolean" Boolean) 700 (const :tag "Array" Array) 701 (const :tag "Object" Object) 702 (const :tag "Key" Key) 703 (const :tag "Null" Null) 704 (const :tag "Enum Member" EnumMember) 705 (const :tag "Struct" Struct) 706 (const :tag "Event" Event) 707 (const :tag "Operator" Operator) 708 (const :tag "Type Parameter" TypeParameter))) 709 :group 'lsp-imenu) 710 711 ;; vibhavp: Should we use a lower value (5)? 712 (defcustom lsp-response-timeout 10 713 "Number of seconds to wait for a response from the language server before 714 timing out. Nil if no timeout." 715 :type '(choice 716 (number :tag "Seconds") 717 (const :tag "No timeout" nil)) 718 :group 'lsp-mode) 719 720 (defcustom lsp-tcp-connection-timeout 2 721 "The timeout for tcp connection in seconds." 722 :type 'number 723 :group 'lsp-mode 724 :package-version '(lsp-mode . "6.2")) 725 726 (defconst lsp--imenu-compare-function-alist 727 (list (cons 'name #'lsp--imenu-compare-name) 728 (cons 'kind #'lsp--imenu-compare-kind) 729 (cons 'position #'lsp--imenu-compare-line-col)) 730 "An alist of (METHOD . FUNCTION). 731 METHOD is one of the symbols accepted by 732 `lsp-imenu-sort-methods'. 733 734 FUNCTION takes two hash tables representing DocumentSymbol. It 735 returns a negative number, 0, or a positive number indicating 736 whether the first parameter is less than, equal to, or greater 737 than the second parameter.") 738 739 (defcustom lsp-diagnostic-clean-after-change nil 740 "When non-nil, clean the diagnostics on change. 741 742 Note that when that setting is nil, `lsp-mode' will show stale 743 diagnostics until server publishes the new set of diagnostics" 744 :type 'boolean 745 :group 'lsp-diagnostics 746 :package-version '(lsp-mode . "7.0.1")) 747 748 (defcustom lsp-server-trace nil 749 "Request tracing on the server side. 750 The actual trace output at each level depends on the language server in use. 751 Changes take effect only when a new session is started." 752 :type '(choice (const :tag "Disabled" "off") 753 (const :tag "Messages only" "messages") 754 (const :tag "Verbose" "verbose") 755 (const :tag "Default (disabled)" nil)) 756 :group 'lsp-mode 757 :package-version '(lsp-mode . "6.1")) 758 759 (defcustom lsp-auto-touch-files t 760 "If non-nil ensure the files exist before sending 761 `textDocument/didOpen' notification." 762 :type 'boolean 763 :group 'lsp-mode 764 :package-version '(lsp-mode . "9.0.0")) 765 766 (defvar lsp-language-id-configuration 767 '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake") 768 ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile") 769 ("\\.astro$" . "astro") 770 ("\\.cs\\'" . "csharp") 771 ("\\.css$" . "css") 772 ("\\.cypher$" . "cypher") 773 ("Earthfile" . "earthfile") 774 ("\\.ebuild$" . "shellscript") 775 ("\\.go\\'" . "go") 776 ("\\.html$" . "html") 777 ("\\.hx$" . "haxe") 778 ("\\.hy$" . "hy") 779 ("\\.java\\'" . "java") 780 ("\\.jq$" . "jq") 781 ("\\.js$" . "javascript") 782 ("\\.json$" . "json") 783 ("\\.jsonc$" . "jsonc") 784 ("\\.jsonnet$" . "jsonnet") 785 ("\\.jsx$" . "javascriptreact") 786 ("\\.lua$" . "lua") 787 ("\\.mdx\\'" . "mdx") 788 ("\\.nu$" . "nushell") 789 ("\\.php$" . "php") 790 ("\\.ps[dm]?1\\'" . "powershell") 791 ("\\.rs\\'" . "rust") 792 ("\\.spec\\'" . "rpm-spec") 793 ("\\.sql$" . "sql") 794 ("\\.svelte$" . "svelte") 795 ("\\.toml\\'" . "toml") 796 ("\\.ts$" . "typescript") 797 ("\\.tsx$" . "typescriptreact") 798 ("\\.ttcn3$" . "ttcn3") 799 ("\\.vue$" . "vue") 800 ("\\.xml$" . "xml") 801 ("\\ya?ml$" . "yaml") 802 ("^PKGBUILD$" . "shellscript") 803 ("^go\\.mod\\'" . "go.mod") 804 ("^settings\\.json$" . "jsonc") 805 ("^yang\\.settings$" . "jsonc") 806 ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson") 807 (ada-mode . "ada") 808 (ada-ts-mode . "ada") 809 (gpr-mode . "gpr") 810 (gpr-ts-mode . "gpr") 811 (awk-mode . "awk") 812 (awk-ts-mode . "awk") 813 (nxml-mode . "xml") 814 (sql-mode . "sql") 815 (vimrc-mode . "vim") 816 (vimscript-ts-mode . "vim") 817 (sh-mode . "shellscript") 818 (bash-ts-mode . "shellscript") 819 (ebuild-mode . "shellscript") 820 (pkgbuild-mode . "shellscript") 821 (envrc-file-mode . "shellscript") 822 (scala-mode . "scala") 823 (scala-ts-mode . "scala") 824 (julia-mode . "julia") 825 (julia-ts-mode . "julia") 826 (clojure-mode . "clojure") 827 (clojurec-mode . "clojure") 828 (clojurescript-mode . "clojurescript") 829 (clojure-ts-mode . "clojure") 830 (clojure-ts-clojurec-mode . "clojure") 831 (clojure-ts-clojurescript-mode . "clojurescript") 832 (java-mode . "java") 833 (java-ts-mode . "java") 834 (jdee-mode . "java") 835 (groovy-mode . "groovy") 836 (python-mode . "python") 837 (python-ts-mode . "python") 838 (cython-mode . "python") 839 ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo") 840 (lsp--render-markdown . "markdown") 841 (move-mode . "move") 842 (rust-mode . "rust") 843 (rust-ts-mode . "rust") 844 (rustic-mode . "rust") 845 (kotlin-mode . "kotlin") 846 (kotlin-ts-mode . "kotlin") 847 (css-mode . "css") 848 (css-ts-mode . "css") 849 (less-mode . "less") 850 (less-css-mode . "less") 851 (lua-mode . "lua") 852 (lua-ts-mode . "lua") 853 (sass-mode . "sass") 854 (ssass-mode . "sass") 855 (scss-mode . "scss") 856 (scad-mode . "openscad") 857 (xml-mode . "xml") 858 (c-mode . "c") 859 (c-ts-mode . "c") 860 (c++-mode . "cpp") 861 (c++-ts-mode . "cpp") 862 (cuda-mode . "cuda") 863 (objc-mode . "objective-c") 864 (html-mode . "html") 865 (html-ts-mode . "html") 866 (sgml-mode . "html") 867 (mhtml-mode . "html") 868 (mint-mode . "mint") 869 (go-dot-mod-mode . "go.mod") 870 (go-mod-ts-mode . "go.mod") 871 (go-mode . "go") 872 (go-ts-mode . "go") 873 (graphql-mode . "graphql") 874 (haskell-mode . "haskell") 875 (haskell-ts-mode . "haskell") 876 (hack-mode . "hack") 877 (php-mode . "php") 878 (php-ts-mode . "php") 879 (powershell-mode . "powershell") 880 (powershell-mode . "PowerShell") 881 (powershell-ts-mode . "powershell") 882 (json-mode . "json") 883 (json-ts-mode . "json") 884 (jsonc-mode . "jsonc") 885 (rjsx-mode . "javascript") 886 (js2-mode . "javascript") 887 (js-mode . "javascript") 888 (js-ts-mode . "javascript") 889 (typescript-mode . "typescript") 890 (typescript-ts-mode . "typescript") 891 (tsx-ts-mode . "typescriptreact") 892 (svelte-mode . "svelte") 893 (fsharp-mode . "fsharp") 894 (reason-mode . "reason") 895 (caml-mode . "ocaml") 896 (tuareg-mode . "ocaml") 897 (futhark-mode . "futhark") 898 (swift-mode . "swift") 899 (elixir-mode . "elixir") 900 (elixir-ts-mode . "elixir") 901 (heex-ts-mode . "elixir") 902 (conf-javaprop-mode . "spring-boot-properties") 903 (yaml-mode . "yaml") 904 (yaml-ts-mode . "yaml") 905 (ruby-mode . "ruby") 906 (enh-ruby-mode . "ruby") 907 (ruby-ts-mode . "ruby") 908 (feature-mode . "cucumber") 909 (fortran-mode . "fortran") 910 (f90-mode . "fortran") 911 (elm-mode . "elm") 912 (dart-mode . "dart") 913 (erlang-mode . "erlang") 914 (dockerfile-mode . "dockerfile") 915 (dockerfile-ts-mode . "dockerfile") 916 (csharp-mode . "csharp") 917 (csharp-tree-sitter-mode . "csharp") 918 (csharp-ts-mode . "csharp") 919 (plain-tex-mode . "plaintex") 920 (context-mode . "context") 921 (cypher-mode . "cypher") 922 (latex-mode . "latex") 923 (LaTeX-mode . "latex") 924 (v-mode . "v") 925 (vhdl-mode . "vhdl") 926 (vhdl-ts-mode . "vhdl") 927 (verilog-mode . "verilog") 928 (terraform-mode . "terraform") 929 (ess-julia-mode . "julia") 930 (ess-r-mode . "r") 931 (crystal-mode . "crystal") 932 (nim-mode . "nim") 933 (dhall-mode . "dhall") 934 (cmake-mode . "cmake") 935 (cmake-ts-mode . "cmake") 936 (purescript-mode . "purescript") 937 (gdscript-mode . "gdscript") 938 (gdscript-ts-mode . "gdscript") 939 (perl-mode . "perl") 940 (cperl-mode . "perl") 941 (robot-mode . "robot") 942 (racket-mode . "racket") 943 (nix-mode . "nix") 944 (nix-ts-mode . "nix") 945 (prolog-mode . "prolog") 946 (vala-mode . "vala") 947 (actionscript-mode . "actionscript") 948 (d-mode . "d") 949 (zig-mode . "zig") 950 (text-mode . "plaintext") 951 (markdown-mode . "markdown") 952 (gfm-mode . "markdown") 953 (beancount-mode . "beancount") 954 (conf-toml-mode . "toml") 955 (toml-ts-mode . "toml") 956 (org-mode . "org") 957 (org-journal-mode . "org") 958 (nginx-mode . "nginx") 959 (magik-mode . "magik") 960 (magik-ts-mode . "magik") 961 (idris-mode . "idris") 962 (idris2-mode . "idris2") 963 (gleam-mode . "gleam") 964 (gleam-ts-mode . "gleam") 965 (graphviz-dot-mode . "dot") 966 (tiltfile-mode . "tiltfile") 967 (solidity-mode . "solidity") 968 (bibtex-mode . "bibtex") 969 (rst-mode . "restructuredtext") 970 (glsl-mode . "glsl") 971 (shader-mode . "shaderlab") 972 (wgsl-mode . "wgsl") 973 (jq-mode . "jq") 974 (jq-ts-mode . "jq") 975 (protobuf-mode . "protobuf") 976 (nushell-mode . "nushell") 977 (nushell-ts-mode . "nushell") 978 (meson-mode . "meson") 979 (yang-mode . "yang")) 980 "Language id configuration.") 981 982 (defvar lsp--last-active-workspaces nil 983 "Keep track of last active workspace. 984 We want to try the last workspace first when jumping into a library 985 directory") 986 987 (defvar lsp-method-requirements 988 '(("textDocument/callHierarchy" :capability :callHierarchyProvider) 989 ("textDocument/codeAction" :capability :codeActionProvider) 990 ("codeAction/resolve" 991 :check-command (lambda (workspace) 992 (with-lsp-workspace workspace 993 (lsp:code-action-options-resolve-provider? 994 (lsp--capability-for-method "textDocument/codeAction"))))) 995 ("textDocument/codeLens" :capability :codeLensProvider) 996 ("textDocument/completion" :capability :completionProvider) 997 ("completionItem/resolve" 998 :check-command (lambda (wk) 999 (with-lsp-workspace wk 1000 (lsp:completion-options-resolve-provider? 1001 (lsp--capability-for-method "textDocument/completion"))))) 1002 ("textDocument/declaration" :capability :declarationProvider) 1003 ("textDocument/definition" :capability :definitionProvider) 1004 ("textDocument/documentColor" :capability :colorProvider) 1005 ("textDocument/documentLink" :capability :documentLinkProvider) 1006 ("textDocument/inlayHint" :capability :inlayHintProvider) 1007 ("textDocument/documentHighlight" :capability :documentHighlightProvider) 1008 ("textDocument/documentSymbol" :capability :documentSymbolProvider) 1009 ("textDocument/foldingRange" :capability :foldingRangeProvider) 1010 ("textDocument/formatting" :capability :documentFormattingProvider) 1011 ("textDocument/hover" :capability :hoverProvider) 1012 ("textDocument/implementation" :capability :implementationProvider) 1013 ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider) 1014 ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider) 1015 ("textDocument/prepareRename" 1016 :check-command (lambda (workspace) 1017 (with-lsp-workspace workspace 1018 (lsp:rename-options-prepare-provider? 1019 (lsp--capability-for-method "textDocument/rename"))))) 1020 ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider) 1021 ("textDocument/references" :capability :referencesProvider) 1022 ("textDocument/rename" :capability :renameProvider) 1023 ("textDocument/selectionRange" :capability :selectionRangeProvider) 1024 ("textDocument/semanticTokens" :capability :semanticTokensProvider) 1025 ("textDocument/semanticTokensFull" 1026 :check-command (lambda (workspace) 1027 (with-lsp-workspace workspace 1028 (lsp-get (lsp--capability :semanticTokensProvider) :full)))) 1029 ("textDocument/semanticTokensFull/Delta" 1030 :check-command (lambda (workspace) 1031 (with-lsp-workspace workspace 1032 (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full))) 1033 (and (not (booleanp capFull)) (lsp-get capFull :delta)))))) 1034 ("textDocument/semanticTokensRangeProvider" 1035 :check-command (lambda (workspace) 1036 (with-lsp-workspace workspace 1037 (lsp-get (lsp--capability :semanticTokensProvider) :range)))) 1038 ("textDocument/signatureHelp" :capability :signatureHelpProvider) 1039 ("textDocument/typeDefinition" :capability :typeDefinitionProvider) 1040 ("textDocument/typeHierarchy" :capability :typeHierarchyProvider) 1041 ("textDocument/diagnostic" :capability :diagnosticProvider) 1042 ("workspace/executeCommand" :capability :executeCommandProvider) 1043 ("workspace/symbol" :capability :workspaceSymbolProvider)) 1044 1045 "Map methods to requirements. 1046 It is used by request-sending functions to determine which server 1047 must be used for handling a particular message.") 1048 1049 (defconst lsp--file-change-type 1050 `((created . 1) 1051 (changed . 2) 1052 (deleted . 3))) 1053 1054 (defconst lsp--watch-kind 1055 `((create . 1) 1056 (change . 2) 1057 (delete . 4))) 1058 1059 (defvar lsp-window-body-width 40 1060 "Window body width when rendering doc.") 1061 1062 (defface lsp-face-highlight-textual 1063 '((t :inherit highlight)) 1064 "Face used for textual occurrences of symbols." 1065 :group 'lsp-mode) 1066 1067 (defface lsp-face-highlight-read 1068 '((t :inherit highlight :underline t)) 1069 "Face used for highlighting symbols being read." 1070 :group 'lsp-mode) 1071 1072 (defface lsp-face-highlight-write 1073 '((t :inherit highlight :weight bold)) 1074 "Face used for highlighting symbols being written to." 1075 :group 'lsp-mode) 1076 1077 (define-obsolete-variable-alias 'lsp-lens-auto-enable 1078 'lsp-lens-enable "lsp-mode 7.0.1") 1079 1080 (defcustom lsp-lens-enable t 1081 "Auto enable lenses if server supports." 1082 :group 'lsp-lens 1083 :type 'boolean 1084 :package-version '(lsp-mode . "6.3")) 1085 1086 (defcustom lsp-symbol-highlighting-skip-current nil 1087 "If non-nil skip current symbol when setting symbol highlights." 1088 :group 'lsp-mode 1089 :type 'boolean) 1090 1091 (defcustom lsp-file-watch-threshold 1000 1092 "Show warning if the files to watch are more than. 1093 Set to nil to disable the warning." 1094 :type 'number 1095 :group 'lsp-mode) 1096 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i)))) 1097 1098 (defvar lsp-custom-markup-modes 1099 '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic")) 1100 "Mode to uses with markdown code blocks. 1101 They are added to `markdown-code-lang-modes'") 1102 1103 (defcustom lsp-signature-render-documentation t 1104 "Display signature documentation in `eldoc'." 1105 :type 'boolean 1106 :group 'lsp-mode 1107 :package-version '(lsp-mode . "6.2")) 1108 1109 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request) 1110 "Auto activate signature conditions." 1111 :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char) 1112 (const :tag "After selected completion." :after-completion) 1113 (const :tag "When the server has sent show signature help." :on-server-request))) 1114 :group 'lsp-mode 1115 :package-version '(lsp-mode . "6.2")) 1116 1117 (defcustom lsp-signature-doc-lines 20 1118 "If number, limit the number of lines to show in the docs." 1119 :type 'number 1120 :group 'lsp-mode 1121 :package-version '(lsp-mode . "6.3")) 1122 1123 (defcustom lsp-signature-function 'lsp-lv-message 1124 "The function used for displaying signature info. 1125 It will be called with one param - the signature info. When 1126 called with nil the signature info must be cleared." 1127 :type 'function 1128 :group 'lsp-mode 1129 :package-version '(lsp-mode . "6.3")) 1130 1131 (defcustom lsp-keymap-prefix "s-l" 1132 "LSP-mode keymap prefix." 1133 :group 'lsp-mode 1134 :type 'string 1135 :package-version '(lsp-mode . "6.3")) 1136 1137 (defvar-local lsp--buffer-workspaces () 1138 "List of the buffer workspaces.") 1139 1140 (defvar-local lsp--buffer-deferred nil 1141 "Whether buffer was loaded via `lsp-deferred'.") 1142 1143 (defvar lsp--session nil 1144 "Contain the `lsp-session' for the current Emacs instance.") 1145 1146 (defvar lsp--tcp-port 10000) 1147 1148 (defvar lsp--client-packages-required nil 1149 "If nil, `lsp-client-packages' are yet to be required.") 1150 1151 (defvar lsp--tcp-server-port 0 1152 "The server socket which is opened when using `lsp-tcp-server' (a server 1153 socket is opened in Emacs and the language server connects to it). The 1154 default value of 0 ensures that a random high port is used. Set it to a positive 1155 integer to use a specific port.") 1156 1157 (defvar lsp--tcp-server-wait-seconds 10 1158 "Wait this amount of time for the client to connect to our server socket 1159 when using `lsp-tcp-server'.") 1160 1161 (defvar-local lsp--document-symbols nil 1162 "The latest document symbols.") 1163 1164 (defvar-local lsp--document-selection-range-cache nil 1165 "The document selection cache.") 1166 1167 (defvar-local lsp--document-symbols-request-async nil 1168 "If non-nil, request document symbols asynchronously.") 1169 1170 (defvar-local lsp--document-symbols-tick -1 1171 "The value of `buffer-chars-modified-tick' when document 1172 symbols were last retrieved.") 1173 1174 (defvar-local lsp--have-document-highlights nil 1175 "Set to `t' on symbol highlighting, cleared on 1176 `lsp--cleanup-highlights-if-needed'. Checking a separately 1177 defined flag is substantially faster than unconditionally 1178 calling `remove-overlays'.") 1179 1180 ;; Buffer local variable for storing number of lines. 1181 (defvar lsp--log-lines) 1182 1183 (defvar-local lsp--eldoc-saved-message nil) 1184 1185 (defvar lsp--on-change-timer nil) 1186 (defvar lsp--on-idle-timer nil) 1187 1188 (defvar-local lsp--signature-last nil) 1189 (defvar-local lsp--signature-last-index nil) 1190 (defvar lsp--signature-last-buffer nil) 1191 1192 (defvar-local lsp--virtual-buffer-point-max nil) 1193 1194 (cl-defmethod lsp-execute-command (_server _command _arguments) 1195 "Ask SERVER to execute COMMAND with ARGUMENTS.") 1196 1197 (defun lsp-elt (sequence n) 1198 "Return Nth element of SEQUENCE or nil if N is out of range." 1199 (cond 1200 ((listp sequence) (elt sequence n)) 1201 ((arrayp sequence) 1202 (and (> (length sequence) n) (aref sequence n))) 1203 (t (and (> (length sequence) n) (elt sequence n))))) 1204 1205 ;; define seq-first and seq-rest for older emacs 1206 (defun lsp-seq-first (sequence) 1207 "Return the first element of SEQUENCE." 1208 (lsp-elt sequence 0)) 1209 1210 (defun lsp-seq-rest (sequence) 1211 "Return a sequence of the elements of SEQUENCE except the first one." 1212 (seq-drop sequence 1)) 1213 1214 ;;;###autoload 1215 (defun lsp--string-listp (sequence) 1216 "Return t if all elements of SEQUENCE are strings, else nil." 1217 (not (seq-find (lambda (x) (not (stringp x))) sequence))) 1218 1219 (defun lsp--string-vector-p (candidate) 1220 "Returns true if CANDIDATE is a vector data structure and 1221 every element of it is of type string, else nil." 1222 (and 1223 (vectorp candidate) 1224 (seq-every-p #'stringp candidate))) 1225 1226 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0") 1227 1228 (defun lsp--editable-vector-match (widget value) 1229 "Function for `lsp-editable-vector' :match." 1230 ;; Value must be a list or a vector and all the members must match the type. 1231 (and (or (listp value) (vectorp value)) 1232 (length (cdr (lsp--editable-vector-match-inline widget value))))) 1233 1234 (defun lsp--editable-vector-match-inline (widget value) 1235 "Value for `lsp-editable-vector' :match-inline." 1236 (let ((type (nth 0 (widget-get widget :args))) 1237 (ok t) 1238 found) 1239 (while (and value ok) 1240 (let ((answer (widget-match-inline type value))) 1241 (if answer 1242 (let ((head (if (vectorp answer) (aref answer 0) (car answer))) 1243 (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer)))) 1244 (setq found (append found head) 1245 value tail)) 1246 (setq ok nil)))) 1247 (cons found value))) 1248 1249 (defun lsp--editable-vector-value-to-external (_widget internal-value) 1250 "Convert the internal list value to a vector." 1251 (if (listp internal-value) 1252 (apply 'vector internal-value) 1253 internal-value)) 1254 1255 (defun lsp--editable-vector-value-to-internal (_widget external-value) 1256 "Convert the external vector value to a list." 1257 (if (vectorp external-value) 1258 (append external-value nil) 1259 external-value)) 1260 1261 (define-widget 'lsp--editable-vector 'editable-list 1262 "A subclass of `editable-list' that accepts and returns a 1263 vector instead of a list." 1264 :value-to-external 'lsp--editable-vector-value-to-external 1265 :value-to-internal 'lsp--editable-vector-value-to-internal 1266 :match 'lsp--editable-vector-match 1267 :match-inline 'lsp--editable-vector-match-inline) 1268 1269 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector 1270 "A variable length homogeneous vector." 1271 :tag "Repeat" 1272 :format "%{%t%}:\n%v%i\n") 1273 1274 (define-widget 'lsp-string-vector 'lazy 1275 "A vector of zero or more elements, every element of which is a string. 1276 Appropriate for any language-specific `defcustom' that needs to 1277 serialize as a JSON array of strings. 1278 1279 Deprecated. Use `lsp-repeatable-vector' instead. " 1280 :offset 4 1281 :tag "Vector" 1282 :type '(lsp-repeatable-vector string)) 1283 1284 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0") 1285 1286 (defvar lsp--show-message t 1287 "If non-nil, show debug message from `lsp-mode'.") 1288 1289 (defun lsp--message (format &rest args) 1290 "Wrapper for `message' 1291 1292 We `inhibit-message' the message when the cursor is in the 1293 minibuffer and when emacs version is before emacs 27 due to the 1294 fact that we often use `lsp--info', `lsp--warn' and `lsp--error' 1295 in async context and the call to these function is removing the 1296 minibuffer prompt. The issue with async messages is already fixed 1297 in emacs 27. 1298 1299 See #2049" 1300 (when lsp--show-message 1301 (let ((inhibit-message (or inhibit-message 1302 (and (minibufferp) 1303 (version< emacs-version "27.0"))))) 1304 (apply #'message format args)))) 1305 1306 (defun lsp--info (format &rest args) 1307 "Display lsp info message with FORMAT with ARGS." 1308 (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args))) 1309 1310 (defun lsp--warn (format &rest args) 1311 "Display lsp warn message with FORMAT with ARGS." 1312 (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args))) 1313 1314 (defun lsp--error (format &rest args) 1315 "Display lsp error message with FORMAT with ARGS." 1316 (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args))) 1317 1318 (defun lsp-log (format &rest args) 1319 "Log message to the ’*lsp-log*’ buffer. 1320 1321 FORMAT and ARGS i the same as for `message'." 1322 (when lsp-log-max 1323 (let ((log-buffer (get-buffer "*lsp-log*")) 1324 (inhibit-read-only t)) 1325 (unless log-buffer 1326 (setq log-buffer (get-buffer-create "*lsp-log*")) 1327 (with-current-buffer log-buffer 1328 (buffer-disable-undo) 1329 (view-mode 1) 1330 (set (make-local-variable 'lsp--log-lines) 0))) 1331 (with-current-buffer log-buffer 1332 (save-excursion 1333 (let* ((message (apply 'format format args)) 1334 ;; Count newlines in message. 1335 (newlines (1+ (cl-loop with start = 0 1336 for count from 0 1337 while (string-match "\n" message start) 1338 do (setq start (match-end 0)) 1339 finally return count)))) 1340 (goto-char (point-max)) 1341 1342 ;; in case the buffer is not empty insert before last \n to preserve 1343 ;; the point position(in case it is in the end) 1344 (if (eq (point) (point-min)) 1345 (progn 1346 (insert "\n") 1347 (backward-char)) 1348 (backward-char) 1349 (insert "\n")) 1350 (insert message) 1351 1352 (setq lsp--log-lines (+ lsp--log-lines newlines)) 1353 1354 (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max)) 1355 (let ((to-delete (- lsp--log-lines lsp-log-max))) 1356 (goto-char (point-min)) 1357 (forward-line to-delete) 1358 (delete-region (point-min) (point)) 1359 (setq lsp--log-lines lsp-log-max))))))))) 1360 1361 (defalias 'lsp-message 'lsp-log) 1362 1363 (defalias 'lsp-ht 'ht) 1364 1365 (defalias 'lsp-file-local-name 'file-local-name) 1366 1367 (defun lsp-f-canonical (file-name) 1368 "Return the canonical FILE-NAME, without a trailing slash." 1369 (directory-file-name (expand-file-name file-name))) 1370 1371 (defalias 'lsp-canonical-file-name 'lsp-f-canonical) 1372 1373 (defun lsp-f-same? (path-a path-b) 1374 "Return t if PATH-A and PATH-B are references to the same file. 1375 Symlinks are not followed." 1376 (when (and (f-exists? path-a) 1377 (f-exists? path-b)) 1378 (equal 1379 (lsp-f-canonical (directory-file-name (f-expand path-a))) 1380 (lsp-f-canonical (directory-file-name (f-expand path-b)))))) 1381 1382 (defun lsp-f-parent (path) 1383 "Return the parent directory to PATH. 1384 Symlinks are not followed." 1385 (let ((parent (file-name-directory 1386 (directory-file-name (f-expand path default-directory))))) 1387 (unless (lsp-f-same? path parent) 1388 (if (f-relative? path) 1389 (f-relative parent) 1390 (directory-file-name parent))))) 1391 1392 (defun lsp-f-ancestor-of? (path-a path-b) 1393 "Return t if PATH-A is an ancestor of PATH-B. 1394 Symlinks are not followed." 1395 (unless (lsp-f-same? path-a path-b) 1396 (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator)) 1397 (lsp-f-canonical path-b)))) 1398 1399 (defun lsp--merge-results (results method) 1400 "Merge RESULTS by filtering the empty hash-tables and merging 1401 the lists according to METHOD." 1402 (pcase (--map (if (vectorp it) 1403 (append it nil) it) 1404 (-filter #'identity results)) 1405 (`() ()) 1406 ;; only one result - simply return it 1407 (`(,fst) fst) 1408 ;; multiple results merge it based on strategy 1409 (results 1410 (pcase method 1411 ("textDocument/hover" (pcase (seq-filter 1412 (-compose #'not #'lsp-empty?) 1413 results) 1414 (`(,hover) hover) 1415 (hovers (lsp-make-hover 1416 :contents 1417 (-mapcat 1418 (-lambda ((&Hover :contents)) 1419 (if (and (sequencep contents) 1420 (not (stringp contents))) 1421 (append contents ()) 1422 (list contents))) 1423 hovers))))) 1424 ("textDocument/completion" 1425 (lsp-make-completion-list 1426 :is-incomplete (seq-some 1427 #'lsp:completion-list-is-incomplete 1428 results) 1429 :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it) 1430 (lsp:completion-list-items it) 1431 it) 1432 nil)) 1433 results))) 1434 ("completionItem/resolve" 1435 (let ((item (cl-first results))) 1436 (when-let ((details (seq-filter #'identity 1437 (seq-map #'lsp:completion-item-detail? results)))) 1438 (lsp:set-completion-item-detail? 1439 item 1440 (string-join details " "))) 1441 (when-let ((docs (seq-filter #'identity 1442 (seq-map #'lsp:completion-item-documentation? results)))) 1443 (lsp:set-completion-item-documentation? 1444 item 1445 (lsp-make-markup-content 1446 :kind (or (seq-some (lambda (it) 1447 (when (equal (lsp:markup-content-kind it) 1448 lsp/markup-kind-markdown) 1449 lsp/markup-kind-markdown)) 1450 docs) 1451 lsp/markup-kind-plain-text) 1452 :value (string-join (seq-map (lambda (doc) 1453 (or (lsp:markup-content-value doc) 1454 (and (stringp doc) doc))) 1455 docs) 1456 "\n")))) 1457 (when-let ((edits (seq-filter #'identity 1458 (seq-map #'lsp:completion-item-additional-text-edits? results)))) 1459 (lsp:set-completion-item-additional-text-edits? 1460 item 1461 (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits))) 1462 item)) 1463 (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results)))))) 1464 1465 (defun lsp--spinner-start () 1466 "Start spinner indication." 1467 (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error))) 1468 1469 (defun lsp--propertize (str type) 1470 "Propertize STR as per TYPE." 1471 (propertize str 'face (alist-get type lsp--message-type-face))) 1472 1473 (defun lsp-workspaces () 1474 "Return the lsp workspaces associated with the current project." 1475 (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces)) 1476 1477 (defun lsp--completing-read (prompt collection transform-fn &optional predicate 1478 require-match initial-input 1479 hist def inherit-input-method) 1480 "Wrap `completing-read' to provide transformation function and disable sort. 1481 1482 TRANSFORM-FN will be used to transform each of the items before displaying. 1483 1484 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF 1485 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes." 1486 (let* ((col (--map (cons (funcall transform-fn it) it) collection)) 1487 (completion (completing-read prompt 1488 (lambda (string pred action) 1489 (if (eq action 'metadata) 1490 `(metadata (display-sort-function . identity)) 1491 (complete-with-action action col string pred))) 1492 predicate require-match initial-input hist 1493 def inherit-input-method))) 1494 (cdr (assoc completion col)))) 1495 1496 (defconst lsp--system-arch (lambda () 1497 (setq lsp--system-arch 1498 (pcase system-type 1499 ('windows-nt 1500 (pcase system-configuration 1501 ((rx bol "x86_64-") 'x64) 1502 (_ 'x86))) 1503 ('darwin 1504 (pcase system-configuration 1505 ((rx "aarch64-") 'arm64) 1506 (_ 'x64))) 1507 ('gnu/linux 1508 (pcase system-configuration 1509 ((rx bol "aarch64-") 'arm64) 1510 ((rx bol "x86_64") 'x64) 1511 ((rx bol (| "i386" "i886")) 'x32))) 1512 (_ 1513 (pcase system-configuration 1514 ((rx bol "x86_64") 'x64) 1515 ((rx bol (| "i386" "i886")) 'x32)))))) 1516 "Return the system architecture of `Emacs'. 1517 Special values: 1518 `x64' 64bit 1519 `x32' 32bit 1520 `arm64' ARM 64bit") 1521 1522 (defmacro lsp-with-current-buffer (buffer-id &rest body) 1523 (declare (indent 1) (debug t)) 1524 `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer))) 1525 (with-lsp-workspaces (plist-get ,buffer-id :workspaces) 1526 (funcall wcb (lambda () ,@body))) 1527 (with-current-buffer ,buffer-id 1528 ,@body))) 1529 1530 (defvar lsp--throw-on-input nil 1531 "Make `lsp-*-while-no-input' throws `input' on interrupted.") 1532 1533 (defmacro lsp--catch (tag bodyform &rest handlers) 1534 "Catch TAG thrown in BODYFORM. 1535 The return value from TAG will be handled in HANDLERS by `pcase'." 1536 (declare (debug (form form &rest (pcase-PAT body))) (indent 2)) 1537 (let ((re-sym (make-symbol "re"))) 1538 `(let ((,re-sym (catch ,tag ,bodyform))) 1539 (pcase ,re-sym 1540 ,@handlers)))) 1541 1542 (defmacro lsp--while-no-input (&rest body) 1543 "Wrap BODY in `while-no-input' and respecting `non-essential'. 1544 If `lsp--throw-on-input' is set, will throw if input is pending, else 1545 return value of `body' or nil if interrupted." 1546 (declare (debug t) (indent 0)) 1547 `(if non-essential 1548 (let ((res (while-no-input ,@body))) 1549 (cond 1550 ((and lsp--throw-on-input (equal res t)) 1551 (throw 'input :interrupted)) 1552 ((booleanp res) nil) 1553 (t res))) 1554 ,@body)) 1555 1556 ;; A ‘lsp--client’ object describes the client-side behavior of a language 1557 ;; server. It is used to start individual server processes, each of which is 1558 ;; represented by a ‘lsp--workspace’ object. Client objects are normally 1559 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’. Each 1560 ;; workspace refers to exactly one client, but there can be multiple workspaces 1561 ;; for a single client. 1562 (cl-defstruct lsp--client 1563 ;; ‘language-id’ is a function that receives a buffer as a single argument 1564 ;; and should return the language identifier for that buffer. See 1565 ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem 1566 ;; for a list of language identifiers. Also consult the documentation for 1567 ;; the language server represented by this client to find out what language 1568 ;; identifiers it supports or expects. 1569 (language-id nil) 1570 1571 ;; ‘add-on?’ when set to t the server will be started no matter whether there 1572 ;; is another server handling the same mode. 1573 (add-on? nil) 1574 ;; ‘new-connection’ is a function that should start a language server process 1575 ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS). 1576 ;; COMMAND-PROCESS must be a process object representing the server process 1577 ;; just started. COMMUNICATION-PROCESS must be a process (including pipe and 1578 ;; network processes) that ‘lsp-mode’ uses to communicate with the language 1579 ;; server using the language server protocol. COMMAND-PROCESS and 1580 ;; COMMUNICATION-PROCESS may be the same process; in that case 1581 ;; ‘new-connection’ may also return that process as a single 1582 ;; object. ‘new-connection’ is called with two arguments, FILTER and 1583 ;; SENTINEL. FILTER should be used as process filter for 1584 ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for 1585 ;; COMMAND-PROCESS. 1586 (new-connection nil) 1587 1588 ;; ‘ignore-regexps’ is a list of regexps. When a data packet from the 1589 ;; language server matches any of these regexps, it will be ignored. This is 1590 ;; intended for dealing with language servers that output non-protocol data. 1591 (ignore-regexps nil) 1592 1593 ;; ‘ignore-messages’ is a list of regexps. When a message from the language 1594 ;; server matches any of these regexps, it will be ignored. This is useful 1595 ;; for filtering out unwanted messages; such as servers that send nonstandard 1596 ;; message types, or extraneous log messages. 1597 (ignore-messages nil) 1598 1599 ;; ‘notification-handlers’ is a hash table mapping notification method names 1600 ;; (strings) to functions handling the respective notifications. Upon 1601 ;; receiving a notification, ‘lsp-mode’ will call the associated handler 1602 ;; function passing two arguments, the ‘lsp--workspace’ object and the 1603 ;; deserialized notification parameters. 1604 (notification-handlers (make-hash-table :test 'equal)) 1605 1606 ;; ‘request-handlers’ is a hash table mapping request method names 1607 ;; (strings) to functions handling the respective notifications. Upon 1608 ;; receiving a request, ‘lsp-mode’ will call the associated handler function 1609 ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized 1610 ;; request parameters. 1611 (request-handlers (make-hash-table :test 'equal)) 1612 1613 ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request 1614 ;; identifiers for pending asynchronous requests to functions handling the 1615 ;; respective responses. Upon receiving a response from the language server, 1616 ;; ‘lsp-mode’ will call the associated response handler function with a 1617 ;; single argument, the deserialized response parameters. 1618 (response-handlers (make-hash-table :test 'eql)) 1619 1620 ;; ‘prefix-function’ is called for getting the prefix for completion. 1621 ;; The function takes no parameter and returns a cons (start . end) representing 1622 ;; the start and end bounds of the prefix. If it's not set, the client uses a 1623 ;; default prefix function." 1624 (prefix-function nil) 1625 1626 ;; Contains mapping of scheme to the function that is going to be used to load 1627 ;; the file. 1628 (uri-handlers (make-hash-table :test #'equal)) 1629 1630 ;; ‘action-handlers’ is a hash table mapping action to a handler function. It 1631 ;; can be used in `lsp-execute-code-action' to determine whether the action 1632 ;; current client is interested in executing the action instead of sending it 1633 ;; to the server. 1634 (action-handlers (make-hash-table :test 'equal)) 1635 1636 ;; `action-filter' can be set to a function that modifies any incoming 1637 ;; `CodeAction' in place before it is executed. The return value is ignored. 1638 ;; This can be used to patch up broken code action requests before they are 1639 ;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an 1640 ;; example of a function that can be useful here. 1641 (action-filter nil) 1642 1643 ;; major modes supported by the client. 1644 major-modes 1645 ;; Function that will be called to decide if this language client 1646 ;; should manage a particular buffer. The function will be passed 1647 ;; the file name and major mode to inform the decision. Setting 1648 ;; `activation-fn' will override `major-modes', if 1649 ;; present. 1650 activation-fn 1651 ;; Break the tie when major-mode is supported by multiple clients. 1652 (priority 0) 1653 ;; Unique identifier for representing the client object. 1654 server-id 1655 ;; defines whether the client supports multi root workspaces. 1656 multi-root 1657 ;; Initialization options or a function that returns initialization options. 1658 initialization-options 1659 ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or 1660 ;; completely replace, the faces used for semantic highlighting on a 1661 ;; client-by-client basis. 1662 ;; 1663 ;; It recognizes four members, all of which are optional: `:types’ and 1664 ;; `:modifiers’, respectively, should be face definition lists akin to 1665 ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be 1666 ;; merged with the default face definition list. 1667 ;; 1668 ;; Alternatively, if the plist members `:discard-default-types’ or 1669 ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers' 1670 ;; face definitions will be replaced entirely by their respective overrides. 1671 ;; 1672 ;; For example, setting `:semantic-tokens-faces-overrides' to 1673 ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from 1674 ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'. 1675 ;; 1676 ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))' 1677 ;; will also remap "macro", but on top of that associate the fictional token type 1678 ;; "not-quite-a-macro" with the face named `some-face'. 1679 ;; 1680 ;; `(:types (("macro" . font-lock-keyword-face)) 1681 ;; :modifiers (("declaration" . lsp-face-semhl-interface)) 1682 ;; :discard-default-types t 1683 ;; :discard-default-modifiers t)' 1684 ;; will discard all default face definitions, hence leaving the client with 1685 ;; only one token type "macro", mapped to `font-lock-keyword-face', and one 1686 ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'. 1687 semantic-tokens-faces-overrides 1688 ;; Provides support for registering LSP Server specific capabilities. 1689 custom-capabilities 1690 ;; Function which returns the folders that are considered to be not projects but library files. 1691 ;; The function accepts one parameter currently active workspace. 1692 ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225. 1693 library-folders-fn 1694 ;; function which will be called when opening file in the workspace to perform 1695 ;; client specific initialization. The function accepts one parameter 1696 ;; currently active workspace. 1697 before-file-open-fn 1698 ;; Function which will be called right after a workspace has been initialized. 1699 initialized-fn 1700 ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP. 1701 (remote? nil) 1702 1703 ;; ‘completion-in-comments?’ t if the client supports completion in comments. 1704 (completion-in-comments? nil) 1705 1706 ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client. 1707 (path->uri-fn nil) 1708 1709 ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client. 1710 (uri->path-fn nil) 1711 ;; Function that returns an environment structure that will be used 1712 ;; to set some environment variables when starting the language 1713 ;; server process. These environment variables enable some 1714 ;; additional features in the language server. The environment 1715 ;; structure is an alist of the form (KEY . VALUE), where KEY is a 1716 ;; string (regularly in all caps), and VALUE may be a string, a 1717 ;; boolean, or a sequence of strings. 1718 environment-fn 1719 1720 ;; ‘after-open-fn’ workspace after open specific hooks. 1721 (after-open-fn nil) 1722 1723 ;; ‘async-request-handlers’ is a hash table mapping request method names 1724 ;; (strings) to functions handling the respective requests that may take 1725 ;; time to finish. Upon receiving a request, ‘lsp-mode’ will call the 1726 ;; associated handler function passing three arguments, the ‘lsp--workspace’ 1727 ;; object, the deserialized request parameters and the callback which accept 1728 ;; result as its parameter. 1729 (async-request-handlers (make-hash-table :test 'equal)) 1730 download-server-fn 1731 download-in-progress? 1732 buffers 1733 synchronize-sections) 1734 1735 (defun lsp-clients-executable-find (find-command &rest args) 1736 "Finds an executable by invoking a search command. 1737 1738 FIND-COMMAND is the executable finder that searches for the 1739 actual language server executable. ARGS is a list of arguments to 1740 give to FIND-COMMAND to find the language server. Returns the 1741 output of FIND-COMMAND if it exits successfully, nil otherwise. 1742 1743 Typical uses include finding an executable by invoking `find' in 1744 a project, finding LLVM commands on macOS with `xcrun', or 1745 looking up project-specific language servers for projects written 1746 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv' 1747 etc." 1748 (when-let* ((find-command-path (executable-find find-command)) 1749 (executable-path 1750 (with-temp-buffer 1751 (when (zerop (apply 'call-process find-command-path nil t nil args)) 1752 (buffer-substring-no-properties (point-min) (point-max)))))) 1753 (string-trim executable-path))) 1754 1755 (defvar lsp--already-widened nil) 1756 1757 (defmacro lsp-save-restriction-and-excursion (&rest form) 1758 (declare (indent 0) (debug t)) 1759 `(if lsp--already-widened 1760 (save-excursion ,@form) 1761 (-let [lsp--already-widened t] 1762 (save-restriction 1763 (widen) 1764 (save-excursion ,@form))))) 1765 1766 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number 1767 (defun lsp--line-character-to-point (line character) 1768 "Return the point for character CHARACTER on line LINE." 1769 (or (lsp-virtual-buffer-call :line/character->point line character) 1770 (let ((inhibit-field-text-motion t)) 1771 (lsp-save-restriction-and-excursion 1772 (goto-char (point-min)) 1773 (forward-line line) 1774 ;; server may send character position beyond the current line and we 1775 ;; should fallback to line end. 1776 (-let [line-end (line-end-position)] 1777 (if (> character (- line-end (point))) 1778 line-end 1779 (forward-char character) 1780 (point))))))) 1781 1782 (lsp-defun lsp--position-to-point ((&Position :line :character)) 1783 "Convert `Position' object in PARAMS to a point." 1784 (lsp--line-character-to-point line character)) 1785 1786 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end)) 1787 (cons start end)) 1788 1789 (lsp-defun lsp--range-text ((&RangeToPoint :start :end)) 1790 (buffer-substring start end)) 1791 1792 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end))) 1793 (cond 1794 ((and 1795 (region-active-p) 1796 (<= start (region-beginning) end) 1797 (<= start (region-end) end) 1798 (or (not (= start (region-beginning))) 1799 (not (= end (region-end))))) 1800 (cons start end)) 1801 ((and (<= start (point) end) 1802 (not (region-active-p))) 1803 (cons start end)) 1804 (parent? (lsp--find-wrapping-range parent?)))) 1805 1806 (defun lsp--get-selection-range () 1807 (or 1808 (-when-let ((cache . cache-tick) lsp--document-selection-range-cache) 1809 (when (= cache-tick (buffer-modified-tick)) cache)) 1810 (let ((response (cl-first 1811 (lsp-request 1812 "textDocument/selectionRange" 1813 (list :textDocument (lsp--text-document-identifier) 1814 :positions (vector (lsp--cur-position))))))) 1815 (setq lsp--document-selection-range-cache 1816 (cons response (buffer-modified-tick))) 1817 response))) 1818 1819 (defun lsp-extend-selection () 1820 "Extend selection." 1821 (interactive) 1822 (unless (lsp-feature? "textDocument/selectionRange") 1823 (signal 'lsp-capability-not-supported (list "selectionRangeProvider"))) 1824 (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range))) 1825 (goto-char start) 1826 (set-mark (point)) 1827 (goto-char end) 1828 (exchange-point-and-mark))) 1829 1830 (defun lsp-warn (message &rest args) 1831 "Display a warning message made from (`format-message' MESSAGE ARGS...). 1832 This is equivalent to `display-warning', using `lsp-mode' as the type and 1833 `:warning' as the level." 1834 (display-warning 'lsp-mode (apply #'format-message message args))) 1835 1836 (defun lsp--get-uri-handler (scheme) 1837 "Get uri handler for SCHEME in the current workspace." 1838 (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it))) 1839 (or (lsp-workspaces) (lsp--session-workspaces (lsp-session))))) 1840 1841 (defun lsp--fix-path-casing (path) 1842 "On windows, downcases path because the windows file system is 1843 case-insensitive. 1844 1845 On other systems, returns path without change." 1846 (if (eq system-type 'windows-nt) (downcase path) path)) 1847 1848 (defun lsp--uri-to-path (uri) 1849 "Convert URI to a file path." 1850 (if-let ((fn (->> (lsp-workspaces) 1851 (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client)) 1852 (cl-first)))) 1853 (funcall fn uri) 1854 (lsp--uri-to-path-1 uri))) 1855 1856 (defun lsp-remap-path-if-needed (file-name) 1857 (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings)) 1858 (propertize (buffer-local-value 'buffer-file-name buffer) 1859 'lsp-virtual-buffer virtual-buffer) 1860 file-name)) 1861 1862 (defun lsp--uri-to-path-1 (uri) 1863 "Convert URI to a file path." 1864 (let* ((url (url-generic-parse-url (url-unhex-string uri))) 1865 (type (url-type url)) 1866 (target (url-target url)) 1867 (file 1868 (concat (decode-coding-string (url-filename url) 1869 (or locale-coding-system 'utf-8)) 1870 (when (and target 1871 (not (s-match 1872 (rx "#" (group (1+ num)) (or "," "#") 1873 (group (1+ num)) 1874 string-end) 1875 uri))) 1876 (concat "#" target)))) 1877 (file-name (if (and type (not (string= type "file"))) 1878 (if-let ((handler (lsp--get-uri-handler type))) 1879 (funcall handler uri) 1880 uri) 1881 ;; `url-generic-parse-url' is buggy on windows: 1882 ;; https://github.com/emacs-lsp/lsp-mode/pull/265 1883 (or (and (eq system-type 'windows-nt) 1884 (eq (elt file 0) ?\/) 1885 (substring file 1)) 1886 file)))) 1887 (->> file-name 1888 (concat (-some #'lsp--workspace-host-root (lsp-workspaces))) 1889 (lsp-remap-path-if-needed)))) 1890 1891 (defun lsp--buffer-uri () 1892 "Return URI of the current buffer." 1893 (or lsp-buffer-uri 1894 (plist-get lsp--virtual-buffer :buffer-uri) 1895 (lsp--path-to-uri 1896 (or (buffer-file-name) (buffer-file-name (buffer-base-buffer)))))) 1897 1898 (defun lsp-register-client-capabilities (&rest _args) 1899 "Implemented only to make `company-lsp' happy. 1900 DELETE when `lsp-mode.el' is deleted.") 1901 1902 (defconst lsp--url-path-allowed-chars 1903 (url--allowed-chars (append '(?/) url-unreserved-chars)) 1904 "`url-unreserved-chars' with additional delim ?/. 1905 This set of allowed chars is enough for hexifying local file paths.") 1906 1907 (defun lsp--path-to-uri-1 (path) 1908 (concat lsp--uri-file-prefix 1909 (--> path 1910 (expand-file-name it) 1911 (or (file-remote-p it 'localname t) it) 1912 (url-hexify-string it lsp--url-path-allowed-chars)))) 1913 1914 (defun lsp--path-to-uri (path) 1915 "Convert PATH to a uri." 1916 (if-let ((uri-fn (->> (lsp-workspaces) 1917 (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client)) 1918 (cl-first)))) 1919 (funcall uri-fn path) 1920 (lsp--path-to-uri-1 path))) 1921 1922 (defun lsp--string-match-any (regex-list str) 1923 "Return the first regex, if any, within REGEX-LIST matching STR." 1924 (--first (string-match it str) regex-list)) 1925 1926 (cl-defstruct lsp-watch 1927 (descriptors (make-hash-table :test 'equal)) 1928 root-directory) 1929 1930 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories) 1931 (let ((file-name (cl-third event)) 1932 (event-type (cl-second event))) 1933 (cond 1934 ((and (file-directory-p file-name) 1935 (equal 'created event-type) 1936 (not (lsp--string-match-any ignored-directories file-name))) 1937 1938 (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch) 1939 1940 ;; process the files that are already present in 1941 ;; the directory. 1942 (->> (directory-files-recursively file-name ".*" t) 1943 (seq-do (lambda (f) 1944 (unless (file-directory-p f) 1945 (funcall callback (list nil 'created f))))))) 1946 ((and (memq event-type '(created deleted changed)) 1947 (not (file-directory-p file-name)) 1948 (not (lsp--string-match-any ignored-files file-name))) 1949 (funcall callback event)) 1950 ((and (memq event-type '(renamed)) 1951 (not (file-directory-p file-name)) 1952 (not (lsp--string-match-any ignored-files file-name))) 1953 (funcall callback `(,(cl-first event) deleted ,(cl-third event))) 1954 (funcall callback `(,(cl-first event) created ,(cl-fourth event))))))) 1955 1956 (defun lsp--ask-about-watching-big-repo (number-of-directories dir) 1957 "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR. 1958 This is useful when there is a lot of files in a repository, as 1959 that may slow Emacs down. Returns t if the user wants to watch 1960 the entire repository, nil otherwise." 1961 (prog1 1962 (yes-or-no-p 1963 (format 1964 "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down. 1965 Do you want to watch all files in %s? " 1966 dir 1967 number-of-directories 1968 dir)) 1969 (lsp--info 1970 (concat "You can configure this warning with the `lsp-enable-file-watchers' " 1971 "and `lsp-file-watch-threshold' variables")))) 1972 1973 1974 (defun lsp--path-is-watchable-directory (path dir ignored-directories) 1975 "Figure out whether PATH (inside of DIR) is meant to have a file watcher set. 1976 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't 1977 want to watch." 1978 (let 1979 ((full-path (f-join dir path))) 1980 (and (file-accessible-directory-p full-path) 1981 (not (equal path ".")) 1982 (not (equal path "..")) 1983 (not (lsp--string-match-any ignored-directories full-path))))) 1984 1985 1986 (defun lsp--all-watchable-directories (dir ignored-directories) 1987 "Traverse DIR recursively returning a list of paths that should have watchers. 1988 IGNORED-DIRECTORIES will be used for exclusions" 1989 (let* ((dir (if (f-symlink? dir) 1990 (file-truename dir) 1991 dir))) 1992 (apply #'nconc 1993 ;; the directory itself is assumed to be part of the set 1994 (list dir) 1995 ;; collect all subdirectories that are watchable 1996 (-map 1997 (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories)) 1998 ;; but only look at subdirectories that are watchable 1999 (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories)) 2000 (directory-files dir)))))) 2001 2002 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?) 2003 "Create recursive file notification watch in DIR. 2004 CALLBACK will be called when there are changes in any of 2005 the monitored files. WATCHES is a hash table directory->file 2006 notification handle which contains all of the watch that 2007 already have been created. Watches will not be created for 2008 any directory that matches any regex in IGNORED-DIRECTORIES. 2009 Watches will not be created for any file that matches any 2010 regex in IGNORED-FILES." 2011 (let* ((dir (if (f-symlink? dir) 2012 (file-truename dir) 2013 dir)) 2014 (watch (or watch (make-lsp-watch :root-directory dir))) 2015 (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories))) 2016 (lsp-log "Creating watchers for following %s folders:\n %s" 2017 (length dirs-to-watch) 2018 (s-join "\n " dirs-to-watch)) 2019 (when (or 2020 (not warn-big-repo?) 2021 (not lsp-file-watch-threshold) 2022 (let ((number-of-directories (length dirs-to-watch))) 2023 (or 2024 (< number-of-directories lsp-file-watch-threshold) 2025 (condition-case nil 2026 (lsp--ask-about-watching-big-repo number-of-directories dir) 2027 (quit))))) 2028 (dolist (current-dir dirs-to-watch) 2029 (condition-case err 2030 (progn 2031 (puthash 2032 current-dir 2033 (file-notify-add-watch current-dir 2034 '(change) 2035 (lambda (event) 2036 (lsp--folder-watch-callback event callback watch ignored-files ignored-directories))) 2037 (lsp-watch-descriptors watch))) 2038 (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err))) 2039 (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))))) 2040 watch)) 2041 2042 (defun lsp-kill-watch (watch) 2043 "Delete WATCH." 2044 (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch)) 2045 (ht-clear! (lsp-watch-descriptors watch))) 2046 2047 (defun lsp-json-bool (val) 2048 "Convert VAL to JSON boolean." 2049 (if val t :json-false)) 2050 2051 (defmacro with-lsp-workspace (workspace &rest body) 2052 "Helper macro for invoking BODY in WORKSPACE context." 2053 (declare (debug (form body)) 2054 (indent 1)) 2055 `(let ((lsp--cur-workspace ,workspace)) ,@body)) 2056 2057 (defmacro with-lsp-workspaces (workspaces &rest body) 2058 "Helper macro for invoking BODY against multiple WORKSPACES." 2059 (declare (debug (form body)) 2060 (indent 1)) 2061 `(let ((lsp--buffer-workspaces ,workspaces)) ,@body)) 2062 2063 2064 2065 (defmacro lsp-consistency-check (package) 2066 `(defconst ,(intern (concat (symbol-name package) 2067 "-plist-value-when-compiled")) 2068 (eval-when-compile lsp-use-plists))) 2069 2070 2071 ;; loading code-workspace files 2072 2073 ;;;###autoload 2074 (defun lsp-load-vscode-workspace (file) 2075 "Load vscode workspace from FILE" 2076 (interactive "fSelect file to import: ") 2077 (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session))) 2078 2079 (let ((dir (f-dirname file))) 2080 (->> file 2081 (json-read-file) 2082 (alist-get 'folders) 2083 (-map (-lambda ((&alist 'path)) 2084 (lsp-workspace-folders-add (expand-file-name path dir))))))) 2085 2086 ;;;###autoload 2087 (defun lsp-save-vscode-workspace (file) 2088 "Save vscode workspace to FILE" 2089 (interactive "FSelect file to save to: ") 2090 2091 (let ((json-encoding-pretty-print t)) 2092 (f-write-text (json-encode 2093 `((folders . ,(->> (lsp-session) 2094 (lsp-session-folders) 2095 (--map `((path . ,it))))))) 2096 'utf-8 2097 file))) 2098 2099 2100 (defmacro lsp-foreach-workspace (&rest body) 2101 "Execute BODY for each of the current workspaces." 2102 (declare (debug (form body))) 2103 `(--map (with-lsp-workspace it ,@body) (lsp-workspaces))) 2104 2105 (defmacro when-lsp-workspace (workspace &rest body) 2106 "Helper macro for invoking BODY in WORKSPACE context if present." 2107 (declare (debug (form body)) 2108 (indent 1)) 2109 `(when-let ((lsp--cur-workspace ,workspace)) ,@body)) 2110 2111 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items)) 2112 (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read)) 2113 (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label)) 2114 items)) 2115 (result (funcall-interactively 2116 selectfunc 2117 (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels)) 2118 (choices (if (listp result) 2119 (if (equal result '("*")) 2120 itemLabels 2121 result) 2122 (list result)))) 2123 (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data)) 2124 (if (member label choices) 2125 (lsp-make-quick-pick-item :label label :picked t :user-data user-data) 2126 nil)) 2127 items))))) 2128 2129 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?)) 2130 (read-string (format "%s: " prompt) (or value? ""))) 2131 2132 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type)) 2133 "Send the server's messages to log. 2134 PARAMS - the data sent from _WORKSPACE." 2135 (funcall (cl-case type 2136 (1 'lsp--error) 2137 (2 'lsp--warn) 2138 (t 'lsp--info)) 2139 "%s" 2140 message)) 2141 2142 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type)) 2143 "Send the server's messages to log. 2144 PARAMS - the data sent from WORKSPACE." 2145 (ignore 2146 (let ((client (lsp--workspace-client workspace))) 2147 (when (or (not client) 2148 (cl-notany (-rpartial #'string-match-p message) 2149 (lsp--client-ignore-messages client))) 2150 (lsp-log "%s" (lsp--propertize message type)))))) 2151 2152 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?)) 2153 "Display a message request to user sending the user selection back to server." 2154 (let* ((message (lsp--propertize message type)) 2155 (choices (seq-map #'lsp:message-action-item-title actions?))) 2156 (if choices 2157 (completing-read (concat message " ") (seq-into choices 'list) nil t) 2158 (lsp-log message)))) 2159 2160 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?)) 2161 "Show document URI in a buffer and go to SELECTION if any." 2162 (let ((path (lsp--uri-to-path uri))) 2163 (when (f-exists? path) 2164 (with-current-buffer (find-file path) 2165 (when selection? 2166 (goto-char (lsp--position-to-point (lsp:range-start selection?)))) 2167 t)))) 2168 2169 (defcustom lsp-progress-prefix "⌛ " 2170 "Progress prefix." 2171 :group 'lsp-mode 2172 :type 'string 2173 :package-version '(lsp-mode . "8.0.0")) 2174 2175 (defcustom lsp-progress-function #'lsp-on-progress-modeline 2176 "Function for handling the progress notifications." 2177 :group 'lsp-mode 2178 :type '(choice 2179 (const :tag "Use modeline" lsp-on-progress-modeline) 2180 (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')" 2181 lsp-on-progress-legacy) 2182 (const :tag "Ignore" ignore) 2183 (function :tag "Other function")) 2184 :package-version '(lsp-mode . "8.0.0")) 2185 2186 (defcustom lsp-request-while-no-input-may-block nil 2187 "Have `lsp-request-while-no-input` block unless `non-essential` is t." 2188 :group 'lsp-mode 2189 :type 'boolean) 2190 2191 (defun lsp--progress-status () 2192 "Returns the status of the progress for the current workspaces." 2193 (-let ((progress-status 2194 (s-join 2195 "|" 2196 (-keep 2197 (lambda (workspace) 2198 (let ((tokens (lsp--workspace-work-done-tokens workspace))) 2199 (unless (ht-empty? tokens) 2200 (mapconcat 2201 (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?)) 2202 (concat (if percentage? 2203 (if (numberp percentage?) 2204 (format "%.0f%%%% " percentage?) 2205 (format "%s%%%% " percentage?)) 2206 "") 2207 (or message? title))) 2208 (ht-values tokens) 2209 "|")))) 2210 (lsp-workspaces))))) 2211 (unless (s-blank? progress-status) 2212 (concat lsp-progress-prefix progress-status " ")))) 2213 2214 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value 2215 (value &as &WorkDoneProgress :kind))) 2216 "PARAMS contains the progress data. 2217 WORKSPACE is the workspace that contains the progress token." 2218 (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status)))) 2219 (pcase kind 2220 ("begin" (lsp-workspace-set-work-done-token token value workspace)) 2221 ("report" (lsp-workspace-set-work-done-token token value workspace)) 2222 ("end" (lsp-workspace-rem-work-done-token token workspace))) 2223 (force-mode-line-update)) 2224 2225 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value 2226 (value &as &WorkDoneProgress :kind))) 2227 "PARAMS contains the progress data. 2228 WORKSPACE is the workspace that contains the progress token." 2229 (pcase kind 2230 ("begin" 2231 (-let* (((&WorkDoneProgressBegin :title :percentage?) value) 2232 (reporter 2233 (if lsp-progress-via-spinner 2234 (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types)) 2235 ;; Set message as a tooltip for the spinner strings 2236 (propertized-strings 2237 (seq-map (lambda (string) (propertize string 'help-echo title)) 2238 spinner-strings)) 2239 (spinner-type (vconcat propertized-strings))) 2240 ;; The progress relates to the server as a whole, 2241 ;; display it on all buffers. 2242 (mapcar (lambda (buffer) 2243 (lsp-with-current-buffer buffer 2244 (spinner-start spinner-type)) 2245 buffer) 2246 (lsp--workspace-buffers workspace))) 2247 (if percentage? 2248 (make-progress-reporter title 0 100 percentage?) 2249 ;; No percentage, just progress 2250 (make-progress-reporter title nil nil))))) 2251 (lsp-workspace-set-work-done-token token reporter workspace))) 2252 ("report" 2253 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2254 (unless lsp-progress-via-spinner 2255 (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value))))) 2256 2257 ("end" 2258 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2259 (if lsp-progress-via-spinner 2260 (mapc (lambda (buffer) 2261 (when (lsp-buffer-live-p buffer) 2262 (lsp-with-current-buffer buffer 2263 (spinner-stop)))) 2264 reporter) 2265 (progress-reporter-done reporter)) 2266 (lsp-workspace-rem-work-done-token token workspace))))) 2267 2268 2269 ;; diagnostics 2270 2271 (defvar lsp-diagnostic-filter nil 2272 "A a function which will be called with 2273 `&PublishDiagnosticsParams' and `workspace' which can be used 2274 to filter out the diagnostics. The function should return 2275 `&PublishDiagnosticsParams'. 2276 2277 Common usecase are: 2278 1. Filter the diagnostics for a particular language server. 2279 2. Filter out the diagnostics under specific level.") 2280 2281 (defvar lsp-diagnostic-stats (ht)) 2282 2283 (defun lsp-diagnostics (&optional current-workspace?) 2284 "Return the diagnostics from all workspaces." 2285 (or (pcase (if current-workspace? 2286 (lsp-workspaces) 2287 (lsp--session-workspaces (lsp-session))) 2288 (`() ()) 2289 (`(,workspace) (lsp--workspace-diagnostics workspace)) 2290 (`,workspaces (let ((result (make-hash-table :test 'equal))) 2291 (mapc (lambda (workspace) 2292 (->> workspace 2293 (lsp--workspace-diagnostics) 2294 (maphash (lambda (file-name diagnostics) 2295 (puthash file-name 2296 (append (gethash file-name result) diagnostics) 2297 result))))) 2298 workspaces) 2299 result))) 2300 (ht))) 2301 2302 (defun lsp-diagnostics-stats-for (path) 2303 "Get diagnostics statistics for PATH. 2304 The result format is vector [_ errors warnings infos hints] or nil." 2305 (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats)) 2306 2307 (defun lsp-diagnostics--request-pull-diagnostics (workspace) 2308 "Request new diagnostics for the current file within WORKSPACE. 2309 This is only executed if the server supports pull diagnostics." 2310 (when (lsp-feature? "textDocument/diagnostic") 2311 (let ((path (lsp--fix-path-casing (buffer-file-name)))) 2312 (lsp-request-async "textDocument/diagnostic" 2313 (list :textDocument (lsp--text-document-identifier)) 2314 (-lambda ((&DocumentDiagnosticReport :kind :items?)) 2315 (lsp-diagnostics--apply-pull-diagnostics workspace path kind items?)) 2316 :mode 'tick)))) 2317 2318 (defun lsp-diagnostics--update-path (path new-stats) 2319 (let ((new-stats (copy-sequence new-stats)) 2320 (path (lsp--fix-path-casing (directory-file-name path)))) 2321 (if-let ((old-data (gethash path lsp-diagnostic-stats))) 2322 (dotimes (idx 5) 2323 (cl-callf + (aref old-data idx) 2324 (aref new-stats idx))) 2325 (puthash path new-stats lsp-diagnostic-stats)))) 2326 2327 (defun lsp-diagnostics--convert-and-update-path-stats (workspace path diagnostics) 2328 (let ((path (lsp--fix-path-casing path)) 2329 (new-stats (make-vector 5 0))) 2330 (mapc (-lambda ((&Diagnostic :severity?)) 2331 (cl-incf (aref new-stats (or severity? 1)))) 2332 diagnostics) 2333 (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace)))) 2334 (mapc (-lambda ((&Diagnostic :severity?)) 2335 (cl-decf (aref new-stats (or severity? 1)))) 2336 old-diags)) 2337 (lsp-diagnostics--update-path path new-stats) 2338 (while (not (string= path (setf path (file-name-directory 2339 (directory-file-name path))))) 2340 (lsp-diagnostics--update-path path new-stats)))) 2341 2342 (lsp-defun lsp--on-diagnostics-update-stats (workspace 2343 (&PublishDiagnosticsParams :uri :diagnostics)) 2344 (lsp-diagnostics--convert-and-update-path-stats workspace (lsp--uri-to-path uri) diagnostics)) 2345 2346 (defun lsp-diagnostics--apply-pull-diagnostics (workspace path kind diagnostics?) 2347 "Update WORKSPACE diagnostics at PATH with DIAGNOSTICS?. 2348 Depends on KIND being a \\='full\\=' update." 2349 (cond 2350 ((equal kind "full") 2351 ;; TODO support `lsp-diagnostic-filter' 2352 ;; (the params types differ from the published diagnostics response) 2353 (lsp-diagnostics--convert-and-update-path-stats workspace path diagnostics?) 2354 (-let* ((lsp--virtual-buffer-mappings (ht)) 2355 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2356 (if (seq-empty-p diagnostics?) 2357 (remhash path workspace-diagnostics) 2358 (puthash path (append diagnostics? nil) workspace-diagnostics)) 2359 (run-hooks 'lsp-diagnostics-updated-hook))) 2360 ((equal kind "unchanged") t) 2361 (t (lsp--error "Unknown pull diagnostic result kind '%s'" kind)))) 2362 2363 (defun lsp--on-diagnostics (workspace params) 2364 "Callback for textDocument/publishDiagnostics. 2365 interface PublishDiagnosticsParams { 2366 uri: string; 2367 diagnostics: Diagnostic[]; 2368 } 2369 PARAMS contains the diagnostics data. 2370 WORKSPACE is the workspace that contains the diagnostics." 2371 (when lsp-diagnostic-filter 2372 (setf params (funcall lsp-diagnostic-filter params workspace))) 2373 2374 (lsp--on-diagnostics-update-stats workspace params) 2375 2376 (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params) 2377 (lsp--virtual-buffer-mappings (ht)) 2378 (file (lsp--fix-path-casing (lsp--uri-to-path uri))) 2379 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2380 2381 (if (seq-empty-p diagnostics) 2382 (remhash file workspace-diagnostics) 2383 (puthash file (append diagnostics nil) workspace-diagnostics)) 2384 2385 (run-hooks 'lsp-diagnostics-updated-hook))) 2386 2387 (defun lsp-diagnostics--workspace-cleanup (workspace) 2388 (->> workspace 2389 (lsp--workspace-diagnostics) 2390 (maphash (lambda (key _) 2391 (lsp--on-diagnostics-update-stats 2392 workspace 2393 (lsp-make-publish-diagnostics-params 2394 :uri (lsp--path-to-uri key) 2395 :diagnostics []))))) 2396 (clrhash (lsp--workspace-diagnostics workspace))) 2397 2398 2399 2400 ;; textDocument/foldingRange support 2401 2402 (cl-defstruct lsp--folding-range beg end kind children) 2403 2404 (defvar-local lsp--cached-folding-ranges nil) 2405 (defvar-local lsp--cached-nested-folding-ranges nil) 2406 2407 (defun lsp--folding-range-width (range) 2408 (- (lsp--folding-range-end range) 2409 (lsp--folding-range-beg range))) 2410 2411 (defun lsp--get-folding-ranges () 2412 "Get the folding ranges for the current buffer." 2413 (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges)) 2414 (let* ((ranges (lsp-request "textDocument/foldingRange" 2415 `(:textDocument ,(lsp--text-document-identifier)))) 2416 (sorted-line-col-pairs (->> ranges 2417 (cl-mapcan (-lambda ((&FoldingRange :start-line 2418 :start-character? 2419 :end-line 2420 :end-character?)) 2421 (list (cons start-line start-character?) 2422 (cons end-line end-character?)))) 2423 (-sort #'lsp--line-col-comparator))) 2424 (line-col-to-point-map (lsp--convert-line-col-to-points-batch 2425 sorted-line-col-pairs))) 2426 (setq lsp--cached-folding-ranges 2427 (cons (buffer-chars-modified-tick) 2428 (--> ranges 2429 (seq-map (-lambda ((range &as 2430 &FoldingRange :start-line 2431 :start-character? 2432 :end-line 2433 :end-character? 2434 :kind?)) 2435 (make-lsp--folding-range 2436 :beg (ht-get line-col-to-point-map 2437 (cons start-line start-character?)) 2438 :end (ht-get line-col-to-point-map 2439 (cons end-line end-character?)) 2440 :kind kind?)) 2441 it) 2442 (seq-filter (lambda (folding-range) 2443 (< (lsp--folding-range-beg folding-range) 2444 (lsp--folding-range-end folding-range))) 2445 it) 2446 (seq-into it 'list) 2447 (delete-dups it)))))) 2448 (cdr lsp--cached-folding-ranges)) 2449 2450 (defun lsp--get-nested-folding-ranges () 2451 "Get a list of nested folding ranges for the current buffer." 2452 (-let [(tick . _) lsp--cached-folding-ranges] 2453 (if (and (eq tick (buffer-chars-modified-tick)) 2454 lsp--cached-nested-folding-ranges) 2455 lsp--cached-nested-folding-ranges 2456 (setq lsp--cached-nested-folding-ranges 2457 (lsp--folding-range-build-trees (lsp--get-folding-ranges)))))) 2458 2459 (defun lsp--folding-range-build-trees (ranges) 2460 (setq ranges (seq-sort #'lsp--range-before-p ranges)) 2461 (let* ((dummy-node (make-lsp--folding-range 2462 :beg most-negative-fixnum 2463 :end most-positive-fixnum)) 2464 (stack (list dummy-node))) 2465 (dolist (range ranges) 2466 (while (not (lsp--range-inside-p range (car stack))) 2467 (pop stack)) 2468 (push range (lsp--folding-range-children (car stack))) 2469 (push range stack)) 2470 (lsp--folding-range-children dummy-node))) 2471 2472 (defun lsp--range-inside-p (r1 r2) 2473 "Return non-nil if folding range R1 lies inside R2" 2474 (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2)) 2475 (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2)))) 2476 2477 (defun lsp--range-before-p (r1 r2) 2478 "Return non-nil if folding range R1 ends before R2" 2479 ;; Ensure r1 comes before r2 2480 (or (< (lsp--folding-range-beg r1) 2481 (lsp--folding-range-beg r2)) 2482 ;; If beg(r1) == beg(r2) make sure r2 ends first 2483 (and (= (lsp--folding-range-beg r1) 2484 (lsp--folding-range-beg r2)) 2485 (< (lsp--folding-range-end r2) 2486 (lsp--folding-range-end r1))))) 2487 2488 (defun lsp--point-inside-range-p (point range) 2489 "Return non-nil if POINT lies inside folding range RANGE." 2490 (and (>= point (lsp--folding-range-beg range)) 2491 (<= point (lsp--folding-range-end range)))) 2492 2493 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point))) 2494 "Return the innermost folding range POINT lies in." 2495 (seq-reduce (lambda (innermost-range curr-range) 2496 (if (and (lsp--point-inside-range-p point curr-range) 2497 (or (null innermost-range) 2498 (lsp--range-inside-p curr-range innermost-range))) 2499 curr-range 2500 innermost-range)) 2501 (lsp--get-folding-ranges) 2502 nil)) 2503 2504 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point))) 2505 "Return the outermost folding range POINT lies in." 2506 (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range) 2507 (let ((curr-width (lsp--folding-range-width curr-range))) 2508 (if (and (lsp--point-inside-range-p point curr-range) 2509 (or (null best-pair) 2510 (> curr-width outermost-width))) 2511 (cons curr-width curr-range) 2512 best-pair))) 2513 (lsp--get-folding-ranges) 2514 nil))) 2515 2516 (defun lsp--folding-range-at-point-bounds () 2517 (when (and lsp-enable-folding 2518 (lsp-feature? "textDocument/foldingRange")) 2519 (if-let ((range (lsp--get-current-innermost-folding-range))) 2520 (cons (lsp--folding-range-beg range) 2521 (lsp--folding-range-end range))))) 2522 (put 'lsp--folding-range 'bounds-of-thing-at-point 2523 #'lsp--folding-range-at-point-bounds) 2524 2525 (defun lsp--get-nearest-folding-range (&optional backward) 2526 (let ((point (point)) 2527 (found nil)) 2528 (while (not 2529 (or found 2530 (if backward 2531 (<= point (point-min)) 2532 (>= point (point-max))))) 2533 (if backward (cl-decf point) (cl-incf point)) 2534 (setq found (lsp--get-current-innermost-folding-range point))) 2535 found)) 2536 2537 (defun lsp--folding-range-at-point-forward-op (n) 2538 (when (and lsp-enable-folding 2539 (not (zerop n)) 2540 (lsp-feature? "textDocument/foldingRange")) 2541 (cl-block break 2542 (dotimes (_ (abs n)) 2543 (if-let ((range (lsp--get-nearest-folding-range (< n 0)))) 2544 (goto-char (if (< n 0) 2545 (lsp--folding-range-beg range) 2546 (lsp--folding-range-end range))) 2547 (cl-return-from break)))))) 2548 (put 'lsp--folding-range 'forward-op 2549 #'lsp--folding-range-at-point-forward-op) 2550 2551 (defun lsp--folding-range-at-point-beginning-op () 2552 (goto-char (car (lsp--folding-range-at-point-bounds)))) 2553 (put 'lsp--folding-range 'beginning-op 2554 #'lsp--folding-range-at-point-beginning-op) 2555 2556 (defun lsp--folding-range-at-point-end-op () 2557 (goto-char (cdr (lsp--folding-range-at-point-bounds)))) 2558 (put 'lsp--folding-range 'end-op 2559 #'lsp--folding-range-at-point-end-op) 2560 2561 (defun lsp--range-at-point-bounds () 2562 (or (lsp--folding-range-at-point-bounds) 2563 (when-let ((range (and 2564 (lsp-feature? "textDocument/hover") 2565 (->> (lsp--text-document-position-params) 2566 (lsp-request "textDocument/hover") 2567 (lsp:hover-range?))))) 2568 (lsp--range-to-region range)))) 2569 2570 ;; A more general purpose "thing", useful for applications like focus.el 2571 (put 'lsp--range 'bounds-of-thing-at-point 2572 #'lsp--range-at-point-bounds) 2573 2574 (defun lsp--log-io-p (method) 2575 "Return non nil if should log for METHOD." 2576 (and lsp-log-io 2577 (or (not lsp-log-io-allowlist-methods) 2578 (member method lsp-log-io-allowlist-methods)))) 2579 2580 2581 ;; toggles 2582 2583 (defun lsp-toggle-trace-io () 2584 "Toggle client-server protocol logging." 2585 (interactive) 2586 (setq lsp-log-io (not lsp-log-io)) 2587 (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled"))) 2588 2589 (defun lsp-toggle-signature-auto-activate () 2590 "Toggle signature auto activate." 2591 (interactive) 2592 (setq lsp-signature-auto-activate 2593 (unless lsp-signature-auto-activate '(:on-trigger-char))) 2594 (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled")) 2595 (lsp--update-signature-help-hook)) 2596 2597 (defun lsp-toggle-on-type-formatting () 2598 "Toggle on type formatting." 2599 (interactive) 2600 (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting)) 2601 (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled")) 2602 (lsp--update-on-type-formatting-hook)) 2603 2604 (defun lsp-toggle-symbol-highlight () 2605 "Toggle symbol highlighting." 2606 (interactive) 2607 (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting)) 2608 2609 (cond 2610 ((and lsp-enable-symbol-highlighting 2611 (lsp-feature? "textDocument/documentHighlight")) 2612 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t) 2613 (lsp--info "Symbol highlighting enabled in current buffer.")) 2614 ((not lsp-enable-symbol-highlighting) 2615 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 2616 (lsp--remove-overlays 'lsp-highlight) 2617 (lsp--info "Symbol highlighting disabled in current buffer.")))) 2618 2619 2620 ;; keybindings 2621 (defvar lsp--binding-descriptions nil 2622 "List of key binding/short description pair.") 2623 2624 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings) 2625 "In KEYMAP, define key sequence KEY as DEF conditionally. 2626 This is like `define-key', except the definition disappears 2627 whenever COND evaluates to nil. 2628 DESC is the short-description for the binding. 2629 BINDINGS is a list of (key def desc cond)." 2630 (declare (indent defun) 2631 (debug (form form form form form &rest sexp))) 2632 (->> (cl-list* key def desc cond bindings) 2633 (-partition 4) 2634 (-mapcat (-lambda ((key def desc cond)) 2635 `((define-key ,keymap ,key 2636 '(menu-item 2637 ,(format "maybe-%s" def) 2638 ,def 2639 :filter 2640 (lambda (item) 2641 (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer) 2642 lsp--describe-buffer) 2643 (current-buffer)) 2644 ,cond) 2645 item)))) 2646 (when (stringp ,key) 2647 (setq lsp--binding-descriptions 2648 (append lsp--binding-descriptions '(,key ,desc))))))) 2649 macroexp-progn)) 2650 2651 (defvar lsp--describe-buffer nil) 2652 2653 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus) 2654 (let ((lsp--describe-buffer buffer)) 2655 (funcall fn buffer prefix menus))) 2656 2657 (advice-add 'describe-buffer-bindings 2658 :around 2659 #'lsp-describe-buffer-bindings-advice) 2660 2661 (defun lsp--prepend-prefix (mappings) 2662 (->> mappings 2663 (-partition 2) 2664 (-mapcat (-lambda ((key description)) 2665 (list (concat lsp-keymap-prefix " " key) 2666 description))))) 2667 2668 (defvar lsp-command-map 2669 (-doto (make-sparse-keymap) 2670 (lsp-define-conditional-key 2671 ;; workspaces 2672 "wD" lsp-disconnect "disconnect" (lsp-workspaces) 2673 "wd" lsp-describe-session "describe session" t 2674 "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces) 2675 "wr" lsp-workspace-restart "restart server" (lsp-workspaces) 2676 "ws" lsp "start server" t 2677 2678 ;; formatting 2679 "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting") 2680 (lsp-feature? "textDocument/formatting")) 2681 "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting") 2682 2683 ;; folders 2684 "Fa" lsp-workspace-folders-add "add folder" t 2685 "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t 2686 "Fr" lsp-workspace-folders-remove "remove folder" t 2687 2688 ;; toggles 2689 "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature? 2690 "textDocument/publishDiagnostics") 2691 "TL" lsp-toggle-trace-io "toggle log io" t 2692 "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline) 2693 "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs) 2694 "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature? 2695 "textDocument/codeAction") 2696 "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature? 2697 "textDocument/documentSymbol") 2698 "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc) 2699 "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature? 2700 "textDocument/onTypeFormatting") 2701 "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight") 2702 "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens") 2703 "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp") 2704 2705 ;; goto 2706 "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol") 2707 "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration") 2708 "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list) 2709 "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition") 2710 "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls") 2711 (fboundp 'lsp-treemacs-call-hierarchy)) 2712 "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation") 2713 "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references") 2714 "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition") 2715 2716 ;; help 2717 "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc) 2718 (lsp-feature? "textDocument/hover")) 2719 "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover") 2720 "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp") 2721 2722 ;; refactoring 2723 "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction") 2724 "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename") 2725 2726 ;; actions 2727 "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction") 2728 "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight") 2729 "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy)) 2730 2731 ;; peeks 2732 "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition") 2733 (fboundp 'lsp-ui-peek-find-definitions)) 2734 "Gi" lsp-ui-peek-find-implementation "peek implementations" (and 2735 (fboundp 'lsp-ui-peek-find-implementation) 2736 (lsp-feature? "textDocument/implementation")) 2737 "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references) 2738 (lsp-feature? "textDocument/references")) 2739 "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp 2740 'lsp-ui-peek-find-workspace-symbol) 2741 (lsp-feature? "workspace/symbol"))))) 2742 2743 2744 ;; which-key integration 2745 2746 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key") 2747 (declare-function which-key-add-key-based-replacements "ext:which-key") 2748 2749 (defun lsp-enable-which-key-integration (&optional all-modes) 2750 "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current 2751 active `major-mode', or for all major modes when ALL-MODES is t." 2752 (cl-flet ((which-key-fn (if all-modes 2753 'which-key-add-key-based-replacements 2754 (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode)))) 2755 (apply 2756 #'which-key-fn 2757 (lsp--prepend-prefix 2758 (cl-list* 2759 "" "lsp" 2760 "w" "workspaces" 2761 "F" "folders" 2762 "=" "formatting" 2763 "T" "toggle" 2764 "g" "goto" 2765 "h" "help" 2766 "r" "refactor" 2767 "a" "code actions" 2768 "G" "peek" 2769 lsp--binding-descriptions))))) 2770 2771 2772 ;; Globbing syntax 2773 2774 ;; We port VSCode's glob-to-regexp code 2775 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts) 2776 ;; since the LSP globbing syntax seems to be the same as that of 2777 ;; VSCode. 2778 2779 (defconst lsp-globstar "**" 2780 "Globstar pattern.") 2781 2782 (defconst lsp-glob-split ?/ 2783 "The character by which we split path components in a glob 2784 pattern.") 2785 2786 (defconst lsp-path-regexp "[/\\\\]" 2787 "Forward or backslash to be used as a path separator in 2788 computed regexps.") 2789 2790 (defconst lsp-non-path-regexp "[^/\\\\]" 2791 "A regexp matching anything other than a slash.") 2792 2793 (defconst lsp-globstar-regexp 2794 (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?" 2795 lsp-path-regexp 2796 lsp-non-path-regexp lsp-path-regexp 2797 lsp-path-regexp lsp-non-path-regexp) 2798 "Globstar in regexp form.") 2799 2800 (defun lsp-split-glob-pattern (pattern split-char) 2801 "Split PATTERN at SPLIT-CHAR while respecting braces and brackets." 2802 (when pattern 2803 (let ((segments nil) 2804 (in-braces nil) 2805 (in-brackets nil) 2806 (current-segment "")) 2807 (dolist (char (string-to-list pattern)) 2808 (cl-block 'exit-point 2809 (if (eq char split-char) 2810 (when (and (null in-braces) 2811 (null in-brackets)) 2812 (push current-segment segments) 2813 (setq current-segment "") 2814 (cl-return-from 'exit-point)) 2815 (pcase char 2816 (?{ 2817 (setq in-braces t)) 2818 (?} 2819 (setq in-braces nil)) 2820 (?\[ 2821 (setq in-brackets t)) 2822 (?\] 2823 (setq in-brackets nil)))) 2824 (setq current-segment (concat current-segment 2825 (char-to-string char))))) 2826 (unless (string-empty-p current-segment) 2827 (push current-segment segments)) 2828 (nreverse segments)))) 2829 2830 (defun lsp--glob-to-regexp (pattern) 2831 "Helper function to convert a PATTERN from LSP's glob syntax to 2832 an Elisp regexp." 2833 (if (string-empty-p pattern) 2834 "" 2835 (let ((current-regexp "") 2836 (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split))) 2837 (if (-all? (lambda (segment) (eq segment lsp-globstar)) 2838 glob-segments) 2839 ".*" 2840 (let ((prev-segment-was-globstar nil)) 2841 (seq-do-indexed 2842 (lambda (segment index) 2843 (if (string-equal segment lsp-globstar) 2844 (unless prev-segment-was-globstar 2845 (setq current-regexp (concat current-regexp 2846 lsp-globstar-regexp)) 2847 (setq prev-segment-was-globstar t)) 2848 (let ((in-braces nil) 2849 (brace-val "") 2850 (in-brackets nil) 2851 (bracket-val "")) 2852 (dolist (char (string-to-list segment)) 2853 (cond 2854 ((and (not (char-equal char ?\})) 2855 in-braces) 2856 (setq brace-val (concat brace-val 2857 (char-to-string char)))) 2858 ((and in-brackets 2859 (or (not (char-equal char ?\])) 2860 (string-empty-p bracket-val))) 2861 (let ((curr (cond 2862 ((char-equal char ?-) 2863 "-") 2864 ;; NOTE: ?\^ and ?^ are different characters 2865 ((and (memq char '(?^ ?!)) 2866 (string-empty-p bracket-val)) 2867 "^") 2868 ((char-equal char lsp-glob-split) 2869 "") 2870 (t 2871 (regexp-quote (char-to-string char)))))) 2872 (setq bracket-val (concat bracket-val curr)))) 2873 (t 2874 (cl-case char 2875 (?{ 2876 (setq in-braces t)) 2877 (?\[ 2878 (setq in-brackets t)) 2879 (?} 2880 (let* ((choices (lsp-split-glob-pattern brace-val ?\,)) 2881 (brace-regexp (concat "\\(?:" 2882 (mapconcat #'lsp--glob-to-regexp choices "\\|") 2883 "\\)"))) 2884 (setq current-regexp (concat current-regexp 2885 brace-regexp)) 2886 (setq in-braces nil) 2887 (setq brace-val ""))) 2888 (?\] 2889 (setq current-regexp 2890 (concat current-regexp 2891 "[" bracket-val "]")) 2892 (setq in-brackets nil) 2893 (setq bracket-val "")) 2894 (?? 2895 (setq current-regexp 2896 (concat current-regexp 2897 lsp-non-path-regexp))) 2898 (?* 2899 (setq current-regexp 2900 (concat current-regexp 2901 lsp-non-path-regexp "*?"))) 2902 (t 2903 (setq current-regexp 2904 (concat current-regexp 2905 (regexp-quote (char-to-string char))))))))) 2906 (when (and (< index (1- (length glob-segments))) 2907 (or (not (string-equal (nth (1+ index) glob-segments) 2908 lsp-globstar)) 2909 (< (+ index 2) 2910 (length glob-segments)))) 2911 (setq current-regexp 2912 (concat current-regexp 2913 lsp-path-regexp))) 2914 (setq prev-segment-was-globstar nil)))) 2915 glob-segments) 2916 current-regexp))))) 2917 2918 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365 2919 (defun lsp-glob-unbrace-at-top-level (glob-pattern) 2920 "If GLOB-PATTERN does not start with a brace, return a singleton list 2921 containing GLOB-PATTERN. 2922 2923 If GLOB-PATTERN does start with a brace, return a list of the 2924 comma-separated globs within the top-level braces." 2925 (if (not (string-prefix-p "{" glob-pattern)) 2926 (list glob-pattern) 2927 (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,))) 2928 2929 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern) 2930 "Convert GLOB-PATTERN to a regexp wrapped with the beginning- 2931 and end-of-string meta-characters." 2932 (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'")) 2933 2934 (defun lsp-glob-to-regexps (glob-pattern) 2935 "Convert a GLOB-PATTERN to a list of Elisp regexps." 2936 (when-let* 2937 ((glob-pattern (cond ((hash-table-p glob-pattern) 2938 (ht-get glob-pattern "pattern")) 2939 ((stringp glob-pattern) glob-pattern) 2940 (t (error "Unknown glob-pattern type: %s" glob-pattern)))) 2941 (trimmed-pattern (string-trim glob-pattern)) 2942 (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern))) 2943 (seq-map #'lsp-glob-convert-to-wrapped-regexp 2944 top-level-unbraced-patterns))) 2945 2946 2947 2948 (defvar lsp-mode-menu) 2949 2950 (defun lsp-mouse-click (event) 2951 (interactive "e") 2952 (let* ((ec (event-start event)) 2953 (choice (x-popup-menu event lsp-mode-menu)) 2954 (action (lookup-key lsp-mode-menu (apply 'vector choice)))) 2955 2956 (select-window (posn-window ec)) 2957 2958 (unless (and (region-active-p) (eq action 'lsp-execute-code-action)) 2959 (goto-char (posn-point ec))) 2960 (run-with-idle-timer 2961 0.001 nil 2962 (lambda () 2963 (cl-labels ((check (value) (not (null value)))) 2964 (when choice 2965 (call-interactively action))))))) 2966 2967 (defvar lsp-mode-map 2968 (let ((map (make-sparse-keymap))) 2969 (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse) 2970 (define-key map (kbd "C-<mouse-1>") #'ignore) 2971 (define-key map (kbd "<mouse-3>") #'lsp-mouse-click) 2972 (define-key map (kbd "C-S-SPC") #'lsp-signature-activate) 2973 (when lsp-keymap-prefix 2974 (define-key map (kbd lsp-keymap-prefix) lsp-command-map)) 2975 map) 2976 "Keymap for `lsp-mode'.") 2977 2978 (define-minor-mode lsp-mode "Mode for LSP interaction." 2979 :keymap lsp-mode-map 2980 :lighter 2981 (" LSP[" 2982 (lsp--buffer-workspaces 2983 (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "][")) 2984 (:propertize "Disconnected" face warning)) 2985 "]") 2986 :group 'lsp-mode 2987 (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred)) 2988 ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp' 2989 (lsp))) 2990 2991 (defvar lsp-mode-menu 2992 (easy-menu-create-menu 2993 nil 2994 `(["Go to definition" lsp-find-definition 2995 :active (lsp-feature? "textDocument/definition")] 2996 ["Find references" lsp-find-references 2997 :active (lsp-feature? "textDocument/references")] 2998 ["Find implementations" lsp-find-implementation 2999 :active (lsp-feature? "textDocument/implementation")] 3000 ["Find declarations" lsp-find-declaration 3001 :active (lsp-feature? "textDocument/declaration")] 3002 ["Go to type declaration" lsp-find-type-definition 3003 :active (lsp-feature? "textDocument/typeDefinition")] 3004 "--" 3005 ["Describe" lsp-describe-thing-at-point] 3006 ["Code action" lsp-execute-code-action] 3007 ["Format" lsp-format-buffer] 3008 ["Highlight references" lsp-document-highlight] 3009 ["Type Hierarchy" lsp-java-type-hierarchy 3010 :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")] 3011 ["Type Hierarchy" lsp-treemacs-type-hierarchy 3012 :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")) 3013 (functionp 'lsp-treemacs-type-hierarchy) 3014 (lsp-feature? "textDocument/typeHierarchy"))] 3015 ["Call Hierarchy" lsp-treemacs-call-hierarchy 3016 :visible (and (functionp 'lsp-treemacs-call-hierarchy) 3017 (lsp-feature? "textDocument/callHierarchy"))] 3018 ["Rename" lsp-rename 3019 :active (lsp-feature? "textDocument/rename")] 3020 "--" 3021 ("Session" 3022 ["View logs" lsp-workspace-show-log] 3023 ["Describe" lsp-describe-session] 3024 ["Shutdown" lsp-shutdown-workspace] 3025 ["Restart" lsp-restart-workspace]) 3026 ("Workspace Folders" 3027 ["Add" lsp-workspace-folders-add] 3028 ["Remove" lsp-workspace-folders-remove] 3029 ["Open" lsp-workspace-folders-open]) 3030 ("Toggle features" 3031 ["Lenses" lsp-lens-mode] 3032 ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode] 3033 ["Modeline code actions" lsp-modeline-code-actions-mode] 3034 ["Modeline diagnostics" lsp-modeline-diagnostics-mode]) 3035 "---" 3036 ("Debug" 3037 :active (bound-and-true-p dap-ui-mode) 3038 :filter ,(lambda (_) 3039 (and (boundp 'dap-ui-menu-items) 3040 (nthcdr 3 dap-ui-menu-items)))))) 3041 "Menu for lsp-mode.") 3042 3043 (defalias 'make-lsp-client 'make-lsp--client) 3044 3045 (cl-defstruct lsp--registered-capability 3046 (id "") 3047 (method " ") 3048 (options nil)) 3049 3050 ;; A ‘lsp--workspace’ object represents exactly one language server process. 3051 (cl-defstruct lsp--workspace 3052 ;; the `ewoc' object for displaying I/O to and from the server 3053 (ewoc nil) 3054 3055 ;; ‘server-capabilities’ is a hash table of the language server capabilities. 3056 ;; It is the hash table representation of a LSP ServerCapabilities structure; 3057 ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize. 3058 (server-capabilities nil) 3059 3060 ;; ‘registered-server-capabilities’ is a list of hash tables that represent 3061 ;; dynamically-registered Registration objects. See 3062 ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability. 3063 (registered-server-capabilities nil) 3064 3065 ;; ‘root’ is a directory name or a directory file name for the workspace 3066 ;; root. ‘lsp-mode’ passes this directory to the ‘initialize’ method of the 3067 ;; language server; see 3068 ;; https://microsoft.github.io/language-server-protocol/specification#initialize. 3069 (root nil) 3070 3071 ;; ‘client’ is the ‘lsp--client’ object associated with this workspace. 3072 (client nil) 3073 3074 ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It 3075 ;; used to derive the file path in `lsp--uri-to-path' when using tramp 3076 ;; connection. 3077 (host-root nil) 3078 3079 ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or 3080 ;; a network connection. ‘lsp-mode’ communicates with ‘proc’ using the 3081 ;; language server protocol. ‘proc’ corresponds to the COMMUNICATION-PROCESS 3082 ;; element of the return value of the client’s ‘get-root’ field, which see. 3083 (proc nil) 3084 3085 ;; ‘proc’ is a process object; it must represent a regular process, not a 3086 ;; pipe or network process. It represents the actual server process that 3087 ;; corresponds to this workspace. ‘cmd-proc’ corresponds to the 3088 ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’ 3089 ;; field, which see. 3090 (cmd-proc nil) 3091 3092 ;; ‘buffers’ is a list of buffers associated with this workspace. 3093 (buffers nil) 3094 3095 ;; if semantic tokens is enabled, `semantic-tokens-faces' contains 3096 ;; one face (or nil) for each token type supported by the language server. 3097 (semantic-tokens-faces nil) 3098 3099 ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces' 3100 ;; contains one face (or nil) for each modifier type supported by the language 3101 ;; server 3102 (semantic-tokens-modifier-faces nil) 3103 3104 ;; Extra client capabilities provided by third-party packages using 3105 ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME 3106 ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name, 3107 ;; and CAPS is either a plist of the client capabilities, or a function that 3108 ;; takes no argument and returns a plist of the client capabilities or nil. 3109 (extra-client-capabilities nil) 3110 3111 ;; Workspace status 3112 (status nil) 3113 3114 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3115 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3116 (metadata (make-hash-table :test 'equal)) 3117 3118 ;; contains all the file notification watches that have been created for the 3119 ;; current workspace in format filePath->file notification handle. 3120 (watches (make-hash-table :test 'equal)) 3121 3122 ;; list of workspace folders 3123 (workspace-folders nil) 3124 3125 ;; ‘last-id’ the last request id for the current workspace. 3126 (last-id 0) 3127 3128 ;; ‘status-string’ allows extensions to specify custom status string based on 3129 ;; the Language Server specific messages. 3130 (status-string nil) 3131 3132 ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it 3133 ;; was stopped). 3134 shutdown-action 3135 3136 ;; ‘diagnostics’ a hashmap with workspace diagnostics. 3137 (diagnostics (make-hash-table :test 'equal)) 3138 3139 ;; contains all the workDone progress tokens that have been created 3140 ;; for the current workspace. 3141 (work-done-tokens (make-hash-table :test 'equal))) 3142 3143 3144 (cl-defstruct lsp-session 3145 ;; contains the folders that are part of the current session 3146 folders 3147 ;; contains the folders that must not be imported in the current workspace. 3148 folders-blocklist 3149 ;; contains the list of folders that must be imported in a project in case of 3150 ;; multi root LSP server. 3151 (server-id->folders (make-hash-table :test 'equal)) 3152 ;; folder to list of the servers that are associated with the folder. 3153 (folder->servers (make-hash-table :test 'equal)) 3154 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3155 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3156 (metadata (make-hash-table :test 'equal))) 3157 3158 (defun lsp-workspace-status (status-string &optional workspace) 3159 "Set current workspace status to STATUS-STRING. 3160 If WORKSPACE is not specified defaults to lsp--cur-workspace." 3161 (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string)))) 3162 (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string))) 3163 3164 (defun lsp-session-set-metadata (key value &optional _workspace) 3165 "Associate KEY with VALUE in the WORKSPACE metadata. 3166 If WORKSPACE is not provided current workspace will be used." 3167 (puthash key value (lsp-session-metadata (lsp-session)))) 3168 3169 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata) 3170 3171 (defun lsp-session-get-metadata (key &optional _workspace) 3172 "Lookup KEY in WORKSPACE metadata. 3173 If WORKSPACE is not provided current workspace will be used." 3174 (gethash key (lsp-session-metadata (lsp-session)))) 3175 3176 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata) 3177 3178 (defun lsp-workspace-set-work-done-token (token value workspace) 3179 "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens." 3180 (puthash token value (lsp--workspace-work-done-tokens workspace))) 3181 3182 (defun lsp-workspace-get-work-done-token (token workspace) 3183 "Lookup TOKEN in the WORKSPACE work-done-tokens." 3184 (gethash token (lsp--workspace-work-done-tokens workspace))) 3185 3186 (defun lsp-workspace-rem-work-done-token (token workspace) 3187 "Remove TOKEN from the WORKSPACE work-done-tokens." 3188 (remhash token (lsp--workspace-work-done-tokens workspace))) 3189 3190 3191 (defun lsp--make-notification (method &optional params) 3192 "Create notification body for method METHOD and parameters PARAMS." 3193 (list :jsonrpc "2.0" :method method :params params)) 3194 3195 (defalias 'lsp--make-request 'lsp--make-notification) 3196 (defalias 'lsp-make-request 'lsp--make-notification) 3197 3198 (defun lsp--make-response (id result) 3199 "Create response for REQUEST with RESULT." 3200 `(:jsonrpc "2.0" :id ,id :result ,result)) 3201 3202 (defun lsp-make-notification (method &optional params) 3203 "Create notification body for method METHOD and parameters PARAMS." 3204 (lsp--make-notification method params)) 3205 3206 (defmacro lsp--json-serialize (params) 3207 (if (progn 3208 (require 'json) 3209 (fboundp 'json-serialize)) 3210 `(json-serialize ,params 3211 :null-object nil 3212 :false-object :json-false) 3213 `(let ((json-false :json-false)) 3214 (json-encode ,params)))) 3215 3216 (defun lsp--make-message (params) 3217 "Create a LSP message from PARAMS, after encoding it to a JSON string." 3218 (let ((body (lsp--json-serialize params))) 3219 (concat "Content-Length: " 3220 (number-to-string (1+ (string-bytes body))) 3221 "\r\n\r\n" 3222 body 3223 "\n"))) 3224 3225 (cl-defstruct lsp--log-entry timestamp process-time type method id body) 3226 3227 (defun lsp--make-log-entry (method id body type &optional process-time) 3228 "Create an outgoing log object from BODY with method METHOD and id ID. 3229 If ID is non-nil, then the body is assumed to be a notification. 3230 TYPE can either be `incoming' or `outgoing'" 3231 (cl-assert (memq type '(incoming-req outgoing-req incoming-notif 3232 outgoing-notif incoming-resp 3233 outgoing-resp))) 3234 (make-lsp--log-entry 3235 :timestamp (format-time-string "%I:%M:%S %p") 3236 :process-time process-time 3237 :method method 3238 :id id 3239 :type type 3240 :body body)) 3241 3242 (defun lsp--log-font-lock-json (body) 3243 "Font lock JSON BODY." 3244 (with-temp-buffer 3245 (insert body) 3246 ;; We set the temp buffer file-name extension to .json and call `set-auto-mode' 3247 ;; so the users configured json mode is used which could be 3248 ;; `json-mode', `json-ts-mode', `jsonian-mode', etc. 3249 (let ((buffer-file-name "lsp-log.json")) 3250 (delay-mode-hooks 3251 (set-auto-mode) 3252 (if (fboundp 'font-lock-ensure) 3253 (font-lock-ensure) 3254 (with-no-warnings 3255 (font-lock-fontify-buffer))))) 3256 (buffer-string))) 3257 3258 (defun lsp--log-entry-pp (entry) 3259 (cl-assert (lsp--log-entry-p entry)) 3260 (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time 3261 body) 3262 entry) 3263 (json-false :json-false) 3264 (json-encoding-pretty-print t) 3265 (str nil)) 3266 (setq str 3267 (concat (format "[Trace - %s] " timestamp) 3268 (pcase type 3269 ('incoming-req (format "Received request '%s - (%s)." method id)) 3270 ('outgoing-req (format "Sending request '%s - (%s)'." method id)) 3271 3272 ('incoming-notif (format "Received notification '%s'." method)) 3273 ('outgoing-notif (format "Sending notification '%s'." method)) 3274 3275 ('incoming-resp (format "Received response '%s - (%s)' in %dms." 3276 method id process-time)) 3277 ('outgoing-resp 3278 (format 3279 "Sending response '%s - (%s)'. Processing request took %dms" 3280 method id process-time))) 3281 "\n" 3282 (if (memq type '(incoming-resp ougoing-resp)) 3283 "Result: " 3284 "Params: ") 3285 (lsp--log-font-lock-json (json-encode body)) 3286 "\n\n\n")) 3287 (setq str (propertize str 'mouse-face 'highlight 'read-only t)) 3288 (insert str))) 3289 3290 (defvar-local lsp--log-io-ewoc nil) 3291 3292 (defun lsp--get-create-io-ewoc (workspace) 3293 (if (and (lsp--workspace-ewoc workspace) 3294 (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace)))) 3295 (lsp--workspace-ewoc workspace) 3296 (with-current-buffer (lsp--get-log-buffer-create workspace) 3297 (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode)) 3298 (setq-local window-point-insertion-type t) 3299 (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t)) 3300 (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc)) 3301 (lsp--workspace-ewoc workspace))) 3302 3303 (defun lsp--ewoc-count (ewoc) 3304 (let* ((count 0) 3305 (count-fn (lambda (_) (setq count (1+ count))))) 3306 (ewoc-map count-fn ewoc) 3307 count)) 3308 3309 (defun lsp--log-entry-new (entry workspace) 3310 (let* ((ewoc (lsp--get-create-io-ewoc workspace)) 3311 (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc))) 3312 (node (if (or (eq lsp-io-messages-max t) 3313 (>= lsp-io-messages-max count)) 3314 nil 3315 (ewoc-nth ewoc (1- lsp-io-messages-max)))) 3316 (prev nil) 3317 (inhibit-read-only t)) 3318 (while node 3319 (setq prev (ewoc-prev ewoc node)) 3320 (ewoc-delete ewoc node) 3321 (setq node prev)) 3322 (ewoc-enter-last ewoc entry))) 3323 3324 (defun lsp--send-notification (body) 3325 "Send BODY as a notification to the language server." 3326 (lsp-foreach-workspace 3327 (when (lsp--log-io-p (plist-get body :method)) 3328 (lsp--log-entry-new (lsp--make-log-entry 3329 (plist-get body :method) 3330 nil (plist-get body :params) 'outgoing-notif) 3331 lsp--cur-workspace)) 3332 (lsp--send-no-wait body 3333 (lsp--workspace-proc lsp--cur-workspace)))) 3334 3335 (defalias 'lsp-send-notification 'lsp--send-notification) 3336 3337 (defun lsp-notify (method params) 3338 "Send notification METHOD with PARAMS." 3339 (lsp--send-notification (lsp--make-notification method params))) 3340 3341 (defun lsp--cur-workspace-check () 3342 "Check whether buffer lsp workspace(s) are set." 3343 (cl-assert (lsp-workspaces) nil 3344 "No language server(s) is associated with this buffer.")) 3345 3346 (defun lsp--send-request (body &optional no-wait no-merge) 3347 "Send BODY as a request to the language server, get the response. 3348 If NO-WAIT is non-nil, don't synchronously wait for a response. 3349 If NO-MERGE is non-nil, don't merge the results but return an 3350 alist mapping workspace->result." 3351 (lsp-request (plist-get body :method) 3352 (plist-get body :params) 3353 :no-wait no-wait 3354 :no-merge no-merge)) 3355 3356 (defalias 'lsp-send-request 'lsp--send-request 3357 "Send BODY as a request to the language server and return the response 3358 synchronously. 3359 \n(fn BODY)") 3360 3361 (cl-defun lsp-request (method params &key no-wait no-merge) 3362 "Send request METHOD with PARAMS. 3363 If NO-MERGE is non-nil, don't merge the results but return alist 3364 workspace->result. 3365 If NO-WAIT is non-nil send the request as notification." 3366 (if no-wait 3367 (lsp-notify method params) 3368 (let* ((send-time (float-time)) 3369 ;; max time by which we must get a response 3370 (expected-time 3371 (and 3372 lsp-response-timeout 3373 (+ send-time lsp-response-timeout))) 3374 resp-result resp-error done?) 3375 (unwind-protect 3376 (progn 3377 (lsp-request-async method params 3378 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3379 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3380 :no-merge no-merge 3381 :mode 'detached 3382 :cancel-token :sync-request) 3383 (while (not (or resp-error resp-result)) 3384 (if (functionp 'json-rpc-connection) 3385 (catch 'lsp-done (sit-for 0.01)) 3386 (catch 'lsp-done 3387 (accept-process-output 3388 nil 3389 (if expected-time (- expected-time send-time) 1)))) 3390 (setq send-time (float-time)) 3391 (when (and expected-time (< expected-time send-time)) 3392 (error "Timeout while waiting for response. Method: %s" method))) 3393 (setq done? t) 3394 (cond 3395 ((eq resp-result :finished) nil) 3396 (resp-result resp-result) 3397 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3398 ((lsp-json-error? (cl-first resp-error)) 3399 (error (lsp:json-error-message (cl-first resp-error)))))) 3400 (unless done? 3401 (lsp-cancel-request-by-token :sync-request)))))) 3402 3403 (cl-defun lsp-request-while-no-input (method params) 3404 "Send request METHOD with PARAMS and waits until there is no input. 3405 Return same value as `lsp--while-no-input' and respecting `non-essential'." 3406 (if (or non-essential (not lsp-request-while-no-input-may-block)) 3407 (let* ((send-time (float-time)) 3408 ;; max time by which we must get a response 3409 (expected-time 3410 (and 3411 lsp-response-timeout 3412 (+ send-time lsp-response-timeout))) 3413 resp-result resp-error done?) 3414 (unwind-protect 3415 (progn 3416 (lsp-request-async method params 3417 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3418 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3419 :mode 'detached 3420 :cancel-token :sync-request) 3421 (while (not (or resp-error resp-result (input-pending-p))) 3422 (catch 'lsp-done 3423 (sit-for 3424 (if expected-time (- expected-time send-time) 1))) 3425 (setq send-time (float-time)) 3426 (when (and expected-time (< expected-time send-time)) 3427 (error "Timeout while waiting for response. Method: %s" method))) 3428 (setq done? (or resp-error resp-result)) 3429 (cond 3430 ((eq resp-result :finished) nil) 3431 (resp-result resp-result) 3432 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3433 ((lsp-json-error? (cl-first resp-error)) 3434 (error (lsp:json-error-message (cl-first resp-error)))))) 3435 (unless done? 3436 (lsp-cancel-request-by-token :sync-request)) 3437 (when (and (input-pending-p) lsp--throw-on-input) 3438 (throw 'input :interrupted)))) 3439 (lsp-request method params))) 3440 3441 (defvar lsp--cancelable-requests (ht)) 3442 3443 (cl-defun lsp-request-async (method params callback 3444 &key mode error-handler cancel-handler no-merge cancel-token) 3445 "Send METHOD with PARAMS as a request to the language server. 3446 Call CALLBACK with the response received from the server 3447 asynchronously. 3448 MODE determines when the callback will be called depending on the 3449 condition of the original buffer. It could be: 3450 - `detached' which means that the callback will be executed no 3451 matter what has happened to the buffer. 3452 - `alive' - the callback will be executed only if the buffer from 3453 which the call was executed is still alive. 3454 - `current' the callback will be executed only if the original buffer 3455 is still selected. 3456 - `tick' - the callback will be executed only if the buffer was not modified. 3457 - `unchanged' - the callback will be executed only if the buffer hasn't 3458 changed and if the buffer is not modified. 3459 3460 ERROR-HANDLER will be called in case the request has failed. 3461 CANCEL-HANDLER will be called in case the request is being canceled. 3462 If NO-MERGE is non-nil, don't merge the results but return alist 3463 workspace->result. 3464 CANCEL-TOKEN is the token that can be used to cancel request." 3465 (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params) 3466 callback mode error-handler cancel-handler no-merge cancel-token)) 3467 3468 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback) 3469 (lambda (&rest _) 3470 (unless (and (equal 'post-command-hook hook) 3471 (equal (current-buffer) buf)) 3472 (lsp--request-cleanup-hooks id) 3473 (with-lsp-workspaces workspaces 3474 (lsp--cancel-request id) 3475 (when cancel-callback (funcall cancel-callback))) 3476 (lsp-log "Cancelling %s(%s) in hook %s" method id hook)))) 3477 3478 (defun lsp--create-async-callback 3479 (callback method no-merge workspaces) 3480 "Create async handler expecting COUNT results, merge them and call CALLBACK. 3481 MODE determines when the callback will be called depending on the 3482 condition of the original buffer. METHOD is the invoked method. 3483 If NO-MERGE is non-nil, don't merge the results but return alist 3484 workspace->result. ID is the request id." 3485 (let (results errors) 3486 (lambda (result) 3487 (push (cons lsp--cur-workspace result) 3488 (if (eq result :error) errors results)) 3489 (when (and (not (eq (length errors) (length workspaces))) 3490 (eq (+ (length errors) (length results)) (length workspaces))) 3491 (funcall callback 3492 (if no-merge 3493 results 3494 (lsp--merge-results (-map #'cl-rest results) method))))))) 3495 3496 (defcustom lsp-default-create-error-handler-fn nil 3497 "Default error handler customization. 3498 Handler should give METHOD as argument and return function of one argument 3499 ERROR." 3500 :type 'function 3501 :group 'lsp-mode 3502 :package-version '(lsp-mode . "9.0.0")) 3503 3504 (defun lsp--create-default-error-handler (method) 3505 "Default error handler. 3506 METHOD is the executed method." 3507 (if lsp-default-create-error-handler-fn 3508 (funcall lsp-default-create-error-handler-fn method) 3509 (lambda (error) 3510 (lsp--warn "%s" (or (lsp--error-string error) 3511 (format "%s Request has failed" method)))))) 3512 3513 (defvar lsp--request-cleanup-hooks (ht)) 3514 3515 (defun lsp--request-cleanup-hooks (request-id) 3516 (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks))) 3517 (funcall cleanup-function) 3518 (remhash request-id lsp--request-cleanup-hooks))) 3519 3520 (defun lsp-cancel-request-by-token (cancel-token) 3521 "Cancel request using CANCEL-TOKEN." 3522 (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests)) 3523 (with-lsp-workspaces workspaces 3524 (lsp--cancel-request request-id)) 3525 (remhash cancel-token lsp--cancelable-requests) 3526 (lsp--request-cleanup-hooks request-id))) 3527 3528 (defun lsp--send-request-async (body callback 3529 &optional mode error-callback cancel-callback 3530 no-merge cancel-token) 3531 "Send BODY as a request to the language server. 3532 Call CALLBACK with the response received from the server 3533 asynchronously. 3534 MODE determines when the callback will be called depending on the 3535 condition of the original buffer. It could be: 3536 - `detached' which means that the callback will be executed no 3537 matter what has happened to the buffer. 3538 - `alive' - the callback will be executed only if the buffer from 3539 which the call was executed is still alive. 3540 - `current' the callback will be executed only if the original buffer 3541 is still selected. 3542 - `tick' - the callback will be executed only if the buffer was not modified. 3543 - `unchanged' - the callback will be executed only if the buffer hasn't 3544 changed and if the buffer is not modified. 3545 3546 ERROR-CALLBACK will be called in case the request has failed. 3547 CANCEL-CALLBACK will be called in case the request is being canceled. 3548 If NO-MERGE is non-nil, don't merge the results but return alist 3549 workspace->result. 3550 CANCEL-TOKEN is the token that can be used to cancel request." 3551 (when cancel-token 3552 (lsp-cancel-request-by-token cancel-token)) 3553 3554 (if-let ((target-workspaces (lsp--find-workspaces-for body))) 3555 (let* ((start-time (current-time)) 3556 (method (plist-get body :method)) 3557 (id (cl-incf lsp-last-id)) 3558 (buf (current-buffer)) 3559 (cancel-callback (when cancel-callback 3560 (pcase mode 3561 ((or 'alive 'tick 'unchanged) 3562 (lambda () 3563 (with-current-buffer buf 3564 (funcall cancel-callback)))) 3565 (_ cancel-callback)))) 3566 ;; calculate what are the (hook . local) pairs which will cancel 3567 ;; the request 3568 (hooks (pcase mode 3569 ('alive '((kill-buffer-hook . t))) 3570 ('tick '((kill-buffer-hook . t) (after-change-functions . t))) 3571 ('unchanged '((after-change-functions . t) (post-command-hook . nil))) 3572 ('current '((post-command-hook . nil))))) 3573 ;; note: lambdas in emacs can be compared but we should make sure 3574 ;; that all of the captured arguments are the same - in our case 3575 ;; `lsp--create-request-cancel' will return the same lambda when 3576 ;; called with the same params. 3577 (cleanup-hooks 3578 (lambda () (mapc 3579 (-lambda ((hook . local)) 3580 (if local 3581 (when (buffer-live-p buf) 3582 (with-current-buffer buf 3583 (remove-hook hook 3584 (lsp--create-request-cancel 3585 id target-workspaces hook buf method cancel-callback) 3586 t))) 3587 (remove-hook hook (lsp--create-request-cancel 3588 id target-workspaces hook buf method cancel-callback)))) 3589 hooks) 3590 (remhash cancel-token lsp--cancelable-requests))) 3591 (callback (pcase mode 3592 ((or 'alive 'tick 'unchanged) (lambda (&rest args) 3593 (with-current-buffer buf 3594 (apply callback args)))) 3595 (_ callback))) 3596 (callback (lsp--create-async-callback callback 3597 method 3598 no-merge 3599 target-workspaces)) 3600 (callback (lambda (result) 3601 (lsp--request-cleanup-hooks id) 3602 (funcall callback result))) 3603 (error-callback (lsp--create-async-callback 3604 (or error-callback 3605 (lsp--create-default-error-handler method)) 3606 method 3607 nil 3608 target-workspaces)) 3609 (error-callback (lambda (error) 3610 (funcall callback :error) 3611 (lsp--request-cleanup-hooks id) 3612 (funcall error-callback error))) 3613 (body (plist-put body :id id))) 3614 3615 ;; cancel request in any of the hooks 3616 (mapc (-lambda ((hook . local)) 3617 (add-hook hook 3618 (lsp--create-request-cancel 3619 id target-workspaces hook buf method cancel-callback) 3620 nil local)) 3621 hooks) 3622 (puthash id cleanup-hooks lsp--request-cleanup-hooks) 3623 3624 (setq lsp--last-active-workspaces target-workspaces) 3625 3626 (when cancel-token 3627 (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests)) 3628 3629 (seq-doseq (workspace target-workspaces) 3630 (when (lsp--log-io-p method) 3631 (lsp--log-entry-new (lsp--make-log-entry method id 3632 (plist-get body :params) 3633 'outgoing-req) 3634 workspace)) 3635 (puthash id 3636 (list callback error-callback method start-time (current-time)) 3637 (-> workspace 3638 (lsp--workspace-client) 3639 (lsp--client-response-handlers))) 3640 (lsp--send-no-wait body (lsp--workspace-proc workspace))) 3641 body) 3642 (error "The connected server(s) does not support method %s. 3643 To find out what capabilities support your server use `M-x lsp-describe-session' 3644 and expand the capabilities section" 3645 (plist-get body :method)))) 3646 3647 ;; deprecated, use lsp-request-async. 3648 (defalias 'lsp-send-request-async 'lsp--send-request-async) 3649 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1") 3650 3651 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any 3652 ;; pending language servers. 3653 (add-hook 'kill-emacs-hook #'lsp--global-teardown) 3654 3655 (defun lsp--global-teardown () 3656 "Unload working workspaces." 3657 (lsp-foreach-workspace (lsp--shutdown-workspace))) 3658 3659 (defun lsp--shutdown-workspace (&optional restart) 3660 "Shut down the language server process for ‘lsp--cur-workspace’." 3661 (with-demoted-errors "LSP error: %S" 3662 (let ((lsp-response-timeout 0.5)) 3663 (condition-case err 3664 (lsp-request "shutdown" nil) 3665 (error (lsp--error "%s" err)))) 3666 (lsp-notify "exit" nil)) 3667 (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown)) 3668 (lsp--uninitialize-workspace)) 3669 3670 (defcustom lsp-inlay-hint-enable nil 3671 "If non-nil it will enable inlay hints." 3672 :type 'boolean 3673 :group 'lsp-mode 3674 :package-version '(lsp-mode . "9.0.0")) 3675 3676 (defun lsp--uninitialize-workspace () 3677 "Cleanup buffer state. 3678 When a workspace is shut down, by request or from just 3679 disappearing, unset all the variables related to it." 3680 (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace] 3681 (lsp-process-kill cmd-proc) 3682 (mapc (lambda (buf) 3683 (when (lsp-buffer-live-p buf) 3684 (lsp-with-current-buffer buf 3685 (lsp-managed-mode -1)))) 3686 buffers) 3687 (lsp-diagnostics--workspace-cleanup lsp--cur-workspace))) 3688 3689 (defun lsp--client-capabilities (&optional custom-capabilities) 3690 "Return the client capabilities appending CUSTOM-CAPABILITIES." 3691 (append 3692 `((general . ((positionEncodings . ["utf-32", "utf-16"]))) 3693 (workspace . ((workspaceEdit . ((documentChanges . t) 3694 (resourceOperations . ["create" "rename" "delete"]))) 3695 (applyEdit . t) 3696 (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))))) 3697 (executeCommand . ((dynamicRegistration . :json-false))) 3698 ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t))))) 3699 (workspaceFolders . t) 3700 (configuration . t) 3701 ,@(when lsp-semantic-tokens-enable 3702 `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests) 3703 lsp-semantic-tokens-honor-refresh-requests) 3704 :json-false)))))) 3705 ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t))))) 3706 ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false))))) 3707 (fileOperations . ((didCreate . :json-false) 3708 (willCreate . :json-false) 3709 (didRename . t) 3710 (willRename . t) 3711 (didDelete . :json-false) 3712 (willDelete . :json-false))))) 3713 (textDocument . ((declaration . ((dynamicRegistration . t) 3714 (linkSupport . t))) 3715 (definition . ((dynamicRegistration . t) 3716 (linkSupport . t))) 3717 (references . ((dynamicRegistration . t))) 3718 (implementation . ((dynamicRegistration . t) 3719 (linkSupport . t))) 3720 (typeDefinition . ((dynamicRegistration . t) 3721 (linkSupport . t))) 3722 (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t))) 3723 (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))) 3724 (hierarchicalDocumentSymbolSupport . t))) 3725 (formatting . ((dynamicRegistration . t))) 3726 (rangeFormatting . ((dynamicRegistration . t))) 3727 (onTypeFormatting . ((dynamicRegistration . t))) 3728 ,@(when (and lsp-semantic-tokens-enable 3729 (functionp 'lsp--semantic-tokens-capabilities)) 3730 (lsp--semantic-tokens-capabilities)) 3731 (rename . ((dynamicRegistration . t) (prepareSupport . t))) 3732 (codeAction . ((dynamicRegistration . t) 3733 (isPreferredSupport . t) 3734 (codeActionLiteralSupport . ((codeActionKind . ((valueSet . ["" 3735 "quickfix" 3736 "refactor" 3737 "refactor.extract" 3738 "refactor.inline" 3739 "refactor.rewrite" 3740 "source" 3741 "source.organizeImports"]))))) 3742 (resolveSupport . ((properties . ["edit" "command"]))) 3743 (dataSupport . t))) 3744 (completion . ((completionItem . ((snippetSupport . ,(cond 3745 ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode))) 3746 (lsp--warn (concat 3747 "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. " 3748 "You must either install yasnippet, or disable snippet support.")) 3749 :json-false) 3750 (lsp-enable-snippet t) 3751 (t :json-false))) 3752 (documentationFormat . ["markdown" "plaintext"]) 3753 ;; Remove this after jdtls support resolveSupport 3754 (resolveAdditionalTextEditsSupport . t) 3755 (insertReplaceSupport . t) 3756 (deprecatedSupport . t) 3757 (resolveSupport 3758 . ((properties . ["documentation" 3759 "detail" 3760 "additionalTextEdits" 3761 "command"]))) 3762 (insertTextModeSupport . ((valueSet . [1 2]))))) 3763 (contextSupport . t) 3764 (dynamicRegistration . t))) 3765 (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t))))) 3766 (dynamicRegistration . t))) 3767 (documentLink . ((dynamicRegistration . t) 3768 (tooltipSupport . t))) 3769 (hover . ((contentFormat . ["markdown" "plaintext"]) 3770 (dynamicRegistration . t))) 3771 ,@(when lsp-enable-folding 3772 `((foldingRange . ((dynamicRegistration . t) 3773 ,@(when lsp-folding-range-limit 3774 `((rangeLimit . ,lsp-folding-range-limit))) 3775 ,@(when lsp-folding-line-folding-only 3776 `((lineFoldingOnly . t))))))) 3777 (selectionRange . ((dynamicRegistration . t))) 3778 (callHierarchy . ((dynamicRegistration . :json-false))) 3779 (typeHierarchy . ((dynamicRegistration . t))) 3780 (publishDiagnostics . ((relatedInformation . t) 3781 (tagSupport . ((valueSet . [1 2]))) 3782 (versionSupport . t))) 3783 (diagnostic . ((dynamicRegistration . :json-false) 3784 (relatedDocumentSupport . :json-false))) 3785 (linkedEditingRange . ((dynamicRegistration . t))))) 3786 (window . ((workDoneProgress . t) 3787 (showDocument . ((support . t)))))) 3788 custom-capabilities)) 3789 3790 (defun lsp-find-roots-for-workspace (workspace session) 3791 "Get all roots for the WORKSPACE." 3792 (-filter #'identity (ht-map (lambda (folder workspaces) 3793 (when (-contains? workspaces workspace) 3794 folder)) 3795 (lsp-session-folder->servers session)))) 3796 3797 (defun lsp-session-watches (&optional session) 3798 "Get watches created for SESSION." 3799 (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session)))) 3800 (-let [res (make-hash-table :test 'equal)] 3801 (puthash "__watches" res (lsp-session-metadata (or session (lsp-session)))) 3802 res))) 3803 3804 (defun lsp--file-process-event (session root-folder event) 3805 "Process file event." 3806 (let* ((changed-file (cl-third event)) 3807 (rel-changed-file (f-relative changed-file root-folder)) 3808 (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type)) 3809 (bit-position (1- event-numeric-kind)) 3810 (watch-bit (ash 1 bit-position))) 3811 (->> 3812 session 3813 lsp-session-folder->servers 3814 (gethash root-folder) 3815 (seq-do (lambda (workspace) 3816 (when (->> 3817 workspace 3818 lsp--workspace-registered-server-capabilities 3819 (-any? 3820 (lambda (capability) 3821 (and 3822 (equal (lsp--registered-capability-method capability) 3823 "workspace/didChangeWatchedFiles") 3824 (->> 3825 capability 3826 lsp--registered-capability-options 3827 (lsp:did-change-watched-files-registration-options-watchers) 3828 (seq-find 3829 (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp)) 3830 (when (or (null kind?) 3831 (> (logand kind? watch-bit) 0)) 3832 (-let [regexes (or cached-regexp 3833 (let ((regexp (lsp-glob-to-regexps glob-pattern))) 3834 (lsp-put fs-watcher :_cachedRegexp regexp) 3835 regexp))] 3836 (-any? (lambda (re) 3837 (or (string-match re changed-file) 3838 (string-match re rel-changed-file))) 3839 regexes)))))))))) 3840 (with-lsp-workspace workspace 3841 (lsp-notify 3842 "workspace/didChangeWatchedFiles" 3843 `((changes . [((type . ,event-numeric-kind) 3844 (uri . ,(lsp--path-to-uri changed-file)))])))))))))) 3845 3846 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?)) 3847 "Register capability REG." 3848 (when (and lsp-enable-file-watchers 3849 (equal method "workspace/didChangeWatchedFiles")) 3850 (-let* ((created-watches (lsp-session-watches (lsp-session))) 3851 (root-folders (cl-set-difference 3852 (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session)) 3853 (ht-keys created-watches)))) 3854 ;; create watch for each root folder without such 3855 (dolist (folder root-folders) 3856 (let* ((watch (make-lsp-watch :root-directory folder)) 3857 (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder)) 3858 (ignored-files-regex-list (car ignored-things)) 3859 (ignored-directories-regex-list (cadr ignored-things))) 3860 (puthash folder watch created-watches) 3861 (lsp-watch-root-folder (file-truename folder) 3862 (-partial #'lsp--file-process-event (lsp-session) folder) 3863 ignored-files-regex-list 3864 ignored-directories-regex-list 3865 watch 3866 t))))) 3867 3868 (push 3869 (make-lsp--registered-capability :id id :method method :options register-options?) 3870 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3871 3872 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body) 3873 "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to 3874 access dir-local variables." 3875 (declare (indent 1) (debug t)) 3876 `(with-temp-buffer 3877 ;; Set the buffer's name to something under the root so that we can hack the local variables 3878 ;; This file doesn't need to exist and will not be created due to this. 3879 (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root))) 3880 (hack-local-variables) 3881 (prog1 ,@body 3882 (setq-local buffer-file-name nil)))) 3883 3884 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root) 3885 "Return a list of the form 3886 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given 3887 WORKSPACE-ROOT." 3888 ;; The intent of this function is to provide per-root workspace-level customization of the 3889 ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables. 3890 (lsp--with-workspace-temp-buffer workspace-root 3891 (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories)))) 3892 3893 3894 (defun lsp--cleanup-hanging-watches () 3895 "Cleanup watches in case there are no more workspaces that are interested 3896 in that particular folder." 3897 (let* ((session (lsp-session)) 3898 (watches (lsp-session-watches session))) 3899 (dolist (watched-folder (ht-keys watches)) 3900 (when (-none? (lambda (workspace) 3901 (with-lsp-workspace workspace 3902 (lsp--registered-capability "workspace/didChangeWatchedFiles"))) 3903 (gethash watched-folder (lsp-session-folder->servers (lsp-session)))) 3904 (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder) 3905 (lsp-kill-watch (gethash watched-folder watches)) 3906 (remhash watched-folder watches))))) 3907 3908 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method)) 3909 "Unregister capability UNREG." 3910 (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace) 3911 (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id)) 3912 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3913 (when (equal method "workspace/didChangeWatchedFiles") 3914 (lsp--cleanup-hanging-watches))) 3915 3916 (defun lsp--server-capabilities () 3917 "Return the capabilities of the language server associated with the buffer." 3918 (->> (lsp-workspaces) 3919 (-keep #'lsp--workspace-server-capabilities) 3920 (apply #'lsp-merge))) 3921 3922 (defun lsp--send-open-close-p () 3923 "Return whether open and close notifications should be sent to the server." 3924 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3925 (or (memq sync '(1 2)) 3926 (lsp:text-document-sync-options-open-close? sync)))) 3927 3928 (defun lsp--send-will-save-p () 3929 "Return whether willSave notifications should be sent to the server." 3930 (-> (lsp--server-capabilities) 3931 (lsp:server-capabilities-text-document-sync?) 3932 (lsp:text-document-sync-options-will-save?))) 3933 3934 (defun lsp--send-will-save-wait-until-p () 3935 "Return whether willSaveWaitUntil notifications should be sent to the server." 3936 (-> (lsp--server-capabilities) 3937 (lsp:server-capabilities-text-document-sync?) 3938 (lsp:text-document-sync-options-will-save-wait-until?))) 3939 3940 (defun lsp--send-did-save-p () 3941 "Return whether didSave notifications should be sent to the server." 3942 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3943 (or (memq sync '(1 2)) 3944 (lsp:text-document-sync-options-save? sync)))) 3945 3946 (defun lsp--save-include-text-p () 3947 "Return whether save notifications should include the text document's contents." 3948 (->> (lsp--server-capabilities) 3949 (lsp:server-capabilities-text-document-sync?) 3950 (lsp:text-document-sync-options-save?) 3951 (lsp:text-document-save-registration-options-include-text?))) 3952 3953 (defun lsp--send-will-rename-files-p (path) 3954 "Return whether willRenameFiles request should be sent to the server. 3955 If any filters, checks if it applies for PATH." 3956 (let* ((will-rename (-> (lsp--server-capabilities) 3957 (lsp:server-capabilities-workspace?) 3958 (lsp:workspace-server-capabilities-file-operations?) 3959 (lsp:workspace-file-operations-will-rename?))) 3960 (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list))) 3961 (and will-rename 3962 (or (seq-empty-p filters) 3963 (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob))) 3964 (-let [regexes (lsp-glob-to-regexps glob)] 3965 (and (or (not scheme?) 3966 (string-prefix-p scheme? (lsp--path-to-uri path))) 3967 (-any? (lambda (re) 3968 (string-match re path)) 3969 regexes)))) 3970 filters))))) 3971 3972 (defun lsp--send-did-rename-files-p () 3973 "Return whether didRenameFiles notification should be sent to the server." 3974 (-> (lsp--server-capabilities) 3975 (lsp:server-capabilities-workspace?) 3976 (lsp:workspace-server-capabilities-file-operations?) 3977 (lsp:workspace-file-operations-did-rename?))) 3978 3979 (declare-function project-roots "ext:project" (project) t) 3980 (declare-function project-root "ext:project" (project) t) 3981 3982 (defun lsp--suggest-project-root () 3983 "Get project root." 3984 (or 3985 (when (fboundp 'projectile-project-root) 3986 (condition-case nil 3987 (projectile-project-root) 3988 (error nil))) 3989 (when (fboundp 'project-current) 3990 (when-let ((project (project-current))) 3991 (if (fboundp 'project-root) 3992 (project-root project) 3993 (car (with-no-warnings 3994 (project-roots project)))))) 3995 default-directory)) 3996 3997 (defun lsp--read-from-file (file) 3998 "Read FILE content." 3999 (when (file-exists-p file) 4000 (cl-first (read-from-string (f-read-text file 'utf-8))))) 4001 4002 (defun lsp--persist (file-name to-persist) 4003 "Persist TO-PERSIST in FILE-NAME. 4004 4005 This function creates the parent directories if they don't exist 4006 yet." 4007 (let ((print-length nil) 4008 (print-level nil)) 4009 ;; Create all parent directories: 4010 (make-directory (f-parent file-name) t) 4011 (f-write-text (prin1-to-string to-persist) 'utf-8 file-name))) 4012 4013 (defun lsp-workspace-folders-add (project-root) 4014 "Add PROJECT-ROOT to the list of workspace folders." 4015 (interactive 4016 (list (read-directory-name "Select folder to add: " 4017 (or (lsp--suggest-project-root) default-directory) nil t))) 4018 (cl-pushnew (lsp-f-canonical project-root) 4019 (lsp-session-folders (lsp-session)) :test 'equal) 4020 (lsp--persist-session (lsp-session)) 4021 4022 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)) 4023 4024 (defun lsp-workspace-folders-remove (project-root) 4025 "Remove PROJECT-ROOT from the list of workspace folders." 4026 (interactive (list (completing-read "Select folder to remove: " 4027 (lsp-session-folders (lsp-session)) 4028 nil t nil nil 4029 (lsp-find-session-folder (lsp-session) default-directory)))) 4030 4031 (setq project-root (lsp-f-canonical project-root)) 4032 4033 ;; send remove folder to each multiroot workspace associated with the folder 4034 (dolist (wks (->> (lsp-session) 4035 (lsp-session-folder->servers) 4036 (gethash project-root) 4037 (--filter (lsp--client-multi-root (lsp--workspace-client it))))) 4038 (with-lsp-workspace wks 4039 (lsp-notify "workspace/didChangeWorkspaceFolders" 4040 (lsp-make-did-change-workspace-folders-params 4041 :event (lsp-make-workspace-folders-change-event 4042 :removed (vector (lsp-make-workspace-folder 4043 :uri (lsp--path-to-uri project-root) 4044 :name (f-filename project-root))) 4045 :added []))))) 4046 4047 ;; turn off servers in the removed directory 4048 (let* ((session (lsp-session)) 4049 (folder->servers (lsp-session-folder->servers session)) 4050 (server-id->folders (lsp-session-server-id->folders session)) 4051 (workspaces (gethash project-root folder->servers))) 4052 4053 (remhash project-root folder->servers) 4054 4055 ;; turn off the servers without root folders 4056 (dolist (workspace workspaces) 4057 (when (--none? (-contains? it workspace) (ht-values folder->servers)) 4058 (lsp--info "Shutdown %s since folder %s is removed..." 4059 (lsp--workspace-print workspace) project-root) 4060 (with-lsp-workspace workspace (lsp--shutdown-workspace)))) 4061 4062 (setf (lsp-session-folders session) 4063 (-remove-item project-root (lsp-session-folders session))) 4064 4065 (ht-aeach (puthash key 4066 (-remove-item project-root value) 4067 server-id->folders) 4068 server-id->folders) 4069 (lsp--persist-session (lsp-session))) 4070 4071 (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root))) 4072 4073 (defun lsp-workspace-blocklist-remove (project-root) 4074 "Remove PROJECT-ROOT from the workspace blocklist." 4075 (interactive (list (completing-read "Select folder to remove:" 4076 (lsp-session-folders-blocklist (lsp-session)) 4077 nil t))) 4078 (setf (lsp-session-folders-blocklist (lsp-session)) 4079 (delete project-root 4080 (lsp-session-folders-blocklist (lsp-session)))) 4081 (lsp--persist-session (lsp-session))) 4082 4083 (define-obsolete-function-alias 'lsp-workspace-folders-switch 4084 'lsp-workspace-folders-open "lsp-mode 6.1") 4085 4086 (defun lsp-workspace-folders-open (project-root) 4087 "Open the directory located at PROJECT-ROOT" 4088 (interactive (list (completing-read "Open folder: " 4089 (lsp-session-folders (lsp-session)) 4090 nil t))) 4091 (find-file project-root)) 4092 4093 (defun lsp--maybe-enable-signature-help (trigger-characters) 4094 (let ((ch last-command-event)) 4095 (when (cl-find ch trigger-characters :key #'string-to-char) 4096 (lsp-signature-activate)))) 4097 4098 (defun lsp--on-type-formatting-handler-create () 4099 (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" ))) 4100 (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character? 4101 :first-trigger-character) provider] 4102 (lambda () 4103 (lsp--on-type-formatting first-trigger-character 4104 more-trigger-character?))))) 4105 4106 (defun lsp--update-on-type-formatting-hook (&optional cleanup?) 4107 (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create))) 4108 (cond 4109 ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?)) 4110 (add-hook 'post-self-insert-hook on-type-formatting-handler nil t)) 4111 ((or cleanup? 4112 (not lsp-enable-on-type-formatting)) 4113 (remove-hook 'post-self-insert-hook on-type-formatting-handler t))))) 4114 4115 (defun lsp--signature-help-handler-create () 4116 (-when-let ((&SignatureHelpOptions? :trigger-characters?) 4117 (lsp--capability-for-method "textDocument/signatureHelp")) 4118 (lambda () 4119 (lsp--maybe-enable-signature-help trigger-characters?)))) 4120 4121 (defun lsp--update-signature-help-hook (&optional cleanup?) 4122 (let ((signature-help-handler (lsp--signature-help-handler-create))) 4123 (cond 4124 ((and (or (equal lsp-signature-auto-activate t) 4125 (memq :on-trigger-char lsp-signature-auto-activate)) 4126 signature-help-handler 4127 (not cleanup?)) 4128 (add-hook 'post-self-insert-hook signature-help-handler nil t)) 4129 4130 ((or cleanup? 4131 (not (or (equal lsp-signature-auto-activate t) 4132 (memq :on-trigger-char lsp-signature-auto-activate)))) 4133 (remove-hook 'post-self-insert-hook signature-help-handler t))))) 4134 4135 (defun lsp--after-set-visited-file-name () 4136 (lsp-disconnect) 4137 (lsp)) 4138 4139 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27 4140 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099 4141 (defvar eldoc-documentation-default) ; CI 4142 (when (< emacs-major-version 28) 4143 (unless (boundp 'eldoc-documentation-functions) 4144 (load "eldoc" nil 'nomessage)) 4145 (when (memq (default-value 'eldoc-documentation-function) '(nil ignore)) 4146 ;; actually `eldoc-documentation-strategy', but CI was failing 4147 (setq-default eldoc-documentation-function 'eldoc-documentation-default))) 4148 4149 (define-minor-mode lsp-managed-mode 4150 "Mode for source buffers managed by lsp-mode." 4151 :lighter nil 4152 (cond 4153 (lsp-managed-mode 4154 (when (lsp-feature? "textDocument/hover") 4155 (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t) 4156 (eldoc-mode 1)) 4157 4158 (add-hook 'after-change-functions #'lsp-on-change nil t) 4159 (add-hook 'after-revert-hook #'lsp-on-revert nil t) 4160 (add-hook 'after-save-hook #'lsp-on-save nil t) 4161 (add-hook 'auto-save-hook #'lsp--on-auto-save nil t) 4162 (add-hook 'before-change-functions #'lsp-before-change nil t) 4163 (add-hook 'before-save-hook #'lsp--before-save nil t) 4164 (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t) 4165 (add-hook 'post-command-hook #'lsp--post-command nil t) 4166 4167 (lsp--update-on-type-formatting-hook) 4168 (lsp--update-signature-help-hook) 4169 4170 (when lsp-enable-xref 4171 (add-hook 'xref-backend-functions #'lsp--xref-backend nil t)) 4172 4173 (lsp-configure-buffer) 4174 4175 ;; make sure we turn off lsp-mode in case major mode changes, because major 4176 ;; mode change will wipe the buffer locals. 4177 (add-hook 'change-major-mode-hook #'lsp-disconnect nil t) 4178 (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t) 4179 4180 (let ((buffer (lsp-current-buffer))) 4181 (run-with-idle-timer 4182 0.0 nil 4183 (lambda () 4184 (when (lsp-buffer-live-p buffer) 4185 (lsp-with-current-buffer buffer 4186 (lsp--on-change-debounce buffer) 4187 (lsp--on-idle buffer))))))) 4188 (t 4189 (lsp-unconfig-buffer) 4190 4191 (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t) 4192 (remove-hook 'post-command-hook #'lsp--post-command t) 4193 (remove-hook 'after-change-functions #'lsp-on-change t) 4194 (remove-hook 'after-revert-hook #'lsp-on-revert t) 4195 (remove-hook 'after-save-hook #'lsp-on-save t) 4196 (remove-hook 'auto-save-hook #'lsp--on-auto-save t) 4197 (remove-hook 'before-change-functions #'lsp-before-change t) 4198 (remove-hook 'before-save-hook #'lsp--before-save t) 4199 (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t) 4200 4201 (lsp--update-on-type-formatting-hook :cleanup) 4202 (lsp--update-signature-help-hook :cleanup) 4203 4204 (when lsp--on-idle-timer 4205 (cancel-timer lsp--on-idle-timer) 4206 (setq lsp--on-idle-timer nil)) 4207 4208 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4209 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4210 4211 (lsp--remove-overlays 'lsp-highlight) 4212 (lsp--remove-overlays 'lsp-links) 4213 4214 (remove-hook 'xref-backend-functions #'lsp--xref-backend t) 4215 (remove-hook 'change-major-mode-hook #'lsp-disconnect t) 4216 (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t) 4217 (setq-local lsp-buffer-uri nil)))) 4218 4219 (defun lsp-configure-buffer () 4220 "Configure LSP features for current buffer." 4221 ;; make sure the core is running in the context of all available workspaces 4222 ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context 4223 (let ((lsp--buffer-workspaces (cond 4224 (lsp--buffer-workspaces) 4225 (lsp--cur-workspace (list lsp--cur-workspace)))) 4226 lsp--cur-workspace) 4227 (when lsp-auto-configure 4228 (lsp--auto-configure) 4229 4230 (when (and lsp-enable-text-document-color 4231 (lsp-feature? "textDocument/documentColor")) 4232 (add-hook 'lsp-on-change-hook #'lsp--document-color nil t)) 4233 4234 (when (and lsp-enable-imenu 4235 (lsp-feature? "textDocument/documentSymbol")) 4236 (lsp-enable-imenu)) 4237 4238 (when (and lsp-enable-indentation 4239 (lsp-feature? "textDocument/rangeFormatting")) 4240 (add-function :override (local 'indent-region-function) #'lsp-format-region)) 4241 4242 (when (and lsp-enable-symbol-highlighting 4243 (lsp-feature? "textDocument/documentHighlight")) 4244 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)) 4245 4246 (when (and lsp-enable-links 4247 (lsp-feature? "textDocument/documentLink")) 4248 (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t)) 4249 4250 (when (and lsp-inlay-hint-enable 4251 (lsp-feature? "textDocument/inlayHint")) 4252 (lsp-inlay-hints-mode)) 4253 4254 (when (and lsp-enable-dap-auto-configure 4255 (functionp 'dap-mode)) 4256 (dap-auto-configure-mode 1))) 4257 (run-hooks 'lsp-configure-hook))) 4258 4259 (defun lsp-unconfig-buffer () 4260 "Unconfigure LSP features for buffer." 4261 (lsp--remove-overlays 'lsp-color) 4262 4263 (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function) 4264 (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index) 4265 (setq-local imenu-menubar-modified-tick 0) 4266 (setq-local imenu--index-alist nil) 4267 (imenu--cleanup)) 4268 4269 (remove-function (local 'indent-region-function) #'lsp-format-region) 4270 4271 (remove-hook 'lsp-on-change-hook #'lsp--document-color t) 4272 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4273 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4274 4275 (when (and lsp-enable-dap-auto-configure 4276 (functionp 'dap-mode)) 4277 (dap-auto-configure-mode -1)) 4278 4279 (run-hooks 'lsp-unconfigure-hook)) 4280 4281 (defun lsp--buffer-content () 4282 (lsp-save-restriction-and-excursion 4283 (or (lsp-virtual-buffer-call :buffer-string) 4284 (buffer-substring-no-properties (point-min) 4285 (point-max))))) 4286 4287 (defun lsp--text-document-did-open () 4288 "`document/didOpen' event." 4289 (run-hooks 'lsp-before-open-hook) 4290 (when (and lsp-auto-touch-files 4291 (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri))))) 4292 (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri)) 4293 (save-buffer)) 4294 4295 (setq lsp--cur-version (or lsp--cur-version 0)) 4296 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 4297 (lsp-notify 4298 "textDocument/didOpen" 4299 (list :textDocument 4300 (list :uri (lsp--buffer-uri) 4301 :languageId (lsp-buffer-language) 4302 :version lsp--cur-version 4303 :text (lsp--buffer-content)))) 4304 4305 (lsp-managed-mode 1) 4306 4307 (lsp-diagnostics--request-pull-diagnostics lsp--cur-workspace) 4308 4309 (run-hooks 'lsp-after-open-hook) 4310 (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client)))) 4311 (-some-> (lsp--client-after-open-fn client) 4312 (funcall)) 4313 (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client)) 4314 (intern-soft) 4315 (run-hooks)))) 4316 4317 (defun lsp--text-document-identifier () 4318 "Make TextDocumentIdentifier." 4319 (list :uri (lsp--buffer-uri))) 4320 4321 (defun lsp--versioned-text-document-identifier () 4322 "Make VersionedTextDocumentIdentifier." 4323 (plist-put (lsp--text-document-identifier) :version lsp--cur-version)) 4324 4325 (defun lsp--cur-line (&optional point) 4326 (1- (line-number-at-pos point))) 4327 4328 (defun lsp--cur-position () 4329 "Make a Position object for the current point." 4330 (or (lsp-virtual-buffer-call :cur-position) 4331 (lsp-save-restriction-and-excursion 4332 (list :line (lsp--cur-line) 4333 :character (- (point) (line-beginning-position)))))) 4334 4335 (defun lsp--point-to-position (point) 4336 "Convert POINT to Position." 4337 (lsp-save-restriction-and-excursion 4338 (goto-char point) 4339 (lsp--cur-position))) 4340 4341 (defun lsp--range (start end) 4342 "Make Range body from START and END." 4343 ;; make sure start and end are Position objects 4344 (list :start start :end end)) 4345 4346 (defun lsp--region-to-range (start end) 4347 "Make Range object for the current region." 4348 (lsp--range (lsp--point-to-position start) 4349 (lsp--point-to-position end))) 4350 4351 (defun lsp--region-or-line () 4352 "The active region or the current line." 4353 (if (use-region-p) 4354 (lsp--region-to-range (region-beginning) (region-end)) 4355 (lsp--region-to-range (line-beginning-position) (line-end-position)))) 4356 4357 (defun lsp--check-document-changes-version (document-changes) 4358 "Verify that DOCUMENT-CHANGES have the proper version." 4359 (unless (seq-every-p 4360 (-lambda ((&TextDocumentEdit :text-document)) 4361 (or 4362 (not text-document) 4363 (let* ((filename (-> text-document 4364 lsp:versioned-text-document-identifier-uri 4365 lsp--uri-to-path)) 4366 (version (lsp:versioned-text-document-identifier-version? text-document))) 4367 (with-current-buffer (find-file-noselect filename) 4368 (or (null version) (zerop version) (= -1 version) 4369 (equal version lsp--cur-version)))))) 4370 document-changes) 4371 (error "Document changes cannot be applied due to different document version"))) 4372 4373 (defun lsp--apply-workspace-edit (workspace-edit &optional operation) 4374 "Apply the WorkspaceEdit object WORKSPACE-EDIT. 4375 OPERATION is symbol representing the source of this text edit." 4376 (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit)) 4377 (if-let ((document-changes (seq-reverse document-changes?))) 4378 (progn 4379 (lsp--check-document-changes-version document-changes) 4380 (->> document-changes 4381 (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create"))) 4382 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4383 (->> document-changes 4384 (seq-filter (-lambda ((&CreateFile :kind)) 4385 (and (or (not kind) (equal kind "edit")) 4386 (not (equal kind "create"))))) 4387 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4388 (->> document-changes 4389 (seq-filter (-lambda ((&CreateFile :kind)) 4390 (and (not (or (not kind) (equal kind "edit"))) 4391 (not (equal kind "create"))))) 4392 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))) 4393 (lsp-map 4394 (lambda (uri text-edits) 4395 (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect) 4396 (lsp--apply-text-edits text-edits operation))) 4397 changes?)))) 4398 4399 (defmacro lsp-with-filename (file &rest body) 4400 "Execute BODY with FILE as a context. 4401 Need to handle the case when FILE indicates virtual buffer." 4402 (declare (indent 1) (debug t)) 4403 `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file))) 4404 (lsp-with-current-buffer lsp--virtual-buffer 4405 ,@body) 4406 ,@body)) 4407 4408 (defun lsp--apply-text-document-edit (edit &optional operation) 4409 "Apply the TextDocumentEdit object EDIT. 4410 OPERATION is symbol representing the source of this text edit. 4411 If the file is not being visited by any buffer, it is opened with 4412 `find-file-noselect'. 4413 Because lsp-mode does not store previous document versions, the edit is only 4414 applied if the version of the textDocument matches the version of the 4415 corresponding file. 4416 4417 interface TextDocumentEdit { 4418 textDocument: VersionedTextDocumentIdentifier; 4419 edits: TextEdit[]; 4420 }" 4421 (pcase (lsp:edit-kind edit) 4422 ("create" (-let* (((&CreateFile :uri :options?) edit) 4423 (file-name (lsp--uri-to-path uri))) 4424 (mkdir (f-dirname file-name) t) 4425 (f-touch file-name) 4426 (when (lsp:create-file-options-overwrite? options?) 4427 (f-write-text "" nil file-name)) 4428 (find-file-noselect file-name))) 4429 ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit)) 4430 (f-delete (lsp--uri-to-path uri) recursive?))) 4431 ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit) 4432 (old-file-name (lsp--uri-to-path old-uri)) 4433 (new-file-name (lsp--uri-to-path new-uri)) 4434 (buf (find-buffer-visiting old-file-name))) 4435 (when buf 4436 (lsp-with-current-buffer buf 4437 (save-buffer) 4438 (lsp--text-document-did-close))) 4439 (mkdir (f-dirname new-file-name) t) 4440 (rename-file old-file-name new-file-name overwrite?) 4441 (when buf 4442 (lsp-with-current-buffer buf 4443 (set-buffer-modified-p nil) 4444 (setq lsp-buffer-uri nil) 4445 (set-visited-file-name new-file-name) 4446 (lsp))))) 4447 (_ (let ((file-name (->> edit 4448 (lsp:text-document-edit-text-document) 4449 (lsp:versioned-text-document-identifier-uri) 4450 (lsp--uri-to-path)))) 4451 (lsp-with-current-buffer (find-buffer-visiting file-name) 4452 (lsp-with-filename file-name 4453 (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation))))))) 4454 4455 (lsp-defun lsp--position-compare ((&Position :line left-line 4456 :character left-character) 4457 (&Position :line right-line 4458 :character right-character)) 4459 "Return t if position LEFT is greater than RIGHT." 4460 (if (= left-line right-line) 4461 (> left-character right-character) 4462 (> left-line right-line))) 4463 4464 (lsp-defun lsp-point-in-range? (position (&Range :start :end)) 4465 "Returns if POINT is in RANGE." 4466 (not (or (lsp--position-compare start position) 4467 (lsp--position-compare position end)))) 4468 4469 (lsp-defun lsp--position-equal ((&Position :line left-line 4470 :character left-character) 4471 (&Position :line right-line 4472 :character right-character)) 4473 "Return whether LEFT and RIGHT positions are equal." 4474 (and (= left-line right-line) 4475 (= left-character right-character))) 4476 4477 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end)) 4478 (&TextEdit :range (&Range :start right-start :end right-end))) 4479 (if (lsp--position-equal left-start right-start) 4480 (lsp--position-compare left-end right-end) 4481 (lsp--position-compare left-start right-start))) 4482 4483 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text)) 4484 "Apply the edits described in the TextEdit object in TEXT-EDIT." 4485 (setq new-text (s-replace "\r" "" (or new-text ""))) 4486 (lsp:set-text-edit-new-text edit new-text) 4487 (goto-char start) 4488 (delete-region start end) 4489 (insert new-text)) 4490 4491 ;; WORKAROUND: typescript-language might send -1 when applying code actions. 4492 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582 4493 (lsp-defun lsp--fix-point ((point &as &Position :character :line)) 4494 (-doto point 4495 (lsp:set-position-line (max 0 line)) 4496 (lsp:set-position-character (max 0 character)))) 4497 4498 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as 4499 &TextEdit 4500 :range (&Range :start :end) 4501 :new-text)) 4502 "Apply the edits described in the TextEdit object in TEXT-EDIT. 4503 The method uses `replace-buffer-contents'." 4504 (setq new-text (s-replace "\r" "" (or new-text ""))) 4505 (lsp:set-text-edit-new-text edit new-text) 4506 (-let* ((source (current-buffer)) 4507 ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start) 4508 :end (lsp--fix-point end))))) 4509 (with-temp-buffer 4510 (insert new-text) 4511 (let ((temp (current-buffer))) 4512 (with-current-buffer source 4513 (save-excursion 4514 (save-restriction 4515 (narrow-to-region beg end) 4516 4517 ;; On emacs versions < 26.2, 4518 ;; `replace-buffer-contents' is buggy - it calls 4519 ;; change functions with invalid arguments - so we 4520 ;; manually call the change functions here. 4521 ;; 4522 ;; See emacs bugs #32237, #32278: 4523 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237 4524 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278 4525 (let ((inhibit-modification-hooks t) 4526 (length (- end beg))) 4527 (run-hook-with-args 'before-change-functions 4528 beg end) 4529 (replace-buffer-contents temp) 4530 (run-hook-with-args 'after-change-functions 4531 beg (+ beg (length new-text)) 4532 length))))))))) 4533 4534 (defun lsp--to-yasnippet-snippet (snippet) 4535 "Convert LSP SNIPPET to yasnippet snippet." 4536 ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it. 4537 (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`"))) 4538 (rx "\\" (backref 1)) 4539 snippet 4540 nil nil 1)) 4541 4542 (defvar-local lsp-enable-relative-indentation nil 4543 "Enable relative indentation when insert texts, snippets ... 4544 from language server.") 4545 4546 (defun lsp--expand-snippet (snippet &optional start end expand-env) 4547 "Wrapper of `yas-expand-snippet' with all of it arguments. 4548 The snippet will be convert to LSP style and indent according to 4549 LSP server result." 4550 (require 'yasnippet nil t) 4551 (let* ((inhibit-field-text-motion t) 4552 (yas-wrap-around-region nil) 4553 (yas-indent-line 'none) 4554 (yas-also-auto-indent-first-line nil)) 4555 (yas-expand-snippet 4556 (lsp--to-yasnippet-snippet snippet) 4557 start end expand-env))) 4558 4559 (defun lsp--indent-lines (start end &optional insert-text-mode?) 4560 "Indent from START to END based on INSERT-TEXT-MODE? value. 4561 - When INSERT-TEXT-MODE? is provided 4562 - if it's `lsp/insert-text-mode-as-it', do no editor indentation. 4563 - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading 4564 whitespaces to match the line where text is inserted. 4565 - When it's not provided, using `indent-line-function' for each line." 4566 (save-excursion 4567 (goto-char end) 4568 (let* ((end-line (line-number-at-pos)) 4569 (offset (save-excursion 4570 (goto-char start) 4571 (current-indentation))) 4572 (indent-line-function 4573 (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it) 4574 #'ignore) 4575 ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation) 4576 lsp-enable-relative-indentation 4577 ;; Indenting snippets is extremely slow in `org-mode' buffers 4578 ;; since it has to calculate indentation based on SRC block 4579 ;; position. Thus we use relative indentation as default. 4580 (derived-mode-p 'org-mode)) 4581 (lambda () (save-excursion 4582 (beginning-of-line) 4583 (indent-to-column offset)))) 4584 (t indent-line-function)))) 4585 (goto-char start) 4586 (forward-line) 4587 (while (and (not (eobp)) 4588 (<= (line-number-at-pos) end-line)) 4589 (funcall indent-line-function) 4590 (forward-line))))) 4591 4592 (defun lsp--apply-text-edits (edits &optional operation) 4593 "Apply the EDITS described in the TextEdit[] object. 4594 OPERATION is symbol representing the source of this text edit." 4595 (unless (seq-empty-p edits) 4596 (atomic-change-group 4597 (run-hooks 'lsp-before-apply-edits-hook) 4598 (let* ((change-group (prepare-change-group)) 4599 (howmany (length edits)) 4600 (message (format "Applying %s edits to `%s' ..." howmany (current-buffer))) 4601 (_ (lsp--info message)) 4602 (reporter (make-progress-reporter message 0 howmany)) 4603 (done 0) 4604 (apply-edit (if (not lsp--virtual-buffer) 4605 #'lsp--apply-text-edit-replace-buffer-contents 4606 #'lsp--apply-text-edit))) 4607 (unwind-protect 4608 (->> edits 4609 ;; We sort text edits so as to apply edits that modify latter 4610 ;; parts of the document first. Furthermore, because the LSP 4611 ;; spec dictates that: "If multiple inserts have the same 4612 ;; position, the order in the array defines which edit to 4613 ;; apply first." We reverse the initial list and sort stably 4614 ;; to make sure the order among edits with the same position 4615 ;; is preserved. 4616 (nreverse) 4617 (seq-sort #'lsp--text-edit-sort-predicate) 4618 (mapc (lambda (edit) 4619 (progress-reporter-update reporter (cl-incf done)) 4620 (funcall apply-edit edit) 4621 (when (lsp:snippet-text-edit-insert-text-format? edit) 4622 (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start) 4623 :insert-text-format? :new-text) edit) 4624 (when (eq insert-text-format? lsp/insert-text-format-snippet) 4625 ;; No `save-excursion' needed since expand snippet will change point anyway 4626 (goto-char (+ start (length new-text))) 4627 (lsp--indent-lines start (point)) 4628 (lsp--expand-snippet new-text start (point))))) 4629 (run-hook-with-args 'lsp-after-apply-edits-hook operation)))) 4630 (undo-amalgamate-change-group change-group) 4631 (progress-reporter-done reporter)))))) 4632 4633 (defun lsp--create-apply-text-edits-handlers () 4634 "Create (handler cleanup-fn) for applying text edits in async request. 4635 Only works when mode is `tick or `alive." 4636 (let* (first-edited 4637 (func (lambda (start &rest _) 4638 (setq first-edited (if first-edited 4639 (min start first-edited) 4640 start))))) 4641 (add-hook 'before-change-functions func nil t) 4642 (list 4643 (lambda (edits) 4644 (if (and first-edited 4645 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end))) 4646 ;; Text edit region is overlapped 4647 (> end first-edited)) 4648 edits)) 4649 (lsp--warn "TextEdits will not be applied since document has been modified before of them.") 4650 (lsp--apply-text-edits edits 'completion-cleanup))) 4651 (lambda () 4652 (remove-hook 'before-change-functions func t))))) 4653 4654 (defun lsp--capability (cap &optional capabilities) 4655 "Get the value of capability CAP. If CAPABILITIES is non-nil, use them instead." 4656 (when (stringp cap) 4657 (setq cap (intern (concat ":" cap)))) 4658 4659 (lsp-get (or capabilities 4660 (lsp--server-capabilities)) 4661 cap)) 4662 4663 (defun lsp--registered-capability (method) 4664 "Check whether there is workspace providing METHOD." 4665 (->> (lsp-workspaces) 4666 (--keep (seq-find (lambda (reg) 4667 (equal (lsp--registered-capability-method reg) method)) 4668 (lsp--workspace-registered-server-capabilities it))) 4669 cl-first)) 4670 4671 (defun lsp--capability-for-method (method) 4672 "Get the value of capability for METHOD." 4673 (-let* ((reqs (cdr (assoc method lsp-method-requirements))) 4674 ((&plist :capability) reqs)) 4675 (or (and capability (lsp--capability capability)) 4676 (-some-> (lsp--registered-capability method) 4677 (lsp--registered-capability-options))))) 4678 4679 (defvar-local lsp--before-change-vals nil 4680 "Store the positions from the `lsp-before-change' function call, for 4681 validation and use in the `lsp-on-change' function.") 4682 4683 (defun lsp--text-document-content-change-event (start end length) 4684 "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH." 4685 ;; So (47 54 0) means add 7 chars starting at pos 47 4686 ;; must become 4687 ;; {"range":{"start":{"line":5,"character":6} 4688 ;; ,"end" :{"line":5,"character":6}} 4689 ;; ,"rangeLength":0 4690 ;; ,"text":"\nbb = 5"} 4691 ;; 4692 ;; And (47 47 7) means delete 7 chars starting at pos 47 4693 ;; must become 4694 ;; {"range":{"start":{"line":6,"character":0} 4695 ;; ,"end" :{"line":7,"character":0}} 4696 ;; ,"rangeLength":7 4697 ;; ,"text":""} 4698 ;; 4699 ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with 4700 ;; 13 chars. So it must become 4701 ;; {"range":{"start":{"line":5,"character":8} 4702 ;; ,"end" :{"line":5,"character":11}} 4703 ;; ,"rangeLength":3 4704 ;; ,"text":"new-chars-xxx"} 4705 ;; 4706 4707 ;; Adding text: 4708 ;; lsp-before-change:(start,end)=(33,33) 4709 ;; lsp-on-change:(start,end,length)=(33,34,0) 4710 ;; 4711 ;; Changing text: 4712 ;; lsp-before-change:(start,end)=(208,211) 4713 ;; lsp-on-change:(start,end,length)=(208,221,3) 4714 ;; 4715 ;; Deleting text: 4716 ;; lsp-before-change:(start,end)=(19,27) 4717 ;; lsp-on-change:(start,end,length)=(19,19,8) 4718 (if (zerop length) 4719 ;; Adding something only, work from start only 4720 `( :range ,(lsp--range 4721 (lsp--point-to-position start) 4722 (lsp--point-to-position start)) 4723 :rangeLength 0 4724 :text ,(buffer-substring-no-properties start end)) 4725 4726 (if (eq start end) 4727 ;; Deleting something only 4728 (if (lsp--bracketed-change-p start length) 4729 ;; The before-change value is bracketed, use it 4730 `( :range ,(lsp--range 4731 (lsp--point-to-position start) 4732 (plist-get lsp--before-change-vals :end-pos)) 4733 :rangeLength ,length 4734 :text "") 4735 ;; If the change is not bracketed, send a full change event instead. 4736 (lsp--full-change-event)) 4737 4738 ;; Deleting some things, adding others 4739 (if (lsp--bracketed-change-p start length) 4740 ;; The before-change value is valid, use it 4741 `( :range ,(lsp--range 4742 (lsp--point-to-position start) 4743 (plist-get lsp--before-change-vals :end-pos)) 4744 :rangeLength ,length 4745 :text ,(buffer-substring-no-properties start end)) 4746 (lsp--full-change-event))))) 4747 4748 (defun lsp--bracketed-change-p (start length) 4749 "If the before and after positions are the same, and the length 4750 is the size of the start range, we are probably good." 4751 (-let [(&plist :end before-end :start before-start) lsp--before-change-vals] 4752 (and (eq start before-start) 4753 (eq length (- before-end before-start))))) 4754 4755 (defun lsp--full-change-event () 4756 `(:text ,(lsp--buffer-content))) 4757 4758 (defun lsp-before-change (start end) 4759 "Executed before a file is changed. 4760 Added to `before-change-functions'." 4761 ;; Note: 4762 ;; 4763 ;; This variable holds a list of functions to call when Emacs is about to 4764 ;; modify a buffer. Each function gets two arguments, the beginning and end of 4765 ;; the region that is about to change, represented as integers. The buffer 4766 ;; that is about to change is always the current buffer when the function is 4767 ;; called. 4768 ;; 4769 ;; WARNING: 4770 ;; 4771 ;; Do not expect the before-change hooks and the after-change hooks be called 4772 ;; in balanced pairs around each buffer change. Also don't expect the 4773 ;; before-change hooks to be called for every chunk of text Emacs is about to 4774 ;; delete. These hooks are provided on the assumption that Lisp programs will 4775 ;; use either before- or the after-change hooks, but not both, and the 4776 ;; boundaries of the region where the changes happen might include more than 4777 ;; just the actual changed text, or even lump together several changes done 4778 ;; piecemeal. 4779 (save-match-data 4780 (lsp-save-restriction-and-excursion 4781 (setq lsp--before-change-vals 4782 (list :start start 4783 :end end 4784 :end-pos (lsp--point-to-position end)))))) 4785 4786 (defun lsp--flush-delayed-changes () 4787 (let ((inhibit-quit t)) 4788 (when lsp--delay-timer 4789 (cancel-timer lsp--delay-timer)) 4790 (mapc (-lambda ((workspace buffer document change)) 4791 (with-current-buffer buffer 4792 (with-lsp-workspace workspace 4793 (lsp-notify "textDocument/didChange" 4794 (list :textDocument document 4795 :contentChanges (vector change)))))) 4796 (prog1 (nreverse lsp--delayed-requests) 4797 (setq lsp--delayed-requests nil))))) 4798 4799 (defun lsp--workspace-sync-method (workspace) 4800 (let ((sync (-> workspace 4801 (lsp--workspace-server-capabilities) 4802 (lsp:server-capabilities-text-document-sync?)))) 4803 (if (lsp-text-document-sync-options? sync) 4804 (lsp:text-document-sync-options-change? sync) 4805 sync))) 4806 4807 (defun lsp-on-change (start end length &optional content-change-event-fn) 4808 "Executed when a file is changed. 4809 Added to `after-change-functions'." 4810 ;; Note: 4811 ;; 4812 ;; Each function receives three arguments: the beginning and end of the region 4813 ;; just changed, and the length of the text that existed before the change. 4814 ;; All three arguments are integers. The buffer that has been changed is 4815 ;; always the current buffer when the function is called. 4816 ;; 4817 ;; The length of the old text is the difference between the buffer positions 4818 ;; before and after that text as it was before the change. As for the 4819 ;; changed text, its length is simply the difference between the first two 4820 ;; arguments. 4821 ;; 4822 ;; So (47 54 0) means add 7 chars starting at pos 47 4823 ;; So (47 47 7) means delete 7 chars starting at pos 47 4824 (save-match-data 4825 (let ((inhibit-quit t) 4826 ;; make sure that `lsp-on-change' is called in multi-workspace context 4827 ;; see #2901 4828 lsp--cur-workspace) 4829 ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done 4830 ;; by auto-revert-mode) will cause this handler to get called with a nil 4831 ;; buffer-file-name. We need the buffer-file-name to send notifications; 4832 ;; so we skip handling revert-buffer-caused changes and instead handle 4833 ;; reverts separately in lsp-on-revert 4834 (when (not revert-buffer-in-progress-p) 4835 (cl-incf lsp--cur-version) 4836 (mapc 4837 (lambda (workspace) 4838 (pcase (or lsp-document-sync-method 4839 (lsp--workspace-sync-method workspace)) 4840 (1 4841 (if lsp-debounce-full-sync-notifications 4842 (setq lsp--delayed-requests 4843 (->> lsp--delayed-requests 4844 (-remove (-lambda ((_ buffer)) 4845 (equal (current-buffer) buffer))) 4846 (cons (list workspace 4847 (current-buffer) 4848 (lsp--versioned-text-document-identifier) 4849 (lsp--full-change-event))))) 4850 (with-lsp-workspace workspace 4851 (lsp-notify "textDocument/didChange" 4852 (list :contentChanges (vector (lsp--full-change-event)) 4853 :textDocument (lsp--versioned-text-document-identifier))) 4854 (lsp-diagnostics--request-pull-diagnostics workspace)))) 4855 (2 4856 (with-lsp-workspace workspace 4857 (lsp-notify 4858 "textDocument/didChange" 4859 (list :textDocument (lsp--versioned-text-document-identifier) 4860 :contentChanges (vector 4861 (if content-change-event-fn 4862 (funcall content-change-event-fn start end length) 4863 (lsp--text-document-content-change-event 4864 start end length))))) 4865 (lsp-diagnostics--request-pull-diagnostics workspace))))) 4866 (lsp-workspaces)) 4867 (when lsp--delay-timer (cancel-timer lsp--delay-timer)) 4868 (setq lsp--delay-timer (run-with-idle-timer 4869 lsp-debounce-full-sync-notifications-interval 4870 nil 4871 #'lsp--flush-delayed-changes)) 4872 ;; force cleanup overlays after each change 4873 (lsp--remove-overlays 'lsp-highlight) 4874 (lsp--after-change (current-buffer)))))) 4875 4876 4877 4878 ;; facilities for on change hooks. We do not want to make lsp calls on each 4879 ;; change event so we add debounce to avoid flooding the server with events. 4880 ;; Additionally, we want to have a mechanism for stopping the server calls in 4881 ;; particular cases like, e. g. when performing completion. 4882 4883 (defvar lsp-inhibit-lsp-hooks nil 4884 "Flag to control.") 4885 4886 (defcustom lsp-on-change-hook nil 4887 "Hooks to run when buffer has changed." 4888 :type 'hook 4889 :group 'lsp-mode) 4890 4891 (defcustom lsp-idle-delay 0.500 4892 "Debounce interval for `after-change-functions'." 4893 :type 'number 4894 :group 'lsp-mode) 4895 4896 (defcustom lsp-on-idle-hook nil 4897 "Hooks to run after `lsp-idle-delay'." 4898 :type 'hook 4899 :group 'lsp-mode) 4900 4901 (defun lsp--idle-reschedule (buffer) 4902 (when lsp--on-idle-timer 4903 (cancel-timer lsp--on-idle-timer)) 4904 4905 (setq lsp--on-idle-timer (run-with-idle-timer 4906 lsp-idle-delay 4907 nil 4908 #'lsp--on-idle 4909 buffer))) 4910 4911 (defun lsp--post-command () 4912 (lsp--cleanup-highlights-if-needed) 4913 (lsp--idle-reschedule (current-buffer))) 4914 4915 (defun lsp--on-idle (buffer) 4916 "Start post command loop." 4917 (when (and (buffer-live-p buffer) 4918 (equal buffer (current-buffer)) 4919 (not lsp-inhibit-lsp-hooks) 4920 lsp-managed-mode) 4921 (run-hooks 'lsp-on-idle-hook))) 4922 4923 (defun lsp--on-change-debounce (buffer) 4924 (when (and (buffer-live-p buffer) 4925 (equal buffer (current-buffer)) 4926 (not lsp-inhibit-lsp-hooks) 4927 lsp-managed-mode) 4928 (run-hooks 'lsp-on-change-hook))) 4929 4930 (defun lsp--after-change (buffer) 4931 "Called after most textDocument/didChange events." 4932 (setq lsp--signature-last-index nil 4933 lsp--signature-last nil) 4934 4935 ;; cleanup diagnostics 4936 (when lsp-diagnostic-clean-after-change 4937 (dolist (workspace (lsp-workspaces)) 4938 (-let [diagnostics (lsp--workspace-diagnostics workspace)] 4939 (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics)))) 4940 4941 (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled) 4942 (lsp--semantic-tokens-refresh-if-enabled buffer)) 4943 (when lsp--on-change-timer 4944 (cancel-timer lsp--on-change-timer)) 4945 (setq lsp--on-change-timer (run-with-idle-timer 4946 lsp-idle-delay 4947 nil 4948 #'lsp--on-change-debounce 4949 buffer)) 4950 (lsp--idle-reschedule buffer)) 4951 4952 4953 (defcustom lsp-trim-trailing-whitespace t 4954 "Trim trailing whitespace on a line." 4955 :group 'lsp-mode 4956 :type 'boolean) 4957 4958 (defcustom lsp-insert-final-newline t 4959 "Insert a newline character at the end of the file if one does not exist." 4960 :group 'lsp-mode 4961 :type 'boolean) 4962 4963 (defcustom lsp-trim-final-newlines t 4964 "Trim all newlines after the final newline at the end of the file." 4965 :group 'lsp-mode 4966 :type 'boolean) 4967 4968 4969 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters) 4970 "Self insert handling. 4971 Applies on type formatting." 4972 (let ((ch last-command-event)) 4973 (when (or (eq (string-to-char first-trigger-characters) ch) 4974 (cl-find ch more-trigger-characters :key #'string-to-char)) 4975 (lsp-request-async "textDocument/onTypeFormatting" 4976 (lsp-make-document-on-type-formatting-params 4977 :text-document (lsp--text-document-identifier) 4978 :options (lsp-make-formatting-options 4979 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 4980 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 4981 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 4982 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 4983 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)) 4984 :ch (char-to-string ch) 4985 :position (lsp--cur-position)) 4986 (lambda (data) (lsp--apply-text-edits data 'format)) 4987 :mode 'tick)))) 4988 4989 4990 ;; links 4991 (defun lsp--document-links () 4992 (when (lsp-feature? "textDocument/documentLink") 4993 (lsp-request-async 4994 "textDocument/documentLink" 4995 `(:textDocument ,(lsp--text-document-identifier)) 4996 (lambda (links) 4997 (lsp--remove-overlays 'lsp-link) 4998 (seq-do 4999 (-lambda ((link &as &DocumentLink :range (&Range :start :end))) 5000 (-doto (make-button (lsp--position-to-point start) 5001 (lsp--position-to-point end) 5002 'action (lsp--document-link-keymap link) 5003 'keymap (let ((map (make-sparse-keymap))) 5004 (define-key map [M-return] 'push-button) 5005 (define-key map [mouse-2] 'push-button) 5006 map) 5007 'help-echo "mouse-2, M-RET: Visit this link") 5008 (overlay-put 'lsp-link t))) 5009 links)) 5010 :mode 'unchanged))) 5011 5012 (defun lsp--document-link-handle-target (url) 5013 (let* ((parsed-url (url-generic-parse-url (url-unhex-string url))) 5014 (type (url-type parsed-url))) 5015 (pcase type 5016 ("file" 5017 (xref-push-marker-stack) 5018 (find-file (lsp--uri-to-path url)) 5019 (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url)) 5020 (goto-char (lsp--position-to-point 5021 (lsp-make-position :character (1- (string-to-number column)) 5022 :line (1- (string-to-number line))))))) 5023 ((or "http" "https") (browse-url url)) 5024 (type (if-let ((handler (lsp--get-uri-handler type))) 5025 (funcall handler url) 5026 (signal 'lsp-file-scheme-not-supported (list url))))))) 5027 5028 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?)) 5029 (if target? 5030 (lambda (_) 5031 (interactive) 5032 (lsp--document-link-handle-target target?)) 5033 (lambda (_) 5034 (interactive) 5035 (when (lsp:document-link-registration-options-resolve-provider? 5036 (lsp--capability-for-method "textDocument/documentLink")) 5037 (lsp-request-async 5038 "documentLink/resolve" 5039 link 5040 (-lambda ((&DocumentLink :target?)) 5041 (lsp--document-link-handle-target target?))))))) 5042 5043 5044 5045 (defcustom lsp-warn-no-matched-clients t 5046 "Whether to show messages when there are no supported clients." 5047 :group 'lsp-mode 5048 :type 'boolean) 5049 5050 (defun lsp-buffer-language--configured-id () 5051 "Return nil when not registered." 5052 (->> lsp-language-id-configuration 5053 (-first 5054 (-lambda ((mode-or-pattern . language)) 5055 (cond 5056 ((and (stringp mode-or-pattern) 5057 (s-matches? mode-or-pattern (buffer-file-name))) 5058 language) 5059 ((eq mode-or-pattern major-mode) language)))) 5060 cl-rest)) 5061 5062 (defvar-local lsp--buffer-language nil 5063 "Locally cached returned value of `lsp-buffer-language'.") 5064 5065 (defun lsp-buffer-language () 5066 "Get language corresponding current buffer." 5067 (or lsp--buffer-language 5068 (let* ((configured-language (lsp-buffer-language--configured-id))) 5069 (setq lsp--buffer-language 5070 (or configured-language 5071 ;; ensure non-nil 5072 (string-remove-suffix "-mode" (symbol-name major-mode)))) 5073 (when (and lsp-warn-no-matched-clients 5074 (null configured-language)) 5075 (lsp-warn "Unable to calculate the languageId for buffer `%s'. \ 5076 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s" 5077 (buffer-name) 5078 major-mode)) 5079 lsp--buffer-language))) 5080 5081 (defun lsp-activate-on (&rest languages) 5082 "Returns language activation function. 5083 The function will return t when the `lsp-buffer-language' returns 5084 one of the LANGUAGES." 5085 (lambda (_file-name _mode) 5086 (-contains? languages (lsp-buffer-language)))) 5087 5088 (defun lsp-workspace-root (&optional path) 5089 "Find the workspace root for the current file or PATH." 5090 (-when-let* ((file-name (or path (buffer-file-name))) 5091 (file-name (lsp-f-canonical file-name))) 5092 (->> (lsp-session) 5093 (lsp-session-folders) 5094 (--filter (and (lsp--files-same-host it file-name) 5095 (or (lsp-f-ancestor-of? it file-name) 5096 (equal it file-name)))) 5097 (--max-by (> (length it) (length other)))))) 5098 5099 (defun lsp-on-revert () 5100 "Executed when a file is reverted. 5101 Added to `after-revert-hook'." 5102 (let ((n (buffer-size)) 5103 (revert-buffer-in-progress-p nil)) 5104 (lsp-on-change 0 n n))) 5105 5106 (defun lsp--text-document-did-close (&optional keep-workspace-alive) 5107 "Executed when the file is closed, added to `kill-buffer-hook'. 5108 5109 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace 5110 if it's closing the last buffer in the workspace." 5111 (lsp-foreach-workspace 5112 (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 5113 (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S" 5114 (lsp-notify "textDocument/didClose" 5115 `(:textDocument ,(lsp--text-document-identifier)))) 5116 (when (and (not lsp-keep-workspace-alive) 5117 (not keep-workspace-alive) 5118 (not (lsp--workspace-buffers lsp--cur-workspace))) 5119 (lsp--shutdown-workspace)))) 5120 5121 (defun lsp--will-save-text-document-params (reason) 5122 (list :textDocument (lsp--text-document-identifier) 5123 :reason reason)) 5124 5125 (defun lsp--before-save () 5126 "Before save handler." 5127 (with-demoted-errors "Error in ‘lsp--before-save’: %S" 5128 (let ((params (lsp--will-save-text-document-params 1))) 5129 (when (lsp--send-will-save-p) 5130 (lsp-notify "textDocument/willSave" params)) 5131 (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits) 5132 (let ((lsp-response-timeout 0.1)) 5133 (condition-case nil 5134 (lsp--apply-text-edits 5135 (lsp-request "textDocument/willSaveWaitUntil" 5136 params) 5137 'before-save) 5138 (error))))))) 5139 5140 (defun lsp--on-auto-save () 5141 "Handler for auto-save." 5142 (when (lsp--send-will-save-p) 5143 (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S" 5144 (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2))))) 5145 5146 (defun lsp--text-document-did-save () 5147 "Executed when the file is closed, added to `after-save-hook''." 5148 (when (lsp--send-did-save-p) 5149 (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’" 5150 (lsp-notify "textDocument/didSave" 5151 `( :textDocument ,(lsp--versioned-text-document-identifier) 5152 ,@(when (lsp--save-include-text-p) 5153 (list :text (lsp--buffer-content)))))))) 5154 5155 (defun lsp--text-document-position-params (&optional identifier position) 5156 "Make TextDocumentPositionParams for the current point in the current document. 5157 If IDENTIFIER and POSITION are non-nil, they will be used as the document 5158 identifier and the position respectively." 5159 (list :textDocument (or identifier (lsp--text-document-identifier)) 5160 :position (or position (lsp--cur-position)))) 5161 5162 (defun lsp--get-buffer-diagnostics () 5163 "Return buffer diagnostics." 5164 (gethash (or 5165 (plist-get lsp--virtual-buffer :buffer-file-name) 5166 (lsp--fix-path-casing (buffer-file-name))) 5167 (lsp-diagnostics t))) 5168 5169 (defun lsp-cur-line-diagnostics () 5170 "Return any diagnostics that apply to the current line." 5171 (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)] 5172 (cl-coerce (-filter 5173 (-lambda ((&Diagnostic :range (&Range :start (&Position :line)))) 5174 (and (>= line start) (<= line end))) 5175 (lsp--get-buffer-diagnostics)) 5176 'vector))) 5177 5178 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end) 5179 (right &as &Range :start right-start :end right-end)) 5180 (or (lsp-point-in-range? right-start left) 5181 (lsp-point-in-range? right-end left) 5182 (lsp-point-in-range? left-start right) 5183 (lsp-point-in-range? left-end right))) 5184 5185 (defun lsp-make-position-1 (position) 5186 (lsp-make-position :line (plist-get position :line) 5187 :character (plist-get position :character))) 5188 5189 (defun lsp-cur-possition-diagnostics () 5190 "Return any diagnostics that apply to the current line." 5191 (-let* ((start (if (use-region-p) (region-beginning) (point))) 5192 (end (if (use-region-p) (region-end) (point))) 5193 (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start)) 5194 :end (lsp-make-position-1 (lsp-point-to-position end))))) 5195 (->> (lsp--get-buffer-diagnostics) 5196 (-filter 5197 (-lambda ((&Diagnostic :range)) 5198 (lsp-range-overlapping? range current-range))) 5199 (apply 'vector)))) 5200 5201 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics) 5202 5203 (defun lsp--extract-line-from-buffer (pos) 5204 "Return the line pointed to by POS (a Position object) in the current buffer." 5205 (let* ((point (lsp--position-to-point pos)) 5206 (inhibit-field-text-motion t)) 5207 (save-excursion 5208 (goto-char point) 5209 (buffer-substring (line-beginning-position) (line-end-position))))) 5210 5211 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line) 5212 :end (end &as &Position :character end-char))) 5213 "Return a xref-item from a RANGE in FILENAME." 5214 (let* ((line (lsp--extract-line-from-buffer start)) 5215 (len (length line))) 5216 (add-face-text-property (max (min start-char len) 0) 5217 (max (min end-char len) 0) 5218 'xref-match t line) 5219 ;; LINE is nil when FILENAME is not being current visited by any buffer. 5220 (xref-make-match (or line filename) 5221 (xref-make-file-location 5222 filename 5223 (lsp-translate-line (1+ start-line)) 5224 (lsp-translate-column start-char)) 5225 (- end-char start-char)))) 5226 5227 (defun lsp--location-uri (loc) 5228 (if (lsp-location? loc) 5229 (lsp:location-uri loc) 5230 (lsp:location-link-target-uri loc))) 5231 5232 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start))) 5233 "Go to location." 5234 (let ((path (lsp--uri-to-path uri))) 5235 (if (f-exists? path) 5236 (with-current-buffer (find-file path) 5237 (goto-char (lsp--position-to-point start))) 5238 (error "There is no file %s" path)))) 5239 5240 (defun lsp--location-range (loc) 5241 (if (lsp-location? loc) 5242 (lsp:location-range loc) 5243 (lsp:location-link-target-selection-range loc))) 5244 5245 (defun lsp--locations-to-xref-items (locations) 5246 "Return a list of `xref-item' given LOCATIONS, which can be of 5247 type Location, LocationLink, Location[] or LocationLink[]." 5248 (setq locations 5249 (pcase locations 5250 ((seq (or (lsp-interface Location) 5251 (lsp-interface LocationLink))) 5252 (append locations nil)) 5253 ((or (lsp-interface Location) 5254 (lsp-interface LocationLink)) 5255 (list locations)))) 5256 5257 (cl-labels ((get-xrefs-in-file 5258 (file-locs) 5259 (-let [(filename . matches) file-locs] 5260 (condition-case err 5261 (let ((visiting (find-buffer-visiting filename)) 5262 (fn (lambda (loc) 5263 (lsp-with-filename filename 5264 (lsp--xref-make-item filename 5265 (lsp--location-range loc)))))) 5266 (if visiting 5267 (with-current-buffer visiting 5268 (seq-map fn matches)) 5269 (when (file-readable-p filename) 5270 (with-temp-buffer 5271 (insert-file-contents-literally filename) 5272 (seq-map fn matches))))) 5273 (error (lsp-warn "Failed to process xref entry for filename '%s': %s" 5274 filename (error-message-string err))) 5275 (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s" 5276 filename (error-message-string err))))))) 5277 5278 (->> locations 5279 (seq-sort #'lsp--location-before-p) 5280 (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri)) 5281 (seq-map #'get-xrefs-in-file) 5282 (apply #'nconc)))) 5283 5284 (defun lsp--location-before-p (left right) 5285 "Sort first by file, then by line, then by column." 5286 (let ((left-uri (lsp--location-uri left)) 5287 (right-uri (lsp--location-uri right))) 5288 (if (not (string= left-uri right-uri)) 5289 (string< left-uri right-uri) 5290 (-let (((&Range :start left-start) (lsp--location-range left)) 5291 ((&Range :start right-start) (lsp--location-range right))) 5292 (lsp--position-compare right-start left-start))))) 5293 5294 (defun lsp--make-reference-params (&optional td-position exclude-declaration) 5295 "Make a ReferenceParam object. 5296 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead. 5297 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." 5298 (let ((json-false :json-false)) 5299 (plist-put (or td-position (lsp--text-document-position-params)) 5300 :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration)))))) 5301 5302 (defun lsp--cancel-request (id) 5303 "Cancel request with ID in all workspaces." 5304 (lsp-foreach-workspace 5305 (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id)) 5306 (lsp-notify "$/cancelRequest" `(:id ,id)))) 5307 5308 (defvar-local lsp--hover-saved-bounds nil) 5309 5310 (defun lsp-eldoc-function (cb &rest _ignored) 5311 "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')." 5312 (if (and lsp--hover-saved-bounds 5313 (lsp--point-in-bounds-p lsp--hover-saved-bounds)) 5314 lsp--eldoc-saved-message 5315 (setq lsp--hover-saved-bounds nil 5316 lsp--eldoc-saved-message nil) 5317 (if (looking-at-p "[[:space:]\n]") 5318 (setq lsp--eldoc-saved-message nil) ; And returns nil. 5319 (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover")) 5320 (lsp-request-async 5321 "textDocument/hover" 5322 (lsp--text-document-position-params) 5323 (-lambda ((hover &as &Hover? :range? :contents)) 5324 (setq lsp--hover-saved-bounds (when range? 5325 (lsp--range-to-region range?))) 5326 (funcall cb (setq lsp--eldoc-saved-message 5327 (when contents 5328 (lsp--render-on-hover-content 5329 contents 5330 lsp-eldoc-render-all))))) 5331 :error-handler #'ignore 5332 :mode 'tick 5333 :cancel-token :eldoc-hover))))) 5334 5335 (defun lsp--point-on-highlight? () 5336 (-some? (lambda (overlay) 5337 (overlay-get overlay 'lsp-highlight)) 5338 (overlays-at (point)))) 5339 5340 (defun lsp--cleanup-highlights-if-needed () 5341 (when (and lsp-enable-symbol-highlighting 5342 lsp--have-document-highlights 5343 (not (lsp--point-on-highlight?))) 5344 (lsp--remove-overlays 'lsp-highlight) 5345 (setq lsp--have-document-highlights nil) 5346 (lsp-cancel-request-by-token :highlights))) 5347 5348 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil 5349 "The bounds of the symbol from which `lsp--document-highlight' 5350 most recently requested highlights.") 5351 5352 (defun lsp--document-highlight () 5353 (when (lsp-feature? "textDocument/documentHighlight") 5354 (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol))) 5355 (unless (or (looking-at-p "[[:space:]\n]") 5356 (not lsp-enable-symbol-highlighting) 5357 (and lsp--have-document-highlights 5358 curr-sym-bounds 5359 (equal curr-sym-bounds 5360 lsp--symbol-bounds-of-last-highlight-invocation))) 5361 (setq lsp--symbol-bounds-of-last-highlight-invocation 5362 curr-sym-bounds) 5363 (lsp-request-async "textDocument/documentHighlight" 5364 (lsp--text-document-position-params) 5365 #'lsp--document-highlight-callback 5366 :mode 'tick 5367 :cancel-token :highlights))))) 5368 5369 (defun lsp--help-open-link (&rest _) 5370 "Open markdown link at point via mouse or keyboard." 5371 (interactive "P") 5372 (let ((buffer-list-update-hook nil)) 5373 (-let [(buffer point) (if-let* ((valid (and (listp last-input-event) 5374 (eq (car last-input-event) 'mouse-2))) 5375 (event (cadr last-input-event)) 5376 (win (posn-window event)) 5377 (buffer (window-buffer win))) 5378 `(,buffer ,(posn-point event)) 5379 `(,(current-buffer) ,(point)))] 5380 (with-current-buffer buffer 5381 (when-let* ((face (get-text-property point 'face)) 5382 (url (or (and (eq face 'markdown-link-face) 5383 (get-text-property point 'help-echo)) 5384 (and (memq face '(markdown-url-face markdown-plain-url-face)) 5385 (nth 3 (markdown-link-at-pos point)))))) 5386 (lsp--document-link-handle-target url)))))) 5387 5388 (defvar lsp-help-mode-map 5389 (-doto (make-sparse-keymap) 5390 (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link)) 5391 "Keymap for `lsp-help-mode'.") 5392 5393 (define-derived-mode lsp-help-mode help-mode "LspHelp" 5394 "Major mode for displaying lsp help.") 5395 5396 (defun lsp-describe-thing-at-point () 5397 "Display the type signature and documentation of the thing at point." 5398 (interactive) 5399 (let ((contents (-some->> (lsp--text-document-position-params) 5400 (lsp--make-request "textDocument/hover") 5401 (lsp--send-request) 5402 (lsp:hover-contents)))) 5403 (if (and contents (not (equal contents ""))) 5404 (let ((lsp-help-buf-name "*lsp-help*")) 5405 (with-current-buffer (get-buffer-create lsp-help-buf-name) 5406 (delay-mode-hooks 5407 (lsp-help-mode) 5408 (with-help-window lsp-help-buf-name 5409 (insert (string-trim-right (lsp--render-on-hover-content contents t))))) 5410 (run-mode-hooks))) 5411 (lsp--info "No content at point.")))) 5412 5413 (defun lsp--point-in-bounds-p (bounds) 5414 "Return whether the current point is within BOUNDS." 5415 (and (<= (car bounds) (point)) (< (point) (cdr bounds)))) 5416 5417 (defun lsp-get-renderer (language) 5418 "Get renderer for LANGUAGE." 5419 (lambda (str) 5420 (lsp--render-string str language))) 5421 5422 (defun lsp--setup-markdown (mode) 5423 "Setup the ‘markdown-mode’ in the frame. 5424 MODE is the mode used in the parent frame." 5425 (make-local-variable 'markdown-code-lang-modes) 5426 (dolist (mark (alist-get mode lsp-custom-markup-modes)) 5427 (add-to-list 'markdown-code-lang-modes (cons mark mode))) 5428 (setq-local markdown-fontify-code-blocks-natively t) 5429 (setq-local markdown-fontify-code-block-default-mode mode) 5430 (setq-local markdown-hide-markup t) 5431 5432 ;; Render some common HTML entities. 5433 ;; This should really happen in markdown-mode instead, 5434 ;; but it doesn't, so we do it here for now. 5435 (setq prettify-symbols-alist 5436 (cl-loop for i from 0 to 255 5437 collect (cons (format "&#x%02X;" i) i))) 5438 (push '("<" . ?<) prettify-symbols-alist) 5439 (push '(">" . ?>) prettify-symbols-alist) 5440 (push '("&" . ?&) prettify-symbols-alist) 5441 (push '(" " . ? ) prettify-symbols-alist) 5442 (setq prettify-symbols-compose-predicate 5443 (lambda (_start _end _match) t)) 5444 (prettify-symbols-mode 1)) 5445 5446 (defvar lsp-help-link-keymap 5447 (let ((map (make-sparse-keymap))) 5448 (define-key map [mouse-2] #'lsp--help-open-link) 5449 (define-key map "\r" #'lsp--help-open-link) 5450 map) 5451 "Keymap active on links in *lsp-help* mode.") 5452 5453 (defun lsp--fix-markdown-links () 5454 (let ((inhibit-read-only t) 5455 (inhibit-modification-hooks t) 5456 (prop)) 5457 (save-restriction 5458 (goto-char (point-min)) 5459 (while (setq prop (markdown-find-next-prop 'face)) 5460 (let ((end (or (next-single-property-change (car prop) 'face) 5461 (point-max)))) 5462 (when (memq (get-text-property (car prop) 'face) 5463 '(markdown-link-face 5464 markdown-url-face 5465 markdown-plain-url-face)) 5466 (add-text-properties (car prop) end 5467 (list 'button t 5468 'category 'lsp-help-link 5469 'follow-link t 5470 'keymap lsp-help-link-keymap))) 5471 (goto-char end)))))) 5472 5473 (defun lsp--buffer-string-visible () 5474 "Return visible buffer string. 5475 Stolen from `org-copy-visible'." 5476 (let ((temp (generate-new-buffer " *temp*")) 5477 (beg (point-min)) 5478 (end (point-max))) 5479 (while (/= beg end) 5480 (when (get-char-property beg 'invisible) 5481 (setq beg (next-single-char-property-change beg 'invisible nil end))) 5482 (let* ((next (next-single-char-property-change beg 'invisible nil end)) 5483 (substring (buffer-substring beg next))) 5484 (with-current-buffer temp (insert substring)) 5485 ;; (setq result (concat result substring)) 5486 (setq beg next))) 5487 (setq deactivate-mark t) 5488 (prog1 (with-current-buffer temp 5489 (s-chop-suffix "\n" (buffer-string))) 5490 (kill-buffer temp)))) 5491 5492 (defvar lsp-buffer-major-mode nil 5493 "Holds the major mode when fontification function is running. 5494 See #2588") 5495 5496 (defvar view-inhibit-help-message) 5497 5498 (defun lsp--render-markdown () 5499 "Render markdown." 5500 5501 (let ((markdown-enable-math nil)) 5502 (goto-char (point-min)) 5503 (while (re-search-forward 5504 (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/" 5505 "{" "}" "[" "]" "(" ")" 5506 "#" "+" "-" "." "!" "|")))) 5507 nil t) 5508 (replace-match (rx (backref 1)))) 5509 5510 ;; markdown-mode v2.3 does not yet provide gfm-view-mode 5511 (if (fboundp 'gfm-view-mode) 5512 (let ((view-inhibit-help-message t)) 5513 (gfm-view-mode)) 5514 (gfm-mode)) 5515 5516 (lsp--setup-markdown lsp-buffer-major-mode))) 5517 5518 (defvar lsp--display-inline-image-alist 5519 '((lsp--render-markdown 5520 (:regexp 5521 "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)" 5522 :sexp 5523 (create-image 5524 (base64-decode-string 5525 (buffer-substring-no-properties (match-beginning 1) (match-end 1))) 5526 nil t)))) 5527 "Replaced string regexp and function returning image. 5528 Each element should have the form (MODE . (PROPERTY-LIST...)). 5529 MODE (car) is function which is defined in `lsp-language-id-configuration'. 5530 Cdr should be list of PROPERTY-LIST. 5531 5532 Each PROPERTY-LIST should have properties: 5533 :regexp Regexp which determines what string is relpaced to image. 5534 You should also get information of image, by parenthesis constructs. 5535 By default, all matched string is replaced to image, but you can 5536 change index of replaced string by keyword :replaced-index. 5537 5538 :sexp Return image when evaluated. You can use information of regexp 5539 by using (match-beggining N), (match-end N) or (match-substring N). 5540 5541 In addition, each can have property: 5542 :replaced-index Determine index which is used to replace regexp to image. 5543 The value means first argument of `match-beginning' and 5544 `match-end'. If omitted, interpreted as index 0.") 5545 5546 (defcustom lsp-display-inline-image t 5547 "Showing inline image or not." 5548 :group 'lsp-mode 5549 :type 'boolean) 5550 5551 (defcustom lsp-enable-suggest-server-download t 5552 "When non-nil enable server downloading suggestions." 5553 :group 'lsp-mode 5554 :type 'boolean 5555 :package-version '(lsp-mode . "9.0.0")) 5556 5557 (defcustom lsp-auto-register-remote-clients t 5558 "When non-nil register remote when registering the local one." 5559 :group 'lsp-mode 5560 :type 'boolean 5561 :package-version '(lsp-mode . "9.0.0")) 5562 5563 (defun lsp--display-inline-image (mode) 5564 "Add image property if available." 5565 (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist)))) 5566 (when (and (display-images-p) lsp-display-inline-image) 5567 (cl-loop 5568 for plist in plist-list 5569 with regexp with replaced-index 5570 do 5571 (setq regexp (plist-get plist :regexp)) 5572 (setq replaced-index (or (plist-get plist :replaced-index) 0)) 5573 5574 (font-lock-remove-keywords nil (list regexp replaced-index)) 5575 (let ((inhibit-read-only t)) 5576 (save-excursion 5577 (goto-char (point-min)) 5578 (while (re-search-forward regexp nil t) 5579 (set-text-properties 5580 (match-beginning replaced-index) (match-end replaced-index) 5581 nil) 5582 (add-text-properties 5583 (match-beginning replaced-index) (match-end replaced-index) 5584 `(display ,(eval (plist-get plist :sexp))))))))))) 5585 5586 (defun lsp--fontlock-with-mode (str mode) 5587 "Fontlock STR with MODE." 5588 (let ((lsp-buffer-major-mode major-mode)) 5589 (with-temp-buffer 5590 (with-demoted-errors "Error during doc rendering: %s" 5591 (insert str) 5592 (delay-mode-hooks (funcall mode)) 5593 (cl-flet ((window-body-width () lsp-window-body-width)) 5594 ;; This can go wrong in some cases, and the fontification would 5595 ;; not work as expected. 5596 ;; 5597 ;; See #2984 5598 (ignore-errors (font-lock-ensure)) 5599 (lsp--display-inline-image mode) 5600 (when (eq mode 'lsp--render-markdown) 5601 (lsp--fix-markdown-links)))) 5602 (lsp--buffer-string-visible)))) 5603 5604 (defun lsp--render-string (str language) 5605 "Render STR using `major-mode' corresponding to LANGUAGE. 5606 When language is nil render as markup if `markdown-mode' is loaded." 5607 (setq str (s-replace "\r" "" (or str ""))) 5608 (if-let* ((modes (-keep (-lambda ((mode . lang)) 5609 (when (and (equal lang language) (functionp mode)) 5610 mode)) 5611 lsp-language-id-configuration)) 5612 (mode (car (or (member major-mode modes) modes)))) 5613 (lsp--fontlock-with-mode str mode) 5614 str)) 5615 5616 (defun lsp--render-element (content) 5617 "Render CONTENT element." 5618 (let ((inhibit-message t)) 5619 (or 5620 (pcase content 5621 ((lsp-interface MarkedString :value :language) 5622 (lsp--render-string value language)) 5623 ((lsp-interface MarkupContent :value :kind) 5624 (lsp--render-string value kind)) 5625 ;; plain string 5626 ((pred stringp) (lsp--render-string content "markdown")) 5627 ((pred null) "") 5628 (_ (error "Failed to handle %s" content))) 5629 ""))) 5630 5631 (defun lsp--create-unique-string-fn () 5632 (let (elements) 5633 (lambda (element) 5634 (let ((count (cl-count element elements :test #'string=))) 5635 (prog1 (if (zerop count) 5636 element 5637 (format "%s (%s)" element count)) 5638 (push element elements)))))) 5639 5640 (defun lsp--select-action (actions) 5641 "Select an action to execute from ACTIONS." 5642 (cond 5643 ((seq-empty-p actions) (signal 'lsp-no-code-actions nil)) 5644 ((and (eq (seq-length actions) 1) lsp-auto-execute-action) 5645 (lsp-seq-first actions)) 5646 (t (let ((completion-ignore-case t)) 5647 (lsp--completing-read "Select code action: " 5648 (seq-into actions 'list) 5649 (-compose (lsp--create-unique-string-fn) 5650 #'lsp:code-action-title) 5651 nil t))))) 5652 5653 (defun lsp--workspace-server-id (workspace) 5654 "Return the server ID of WORKSPACE." 5655 (-> workspace lsp--workspace-client lsp--client-server-id)) 5656 5657 (defun lsp--handle-rendered-for-echo-area (contents) 5658 "Return a single line from RENDERED, appropriate for display in the echo area." 5659 (pcase (lsp-workspaces) 5660 (`(,workspace) 5661 (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace))) 5662 ;; For projects with multiple active workspaces we also default to 5663 ;; render the first line. 5664 (_ (lsp-clients-extract-signature-on-hover contents nil)))) 5665 5666 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id) 5667 "Extract a representative line from CONTENTS, to show in the echo area." 5668 (car (s-lines (s-trim (lsp--render-element contents))))) 5669 5670 (defun lsp--render-on-hover-content (contents render-all) 5671 "Render the content received from `document/onHover' request. 5672 CONTENTS - MarkedString | MarkedString[] | MarkupContent 5673 RENDER-ALL - nil if only the signature should be rendered." 5674 (cond 5675 ((lsp-markup-content? contents) 5676 ;; MarkupContent. 5677 ;; It tends to be long and is not suitable to display fully in the echo area. 5678 ;; Just display the first line which is typically the signature. 5679 (if render-all 5680 (lsp--render-element contents) 5681 (lsp--handle-rendered-for-echo-area contents))) 5682 ((and (stringp contents) (not (string-match-p "\n" contents))) 5683 ;; If the contents is a single string containing a single line, 5684 ;; render it always. 5685 (lsp--render-element contents)) 5686 (t 5687 ;; MarkedString -> MarkedString[] 5688 (when (or (lsp-marked-string? contents) (stringp contents)) 5689 (setq contents (list contents))) 5690 ;; Consider the signature consisting of the elements who have a renderable 5691 ;; "language" property. When render-all is nil, ignore other elements. 5692 (string-join 5693 (seq-map 5694 #'lsp--render-element 5695 (if render-all 5696 contents 5697 ;; Only render contents that have an available renderer. 5698 (seq-take 5699 (seq-filter 5700 (-andfn #'lsp-marked-string? 5701 (-compose #'lsp-get-renderer #'lsp:marked-string-language)) 5702 contents) 5703 1))) 5704 (if (bound-and-true-p page-break-lines-mode) 5705 "\n\n" 5706 "\n"))))) 5707 5708 5709 5710 (defvar lsp-signature-mode-map 5711 (-doto (make-sparse-keymap) 5712 (define-key (kbd "M-n") #'lsp-signature-next) 5713 (define-key (kbd "M-p") #'lsp-signature-previous) 5714 (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs) 5715 (define-key (kbd "C-c C-k") #'lsp-signature-stop) 5716 (define-key (kbd "C-g") #'lsp-signature-stop)) 5717 "Keymap for `lsp-signature-mode'.") 5718 5719 (define-minor-mode lsp-signature-mode 5720 "Mode used to show signature popup." 5721 :keymap lsp-signature-mode-map 5722 :lighter "" 5723 :group 'lsp-mode) 5724 5725 (defun lsp-signature-stop () 5726 "Stop showing current signature help." 5727 (interactive) 5728 (lsp-cancel-request-by-token :signature) 5729 (remove-hook 'post-command-hook #'lsp-signature) 5730 (funcall lsp-signature-function nil) 5731 (lsp-signature-mode -1)) 5732 5733 (declare-function page-break-lines--update-display-tables "ext:page-break-lines") 5734 5735 (defun lsp--setup-page-break-mode-if-present () 5736 "Enable `page-break-lines-mode' in current buffer." 5737 (when (fboundp 'page-break-lines-mode) 5738 (page-break-lines-mode) 5739 ;; force page-break-lines-mode to update the display tables. 5740 (page-break-lines--update-display-tables))) 5741 5742 (defun lsp-lv-message (message) 5743 (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present) 5744 (if message 5745 (progn 5746 (setq lsp--signature-last-buffer (current-buffer)) 5747 (let ((lv-force-update t)) 5748 (lv-message "%s" message))) 5749 (lv-delete-window) 5750 (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present))) 5751 5752 (declare-function posframe-show "ext:posframe") 5753 (declare-function posframe-hide "ext:posframe") 5754 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe") 5755 5756 (defface lsp-signature-posframe 5757 '((t :inherit tooltip)) 5758 "Background and foreground for `lsp-signature-posframe'." 5759 :group 'lsp-mode) 5760 5761 (defvar lsp-signature-posframe-params 5762 (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward 5763 :height 10 5764 :width 60 5765 :border-width 1 5766 :min-width 60) 5767 "Params for signature and `posframe-show'.") 5768 5769 (defun lsp-signature-posframe (str) 5770 "Use posframe to show the STR signatureHelp string." 5771 (if str 5772 (apply #'posframe-show 5773 (with-current-buffer (get-buffer-create " *lsp-signature*") 5774 (erase-buffer) 5775 (insert str) 5776 (visual-line-mode 1) 5777 (lsp--setup-page-break-mode-if-present) 5778 (current-buffer)) 5779 (append 5780 lsp-signature-posframe-params 5781 (list :position (point) 5782 :background-color (face-attribute 'lsp-signature-posframe :background nil t) 5783 :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t) 5784 :border-color (face-attribute 'font-lock-comment-face :foreground nil t)))) 5785 (posframe-hide " *lsp-signature*"))) 5786 5787 (defun lsp--handle-signature-update (signature) 5788 (let ((message 5789 (if (lsp-signature-help? signature) 5790 (lsp--signature->message signature) 5791 (mapconcat #'lsp--signature->message signature "\n")))) 5792 (if (s-present? message) 5793 (funcall lsp-signature-function message) 5794 (lsp-signature-stop)))) 5795 5796 (defun lsp-signature-activate () 5797 "Activate signature help. 5798 It will show up only if current point has signature help." 5799 (interactive) 5800 (setq lsp--signature-last nil 5801 lsp--signature-last-index nil 5802 lsp--signature-last-buffer (current-buffer)) 5803 (add-hook 'post-command-hook #'lsp-signature) 5804 (lsp-signature-mode t)) 5805 5806 (defcustom lsp-signature-cycle t 5807 "Whether `lsp-signature-next' and prev should cycle." 5808 :type 'boolean 5809 :group 'lsp-mode) 5810 5811 (defun lsp-signature-next () 5812 "Show next signature." 5813 (interactive) 5814 (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last)))) 5815 (when (and lsp--signature-last-index 5816 lsp--signature-last 5817 (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs))) 5818 (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs)) 5819 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))) 5820 5821 (defun lsp-signature-previous () 5822 "Next signature." 5823 (interactive) 5824 (when (and lsp--signature-last-index 5825 lsp--signature-last 5826 (or lsp-signature-cycle (not (zerop lsp--signature-last-index)))) 5827 (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index) 5828 (length (lsp:signature-help-signatures lsp--signature-last)) 5829 lsp--signature-last-index))) 5830 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))) 5831 5832 (defun lsp-signature-toggle-full-docs () 5833 "Toggle full/partial signature documentation." 5834 (interactive) 5835 (let ((all? (not (numberp lsp-signature-doc-lines)))) 5836 (setq lsp-signature-doc-lines (if all? 5837 (or (car-safe lsp-signature-doc-lines) 5838 20) 5839 (list lsp-signature-doc-lines)))) 5840 (lsp-signature-activate)) 5841 5842 (defun lsp--signature->message (signature-help) 5843 "Generate eldoc message from SIGNATURE-HELP response." 5844 (setq lsp--signature-last signature-help) 5845 5846 (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help)))) 5847 (-let* (((&SignatureHelp :active-signature? 5848 :active-parameter? 5849 :signatures) signature-help) 5850 (active-signature? (or lsp--signature-last-index active-signature? 0)) 5851 (_ (setq lsp--signature-last-index active-signature?)) 5852 ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?)) 5853 (prefix (if (= (length signatures) 1) 5854 "" 5855 (concat (propertize (format " %s/%s" 5856 (1+ active-signature?) 5857 (length signatures)) 5858 'face 'success) 5859 " "))) 5860 (method-docs (when 5861 (and lsp-signature-render-documentation 5862 (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines))) 5863 (let ((docs (lsp--render-element 5864 (lsp:parameter-information-documentation? signature)))) 5865 (when (s-present? docs) 5866 (concat 5867 "\n" 5868 (if (fboundp 'page-break-lines-mode) 5869 "\n" 5870 "") 5871 (if (and (numberp lsp-signature-doc-lines) 5872 (> (length (s-lines docs)) lsp-signature-doc-lines)) 5873 (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs))) 5874 (propertize "\nTruncated..." 'face 'highlight)) 5875 docs))))))) 5876 (when (and active-parameter? (not (seq-empty-p parameters?))) 5877 (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?))) 5878 (seq-elt parameters? active-parameter?))) 5879 (selected-param-label (let ((label (lsp:parameter-information-label param))) 5880 (if (stringp label) label (append label nil)))) 5881 (start (if (stringp selected-param-label) 5882 (s-index-of selected-param-label label) 5883 (cl-first selected-param-label))) 5884 (end (if (stringp selected-param-label) 5885 (+ start (length selected-param-label)) 5886 (cl-second selected-param-label)))) 5887 (add-face-text-property start end 'eldoc-highlight-function-argument nil label))) 5888 (concat prefix label method-docs)))) 5889 5890 (defun lsp-signature () 5891 "Display signature info (based on `textDocument/signatureHelp')" 5892 (if (and lsp--signature-last-buffer 5893 (not (equal (current-buffer) lsp--signature-last-buffer))) 5894 (lsp-signature-stop) 5895 (lsp-request-async "textDocument/signatureHelp" 5896 (lsp--text-document-position-params) 5897 #'lsp--handle-signature-update 5898 :cancel-token :signature))) 5899 5900 5901 (defcustom lsp-overlay-document-color-char "■" 5902 "Display the char represent the document color in overlay" 5903 :type 'string 5904 :group 'lsp-mode) 5905 5906 ;; color presentation 5907 (defun lsp--color-create-interactive-command (color range) 5908 (lambda () 5909 (interactive) 5910 (-let [(&ColorPresentation? :text-edit? 5911 :additional-text-edits?) 5912 (lsp--completing-read 5913 "Select color presentation: " 5914 (lsp-request 5915 "textDocument/colorPresentation" 5916 `( :textDocument ,(lsp--text-document-identifier) 5917 :color ,color 5918 :range ,range)) 5919 #'lsp:color-presentation-label 5920 nil 5921 t)] 5922 (when text-edit? 5923 (lsp--apply-text-edit text-edit?)) 5924 (when additional-text-edits? 5925 (lsp--apply-text-edits additional-text-edits? 'color-presentation))))) 5926 5927 (defun lsp--number->color (number) 5928 (let ((result (format "%x" 5929 (round (* (or number 0) 255.0))))) 5930 (if (= 1 (length result)) 5931 (concat "0" result) 5932 result))) 5933 5934 (defun lsp--document-color () 5935 "Document color handler." 5936 (when (lsp-feature? "textDocument/documentColor") 5937 (lsp-request-async 5938 "textDocument/documentColor" 5939 `(:textDocument ,(lsp--text-document-identifier)) 5940 (lambda (result) 5941 (lsp--remove-overlays 'lsp-color) 5942 (seq-do 5943 (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue) 5944 :range)) 5945 (-let* (((beg . end) (lsp--range-to-region range)) 5946 (overlay (make-overlay beg end)) 5947 (command (lsp--color-create-interactive-command color range))) 5948 (overlay-put overlay 'lsp-color t) 5949 (overlay-put overlay 'evaporate t) 5950 (overlay-put overlay 5951 'before-string 5952 (propertize 5953 lsp-overlay-document-color-char 5954 'face `((:foreground ,(format 5955 "#%s%s%s" 5956 (lsp--number->color red) 5957 (lsp--number->color green) 5958 (lsp--number->color blue)))) 5959 'action command 5960 'mouse-face 'lsp-lens-mouse-face 5961 'local-map (-doto (make-sparse-keymap) 5962 (define-key [mouse-1] command)))))) 5963 result)) 5964 :mode 'unchanged 5965 :cancel-token :document-color-token))) 5966 5967 5968 5969 (defun lsp--action-trigger-parameter-hints (_command) 5970 "Handler for editor.action.triggerParameterHints." 5971 (when (member :on-server-request lsp-signature-auto-activate) 5972 (lsp-signature-activate))) 5973 5974 (defun lsp--action-trigger-suggest (_command) 5975 "Handler for editor.action.triggerSuggest." 5976 (cond 5977 ((and (bound-and-true-p company-mode) 5978 (fboundp 'company-auto-begin) 5979 (fboundp 'company-post-command)) 5980 (run-at-time 0 nil 5981 (lambda () 5982 (let ((this-command 'company-idle-begin) 5983 (company-minimum-prefix-length 0)) 5984 (company-auto-begin) 5985 (company-post-command))))) 5986 (t 5987 (completion-at-point)))) 5988 5989 (defconst lsp--default-action-handlers 5990 (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints) 5991 ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest)) 5992 "Default action handlers.") 5993 5994 (defun lsp--find-action-handler (command) 5995 "Find action handler for particular COMMAND." 5996 (or 5997 (--some (-some->> it 5998 (lsp--workspace-client) 5999 (lsp--client-action-handlers) 6000 (gethash command)) 6001 (lsp-workspaces)) 6002 (gethash command lsp--default-action-handlers))) 6003 6004 (defun lsp--text-document-code-action-params (&optional kind) 6005 "Code action params." 6006 (list :textDocument (lsp--text-document-identifier) 6007 :range (if (use-region-p) 6008 (lsp--region-to-range (region-beginning) (region-end)) 6009 (lsp--region-to-range (point) (point))) 6010 :context `( :diagnostics ,(lsp-cur-possition-diagnostics) 6011 ,@(when kind (list :only (vector kind)))))) 6012 6013 (defun lsp-code-actions-at-point (&optional kind) 6014 "Retrieve the code actions for the active region or the current line. 6015 It will filter by KIND if non nil." 6016 (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind))) 6017 6018 (defun lsp-execute-code-action-by-kind (command-kind) 6019 "Execute code action by COMMAND-KIND." 6020 (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind) 6021 (-filter (-lambda ((&CodeAction :kind?)) 6022 (and kind? (s-prefix? command-kind kind?)))) 6023 lsp--select-action))) 6024 (lsp-execute-code-action action) 6025 (signal 'lsp-no-code-actions '(command-kind)))) 6026 6027 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point) 6028 6029 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?)) 6030 "Parse and execute a code ACTION represented as a Command LSP type." 6031 (let ((server-id (->> (lsp-workspaces) 6032 (cl-first) 6033 (or lsp--cur-workspace) 6034 (lsp--workspace-client) 6035 (lsp--client-server-id)))) 6036 (condition-case nil 6037 (with-no-warnings 6038 (lsp-execute-command server-id (intern command) arguments?)) 6039 (cl-no-applicable-method 6040 (if-let ((action-handler (lsp--find-action-handler command))) 6041 (funcall action-handler action) 6042 (lsp-send-execute-command command arguments?)))))) 6043 6044 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?)) 6045 "Execute code action ACTION. For example, when text under the 6046 caret has a suggestion to apply a fix from an lsp-server, calling 6047 this function will do so. 6048 If ACTION is not set it will be selected from `lsp-code-actions-at-point'. 6049 Request codeAction/resolve for more info if server supports." 6050 (interactive (list (lsp--select-action (lsp-code-actions-at-point)))) 6051 (if (and (lsp-feature? "codeAction/resolve") 6052 (not command?) 6053 (not edit?)) 6054 (lsp--execute-code-action (lsp-request "codeAction/resolve" action)) 6055 (lsp--execute-code-action action))) 6056 6057 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?)) 6058 "Execute code action ACTION." 6059 (when edit? 6060 (lsp--apply-workspace-edit edit? 'code-action)) 6061 6062 (cond 6063 ((stringp command?) (lsp--execute-command action)) 6064 ((lsp-command? command?) (progn 6065 (when-let ((action-filter (->> (lsp-workspaces) 6066 (cl-first) 6067 (or lsp--cur-workspace) 6068 (lsp--workspace-client) 6069 (lsp--client-action-filter)))) 6070 (funcall action-filter command?)) 6071 (lsp--execute-command command?))))) 6072 6073 (lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments) 6074 "Patch incorrect boolean argument values in the provided `CodeAction' command 6075 in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values 6076 in this list can be either symbols or lists of symbols that 6077 represent paths to boolean arguments in code actions: 6078 6079 > (lsp-fix-code-action-booleans command `(:foo :bar (:some :nested :boolean))) 6080 6081 When there are available code actions, the server sends 6082 `lsp-mode' a list of possible command names and arguments as 6083 JSON. `lsp-mode' parses all boolean false values as `nil'. As a 6084 result code action arguments containing falsy values don't 6085 roundtrip correctly because `lsp-mode' will end up sending null 6086 values back to the client. This list makes it possible to 6087 selectively transform `nil' values back into `:json-false'." 6088 (seq-doseq (path boolean-action-arguments) 6089 (seq-doseq (args arguments?) 6090 (lsp--fix-nested-boolean args (if (listp path) path (list path)))))) 6091 6092 (defun lsp--fix-nested-boolean (structure path) 6093 "Traverse STRUCTURE using the paths from the PATH list, changing the value to 6094 `:json-false' if it was `nil'. PATH should be a list containing 6095 one or more symbols, and STRUCTURE should be compatible with 6096 `lsp-member?', `lsp-get', and `lsp-put'." 6097 (let ((key (car path)) 6098 (rest (cdr path))) 6099 (if (null rest) 6100 ;; `lsp-put' returns `nil' both when the key doesn't exist and when the 6101 ;; value is `nil', so we need to explicitly check its presence here 6102 (when (and (lsp-member? structure key) (not (lsp-get structure key))) 6103 (lsp-put structure key :json-false)) 6104 ;; If `key' does not exist, then we'll silently ignore it 6105 (when-let ((child (lsp-get structure key))) 6106 (lsp--fix-nested-boolean child rest))))) 6107 6108 (defvar lsp--formatting-indent-alist 6109 ;; Taken from `dtrt-indent-mode' 6110 '( 6111 (ada-mode . ada-indent) ; Ada 6112 (ada-ts-mode . ada-ts-mode-indent-offset) 6113 (c++-mode . c-basic-offset) ; C++ 6114 (c++-ts-mode . c-ts-mode-indent-offset) 6115 (c-mode . c-basic-offset) ; C 6116 (c-ts-mode . c-ts-mode-indent-offset) 6117 (cperl-mode . cperl-indent-level) ; Perl 6118 (crystal-mode . crystal-indent-level) ; Crystal (Ruby) 6119 (csharp-mode . c-basic-offset) ; C# 6120 (csharp-tree-sitter-mode . csharp-tree-sitter-indent-offset) ; C# 6121 (csharp-ts-mode . csharp-ts-mode-indent-offset) ; C# (tree-sitter, Emacs29) 6122 (css-mode . css-indent-offset) ; CSS 6123 (d-mode . c-basic-offset) ; D 6124 (enh-ruby-mode . enh-ruby-indent-level) ; Ruby 6125 (erlang-mode . erlang-indent-level) ; Erlang 6126 (ess-mode . ess-indent-offset) ; ESS (R) 6127 (go-ts-mode . go-ts-mode-indent-offset) 6128 (gpr-mode . gpr-indent-offset) ; GNAT Project 6129 (gpr-ts-mode . gpr-ts-mode-indent-offset) 6130 (hack-mode . hack-indent-offset) ; Hack 6131 (java-mode . c-basic-offset) ; Java 6132 (java-ts-mode . java-ts-mode-indent-offset) 6133 (jde-mode . c-basic-offset) ; Java (JDE) 6134 (js-mode . js-indent-level) ; JavaScript 6135 (js-ts-mode . js-indent-level) 6136 (js2-mode . js2-basic-offset) ; JavaScript-IDE 6137 (js3-mode . js3-indent-level) ; JavaScript-IDE 6138 (json-mode . js-indent-level) ; JSON 6139 (json-ts-mode . json-ts-mode-indent-offset) 6140 (lua-mode . lua-indent-level) ; Lua 6141 (lua-ts-mode . lua-ts-indent-offset) 6142 (nxml-mode . nxml-child-indent) ; XML 6143 (objc-mode . c-basic-offset) ; Objective C 6144 (pascal-mode . pascal-indent-level) ; Pascal 6145 (perl-mode . perl-indent-level) ; Perl 6146 (php-mode . c-basic-offset) ; PHP 6147 (php-ts-mode . php-ts-mode-indent-offset) ; PHP 6148 (powershell-mode . powershell-indent) ; PowerShell 6149 (powershell-ts-mode . powershell-ts-mode-indent-offset) ; PowerShell 6150 (raku-mode . raku-indent-offset) ; Perl6/Raku 6151 (ruby-mode . ruby-indent-level) ; Ruby 6152 (rust-mode . rust-indent-offset) ; Rust 6153 (rust-ts-mode . rust-ts-mode-indent-offset) 6154 (rustic-mode . rustic-indent-offset) ; Rust 6155 (scala-mode . scala-indent:step) ; Scala 6156 (sgml-mode . sgml-basic-offset) ; SGML 6157 (sh-mode . sh-basic-offset) ; Shell Script 6158 (toml-ts-mode . toml-ts-mode-indent-offset) 6159 (typescript-mode . typescript-indent-level) ; Typescript 6160 (typescript-ts-mode . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29) 6161 (yaml-mode . yaml-indent-offset) ; YAML 6162 (yang-mode . c-basic-offset) ; YANG (yang-mode) 6163 6164 (default . standard-indent)) ; default fallback 6165 "A mapping from `major-mode' to its indent variable.") 6166 6167 (defun lsp--get-indent-width (mode) 6168 "Get indentation offset for MODE." 6169 (or (alist-get mode lsp--formatting-indent-alist) 6170 (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default)))) 6171 6172 (defun lsp--make-document-formatting-params () 6173 "Create document formatting params." 6174 (lsp-make-document-formatting-params 6175 :text-document (lsp--text-document-identifier) 6176 :options (lsp-make-formatting-options 6177 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 6178 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 6179 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 6180 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 6181 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)))) 6182 6183 (defun lsp-format-buffer () 6184 "Ask the server to format this document." 6185 (interactive "*") 6186 (cond ((lsp-feature? "textDocument/formatting") 6187 (let ((edits (lsp-request "textDocument/formatting" 6188 (lsp--make-document-formatting-params)))) 6189 (if (seq-empty-p edits) 6190 (lsp--info "No formatting changes provided") 6191 (lsp--apply-text-edits edits 'format)))) 6192 ((lsp-feature? "textDocument/rangeFormatting") 6193 (save-restriction 6194 (widen) 6195 (lsp-format-region (point-min) (point-max)))) 6196 (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider"))))) 6197 6198 (defun lsp-format-region (s e) 6199 "Ask the server to format the region, or if none is selected, the current line." 6200 (interactive "r") 6201 (let ((edits (lsp-request 6202 "textDocument/rangeFormatting" 6203 (lsp--make-document-range-formatting-params s e)))) 6204 (if (seq-empty-p edits) 6205 (lsp--info "No formatting changes provided") 6206 (lsp--apply-text-edits edits 'format)))) 6207 6208 (defmacro lsp-make-interactive-code-action (func-name code-action-kind) 6209 "Define an interactive function FUNC-NAME that attempts to 6210 execute a CODE-ACTION-KIND action." 6211 `(defun ,(intern (concat "lsp-" (symbol-name func-name))) () 6212 ,(format "Perform the %s code action, if available." code-action-kind) 6213 (interactive) 6214 ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to 6215 ;; auto-execute here: the user has specified exactly what they want. 6216 (let ((lsp-auto-execute-action t)) 6217 (condition-case nil 6218 (lsp-execute-code-action-by-kind ,code-action-kind) 6219 (lsp-no-code-actions 6220 (when (called-interactively-p 'any) 6221 (lsp--info ,(format "%s action not available" code-action-kind)))))))) 6222 6223 (lsp-make-interactive-code-action organize-imports "source.organizeImports") 6224 6225 (defun lsp--make-document-range-formatting-params (start end) 6226 "Make DocumentRangeFormattingParams for selected region." 6227 (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params) 6228 (lsp--region-to-range start end))) 6229 6230 (defconst lsp--highlight-kind-face 6231 '((1 . lsp-face-highlight-textual) 6232 (2 . lsp-face-highlight-read) 6233 (3 . lsp-face-highlight-write))) 6234 6235 (defun lsp--remove-overlays (name) 6236 (save-restriction 6237 (widen) 6238 (remove-overlays (point-min) (point-max) name t))) 6239 6240 (defun lsp-document-highlight () 6241 "Highlight all relevant references to the symbol under point." 6242 (interactive) 6243 (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights 6244 (setq lsp--have-document-highlights nil 6245 lsp--symbol-bounds-of-last-highlight-invocation nil) 6246 (let ((lsp-enable-symbol-highlighting t)) 6247 (lsp--document-highlight))) 6248 6249 (defun lsp--document-highlight-callback (highlights) 6250 "Create a callback to process the reply of a 6251 `textDocument/documentHighlight' message for the buffer BUF. 6252 A reference is highlighted only if it is visible in a window." 6253 (lsp--remove-overlays 'lsp-highlight) 6254 6255 (let* ((wins-visible-pos (-map (lambda (win) 6256 (cons (1- (line-number-at-pos (window-start win) t)) 6257 (1+ (line-number-at-pos (window-end win) t)))) 6258 (get-buffer-window-list nil nil 'visible)))) 6259 (setq lsp--have-document-highlights t) 6260 (-map 6261 (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line) 6262 :end (end &as &Position :line end-line)) 6263 :kind?)) 6264 (-map 6265 (-lambda ((start-window . end-window)) 6266 ;; Make the overlay only if the reference is visible 6267 (when (and (> (1+ start-line) start-window) 6268 (< (1+ end-line) end-window)) 6269 (let ((start-point (lsp--position-to-point start)) 6270 (end-point (lsp--position-to-point end))) 6271 (when (not (and lsp-symbol-highlighting-skip-current 6272 (<= start-point (point) end-point))) 6273 (-doto (make-overlay start-point end-point) 6274 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face))) 6275 (overlay-put 'lsp-highlight t)))))) 6276 wins-visible-pos)) 6277 highlights))) 6278 6279 (defcustom lsp-symbol-kinds 6280 '((1 . "File") 6281 (2 . "Module") 6282 (3 . "Namespace") 6283 (4 . "Package") 6284 (5 . "Class") 6285 (6 . "Method") 6286 (7 . "Property") 6287 (8 . "Field") 6288 (9 . "Constructor") 6289 (10 . "Enum") 6290 (11 . "Interface") 6291 (12 . "Function") 6292 (13 . "Variable") 6293 (14 . "Constant") 6294 (15 . "String") 6295 (16 . "Number") 6296 (17 . "Boolean") 6297 (18 . "Array") 6298 (19 . "Object") 6299 (20 . "Key") 6300 (21 . "Null") 6301 (22 . "Enum Member") 6302 (23 . "Struct") 6303 (24 . "Event") 6304 (25 . "Operator") 6305 (26 . "Type Parameter")) 6306 "Alist mapping SymbolKinds to human-readable strings. 6307 Various Symbol objects in the LSP protocol have an integral type, 6308 specifying what they are. This alist maps such type integrals to 6309 readable representations of them. See 6310 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/', 6311 namespace SymbolKind." 6312 :group 'lsp-mode 6313 :type '(alist :key-type integer :value-type string)) 6314 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds) 6315 6316 (lsp-defun lsp--symbol-information-to-xref 6317 ((&SymbolInformation :kind :name 6318 :location (&Location :uri :range (&Range :start 6319 (&Position :line :character))))) 6320 "Return a `xref-item' from SYMBOL information." 6321 (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name) 6322 (xref-make-file-location (lsp--uri-to-path uri) 6323 line 6324 character))) 6325 6326 (defun lsp--get-document-symbols () 6327 "Get document symbols. 6328 6329 If the buffer has not been modified since symbols were last 6330 retrieved, simply return the latest result. 6331 6332 Else, if the request was initiated by Imenu updating its menu-bar 6333 entry, perform it asynchronously; i.e., give Imenu the latest 6334 result and then force a refresh when a new one is available. 6335 6336 Else (e.g., due to interactive use of `imenu' or `xref'), 6337 perform the request synchronously." 6338 (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick) 6339 lsp--document-symbols 6340 (let ((method "textDocument/documentSymbol") 6341 (params `(:textDocument ,(lsp--text-document-identifier))) 6342 (tick (buffer-chars-modified-tick))) 6343 (if (not lsp--document-symbols-request-async) 6344 (prog1 6345 (setq lsp--document-symbols (lsp-request method params)) 6346 (setq lsp--document-symbols-tick tick)) 6347 (lsp-request-async method params 6348 (lambda (document-symbols) 6349 (setq lsp--document-symbols document-symbols 6350 lsp--document-symbols-tick tick) 6351 (lsp--imenu-refresh)) 6352 :mode 'alive 6353 :cancel-token :document-symbols) 6354 lsp--document-symbols)))) 6355 6356 (advice-add 'imenu-update-menubar :around 6357 (lambda (oldfun &rest r) 6358 (let ((lsp--document-symbols-request-async t)) 6359 (apply oldfun r)))) 6360 6361 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position) 6362 "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION." 6363 (-let (((symbol &as &DocumentSymbol? :children?) 6364 (seq-find (-lambda ((&DocumentSymbol :range)) 6365 (lsp-point-in-range? current-position range)) 6366 document-symbols))) 6367 (if children? 6368 (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position)) 6369 (when symbol 6370 (list symbol))))) 6371 6372 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?)) 6373 "Convert a SymbolInformation to a DocumentInformation" 6374 (lsp-make-document-symbol :name name 6375 :kind kind 6376 :range (lsp:location-range location) 6377 :children? nil 6378 :deprecated? deprecated? 6379 :selection-range (lsp:location-range location) 6380 :detail? container-name?)) 6381 6382 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position) 6383 "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION." 6384 (--> symbols-informations 6385 (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range))) 6386 (when (lsp-point-in-range? current-position range) 6387 (lsp--symbol-information->document-symbol symbol))) 6388 it) 6389 (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position)) 6390 (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position))) 6391 (and (lsp--position-compare b-start-position a-start-position) 6392 (lsp--position-compare a-end-position b-end-position)))))) 6393 6394 (defun lsp--symbols->document-symbols-hierarchy (symbols) 6395 "Convert SYMBOLS to symbols-hierarchy." 6396 (when-let ((first-symbol (lsp-seq-first symbols))) 6397 (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line) 6398 :character (plist-get (lsp--cur-position) :character)))) 6399 (if (lsp-symbol-information? first-symbol) 6400 (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position) 6401 (lsp--document-symbols->document-symbols-hierarchy symbols cur-position))))) 6402 6403 (defun lsp--xref-backend () 'xref-lsp) 6404 6405 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp))) 6406 (propertize (or (thing-at-point 'symbol) "") 6407 'identifier-at-point t)) 6408 6409 (defun lsp--xref-elements-index (symbols path) 6410 (-mapcat 6411 (-lambda (sym) 6412 (pcase-exhaustive sym 6413 ((lsp-interface DocumentSymbol :name :children? :selection-range (lsp-interface Range :start)) 6414 (cons (cons (concat path name) 6415 (lsp--position-to-point start)) 6416 (lsp--xref-elements-index children? (concat path name " / ")))) 6417 ((lsp-interface SymbolInformation :name :location (lsp-interface Location :range (lsp-interface Range :start))) 6418 (list (cons (concat path name) 6419 (lsp--position-to-point start)))))) 6420 symbols)) 6421 6422 (defvar-local lsp--symbols-cache nil) 6423 6424 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp))) 6425 (if (lsp--find-workspaces-for "textDocument/documentSymbol") 6426 (progn 6427 (setq lsp--symbols-cache (lsp--xref-elements-index 6428 (lsp--get-document-symbols) nil)) 6429 lsp--symbols-cache) 6430 (list (propertize (or (thing-at-point 'symbol) "") 6431 'identifier-at-point t)))) 6432 6433 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier) 6434 (save-excursion 6435 (unless (get-text-property 0 'identifier-at-point identifier) 6436 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6437 (user-error "Unable to find symbol %s in current document" identifier))))) 6438 (lsp--locations-to-xref-items (lsp-request "textDocument/definition" 6439 (lsp--text-document-position-params))))) 6440 6441 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier) 6442 (save-excursion 6443 (unless (get-text-property 0 'identifier-at-point identifier) 6444 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6445 (user-error "Unable to find symbol %s" identifier))))) 6446 (lsp--locations-to-xref-items (lsp-request "textDocument/references" 6447 (lsp--make-reference-params nil lsp-references-exclude-definition))))) 6448 6449 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern) 6450 (seq-map #'lsp--symbol-information-to-xref 6451 (lsp-request "workspace/symbol" `(:query ,pattern)))) 6452 6453 (defcustom lsp-rename-use-prepare t 6454 "Whether `lsp-rename' should do a prepareRename first. 6455 For some language servers, textDocument/prepareRename might be 6456 too slow, in which case this variable may be set to nil. 6457 `lsp-rename' will then use `thing-at-point' `symbol' to determine 6458 the symbol to rename at point." 6459 :group 'lsp-mode 6460 :type 'boolean) 6461 6462 (defun lsp--get-symbol-to-rename () 6463 "Get a symbol to rename and placeholder at point. 6464 Returns a cons ((START . END) . PLACEHOLDER?), and nil if 6465 renaming is generally supported but cannot be done at point. 6466 START and END are the bounds of the identifiers being renamed, 6467 while PLACEHOLDER?, is either nil or a string suggested by the 6468 language server as the initial input of a new-name prompt." 6469 (unless (lsp-feature? "textDocument/rename") 6470 (error "The connected server(s) doesn't support renaming")) 6471 (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename")) 6472 (when-let ((response 6473 (lsp-request "textDocument/prepareRename" 6474 (lsp--text-document-position-params)))) 6475 (let* ((bounds (lsp--range-to-region 6476 (if (lsp-range? response) 6477 response 6478 (lsp:prepare-rename-result-range response)))) 6479 (placeholder 6480 (and (not (lsp-range? response)) 6481 (lsp:prepare-rename-result-placeholder response)))) 6482 (cons bounds placeholder))) 6483 (when-let ((bounds (bounds-of-thing-at-point 'symbol))) 6484 (cons bounds nil)))) 6485 6486 (defface lsp-face-rename '((t :underline t)) 6487 "Face used to highlight the identifier being renamed. 6488 Renaming can be done using `lsp-rename'." 6489 :group 'lsp-mode) 6490 6491 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face)) 6492 "Face used to display the rename placeholder in. 6493 When calling `lsp-rename' interactively, this will be the face of 6494 the new name." 6495 :group 'lsp-mode) 6496 6497 (defvar lsp-rename-history '() 6498 "History for `lsp--read-rename'.") 6499 6500 (defun lsp--read-rename (at-point) 6501 "Read a new name for a `lsp-rename' at `point' from the user. 6502 AT-POINT shall be a structure as returned by 6503 `lsp--get-symbol-to-rename'. 6504 6505 Returns a string, which should be the new name for the identifier 6506 at point. If renaming cannot be done at point (as determined from 6507 AT-POINT), throw a `user-error'. 6508 6509 This function is for use in `lsp-rename' only, and shall not be 6510 relied upon." 6511 (unless at-point 6512 (user-error "`lsp-rename' is invalid here")) 6513 (-let* ((((start . end) . placeholder?) at-point) 6514 ;; Do the `buffer-substring' first to not include `lsp-face-rename' 6515 (rename-me (buffer-substring start end)) 6516 (placeholder (or placeholder? rename-me)) 6517 (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face)) 6518 6519 overlay) 6520 ;; We need unwind protect, as the user might cancel here, causing the 6521 ;; overlay to linger. 6522 (unwind-protect 6523 (progn 6524 (setq overlay (make-overlay start end)) 6525 (overlay-put overlay 'face 'lsp-face-rename) 6526 6527 (read-string (format "Rename %s to: " rename-me) placeholder 6528 'lsp-rename-history)) 6529 (and overlay (delete-overlay overlay))))) 6530 6531 (defun lsp-rename (newname) 6532 "Rename the symbol (and all references to it) under point to NEWNAME." 6533 (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename)))) 6534 (when-let ((edits (lsp-request "textDocument/rename" 6535 `( :textDocument ,(lsp--text-document-identifier) 6536 :position ,(lsp--cur-position) 6537 :newName ,newname)))) 6538 (lsp--apply-workspace-edit edits 'rename))) 6539 6540 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?) 6541 "Advice around function `rename-file'. 6542 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?. 6543 6544 This advice sends workspace/willRenameFiles before renaming file 6545 to check if server wants to apply any workspaceEdits after renamed." 6546 (if (and lsp-apply-edits-after-file-operations 6547 (lsp--send-will-rename-files-p old-name)) 6548 (let ((params (lsp-make-rename-files-params 6549 :files (vector (lsp-make-file-rename 6550 :oldUri (lsp--path-to-uri old-name) 6551 :newUri (lsp--path-to-uri new-name)))))) 6552 (when-let ((edits (lsp-request "workspace/willRenameFiles" params))) 6553 (lsp--apply-workspace-edit edits 'rename-file) 6554 (funcall old-func old-name new-name ok-if-already-exists?) 6555 (when (lsp--send-did-rename-files-p) 6556 (lsp-notify "workspace/didRenameFiles" params)))) 6557 (funcall old-func old-name new-name ok-if-already-exists?))) 6558 6559 (advice-add 'rename-file :around #'lsp--on-rename-file) 6560 6561 (defcustom lsp-xref-force-references nil 6562 "If non-nil threat everything as references(e. g. jump if only one item.)" 6563 :group 'lsp-mode 6564 :type 'boolean) 6565 6566 (defun lsp-show-xrefs (xrefs display-action references?) 6567 (unless (region-active-p) (push-mark nil t)) 6568 (if (boundp 'xref-show-definitions-function) 6569 (with-no-warnings 6570 (xref-push-marker-stack) 6571 (funcall (if (and references? (not lsp-xref-force-references)) 6572 xref-show-xrefs-function 6573 xref-show-definitions-function) 6574 (-const xrefs) 6575 `((window . ,(selected-window)) 6576 (display-action . ,display-action) 6577 ,(if (and references? (not lsp-xref-force-references)) 6578 `(auto-jump . ,xref-auto-jump-to-first-xref) 6579 `(auto-jump . ,xref-auto-jump-to-first-definition))))) 6580 (xref--show-xrefs xrefs display-action))) 6581 6582 (cl-defmethod seq-empty-p ((ht hash-table)) 6583 "Function `seq-empty-p' for hash-table." 6584 (hash-table-empty-p ht)) 6585 6586 (cl-defun lsp-find-locations (method &optional extra &key display-action references?) 6587 "Send request named METHOD and get cross references of the symbol under point. 6588 EXTRA is a plist of extra parameters. 6589 REFERENCES? t when METHOD returns references." 6590 (let ((loc (lsp-request method 6591 (append (lsp--text-document-position-params) extra)))) 6592 (if (seq-empty-p loc) 6593 (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) "")) 6594 (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?)))) 6595 6596 (cl-defun lsp-find-declaration (&key display-action) 6597 "Find declarations of the symbol under point." 6598 (interactive) 6599 (lsp-find-locations "textDocument/declaration" nil :display-action display-action)) 6600 6601 (cl-defun lsp-find-definition (&key display-action) 6602 "Find definitions of the symbol under point." 6603 (interactive) 6604 (lsp-find-locations "textDocument/definition" nil :display-action display-action)) 6605 6606 (defun lsp-find-definition-mouse (click) 6607 "Click to start `lsp-find-definition' at clicked point." 6608 (interactive "e") 6609 (let* ((ec (event-start click)) 6610 (p1 (posn-point ec)) 6611 (w1 (posn-window ec))) 6612 (select-window w1) 6613 (goto-char p1) 6614 (lsp-find-definition))) 6615 6616 (cl-defun lsp-find-implementation (&key display-action) 6617 "Find implementations of the symbol under point." 6618 (interactive) 6619 (lsp-find-locations "textDocument/implementation" 6620 nil 6621 :display-action display-action 6622 :references? t)) 6623 6624 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action) 6625 "Find references of the symbol under point." 6626 (interactive "P") 6627 (lsp-find-locations "textDocument/references" 6628 (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition))))) 6629 :display-action display-action 6630 :references? t)) 6631 6632 (cl-defun lsp-find-type-definition (&key display-action) 6633 "Find type definitions of the symbol under point." 6634 (interactive) 6635 (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action)) 6636 6637 (defalias 'lsp-find-custom #'lsp-find-locations) 6638 (defalias 'lsp-goto-implementation #'lsp-find-implementation) 6639 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition) 6640 6641 (with-eval-after-load 'evil 6642 (evil-set-command-property 'lsp-find-definition :jump t) 6643 (evil-set-command-property 'lsp-find-implementation :jump t) 6644 (evil-set-command-property 'lsp-find-references :jump t) 6645 (evil-set-command-property 'lsp-find-type-definition :jump t)) 6646 6647 (defun lsp--workspace-method-supported? (check-command method capability workspace) 6648 (with-lsp-workspace workspace 6649 (if check-command 6650 (funcall check-command workspace) 6651 (or 6652 (when capability (lsp--capability capability)) 6653 (lsp--registered-capability method) 6654 (and (not capability) (not check-command)))))) 6655 6656 (defun lsp-disable-method-for-server (method server-id) 6657 "Disable METHOD for SERVER-ID." 6658 (cl-callf 6659 (lambda (reqs) 6660 (-let (((&plist :check-command :capability) reqs)) 6661 (list :check-command 6662 (lambda (workspace) 6663 (unless (-> workspace 6664 lsp--workspace-client 6665 lsp--client-server-id 6666 (eq server-id)) 6667 (lsp--workspace-method-supported? check-command 6668 method 6669 capability 6670 workspace)))))) 6671 (alist-get method lsp-method-requirements nil nil 'string=))) 6672 6673 (defun lsp--find-workspaces-for (msg-or-method) 6674 "Find all workspaces in the current project that can handle MSG." 6675 (let ((method (if (stringp msg-or-method) 6676 msg-or-method 6677 (plist-get msg-or-method :method)))) 6678 (-if-let (reqs (cdr (assoc method lsp-method-requirements))) 6679 (-let (((&plist :capability :check-command) reqs)) 6680 (-filter 6681 (-partial #'lsp--workspace-method-supported? 6682 check-command method capability) 6683 (lsp-workspaces))) 6684 (lsp-workspaces)))) 6685 6686 (defun lsp-can-execute-command? (command-name) 6687 "Returns non-nil if current language server(s) can execute COMMAND-NAME. 6688 The command is executed via `workspace/executeCommand'" 6689 (cl-position 6690 command-name 6691 (lsp:execute-command-options-commands 6692 (lsp:server-capabilities-execute-command-provider? 6693 (lsp--server-capabilities))) 6694 :test #'equal)) 6695 6696 (defalias 'lsp-feature? 'lsp--find-workspaces-for) 6697 6698 (cl-defmethod lsp-execute-command (_server _command _arguments) 6699 "Dispatch COMMAND execution." 6700 (signal 'cl-no-applicable-method nil)) 6701 6702 (defun lsp-workspace-command-execute (command &optional args) 6703 "Execute workspace COMMAND with ARGS." 6704 (condition-case-unless-debug err 6705 (let ((params (if args 6706 (list :command command :arguments args) 6707 (list :command command)))) 6708 (lsp-request "workspace/executeCommand" params)) 6709 (error 6710 (error "`workspace/executeCommand' with `%s' failed.\n\n%S" 6711 command err)))) 6712 6713 (defun lsp-send-execute-command (command &optional args) 6714 "Create and send a `workspace/executeCommand' message having command COMMAND 6715 and optional ARGS." 6716 (lsp-workspace-command-execute command args)) 6717 6718 (defalias 'lsp-point-to-position #'lsp--point-to-position) 6719 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier) 6720 (defalias 'lsp--send-execute-command #'lsp-send-execute-command) 6721 (defalias 'lsp-on-open #'lsp--text-document-did-open) 6722 (defalias 'lsp-on-save #'lsp--text-document-did-save) 6723 6724 (defun lsp--set-configuration (settings) 6725 "Set the SETTINGS for the lsp server." 6726 (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings))) 6727 6728 (defun lsp-current-buffer () 6729 (or lsp--virtual-buffer 6730 (current-buffer))) 6731 6732 (defun lsp-buffer-live-p (buffer-id) 6733 (if-let ((buffer-live (plist-get buffer-id :buffer-live?))) 6734 (funcall buffer-live buffer-id) 6735 (buffer-live-p buffer-id))) 6736 6737 (defun lsp--on-set-visited-file-name (old-func &rest args) 6738 "Advice around function `set-visited-file-name'. 6739 6740 This advice sends textDocument/didClose for the old file and 6741 textDocument/didOpen for the new file." 6742 (when lsp--cur-workspace 6743 (lsp--text-document-did-close t)) 6744 (prog1 (apply old-func args) 6745 (when lsp--cur-workspace 6746 (lsp--text-document-did-open)))) 6747 6748 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name) 6749 6750 (defcustom lsp-flush-delayed-changes-before-next-message t 6751 "If non-nil send the document changes update before sending other messages. 6752 6753 If nil, and `lsp-debounce-full-sync-notifications' is non-nil, 6754 change notifications will be throttled by 6755 `lsp-debounce-full-sync-notifications-interval' regardless of 6756 other messages." 6757 :group 'lsp-mode 6758 :type 'boolean) 6759 6760 (defvar lsp--not-flushing-delayed-changes t) 6761 6762 (defun lsp--send-no-wait (message proc) 6763 "Send MESSAGE to PROC without waiting for further output." 6764 6765 (when (and lsp--not-flushing-delayed-changes 6766 lsp-flush-delayed-changes-before-next-message) 6767 (let ((lsp--not-flushing-delayed-changes nil)) 6768 (lsp--flush-delayed-changes))) 6769 (lsp-process-send proc message)) 6770 6771 (define-error 'lsp-parse-error 6772 "Error parsing message from language server" 'lsp-error) 6773 (define-error 'lsp-unknown-message-type 6774 "Unknown message type" '(lsp-error lsp-parse-error)) 6775 (define-error 'lsp-unknown-json-rpc-version 6776 "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error)) 6777 (define-error 'lsp-no-content-length 6778 "Content-Length header missing in message" '(lsp-error lsp-parse-error)) 6779 (define-error 'lsp-invalid-header-name 6780 "Invalid header name" '(lsp-error lsp-parse-error)) 6781 6782 ;; id method 6783 ;; x x request 6784 ;; x . response 6785 ;; . x notification 6786 (defun lsp--get-message-type (json-data) 6787 "Get the message type from JSON-DATA." 6788 (if (lsp:json-message-id? json-data) 6789 (if (lsp:json-message-error? json-data) 6790 'response-error 6791 (if (lsp:json-message-method? json-data) 6792 'request 6793 'response)) 6794 'notification)) 6795 6796 (defconst lsp--default-notification-handlers 6797 (ht ("window/showMessage" #'lsp--window-show-message) 6798 ("window/logMessage" #'lsp--window-log-message) 6799 ("window/showInputBox" #'lsp--window-show-input-box) 6800 ("window/showQuickPick" #'lsp--window-show-quick-pick) 6801 ("textDocument/publishDiagnostics" #'lsp--on-diagnostics) 6802 ("textDocument/diagnosticsEnd" #'ignore) 6803 ("textDocument/diagnosticsBegin" #'ignore) 6804 ("telemetry/event" #'ignore) 6805 ("$/progress" (lambda (workspace params) 6806 (funcall lsp-progress-function workspace params))))) 6807 6808 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method)) 6809 "Call the appropriate handler for NOTIFICATION." 6810 (-let ((client (lsp--workspace-client workspace))) 6811 (when (lsp--log-io-p method) 6812 (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif) 6813 lsp--cur-workspace)) 6814 (if-let ((handler (or (gethash method (lsp--client-notification-handlers client)) 6815 (gethash method lsp--default-notification-handlers)))) 6816 (funcall handler workspace params) 6817 (when (and method (not (string-prefix-p "$" method))) 6818 (lsp-warn "Unknown notification: %s" method))))) 6819 6820 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items)) 6821 "Get section configuration. 6822 PARAMS are the `workspace/configuration' request params" 6823 (->> items 6824 (-map (-lambda ((&ConfigurationItem :section?)) 6825 (-let* ((path-parts (split-string section? "\\.")) 6826 (path-without-last (s-join "." (-slice path-parts 0 -1))) 6827 (path-parts-len (length path-parts))) 6828 (cond 6829 ((<= path-parts-len 1) 6830 (ht-get (lsp-configuration-section section?) 6831 (car-safe path-parts) 6832 (ht-create))) 6833 ((> path-parts-len 1) 6834 (when-let ((section (lsp-configuration-section path-without-last)) 6835 (keys path-parts)) 6836 (while (and keys section) 6837 (setf section (ht-get section (pop keys)))) 6838 section)))))) 6839 (apply #'vector))) 6840 6841 (defun lsp--ms-since (timestamp) 6842 "Integer number of milliseconds since TIMESTAMP. Fractions discarded." 6843 (floor (* 1000 (float-time (time-since timestamp))))) 6844 6845 (defun lsp--send-request-response (workspace recv-time request response) 6846 "Send the RESPONSE for REQUEST in WORKSPACE and log if needed." 6847 (-let* (((&JSONResponse :params :method :id) request) 6848 (process (lsp--workspace-proc workspace)) 6849 (response (lsp--make-response id response)) 6850 (req-entry (and lsp-log-io 6851 (lsp--make-log-entry method id params 'incoming-req))) 6852 (resp-entry (and lsp-log-io 6853 (lsp--make-log-entry method id response 'outgoing-resp 6854 (lsp--ms-since recv-time))))) 6855 ;; Send response to the server. 6856 (when (lsp--log-io-p method) 6857 (lsp--log-entry-new req-entry workspace) 6858 (lsp--log-entry-new resp-entry workspace)) 6859 (lsp--send-no-wait response process))) 6860 6861 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method)) 6862 "Call the appropriate handler for REQUEST, and send the return value to the 6863 server. WORKSPACE is the active workspace." 6864 (-let* ((recv-time (current-time)) 6865 (client (lsp--workspace-client workspace)) 6866 (buffers (lsp--workspace-buffers workspace)) 6867 handler 6868 (response (cond 6869 ((setq handler (gethash method (lsp--client-request-handlers client) nil)) 6870 (funcall handler workspace params)) 6871 ((setq handler (gethash method (lsp--client-async-request-handlers client) nil)) 6872 (funcall handler workspace params 6873 (-partial #'lsp--send-request-response 6874 workspace recv-time request)) 6875 'delay-response) 6876 ((equal method "client/registerCapability") 6877 (mapc #'lsp--server-register-capability 6878 (lsp:registration-params-registrations params)) 6879 (mapc (lambda (buf) 6880 (when (lsp-buffer-live-p buf) 6881 (lsp-with-current-buffer buf 6882 (lsp-unconfig-buffer) 6883 (lsp-configure-buffer)))) 6884 buffers) 6885 nil) 6886 ((equal method "window/showMessageRequest") 6887 (let ((choice (lsp--window-log-message-request params))) 6888 `(:title ,choice))) 6889 ((equal method "window/showDocument") 6890 (let ((success? (lsp--window-show-document params))) 6891 (lsp-make-show-document-result :success (or success? 6892 :json-false)))) 6893 ((equal method "client/unregisterCapability") 6894 (mapc #'lsp--server-unregister-capability 6895 (lsp:unregistration-params-unregisterations params)) 6896 (mapc (lambda (buf) 6897 (when (lsp-buffer-live-p buf) 6898 (lsp-with-current-buffer buf 6899 (lsp-unconfig-buffer) 6900 (lsp-configure-buffer)))) 6901 buffers) 6902 nil) 6903 ((equal method "workspace/applyEdit") 6904 (list :applied (condition-case err 6905 (prog1 t 6906 (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested)) 6907 (error 6908 (lsp--error "Failed to apply edits with message %s" 6909 (error-message-string err)) 6910 :json-false)))) 6911 ((equal method "workspace/configuration") 6912 (with-lsp-workspace workspace 6913 (if-let ((buf (car buffers))) 6914 (lsp-with-current-buffer buf 6915 (lsp--build-workspace-configuration-response params)) 6916 (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace) 6917 (lsp--build-workspace-configuration-response params))))) 6918 ((equal method "workspace/workspaceFolders") 6919 (let ((folders (or (-> workspace 6920 (lsp--workspace-client) 6921 (lsp--client-server-id) 6922 (gethash (lsp-session-server-id->folders (lsp-session)))) 6923 (lsp-session-folders (lsp-session))))) 6924 (->> folders 6925 (-distinct) 6926 (-map (lambda (folder) 6927 (list :uri (lsp--path-to-uri folder)))) 6928 (apply #'vector)))) 6929 ((equal method "window/workDoneProgress/create") 6930 nil ;; no specific reply, no processing required 6931 ) 6932 ((equal method "workspace/semanticTokens/refresh") 6933 (when (and lsp-semantic-tokens-enable 6934 (fboundp 'lsp--semantic-tokens-on-refresh)) 6935 (lsp--semantic-tokens-on-refresh workspace)) 6936 nil) 6937 ((equal method "workspace/codeLens/refresh") 6938 (when (and lsp-lens-enable 6939 (fboundp 'lsp--lens-on-refresh)) 6940 (lsp--lens-on-refresh workspace)) 6941 nil) 6942 (t (lsp-warn "Unknown request method: %s" method) nil)))) 6943 ;; Send response to the server. 6944 (unless (eq response 'delay-response) 6945 (lsp--send-request-response workspace recv-time request response)))) 6946 6947 (lsp-defun lsp--error-string ((&JSONError :message :code)) 6948 "Format ERR as a user friendly string." 6949 (format "Error from the Language Server: %s (%s)" 6950 message 6951 (or (car (alist-get code lsp--errors)) "Unknown error"))) 6952 6953 (defun lsp--get-body-length (headers) 6954 (let ((content-length (cdr (assoc "Content-Length" headers)))) 6955 (if content-length 6956 (string-to-number content-length) 6957 6958 ;; This usually means either the server or our parser is 6959 ;; screwed up with a previous Content-Length 6960 (error "No Content-Length header")))) 6961 6962 (defun lsp--parse-header (s) 6963 "Parse string S as a LSP (KEY . VAL) header." 6964 (let ((pos (string-match "\:" s)) 6965 key val) 6966 (unless pos 6967 (signal 'lsp-invalid-header-name (list s))) 6968 (setq key (substring s 0 pos) 6969 val (s-trim-left (substring s (+ 1 pos)))) 6970 (when (equal key "Content-Length") 6971 (cl-assert (cl-loop for c across val 6972 when (or (> c ?9) (< c ?0)) return nil 6973 finally return t) 6974 nil (format "Invalid Content-Length value: %s" val))) 6975 (cons key val))) 6976 6977 (defmacro lsp--read-json (str) 6978 "Read json string STR." 6979 (if (progn 6980 (require 'json) 6981 (fboundp 'json-parse-string)) 6982 `(json-parse-string ,str 6983 :object-type (if lsp-use-plists 6984 'plist 6985 'hash-table) 6986 :null-object nil 6987 :false-object nil) 6988 `(let ((json-array-type 'vector) 6989 (json-object-type (if lsp-use-plists 6990 'plist 6991 'hash-table)) 6992 (json-false nil)) 6993 (json-read-from-string ,str)))) 6994 6995 (defmacro lsp-json-read-buffer () 6996 "Read json from the current buffer." 6997 (if (progn 6998 (require 'json) 6999 (fboundp 'json-parse-buffer)) 7000 `(json-parse-buffer :object-type (if lsp-use-plists 7001 'plist 7002 'hash-table) 7003 :null-object nil 7004 :false-object nil) 7005 `(let ((json-array-type 'vector) 7006 (json-object-type (if lsp-use-plists 7007 'plist 7008 'hash-table)) 7009 (json-false nil)) 7010 (json-read)))) 7011 7012 (defun lsp--read-json-file (file-path) 7013 "Read json file." 7014 (-> file-path 7015 (f-read-text) 7016 (lsp--read-json))) 7017 7018 (defun lsp--parser-on-message (json-data workspace) 7019 "Called when the parser P read a complete MSG from the server." 7020 (with-demoted-errors "Error processing message %S." 7021 (with-lsp-workspace workspace 7022 (let* ((client (lsp--workspace-client workspace)) 7023 (id (--when-let (lsp:json-response-id json-data) 7024 (if (stringp it) (string-to-number it) it))) 7025 (data (lsp:json-response-result json-data))) 7026 (pcase (lsp--get-message-type json-data) 7027 ('response 7028 (cl-assert id) 7029 (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))] 7030 (when (lsp--log-io-p method) 7031 (lsp--log-entry-new 7032 (lsp--make-log-entry method id data 'incoming-resp 7033 (lsp--ms-since before-send)) 7034 workspace)) 7035 (when callback 7036 (remhash id (lsp--client-response-handlers client)) 7037 (funcall callback (lsp:json-response-result json-data))))) 7038 ('response-error 7039 (cl-assert id) 7040 (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))] 7041 (when (lsp--log-io-p method) 7042 (lsp--log-entry-new 7043 (lsp--make-log-entry method id (lsp:json-response-error-error json-data) 7044 'incoming-resp (lsp--ms-since before-send)) 7045 workspace)) 7046 (when callback 7047 (remhash id (lsp--client-response-handlers client)) 7048 (funcall callback (lsp:json-response-error-error json-data))))) 7049 ('notification 7050 (lsp--on-notification workspace json-data)) 7051 ('request (lsp--on-request workspace json-data))))))) 7052 7053 (defun lsp--create-filter-function (workspace) 7054 "Make filter for the workspace." 7055 (let ((body-received 0) 7056 leftovers body-length body chunk) 7057 (lambda (_proc input) 7058 (setf chunk (if (s-blank? leftovers) 7059 input 7060 (concat leftovers input))) 7061 7062 (let (messages) 7063 (while (not (s-blank? chunk)) 7064 (if (not body-length) 7065 ;; Read headers 7066 (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk))) 7067 ;; We've got all the headers, handle them all at once: 7068 (setf body-length (lsp--get-body-length 7069 (mapcar #'lsp--parse-header 7070 (split-string 7071 (substring-no-properties chunk 7072 (or (string-match-p "Content-Length" chunk) 7073 (error "Unable to find Content-Length header.")) 7074 body-sep-pos) 7075 "\r\n"))) 7076 body-received 0 7077 leftovers nil 7078 chunk (substring-no-properties chunk (+ body-sep-pos 4))) 7079 7080 ;; Haven't found the end of the headers yet. Save everything 7081 ;; for when the next chunk arrives and await further input. 7082 (setf leftovers chunk 7083 chunk nil)) 7084 (let* ((chunk-length (string-bytes chunk)) 7085 (left-to-receive (- body-length body-received)) 7086 (this-body (if (< left-to-receive chunk-length) 7087 (prog1 (substring-no-properties chunk 0 left-to-receive) 7088 (setf chunk (substring-no-properties chunk left-to-receive))) 7089 (prog1 chunk 7090 (setf chunk nil)))) 7091 (body-bytes (string-bytes this-body))) 7092 (push this-body body) 7093 (setf body-received (+ body-received body-bytes)) 7094 (when (>= chunk-length left-to-receive) 7095 (condition-case err 7096 (with-temp-buffer 7097 (apply #'insert 7098 (nreverse 7099 (prog1 body 7100 (setf leftovers nil 7101 body-length nil 7102 body-received nil 7103 body nil)))) 7104 (decode-coding-region (point-min) 7105 (point-max) 7106 'utf-8) 7107 (goto-char (point-min)) 7108 (push (lsp-json-read-buffer) messages)) 7109 7110 (error 7111 (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s" 7112 (concat leftovers input) 7113 err))))))) 7114 (mapc (lambda (msg) 7115 (lsp--parser-on-message msg workspace)) 7116 (nreverse messages)))))) 7117 7118 (defvar-local lsp--line-col-to-point-hash-table nil 7119 "Hash table with keys (line . col) and values that are either point positions 7120 or markers.") 7121 7122 (defcustom lsp-imenu-detailed-outline t 7123 "Whether `lsp-imenu' should include signatures. 7124 This will be ignored if the server doesn't provide the necessary 7125 information, for example if it doesn't support DocumentSymbols." 7126 :group 'lsp-imenu 7127 :type 'boolean) 7128 7129 (defcustom lsp-imenu-hide-parent-details t 7130 "Whether `lsp-imenu' should hide signatures of parent nodes." 7131 :group 'lsp-imenu 7132 :type 'boolean) 7133 7134 (defface lsp-details-face '((t :height 0.8 :inherit shadow)) 7135 "Used to display additional information throughout `lsp'. 7136 Things like line numbers, signatures, ... are considered 7137 additional information. Often, additional faces are defined that 7138 inherit from this face by default, like `lsp-signature-face', and 7139 they may be customized for finer control." 7140 :group 'lsp-mode) 7141 7142 (defface lsp-signature-face '((t :inherit lsp-details-face)) 7143 "Used to display signatures in `imenu', ...." 7144 :group 'lsp-mode) 7145 7146 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?) 7147 show-detail?) 7148 "Render INPUT0, an `&DocumentSymbol', to a string. 7149 If SHOW-DETAIL? is set, make use of its `:detail?' field (often 7150 the signature)." 7151 (let ((detail (and show-detail? (s-present? detail?) 7152 (propertize (concat " " (s-trim-left detail?)) 7153 'face 'lsp-signature-face))) 7154 (name (if deprecated? 7155 (propertize name 'face 'lsp-face-semhl-deprecated) name))) 7156 (concat name detail))) 7157 7158 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?) 7159 separator) 7160 "Render a piece of SymbolInformation. 7161 Handle :deprecated?. If SEPARATOR is non-nil, the 7162 symbol's (optional) parent, SEPARATOR and the symbol itself are 7163 concatenated." 7164 (when (and separator container-name? (not (string-empty-p container-name?))) 7165 (setq name (concat name separator container-name?))) 7166 (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name)) 7167 7168 (defun lsp--symbol-to-imenu-elem (sym) 7169 "Convert SYM to imenu element. 7170 7171 SYM is a SymbolInformation message. 7172 7173 Return a cons cell (full-name . start-point)." 7174 (let ((start-point (ht-get lsp--line-col-to-point-hash-table 7175 (lsp--get-line-and-col sym)))) 7176 (cons (lsp-render-symbol-information 7177 sym (and lsp-imenu-show-container-name 7178 lsp-imenu-container-name-separator)) 7179 start-point))) 7180 7181 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?)) 7182 "Convert SYM to hierarchical imenu elements. 7183 7184 SYM is a DocumentSymbol message. 7185 7186 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if 7187 SYM doesn't have any children. Otherwise return a cons cell with 7188 an alist 7189 7190 (\"symbol-name\" . ((\"(symbol-kind)\" . start-point) 7191 cons-cells-from-children))" 7192 (let ((filtered-children (lsp--imenu-filter-symbols children?)) 7193 (signature (lsp-render-symbol sym lsp-imenu-detailed-outline))) 7194 (if (seq-empty-p filtered-children) 7195 (cons signature 7196 (ht-get lsp--line-col-to-point-hash-table 7197 (lsp--get-line-and-col sym))) 7198 (cons signature 7199 (lsp--imenu-create-hierarchical-index filtered-children))))) 7200 7201 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind)) 7202 "Determine if SYM is for the current document and is to be shown." 7203 ;; It's a SymbolInformation or DocumentSymbol, which is always in the 7204 ;; current buffer file. 7205 (and lsp-imenu-index-symbol-kinds 7206 (numberp kind) 7207 (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup)) 7208 kind 7209 0))) 7210 (not (memql (aref lsp/symbol-kind-lookup clamped-kind) 7211 lsp-imenu-index-symbol-kinds))))) 7212 7213 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind)) 7214 "The string name of the kind of SYM." 7215 (alist-get kind lsp-symbol-kinds "Other")) 7216 7217 (defun lsp--get-line-and-col (sym) 7218 "Obtain the line and column corresponding to SYM." 7219 (-let* ((location (lsp:symbol-information-location sym)) 7220 (name-range (or (and location (lsp:location-range location)) 7221 (lsp:document-symbol-selection-range sym))) 7222 ((&Range :start (&Position :line :character)) name-range)) 7223 (cons line character))) 7224 7225 (defun lsp--collect-lines-and-cols (symbols) 7226 "Return a sorted list ((line . col) ...) of the locations of SYMBOLS." 7227 (let ((stack (mapcar 'identity symbols)) 7228 line-col-list) 7229 (while stack 7230 (let ((sym (pop stack))) 7231 (push (lsp--get-line-and-col sym) line-col-list) 7232 (unless (seq-empty-p (lsp:document-symbol-children? sym)) 7233 (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack))))) 7234 (-sort #'lsp--line-col-comparator line-col-list))) 7235 7236 (defun lsp--convert-line-col-to-points-batch (line-col-list) 7237 "Convert a sorted list of positions from line-column 7238 representation to point representation." 7239 (let ((line-col-to-point-map (ht-create)) 7240 (inhibit-field-text-motion t) 7241 (curr-line 0)) 7242 (lsp-save-restriction-and-excursion 7243 (goto-char (point-min)) 7244 (cl-loop for (line . col) in line-col-list do 7245 (forward-line (- line curr-line)) 7246 (setq curr-line line) 7247 (let ((line-end (line-end-position))) 7248 (if (or (not col) (> col (- line-end (point)))) 7249 (goto-char line-end) 7250 (forward-char col))) 7251 (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers 7252 (point-marker) 7253 (point))))) 7254 line-col-to-point-map)) 7255 7256 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2)) 7257 (or (< l1 l2) 7258 (and (= l1 l2) 7259 (cond ((and c1 c2) 7260 (< c1 c2)) 7261 (c1 t))))) 7262 7263 (defun lsp-imenu-create-uncategorized-index (symbols) 7264 "Create imenu index from document SYMBOLS. 7265 This function, unlike `lsp-imenu-create-categorized-index', does 7266 not categorize by type, but instead returns an `imenu' index 7267 corresponding to the symbol hierarchy returned by the server 7268 directly." 7269 (let* ((lsp--line-col-to-point-hash-table (-> symbols 7270 lsp--collect-lines-and-cols 7271 lsp--convert-line-col-to-points-batch))) 7272 (if (lsp--imenu-hierarchical-p symbols) 7273 (lsp--imenu-create-hierarchical-index symbols) 7274 (lsp--imenu-create-non-hierarchical-index symbols)))) 7275 7276 (defcustom lsp-imenu-symbol-kinds 7277 '((1 . "Files") 7278 (2 . "Modules") 7279 (3 . "Namespaces") 7280 (4 . "Packages") 7281 (5 . "Classes") 7282 (6 . "Methods") 7283 (7 . "Properties") 7284 (8 . "Fields") 7285 (9 . "Constructors") 7286 (10 . "Enums") 7287 (11 . "Interfaces") 7288 (12 . "Functions") 7289 (13 . "Variables") 7290 (14 . "Constants") 7291 (15 . "Strings") 7292 (16 . "Numbers") 7293 (17 . "Booleans") 7294 (18 . "Arrays") 7295 (19 . "Objects") 7296 (20 . "Keys") 7297 (21 . "Nulls") 7298 (22 . "Enum Members") 7299 (23 . "Structs") 7300 (24 . "Events") 7301 (25 . "Operators") 7302 (26 . "Type Parameters")) 7303 "`lsp-symbol-kinds', but only used by `imenu'. 7304 A new variable is needed, as it is `imenu' convention to use 7305 pluralized categories, which `lsp-symbol-kinds' doesn't. If the 7306 non-pluralized names are preferred, this can be set to 7307 `lsp-symbol-kinds'." 7308 :type '(alist :key-type integer :value-type string)) 7309 7310 (defun lsp--imenu-kind->name (kind) 7311 (alist-get kind lsp-imenu-symbol-kinds "?")) 7312 7313 (defun lsp-imenu-create-top-level-categorized-index (symbols) 7314 "Create an `imenu' index categorizing SYMBOLS by type. 7315 Only root symbols are categorized. 7316 7317 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS 7318 shall be a list of DocumentSymbols or SymbolInformation." 7319 (mapcan 7320 (-lambda ((type . symbols)) 7321 (let ((cat (lsp--imenu-kind->name type)) 7322 (symbols (lsp-imenu-create-uncategorized-index symbols))) 7323 ;; If there is no :kind (this is being defensive), or we couldn't look it 7324 ;; up, just display the symbols inline, without categories. 7325 (if cat (list (cons cat symbols)) symbols))) 7326 (sort (seq-group-by #'lsp:document-symbol-kind symbols) 7327 (-lambda ((kinda) (kindb)) (< kinda kindb))))) 7328 7329 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start))) 7330 "Convert an `&DocumentSymbol' to an `imenu' entry." 7331 (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start)) 7332 7333 (defun lsp--imenu-create-categorized-index-1 (symbols) 7334 "Returns an `imenu' index from SYMBOLS categorized by type. 7335 The result looks like this: ((\"Variables\" . (...)))." 7336 (->> 7337 symbols 7338 (mapcan 7339 (-lambda ((sym &as &DocumentSymbol :kind :children?)) 7340 (if (seq-empty-p children?) 7341 (list (list kind (lsp--symbol->imenu sym))) 7342 (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline 7343 (not lsp-imenu-hide-parent-details))))) 7344 (cons 7345 (list kind (lsp--symbol->imenu sym)) 7346 (mapcar (-lambda ((type . imenu-items)) 7347 (list type (cons parent (mapcan #'cdr imenu-items)))) 7348 (-group-by #'car (lsp--imenu-create-categorized-index-1 children?)))))))) 7349 (-group-by #'car) 7350 (mapcar 7351 (-lambda ((kind . syms)) 7352 (cons kind (mapcan #'cdr syms)))))) 7353 7354 (defun lsp--imenu-create-categorized-index (symbols) 7355 (let ((syms (lsp--imenu-create-categorized-index-1 symbols))) 7356 (dolist (sym syms) 7357 (setcar sym (lsp--imenu-kind->name (car sym)))) 7358 syms)) 7359 7360 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start)))) 7361 (cons (lsp-render-symbol-information sym nil) start)) 7362 7363 (defun lsp--imenu-create-categorized-index-flat (symbols) 7364 "Create a kind-categorized index for SymbolInformation." 7365 (mapcar (-lambda ((kind . syms)) 7366 (cons (lsp--imenu-kind->name kind) 7367 (mapcan (-lambda ((parent . children)) 7368 (let ((children (mapcar #'lsp--symbol-information->imenu children))) 7369 (if parent (list (cons parent children)) children))) 7370 (-group-by #'lsp:symbol-information-container-name? syms)))) 7371 (seq-group-by #'lsp:symbol-information-kind symbols))) 7372 7373 (defun lsp-imenu-create-categorized-index (symbols) 7374 (if (lsp--imenu-hierarchical-p symbols) 7375 (lsp--imenu-create-categorized-index symbols) 7376 (lsp--imenu-create-categorized-index-flat symbols))) 7377 7378 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index 7379 "Function that should create an `imenu' index. 7380 It will be called with a list of SymbolInformation or 7381 DocumentSymbols, whose first level is already filtered. It shall 7382 then return an appropriate `imenu' index (see 7383 `imenu-create-index-function'). 7384 7385 Note that this interface is not stable, and subject to change any 7386 time." 7387 :group 'lsp-imenu 7388 :type '(radio 7389 (const :tag "Categorize by type" 7390 lsp-imenu-create-categorized-index) 7391 (const :tag "Categorize root symbols by type" 7392 lsp-imenu-create-top-level-categorized-index) 7393 (const :tag "Uncategorized, inline entries" 7394 lsp-imenu-create-uncategorized-index) 7395 (function :tag "Custom function"))) 7396 7397 (defun lsp--imenu-create-index () 7398 "Create an `imenu' index based on the language server. 7399 Respects `lsp-imenu-index-function'." 7400 (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols)))) 7401 (funcall lsp-imenu-index-function symbols))) 7402 7403 (defun lsp--imenu-filter-symbols (symbols) 7404 "Filter out unsupported symbols from SYMBOLS." 7405 (seq-remove #'lsp--symbol-ignore symbols)) 7406 7407 (defun lsp--imenu-hierarchical-p (symbols) 7408 "Determine whether any element in SYMBOLS has children." 7409 (seq-some #'lsp-document-symbol? symbols)) 7410 7411 (defun lsp--imenu-create-non-hierarchical-index (symbols) 7412 "Create imenu index for non-hierarchical SYMBOLS. 7413 7414 SYMBOLS are a list of DocumentSymbol messages. 7415 7416 Return a nested alist keyed by symbol names. e.g. 7417 7418 ((\"SomeClass\" (\"(Class)\" . 10) 7419 (\"someField (Field)\" . 20) 7420 (\"someFunction (Function)\" . 25) 7421 (\"SomeSubClass\" (\"(Class)\" . 30) 7422 (\"someSubField (Field)\" . 35)) 7423 (\"someFunction (Function)\" . 40))" 7424 (seq-map (lambda (nested-alist) 7425 (cons (car nested-alist) 7426 (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist)))) 7427 (seq-group-by #'lsp--get-symbol-type symbols))) 7428 7429 (defun lsp--imenu-create-hierarchical-index (symbols) 7430 "Create imenu index for hierarchical SYMBOLS. 7431 7432 SYMBOLS are a list of DocumentSymbol messages. 7433 7434 Return a nested alist keyed by symbol names. e.g. 7435 7436 ((\"SomeClass\" (\"(Class)\" . 10) 7437 (\"someField (Field)\" . 20) 7438 (\"someFunction (Function)\" . 25) 7439 (\"SomeSubClass\" (\"(Class)\" . 30) 7440 (\"someSubField (Field)\" . 35)) 7441 (\"someFunction (Function)\" . 40))" 7442 (seq-map #'lsp--symbol-to-hierarchical-imenu-elem 7443 (seq-sort #'lsp--imenu-symbol-lessp symbols))) 7444 7445 (defun lsp--imenu-symbol-lessp (sym1 sym2) 7446 (let* ((compare-results (mapcar (lambda (method) 7447 (funcall (alist-get method lsp--imenu-compare-function-alist) 7448 sym1 sym2)) 7449 lsp-imenu-sort-methods)) 7450 (result (seq-find (lambda (result) 7451 (not (= result 0))) 7452 compare-results 7453 0))) 7454 (and (numberp result) (< result 0)))) 7455 7456 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left) 7457 (&SymbolInformation :kind right)) 7458 "Compare SYM1 and SYM2 by kind." 7459 (- left right)) 7460 7461 (defun lsp--imenu-compare-line-col (sym1 sym2) 7462 (if (lsp--line-col-comparator 7463 (lsp--get-line-and-col sym1) 7464 (lsp--get-line-and-col sym2)) 7465 -1 7466 1)) 7467 7468 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1) 7469 (&SymbolInformation :name name2)) 7470 "Compare SYM1 and SYM2 by name." 7471 (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2)))) 7472 (if (numberp result) result 0))) 7473 7474 (defun lsp--imenu-refresh () 7475 "Force Imenu to refresh itself." 7476 (imenu--menubar-select imenu--rescan-item)) 7477 7478 (defun lsp-enable-imenu () 7479 "Use lsp-imenu for the current buffer." 7480 (imenu--cleanup) 7481 (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index) 7482 (setq-local imenu-menubar-modified-tick -1) 7483 (setq-local imenu--index-alist nil) 7484 (when menu-bar-mode 7485 (lsp--imenu-refresh))) 7486 7487 (defun lsp-resolve-final-command (command &optional test?) 7488 "Resolve final function COMMAND." 7489 (let* ((command (lsp-resolve-value command)) 7490 (command (cl-etypecase command 7491 (list 7492 (cl-assert (seq-every-p (apply-partially #'stringp) command) nil 7493 "Invalid command list") 7494 command) 7495 (string (list command))))) 7496 (if (and (file-remote-p default-directory) (not test?)) 7497 (list shell-file-name "-c" 7498 (string-join (cons "stty raw > /dev/null;" 7499 (mapcar #'shell-quote-argument command)) 7500 " ")) 7501 command))) 7502 7503 (defun lsp-server-present? (final-command) 7504 "Check whether FINAL-COMMAND is present." 7505 (let ((binary-found? (executable-find (cl-first final-command) t))) 7506 (if binary-found? 7507 (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command)) 7508 (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command))) 7509 binary-found?)) 7510 7511 (defun lsp--value-to-string (value) 7512 "Convert VALUE to a string that can be set as value in an environment 7513 variable." 7514 (cond 7515 ((stringp value) value) 7516 ((booleanp value) (if value 7517 "1" 7518 "0")) 7519 ((and (sequencep value) 7520 (seq-every-p #'stringp value)) (string-join value ":")) 7521 (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables")))) 7522 7523 (defun lsp--compute-process-environment (environment-fn) 7524 "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'. 7525 Ignore non-boolean keys whose value is nil." 7526 (let ((environment (if environment-fn 7527 (funcall environment-fn) 7528 nil))) 7529 (-flatten (cons (cl-loop for (key . value) in environment 7530 if (or (eval value) 7531 (eq (get value 'custom-type) 'boolean)) 7532 collect (concat key "=" (lsp--value-to-string 7533 (eval value)))) 7534 process-environment)))) 7535 7536 (defun lsp--default-directory-for-connection (&optional path) 7537 "Return path to be used for the working directory of a LSP process. 7538 7539 If `lsp-use-workspace-root-for-server-default-directory' is 7540 non-nil, uses `lsp-workspace-root' to find the directory 7541 corresponding to PATH, else returns `default-directory'." 7542 (if lsp-use-workspace-root-for-server-default-directory 7543 (lsp-workspace-root path) 7544 default-directory)) 7545 7546 (defun lsp--fix-remote-cmd (program) 7547 "Helper for `lsp-stdio-connection'. 7548 Originally coppied from eglot." 7549 7550 (if (file-remote-p default-directory) 7551 (list shell-file-name "-c" 7552 (string-join (cons "stty raw > /dev/null;" 7553 (mapcar #'shell-quote-argument program)) 7554 " ")) 7555 program)) 7556 7557 (defvar tramp-use-ssh-controlmaster-options) 7558 (defvar tramp-ssh-controlmaster-options) 7559 7560 (defun lsp-stdio-connection (command &optional test-command) 7561 "Returns a connection property list using COMMAND. 7562 COMMAND can be: A string, denoting the command to launch the 7563 language server. A list of strings, denoting an executable with 7564 its command line arguments. A function, that either returns a 7565 string or a list of strings. In all cases, the launched language 7566 server should send and receive messages on standard I/O. 7567 TEST-COMMAND is a function with no arguments which returns 7568 whether the command is present or not. When not specified 7569 `lsp-mode' will check whether the first element of the list 7570 returned by COMMAND is available via `executable-find'" 7571 (cl-check-type command (or string 7572 function 7573 (and list 7574 (satisfies (lambda (l) 7575 (seq-every-p (lambda (el) 7576 (stringp el)) 7577 l)))))) 7578 (list :connect (lambda (filter sentinel name environment-fn workspace) 7579 (if (and (functionp 'json-rpc-connection) 7580 (not (file-remote-p default-directory))) 7581 (lsp-json-rpc-connection workspace (lsp-resolve-final-command command)) 7582 (let ((final-command (lsp-resolve-final-command command)) 7583 (process-name (generate-new-buffer-name name)) 7584 (process-environment 7585 (lsp--compute-process-environment environment-fn))) 7586 (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name))) 7587 (default-directory (lsp--default-directory-for-connection)) 7588 (tramp-use-ssh-controlmaster-options 'suppress) 7589 (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none") 7590 (proc (make-process 7591 :name process-name 7592 :connection-type 'pipe 7593 :buffer (format "*%s*" process-name) 7594 :coding 'no-conversion 7595 :command final-command 7596 :filter filter 7597 :sentinel sentinel 7598 :stderr stderr-buf 7599 :noquery t 7600 :file-handler t))) 7601 (set-process-query-on-exit-flag proc nil) 7602 (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) 7603 (with-current-buffer (get-buffer stderr-buf) 7604 ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc. 7605 (special-mode)) 7606 (cons proc proc))))) 7607 :test? (or 7608 test-command 7609 (lambda () 7610 (lsp-server-present? (lsp-resolve-final-command command t)))))) 7611 7612 (defun lsp--open-network-stream (host port name) 7613 "Open network stream to HOST:PORT. 7614 NAME will be passed to `open-network-stream'. 7615 RETRY-COUNT is the number of the retries. 7616 SLEEP-INTERVAL is the sleep interval between each retry." 7617 (let* ((retries 0) 7618 (sleep-interval 0.01) 7619 (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval)) 7620 connection) 7621 (while (and (not connection) (< retries number-of-retries)) 7622 (condition-case err 7623 (setq connection (open-network-stream name nil host port 7624 :type 'plain 7625 :coding 'no-conversion)) 7626 (file-error 7627 (let ((inhibit-message t)) 7628 (lsp--warn "Failed to connect to %s:%s with error message %s" 7629 host 7630 port 7631 (error-message-string err)) 7632 (sleep-for sleep-interval) 7633 (cl-incf retries))))) 7634 (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port)))) 7635 7636 (defun lsp--port-available (host port) 7637 "Return non-nil if HOST and PORT are available." 7638 (condition-case _err 7639 (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain)) 7640 (file-error t))) 7641 7642 (defun lsp--find-available-port (host starting-port) 7643 "Find available port on HOST starting from STARTING-PORT." 7644 (let ((port starting-port)) 7645 (while (not (lsp--port-available host port)) 7646 (cl-incf port)) 7647 port)) 7648 7649 (defun lsp-tcp-connection (command-fn) 7650 "Returns a connection property list similar to `lsp-stdio-connection'. 7651 COMMAND-FN can only be a function that takes a single argument, a 7652 port number. It should return a command for launches a language server 7653 process listening for TCP connections on the provided port." 7654 (cl-check-type command-fn function) 7655 (list 7656 :connect (lambda (filter sentinel name environment-fn _workspace) 7657 (let* ((host "localhost") 7658 (port (lsp--find-available-port host (cl-incf lsp--tcp-port))) 7659 (command (funcall command-fn port)) 7660 (final-command (if (consp command) command (list command))) 7661 (_ (unless (lsp-server-present? final-command) 7662 (user-error (format "Couldn't find executable %s" (cl-first final-command))))) 7663 (process-environment 7664 (lsp--compute-process-environment environment-fn)) 7665 (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion 7666 :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t)) 7667 (tcp-proc (lsp--open-network-stream host port (concat name "::tcp")))) 7668 7669 ;; TODO: Same :noquery issue (see above) 7670 (set-process-query-on-exit-flag proc nil) 7671 (set-process-query-on-exit-flag tcp-proc nil) 7672 (set-process-filter tcp-proc filter) 7673 (cons tcp-proc proc))) 7674 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7675 7676 (defalias 'lsp-tcp-server 'lsp-tcp-server-command) 7677 7678 (defun lsp-tcp-server-command (command-fn) 7679 "Create tcp server connection. 7680 In this mode Emacs is TCP server and the language server connects 7681 to it. COMMAND is function with one parameter(the port) and it 7682 should return the command to start the LS server." 7683 (cl-check-type command-fn function) 7684 (list 7685 :connect (lambda (filter sentinel name environment-fn _workspace) 7686 (let* (tcp-client-connection 7687 (tcp-server (make-network-process :name (format "*tcp-server-%s*" name) 7688 :buffer (format "*tcp-server-%s*" name) 7689 :family 'ipv4 7690 :service lsp--tcp-server-port 7691 :sentinel (lambda (proc _string) 7692 (lsp-log "Language server %s is connected." name) 7693 (setf tcp-client-connection proc)) 7694 :server 't)) 7695 (port (process-contact tcp-server :service)) 7696 (final-command (funcall command-fn port)) 7697 (process-environment 7698 (lsp--compute-process-environment environment-fn)) 7699 (cmd-proc (make-process :name name 7700 :connection-type 'pipe 7701 :coding 'no-conversion 7702 :command final-command 7703 :stderr (format "*tcp-server-%s*::stderr" name) 7704 :noquery t))) 7705 (let ((retries 0)) 7706 ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds) 7707 (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds))) 7708 (lsp--info "Waiting for connection for %s, retries: %s" name retries) 7709 (sit-for 0.500) 7710 (cl-incf retries))) 7711 7712 (unless tcp-client-connection 7713 (condition-case nil (delete-process tcp-server) (error)) 7714 (condition-case nil (delete-process cmd-proc) (error)) 7715 (error "Failed to create connection to %s on port %s" name port)) 7716 (lsp--info "Successfully connected to %s" name) 7717 7718 (set-process-query-on-exit-flag cmd-proc nil) 7719 (set-process-query-on-exit-flag tcp-client-connection nil) 7720 (set-process-query-on-exit-flag tcp-server nil) 7721 7722 (set-process-filter tcp-client-connection filter) 7723 (set-process-sentinel tcp-client-connection sentinel) 7724 (cons tcp-client-connection cmd-proc))) 7725 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7726 7727 (defalias 'lsp-tramp-connection 'lsp-stdio-connection) 7728 7729 (defun lsp--auto-configure () 7730 "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed." 7731 (when (functionp 'lsp-ui-mode) 7732 (lsp-ui-mode)) 7733 7734 (if lsp-headerline-breadcrumb-enable 7735 (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode) 7736 (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)) 7737 (if lsp-modeline-code-actions-enable 7738 (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode) 7739 (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)) 7740 (if lsp-modeline-diagnostics-enable 7741 (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode) 7742 (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)) 7743 (if lsp-modeline-workspace-status-enable 7744 (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode) 7745 (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)) 7746 (if lsp-lens-enable 7747 (add-hook 'lsp-configure-hook 'lsp-lens--enable) 7748 (remove-hook 'lsp-configure-hook 'lsp-lens--enable)) 7749 (if lsp-semantic-tokens-enable 7750 (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable) 7751 (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)) 7752 7753 ;; yas-snippet config 7754 (setq-local yas-inhibit-overlay-modification-protection t)) 7755 7756 (defun lsp--restart-if-needed (workspace) 7757 "Handler restart for WORKSPACE." 7758 (when (or (eq lsp-restart 'auto-restart) 7759 (eq (lsp--workspace-shutdown-action workspace) 'restart) 7760 (and (eq lsp-restart 'interactive) 7761 (let ((query (format 7762 "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?" 7763 (lsp--workspace-print workspace)))) 7764 (y-or-n-p query)))) 7765 (--each (lsp--workspace-buffers workspace) 7766 (when (lsp-buffer-live-p it) 7767 (lsp-with-current-buffer it 7768 (if lsp--buffer-deferred 7769 (lsp-deferred) 7770 (lsp--info "Restarting LSP in buffer %s" (buffer-name)) 7771 (lsp))))))) 7772 7773 (defun lsp--update-key (table key fn) 7774 "Apply FN on value corresponding to KEY in TABLE." 7775 (let ((existing-value (gethash key table))) 7776 (if-let ((new-value (funcall fn existing-value))) 7777 (puthash key new-value table) 7778 (remhash key table)))) 7779 7780 (defun lsp--process-sentinel (workspace process exit-str) 7781 "Create the sentinel for WORKSPACE." 7782 (unless (process-live-p process) 7783 (lsp--handle-process-exit workspace exit-str))) 7784 7785 (defun lsp--handle-process-exit (workspace exit-str) 7786 (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session))) 7787 (proc (lsp--workspace-proc workspace))) 7788 (lsp--warn "%s has exited (%s)" 7789 (lsp-process-name proc) 7790 (string-trim-right (or exit-str ""))) 7791 (with-lsp-workspace workspace 7792 ;; Clean workspace related data in each of the buffers 7793 ;; in the workspace. 7794 (--each (lsp--workspace-buffers workspace) 7795 (when (lsp-buffer-live-p it) 7796 (lsp-with-current-buffer it 7797 (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces)) 7798 (lsp--uninitialize-workspace) 7799 (lsp--spinner-stop) 7800 (lsp--remove-overlays 'lsp-highlight)))) 7801 7802 ;; Cleanup session from references to the closed workspace. 7803 (--each (hash-table-keys folder->workspaces) 7804 (lsp--update-key folder->workspaces it (apply-partially 'delete workspace))) 7805 7806 (lsp-process-cleanup proc)) 7807 7808 (run-hook-with-args 'lsp-after-uninitialized-functions workspace) 7809 7810 (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown) 7811 (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace)) 7812 (lsp--restart-if-needed workspace)) 7813 (lsp--cleanup-hanging-watches))) 7814 7815 (defun lsp-workspace-folders (workspace) 7816 "Return all folders associated with WORKSPACE." 7817 (let (result) 7818 (->> (lsp-session) 7819 (lsp-session-folder->servers) 7820 (maphash (lambda (folder workspaces) 7821 (when (-contains? workspaces workspace) 7822 (push folder result))))) 7823 result)) 7824 7825 (defun lsp--start-workspace (session client-template root &optional initialization-options) 7826 "Create new workspace for CLIENT-TEMPLATE with project root ROOT. 7827 INITIALIZATION-OPTIONS are passed to initialize function. 7828 SESSION is the active session." 7829 (lsp--spinner-start) 7830 (-let* ((default-directory root) 7831 (client (copy-lsp--client client-template)) 7832 (workspace (make-lsp--workspace 7833 :root root 7834 :client client 7835 :status 'starting 7836 :buffers (list (lsp-current-buffer)) 7837 :host-root (file-remote-p root))) 7838 ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities 7839 'multi-root 'initialized-fn) client) 7840 ((proc . cmd-proc) (funcall 7841 (or (plist-get new-connection :connect) 7842 (user-error "Client %s is configured incorrectly" client)) 7843 (lsp--create-filter-function workspace) 7844 (apply-partially #'lsp--process-sentinel workspace) 7845 (format "%s" server-id) 7846 environment-fn 7847 workspace)) 7848 (workspace-folders (gethash server-id (lsp-session-server-id->folders session)))) 7849 (setf (lsp--workspace-proc workspace) proc 7850 (lsp--workspace-cmd-proc workspace) cmd-proc) 7851 7852 ;; update (lsp-session-folder->servers) depending on whether we are starting 7853 ;; multi/single folder workspace 7854 (mapc (lambda (project-root) 7855 (->> session 7856 (lsp-session-folder->servers) 7857 (gethash project-root) 7858 (cl-pushnew workspace))) 7859 (or workspace-folders (list root))) 7860 7861 (with-lsp-workspace workspace 7862 (run-hooks 'lsp-before-initialize-hook) 7863 (lsp-request-async 7864 "initialize" 7865 (append 7866 (list :processId (unless (file-remote-p (buffer-file-name)) 7867 (emacs-pid)) 7868 :rootPath (lsp-file-local-name (expand-file-name root)) 7869 :clientInfo (list :name "emacs" 7870 :version (emacs-version)) 7871 :rootUri (lsp--path-to-uri root) 7872 :capabilities (lsp--client-capabilities custom-capabilities) 7873 :initializationOptions initialization-options 7874 :workDoneToken "1") 7875 (when lsp-server-trace 7876 (list :trace lsp-server-trace)) 7877 (when multi-root 7878 (->> workspace-folders 7879 (-distinct) 7880 (-map (lambda (folder) 7881 (list :uri (lsp--path-to-uri folder) 7882 :name (f-filename folder)))) 7883 (apply 'vector) 7884 (list :workspaceFolders)))) 7885 (-lambda ((&InitializeResult :capabilities)) 7886 ;; we know that Rust Analyzer will send {} which will be parsed as null 7887 ;; when using plists 7888 (when (equal 'rust-analyzer server-id) 7889 (-> capabilities 7890 (lsp:server-capabilities-text-document-sync?) 7891 (lsp:set-text-document-sync-options-save? t))) 7892 7893 (setf (lsp--workspace-server-capabilities workspace) capabilities 7894 (lsp--workspace-status workspace) 'initialized) 7895 7896 (with-lsp-workspace workspace 7897 (lsp-notify "initialized" lsp--empty-ht)) 7898 7899 (when initialized-fn (funcall initialized-fn workspace)) 7900 7901 (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace)) 7902 (->> workspace 7903 (lsp--workspace-buffers) 7904 (mapc (lambda (buffer) 7905 (lsp-with-current-buffer buffer 7906 (lsp--open-in-workspace workspace))))) 7907 7908 (with-lsp-workspace workspace 7909 (run-hooks 'lsp-after-initialize-hook)) 7910 (lsp--info "%s initialized successfully in folders: %s" 7911 (lsp--workspace-print workspace) 7912 (lsp-workspace-folders workspace))) 7913 :mode 'detached)) 7914 workspace)) 7915 7916 (defun lsp--load-default-session () 7917 "Load default session." 7918 (setq lsp--session (or (condition-case err 7919 (lsp--read-from-file lsp-session-file) 7920 (error (lsp--error "Failed to parse the session %s, starting with clean one." 7921 (error-message-string err)) 7922 nil)) 7923 (make-lsp-session)))) 7924 7925 (defun lsp-session () 7926 "Get the session associated with the current buffer." 7927 (or lsp--session (setq lsp--session (lsp--load-default-session)))) 7928 7929 (defun lsp--client-disabled-p (buffer-major-mode client) 7930 (seq-some 7931 (lambda (entry) 7932 (pcase entry 7933 ((pred symbolp) (eq entry client)) 7934 (`(,mode . ,client-or-list) 7935 (and (eq mode buffer-major-mode) 7936 (if (listp client-or-list) 7937 (memq client client-or-list) 7938 (eq client client-or-list)))))) 7939 lsp-disabled-clients)) 7940 7941 7942 ;; download server 7943 7944 (defcustom lsp-server-install-dir (expand-file-name 7945 (locate-user-emacs-file (f-join ".cache" "lsp"))) 7946 "Directory in which the servers will be installed." 7947 :risky t 7948 :type 'directory 7949 :package-version '(lsp-mode . "6.3") 7950 :group 'lsp-mode) 7951 7952 (defcustom lsp-verify-signature t 7953 "Whether to check GPG signatures of downloaded files." 7954 :type 'boolean 7955 :package-version '(lsp-mode . "8.0.0") 7956 :group 'lsp-mode) 7957 7958 (defvar lsp--dependencies (ht)) 7959 7960 (defun lsp-dependency (name &rest definitions) 7961 "Used to specify a language server DEPENDENCY, the server 7962 executable or other required file path. Typically, the 7963 DEPENDENCY is found by locating it on the system path using 7964 `executable-find'. 7965 7966 You can explicitly call lsp-dependency in your environment to 7967 specify the absolute path to the DEPENDENCY. For example, the 7968 typescript-language-server requires both the server and the 7969 typescript compiler. If you have installed them in a team shared 7970 read-only location, you can instruct lsp-mode to use them via 7971 7972 (eval-after-load `lsp-mode 7973 `(progn 7974 (require lsp-javascript) 7975 (lsp-dependency typescript-language-server (:system ,tls-exe)) 7976 (lsp-dependency typescript (:system ,ts-js)))) 7977 7978 where tls-exe is the absolute path to the typescript-language-server 7979 executable and ts-js is the absolute path to the typescript compiler 7980 JavaScript file, tsserver.js (the *.js is required for Windows)." 7981 (ht-set lsp--dependencies name definitions)) 7982 7983 (defun lsp--server-binary-present? (client) 7984 (unless (equal (lsp--client-server-id client) 'lsp-pwsh) 7985 (condition-case () 7986 (-some-> client lsp--client-new-connection (plist-get :test?) funcall) 7987 (error nil) 7988 (args-out-of-range nil)))) 7989 7990 (define-minor-mode lsp-installation-buffer-mode 7991 "Mode used in *lsp-installation* buffers. 7992 It can be used to set-up keybindings, etc. Disabling this mode 7993 detaches the installation buffer from commands like 7994 `lsp-select-installation-buffer'." 7995 :init-value nil 7996 :lighter nil) 7997 7998 (defface lsp-installation-finished-buffer-face '((t :foreground "orange")) 7999 "Face used for finished installation buffers. 8000 Used in `lsp-select-installation-buffer'." 8001 :group 'lsp-mode) 8002 8003 (defface lsp-installation-buffer-face '((t :foreground "green")) 8004 "Face used for installation buffers still in progress. 8005 Used in `lsp-select-installation-buffer'." 8006 :group 'lsp-mode) 8007 8008 (defun lsp--installation-buffer? (buf) 8009 "Check whether BUF is an `lsp-async-start-process' buffer." 8010 (buffer-local-value 'lsp-installation-buffer-mode buf)) 8011 8012 (defun lsp-select-installation-buffer (&optional show-finished) 8013 "Interactively choose an installation buffer. 8014 If SHOW-FINISHED is set, leftover (finished) installation buffers 8015 are still shown." 8016 (interactive "P") 8017 (let ((bufs (--filter (and (lsp--installation-buffer? it) 8018 (or show-finished (get-buffer-process it))) 8019 (buffer-list)))) 8020 (pcase bufs 8021 (`nil (user-error "No installation buffers")) 8022 (`(,buf) (pop-to-buffer buf)) 8023 (bufs (pop-to-buffer (completing-read "Select installation buffer: " 8024 (--map (propertize (buffer-name it) 'face 8025 (if (get-buffer-process it) 8026 'lsp-installation-buffer-face 8027 'lsp-installation-finished-buffer-face)) 8028 bufs))))))) 8029 8030 (defun lsp-cleanup-installation-buffers () 8031 "Delete finished *lsp-installation* buffers." 8032 (interactive) 8033 (dolist (buf (buffer-list)) 8034 (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf))) 8035 (kill-buffer buf)))) 8036 8037 (defun lsp--download-status () 8038 (-some--> #'lsp--client-download-in-progress? 8039 (lsp--filter-clients it) 8040 (-map (-compose #'symbol-name #'lsp--client-server-id) it) 8041 (format "%s" it) 8042 (propertize it 'face 'success) 8043 (format " Installing following servers: %s" it) 8044 (propertize it 8045 'local-map (make-mode-line-mouse-map 8046 'mouse-1 #'lsp-select-installation-buffer) 8047 'mouse-face 'highlight))) 8048 8049 (defun lsp--install-server-internal (client &optional update?) 8050 (unless (lsp--client-download-server-fn client) 8051 (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation." 8052 (lsp--client-server-id client))) 8053 8054 (setf (lsp--client-download-in-progress? client) t) 8055 (add-to-list 'global-mode-string '(t (:eval (lsp--download-status)))) 8056 (cl-flet ((done 8057 (success? &optional error-message) 8058 ;; run with idle timer to make sure the lsp command is executed in 8059 ;; the main thread, see #2739. 8060 (run-with-timer 8061 0.0 8062 nil 8063 (lambda () 8064 (-let [(&lsp-cln 'server-id 'buffers) client] 8065 (setf (lsp--client-download-in-progress? client) nil 8066 (lsp--client-buffers client) nil) 8067 (if success? 8068 (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id 8069 (length buffers)) 8070 (lsp--error "Server %s install process failed with the following error message: %s. 8071 Check `*lsp-install*' and `*lsp-log*' buffer." 8072 server-id 8073 error-message)) 8074 (seq-do 8075 (lambda (buffer) 8076 (when (lsp-buffer-live-p buffer) 8077 (lsp-with-current-buffer buffer 8078 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8079 global-mode-string) 8080 (when success? (lsp))))) 8081 buffers) 8082 (unless (lsp--filter-clients #'lsp--client-download-in-progress?) 8083 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8084 global-mode-string))))))) 8085 (lsp--info "Download %s started." (lsp--client-server-id client)) 8086 (condition-case err 8087 (funcall 8088 (lsp--client-download-server-fn client) 8089 client 8090 (lambda () (done t)) 8091 (lambda (msg) (done nil msg)) 8092 update?) 8093 (error 8094 (done nil (error-message-string err)))))) 8095 8096 (defun lsp--require-packages () 8097 "Load `lsp-client-packages' if needed." 8098 (when (and lsp-auto-configure (not lsp--client-packages-required)) 8099 (seq-do (lambda (package) 8100 ;; loading client is slow and `lsp' can be called repeatedly 8101 (unless (featurep package) 8102 (require package nil t))) 8103 lsp-client-packages) 8104 (setq lsp--client-packages-required t))) 8105 8106 ;;;###autoload 8107 (defun lsp-install-server (update? &optional server-id) 8108 "Interactively install or re-install server. 8109 When prefix UPDATE? is t force installation even if the server is present." 8110 (interactive "P") 8111 (lsp--require-packages) 8112 (let* ((chosen-client (or (gethash server-id lsp-clients) 8113 (lsp--completing-read 8114 "Select server to install/re-install: " 8115 (or (->> lsp-clients 8116 (ht-values) 8117 (-filter (-andfn 8118 (-not #'lsp--client-download-in-progress?) 8119 #'lsp--client-download-server-fn))) 8120 (user-error "There are no servers with automatic installation")) 8121 (lambda (client) 8122 (let ((server-name (-> client lsp--client-server-id symbol-name))) 8123 (if (lsp--server-binary-present? client) 8124 (concat server-name " (Already installed)") 8125 server-name))) 8126 nil 8127 t))) 8128 (update? (or update? 8129 (and (not (lsp--client-download-in-progress? chosen-client)) 8130 (lsp--server-binary-present? chosen-client))))) 8131 (lsp--install-server-internal chosen-client update?))) 8132 8133 ;;;###autoload 8134 (defun lsp-uninstall-server (dir) 8135 "Delete a LSP server from `lsp-server-install-dir'." 8136 (interactive 8137 (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir)))) 8138 (unless (file-directory-p dir) 8139 (user-error "Couldn't find %s directory" dir)) 8140 (delete-directory dir 'recursive) 8141 (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir)))) 8142 8143 ;;;###autoload 8144 (defun lsp-uninstall-servers () 8145 "Uninstall all installed servers." 8146 (interactive) 8147 (let* ((dir lsp-server-install-dir) 8148 (servers (ignore-errors 8149 (directory-files dir t 8150 directory-files-no-dot-files-regexp)))) 8151 (if (or (not (file-directory-p dir)) (zerop (length servers))) 8152 (user-error "No servers to uninstall") 8153 (when (yes-or-no-p 8154 (format "Servers to uninstall: %d (%s), proceed? " 8155 (length servers) 8156 (mapconcat (lambda (server) 8157 (file-name-nondirectory (directory-file-name server))) 8158 servers " "))) 8159 (mapc #'lsp-uninstall-server servers) 8160 (message "All servers uninstalled"))))) 8161 8162 ;;;###autoload 8163 (defun lsp-update-server (&optional server-id) 8164 "Interactively update (reinstall) a server." 8165 (interactive) 8166 (lsp--require-packages) 8167 (let ((chosen-client (or (gethash server-id lsp-clients) 8168 (lsp--completing-read 8169 "Select server to update (if not on the list, probably you need to `lsp-install-server`): " 8170 (or (->> lsp-clients 8171 (ht-values) 8172 (-filter (-andfn 8173 (-not #'lsp--client-download-in-progress?) 8174 #'lsp--client-download-server-fn 8175 #'lsp--server-binary-present?))) 8176 (user-error "There are no servers to update")) 8177 (lambda (client) 8178 (-> client lsp--client-server-id symbol-name)) 8179 nil 8180 t)))) 8181 (lsp--install-server-internal chosen-client t))) 8182 8183 ;;;###autoload 8184 (defun lsp-update-servers () 8185 "Update (reinstall) all installed servers." 8186 (interactive) 8187 (lsp--require-packages) 8188 (mapc (lambda (client) (lsp--install-server-internal client t)) 8189 (-filter (-andfn 8190 (-not #'lsp--client-download-in-progress?) 8191 #'lsp--client-download-server-fn 8192 #'lsp--server-binary-present?) (hash-table-values lsp-clients)))) 8193 8194 ;;;###autoload 8195 (defun lsp-ensure-server (server-id) 8196 "Ensure server SERVER-ID" 8197 (lsp--require-packages) 8198 (if-let ((client (gethash server-id lsp-clients))) 8199 (unless (lsp--server-binary-present? client) 8200 (lsp--info "Server `%s' is not preset, installing..." server-id) 8201 (lsp-install-server nil server-id)) 8202 (warn "Unable to find server registration with id %s" server-id))) 8203 8204 (defun lsp-async-start-process (callback error-callback &rest command) 8205 "Start async process COMMAND with CALLBACK and ERROR-CALLBACK." 8206 (let ((name (cl-first command))) 8207 (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd) 8208 (not (null cmd))) 8209 command) 8210 " ") t 8211 (lambda (&rest _) 8212 (generate-new-buffer-name (format "*lsp-install: %s*" name)))) 8213 (lsp-installation-buffer-mode +1) 8214 (view-mode +1) 8215 (add-hook 8216 'compilation-finish-functions 8217 (lambda (_buf status) 8218 (if (string= "finished\n" status) 8219 (condition-case err 8220 (funcall callback) 8221 (error 8222 (funcall error-callback (error-message-string err)))) 8223 (funcall error-callback (s-trim-right status)))) 8224 nil t)))) 8225 8226 (defun lsp-resolve-value (value) 8227 "Resolve VALUE's value. 8228 If it is function - call it. 8229 If it is a variable - return it's value 8230 Otherwise returns value itself." 8231 (cond 8232 ((functionp value) (funcall value)) 8233 ((and (symbolp value) (boundp value)) (symbol-value value)) 8234 (value))) 8235 8236 (defvar lsp-deps-providers 8237 (list :npm (list :path #'lsp--npm-dependency-path 8238 :install #'lsp--npm-dependency-install) 8239 :cargo (list :path #'lsp--cargo-dependency-path 8240 :install #'lsp--cargo-dependency-install) 8241 :system (list :path #'lsp--system-path) 8242 :download (list :path #'lsp-download-path 8243 :install #'lsp-download-install))) 8244 8245 (defun lsp--system-path (path) 8246 "If PATH is absolute and exists return it as is. Otherwise, 8247 return the absolute path to the executable defined by PATH or 8248 nil." 8249 ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the 8250 ;; typescript-language-server. When lsp invokes the server, lsp needs to 8251 ;; supply the path to the typescript compiler, tsserver.js, as an argument. To 8252 ;; make code platform independent, one must pass the absolute path to the 8253 ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript 8254 ;; child process spawn command that is invoked by the 8255 ;; typescript-language-server). This is why we check for existence and not 8256 ;; that the path is executable. 8257 (let ((path (lsp-resolve-value path))) 8258 (cond 8259 ((and (f-absolute? path) 8260 (f-exists? path)) 8261 path) 8262 ((executable-find path t) path)))) 8263 8264 (defun lsp-package-path (dependency) 8265 "Path to the DEPENDENCY each of the registered providers." 8266 (let (path) 8267 (-first (-lambda ((provider . rest)) 8268 (setq path (-some-> lsp-deps-providers 8269 (plist-get provider) 8270 (plist-get :path) 8271 (apply rest)))) 8272 (gethash dependency lsp--dependencies)) 8273 path)) 8274 8275 (defun lsp-package-ensure (dependency callback error-callback) 8276 "Asynchronously ensure a package." 8277 (or (-first (-lambda ((provider . rest)) 8278 (-some-> lsp-deps-providers 8279 (plist-get provider) 8280 (plist-get :install) 8281 (apply (cl-list* callback error-callback rest)))) 8282 (gethash dependency lsp--dependencies)) 8283 (funcall error-callback (format "Unable to find a way to install %s" dependency)))) 8284 8285 8286 ;; npm handling 8287 8288 ;; https://docs.npmjs.com/files/folders#executables 8289 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys) 8290 "Return npm dependency PATH for PACKAGE." 8291 (let ((path (executable-find 8292 (f-join lsp-server-install-dir "npm" package 8293 (cond ((eq system-type 'windows-nt) "") 8294 (t "bin")) 8295 path) 8296 t))) 8297 (unless (and path (f-exists? path)) 8298 (error "The package %s is not installed. Unable to find %s" package path)) 8299 path)) 8300 8301 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys) 8302 (if-let ((npm-binary (executable-find "npm"))) 8303 (progn 8304 ;; Explicitly `make-directory' to work around NPM bug in 8305 ;; versions 7.0.0 through 7.4.1. See 8306 ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for 8307 ;; discussion. 8308 (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents) 8309 (lsp-async-start-process (lambda () 8310 (if (string-empty-p 8311 (string-trim (shell-command-to-string 8312 (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " ")))) 8313 (funcall callback) 8314 (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json"))))) 8315 (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx 8316 (when (f-dir-p default-directory) 8317 (lsp-async-start-process callback 8318 error-callback 8319 (executable-find "npx") 8320 "npm-install-peers"))))) 8321 error-callback 8322 npm-binary 8323 "-g" 8324 "--prefix" 8325 (f-join lsp-server-install-dir "npm" package) 8326 "install" 8327 package)) 8328 (lsp-log "Unable to install %s via `npm' because it is not present" package) 8329 nil)) 8330 8331 8332 ;; Cargo dependency handling 8333 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys) 8334 (let ((path (executable-find 8335 (f-join lsp-server-install-dir 8336 "cargo" 8337 package 8338 "bin" 8339 path) 8340 t))) 8341 (unless (and path (f-exists? path)) 8342 (error "The package %s is not installed. Unable to find %s" package path)) 8343 path)) 8344 8345 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys) 8346 (if-let ((cargo-binary (executable-find "cargo"))) 8347 (lsp-async-start-process 8348 callback 8349 error-callback 8350 cargo-binary 8351 "install" 8352 package 8353 (when git 8354 "--git") 8355 git 8356 "--root" 8357 (f-join lsp-server-install-dir "cargo" package)) 8358 (lsp-log "Unable to install %s via `cargo' because it is not present" package) 8359 nil)) 8360 8361 8362 8363 ;; Download URL handling 8364 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys) 8365 (let* ((url (lsp-resolve-value url)) 8366 (store-path (lsp-resolve-value store-path)) 8367 ;; (decompress (lsp-resolve-value decompress)) 8368 (download-path 8369 (pcase decompress 8370 (:gzip (concat store-path ".gz")) 8371 (:zip (concat store-path ".zip")) 8372 (:targz (concat store-path ".tar.gz")) 8373 (`nil store-path) 8374 (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'"))))) 8375 (make-thread 8376 (lambda () 8377 (condition-case err 8378 (progn 8379 (when (f-exists? download-path) 8380 (f-delete download-path)) 8381 (when (f-exists? store-path) 8382 (f-delete store-path)) 8383 (lsp--info "Starting to download %s to %s..." url download-path) 8384 (mkdir (f-parent download-path) t) 8385 (url-copy-file url download-path) 8386 (lsp--info "Finished downloading %s..." download-path) 8387 (when (and lsp-verify-signature asc-url pgp-key) 8388 (if (executable-find epg-gpg-program) 8389 (let ((asc-download-path (concat download-path ".asc")) 8390 (context (epg-make-context)) 8391 (fingerprint) 8392 (signature)) 8393 (when (f-exists? asc-download-path) 8394 (f-delete asc-download-path)) 8395 (lsp--info "Starting to download %s to %s..." asc-url asc-download-path) 8396 (url-copy-file asc-url asc-download-path) 8397 (lsp--info "Finished downloading %s..." asc-download-path) 8398 (epg-import-keys-from-string context pgp-key) 8399 (setq fingerprint (epg-import-status-fingerprint 8400 (car 8401 (epg-import-result-imports 8402 (epg-context-result-for context 'import))))) 8403 (lsp--info "Verifying signature %s..." asc-download-path) 8404 (epg-verify-file context asc-download-path download-path) 8405 (setq signature (car (epg-context-result-for context 'verify))) 8406 (unless (and 8407 (eq (epg-signature-status signature) 'good) 8408 (equal (epg-signature-fingerprint signature) fingerprint)) 8409 (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature)))) 8410 (lsp--warn "GPG is not installed, skipping the signature check."))) 8411 (when decompress 8412 (lsp--info "Decompressing %s..." download-path) 8413 (pcase decompress 8414 (:gzip 8415 (lsp-gunzip download-path)) 8416 (:zip (lsp-unzip download-path (f-parent store-path))) 8417 (:targz (lsp-tar-gz-decompress download-path (f-parent store-path)))) 8418 (lsp--info "Decompressed %s..." store-path)) 8419 (funcall callback)) 8420 (error (funcall error-callback err))))))) 8421 8422 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys) 8423 "Download URL and store it into STORE-PATH. 8424 8425 SET-EXECUTABLE? when non-nil change the executable flags of 8426 STORE-PATH to make it executable. BINARY-PATH can be specified 8427 when the binary to start does not match the name of the 8428 archive (e.g. when the archive has multiple files)" 8429 (let ((store-path (or (lsp-resolve-value binary-path) 8430 (lsp-resolve-value store-path)))) 8431 (cond 8432 ((executable-find store-path) store-path) 8433 ((and set-executable? (f-exists? store-path)) 8434 (set-file-modes store-path #o0700) 8435 store-path) 8436 ((f-exists? store-path) store-path)))) 8437 8438 (defun lsp--find-latest-gh-release-url (url regex) 8439 "Fetch the latest version in the releases given by URL by using REGEX." 8440 (let ((url-request-method "GET")) 8441 (with-current-buffer (url-retrieve-synchronously url) 8442 (goto-char (point-min)) 8443 (re-search-forward "\n\n" nil 'noerror) 8444 (delete-region (point-min) (point)) 8445 (let* ((json-result (lsp-json-read-buffer))) 8446 (message "Latest version found: %s" (lsp-get json-result :tag_name)) 8447 (--> json-result 8448 (lsp-get it :assets) 8449 (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it) 8450 (lsp-get it :browser_download_url)))))) 8451 8452 ;; unzip 8453 8454 (defconst lsp-ext-pwsh-script "powershell -noprofile -noninteractive \ 8455 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'" 8456 "Powershell script to unzip file.") 8457 8458 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'" 8459 "Unzip script to unzip file.") 8460 8461 (defcustom lsp-unzip-script (lambda () 8462 (cond ((and (eq system-type 'windows-nt) 8463 (executable-find "powershell")) 8464 lsp-ext-pwsh-script) 8465 ((executable-find "unzip") lsp-ext-unzip-script) 8466 ((executable-find "powershell") lsp-ext-pwsh-script) 8467 (t nil))) 8468 "The script to unzip." 8469 :group 'lsp-mode 8470 :type 'string 8471 :package-version '(lsp-mode . "8.0.0")) 8472 8473 (defun lsp-unzip (zip-file dest) 8474 "Unzip ZIP-FILE to DEST." 8475 (unless lsp-unzip-script 8476 (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'")) 8477 (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest))) 8478 8479 ;; gunzip 8480 8481 (defconst lsp-ext-gunzip-script "gzip -d %1$s" 8482 "Script to decompress a gzippped file with gzip.") 8483 8484 (defcustom lsp-gunzip-script (lambda () 8485 (cond ((executable-find "gzip") lsp-ext-gunzip-script) 8486 (t nil))) 8487 "The script to decompress a gzipped file. 8488 Should be a format string with one argument for the file to be decompressed 8489 in place." 8490 :group 'lsp-mode 8491 :type 'string 8492 :package-version '(lsp-mode . "8.0.0")) 8493 8494 (defun lsp-gunzip (gz-file) 8495 "Decompress GZ-FILE in place." 8496 (unless lsp-gunzip-script 8497 (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file)) 8498 (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file))) 8499 8500 ;; tar.gz decompression 8501 8502 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'" 8503 "Script to decompress a .tar.gz file.") 8504 8505 (defcustom lsp-tar-script (lambda () 8506 (cond ((executable-find "tar") lsp-ext-tar-script) 8507 (t nil))) 8508 "The script to decompress a .tar.gz file. 8509 Should be a format string with one argument for the file to be decompressed 8510 in place." 8511 :group 'lsp-mode 8512 :type 'string) 8513 8514 (defun lsp-tar-gz-decompress (targz-file dest) 8515 "Decompress TARGZ-FILE in DEST." 8516 (unless lsp-tar-script 8517 (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file)) 8518 (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest))) 8519 8520 8521 ;; VSCode marketplace 8522 8523 (defcustom lsp-vscode-ext-url 8524 "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s" 8525 "Vscode extension template url." 8526 :group 'lsp-mode 8527 :type 'string 8528 :package-version '(lsp-mode . "8.0.0")) 8529 8530 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform) 8531 "Return the URL to vscode extension. 8532 PUBLISHER is the extension publisher. 8533 NAME is the name of the extension. 8534 VERSION is the version of the extension. 8535 TARGETPLATFORM is the targetPlatform of the extension." 8536 (format lsp-vscode-ext-url publisher name version (or targetPlatform ""))) 8537 8538 8539 8540 ;; Queueing prompts 8541 8542 (defvar lsp--question-queue nil 8543 "List of questions yet to be asked by `lsp-ask-question'.") 8544 8545 (defun lsp-ask-question (question options callback) 8546 "Prompt the user to answer the QUESTION with one of the OPTIONS from the 8547 minibuffer. Once the user selects an option, the CALLBACK function will be 8548 called, passing the selected option to it. 8549 8550 If the user is currently being shown a question, the question will be stored in 8551 `lsp--question-queue', and will be asked once the user has answered the current 8552 question." 8553 (add-to-list 'lsp--question-queue `(("question" . ,question) 8554 ("options" . ,options) 8555 ("callback" . ,callback)) t) 8556 (when (eq (length lsp--question-queue) 1) 8557 (lsp--process-question-queue))) 8558 8559 (defun lsp--process-question-queue () 8560 "Take the first question from `lsp--question-queue', process it, then process 8561 the next question until the queue is empty." 8562 (-let* (((&alist "question" "options" "callback") (car lsp--question-queue)) 8563 (answer (completing-read question options nil t))) 8564 (pop lsp--question-queue) 8565 (funcall callback answer) 8566 (when lsp--question-queue 8567 (lsp--process-question-queue)))) 8568 8569 (defun lsp--supports-buffer? (client) 8570 (and 8571 ;; both file and client remote or both local 8572 (eq (---truthy? (file-remote-p (buffer-file-name))) 8573 (---truthy? (lsp--client-remote? client))) 8574 8575 ;; activation function or major-mode match. 8576 (if-let ((activation-fn (lsp--client-activation-fn client))) 8577 (funcall activation-fn (buffer-file-name) major-mode) 8578 (-contains? (lsp--client-major-modes client) major-mode)) 8579 8580 ;; check whether it is enabled if `lsp-enabled-clients' is not null 8581 (or (null lsp-enabled-clients) 8582 (or (member (lsp--client-server-id client) lsp-enabled-clients) 8583 (ignore (lsp--info "Client %s is not in lsp-enabled-clients" 8584 (lsp--client-server-id client))))) 8585 8586 ;; check whether it is not disabled. 8587 (not (lsp--client-disabled-p major-mode (lsp--client-server-id client))))) 8588 8589 (defun lsp--filter-clients (pred) 8590 (->> lsp-clients hash-table-values (-filter pred))) 8591 8592 (defun lsp--find-clients () 8593 "Find clients which can handle current buffer." 8594 (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 8595 #'lsp--server-binary-present?))) 8596 (lsp-log "Found the following clients for %s: %s" 8597 (buffer-file-name) 8598 (s-join ", " 8599 (-map (lambda (client) 8600 (format "(server-id %s, priority %s)" 8601 (lsp--client-server-id client) 8602 (lsp--client-priority client))) 8603 matching-clients))) 8604 (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients)) 8605 (selected-clients (if-let ((main-client (and main-clients 8606 (--max-by (> (lsp--client-priority it) 8607 (lsp--client-priority other)) 8608 main-clients)))) 8609 (cons main-client add-on-clients) 8610 add-on-clients))) 8611 (lsp-log "The following clients were selected based on priority: %s" 8612 (s-join ", " 8613 (-map (lambda (client) 8614 (format "(server-id %s, priority %s)" 8615 (lsp--client-server-id client) 8616 (lsp--client-priority client))) 8617 selected-clients))) 8618 selected-clients))) 8619 8620 (defun lsp-workspace-remove-all-folders() 8621 "Delete all lsp tracked folders." 8622 (interactive) 8623 (--each (lsp-session-folders (lsp-session)) 8624 (lsp-workspace-folders-remove it))) 8625 8626 (defun lsp-register-client (client) 8627 "Registers LSP client CLIENT." 8628 (let ((client-id (lsp--client-server-id client))) 8629 (puthash client-id client lsp-clients) 8630 (setplist (intern (format "lsp-%s-after-open-hook" client-id)) 8631 `( standard-value (nil) custom-type hook 8632 custom-package-version (lsp-mode . "7.0.1") 8633 variable-documentation ,(format "Hooks to run after `%s' server is run." client-id) 8634 custom-requests nil))) 8635 (when (and lsp-auto-register-remote-clients 8636 (not (lsp--client-remote? client))) 8637 (let ((remote-client (copy-lsp--client client))) 8638 (setf (lsp--client-remote? remote-client) t 8639 (lsp--client-server-id remote-client) (intern 8640 (format "%s-tramp" 8641 (lsp--client-server-id client))) 8642 ;; disable automatic download 8643 (lsp--client-download-server-fn remote-client) nil) 8644 (lsp-register-client remote-client)))) 8645 8646 (defun lsp--create-initialization-options (_session client) 8647 "Create initialization-options from SESSION and CLIENT. 8648 Add workspace folders depending on server being multiroot and 8649 session workspace folder configuration for the server." 8650 (let* ((initialization-options-or-fn (lsp--client-initialization-options client))) 8651 (if (functionp initialization-options-or-fn) 8652 (funcall initialization-options-or-fn) 8653 initialization-options-or-fn))) 8654 8655 (defvar lsp-client-settings (make-hash-table :test 'equal) 8656 "For internal use, any external users please use 8657 `lsp-register-custom-settings' function instead") 8658 8659 (defun lsp-register-custom-settings (props) 8660 "Register PROPS. 8661 PROPS is list of triple (path value boolean?) where PATH is the path to the 8662 property; VALUE can be a literal value, symbol to be evaluated, or either a 8663 function or lambda function to be called without arguments; BOOLEAN? is an 8664 optional flag that should be non-nil for boolean settings, when it is nil the 8665 property will be ignored if the VALUE is nil. 8666 8667 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))' 8668 \(note the double parentheses)" 8669 (mapc 8670 (-lambda ((path . rest)) 8671 (puthash path rest lsp-client-settings)) 8672 props)) 8673 8674 (defun lsp-region-text (region) 8675 "Get the text for REGION in current buffer." 8676 (-let (((start . end) (lsp--range-to-region region))) 8677 (buffer-substring-no-properties start end))) 8678 8679 (defun lsp-ht-set (tbl paths value) 8680 "Set nested hash table value. 8681 TBL - a hash table, PATHS is the path to the nested VALUE." 8682 (pcase paths 8683 (`(,path) (ht-set! tbl path value)) 8684 (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl) 8685 (let ((temp-tbl (ht))) 8686 (ht-set! tbl path temp-tbl) 8687 temp-tbl)))) 8688 (lsp-ht-set nested-tbl rst value))))) 8689 8690 ;; sections 8691 8692 (defalias 'defcustom-lsp 'lsp-defcustom) 8693 8694 (defmacro lsp-defcustom (symbol standard doc &rest args) 8695 "Defines `lsp-mode' server property." 8696 (declare (doc-string 3) (debug (name body)) 8697 (indent defun)) 8698 (let ((path (plist-get args :lsp-path)) 8699 (setter (intern (concat (symbol-name symbol) "--set")))) 8700 (cl-remf args :lsp-path) 8701 `(progn 8702 (lsp-register-custom-settings 8703 (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type)))))) 8704 8705 (defcustom ,symbol ,standard ,doc ,@args) 8706 8707 ;; Use a variable watcher instead of registering a `defcustom' 8708 ;; setter since `hack-local-variables' is not aware of custom 8709 ;; setters and won't invoke them. 8710 8711 (defun ,setter (sym val op _where) 8712 (when (eq op 'set) 8713 (lsp--set-custom-property sym val ,path))) 8714 8715 (add-variable-watcher ',symbol #',setter)))) 8716 8717 (defun lsp--set-custom-property (sym val path) 8718 (set sym val) 8719 (let ((section (cl-first (s-split "\\." path)))) 8720 (mapc (lambda (workspace) 8721 (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace)) 8722 section) 8723 (with-lsp-workspace workspace 8724 (lsp--set-configuration (lsp-configuration-section section))))) 8725 (lsp--session-workspaces (lsp-session))))) 8726 8727 (defun lsp-configuration-section (section) 8728 "Get settings for SECTION." 8729 (let ((ret (ht-create))) 8730 (maphash (-lambda (path (variable boolean?)) 8731 (when (s-matches? (concat (regexp-quote section) "\\..*") path) 8732 (let* ((symbol-value (-> variable 8733 lsp-resolve-value 8734 lsp-resolve-value)) 8735 (value (if (and boolean? (not symbol-value)) 8736 :json-false 8737 symbol-value))) 8738 (when (or boolean? value) 8739 (lsp-ht-set ret (s-split "\\." path) value))))) 8740 lsp-client-settings) 8741 ret)) 8742 8743 8744 (defun lsp--start-connection (session client project-root) 8745 "Initiates connection created from CLIENT for PROJECT-ROOT. 8746 SESSION is the active session." 8747 (when (lsp--client-multi-root client) 8748 (cl-pushnew project-root (gethash (lsp--client-server-id client) 8749 (lsp-session-server-id->folders session)))) 8750 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil) 8751 8752 (unwind-protect 8753 (lsp--start-workspace session client project-root (lsp--create-initialization-options session client)) 8754 (lsp--spinner-stop))) 8755 8756 ;; lsp-log-io-mode 8757 8758 (defvar lsp-log-io-mode-map 8759 (let ((map (make-sparse-keymap))) 8760 (define-key map (kbd "M-n") #'lsp-log-io-next) 8761 (define-key map (kbd "M-p") #'lsp-log-io-prev) 8762 (define-key map (kbd "k") #'lsp--erase-log-buffer) 8763 (define-key map (kbd "K") #'lsp--erase-session-log-buffers) 8764 map) 8765 "Keymap for lsp log buffer mode.") 8766 8767 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo" 8768 "Special mode for viewing IO logs.") 8769 8770 (defun lsp-workspace-show-log (workspace) 8771 "Display the log buffer of WORKSPACE." 8772 (interactive 8773 (list (if lsp-log-io 8774 (if (eq (length (lsp-workspaces)) 1) 8775 (cl-first (lsp-workspaces)) 8776 (lsp--completing-read "Workspace: " (lsp-workspaces) 8777 #'lsp--workspace-print nil t)) 8778 (user-error "IO logging is disabled")))) 8779 (pop-to-buffer (lsp--get-log-buffer-create workspace))) 8780 8781 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log) 8782 8783 (defun lsp--get-log-buffer-create (workspace) 8784 "Return the lsp log buffer of WORKSPACE, creating a new one if needed." 8785 (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8786 (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id))) 8787 (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid)))) 8788 8789 (defun lsp--erase-log-buffer (&optional all) 8790 "Delete contents of current lsp log buffer. 8791 When ALL is t, erase all log buffers of the running session." 8792 (interactive) 8793 (let* ((workspaces (lsp--session-workspaces (lsp-session))) 8794 (current-log-buffer (current-buffer))) 8795 (dolist (w workspaces) 8796 (let ((b (lsp--get-log-buffer-create w))) 8797 (when (or all (eq b current-log-buffer)) 8798 (with-current-buffer b 8799 (let ((inhibit-read-only t)) 8800 (erase-buffer)))))))) 8801 8802 (defun lsp--erase-session-log-buffers () 8803 "Erase log buffers of the running session." 8804 (interactive) 8805 (lsp--erase-log-buffer t)) 8806 8807 (defun lsp-log-io-next (arg) 8808 "Move to next log entry." 8809 (interactive "P") 8810 (ewoc-goto-next lsp--log-io-ewoc (or arg 1))) 8811 8812 (defun lsp-log-io-prev (arg) 8813 "Move to previous log entry." 8814 (interactive "P") 8815 (ewoc-goto-prev lsp--log-io-ewoc (or arg 1))) 8816 8817 8818 8819 (cl-defmethod lsp-process-id ((process process)) 8820 (process-id process)) 8821 8822 (cl-defmethod lsp-process-name ((process process)) (process-name process)) 8823 8824 (cl-defmethod lsp-process-status ((process process)) (process-status process)) 8825 8826 (cl-defmethod lsp-process-kill ((process process)) 8827 (when (process-live-p process) 8828 (kill-process process))) 8829 8830 (cl-defmethod lsp-process-send ((process process) message) 8831 (condition-case err 8832 (process-send-string process (lsp--make-message message)) 8833 (error (lsp--error "Sending to process failed with the following error: %s" 8834 (error-message-string err))))) 8835 8836 (cl-defmethod lsp-process-cleanup (process) 8837 ;; Kill standard error buffer only if the process exited normally. 8838 ;; Leave it intact otherwise for debugging purposes. 8839 (let ((buffer (-> process process-name get-buffer))) 8840 (when (and (eq (process-status process) 'exit) 8841 (zerop (process-exit-status process)) 8842 (buffer-live-p buffer)) 8843 (kill-buffer buffer)))) 8844 8845 8846 ;; native JSONRPC 8847 8848 (declare-function json-rpc "ext:json") 8849 (declare-function json-rpc-connection "ext:json") 8850 (declare-function json-rpc-send "ext:json") 8851 (declare-function json-rpc-shutdown "ext:json") 8852 (declare-function json-rpc-stderr "ext:json") 8853 (declare-function json-rpc-pid "ext:json") 8854 8855 (defvar lsp-json-rpc-thread nil) 8856 (defvar lsp-json-rpc-queue nil) 8857 (defvar lsp-json-rpc-done nil) 8858 (defvar lsp-json-rpc-mutex (make-mutex)) 8859 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex)) 8860 8861 (defun lsp-json-rpc-process-queue () 8862 (while (not lsp-json-rpc-done) 8863 (while lsp-json-rpc-queue 8864 (-let (((proc . message) (pop lsp-json-rpc-queue))) 8865 (json-rpc-send 8866 proc message 8867 :null-object nil 8868 :false-object :json-false))) 8869 (with-mutex lsp-json-rpc-mutex 8870 (condition-wait lsp-json-rpc-condition)))) 8871 8872 (cl-defmethod lsp-process-id (process) (json-rpc-pid process)) 8873 8874 (cl-defmethod lsp-process-name (_process) "TBD") 8875 8876 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process)) 8877 8878 (cl-defmethod lsp-process-send (proc message) 8879 (unless lsp-json-rpc-thread 8880 (with-current-buffer (get-buffer-create " *json-rpc*") 8881 (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*")))) 8882 8883 (with-mutex lsp-json-rpc-mutex 8884 (setq lsp-json-rpc-queue (append lsp-json-rpc-queue 8885 (list (cons proc message)))) 8886 (condition-notify lsp-json-rpc-condition))) 8887 8888 (cl-defmethod lsp-process-cleanup (_proc)) 8889 8890 (defun lsp-json-rpc-connection (workspace command) 8891 (let ((con (apply #'json-rpc-connection command)) 8892 (object-type (if lsp-use-plists 'plist 'hash-table))) 8893 (with-current-buffer (get-buffer-create " *json-rpc*") 8894 (make-thread 8895 (lambda () 8896 (json-rpc 8897 con 8898 (lambda (result err done) 8899 (run-with-timer 8900 0.0 8901 nil 8902 (lambda () 8903 (cond 8904 (result (lsp--parser-on-message result workspace)) 8905 (err (warn "Json parsing failed with the following error: %s" err)) 8906 (done (lsp--handle-process-exit workspace "")))))) 8907 :object-type object-type 8908 :null-object nil 8909 :false-object nil)) 8910 "*json-rpc-connection*")) 8911 (cons con con))) 8912 8913 (defun lsp-json-rpc-stderr () 8914 (interactive) 8915 (--when-let (pcase (lsp-workspaces) 8916 (`nil (user-error "There are no active servers in the current buffer")) 8917 (`(,workspace) workspace) 8918 (workspaces (lsp--completing-read "Select server: " 8919 workspaces 8920 'lsp--workspace-print nil t))) 8921 (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it))) 8922 (buffer (format "*stderr-%s*" (lsp--workspace-print it)) )) 8923 (with-current-buffer (get-buffer-create buffer) 8924 (with-help-window buffer 8925 (insert content)))))) 8926 8927 8928 (defun lsp--workspace-print (workspace) 8929 "Visual representation WORKSPACE." 8930 (let* ((proc (lsp--workspace-cmd-proc workspace)) 8931 (status (lsp--workspace-status workspace)) 8932 (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8933 (pid (lsp-process-id proc))) 8934 8935 (if (eq 'initialized status) 8936 (format "%s:%s" server-id pid) 8937 (format "%s:%s/%s" server-id pid status)))) 8938 8939 (defun lsp--map-tree-widget (m) 8940 "Build `tree-widget' from a hash-table or plist M." 8941 (when (lsp-structure-p m) 8942 (let (nodes) 8943 (lsp-map (lambda (k v) 8944 (push `(tree-widget 8945 :tag ,(if (lsp-structure-p v) 8946 (format "%s:" k) 8947 (format "%s: %s" k 8948 (propertize (format "%s" v) 8949 'face 8950 'font-lock-string-face))) 8951 :open t 8952 ,@(lsp--map-tree-widget v)) 8953 nodes)) 8954 m) 8955 nodes))) 8956 8957 (defun lsp-buffer-name (buffer-id) 8958 (if-let ((buffer-name (plist-get buffer-id :buffer-name))) 8959 (funcall buffer-name buffer-id) 8960 (buffer-name buffer-id))) 8961 8962 (defun lsp--render-workspace (workspace) 8963 "Tree node representation of WORKSPACE." 8964 `(tree-widget :tag ,(lsp--workspace-print workspace) 8965 :open t 8966 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face) 8967 :open t 8968 ,@(->> workspace 8969 (lsp--workspace-buffers) 8970 (--map `(tree-widget 8971 :tag ,(when (lsp-buffer-live-p it) 8972 (let ((buffer-name (lsp-buffer-name it))) 8973 (if (lsp-with-current-buffer it buffer-read-only) 8974 (propertize buffer-name 'face 'font-lock-constant-face) 8975 buffer-name))))))) 8976 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face) 8977 ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget)))) 8978 8979 (define-derived-mode lsp-browser-mode special-mode "LspBrowser" 8980 "Define mode for displaying lsp sessions." 8981 (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t))))) 8982 8983 (defun lsp-describe-session () 8984 "Describes current `lsp-session'." 8985 (interactive) 8986 (let ((session (lsp-session)) 8987 (buf (get-buffer-create "*lsp session*")) 8988 (root (lsp-workspace-root))) 8989 (with-current-buffer buf 8990 (lsp-browser-mode) 8991 (let ((inhibit-read-only t)) 8992 (erase-buffer) 8993 (--each (lsp-session-folders session) 8994 (widget-create 8995 `(tree-widget 8996 :tag ,(propertize it 'face 'font-lock-keyword-face) 8997 :open t 8998 ,@(->> session 8999 (lsp-session-folder->servers) 9000 (gethash it) 9001 (-map 'lsp--render-workspace))))))) 9002 (pop-to-buffer buf) 9003 (goto-char (point-min)) 9004 (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag) 9005 until (or (and root (string= tag root)) (eobp)) 9006 do (goto-char (next-overlay-change (point)))))) 9007 9008 (defun lsp--session-workspaces (session) 9009 "Get all workspaces that are part of the SESSION." 9010 (-> session lsp-session-folder->servers hash-table-values -flatten -uniq)) 9011 9012 (defun lsp--find-multiroot-workspace (session client project-root) 9013 "Look for a multiroot connection in SESSION created from CLIENT for 9014 PROJECT-ROOT and BUFFER-MAJOR-MODE." 9015 (when (lsp--client-multi-root client) 9016 (-when-let (multi-root-workspace (->> session 9017 (lsp--session-workspaces) 9018 (--first (eq (-> it lsp--workspace-client lsp--client-server-id) 9019 (lsp--client-server-id client))))) 9020 (with-lsp-workspace multi-root-workspace 9021 (lsp-notify "workspace/didChangeWorkspaceFolders" 9022 (lsp-make-did-change-workspace-folders-params 9023 :event (lsp-make-workspace-folders-change-event 9024 :added (vector (lsp-make-workspace-folder 9025 :uri (lsp--path-to-uri project-root) 9026 :name (f-filename project-root))) 9027 :removed [])))) 9028 9029 (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace)) 9030 (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root)) 9031 9032 (lsp--persist-session session) 9033 9034 (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace)) 9035 (lsp--open-in-workspace multi-root-workspace) 9036 9037 multi-root-workspace))) 9038 9039 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder) 9040 "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT. 9041 IGNORE-MULTI-FOLDER to ignore multi folder server." 9042 (-map (lambda (client) 9043 (or 9044 (lsp--find-workspace session client project-root) 9045 (unless ignore-multi-folder 9046 (lsp--find-multiroot-workspace session client project-root)) 9047 (lsp--start-connection session client project-root))) 9048 clients)) 9049 9050 (defun lsp--spinner-stop () 9051 "Stop the spinner in case all of the workspaces are started." 9052 (when (--all? (eq (lsp--workspace-status it) 'initialized) 9053 lsp--buffer-workspaces) 9054 (spinner-stop))) 9055 9056 (defun lsp--open-in-workspace (workspace) 9057 "Open in existing WORKSPACE." 9058 (if (eq 'initialized (lsp--workspace-status workspace)) 9059 ;; when workspace is initialized just call document did open. 9060 (progn 9061 (with-lsp-workspace workspace 9062 (when-let ((before-document-open-fn (-> workspace 9063 lsp--workspace-client 9064 lsp--client-before-file-open-fn))) 9065 (funcall before-document-open-fn workspace)) 9066 (lsp--text-document-did-open)) 9067 (lsp--spinner-stop)) 9068 ;; when it is not initialized 9069 (lsp--spinner-start) 9070 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace)))) 9071 9072 (defun lsp--find-workspace (session client project-root) 9073 "Find server connection created with CLIENT in SESSION for PROJECT-ROOT." 9074 (when-let ((workspace (->> session 9075 (lsp-session-folder->servers) 9076 (gethash project-root) 9077 (--first (eql (-> it lsp--workspace-client lsp--client-server-id) 9078 (lsp--client-server-id client)))))) 9079 (lsp--open-in-workspace workspace) 9080 workspace)) 9081 9082 (defun lsp--read-char (prompt &optional options) 9083 "Wrapper for `read-char-from-minibuffer' if Emacs +27. 9084 Fallback to `read-key' otherwise. 9085 PROMPT is the message and OPTIONS the available options." 9086 (if (fboundp 'read-char-from-minibuffer) 9087 (read-char-from-minibuffer prompt options) 9088 (read-key prompt))) 9089 9090 (defun lsp--find-root-interactively (session) 9091 "Find project interactively. 9092 Returns nil if the project should not be added to the current SESSION." 9093 (condition-case nil 9094 (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory)) 9095 (action (lsp--read-char 9096 (format 9097 "%s is not part of any project. 9098 9099 %s ==> Import project root %s 9100 %s ==> Import project by selecting root directory interactively 9101 %s ==> Import project at current directory %s 9102 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist 9103 %s ==> Do not ask again for the current project by selecting ignore path interactively 9104 %s ==> Do nothing: ask again when opening other files from the current project 9105 9106 Select action: " 9107 (propertize (buffer-name) 'face 'bold) 9108 (propertize "i" 'face 'success) 9109 (propertize project-root-suggestion 'face 'bold) 9110 (propertize "I" 'face 'success) 9111 (propertize "." 'face 'success) 9112 (propertize default-directory 'face 'bold) 9113 (propertize "d" 'face 'warning) 9114 (propertize project-root-suggestion 'face 'bold) 9115 (propertize "D" 'face 'warning) 9116 (propertize "n" 'face 'warning)) 9117 '(?i ?\r ?I ?. ?d ?D ?n)))) 9118 (cl-case action 9119 (?i project-root-suggestion) 9120 (?\r project-root-suggestion) 9121 (?I (read-directory-name "Select workspace folder to add: " 9122 (or project-root-suggestion default-directory) 9123 nil 9124 t)) 9125 (?. default-directory) 9126 (?d (push project-root-suggestion (lsp-session-folders-blocklist session)) 9127 (lsp--persist-session session) 9128 nil) 9129 (?D (push (read-directory-name "Select folder to blocklist: " 9130 (or project-root-suggestion default-directory) 9131 nil 9132 t) 9133 (lsp-session-folders-blocklist session)) 9134 (lsp--persist-session session) 9135 nil) 9136 (t nil))) 9137 (quit))) 9138 9139 (declare-function tramp-file-name-host "ext:tramp" (file) t) 9140 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault)) 9141 9142 (defun lsp--files-same-host (f1 f2) 9143 "Predicate on whether or not two files are on the same host." 9144 (or (not (or (file-remote-p f1) (file-remote-p f2))) 9145 (and (file-remote-p f1) 9146 (file-remote-p f2) 9147 (progn (require 'tramp) 9148 (equal (tramp-file-name-host (tramp-dissect-file-name f1)) 9149 (tramp-file-name-host (tramp-dissect-file-name f2))))))) 9150 9151 (defun lsp-find-session-folder (session file-name) 9152 "Look in the current SESSION for folder containing FILE-NAME." 9153 (let ((file-name-canonical (lsp-f-canonical file-name))) 9154 (->> session 9155 (lsp-session-folders) 9156 (--filter (and (lsp--files-same-host it file-name-canonical) 9157 (or (lsp-f-same? it file-name-canonical) 9158 (and (f-dir? it) 9159 (lsp-f-ancestor-of? it file-name-canonical))))) 9160 (--max-by (> (length it) 9161 (length other)))))) 9162 9163 (defun lsp-find-workspace (server-id &optional file-name) 9164 "Find workspace for SERVER-ID for FILE-NAME." 9165 (-when-let* ((session (lsp-session)) 9166 (folder->servers (lsp-session-folder->servers session)) 9167 (workspaces (if file-name 9168 (gethash (lsp-find-session-folder session file-name) folder->servers) 9169 (lsp--session-workspaces session)))) 9170 9171 (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces))) 9172 9173 (defun lsp--calculate-root (session file-name) 9174 "Calculate project root for FILE-NAME in SESSION." 9175 (and 9176 (->> session 9177 (lsp-session-folders-blocklist) 9178 (--first (and (lsp--files-same-host it file-name) 9179 (lsp-f-ancestor-of? it file-name) 9180 (prog1 t 9181 (lsp--info "File %s is in blocklisted directory %s" file-name it)))) 9182 not) 9183 (or 9184 (when lsp-auto-guess-root 9185 (lsp--suggest-project-root)) 9186 (unless lsp-guess-root-without-session 9187 (lsp-find-session-folder session file-name)) 9188 (unless lsp-auto-guess-root 9189 (when-let ((root-folder (lsp--find-root-interactively session))) 9190 (if (or (not (f-equal? root-folder (expand-file-name "~/"))) 9191 (yes-or-no-p 9192 (concat 9193 (propertize "[WARNING] " 'face 'warning) 9194 "You are trying to import your home folder as project root. This may cause performance issue because some language servers (python, lua, etc) will try to scan all files under project root. To avoid that you may: 9195 9196 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/'). 9197 2. If your file is under `~/' then create a subfolder and move that file in this folder. 9198 9199 Type `No' to go back to project selection. 9200 Type `Yes' to confirm `HOME' as project root. 9201 Type `C-g' to cancel project import process and stop `lsp'"))) 9202 root-folder 9203 (lsp--calculate-root session file-name))))))) 9204 9205 (defun lsp--try-open-in-library-workspace () 9206 "Try opening current file as library file in any of the active workspace. 9207 The library folders are defined by each client for each of the active workspace." 9208 (when-let ((workspace (->> (lsp-session) 9209 (lsp--session-workspaces) 9210 ;; Sort the last active workspaces first as they are more likely to be 9211 ;; the correct ones, especially when jumping to a definition. 9212 (-sort (lambda (a _b) 9213 (-contains? lsp--last-active-workspaces a))) 9214 (--first 9215 (and (-> it lsp--workspace-client lsp--supports-buffer?) 9216 (when-let ((library-folders-fn 9217 (-> it lsp--workspace-client lsp--client-library-folders-fn))) 9218 (-first (lambda (library-folder) 9219 (lsp-f-ancestor-of? library-folder (buffer-file-name))) 9220 (funcall library-folders-fn it)))))))) 9221 (lsp--open-in-workspace workspace) 9222 (view-mode t) 9223 (lsp--info "Opening read-only library file %s." (buffer-file-name)) 9224 (list workspace))) 9225 9226 (defun lsp--persist-session (session) 9227 "Persist SESSION to `lsp-session-file'." 9228 (lsp--persist lsp-session-file (make-lsp-session 9229 :folders (lsp-session-folders session) 9230 :folders-blocklist (lsp-session-folders-blocklist session) 9231 :server-id->folders (lsp-session-server-id->folders session)))) 9232 9233 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder) 9234 "Try create opening file as a project file. 9235 When IGNORE-MULTI-FOLDER is t the lsp mode will start new 9236 language server even if there is language server which can handle 9237 current language. When IGNORE-MULTI-FOLDER is nil current file 9238 will be opened in multi folder language server if there is 9239 such." 9240 (-let ((session (lsp-session))) 9241 (-if-let (clients (if ask-for-client 9242 (list (lsp--completing-read "Select server to start: " 9243 (ht-values lsp-clients) 9244 (-compose 'symbol-name 'lsp--client-server-id) nil t)) 9245 (lsp--find-clients))) 9246 (-if-let (project-root (-some-> session 9247 (lsp--calculate-root (buffer-file-name)) 9248 (lsp-f-canonical))) 9249 (progn 9250 ;; update project roots if needed and persist the lsp session 9251 (unless (-contains? (lsp-session-folders session) project-root) 9252 (cl-pushnew project-root (lsp-session-folders session)) 9253 (lsp--persist-session session)) 9254 (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder)) 9255 (lsp--warn "%s not in project or it is blocklisted." (buffer-name)) 9256 nil) 9257 (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode) 9258 nil))) 9259 9260 (defun lsp-shutdown-workspace () 9261 "Shutdown language server." 9262 (interactive) 9263 (--when-let (pcase (lsp-workspaces) 9264 (`nil (user-error "There are no active servers in the current buffer")) 9265 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?" 9266 (lsp--workspace-print workspace))) 9267 workspace)) 9268 (workspaces (lsp--completing-read "Select server: " 9269 workspaces 9270 'lsp--workspace-print nil t))) 9271 (lsp-workspace-shutdown it))) 9272 9273 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1") 9274 9275 (defcustom lsp-auto-select-workspace t 9276 "Shutdown or restart a single workspace. 9277 If set and the current buffer has only a single workspace 9278 associated with it, `lsp-shutdown-workspace' and 9279 `lsp-restart-workspace' will act on it without asking." 9280 :type 'boolean 9281 :group 'lsp-mode) 9282 9283 (defun lsp--read-workspace () 9284 "Ask the user to select a workspace. 9285 Errors if there are none." 9286 (pcase (lsp-workspaces) 9287 (`nil (error "No workspaces associated with the current buffer")) 9288 ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace) 9289 (workspaces (lsp--completing-read "Select workspace: " workspaces 9290 #'lsp--workspace-print nil t)))) 9291 9292 (defun lsp-workspace-shutdown (workspace) 9293 "Shut the workspace WORKSPACE and the language server associated with it" 9294 (interactive (list (lsp--read-workspace))) 9295 (lsp--warn "Stopping %s" (lsp--workspace-print workspace)) 9296 (with-lsp-workspace workspace (lsp--shutdown-workspace))) 9297 9298 (defun lsp-disconnect () 9299 "Disconnect the buffer from the language server." 9300 (interactive) 9301 (lsp--text-document-did-close t) 9302 (lsp-managed-mode -1) 9303 (lsp-mode -1) 9304 (setq lsp--buffer-workspaces nil) 9305 (lsp--info "Disconnected")) 9306 9307 (defun lsp-restart-workspace () 9308 (interactive) 9309 (--when-let (pcase (lsp-workspaces) 9310 (`nil (user-error "There are no active servers in the current buffer")) 9311 (`(,workspace) workspace) 9312 (workspaces (lsp--completing-read "Select server: " 9313 workspaces 9314 'lsp--workspace-print nil t))) 9315 (lsp-workspace-restart it))) 9316 9317 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1") 9318 9319 (defun lsp-workspace-restart (workspace) 9320 "Restart the workspace WORKSPACE and the language server associated with it" 9321 (interactive (list (lsp--read-workspace))) 9322 (lsp--warn "Restarting %s" (lsp--workspace-print workspace)) 9323 (with-lsp-workspace workspace (lsp--shutdown-workspace t))) 9324 9325 ;;;###autoload 9326 (defun lsp (&optional arg) 9327 "Entry point for the server startup. 9328 When ARG is t the lsp mode will start new language server even if 9329 there is language server which can handle current language. When 9330 ARG is nil current file will be opened in multi folder language 9331 server if there is such. When `lsp' is called with prefix 9332 argument ask the user to select which language server to start." 9333 (interactive "P") 9334 9335 (lsp--require-packages) 9336 9337 (when (buffer-file-name) 9338 (let (clients 9339 (matching-clients (lsp--filter-clients 9340 (-andfn #'lsp--supports-buffer? 9341 #'lsp--server-binary-present?)))) 9342 (cond 9343 (matching-clients 9344 (when (setq lsp--buffer-workspaces 9345 (or (and 9346 ;; Don't open as library file if file is part of a project. 9347 (not (lsp-find-session-folder (lsp-session) (buffer-file-name))) 9348 (lsp--try-open-in-library-workspace)) 9349 (lsp--try-project-root-workspaces (equal arg '(4)) 9350 (and arg (not (equal arg 1)))))) 9351 (lsp-mode 1) 9352 (when lsp-auto-configure (lsp--auto-configure)) 9353 (setq lsp-buffer-uri (lsp--buffer-uri)) 9354 (lsp--info "Connected to %s." 9355 (apply 'concat (--map (format "[%s %s]" 9356 (lsp--workspace-print it) 9357 (lsp--workspace-root it)) 9358 lsp--buffer-workspaces))))) 9359 ;; look for servers which are currently being downloaded. 9360 ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9361 #'lsp--client-download-in-progress?))) 9362 (lsp--info "There are language server(%s) installation in progress. 9363 The server(s) will be started in the buffer when it has finished." 9364 (-map #'lsp--client-server-id clients)) 9365 (seq-do (lambda (client) 9366 (cl-pushnew (current-buffer) (lsp--client-buffers client))) 9367 clients)) 9368 ;; look for servers to install 9369 ((setq clients (lsp--filter-clients 9370 (-andfn #'lsp--supports-buffer? 9371 (-const lsp-enable-suggest-server-download) 9372 #'lsp--client-download-server-fn 9373 (-not #'lsp--client-download-in-progress?)))) 9374 (let ((client (lsp--completing-read 9375 (concat "Unable to find installed server supporting this file. " 9376 "The following servers could be installed automatically: ") 9377 clients 9378 (-compose #'symbol-name #'lsp--client-server-id) 9379 nil 9380 t))) 9381 (cl-pushnew (current-buffer) (lsp--client-buffers client)) 9382 (lsp--install-server-internal client))) 9383 ;; ignore other warnings 9384 ((not lsp-warn-no-matched-clients) 9385 nil) 9386 ;; automatic installation disabled 9387 ((setq clients (unless matching-clients 9388 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9389 #'lsp--client-download-server-fn 9390 (-not (-const lsp-enable-suggest-server-download)) 9391 (-not #'lsp--server-binary-present?))))) 9392 (lsp--warn "The following servers support current file but automatic download is disabled: %s 9393 \(If you have already installed the server check *lsp-log*)." 9394 (mapconcat (lambda (client) 9395 (symbol-name (lsp--client-server-id client))) 9396 clients 9397 " "))) 9398 ;; no clients present 9399 ((setq clients (unless matching-clients 9400 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9401 (-not #'lsp--server-binary-present?))))) 9402 (lsp--warn "The following servers support current file but do not have automatic installation: %s 9403 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages. 9404 \(If you have already installed the server check *lsp-log*)." 9405 (mapconcat (lambda (client) 9406 (symbol-name (lsp--client-server-id client))) 9407 clients 9408 " "))) 9409 ;; no matches 9410 ((-> #'lsp--supports-buffer? lsp--filter-clients not) 9411 (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'. 9412 This issue might be caused by: 9413 1. The language you are trying to use does not have built-in support in `lsp-mode'. You must install the required support manually. Examples of this are `lsp-java' or `lsp-metals'. 9414 2. The language server that you expect to run is not configured to run for major mode `%s'. You may check that by checking the `:major-modes' that are passed to `lsp-register-client'. 9415 3. `lsp-mode' doesn't have any integration for the language behind `%s'. Refer to https://emacs-lsp.github.io/lsp-mode/page/languages and https://langserver.org/ . 9416 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/. 9417 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients'). 9418 You can customize `lsp-warn-no-matched-clients' to disable this message." 9419 major-mode major-mode major-mode)))))) 9420 9421 (defun lsp--buffer-visible-p () 9422 "Return non nil if current buffer is visible." 9423 (or (buffer-modified-p) (get-buffer-window nil t))) 9424 9425 (defun lsp--init-if-visible () 9426 "Run `lsp' for the current buffer if the buffer is visible. 9427 Returns non nil if `lsp' was run for the buffer." 9428 (when (lsp--buffer-visible-p) 9429 (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t) 9430 (lsp) 9431 t)) 9432 9433 ;;;###autoload 9434 (defun lsp-deferred () 9435 "Entry point that defers server startup until buffer is visible. 9436 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'. 9437 This avoids overloading the server with many files when starting Emacs." 9438 ;; Workspace may not be initialized yet. Use a buffer local variable to 9439 ;; remember that we deferred loading of this buffer. 9440 (setq lsp--buffer-deferred t) 9441 (let ((buffer (current-buffer))) 9442 ;; Avoid false positives as desktop-mode restores buffers by deferring 9443 ;; visibility check until the stack clears. 9444 (run-with-idle-timer 0 nil (lambda () 9445 (when (buffer-live-p buffer) 9446 (with-current-buffer buffer 9447 (unless (lsp--init-if-visible) 9448 (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t)))))))) 9449 9450 9451 9452 (defvar lsp-file-truename-cache (ht)) 9453 9454 (defmacro lsp-with-cached-filetrue-name (&rest body) 9455 "Executes BODY caching the `file-truename' calls." 9456 `(let ((old-fn (symbol-function 'file-truename))) 9457 (unwind-protect 9458 (progn 9459 (fset 'file-truename 9460 (lambda (file-name &optional counter prev-dirs) 9461 (or (gethash file-name lsp-file-truename-cache) 9462 (puthash file-name (apply old-fn (list file-name counter prev-dirs)) 9463 lsp-file-truename-cache)))) 9464 ,@body) 9465 (fset 'file-truename old-fn)))) 9466 9467 9468 (defun lsp-virtual-buffer-call (key &rest args) 9469 (when lsp--virtual-buffer 9470 (when-let ((fn (plist-get lsp--virtual-buffer key))) 9471 (apply fn args)))) 9472 9473 (defun lsp-translate-column (column) 9474 "Translate COLUMN taking into account virtual buffers." 9475 (or (lsp-virtual-buffer-call :real->virtual-char column) 9476 column)) 9477 9478 (defun lsp-translate-line (line) 9479 "Translate LINE taking into account virtual buffers." 9480 (or (lsp-virtual-buffer-call :real->virtual-line line) 9481 line)) 9482 9483 9484 ;; lsp internal validation. 9485 9486 (defmacro lsp--doctor (&rest checks) 9487 `(-let [buf (current-buffer)] 9488 (with-current-buffer (get-buffer-create "*lsp-performance*") 9489 (with-help-window (current-buffer) 9490 ,@(-map (-lambda ((msg form)) 9491 `(insert (format "%s: %s\n" ,msg 9492 (let ((res (with-current-buffer buf 9493 ,form))) 9494 (cond 9495 ((eq res :optional) (propertize "OPTIONAL" 'face 'warning)) 9496 (res (propertize "OK" 'face 'success)) 9497 (t (propertize "ERROR" 'face 'error))))))) 9498 (-partition 2 checks)))))) 9499 9500 (define-obsolete-function-alias 'lsp-diagnose 9501 'lsp-doctor "lsp-mode 8.0.0") 9502 9503 (defun lsp-doctor () 9504 "Validate performance settings." 9505 (interactive) 9506 (lsp--doctor 9507 "Checking for Native JSON support" (functionp 'json-serialize) 9508 "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max) 9509 "Check `read-process-output-max' default has been changed from 4k" 9510 (and (boundp 'read-process-output-max) 9511 (> read-process-output-max 4096)) 9512 "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)" 9513 (condition-case _err 9514 (progn (lsp--make-message (list "a" "b")) 9515 nil) 9516 (error t)) 9517 "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000) 9518 "Using `plist' for deserialized objects? (refer to https://emacs-lsp.github.io/lsp-mode/page/performance/#use-plists-for-deserialization)" (or lsp-use-plists :optional) 9519 "Using emacs 28+ with native compilation?" 9520 (or (and (fboundp 'native-comp-available-p) 9521 (native-comp-available-p)) 9522 :optional))) 9523 9524 (declare-function package-version-join "ext:package") 9525 (declare-function package-desc-version "ext:package") 9526 (declare-function package--alist "ext:package") 9527 9528 (defun lsp-version () 9529 "Return string describing current version of `lsp-mode'." 9530 (interactive) 9531 (unless (featurep 'package) 9532 (require 'package)) 9533 (let ((ver (format "lsp-mode %s, Emacs %s, %s" 9534 (package-version-join 9535 (package-desc-version 9536 (car (alist-get 'lsp-mode (package--alist))))) 9537 emacs-version 9538 system-type))) 9539 (if (called-interactively-p 'interactive) 9540 (lsp--info "%s" ver) 9541 ver))) 9542 9543 9544 9545 ;; org-mode/virtual-buffer 9546 9547 (declare-function org-babel-get-src-block-info "ext:ob-core") 9548 (declare-function org-do-remove-indentation "ext:org-macs") 9549 (declare-function org-src-get-lang-mode "ext:org-src") 9550 (declare-function org-element-context "ext:org-element") 9551 9552 (defun lsp--virtual-buffer-update-position () 9553 (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range)) 9554 (funcall in-range)) 9555 lsp--virtual-buffer-connections)) 9556 (unless (equal virtual-buffer lsp--virtual-buffer) 9557 (lsp-org)) 9558 (when lsp-managed-mode 9559 (lsp-managed-mode -1) 9560 (lsp-mode -1) 9561 (setq lsp--buffer-workspaces nil) 9562 (setq lsp--virtual-buffer nil) 9563 (setq lsp-buffer-uri nil) 9564 9565 ;; force refresh of diagnostics 9566 (run-hooks 'lsp-after-diagnostics-hook)))) 9567 9568 (defun lsp-virtual-buffer-on-change (start end length) 9569 "Adjust on change event to be executed against the proper language server." 9570 (let ((max-point (max end 9571 (or (plist-get lsp--before-change-vals :end) 0) 9572 (+ start length)))) 9573 (when-let ((virtual-buffer (-first (lambda (vb) 9574 (let ((lsp--virtual-buffer vb)) 9575 (and (lsp-virtual-buffer-call :in-range start) 9576 (lsp-virtual-buffer-call :in-range max-point)))) 9577 lsp--virtual-buffer-connections))) 9578 (lsp-with-current-buffer virtual-buffer 9579 (lsp-on-change start end length 9580 (lambda (&rest _) 9581 (list :range (lsp--range (list :character 0 :line 0) 9582 lsp--virtual-buffer-point-max) 9583 :text (lsp--buffer-content)))))))) 9584 9585 (defun lsp-virtual-buffer-before-change (start _end) 9586 (when-let ((virtual-buffer (-first (lambda (vb) 9587 (lsp-with-current-buffer vb 9588 (lsp-virtual-buffer-call :in-range start))) 9589 lsp--virtual-buffer-connections))) 9590 (lsp-with-current-buffer virtual-buffer 9591 (setq lsp--virtual-buffer-point-max 9592 (lsp--point-to-position (lsp-virtual-buffer-call :last-point)))))) 9593 9594 (defun lsp-patch-on-change-event () 9595 (remove-hook 'after-change-functions #'lsp-on-change t) 9596 (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t) 9597 (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t)) 9598 9599 (defun lsp-kill-virtual-buffers () 9600 (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections)) 9601 9602 (defun lsp--move-point-in-indentation (point indentation) 9603 (save-excursion 9604 (goto-char point) 9605 (if (<= point (+ (line-beginning-position) indentation)) 9606 (line-beginning-position) 9607 point))) 9608 9609 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck") 9610 (declare-function flycheck-add-mode "ext:flycheck") 9611 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics") 9612 9613 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn) 9614 9615 (defun lsp-flycheck-add-mode (mode) 9616 "Register flycheck support for MODE." 9617 (lsp-diagnostics-lsp-checker-if-needed) 9618 (unless (flycheck-checker-supports-major-mode-p 'lsp mode) 9619 (flycheck-add-mode 'lsp mode))) 9620 9621 (defun lsp-progress-spinner-type () 9622 "Retrieve the spinner type value, if value is not a symbol of `spinner-types 9623 defaults to `progress-bar." 9624 (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar)) 9625 9626 (defun lsp-org () 9627 (interactive) 9628 (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range)) 9629 (funcall in-range)) 9630 lsp--virtual-buffer-connections)) 9631 (unless (equal lsp--virtual-buffer virtual-buffer) 9632 (setq lsp--buffer-workspaces workspaces) 9633 (setq lsp--virtual-buffer virtual-buffer) 9634 (setq lsp-buffer-uri nil) 9635 (lsp-mode 1) 9636 (lsp-managed-mode 1) 9637 (lsp-patch-on-change-event)) 9638 9639 (save-excursion 9640 (-let* (virtual-buffer 9641 (wcb (lambda (f) 9642 (with-current-buffer (plist-get virtual-buffer :buffer) 9643 (-let* (((&plist :major-mode :buffer-file-name 9644 :goto-buffer :workspaces) virtual-buffer) 9645 (lsp--virtual-buffer virtual-buffer) 9646 (lsp--buffer-workspaces workspaces)) 9647 (save-excursion 9648 (funcall goto-buffer) 9649 (funcall f)))))) 9650 ((&plist :begin :end :post-blank :language) (cl-second (org-element-context))) 9651 ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light))) 9652 9653 (file-name (if file-name 9654 (f-expand file-name) 9655 (user-error "You should specify file name in the src block header."))) 9656 (begin-marker (progn 9657 (goto-char begin) 9658 (forward-line) 9659 (set-marker (make-marker) (point)))) 9660 (end-marker (progn 9661 (goto-char end) 9662 (forward-line (1- (- post-blank))) 9663 (set-marker (make-marker) (1+ (point))))) 9664 (buf (current-buffer)) 9665 (src-block (buffer-substring-no-properties begin-marker 9666 (1- end-marker))) 9667 (indentation (with-temp-buffer 9668 (insert src-block) 9669 9670 (goto-char (point-min)) 9671 (let ((indentation (current-indentation))) 9672 (plist-put lsp--virtual-buffer :indentation indentation) 9673 (org-do-remove-indentation) 9674 (goto-char (point-min)) 9675 (- indentation (current-indentation)))))) 9676 (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t) 9677 9678 (when (fboundp 'flycheck-add-mode) 9679 (lsp-flycheck-add-mode 'org-mode)) 9680 9681 (setq lsp--virtual-buffer 9682 (list 9683 :in-range (lambda (&optional point) 9684 (<= begin-marker (or point (point)) (1- end-marker))) 9685 :goto-buffer (lambda () (goto-char begin-marker)) 9686 :buffer-string 9687 (lambda () 9688 (let ((src-block (buffer-substring-no-properties 9689 begin-marker 9690 (1- end-marker)))) 9691 (with-temp-buffer 9692 (insert src-block) 9693 9694 (goto-char (point-min)) 9695 (while (not (eobp)) 9696 (delete-region (point) (if (> (+ (point) indentation) (line-end-position)) 9697 (line-end-position) 9698 (+ (point) indentation))) 9699 (forward-line)) 9700 (buffer-substring-no-properties (point-min) 9701 (point-max))))) 9702 :buffer buf 9703 :begin begin-marker 9704 :end end-marker 9705 :indentation indentation 9706 :last-point (lambda () (1- end-marker)) 9707 :cur-position (lambda () 9708 (lsp-save-restriction-and-excursion 9709 (list :line (- (lsp--cur-line) 9710 (lsp--cur-line begin-marker)) 9711 :character (let ((character (- (point) 9712 (line-beginning-position) 9713 indentation))) 9714 (if (< character 0) 9715 0 9716 character))))) 9717 :line/character->point (-lambda (line character) 9718 (-let [inhibit-field-text-motion t] 9719 (+ indentation 9720 (lsp-save-restriction-and-excursion 9721 (goto-char begin-marker) 9722 (forward-line line) 9723 (-let [line-end (line-end-position)] 9724 (if (> character (- line-end (point))) 9725 line-end 9726 (forward-char character) 9727 (point))))))) 9728 :major-mode (org-src-get-lang-mode language) 9729 :buffer-file-name file-name 9730 :buffer-uri (lsp--path-to-uri file-name) 9731 :with-current-buffer wcb 9732 :buffer-live? (lambda (_) (buffer-live-p buf)) 9733 :buffer-name (lambda (_) 9734 (propertize (format "%s(%s:%s)%s" 9735 (buffer-name buf) 9736 begin-marker 9737 end-marker 9738 language) 9739 'face 'italic)) 9740 :real->virtual-line (lambda (line) 9741 (+ line (line-number-at-pos begin-marker) -1)) 9742 :real->virtual-char (lambda (char) (+ char indentation)) 9743 :cleanup (lambda () 9744 (set-marker begin-marker nil) 9745 (set-marker end-marker nil)))) 9746 (setf virtual-buffer lsp--virtual-buffer) 9747 (puthash file-name virtual-buffer lsp--virtual-buffer-mappings) 9748 (push virtual-buffer lsp--virtual-buffer-connections) 9749 9750 ;; TODO: tangle only connected sections 9751 (add-hook 'after-save-hook 'org-babel-tangle nil t) 9752 (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t) 9753 (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t) 9754 9755 (setq lsp--buffer-workspaces 9756 (lsp-with-current-buffer virtual-buffer 9757 (lsp) 9758 (plist-put virtual-buffer :workspaces (lsp-workspaces)) 9759 (lsp-workspaces))))))) 9760 9761 (defun lsp-virtual-buffer-disconnect (virtual-buffer) 9762 (interactive (list (or 9763 lsp--virtual-buffer 9764 (when lsp--virtual-buffer-connections 9765 (lsp--completing-read "Select virtual buffer to disconnect: " 9766 lsp--virtual-buffer-connections 9767 (-lambda ((&plist :buffer-file-name)) 9768 buffer-file-name)))))) 9769 (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer) 9770 (progn 9771 (lsp-with-current-buffer virtual-buffer 9772 (lsp--text-document-did-close)) 9773 (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections)) 9774 (when (eq virtual-buffer lsp--virtual-buffer) 9775 (setf lsp--virtual-buffer nil)) 9776 (when cleanup (funcall cleanup)) 9777 (remhash file-name lsp--virtual-buffer-mappings) 9778 9779 (lsp--virtual-buffer-update-position) 9780 (lsp--info "Disconnected from buffer %s" file-name)) 9781 (lsp--error "Nothing to disconnect from?"))) 9782 9783 9784 ;; inlay hints 9785 9786 (defface lsp-inlay-hint-face 9787 '((t :inherit font-lock-comment-face)) 9788 "The face to use for the JavaScript inlays." 9789 :group 'lsp-mode 9790 :package-version '(lsp-mode . "9.0.0")) 9791 9792 (defface lsp-inlay-hint-type-face 9793 '((t :inherit lsp-inlay-hint-face)) 9794 "Face for inlay type hints (e.g. inferred variable types)." 9795 :group 'lsp-mode 9796 :package-version '(lsp-mode . "9.0.0")) 9797 9798 (defcustom lsp-inlay-hint-type-format "%s" 9799 "Format string for variable inlays (part of the inlay face)." 9800 :type '(string :tag "String") 9801 :group 'lsp-mode 9802 :package-version '(lsp-mode . "9.0.0")) 9803 9804 (defface lsp-inlay-hint-parameter-face 9805 '((t :inherit lsp-inlay-hint-face)) 9806 "Face for inlay parameter hints (e.g. function parameter names at 9807 call-site)." 9808 :group 'lsp-mode 9809 :package-version '(lsp-mode . "9.0.0")) 9810 9811 (defcustom lsp-inlay-hint-param-format "%s" 9812 "Format string for parameter inlays (part of the inlay face)." 9813 :type '(string :tag "String") 9814 :group 'lsp-mode 9815 :package-version '(lsp-mode . "9.0.0")) 9816 9817 (defcustom lsp-update-inlay-hints-on-scroll t 9818 "If non-nil update inlay hints immediately when scrolling or 9819 modifying window sizes." 9820 :type 'boolean 9821 :package-version '(lsp-mode . "9.0.0")) 9822 9823 (defun lsp--format-inlay (text kind) 9824 (cond 9825 ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text)) 9826 ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text)) 9827 (t text))) 9828 9829 (defun lsp--face-for-inlay (kind) 9830 (cond 9831 ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face) 9832 ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face) 9833 (t 'lsp-inlay-hint-face))) 9834 9835 (defun lsp--update-inlay-hints-scroll-function (window start) 9836 (lsp-update-inlay-hints start (window-end window t))) 9837 9838 (defun lsp--update-inlay-hints () 9839 (lsp-update-inlay-hints (window-start) (window-end nil t))) 9840 9841 (defun lsp--label-from-inlay-hints-response (label) 9842 "Returns a string label built from an array of 9843 InlayHintLabelParts or the argument itself if it's already a 9844 string." 9845 (cl-typecase label 9846 (string label) 9847 (vector 9848 (string-join (mapcar (lambda (part) 9849 (-let (((&InlayHintLabelPart :value) part)) 9850 value)) 9851 label))))) 9852 9853 (defun lsp-update-inlay-hints (start end) 9854 (lsp-request-async 9855 "textDocument/inlayHint" 9856 (lsp-make-inlay-hints-params 9857 :text-document (lsp--text-document-identifier) 9858 :range (lsp-make-range :start 9859 (lsp-point-to-position start) 9860 :end 9861 (lsp-point-to-position end))) 9862 (lambda (res) 9863 (lsp--remove-overlays 'lsp-inlay-hint) 9864 (dolist (hint res) 9865 (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint) 9866 (kind (or kind? lsp/inlay-hint-kind-type-hint)) 9867 (label (lsp--label-from-inlay-hints-response label)) 9868 (pos (lsp--position-to-point position)) 9869 (overlay (make-overlay pos pos nil 'front-advance 'end-advance))) 9870 (when (stringp label) 9871 (overlay-put overlay 'lsp-inlay-hint t) 9872 (overlay-put overlay 'before-string 9873 (format "%s%s%s" 9874 (if padding-left? " " "") 9875 (propertize (lsp--format-inlay label kind) 9876 'font-lock-face (lsp--face-for-inlay kind)) 9877 (if padding-right? " " ""))))))) 9878 :mode 'tick)) 9879 9880 (define-minor-mode lsp-inlay-hints-mode 9881 "Mode for displaying inlay hints." 9882 :lighter nil 9883 (cond 9884 ((and lsp-inlay-hints-mode lsp--buffer-workspaces) 9885 (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t) 9886 (when lsp-update-inlay-hints-on-scroll 9887 (add-to-list (make-local-variable 'window-scroll-functions) 9888 #'lsp--update-inlay-hints-scroll-function))) 9889 (t 9890 (lsp--remove-overlays 'lsp-inlay-hint) 9891 (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t) 9892 (setf window-scroll-functions 9893 (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions))))) 9894 9895 9896 9897 ;;;###autoload 9898 (defun lsp-start-plain () 9899 "Start `lsp-mode' using minimal configuration using the latest `melpa' version 9900 of the packages. 9901 9902 In case the major-mode that you are using for " 9903 (interactive) 9904 (let ((start-plain (make-temp-file "plain" nil ".el"))) 9905 (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el" 9906 start-plain t) 9907 (async-shell-command 9908 (format "%s -q -l %s %s" 9909 (expand-file-name invocation-name invocation-directory) 9910 start-plain 9911 (or (buffer-file-name) "")) 9912 (generate-new-buffer " *lsp-start-plain*")))) 9913 9914 9915 9916 (provide 'lsp-mode) 9917 ;;; lsp-mode.el ends here