lsp-mode.el (430738B)
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 ;; Version: 9.0.1 9 10 ;; URL: https://github.com/emacs-lsp/lsp-mode 11 ;; This program is free software; you can redistribute it and/or modify 12 ;; it under the terms of the GNU General Public License as published by 13 ;; the Free Software Foundation, either version 3 of the License, or 14 ;; (at your option) any later version. 15 16 ;; This program is distributed in the hope that it will be useful, 17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 ;; GNU General Public License for more details. 20 21 ;; You should have received a copy of the GNU General Public License 22 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 23 24 ;;; Commentary: 25 26 ;; Emacs client/library for the Language Server Protocol 27 28 ;;; Code: 29 30 (require 'cl-generic) 31 (require 'cl-lib) 32 (require 'compile) 33 (require 'dash) 34 (require 'epg) 35 (require 'ewoc) 36 (require 'f) 37 (require 'filenotify) 38 (require 'files) 39 (require 'ht) 40 (require 'imenu) 41 (require 'inline) 42 (require 'json) 43 (require 'lv) 44 (require 'markdown-mode) 45 (require 'network-stream) 46 (require 'pcase) 47 (require 'rx) 48 (require 's) 49 (require 'seq) 50 (require 'spinner) 51 (require 'subr-x) 52 (require 'tree-widget) 53 (require 'url-parse) 54 (require 'url-util) 55 (require 'widget) 56 (require 'xref) 57 (require 'minibuffer) 58 (require 'help-mode) 59 (require 'lsp-protocol) 60 61 (defgroup lsp-mode nil 62 "Language Server Protocol client." 63 :group 'tools 64 :tag "Language Server (lsp-mode)") 65 66 (declare-function evil-set-command-property "ext:evil-common") 67 (declare-function projectile-project-root "ext:projectile") 68 (declare-function yas-expand-snippet "ext:yasnippet") 69 (declare-function dap-mode "ext:dap-mode") 70 (declare-function dap-auto-configure-mode "ext:dap-mode") 71 72 (defvar yas-inhibit-overlay-modification-protection) 73 (defvar yas-indent-line) 74 (defvar yas-wrap-around-region) 75 (defvar yas-also-auto-indent-first-line) 76 (defvar dap-auto-configure-mode) 77 (defvar dap-ui-menu-items) 78 (defvar company-minimum-prefix-length) 79 80 (defconst lsp--message-type-face 81 `((1 . ,compilation-error-face) 82 (2 . ,compilation-warning-face) 83 (3 . ,compilation-message-face) 84 (4 . ,compilation-info-face))) 85 86 (defconst lsp--errors 87 '((-32700 "Parse Error") 88 (-32600 "Invalid Request") 89 (-32601 "Method not Found") 90 (-32602 "Invalid Parameters") 91 (-32603 "Internal Error") 92 (-32099 "Server Start Error") 93 (-32000 "Server End Error") 94 (-32002 "Server Not Initialized") 95 (-32001 "Unknown Error Code") 96 (-32800 "Request Cancelled")) 97 "Alist of error codes to user friendly strings.") 98 99 (defconst lsp--empty-ht (make-hash-table)) 100 101 (eval-and-compile 102 (defun dash-expand:&lsp-wks (key source) 103 `(,(intern-soft (format "lsp--workspace-%s" (eval key))) ,source)) 104 105 (defun dash-expand:&lsp-cln (key source) 106 `(,(intern-soft (format "lsp--client-%s" (eval key))) ,source))) 107 108 (define-obsolete-variable-alias 'lsp-print-io 'lsp-log-io "lsp-mode 6.1") 109 110 (defcustom lsp-log-io nil 111 "If non-nil, log all messages from the language server to a *lsp-log* buffer." 112 :group 'lsp-mode 113 :type 'boolean) 114 115 (defcustom lsp-log-io-allowlist-methods '() 116 "The methods to filter before print to lsp-log-io." 117 :group 'lsp-mode 118 :type '(repeat string) 119 :package-version '(lsp-mode . "9.0.0")) 120 121 (defcustom lsp-log-max message-log-max 122 "Maximum number of lines to keep in the log buffer. 123 If nil, disable message logging. If t, log messages but don’t truncate 124 the buffer when it becomes large." 125 :group 'lsp-mode 126 :type '(choice (const :tag "Disable" nil) 127 (integer :tag "lines") 128 (const :tag "Unlimited" t)) 129 :package-version '(lsp-mode . "6.1")) 130 131 (defcustom lsp-io-messages-max t 132 "Maximum number of messages that can be locked in a `lsp-io' buffer." 133 :group 'lsp-mode 134 :type '(choice (const :tag "Unlimited" t) 135 (integer :tag "Messages")) 136 :package-version '(lsp-mode . "6.1")) 137 138 (defcustom lsp-keep-workspace-alive t 139 "If non nil keep workspace alive when the last workspace buffer is closed." 140 :group 'lsp-mode 141 :type 'boolean) 142 143 (defcustom lsp-enable-snippet t 144 "Enable/disable snippet completion support." 145 :group 'lsp-completion 146 :type 'boolean) 147 148 (defcustom lsp-enable-folding t 149 "Enable/disable code folding support." 150 :group 'lsp-mode 151 :type 'boolean 152 :package-version '(lsp-mode . "6.1")) 153 154 (define-obsolete-variable-alias 'lsp-enable-semantic-highlighting 'lsp-semantic-tokens-enable "lsp-mode 8.0.0") 155 156 (defcustom lsp-semantic-tokens-enable nil 157 "Enable/disable support for semantic tokens. 158 As defined by the Language Server Protocol 3.16." 159 :group 'lsp-semantic-tokens 160 :type 'boolean) 161 162 (defcustom lsp-folding-range-limit nil 163 "The maximum number of folding ranges to receive from the language server." 164 :group 'lsp-mode 165 :type '(choice (const :tag "No limit." nil) 166 (integer :tag "Number of lines.")) 167 :package-version '(lsp-mode . "6.1")) 168 169 (defcustom lsp-folding-line-folding-only nil 170 "If non-nil, only fold complete lines." 171 :group 'lsp-mode 172 :type 'boolean 173 :package-version '(lsp-mode . "6.1")) 174 175 (defcustom lsp-client-packages 176 '( ccls lsp-actionscript lsp-ada lsp-angular lsp-ansible lsp-asm lsp-astro 177 lsp-autotools lsp-awk lsp-bash lsp-beancount lsp-bufls lsp-clangd 178 lsp-clojure lsp-cmake lsp-cobol lsp-credo lsp-crystal lsp-csharp lsp-css 179 lsp-cucumber lsp-cypher lsp-d lsp-dart lsp-dhall lsp-docker lsp-dockerfile 180 lsp-earthly lsp-elixir lsp-elm lsp-emmet lsp-erlang lsp-eslint lsp-fortran lsp-fsharp 181 lsp-gdscript lsp-gleam lsp-glsl lsp-go lsp-golangci-lint lsp-grammarly 182 lsp-graphql lsp-groovy lsp-hack lsp-haskell lsp-haxe lsp-idris lsp-java 183 lsp-javascript lsp-jq lsp-json lsp-kotlin lsp-latex lsp-lisp lsp-ltex 184 lsp-lua lsp-magik lsp-markdown lsp-marksman lsp-mdx lsp-meson lsp-metals lsp-mint 185 lsp-mojo lsp-move lsp-mssql lsp-nginx lsp-nim lsp-nix lsp-nushell lsp-ocaml 186 lsp-openscad lsp-pascal lsp-perl lsp-perlnavigator lsp-php lsp-pls 187 lsp-purescript lsp-pwsh lsp-pyls lsp-pylsp lsp-pyright lsp-python-ms 188 lsp-qml lsp-r lsp-racket lsp-remark lsp-rf lsp-roslyn lsp-rubocop lsp-ruby-lsp 189 lsp-ruby-syntax-tree lsp-ruff-lsp lsp-rust lsp-semgrep lsp-shader 190 lsp-solargraph lsp-solidity lsp-sonarlint lsp-sorbet lsp-sourcekit 191 lsp-sql lsp-sqls lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform 192 lsp-tex lsp-tilt lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-v 193 lsp-vala lsp-verilog lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl 194 lsp-xml lsp-yaml lsp-yang lsp-zig) 195 "List of the clients to be automatically required." 196 :group 'lsp-mode 197 :type '(repeat symbol)) 198 199 (defcustom lsp-progress-via-spinner t 200 "If non-nil, display LSP $/progress reports via a spinner in the modeline." 201 :group 'lsp-mode 202 :type 'boolean) 203 204 (defcustom lsp-progress-spinner-type 'progress-bar 205 "Holds the type of spinner to be used in the mode-line. 206 Takes a value accepted by `spinner-start'." 207 :group 'lsp-mode 208 :type `(choice :tag "Choose a spinner by name" 209 ,@(mapcar (lambda (c) (list 'const (car c))) 210 spinner-types))) 211 212 (defvar-local lsp-use-workspace-root-for-server-default-directory nil 213 "Use `lsp-workspace-root' for `default-directory' when starting LSP process.") 214 215 (defvar-local lsp--cur-workspace nil) 216 217 (defvar-local lsp--cur-version 0) 218 (defvar-local lsp--virtual-buffer-connections nil) 219 (defvar-local lsp--virtual-buffer nil) 220 (defvar lsp--virtual-buffer-mappings (ht)) 221 222 (defvar lsp--uri-file-prefix (pcase system-type 223 (`windows-nt "file:///") 224 (_ "file://")) 225 "Prefix for a file-uri.") 226 227 (defvar-local lsp-buffer-uri nil 228 "If set, return it instead of calculating it using `buffer-file-name'.") 229 230 (define-error 'lsp-error "Unknown lsp-mode error") 231 (define-error 'lsp-empty-response-error 232 "Empty response from the language server" 'lsp-error) 233 (define-error 'lsp-timed-out-error 234 "Timed out while waiting for a response from the language server" 'lsp-error) 235 (define-error 'lsp-capability-not-supported 236 "Capability not supported by the language server" 'lsp-error) 237 (define-error 'lsp-file-scheme-not-supported 238 "Unsupported file scheme" 'lsp-error) 239 (define-error 'lsp-client-already-exists-error 240 "A client with this server-id already exists" 'lsp-error) 241 (define-error 'lsp-no-code-actions 242 "No code actions" 'lsp-error) 243 244 (defcustom lsp-auto-guess-root nil 245 "Automatically guess the project root using projectile/project. 246 Do *not* use this setting unless you are familiar with `lsp-mode' 247 internals and you are sure that all of your projects are 248 following `projectile'/`project.el' conventions." 249 :group 'lsp-mode 250 :type 'boolean) 251 252 (defcustom lsp-guess-root-without-session nil 253 "Ignore the session file when calculating the project root. 254 You almost always want to set lsp-auto-guess-root too. 255 Do *not* use this setting unless you are familiar with `lsp-mode' 256 internals and you are sure that all of your projects are 257 following `projectile'/`project.el' conventions." 258 :group 'lsp-mode 259 :type 'boolean) 260 261 (defcustom lsp-restart 'interactive 262 "Defines how server-exited events must be handled." 263 :group 'lsp-mode 264 :type '(choice (const interactive) 265 (const auto-restart) 266 (const ignore))) 267 268 (defcustom lsp-session-file (expand-file-name (locate-user-emacs-file ".lsp-session-v1")) 269 "File where session information is stored." 270 :group 'lsp-mode 271 :type 'file) 272 273 (defcustom lsp-auto-configure t 274 "Auto configure `lsp-mode' main features. 275 When set to t `lsp-mode' will auto-configure completion, 276 code-actions, breadcrumb, `flycheck', `flymake', `imenu', symbol highlighting, 277 lenses, links, and so on. 278 279 For finer granularity you may use `lsp-enable-*' properties." 280 :group 'lsp-mode 281 :type 'boolean 282 :package-version '(lsp-mode . "6.1")) 283 284 (defcustom lsp-disabled-clients nil 285 "A list of disabled/blocklisted clients. 286 Each entry in the list can be either: 287 a symbol, the server-id for the LSP client, or 288 a cons pair (MAJOR-MODE . CLIENTS), where MAJOR-MODE is the major-mode, 289 and CLIENTS is either a client or a list of clients. 290 291 This option can also be used as a file- or directory-local variable to 292 disable a language server for individual files or directories/projects 293 respectively." 294 :group 'lsp-mode 295 :type '(repeat (symbol)) 296 :safe 'listp 297 :package-version '(lsp-mode . "6.1")) 298 299 (defvar lsp-clients (make-hash-table :test 'eql) 300 "Hash table server-id -> client. 301 It contains all of the clients that are currently registered.") 302 303 (defvar lsp-enabled-clients nil 304 "List of clients allowed to be used for projects. 305 When nil, all registered clients are considered candidates.") 306 307 (defvar lsp-last-id 0 308 "Last request id.") 309 310 (defcustom lsp-before-initialize-hook nil 311 "List of functions to be called before a Language Server has been initialized 312 for a new workspace." 313 :type 'hook 314 :group 'lsp-mode) 315 316 (defcustom lsp-after-initialize-hook nil 317 "List of functions to be called after a Language Server has been initialized 318 for a new workspace." 319 :type 'hook 320 :group 'lsp-mode) 321 322 (defcustom lsp-before-open-hook nil 323 "List of functions to be called before a new file with LSP support is opened." 324 :type 'hook 325 :group 'lsp-mode) 326 327 (defcustom lsp-after-open-hook nil 328 "List of functions to be called after a new file with LSP support is opened." 329 :type 'hook 330 :group 'lsp-mode) 331 332 (defcustom lsp-enable-file-watchers t 333 "If non-nil lsp-mode will watch the files in the workspace if 334 the server has requested that." 335 :type 'boolean 336 :group 'lsp-mode 337 :package-version '(lsp-mode . "6.1")) 338 ;;;###autoload(put 'lsp-enable-file-watchers 'safe-local-variable #'booleanp) 339 340 (define-obsolete-variable-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "8.0.0") 341 342 (defcustom lsp-file-watch-ignored-directories 343 '(; SCM tools 344 "[/\\\\]\\.git\\'" 345 "[/\\\\]\\.github\\'" 346 "[/\\\\]\\.gitlab\\'" 347 "[/\\\\]\\.circleci\\'" 348 "[/\\\\]\\.hg\\'" 349 "[/\\\\]\\.bzr\\'" 350 "[/\\\\]_darcs\\'" 351 "[/\\\\]\\.svn\\'" 352 "[/\\\\]_FOSSIL_\\'" 353 ;; IDE or build tools 354 "[/\\\\]\\.idea\\'" 355 "[/\\\\]\\.ensime_cache\\'" 356 "[/\\\\]\\.eunit\\'" 357 "[/\\\\]node_modules" 358 "[/\\\\]\\.yarn\\'" 359 "[/\\\\]\\.fslckout\\'" 360 "[/\\\\]\\.tox\\'" 361 "[/\\\\]\\.nox\\'" 362 "[/\\\\]dist\\'" 363 "[/\\\\]dist-newstyle\\'" 364 "[/\\\\]\\.stack-work\\'" 365 "[/\\\\]\\.bloop\\'" 366 "[/\\\\]\\.metals\\'" 367 "[/\\\\]target\\'" 368 "[/\\\\]\\.ccls-cache\\'" 369 "[/\\\\]\\.vs\\'" 370 "[/\\\\]\\.vscode\\'" 371 "[/\\\\]\\.venv\\'" 372 "[/\\\\]\\.mypy_cache\\'" 373 "[/\\\\]\\.pytest_cache\\'" 374 ;; Swift Package Manager 375 "[/\\\\]\\.build\\'" 376 ;; Python 377 "[/\\\\]__pycache__\\'" 378 ;; Autotools output 379 "[/\\\\]\\.deps\\'" 380 "[/\\\\]build-aux\\'" 381 "[/\\\\]autom4te.cache\\'" 382 "[/\\\\]\\.reference\\'" 383 ;; Bazel 384 "[/\\\\]bazel-[^/\\\\]+\\'" 385 ;; CSharp 386 "[/\\\\]\\.cache[/\\\\]lsp-csharp\\'" 387 "[/\\\\]\\.meta\\'" 388 "[/\\\\]\\.nuget\\'" 389 ;; Unity 390 "[/\\\\]Library\\'" 391 ;; Clojure 392 "[/\\\\]\\.lsp\\'" 393 "[/\\\\]\\.clj-kondo\\'" 394 "[/\\\\]\\.shadow-cljs\\'" 395 "[/\\\\]\\.babel_cache\\'" 396 "[/\\\\]\\.cpcache\\'" 397 "[/\\\\]\\checkouts\\'" 398 ;; Gradle 399 "[/\\\\]\\.gradle\\'" 400 ;; Maven 401 "[/\\\\]\\.m2\\'" 402 ;; .Net Core build-output 403 "[/\\\\]bin/Debug\\'" 404 "[/\\\\]obj\\'" 405 ;; OCaml and Dune 406 "[/\\\\]_opam\\'" 407 "[/\\\\]_build\\'" 408 ;; Elixir 409 "[/\\\\]\\.elixir_ls\\'" 410 ;; Elixir Credo 411 "[/\\\\]\\.elixir-tools\\'" 412 ;; terraform and terragrunt 413 "[/\\\\]\\.terraform\\'" 414 "[/\\\\]\\.terragrunt-cache\\'" 415 ;; nix-direnv 416 "[/\\\\]\\result" 417 "[/\\\\]\\result-bin" 418 "[/\\\\]\\.direnv\\'") 419 "List of regexps matching directory paths which won't be monitored when 420 creating file watches. Customization of this variable is only honored at 421 the global level or at a root of an lsp workspace." 422 :group 'lsp-mode 423 :type '(repeat string) 424 :package-version '(lsp-mode . "8.0.0")) 425 426 (define-obsolete-function-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "7.0.1") 427 428 (defun lsp-file-watch-ignored-directories () 429 lsp-file-watch-ignored-directories) 430 431 ;; Allow lsp-file-watch-ignored-directories as a file or directory-local variable 432 ;;;###autoload(put 'lsp-file-watch-ignored-directories 'safe-local-variable 'lsp--string-listp) 433 434 (defcustom lsp-file-watch-ignored-files 435 '( 436 ;; Flycheck tempfiles 437 "[/\\\\]flycheck_[^/\\\\]+\\'" 438 ;; lockfiles 439 "[/\\\\]\\.#[^/\\\\]+\\'" 440 ;; backup files 441 "[/\\\\][^/\\\\]+~\\'" ) 442 "List of regexps matching files for which change events will 443 not be sent to the server. 444 445 This setting has no impact on whether a file-watch is created for 446 a directory; it merely prevents notifications pertaining to 447 matched files from being sent to the server. To prevent a 448 file-watch from being created for a directory, customize 449 `lsp-file-watch-ignored-directories' 450 451 Customization of this variable is only honored at the global 452 level or at a root of an lsp workspace." 453 :group 'lsp-mode 454 :type '(repeat string) 455 :package-version '(lsp-mode . "8.0.0")) 456 457 ;; Allow lsp-file-watch-ignored-files as a file or directory-local variable 458 ;;;###autoload(put 'lsp-file-watch-ignored-files 'safe-local-variable 'lsp--string-listp) 459 460 (defcustom lsp-after-uninitialized-functions nil 461 "List of functions to be called after a Language Server has been uninitialized." 462 :type 'hook 463 :group 'lsp-mode 464 :package-version '(lsp-mode . "6.3")) 465 466 (defconst lsp--sync-full 1) 467 (defconst lsp--sync-incremental 2) 468 469 (defcustom lsp-debounce-full-sync-notifications t 470 "If non-nil debounce full sync events. 471 This flag affects only servers which do not support incremental updates." 472 :type 'boolean 473 :group 'lsp-mode 474 :package-version '(lsp-mode . "6.1")) 475 476 (defcustom lsp-debounce-full-sync-notifications-interval 1.0 477 "Time to wait before sending full sync synchronization after buffer modification." 478 :type 'float 479 :group 'lsp-mode 480 :package-version '(lsp-mode . "6.1")) 481 482 (defvar lsp--stderr-index 0) 483 484 (defvar lsp--delayed-requests nil) 485 (defvar lsp--delay-timer nil) 486 487 (defcustom lsp-document-sync-method nil 488 "How to sync the document with the language server." 489 :type '(choice (const :tag "Documents are synced by always sending the full content of the document." lsp--sync-full) 490 (const :tag "Documents are synced by always sending incremental changes to the document." lsp--sync-incremental) 491 (const :tag "Use the method recommended by the language server." nil)) 492 :group 'lsp-mode) 493 494 (defcustom lsp-auto-execute-action t 495 "Auto-execute single action." 496 :type 'boolean 497 :group 'lsp-mode) 498 499 (defcustom lsp-enable-links t 500 "If non-nil, all references to links in a file will be made clickable, if 501 supported by the language server." 502 :type 'boolean 503 :group 'lsp-mode 504 :package-version '(lsp-mode . "6.1")) 505 506 (defcustom lsp-enable-imenu t 507 "If non-nil, automatically enable `imenu' integration when server provides 508 `textDocument/documentSymbol'." 509 :type 'boolean 510 :group 'lsp-mode 511 :package-version '(lsp-mode . "6.2")) 512 513 (defcustom lsp-enable-dap-auto-configure t 514 "If non-nil, enable `dap-auto-configure-mode`." 515 :type 'boolean 516 :group 'lsp-mode 517 :package-version '(lsp-mode . "7.0")) 518 519 (defcustom lsp-eldoc-enable-hover t 520 "If non-nil, `eldoc' will display hover info when it is present." 521 :type 'boolean 522 :group 'lsp-mode) 523 524 (defcustom lsp-eldoc-render-all nil 525 "Display all of the info returned by document/onHover. 526 If this is set to nil, `eldoc' will show only the symbol information." 527 :type 'boolean 528 :group 'lsp-mode) 529 530 (define-obsolete-variable-alias 'lsp-enable-completion-at-point 531 'lsp-completion-enable "lsp-mode 7.0.1") 532 533 (defcustom lsp-completion-enable t 534 "Enable `completion-at-point' integration." 535 :type 'boolean 536 :group 'lsp-completion) 537 538 (defcustom lsp-enable-symbol-highlighting t 539 "Highlight references of the symbol at point." 540 :type 'boolean 541 :group 'lsp-mode) 542 543 (defcustom lsp-enable-xref t 544 "Enable xref integration." 545 :type 'boolean 546 :group 'lsp-mode) 547 548 (defcustom lsp-references-exclude-definition nil 549 "If non-nil, exclude declarations when finding references." 550 :type 'boolean 551 :group 'lsp-mode) 552 553 (defcustom lsp-enable-indentation t 554 "Indent regions using the file formatting functionality provided by the 555 language server." 556 :type 'boolean 557 :group 'lsp-mode) 558 559 (defcustom lsp-enable-on-type-formatting t 560 "Enable `textDocument/onTypeFormatting' integration." 561 :type 'boolean 562 :group 'lsp-mode) 563 564 (defcustom lsp-enable-text-document-color t 565 "Enable `textDocument/documentColor' integration." 566 :type 'boolean 567 :group 'lsp-mode) 568 569 (defcustom lsp-before-save-edits t 570 "If non-nil, `lsp-mode' will apply edits suggested by the language server 571 before saving a document." 572 :type 'boolean 573 :group 'lsp-mode) 574 575 (defcustom lsp-after-apply-edits-hook nil 576 "Hooks to run when text edit is applied. 577 It contains the operation source." 578 :type 'hook 579 :group 'lsp-mode 580 :package-version '(lsp-mode . "8.0.0")) 581 582 (defcustom lsp-apply-edits-after-file-operations t 583 "Whether to apply edits returned by server after file operations if any. 584 Applicable only if server supports workspace.fileOperations for operations: 585 `workspace/willRenameFiles', `workspace/willCreateFiles' and 586 `workspace/willDeleteFiles'." 587 :group 'lsp-mode 588 :type 'boolean) 589 590 (defcustom lsp-modeline-code-actions-enable t 591 "Whether to show code actions on modeline." 592 :type 'boolean 593 :group 'lsp-modeline) 594 595 (defcustom lsp-modeline-diagnostics-enable t 596 "Whether to show diagnostics on modeline." 597 :type 'boolean 598 :group 'lsp-modeline) 599 600 (defcustom lsp-modeline-workspace-status-enable t 601 "Whether to show workspace status on modeline." 602 :type 'boolean 603 :group 'lsp-modeline 604 :package-version '(lsp-mode . "8.0.0")) 605 606 (defcustom lsp-headerline-breadcrumb-enable t 607 "Whether to enable breadcrumb on headerline." 608 :type 'boolean 609 :group 'lsp-headerline) 610 611 (defcustom lsp-configure-hook nil 612 "Hooks to run when `lsp-configure-buffer' is called." 613 :type 'hook 614 :group 'lsp-mode) 615 616 (defcustom lsp-unconfigure-hook nil 617 "Hooks to run when `lsp-unconfig-buffer' is called." 618 :type 'hook 619 :group 'lsp-mode) 620 621 (defcustom lsp-after-diagnostics-hook nil 622 "Hooks to run after diagnostics are received. 623 Note: it runs only if the receiving buffer is open. Use 624 `lsp-diagnostics-updated-hook'if you want to be notified when 625 diagnostics have changed." 626 :type 'hook 627 :group 'lsp-mode) 628 629 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook 630 'lsp-diagnostics-updated-hook "lsp-mode 6.4") 631 632 (defcustom lsp-diagnostics-updated-hook nil 633 "Hooks to run after diagnostics are received." 634 :type 'hook 635 :group 'lsp-mode) 636 637 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook 638 'lsp-workspace-folders-changed-functions "lsp-mode 6.3") 639 640 (defcustom lsp-workspace-folders-changed-functions nil 641 "Hooks to run after the folders has changed. 642 The hook will receive two parameters list of added and removed folders." 643 :type 'hook 644 :group 'lsp-mode) 645 646 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0") 647 648 (defcustom lsp-before-apply-edits-hook nil 649 "Hooks to run before applying edits." 650 :type 'hook 651 :group 'lsp-mode) 652 653 (defgroup lsp-imenu nil 654 "LSP Imenu." 655 :group 'lsp-mode 656 :tag "LSP Imenu") 657 658 (defcustom lsp-imenu-show-container-name t 659 "Display the symbol's container name in an imenu entry." 660 :type 'boolean 661 :group 'lsp-imenu) 662 663 (defcustom lsp-imenu-container-name-separator "/" 664 "Separator string to use to separate the container name from the symbol while 665 displaying imenu entries." 666 :type 'string 667 :group 'lsp-imenu) 668 669 (defcustom lsp-imenu-sort-methods '(kind name) 670 "How to sort the imenu items. 671 672 The value is a list of `kind' `name' or `position'. Priorities 673 are determined by the index of the element." 674 :type '(repeat (choice (const name) 675 (const position) 676 (const kind))) 677 :group 'lsp-imenu) 678 679 (defcustom lsp-imenu-index-symbol-kinds nil 680 "Which symbol kinds to show in imenu." 681 :type '(repeat (choice (const :tag "Miscellaneous" nil) 682 (const :tag "File" File) 683 (const :tag "Module" Module) 684 (const :tag "Namespace" Namespace) 685 (const :tag "Package" Package) 686 (const :tag "Class" Class) 687 (const :tag "Method" Method) 688 (const :tag "Property" Property) 689 (const :tag "Field" Field) 690 (const :tag "Constructor" Constructor) 691 (const :tag "Enum" Enum) 692 (const :tag "Interface" Interface) 693 (const :tag "Function" Function) 694 (const :tag "Variable" Variable) 695 (const :tag "Constant" Constant) 696 (const :tag "String" String) 697 (const :tag "Number" Number) 698 (const :tag "Boolean" Boolean) 699 (const :tag "Array" Array) 700 (const :tag "Object" Object) 701 (const :tag "Key" Key) 702 (const :tag "Null" Null) 703 (const :tag "Enum Member" EnumMember) 704 (const :tag "Struct" Struct) 705 (const :tag "Event" Event) 706 (const :tag "Operator" Operator) 707 (const :tag "Type Parameter" TypeParameter))) 708 :group 'lsp-imenu) 709 710 ;; vibhavp: Should we use a lower value (5)? 711 (defcustom lsp-response-timeout 10 712 "Number of seconds to wait for a response from the language server before 713 timing out. Nil if no timeout." 714 :type '(choice 715 (number :tag "Seconds") 716 (const :tag "No timeout" nil)) 717 :group 'lsp-mode) 718 719 (defcustom lsp-tcp-connection-timeout 2 720 "The timeout for tcp connection in seconds." 721 :type 'number 722 :group 'lsp-mode 723 :package-version '(lsp-mode . "6.2")) 724 725 (defconst lsp--imenu-compare-function-alist 726 (list (cons 'name #'lsp--imenu-compare-name) 727 (cons 'kind #'lsp--imenu-compare-kind) 728 (cons 'position #'lsp--imenu-compare-line-col)) 729 "An alist of (METHOD . FUNCTION). 730 METHOD is one of the symbols accepted by 731 `lsp-imenu-sort-methods'. 732 733 FUNCTION takes two hash tables representing DocumentSymbol. It 734 returns a negative number, 0, or a positive number indicating 735 whether the first parameter is less than, equal to, or greater 736 than the second parameter.") 737 738 (defcustom lsp-diagnostic-clean-after-change nil 739 "When non-nil, clean the diagnostics on change. 740 741 Note that when that setting is nil, `lsp-mode' will show stale 742 diagnostics until server publishes the new set of diagnostics" 743 :type 'boolean 744 :group 'lsp-diagnostics 745 :package-version '(lsp-mode . "7.0.1")) 746 747 (defcustom lsp-server-trace nil 748 "Request tracing on the server side. 749 The actual trace output at each level depends on the language server in use. 750 Changes take effect only when a new session is started." 751 :type '(choice (const :tag "Disabled" "off") 752 (const :tag "Messages only" "messages") 753 (const :tag "Verbose" "verbose") 754 (const :tag "Default (disabled)" nil)) 755 :group 'lsp-mode 756 :package-version '(lsp-mode . "6.1")) 757 758 (defcustom lsp-auto-touch-files t 759 "If non-nil ensure the files exist before sending 760 `textDocument/didOpen' notification." 761 :type 'boolean 762 :group 'lsp-mode 763 :package-version '(lsp-mode . "9.0.0")) 764 765 (defvar lsp-language-id-configuration 766 '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake") 767 ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile") 768 ("\\.astro$" . "astro") 769 ("\\.cs\\'" . "csharp") 770 ("\\.css$" . "css") 771 ("\\.cypher$" . "cypher") 772 ("Earthfile" . "earthfile") 773 ("\\.ebuild$" . "shellscript") 774 ("\\.go\\'" . "go") 775 ("\\.html$" . "html") 776 ("\\.hx$" . "haxe") 777 ("\\.hy$" . "hy") 778 ("\\.java\\'" . "java") 779 ("\\.jq$" . "jq") 780 ("\\.js$" . "javascript") 781 ("\\.json$" . "json") 782 ("\\.jsonc$" . "jsonc") 783 ("\\.jsonnet$" . "jsonnet") 784 ("\\.jsx$" . "javascriptreact") 785 ("\\.lua$" . "lua") 786 ("\\.mdx\\'" . "mdx") 787 ("\\.nu$" . "nushell") 788 ("\\.php$" . "php") 789 ("\\.ps[dm]?1\\'" . "powershell") 790 ("\\.rs\\'" . "rust") 791 ("\\.spec\\'" . "rpm-spec") 792 ("\\.sql$" . "sql") 793 ("\\.svelte$" . "svelte") 794 ("\\.toml\\'" . "toml") 795 ("\\.ts$" . "typescript") 796 ("\\.tsx$" . "typescriptreact") 797 ("\\.ttcn3$" . "ttcn3") 798 ("\\.vue$" . "vue") 799 ("\\.xml$" . "xml") 800 ("\\ya?ml$" . "yaml") 801 ("^PKGBUILD$" . "shellscript") 802 ("^go\\.mod\\'" . "go.mod") 803 ("^settings\\.json$" . "jsonc") 804 ("^yang\\.settings$" . "jsonc") 805 ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson") 806 (ada-mode . "ada") 807 (ada-ts-mode . "ada") 808 (gpr-mode . "gpr") 809 (gpr-ts-mode . "gpr") 810 (awk-mode . "awk") 811 (awk-ts-mode . "awk") 812 (nxml-mode . "xml") 813 (sql-mode . "sql") 814 (vimrc-mode . "vim") 815 (vimscript-ts-mode . "vim") 816 (sh-mode . "shellscript") 817 (bash-ts-mode . "shellscript") 818 (ebuild-mode . "shellscript") 819 (pkgbuild-mode . "shellscript") 820 (envrc-file-mode . "shellscript") 821 (scala-mode . "scala") 822 (scala-ts-mode . "scala") 823 (julia-mode . "julia") 824 (julia-ts-mode . "julia") 825 (clojure-mode . "clojure") 826 (clojurec-mode . "clojure") 827 (clojurescript-mode . "clojurescript") 828 (clojure-ts-mode . "clojure") 829 (clojure-ts-clojurec-mode . "clojure") 830 (clojure-ts-clojurescript-mode . "clojurescript") 831 (java-mode . "java") 832 (java-ts-mode . "java") 833 (jdee-mode . "java") 834 (groovy-mode . "groovy") 835 (python-mode . "python") 836 (python-ts-mode . "python") 837 (cython-mode . "python") 838 ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo") 839 (lsp--render-markdown . "markdown") 840 (move-mode . "move") 841 (rust-mode . "rust") 842 (rust-ts-mode . "rust") 843 (rustic-mode . "rust") 844 (kotlin-mode . "kotlin") 845 (kotlin-ts-mode . "kotlin") 846 (css-mode . "css") 847 (css-ts-mode . "css") 848 (less-mode . "less") 849 (less-css-mode . "less") 850 (lua-mode . "lua") 851 (lua-ts-mode . "lua") 852 (sass-mode . "sass") 853 (ssass-mode . "sass") 854 (scss-mode . "scss") 855 (scad-mode . "openscad") 856 (xml-mode . "xml") 857 (c-mode . "c") 858 (c-ts-mode . "c") 859 (c++-mode . "cpp") 860 (c++-ts-mode . "cpp") 861 (cuda-mode . "cuda") 862 (objc-mode . "objective-c") 863 (html-mode . "html") 864 (html-ts-mode . "html") 865 (sgml-mode . "html") 866 (mhtml-mode . "html") 867 (mint-mode . "mint") 868 (go-dot-mod-mode . "go.mod") 869 (go-mod-ts-mode . "go.mod") 870 (go-mode . "go") 871 (go-ts-mode . "go") 872 (graphql-mode . "graphql") 873 (haskell-mode . "haskell") 874 (hack-mode . "hack") 875 (php-mode . "php") 876 (php-ts-mode . "php") 877 (powershell-mode . "powershell") 878 (powershell-mode . "PowerShell") 879 (powershell-ts-mode . "powershell") 880 (json-mode . "json") 881 (json-ts-mode . "json") 882 (jsonc-mode . "jsonc") 883 (rjsx-mode . "javascript") 884 (js2-mode . "javascript") 885 (js-mode . "javascript") 886 (js-ts-mode . "javascript") 887 (typescript-mode . "typescript") 888 (typescript-ts-mode . "typescript") 889 (tsx-ts-mode . "typescriptreact") 890 (svelte-mode . "svelte") 891 (fsharp-mode . "fsharp") 892 (reason-mode . "reason") 893 (caml-mode . "ocaml") 894 (tuareg-mode . "ocaml") 895 (swift-mode . "swift") 896 (elixir-mode . "elixir") 897 (elixir-ts-mode . "elixir") 898 (heex-ts-mode . "elixir") 899 (conf-javaprop-mode . "spring-boot-properties") 900 (yaml-mode . "yaml") 901 (yaml-ts-mode . "yaml") 902 (ruby-mode . "ruby") 903 (enh-ruby-mode . "ruby") 904 (ruby-ts-mode . "ruby") 905 (feature-mode . "cucumber") 906 (fortran-mode . "fortran") 907 (f90-mode . "fortran") 908 (elm-mode . "elm") 909 (dart-mode . "dart") 910 (erlang-mode . "erlang") 911 (dockerfile-mode . "dockerfile") 912 (dockerfile-ts-mode . "dockerfile") 913 (csharp-mode . "csharp") 914 (csharp-tree-sitter-mode . "csharp") 915 (csharp-ts-mode . "csharp") 916 (plain-tex-mode . "plaintex") 917 (context-mode . "context") 918 (cypher-mode . "cypher") 919 (latex-mode . "latex") 920 (LaTeX-mode . "latex") 921 (v-mode . "v") 922 (vhdl-mode . "vhdl") 923 (vhdl-ts-mode . "vhdl") 924 (verilog-mode . "verilog") 925 (terraform-mode . "terraform") 926 (ess-julia-mode . "julia") 927 (ess-r-mode . "r") 928 (crystal-mode . "crystal") 929 (nim-mode . "nim") 930 (dhall-mode . "dhall") 931 (cmake-mode . "cmake") 932 (cmake-ts-mode . "cmake") 933 (purescript-mode . "purescript") 934 (gdscript-mode . "gdscript") 935 (gdscript-ts-mode . "gdscript") 936 (perl-mode . "perl") 937 (cperl-mode . "perl") 938 (robot-mode . "robot") 939 (racket-mode . "racket") 940 (nix-mode . "nix") 941 (nix-ts-mode . "Nix") 942 (prolog-mode . "prolog") 943 (vala-mode . "vala") 944 (actionscript-mode . "actionscript") 945 (d-mode . "d") 946 (zig-mode . "zig") 947 (text-mode . "plaintext") 948 (markdown-mode . "markdown") 949 (gfm-mode . "markdown") 950 (beancount-mode . "beancount") 951 (conf-toml-mode . "toml") 952 (toml-ts-mode . "toml") 953 (org-mode . "org") 954 (org-journal-mode . "org") 955 (nginx-mode . "nginx") 956 (magik-mode . "magik") 957 (magik-ts-mode . "magik") 958 (idris-mode . "idris") 959 (idris2-mode . "idris2") 960 (gleam-mode . "gleam") 961 (gleam-ts-mode . "gleam") 962 (graphviz-dot-mode . "dot") 963 (tiltfile-mode . "tiltfile") 964 (solidity-mode . "solidity") 965 (bibtex-mode . "bibtex") 966 (rst-mode . "restructuredtext") 967 (glsl-mode . "glsl") 968 (shader-mode . "shaderlab") 969 (wgsl-mode . "wgsl") 970 (jq-mode . "jq") 971 (jq-ts-mode . "jq") 972 (protobuf-mode . "protobuf") 973 (nushell-mode . "nushell") 974 (nushell-ts-mode . "nushell") 975 (meson-mode . "meson") 976 (yang-mode . "yang")) 977 "Language id configuration.") 978 979 (defvar lsp--last-active-workspaces nil 980 "Keep track of last active workspace. 981 We want to try the last workspace first when jumping into a library 982 directory") 983 984 (defvar lsp-method-requirements 985 '(("textDocument/callHierarchy" :capability :callHierarchyProvider) 986 ("textDocument/codeAction" :capability :codeActionProvider) 987 ("codeAction/resolve" 988 :check-command (lambda (workspace) 989 (with-lsp-workspace workspace 990 (lsp:code-action-options-resolve-provider? 991 (lsp--capability-for-method "textDocument/codeAction"))))) 992 ("textDocument/codeLens" :capability :codeLensProvider) 993 ("textDocument/completion" :capability :completionProvider) 994 ("completionItem/resolve" 995 :check-command (lambda (wk) 996 (with-lsp-workspace wk 997 (lsp:completion-options-resolve-provider? 998 (lsp--capability-for-method "textDocument/completion"))))) 999 ("textDocument/declaration" :capability :declarationProvider) 1000 ("textDocument/definition" :capability :definitionProvider) 1001 ("textDocument/documentColor" :capability :colorProvider) 1002 ("textDocument/documentLink" :capability :documentLinkProvider) 1003 ("textDocument/inlayHint" :capability :inlayHintProvider) 1004 ("textDocument/documentHighlight" :capability :documentHighlightProvider) 1005 ("textDocument/documentSymbol" :capability :documentSymbolProvider) 1006 ("textDocument/foldingRange" :capability :foldingRangeProvider) 1007 ("textDocument/formatting" :capability :documentFormattingProvider) 1008 ("textDocument/hover" :capability :hoverProvider) 1009 ("textDocument/implementation" :capability :implementationProvider) 1010 ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider) 1011 ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider) 1012 ("textDocument/prepareRename" 1013 :check-command (lambda (workspace) 1014 (with-lsp-workspace workspace 1015 (lsp:rename-options-prepare-provider? 1016 (lsp--capability-for-method "textDocument/rename"))))) 1017 ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider) 1018 ("textDocument/references" :capability :referencesProvider) 1019 ("textDocument/rename" :capability :renameProvider) 1020 ("textDocument/selectionRange" :capability :selectionRangeProvider) 1021 ("textDocument/semanticTokens" :capability :semanticTokensProvider) 1022 ("textDocument/semanticTokensFull" 1023 :check-command (lambda (workspace) 1024 (with-lsp-workspace workspace 1025 (lsp-get (lsp--capability :semanticTokensProvider) :full)))) 1026 ("textDocument/semanticTokensFull/Delta" 1027 :check-command (lambda (workspace) 1028 (with-lsp-workspace workspace 1029 (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full))) 1030 (and (not (booleanp capFull)) (lsp-get capFull :delta)))))) 1031 ("textDocument/semanticTokensRangeProvider" 1032 :check-command (lambda (workspace) 1033 (with-lsp-workspace workspace 1034 (lsp-get (lsp--capability :semanticTokensProvider) :range)))) 1035 ("textDocument/signatureHelp" :capability :signatureHelpProvider) 1036 ("textDocument/typeDefinition" :capability :typeDefinitionProvider) 1037 ("textDocument/typeHierarchy" :capability :typeHierarchyProvider) 1038 ("workspace/executeCommand" :capability :executeCommandProvider) 1039 ("workspace/symbol" :capability :workspaceSymbolProvider)) 1040 1041 "Map methods to requirements. 1042 It is used by request-sending functions to determine which server 1043 must be used for handling a particular message.") 1044 1045 (defconst lsp--file-change-type 1046 `((created . 1) 1047 (changed . 2) 1048 (deleted . 3))) 1049 1050 (defconst lsp--watch-kind 1051 `((create . 1) 1052 (change . 2) 1053 (delete . 4))) 1054 1055 (defvar lsp-window-body-width 40 1056 "Window body width when rendering doc.") 1057 1058 (defface lsp-face-highlight-textual 1059 '((t :inherit highlight)) 1060 "Face used for textual occurrences of symbols." 1061 :group 'lsp-mode) 1062 1063 (defface lsp-face-highlight-read 1064 '((t :inherit highlight :underline t)) 1065 "Face used for highlighting symbols being read." 1066 :group 'lsp-mode) 1067 1068 (defface lsp-face-highlight-write 1069 '((t :inherit highlight :weight bold)) 1070 "Face used for highlighting symbols being written to." 1071 :group 'lsp-mode) 1072 1073 (define-obsolete-variable-alias 'lsp-lens-auto-enable 1074 'lsp-lens-enable "lsp-mode 7.0.1") 1075 1076 (defcustom lsp-lens-enable t 1077 "Auto enable lenses if server supports." 1078 :group 'lsp-lens 1079 :type 'boolean 1080 :package-version '(lsp-mode . "6.3")) 1081 1082 (defcustom lsp-symbol-highlighting-skip-current nil 1083 "If non-nil skip current symbol when setting symbol highlights." 1084 :group 'lsp-mode 1085 :type 'boolean) 1086 1087 (defcustom lsp-file-watch-threshold 1000 1088 "Show warning if the files to watch are more than. 1089 Set to nil to disable the warning." 1090 :type 'number 1091 :group 'lsp-mode) 1092 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i)))) 1093 1094 (defvar lsp-custom-markup-modes 1095 '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic")) 1096 "Mode to uses with markdown code blocks. 1097 They are added to `markdown-code-lang-modes'") 1098 1099 (defcustom lsp-signature-render-documentation t 1100 "Display signature documentation in `eldoc'." 1101 :type 'boolean 1102 :group 'lsp-mode 1103 :package-version '(lsp-mode . "6.2")) 1104 1105 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request) 1106 "Auto activate signature conditions." 1107 :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char) 1108 (const :tag "After selected completion." :after-completion) 1109 (const :tag "When the server has sent show signature help." :on-server-request))) 1110 :group 'lsp-mode 1111 :package-version '(lsp-mode . "6.2")) 1112 1113 (defcustom lsp-signature-doc-lines 20 1114 "If number, limit the number of lines to show in the docs." 1115 :type 'number 1116 :group 'lsp-mode 1117 :package-version '(lsp-mode . "6.3")) 1118 1119 (defcustom lsp-signature-function 'lsp-lv-message 1120 "The function used for displaying signature info. 1121 It will be called with one param - the signature info. When 1122 called with nil the signature info must be cleared." 1123 :type 'function 1124 :group 'lsp-mode 1125 :package-version '(lsp-mode . "6.3")) 1126 1127 (defcustom lsp-keymap-prefix "s-l" 1128 "LSP-mode keymap prefix." 1129 :group 'lsp-mode 1130 :type 'string 1131 :package-version '(lsp-mode . "6.3")) 1132 1133 (defvar-local lsp--buffer-workspaces () 1134 "List of the buffer workspaces.") 1135 1136 (defvar-local lsp--buffer-deferred nil 1137 "Whether buffer was loaded via `lsp-deferred'.") 1138 1139 (defvar lsp--session nil 1140 "Contain the `lsp-session' for the current Emacs instance.") 1141 1142 (defvar lsp--tcp-port 10000) 1143 1144 (defvar lsp--client-packages-required nil 1145 "If nil, `lsp-client-packages' are yet to be required.") 1146 1147 (defvar lsp--tcp-server-port 0 1148 "The server socket which is opened when using `lsp-tcp-server' (a server 1149 socket is opened in Emacs and the language server connects to it). The 1150 default value of 0 ensures that a random high port is used. Set it to a positive 1151 integer to use a specific port.") 1152 1153 (defvar lsp--tcp-server-wait-seconds 10 1154 "Wait this amount of time for the client to connect to our server socket 1155 when using `lsp-tcp-server'.") 1156 1157 (defvar-local lsp--document-symbols nil 1158 "The latest document symbols.") 1159 1160 (defvar-local lsp--document-selection-range-cache nil 1161 "The document selection cache.") 1162 1163 (defvar-local lsp--document-symbols-request-async nil 1164 "If non-nil, request document symbols asynchronously.") 1165 1166 (defvar-local lsp--document-symbols-tick -1 1167 "The value of `buffer-chars-modified-tick' when document 1168 symbols were last retrieved.") 1169 1170 (defvar-local lsp--have-document-highlights nil 1171 "Set to `t' on symbol highlighting, cleared on 1172 `lsp--cleanup-highlights-if-needed'. Checking a separately 1173 defined flag is substantially faster than unconditionally 1174 calling `remove-overlays'.") 1175 1176 ;; Buffer local variable for storing number of lines. 1177 (defvar lsp--log-lines) 1178 1179 (defvar-local lsp--eldoc-saved-message nil) 1180 1181 (defvar lsp--on-change-timer nil) 1182 (defvar lsp--on-idle-timer nil) 1183 1184 (defvar-local lsp--signature-last nil) 1185 (defvar-local lsp--signature-last-index nil) 1186 (defvar lsp--signature-last-buffer nil) 1187 1188 (defvar-local lsp--virtual-buffer-point-max nil) 1189 1190 (cl-defmethod lsp-execute-command (_server _command _arguments) 1191 "Ask SERVER to execute COMMAND with ARGUMENTS.") 1192 1193 (defun lsp-elt (sequence n) 1194 "Return Nth element of SEQUENCE or nil if N is out of range." 1195 (cond 1196 ((listp sequence) (elt sequence n)) 1197 ((arrayp sequence) 1198 (and (> (length sequence) n) (aref sequence n))) 1199 (t (and (> (length sequence) n) (elt sequence n))))) 1200 1201 ;; define seq-first and seq-rest for older emacs 1202 (defun lsp-seq-first (sequence) 1203 "Return the first element of SEQUENCE." 1204 (lsp-elt sequence 0)) 1205 1206 (defun lsp-seq-rest (sequence) 1207 "Return a sequence of the elements of SEQUENCE except the first one." 1208 (seq-drop sequence 1)) 1209 1210 ;;;###autoload 1211 (defun lsp--string-listp (sequence) 1212 "Return t if all elements of SEQUENCE are strings, else nil." 1213 (not (seq-find (lambda (x) (not (stringp x))) sequence))) 1214 1215 (defun lsp--string-vector-p (candidate) 1216 "Returns true if CANDIDATE is a vector data structure and 1217 every element of it is of type string, else nil." 1218 (and 1219 (vectorp candidate) 1220 (seq-every-p #'stringp candidate))) 1221 1222 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0") 1223 1224 (defun lsp--editable-vector-match (widget value) 1225 "Function for `lsp-editable-vector' :match." 1226 ;; Value must be a list or a vector and all the members must match the type. 1227 (and (or (listp value) (vectorp value)) 1228 (length (cdr (lsp--editable-vector-match-inline widget value))))) 1229 1230 (defun lsp--editable-vector-match-inline (widget value) 1231 "Value for `lsp-editable-vector' :match-inline." 1232 (let ((type (nth 0 (widget-get widget :args))) 1233 (ok t) 1234 found) 1235 (while (and value ok) 1236 (let ((answer (widget-match-inline type value))) 1237 (if answer 1238 (let ((head (if (vectorp answer) (aref answer 0) (car answer))) 1239 (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer)))) 1240 (setq found (append found head) 1241 value tail)) 1242 (setq ok nil)))) 1243 (cons found value))) 1244 1245 (defun lsp--editable-vector-value-to-external (_widget internal-value) 1246 "Convert the internal list value to a vector." 1247 (if (listp internal-value) 1248 (apply 'vector internal-value) 1249 internal-value)) 1250 1251 (defun lsp--editable-vector-value-to-internal (_widget external-value) 1252 "Convert the external vector value to a list." 1253 (if (vectorp external-value) 1254 (append external-value nil) 1255 external-value)) 1256 1257 (define-widget 'lsp--editable-vector 'editable-list 1258 "A subclass of `editable-list' that accepts and returns a 1259 vector instead of a list." 1260 :value-to-external 'lsp--editable-vector-value-to-external 1261 :value-to-internal 'lsp--editable-vector-value-to-internal 1262 :match 'lsp--editable-vector-match 1263 :match-inline 'lsp--editable-vector-match-inline) 1264 1265 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector 1266 "A variable length homogeneous vector." 1267 :tag "Repeat" 1268 :format "%{%t%}:\n%v%i\n") 1269 1270 (define-widget 'lsp-string-vector 'lazy 1271 "A vector of zero or more elements, every element of which is a string. 1272 Appropriate for any language-specific `defcustom' that needs to 1273 serialize as a JSON array of strings. 1274 1275 Deprecated. Use `lsp-repeatable-vector' instead. " 1276 :offset 4 1277 :tag "Vector" 1278 :type '(lsp-repeatable-vector string)) 1279 1280 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0") 1281 1282 (defvar lsp--show-message t 1283 "If non-nil, show debug message from `lsp-mode'.") 1284 1285 (defun lsp--message (format &rest args) 1286 "Wrapper for `message' 1287 1288 We `inhibit-message' the message when the cursor is in the 1289 minibuffer and when emacs version is before emacs 27 due to the 1290 fact that we often use `lsp--info', `lsp--warn' and `lsp--error' 1291 in async context and the call to these function is removing the 1292 minibuffer prompt. The issue with async messages is already fixed 1293 in emacs 27. 1294 1295 See #2049" 1296 (when lsp--show-message 1297 (let ((inhibit-message (or inhibit-message 1298 (and (minibufferp) 1299 (version< emacs-version "27.0"))))) 1300 (apply #'message format args)))) 1301 1302 (defun lsp--info (format &rest args) 1303 "Display lsp info message with FORMAT with ARGS." 1304 (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args))) 1305 1306 (defun lsp--warn (format &rest args) 1307 "Display lsp warn message with FORMAT with ARGS." 1308 (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args))) 1309 1310 (defun lsp--error (format &rest args) 1311 "Display lsp error message with FORMAT with ARGS." 1312 (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args))) 1313 1314 (defun lsp-log (format &rest args) 1315 "Log message to the ’*lsp-log*’ buffer. 1316 1317 FORMAT and ARGS i the same as for `message'." 1318 (when lsp-log-max 1319 (let ((log-buffer (get-buffer "*lsp-log*")) 1320 (inhibit-read-only t)) 1321 (unless log-buffer 1322 (setq log-buffer (get-buffer-create "*lsp-log*")) 1323 (with-current-buffer log-buffer 1324 (buffer-disable-undo) 1325 (view-mode 1) 1326 (set (make-local-variable 'lsp--log-lines) 0))) 1327 (with-current-buffer log-buffer 1328 (save-excursion 1329 (let* ((message (apply 'format format args)) 1330 ;; Count newlines in message. 1331 (newlines (1+ (cl-loop with start = 0 1332 for count from 0 1333 while (string-match "\n" message start) 1334 do (setq start (match-end 0)) 1335 finally return count)))) 1336 (goto-char (point-max)) 1337 1338 ;; in case the buffer is not empty insert before last \n to preserve 1339 ;; the point position(in case it is in the end) 1340 (if (eq (point) (point-min)) 1341 (progn 1342 (insert "\n") 1343 (backward-char)) 1344 (backward-char) 1345 (insert "\n")) 1346 (insert message) 1347 1348 (setq lsp--log-lines (+ lsp--log-lines newlines)) 1349 1350 (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max)) 1351 (let ((to-delete (- lsp--log-lines lsp-log-max))) 1352 (goto-char (point-min)) 1353 (forward-line to-delete) 1354 (delete-region (point-min) (point)) 1355 (setq lsp--log-lines lsp-log-max))))))))) 1356 1357 (defalias 'lsp-message 'lsp-log) 1358 1359 (defalias 'lsp-ht 'ht) 1360 1361 (defalias 'lsp-file-local-name 'file-local-name) 1362 1363 (defun lsp-f-canonical (file-name) 1364 "Return the canonical FILE-NAME, without a trailing slash." 1365 (directory-file-name (expand-file-name file-name))) 1366 1367 (defalias 'lsp-canonical-file-name 'lsp-f-canonical) 1368 1369 (defun lsp-f-same? (path-a path-b) 1370 "Return t if PATH-A and PATH-B are references to the same file. 1371 Symlinks are not followed." 1372 (when (and (f-exists? path-a) 1373 (f-exists? path-b)) 1374 (equal 1375 (lsp-f-canonical (directory-file-name (f-expand path-a))) 1376 (lsp-f-canonical (directory-file-name (f-expand path-b)))))) 1377 1378 (defun lsp-f-parent (path) 1379 "Return the parent directory to PATH. 1380 Symlinks are not followed." 1381 (let ((parent (file-name-directory 1382 (directory-file-name (f-expand path default-directory))))) 1383 (unless (lsp-f-same? path parent) 1384 (if (f-relative? path) 1385 (f-relative parent) 1386 (directory-file-name parent))))) 1387 1388 (defun lsp-f-ancestor-of? (path-a path-b) 1389 "Return t if PATH-A is an ancestor of PATH-B. 1390 Symlinks are not followed." 1391 (unless (lsp-f-same? path-a path-b) 1392 (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator)) 1393 (lsp-f-canonical path-b)))) 1394 1395 (defun lsp--merge-results (results method) 1396 "Merge RESULTS by filtering the empty hash-tables and merging 1397 the lists according to METHOD." 1398 (pcase (--map (if (vectorp it) 1399 (append it nil) it) 1400 (-filter #'identity results)) 1401 (`() ()) 1402 ;; only one result - simply return it 1403 (`(,fst) fst) 1404 ;; multiple results merge it based on strategy 1405 (results 1406 (pcase method 1407 ("textDocument/hover" (pcase (seq-filter 1408 (-compose #'not #'lsp-empty?) 1409 results) 1410 (`(,hover) hover) 1411 (hovers (lsp-make-hover 1412 :contents 1413 (-mapcat 1414 (-lambda ((&Hover :contents)) 1415 (if (and (sequencep contents) 1416 (not (stringp contents))) 1417 (append contents ()) 1418 (list contents))) 1419 hovers))))) 1420 ("textDocument/completion" 1421 (lsp-make-completion-list 1422 :is-incomplete (seq-some 1423 #'lsp:completion-list-is-incomplete 1424 results) 1425 :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it) 1426 (lsp:completion-list-items it) 1427 it) 1428 nil)) 1429 results))) 1430 ("completionItem/resolve" 1431 (let ((item (cl-first results))) 1432 (when-let ((details (seq-filter #'identity 1433 (seq-map #'lsp:completion-item-detail? results)))) 1434 (lsp:set-completion-item-detail? 1435 item 1436 (string-join details " "))) 1437 (when-let ((docs (seq-filter #'identity 1438 (seq-map #'lsp:completion-item-documentation? results)))) 1439 (lsp:set-completion-item-documentation? 1440 item 1441 (lsp-make-markup-content 1442 :kind (or (seq-some (lambda (it) 1443 (when (equal (lsp:markup-content-kind it) 1444 lsp/markup-kind-markdown) 1445 lsp/markup-kind-markdown)) 1446 docs) 1447 lsp/markup-kind-plain-text) 1448 :value (string-join (seq-map (lambda (doc) 1449 (or (lsp:markup-content-value doc) 1450 (and (stringp doc) doc))) 1451 docs) 1452 "\n")))) 1453 (when-let ((edits (seq-filter #'identity 1454 (seq-map #'lsp:completion-item-additional-text-edits? results)))) 1455 (lsp:set-completion-item-additional-text-edits? 1456 item 1457 (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits))) 1458 item)) 1459 (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results)))))) 1460 1461 (defun lsp--spinner-start () 1462 "Start spinner indication." 1463 (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error))) 1464 1465 (defun lsp--propertize (str type) 1466 "Propertize STR as per TYPE." 1467 (propertize str 'face (alist-get type lsp--message-type-face))) 1468 1469 (defun lsp-workspaces () 1470 "Return the lsp workspaces associated with the current project." 1471 (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces)) 1472 1473 (defun lsp--completing-read (prompt collection transform-fn &optional predicate 1474 require-match initial-input 1475 hist def inherit-input-method) 1476 "Wrap `completing-read' to provide transformation function and disable sort. 1477 1478 TRANSFORM-FN will be used to transform each of the items before displaying. 1479 1480 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF 1481 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes." 1482 (let* ((col (--map (cons (funcall transform-fn it) it) collection)) 1483 (completion (completing-read prompt 1484 (lambda (string pred action) 1485 (if (eq action 'metadata) 1486 `(metadata (display-sort-function . identity)) 1487 (complete-with-action action col string pred))) 1488 predicate require-match initial-input hist 1489 def inherit-input-method))) 1490 (cdr (assoc completion col)))) 1491 1492 (defconst lsp--system-arch (lambda () 1493 (setq lsp--system-arch 1494 (pcase system-type 1495 ('windows-nt 1496 (pcase system-configuration 1497 ((rx bol "x86_64-") 'x64) 1498 (_ 'x86))) 1499 ('darwin 1500 (pcase system-configuration 1501 ((rx "aarch64-") 'arm64) 1502 (_ 'x64))) 1503 ('gnu/linux 1504 (pcase system-configuration 1505 ((rx bol "x86_64") 'x64) 1506 ((rx bol (| "i386" "i886")) 'x32))) 1507 (_ 1508 (pcase system-configuration 1509 ((rx bol "x86_64") 'x64) 1510 ((rx bol (| "i386" "i886")) 'x32)))))) 1511 "Return the system architecture of `Emacs'. 1512 Special values: 1513 `x64' 64bit 1514 `x32' 32bit 1515 `arm64' ARM 64bit") 1516 1517 (defmacro lsp-with-current-buffer (buffer-id &rest body) 1518 (declare (indent 1) (debug t)) 1519 `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer))) 1520 (with-lsp-workspaces (plist-get ,buffer-id :workspaces) 1521 (funcall wcb (lambda () ,@body))) 1522 (with-current-buffer ,buffer-id 1523 ,@body))) 1524 1525 (defvar lsp--throw-on-input nil 1526 "Make `lsp-*-while-no-input' throws `input' on interrupted.") 1527 1528 (defmacro lsp--catch (tag bodyform &rest handlers) 1529 "Catch TAG thrown in BODYFORM. 1530 The return value from TAG will be handled in HANDLERS by `pcase'." 1531 (declare (debug (form form &rest (pcase-PAT body))) (indent 2)) 1532 (let ((re-sym (make-symbol "re"))) 1533 `(let ((,re-sym (catch ,tag ,bodyform))) 1534 (pcase ,re-sym 1535 ,@handlers)))) 1536 1537 (defmacro lsp--while-no-input (&rest body) 1538 "Wrap BODY in `while-no-input' and respecting `non-essential'. 1539 If `lsp--throw-on-input' is set, will throw if input is pending, else 1540 return value of `body' or nil if interrupted." 1541 (declare (debug t) (indent 0)) 1542 `(if non-essential 1543 (let ((res (while-no-input ,@body))) 1544 (cond 1545 ((and lsp--throw-on-input (equal res t)) 1546 (throw 'input :interrupted)) 1547 ((booleanp res) nil) 1548 (t res))) 1549 ,@body)) 1550 1551 ;; A ‘lsp--client’ object describes the client-side behavior of a language 1552 ;; server. It is used to start individual server processes, each of which is 1553 ;; represented by a ‘lsp--workspace’ object. Client objects are normally 1554 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’. Each 1555 ;; workspace refers to exactly one client, but there can be multiple workspaces 1556 ;; for a single client. 1557 (cl-defstruct lsp--client 1558 ;; ‘language-id’ is a function that receives a buffer as a single argument 1559 ;; and should return the language identifier for that buffer. See 1560 ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem 1561 ;; for a list of language identifiers. Also consult the documentation for 1562 ;; the language server represented by this client to find out what language 1563 ;; identifiers it supports or expects. 1564 (language-id nil) 1565 1566 ;; ‘add-on?’ when set to t the server will be started no matter whether there 1567 ;; is another server handling the same mode. 1568 (add-on? nil) 1569 ;; ‘new-connection’ is a function that should start a language server process 1570 ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS). 1571 ;; COMMAND-PROCESS must be a process object representing the server process 1572 ;; just started. COMMUNICATION-PROCESS must be a process (including pipe and 1573 ;; network processes) that ‘lsp-mode’ uses to communicate with the language 1574 ;; server using the language server protocol. COMMAND-PROCESS and 1575 ;; COMMUNICATION-PROCESS may be the same process; in that case 1576 ;; ‘new-connection’ may also return that process as a single 1577 ;; object. ‘new-connection’ is called with two arguments, FILTER and 1578 ;; SENTINEL. FILTER should be used as process filter for 1579 ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for 1580 ;; COMMAND-PROCESS. 1581 (new-connection nil) 1582 1583 ;; ‘ignore-regexps’ is a list of regexps. When a data packet from the 1584 ;; language server matches any of these regexps, it will be ignored. This is 1585 ;; intended for dealing with language servers that output non-protocol data. 1586 (ignore-regexps nil) 1587 1588 ;; ‘ignore-messages’ is a list of regexps. When a message from the language 1589 ;; server matches any of these regexps, it will be ignored. This is useful 1590 ;; for filtering out unwanted messages; such as servers that send nonstandard 1591 ;; message types, or extraneous log messages. 1592 (ignore-messages nil) 1593 1594 ;; ‘notification-handlers’ is a hash table mapping notification method names 1595 ;; (strings) to functions handling the respective notifications. Upon 1596 ;; receiving a notification, ‘lsp-mode’ will call the associated handler 1597 ;; function passing two arguments, the ‘lsp--workspace’ object and the 1598 ;; deserialized notification parameters. 1599 (notification-handlers (make-hash-table :test 'equal)) 1600 1601 ;; ‘request-handlers’ is a hash table mapping request method names 1602 ;; (strings) to functions handling the respective notifications. Upon 1603 ;; receiving a request, ‘lsp-mode’ will call the associated handler function 1604 ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized 1605 ;; request parameters. 1606 (request-handlers (make-hash-table :test 'equal)) 1607 1608 ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request 1609 ;; identifiers for pending asynchronous requests to functions handling the 1610 ;; respective responses. Upon receiving a response from the language server, 1611 ;; ‘lsp-mode’ will call the associated response handler function with a 1612 ;; single argument, the deserialized response parameters. 1613 (response-handlers (make-hash-table :test 'eql)) 1614 1615 ;; ‘prefix-function’ is called for getting the prefix for completion. 1616 ;; The function takes no parameter and returns a cons (start . end) representing 1617 ;; the start and end bounds of the prefix. If it's not set, the client uses a 1618 ;; default prefix function." 1619 (prefix-function nil) 1620 1621 ;; Contains mapping of scheme to the function that is going to be used to load 1622 ;; the file. 1623 (uri-handlers (make-hash-table :test #'equal)) 1624 1625 ;; ‘action-handlers’ is a hash table mapping action to a handler function. It 1626 ;; can be used in `lsp-execute-code-action' to determine whether the action 1627 ;; current client is interested in executing the action instead of sending it 1628 ;; to the server. 1629 (action-handlers (make-hash-table :test 'equal)) 1630 1631 ;; `action-filter' can be set to a function that modifies any incoming 1632 ;; `CodeAction' in place before it is executed. The return value is ignored. 1633 ;; This can be used to patch up broken code action requests before they are 1634 ;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an 1635 ;; example of a function that can be useful here. 1636 (action-filter nil) 1637 1638 ;; major modes supported by the client. 1639 major-modes 1640 ;; Function that will be called to decide if this language client 1641 ;; should manage a particular buffer. The function will be passed 1642 ;; the file name and major mode to inform the decision. Setting 1643 ;; `activation-fn' will override `major-modes', if 1644 ;; present. 1645 activation-fn 1646 ;; Break the tie when major-mode is supported by multiple clients. 1647 (priority 0) 1648 ;; Unique identifier for representing the client object. 1649 server-id 1650 ;; defines whether the client supports multi root workspaces. 1651 multi-root 1652 ;; Initialization options or a function that returns initialization options. 1653 initialization-options 1654 ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or 1655 ;; completely replace, the faces used for semantic highlighting on a 1656 ;; client-by-client basis. 1657 ;; 1658 ;; It recognizes four members, all of which are optional: `:types’ and 1659 ;; `:modifiers’, respectively, should be face definition lists akin to 1660 ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be 1661 ;; merged with the default face definition list. 1662 ;; 1663 ;; Alternatively, if the plist members `:discard-default-types’ or 1664 ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers' 1665 ;; face definitions will be replaced entirely by their respective overrides. 1666 ;; 1667 ;; For example, setting `:semantic-tokens-faces-overrides' to 1668 ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from 1669 ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'. 1670 ;; 1671 ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))' 1672 ;; will also remap "macro", but on top of that associate the fictional token type 1673 ;; "not-quite-a-macro" with the face named `some-face'. 1674 ;; 1675 ;; `(:types (("macro" . font-lock-keyword-face)) 1676 ;; :modifiers (("declaration" . lsp-face-semhl-interface)) 1677 ;; :discard-default-types t 1678 ;; :discard-default-modifiers t)' 1679 ;; will discard all default face definitions, hence leaving the client with 1680 ;; only one token type "macro", mapped to `font-lock-keyword-face', and one 1681 ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'. 1682 semantic-tokens-faces-overrides 1683 ;; Provides support for registering LSP Server specific capabilities. 1684 custom-capabilities 1685 ;; Function which returns the folders that are considered to be not projects but library files. 1686 ;; The function accepts one parameter currently active workspace. 1687 ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225. 1688 library-folders-fn 1689 ;; function which will be called when opening file in the workspace to perform 1690 ;; client specific initialization. The function accepts one parameter 1691 ;; currently active workspace. 1692 before-file-open-fn 1693 ;; Function which will be called right after a workspace has been initialized. 1694 initialized-fn 1695 ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP. 1696 (remote? nil) 1697 1698 ;; ‘completion-in-comments?’ t if the client supports completion in comments. 1699 (completion-in-comments? nil) 1700 1701 ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client. 1702 (path->uri-fn nil) 1703 1704 ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client. 1705 (uri->path-fn nil) 1706 ;; Function that returns an environment structure that will be used 1707 ;; to set some environment variables when starting the language 1708 ;; server process. These environment variables enable some 1709 ;; additional features in the language server. The environment 1710 ;; structure is an alist of the form (KEY . VALUE), where KEY is a 1711 ;; string (regularly in all caps), and VALUE may be a string, a 1712 ;; boolean, or a sequence of strings. 1713 environment-fn 1714 1715 ;; ‘after-open-fn’ workspace after open specific hooks. 1716 (after-open-fn nil) 1717 1718 ;; ‘async-request-handlers’ is a hash table mapping request method names 1719 ;; (strings) to functions handling the respective requests that may take 1720 ;; time to finish. Upon receiving a request, ‘lsp-mode’ will call the 1721 ;; associated handler function passing three arguments, the ‘lsp--workspace’ 1722 ;; object, the deserialized request parameters and the callback which accept 1723 ;; result as its parameter. 1724 (async-request-handlers (make-hash-table :test 'equal)) 1725 download-server-fn 1726 download-in-progress? 1727 buffers 1728 synchronize-sections) 1729 1730 (defun lsp-clients-executable-find (find-command &rest args) 1731 "Finds an executable by invoking a search command. 1732 1733 FIND-COMMAND is the executable finder that searches for the 1734 actual language server executable. ARGS is a list of arguments to 1735 give to FIND-COMMAND to find the language server. Returns the 1736 output of FIND-COMMAND if it exits successfully, nil otherwise. 1737 1738 Typical uses include finding an executable by invoking `find' in 1739 a project, finding LLVM commands on macOS with `xcrun', or 1740 looking up project-specific language servers for projects written 1741 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv' 1742 etc." 1743 (when-let* ((find-command-path (executable-find find-command)) 1744 (executable-path 1745 (with-temp-buffer 1746 (when (zerop (apply 'call-process find-command-path nil t nil args)) 1747 (buffer-substring-no-properties (point-min) (point-max)))))) 1748 (string-trim executable-path))) 1749 1750 (defvar lsp--already-widened nil) 1751 1752 (defmacro lsp-save-restriction-and-excursion (&rest form) 1753 (declare (indent 0) (debug t)) 1754 `(if lsp--already-widened 1755 (save-excursion ,@form) 1756 (-let [lsp--already-widened t] 1757 (save-restriction 1758 (widen) 1759 (save-excursion ,@form))))) 1760 1761 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number 1762 (defun lsp--line-character-to-point (line character) 1763 "Return the point for character CHARACTER on line LINE." 1764 (or (lsp-virtual-buffer-call :line/character->point line character) 1765 (let ((inhibit-field-text-motion t)) 1766 (lsp-save-restriction-and-excursion 1767 (goto-char (point-min)) 1768 (forward-line line) 1769 ;; server may send character position beyond the current line and we 1770 ;; should fallback to line end. 1771 (-let [line-end (line-end-position)] 1772 (if (> character (- line-end (point))) 1773 line-end 1774 (forward-char character) 1775 (point))))))) 1776 1777 (lsp-defun lsp--position-to-point ((&Position :line :character)) 1778 "Convert `Position' object in PARAMS to a point." 1779 (lsp--line-character-to-point line character)) 1780 1781 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end)) 1782 (cons start end)) 1783 1784 (lsp-defun lsp--range-text ((&RangeToPoint :start :end)) 1785 (buffer-substring start end)) 1786 1787 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end))) 1788 (cond 1789 ((and 1790 (region-active-p) 1791 (<= start (region-beginning) end) 1792 (<= start (region-end) end) 1793 (or (not (= start (region-beginning))) 1794 (not (= end (region-end))))) 1795 (cons start end)) 1796 ((and (<= start (point) end) 1797 (not (region-active-p))) 1798 (cons start end)) 1799 (parent? (lsp--find-wrapping-range parent?)))) 1800 1801 (defun lsp--get-selection-range () 1802 (or 1803 (-when-let ((cache . cache-tick) lsp--document-selection-range-cache) 1804 (when (= cache-tick (buffer-modified-tick)) cache)) 1805 (let ((response (cl-first 1806 (lsp-request 1807 "textDocument/selectionRange" 1808 (list :textDocument (lsp--text-document-identifier) 1809 :positions (vector (lsp--cur-position))))))) 1810 (setq lsp--document-selection-range-cache 1811 (cons response (buffer-modified-tick))) 1812 response))) 1813 1814 (defun lsp-extend-selection () 1815 "Extend selection." 1816 (interactive) 1817 (unless (lsp-feature? "textDocument/selectionRange") 1818 (signal 'lsp-capability-not-supported (list "selectionRangeProvider"))) 1819 (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range))) 1820 (goto-char start) 1821 (set-mark (point)) 1822 (goto-char end) 1823 (exchange-point-and-mark))) 1824 1825 (defun lsp-warn (message &rest args) 1826 "Display a warning message made from (`format-message' MESSAGE ARGS...). 1827 This is equivalent to `display-warning', using `lsp-mode' as the type and 1828 `:warning' as the level." 1829 (display-warning 'lsp-mode (apply #'format-message message args))) 1830 1831 (defun lsp--get-uri-handler (scheme) 1832 "Get uri handler for SCHEME in the current workspace." 1833 (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it))) 1834 (or (lsp-workspaces) (lsp--session-workspaces (lsp-session))))) 1835 1836 (defun lsp--fix-path-casing (path) 1837 "On windows, downcases path because the windows file system is 1838 case-insensitive. 1839 1840 On other systems, returns path without change." 1841 (if (eq system-type 'windows-nt) (downcase path) path)) 1842 1843 (defun lsp--uri-to-path (uri) 1844 "Convert URI to a file path." 1845 (if-let ((fn (->> (lsp-workspaces) 1846 (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client)) 1847 (cl-first)))) 1848 (funcall fn uri) 1849 (lsp--uri-to-path-1 uri))) 1850 1851 (defun lsp-remap-path-if-needed (file-name) 1852 (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings)) 1853 (propertize (buffer-local-value 'buffer-file-name buffer) 1854 'lsp-virtual-buffer virtual-buffer) 1855 file-name)) 1856 1857 (defun lsp--uri-to-path-1 (uri) 1858 "Convert URI to a file path." 1859 (let* ((url (url-generic-parse-url (url-unhex-string uri))) 1860 (type (url-type url)) 1861 (target (url-target url)) 1862 (file 1863 (concat (decode-coding-string (url-filename url) 1864 (or locale-coding-system 'utf-8)) 1865 (when (and target 1866 (not (s-match 1867 (rx "#" (group (1+ num)) (or "," "#") 1868 (group (1+ num)) 1869 string-end) 1870 uri))) 1871 (concat "#" target)))) 1872 (file-name (if (and type (not (string= type "file"))) 1873 (if-let ((handler (lsp--get-uri-handler type))) 1874 (funcall handler uri) 1875 uri) 1876 ;; `url-generic-parse-url' is buggy on windows: 1877 ;; https://github.com/emacs-lsp/lsp-mode/pull/265 1878 (or (and (eq system-type 'windows-nt) 1879 (eq (elt file 0) ?\/) 1880 (substring file 1)) 1881 file)))) 1882 (->> file-name 1883 (concat (-some #'lsp--workspace-host-root (lsp-workspaces))) 1884 (lsp-remap-path-if-needed)))) 1885 1886 (defun lsp--buffer-uri () 1887 "Return URI of the current buffer." 1888 (or lsp-buffer-uri 1889 (plist-get lsp--virtual-buffer :buffer-uri) 1890 (lsp--path-to-uri 1891 (or (buffer-file-name) (buffer-file-name (buffer-base-buffer)))))) 1892 1893 (defun lsp-register-client-capabilities (&rest _args) 1894 "Implemented only to make `company-lsp' happy. 1895 DELETE when `lsp-mode.el' is deleted.") 1896 1897 (defconst lsp--url-path-allowed-chars 1898 (url--allowed-chars (append '(?/) url-unreserved-chars)) 1899 "`url-unreserved-chars' with additional delim ?/. 1900 This set of allowed chars is enough for hexifying local file paths.") 1901 1902 (defun lsp--path-to-uri-1 (path) 1903 (concat lsp--uri-file-prefix 1904 (--> path 1905 (expand-file-name it) 1906 (or (file-remote-p it 'localname t) it) 1907 (url-hexify-string it lsp--url-path-allowed-chars)))) 1908 1909 (defun lsp--path-to-uri (path) 1910 "Convert PATH to a uri." 1911 (if-let ((uri-fn (->> (lsp-workspaces) 1912 (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client)) 1913 (cl-first)))) 1914 (funcall uri-fn path) 1915 (lsp--path-to-uri-1 path))) 1916 1917 (defun lsp--string-match-any (regex-list str) 1918 "Return the first regex, if any, within REGEX-LIST matching STR." 1919 (--first (string-match it str) regex-list)) 1920 1921 (cl-defstruct lsp-watch 1922 (descriptors (make-hash-table :test 'equal)) 1923 root-directory) 1924 1925 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories) 1926 (let ((file-name (cl-third event)) 1927 (event-type (cl-second event))) 1928 (cond 1929 ((and (file-directory-p file-name) 1930 (equal 'created event-type) 1931 (not (lsp--string-match-any ignored-directories file-name))) 1932 1933 (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch) 1934 1935 ;; process the files that are already present in 1936 ;; the directory. 1937 (->> (directory-files-recursively file-name ".*" t) 1938 (seq-do (lambda (f) 1939 (unless (file-directory-p f) 1940 (funcall callback (list nil 'created f))))))) 1941 ((and (memq event-type '(created deleted changed)) 1942 (not (file-directory-p file-name)) 1943 (not (lsp--string-match-any ignored-files file-name))) 1944 (funcall callback event)) 1945 ((and (memq event-type '(renamed)) 1946 (not (file-directory-p file-name)) 1947 (not (lsp--string-match-any ignored-files file-name))) 1948 (funcall callback `(,(cl-first event) deleted ,(cl-third event))) 1949 (funcall callback `(,(cl-first event) created ,(cl-fourth event))))))) 1950 1951 (defun lsp--ask-about-watching-big-repo (number-of-directories dir) 1952 "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR. 1953 This is useful when there is a lot of files in a repository, as 1954 that may slow Emacs down. Returns t if the user wants to watch 1955 the entire repository, nil otherwise." 1956 (prog1 1957 (yes-or-no-p 1958 (format 1959 "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down. 1960 Do you want to watch all files in %s? " 1961 dir 1962 number-of-directories 1963 dir)) 1964 (lsp--info 1965 (concat "You can configure this warning with the `lsp-enable-file-watchers' " 1966 "and `lsp-file-watch-threshold' variables")))) 1967 1968 1969 (defun lsp--path-is-watchable-directory (path dir ignored-directories) 1970 "Figure out whether PATH (inside of DIR) is meant to have a file watcher set. 1971 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't 1972 want to watch." 1973 (let 1974 ((full-path (f-join dir path))) 1975 (and (file-accessible-directory-p full-path) 1976 (not (equal path ".")) 1977 (not (equal path "..")) 1978 (not (lsp--string-match-any ignored-directories full-path))))) 1979 1980 1981 (defun lsp--all-watchable-directories (dir ignored-directories) 1982 "Traverse DIR recursively returning a list of paths that should have watchers. 1983 IGNORED-DIRECTORIES will be used for exclusions" 1984 (let* ((dir (if (f-symlink? dir) 1985 (file-truename dir) 1986 dir))) 1987 (apply #'nconc 1988 ;; the directory itself is assumed to be part of the set 1989 (list dir) 1990 ;; collect all subdirectories that are watchable 1991 (-map 1992 (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories)) 1993 ;; but only look at subdirectories that are watchable 1994 (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories)) 1995 (directory-files dir)))))) 1996 1997 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?) 1998 "Create recursive file notification watch in DIR. 1999 CALLBACK will be called when there are changes in any of 2000 the monitored files. WATCHES is a hash table directory->file 2001 notification handle which contains all of the watch that 2002 already have been created. Watches will not be created for 2003 any directory that matches any regex in IGNORED-DIRECTORIES. 2004 Watches will not be created for any file that matches any 2005 regex in IGNORED-FILES." 2006 (let* ((dir (if (f-symlink? dir) 2007 (file-truename dir) 2008 dir)) 2009 (watch (or watch (make-lsp-watch :root-directory dir))) 2010 (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories))) 2011 (lsp-log "Creating watchers for following %s folders:\n %s" 2012 (length dirs-to-watch) 2013 (s-join "\n " dirs-to-watch)) 2014 (when (or 2015 (not warn-big-repo?) 2016 (not lsp-file-watch-threshold) 2017 (let ((number-of-directories (length dirs-to-watch))) 2018 (or 2019 (< number-of-directories lsp-file-watch-threshold) 2020 (condition-case nil 2021 (lsp--ask-about-watching-big-repo number-of-directories dir) 2022 (quit))))) 2023 (dolist (current-dir dirs-to-watch) 2024 (condition-case err 2025 (progn 2026 (puthash 2027 current-dir 2028 (file-notify-add-watch current-dir 2029 '(change) 2030 (lambda (event) 2031 (lsp--folder-watch-callback event callback watch ignored-files ignored-directories))) 2032 (lsp-watch-descriptors watch))) 2033 (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err))) 2034 (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))))) 2035 watch)) 2036 2037 (defun lsp-kill-watch (watch) 2038 "Delete WATCH." 2039 (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch)) 2040 (ht-clear! (lsp-watch-descriptors watch))) 2041 2042 (defun lsp-json-bool (val) 2043 "Convert VAL to JSON boolean." 2044 (if val t :json-false)) 2045 2046 (defmacro with-lsp-workspace (workspace &rest body) 2047 "Helper macro for invoking BODY in WORKSPACE context." 2048 (declare (debug (form body)) 2049 (indent 1)) 2050 `(let ((lsp--cur-workspace ,workspace)) ,@body)) 2051 2052 (defmacro with-lsp-workspaces (workspaces &rest body) 2053 "Helper macro for invoking BODY against multiple WORKSPACES." 2054 (declare (debug (form body)) 2055 (indent 1)) 2056 `(let ((lsp--buffer-workspaces ,workspaces)) ,@body)) 2057 2058 2059 2060 (defmacro lsp-consistency-check (package) 2061 `(defconst ,(intern (concat (symbol-name package) 2062 "-plist-value-when-compiled")) 2063 (eval-when-compile lsp-use-plists))) 2064 2065 2066 ;; loading code-workspace files 2067 2068 ;;;###autoload 2069 (defun lsp-load-vscode-workspace (file) 2070 "Load vscode workspace from FILE" 2071 (interactive "fSelect file to import: ") 2072 (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session))) 2073 2074 (let ((dir (f-dirname file))) 2075 (->> file 2076 (json-read-file) 2077 (alist-get 'folders) 2078 (-map (-lambda ((&alist 'path)) 2079 (lsp-workspace-folders-add (expand-file-name path dir))))))) 2080 2081 ;;;###autoload 2082 (defun lsp-save-vscode-workspace (file) 2083 "Save vscode workspace to FILE" 2084 (interactive "FSelect file to save to: ") 2085 2086 (let ((json-encoding-pretty-print t)) 2087 (f-write-text (json-encode 2088 `((folders . ,(->> (lsp-session) 2089 (lsp-session-folders) 2090 (--map `((path . ,it))))))) 2091 'utf-8 2092 file))) 2093 2094 2095 (defmacro lsp-foreach-workspace (&rest body) 2096 "Execute BODY for each of the current workspaces." 2097 (declare (debug (form body))) 2098 `(--map (with-lsp-workspace it ,@body) (lsp-workspaces))) 2099 2100 (defmacro when-lsp-workspace (workspace &rest body) 2101 "Helper macro for invoking BODY in WORKSPACE context if present." 2102 (declare (debug (form body)) 2103 (indent 1)) 2104 `(when-let ((lsp--cur-workspace ,workspace)) ,@body)) 2105 2106 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items)) 2107 (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read)) 2108 (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label)) 2109 items)) 2110 (result (funcall-interactively 2111 selectfunc 2112 (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels)) 2113 (choices (if (listp result) 2114 (if (equal result '("*")) 2115 itemLabels 2116 result) 2117 (list result)))) 2118 (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data)) 2119 (if (member label choices) 2120 (lsp-make-quick-pick-item :label label :picked t :user-data user-data) 2121 nil)) 2122 items))))) 2123 2124 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?)) 2125 (read-string (format "%s: " prompt) (or value? ""))) 2126 2127 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type)) 2128 "Send the server's messages to log. 2129 PARAMS - the data sent from _WORKSPACE." 2130 (funcall (cl-case type 2131 (1 'lsp--error) 2132 (2 'lsp--warn) 2133 (t 'lsp--info)) 2134 "%s" 2135 message)) 2136 2137 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type)) 2138 "Send the server's messages to log. 2139 PARAMS - the data sent from WORKSPACE." 2140 (ignore 2141 (let ((client (lsp--workspace-client workspace))) 2142 (when (or (not client) 2143 (cl-notany (-rpartial #'string-match-p message) 2144 (lsp--client-ignore-messages client))) 2145 (lsp-log "%s" (lsp--propertize message type)))))) 2146 2147 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?)) 2148 "Display a message request to user sending the user selection back to server." 2149 (let* ((message (lsp--propertize message type)) 2150 (choices (seq-map #'lsp:message-action-item-title actions?))) 2151 (if choices 2152 (completing-read (concat message " ") (seq-into choices 'list) nil t) 2153 (lsp-log message)))) 2154 2155 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?)) 2156 "Show document URI in a buffer and go to SELECTION if any." 2157 (let ((path (lsp--uri-to-path uri))) 2158 (when (f-exists? path) 2159 (with-current-buffer (find-file path) 2160 (when selection? 2161 (goto-char (lsp--position-to-point (lsp:range-start selection?)))) 2162 t)))) 2163 2164 (defcustom lsp-progress-prefix "⌛ " 2165 "Progress prefix." 2166 :group 'lsp-mode 2167 :type 'string 2168 :package-version '(lsp-mode . "8.0.0")) 2169 2170 (defcustom lsp-progress-function #'lsp-on-progress-modeline 2171 "Function for handling the progress notifications." 2172 :group 'lsp-mode 2173 :type '(choice 2174 (const :tag "Use modeline" lsp-on-progress-modeline) 2175 (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')" 2176 lsp-on-progress-legacy) 2177 (const :tag "Ignore" ignore) 2178 (function :tag "Other function")) 2179 :package-version '(lsp-mode . "8.0.0")) 2180 2181 (defcustom lsp-request-while-no-input-may-block nil 2182 "Have `lsp-request-while-no-input` block unless `non-essential` is t." 2183 :group 'lsp-mode 2184 :type 'boolean) 2185 2186 (defun lsp--progress-status () 2187 "Returns the status of the progress for the current workspaces." 2188 (-let ((progress-status 2189 (s-join 2190 "|" 2191 (-keep 2192 (lambda (workspace) 2193 (let ((tokens (lsp--workspace-work-done-tokens workspace))) 2194 (unless (ht-empty? tokens) 2195 (mapconcat 2196 (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?)) 2197 (concat (if percentage? 2198 (if (numberp percentage?) 2199 (format "%.0f%%%% " percentage?) 2200 (format "%s%%%% " percentage?)) 2201 "") 2202 (or message? title))) 2203 (ht-values tokens) 2204 "|")))) 2205 (lsp-workspaces))))) 2206 (unless (s-blank? progress-status) 2207 (concat lsp-progress-prefix progress-status " ")))) 2208 2209 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value 2210 (value &as &WorkDoneProgress :kind))) 2211 "PARAMS contains the progress data. 2212 WORKSPACE is the workspace that contains the progress token." 2213 (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status)))) 2214 (pcase kind 2215 ("begin" (lsp-workspace-set-work-done-token token value workspace)) 2216 ("report" (lsp-workspace-set-work-done-token token value workspace)) 2217 ("end" (lsp-workspace-rem-work-done-token token workspace))) 2218 (force-mode-line-update)) 2219 2220 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value 2221 (value &as &WorkDoneProgress :kind))) 2222 "PARAMS contains the progress data. 2223 WORKSPACE is the workspace that contains the progress token." 2224 (pcase kind 2225 ("begin" 2226 (-let* (((&WorkDoneProgressBegin :title :percentage?) value) 2227 (reporter 2228 (if lsp-progress-via-spinner 2229 (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types)) 2230 ;; Set message as a tooltip for the spinner strings 2231 (propertized-strings 2232 (seq-map (lambda (string) (propertize string 'help-echo title)) 2233 spinner-strings)) 2234 (spinner-type (vconcat propertized-strings))) 2235 ;; The progress relates to the server as a whole, 2236 ;; display it on all buffers. 2237 (mapcar (lambda (buffer) 2238 (lsp-with-current-buffer buffer 2239 (spinner-start spinner-type)) 2240 buffer) 2241 (lsp--workspace-buffers workspace))) 2242 (if percentage? 2243 (make-progress-reporter title 0 100 percentage?) 2244 ;; No percentage, just progress 2245 (make-progress-reporter title nil nil))))) 2246 (lsp-workspace-set-work-done-token token reporter workspace))) 2247 ("report" 2248 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2249 (unless lsp-progress-via-spinner 2250 (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value))))) 2251 2252 ("end" 2253 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2254 (if lsp-progress-via-spinner 2255 (mapc (lambda (buffer) 2256 (when (lsp-buffer-live-p buffer) 2257 (lsp-with-current-buffer buffer 2258 (spinner-stop)))) 2259 reporter) 2260 (progress-reporter-done reporter)) 2261 (lsp-workspace-rem-work-done-token token workspace))))) 2262 2263 2264 ;; diagnostics 2265 2266 (defvar lsp-diagnostic-filter nil 2267 "A a function which will be called with 2268 `&PublishDiagnosticsParams' and `workspace' which can be used 2269 to filter out the diagnostics. The function should return 2270 `&PublishDiagnosticsParams'. 2271 2272 Common usecase are: 2273 1. Filter the diagnostics for a particular language server. 2274 2. Filter out the diagnostics under specific level.") 2275 2276 (defvar lsp-diagnostic-stats (ht)) 2277 2278 (defun lsp-diagnostics (&optional current-workspace?) 2279 "Return the diagnostics from all workspaces." 2280 (or (pcase (if current-workspace? 2281 (lsp-workspaces) 2282 (lsp--session-workspaces (lsp-session))) 2283 (`() ()) 2284 (`(,workspace) (lsp--workspace-diagnostics workspace)) 2285 (`,workspaces (let ((result (make-hash-table :test 'equal))) 2286 (mapc (lambda (workspace) 2287 (->> workspace 2288 (lsp--workspace-diagnostics) 2289 (maphash (lambda (file-name diagnostics) 2290 (puthash file-name 2291 (append (gethash file-name result) diagnostics) 2292 result))))) 2293 workspaces) 2294 result))) 2295 (ht))) 2296 2297 (defun lsp-diagnostics-stats-for (path) 2298 "Get diagnostics statistics for PATH. 2299 The result format is vector [_ errors warnings infos hints] or nil." 2300 (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats)) 2301 2302 (defun lsp-diagnostics--update-path (path new-stats) 2303 (let ((new-stats (copy-sequence new-stats)) 2304 (path (lsp--fix-path-casing (directory-file-name path)))) 2305 (if-let ((old-data (gethash path lsp-diagnostic-stats))) 2306 (dotimes (idx 5) 2307 (cl-callf + (aref old-data idx) 2308 (aref new-stats idx))) 2309 (puthash path new-stats lsp-diagnostic-stats)))) 2310 2311 (lsp-defun lsp--on-diagnostics-update-stats (workspace 2312 (&PublishDiagnosticsParams :uri :diagnostics)) 2313 (let ((path (lsp--fix-path-casing (lsp--uri-to-path uri))) 2314 (new-stats (make-vector 5 0))) 2315 (mapc (-lambda ((&Diagnostic :severity?)) 2316 (cl-incf (aref new-stats (or severity? 1)))) 2317 diagnostics) 2318 (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace)))) 2319 (mapc (-lambda ((&Diagnostic :severity?)) 2320 (cl-decf (aref new-stats (or severity? 1)))) 2321 old-diags)) 2322 (lsp-diagnostics--update-path path new-stats) 2323 (while (not (string= path (setf path (file-name-directory 2324 (directory-file-name path))))) 2325 (lsp-diagnostics--update-path path new-stats)))) 2326 2327 (defun lsp--on-diagnostics (workspace params) 2328 "Callback for textDocument/publishDiagnostics. 2329 interface PublishDiagnosticsParams { 2330 uri: string; 2331 diagnostics: Diagnostic[]; 2332 } 2333 PARAMS contains the diagnostics data. 2334 WORKSPACE is the workspace that contains the diagnostics." 2335 (when lsp-diagnostic-filter 2336 (setf params (funcall lsp-diagnostic-filter params workspace))) 2337 2338 (lsp--on-diagnostics-update-stats workspace params) 2339 2340 (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params) 2341 (lsp--virtual-buffer-mappings (ht)) 2342 (file (lsp--fix-path-casing (lsp--uri-to-path uri))) 2343 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2344 2345 (if (seq-empty-p diagnostics) 2346 (remhash file workspace-diagnostics) 2347 (puthash file (append diagnostics nil) workspace-diagnostics)) 2348 2349 (run-hooks 'lsp-diagnostics-updated-hook))) 2350 2351 (defun lsp-diagnostics--workspace-cleanup (workspace) 2352 (->> workspace 2353 (lsp--workspace-diagnostics) 2354 (maphash (lambda (key _) 2355 (lsp--on-diagnostics-update-stats 2356 workspace 2357 (lsp-make-publish-diagnostics-params 2358 :uri (lsp--path-to-uri key) 2359 :diagnostics []))))) 2360 (clrhash (lsp--workspace-diagnostics workspace))) 2361 2362 2363 2364 ;; textDocument/foldingRange support 2365 2366 (cl-defstruct lsp--folding-range beg end kind children) 2367 2368 (defvar-local lsp--cached-folding-ranges nil) 2369 (defvar-local lsp--cached-nested-folding-ranges nil) 2370 2371 (defun lsp--folding-range-width (range) 2372 (- (lsp--folding-range-end range) 2373 (lsp--folding-range-beg range))) 2374 2375 (defun lsp--get-folding-ranges () 2376 "Get the folding ranges for the current buffer." 2377 (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges)) 2378 (let* ((ranges (lsp-request "textDocument/foldingRange" 2379 `(:textDocument ,(lsp--text-document-identifier)))) 2380 (sorted-line-col-pairs (->> ranges 2381 (cl-mapcan (-lambda ((&FoldingRange :start-line 2382 :start-character? 2383 :end-line 2384 :end-character?)) 2385 (list (cons start-line start-character?) 2386 (cons end-line end-character?)))) 2387 (-sort #'lsp--line-col-comparator))) 2388 (line-col-to-point-map (lsp--convert-line-col-to-points-batch 2389 sorted-line-col-pairs))) 2390 (setq lsp--cached-folding-ranges 2391 (cons (buffer-chars-modified-tick) 2392 (--> ranges 2393 (seq-map (-lambda ((range &as 2394 &FoldingRange :start-line 2395 :start-character? 2396 :end-line 2397 :end-character? 2398 :kind?)) 2399 (make-lsp--folding-range 2400 :beg (ht-get line-col-to-point-map 2401 (cons start-line start-character?)) 2402 :end (ht-get line-col-to-point-map 2403 (cons end-line end-character?)) 2404 :kind kind?)) 2405 it) 2406 (seq-filter (lambda (folding-range) 2407 (< (lsp--folding-range-beg folding-range) 2408 (lsp--folding-range-end folding-range))) 2409 it) 2410 (seq-into it 'list) 2411 (delete-dups it)))))) 2412 (cdr lsp--cached-folding-ranges)) 2413 2414 (defun lsp--get-nested-folding-ranges () 2415 "Get a list of nested folding ranges for the current buffer." 2416 (-let [(tick . _) lsp--cached-folding-ranges] 2417 (if (and (eq tick (buffer-chars-modified-tick)) 2418 lsp--cached-nested-folding-ranges) 2419 lsp--cached-nested-folding-ranges 2420 (setq lsp--cached-nested-folding-ranges 2421 (lsp--folding-range-build-trees (lsp--get-folding-ranges)))))) 2422 2423 (defun lsp--folding-range-build-trees (ranges) 2424 (setq ranges (seq-sort #'lsp--range-before-p ranges)) 2425 (let* ((dummy-node (make-lsp--folding-range 2426 :beg most-negative-fixnum 2427 :end most-positive-fixnum)) 2428 (stack (list dummy-node))) 2429 (dolist (range ranges) 2430 (while (not (lsp--range-inside-p range (car stack))) 2431 (pop stack)) 2432 (push range (lsp--folding-range-children (car stack))) 2433 (push range stack)) 2434 (lsp--folding-range-children dummy-node))) 2435 2436 (defun lsp--range-inside-p (r1 r2) 2437 "Return non-nil if folding range R1 lies inside R2" 2438 (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2)) 2439 (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2)))) 2440 2441 (defun lsp--range-before-p (r1 r2) 2442 "Return non-nil if folding range R1 ends before R2" 2443 ;; Ensure r1 comes before r2 2444 (or (< (lsp--folding-range-beg r1) 2445 (lsp--folding-range-beg r2)) 2446 ;; If beg(r1) == beg(r2) make sure r2 ends first 2447 (and (= (lsp--folding-range-beg r1) 2448 (lsp--folding-range-beg r2)) 2449 (< (lsp--folding-range-end r2) 2450 (lsp--folding-range-end r1))))) 2451 2452 (defun lsp--point-inside-range-p (point range) 2453 "Return non-nil if POINT lies inside folding range RANGE." 2454 (and (>= point (lsp--folding-range-beg range)) 2455 (<= point (lsp--folding-range-end range)))) 2456 2457 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point))) 2458 "Return the innermost folding range POINT lies in." 2459 (seq-reduce (lambda (innermost-range curr-range) 2460 (if (and (lsp--point-inside-range-p point curr-range) 2461 (or (null innermost-range) 2462 (lsp--range-inside-p curr-range innermost-range))) 2463 curr-range 2464 innermost-range)) 2465 (lsp--get-folding-ranges) 2466 nil)) 2467 2468 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point))) 2469 "Return the outermost folding range POINT lies in." 2470 (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range) 2471 (let ((curr-width (lsp--folding-range-width curr-range))) 2472 (if (and (lsp--point-inside-range-p point curr-range) 2473 (or (null best-pair) 2474 (> curr-width outermost-width))) 2475 (cons curr-width curr-range) 2476 best-pair))) 2477 (lsp--get-folding-ranges) 2478 nil))) 2479 2480 (defun lsp--folding-range-at-point-bounds () 2481 (when (and lsp-enable-folding 2482 (lsp-feature? "textDocument/foldingRange")) 2483 (if-let ((range (lsp--get-current-innermost-folding-range))) 2484 (cons (lsp--folding-range-beg range) 2485 (lsp--folding-range-end range))))) 2486 (put 'lsp--folding-range 'bounds-of-thing-at-point 2487 #'lsp--folding-range-at-point-bounds) 2488 2489 (defun lsp--get-nearest-folding-range (&optional backward) 2490 (let ((point (point)) 2491 (found nil)) 2492 (while (not 2493 (or found 2494 (if backward 2495 (<= point (point-min)) 2496 (>= point (point-max))))) 2497 (if backward (cl-decf point) (cl-incf point)) 2498 (setq found (lsp--get-current-innermost-folding-range point))) 2499 found)) 2500 2501 (defun lsp--folding-range-at-point-forward-op (n) 2502 (when (and lsp-enable-folding 2503 (not (zerop n)) 2504 (lsp-feature? "textDocument/foldingRange")) 2505 (cl-block break 2506 (dotimes (_ (abs n)) 2507 (if-let ((range (lsp--get-nearest-folding-range (< n 0)))) 2508 (goto-char (if (< n 0) 2509 (lsp--folding-range-beg range) 2510 (lsp--folding-range-end range))) 2511 (cl-return-from break)))))) 2512 (put 'lsp--folding-range 'forward-op 2513 #'lsp--folding-range-at-point-forward-op) 2514 2515 (defun lsp--folding-range-at-point-beginning-op () 2516 (goto-char (car (lsp--folding-range-at-point-bounds)))) 2517 (put 'lsp--folding-range 'beginning-op 2518 #'lsp--folding-range-at-point-beginning-op) 2519 2520 (defun lsp--folding-range-at-point-end-op () 2521 (goto-char (cdr (lsp--folding-range-at-point-bounds)))) 2522 (put 'lsp--folding-range 'end-op 2523 #'lsp--folding-range-at-point-end-op) 2524 2525 (defun lsp--range-at-point-bounds () 2526 (or (lsp--folding-range-at-point-bounds) 2527 (when-let ((range (and 2528 (lsp-feature? "textDocument/hover") 2529 (->> (lsp--text-document-position-params) 2530 (lsp-request "textDocument/hover") 2531 (lsp:hover-range?))))) 2532 (lsp--range-to-region range)))) 2533 2534 ;; A more general purpose "thing", useful for applications like focus.el 2535 (put 'lsp--range 'bounds-of-thing-at-point 2536 #'lsp--range-at-point-bounds) 2537 2538 (defun lsp--log-io-p (method) 2539 "Return non nil if should log for METHOD." 2540 (and lsp-log-io 2541 (or (not lsp-log-io-allowlist-methods) 2542 (member method lsp-log-io-allowlist-methods)))) 2543 2544 2545 ;; toggles 2546 2547 (defun lsp-toggle-trace-io () 2548 "Toggle client-server protocol logging." 2549 (interactive) 2550 (setq lsp-log-io (not lsp-log-io)) 2551 (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled"))) 2552 2553 (defun lsp-toggle-signature-auto-activate () 2554 "Toggle signature auto activate." 2555 (interactive) 2556 (setq lsp-signature-auto-activate 2557 (unless lsp-signature-auto-activate '(:on-trigger-char))) 2558 (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled")) 2559 (lsp--update-signature-help-hook)) 2560 2561 (defun lsp-toggle-on-type-formatting () 2562 "Toggle on type formatting." 2563 (interactive) 2564 (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting)) 2565 (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled")) 2566 (lsp--update-on-type-formatting-hook)) 2567 2568 (defun lsp-toggle-symbol-highlight () 2569 "Toggle symbol highlighting." 2570 (interactive) 2571 (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting)) 2572 2573 (cond 2574 ((and lsp-enable-symbol-highlighting 2575 (lsp-feature? "textDocument/documentHighlight")) 2576 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t) 2577 (lsp--info "Symbol highlighting enabled in current buffer.")) 2578 ((not lsp-enable-symbol-highlighting) 2579 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 2580 (lsp--remove-overlays 'lsp-highlight) 2581 (lsp--info "Symbol highlighting disabled in current buffer.")))) 2582 2583 2584 ;; keybindings 2585 (defvar lsp--binding-descriptions nil 2586 "List of key binding/short description pair.") 2587 2588 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings) 2589 "In KEYMAP, define key sequence KEY as DEF conditionally. 2590 This is like `define-key', except the definition disappears 2591 whenever COND evaluates to nil. 2592 DESC is the short-description for the binding. 2593 BINDINGS is a list of (key def desc cond)." 2594 (declare (indent defun) 2595 (debug (form form form form form &rest sexp))) 2596 (->> (cl-list* key def desc cond bindings) 2597 (-partition 4) 2598 (-mapcat (-lambda ((key def desc cond)) 2599 `((define-key ,keymap ,key 2600 '(menu-item 2601 ,(format "maybe-%s" def) 2602 ,def 2603 :filter 2604 (lambda (item) 2605 (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer) 2606 lsp--describe-buffer) 2607 (current-buffer)) 2608 ,cond) 2609 item)))) 2610 (when (stringp ,key) 2611 (setq lsp--binding-descriptions 2612 (append lsp--binding-descriptions '(,key ,desc))))))) 2613 macroexp-progn)) 2614 2615 (defvar lsp--describe-buffer nil) 2616 2617 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus) 2618 (let ((lsp--describe-buffer buffer)) 2619 (funcall fn buffer prefix menus))) 2620 2621 (advice-add 'describe-buffer-bindings 2622 :around 2623 #'lsp-describe-buffer-bindings-advice) 2624 2625 (defun lsp--prepend-prefix (mappings) 2626 (->> mappings 2627 (-partition 2) 2628 (-mapcat (-lambda ((key description)) 2629 (list (concat lsp-keymap-prefix " " key) 2630 description))))) 2631 2632 (defvar lsp-command-map 2633 (-doto (make-sparse-keymap) 2634 (lsp-define-conditional-key 2635 ;; workspaces 2636 "wD" lsp-disconnect "disconnect" (lsp-workspaces) 2637 "wd" lsp-describe-session "describe session" t 2638 "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces) 2639 "wr" lsp-workspace-restart "restart server" (lsp-workspaces) 2640 "ws" lsp "start server" t 2641 2642 ;; formatting 2643 "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting") 2644 (lsp-feature? "textDocument/formatting")) 2645 "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting") 2646 2647 ;; folders 2648 "Fa" lsp-workspace-folders-add "add folder" t 2649 "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t 2650 "Fr" lsp-workspace-folders-remove "remove folder" t 2651 2652 ;; toggles 2653 "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature? 2654 "textDocument/publishDiagnostics") 2655 "TL" lsp-toggle-trace-io "toggle log io" t 2656 "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline) 2657 "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs) 2658 "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature? 2659 "textDocument/codeAction") 2660 "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature? 2661 "textDocument/documentSymbol") 2662 "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc) 2663 "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature? 2664 "textDocument/onTypeFormatting") 2665 "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight") 2666 "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens") 2667 "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp") 2668 2669 ;; goto 2670 "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol") 2671 "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration") 2672 "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list) 2673 "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition") 2674 "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls") 2675 (fboundp 'lsp-treemacs-call-hierarchy)) 2676 "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation") 2677 "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references") 2678 "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition") 2679 2680 ;; help 2681 "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc) 2682 (lsp-feature? "textDocument/hover")) 2683 "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover") 2684 "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp") 2685 2686 ;; refactoring 2687 "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction") 2688 "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename") 2689 2690 ;; actions 2691 "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction") 2692 "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight") 2693 "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy)) 2694 2695 ;; peeks 2696 "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition") 2697 (fboundp 'lsp-ui-peek-find-definitions)) 2698 "Gi" lsp-ui-peek-find-implementation "peek implementations" (and 2699 (fboundp 'lsp-ui-peek-find-implementation) 2700 (lsp-feature? "textDocument/implementation")) 2701 "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references) 2702 (lsp-feature? "textDocument/references")) 2703 "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp 2704 'lsp-ui-peek-find-workspace-symbol) 2705 (lsp-feature? "workspace/symbol"))))) 2706 2707 2708 ;; which-key integration 2709 2710 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key") 2711 (declare-function which-key-add-key-based-replacements "ext:which-key") 2712 2713 (defun lsp-enable-which-key-integration (&optional all-modes) 2714 "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current 2715 active `major-mode', or for all major modes when ALL-MODES is t." 2716 (cl-flet ((which-key-fn (if all-modes 2717 'which-key-add-key-based-replacements 2718 (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode)))) 2719 (apply 2720 #'which-key-fn 2721 (lsp--prepend-prefix 2722 (cl-list* 2723 "" "lsp" 2724 "w" "workspaces" 2725 "F" "folders" 2726 "=" "formatting" 2727 "T" "toggle" 2728 "g" "goto" 2729 "h" "help" 2730 "r" "refactor" 2731 "a" "code actions" 2732 "G" "peek" 2733 lsp--binding-descriptions))))) 2734 2735 2736 ;; Globbing syntax 2737 2738 ;; We port VSCode's glob-to-regexp code 2739 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts) 2740 ;; since the LSP globbing syntax seems to be the same as that of 2741 ;; VSCode. 2742 2743 (defconst lsp-globstar "**" 2744 "Globstar pattern.") 2745 2746 (defconst lsp-glob-split ?/ 2747 "The character by which we split path components in a glob 2748 pattern.") 2749 2750 (defconst lsp-path-regexp "[/\\\\]" 2751 "Forward or backslash to be used as a path separator in 2752 computed regexps.") 2753 2754 (defconst lsp-non-path-regexp "[^/\\\\]" 2755 "A regexp matching anything other than a slash.") 2756 2757 (defconst lsp-globstar-regexp 2758 (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?" 2759 lsp-path-regexp 2760 lsp-non-path-regexp lsp-path-regexp 2761 lsp-path-regexp lsp-non-path-regexp) 2762 "Globstar in regexp form.") 2763 2764 (defun lsp-split-glob-pattern (pattern split-char) 2765 "Split PATTERN at SPLIT-CHAR while respecting braces and brackets." 2766 (when pattern 2767 (let ((segments nil) 2768 (in-braces nil) 2769 (in-brackets nil) 2770 (current-segment "")) 2771 (dolist (char (string-to-list pattern)) 2772 (cl-block 'exit-point 2773 (if (eq char split-char) 2774 (when (and (null in-braces) 2775 (null in-brackets)) 2776 (push current-segment segments) 2777 (setq current-segment "") 2778 (cl-return-from 'exit-point)) 2779 (pcase char 2780 (?{ 2781 (setq in-braces t)) 2782 (?} 2783 (setq in-braces nil)) 2784 (?\[ 2785 (setq in-brackets t)) 2786 (?\] 2787 (setq in-brackets nil)))) 2788 (setq current-segment (concat current-segment 2789 (char-to-string char))))) 2790 (unless (string-empty-p current-segment) 2791 (push current-segment segments)) 2792 (nreverse segments)))) 2793 2794 (defun lsp--glob-to-regexp (pattern) 2795 "Helper function to convert a PATTERN from LSP's glob syntax to 2796 an Elisp regexp." 2797 (if (string-empty-p pattern) 2798 "" 2799 (let ((current-regexp "") 2800 (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split))) 2801 (if (-all? (lambda (segment) (eq segment lsp-globstar)) 2802 glob-segments) 2803 ".*" 2804 (let ((prev-segment-was-globstar nil)) 2805 (seq-do-indexed 2806 (lambda (segment index) 2807 (if (string-equal segment lsp-globstar) 2808 (unless prev-segment-was-globstar 2809 (setq current-regexp (concat current-regexp 2810 lsp-globstar-regexp)) 2811 (setq prev-segment-was-globstar t)) 2812 (let ((in-braces nil) 2813 (brace-val "") 2814 (in-brackets nil) 2815 (bracket-val "")) 2816 (dolist (char (string-to-list segment)) 2817 (cond 2818 ((and (not (char-equal char ?\})) 2819 in-braces) 2820 (setq brace-val (concat brace-val 2821 (char-to-string char)))) 2822 ((and in-brackets 2823 (or (not (char-equal char ?\])) 2824 (string-empty-p bracket-val))) 2825 (let ((curr (cond 2826 ((char-equal char ?-) 2827 "-") 2828 ;; NOTE: ?\^ and ?^ are different characters 2829 ((and (memq char '(?^ ?!)) 2830 (string-empty-p bracket-val)) 2831 "^") 2832 ((char-equal char lsp-glob-split) 2833 "") 2834 (t 2835 (regexp-quote (char-to-string char)))))) 2836 (setq bracket-val (concat bracket-val curr)))) 2837 (t 2838 (cl-case char 2839 (?{ 2840 (setq in-braces t)) 2841 (?\[ 2842 (setq in-brackets t)) 2843 (?} 2844 (let* ((choices (lsp-split-glob-pattern brace-val ?\,)) 2845 (brace-regexp (concat "\\(?:" 2846 (mapconcat #'lsp--glob-to-regexp choices "\\|") 2847 "\\)"))) 2848 (setq current-regexp (concat current-regexp 2849 brace-regexp)) 2850 (setq in-braces nil) 2851 (setq brace-val ""))) 2852 (?\] 2853 (setq current-regexp 2854 (concat current-regexp 2855 "[" bracket-val "]")) 2856 (setq in-brackets nil) 2857 (setq bracket-val "")) 2858 (?? 2859 (setq current-regexp 2860 (concat current-regexp 2861 lsp-non-path-regexp))) 2862 (?* 2863 (setq current-regexp 2864 (concat current-regexp 2865 lsp-non-path-regexp "*?"))) 2866 (t 2867 (setq current-regexp 2868 (concat current-regexp 2869 (regexp-quote (char-to-string char))))))))) 2870 (when (and (< index (1- (length glob-segments))) 2871 (or (not (string-equal (nth (1+ index) glob-segments) 2872 lsp-globstar)) 2873 (< (+ index 2) 2874 (length glob-segments)))) 2875 (setq current-regexp 2876 (concat current-regexp 2877 lsp-path-regexp))) 2878 (setq prev-segment-was-globstar nil)))) 2879 glob-segments) 2880 current-regexp))))) 2881 2882 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365 2883 (defun lsp-glob-unbrace-at-top-level (glob-pattern) 2884 "If GLOB-PATTERN does not start with a brace, return a singleton list 2885 containing GLOB-PATTERN. 2886 2887 If GLOB-PATTERN does start with a brace, return a list of the 2888 comma-separated globs within the top-level braces." 2889 (if (not (string-prefix-p "{" glob-pattern)) 2890 (list glob-pattern) 2891 (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,))) 2892 2893 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern) 2894 "Convert GLOB-PATTERN to a regexp wrapped with the beginning- 2895 and end-of-string meta-characters." 2896 (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'")) 2897 2898 (defun lsp-glob-to-regexps (glob-pattern) 2899 "Convert a GLOB-PATTERN to a list of Elisp regexps." 2900 (when-let* 2901 ((glob-pattern (cond ((hash-table-p glob-pattern) 2902 (ht-get glob-pattern "pattern")) 2903 ((stringp glob-pattern) glob-pattern) 2904 (t (error "Unknown glob-pattern type: %s" glob-pattern)))) 2905 (trimmed-pattern (string-trim glob-pattern)) 2906 (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern))) 2907 (seq-map #'lsp-glob-convert-to-wrapped-regexp 2908 top-level-unbraced-patterns))) 2909 2910 2911 2912 (defvar lsp-mode-menu) 2913 2914 (defun lsp-mouse-click (event) 2915 (interactive "e") 2916 (let* ((ec (event-start event)) 2917 (choice (x-popup-menu event lsp-mode-menu)) 2918 (action (lookup-key lsp-mode-menu (apply 'vector choice)))) 2919 2920 (select-window (posn-window ec)) 2921 2922 (unless (and (region-active-p) (eq action 'lsp-execute-code-action)) 2923 (goto-char (posn-point ec))) 2924 (run-with-idle-timer 2925 0.001 nil 2926 (lambda () 2927 (cl-labels ((check (value) (not (null value)))) 2928 (when choice 2929 (call-interactively action))))))) 2930 2931 (defvar lsp-mode-map 2932 (let ((map (make-sparse-keymap))) 2933 (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse) 2934 (define-key map (kbd "C-<mouse-1>") #'ignore) 2935 (define-key map (kbd "<mouse-3>") #'lsp-mouse-click) 2936 (define-key map (kbd "C-S-SPC") #'lsp-signature-activate) 2937 (when lsp-keymap-prefix 2938 (define-key map (kbd lsp-keymap-prefix) lsp-command-map)) 2939 map) 2940 "Keymap for `lsp-mode'.") 2941 2942 (define-minor-mode lsp-mode "Mode for LSP interaction." 2943 :keymap lsp-mode-map 2944 :lighter 2945 (" LSP[" 2946 (lsp--buffer-workspaces 2947 (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "][")) 2948 (:propertize "Disconnected" face warning)) 2949 "]") 2950 :group 'lsp-mode 2951 (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred)) 2952 ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp' 2953 (lsp))) 2954 2955 (defvar lsp-mode-menu 2956 (easy-menu-create-menu 2957 nil 2958 `(["Go to definition" lsp-find-definition 2959 :active (lsp-feature? "textDocument/definition")] 2960 ["Find references" lsp-find-references 2961 :active (lsp-feature? "textDocument/references")] 2962 ["Find implementations" lsp-find-implementation 2963 :active (lsp-feature? "textDocument/implementation")] 2964 ["Find declarations" lsp-find-declaration 2965 :active (lsp-feature? "textDocument/declaration")] 2966 ["Go to type declaration" lsp-find-type-definition 2967 :active (lsp-feature? "textDocument/typeDefinition")] 2968 "--" 2969 ["Describe" lsp-describe-thing-at-point] 2970 ["Code action" lsp-execute-code-action] 2971 ["Format" lsp-format-buffer] 2972 ["Highlight references" lsp-document-highlight] 2973 ["Type Hierarchy" lsp-java-type-hierarchy 2974 :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")] 2975 ["Type Hierarchy" lsp-treemacs-type-hierarchy 2976 :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")) 2977 (functionp 'lsp-treemacs-type-hierarchy) 2978 (lsp-feature? "textDocument/typeHierarchy"))] 2979 ["Call Hierarchy" lsp-treemacs-call-hierarchy 2980 :visible (and (functionp 'lsp-treemacs-call-hierarchy) 2981 (lsp-feature? "textDocument/callHierarchy"))] 2982 ["Rename" lsp-rename 2983 :active (lsp-feature? "textDocument/rename")] 2984 "--" 2985 ("Session" 2986 ["View logs" lsp-workspace-show-log] 2987 ["Describe" lsp-describe-session] 2988 ["Shutdown" lsp-shutdown-workspace] 2989 ["Restart" lsp-restart-workspace]) 2990 ("Workspace Folders" 2991 ["Add" lsp-workspace-folders-add] 2992 ["Remove" lsp-workspace-folders-remove] 2993 ["Open" lsp-workspace-folders-open]) 2994 ("Toggle features" 2995 ["Lenses" lsp-lens-mode] 2996 ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode] 2997 ["Modeline code actions" lsp-modeline-code-actions-mode] 2998 ["Modeline diagnostics" lsp-modeline-diagnostics-mode]) 2999 "---" 3000 ("Debug" 3001 :active (bound-and-true-p dap-ui-mode) 3002 :filter ,(lambda (_) 3003 (and (boundp 'dap-ui-menu-items) 3004 (nthcdr 3 dap-ui-menu-items)))))) 3005 "Menu for lsp-mode.") 3006 3007 (defalias 'make-lsp-client 'make-lsp--client) 3008 3009 (cl-defstruct lsp--registered-capability 3010 (id "") 3011 (method " ") 3012 (options nil)) 3013 3014 ;; A ‘lsp--workspace’ object represents exactly one language server process. 3015 (cl-defstruct lsp--workspace 3016 ;; the `ewoc' object for displaying I/O to and from the server 3017 (ewoc nil) 3018 3019 ;; ‘server-capabilities’ is a hash table of the language server capabilities. 3020 ;; It is the hash table representation of a LSP ServerCapabilities structure; 3021 ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize. 3022 (server-capabilities nil) 3023 3024 ;; ‘registered-server-capabilities’ is a list of hash tables that represent 3025 ;; dynamically-registered Registration objects. See 3026 ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability. 3027 (registered-server-capabilities nil) 3028 3029 ;; ‘root’ is a directory name or a directory file name for the workspace 3030 ;; root. ‘lsp-mode’ passes this directory to the ‘initialize’ method of the 3031 ;; language server; see 3032 ;; https://microsoft.github.io/language-server-protocol/specification#initialize. 3033 (root nil) 3034 3035 ;; ‘client’ is the ‘lsp--client’ object associated with this workspace. 3036 (client nil) 3037 3038 ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It 3039 ;; used to derive the file path in `lsp--uri-to-path' when using tramp 3040 ;; connection. 3041 (host-root nil) 3042 3043 ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or 3044 ;; a network connection. ‘lsp-mode’ communicates with ‘proc’ using the 3045 ;; language server protocol. ‘proc’ corresponds to the COMMUNICATION-PROCESS 3046 ;; element of the return value of the client’s ‘get-root’ field, which see. 3047 (proc nil) 3048 3049 ;; ‘proc’ is a process object; it must represent a regular process, not a 3050 ;; pipe or network process. It represents the actual server process that 3051 ;; corresponds to this workspace. ‘cmd-proc’ corresponds to the 3052 ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’ 3053 ;; field, which see. 3054 (cmd-proc nil) 3055 3056 ;; ‘buffers’ is a list of buffers associated with this workspace. 3057 (buffers nil) 3058 3059 ;; if semantic tokens is enabled, `semantic-tokens-faces' contains 3060 ;; one face (or nil) for each token type supported by the language server. 3061 (semantic-tokens-faces nil) 3062 3063 ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces' 3064 ;; contains one face (or nil) for each modifier type supported by the language 3065 ;; server 3066 (semantic-tokens-modifier-faces nil) 3067 3068 ;; Extra client capabilities provided by third-party packages using 3069 ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME 3070 ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name, 3071 ;; and CAPS is either a plist of the client capabilities, or a function that 3072 ;; takes no argument and returns a plist of the client capabilities or nil. 3073 (extra-client-capabilities nil) 3074 3075 ;; Workspace status 3076 (status nil) 3077 3078 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3079 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3080 (metadata (make-hash-table :test 'equal)) 3081 3082 ;; contains all the file notification watches that have been created for the 3083 ;; current workspace in format filePath->file notification handle. 3084 (watches (make-hash-table :test 'equal)) 3085 3086 ;; list of workspace folders 3087 (workspace-folders nil) 3088 3089 ;; ‘last-id’ the last request id for the current workspace. 3090 (last-id 0) 3091 3092 ;; ‘status-string’ allows extensions to specify custom status string based on 3093 ;; the Language Server specific messages. 3094 (status-string nil) 3095 3096 ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it 3097 ;; was stopped). 3098 shutdown-action 3099 3100 ;; ‘diagnostics’ a hashmap with workspace diagnostics. 3101 (diagnostics (make-hash-table :test 'equal)) 3102 3103 ;; contains all the workDone progress tokens that have been created 3104 ;; for the current workspace. 3105 (work-done-tokens (make-hash-table :test 'equal))) 3106 3107 3108 (cl-defstruct lsp-session 3109 ;; contains the folders that are part of the current session 3110 folders 3111 ;; contains the folders that must not be imported in the current workspace. 3112 folders-blocklist 3113 ;; contains the list of folders that must be imported in a project in case of 3114 ;; multi root LSP server. 3115 (server-id->folders (make-hash-table :test 'equal)) 3116 ;; folder to list of the servers that are associated with the folder. 3117 (folder->servers (make-hash-table :test 'equal)) 3118 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3119 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3120 (metadata (make-hash-table :test 'equal))) 3121 3122 (defun lsp-workspace-status (status-string &optional workspace) 3123 "Set current workspace status to STATUS-STRING. 3124 If WORKSPACE is not specified defaults to lsp--cur-workspace." 3125 (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string)))) 3126 (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string))) 3127 3128 (defun lsp-session-set-metadata (key value &optional _workspace) 3129 "Associate KEY with VALUE in the WORKSPACE metadata. 3130 If WORKSPACE is not provided current workspace will be used." 3131 (puthash key value (lsp-session-metadata (lsp-session)))) 3132 3133 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata) 3134 3135 (defun lsp-session-get-metadata (key &optional _workspace) 3136 "Lookup KEY in WORKSPACE metadata. 3137 If WORKSPACE is not provided current workspace will be used." 3138 (gethash key (lsp-session-metadata (lsp-session)))) 3139 3140 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata) 3141 3142 (defun lsp-workspace-set-work-done-token (token value workspace) 3143 "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens." 3144 (puthash token value (lsp--workspace-work-done-tokens workspace))) 3145 3146 (defun lsp-workspace-get-work-done-token (token workspace) 3147 "Lookup TOKEN in the WORKSPACE work-done-tokens." 3148 (gethash token (lsp--workspace-work-done-tokens workspace))) 3149 3150 (defun lsp-workspace-rem-work-done-token (token workspace) 3151 "Remove TOKEN from the WORKSPACE work-done-tokens." 3152 (remhash token (lsp--workspace-work-done-tokens workspace))) 3153 3154 3155 (defun lsp--make-notification (method &optional params) 3156 "Create notification body for method METHOD and parameters PARAMS." 3157 (list :jsonrpc "2.0" :method method :params params)) 3158 3159 (defalias 'lsp--make-request 'lsp--make-notification) 3160 (defalias 'lsp-make-request 'lsp--make-notification) 3161 3162 (defun lsp--make-response (id result) 3163 "Create response for REQUEST with RESULT." 3164 `(:jsonrpc "2.0" :id ,id :result ,result)) 3165 3166 (defun lsp-make-notification (method &optional params) 3167 "Create notification body for method METHOD and parameters PARAMS." 3168 (lsp--make-notification method params)) 3169 3170 (defmacro lsp--json-serialize (params) 3171 (if (progn 3172 (require 'json) 3173 (fboundp 'json-serialize)) 3174 `(json-serialize ,params 3175 :null-object nil 3176 :false-object :json-false) 3177 `(let ((json-false :json-false)) 3178 (json-encode ,params)))) 3179 3180 (defun lsp--make-message (params) 3181 "Create a LSP message from PARAMS, after encoding it to a JSON string." 3182 (let ((body (lsp--json-serialize params))) 3183 (concat "Content-Length: " 3184 (number-to-string (1+ (string-bytes body))) 3185 "\r\n\r\n" 3186 body 3187 "\n"))) 3188 3189 (cl-defstruct lsp--log-entry timestamp process-time type method id body) 3190 3191 (defun lsp--make-log-entry (method id body type &optional process-time) 3192 "Create an outgoing log object from BODY with method METHOD and id ID. 3193 If ID is non-nil, then the body is assumed to be a notification. 3194 TYPE can either be `incoming' or `outgoing'" 3195 (cl-assert (memq type '(incoming-req outgoing-req incoming-notif 3196 outgoing-notif incoming-resp 3197 outgoing-resp))) 3198 (make-lsp--log-entry 3199 :timestamp (format-time-string "%I:%M:%S %p") 3200 :process-time process-time 3201 :method method 3202 :id id 3203 :type type 3204 :body body)) 3205 3206 (defun lsp--log-font-lock-json (body) 3207 "Font lock JSON BODY." 3208 (with-temp-buffer 3209 (insert body) 3210 ;; We set the temp buffer file-name extension to .json and call `set-auto-mode' 3211 ;; so the users configured json mode is used which could be 3212 ;; `json-mode', `json-ts-mode', `jsonian-mode', etc. 3213 (let ((buffer-file-name "lsp-log.json")) 3214 (delay-mode-hooks 3215 (set-auto-mode) 3216 (if (fboundp 'font-lock-ensure) 3217 (font-lock-ensure) 3218 (with-no-warnings 3219 (font-lock-fontify-buffer))))) 3220 (buffer-string))) 3221 3222 (defun lsp--log-entry-pp (entry) 3223 (cl-assert (lsp--log-entry-p entry)) 3224 (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time 3225 body) 3226 entry) 3227 (json-false :json-false) 3228 (json-encoding-pretty-print t) 3229 (str nil)) 3230 (setq str 3231 (concat (format "[Trace - %s] " timestamp) 3232 (pcase type 3233 ('incoming-req (format "Received request '%s - (%s)." method id)) 3234 ('outgoing-req (format "Sending request '%s - (%s)'." method id)) 3235 3236 ('incoming-notif (format "Received notification '%s'." method)) 3237 ('outgoing-notif (format "Sending notification '%s'." method)) 3238 3239 ('incoming-resp (format "Received response '%s - (%s)' in %dms." 3240 method id process-time)) 3241 ('outgoing-resp 3242 (format 3243 "Sending response '%s - (%s)'. Processing request took %dms" 3244 method id process-time))) 3245 "\n" 3246 (if (memq type '(incoming-resp ougoing-resp)) 3247 "Result: " 3248 "Params: ") 3249 (lsp--log-font-lock-json (json-encode body)) 3250 "\n\n\n")) 3251 (setq str (propertize str 'mouse-face 'highlight 'read-only t)) 3252 (insert str))) 3253 3254 (defvar-local lsp--log-io-ewoc nil) 3255 3256 (defun lsp--get-create-io-ewoc (workspace) 3257 (if (and (lsp--workspace-ewoc workspace) 3258 (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace)))) 3259 (lsp--workspace-ewoc workspace) 3260 (with-current-buffer (lsp--get-log-buffer-create workspace) 3261 (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode)) 3262 (setq-local window-point-insertion-type t) 3263 (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t)) 3264 (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc)) 3265 (lsp--workspace-ewoc workspace))) 3266 3267 (defun lsp--ewoc-count (ewoc) 3268 (let* ((count 0) 3269 (count-fn (lambda (_) (setq count (1+ count))))) 3270 (ewoc-map count-fn ewoc) 3271 count)) 3272 3273 (defun lsp--log-entry-new (entry workspace) 3274 (let* ((ewoc (lsp--get-create-io-ewoc workspace)) 3275 (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc))) 3276 (node (if (or (eq lsp-io-messages-max t) 3277 (>= lsp-io-messages-max count)) 3278 nil 3279 (ewoc-nth ewoc (1- lsp-io-messages-max)))) 3280 (prev nil) 3281 (inhibit-read-only t)) 3282 (while node 3283 (setq prev (ewoc-prev ewoc node)) 3284 (ewoc-delete ewoc node) 3285 (setq node prev)) 3286 (ewoc-enter-last ewoc entry))) 3287 3288 (defun lsp--send-notification (body) 3289 "Send BODY as a notification to the language server." 3290 (lsp-foreach-workspace 3291 (when (lsp--log-io-p (plist-get body :method)) 3292 (lsp--log-entry-new (lsp--make-log-entry 3293 (plist-get body :method) 3294 nil (plist-get body :params) 'outgoing-notif) 3295 lsp--cur-workspace)) 3296 (lsp--send-no-wait body 3297 (lsp--workspace-proc lsp--cur-workspace)))) 3298 3299 (defalias 'lsp-send-notification 'lsp--send-notification) 3300 3301 (defun lsp-notify (method params) 3302 "Send notification METHOD with PARAMS." 3303 (lsp--send-notification (lsp--make-notification method params))) 3304 3305 (defun lsp--cur-workspace-check () 3306 "Check whether buffer lsp workspace(s) are set." 3307 (cl-assert (lsp-workspaces) nil 3308 "No language server(s) is associated with this buffer.")) 3309 3310 (defun lsp--send-request (body &optional no-wait no-merge) 3311 "Send BODY as a request to the language server, get the response. 3312 If NO-WAIT is non-nil, don't synchronously wait for a response. 3313 If NO-MERGE is non-nil, don't merge the results but return an 3314 alist mapping workspace->result." 3315 (lsp-request (plist-get body :method) 3316 (plist-get body :params) 3317 :no-wait no-wait 3318 :no-merge no-merge)) 3319 3320 (defalias 'lsp-send-request 'lsp--send-request 3321 "Send BODY as a request to the language server and return the response 3322 synchronously. 3323 \n(fn BODY)") 3324 3325 (cl-defun lsp-request (method params &key no-wait no-merge) 3326 "Send request METHOD with PARAMS. 3327 If NO-MERGE is non-nil, don't merge the results but return alist 3328 workspace->result. 3329 If NO-WAIT is non-nil send the request as notification." 3330 (if no-wait 3331 (lsp-notify method params) 3332 (let* ((send-time (float-time)) 3333 ;; max time by which we must get a response 3334 (expected-time 3335 (and 3336 lsp-response-timeout 3337 (+ send-time lsp-response-timeout))) 3338 resp-result resp-error done?) 3339 (unwind-protect 3340 (progn 3341 (lsp-request-async method params 3342 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3343 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3344 :no-merge no-merge 3345 :mode 'detached 3346 :cancel-token :sync-request) 3347 (while (not (or resp-error resp-result)) 3348 (if (functionp 'json-rpc-connection) 3349 (catch 'lsp-done (sit-for 0.01)) 3350 (catch 'lsp-done 3351 (accept-process-output 3352 nil 3353 (if expected-time (- expected-time send-time) 1)))) 3354 (setq send-time (float-time)) 3355 (when (and expected-time (< expected-time send-time)) 3356 (error "Timeout while waiting for response. Method: %s" method))) 3357 (setq done? t) 3358 (cond 3359 ((eq resp-result :finished) nil) 3360 (resp-result resp-result) 3361 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3362 ((lsp-json-error? (cl-first resp-error)) 3363 (error (lsp:json-error-message (cl-first resp-error)))))) 3364 (unless done? 3365 (lsp-cancel-request-by-token :sync-request)))))) 3366 3367 (cl-defun lsp-request-while-no-input (method params) 3368 "Send request METHOD with PARAMS and waits until there is no input. 3369 Return same value as `lsp--while-no-input' and respecting `non-essential'." 3370 (if (or non-essential (not lsp-request-while-no-input-may-block)) 3371 (let* ((send-time (float-time)) 3372 ;; max time by which we must get a response 3373 (expected-time 3374 (and 3375 lsp-response-timeout 3376 (+ send-time lsp-response-timeout))) 3377 resp-result resp-error done?) 3378 (unwind-protect 3379 (progn 3380 (lsp-request-async method params 3381 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3382 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3383 :mode 'detached 3384 :cancel-token :sync-request) 3385 (while (not (or resp-error resp-result (input-pending-p))) 3386 (catch 'lsp-done 3387 (sit-for 3388 (if expected-time (- expected-time send-time) 1))) 3389 (setq send-time (float-time)) 3390 (when (and expected-time (< expected-time send-time)) 3391 (error "Timeout while waiting for response. Method: %s" method))) 3392 (setq done? (or resp-error resp-result)) 3393 (cond 3394 ((eq resp-result :finished) nil) 3395 (resp-result resp-result) 3396 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3397 ((lsp-json-error? (cl-first resp-error)) 3398 (error (lsp:json-error-message (cl-first resp-error)))))) 3399 (unless done? 3400 (lsp-cancel-request-by-token :sync-request)) 3401 (when (and (input-pending-p) lsp--throw-on-input) 3402 (throw 'input :interrupted)))) 3403 (lsp-request method params))) 3404 3405 (defvar lsp--cancelable-requests (ht)) 3406 3407 (cl-defun lsp-request-async (method params callback 3408 &key mode error-handler cancel-handler no-merge cancel-token) 3409 "Send METHOD with PARAMS as a request to the language server. 3410 Call CALLBACK with the response received from the server 3411 asynchronously. 3412 MODE determines when the callback will be called depending on the 3413 condition of the original buffer. It could be: 3414 - `detached' which means that the callback will be executed no 3415 matter what has happened to the buffer. 3416 - `alive' - the callback will be executed only if the buffer from 3417 which the call was executed is still alive. 3418 - `current' the callback will be executed only if the original buffer 3419 is still selected. 3420 - `tick' - the callback will be executed only if the buffer was not modified. 3421 - `unchanged' - the callback will be executed only if the buffer hasn't 3422 changed and if the buffer is not modified. 3423 3424 ERROR-HANDLER will be called in case the request has failed. 3425 CANCEL-HANDLER will be called in case the request is being canceled. 3426 If NO-MERGE is non-nil, don't merge the results but return alist 3427 workspace->result. 3428 CANCEL-TOKEN is the token that can be used to cancel request." 3429 (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params) 3430 callback mode error-handler cancel-handler no-merge cancel-token)) 3431 3432 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback) 3433 (lambda (&rest _) 3434 (unless (and (equal 'post-command-hook hook) 3435 (equal (current-buffer) buf)) 3436 (lsp--request-cleanup-hooks id) 3437 (with-lsp-workspaces workspaces 3438 (lsp--cancel-request id) 3439 (when cancel-callback (funcall cancel-callback))) 3440 (lsp-log "Cancelling %s(%s) in hook %s" method id hook)))) 3441 3442 (defun lsp--create-async-callback 3443 (callback method no-merge workspaces) 3444 "Create async handler expecting COUNT results, merge them and call CALLBACK. 3445 MODE determines when the callback will be called depending on the 3446 condition of the original buffer. METHOD is the invoked method. 3447 If NO-MERGE is non-nil, don't merge the results but return alist 3448 workspace->result. ID is the request id." 3449 (let (results errors) 3450 (lambda (result) 3451 (push (cons lsp--cur-workspace result) 3452 (if (eq result :error) errors results)) 3453 (when (and (not (eq (length errors) (length workspaces))) 3454 (eq (+ (length errors) (length results)) (length workspaces))) 3455 (funcall callback 3456 (if no-merge 3457 results 3458 (lsp--merge-results (-map #'cl-rest results) method))))))) 3459 3460 (defcustom lsp-default-create-error-handler-fn nil 3461 "Default error handler customization. 3462 Handler should give METHOD as argument and return function of one argument 3463 ERROR." 3464 :type 'function 3465 :group 'lsp-mode 3466 :package-version '(lsp-mode . "9.0.0")) 3467 3468 (defun lsp--create-default-error-handler (method) 3469 "Default error handler. 3470 METHOD is the executed method." 3471 (if lsp-default-create-error-handler-fn 3472 (funcall lsp-default-create-error-handler-fn method) 3473 (lambda (error) 3474 (lsp--warn "%s" (or (lsp--error-string error) 3475 (format "%s Request has failed" method)))))) 3476 3477 (defvar lsp--request-cleanup-hooks (ht)) 3478 3479 (defun lsp--request-cleanup-hooks (request-id) 3480 (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks))) 3481 (funcall cleanup-function) 3482 (remhash request-id lsp--request-cleanup-hooks))) 3483 3484 (defun lsp-cancel-request-by-token (cancel-token) 3485 "Cancel request using CANCEL-TOKEN." 3486 (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests)) 3487 (with-lsp-workspaces workspaces 3488 (lsp--cancel-request request-id)) 3489 (remhash cancel-token lsp--cancelable-requests) 3490 (lsp--request-cleanup-hooks request-id))) 3491 3492 (defun lsp--send-request-async (body callback 3493 &optional mode error-callback cancel-callback 3494 no-merge cancel-token) 3495 "Send BODY as a request to the language server. 3496 Call CALLBACK with the response received from the server 3497 asynchronously. 3498 MODE determines when the callback will be called depending on the 3499 condition of the original buffer. It could be: 3500 - `detached' which means that the callback will be executed no 3501 matter what has happened to the buffer. 3502 - `alive' - the callback will be executed only if the buffer from 3503 which the call was executed is still alive. 3504 - `current' the callback will be executed only if the original buffer 3505 is still selected. 3506 - `tick' - the callback will be executed only if the buffer was not modified. 3507 - `unchanged' - the callback will be executed only if the buffer hasn't 3508 changed and if the buffer is not modified. 3509 3510 ERROR-CALLBACK will be called in case the request has failed. 3511 CANCEL-CALLBACK will be called in case the request is being canceled. 3512 If NO-MERGE is non-nil, don't merge the results but return alist 3513 workspace->result. 3514 CANCEL-TOKEN is the token that can be used to cancel request." 3515 (when cancel-token 3516 (lsp-cancel-request-by-token cancel-token)) 3517 3518 (if-let ((target-workspaces (lsp--find-workspaces-for body))) 3519 (let* ((start-time (current-time)) 3520 (method (plist-get body :method)) 3521 (id (cl-incf lsp-last-id)) 3522 (buf (current-buffer)) 3523 (cancel-callback (when cancel-callback 3524 (pcase mode 3525 ((or 'alive 'tick 'unchanged) 3526 (lambda () 3527 (with-current-buffer buf 3528 (funcall cancel-callback)))) 3529 (_ cancel-callback)))) 3530 ;; calculate what are the (hook . local) pairs which will cancel 3531 ;; the request 3532 (hooks (pcase mode 3533 ('alive '((kill-buffer-hook . t))) 3534 ('tick '((kill-buffer-hook . t) (after-change-functions . t))) 3535 ('unchanged '((after-change-functions . t) (post-command-hook . nil))) 3536 ('current '((post-command-hook . nil))))) 3537 ;; note: lambdas in emacs can be compared but we should make sure 3538 ;; that all of the captured arguments are the same - in our case 3539 ;; `lsp--create-request-cancel' will return the same lambda when 3540 ;; called with the same params. 3541 (cleanup-hooks 3542 (lambda () (mapc 3543 (-lambda ((hook . local)) 3544 (if local 3545 (when (buffer-live-p buf) 3546 (with-current-buffer buf 3547 (remove-hook hook 3548 (lsp--create-request-cancel 3549 id target-workspaces hook buf method cancel-callback) 3550 t))) 3551 (remove-hook hook (lsp--create-request-cancel 3552 id target-workspaces hook buf method cancel-callback)))) 3553 hooks) 3554 (remhash cancel-token lsp--cancelable-requests))) 3555 (callback (pcase mode 3556 ((or 'alive 'tick 'unchanged) (lambda (&rest args) 3557 (with-current-buffer buf 3558 (apply callback args)))) 3559 (_ callback))) 3560 (callback (lsp--create-async-callback callback 3561 method 3562 no-merge 3563 target-workspaces)) 3564 (callback (lambda (result) 3565 (lsp--request-cleanup-hooks id) 3566 (funcall callback result))) 3567 (error-callback (lsp--create-async-callback 3568 (or error-callback 3569 (lsp--create-default-error-handler method)) 3570 method 3571 nil 3572 target-workspaces)) 3573 (error-callback (lambda (error) 3574 (funcall callback :error) 3575 (lsp--request-cleanup-hooks id) 3576 (funcall error-callback error))) 3577 (body (plist-put body :id id))) 3578 3579 ;; cancel request in any of the hooks 3580 (mapc (-lambda ((hook . local)) 3581 (add-hook hook 3582 (lsp--create-request-cancel 3583 id target-workspaces hook buf method cancel-callback) 3584 nil local)) 3585 hooks) 3586 (puthash id cleanup-hooks lsp--request-cleanup-hooks) 3587 3588 (setq lsp--last-active-workspaces target-workspaces) 3589 3590 (when cancel-token 3591 (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests)) 3592 3593 (seq-doseq (workspace target-workspaces) 3594 (when (lsp--log-io-p method) 3595 (lsp--log-entry-new (lsp--make-log-entry method id 3596 (plist-get body :params) 3597 'outgoing-req) 3598 workspace)) 3599 (puthash id 3600 (list callback error-callback method start-time (current-time)) 3601 (-> workspace 3602 (lsp--workspace-client) 3603 (lsp--client-response-handlers))) 3604 (lsp--send-no-wait body (lsp--workspace-proc workspace))) 3605 body) 3606 (error "The connected server(s) does not support method %s. 3607 To find out what capabilities support your server use `M-x lsp-describe-session' 3608 and expand the capabilities section" 3609 (plist-get body :method)))) 3610 3611 ;; deprecated, use lsp-request-async. 3612 (defalias 'lsp-send-request-async 'lsp--send-request-async) 3613 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1") 3614 3615 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any 3616 ;; pending language servers. 3617 (add-hook 'kill-emacs-hook #'lsp--global-teardown) 3618 3619 (defun lsp--global-teardown () 3620 "Unload working workspaces." 3621 (lsp-foreach-workspace (lsp--shutdown-workspace))) 3622 3623 (defun lsp--shutdown-workspace (&optional restart) 3624 "Shut down the language server process for ‘lsp--cur-workspace’." 3625 (with-demoted-errors "LSP error: %S" 3626 (let ((lsp-response-timeout 0.5)) 3627 (condition-case err 3628 (lsp-request "shutdown" nil) 3629 (error (lsp--error "%s" err)))) 3630 (lsp-notify "exit" nil)) 3631 (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown)) 3632 (lsp--uninitialize-workspace)) 3633 3634 (defcustom lsp-inlay-hint-enable nil 3635 "If non-nil it will enable inlay hints." 3636 :type 'boolean 3637 :group 'lsp-mode 3638 :package-version '(lsp-mode . "9.0.0")) 3639 3640 (defun lsp--uninitialize-workspace () 3641 "Cleanup buffer state. 3642 When a workspace is shut down, by request or from just 3643 disappearing, unset all the variables related to it." 3644 (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace] 3645 (lsp-process-kill cmd-proc) 3646 (mapc (lambda (buf) 3647 (when (lsp-buffer-live-p buf) 3648 (lsp-with-current-buffer buf 3649 (lsp-managed-mode -1)))) 3650 buffers) 3651 (lsp-diagnostics--workspace-cleanup lsp--cur-workspace))) 3652 3653 (defun lsp--client-capabilities (&optional custom-capabilities) 3654 "Return the client capabilities appending CUSTOM-CAPABILITIES." 3655 (append 3656 `((general . ((positionEncodings . ["utf-32", "utf-16"]))) 3657 (workspace . ((workspaceEdit . ((documentChanges . t) 3658 (resourceOperations . ["create" "rename" "delete"]))) 3659 (applyEdit . t) 3660 (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))))) 3661 (executeCommand . ((dynamicRegistration . :json-false))) 3662 ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t))))) 3663 (workspaceFolders . t) 3664 (configuration . t) 3665 ,@(when lsp-semantic-tokens-enable 3666 `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests) 3667 lsp-semantic-tokens-honor-refresh-requests) 3668 :json-false)))))) 3669 ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t))))) 3670 ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false))))) 3671 (fileOperations . ((didCreate . :json-false) 3672 (willCreate . :json-false) 3673 (didRename . t) 3674 (willRename . t) 3675 (didDelete . :json-false) 3676 (willDelete . :json-false))))) 3677 (textDocument . ((declaration . ((dynamicRegistration . t) 3678 (linkSupport . t))) 3679 (definition . ((dynamicRegistration . t) 3680 (linkSupport . t))) 3681 (references . ((dynamicRegistration . t))) 3682 (implementation . ((dynamicRegistration . t) 3683 (linkSupport . t))) 3684 (typeDefinition . ((dynamicRegistration . t) 3685 (linkSupport . t))) 3686 (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t))) 3687 (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))) 3688 (hierarchicalDocumentSymbolSupport . t))) 3689 (formatting . ((dynamicRegistration . t))) 3690 (rangeFormatting . ((dynamicRegistration . t))) 3691 (onTypeFormatting . ((dynamicRegistration . t))) 3692 ,@(when (and lsp-semantic-tokens-enable 3693 (functionp 'lsp--semantic-tokens-capabilities)) 3694 (lsp--semantic-tokens-capabilities)) 3695 (rename . ((dynamicRegistration . t) (prepareSupport . t))) 3696 (codeAction . ((dynamicRegistration . t) 3697 (isPreferredSupport . t) 3698 (codeActionLiteralSupport . ((codeActionKind . ((valueSet . ["" 3699 "quickfix" 3700 "refactor" 3701 "refactor.extract" 3702 "refactor.inline" 3703 "refactor.rewrite" 3704 "source" 3705 "source.organizeImports"]))))) 3706 (resolveSupport . ((properties . ["edit" "command"]))) 3707 (dataSupport . t))) 3708 (completion . ((completionItem . ((snippetSupport . ,(cond 3709 ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode))) 3710 (lsp--warn (concat 3711 "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. " 3712 "You must either install yasnippet, or disable snippet support.")) 3713 :json-false) 3714 (lsp-enable-snippet t) 3715 (t :json-false))) 3716 (documentationFormat . ["markdown" "plaintext"]) 3717 ;; Remove this after jdtls support resolveSupport 3718 (resolveAdditionalTextEditsSupport . t) 3719 (insertReplaceSupport . t) 3720 (deprecatedSupport . t) 3721 (resolveSupport 3722 . ((properties . ["documentation" 3723 "detail" 3724 "additionalTextEdits" 3725 "command"]))) 3726 (insertTextModeSupport . ((valueSet . [1 2]))))) 3727 (contextSupport . t) 3728 (dynamicRegistration . t))) 3729 (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t))))) 3730 (dynamicRegistration . t))) 3731 (documentLink . ((dynamicRegistration . t) 3732 (tooltipSupport . t))) 3733 (hover . ((contentFormat . ["markdown" "plaintext"]) 3734 (dynamicRegistration . t))) 3735 ,@(when lsp-enable-folding 3736 `((foldingRange . ((dynamicRegistration . t) 3737 ,@(when lsp-folding-range-limit 3738 `((rangeLimit . ,lsp-folding-range-limit))) 3739 ,@(when lsp-folding-line-folding-only 3740 `((lineFoldingOnly . t))))))) 3741 (selectionRange . ((dynamicRegistration . t))) 3742 (callHierarchy . ((dynamicRegistration . :json-false))) 3743 (typeHierarchy . ((dynamicRegistration . t))) 3744 (publishDiagnostics . ((relatedInformation . t) 3745 (tagSupport . ((valueSet . [1 2]))) 3746 (versionSupport . t))) 3747 (linkedEditingRange . ((dynamicRegistration . t))))) 3748 (window . ((workDoneProgress . t) 3749 (showDocument . ((support . t)))))) 3750 custom-capabilities)) 3751 3752 (defun lsp-find-roots-for-workspace (workspace session) 3753 "Get all roots for the WORKSPACE." 3754 (-filter #'identity (ht-map (lambda (folder workspaces) 3755 (when (-contains? workspaces workspace) 3756 folder)) 3757 (lsp-session-folder->servers session)))) 3758 3759 (defun lsp-session-watches (&optional session) 3760 "Get watches created for SESSION." 3761 (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session)))) 3762 (-let [res (make-hash-table :test 'equal)] 3763 (puthash "__watches" res (lsp-session-metadata (or session (lsp-session)))) 3764 res))) 3765 3766 (defun lsp--file-process-event (session root-folder event) 3767 "Process file event." 3768 (let* ((changed-file (cl-third event)) 3769 (rel-changed-file (f-relative changed-file root-folder)) 3770 (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type)) 3771 (bit-position (1- event-numeric-kind)) 3772 (watch-bit (ash 1 bit-position))) 3773 (->> 3774 session 3775 lsp-session-folder->servers 3776 (gethash root-folder) 3777 (seq-do (lambda (workspace) 3778 (when (->> 3779 workspace 3780 lsp--workspace-registered-server-capabilities 3781 (-any? 3782 (lambda (capability) 3783 (and 3784 (equal (lsp--registered-capability-method capability) 3785 "workspace/didChangeWatchedFiles") 3786 (->> 3787 capability 3788 lsp--registered-capability-options 3789 (lsp:did-change-watched-files-registration-options-watchers) 3790 (seq-find 3791 (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp)) 3792 (when (or (null kind?) 3793 (> (logand kind? watch-bit) 0)) 3794 (-let [regexes (or cached-regexp 3795 (let ((regexp (lsp-glob-to-regexps glob-pattern))) 3796 (lsp-put fs-watcher :_cachedRegexp regexp) 3797 regexp))] 3798 (-any? (lambda (re) 3799 (or (string-match re changed-file) 3800 (string-match re rel-changed-file))) 3801 regexes)))))))))) 3802 (with-lsp-workspace workspace 3803 (lsp-notify 3804 "workspace/didChangeWatchedFiles" 3805 `((changes . [((type . ,event-numeric-kind) 3806 (uri . ,(lsp--path-to-uri changed-file)))])))))))))) 3807 3808 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?)) 3809 "Register capability REG." 3810 (when (and lsp-enable-file-watchers 3811 (equal method "workspace/didChangeWatchedFiles")) 3812 (-let* ((created-watches (lsp-session-watches (lsp-session))) 3813 (root-folders (cl-set-difference 3814 (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session)) 3815 (ht-keys created-watches)))) 3816 ;; create watch for each root folder without such 3817 (dolist (folder root-folders) 3818 (let* ((watch (make-lsp-watch :root-directory folder)) 3819 (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder)) 3820 (ignored-files-regex-list (car ignored-things)) 3821 (ignored-directories-regex-list (cadr ignored-things))) 3822 (puthash folder watch created-watches) 3823 (lsp-watch-root-folder (file-truename folder) 3824 (-partial #'lsp--file-process-event (lsp-session) folder) 3825 ignored-files-regex-list 3826 ignored-directories-regex-list 3827 watch 3828 t))))) 3829 3830 (push 3831 (make-lsp--registered-capability :id id :method method :options register-options?) 3832 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3833 3834 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body) 3835 "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to 3836 access dir-local variables." 3837 (declare (indent 1) (debug t)) 3838 `(with-temp-buffer 3839 ;; Set the buffer's name to something under the root so that we can hack the local variables 3840 ;; This file doesn't need to exist and will not be created due to this. 3841 (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root))) 3842 (hack-local-variables) 3843 (prog1 ,@body 3844 (setq-local buffer-file-name nil)))) 3845 3846 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root) 3847 "Return a list of the form 3848 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given 3849 WORKSPACE-ROOT." 3850 ;; The intent of this function is to provide per-root workspace-level customization of the 3851 ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables. 3852 (lsp--with-workspace-temp-buffer workspace-root 3853 (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories)))) 3854 3855 3856 (defun lsp--cleanup-hanging-watches () 3857 "Cleanup watches in case there are no more workspaces that are interested 3858 in that particular folder." 3859 (let* ((session (lsp-session)) 3860 (watches (lsp-session-watches session))) 3861 (dolist (watched-folder (ht-keys watches)) 3862 (when (-none? (lambda (workspace) 3863 (with-lsp-workspace workspace 3864 (lsp--registered-capability "workspace/didChangeWatchedFiles"))) 3865 (gethash watched-folder (lsp-session-folder->servers (lsp-session)))) 3866 (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder) 3867 (lsp-kill-watch (gethash watched-folder watches)) 3868 (remhash watched-folder watches))))) 3869 3870 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method)) 3871 "Unregister capability UNREG." 3872 (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace) 3873 (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id)) 3874 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3875 (when (equal method "workspace/didChangeWatchedFiles") 3876 (lsp--cleanup-hanging-watches))) 3877 3878 (defun lsp--server-capabilities () 3879 "Return the capabilities of the language server associated with the buffer." 3880 (->> (lsp-workspaces) 3881 (-keep #'lsp--workspace-server-capabilities) 3882 (apply #'lsp-merge))) 3883 3884 (defun lsp--send-open-close-p () 3885 "Return whether open and close notifications should be sent to the server." 3886 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3887 (or (memq sync '(1 2)) 3888 (lsp:text-document-sync-options-open-close? sync)))) 3889 3890 (defun lsp--send-will-save-p () 3891 "Return whether willSave notifications should be sent to the server." 3892 (-> (lsp--server-capabilities) 3893 (lsp:server-capabilities-text-document-sync?) 3894 (lsp:text-document-sync-options-will-save?))) 3895 3896 (defun lsp--send-will-save-wait-until-p () 3897 "Return whether willSaveWaitUntil notifications should be sent to the server." 3898 (-> (lsp--server-capabilities) 3899 (lsp:server-capabilities-text-document-sync?) 3900 (lsp:text-document-sync-options-will-save-wait-until?))) 3901 3902 (defun lsp--send-did-save-p () 3903 "Return whether didSave notifications should be sent to the server." 3904 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3905 (or (memq sync '(1 2)) 3906 (lsp:text-document-sync-options-save? sync)))) 3907 3908 (defun lsp--save-include-text-p () 3909 "Return whether save notifications should include the text document's contents." 3910 (->> (lsp--server-capabilities) 3911 (lsp:server-capabilities-text-document-sync?) 3912 (lsp:text-document-sync-options-save?) 3913 (lsp:text-document-save-registration-options-include-text?))) 3914 3915 (defun lsp--send-will-rename-files-p (path) 3916 "Return whether willRenameFiles request should be sent to the server. 3917 If any filters, checks if it applies for PATH." 3918 (let* ((will-rename (-> (lsp--server-capabilities) 3919 (lsp:server-capabilities-workspace?) 3920 (lsp:workspace-server-capabilities-file-operations?) 3921 (lsp:workspace-file-operations-will-rename?))) 3922 (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list))) 3923 (and will-rename 3924 (or (seq-empty-p filters) 3925 (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob))) 3926 (-let [regexes (lsp-glob-to-regexps glob)] 3927 (and (or (not scheme?) 3928 (string-prefix-p scheme? (lsp--path-to-uri path))) 3929 (-any? (lambda (re) 3930 (string-match re path)) 3931 regexes)))) 3932 filters))))) 3933 3934 (defun lsp--send-did-rename-files-p () 3935 "Return whether didRenameFiles notification should be sent to the server." 3936 (-> (lsp--server-capabilities) 3937 (lsp:server-capabilities-workspace?) 3938 (lsp:workspace-server-capabilities-file-operations?) 3939 (lsp:workspace-file-operations-did-rename?))) 3940 3941 (declare-function project-roots "ext:project" (project) t) 3942 (declare-function project-root "ext:project" (project) t) 3943 3944 (defun lsp--suggest-project-root () 3945 "Get project root." 3946 (or 3947 (when (fboundp 'projectile-project-root) 3948 (condition-case nil 3949 (projectile-project-root) 3950 (error nil))) 3951 (when (fboundp 'project-current) 3952 (when-let ((project (project-current))) 3953 (if (fboundp 'project-root) 3954 (project-root project) 3955 (car (with-no-warnings 3956 (project-roots project)))))) 3957 default-directory)) 3958 3959 (defun lsp--read-from-file (file) 3960 "Read FILE content." 3961 (when (file-exists-p file) 3962 (cl-first (read-from-string (f-read-text file 'utf-8))))) 3963 3964 (defun lsp--persist (file-name to-persist) 3965 "Persist TO-PERSIST in FILE-NAME. 3966 3967 This function creates the parent directories if they don't exist 3968 yet." 3969 (let ((print-length nil) 3970 (print-level nil)) 3971 ;; Create all parent directories: 3972 (make-directory (f-parent file-name) t) 3973 (f-write-text (prin1-to-string to-persist) 'utf-8 file-name))) 3974 3975 (defun lsp-workspace-folders-add (project-root) 3976 "Add PROJECT-ROOT to the list of workspace folders." 3977 (interactive 3978 (list (read-directory-name "Select folder to add: " 3979 (or (lsp--suggest-project-root) default-directory) nil t))) 3980 (cl-pushnew (lsp-f-canonical project-root) 3981 (lsp-session-folders (lsp-session)) :test 'equal) 3982 (lsp--persist-session (lsp-session)) 3983 3984 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)) 3985 3986 (defun lsp-workspace-folders-remove (project-root) 3987 "Remove PROJECT-ROOT from the list of workspace folders." 3988 (interactive (list (completing-read "Select folder to remove: " 3989 (lsp-session-folders (lsp-session)) 3990 nil t nil nil 3991 (lsp-find-session-folder (lsp-session) default-directory)))) 3992 3993 (setq project-root (lsp-f-canonical project-root)) 3994 3995 ;; send remove folder to each multiroot workspace associated with the folder 3996 (dolist (wks (->> (lsp-session) 3997 (lsp-session-folder->servers) 3998 (gethash project-root) 3999 (--filter (lsp--client-multi-root (lsp--workspace-client it))))) 4000 (with-lsp-workspace wks 4001 (lsp-notify "workspace/didChangeWorkspaceFolders" 4002 (lsp-make-did-change-workspace-folders-params 4003 :event (lsp-make-workspace-folders-change-event 4004 :removed (vector (lsp-make-workspace-folder 4005 :uri (lsp--path-to-uri project-root) 4006 :name (f-filename project-root))) 4007 :added []))))) 4008 4009 ;; turn off servers in the removed directory 4010 (let* ((session (lsp-session)) 4011 (folder->servers (lsp-session-folder->servers session)) 4012 (server-id->folders (lsp-session-server-id->folders session)) 4013 (workspaces (gethash project-root folder->servers))) 4014 4015 (remhash project-root folder->servers) 4016 4017 ;; turn off the servers without root folders 4018 (dolist (workspace workspaces) 4019 (when (--none? (-contains? it workspace) (ht-values folder->servers)) 4020 (lsp--info "Shutdown %s since folder %s is removed..." 4021 (lsp--workspace-print workspace) project-root) 4022 (with-lsp-workspace workspace (lsp--shutdown-workspace)))) 4023 4024 (setf (lsp-session-folders session) 4025 (-remove-item project-root (lsp-session-folders session))) 4026 4027 (ht-aeach (puthash key 4028 (-remove-item project-root value) 4029 server-id->folders) 4030 server-id->folders) 4031 (lsp--persist-session (lsp-session))) 4032 4033 (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root))) 4034 4035 (defun lsp-workspace-blocklist-remove (project-root) 4036 "Remove PROJECT-ROOT from the workspace blocklist." 4037 (interactive (list (completing-read "Select folder to remove:" 4038 (lsp-session-folders-blocklist (lsp-session)) 4039 nil t))) 4040 (setf (lsp-session-folders-blocklist (lsp-session)) 4041 (delete project-root 4042 (lsp-session-folders-blocklist (lsp-session)))) 4043 (lsp--persist-session (lsp-session))) 4044 4045 (define-obsolete-function-alias 'lsp-workspace-folders-switch 4046 'lsp-workspace-folders-open "lsp-mode 6.1") 4047 4048 (defun lsp-workspace-folders-open (project-root) 4049 "Open the directory located at PROJECT-ROOT" 4050 (interactive (list (completing-read "Open folder: " 4051 (lsp-session-folders (lsp-session)) 4052 nil t))) 4053 (find-file project-root)) 4054 4055 (defun lsp--maybe-enable-signature-help (trigger-characters) 4056 (let ((ch last-command-event)) 4057 (when (cl-find ch trigger-characters :key #'string-to-char) 4058 (lsp-signature-activate)))) 4059 4060 (defun lsp--on-type-formatting-handler-create () 4061 (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" ))) 4062 (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character? 4063 :first-trigger-character) provider] 4064 (lambda () 4065 (lsp--on-type-formatting first-trigger-character 4066 more-trigger-character?))))) 4067 4068 (defun lsp--update-on-type-formatting-hook (&optional cleanup?) 4069 (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create))) 4070 (cond 4071 ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?)) 4072 (add-hook 'post-self-insert-hook on-type-formatting-handler nil t)) 4073 ((or cleanup? 4074 (not lsp-enable-on-type-formatting)) 4075 (remove-hook 'post-self-insert-hook on-type-formatting-handler t))))) 4076 4077 (defun lsp--signature-help-handler-create () 4078 (-when-let ((&SignatureHelpOptions? :trigger-characters?) 4079 (lsp--capability-for-method "textDocument/signatureHelp")) 4080 (lambda () 4081 (lsp--maybe-enable-signature-help trigger-characters?)))) 4082 4083 (defun lsp--update-signature-help-hook (&optional cleanup?) 4084 (let ((signature-help-handler (lsp--signature-help-handler-create))) 4085 (cond 4086 ((and (or (equal lsp-signature-auto-activate t) 4087 (memq :on-trigger-char lsp-signature-auto-activate)) 4088 signature-help-handler) 4089 (add-hook 'post-self-insert-hook signature-help-handler nil t)) 4090 4091 ((or cleanup? 4092 (not (or (equal lsp-signature-auto-activate t) 4093 (memq :on-trigger-char lsp-signature-auto-activate)))) 4094 (remove-hook 'post-self-insert-hook signature-help-handler t))))) 4095 4096 (defun lsp--after-set-visited-file-name () 4097 (lsp-disconnect) 4098 (lsp)) 4099 4100 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27 4101 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099 4102 (defvar eldoc-documentation-default) ; CI 4103 (when (< emacs-major-version 28) 4104 (unless (boundp 'eldoc-documentation-functions) 4105 (load "eldoc" nil 'nomessage)) 4106 (when (memq (default-value 'eldoc-documentation-function) '(nil ignore)) 4107 ;; actually `eldoc-documentation-strategy', but CI was failing 4108 (setq-default eldoc-documentation-function 'eldoc-documentation-default))) 4109 4110 (define-minor-mode lsp-managed-mode 4111 "Mode for source buffers managed by lsp-mode." 4112 :lighter nil 4113 (cond 4114 (lsp-managed-mode 4115 (when (lsp-feature? "textDocument/hover") 4116 (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t) 4117 (eldoc-mode 1)) 4118 4119 (add-hook 'after-change-functions #'lsp-on-change nil t) 4120 (add-hook 'after-revert-hook #'lsp-on-revert nil t) 4121 (add-hook 'after-save-hook #'lsp-on-save nil t) 4122 (add-hook 'auto-save-hook #'lsp--on-auto-save nil t) 4123 (add-hook 'before-change-functions #'lsp-before-change nil t) 4124 (add-hook 'before-save-hook #'lsp--before-save nil t) 4125 (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t) 4126 (add-hook 'post-command-hook #'lsp--post-command nil t) 4127 4128 (lsp--update-on-type-formatting-hook) 4129 (lsp--update-signature-help-hook) 4130 4131 (when lsp-enable-xref 4132 (add-hook 'xref-backend-functions #'lsp--xref-backend nil t)) 4133 4134 (lsp-configure-buffer) 4135 4136 ;; make sure we turn off lsp-mode in case major mode changes, because major 4137 ;; mode change will wipe the buffer locals. 4138 (add-hook 'change-major-mode-hook #'lsp-disconnect nil t) 4139 (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t) 4140 4141 (let ((buffer (lsp-current-buffer))) 4142 (run-with-idle-timer 4143 0.0 nil 4144 (lambda () 4145 (when (lsp-buffer-live-p buffer) 4146 (lsp-with-current-buffer buffer 4147 (lsp--on-change-debounce buffer) 4148 (lsp--on-idle buffer))))))) 4149 (t 4150 (lsp-unconfig-buffer) 4151 4152 (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t) 4153 (remove-hook 'post-command-hook #'lsp--post-command t) 4154 (remove-hook 'after-change-functions #'lsp-on-change t) 4155 (remove-hook 'after-revert-hook #'lsp-on-revert t) 4156 (remove-hook 'after-save-hook #'lsp-on-save t) 4157 (remove-hook 'auto-save-hook #'lsp--on-auto-save t) 4158 (remove-hook 'before-change-functions #'lsp-before-change t) 4159 (remove-hook 'before-save-hook #'lsp--before-save t) 4160 (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t) 4161 4162 (lsp--update-on-type-formatting-hook :cleanup) 4163 (lsp--update-signature-help-hook :cleanup) 4164 4165 (when lsp--on-idle-timer 4166 (cancel-timer lsp--on-idle-timer) 4167 (setq lsp--on-idle-timer nil)) 4168 4169 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4170 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4171 4172 (lsp--remove-overlays 'lsp-highlight) 4173 (lsp--remove-overlays 'lsp-links) 4174 4175 (remove-hook 'xref-backend-functions #'lsp--xref-backend t) 4176 (remove-hook 'change-major-mode-hook #'lsp-disconnect t) 4177 (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t) 4178 (setq-local lsp-buffer-uri nil)))) 4179 4180 (defun lsp-configure-buffer () 4181 "Configure LSP features for current buffer." 4182 ;; make sure the core is running in the context of all available workspaces 4183 ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context 4184 (let ((lsp--buffer-workspaces (cond 4185 (lsp--buffer-workspaces) 4186 (lsp--cur-workspace (list lsp--cur-workspace)))) 4187 lsp--cur-workspace) 4188 (when lsp-auto-configure 4189 (lsp--auto-configure) 4190 4191 (when (and lsp-enable-text-document-color 4192 (lsp-feature? "textDocument/documentColor")) 4193 (add-hook 'lsp-on-change-hook #'lsp--document-color nil t)) 4194 4195 (when (and lsp-enable-imenu 4196 (lsp-feature? "textDocument/documentSymbol")) 4197 (lsp-enable-imenu)) 4198 4199 (when (and lsp-enable-indentation 4200 (lsp-feature? "textDocument/rangeFormatting")) 4201 (add-function :override (local 'indent-region-function) #'lsp-format-region)) 4202 4203 (when (and lsp-enable-symbol-highlighting 4204 (lsp-feature? "textDocument/documentHighlight")) 4205 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)) 4206 4207 (when (and lsp-enable-links 4208 (lsp-feature? "textDocument/documentLink")) 4209 (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t)) 4210 4211 (when (and lsp-inlay-hint-enable 4212 (lsp-feature? "textDocument/inlayHint")) 4213 (lsp-inlay-hints-mode)) 4214 4215 (when (and lsp-enable-dap-auto-configure 4216 (functionp 'dap-mode)) 4217 (dap-auto-configure-mode 1))) 4218 (run-hooks 'lsp-configure-hook))) 4219 4220 (defun lsp-unconfig-buffer () 4221 "Unconfigure LSP features for buffer." 4222 (lsp--remove-overlays 'lsp-color) 4223 4224 (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function) 4225 (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index) 4226 (setq-local imenu-menubar-modified-tick 0) 4227 (setq-local imenu--index-alist nil) 4228 (imenu--cleanup)) 4229 4230 (remove-function (local 'indent-region-function) #'lsp-format-region) 4231 4232 (remove-hook 'lsp-on-change-hook #'lsp--document-color t) 4233 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4234 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4235 4236 (when (and lsp-enable-dap-auto-configure 4237 (functionp 'dap-mode)) 4238 (dap-auto-configure-mode -1)) 4239 4240 (run-hooks 'lsp-unconfigure-hook)) 4241 4242 (defun lsp--buffer-content () 4243 (lsp-save-restriction-and-excursion 4244 (or (lsp-virtual-buffer-call :buffer-string) 4245 (buffer-substring-no-properties (point-min) 4246 (point-max))))) 4247 4248 (defun lsp--text-document-did-open () 4249 "`document/didOpen' event." 4250 (run-hooks 'lsp-before-open-hook) 4251 (when (and lsp-auto-touch-files 4252 (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri))))) 4253 (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri)) 4254 (save-buffer)) 4255 4256 (setq lsp--cur-version (or lsp--cur-version 0)) 4257 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 4258 (lsp-notify 4259 "textDocument/didOpen" 4260 (list :textDocument 4261 (list :uri (lsp--buffer-uri) 4262 :languageId (lsp-buffer-language) 4263 :version lsp--cur-version 4264 :text (lsp--buffer-content)))) 4265 4266 (lsp-managed-mode 1) 4267 4268 (run-hooks 'lsp-after-open-hook) 4269 (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client)))) 4270 (-some-> (lsp--client-after-open-fn client) 4271 (funcall)) 4272 (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client)) 4273 (intern-soft) 4274 (run-hooks)))) 4275 4276 (defun lsp--text-document-identifier () 4277 "Make TextDocumentIdentifier." 4278 (list :uri (lsp--buffer-uri))) 4279 4280 (defun lsp--versioned-text-document-identifier () 4281 "Make VersionedTextDocumentIdentifier." 4282 (plist-put (lsp--text-document-identifier) :version lsp--cur-version)) 4283 4284 (defun lsp--cur-line (&optional point) 4285 (1- (line-number-at-pos point))) 4286 4287 (defun lsp--cur-position () 4288 "Make a Position object for the current point." 4289 (or (lsp-virtual-buffer-call :cur-position) 4290 (lsp-save-restriction-and-excursion 4291 (list :line (lsp--cur-line) 4292 :character (- (point) (line-beginning-position)))))) 4293 4294 (defun lsp--point-to-position (point) 4295 "Convert POINT to Position." 4296 (lsp-save-restriction-and-excursion 4297 (goto-char point) 4298 (lsp--cur-position))) 4299 4300 (defun lsp--range (start end) 4301 "Make Range body from START and END." 4302 ;; make sure start and end are Position objects 4303 (list :start start :end end)) 4304 4305 (defun lsp--region-to-range (start end) 4306 "Make Range object for the current region." 4307 (lsp--range (lsp--point-to-position start) 4308 (lsp--point-to-position end))) 4309 4310 (defun lsp--region-or-line () 4311 "The active region or the current line." 4312 (if (use-region-p) 4313 (lsp--region-to-range (region-beginning) (region-end)) 4314 (lsp--region-to-range (line-beginning-position) (line-end-position)))) 4315 4316 (defun lsp--check-document-changes-version (document-changes) 4317 "Verify that DOCUMENT-CHANGES have the proper version." 4318 (unless (seq-every-p 4319 (-lambda ((&TextDocumentEdit :text-document)) 4320 (or 4321 (not text-document) 4322 (let* ((filename (-> text-document 4323 lsp:versioned-text-document-identifier-uri 4324 lsp--uri-to-path)) 4325 (version (lsp:versioned-text-document-identifier-version? text-document))) 4326 (with-current-buffer (find-file-noselect filename) 4327 (or (null version) (zerop version) (= -1 version) 4328 (equal version lsp--cur-version)))))) 4329 document-changes) 4330 (error "Document changes cannot be applied due to different document version"))) 4331 4332 (defun lsp--apply-workspace-edit (workspace-edit &optional operation) 4333 "Apply the WorkspaceEdit object WORKSPACE-EDIT. 4334 OPERATION is symbol representing the source of this text edit." 4335 (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit)) 4336 (if-let ((document-changes (seq-reverse document-changes?))) 4337 (progn 4338 (lsp--check-document-changes-version document-changes) 4339 (->> document-changes 4340 (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create"))) 4341 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4342 (->> document-changes 4343 (seq-filter (-lambda ((&CreateFile :kind)) 4344 (and (or (not kind) (equal kind "edit")) 4345 (not (equal kind "create"))))) 4346 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4347 (->> document-changes 4348 (seq-filter (-lambda ((&CreateFile :kind)) 4349 (and (not (or (not kind) (equal kind "edit"))) 4350 (not (equal kind "create"))))) 4351 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))) 4352 (lsp-map 4353 (lambda (uri text-edits) 4354 (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect) 4355 (lsp--apply-text-edits text-edits operation))) 4356 changes?)))) 4357 4358 (defmacro lsp-with-filename (file &rest body) 4359 "Execute BODY with FILE as a context. 4360 Need to handle the case when FILE indicates virtual buffer." 4361 (declare (indent 1) (debug t)) 4362 `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file))) 4363 (lsp-with-current-buffer lsp--virtual-buffer 4364 ,@body) 4365 ,@body)) 4366 4367 (defun lsp--apply-text-document-edit (edit &optional operation) 4368 "Apply the TextDocumentEdit object EDIT. 4369 OPERATION is symbol representing the source of this text edit. 4370 If the file is not being visited by any buffer, it is opened with 4371 `find-file-noselect'. 4372 Because lsp-mode does not store previous document versions, the edit is only 4373 applied if the version of the textDocument matches the version of the 4374 corresponding file. 4375 4376 interface TextDocumentEdit { 4377 textDocument: VersionedTextDocumentIdentifier; 4378 edits: TextEdit[]; 4379 }" 4380 (pcase (lsp:edit-kind edit) 4381 ("create" (-let* (((&CreateFile :uri :options?) edit) 4382 (file-name (lsp--uri-to-path uri))) 4383 (mkdir (f-dirname file-name) t) 4384 (f-touch file-name) 4385 (when (lsp:create-file-options-overwrite? options?) 4386 (f-write-text "" nil file-name)) 4387 (find-file-noselect file-name))) 4388 ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit)) 4389 (f-delete (lsp--uri-to-path uri) recursive?))) 4390 ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit) 4391 (old-file-name (lsp--uri-to-path old-uri)) 4392 (new-file-name (lsp--uri-to-path new-uri)) 4393 (buf (find-buffer-visiting old-file-name))) 4394 (when buf 4395 (lsp-with-current-buffer buf 4396 (save-buffer) 4397 (lsp--text-document-did-close))) 4398 (mkdir (f-dirname new-file-name) t) 4399 (rename-file old-file-name new-file-name overwrite?) 4400 (when buf 4401 (lsp-with-current-buffer buf 4402 (set-buffer-modified-p nil) 4403 (setq lsp-buffer-uri nil) 4404 (set-visited-file-name new-file-name) 4405 (lsp))))) 4406 (_ (let ((file-name (->> edit 4407 (lsp:text-document-edit-text-document) 4408 (lsp:versioned-text-document-identifier-uri) 4409 (lsp--uri-to-path)))) 4410 (lsp-with-current-buffer (find-buffer-visiting file-name) 4411 (lsp-with-filename file-name 4412 (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation))))))) 4413 4414 (lsp-defun lsp--position-compare ((&Position :line left-line 4415 :character left-character) 4416 (&Position :line right-line 4417 :character right-character)) 4418 "Return t if position LEFT is greater than RIGHT." 4419 (if (= left-line right-line) 4420 (> left-character right-character) 4421 (> left-line right-line))) 4422 4423 (lsp-defun lsp-point-in-range? (position (&Range :start :end)) 4424 "Returns if POINT is in RANGE." 4425 (not (or (lsp--position-compare start position) 4426 (lsp--position-compare position end)))) 4427 4428 (lsp-defun lsp--position-equal ((&Position :line left-line 4429 :character left-character) 4430 (&Position :line right-line 4431 :character right-character)) 4432 "Return whether LEFT and RIGHT positions are equal." 4433 (and (= left-line right-line) 4434 (= left-character right-character))) 4435 4436 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end)) 4437 (&TextEdit :range (&Range :start right-start :end right-end))) 4438 (if (lsp--position-equal left-start right-start) 4439 (lsp--position-compare left-end right-end) 4440 (lsp--position-compare left-start right-start))) 4441 4442 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text)) 4443 "Apply the edits described in the TextEdit object in TEXT-EDIT." 4444 (setq new-text (s-replace "\r" "" (or new-text ""))) 4445 (lsp:set-text-edit-new-text edit new-text) 4446 (goto-char start) 4447 (delete-region start end) 4448 (insert new-text)) 4449 4450 ;; WORKAROUND: typescript-language might send -1 when applying code actions. 4451 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582 4452 (lsp-defun lsp--fix-point ((point &as &Position :character :line)) 4453 (-doto point 4454 (lsp:set-position-line (max 0 line)) 4455 (lsp:set-position-character (max 0 character)))) 4456 4457 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as 4458 &TextEdit 4459 :range (&Range :start :end) 4460 :new-text)) 4461 "Apply the edits described in the TextEdit object in TEXT-EDIT. 4462 The method uses `replace-buffer-contents'." 4463 (setq new-text (s-replace "\r" "" (or new-text ""))) 4464 (lsp:set-text-edit-new-text edit new-text) 4465 (-let* ((source (current-buffer)) 4466 ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start) 4467 :end (lsp--fix-point end))))) 4468 (with-temp-buffer 4469 (insert new-text) 4470 (let ((temp (current-buffer))) 4471 (with-current-buffer source 4472 (save-excursion 4473 (save-restriction 4474 (narrow-to-region beg end) 4475 4476 ;; On emacs versions < 26.2, 4477 ;; `replace-buffer-contents' is buggy - it calls 4478 ;; change functions with invalid arguments - so we 4479 ;; manually call the change functions here. 4480 ;; 4481 ;; See emacs bugs #32237, #32278: 4482 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237 4483 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278 4484 (let ((inhibit-modification-hooks t) 4485 (length (- end beg))) 4486 (run-hook-with-args 'before-change-functions 4487 beg end) 4488 (replace-buffer-contents temp) 4489 (run-hook-with-args 'after-change-functions 4490 beg (+ beg (length new-text)) 4491 length))))))))) 4492 4493 (defun lsp--to-yasnippet-snippet (snippet) 4494 "Convert LSP SNIPPET to yasnippet snippet." 4495 ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it. 4496 (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`"))) 4497 (rx "\\" (backref 1)) 4498 snippet 4499 nil nil 1)) 4500 4501 (defvar-local lsp-enable-relative-indentation nil 4502 "Enable relative indentation when insert texts, snippets ... 4503 from language server.") 4504 4505 (defun lsp--expand-snippet (snippet &optional start end expand-env) 4506 "Wrapper of `yas-expand-snippet' with all of it arguments. 4507 The snippet will be convert to LSP style and indent according to 4508 LSP server result." 4509 (require 'yasnippet nil t) 4510 (let* ((inhibit-field-text-motion t) 4511 (yas-wrap-around-region nil) 4512 (yas-indent-line 'none) 4513 (yas-also-auto-indent-first-line nil)) 4514 (yas-expand-snippet 4515 (lsp--to-yasnippet-snippet snippet) 4516 start end expand-env))) 4517 4518 (defun lsp--indent-lines (start end &optional insert-text-mode?) 4519 "Indent from START to END based on INSERT-TEXT-MODE? value. 4520 - When INSERT-TEXT-MODE? is provided 4521 - if it's `lsp/insert-text-mode-as-it', do no editor indentation. 4522 - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading 4523 whitespaces to match the line where text is inserted. 4524 - When it's not provided, using `indent-line-function' for each line." 4525 (save-excursion 4526 (goto-char end) 4527 (let* ((end-line (line-number-at-pos)) 4528 (offset (save-excursion 4529 (goto-char start) 4530 (current-indentation))) 4531 (indent-line-function 4532 (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it) 4533 #'ignore) 4534 ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation) 4535 lsp-enable-relative-indentation 4536 ;; Indenting snippets is extremely slow in `org-mode' buffers 4537 ;; since it has to calculate indentation based on SRC block 4538 ;; position. Thus we use relative indentation as default. 4539 (derived-mode-p 'org-mode)) 4540 (lambda () (save-excursion 4541 (beginning-of-line) 4542 (indent-to-column offset)))) 4543 (t indent-line-function)))) 4544 (goto-char start) 4545 (forward-line) 4546 (while (and (not (eobp)) 4547 (<= (line-number-at-pos) end-line)) 4548 (funcall indent-line-function) 4549 (forward-line))))) 4550 4551 (defun lsp--apply-text-edits (edits &optional operation) 4552 "Apply the EDITS described in the TextEdit[] object. 4553 OPERATION is symbol representing the source of this text edit." 4554 (unless (seq-empty-p edits) 4555 (atomic-change-group 4556 (run-hooks 'lsp-before-apply-edits-hook) 4557 (let* ((change-group (prepare-change-group)) 4558 (howmany (length edits)) 4559 (message (format "Applying %s edits to `%s' ..." howmany (current-buffer))) 4560 (_ (lsp--info message)) 4561 (reporter (make-progress-reporter message 0 howmany)) 4562 (done 0) 4563 (apply-edit (if (not lsp--virtual-buffer) 4564 #'lsp--apply-text-edit-replace-buffer-contents 4565 #'lsp--apply-text-edit))) 4566 (unwind-protect 4567 (->> edits 4568 ;; We sort text edits so as to apply edits that modify latter 4569 ;; parts of the document first. Furthermore, because the LSP 4570 ;; spec dictates that: "If multiple inserts have the same 4571 ;; position, the order in the array defines which edit to 4572 ;; apply first." We reverse the initial list and sort stably 4573 ;; to make sure the order among edits with the same position 4574 ;; is preserved. 4575 (nreverse) 4576 (seq-sort #'lsp--text-edit-sort-predicate) 4577 (mapc (lambda (edit) 4578 (progress-reporter-update reporter (cl-incf done)) 4579 (funcall apply-edit edit) 4580 (when (lsp:snippet-text-edit-insert-text-format? edit) 4581 (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start) 4582 :insert-text-format? :new-text) edit) 4583 (when (eq insert-text-format? lsp/insert-text-format-snippet) 4584 ;; No `save-excursion' needed since expand snippet will change point anyway 4585 (goto-char (+ start (length new-text))) 4586 (lsp--indent-lines start (point)) 4587 (lsp--expand-snippet new-text start (point))))) 4588 (run-hook-with-args 'lsp-after-apply-edits-hook operation)))) 4589 (undo-amalgamate-change-group change-group) 4590 (progress-reporter-done reporter)))))) 4591 4592 (defun lsp--create-apply-text-edits-handlers () 4593 "Create (handler cleanup-fn) for applying text edits in async request. 4594 Only works when mode is `tick or `alive." 4595 (let* (first-edited 4596 (func (lambda (start &rest _) 4597 (setq first-edited (if first-edited 4598 (min start first-edited) 4599 start))))) 4600 (add-hook 'before-change-functions func nil t) 4601 (list 4602 (lambda (edits) 4603 (if (and first-edited 4604 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end))) 4605 ;; Text edit region is overlapped 4606 (> end first-edited)) 4607 edits)) 4608 (lsp--warn "TextEdits will not be applied since document has been modified before of them.") 4609 (lsp--apply-text-edits edits 'completion-cleanup))) 4610 (lambda () 4611 (remove-hook 'before-change-functions func t))))) 4612 4613 (defun lsp--capability (cap &optional capabilities) 4614 "Get the value of capability CAP. If CAPABILITIES is non-nil, use them instead." 4615 (when (stringp cap) 4616 (setq cap (intern (concat ":" cap)))) 4617 4618 (lsp-get (or capabilities 4619 (lsp--server-capabilities)) 4620 cap)) 4621 4622 (defun lsp--registered-capability (method) 4623 "Check whether there is workspace providing METHOD." 4624 (->> (lsp-workspaces) 4625 (--keep (seq-find (lambda (reg) 4626 (equal (lsp--registered-capability-method reg) method)) 4627 (lsp--workspace-registered-server-capabilities it))) 4628 cl-first)) 4629 4630 (defun lsp--capability-for-method (method) 4631 "Get the value of capability for METHOD." 4632 (-let* ((reqs (cdr (assoc method lsp-method-requirements))) 4633 ((&plist :capability) reqs)) 4634 (or (and capability (lsp--capability capability)) 4635 (-some-> (lsp--registered-capability method) 4636 (lsp--registered-capability-options))))) 4637 4638 (defvar-local lsp--before-change-vals nil 4639 "Store the positions from the `lsp-before-change' function call, for 4640 validation and use in the `lsp-on-change' function.") 4641 4642 (defun lsp--text-document-content-change-event (start end length) 4643 "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH." 4644 ;; So (47 54 0) means add 7 chars starting at pos 47 4645 ;; must become 4646 ;; {"range":{"start":{"line":5,"character":6} 4647 ;; ,"end" :{"line":5,"character":6}} 4648 ;; ,"rangeLength":0 4649 ;; ,"text":"\nbb = 5"} 4650 ;; 4651 ;; And (47 47 7) means delete 7 chars starting at pos 47 4652 ;; must become 4653 ;; {"range":{"start":{"line":6,"character":0} 4654 ;; ,"end" :{"line":7,"character":0}} 4655 ;; ,"rangeLength":7 4656 ;; ,"text":""} 4657 ;; 4658 ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with 4659 ;; 13 chars. So it must become 4660 ;; {"range":{"start":{"line":5,"character":8} 4661 ;; ,"end" :{"line":5,"character":11}} 4662 ;; ,"rangeLength":3 4663 ;; ,"text":"new-chars-xxx"} 4664 ;; 4665 4666 ;; Adding text: 4667 ;; lsp-before-change:(start,end)=(33,33) 4668 ;; lsp-on-change:(start,end,length)=(33,34,0) 4669 ;; 4670 ;; Changing text: 4671 ;; lsp-before-change:(start,end)=(208,211) 4672 ;; lsp-on-change:(start,end,length)=(208,221,3) 4673 ;; 4674 ;; Deleting text: 4675 ;; lsp-before-change:(start,end)=(19,27) 4676 ;; lsp-on-change:(start,end,length)=(19,19,8) 4677 (if (zerop length) 4678 ;; Adding something only, work from start only 4679 `( :range ,(lsp--range 4680 (lsp--point-to-position start) 4681 (lsp--point-to-position start)) 4682 :rangeLength 0 4683 :text ,(buffer-substring-no-properties start end)) 4684 4685 (if (eq start end) 4686 ;; Deleting something only 4687 (if (lsp--bracketed-change-p start length) 4688 ;; The before-change value is bracketed, use it 4689 `( :range ,(lsp--range 4690 (lsp--point-to-position start) 4691 (plist-get lsp--before-change-vals :end-pos)) 4692 :rangeLength ,length 4693 :text "") 4694 ;; If the change is not bracketed, send a full change event instead. 4695 (lsp--full-change-event)) 4696 4697 ;; Deleting some things, adding others 4698 (if (lsp--bracketed-change-p start length) 4699 ;; The before-change value is valid, use it 4700 `( :range ,(lsp--range 4701 (lsp--point-to-position start) 4702 (plist-get lsp--before-change-vals :end-pos)) 4703 :rangeLength ,length 4704 :text ,(buffer-substring-no-properties start end)) 4705 (lsp--full-change-event))))) 4706 4707 (defun lsp--bracketed-change-p (start length) 4708 "If the before and after positions are the same, and the length 4709 is the size of the start range, we are probably good." 4710 (-let [(&plist :end before-end :start before-start) lsp--before-change-vals] 4711 (and (eq start before-start) 4712 (eq length (- before-end before-start))))) 4713 4714 (defun lsp--full-change-event () 4715 `(:text ,(lsp--buffer-content))) 4716 4717 (defun lsp-before-change (start end) 4718 "Executed before a file is changed. 4719 Added to `before-change-functions'." 4720 ;; Note: 4721 ;; 4722 ;; This variable holds a list of functions to call when Emacs is about to 4723 ;; modify a buffer. Each function gets two arguments, the beginning and end of 4724 ;; the region that is about to change, represented as integers. The buffer 4725 ;; that is about to change is always the current buffer when the function is 4726 ;; called. 4727 ;; 4728 ;; WARNING: 4729 ;; 4730 ;; Do not expect the before-change hooks and the after-change hooks be called 4731 ;; in balanced pairs around each buffer change. Also don't expect the 4732 ;; before-change hooks to be called for every chunk of text Emacs is about to 4733 ;; delete. These hooks are provided on the assumption that Lisp programs will 4734 ;; use either before- or the after-change hooks, but not both, and the 4735 ;; boundaries of the region where the changes happen might include more than 4736 ;; just the actual changed text, or even lump together several changes done 4737 ;; piecemeal. 4738 (save-match-data 4739 (lsp-save-restriction-and-excursion 4740 (setq lsp--before-change-vals 4741 (list :start start 4742 :end end 4743 :end-pos (lsp--point-to-position end)))))) 4744 4745 (defun lsp--flush-delayed-changes () 4746 (let ((inhibit-quit t)) 4747 (when lsp--delay-timer 4748 (cancel-timer lsp--delay-timer)) 4749 (mapc (-lambda ((workspace buffer document change)) 4750 (with-current-buffer buffer 4751 (with-lsp-workspace workspace 4752 (lsp-notify "textDocument/didChange" 4753 (list :textDocument document 4754 :contentChanges (vector change)))))) 4755 (prog1 (nreverse lsp--delayed-requests) 4756 (setq lsp--delayed-requests nil))))) 4757 4758 (defun lsp--workspace-sync-method (workspace) 4759 (let ((sync (-> workspace 4760 (lsp--workspace-server-capabilities) 4761 (lsp:server-capabilities-text-document-sync?)))) 4762 (if (lsp-text-document-sync-options? sync) 4763 (lsp:text-document-sync-options-change? sync) 4764 sync))) 4765 4766 (defun lsp-on-change (start end length &optional content-change-event-fn) 4767 "Executed when a file is changed. 4768 Added to `after-change-functions'." 4769 ;; Note: 4770 ;; 4771 ;; Each function receives three arguments: the beginning and end of the region 4772 ;; just changed, and the length of the text that existed before the change. 4773 ;; All three arguments are integers. The buffer that has been changed is 4774 ;; always the current buffer when the function is called. 4775 ;; 4776 ;; The length of the old text is the difference between the buffer positions 4777 ;; before and after that text as it was before the change. As for the 4778 ;; changed text, its length is simply the difference between the first two 4779 ;; arguments. 4780 ;; 4781 ;; So (47 54 0) means add 7 chars starting at pos 47 4782 ;; So (47 47 7) means delete 7 chars starting at pos 47 4783 (save-match-data 4784 (let ((inhibit-quit t) 4785 ;; make sure that `lsp-on-change' is called in multi-workspace context 4786 ;; see #2901 4787 lsp--cur-workspace) 4788 ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done 4789 ;; by auto-revert-mode) will cause this handler to get called with a nil 4790 ;; buffer-file-name. We need the buffer-file-name to send notifications; 4791 ;; so we skip handling revert-buffer-caused changes and instead handle 4792 ;; reverts separately in lsp-on-revert 4793 (when (not revert-buffer-in-progress-p) 4794 (cl-incf lsp--cur-version) 4795 (mapc 4796 (lambda (workspace) 4797 (pcase (or lsp-document-sync-method 4798 (lsp--workspace-sync-method workspace)) 4799 (1 4800 (if lsp-debounce-full-sync-notifications 4801 (setq lsp--delayed-requests 4802 (->> lsp--delayed-requests 4803 (-remove (-lambda ((_ buffer)) 4804 (equal (current-buffer) buffer))) 4805 (cons (list workspace 4806 (current-buffer) 4807 (lsp--versioned-text-document-identifier) 4808 (lsp--full-change-event))))) 4809 (with-lsp-workspace workspace 4810 (lsp-notify "textDocument/didChange" 4811 (list :contentChanges (vector (lsp--full-change-event)) 4812 :textDocument (lsp--versioned-text-document-identifier)))))) 4813 (2 4814 (with-lsp-workspace workspace 4815 (lsp-notify 4816 "textDocument/didChange" 4817 (list :textDocument (lsp--versioned-text-document-identifier) 4818 :contentChanges (vector 4819 (if content-change-event-fn 4820 (funcall content-change-event-fn start end length) 4821 (lsp--text-document-content-change-event 4822 start end length))))))))) 4823 (lsp-workspaces)) 4824 (when lsp--delay-timer (cancel-timer lsp--delay-timer)) 4825 (setq lsp--delay-timer (run-with-idle-timer 4826 lsp-debounce-full-sync-notifications-interval 4827 nil 4828 #'lsp--flush-delayed-changes)) 4829 ;; force cleanup overlays after each change 4830 (lsp--remove-overlays 'lsp-highlight) 4831 (lsp--after-change (current-buffer)) 4832 (setq lsp--signature-last-index nil 4833 lsp--signature-last nil) 4834 ;; cleanup diagnostics 4835 (when lsp-diagnostic-clean-after-change 4836 (lsp-foreach-workspace 4837 (-let [diagnostics (lsp--workspace-diagnostics lsp--cur-workspace)] 4838 (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics)))))))) 4839 4840 4841 4842 ;; facilities for on change hooks. We do not want to make lsp calls on each 4843 ;; change event so we add debounce to avoid flooding the server with events. 4844 ;; Additionally, we want to have a mechanism for stopping the server calls in 4845 ;; particular cases like, e. g. when performing completion. 4846 4847 (defvar lsp-inhibit-lsp-hooks nil 4848 "Flag to control.") 4849 4850 (defcustom lsp-on-change-hook nil 4851 "Hooks to run when buffer has changed." 4852 :type 'hook 4853 :group 'lsp-mode) 4854 4855 (defcustom lsp-idle-delay 0.500 4856 "Debounce interval for `after-change-functions'." 4857 :type 'number 4858 :group 'lsp-mode) 4859 4860 (defcustom lsp-on-idle-hook nil 4861 "Hooks to run after `lsp-idle-delay'." 4862 :type 'hook 4863 :group 'lsp-mode) 4864 4865 (defun lsp--idle-reschedule (buffer) 4866 (when lsp--on-idle-timer 4867 (cancel-timer lsp--on-idle-timer)) 4868 4869 (setq lsp--on-idle-timer (run-with-idle-timer 4870 lsp-idle-delay 4871 nil 4872 #'lsp--on-idle 4873 buffer))) 4874 4875 (defun lsp--post-command () 4876 (lsp--cleanup-highlights-if-needed) 4877 (lsp--idle-reschedule (current-buffer))) 4878 4879 (defun lsp--on-idle (buffer) 4880 "Start post command loop." 4881 (when (and (buffer-live-p buffer) 4882 (equal buffer (current-buffer)) 4883 (not lsp-inhibit-lsp-hooks) 4884 lsp-managed-mode) 4885 (run-hooks 'lsp-on-idle-hook))) 4886 4887 (defun lsp--on-change-debounce (buffer) 4888 (when (and (buffer-live-p buffer) 4889 (equal buffer (current-buffer)) 4890 (not lsp-inhibit-lsp-hooks) 4891 lsp-managed-mode) 4892 (run-hooks 'lsp-on-change-hook))) 4893 4894 (defun lsp--after-change (buffer) 4895 (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled) 4896 (lsp--semantic-tokens-refresh-if-enabled buffer)) 4897 (when lsp--on-change-timer 4898 (cancel-timer lsp--on-change-timer)) 4899 (setq lsp--on-change-timer (run-with-idle-timer 4900 lsp-idle-delay 4901 nil 4902 #'lsp--on-change-debounce 4903 buffer)) 4904 (lsp--idle-reschedule buffer)) 4905 4906 4907 (defcustom lsp-trim-trailing-whitespace t 4908 "Trim trailing whitespace on a line." 4909 :group 'lsp-mode 4910 :type 'boolean) 4911 4912 (defcustom lsp-insert-final-newline t 4913 "Insert a newline character at the end of the file if one does not exist." 4914 :group 'lsp-mode 4915 :type 'boolean) 4916 4917 (defcustom lsp-trim-final-newlines t 4918 "Trim all newlines after the final newline at the end of the file." 4919 :group 'lsp-mode 4920 :type 'boolean) 4921 4922 4923 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters) 4924 "Self insert handling. 4925 Applies on type formatting." 4926 (let ((ch last-command-event)) 4927 (when (or (eq (string-to-char first-trigger-characters) ch) 4928 (cl-find ch more-trigger-characters :key #'string-to-char)) 4929 (lsp-request-async "textDocument/onTypeFormatting" 4930 (lsp-make-document-on-type-formatting-params 4931 :text-document (lsp--text-document-identifier) 4932 :options (lsp-make-formatting-options 4933 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 4934 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 4935 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 4936 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 4937 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)) 4938 :ch (char-to-string ch) 4939 :position (lsp--cur-position)) 4940 (lambda (data) (lsp--apply-text-edits data 'format)) 4941 :mode 'tick)))) 4942 4943 4944 ;; links 4945 (defun lsp--document-links () 4946 (when (lsp-feature? "textDocument/documentLink") 4947 (lsp-request-async 4948 "textDocument/documentLink" 4949 `(:textDocument ,(lsp--text-document-identifier)) 4950 (lambda (links) 4951 (lsp--remove-overlays 'lsp-link) 4952 (seq-do 4953 (-lambda ((link &as &DocumentLink :range (&Range :start :end))) 4954 (-doto (make-button (lsp--position-to-point start) 4955 (lsp--position-to-point end) 4956 'action (lsp--document-link-keymap link) 4957 'keymap (let ((map (make-sparse-keymap))) 4958 (define-key map [M-return] 'push-button) 4959 (define-key map [mouse-2] 'push-button) 4960 map) 4961 'help-echo "mouse-2, M-RET: Visit this link") 4962 (overlay-put 'lsp-link t))) 4963 links)) 4964 :mode 'unchanged))) 4965 4966 (defun lsp--document-link-handle-target (url) 4967 (let* ((parsed-url (url-generic-parse-url (url-unhex-string url))) 4968 (type (url-type parsed-url))) 4969 (pcase type 4970 ("file" 4971 (xref-push-marker-stack) 4972 (find-file (lsp--uri-to-path url)) 4973 (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url)) 4974 (goto-char (lsp--position-to-point 4975 (lsp-make-position :character (1- (string-to-number column)) 4976 :line (1- (string-to-number line))))))) 4977 ((or "http" "https") (browse-url url)) 4978 (type (if-let ((handler (lsp--get-uri-handler type))) 4979 (funcall handler url) 4980 (signal 'lsp-file-scheme-not-supported (list url))))))) 4981 4982 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?)) 4983 (if target? 4984 (lambda (_) 4985 (interactive) 4986 (lsp--document-link-handle-target target?)) 4987 (lambda (_) 4988 (interactive) 4989 (when (lsp:document-link-registration-options-resolve-provider? 4990 (lsp--capability-for-method "textDocument/documentLink")) 4991 (lsp-request-async 4992 "documentLink/resolve" 4993 link 4994 (-lambda ((&DocumentLink :target?)) 4995 (lsp--document-link-handle-target target?))))))) 4996 4997 4998 4999 (defcustom lsp-warn-no-matched-clients t 5000 "Whether to show messages when there are no supported clients." 5001 :group 'lsp-mode 5002 :type 'boolean) 5003 5004 (defun lsp-buffer-language--configured-id () 5005 "Return nil when not registered." 5006 (->> lsp-language-id-configuration 5007 (-first 5008 (-lambda ((mode-or-pattern . language)) 5009 (cond 5010 ((and (stringp mode-or-pattern) 5011 (s-matches? mode-or-pattern (buffer-file-name))) 5012 language) 5013 ((eq mode-or-pattern major-mode) language)))) 5014 cl-rest)) 5015 5016 (defvar-local lsp--buffer-language nil 5017 "Locally cached returned value of `lsp-buffer-language'.") 5018 5019 (defun lsp-buffer-language () 5020 "Get language corresponding current buffer." 5021 (or lsp--buffer-language 5022 (let* ((configured-language (lsp-buffer-language--configured-id))) 5023 (setq lsp--buffer-language 5024 (or configured-language 5025 ;; ensure non-nil 5026 (string-remove-suffix "-mode" (symbol-name major-mode)))) 5027 (when (and lsp-warn-no-matched-clients 5028 (null configured-language)) 5029 (lsp-warn "Unable to calculate the languageId for buffer `%s'. \ 5030 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s" 5031 (buffer-name) 5032 major-mode)) 5033 lsp--buffer-language))) 5034 5035 (defun lsp-activate-on (&rest languages) 5036 "Returns language activation function. 5037 The function will return t when the `lsp-buffer-language' returns 5038 one of the LANGUAGES." 5039 (lambda (_file-name _mode) 5040 (-contains? languages (lsp-buffer-language)))) 5041 5042 (defun lsp-workspace-root (&optional path) 5043 "Find the workspace root for the current file or PATH." 5044 (-when-let* ((file-name (or path (buffer-file-name))) 5045 (file-name (lsp-f-canonical file-name))) 5046 (->> (lsp-session) 5047 (lsp-session-folders) 5048 (--filter (and (lsp--files-same-host it file-name) 5049 (or (lsp-f-ancestor-of? it file-name) 5050 (equal it file-name)))) 5051 (--max-by (> (length it) (length other)))))) 5052 5053 (defun lsp-on-revert () 5054 "Executed when a file is reverted. 5055 Added to `after-revert-hook'." 5056 (let ((n (buffer-size)) 5057 (revert-buffer-in-progress-p nil)) 5058 (lsp-on-change 0 n n))) 5059 5060 (defun lsp--text-document-did-close (&optional keep-workspace-alive) 5061 "Executed when the file is closed, added to `kill-buffer-hook'. 5062 5063 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace 5064 if it's closing the last buffer in the workspace." 5065 (lsp-foreach-workspace 5066 (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 5067 (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S" 5068 (lsp-notify "textDocument/didClose" 5069 `(:textDocument ,(lsp--text-document-identifier)))) 5070 (when (and (not lsp-keep-workspace-alive) 5071 (not keep-workspace-alive) 5072 (not (lsp--workspace-buffers lsp--cur-workspace))) 5073 (lsp--shutdown-workspace)))) 5074 5075 (defun lsp--will-save-text-document-params (reason) 5076 (list :textDocument (lsp--text-document-identifier) 5077 :reason reason)) 5078 5079 (defun lsp--before-save () 5080 "Before save handler." 5081 (with-demoted-errors "Error in ‘lsp--before-save’: %S" 5082 (let ((params (lsp--will-save-text-document-params 1))) 5083 (when (lsp--send-will-save-p) 5084 (lsp-notify "textDocument/willSave" params)) 5085 (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits) 5086 (let ((lsp-response-timeout 0.1)) 5087 (condition-case nil 5088 (lsp--apply-text-edits 5089 (lsp-request "textDocument/willSaveWaitUntil" 5090 params) 5091 'before-save) 5092 (error))))))) 5093 5094 (defun lsp--on-auto-save () 5095 "Handler for auto-save." 5096 (when (lsp--send-will-save-p) 5097 (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S" 5098 (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2))))) 5099 5100 (defun lsp--text-document-did-save () 5101 "Executed when the file is closed, added to `after-save-hook''." 5102 (when (lsp--send-did-save-p) 5103 (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’" 5104 (lsp-notify "textDocument/didSave" 5105 `( :textDocument ,(lsp--versioned-text-document-identifier) 5106 ,@(when (lsp--save-include-text-p) 5107 (list :text (lsp--buffer-content)))))))) 5108 5109 (defun lsp--text-document-position-params (&optional identifier position) 5110 "Make TextDocumentPositionParams for the current point in the current document. 5111 If IDENTIFIER and POSITION are non-nil, they will be used as the document 5112 identifier and the position respectively." 5113 (list :textDocument (or identifier (lsp--text-document-identifier)) 5114 :position (or position (lsp--cur-position)))) 5115 5116 (defun lsp--get-buffer-diagnostics () 5117 "Return buffer diagnostics." 5118 (gethash (or 5119 (plist-get lsp--virtual-buffer :buffer-file-name) 5120 (lsp--fix-path-casing (buffer-file-name))) 5121 (lsp-diagnostics t))) 5122 5123 (defun lsp-cur-line-diagnostics () 5124 "Return any diagnostics that apply to the current line." 5125 (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)] 5126 (cl-coerce (-filter 5127 (-lambda ((&Diagnostic :range (&Range :start (&Position :line)))) 5128 (and (>= line start) (<= line end))) 5129 (lsp--get-buffer-diagnostics)) 5130 'vector))) 5131 5132 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end) 5133 (right &as &Range :start right-start :end right-end)) 5134 (or (lsp-point-in-range? right-start left) 5135 (lsp-point-in-range? right-end left) 5136 (lsp-point-in-range? left-start right) 5137 (lsp-point-in-range? left-end right))) 5138 5139 (defun lsp-make-position-1 (position) 5140 (lsp-make-position :line (plist-get position :line) 5141 :character (plist-get position :character))) 5142 5143 (defun lsp-cur-possition-diagnostics () 5144 "Return any diagnostics that apply to the current line." 5145 (-let* ((start (if (use-region-p) (region-beginning) (point))) 5146 (end (if (use-region-p) (region-end) (point))) 5147 (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start)) 5148 :end (lsp-make-position-1 (lsp-point-to-position end))))) 5149 (->> (lsp--get-buffer-diagnostics) 5150 (-filter 5151 (-lambda ((&Diagnostic :range)) 5152 (lsp-range-overlapping? range current-range))) 5153 (apply 'vector)))) 5154 5155 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics) 5156 5157 (defun lsp--extract-line-from-buffer (pos) 5158 "Return the line pointed to by POS (a Position object) in the current buffer." 5159 (let* ((point (lsp--position-to-point pos)) 5160 (inhibit-field-text-motion t)) 5161 (save-excursion 5162 (goto-char point) 5163 (buffer-substring (line-beginning-position) (line-end-position))))) 5164 5165 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line) 5166 :end (end &as &Position :character end-char))) 5167 "Return a xref-item from a RANGE in FILENAME." 5168 (let* ((line (lsp--extract-line-from-buffer start)) 5169 (len (length line))) 5170 (add-face-text-property (max (min start-char len) 0) 5171 (max (min end-char len) 0) 5172 'xref-match t line) 5173 ;; LINE is nil when FILENAME is not being current visited by any buffer. 5174 (xref-make-match (or line filename) 5175 (xref-make-file-location 5176 filename 5177 (lsp-translate-line (1+ start-line)) 5178 (lsp-translate-column start-char)) 5179 (- end-char start-char)))) 5180 5181 (defun lsp--location-uri (loc) 5182 (if (lsp-location? loc) 5183 (lsp:location-uri loc) 5184 (lsp:location-link-target-uri loc))) 5185 5186 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start))) 5187 "Go to location." 5188 (let ((path (lsp--uri-to-path uri))) 5189 (if (f-exists? path) 5190 (with-current-buffer (find-file path) 5191 (goto-char (lsp--position-to-point start))) 5192 (error "There is no file %s" path)))) 5193 5194 (defun lsp--location-range (loc) 5195 (if (lsp-location? loc) 5196 (lsp:location-range loc) 5197 (lsp:location-link-target-selection-range loc))) 5198 5199 (defun lsp--locations-to-xref-items (locations) 5200 "Return a list of `xref-item' given LOCATIONS, which can be of 5201 type Location, LocationLink, Location[] or LocationLink[]." 5202 (setq locations 5203 (pcase locations 5204 ((seq (or (Location) 5205 (LocationLink))) 5206 (append locations nil)) 5207 ((or (Location) 5208 (LocationLink)) 5209 (list locations)))) 5210 5211 (cl-labels ((get-xrefs-in-file 5212 (file-locs) 5213 (-let [(filename . matches) file-locs] 5214 (condition-case err 5215 (let ((visiting (find-buffer-visiting filename)) 5216 (fn (lambda (loc) 5217 (lsp-with-filename filename 5218 (lsp--xref-make-item filename 5219 (lsp--location-range loc)))))) 5220 (if visiting 5221 (with-current-buffer visiting 5222 (seq-map fn matches)) 5223 (when (file-readable-p filename) 5224 (with-temp-buffer 5225 (insert-file-contents-literally filename) 5226 (seq-map fn matches))))) 5227 (error (lsp-warn "Failed to process xref entry for filename '%s': %s" 5228 filename (error-message-string err))) 5229 (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s" 5230 filename (error-message-string err))))))) 5231 5232 (->> locations 5233 (seq-sort #'lsp--location-before-p) 5234 (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri)) 5235 (seq-map #'get-xrefs-in-file) 5236 (apply #'nconc)))) 5237 5238 (defun lsp--location-before-p (left right) 5239 "Sort first by file, then by line, then by column." 5240 (let ((left-uri (lsp--location-uri left)) 5241 (right-uri (lsp--location-uri right))) 5242 (if (not (string= left-uri right-uri)) 5243 (string< left-uri right-uri) 5244 (-let (((&Range :start left-start) (lsp--location-range left)) 5245 ((&Range :start right-start) (lsp--location-range right))) 5246 (lsp--position-compare right-start left-start))))) 5247 5248 (defun lsp--make-reference-params (&optional td-position exclude-declaration) 5249 "Make a ReferenceParam object. 5250 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead. 5251 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." 5252 (let ((json-false :json-false)) 5253 (plist-put (or td-position (lsp--text-document-position-params)) 5254 :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration)))))) 5255 5256 (defun lsp--cancel-request (id) 5257 "Cancel request with ID in all workspaces." 5258 (lsp-foreach-workspace 5259 (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id)) 5260 (lsp-notify "$/cancelRequest" `(:id ,id)))) 5261 5262 (defvar-local lsp--hover-saved-bounds nil) 5263 5264 (defun lsp-eldoc-function (cb &rest _ignored) 5265 "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')." 5266 (if (and lsp--hover-saved-bounds 5267 (lsp--point-in-bounds-p lsp--hover-saved-bounds)) 5268 lsp--eldoc-saved-message 5269 (setq lsp--hover-saved-bounds nil 5270 lsp--eldoc-saved-message nil) 5271 (if (looking-at-p "[[:space:]\n]") 5272 (setq lsp--eldoc-saved-message nil) ; And returns nil. 5273 (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover")) 5274 (lsp-request-async 5275 "textDocument/hover" 5276 (lsp--text-document-position-params) 5277 (-lambda ((hover &as &Hover? :range? :contents)) 5278 (setq lsp--hover-saved-bounds (when range? 5279 (lsp--range-to-region range?))) 5280 (funcall cb (setq lsp--eldoc-saved-message 5281 (when contents 5282 (lsp--render-on-hover-content 5283 contents 5284 lsp-eldoc-render-all))))) 5285 :error-handler #'ignore 5286 :mode 'tick 5287 :cancel-token :eldoc-hover))))) 5288 5289 (defun lsp--point-on-highlight? () 5290 (-some? (lambda (overlay) 5291 (overlay-get overlay 'lsp-highlight)) 5292 (overlays-at (point)))) 5293 5294 (defun lsp--cleanup-highlights-if-needed () 5295 (when (and lsp-enable-symbol-highlighting 5296 lsp--have-document-highlights 5297 (not (lsp--point-on-highlight?))) 5298 (lsp--remove-overlays 'lsp-highlight) 5299 (setq lsp--have-document-highlights nil) 5300 (lsp-cancel-request-by-token :highlights))) 5301 5302 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil 5303 "The bounds of the symbol from which `lsp--document-highlight' 5304 most recently requested highlights.") 5305 5306 (defun lsp--document-highlight () 5307 (when (lsp-feature? "textDocument/documentHighlight") 5308 (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol))) 5309 (unless (or (looking-at-p "[[:space:]\n]") 5310 (not lsp-enable-symbol-highlighting) 5311 (and lsp--have-document-highlights 5312 curr-sym-bounds 5313 (equal curr-sym-bounds 5314 lsp--symbol-bounds-of-last-highlight-invocation))) 5315 (setq lsp--symbol-bounds-of-last-highlight-invocation 5316 curr-sym-bounds) 5317 (lsp-request-async "textDocument/documentHighlight" 5318 (lsp--text-document-position-params) 5319 #'lsp--document-highlight-callback 5320 :mode 'tick 5321 :cancel-token :highlights))))) 5322 5323 (defun lsp--help-open-link (&rest _) 5324 "Open markdown link at point via mouse or keyboard." 5325 (interactive "P") 5326 (let ((buffer-list-update-hook nil)) 5327 (-let [(buffer point) (if-let* ((valid (and (listp last-input-event) 5328 (eq (car last-input-event) 'mouse-2))) 5329 (event (cadr last-input-event)) 5330 (win (posn-window event)) 5331 (buffer (window-buffer win))) 5332 `(,buffer ,(posn-point event)) 5333 `(,(current-buffer) ,(point)))] 5334 (with-current-buffer buffer 5335 (when-let* ((face (get-text-property point 'face)) 5336 (url (or (and (eq face 'markdown-link-face) 5337 (get-text-property point 'help-echo)) 5338 (and (memq face '(markdown-url-face markdown-plain-url-face)) 5339 (nth 3 (markdown-link-at-pos point)))))) 5340 (lsp--document-link-handle-target url)))))) 5341 5342 (defvar lsp-help-mode-map 5343 (-doto (make-sparse-keymap) 5344 (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link)) 5345 "Keymap for `lsp-help-mode'.") 5346 5347 (define-derived-mode lsp-help-mode help-mode "LspHelp" 5348 "Major mode for displaying lsp help.") 5349 5350 (defun lsp-describe-thing-at-point () 5351 "Display the type signature and documentation of the thing at point." 5352 (interactive) 5353 (let ((contents (-some->> (lsp--text-document-position-params) 5354 (lsp--make-request "textDocument/hover") 5355 (lsp--send-request) 5356 (lsp:hover-contents)))) 5357 (if (and contents (not (equal contents ""))) 5358 (let ((lsp-help-buf-name "*lsp-help*")) 5359 (with-current-buffer (get-buffer-create lsp-help-buf-name) 5360 (delay-mode-hooks 5361 (lsp-help-mode) 5362 (with-help-window lsp-help-buf-name 5363 (insert (string-trim-right (lsp--render-on-hover-content contents t))))) 5364 (run-mode-hooks))) 5365 (lsp--info "No content at point.")))) 5366 5367 (defun lsp--point-in-bounds-p (bounds) 5368 "Return whether the current point is within BOUNDS." 5369 (and (<= (car bounds) (point)) (< (point) (cdr bounds)))) 5370 5371 (defun lsp-get-renderer (language) 5372 "Get renderer for LANGUAGE." 5373 (lambda (str) 5374 (lsp--render-string str language))) 5375 5376 (defun lsp--setup-markdown (mode) 5377 "Setup the ‘markdown-mode’ in the frame. 5378 MODE is the mode used in the parent frame." 5379 (make-local-variable 'markdown-code-lang-modes) 5380 (dolist (mark (alist-get mode lsp-custom-markup-modes)) 5381 (add-to-list 'markdown-code-lang-modes (cons mark mode))) 5382 (setq-local markdown-fontify-code-blocks-natively t) 5383 (setq-local markdown-fontify-code-block-default-mode mode) 5384 (setq-local markdown-hide-markup t) 5385 5386 ;; Render some common HTML entities. 5387 ;; This should really happen in markdown-mode instead, 5388 ;; but it doesn't, so we do it here for now. 5389 (setq prettify-symbols-alist 5390 (cl-loop for i from 0 to 255 5391 collect (cons (format "&#x%02X;" i) i))) 5392 (push '("<" . ?<) prettify-symbols-alist) 5393 (push '(">" . ?>) prettify-symbols-alist) 5394 (push '("&" . ?&) prettify-symbols-alist) 5395 (push '(" " . ? ) prettify-symbols-alist) 5396 (setq prettify-symbols-compose-predicate 5397 (lambda (_start _end _match) t)) 5398 (prettify-symbols-mode 1)) 5399 5400 (defvar lsp-help-link-keymap 5401 (let ((map (make-sparse-keymap))) 5402 (define-key map [mouse-2] #'lsp--help-open-link) 5403 (define-key map "\r" #'lsp--help-open-link) 5404 map) 5405 "Keymap active on links in *lsp-help* mode.") 5406 5407 (defun lsp--fix-markdown-links () 5408 (let ((inhibit-read-only t) 5409 (inhibit-modification-hooks t) 5410 (prop)) 5411 (save-restriction 5412 (goto-char (point-min)) 5413 (while (setq prop (markdown-find-next-prop 'face)) 5414 (let ((end (or (next-single-property-change (car prop) 'face) 5415 (point-max)))) 5416 (when (memq (get-text-property (car prop) 'face) 5417 '(markdown-link-face 5418 markdown-url-face 5419 markdown-plain-url-face)) 5420 (add-text-properties (car prop) end 5421 (list 'button t 5422 'category 'lsp-help-link 5423 'follow-link t 5424 'keymap lsp-help-link-keymap))) 5425 (goto-char end)))))) 5426 5427 (defun lsp--buffer-string-visible () 5428 "Return visible buffer string. 5429 Stolen from `org-copy-visible'." 5430 (let ((temp (generate-new-buffer " *temp*")) 5431 (beg (point-min)) 5432 (end (point-max))) 5433 (while (/= beg end) 5434 (when (get-char-property beg 'invisible) 5435 (setq beg (next-single-char-property-change beg 'invisible nil end))) 5436 (let* ((next (next-single-char-property-change beg 'invisible nil end)) 5437 (substring (buffer-substring beg next))) 5438 (with-current-buffer temp (insert substring)) 5439 ;; (setq result (concat result substring)) 5440 (setq beg next))) 5441 (setq deactivate-mark t) 5442 (prog1 (with-current-buffer temp 5443 (s-chop-suffix "\n" (buffer-string))) 5444 (kill-buffer temp)))) 5445 5446 (defvar lsp-buffer-major-mode nil 5447 "Holds the major mode when fontification function is running. 5448 See #2588") 5449 5450 (defvar view-inhibit-help-message) 5451 5452 (defun lsp--render-markdown () 5453 "Render markdown." 5454 5455 (let ((markdown-enable-math nil)) 5456 (goto-char (point-min)) 5457 (while (re-search-forward 5458 (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/" 5459 "{" "}" "[" "]" "(" ")" 5460 "#" "+" "-" "." "!" "|")))) 5461 nil t) 5462 (replace-match (rx (backref 1)))) 5463 5464 ;; markdown-mode v2.3 does not yet provide gfm-view-mode 5465 (if (fboundp 'gfm-view-mode) 5466 (let ((view-inhibit-help-message t)) 5467 (gfm-view-mode)) 5468 (gfm-mode)) 5469 5470 (lsp--setup-markdown lsp-buffer-major-mode))) 5471 5472 (defvar lsp--display-inline-image-alist 5473 '((lsp--render-markdown 5474 (:regexp 5475 "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)" 5476 :sexp 5477 (create-image 5478 (base64-decode-string 5479 (buffer-substring-no-properties (match-beginning 1) (match-end 1))) 5480 nil t)))) 5481 "Replaced string regexp and function returning image. 5482 Each element should have the form (MODE . (PROPERTY-LIST...)). 5483 MODE (car) is function which is defined in `lsp-language-id-configuration'. 5484 Cdr should be list of PROPERTY-LIST. 5485 5486 Each PROPERTY-LIST should have properties: 5487 :regexp Regexp which determines what string is relpaced to image. 5488 You should also get information of image, by parenthesis constructs. 5489 By default, all matched string is replaced to image, but you can 5490 change index of replaced string by keyword :replaced-index. 5491 5492 :sexp Return image when evaluated. You can use information of regexp 5493 by using (match-beggining N), (match-end N) or (match-substring N). 5494 5495 In addition, each can have property: 5496 :replaced-index Determine index which is used to replace regexp to image. 5497 The value means first argument of `match-beginning' and 5498 `match-end'. If omitted, interpreted as index 0.") 5499 5500 (defcustom lsp-display-inline-image t 5501 "Showing inline image or not." 5502 :group 'lsp-mode 5503 :type 'boolean) 5504 5505 (defcustom lsp-enable-suggest-server-download t 5506 "When non-nil enable server downloading suggestions." 5507 :group 'lsp-mode 5508 :type 'boolean 5509 :package-version '(lsp-mode . "9.0.0")) 5510 5511 (defcustom lsp-auto-register-remote-clients t 5512 "When non-nil register remote when registering the local one." 5513 :group 'lsp-mode 5514 :type 'boolean 5515 :package-version '(lsp-mode . "9.0.0")) 5516 5517 (defun lsp--display-inline-image (mode) 5518 "Add image property if available." 5519 (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist)))) 5520 (when (and (display-images-p) lsp-display-inline-image) 5521 (cl-loop 5522 for plist in plist-list 5523 with regexp with replaced-index 5524 do 5525 (setq regexp (plist-get plist :regexp)) 5526 (setq replaced-index (or (plist-get plist :replaced-index) 0)) 5527 5528 (font-lock-remove-keywords nil (list regexp replaced-index)) 5529 (let ((inhibit-read-only t)) 5530 (save-excursion 5531 (goto-char (point-min)) 5532 (while (re-search-forward regexp nil t) 5533 (set-text-properties 5534 (match-beginning replaced-index) (match-end replaced-index) 5535 nil) 5536 (add-text-properties 5537 (match-beginning replaced-index) (match-end replaced-index) 5538 `(display ,(eval (plist-get plist :sexp))))))))))) 5539 5540 (defun lsp--fontlock-with-mode (str mode) 5541 "Fontlock STR with MODE." 5542 (let ((lsp-buffer-major-mode major-mode)) 5543 (with-temp-buffer 5544 (with-demoted-errors "Error during doc rendering: %s" 5545 (insert str) 5546 (delay-mode-hooks (funcall mode)) 5547 (cl-flet ((window-body-width () lsp-window-body-width)) 5548 ;; This can go wrong in some cases, and the fontification would 5549 ;; not work as expected. 5550 ;; 5551 ;; See #2984 5552 (ignore-errors (font-lock-ensure)) 5553 (lsp--display-inline-image mode) 5554 (when (eq mode 'lsp--render-markdown) 5555 (lsp--fix-markdown-links)))) 5556 (lsp--buffer-string-visible)))) 5557 5558 (defun lsp--render-string (str language) 5559 "Render STR using `major-mode' corresponding to LANGUAGE. 5560 When language is nil render as markup if `markdown-mode' is loaded." 5561 (setq str (s-replace "\r" "" (or str ""))) 5562 (if-let* ((modes (-keep (-lambda ((mode . lang)) 5563 (when (and (equal lang language) (functionp mode)) 5564 mode)) 5565 lsp-language-id-configuration)) 5566 (mode (car (or (member major-mode modes) modes)))) 5567 (lsp--fontlock-with-mode str mode) 5568 str)) 5569 5570 (defun lsp--render-element (content) 5571 "Render CONTENT element." 5572 (let ((inhibit-message t)) 5573 (or 5574 (pcase content 5575 ((MarkedString :value :language) 5576 (lsp--render-string value language)) 5577 ((MarkupContent :value :kind) 5578 (lsp--render-string value kind)) 5579 ;; plain string 5580 ((pred stringp) (lsp--render-string content "markdown")) 5581 ((pred null) "") 5582 (_ (error "Failed to handle %s" content))) 5583 ""))) 5584 5585 (defun lsp--create-unique-string-fn () 5586 (let (elements) 5587 (lambda (element) 5588 (let ((count (cl-count element elements :test #'string=))) 5589 (prog1 (if (zerop count) 5590 element 5591 (format "%s (%s)" element count)) 5592 (push element elements)))))) 5593 5594 (defun lsp--select-action (actions) 5595 "Select an action to execute from ACTIONS." 5596 (cond 5597 ((seq-empty-p actions) (signal 'lsp-no-code-actions nil)) 5598 ((and (eq (seq-length actions) 1) lsp-auto-execute-action) 5599 (lsp-seq-first actions)) 5600 (t (let ((completion-ignore-case t)) 5601 (lsp--completing-read "Select code action: " 5602 (seq-into actions 'list) 5603 (-compose (lsp--create-unique-string-fn) 5604 #'lsp:code-action-title) 5605 nil t))))) 5606 5607 (defun lsp--workspace-server-id (workspace) 5608 "Return the server ID of WORKSPACE." 5609 (-> workspace lsp--workspace-client lsp--client-server-id)) 5610 5611 (defun lsp--handle-rendered-for-echo-area (contents) 5612 "Return a single line from RENDERED, appropriate for display in the echo area." 5613 (pcase (lsp-workspaces) 5614 (`(,workspace) 5615 (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace))) 5616 ;; For projects with multiple active workspaces we also default to 5617 ;; render the first line. 5618 (_ (lsp-clients-extract-signature-on-hover contents nil)))) 5619 5620 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id) 5621 "Extract a representative line from CONTENTS, to show in the echo area." 5622 (car (s-lines (s-trim (lsp--render-element contents))))) 5623 5624 (defun lsp--render-on-hover-content (contents render-all) 5625 "Render the content received from `document/onHover' request. 5626 CONTENTS - MarkedString | MarkedString[] | MarkupContent 5627 RENDER-ALL - nil if only the signature should be rendered." 5628 (cond 5629 ((lsp-markup-content? contents) 5630 ;; MarkupContent. 5631 ;; It tends to be long and is not suitable to display fully in the echo area. 5632 ;; Just display the first line which is typically the signature. 5633 (if render-all 5634 (lsp--render-element contents) 5635 (lsp--handle-rendered-for-echo-area contents))) 5636 ((and (stringp contents) (not (string-match-p "\n" contents))) 5637 ;; If the contents is a single string containing a single line, 5638 ;; render it always. 5639 (lsp--render-element contents)) 5640 (t 5641 ;; MarkedString -> MarkedString[] 5642 (when (or (lsp-marked-string? contents) (stringp contents)) 5643 (setq contents (list contents))) 5644 ;; Consider the signature consisting of the elements who have a renderable 5645 ;; "language" property. When render-all is nil, ignore other elements. 5646 (string-join 5647 (seq-map 5648 #'lsp--render-element 5649 (if render-all 5650 contents 5651 ;; Only render contents that have an available renderer. 5652 (seq-take 5653 (seq-filter 5654 (-andfn #'lsp-marked-string? 5655 (-compose #'lsp-get-renderer #'lsp:marked-string-language)) 5656 contents) 5657 1))) 5658 (if (bound-and-true-p page-break-lines-mode) 5659 "\n\n" 5660 "\n"))))) 5661 5662 5663 5664 (defvar lsp-signature-mode-map 5665 (-doto (make-sparse-keymap) 5666 (define-key (kbd "M-n") #'lsp-signature-next) 5667 (define-key (kbd "M-p") #'lsp-signature-previous) 5668 (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs) 5669 (define-key (kbd "C-c C-k") #'lsp-signature-stop) 5670 (define-key (kbd "C-g") #'lsp-signature-stop)) 5671 "Keymap for `lsp-signature-mode'.") 5672 5673 (define-minor-mode lsp-signature-mode 5674 "Mode used to show signature popup." 5675 :keymap lsp-signature-mode-map 5676 :lighter "" 5677 :group 'lsp-mode) 5678 5679 (defun lsp-signature-stop () 5680 "Stop showing current signature help." 5681 (interactive) 5682 (lsp-cancel-request-by-token :signature) 5683 (remove-hook 'post-command-hook #'lsp-signature) 5684 (funcall lsp-signature-function nil) 5685 (lsp-signature-mode -1)) 5686 5687 (declare-function page-break-lines--update-display-tables "ext:page-break-lines") 5688 5689 (defun lsp--setup-page-break-mode-if-present () 5690 "Enable `page-break-lines-mode' in current buffer." 5691 (when (fboundp 'page-break-lines-mode) 5692 (page-break-lines-mode) 5693 ;; force page-break-lines-mode to update the display tables. 5694 (page-break-lines--update-display-tables))) 5695 5696 (defun lsp-lv-message (message) 5697 (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present) 5698 (if message 5699 (progn 5700 (setq lsp--signature-last-buffer (current-buffer)) 5701 (let ((lv-force-update t)) 5702 (lv-message "%s" message))) 5703 (lv-delete-window) 5704 (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present))) 5705 5706 (declare-function posframe-show "ext:posframe") 5707 (declare-function posframe-hide "ext:posframe") 5708 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe") 5709 5710 (defface lsp-signature-posframe 5711 '((t :inherit tooltip)) 5712 "Background and foreground for `lsp-signature-posframe'." 5713 :group 'lsp-mode) 5714 5715 (defvar lsp-signature-posframe-params 5716 (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward 5717 :height 10 5718 :width 60 5719 :border-width 1 5720 :min-width 60) 5721 "Params for signature and `posframe-show'.") 5722 5723 (defun lsp-signature-posframe (str) 5724 "Use posframe to show the STR signatureHelp string." 5725 (if str 5726 (apply #'posframe-show 5727 (with-current-buffer (get-buffer-create " *lsp-signature*") 5728 (erase-buffer) 5729 (insert str) 5730 (visual-line-mode 1) 5731 (lsp--setup-page-break-mode-if-present) 5732 (current-buffer)) 5733 (append 5734 lsp-signature-posframe-params 5735 (list :position (point) 5736 :background-color (face-attribute 'lsp-signature-posframe :background nil t) 5737 :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t) 5738 :border-color (face-attribute 'font-lock-comment-face :foreground nil t)))) 5739 (posframe-hide " *lsp-signature*"))) 5740 5741 (defun lsp--handle-signature-update (signature) 5742 (let ((message 5743 (if (lsp-signature-help? signature) 5744 (lsp--signature->message signature) 5745 (mapconcat #'lsp--signature->message signature "\n")))) 5746 (if (s-present? message) 5747 (funcall lsp-signature-function message) 5748 (lsp-signature-stop)))) 5749 5750 (defun lsp-signature-activate () 5751 "Activate signature help. 5752 It will show up only if current point has signature help." 5753 (interactive) 5754 (setq lsp--signature-last nil 5755 lsp--signature-last-index nil 5756 lsp--signature-last-buffer (current-buffer)) 5757 (add-hook 'post-command-hook #'lsp-signature) 5758 (lsp-signature-mode t)) 5759 5760 (defcustom lsp-signature-cycle t 5761 "Whether `lsp-signature-next' and prev should cycle." 5762 :type 'boolean 5763 :group 'lsp-mode) 5764 5765 (defun lsp-signature-next () 5766 "Show next signature." 5767 (interactive) 5768 (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last)))) 5769 (when (and lsp--signature-last-index 5770 lsp--signature-last 5771 (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs))) 5772 (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs)) 5773 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))) 5774 5775 (defun lsp-signature-previous () 5776 "Next signature." 5777 (interactive) 5778 (when (and lsp--signature-last-index 5779 lsp--signature-last 5780 (or lsp-signature-cycle (not (zerop lsp--signature-last-index)))) 5781 (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index) 5782 (length (lsp:signature-help-signatures lsp--signature-last)) 5783 lsp--signature-last-index))) 5784 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))) 5785 5786 (defun lsp-signature-toggle-full-docs () 5787 "Toggle full/partial signature documentation." 5788 (interactive) 5789 (let ((all? (not (numberp lsp-signature-doc-lines)))) 5790 (setq lsp-signature-doc-lines (if all? 5791 (or (car-safe lsp-signature-doc-lines) 5792 20) 5793 (list lsp-signature-doc-lines)))) 5794 (lsp-signature-activate)) 5795 5796 (defun lsp--signature->message (signature-help) 5797 "Generate eldoc message from SIGNATURE-HELP response." 5798 (setq lsp--signature-last signature-help) 5799 5800 (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help)))) 5801 (-let* (((&SignatureHelp :active-signature? 5802 :active-parameter? 5803 :signatures) signature-help) 5804 (active-signature? (or lsp--signature-last-index active-signature? 0)) 5805 (_ (setq lsp--signature-last-index active-signature?)) 5806 ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?)) 5807 (prefix (if (= (length signatures) 1) 5808 "" 5809 (concat (propertize (format " %s/%s" 5810 (1+ active-signature?) 5811 (length signatures)) 5812 'face 'success) 5813 " "))) 5814 (method-docs (when 5815 (and lsp-signature-render-documentation 5816 (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines))) 5817 (let ((docs (lsp--render-element 5818 (lsp:parameter-information-documentation? signature)))) 5819 (when (s-present? docs) 5820 (concat 5821 "\n" 5822 (if (fboundp 'page-break-lines-mode) 5823 "\n" 5824 "") 5825 (if (and (numberp lsp-signature-doc-lines) 5826 (> (length (s-lines docs)) lsp-signature-doc-lines)) 5827 (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs))) 5828 (propertize "\nTruncated..." 'face 'highlight)) 5829 docs))))))) 5830 (when (and active-parameter? (not (seq-empty-p parameters?))) 5831 (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?))) 5832 (seq-elt parameters? active-parameter?))) 5833 (selected-param-label (let ((label (lsp:parameter-information-label param))) 5834 (if (stringp label) label (append label nil)))) 5835 (start (if (stringp selected-param-label) 5836 (s-index-of selected-param-label label) 5837 (cl-first selected-param-label))) 5838 (end (if (stringp selected-param-label) 5839 (+ start (length selected-param-label)) 5840 (cl-second selected-param-label)))) 5841 (add-face-text-property start end 'eldoc-highlight-function-argument nil label))) 5842 (concat prefix label method-docs)))) 5843 5844 (defun lsp-signature () 5845 "Display signature info (based on `textDocument/signatureHelp')" 5846 (if (and lsp--signature-last-buffer 5847 (not (equal (current-buffer) lsp--signature-last-buffer))) 5848 (lsp-signature-stop) 5849 (lsp-request-async "textDocument/signatureHelp" 5850 (lsp--text-document-position-params) 5851 #'lsp--handle-signature-update 5852 :cancel-token :signature))) 5853 5854 5855 (defcustom lsp-overlay-document-color-char "■" 5856 "Display the char represent the document color in overlay" 5857 :type 'string 5858 :group 'lsp-mode) 5859 5860 ;; color presentation 5861 (defun lsp--color-create-interactive-command (color range) 5862 (lambda () 5863 (interactive) 5864 (-let [(&ColorPresentation? :text-edit? 5865 :additional-text-edits?) 5866 (lsp--completing-read 5867 "Select color presentation: " 5868 (lsp-request 5869 "textDocument/colorPresentation" 5870 `( :textDocument ,(lsp--text-document-identifier) 5871 :color ,color 5872 :range ,range)) 5873 #'lsp:color-presentation-label 5874 nil 5875 t)] 5876 (when text-edit? 5877 (lsp--apply-text-edit text-edit?)) 5878 (when additional-text-edits? 5879 (lsp--apply-text-edits additional-text-edits? 'color-presentation))))) 5880 5881 (defun lsp--number->color (number) 5882 (let ((result (format "%x" 5883 (round (* (or number 0) 255.0))))) 5884 (if (= 1 (length result)) 5885 (concat "0" result) 5886 result))) 5887 5888 (defun lsp--document-color () 5889 "Document color handler." 5890 (when (lsp-feature? "textDocument/documentColor") 5891 (lsp-request-async 5892 "textDocument/documentColor" 5893 `(:textDocument ,(lsp--text-document-identifier)) 5894 (lambda (result) 5895 (lsp--remove-overlays 'lsp-color) 5896 (seq-do 5897 (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue) 5898 :range)) 5899 (-let* (((beg . end) (lsp--range-to-region range)) 5900 (overlay (make-overlay beg end)) 5901 (command (lsp--color-create-interactive-command color range))) 5902 (overlay-put overlay 'lsp-color t) 5903 (overlay-put overlay 'evaporate t) 5904 (overlay-put overlay 5905 'before-string 5906 (propertize 5907 lsp-overlay-document-color-char 5908 'face `((:foreground ,(format 5909 "#%s%s%s" 5910 (lsp--number->color red) 5911 (lsp--number->color green) 5912 (lsp--number->color blue)))) 5913 'action command 5914 'mouse-face 'lsp-lens-mouse-face 5915 'local-map (-doto (make-sparse-keymap) 5916 (define-key [mouse-1] command)))))) 5917 result)) 5918 :mode 'unchanged 5919 :cancel-token :document-color-token))) 5920 5921 5922 5923 (defun lsp--action-trigger-parameter-hints (_command) 5924 "Handler for editor.action.triggerParameterHints." 5925 (when (member :on-server-request lsp-signature-auto-activate) 5926 (lsp-signature-activate))) 5927 5928 (defun lsp--action-trigger-suggest (_command) 5929 "Handler for editor.action.triggerSuggest." 5930 (cond 5931 ((and (bound-and-true-p company-mode) 5932 (fboundp 'company-auto-begin) 5933 (fboundp 'company-post-command)) 5934 (run-at-time 0 nil 5935 (lambda () 5936 (let ((this-command 'company-idle-begin) 5937 (company-minimum-prefix-length 0)) 5938 (company-auto-begin) 5939 (company-post-command))))) 5940 (t 5941 (completion-at-point)))) 5942 5943 (defconst lsp--default-action-handlers 5944 (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints) 5945 ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest)) 5946 "Default action handlers.") 5947 5948 (defun lsp--find-action-handler (command) 5949 "Find action handler for particular COMMAND." 5950 (or 5951 (--some (-some->> it 5952 (lsp--workspace-client) 5953 (lsp--client-action-handlers) 5954 (gethash command)) 5955 (lsp-workspaces)) 5956 (gethash command lsp--default-action-handlers))) 5957 5958 (defun lsp--text-document-code-action-params (&optional kind) 5959 "Code action params." 5960 (list :textDocument (lsp--text-document-identifier) 5961 :range (if (use-region-p) 5962 (lsp--region-to-range (region-beginning) (region-end)) 5963 (lsp--region-to-range (point) (point))) 5964 :context `( :diagnostics ,(lsp-cur-possition-diagnostics) 5965 ,@(when kind (list :only (vector kind)))))) 5966 5967 (defun lsp-code-actions-at-point (&optional kind) 5968 "Retrieve the code actions for the active region or the current line. 5969 It will filter by KIND if non nil." 5970 (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind))) 5971 5972 (defun lsp-execute-code-action-by-kind (command-kind) 5973 "Execute code action by COMMAND-KIND." 5974 (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind) 5975 (-filter (-lambda ((&CodeAction :kind?)) 5976 (and kind? (s-prefix? command-kind kind?)))) 5977 lsp--select-action))) 5978 (lsp-execute-code-action action) 5979 (signal 'lsp-no-code-actions '(command-kind)))) 5980 5981 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point) 5982 5983 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?)) 5984 "Parse and execute a code ACTION represented as a Command LSP type." 5985 (let ((server-id (->> (lsp-workspaces) 5986 (cl-first) 5987 (or lsp--cur-workspace) 5988 (lsp--workspace-client) 5989 (lsp--client-server-id)))) 5990 (condition-case nil 5991 (with-no-warnings 5992 (lsp-execute-command server-id (intern command) arguments?)) 5993 (cl-no-applicable-method 5994 (if-let ((action-handler (lsp--find-action-handler command))) 5995 (funcall action-handler action) 5996 (lsp-send-execute-command command arguments?)))))) 5997 5998 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?)) 5999 "Execute code action ACTION. For example, when text under the 6000 caret has a suggestion to apply a fix from an lsp-server, calling 6001 this function will do so. 6002 If ACTION is not set it will be selected from `lsp-code-actions-at-point'. 6003 Request codeAction/resolve for more info if server supports." 6004 (interactive (list (lsp--select-action (lsp-code-actions-at-point)))) 6005 (if (and (lsp-feature? "codeAction/resolve") 6006 (not command?) 6007 (not edit?)) 6008 (lsp--execute-code-action (lsp-request "codeAction/resolve" action)) 6009 (lsp--execute-code-action action))) 6010 6011 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?)) 6012 "Execute code action ACTION." 6013 (when edit? 6014 (lsp--apply-workspace-edit edit? 'code-action)) 6015 6016 (cond 6017 ((stringp command?) (lsp--execute-command action)) 6018 ((lsp-command? command?) (progn 6019 (when-let ((action-filter (->> (lsp-workspaces) 6020 (cl-first) 6021 (or lsp--cur-workspace) 6022 (lsp--workspace-client) 6023 (lsp--client-action-filter)))) 6024 (funcall action-filter command?)) 6025 (lsp--execute-command command?))))) 6026 6027 (lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments) 6028 "Patch incorrect boolean argument values in the provided `CodeAction' command 6029 in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values 6030 in this list can be either symbols or lists of symbols that 6031 represent paths to boolean arguments in code actions: 6032 6033 > (lsp-fix-code-action-booleans command `(:foo :bar (:some :nested :boolean))) 6034 6035 When there are available code actions, the server sends 6036 `lsp-mode' a list of possible command names and arguments as 6037 JSON. `lsp-mode' parses all boolean false values as `nil'. As a 6038 result code action arguments containing falsy values don't 6039 roundtrip correctly because `lsp-mode' will end up sending null 6040 values back to the client. This list makes it possible to 6041 selectively transform `nil' values back into `:json-false'." 6042 (seq-doseq (path boolean-action-arguments) 6043 (seq-doseq (args arguments?) 6044 (lsp--fix-nested-boolean args (if (listp path) path (list path)))))) 6045 6046 (defun lsp--fix-nested-boolean (structure path) 6047 "Traverse STRUCTURE using the paths from the PATH list, changing the value to 6048 `:json-false' if it was `nil'. PATH should be a list containing 6049 one or more symbols, and STRUCTURE should be compatible with 6050 `lsp-member?', `lsp-get', and `lsp-put'." 6051 (let ((key (car path)) 6052 (rest (cdr path))) 6053 (if (null rest) 6054 ;; `lsp-put' returns `nil' both when the key doesn't exist and when the 6055 ;; value is `nil', so we need to explicitly check its presence here 6056 (when (and (lsp-member? structure key) (not (lsp-get structure key))) 6057 (lsp-put structure key :json-false)) 6058 ;; If `key' does not exist, then we'll silently ignore it 6059 (when-let ((child (lsp-get structure key))) 6060 (lsp--fix-nested-boolean child rest))))) 6061 6062 (defvar lsp--formatting-indent-alist 6063 ;; Taken from `dtrt-indent-mode' 6064 '( 6065 (ada-mode . ada-indent) ; Ada 6066 (ada-ts-mode . ada-ts-mode-indent-offset) 6067 (c++-mode . c-basic-offset) ; C++ 6068 (c++-ts-mode . c-ts-mode-indent-offset) 6069 (c-mode . c-basic-offset) ; C 6070 (c-ts-mode . c-ts-mode-indent-offset) 6071 (cperl-mode . cperl-indent-level) ; Perl 6072 (crystal-mode . crystal-indent-level) ; Crystal (Ruby) 6073 (csharp-mode . c-basic-offset) ; C# 6074 (csharp-tree-sitter-mode . csharp-tree-sitter-indent-offset) ; C# 6075 (csharp-ts-mode . csharp-ts-mode-indent-offset) ; C# (tree-sitter, Emacs29) 6076 (css-mode . css-indent-offset) ; CSS 6077 (d-mode . c-basic-offset) ; D 6078 (enh-ruby-mode . enh-ruby-indent-level) ; Ruby 6079 (erlang-mode . erlang-indent-level) ; Erlang 6080 (ess-mode . ess-indent-offset) ; ESS (R) 6081 (go-ts-mode . go-ts-mode-indent-offset) 6082 (gpr-mode . gpr-indent-offset) ; GNAT Project 6083 (gpr-ts-mode . gpr-ts-mode-indent-offset) 6084 (hack-mode . hack-indent-offset) ; Hack 6085 (java-mode . c-basic-offset) ; Java 6086 (java-ts-mode . java-ts-mode-indent-offset) 6087 (jde-mode . c-basic-offset) ; Java (JDE) 6088 (js-mode . js-indent-level) ; JavaScript 6089 (js-ts-mode . js-indent-level) 6090 (js2-mode . js2-basic-offset) ; JavaScript-IDE 6091 (js3-mode . js3-indent-level) ; JavaScript-IDE 6092 (json-mode . js-indent-level) ; JSON 6093 (json-ts-mode . json-ts-mode-indent-offset) 6094 (lua-mode . lua-indent-level) ; Lua 6095 (lua-ts-mode . lua-ts-indent-offset) 6096 (nxml-mode . nxml-child-indent) ; XML 6097 (objc-mode . c-basic-offset) ; Objective C 6098 (pascal-mode . pascal-indent-level) ; Pascal 6099 (perl-mode . perl-indent-level) ; Perl 6100 (php-mode . c-basic-offset) ; PHP 6101 (php-ts-mode . php-ts-mode-indent-offset) ; PHP 6102 (powershell-mode . powershell-indent) ; PowerShell 6103 (powershell-ts-mode . powershell-ts-mode-indent-offset) ; PowerShell 6104 (raku-mode . raku-indent-offset) ; Perl6/Raku 6105 (ruby-mode . ruby-indent-level) ; Ruby 6106 (rust-mode . rust-indent-offset) ; Rust 6107 (rust-ts-mode . rust-ts-mode-indent-offset) 6108 (rustic-mode . rustic-indent-offset) ; Rust 6109 (scala-mode . scala-indent:step) ; Scala 6110 (sgml-mode . sgml-basic-offset) ; SGML 6111 (sh-mode . sh-basic-offset) ; Shell Script 6112 (toml-ts-mode . toml-ts-mode-indent-offset) 6113 (typescript-mode . typescript-indent-level) ; Typescript 6114 (typescript-ts-mode . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29) 6115 (yaml-mode . yaml-indent-offset) ; YAML 6116 (yang-mode . c-basic-offset) ; YANG (yang-mode) 6117 6118 (default . standard-indent)) ; default fallback 6119 "A mapping from `major-mode' to its indent variable.") 6120 6121 (defun lsp--get-indent-width (mode) 6122 "Get indentation offset for MODE." 6123 (or (alist-get mode lsp--formatting-indent-alist) 6124 (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default)))) 6125 6126 (defun lsp--make-document-formatting-params () 6127 "Create document formatting params." 6128 (lsp-make-document-formatting-params 6129 :text-document (lsp--text-document-identifier) 6130 :options (lsp-make-formatting-options 6131 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 6132 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 6133 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 6134 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 6135 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)))) 6136 6137 (defun lsp-format-buffer () 6138 "Ask the server to format this document." 6139 (interactive "*") 6140 (cond ((lsp-feature? "textDocument/formatting") 6141 (let ((edits (lsp-request "textDocument/formatting" 6142 (lsp--make-document-formatting-params)))) 6143 (if (seq-empty-p edits) 6144 (lsp--info "No formatting changes provided") 6145 (lsp--apply-text-edits edits 'format)))) 6146 ((lsp-feature? "textDocument/rangeFormatting") 6147 (save-restriction 6148 (widen) 6149 (lsp-format-region (point-min) (point-max)))) 6150 (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider"))))) 6151 6152 (defun lsp-format-region (s e) 6153 "Ask the server to format the region, or if none is selected, the current line." 6154 (interactive "r") 6155 (let ((edits (lsp-request 6156 "textDocument/rangeFormatting" 6157 (lsp--make-document-range-formatting-params s e)))) 6158 (if (seq-empty-p edits) 6159 (lsp--info "No formatting changes provided") 6160 (lsp--apply-text-edits edits 'format)))) 6161 6162 (defmacro lsp-make-interactive-code-action (func-name code-action-kind) 6163 "Define an interactive function FUNC-NAME that attempts to 6164 execute a CODE-ACTION-KIND action." 6165 `(defun ,(intern (concat "lsp-" (symbol-name func-name))) () 6166 ,(format "Perform the %s code action, if available." code-action-kind) 6167 (interactive) 6168 ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to 6169 ;; auto-execute here: the user has specified exactly what they want. 6170 (let ((lsp-auto-execute-action t)) 6171 (condition-case nil 6172 (lsp-execute-code-action-by-kind ,code-action-kind) 6173 (lsp-no-code-actions 6174 (when (called-interactively-p 'any) 6175 (lsp--info ,(format "%s action not available" code-action-kind)))))))) 6176 6177 (lsp-make-interactive-code-action organize-imports "source.organizeImports") 6178 6179 (defun lsp--make-document-range-formatting-params (start end) 6180 "Make DocumentRangeFormattingParams for selected region." 6181 (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params) 6182 (lsp--region-to-range start end))) 6183 6184 (defconst lsp--highlight-kind-face 6185 '((1 . lsp-face-highlight-textual) 6186 (2 . lsp-face-highlight-read) 6187 (3 . lsp-face-highlight-write))) 6188 6189 (defun lsp--remove-overlays (name) 6190 (save-restriction 6191 (widen) 6192 (remove-overlays (point-min) (point-max) name t))) 6193 6194 (defun lsp-document-highlight () 6195 "Highlight all relevant references to the symbol under point." 6196 (interactive) 6197 (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights 6198 (setq lsp--have-document-highlights nil 6199 lsp--symbol-bounds-of-last-highlight-invocation nil) 6200 (let ((lsp-enable-symbol-highlighting t)) 6201 (lsp--document-highlight))) 6202 6203 (defun lsp--document-highlight-callback (highlights) 6204 "Create a callback to process the reply of a 6205 `textDocument/documentHighlight' message for the buffer BUF. 6206 A reference is highlighted only if it is visible in a window." 6207 (lsp--remove-overlays 'lsp-highlight) 6208 6209 (let* ((wins-visible-pos (-map (lambda (win) 6210 (cons (1- (line-number-at-pos (window-start win) t)) 6211 (1+ (line-number-at-pos (window-end win) t)))) 6212 (get-buffer-window-list nil nil 'visible)))) 6213 (setq lsp--have-document-highlights t) 6214 (-map 6215 (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line) 6216 :end (end &as &Position :line end-line)) 6217 :kind?)) 6218 (-map 6219 (-lambda ((start-window . end-window)) 6220 ;; Make the overlay only if the reference is visible 6221 (let ((start-point (lsp--position-to-point start)) 6222 (end-point (lsp--position-to-point end))) 6223 (when (and (> (1+ start-line) start-window) 6224 (< (1+ end-line) end-window) 6225 (not (and lsp-symbol-highlighting-skip-current 6226 (<= start-point (point) end-point)))) 6227 (-doto (make-overlay start-point end-point) 6228 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face))) 6229 (overlay-put 'lsp-highlight t))))) 6230 wins-visible-pos)) 6231 highlights))) 6232 6233 (defcustom lsp-symbol-kinds 6234 '((1 . "File") 6235 (2 . "Module") 6236 (3 . "Namespace") 6237 (4 . "Package") 6238 (5 . "Class") 6239 (6 . "Method") 6240 (7 . "Property") 6241 (8 . "Field") 6242 (9 . "Constructor") 6243 (10 . "Enum") 6244 (11 . "Interface") 6245 (12 . "Function") 6246 (13 . "Variable") 6247 (14 . "Constant") 6248 (15 . "String") 6249 (16 . "Number") 6250 (17 . "Boolean") 6251 (18 . "Array") 6252 (19 . "Object") 6253 (20 . "Key") 6254 (21 . "Null") 6255 (22 . "Enum Member") 6256 (23 . "Struct") 6257 (24 . "Event") 6258 (25 . "Operator") 6259 (26 . "Type Parameter")) 6260 "Alist mapping SymbolKinds to human-readable strings. 6261 Various Symbol objects in the LSP protocol have an integral type, 6262 specifying what they are. This alist maps such type integrals to 6263 readable representations of them. See 6264 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/', 6265 namespace SymbolKind." 6266 :group 'lsp-mode 6267 :type '(alist :key-type integer :value-type string)) 6268 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds) 6269 6270 (lsp-defun lsp--symbol-information-to-xref 6271 ((&SymbolInformation :kind :name 6272 :location (&Location :uri :range (&Range :start 6273 (&Position :line :character))))) 6274 "Return a `xref-item' from SYMBOL information." 6275 (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name) 6276 (xref-make-file-location (lsp--uri-to-path uri) 6277 line 6278 character))) 6279 6280 (defun lsp--get-document-symbols () 6281 "Get document symbols. 6282 6283 If the buffer has not been modified since symbols were last 6284 retrieved, simply return the latest result. 6285 6286 Else, if the request was initiated by Imenu updating its menu-bar 6287 entry, perform it asynchronously; i.e., give Imenu the latest 6288 result and then force a refresh when a new one is available. 6289 6290 Else (e.g., due to interactive use of `imenu' or `xref'), 6291 perform the request synchronously." 6292 (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick) 6293 lsp--document-symbols 6294 (let ((method "textDocument/documentSymbol") 6295 (params `(:textDocument ,(lsp--text-document-identifier))) 6296 (tick (buffer-chars-modified-tick))) 6297 (if (not lsp--document-symbols-request-async) 6298 (prog1 6299 (setq lsp--document-symbols (lsp-request method params)) 6300 (setq lsp--document-symbols-tick tick)) 6301 (lsp-request-async method params 6302 (lambda (document-symbols) 6303 (setq lsp--document-symbols document-symbols 6304 lsp--document-symbols-tick tick) 6305 (lsp--imenu-refresh)) 6306 :mode 'alive 6307 :cancel-token :document-symbols) 6308 lsp--document-symbols)))) 6309 6310 (advice-add 'imenu-update-menubar :around 6311 (lambda (oldfun &rest r) 6312 (let ((lsp--document-symbols-request-async t)) 6313 (apply oldfun r)))) 6314 6315 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position) 6316 "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION." 6317 (-let (((symbol &as &DocumentSymbol? :children?) 6318 (seq-find (-lambda ((&DocumentSymbol :range)) 6319 (lsp-point-in-range? current-position range)) 6320 document-symbols))) 6321 (if children? 6322 (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position)) 6323 (when symbol 6324 (list symbol))))) 6325 6326 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?)) 6327 "Convert a SymbolInformation to a DocumentInformation" 6328 (lsp-make-document-symbol :name name 6329 :kind kind 6330 :range (lsp:location-range location) 6331 :children? nil 6332 :deprecated? deprecated? 6333 :selection-range (lsp:location-range location) 6334 :detail? container-name?)) 6335 6336 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position) 6337 "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION." 6338 (--> symbols-informations 6339 (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range))) 6340 (when (lsp-point-in-range? current-position range) 6341 (lsp--symbol-information->document-symbol symbol))) 6342 it) 6343 (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position)) 6344 (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position))) 6345 (and (lsp--position-compare b-start-position a-start-position) 6346 (lsp--position-compare a-end-position b-end-position)))))) 6347 6348 (defun lsp--symbols->document-symbols-hierarchy (symbols) 6349 "Convert SYMBOLS to symbols-hierarchy." 6350 (when-let ((first-symbol (lsp-seq-first symbols))) 6351 (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line) 6352 :character (plist-get (lsp--cur-position) :character)))) 6353 (if (lsp-symbol-information? first-symbol) 6354 (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position) 6355 (lsp--document-symbols->document-symbols-hierarchy symbols cur-position))))) 6356 6357 (defun lsp--xref-backend () 'xref-lsp) 6358 6359 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp))) 6360 (propertize (or (thing-at-point 'symbol) "") 6361 'identifier-at-point t)) 6362 6363 (defun lsp--xref-elements-index (symbols path) 6364 (-mapcat 6365 (-lambda (sym) 6366 (pcase-exhaustive sym 6367 ((DocumentSymbol :name :children? :selection-range (Range :start)) 6368 (cons (cons (concat path name) 6369 (lsp--position-to-point start)) 6370 (lsp--xref-elements-index children? (concat path name " / ")))) 6371 ((SymbolInformation :name :location (Location :range (Range :start))) 6372 (list (cons (concat path name) 6373 (lsp--position-to-point start)))))) 6374 symbols)) 6375 6376 (defvar-local lsp--symbols-cache nil) 6377 6378 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp))) 6379 (if (lsp--find-workspaces-for "textDocument/documentSymbol") 6380 (progn 6381 (setq lsp--symbols-cache (lsp--xref-elements-index 6382 (lsp--get-document-symbols) nil)) 6383 lsp--symbols-cache) 6384 (list (propertize (or (thing-at-point 'symbol) "") 6385 'identifier-at-point t)))) 6386 6387 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier) 6388 (save-excursion 6389 (unless (get-text-property 0 'identifier-at-point identifier) 6390 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6391 (user-error "Unable to find symbol %s in current document" identifier))))) 6392 (lsp--locations-to-xref-items (lsp-request "textDocument/definition" 6393 (lsp--text-document-position-params))))) 6394 6395 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier) 6396 (save-excursion 6397 (unless (get-text-property 0 'identifier-at-point identifier) 6398 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6399 (user-error "Unable to find symbol %s" identifier))))) 6400 (lsp--locations-to-xref-items (lsp-request "textDocument/references" 6401 (lsp--make-reference-params nil lsp-references-exclude-definition))))) 6402 6403 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern) 6404 (seq-map #'lsp--symbol-information-to-xref 6405 (lsp-request "workspace/symbol" `(:query ,pattern)))) 6406 6407 (defcustom lsp-rename-use-prepare t 6408 "Whether `lsp-rename' should do a prepareRename first. 6409 For some language servers, textDocument/prepareRename might be 6410 too slow, in which case this variable may be set to nil. 6411 `lsp-rename' will then use `thing-at-point' `symbol' to determine 6412 the symbol to rename at point." 6413 :group 'lsp-mode 6414 :type 'boolean) 6415 6416 (defun lsp--get-symbol-to-rename () 6417 "Get a symbol to rename and placeholder at point. 6418 Returns a cons ((START . END) . PLACEHOLDER?), and nil if 6419 renaming is generally supported but cannot be done at point. 6420 START and END are the bounds of the identifiers being renamed, 6421 while PLACEHOLDER?, is either nil or a string suggested by the 6422 language server as the initial input of a new-name prompt." 6423 (unless (lsp-feature? "textDocument/rename") 6424 (error "The connected server(s) doesn't support renaming")) 6425 (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename")) 6426 (when-let ((response 6427 (lsp-request "textDocument/prepareRename" 6428 (lsp--text-document-position-params)))) 6429 (let* ((bounds (lsp--range-to-region 6430 (if (lsp-range? response) 6431 response 6432 (lsp:prepare-rename-result-range response)))) 6433 (placeholder 6434 (and (not (lsp-range? response)) 6435 (lsp:prepare-rename-result-placeholder response)))) 6436 (cons bounds placeholder))) 6437 (when-let ((bounds (bounds-of-thing-at-point 'symbol))) 6438 (cons bounds nil)))) 6439 6440 (defface lsp-face-rename '((t :underline t)) 6441 "Face used to highlight the identifier being renamed. 6442 Renaming can be done using `lsp-rename'." 6443 :group 'lsp-mode) 6444 6445 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face)) 6446 "Face used to display the rename placeholder in. 6447 When calling `lsp-rename' interactively, this will be the face of 6448 the new name." 6449 :group 'lsp-mode) 6450 6451 (defvar lsp-rename-history '() 6452 "History for `lsp--read-rename'.") 6453 6454 (defun lsp--read-rename (at-point) 6455 "Read a new name for a `lsp-rename' at `point' from the user. 6456 AT-POINT shall be a structure as returned by 6457 `lsp--get-symbol-to-rename'. 6458 6459 Returns a string, which should be the new name for the identifier 6460 at point. If renaming cannot be done at point (as determined from 6461 AT-POINT), throw a `user-error'. 6462 6463 This function is for use in `lsp-rename' only, and shall not be 6464 relied upon." 6465 (unless at-point 6466 (user-error "`lsp-rename' is invalid here")) 6467 (-let* ((((start . end) . placeholder?) at-point) 6468 ;; Do the `buffer-substring' first to not include `lsp-face-rename' 6469 (rename-me (buffer-substring start end)) 6470 (placeholder (or placeholder? rename-me)) 6471 (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face)) 6472 6473 overlay) 6474 ;; We need unwind protect, as the user might cancel here, causing the 6475 ;; overlay to linger. 6476 (unwind-protect 6477 (progn 6478 (setq overlay (make-overlay start end)) 6479 (overlay-put overlay 'face 'lsp-face-rename) 6480 6481 (read-string (format "Rename %s to: " rename-me) placeholder 6482 'lsp-rename-history)) 6483 (and overlay (delete-overlay overlay))))) 6484 6485 (defun lsp-rename (newname) 6486 "Rename the symbol (and all references to it) under point to NEWNAME." 6487 (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename)))) 6488 (when-let ((edits (lsp-request "textDocument/rename" 6489 `( :textDocument ,(lsp--text-document-identifier) 6490 :position ,(lsp--cur-position) 6491 :newName ,newname)))) 6492 (lsp--apply-workspace-edit edits 'rename))) 6493 6494 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?) 6495 "Advice around function `rename-file'. 6496 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?. 6497 6498 This advice sends workspace/willRenameFiles before renaming file 6499 to check if server wants to apply any workspaceEdits after renamed." 6500 (if (and lsp-apply-edits-after-file-operations 6501 (lsp--send-will-rename-files-p old-name)) 6502 (let ((params (lsp-make-rename-files-params 6503 :files (vector (lsp-make-file-rename 6504 :oldUri (lsp--path-to-uri old-name) 6505 :newUri (lsp--path-to-uri new-name)))))) 6506 (when-let ((edits (lsp-request "workspace/willRenameFiles" params))) 6507 (lsp--apply-workspace-edit edits 'rename-file) 6508 (funcall old-func old-name new-name ok-if-already-exists?) 6509 (when (lsp--send-did-rename-files-p) 6510 (lsp-notify "workspace/didRenameFiles" params)))) 6511 (funcall old-func old-name new-name ok-if-already-exists?))) 6512 6513 (advice-add 'rename-file :around #'lsp--on-rename-file) 6514 6515 (defcustom lsp-xref-force-references nil 6516 "If non-nil threat everything as references(e. g. jump if only one item.)" 6517 :group 'lsp-mode 6518 :type 'boolean) 6519 6520 (defun lsp-show-xrefs (xrefs display-action references?) 6521 (unless (region-active-p) (push-mark nil t)) 6522 (if (boundp 'xref-show-definitions-function) 6523 (with-no-warnings 6524 (xref-push-marker-stack) 6525 (funcall (if (and references? (not lsp-xref-force-references)) 6526 xref-show-xrefs-function 6527 xref-show-definitions-function) 6528 (-const xrefs) 6529 `((window . ,(selected-window)) 6530 (display-action . ,display-action) 6531 ,(if (and references? (not lsp-xref-force-references)) 6532 `(auto-jump . ,xref-auto-jump-to-first-xref) 6533 `(auto-jump . ,xref-auto-jump-to-first-definition))))) 6534 (xref--show-xrefs xrefs display-action))) 6535 6536 (cl-defmethod seq-empty-p ((ht hash-table)) 6537 "Function `seq-empty-p' for hash-table." 6538 (hash-table-empty-p ht)) 6539 6540 (cl-defun lsp-find-locations (method &optional extra &key display-action references?) 6541 "Send request named METHOD and get cross references of the symbol under point. 6542 EXTRA is a plist of extra parameters. 6543 REFERENCES? t when METHOD returns references." 6544 (let ((loc (lsp-request method 6545 (append (lsp--text-document-position-params) extra)))) 6546 (if (seq-empty-p loc) 6547 (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) "")) 6548 (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?)))) 6549 6550 (cl-defun lsp-find-declaration (&key display-action) 6551 "Find declarations of the symbol under point." 6552 (interactive) 6553 (lsp-find-locations "textDocument/declaration" nil :display-action display-action)) 6554 6555 (cl-defun lsp-find-definition (&key display-action) 6556 "Find definitions of the symbol under point." 6557 (interactive) 6558 (lsp-find-locations "textDocument/definition" nil :display-action display-action)) 6559 6560 (defun lsp-find-definition-mouse (click) 6561 "Click to start `lsp-find-definition' at clicked point." 6562 (interactive "e") 6563 (let* ((ec (event-start click)) 6564 (p1 (posn-point ec)) 6565 (w1 (posn-window ec))) 6566 (select-window w1) 6567 (goto-char p1) 6568 (lsp-find-definition))) 6569 6570 (cl-defun lsp-find-implementation (&key display-action) 6571 "Find implementations of the symbol under point." 6572 (interactive) 6573 (lsp-find-locations "textDocument/implementation" 6574 nil 6575 :display-action display-action 6576 :references? t)) 6577 6578 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action) 6579 "Find references of the symbol under point." 6580 (interactive "P") 6581 (lsp-find-locations "textDocument/references" 6582 (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition))))) 6583 :display-action display-action 6584 :references? t)) 6585 6586 (cl-defun lsp-find-type-definition (&key display-action) 6587 "Find type definitions of the symbol under point." 6588 (interactive) 6589 (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action)) 6590 6591 (defalias 'lsp-find-custom #'lsp-find-locations) 6592 (defalias 'lsp-goto-implementation #'lsp-find-implementation) 6593 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition) 6594 6595 (with-eval-after-load 'evil 6596 (evil-set-command-property 'lsp-find-definition :jump t) 6597 (evil-set-command-property 'lsp-find-implementation :jump t) 6598 (evil-set-command-property 'lsp-find-references :jump t) 6599 (evil-set-command-property 'lsp-find-type-definition :jump t)) 6600 6601 (defun lsp--workspace-method-supported? (check-command method capability workspace) 6602 (with-lsp-workspace workspace 6603 (if check-command 6604 (funcall check-command workspace) 6605 (or 6606 (when capability (lsp--capability capability)) 6607 (lsp--registered-capability method) 6608 (and (not capability) (not check-command)))))) 6609 6610 (defun lsp-disable-method-for-server (method server-id) 6611 "Disable METHOD for SERVER-ID." 6612 (cl-callf 6613 (lambda (reqs) 6614 (-let (((&plist :check-command :capability) reqs)) 6615 (list :check-command 6616 (lambda (workspace) 6617 (unless (-> workspace 6618 lsp--workspace-client 6619 lsp--client-server-id 6620 (eq server-id)) 6621 (lsp--workspace-method-supported? check-command 6622 method 6623 capability 6624 workspace)))))) 6625 (alist-get method lsp-method-requirements nil nil 'string=))) 6626 6627 (defun lsp--find-workspaces-for (msg-or-method) 6628 "Find all workspaces in the current project that can handle MSG." 6629 (let ((method (if (stringp msg-or-method) 6630 msg-or-method 6631 (plist-get msg-or-method :method)))) 6632 (-if-let (reqs (cdr (assoc method lsp-method-requirements))) 6633 (-let (((&plist :capability :check-command) reqs)) 6634 (-filter 6635 (-partial #'lsp--workspace-method-supported? 6636 check-command method capability) 6637 (lsp-workspaces))) 6638 (lsp-workspaces)))) 6639 6640 (defun lsp-can-execute-command? (command-name) 6641 "Returns non-nil if current language server(s) can execute COMMAND-NAME. 6642 The command is executed via `workspace/executeCommand'" 6643 (cl-position 6644 command-name 6645 (lsp:execute-command-options-commands 6646 (lsp:server-capabilities-execute-command-provider? 6647 (lsp--server-capabilities))) 6648 :test #'equal)) 6649 6650 (defalias 'lsp-feature? 'lsp--find-workspaces-for) 6651 6652 (cl-defmethod lsp-execute-command (_server _command _arguments) 6653 "Dispatch COMMAND execution." 6654 (signal 'cl-no-applicable-method nil)) 6655 6656 (defun lsp-workspace-command-execute (command &optional args) 6657 "Execute workspace COMMAND with ARGS." 6658 (condition-case-unless-debug err 6659 (let ((params (if args 6660 (list :command command :arguments args) 6661 (list :command command)))) 6662 (lsp-request "workspace/executeCommand" params)) 6663 (error 6664 (error "`workspace/executeCommand' with `%s' failed.\n\n%S" 6665 command err)))) 6666 6667 (defun lsp-send-execute-command (command &optional args) 6668 "Create and send a `workspace/executeCommand' message having command COMMAND 6669 and optional ARGS." 6670 (lsp-workspace-command-execute command args)) 6671 6672 (defalias 'lsp-point-to-position #'lsp--point-to-position) 6673 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier) 6674 (defalias 'lsp--send-execute-command #'lsp-send-execute-command) 6675 (defalias 'lsp-on-open #'lsp--text-document-did-open) 6676 (defalias 'lsp-on-save #'lsp--text-document-did-save) 6677 6678 (defun lsp--set-configuration (settings) 6679 "Set the SETTINGS for the lsp server." 6680 (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings))) 6681 6682 (defun lsp-current-buffer () 6683 (or lsp--virtual-buffer 6684 (current-buffer))) 6685 6686 (defun lsp-buffer-live-p (buffer-id) 6687 (if-let ((buffer-live (plist-get buffer-id :buffer-live?))) 6688 (funcall buffer-live buffer-id) 6689 (buffer-live-p buffer-id))) 6690 6691 (defun lsp--on-set-visited-file-name (old-func &rest args) 6692 "Advice around function `set-visited-file-name'. 6693 6694 This advice sends textDocument/didClose for the old file and 6695 textDocument/didOpen for the new file." 6696 (when lsp--cur-workspace 6697 (lsp--text-document-did-close t)) 6698 (prog1 (apply old-func args) 6699 (when lsp--cur-workspace 6700 (lsp--text-document-did-open)))) 6701 6702 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name) 6703 6704 (defvar lsp--flushing-delayed-changes nil) 6705 6706 (defun lsp--send-no-wait (message proc) 6707 "Send MESSAGE to PROC without waiting for further output." 6708 6709 (unless lsp--flushing-delayed-changes 6710 (let ((lsp--flushing-delayed-changes t)) 6711 (lsp--flush-delayed-changes))) 6712 (lsp-process-send proc message)) 6713 6714 (define-error 'lsp-parse-error 6715 "Error parsing message from language server" 'lsp-error) 6716 (define-error 'lsp-unknown-message-type 6717 "Unknown message type" '(lsp-error lsp-parse-error)) 6718 (define-error 'lsp-unknown-json-rpc-version 6719 "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error)) 6720 (define-error 'lsp-no-content-length 6721 "Content-Length header missing in message" '(lsp-error lsp-parse-error)) 6722 (define-error 'lsp-invalid-header-name 6723 "Invalid header name" '(lsp-error lsp-parse-error)) 6724 6725 ;; id method 6726 ;; x x request 6727 ;; x . response 6728 ;; . x notification 6729 (defun lsp--get-message-type (json-data) 6730 "Get the message type from JSON-DATA." 6731 (if (lsp:json-message-id? json-data) 6732 (if (lsp:json-message-error? json-data) 6733 'response-error 6734 (if (lsp:json-message-method? json-data) 6735 'request 6736 'response)) 6737 'notification)) 6738 6739 (defconst lsp--default-notification-handlers 6740 (ht ("window/showMessage" #'lsp--window-show-message) 6741 ("window/logMessage" #'lsp--window-log-message) 6742 ("window/showInputBox" #'lsp--window-show-input-box) 6743 ("window/showQuickPick" #'lsp--window-show-quick-pick) 6744 ("textDocument/publishDiagnostics" #'lsp--on-diagnostics) 6745 ("textDocument/diagnosticsEnd" #'ignore) 6746 ("textDocument/diagnosticsBegin" #'ignore) 6747 ("telemetry/event" #'ignore) 6748 ("$/progress" (lambda (workspace params) 6749 (funcall lsp-progress-function workspace params))))) 6750 6751 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method)) 6752 "Call the appropriate handler for NOTIFICATION." 6753 (-let ((client (lsp--workspace-client workspace))) 6754 (when (lsp--log-io-p method) 6755 (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif) 6756 lsp--cur-workspace)) 6757 (if-let ((handler (or (gethash method (lsp--client-notification-handlers client)) 6758 (gethash method lsp--default-notification-handlers)))) 6759 (funcall handler workspace params) 6760 (when (and method (not (string-prefix-p "$" method))) 6761 (lsp-warn "Unknown notification: %s" method))))) 6762 6763 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items)) 6764 "Get section configuration. 6765 PARAMS are the `workspace/configuration' request params" 6766 (->> items 6767 (-map (-lambda ((&ConfigurationItem :section?)) 6768 (-let* ((path-parts (split-string section? "\\.")) 6769 (path-without-last (s-join "." (-slice path-parts 0 -1))) 6770 (path-parts-len (length path-parts))) 6771 (cond 6772 ((<= path-parts-len 1) 6773 (ht-get (lsp-configuration-section section?) 6774 (car-safe path-parts) 6775 (ht-create))) 6776 ((> path-parts-len 1) 6777 (when-let ((section (lsp-configuration-section path-without-last)) 6778 (keys path-parts)) 6779 (while (and keys section) 6780 (setf section (ht-get section (pop keys)))) 6781 section)))))) 6782 (apply #'vector))) 6783 6784 (defun lsp--ms-since (timestamp) 6785 "Integer number of milliseconds since TIMESTAMP. Fractions discarded." 6786 (floor (* 1000 (float-time (time-since timestamp))))) 6787 6788 (defun lsp--send-request-response (workspace recv-time request response) 6789 "Send the RESPONSE for REQUEST in WORKSPACE and log if needed." 6790 (-let* (((&JSONResponse :params :method :id) request) 6791 (process (lsp--workspace-proc workspace)) 6792 (response (lsp--make-response id response)) 6793 (req-entry (and lsp-log-io 6794 (lsp--make-log-entry method id params 'incoming-req))) 6795 (resp-entry (and lsp-log-io 6796 (lsp--make-log-entry method id response 'outgoing-resp 6797 (lsp--ms-since recv-time))))) 6798 ;; Send response to the server. 6799 (when (lsp--log-io-p method) 6800 (lsp--log-entry-new req-entry workspace) 6801 (lsp--log-entry-new resp-entry workspace)) 6802 (lsp--send-no-wait response process))) 6803 6804 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method)) 6805 "Call the appropriate handler for REQUEST, and send the return value to the 6806 server. WORKSPACE is the active workspace." 6807 (-let* ((recv-time (current-time)) 6808 (client (lsp--workspace-client workspace)) 6809 (buffers (lsp--workspace-buffers workspace)) 6810 handler 6811 (response (cond 6812 ((setq handler (gethash method (lsp--client-request-handlers client) nil)) 6813 (funcall handler workspace params)) 6814 ((setq handler (gethash method (lsp--client-async-request-handlers client) nil)) 6815 (funcall handler workspace params 6816 (-partial #'lsp--send-request-response 6817 workspace recv-time request)) 6818 'delay-response) 6819 ((equal method "client/registerCapability") 6820 (mapc #'lsp--server-register-capability 6821 (lsp:registration-params-registrations params)) 6822 (mapc (lambda (buf) 6823 (when (lsp-buffer-live-p buf) 6824 (lsp-with-current-buffer buf 6825 (lsp-unconfig-buffer) 6826 (lsp-configure-buffer)))) 6827 buffers) 6828 nil) 6829 ((equal method "window/showMessageRequest") 6830 (let ((choice (lsp--window-log-message-request params))) 6831 `(:title ,choice))) 6832 ((equal method "window/showDocument") 6833 (let ((success? (lsp--window-show-document params))) 6834 (lsp-make-show-document-result :success (or success? 6835 :json-false)))) 6836 ((equal method "client/unregisterCapability") 6837 (mapc #'lsp--server-unregister-capability 6838 (lsp:unregistration-params-unregisterations params)) 6839 (mapc (lambda (buf) 6840 (when (lsp-buffer-live-p buf) 6841 (lsp-with-current-buffer buf 6842 (lsp-unconfig-buffer) 6843 (lsp-configure-buffer)))) 6844 buffers) 6845 nil) 6846 ((equal method "workspace/applyEdit") 6847 (list :applied (condition-case err 6848 (prog1 t 6849 (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested)) 6850 (error 6851 (lsp--error "Failed to apply edits with message %s" 6852 (error-message-string err)) 6853 :json-false)))) 6854 ((equal method "workspace/configuration") 6855 (with-lsp-workspace workspace 6856 (if-let ((buf (car buffers))) 6857 (lsp-with-current-buffer buf 6858 (lsp--build-workspace-configuration-response params)) 6859 (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace) 6860 (lsp--build-workspace-configuration-response params))))) 6861 ((equal method "workspace/workspaceFolders") 6862 (let ((folders (or (-> workspace 6863 (lsp--workspace-client) 6864 (lsp--client-server-id) 6865 (gethash (lsp-session-server-id->folders (lsp-session)))) 6866 (lsp-session-folders (lsp-session))))) 6867 (->> folders 6868 (-distinct) 6869 (-map (lambda (folder) 6870 (list :uri (lsp--path-to-uri folder)))) 6871 (apply #'vector)))) 6872 ((equal method "window/workDoneProgress/create") 6873 nil ;; no specific reply, no processing required 6874 ) 6875 ((equal method "workspace/semanticTokens/refresh") 6876 (when (and lsp-semantic-tokens-enable 6877 (fboundp 'lsp--semantic-tokens-on-refresh)) 6878 (lsp--semantic-tokens-on-refresh workspace)) 6879 nil) 6880 ((equal method "workspace/codeLens/refresh") 6881 (when (and lsp-lens-enable 6882 (fboundp 'lsp--lens-on-refresh)) 6883 (lsp--lens-on-refresh workspace)) 6884 nil) 6885 (t (lsp-warn "Unknown request method: %s" method) nil)))) 6886 ;; Send response to the server. 6887 (unless (eq response 'delay-response) 6888 (lsp--send-request-response workspace recv-time request response)))) 6889 6890 (lsp-defun lsp--error-string ((&JSONError :message :code)) 6891 "Format ERR as a user friendly string." 6892 (format "Error from the Language Server: %s (%s)" 6893 message 6894 (or (car (alist-get code lsp--errors)) "Unknown error"))) 6895 6896 (defun lsp--get-body-length (headers) 6897 (let ((content-length (cdr (assoc "Content-Length" headers)))) 6898 (if content-length 6899 (string-to-number content-length) 6900 6901 ;; This usually means either the server or our parser is 6902 ;; screwed up with a previous Content-Length 6903 (error "No Content-Length header")))) 6904 6905 (defun lsp--parse-header (s) 6906 "Parse string S as a LSP (KEY . VAL) header." 6907 (let ((pos (string-match "\:" s)) 6908 key val) 6909 (unless pos 6910 (signal 'lsp-invalid-header-name (list s))) 6911 (setq key (substring s 0 pos) 6912 val (s-trim-left (substring s (+ 1 pos)))) 6913 (when (equal key "Content-Length") 6914 (cl-assert (cl-loop for c across val 6915 when (or (> c ?9) (< c ?0)) return nil 6916 finally return t) 6917 nil (format "Invalid Content-Length value: %s" val))) 6918 (cons key val))) 6919 6920 (defmacro lsp--read-json (str) 6921 "Read json string STR." 6922 (if (progn 6923 (require 'json) 6924 (fboundp 'json-parse-string)) 6925 `(json-parse-string ,str 6926 :object-type (if lsp-use-plists 6927 'plist 6928 'hash-table) 6929 :null-object nil 6930 :false-object nil) 6931 `(let ((json-array-type 'vector) 6932 (json-object-type (if lsp-use-plists 6933 'plist 6934 'hash-table)) 6935 (json-false nil)) 6936 (json-read-from-string ,str)))) 6937 6938 (defmacro lsp-json-read-buffer () 6939 "Read json from the current buffer." 6940 (if (progn 6941 (require 'json) 6942 (fboundp 'json-parse-buffer)) 6943 `(json-parse-buffer :object-type (if lsp-use-plists 6944 'plist 6945 'hash-table) 6946 :null-object nil 6947 :false-object nil) 6948 `(let ((json-array-type 'vector) 6949 (json-object-type (if lsp-use-plists 6950 'plist 6951 'hash-table)) 6952 (json-false nil)) 6953 (json-read)))) 6954 6955 (defun lsp--read-json-file (file-path) 6956 "Read json file." 6957 (-> file-path 6958 (f-read-text) 6959 (lsp--read-json))) 6960 6961 (defun lsp--parser-on-message (json-data workspace) 6962 "Called when the parser P read a complete MSG from the server." 6963 (with-demoted-errors "Error processing message %S." 6964 (with-lsp-workspace workspace 6965 (let* ((client (lsp--workspace-client workspace)) 6966 (id (--when-let (lsp:json-response-id json-data) 6967 (if (stringp it) (string-to-number it) it))) 6968 (data (lsp:json-response-result json-data))) 6969 (pcase (lsp--get-message-type json-data) 6970 ('response 6971 (cl-assert id) 6972 (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))] 6973 (when (lsp--log-io-p method) 6974 (lsp--log-entry-new 6975 (lsp--make-log-entry method id data 'incoming-resp 6976 (lsp--ms-since before-send)) 6977 workspace)) 6978 (when callback 6979 (remhash id (lsp--client-response-handlers client)) 6980 (funcall callback (lsp:json-response-result json-data))))) 6981 ('response-error 6982 (cl-assert id) 6983 (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))] 6984 (when (lsp--log-io-p method) 6985 (lsp--log-entry-new 6986 (lsp--make-log-entry method id (lsp:json-response-error-error json-data) 6987 'incoming-resp (lsp--ms-since before-send)) 6988 workspace)) 6989 (when callback 6990 (remhash id (lsp--client-response-handlers client)) 6991 (funcall callback (lsp:json-response-error-error json-data))))) 6992 ('notification 6993 (lsp--on-notification workspace json-data)) 6994 ('request (lsp--on-request workspace json-data))))))) 6995 6996 (defun lsp--create-filter-function (workspace) 6997 "Make filter for the workspace." 6998 (let ((body-received 0) 6999 leftovers body-length body chunk) 7000 (lambda (_proc input) 7001 (setf chunk (if (s-blank? leftovers) 7002 input 7003 (concat leftovers input))) 7004 7005 (let (messages) 7006 (while (not (s-blank? chunk)) 7007 (if (not body-length) 7008 ;; Read headers 7009 (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk))) 7010 ;; We've got all the headers, handle them all at once: 7011 (setf body-length (lsp--get-body-length 7012 (mapcar #'lsp--parse-header 7013 (split-string 7014 (substring-no-properties chunk 7015 (or (string-match-p "Content-Length" chunk) 7016 (error "Unable to find Content-Length header.")) 7017 body-sep-pos) 7018 "\r\n"))) 7019 body-received 0 7020 leftovers nil 7021 chunk (substring-no-properties chunk (+ body-sep-pos 4))) 7022 7023 ;; Haven't found the end of the headers yet. Save everything 7024 ;; for when the next chunk arrives and await further input. 7025 (setf leftovers chunk 7026 chunk nil)) 7027 (let* ((chunk-length (string-bytes chunk)) 7028 (left-to-receive (- body-length body-received)) 7029 (this-body (if (< left-to-receive chunk-length) 7030 (prog1 (substring-no-properties chunk 0 left-to-receive) 7031 (setf chunk (substring-no-properties chunk left-to-receive))) 7032 (prog1 chunk 7033 (setf chunk nil)))) 7034 (body-bytes (string-bytes this-body))) 7035 (push this-body body) 7036 (setf body-received (+ body-received body-bytes)) 7037 (when (>= chunk-length left-to-receive) 7038 (condition-case err 7039 (with-temp-buffer 7040 (apply #'insert 7041 (nreverse 7042 (prog1 body 7043 (setf leftovers nil 7044 body-length nil 7045 body-received nil 7046 body nil)))) 7047 (decode-coding-region (point-min) 7048 (point-max) 7049 'utf-8) 7050 (goto-char (point-min)) 7051 (push (lsp-json-read-buffer) messages)) 7052 7053 (error 7054 (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s" 7055 (concat leftovers input) 7056 err))))))) 7057 (mapc (lambda (msg) 7058 (lsp--parser-on-message msg workspace)) 7059 (nreverse messages)))))) 7060 7061 (defvar-local lsp--line-col-to-point-hash-table nil 7062 "Hash table with keys (line . col) and values that are either point positions 7063 or markers.") 7064 7065 (defcustom lsp-imenu-detailed-outline t 7066 "Whether `lsp-imenu' should include signatures. 7067 This will be ignored if the server doesn't provide the necessary 7068 information, for example if it doesn't support DocumentSymbols." 7069 :group 'lsp-imenu 7070 :type 'boolean) 7071 7072 (defcustom lsp-imenu-hide-parent-details t 7073 "Whether `lsp-imenu' should hide signatures of parent nodes." 7074 :group 'lsp-imenu 7075 :type 'boolean) 7076 7077 (defface lsp-details-face '((t :height 0.8 :inherit shadow)) 7078 "Used to display additional information throughout `lsp'. 7079 Things like line numbers, signatures, ... are considered 7080 additional information. Often, additional faces are defined that 7081 inherit from this face by default, like `lsp-signature-face', and 7082 they may be customized for finer control." 7083 :group 'lsp-mode) 7084 7085 (defface lsp-signature-face '((t :inherit lsp-details-face)) 7086 "Used to display signatures in `imenu', ...." 7087 :group 'lsp-mode) 7088 7089 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?) 7090 show-detail?) 7091 "Render INPUT0, an `&DocumentSymbol', to a string. 7092 If SHOW-DETAIL? is set, make use of its `:detail?' field (often 7093 the signature)." 7094 (let ((detail (and show-detail? (s-present? detail?) 7095 (propertize (concat " " (s-trim-left detail?)) 7096 'face 'lsp-signature-face))) 7097 (name (if deprecated? 7098 (propertize name 'face 'lsp-face-semhl-deprecated) name))) 7099 (concat name detail))) 7100 7101 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?) 7102 separator) 7103 "Render a piece of SymbolInformation. 7104 Handle :deprecated?. If SEPARATOR is non-nil, the 7105 symbol's (optional) parent, SEPARATOR and the symbol itself are 7106 concatenated." 7107 (when (and separator container-name? (not (string-empty-p container-name?))) 7108 (setq name (concat name separator container-name?))) 7109 (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name)) 7110 7111 (defun lsp--symbol-to-imenu-elem (sym) 7112 "Convert SYM to imenu element. 7113 7114 SYM is a SymbolInformation message. 7115 7116 Return a cons cell (full-name . start-point)." 7117 (let ((start-point (ht-get lsp--line-col-to-point-hash-table 7118 (lsp--get-line-and-col sym)))) 7119 (cons (lsp-render-symbol-information 7120 sym (and lsp-imenu-show-container-name 7121 lsp-imenu-container-name-separator)) 7122 start-point))) 7123 7124 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?)) 7125 "Convert SYM to hierarchical imenu elements. 7126 7127 SYM is a DocumentSymbol message. 7128 7129 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if 7130 SYM doesn't have any children. Otherwise return a cons cell with 7131 an alist 7132 7133 (\"symbol-name\" . ((\"(symbol-kind)\" . start-point) 7134 cons-cells-from-children))" 7135 (let ((filtered-children (lsp--imenu-filter-symbols children?)) 7136 (signature (lsp-render-symbol sym lsp-imenu-detailed-outline))) 7137 (if (seq-empty-p filtered-children) 7138 (cons signature 7139 (ht-get lsp--line-col-to-point-hash-table 7140 (lsp--get-line-and-col sym))) 7141 (cons signature 7142 (lsp--imenu-create-hierarchical-index filtered-children))))) 7143 7144 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind)) 7145 "Determine if SYM is for the current document and is to be shown." 7146 ;; It's a SymbolInformation or DocumentSymbol, which is always in the 7147 ;; current buffer file. 7148 (and lsp-imenu-index-symbol-kinds 7149 (numberp kind) 7150 (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup)) 7151 kind 7152 0))) 7153 (not (memql (aref lsp/symbol-kind-lookup clamped-kind) 7154 lsp-imenu-index-symbol-kinds))))) 7155 7156 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind)) 7157 "The string name of the kind of SYM." 7158 (alist-get kind lsp-symbol-kinds "Other")) 7159 7160 (defun lsp--get-line-and-col (sym) 7161 "Obtain the line and column corresponding to SYM." 7162 (-let* ((location (lsp:symbol-information-location sym)) 7163 (name-range (or (and location (lsp:location-range location)) 7164 (lsp:document-symbol-selection-range sym))) 7165 ((&Range :start (&Position :line :character)) name-range)) 7166 (cons line character))) 7167 7168 (defun lsp--collect-lines-and-cols (symbols) 7169 "Return a sorted list ((line . col) ...) of the locations of SYMBOLS." 7170 (let ((stack (mapcar 'identity symbols)) 7171 line-col-list) 7172 (while stack 7173 (let ((sym (pop stack))) 7174 (push (lsp--get-line-and-col sym) line-col-list) 7175 (unless (seq-empty-p (lsp:document-symbol-children? sym)) 7176 (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack))))) 7177 (-sort #'lsp--line-col-comparator line-col-list))) 7178 7179 (defun lsp--convert-line-col-to-points-batch (line-col-list) 7180 "Convert a sorted list of positions from line-column 7181 representation to point representation." 7182 (let ((line-col-to-point-map (ht-create)) 7183 (inhibit-field-text-motion t) 7184 (curr-line 0)) 7185 (lsp-save-restriction-and-excursion 7186 (goto-char (point-min)) 7187 (cl-loop for (line . col) in line-col-list do 7188 (forward-line (- line curr-line)) 7189 (setq curr-line line) 7190 (let ((line-end (line-end-position))) 7191 (if (or (not col) (> col (- line-end (point)))) 7192 (goto-char line-end) 7193 (forward-char col))) 7194 (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers 7195 (point-marker) 7196 (point))))) 7197 line-col-to-point-map)) 7198 7199 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2)) 7200 (or (< l1 l2) 7201 (and (= l1 l2) 7202 (cond ((and c1 c2) 7203 (< c1 c2)) 7204 (c1 t))))) 7205 7206 (defun lsp-imenu-create-uncategorized-index (symbols) 7207 "Create imenu index from document SYMBOLS. 7208 This function, unlike `lsp-imenu-create-categorized-index', does 7209 not categorize by type, but instead returns an `imenu' index 7210 corresponding to the symbol hierarchy returned by the server 7211 directly." 7212 (let* ((lsp--line-col-to-point-hash-table (-> symbols 7213 lsp--collect-lines-and-cols 7214 lsp--convert-line-col-to-points-batch))) 7215 (if (lsp--imenu-hierarchical-p symbols) 7216 (lsp--imenu-create-hierarchical-index symbols) 7217 (lsp--imenu-create-non-hierarchical-index symbols)))) 7218 7219 (defcustom lsp-imenu-symbol-kinds 7220 '((1 . "Files") 7221 (2 . "Modules") 7222 (3 . "Namespaces") 7223 (4 . "Packages") 7224 (5 . "Classes") 7225 (6 . "Methods") 7226 (7 . "Properties") 7227 (8 . "Fields") 7228 (9 . "Constructors") 7229 (10 . "Enums") 7230 (11 . "Interfaces") 7231 (12 . "Functions") 7232 (13 . "Variables") 7233 (14 . "Constants") 7234 (15 . "Strings") 7235 (16 . "Numbers") 7236 (17 . "Booleans") 7237 (18 . "Arrays") 7238 (19 . "Objects") 7239 (20 . "Keys") 7240 (21 . "Nulls") 7241 (22 . "Enum Members") 7242 (23 . "Structs") 7243 (24 . "Events") 7244 (25 . "Operators") 7245 (26 . "Type Parameters")) 7246 "`lsp-symbol-kinds', but only used by `imenu'. 7247 A new variable is needed, as it is `imenu' convention to use 7248 pluralized categories, which `lsp-symbol-kinds' doesn't. If the 7249 non-pluralized names are preferred, this can be set to 7250 `lsp-symbol-kinds'." 7251 :type '(alist :key-type integer :value-type string)) 7252 7253 (defun lsp--imenu-kind->name (kind) 7254 (alist-get kind lsp-imenu-symbol-kinds "?")) 7255 7256 (defun lsp-imenu-create-top-level-categorized-index (symbols) 7257 "Create an `imenu' index categorizing SYMBOLS by type. 7258 Only root symbols are categorized. 7259 7260 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS 7261 shall be a list of DocumentSymbols or SymbolInformation." 7262 (mapcan 7263 (-lambda ((type . symbols)) 7264 (let ((cat (lsp--imenu-kind->name type)) 7265 (symbols (lsp-imenu-create-uncategorized-index symbols))) 7266 ;; If there is no :kind (this is being defensive), or we couldn't look it 7267 ;; up, just display the symbols inline, without categories. 7268 (if cat (list (cons cat symbols)) symbols))) 7269 (sort (seq-group-by #'lsp:document-symbol-kind symbols) 7270 (-lambda ((kinda) (kindb)) (< kinda kindb))))) 7271 7272 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start))) 7273 "Convert an `&DocumentSymbol' to an `imenu' entry." 7274 (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start)) 7275 7276 (defun lsp--imenu-create-categorized-index-1 (symbols) 7277 "Returns an `imenu' index from SYMBOLS categorized by type. 7278 The result looks like this: ((\"Variables\" . (...)))." 7279 (->> 7280 symbols 7281 (mapcan 7282 (-lambda ((sym &as &DocumentSymbol :kind :children?)) 7283 (if (seq-empty-p children?) 7284 (list (list kind (lsp--symbol->imenu sym))) 7285 (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline 7286 (not lsp-imenu-hide-parent-details))))) 7287 (cons 7288 (list kind (lsp--symbol->imenu sym)) 7289 (mapcar (-lambda ((type . imenu-items)) 7290 (list type (cons parent (mapcan #'cdr imenu-items)))) 7291 (-group-by #'car (lsp--imenu-create-categorized-index-1 children?)))))))) 7292 (-group-by #'car) 7293 (mapcar 7294 (-lambda ((kind . syms)) 7295 (cons kind (mapcan #'cdr syms)))))) 7296 7297 (defun lsp--imenu-create-categorized-index (symbols) 7298 (let ((syms (lsp--imenu-create-categorized-index-1 symbols))) 7299 (dolist (sym syms) 7300 (setcar sym (lsp--imenu-kind->name (car sym)))) 7301 syms)) 7302 7303 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start)))) 7304 (cons (lsp-render-symbol-information sym nil) start)) 7305 7306 (defun lsp--imenu-create-categorized-index-flat (symbols) 7307 "Create a kind-categorized index for SymbolInformation." 7308 (mapcar (-lambda ((kind . syms)) 7309 (cons (lsp--imenu-kind->name kind) 7310 (mapcan (-lambda ((parent . children)) 7311 (let ((children (mapcar #'lsp--symbol-information->imenu children))) 7312 (if parent (list (cons parent children)) children))) 7313 (-group-by #'lsp:symbol-information-container-name? syms)))) 7314 (seq-group-by #'lsp:symbol-information-kind symbols))) 7315 7316 (defun lsp-imenu-create-categorized-index (symbols) 7317 (if (lsp--imenu-hierarchical-p symbols) 7318 (lsp--imenu-create-categorized-index symbols) 7319 (lsp--imenu-create-categorized-index-flat symbols))) 7320 7321 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index 7322 "Function that should create an `imenu' index. 7323 It will be called with a list of SymbolInformation or 7324 DocumentSymbols, whose first level is already filtered. It shall 7325 then return an appropriate `imenu' index (see 7326 `imenu-create-index-function'). 7327 7328 Note that this interface is not stable, and subject to change any 7329 time." 7330 :group 'lsp-imenu 7331 :type '(radio 7332 (const :tag "Categorize by type" 7333 lsp-imenu-create-categorized-index) 7334 (const :tag "Categorize root symbols by type" 7335 lsp-imenu-create-top-level-categorized-index) 7336 (const :tag "Uncategorized, inline entries" 7337 lsp-imenu-create-uncategorized-index) 7338 (function :tag "Custom function"))) 7339 7340 (defun lsp--imenu-create-index () 7341 "Create an `imenu' index based on the language server. 7342 Respects `lsp-imenu-index-function'." 7343 (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols)))) 7344 (funcall lsp-imenu-index-function symbols))) 7345 7346 (defun lsp--imenu-filter-symbols (symbols) 7347 "Filter out unsupported symbols from SYMBOLS." 7348 (seq-remove #'lsp--symbol-ignore symbols)) 7349 7350 (defun lsp--imenu-hierarchical-p (symbols) 7351 "Determine whether any element in SYMBOLS has children." 7352 (seq-some #'lsp-document-symbol? symbols)) 7353 7354 (defun lsp--imenu-create-non-hierarchical-index (symbols) 7355 "Create imenu index for non-hierarchical SYMBOLS. 7356 7357 SYMBOLS are a list of DocumentSymbol messages. 7358 7359 Return a nested alist keyed by symbol names. e.g. 7360 7361 ((\"SomeClass\" (\"(Class)\" . 10) 7362 (\"someField (Field)\" . 20) 7363 (\"someFunction (Function)\" . 25) 7364 (\"SomeSubClass\" (\"(Class)\" . 30) 7365 (\"someSubField (Field)\" . 35)) 7366 (\"someFunction (Function)\" . 40))" 7367 (seq-map (lambda (nested-alist) 7368 (cons (car nested-alist) 7369 (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist)))) 7370 (seq-group-by #'lsp--get-symbol-type symbols))) 7371 7372 (defun lsp--imenu-create-hierarchical-index (symbols) 7373 "Create imenu index for hierarchical SYMBOLS. 7374 7375 SYMBOLS are a list of DocumentSymbol messages. 7376 7377 Return a nested alist keyed by symbol names. e.g. 7378 7379 ((\"SomeClass\" (\"(Class)\" . 10) 7380 (\"someField (Field)\" . 20) 7381 (\"someFunction (Function)\" . 25) 7382 (\"SomeSubClass\" (\"(Class)\" . 30) 7383 (\"someSubField (Field)\" . 35)) 7384 (\"someFunction (Function)\" . 40))" 7385 (seq-map #'lsp--symbol-to-hierarchical-imenu-elem 7386 (seq-sort #'lsp--imenu-symbol-lessp symbols))) 7387 7388 (defun lsp--imenu-symbol-lessp (sym1 sym2) 7389 (let* ((compare-results (mapcar (lambda (method) 7390 (funcall (alist-get method lsp--imenu-compare-function-alist) 7391 sym1 sym2)) 7392 lsp-imenu-sort-methods)) 7393 (result (seq-find (lambda (result) 7394 (not (= result 0))) 7395 compare-results 7396 0))) 7397 (and (numberp result) (< result 0)))) 7398 7399 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left) 7400 (&SymbolInformation :kind right)) 7401 "Compare SYM1 and SYM2 by kind." 7402 (- left right)) 7403 7404 (defun lsp--imenu-compare-line-col (sym1 sym2) 7405 (if (lsp--line-col-comparator 7406 (lsp--get-line-and-col sym1) 7407 (lsp--get-line-and-col sym2)) 7408 -1 7409 1)) 7410 7411 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1) 7412 (&SymbolInformation :name name2)) 7413 "Compare SYM1 and SYM2 by name." 7414 (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2)))) 7415 (if (numberp result) result 0))) 7416 7417 (defun lsp--imenu-refresh () 7418 "Force Imenu to refresh itself." 7419 (imenu--menubar-select imenu--rescan-item)) 7420 7421 (defun lsp-enable-imenu () 7422 "Use lsp-imenu for the current buffer." 7423 (imenu--cleanup) 7424 (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index) 7425 (setq-local imenu-menubar-modified-tick -1) 7426 (setq-local imenu--index-alist nil) 7427 (when menu-bar-mode 7428 (lsp--imenu-refresh))) 7429 7430 (defun lsp-resolve-final-command (command &optional test?) 7431 "Resolve final function COMMAND." 7432 (let* ((command (lsp-resolve-value command)) 7433 (command (cl-etypecase command 7434 (list 7435 (cl-assert (seq-every-p (apply-partially #'stringp) command) nil 7436 "Invalid command list") 7437 command) 7438 (string (list command))))) 7439 (if (and (file-remote-p default-directory) (not test?)) 7440 (list shell-file-name "-c" 7441 (string-join (cons "stty raw > /dev/null;" 7442 (mapcar #'shell-quote-argument command)) 7443 " ")) 7444 command))) 7445 7446 (defun lsp-server-present? (final-command) 7447 "Check whether FINAL-COMMAND is present." 7448 (let ((binary-found? (executable-find (cl-first final-command) t))) 7449 (if binary-found? 7450 (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command)) 7451 (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command))) 7452 binary-found?)) 7453 7454 (defun lsp--value-to-string (value) 7455 "Convert VALUE to a string that can be set as value in an environment 7456 variable." 7457 (cond 7458 ((stringp value) value) 7459 ((booleanp value) (if value 7460 "1" 7461 "0")) 7462 ((and (sequencep value) 7463 (seq-every-p #'stringp value)) (string-join value ":")) 7464 (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables")))) 7465 7466 (defun lsp--compute-process-environment (environment-fn) 7467 "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'. 7468 Ignore non-boolean keys whose value is nil." 7469 (let ((environment (if environment-fn 7470 (funcall environment-fn) 7471 nil))) 7472 (-flatten (cons (cl-loop for (key . value) in environment 7473 if (or (eval value) 7474 (eq (get value 'custom-type) 'boolean)) 7475 collect (concat key "=" (lsp--value-to-string 7476 (eval value)))) 7477 process-environment)))) 7478 7479 (defun lsp--default-directory-for-connection (&optional path) 7480 "Return path to be used for the working directory of a LSP process. 7481 7482 If `lsp-use-workspace-root-for-server-default-directory' is 7483 non-nil, uses `lsp-workspace-root' to find the directory 7484 corresponding to PATH, else returns `default-directory'." 7485 (if lsp-use-workspace-root-for-server-default-directory 7486 (lsp-workspace-root path) 7487 default-directory)) 7488 7489 (defun lsp--fix-remote-cmd (program) 7490 "Helper for `lsp-stdio-connection'. 7491 Originally coppied from eglot." 7492 7493 (if (file-remote-p default-directory) 7494 (list shell-file-name "-c" 7495 (string-join (cons "stty raw > /dev/null;" 7496 (mapcar #'shell-quote-argument program)) 7497 " ")) 7498 program)) 7499 7500 (defvar tramp-use-ssh-controlmaster-options) 7501 (defvar tramp-ssh-controlmaster-options) 7502 7503 (defun lsp-stdio-connection (command &optional test-command) 7504 "Returns a connection property list using COMMAND. 7505 COMMAND can be: A string, denoting the command to launch the 7506 language server. A list of strings, denoting an executable with 7507 its command line arguments. A function, that either returns a 7508 string or a list of strings. In all cases, the launched language 7509 server should send and receive messages on standard I/O. 7510 TEST-COMMAND is a function with no arguments which returns 7511 whether the command is present or not. When not specified 7512 `lsp-mode' will check whether the first element of the list 7513 returned by COMMAND is available via `executable-find'" 7514 (cl-check-type command (or string 7515 function 7516 (and list 7517 (satisfies (lambda (l) 7518 (seq-every-p (lambda (el) 7519 (stringp el)) 7520 l)))))) 7521 (list :connect (lambda (filter sentinel name environment-fn workspace) 7522 (if (and (functionp 'json-rpc-connection) 7523 (not (file-remote-p default-directory))) 7524 (lsp-json-rpc-connection workspace (lsp-resolve-final-command command)) 7525 (let ((final-command (lsp-resolve-final-command command)) 7526 (process-name (generate-new-buffer-name name)) 7527 (process-environment 7528 (lsp--compute-process-environment environment-fn))) 7529 (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name))) 7530 (default-directory (lsp--default-directory-for-connection)) 7531 (tramp-use-ssh-controlmaster-options 'suppress) 7532 (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none") 7533 (proc (make-process 7534 :name process-name 7535 :connection-type 'pipe 7536 :buffer (format "*%s*" process-name) 7537 :coding 'no-conversion 7538 :command final-command 7539 :filter filter 7540 :sentinel sentinel 7541 :stderr stderr-buf 7542 :noquery t 7543 :file-handler t))) 7544 (set-process-query-on-exit-flag proc nil) 7545 (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) 7546 (with-current-buffer (get-buffer stderr-buf) 7547 ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc. 7548 (special-mode)) 7549 (cons proc proc))))) 7550 :test? (or 7551 test-command 7552 (lambda () 7553 (lsp-server-present? (lsp-resolve-final-command command t)))))) 7554 7555 (defun lsp--open-network-stream (host port name) 7556 "Open network stream to HOST:PORT. 7557 NAME will be passed to `open-network-stream'. 7558 RETRY-COUNT is the number of the retries. 7559 SLEEP-INTERVAL is the sleep interval between each retry." 7560 (let* ((retries 0) 7561 (sleep-interval 0.01) 7562 (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval)) 7563 connection) 7564 (while (and (not connection) (< retries number-of-retries)) 7565 (condition-case err 7566 (setq connection (open-network-stream name nil host port 7567 :type 'plain 7568 :coding 'no-conversion)) 7569 (file-error 7570 (let ((inhibit-message t)) 7571 (lsp--warn "Failed to connect to %s:%s with error message %s" 7572 host 7573 port 7574 (error-message-string err)) 7575 (sleep-for sleep-interval) 7576 (cl-incf retries))))) 7577 (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port)))) 7578 7579 (defun lsp--port-available (host port) 7580 "Return non-nil if HOST and PORT are available." 7581 (condition-case _err 7582 (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain)) 7583 (file-error t))) 7584 7585 (defun lsp--find-available-port (host starting-port) 7586 "Find available port on HOST starting from STARTING-PORT." 7587 (let ((port starting-port)) 7588 (while (not (lsp--port-available host port)) 7589 (cl-incf port)) 7590 port)) 7591 7592 (defun lsp-tcp-connection (command-fn) 7593 "Returns a connection property list similar to `lsp-stdio-connection'. 7594 COMMAND-FN can only be a function that takes a single argument, a 7595 port number. It should return a command for launches a language server 7596 process listening for TCP connections on the provided port." 7597 (cl-check-type command-fn function) 7598 (list 7599 :connect (lambda (filter sentinel name environment-fn _workspace) 7600 (let* ((host "localhost") 7601 (port (lsp--find-available-port host (cl-incf lsp--tcp-port))) 7602 (command (funcall command-fn port)) 7603 (final-command (if (consp command) command (list command))) 7604 (_ (unless (lsp-server-present? final-command) 7605 (user-error (format "Couldn't find executable %s" (cl-first final-command))))) 7606 (process-environment 7607 (lsp--compute-process-environment environment-fn)) 7608 (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion 7609 :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t)) 7610 (tcp-proc (lsp--open-network-stream host port (concat name "::tcp")))) 7611 7612 ;; TODO: Same :noquery issue (see above) 7613 (set-process-query-on-exit-flag proc nil) 7614 (set-process-query-on-exit-flag tcp-proc nil) 7615 (set-process-filter tcp-proc filter) 7616 (cons tcp-proc proc))) 7617 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7618 7619 (defalias 'lsp-tcp-server 'lsp-tcp-server-command) 7620 7621 (defun lsp-tcp-server-command (command-fn) 7622 "Create tcp server connection. 7623 In this mode Emacs is TCP server and the language server connects 7624 to it. COMMAND is function with one parameter(the port) and it 7625 should return the command to start the LS server." 7626 (cl-check-type command-fn function) 7627 (list 7628 :connect (lambda (filter sentinel name environment-fn _workspace) 7629 (let* (tcp-client-connection 7630 (tcp-server (make-network-process :name (format "*tcp-server-%s*" name) 7631 :buffer (format "*tcp-server-%s*" name) 7632 :family 'ipv4 7633 :service lsp--tcp-server-port 7634 :sentinel (lambda (proc _string) 7635 (lsp-log "Language server %s is connected." name) 7636 (setf tcp-client-connection proc)) 7637 :server 't)) 7638 (port (process-contact tcp-server :service)) 7639 (final-command (funcall command-fn port)) 7640 (process-environment 7641 (lsp--compute-process-environment environment-fn)) 7642 (cmd-proc (make-process :name name 7643 :connection-type 'pipe 7644 :coding 'no-conversion 7645 :command final-command 7646 :stderr (format "*tcp-server-%s*::stderr" name) 7647 :noquery t))) 7648 (let ((retries 0)) 7649 ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds) 7650 (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds))) 7651 (lsp--info "Waiting for connection for %s, retries: %s" name retries) 7652 (sit-for 0.500) 7653 (cl-incf retries))) 7654 7655 (unless tcp-client-connection 7656 (condition-case nil (delete-process tcp-server) (error)) 7657 (condition-case nil (delete-process cmd-proc) (error)) 7658 (error "Failed to create connection to %s on port %s" name port)) 7659 (lsp--info "Successfully connected to %s" name) 7660 7661 (set-process-query-on-exit-flag cmd-proc nil) 7662 (set-process-query-on-exit-flag tcp-client-connection nil) 7663 (set-process-query-on-exit-flag tcp-server nil) 7664 7665 (set-process-filter tcp-client-connection filter) 7666 (set-process-sentinel tcp-client-connection sentinel) 7667 (cons tcp-client-connection cmd-proc))) 7668 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7669 7670 (defalias 'lsp-tramp-connection 'lsp-stdio-connection) 7671 7672 (defun lsp--auto-configure () 7673 "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed." 7674 (when (functionp 'lsp-ui-mode) 7675 (lsp-ui-mode)) 7676 7677 (if lsp-headerline-breadcrumb-enable 7678 (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode) 7679 (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)) 7680 (if lsp-modeline-code-actions-enable 7681 (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode) 7682 (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)) 7683 (if lsp-modeline-diagnostics-enable 7684 (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode) 7685 (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)) 7686 (if lsp-modeline-workspace-status-enable 7687 (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode) 7688 (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)) 7689 (if lsp-lens-enable 7690 (add-hook 'lsp-configure-hook 'lsp-lens--enable) 7691 (remove-hook 'lsp-configure-hook 'lsp-lens--enable)) 7692 (if lsp-semantic-tokens-enable 7693 (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable) 7694 (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)) 7695 7696 ;; yas-snippet config 7697 (setq-local yas-inhibit-overlay-modification-protection t)) 7698 7699 (defun lsp--restart-if-needed (workspace) 7700 "Handler restart for WORKSPACE." 7701 (when (or (eq lsp-restart 'auto-restart) 7702 (eq (lsp--workspace-shutdown-action workspace) 'restart) 7703 (and (eq lsp-restart 'interactive) 7704 (let ((query (format 7705 "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?" 7706 (lsp--workspace-print workspace)))) 7707 (y-or-n-p query)))) 7708 (--each (lsp--workspace-buffers workspace) 7709 (when (lsp-buffer-live-p it) 7710 (lsp-with-current-buffer it 7711 (if lsp--buffer-deferred 7712 (lsp-deferred) 7713 (lsp--info "Restarting LSP in buffer %s" (buffer-name)) 7714 (lsp))))))) 7715 7716 (defun lsp--update-key (table key fn) 7717 "Apply FN on value corresponding to KEY in TABLE." 7718 (let ((existing-value (gethash key table))) 7719 (if-let ((new-value (funcall fn existing-value))) 7720 (puthash key new-value table) 7721 (remhash key table)))) 7722 7723 (defun lsp--process-sentinel (workspace process exit-str) 7724 "Create the sentinel for WORKSPACE." 7725 (unless (process-live-p process) 7726 (lsp--handle-process-exit workspace exit-str))) 7727 7728 (defun lsp--handle-process-exit (workspace exit-str) 7729 (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session))) 7730 (proc (lsp--workspace-proc workspace))) 7731 (lsp--warn "%s has exited (%s)" 7732 (lsp-process-name proc) 7733 (string-trim-right (or exit-str ""))) 7734 (with-lsp-workspace workspace 7735 ;; Clean workspace related data in each of the buffers 7736 ;; in the workspace. 7737 (--each (lsp--workspace-buffers workspace) 7738 (when (lsp-buffer-live-p it) 7739 (lsp-with-current-buffer it 7740 (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces)) 7741 (lsp--uninitialize-workspace) 7742 (lsp--spinner-stop) 7743 (lsp--remove-overlays 'lsp-highlight)))) 7744 7745 ;; Cleanup session from references to the closed workspace. 7746 (--each (hash-table-keys folder->workspaces) 7747 (lsp--update-key folder->workspaces it (apply-partially 'delete workspace))) 7748 7749 (lsp-process-cleanup proc)) 7750 7751 (run-hook-with-args 'lsp-after-uninitialized-functions workspace) 7752 7753 (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown) 7754 (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace)) 7755 (lsp--restart-if-needed workspace)) 7756 (lsp--cleanup-hanging-watches))) 7757 7758 (defun lsp-workspace-folders (workspace) 7759 "Return all folders associated with WORKSPACE." 7760 (let (result) 7761 (->> (lsp-session) 7762 (lsp-session-folder->servers) 7763 (maphash (lambda (folder workspaces) 7764 (when (-contains? workspaces workspace) 7765 (push folder result))))) 7766 result)) 7767 7768 (defun lsp--start-workspace (session client-template root &optional initialization-options) 7769 "Create new workspace for CLIENT-TEMPLATE with project root ROOT. 7770 INITIALIZATION-OPTIONS are passed to initialize function. 7771 SESSION is the active session." 7772 (lsp--spinner-start) 7773 (-let* ((default-directory root) 7774 (client (copy-lsp--client client-template)) 7775 (workspace (make-lsp--workspace 7776 :root root 7777 :client client 7778 :status 'starting 7779 :buffers (list (lsp-current-buffer)) 7780 :host-root (file-remote-p root))) 7781 ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities 7782 'multi-root 'initialized-fn) client) 7783 ((proc . cmd-proc) (funcall 7784 (or (plist-get new-connection :connect) 7785 (user-error "Client %s is configured incorrectly" client)) 7786 (lsp--create-filter-function workspace) 7787 (apply-partially #'lsp--process-sentinel workspace) 7788 (format "%s" server-id) 7789 environment-fn 7790 workspace)) 7791 (workspace-folders (gethash server-id (lsp-session-server-id->folders session)))) 7792 (setf (lsp--workspace-proc workspace) proc 7793 (lsp--workspace-cmd-proc workspace) cmd-proc) 7794 7795 ;; update (lsp-session-folder->servers) depending on whether we are starting 7796 ;; multi/single folder workspace 7797 (mapc (lambda (project-root) 7798 (->> session 7799 (lsp-session-folder->servers) 7800 (gethash project-root) 7801 (cl-pushnew workspace))) 7802 (or workspace-folders (list root))) 7803 7804 (with-lsp-workspace workspace 7805 (run-hooks 'lsp-before-initialize-hook) 7806 (lsp-request-async 7807 "initialize" 7808 (append 7809 (list :processId (unless (file-remote-p (buffer-file-name)) 7810 (emacs-pid)) 7811 :rootPath (lsp-file-local-name (expand-file-name root)) 7812 :clientInfo (list :name "emacs" 7813 :version (emacs-version)) 7814 :rootUri (lsp--path-to-uri root) 7815 :capabilities (lsp--client-capabilities custom-capabilities) 7816 :initializationOptions initialization-options 7817 :workDoneToken "1") 7818 (when lsp-server-trace 7819 (list :trace lsp-server-trace)) 7820 (when multi-root 7821 (->> workspace-folders 7822 (-distinct) 7823 (-map (lambda (folder) 7824 (list :uri (lsp--path-to-uri folder) 7825 :name (f-filename folder)))) 7826 (apply 'vector) 7827 (list :workspaceFolders)))) 7828 (-lambda ((&InitializeResult :capabilities)) 7829 ;; we know that Rust Analyzer will send {} which will be parsed as null 7830 ;; when using plists 7831 (when (equal 'rust-analyzer server-id) 7832 (-> capabilities 7833 (lsp:server-capabilities-text-document-sync?) 7834 (lsp:set-text-document-sync-options-save? t))) 7835 7836 (setf (lsp--workspace-server-capabilities workspace) capabilities 7837 (lsp--workspace-status workspace) 'initialized) 7838 7839 (with-lsp-workspace workspace 7840 (lsp-notify "initialized" lsp--empty-ht)) 7841 7842 (when initialized-fn (funcall initialized-fn workspace)) 7843 7844 (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace)) 7845 (->> workspace 7846 (lsp--workspace-buffers) 7847 (mapc (lambda (buffer) 7848 (lsp-with-current-buffer buffer 7849 (lsp--open-in-workspace workspace))))) 7850 7851 (with-lsp-workspace workspace 7852 (run-hooks 'lsp-after-initialize-hook)) 7853 (lsp--info "%s initialized successfully in folders: %s" 7854 (lsp--workspace-print workspace) 7855 (lsp-workspace-folders workspace))) 7856 :mode 'detached)) 7857 workspace)) 7858 7859 (defun lsp--load-default-session () 7860 "Load default session." 7861 (setq lsp--session (or (condition-case err 7862 (lsp--read-from-file lsp-session-file) 7863 (error (lsp--error "Failed to parse the session %s, starting with clean one." 7864 (error-message-string err)) 7865 nil)) 7866 (make-lsp-session)))) 7867 7868 (defun lsp-session () 7869 "Get the session associated with the current buffer." 7870 (or lsp--session (setq lsp--session (lsp--load-default-session)))) 7871 7872 (defun lsp--client-disabled-p (buffer-major-mode client) 7873 (seq-some 7874 (lambda (entry) 7875 (pcase entry 7876 ((pred symbolp) (eq entry client)) 7877 (`(,mode . ,client-or-list) 7878 (and (eq mode buffer-major-mode) 7879 (if (listp client-or-list) 7880 (memq client client-or-list) 7881 (eq client client-or-list)))))) 7882 lsp-disabled-clients)) 7883 7884 7885 ;; download server 7886 7887 (defcustom lsp-server-install-dir (expand-file-name 7888 (locate-user-emacs-file (f-join ".cache" "lsp"))) 7889 "Directory in which the servers will be installed." 7890 :risky t 7891 :type 'directory 7892 :package-version '(lsp-mode . "6.3") 7893 :group 'lsp-mode) 7894 7895 (defcustom lsp-verify-signature t 7896 "Whether to check GPG signatures of downloaded files." 7897 :type 'boolean 7898 :package-version '(lsp-mode . "8.0.0") 7899 :group 'lsp-mode) 7900 7901 (defvar lsp--dependencies (ht)) 7902 7903 (defun lsp-dependency (name &rest definitions) 7904 "Used to specify a language server DEPENDENCY, the server 7905 executable or other required file path. Typically, the 7906 DEPENDENCY is found by locating it on the system path using 7907 `executable-find'. 7908 7909 You can explicitly call lsp-dependency in your environment to 7910 specify the absolute path to the DEPENDENCY. For example, the 7911 typescript-language-server requires both the server and the 7912 typescript compiler. If you have installed them in a team shared 7913 read-only location, you can instruct lsp-mode to use them via 7914 7915 (eval-after-load `lsp-mode 7916 `(progn 7917 (require lsp-javascript) 7918 (lsp-dependency typescript-language-server (:system ,tls-exe)) 7919 (lsp-dependency typescript (:system ,ts-js)))) 7920 7921 where tls-exe is the absolute path to the typescript-language-server 7922 executable and ts-js is the absolute path to the typescript compiler 7923 JavaScript file, tsserver.js (the *.js is required for Windows)." 7924 (ht-set lsp--dependencies name definitions)) 7925 7926 (defun lsp--server-binary-present? (client) 7927 (unless (equal (lsp--client-server-id client) 'lsp-pwsh) 7928 (condition-case () 7929 (-some-> client lsp--client-new-connection (plist-get :test?) funcall) 7930 (error nil) 7931 (args-out-of-range nil)))) 7932 7933 (define-minor-mode lsp-installation-buffer-mode 7934 "Mode used in *lsp-installation* buffers. 7935 It can be used to set-up keybindings, etc. Disabling this mode 7936 detaches the installation buffer from commands like 7937 `lsp-select-installation-buffer'." 7938 :init-value nil 7939 :lighter nil) 7940 7941 (defface lsp-installation-finished-buffer-face '((t :foreground "orange")) 7942 "Face used for finished installation buffers. 7943 Used in `lsp-select-installation-buffer'." 7944 :group 'lsp-mode) 7945 7946 (defface lsp-installation-buffer-face '((t :foreground "green")) 7947 "Face used for installation buffers still in progress. 7948 Used in `lsp-select-installation-buffer'." 7949 :group 'lsp-mode) 7950 7951 (defun lsp--installation-buffer? (buf) 7952 "Check whether BUF is an `lsp-async-start-process' buffer." 7953 (buffer-local-value 'lsp-installation-buffer-mode buf)) 7954 7955 (defun lsp-select-installation-buffer (&optional show-finished) 7956 "Interactively choose an installation buffer. 7957 If SHOW-FINISHED is set, leftover (finished) installation buffers 7958 are still shown." 7959 (interactive "P") 7960 (let ((bufs (--filter (and (lsp--installation-buffer? it) 7961 (or show-finished (get-buffer-process it))) 7962 (buffer-list)))) 7963 (pcase bufs 7964 (`nil (user-error "No installation buffers")) 7965 (`(,buf) (pop-to-buffer buf)) 7966 (bufs (pop-to-buffer (completing-read "Select installation buffer: " 7967 (--map (propertize (buffer-name it) 'face 7968 (if (get-buffer-process it) 7969 'lsp-installation-buffer-face 7970 'lsp-installation-finished-buffer-face)) 7971 bufs))))))) 7972 7973 (defun lsp-cleanup-installation-buffers () 7974 "Delete finished *lsp-installation* buffers." 7975 (interactive) 7976 (dolist (buf (buffer-list)) 7977 (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf))) 7978 (kill-buffer buf)))) 7979 7980 (defun lsp--download-status () 7981 (-some--> #'lsp--client-download-in-progress? 7982 (lsp--filter-clients it) 7983 (-map (-compose #'symbol-name #'lsp--client-server-id) it) 7984 (format "%s" it) 7985 (propertize it 'face 'success) 7986 (format " Installing following servers: %s" it) 7987 (propertize it 7988 'local-map (make-mode-line-mouse-map 7989 'mouse-1 #'lsp-select-installation-buffer) 7990 'mouse-face 'highlight))) 7991 7992 (defun lsp--install-server-internal (client &optional update?) 7993 (unless (lsp--client-download-server-fn client) 7994 (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation." 7995 (lsp--client-server-id client))) 7996 7997 (setf (lsp--client-download-in-progress? client) t) 7998 (add-to-list 'global-mode-string '(t (:eval (lsp--download-status)))) 7999 (cl-flet ((done 8000 (success? &optional error-message) 8001 ;; run with idle timer to make sure the lsp command is executed in 8002 ;; the main thread, see #2739. 8003 (run-with-timer 8004 0.0 8005 nil 8006 (lambda () 8007 (-let [(&lsp-cln 'server-id 'buffers) client] 8008 (setf (lsp--client-download-in-progress? client) nil 8009 (lsp--client-buffers client) nil) 8010 (if success? 8011 (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id 8012 (length buffers)) 8013 (lsp--error "Server %s install process failed with the following error message: %s. 8014 Check `*lsp-install*' and `*lsp-log*' buffer." 8015 server-id 8016 error-message)) 8017 (seq-do 8018 (lambda (buffer) 8019 (when (lsp-buffer-live-p buffer) 8020 (lsp-with-current-buffer buffer 8021 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8022 global-mode-string) 8023 (when success? (lsp))))) 8024 buffers) 8025 (unless (lsp--filter-clients #'lsp--client-download-in-progress?) 8026 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8027 global-mode-string))))))) 8028 (lsp--info "Download %s started." (lsp--client-server-id client)) 8029 (condition-case err 8030 (funcall 8031 (lsp--client-download-server-fn client) 8032 client 8033 (lambda () (done t)) 8034 (lambda (msg) (done nil msg)) 8035 update?) 8036 (error 8037 (done nil (error-message-string err)))))) 8038 8039 (defun lsp--require-packages () 8040 "Load `lsp-client-packages' if needed." 8041 (when (and lsp-auto-configure (not lsp--client-packages-required)) 8042 (seq-do (lambda (package) 8043 ;; loading client is slow and `lsp' can be called repeatedly 8044 (unless (featurep package) 8045 (require package nil t))) 8046 lsp-client-packages) 8047 (setq lsp--client-packages-required t))) 8048 8049 ;;;###autoload 8050 (defun lsp-install-server (update? &optional server-id) 8051 "Interactively install or re-install server. 8052 When prefix UPDATE? is t force installation even if the server is present." 8053 (interactive "P") 8054 (lsp--require-packages) 8055 (let* ((chosen-client (or (gethash server-id lsp-clients) 8056 (lsp--completing-read 8057 "Select server to install/re-install: " 8058 (or (->> lsp-clients 8059 (ht-values) 8060 (-filter (-andfn 8061 (-not #'lsp--client-download-in-progress?) 8062 #'lsp--client-download-server-fn))) 8063 (user-error "There are no servers with automatic installation")) 8064 (lambda (client) 8065 (let ((server-name (-> client lsp--client-server-id symbol-name))) 8066 (if (lsp--server-binary-present? client) 8067 (concat server-name " (Already installed)") 8068 server-name))) 8069 nil 8070 t))) 8071 (update? (or update? 8072 (and (not (lsp--client-download-in-progress? chosen-client)) 8073 (lsp--server-binary-present? chosen-client))))) 8074 (lsp--install-server-internal chosen-client update?))) 8075 8076 ;;;###autoload 8077 (defun lsp-uninstall-server (dir) 8078 "Delete a LSP server from `lsp-server-install-dir'." 8079 (interactive 8080 (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir)))) 8081 (unless (file-directory-p dir) 8082 (user-error "Couldn't find %s directory" dir)) 8083 (delete-directory dir 'recursive) 8084 (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir)))) 8085 8086 ;;;###autoload 8087 (defun lsp-uninstall-servers () 8088 "Uninstall all installed servers." 8089 (interactive) 8090 (let* ((dir lsp-server-install-dir) 8091 (servers (ignore-errors 8092 (directory-files dir t 8093 directory-files-no-dot-files-regexp)))) 8094 (if (or (not (file-directory-p dir)) (zerop (length servers))) 8095 (user-error "No servers to uninstall") 8096 (when (yes-or-no-p 8097 (format "Servers to uninstall: %d (%s), proceed? " 8098 (length servers) 8099 (mapconcat (lambda (server) 8100 (file-name-nondirectory (directory-file-name server))) 8101 servers " "))) 8102 (mapc #'lsp-uninstall-server servers) 8103 (message "All servers uninstalled"))))) 8104 8105 ;;;###autoload 8106 (defun lsp-update-server (&optional server-id) 8107 "Interactively update (reinstall) a server." 8108 (interactive) 8109 (lsp--require-packages) 8110 (let ((chosen-client (or (gethash server-id lsp-clients) 8111 (lsp--completing-read 8112 "Select server to update (if not on the list, probably you need to `lsp-install-server`): " 8113 (or (->> lsp-clients 8114 (ht-values) 8115 (-filter (-andfn 8116 (-not #'lsp--client-download-in-progress?) 8117 #'lsp--client-download-server-fn 8118 #'lsp--server-binary-present?))) 8119 (user-error "There are no servers to update")) 8120 (lambda (client) 8121 (-> client lsp--client-server-id symbol-name)) 8122 nil 8123 t)))) 8124 (lsp--install-server-internal chosen-client t))) 8125 8126 ;;;###autoload 8127 (defun lsp-update-servers () 8128 "Update (reinstall) all installed servers." 8129 (interactive) 8130 (lsp--require-packages) 8131 (mapc (lambda (client) (lsp--install-server-internal client t)) 8132 (-filter (-andfn 8133 (-not #'lsp--client-download-in-progress?) 8134 #'lsp--client-download-server-fn 8135 #'lsp--server-binary-present?) (hash-table-values lsp-clients)))) 8136 8137 ;;;###autoload 8138 (defun lsp-ensure-server (server-id) 8139 "Ensure server SERVER-ID" 8140 (lsp--require-packages) 8141 (if-let ((client (gethash server-id lsp-clients))) 8142 (unless (lsp--server-binary-present? client) 8143 (lsp--info "Server `%s' is not preset, installing..." server-id) 8144 (lsp-install-server nil server-id)) 8145 (warn "Unable to find server registration with id %s" server-id))) 8146 8147 (defun lsp-async-start-process (callback error-callback &rest command) 8148 "Start async process COMMAND with CALLBACK and ERROR-CALLBACK." 8149 (let ((name (cl-first command))) 8150 (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd) 8151 (not (null cmd))) 8152 command) 8153 " ") t 8154 (lambda (&rest _) 8155 (generate-new-buffer-name (format "*lsp-install: %s*" name)))) 8156 (lsp-installation-buffer-mode +1) 8157 (view-mode +1) 8158 (add-hook 8159 'compilation-finish-functions 8160 (lambda (_buf status) 8161 (if (string= "finished\n" status) 8162 (condition-case err 8163 (funcall callback) 8164 (error 8165 (funcall error-callback (error-message-string err)))) 8166 (funcall error-callback (s-trim-right status)))) 8167 nil t)))) 8168 8169 (defun lsp-resolve-value (value) 8170 "Resolve VALUE's value. 8171 If it is function - call it. 8172 If it is a variable - return it's value 8173 Otherwise returns value itself." 8174 (cond 8175 ((functionp value) (funcall value)) 8176 ((and (symbolp value) (boundp value)) (symbol-value value)) 8177 (value))) 8178 8179 (defvar lsp-deps-providers 8180 (list :npm (list :path #'lsp--npm-dependency-path 8181 :install #'lsp--npm-dependency-install) 8182 :cargo (list :path #'lsp--cargo-dependency-path 8183 :install #'lsp--cargo-dependency-install) 8184 :system (list :path #'lsp--system-path) 8185 :download (list :path #'lsp-download-path 8186 :install #'lsp-download-install))) 8187 8188 (defun lsp--system-path (path) 8189 "If PATH is absolute and exists return it as is. Otherwise, 8190 return the absolute path to the executable defined by PATH or 8191 nil." 8192 ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the 8193 ;; typescript-language-server. When lsp invokes the server, lsp needs to 8194 ;; supply the path to the typescript compiler, tsserver.js, as an argument. To 8195 ;; make code platform independent, one must pass the absolute path to the 8196 ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript 8197 ;; child process spawn command that is invoked by the 8198 ;; typescript-language-server). This is why we check for existence and not 8199 ;; that the path is executable. 8200 (let ((path (lsp-resolve-value path))) 8201 (cond 8202 ((and (f-absolute? path) 8203 (f-exists? path)) 8204 path) 8205 ((executable-find path t) path)))) 8206 8207 (defun lsp-package-path (dependency) 8208 "Path to the DEPENDENCY each of the registered providers." 8209 (let (path) 8210 (-first (-lambda ((provider . rest)) 8211 (setq path (-some-> lsp-deps-providers 8212 (plist-get provider) 8213 (plist-get :path) 8214 (apply rest)))) 8215 (gethash dependency lsp--dependencies)) 8216 path)) 8217 8218 (defun lsp-package-ensure (dependency callback error-callback) 8219 "Asynchronously ensure a package." 8220 (or (-first (-lambda ((provider . rest)) 8221 (-some-> lsp-deps-providers 8222 (plist-get provider) 8223 (plist-get :install) 8224 (apply (cl-list* callback error-callback rest)))) 8225 (gethash dependency lsp--dependencies)) 8226 (funcall error-callback (format "Unable to find a way to install %s" dependency)))) 8227 8228 8229 ;; npm handling 8230 8231 ;; https://docs.npmjs.com/files/folders#executables 8232 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys) 8233 "Return npm dependency PATH for PACKAGE." 8234 (let ((path (executable-find 8235 (f-join lsp-server-install-dir "npm" package 8236 (cond ((eq system-type 'windows-nt) "") 8237 (t "bin")) 8238 path) 8239 t))) 8240 (unless (and path (f-exists? path)) 8241 (error "The package %s is not installed. Unable to find %s" package path)) 8242 path)) 8243 8244 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys) 8245 (if-let ((npm-binary (executable-find "npm"))) 8246 (progn 8247 ;; Explicitly `make-directory' to work around NPM bug in 8248 ;; versions 7.0.0 through 7.4.1. See 8249 ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for 8250 ;; discussion. 8251 (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents) 8252 (lsp-async-start-process (lambda () 8253 (if (string-empty-p 8254 (string-trim (shell-command-to-string 8255 (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " ")))) 8256 (funcall callback) 8257 (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json"))))) 8258 (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx 8259 (when (f-dir-p default-directory) 8260 (lsp-async-start-process callback 8261 error-callback 8262 (executable-find "npx") 8263 "npm-install-peers"))))) 8264 error-callback 8265 npm-binary 8266 "-g" 8267 "--prefix" 8268 (f-join lsp-server-install-dir "npm" package) 8269 "install" 8270 package)) 8271 (lsp-log "Unable to install %s via `npm' because it is not present" package) 8272 nil)) 8273 8274 8275 ;; Cargo dependency handling 8276 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys) 8277 (let ((path (executable-find 8278 (f-join lsp-server-install-dir 8279 "cargo" 8280 package 8281 "bin" 8282 path) 8283 t))) 8284 (unless (and path (f-exists? path)) 8285 (error "The package %s is not installed. Unable to find %s" package path)) 8286 path)) 8287 8288 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys) 8289 (if-let ((cargo-binary (executable-find "cargo"))) 8290 (lsp-async-start-process 8291 callback 8292 error-callback 8293 cargo-binary 8294 "install" 8295 package 8296 (when git 8297 "--git") 8298 git 8299 "--root" 8300 (f-join lsp-server-install-dir "cargo" package)) 8301 (lsp-log "Unable to install %s via `cargo' because it is not present" package) 8302 nil)) 8303 8304 8305 8306 ;; Download URL handling 8307 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys) 8308 (let* ((url (lsp-resolve-value url)) 8309 (store-path (lsp-resolve-value store-path)) 8310 ;; (decompress (lsp-resolve-value decompress)) 8311 (download-path 8312 (pcase decompress 8313 (:gzip (concat store-path ".gz")) 8314 (:zip (concat store-path ".zip")) 8315 (:targz (concat store-path ".tar.gz")) 8316 (`nil store-path) 8317 (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'"))))) 8318 (make-thread 8319 (lambda () 8320 (condition-case err 8321 (progn 8322 (when (f-exists? download-path) 8323 (f-delete download-path)) 8324 (when (f-exists? store-path) 8325 (f-delete store-path)) 8326 (lsp--info "Starting to download %s to %s..." url download-path) 8327 (mkdir (f-parent download-path) t) 8328 (url-copy-file url download-path) 8329 (lsp--info "Finished downloading %s..." download-path) 8330 (when (and lsp-verify-signature asc-url pgp-key) 8331 (if (executable-find epg-gpg-program) 8332 (let ((asc-download-path (concat download-path ".asc")) 8333 (context (epg-make-context)) 8334 (fingerprint) 8335 (signature)) 8336 (when (f-exists? asc-download-path) 8337 (f-delete asc-download-path)) 8338 (lsp--info "Starting to download %s to %s..." asc-url asc-download-path) 8339 (url-copy-file asc-url asc-download-path) 8340 (lsp--info "Finished downloading %s..." asc-download-path) 8341 (epg-import-keys-from-string context pgp-key) 8342 (setq fingerprint (epg-import-status-fingerprint 8343 (car 8344 (epg-import-result-imports 8345 (epg-context-result-for context 'import))))) 8346 (lsp--info "Verifying signature %s..." asc-download-path) 8347 (epg-verify-file context asc-download-path download-path) 8348 (setq signature (car (epg-context-result-for context 'verify))) 8349 (unless (and 8350 (eq (epg-signature-status signature) 'good) 8351 (equal (epg-signature-fingerprint signature) fingerprint)) 8352 (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature)))) 8353 (lsp--warn "GPG is not installed, skipping the signature check."))) 8354 (when decompress 8355 (lsp--info "Decompressing %s..." download-path) 8356 (pcase decompress 8357 (:gzip 8358 (lsp-gunzip download-path)) 8359 (:zip (lsp-unzip download-path (f-parent store-path))) 8360 (:targz (lsp-tar-gz-decompress download-path (f-parent store-path)))) 8361 (lsp--info "Decompressed %s..." store-path)) 8362 (funcall callback)) 8363 (error (funcall error-callback err))))))) 8364 8365 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys) 8366 "Download URL and store it into STORE-PATH. 8367 8368 SET-EXECUTABLE? when non-nil change the executable flags of 8369 STORE-PATH to make it executable. BINARY-PATH can be specified 8370 when the binary to start does not match the name of the 8371 archive (e.g. when the archive has multiple files)" 8372 (let ((store-path (or (lsp-resolve-value binary-path) 8373 (lsp-resolve-value store-path)))) 8374 (cond 8375 ((executable-find store-path) store-path) 8376 ((and set-executable? (f-exists? store-path)) 8377 (set-file-modes store-path #o0700) 8378 store-path) 8379 ((f-exists? store-path) store-path)))) 8380 8381 (defun lsp--find-latest-gh-release-url (url regex) 8382 "Fetch the latest version in the releases given by URL by using REGEX." 8383 (let ((url-request-method "GET")) 8384 (with-current-buffer (url-retrieve-synchronously url) 8385 (goto-char (point-min)) 8386 (re-search-forward "\n\n" nil 'noerror) 8387 (delete-region (point-min) (point)) 8388 (let* ((json-result (lsp-json-read-buffer))) 8389 (message "Latest version found: %s" (lsp-get json-result :tag_name)) 8390 (--> json-result 8391 (lsp-get it :assets) 8392 (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it) 8393 (lsp-get it :browser_download_url)))))) 8394 8395 ;; unzip 8396 8397 (defconst lsp-ext-pwsh-script "powershell -noprofile -noninteractive \ 8398 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'" 8399 "Powershell script to unzip file.") 8400 8401 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'" 8402 "Unzip script to unzip file.") 8403 8404 (defcustom lsp-unzip-script (lambda () 8405 (cond ((executable-find "unzip") lsp-ext-unzip-script) 8406 ((executable-find "powershell") lsp-ext-pwsh-script) 8407 (t nil))) 8408 "The script to unzip." 8409 :group 'lsp-mode 8410 :type 'string 8411 :package-version '(lsp-mode . "8.0.0")) 8412 8413 (defun lsp-unzip (zip-file dest) 8414 "Unzip ZIP-FILE to DEST." 8415 (unless lsp-unzip-script 8416 (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'")) 8417 (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest))) 8418 8419 ;; gunzip 8420 8421 (defconst lsp-ext-gunzip-script "gzip -d %1$s" 8422 "Script to decompress a gzippped file with gzip.") 8423 8424 (defcustom lsp-gunzip-script (lambda () 8425 (cond ((executable-find "gzip") lsp-ext-gunzip-script) 8426 (t nil))) 8427 "The script to decompress a gzipped file. 8428 Should be a format string with one argument for the file to be decompressed 8429 in place." 8430 :group 'lsp-mode 8431 :type 'string 8432 :package-version '(lsp-mode . "8.0.0")) 8433 8434 (defun lsp-gunzip (gz-file) 8435 "Decompress GZ-FILE in place." 8436 (unless lsp-gunzip-script 8437 (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file)) 8438 (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file))) 8439 8440 ;; tar.gz decompression 8441 8442 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'" 8443 "Script to decompress a .tar.gz file.") 8444 8445 (defcustom lsp-tar-script (lambda () 8446 (cond ((executable-find "tar") lsp-ext-tar-script) 8447 (t nil))) 8448 "The script to decompress a .tar.gz file. 8449 Should be a format string with one argument for the file to be decompressed 8450 in place." 8451 :group 'lsp-mode 8452 :type 'string) 8453 8454 (defun lsp-tar-gz-decompress (targz-file dest) 8455 "Decompress TARGZ-FILE in DEST." 8456 (unless lsp-tar-script 8457 (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file)) 8458 (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest))) 8459 8460 8461 ;; VSCode marketplace 8462 8463 (defcustom lsp-vscode-ext-url 8464 "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s" 8465 "Vscode extension template url." 8466 :group 'lsp-mode 8467 :type 'string 8468 :package-version '(lsp-mode . "8.0.0")) 8469 8470 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform) 8471 "Return the URL to vscode extension. 8472 PUBLISHER is the extension publisher. 8473 NAME is the name of the extension. 8474 VERSION is the version of the extension. 8475 TARGETPLATFORM is the targetPlatform of the extension." 8476 (format lsp-vscode-ext-url publisher name version (or targetPlatform ""))) 8477 8478 8479 8480 ;; Queueing prompts 8481 8482 (defvar lsp--question-queue nil 8483 "List of questions yet to be asked by `lsp-ask-question'.") 8484 8485 (defun lsp-ask-question (question options callback) 8486 "Prompt the user to answer the QUESTION with one of the OPTIONS from the 8487 minibuffer. Once the user selects an option, the CALLBACK function will be 8488 called, passing the selected option to it. 8489 8490 If the user is currently being shown a question, the question will be stored in 8491 `lsp--question-queue', and will be asked once the user has answered the current 8492 question." 8493 (add-to-list 'lsp--question-queue `(("question" . ,question) 8494 ("options" . ,options) 8495 ("callback" . ,callback)) t) 8496 (when (eq (length lsp--question-queue) 1) 8497 (lsp--process-question-queue))) 8498 8499 (defun lsp--process-question-queue () 8500 "Take the first question from `lsp--question-queue', process it, then process 8501 the next question until the queue is empty." 8502 (-let* (((&alist "question" "options" "callback") (car lsp--question-queue)) 8503 (answer (completing-read question options nil t))) 8504 (pop lsp--question-queue) 8505 (funcall callback answer) 8506 (when lsp--question-queue 8507 (lsp--process-question-queue)))) 8508 8509 (defun lsp--supports-buffer? (client) 8510 (and 8511 ;; both file and client remote or both local 8512 (eq (---truthy? (file-remote-p (buffer-file-name))) 8513 (---truthy? (lsp--client-remote? client))) 8514 8515 ;; activation function or major-mode match. 8516 (if-let ((activation-fn (lsp--client-activation-fn client))) 8517 (funcall activation-fn (buffer-file-name) major-mode) 8518 (-contains? (lsp--client-major-modes client) major-mode)) 8519 8520 ;; check whether it is enabled if `lsp-enabled-clients' is not null 8521 (or (null lsp-enabled-clients) 8522 (or (member (lsp--client-server-id client) lsp-enabled-clients) 8523 (ignore (lsp--info "Client %s is not in lsp-enabled-clients" 8524 (lsp--client-server-id client))))) 8525 8526 ;; check whether it is not disabled. 8527 (not (lsp--client-disabled-p major-mode (lsp--client-server-id client))))) 8528 8529 (defun lsp--filter-clients (pred) 8530 (->> lsp-clients hash-table-values (-filter pred))) 8531 8532 (defun lsp--find-clients () 8533 "Find clients which can handle current buffer." 8534 (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 8535 #'lsp--server-binary-present?))) 8536 (lsp-log "Found the following clients for %s: %s" 8537 (buffer-file-name) 8538 (s-join ", " 8539 (-map (lambda (client) 8540 (format "(server-id %s, priority %s)" 8541 (lsp--client-server-id client) 8542 (lsp--client-priority client))) 8543 matching-clients))) 8544 (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients)) 8545 (selected-clients (if-let ((main-client (and main-clients 8546 (--max-by (> (lsp--client-priority it) 8547 (lsp--client-priority other)) 8548 main-clients)))) 8549 (cons main-client add-on-clients) 8550 add-on-clients))) 8551 (lsp-log "The following clients were selected based on priority: %s" 8552 (s-join ", " 8553 (-map (lambda (client) 8554 (format "(server-id %s, priority %s)" 8555 (lsp--client-server-id client) 8556 (lsp--client-priority client))) 8557 selected-clients))) 8558 selected-clients))) 8559 8560 (defun lsp-workspace-remove-all-folders() 8561 "Delete all lsp tracked folders." 8562 (interactive) 8563 (--each (lsp-session-folders (lsp-session)) 8564 (lsp-workspace-folders-remove it))) 8565 8566 (defun lsp-register-client (client) 8567 "Registers LSP client CLIENT." 8568 (let ((client-id (lsp--client-server-id client))) 8569 (puthash client-id client lsp-clients) 8570 (setplist (intern (format "lsp-%s-after-open-hook" client-id)) 8571 `( standard-value (nil) custom-type hook 8572 custom-package-version (lsp-mode . "7.0.1") 8573 variable-documentation ,(format "Hooks to run after `%s' server is run." client-id) 8574 custom-requests nil))) 8575 (when (and lsp-auto-register-remote-clients 8576 (not (lsp--client-remote? client))) 8577 (let ((remote-client (copy-lsp--client client))) 8578 (setf (lsp--client-remote? remote-client) t 8579 (lsp--client-server-id remote-client) (intern 8580 (format "%s-tramp" 8581 (lsp--client-server-id client))) 8582 ;; disable automatic download 8583 (lsp--client-download-server-fn remote-client) nil) 8584 (lsp-register-client remote-client)))) 8585 8586 (defun lsp--create-initialization-options (_session client) 8587 "Create initialization-options from SESSION and CLIENT. 8588 Add workspace folders depending on server being multiroot and 8589 session workspace folder configuration for the server." 8590 (let* ((initialization-options-or-fn (lsp--client-initialization-options client))) 8591 (if (functionp initialization-options-or-fn) 8592 (funcall initialization-options-or-fn) 8593 initialization-options-or-fn))) 8594 8595 (defvar lsp-client-settings (make-hash-table :test 'equal) 8596 "For internal use, any external users please use 8597 `lsp-register-custom-settings' function instead") 8598 8599 (defun lsp-register-custom-settings (props) 8600 "Register PROPS. 8601 PROPS is list of triple (path value boolean?) where PATH is the path to the 8602 property; VALUE can be a literal value, symbol to be evaluated, or either a 8603 function or lambda function to be called without arguments; BOOLEAN? is an 8604 optional flag that should be non-nil for boolean settings, when it is nil the 8605 property will be ignored if the VALUE is nil. 8606 8607 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))' 8608 \(note the double parentheses)" 8609 (mapc 8610 (-lambda ((path . rest)) 8611 (puthash path rest lsp-client-settings)) 8612 props)) 8613 8614 (defun lsp-region-text (region) 8615 "Get the text for REGION in current buffer." 8616 (-let (((start . end) (lsp--range-to-region region))) 8617 (buffer-substring-no-properties start end))) 8618 8619 (defun lsp-ht-set (tbl paths value) 8620 "Set nested hash table value. 8621 TBL - a hash table, PATHS is the path to the nested VALUE." 8622 (pcase paths 8623 (`(,path) (ht-set! tbl path value)) 8624 (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl) 8625 (let ((temp-tbl (ht))) 8626 (ht-set! tbl path temp-tbl) 8627 temp-tbl)))) 8628 (lsp-ht-set nested-tbl rst value))))) 8629 8630 ;; sections 8631 8632 (defalias 'defcustom-lsp 'lsp-defcustom) 8633 8634 (defmacro lsp-defcustom (symbol standard doc &rest args) 8635 "Defines `lsp-mode' server property." 8636 (declare (doc-string 3) (debug (name body)) 8637 (indent defun)) 8638 (let ((path (plist-get args :lsp-path))) 8639 (cl-remf args :lsp-path) 8640 `(progn 8641 (lsp-register-custom-settings 8642 (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type)))))) 8643 8644 (defcustom ,symbol ,standard ,doc 8645 :set (lambda (sym val) 8646 (lsp--set-custom-property sym val ,path)) 8647 ,@args)))) 8648 8649 (defun lsp--set-custom-property (sym val path) 8650 (set sym val) 8651 (let ((section (cl-first (s-split "\\." path)))) 8652 (mapc (lambda (workspace) 8653 (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace)) 8654 section) 8655 (with-lsp-workspace workspace 8656 (lsp--set-configuration (lsp-configuration-section section))))) 8657 (lsp--session-workspaces (lsp-session))))) 8658 8659 (defun lsp-configuration-section (section) 8660 "Get settings for SECTION." 8661 (let ((ret (ht-create))) 8662 (maphash (-lambda (path (variable boolean?)) 8663 (when (s-matches? (concat (regexp-quote section) "\\..*") path) 8664 (let* ((symbol-value (-> variable 8665 lsp-resolve-value 8666 lsp-resolve-value)) 8667 (value (if (and boolean? (not symbol-value)) 8668 :json-false 8669 symbol-value))) 8670 (when (or boolean? value) 8671 (lsp-ht-set ret (s-split "\\." path) value))))) 8672 lsp-client-settings) 8673 ret)) 8674 8675 8676 (defun lsp--start-connection (session client project-root) 8677 "Initiates connection created from CLIENT for PROJECT-ROOT. 8678 SESSION is the active session." 8679 (when (lsp--client-multi-root client) 8680 (cl-pushnew project-root (gethash (lsp--client-server-id client) 8681 (lsp-session-server-id->folders session)))) 8682 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil) 8683 8684 (unwind-protect 8685 (lsp--start-workspace session client project-root (lsp--create-initialization-options session client)) 8686 (lsp--spinner-stop))) 8687 8688 ;; lsp-log-io-mode 8689 8690 (defvar lsp-log-io-mode-map 8691 (let ((map (make-sparse-keymap))) 8692 (define-key map (kbd "M-n") #'lsp-log-io-next) 8693 (define-key map (kbd "M-p") #'lsp-log-io-prev) 8694 (define-key map (kbd "k") #'lsp--erase-log-buffer) 8695 (define-key map (kbd "K") #'lsp--erase-session-log-buffers) 8696 map) 8697 "Keymap for lsp log buffer mode.") 8698 8699 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo" 8700 "Special mode for viewing IO logs.") 8701 8702 (defun lsp-workspace-show-log (workspace) 8703 "Display the log buffer of WORKSPACE." 8704 (interactive 8705 (list (if lsp-log-io 8706 (if (eq (length (lsp-workspaces)) 1) 8707 (cl-first (lsp-workspaces)) 8708 (lsp--completing-read "Workspace: " (lsp-workspaces) 8709 #'lsp--workspace-print nil t)) 8710 (user-error "IO logging is disabled")))) 8711 (pop-to-buffer (lsp--get-log-buffer-create workspace))) 8712 8713 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log) 8714 8715 (defun lsp--get-log-buffer-create (workspace) 8716 "Return the lsp log buffer of WORKSPACE, creating a new one if needed." 8717 (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8718 (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id))) 8719 (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid)))) 8720 8721 (defun lsp--erase-log-buffer (&optional all) 8722 "Delete contents of current lsp log buffer. 8723 When ALL is t, erase all log buffers of the running session." 8724 (interactive) 8725 (let* ((workspaces (lsp--session-workspaces (lsp-session))) 8726 (current-log-buffer (current-buffer))) 8727 (dolist (w workspaces) 8728 (let ((b (lsp--get-log-buffer-create w))) 8729 (when (or all (eq b current-log-buffer)) 8730 (with-current-buffer b 8731 (let ((inhibit-read-only t)) 8732 (erase-buffer)))))))) 8733 8734 (defun lsp--erase-session-log-buffers () 8735 "Erase log buffers of the running session." 8736 (interactive) 8737 (lsp--erase-log-buffer t)) 8738 8739 (defun lsp-log-io-next (arg) 8740 "Move to next log entry." 8741 (interactive "P") 8742 (ewoc-goto-next lsp--log-io-ewoc (or arg 1))) 8743 8744 (defun lsp-log-io-prev (arg) 8745 "Move to previous log entry." 8746 (interactive "P") 8747 (ewoc-goto-prev lsp--log-io-ewoc (or arg 1))) 8748 8749 8750 8751 (cl-defmethod lsp-process-id ((process process)) 8752 (process-id process)) 8753 8754 (cl-defmethod lsp-process-name ((process process)) (process-name process)) 8755 8756 (cl-defmethod lsp-process-status ((process process)) (process-status process)) 8757 8758 (cl-defmethod lsp-process-kill ((process process)) 8759 (when (process-live-p process) 8760 (kill-process process))) 8761 8762 (cl-defmethod lsp-process-send ((process process) message) 8763 (condition-case err 8764 (process-send-string process (lsp--make-message message)) 8765 (error (lsp--error "Sending to process failed with the following error: %s" 8766 (error-message-string err))))) 8767 8768 (cl-defmethod lsp-process-cleanup (process) 8769 ;; Kill standard error buffer only if the process exited normally. 8770 ;; Leave it intact otherwise for debugging purposes. 8771 (let ((buffer (-> process process-name get-buffer))) 8772 (when (and (eq (process-status process) 'exit) 8773 (zerop (process-exit-status process)) 8774 (buffer-live-p buffer)) 8775 (kill-buffer buffer)))) 8776 8777 8778 ;; native JSONRPC 8779 8780 (declare-function json-rpc "ext:json") 8781 (declare-function json-rpc-connection "ext:json") 8782 (declare-function json-rpc-send "ext:json") 8783 (declare-function json-rpc-shutdown "ext:json") 8784 (declare-function json-rpc-stderr "ext:json") 8785 (declare-function json-rpc-pid "ext:json") 8786 8787 (defvar lsp-json-rpc-thread nil) 8788 (defvar lsp-json-rpc-queue nil) 8789 (defvar lsp-json-rpc-done nil) 8790 (defvar lsp-json-rpc-mutex (make-mutex)) 8791 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex)) 8792 8793 (defun lsp-json-rpc-process-queue () 8794 (while (not lsp-json-rpc-done) 8795 (while lsp-json-rpc-queue 8796 (-let (((proc . message) (pop lsp-json-rpc-queue))) 8797 (json-rpc-send 8798 proc message 8799 :null-object nil 8800 :false-object :json-false))) 8801 (with-mutex lsp-json-rpc-mutex 8802 (condition-wait lsp-json-rpc-condition)))) 8803 8804 (cl-defmethod lsp-process-id (process) (json-rpc-pid process)) 8805 8806 (cl-defmethod lsp-process-name (_process) "TBD") 8807 8808 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process)) 8809 8810 (cl-defmethod lsp-process-send (proc message) 8811 (unless lsp-json-rpc-thread 8812 (with-current-buffer (get-buffer-create " *json-rpc*") 8813 (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*")))) 8814 8815 (with-mutex lsp-json-rpc-mutex 8816 (setq lsp-json-rpc-queue (append lsp-json-rpc-queue 8817 (list (cons proc message)))) 8818 (condition-notify lsp-json-rpc-condition))) 8819 8820 (cl-defmethod lsp-process-cleanup (_proc)) 8821 8822 (defun lsp-json-rpc-connection (workspace command) 8823 (let ((con (apply #'json-rpc-connection command)) 8824 (object-type (if lsp-use-plists 'plist 'hash-table))) 8825 (with-current-buffer (get-buffer-create " *json-rpc*") 8826 (make-thread 8827 (lambda () 8828 (json-rpc 8829 con 8830 (lambda (result err done) 8831 (run-with-timer 8832 0.0 8833 nil 8834 (lambda () 8835 (cond 8836 (result (lsp--parser-on-message result workspace)) 8837 (err (warn "Json parsing failed with the following error: %s" err)) 8838 (done (lsp--handle-process-exit workspace "")))))) 8839 :object-type object-type 8840 :null-object nil 8841 :false-object nil)) 8842 "*json-rpc-connection*")) 8843 (cons con con))) 8844 8845 (defun lsp-json-rpc-stderr () 8846 (interactive) 8847 (--when-let (pcase (lsp-workspaces) 8848 (`nil (user-error "There are no active servers in the current buffer")) 8849 (`(,workspace) workspace) 8850 (workspaces (lsp--completing-read "Select server: " 8851 workspaces 8852 'lsp--workspace-print nil t))) 8853 (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it))) 8854 (buffer (format "*stderr-%s*" (lsp--workspace-print it)) )) 8855 (with-current-buffer (get-buffer-create buffer) 8856 (with-help-window buffer 8857 (insert content)))))) 8858 8859 8860 (defun lsp--workspace-print (workspace) 8861 "Visual representation WORKSPACE." 8862 (let* ((proc (lsp--workspace-cmd-proc workspace)) 8863 (status (lsp--workspace-status workspace)) 8864 (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8865 (pid (lsp-process-id proc))) 8866 8867 (if (eq 'initialized status) 8868 (format "%s:%s" server-id pid) 8869 (format "%s:%s/%s" server-id pid status)))) 8870 8871 (defun lsp--map-tree-widget (m) 8872 "Build `tree-widget' from a hash-table or plist M." 8873 (when (lsp-structure-p m) 8874 (let (nodes) 8875 (lsp-map (lambda (k v) 8876 (push `(tree-widget 8877 :tag ,(if (lsp-structure-p v) 8878 (format "%s:" k) 8879 (format "%s: %s" k 8880 (propertize (format "%s" v) 8881 'face 8882 'font-lock-string-face))) 8883 :open t 8884 ,@(lsp--map-tree-widget v)) 8885 nodes)) 8886 m) 8887 nodes))) 8888 8889 (defun lsp-buffer-name (buffer-id) 8890 (if-let ((buffer-name (plist-get buffer-id :buffer-name))) 8891 (funcall buffer-name buffer-id) 8892 (buffer-name buffer-id))) 8893 8894 (defun lsp--render-workspace (workspace) 8895 "Tree node representation of WORKSPACE." 8896 `(tree-widget :tag ,(lsp--workspace-print workspace) 8897 :open t 8898 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face) 8899 :open t 8900 ,@(->> workspace 8901 (lsp--workspace-buffers) 8902 (--map `(tree-widget 8903 :tag ,(when (lsp-buffer-live-p it) 8904 (let ((buffer-name (lsp-buffer-name it))) 8905 (if (lsp-with-current-buffer it buffer-read-only) 8906 (propertize buffer-name 'face 'font-lock-constant-face) 8907 buffer-name))))))) 8908 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face) 8909 ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget)))) 8910 8911 (define-derived-mode lsp-browser-mode special-mode "LspBrowser" 8912 "Define mode for displaying lsp sessions." 8913 (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t))))) 8914 8915 (defun lsp-describe-session () 8916 "Describes current `lsp-session'." 8917 (interactive) 8918 (let ((session (lsp-session)) 8919 (buf (get-buffer-create "*lsp session*")) 8920 (root (lsp-workspace-root))) 8921 (with-current-buffer buf 8922 (lsp-browser-mode) 8923 (let ((inhibit-read-only t)) 8924 (erase-buffer) 8925 (--each (lsp-session-folders session) 8926 (widget-create 8927 `(tree-widget 8928 :tag ,(propertize it 'face 'font-lock-keyword-face) 8929 :open t 8930 ,@(->> session 8931 (lsp-session-folder->servers) 8932 (gethash it) 8933 (-map 'lsp--render-workspace))))))) 8934 (pop-to-buffer buf) 8935 (goto-char (point-min)) 8936 (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag) 8937 until (or (and root (string= tag root)) (eobp)) 8938 do (goto-char (next-overlay-change (point)))))) 8939 8940 (defun lsp--session-workspaces (session) 8941 "Get all workspaces that are part of the SESSION." 8942 (-> session lsp-session-folder->servers hash-table-values -flatten -uniq)) 8943 8944 (defun lsp--find-multiroot-workspace (session client project-root) 8945 "Look for a multiroot connection in SESSION created from CLIENT for 8946 PROJECT-ROOT and BUFFER-MAJOR-MODE." 8947 (when (lsp--client-multi-root client) 8948 (-when-let (multi-root-workspace (->> session 8949 (lsp--session-workspaces) 8950 (--first (eq (-> it lsp--workspace-client lsp--client-server-id) 8951 (lsp--client-server-id client))))) 8952 (with-lsp-workspace multi-root-workspace 8953 (lsp-notify "workspace/didChangeWorkspaceFolders" 8954 (lsp-make-did-change-workspace-folders-params 8955 :event (lsp-make-workspace-folders-change-event 8956 :added (vector (lsp-make-workspace-folder 8957 :uri (lsp--path-to-uri project-root) 8958 :name (f-filename project-root))) 8959 :removed [])))) 8960 8961 (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace)) 8962 (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root)) 8963 8964 (lsp--persist-session session) 8965 8966 (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace)) 8967 (lsp--open-in-workspace multi-root-workspace) 8968 8969 multi-root-workspace))) 8970 8971 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder) 8972 "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT. 8973 IGNORE-MULTI-FOLDER to ignore multi folder server." 8974 (-map (lambda (client) 8975 (or 8976 (lsp--find-workspace session client project-root) 8977 (unless ignore-multi-folder 8978 (lsp--find-multiroot-workspace session client project-root)) 8979 (lsp--start-connection session client project-root))) 8980 clients)) 8981 8982 (defun lsp--spinner-stop () 8983 "Stop the spinner in case all of the workspaces are started." 8984 (when (--all? (eq (lsp--workspace-status it) 'initialized) 8985 lsp--buffer-workspaces) 8986 (spinner-stop))) 8987 8988 (defun lsp--open-in-workspace (workspace) 8989 "Open in existing WORKSPACE." 8990 (if (eq 'initialized (lsp--workspace-status workspace)) 8991 ;; when workspace is initialized just call document did open. 8992 (progn 8993 (with-lsp-workspace workspace 8994 (when-let ((before-document-open-fn (-> workspace 8995 lsp--workspace-client 8996 lsp--client-before-file-open-fn))) 8997 (funcall before-document-open-fn workspace)) 8998 (lsp--text-document-did-open)) 8999 (lsp--spinner-stop)) 9000 ;; when it is not initialized 9001 (lsp--spinner-start) 9002 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace)))) 9003 9004 (defun lsp--find-workspace (session client project-root) 9005 "Find server connection created with CLIENT in SESSION for PROJECT-ROOT." 9006 (when-let ((workspace (->> session 9007 (lsp-session-folder->servers) 9008 (gethash project-root) 9009 (--first (eql (-> it lsp--workspace-client lsp--client-server-id) 9010 (lsp--client-server-id client)))))) 9011 (lsp--open-in-workspace workspace) 9012 workspace)) 9013 9014 (defun lsp--read-char (prompt &optional options) 9015 "Wrapper for `read-char-from-minibuffer' if Emacs +27. 9016 Fallback to `read-key' otherwise. 9017 PROMPT is the message and OPTIONS the available options." 9018 (if (fboundp 'read-char-from-minibuffer) 9019 (read-char-from-minibuffer prompt options) 9020 (read-key prompt))) 9021 9022 (defun lsp--find-root-interactively (session) 9023 "Find project interactively. 9024 Returns nil if the project should not be added to the current SESSION." 9025 (condition-case nil 9026 (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory)) 9027 (action (lsp--read-char 9028 (format 9029 "%s is not part of any project. 9030 9031 %s ==> Import project root %s 9032 %s ==> Import project by selecting root directory interactively 9033 %s ==> Import project at current directory %s 9034 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist 9035 %s ==> Do not ask again for the current project by selecting ignore path interactively 9036 %s ==> Do nothing: ask again when opening other files from the current project 9037 9038 Select action: " 9039 (propertize (buffer-name) 'face 'bold) 9040 (propertize "i" 'face 'success) 9041 (propertize project-root-suggestion 'face 'bold) 9042 (propertize "I" 'face 'success) 9043 (propertize "." 'face 'success) 9044 (propertize default-directory 'face 'bold) 9045 (propertize "d" 'face 'warning) 9046 (propertize project-root-suggestion 'face 'bold) 9047 (propertize "D" 'face 'warning) 9048 (propertize "n" 'face 'warning)) 9049 '(?i ?\r ?I ?. ?d ?D ?n)))) 9050 (cl-case action 9051 (?i project-root-suggestion) 9052 (?\r project-root-suggestion) 9053 (?I (read-directory-name "Select workspace folder to add: " 9054 (or project-root-suggestion default-directory) 9055 nil 9056 t)) 9057 (?. default-directory) 9058 (?d (push project-root-suggestion (lsp-session-folders-blocklist session)) 9059 (lsp--persist-session session) 9060 nil) 9061 (?D (push (read-directory-name "Select folder to blocklist: " 9062 (or project-root-suggestion default-directory) 9063 nil 9064 t) 9065 (lsp-session-folders-blocklist session)) 9066 (lsp--persist-session session) 9067 nil) 9068 (t nil))) 9069 (quit))) 9070 9071 (declare-function tramp-file-name-host "ext:tramp" (file) t) 9072 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault)) 9073 9074 (defun lsp--files-same-host (f1 f2) 9075 "Predicate on whether or not two files are on the same host." 9076 (or (not (or (file-remote-p f1) (file-remote-p f2))) 9077 (and (file-remote-p f1) 9078 (file-remote-p f2) 9079 (progn (require 'tramp) 9080 (equal (tramp-file-name-host (tramp-dissect-file-name f1)) 9081 (tramp-file-name-host (tramp-dissect-file-name f2))))))) 9082 9083 (defun lsp-find-session-folder (session file-name) 9084 "Look in the current SESSION for folder containing FILE-NAME." 9085 (let ((file-name-canonical (lsp-f-canonical file-name))) 9086 (->> session 9087 (lsp-session-folders) 9088 (--filter (and (lsp--files-same-host it file-name-canonical) 9089 (or (lsp-f-same? it file-name-canonical) 9090 (and (f-dir? it) 9091 (lsp-f-ancestor-of? it file-name-canonical))))) 9092 (--max-by (> (length it) 9093 (length other)))))) 9094 9095 (defun lsp-find-workspace (server-id &optional file-name) 9096 "Find workspace for SERVER-ID for FILE-NAME." 9097 (-when-let* ((session (lsp-session)) 9098 (folder->servers (lsp-session-folder->servers session)) 9099 (workspaces (if file-name 9100 (gethash (lsp-find-session-folder session file-name) folder->servers) 9101 (lsp--session-workspaces session)))) 9102 9103 (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces))) 9104 9105 (defun lsp--calculate-root (session file-name) 9106 "Calculate project root for FILE-NAME in SESSION." 9107 (and 9108 (->> session 9109 (lsp-session-folders-blocklist) 9110 (--first (and (lsp--files-same-host it file-name) 9111 (lsp-f-ancestor-of? it file-name) 9112 (prog1 t 9113 (lsp--info "File %s is in blocklisted directory %s" file-name it)))) 9114 not) 9115 (or 9116 (when lsp-auto-guess-root 9117 (lsp--suggest-project-root)) 9118 (unless lsp-guess-root-without-session 9119 (lsp-find-session-folder session file-name)) 9120 (unless lsp-auto-guess-root 9121 (when-let ((root-folder (lsp--find-root-interactively session))) 9122 (if (or (not (f-equal? root-folder (expand-file-name "~/"))) 9123 (yes-or-no-p 9124 (concat 9125 (propertize "[WARNING] " 'face 'warning) 9126 "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: 9127 9128 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/'). 9129 2. If your file is under `~/' then create a subfolder and move that file in this folder. 9130 9131 Type `No' to go back to project selection. 9132 Type `Yes' to confirm `HOME' as project root. 9133 Type `C-g' to cancel project import process and stop `lsp'"))) 9134 root-folder 9135 (lsp--calculate-root session file-name))))))) 9136 9137 (defun lsp--try-open-in-library-workspace () 9138 "Try opening current file as library file in any of the active workspace. 9139 The library folders are defined by each client for each of the active workspace." 9140 (when-let ((workspace (->> (lsp-session) 9141 (lsp--session-workspaces) 9142 ;; Sort the last active workspaces first as they are more likely to be 9143 ;; the correct ones, especially when jumping to a definition. 9144 (-sort (lambda (a _b) 9145 (-contains? lsp--last-active-workspaces a))) 9146 (--first 9147 (and (-> it lsp--workspace-client lsp--supports-buffer?) 9148 (when-let ((library-folders-fn 9149 (-> it lsp--workspace-client lsp--client-library-folders-fn))) 9150 (-first (lambda (library-folder) 9151 (lsp-f-ancestor-of? library-folder (buffer-file-name))) 9152 (funcall library-folders-fn it)))))))) 9153 (lsp--open-in-workspace workspace) 9154 (view-mode t) 9155 (lsp--info "Opening read-only library file %s." (buffer-file-name)) 9156 (list workspace))) 9157 9158 (defun lsp--persist-session (session) 9159 "Persist SESSION to `lsp-session-file'." 9160 (lsp--persist lsp-session-file (make-lsp-session 9161 :folders (lsp-session-folders session) 9162 :folders-blocklist (lsp-session-folders-blocklist session) 9163 :server-id->folders (lsp-session-server-id->folders session)))) 9164 9165 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder) 9166 "Try create opening file as a project file. 9167 When IGNORE-MULTI-FOLDER is t the lsp mode will start new 9168 language server even if there is language server which can handle 9169 current language. When IGNORE-MULTI-FOLDER is nil current file 9170 will be opened in multi folder language server if there is 9171 such." 9172 (-let ((session (lsp-session))) 9173 (-if-let (clients (if ask-for-client 9174 (list (lsp--completing-read "Select server to start: " 9175 (ht-values lsp-clients) 9176 (-compose 'symbol-name 'lsp--client-server-id) nil t)) 9177 (lsp--find-clients))) 9178 (-if-let (project-root (-some-> session 9179 (lsp--calculate-root (buffer-file-name)) 9180 (lsp-f-canonical))) 9181 (progn 9182 ;; update project roots if needed and persist the lsp session 9183 (unless (-contains? (lsp-session-folders session) project-root) 9184 (cl-pushnew project-root (lsp-session-folders session)) 9185 (lsp--persist-session session)) 9186 (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder)) 9187 (lsp--warn "%s not in project or it is blocklisted." (buffer-name)) 9188 nil) 9189 (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode) 9190 nil))) 9191 9192 (defun lsp-shutdown-workspace () 9193 "Shutdown language server." 9194 (interactive) 9195 (--when-let (pcase (lsp-workspaces) 9196 (`nil (user-error "There are no active servers in the current buffer")) 9197 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?" 9198 (lsp--workspace-print workspace))) 9199 workspace)) 9200 (workspaces (lsp--completing-read "Select server: " 9201 workspaces 9202 'lsp--workspace-print nil t))) 9203 (lsp-workspace-shutdown it))) 9204 9205 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1") 9206 9207 (defcustom lsp-auto-select-workspace t 9208 "Shutdown or restart a single workspace. 9209 If set and the current buffer has only a single workspace 9210 associated with it, `lsp-shutdown-workspace' and 9211 `lsp-restart-workspace' will act on it without asking." 9212 :type 'boolean 9213 :group 'lsp-mode) 9214 9215 (defun lsp--read-workspace () 9216 "Ask the user to select a workspace. 9217 Errors if there are none." 9218 (pcase (lsp-workspaces) 9219 (`nil (error "No workspaces associated with the current buffer")) 9220 ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace) 9221 (workspaces (lsp--completing-read "Select workspace: " workspaces 9222 #'lsp--workspace-print nil t)))) 9223 9224 (defun lsp-workspace-shutdown (workspace) 9225 "Shut the workspace WORKSPACE and the language server associated with it" 9226 (interactive (list (lsp--read-workspace))) 9227 (lsp--warn "Stopping %s" (lsp--workspace-print workspace)) 9228 (with-lsp-workspace workspace (lsp--shutdown-workspace))) 9229 9230 (defun lsp-disconnect () 9231 "Disconnect the buffer from the language server." 9232 (interactive) 9233 (lsp--text-document-did-close t) 9234 (lsp-managed-mode -1) 9235 (lsp-mode -1) 9236 (setq lsp--buffer-workspaces nil) 9237 (lsp--info "Disconnected")) 9238 9239 (defun lsp-restart-workspace () 9240 (interactive) 9241 (--when-let (pcase (lsp-workspaces) 9242 (`nil (user-error "There are no active servers in the current buffer")) 9243 (`(,workspace) workspace) 9244 (workspaces (lsp--completing-read "Select server: " 9245 workspaces 9246 'lsp--workspace-print nil t))) 9247 (lsp-workspace-restart it))) 9248 9249 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1") 9250 9251 (defun lsp-workspace-restart (workspace) 9252 "Restart the workspace WORKSPACE and the language server associated with it" 9253 (interactive (list (lsp--read-workspace))) 9254 (lsp--warn "Restarting %s" (lsp--workspace-print workspace)) 9255 (with-lsp-workspace workspace (lsp--shutdown-workspace t))) 9256 9257 ;;;###autoload 9258 (defun lsp (&optional arg) 9259 "Entry point for the server startup. 9260 When ARG is t the lsp mode will start new language server even if 9261 there is language server which can handle current language. When 9262 ARG is nil current file will be opened in multi folder language 9263 server if there is such. When `lsp' is called with prefix 9264 argument ask the user to select which language server to start." 9265 (interactive "P") 9266 9267 (lsp--require-packages) 9268 9269 (when (buffer-file-name) 9270 (let (clients 9271 (matching-clients (lsp--filter-clients 9272 (-andfn #'lsp--supports-buffer? 9273 #'lsp--server-binary-present?)))) 9274 (cond 9275 (matching-clients 9276 (when (setq lsp--buffer-workspaces 9277 (or (and 9278 ;; Don't open as library file if file is part of a project. 9279 (not (lsp-find-session-folder (lsp-session) (buffer-file-name))) 9280 (lsp--try-open-in-library-workspace)) 9281 (lsp--try-project-root-workspaces (equal arg '(4)) 9282 (and arg (not (equal arg 1)))))) 9283 (lsp-mode 1) 9284 (when lsp-auto-configure (lsp--auto-configure)) 9285 (setq lsp-buffer-uri (lsp--buffer-uri)) 9286 (lsp--info "Connected to %s." 9287 (apply 'concat (--map (format "[%s %s]" 9288 (lsp--workspace-print it) 9289 (lsp--workspace-root it)) 9290 lsp--buffer-workspaces))))) 9291 ;; look for servers which are currently being downloaded. 9292 ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9293 #'lsp--client-download-in-progress?))) 9294 (lsp--info "There are language server(%s) installation in progress. 9295 The server(s) will be started in the buffer when it has finished." 9296 (-map #'lsp--client-server-id clients)) 9297 (seq-do (lambda (client) 9298 (cl-pushnew (current-buffer) (lsp--client-buffers client))) 9299 clients)) 9300 ;; look for servers to install 9301 ((setq clients (lsp--filter-clients 9302 (-andfn #'lsp--supports-buffer? 9303 (-const lsp-enable-suggest-server-download) 9304 #'lsp--client-download-server-fn 9305 (-not #'lsp--client-download-in-progress?)))) 9306 (let ((client (lsp--completing-read 9307 (concat "Unable to find installed server supporting this file. " 9308 "The following servers could be installed automatically: ") 9309 clients 9310 (-compose #'symbol-name #'lsp--client-server-id) 9311 nil 9312 t))) 9313 (cl-pushnew (current-buffer) (lsp--client-buffers client)) 9314 (lsp--install-server-internal client))) 9315 ;; ignore other warnings 9316 ((not lsp-warn-no-matched-clients) 9317 nil) 9318 ;; automatic installation disabled 9319 ((setq clients (unless matching-clients 9320 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9321 #'lsp--client-download-server-fn 9322 (-not (-const lsp-enable-suggest-server-download)) 9323 (-not #'lsp--server-binary-present?))))) 9324 (lsp--warn "The following servers support current file but automatic download is disabled: %s 9325 \(If you have already installed the server check *lsp-log*)." 9326 (mapconcat (lambda (client) 9327 (symbol-name (lsp--client-server-id client))) 9328 clients 9329 " "))) 9330 ;; no clients present 9331 ((setq clients (unless matching-clients 9332 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9333 (-not #'lsp--server-binary-present?))))) 9334 (lsp--warn "The following servers support current file but do not have automatic installation: %s 9335 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages. 9336 \(If you have already installed the server check *lsp-log*)." 9337 (mapconcat (lambda (client) 9338 (symbol-name (lsp--client-server-id client))) 9339 clients 9340 " "))) 9341 ;; no matches 9342 ((-> #'lsp--supports-buffer? lsp--filter-clients not) 9343 (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'. 9344 This issue might be caused by: 9345 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'. 9346 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'. 9347 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/ . 9348 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/. 9349 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients'). 9350 You can customize `lsp-warn-no-matched-clients' to disable this message." 9351 major-mode major-mode major-mode)))))) 9352 9353 (defun lsp--buffer-visible-p () 9354 "Return non nil if current buffer is visible." 9355 (or (buffer-modified-p) (get-buffer-window nil t))) 9356 9357 (defun lsp--init-if-visible () 9358 "Run `lsp' for the current buffer if the buffer is visible. 9359 Returns non nil if `lsp' was run for the buffer." 9360 (when (lsp--buffer-visible-p) 9361 (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t) 9362 (lsp) 9363 t)) 9364 9365 ;;;###autoload 9366 (defun lsp-deferred () 9367 "Entry point that defers server startup until buffer is visible. 9368 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'. 9369 This avoids overloading the server with many files when starting Emacs." 9370 ;; Workspace may not be initialized yet. Use a buffer local variable to 9371 ;; remember that we deferred loading of this buffer. 9372 (setq lsp--buffer-deferred t) 9373 (let ((buffer (current-buffer))) 9374 ;; Avoid false positives as desktop-mode restores buffers by deferring 9375 ;; visibility check until the stack clears. 9376 (run-with-idle-timer 0 nil (lambda () 9377 (when (buffer-live-p buffer) 9378 (with-current-buffer buffer 9379 (unless (lsp--init-if-visible) 9380 (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t)))))))) 9381 9382 9383 9384 (defvar lsp-file-truename-cache (ht)) 9385 9386 (defmacro lsp-with-cached-filetrue-name (&rest body) 9387 "Executes BODY caching the `file-truename' calls." 9388 `(let ((old-fn (symbol-function 'file-truename))) 9389 (unwind-protect 9390 (progn 9391 (fset 'file-truename 9392 (lambda (file-name &optional counter prev-dirs) 9393 (or (gethash file-name lsp-file-truename-cache) 9394 (puthash file-name (apply old-fn (list file-name counter prev-dirs)) 9395 lsp-file-truename-cache)))) 9396 ,@body) 9397 (fset 'file-truename old-fn)))) 9398 9399 9400 (defun lsp-virtual-buffer-call (key &rest args) 9401 (when lsp--virtual-buffer 9402 (when-let ((fn (plist-get lsp--virtual-buffer key))) 9403 (apply fn args)))) 9404 9405 (defun lsp-translate-column (column) 9406 "Translate COLUMN taking into account virtual buffers." 9407 (or (lsp-virtual-buffer-call :real->virtual-char column) 9408 column)) 9409 9410 (defun lsp-translate-line (line) 9411 "Translate LINE taking into account virtual buffers." 9412 (or (lsp-virtual-buffer-call :real->virtual-line line) 9413 line)) 9414 9415 9416 ;; lsp internal validation. 9417 9418 (defmacro lsp--doctor (&rest checks) 9419 `(-let [buf (current-buffer)] 9420 (with-current-buffer (get-buffer-create "*lsp-performance*") 9421 (with-help-window (current-buffer) 9422 ,@(-map (-lambda ((msg form)) 9423 `(insert (format "%s: %s\n" ,msg 9424 (let ((res (with-current-buffer buf 9425 ,form))) 9426 (cond 9427 ((eq res :optional) (propertize "OPTIONAL" 'face 'warning)) 9428 (res (propertize "OK" 'face 'success)) 9429 (t (propertize "ERROR" 'face 'error))))))) 9430 (-partition 2 checks)))))) 9431 9432 (define-obsolete-function-alias 'lsp-diagnose 9433 'lsp-doctor "lsp-mode 8.0.0") 9434 9435 (defun lsp-doctor () 9436 "Validate performance settings." 9437 (interactive) 9438 (lsp--doctor 9439 "Checking for Native JSON support" (functionp 'json-serialize) 9440 "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max) 9441 "Check `read-process-output-max' default has been changed from 4k" 9442 (and (boundp 'read-process-output-max) 9443 (> read-process-output-max 4096)) 9444 "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)" 9445 (condition-case _err 9446 (progn (lsp--make-message (list "a" "b")) 9447 nil) 9448 (error t)) 9449 "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000) 9450 "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) 9451 "Using emacs 28+ with native compilation?" 9452 (or (and (fboundp 'native-comp-available-p) 9453 (native-comp-available-p)) 9454 :optional))) 9455 9456 (declare-function package-version-join "ext:package") 9457 (declare-function package-desc-version "ext:package") 9458 (declare-function package--alist "ext:package") 9459 9460 (defun lsp-version () 9461 "Return string describing current version of `lsp-mode'." 9462 (interactive) 9463 (unless (featurep 'package) 9464 (require 'package)) 9465 (let ((ver (format "lsp-mode %s, Emacs %s, %s" 9466 (package-version-join 9467 (package-desc-version 9468 (car (alist-get 'lsp-mode (package--alist))))) 9469 emacs-version 9470 system-type))) 9471 (if (called-interactively-p 'interactive) 9472 (lsp--info "%s" ver) 9473 ver))) 9474 9475 9476 9477 ;; org-mode/virtual-buffer 9478 9479 (declare-function org-babel-get-src-block-info "ext:ob-core") 9480 (declare-function org-do-remove-indentation "ext:org-macs") 9481 (declare-function org-src-get-lang-mode "ext:org-src") 9482 (declare-function org-element-context "ext:org-element") 9483 9484 (defun lsp--virtual-buffer-update-position () 9485 (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range)) 9486 (funcall in-range)) 9487 lsp--virtual-buffer-connections)) 9488 (unless (equal virtual-buffer lsp--virtual-buffer) 9489 (lsp-org)) 9490 (when lsp-managed-mode 9491 (lsp-managed-mode -1) 9492 (lsp-mode -1) 9493 (setq lsp--buffer-workspaces nil) 9494 (setq lsp--virtual-buffer nil) 9495 (setq lsp-buffer-uri nil) 9496 9497 ;; force refresh of diagnostics 9498 (run-hooks 'lsp-after-diagnostics-hook)))) 9499 9500 (defun lsp-virtual-buffer-on-change (start end length) 9501 "Adjust on change event to be executed against the proper language server." 9502 (let ((max-point (max end 9503 (or (plist-get lsp--before-change-vals :end) 0) 9504 (+ start length)))) 9505 (when-let ((virtual-buffer (-first (lambda (vb) 9506 (let ((lsp--virtual-buffer vb)) 9507 (and (lsp-virtual-buffer-call :in-range start) 9508 (lsp-virtual-buffer-call :in-range max-point)))) 9509 lsp--virtual-buffer-connections))) 9510 (lsp-with-current-buffer virtual-buffer 9511 (lsp-on-change start end length 9512 (lambda (&rest _) 9513 (list :range (lsp--range (list :character 0 :line 0) 9514 lsp--virtual-buffer-point-max) 9515 :text (lsp--buffer-content)))))))) 9516 9517 (defun lsp-virtual-buffer-before-change (start _end) 9518 (when-let ((virtual-buffer (-first (lambda (vb) 9519 (lsp-with-current-buffer vb 9520 (lsp-virtual-buffer-call :in-range start))) 9521 lsp--virtual-buffer-connections))) 9522 (lsp-with-current-buffer virtual-buffer 9523 (setq lsp--virtual-buffer-point-max 9524 (lsp--point-to-position (lsp-virtual-buffer-call :last-point)))))) 9525 9526 (defun lsp-patch-on-change-event () 9527 (remove-hook 'after-change-functions #'lsp-on-change t) 9528 (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t) 9529 (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t)) 9530 9531 (defun lsp-kill-virtual-buffers () 9532 (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections)) 9533 9534 (defun lsp--move-point-in-indentation (point indentation) 9535 (save-excursion 9536 (goto-char point) 9537 (if (<= point (+ (line-beginning-position) indentation)) 9538 (line-beginning-position) 9539 point))) 9540 9541 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck") 9542 (declare-function flycheck-add-mode "ext:flycheck") 9543 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics") 9544 9545 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn) 9546 9547 (defun lsp-flycheck-add-mode (mode) 9548 "Register flycheck support for MODE." 9549 (lsp-diagnostics-lsp-checker-if-needed) 9550 (unless (flycheck-checker-supports-major-mode-p 'lsp mode) 9551 (flycheck-add-mode 'lsp mode))) 9552 9553 (defun lsp-progress-spinner-type () 9554 "Retrieve the spinner type value, if value is not a symbol of `spinner-types 9555 defaults to `progress-bar." 9556 (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar)) 9557 9558 (defun lsp-org () 9559 (interactive) 9560 (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range)) 9561 (funcall in-range)) 9562 lsp--virtual-buffer-connections)) 9563 (unless (equal lsp--virtual-buffer virtual-buffer) 9564 (setq lsp--buffer-workspaces workspaces) 9565 (setq lsp--virtual-buffer virtual-buffer) 9566 (setq lsp-buffer-uri nil) 9567 (lsp-mode 1) 9568 (lsp-managed-mode 1) 9569 (lsp-patch-on-change-event)) 9570 9571 (save-excursion 9572 (-let* (virtual-buffer 9573 (wcb (lambda (f) 9574 (with-current-buffer (plist-get virtual-buffer :buffer) 9575 (-let* (((&plist :major-mode :buffer-file-name 9576 :goto-buffer :workspaces) virtual-buffer) 9577 (lsp--virtual-buffer virtual-buffer) 9578 (lsp--buffer-workspaces workspaces)) 9579 (save-excursion 9580 (funcall goto-buffer) 9581 (funcall f)))))) 9582 ((&plist :begin :end :post-blank :language) (cl-second (org-element-context))) 9583 ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light))) 9584 9585 (file-name (if file-name 9586 (f-expand file-name) 9587 (user-error "You should specify file name in the src block header."))) 9588 (begin-marker (progn 9589 (goto-char begin) 9590 (forward-line) 9591 (set-marker (make-marker) (point)))) 9592 (end-marker (progn 9593 (goto-char end) 9594 (forward-line (1- (- post-blank))) 9595 (set-marker (make-marker) (1+ (point))))) 9596 (buf (current-buffer)) 9597 (src-block (buffer-substring-no-properties begin-marker 9598 (1- end-marker))) 9599 (indentation (with-temp-buffer 9600 (insert src-block) 9601 9602 (goto-char (point-min)) 9603 (let ((indentation (current-indentation))) 9604 (plist-put lsp--virtual-buffer :indentation indentation) 9605 (org-do-remove-indentation) 9606 (goto-char (point-min)) 9607 (- indentation (current-indentation)))))) 9608 (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t) 9609 9610 (when (fboundp 'flycheck-add-mode) 9611 (lsp-flycheck-add-mode 'org-mode)) 9612 9613 (setq lsp--virtual-buffer 9614 (list 9615 :in-range (lambda (&optional point) 9616 (<= begin-marker (or point (point)) (1- end-marker))) 9617 :goto-buffer (lambda () (goto-char begin-marker)) 9618 :buffer-string 9619 (lambda () 9620 (let ((src-block (buffer-substring-no-properties 9621 begin-marker 9622 (1- end-marker)))) 9623 (with-temp-buffer 9624 (insert src-block) 9625 9626 (goto-char (point-min)) 9627 (while (not (eobp)) 9628 (delete-region (point) (if (> (+ (point) indentation) (line-end-position)) 9629 (line-end-position) 9630 (+ (point) indentation))) 9631 (forward-line)) 9632 (buffer-substring-no-properties (point-min) 9633 (point-max))))) 9634 :buffer buf 9635 :begin begin-marker 9636 :end end-marker 9637 :indentation indentation 9638 :last-point (lambda () (1- end-marker)) 9639 :cur-position (lambda () 9640 (lsp-save-restriction-and-excursion 9641 (list :line (- (lsp--cur-line) 9642 (lsp--cur-line begin-marker)) 9643 :character (let ((character (- (point) 9644 (line-beginning-position) 9645 indentation))) 9646 (if (< character 0) 9647 0 9648 character))))) 9649 :line/character->point (-lambda (line character) 9650 (-let [inhibit-field-text-motion t] 9651 (+ indentation 9652 (lsp-save-restriction-and-excursion 9653 (goto-char begin-marker) 9654 (forward-line line) 9655 (-let [line-end (line-end-position)] 9656 (if (> character (- line-end (point))) 9657 line-end 9658 (forward-char character) 9659 (point))))))) 9660 :major-mode (org-src-get-lang-mode language) 9661 :buffer-file-name file-name 9662 :buffer-uri (lsp--path-to-uri file-name) 9663 :with-current-buffer wcb 9664 :buffer-live? (lambda (_) (buffer-live-p buf)) 9665 :buffer-name (lambda (_) 9666 (propertize (format "%s(%s:%s)%s" 9667 (buffer-name buf) 9668 begin-marker 9669 end-marker 9670 language) 9671 'face 'italic)) 9672 :real->virtual-line (lambda (line) 9673 (+ line (line-number-at-pos begin-marker) -1)) 9674 :real->virtual-char (lambda (char) (+ char indentation)) 9675 :cleanup (lambda () 9676 (set-marker begin-marker nil) 9677 (set-marker end-marker nil)))) 9678 (setf virtual-buffer lsp--virtual-buffer) 9679 (puthash file-name virtual-buffer lsp--virtual-buffer-mappings) 9680 (push virtual-buffer lsp--virtual-buffer-connections) 9681 9682 ;; TODO: tangle only connected sections 9683 (add-hook 'after-save-hook 'org-babel-tangle nil t) 9684 (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t) 9685 (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t) 9686 9687 (setq lsp--buffer-workspaces 9688 (lsp-with-current-buffer virtual-buffer 9689 (lsp) 9690 (plist-put virtual-buffer :workspaces (lsp-workspaces)) 9691 (lsp-workspaces))))))) 9692 9693 (defun lsp-virtual-buffer-disconnect (virtual-buffer) 9694 (interactive (list (or 9695 lsp--virtual-buffer 9696 (when lsp--virtual-buffer-connections 9697 (lsp--completing-read "Select virtual buffer to disconnect: " 9698 lsp--virtual-buffer-connections 9699 (-lambda ((&plist :buffer-file-name)) 9700 buffer-file-name)))))) 9701 (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer) 9702 (progn 9703 (lsp-with-current-buffer virtual-buffer 9704 (lsp--text-document-did-close)) 9705 (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections)) 9706 (when (eq virtual-buffer lsp--virtual-buffer) 9707 (setf lsp--virtual-buffer nil)) 9708 (when cleanup (funcall cleanup)) 9709 (remhash file-name lsp--virtual-buffer-mappings) 9710 9711 (lsp--virtual-buffer-update-position) 9712 (lsp--info "Disconnected from buffer %s" file-name)) 9713 (lsp--error "Nothing to disconnect from?"))) 9714 9715 9716 ;; inlay hints 9717 9718 (defface lsp-inlay-hint-face 9719 '((t :inherit font-lock-comment-face)) 9720 "The face to use for the JavaScript inlays." 9721 :group 'lsp-mode 9722 :package-version '(lsp-mode . "9.0.0")) 9723 9724 (defface lsp-inlay-hint-type-face 9725 '((t :inherit lsp-inlay-hint-face)) 9726 "Face for inlay type hints (e.g. inferred variable types)." 9727 :group 'lsp-mode 9728 :package-version '(lsp-mode . "9.0.0")) 9729 9730 (defcustom lsp-inlay-hint-type-format "%s" 9731 "Format string for variable inlays (part of the inlay face)." 9732 :type '(string :tag "String") 9733 :group 'lsp-mode 9734 :package-version '(lsp-mode . "9.0.0")) 9735 9736 (defface lsp-inlay-hint-parameter-face 9737 '((t :inherit lsp-inlay-hint-face)) 9738 "Face for inlay parameter hints (e.g. function parameter names at 9739 call-site)." 9740 :group 'lsp-mode 9741 :package-version '(lsp-mode . "9.0.0")) 9742 9743 (defcustom lsp-inlay-hint-param-format "%s" 9744 "Format string for parameter inlays (part of the inlay face)." 9745 :type '(string :tag "String") 9746 :group 'lsp-mode 9747 :package-version '(lsp-mode . "9.0.0")) 9748 9749 (defcustom lsp-update-inlay-hints-on-scroll t 9750 "If non-nil update inlay hints immediately when scrolling or 9751 modifying window sizes." 9752 :type 'boolean 9753 :package-version '(lsp-mode . "9.0.0")) 9754 9755 (defun lsp--format-inlay (text kind) 9756 (cond 9757 ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text)) 9758 ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text)) 9759 (t text))) 9760 9761 (defun lsp--face-for-inlay (kind) 9762 (cond 9763 ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face) 9764 ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face) 9765 (t 'lsp-inlay-hint-face))) 9766 9767 (defun lsp--update-inlay-hints-scroll-function (window start) 9768 (lsp-update-inlay-hints start (window-end window t))) 9769 9770 (defun lsp--update-inlay-hints () 9771 (lsp-update-inlay-hints (window-start) (window-end nil t))) 9772 9773 (defun lsp--label-from-inlay-hints-response (label) 9774 "Returns a string label built from an array of 9775 InlayHintLabelParts or the argument itself if it's already a 9776 string." 9777 (cl-typecase label 9778 (string label) 9779 (vector 9780 (string-join (mapcar (lambda (part) 9781 (-let (((&InlayHintLabelPart :value) part)) 9782 value)) 9783 label))))) 9784 9785 (defun lsp-update-inlay-hints (start end) 9786 (lsp-request-async 9787 "textDocument/inlayHint" 9788 (lsp-make-inlay-hints-params 9789 :text-document (lsp--text-document-identifier) 9790 :range (lsp-make-range :start 9791 (lsp-point-to-position start) 9792 :end 9793 (lsp-point-to-position end))) 9794 (lambda (res) 9795 (lsp--remove-overlays 'lsp-inlay-hint) 9796 (dolist (hint res) 9797 (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint) 9798 (kind (or kind? lsp/inlay-hint-kind-type-hint)) 9799 (label (lsp--label-from-inlay-hints-response label)) 9800 (pos (lsp--position-to-point position)) 9801 (overlay (make-overlay pos pos nil 'front-advance 'end-advance))) 9802 (when (stringp label) 9803 (overlay-put overlay 'lsp-inlay-hint t) 9804 (overlay-put overlay 'before-string 9805 (format "%s%s%s" 9806 (if padding-left? " " "") 9807 (propertize (lsp--format-inlay label kind) 9808 'font-lock-face (lsp--face-for-inlay kind)) 9809 (if padding-right? " " ""))))))) 9810 :mode 'tick)) 9811 9812 (define-minor-mode lsp-inlay-hints-mode 9813 "Mode for displaying inlay hints." 9814 :lighter nil 9815 (cond 9816 ((and lsp-inlay-hints-mode lsp--buffer-workspaces) 9817 (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t) 9818 (when lsp-update-inlay-hints-on-scroll 9819 (add-to-list (make-local-variable 'window-scroll-functions) 9820 #'lsp--update-inlay-hints-scroll-function))) 9821 (t 9822 (lsp--remove-overlays 'lsp-inlay-hint) 9823 (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t) 9824 (setf window-scroll-functions 9825 (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions))))) 9826 9827 9828 9829 ;;;###autoload 9830 (defun lsp-start-plain () 9831 "Start `lsp-mode' using minimal configuration using the latest `melpa' version 9832 of the packages. 9833 9834 In case the major-mode that you are using for " 9835 (interactive) 9836 (let ((start-plain (make-temp-file "plain" nil ".el"))) 9837 (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el" 9838 start-plain t) 9839 (async-shell-command 9840 (format "%s -q -l %s %s" 9841 (expand-file-name invocation-name invocation-directory) 9842 start-plain 9843 (or (buffer-file-name) "")) 9844 (generate-new-buffer " *lsp-start-plain*")))) 9845 9846 9847 9848 (provide 'lsp-mode) 9849 ;;; lsp-mode.el ends here