lsp-mode.el (427693B)
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-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-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-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 lsp-sqls 191 lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform lsp-tex lsp-tilt 192 lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-v lsp-vala lsp-verilog 193 lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl lsp-xml lsp-yaml 194 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 ("\\.ebuild$" . "shellscript") 773 ("\\.go\\'" . "go") 774 ("\\.html$" . "html") 775 ("\\.hx$" . "haxe") 776 ("\\.hy$" . "hy") 777 ("\\.java\\'" . "java") 778 ("\\.jq$" . "jq") 779 ("\\.js$" . "javascript") 780 ("\\.json$" . "json") 781 ("\\.jsonc$" . "jsonc") 782 ("\\.jsonnet$" . "jsonnet") 783 ("\\.jsx$" . "javascriptreact") 784 ("\\.lua$" . "lua") 785 ("\\.mdx\\'" . "mdx") 786 ("\\.nu$" . "nushell") 787 ("\\.php$" . "php") 788 ("\\.ps[dm]?1\\'" . "powershell") 789 ("\\.rs\\'" . "rust") 790 ("\\.spec\\'" . "rpm-spec") 791 ("\\.sql$" . "sql") 792 ("\\.svelte$" . "svelte") 793 ("\\.toml\\'" . "toml") 794 ("\\.ts$" . "typescript") 795 ("\\.tsx$" . "typescriptreact") 796 ("\\.ttcn3$" . "ttcn3") 797 ("\\.vue$" . "vue") 798 ("\\.xml$" . "xml") 799 ("\\ya?ml$" . "yaml") 800 ("^PKGBUILD$" . "shellscript") 801 ("^go\\.mod\\'" . "go.mod") 802 ("^settings\\.json$" . "jsonc") 803 ("^yang\\.settings$" . "jsonc") 804 (ada-mode . "ada") 805 (ada-ts-mode . "ada") 806 (gpr-mode . "gpr") 807 (gpr-ts-mode . "gpr") 808 (awk-mode . "awk") 809 (awk-ts-mode . "awk") 810 (nxml-mode . "xml") 811 (sql-mode . "sql") 812 (vimrc-mode . "vim") 813 (vimscript-ts-mode . "vim") 814 (sh-mode . "shellscript") 815 (bash-ts-mode . "shellscript") 816 (ebuild-mode . "shellscript") 817 (pkgbuild-mode . "shellscript") 818 (envrc-file-mode . "shellscript") 819 (scala-mode . "scala") 820 (scala-ts-mode . "scala") 821 (julia-mode . "julia") 822 (julia-ts-mode . "julia") 823 (clojure-mode . "clojure") 824 (clojurec-mode . "clojure") 825 (clojurescript-mode . "clojurescript") 826 (clojure-ts-mode . "clojure") 827 (clojure-ts-clojurec-mode . "clojure") 828 (clojure-ts-clojurescript-mode . "clojurescript") 829 (java-mode . "java") 830 (java-ts-mode . "java") 831 (jdee-mode . "java") 832 (groovy-mode . "groovy") 833 (python-mode . "python") 834 (python-ts-mode . "python") 835 (cython-mode . "python") 836 ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo") 837 (lsp--render-markdown . "markdown") 838 (move-mode . "move") 839 (rust-mode . "rust") 840 (rust-ts-mode . "rust") 841 (rustic-mode . "rust") 842 (kotlin-mode . "kotlin") 843 (kotlin-ts-mode . "kotlin") 844 (css-mode . "css") 845 (css-ts-mode . "css") 846 (less-mode . "less") 847 (less-css-mode . "less") 848 (lua-mode . "lua") 849 (lua-ts-mode . "lua") 850 (sass-mode . "sass") 851 (ssass-mode . "sass") 852 (scss-mode . "scss") 853 (scad-mode . "openscad") 854 (xml-mode . "xml") 855 (c-mode . "c") 856 (c-ts-mode . "c") 857 (c++-mode . "cpp") 858 (c++-ts-mode . "cpp") 859 (cuda-mode . "cuda") 860 (objc-mode . "objective-c") 861 (html-mode . "html") 862 (html-ts-mode . "html") 863 (sgml-mode . "html") 864 (mhtml-mode . "html") 865 (mint-mode . "mint") 866 (go-dot-mod-mode . "go.mod") 867 (go-mod-ts-mode . "go.mod") 868 (go-mode . "go") 869 (go-ts-mode . "go") 870 (graphql-mode . "graphql") 871 (haskell-mode . "haskell") 872 (hack-mode . "hack") 873 (php-mode . "php") 874 (php-ts-mode . "php") 875 (powershell-mode . "powershell") 876 (powershell-mode . "PowerShell") 877 (powershell-ts-mode . "powershell") 878 (json-mode . "json") 879 (json-ts-mode . "json") 880 (jsonc-mode . "jsonc") 881 (rjsx-mode . "javascript") 882 (js2-mode . "javascript") 883 (js-mode . "javascript") 884 (js-ts-mode . "javascript") 885 (typescript-mode . "typescript") 886 (typescript-ts-mode . "typescript") 887 (tsx-ts-mode . "typescriptreact") 888 (svelte-mode . "svelte") 889 (fsharp-mode . "fsharp") 890 (reason-mode . "reason") 891 (caml-mode . "ocaml") 892 (tuareg-mode . "ocaml") 893 (swift-mode . "swift") 894 (elixir-mode . "elixir") 895 (elixir-ts-mode . "elixir") 896 (heex-ts-mode . "elixir") 897 (conf-javaprop-mode . "spring-boot-properties") 898 (yaml-mode . "yaml") 899 (yaml-ts-mode . "yaml") 900 (ruby-mode . "ruby") 901 (enh-ruby-mode . "ruby") 902 (ruby-ts-mode . "ruby") 903 (fortran-mode . "fortran") 904 (f90-mode . "fortran") 905 (elm-mode . "elm") 906 (dart-mode . "dart") 907 (erlang-mode . "erlang") 908 (dockerfile-mode . "dockerfile") 909 (dockerfile-ts-mode . "dockerfile") 910 (csharp-mode . "csharp") 911 (csharp-tree-sitter-mode . "csharp") 912 (csharp-ts-mode . "csharp") 913 (plain-tex-mode . "plaintex") 914 (context-mode . "context") 915 (cypher-mode . "cypher") 916 (latex-mode . "latex") 917 (v-mode . "v") 918 (vhdl-mode . "vhdl") 919 (vhdl-ts-mode . "vhdl") 920 (verilog-mode . "verilog") 921 (terraform-mode . "terraform") 922 (ess-julia-mode . "julia") 923 (ess-r-mode . "r") 924 (crystal-mode . "crystal") 925 (nim-mode . "nim") 926 (dhall-mode . "dhall") 927 (cmake-mode . "cmake") 928 (cmake-ts-mode . "cmake") 929 (purescript-mode . "purescript") 930 (gdscript-mode . "gdscript") 931 (gdscript-ts-mode . "gdscript") 932 (perl-mode . "perl") 933 (cperl-mode . "perl") 934 (robot-mode . "robot") 935 (racket-mode . "racket") 936 (nix-mode . "nix") 937 (nix-ts-mode . "Nix") 938 (prolog-mode . "prolog") 939 (vala-mode . "vala") 940 (actionscript-mode . "actionscript") 941 (d-mode . "d") 942 (zig-mode . "zig") 943 (text-mode . "plaintext") 944 (markdown-mode . "markdown") 945 (gfm-mode . "markdown") 946 (beancount-mode . "beancount") 947 (conf-toml-mode . "toml") 948 (toml-ts-mode . "toml") 949 (org-mode . "org") 950 (org-journal-mode . "org") 951 (nginx-mode . "nginx") 952 (magik-mode . "magik") 953 (magik-ts-mode . "magik") 954 (idris-mode . "idris") 955 (idris2-mode . "idris2") 956 (gleam-mode . "gleam") 957 (graphviz-dot-mode . "dot") 958 (tiltfile-mode . "tiltfile") 959 (solidity-mode . "solidity") 960 (bibtex-mode . "bibtex") 961 (rst-mode . "restructuredtext") 962 (glsl-mode . "glsl") 963 (shader-mode . "shaderlab") 964 (wgsl-mode . "wgsl") 965 (jq-mode . "jq") 966 (jq-ts-mode . "jq") 967 (protobuf-mode . "protobuf") 968 (nushell-mode . "nushell") 969 (nushell-ts-mode . "nushell") 970 (yang-mode . "yang")) 971 "Language id configuration.") 972 973 (defvar lsp--last-active-workspaces nil 974 "Keep track of last active workspace. 975 We want to try the last workspace first when jumping into a library 976 directory") 977 978 (defvar lsp-method-requirements 979 '(("textDocument/callHierarchy" :capability :callHierarchyProvider) 980 ("textDocument/codeAction" :capability :codeActionProvider) 981 ("codeAction/resolve" 982 :check-command (lambda (workspace) 983 (with-lsp-workspace workspace 984 (lsp:code-action-options-resolve-provider? 985 (lsp--capability-for-method "textDocument/codeAction"))))) 986 ("textDocument/codeLens" :capability :codeLensProvider) 987 ("textDocument/completion" :capability :completionProvider) 988 ("completionItem/resolve" 989 :check-command (lambda (wk) 990 (with-lsp-workspace wk 991 (lsp:completion-options-resolve-provider? 992 (lsp--capability-for-method "textDocument/completion"))))) 993 ("textDocument/declaration" :capability :declarationProvider) 994 ("textDocument/definition" :capability :definitionProvider) 995 ("textDocument/documentColor" :capability :colorProvider) 996 ("textDocument/documentLink" :capability :documentLinkProvider) 997 ("textDocument/inlayHint" :capability :inlayHintProvider) 998 ("textDocument/documentHighlight" :capability :documentHighlightProvider) 999 ("textDocument/documentSymbol" :capability :documentSymbolProvider) 1000 ("textDocument/foldingRange" :capability :foldingRangeProvider) 1001 ("textDocument/formatting" :capability :documentFormattingProvider) 1002 ("textDocument/hover" :capability :hoverProvider) 1003 ("textDocument/implementation" :capability :implementationProvider) 1004 ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider) 1005 ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider) 1006 ("textDocument/prepareRename" 1007 :check-command (lambda (workspace) 1008 (with-lsp-workspace workspace 1009 (lsp:rename-options-prepare-provider? 1010 (lsp--capability-for-method "textDocument/rename"))))) 1011 ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider) 1012 ("textDocument/references" :capability :referencesProvider) 1013 ("textDocument/rename" :capability :renameProvider) 1014 ("textDocument/selectionRange" :capability :selectionRangeProvider) 1015 ("textDocument/semanticTokens" :capability :semanticTokensProvider) 1016 ("textDocument/semanticTokensFull" 1017 :check-command (lambda (workspace) 1018 (with-lsp-workspace workspace 1019 (lsp-get (lsp--capability :semanticTokensProvider) :full)))) 1020 ("textDocument/semanticTokensFull/Delta" 1021 :check-command (lambda (workspace) 1022 (with-lsp-workspace workspace 1023 (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full))) 1024 (and (not (booleanp capFull)) (lsp-get capFull :delta)))))) 1025 ("textDocument/semanticTokensRangeProvider" 1026 :check-command (lambda (workspace) 1027 (with-lsp-workspace workspace 1028 (lsp-get (lsp--capability :semanticTokensProvider) :range)))) 1029 ("textDocument/signatureHelp" :capability :signatureHelpProvider) 1030 ("textDocument/typeDefinition" :capability :typeDefinitionProvider) 1031 ("textDocument/typeHierarchy" :capability :typeHierarchyProvider) 1032 ("workspace/executeCommand" :capability :executeCommandProvider) 1033 ("workspace/symbol" :capability :workspaceSymbolProvider)) 1034 1035 "Map methods to requirements. 1036 It is used by request-sending functions to determine which server 1037 must be used for handling a particular message.") 1038 1039 (defconst lsp--file-change-type 1040 `((created . 1) 1041 (changed . 2) 1042 (deleted . 3))) 1043 1044 (defconst lsp--watch-kind 1045 `((create . 1) 1046 (change . 2) 1047 (delete . 4))) 1048 1049 (defvar lsp-window-body-width 40 1050 "Window body width when rendering doc.") 1051 1052 (defface lsp-face-highlight-textual 1053 '((t :inherit highlight)) 1054 "Face used for textual occurrences of symbols." 1055 :group 'lsp-mode) 1056 1057 (defface lsp-face-highlight-read 1058 '((t :inherit highlight :underline t)) 1059 "Face used for highlighting symbols being read." 1060 :group 'lsp-mode) 1061 1062 (defface lsp-face-highlight-write 1063 '((t :inherit highlight :weight bold)) 1064 "Face used for highlighting symbols being written to." 1065 :group 'lsp-mode) 1066 1067 (define-obsolete-variable-alias 'lsp-lens-auto-enable 1068 'lsp-lens-enable "lsp-mode 7.0.1") 1069 1070 (defcustom lsp-lens-enable t 1071 "Auto enable lenses if server supports." 1072 :group 'lsp-lens 1073 :type 'boolean 1074 :package-version '(lsp-mode . "6.3")) 1075 1076 (defcustom lsp-symbol-highlighting-skip-current nil 1077 "If non-nil skip current symbol when setting symbol highlights." 1078 :group 'lsp-mode 1079 :type 'boolean) 1080 1081 (defcustom lsp-file-watch-threshold 1000 1082 "Show warning if the files to watch are more than. 1083 Set to nil to disable the warning." 1084 :type 'number 1085 :group 'lsp-mode) 1086 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i)))) 1087 1088 (defvar lsp-custom-markup-modes 1089 '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic")) 1090 "Mode to uses with markdown code blocks. 1091 They are added to `markdown-code-lang-modes'") 1092 1093 (defcustom lsp-signature-render-documentation t 1094 "Display signature documentation in `eldoc'." 1095 :type 'boolean 1096 :group 'lsp-mode 1097 :package-version '(lsp-mode . "6.2")) 1098 1099 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request) 1100 "Auto activate signature conditions." 1101 :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char) 1102 (const :tag "After selected completion." :after-completion) 1103 (const :tag "When the server has sent show signature help." :on-server-request))) 1104 :group 'lsp-mode 1105 :package-version '(lsp-mode . "6.2")) 1106 1107 (defcustom lsp-signature-doc-lines 20 1108 "If number, limit the number of lines to show in the docs." 1109 :type 'number 1110 :group 'lsp-mode 1111 :package-version '(lsp-mode . "6.3")) 1112 1113 (defcustom lsp-signature-function 'lsp-lv-message 1114 "The function used for displaying signature info. 1115 It will be called with one param - the signature info. When 1116 called with nil the signature info must be cleared." 1117 :type 'function 1118 :group 'lsp-mode 1119 :package-version '(lsp-mode . "6.3")) 1120 1121 (defcustom lsp-keymap-prefix "s-l" 1122 "LSP-mode keymap prefix." 1123 :group 'lsp-mode 1124 :type 'string 1125 :package-version '(lsp-mode . "6.3")) 1126 1127 (defvar-local lsp--buffer-workspaces () 1128 "List of the buffer workspaces.") 1129 1130 (defvar-local lsp--buffer-deferred nil 1131 "Whether buffer was loaded via `lsp-deferred'.") 1132 1133 (defvar lsp--session nil 1134 "Contain the `lsp-session' for the current Emacs instance.") 1135 1136 (defvar lsp--tcp-port 10000) 1137 1138 (defvar lsp--client-packages-required nil 1139 "If nil, `lsp-client-packages' are yet to be required.") 1140 1141 (defvar lsp--tcp-server-port 0 1142 "The server socket which is opened when using `lsp-tcp-server' (a server 1143 socket is opened in Emacs and the language server connects to it). The 1144 default value of 0 ensures that a random high port is used. Set it to a positive 1145 integer to use a specific port.") 1146 1147 (defvar lsp--tcp-server-wait-seconds 10 1148 "Wait this amount of time for the client to connect to our server socket 1149 when using `lsp-tcp-server'.") 1150 1151 (defvar-local lsp--document-symbols nil 1152 "The latest document symbols.") 1153 1154 (defvar-local lsp--document-selection-range-cache nil 1155 "The document selection cache.") 1156 1157 (defvar-local lsp--document-symbols-request-async nil 1158 "If non-nil, request document symbols asynchronously.") 1159 1160 (defvar-local lsp--document-symbols-tick -1 1161 "The value of `buffer-chars-modified-tick' when document 1162 symbols were last retrieved.") 1163 1164 (defvar-local lsp--have-document-highlights nil 1165 "Set to `t' on symbol highlighting, cleared on 1166 `lsp--cleanup-highlights-if-needed'. Checking a separately 1167 defined flag is substantially faster than unconditionally 1168 calling `remove-overlays'.") 1169 1170 ;; Buffer local variable for storing number of lines. 1171 (defvar lsp--log-lines) 1172 1173 (defvar-local lsp--eldoc-saved-message nil) 1174 1175 (defvar lsp--on-change-timer nil) 1176 (defvar lsp--on-idle-timer nil) 1177 1178 (defvar-local lsp--signature-last nil) 1179 (defvar-local lsp--signature-last-index nil) 1180 (defvar lsp--signature-last-buffer nil) 1181 1182 (defvar-local lsp--virtual-buffer-point-max nil) 1183 1184 (cl-defmethod lsp-execute-command (_server _command _arguments) 1185 "Ask SERVER to execute COMMAND with ARGUMENTS.") 1186 1187 (defun lsp-elt (sequence n) 1188 "Return Nth element of SEQUENCE or nil if N is out of range." 1189 (cond 1190 ((listp sequence) (elt sequence n)) 1191 ((arrayp sequence) 1192 (and (> (length sequence) n) (aref sequence n))) 1193 (t (and (> (length sequence) n) (elt sequence n))))) 1194 1195 ;; define seq-first and seq-rest for older emacs 1196 (defun lsp-seq-first (sequence) 1197 "Return the first element of SEQUENCE." 1198 (lsp-elt sequence 0)) 1199 1200 (defun lsp-seq-rest (sequence) 1201 "Return a sequence of the elements of SEQUENCE except the first one." 1202 (seq-drop sequence 1)) 1203 1204 ;;;###autoload 1205 (defun lsp--string-listp (sequence) 1206 "Return t if all elements of SEQUENCE are strings, else nil." 1207 (not (seq-find (lambda (x) (not (stringp x))) sequence))) 1208 1209 (defun lsp--string-vector-p (candidate) 1210 "Returns true if CANDIDATE is a vector data structure and 1211 every element of it is of type string, else nil." 1212 (and 1213 (vectorp candidate) 1214 (seq-every-p #'stringp candidate))) 1215 1216 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0") 1217 1218 (defun lsp--editable-vector-match (widget value) 1219 "Function for `lsp-editable-vector' :match." 1220 ;; Value must be a list or a vector and all the members must match the type. 1221 (and (or (listp value) (vectorp value)) 1222 (length (cdr (lsp--editable-vector-match-inline widget value))))) 1223 1224 (defun lsp--editable-vector-match-inline (widget value) 1225 "Value for `lsp-editable-vector' :match-inline." 1226 (let ((type (nth 0 (widget-get widget :args))) 1227 (ok t) 1228 found) 1229 (while (and value ok) 1230 (let ((answer (widget-match-inline type value))) 1231 (if answer 1232 (let ((head (if (vectorp answer) (aref answer 0) (car answer))) 1233 (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer)))) 1234 (setq found (append found head) 1235 value tail)) 1236 (setq ok nil)))) 1237 (cons found value))) 1238 1239 (defun lsp--editable-vector-value-to-external (_widget internal-value) 1240 "Convert the internal list value to a vector." 1241 (if (listp internal-value) 1242 (apply 'vector internal-value) 1243 internal-value)) 1244 1245 (defun lsp--editable-vector-value-to-internal (_widget external-value) 1246 "Convert the external vector value to a list." 1247 (if (vectorp external-value) 1248 (append external-value nil) 1249 external-value)) 1250 1251 (define-widget 'lsp--editable-vector 'editable-list 1252 "A subclass of `editable-list' that accepts and returns a 1253 vector instead of a list." 1254 :value-to-external 'lsp--editable-vector-value-to-external 1255 :value-to-internal 'lsp--editable-vector-value-to-internal 1256 :match 'lsp--editable-vector-match 1257 :match-inline 'lsp--editable-vector-match-inline) 1258 1259 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector 1260 "A variable length homogeneous vector." 1261 :tag "Repeat" 1262 :format "%{%t%}:\n%v%i\n") 1263 1264 (define-widget 'lsp-string-vector 'lazy 1265 "A vector of zero or more elements, every element of which is a string. 1266 Appropriate for any language-specific `defcustom' that needs to 1267 serialize as a JSON array of strings. 1268 1269 Deprecated. Use `lsp-repeatable-vector' instead. " 1270 :offset 4 1271 :tag "Vector" 1272 :type '(lsp-repeatable-vector string)) 1273 1274 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0") 1275 1276 (defvar lsp--show-message t 1277 "If non-nil, show debug message from `lsp-mode'.") 1278 1279 (defun lsp--message (format &rest args) 1280 "Wrapper for `message' 1281 1282 We `inhibit-message' the message when the cursor is in the 1283 minibuffer and when emacs version is before emacs 27 due to the 1284 fact that we often use `lsp--info', `lsp--warn' and `lsp--error' 1285 in async context and the call to these function is removing the 1286 minibuffer prompt. The issue with async messages is already fixed 1287 in emacs 27. 1288 1289 See #2049" 1290 (when lsp--show-message 1291 (let ((inhibit-message (or inhibit-message 1292 (and (minibufferp) 1293 (version< emacs-version "27.0"))))) 1294 (apply #'message format args)))) 1295 1296 (defun lsp--info (format &rest args) 1297 "Display lsp info message with FORMAT with ARGS." 1298 (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args))) 1299 1300 (defun lsp--warn (format &rest args) 1301 "Display lsp warn message with FORMAT with ARGS." 1302 (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args))) 1303 1304 (defun lsp--error (format &rest args) 1305 "Display lsp error message with FORMAT with ARGS." 1306 (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args))) 1307 1308 (defun lsp-log (format &rest args) 1309 "Log message to the ’*lsp-log*’ buffer. 1310 1311 FORMAT and ARGS i the same as for `message'." 1312 (when lsp-log-max 1313 (let ((log-buffer (get-buffer "*lsp-log*")) 1314 (inhibit-read-only t)) 1315 (unless log-buffer 1316 (setq log-buffer (get-buffer-create "*lsp-log*")) 1317 (with-current-buffer log-buffer 1318 (buffer-disable-undo) 1319 (view-mode 1) 1320 (set (make-local-variable 'lsp--log-lines) 0))) 1321 (with-current-buffer log-buffer 1322 (save-excursion 1323 (let* ((message (apply 'format format args)) 1324 ;; Count newlines in message. 1325 (newlines (1+ (cl-loop with start = 0 1326 for count from 0 1327 while (string-match "\n" message start) 1328 do (setq start (match-end 0)) 1329 finally return count)))) 1330 (goto-char (point-max)) 1331 1332 ;; in case the buffer is not empty insert before last \n to preserve 1333 ;; the point position(in case it is in the end) 1334 (if (eq (point) (point-min)) 1335 (progn 1336 (insert "\n") 1337 (backward-char)) 1338 (backward-char) 1339 (insert "\n")) 1340 (insert message) 1341 1342 (setq lsp--log-lines (+ lsp--log-lines newlines)) 1343 1344 (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max)) 1345 (let ((to-delete (- lsp--log-lines lsp-log-max))) 1346 (goto-char (point-min)) 1347 (forward-line to-delete) 1348 (delete-region (point-min) (point)) 1349 (setq lsp--log-lines lsp-log-max))))))))) 1350 1351 (defalias 'lsp-message 'lsp-log) 1352 1353 (defalias 'lsp-ht 'ht) 1354 1355 (defalias 'lsp-file-local-name 'file-local-name) 1356 1357 (defun lsp-f-canonical (file-name) 1358 "Return the canonical FILE-NAME, without a trailing slash." 1359 (directory-file-name (expand-file-name file-name))) 1360 1361 (defalias 'lsp-canonical-file-name 'lsp-f-canonical) 1362 1363 (defun lsp-f-same? (path-a path-b) 1364 "Return t if PATH-A and PATH-B are references to the same file. 1365 Symlinks are not followed." 1366 (when (and (f-exists? path-a) 1367 (f-exists? path-b)) 1368 (equal 1369 (lsp-f-canonical (directory-file-name (f-expand path-a))) 1370 (lsp-f-canonical (directory-file-name (f-expand path-b)))))) 1371 1372 (defun lsp-f-parent (path) 1373 "Return the parent directory to PATH. 1374 Symlinks are not followed." 1375 (let ((parent (file-name-directory 1376 (directory-file-name (f-expand path default-directory))))) 1377 (unless (lsp-f-same? path parent) 1378 (if (f-relative? path) 1379 (f-relative parent) 1380 (directory-file-name parent))))) 1381 1382 (defun lsp-f-ancestor-of? (path-a path-b) 1383 "Return t if PATH-A is an ancestor of PATH-B. 1384 Symlinks are not followed." 1385 (unless (lsp-f-same? path-a path-b) 1386 (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator)) 1387 (lsp-f-canonical path-b)))) 1388 1389 (defun lsp--merge-results (results method) 1390 "Merge RESULTS by filtering the empty hash-tables and merging 1391 the lists according to METHOD." 1392 (pcase (--map (if (vectorp it) 1393 (append it nil) it) 1394 (-filter #'identity results)) 1395 (`() ()) 1396 ;; only one result - simply return it 1397 (`(,fst) fst) 1398 ;; multiple results merge it based on strategy 1399 (results 1400 (pcase method 1401 ("textDocument/hover" (pcase (seq-filter 1402 (-compose #'not #'lsp-empty?) 1403 results) 1404 (`(,hover) hover) 1405 (hovers (lsp-make-hover 1406 :contents 1407 (-mapcat 1408 (-lambda ((&Hover :contents)) 1409 (if (and (sequencep contents) 1410 (not (stringp contents))) 1411 (append contents ()) 1412 (list contents))) 1413 hovers))))) 1414 ("textDocument/completion" 1415 (lsp-make-completion-list 1416 :is-incomplete (seq-some 1417 #'lsp:completion-list-is-incomplete 1418 results) 1419 :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it) 1420 (lsp:completion-list-items it) 1421 it) 1422 nil)) 1423 results))) 1424 ("completionItem/resolve" 1425 (let ((item (cl-first results))) 1426 (when-let ((details (seq-filter #'identity 1427 (seq-map #'lsp:completion-item-detail? results)))) 1428 (lsp:set-completion-item-detail? 1429 item 1430 (string-join details " "))) 1431 (when-let ((docs (seq-filter #'identity 1432 (seq-map #'lsp:completion-item-documentation? results)))) 1433 (lsp:set-completion-item-documentation? 1434 item 1435 (lsp-make-markup-content 1436 :kind (or (seq-some (lambda (it) 1437 (when (equal (lsp:markup-content-kind it) 1438 lsp/markup-kind-markdown) 1439 lsp/markup-kind-markdown)) 1440 docs) 1441 lsp/markup-kind-plain-text) 1442 :value (string-join (seq-map (lambda (doc) 1443 (or (lsp:markup-content-value doc) 1444 (and (stringp doc) doc))) 1445 docs) 1446 "\n")))) 1447 (when-let ((edits (seq-filter #'identity 1448 (seq-map #'lsp:completion-item-additional-text-edits? results)))) 1449 (lsp:set-completion-item-additional-text-edits? 1450 item 1451 (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits))) 1452 item)) 1453 (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results)))))) 1454 1455 (defun lsp--spinner-start () 1456 "Start spinner indication." 1457 (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error))) 1458 1459 (defun lsp--propertize (str type) 1460 "Propertize STR as per TYPE." 1461 (propertize str 'face (alist-get type lsp--message-type-face))) 1462 1463 (defun lsp-workspaces () 1464 "Return the lsp workspaces associated with the current project." 1465 (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces)) 1466 1467 (defun lsp--completing-read (prompt collection transform-fn &optional predicate 1468 require-match initial-input 1469 hist def inherit-input-method) 1470 "Wrap `completing-read' to provide transformation function and disable sort. 1471 1472 TRANSFORM-FN will be used to transform each of the items before displaying. 1473 1474 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF 1475 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes." 1476 (let* ((col (--map (cons (funcall transform-fn it) it) collection)) 1477 (completion (completing-read prompt 1478 (lambda (string pred action) 1479 (if (eq action 'metadata) 1480 `(metadata (display-sort-function . identity)) 1481 (complete-with-action action col string pred))) 1482 predicate require-match initial-input hist 1483 def inherit-input-method))) 1484 (cdr (assoc completion col)))) 1485 1486 (defconst lsp--system-arch (lambda () 1487 (setq lsp--system-arch 1488 (pcase system-type 1489 ('windows-nt 1490 (pcase system-configuration 1491 ((rx bol "x86_64-") 'x64) 1492 (_ 'x86))) 1493 ('darwin 1494 (pcase system-configuration 1495 ((rx "aarch64-") 'arm64) 1496 (_ 'x64))) 1497 ('gnu/linux 1498 (pcase system-configuration 1499 ((rx bol "x86_64") 'x64) 1500 ((rx bol (| "i386" "i886")) 'x32))) 1501 (_ 1502 (pcase system-configuration 1503 ((rx bol "x86_64") 'x64) 1504 ((rx bol (| "i386" "i886")) 'x32)))))) 1505 "Return the system architecture of `Emacs'. 1506 Special values: 1507 `x64' 64bit 1508 `x32' 32bit 1509 `arm64' ARM 64bit") 1510 1511 (defmacro lsp-with-current-buffer (buffer-id &rest body) 1512 (declare (indent 1) (debug t)) 1513 `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer))) 1514 (with-lsp-workspaces (plist-get ,buffer-id :workspaces) 1515 (funcall wcb (lambda () ,@body))) 1516 (with-current-buffer ,buffer-id 1517 ,@body))) 1518 1519 (defvar lsp--throw-on-input nil 1520 "Make `lsp-*-while-no-input' throws `input' on interrupted.") 1521 1522 (defmacro lsp--catch (tag bodyform &rest handlers) 1523 "Catch TAG thrown in BODYFORM. 1524 The return value from TAG will be handled in HANDLERS by `pcase'." 1525 (declare (debug (form form &rest (pcase-PAT body))) (indent 2)) 1526 (let ((re-sym (make-symbol "re"))) 1527 `(let ((,re-sym (catch ,tag ,bodyform))) 1528 (pcase ,re-sym 1529 ,@handlers)))) 1530 1531 (defmacro lsp--while-no-input (&rest body) 1532 "Wrap BODY in `while-no-input' and respecting `non-essential'. 1533 If `lsp--throw-on-input' is set, will throw if input is pending, else 1534 return value of `body' or nil if interrupted." 1535 (declare (debug t) (indent 0)) 1536 `(if non-essential 1537 (let ((res (while-no-input ,@body))) 1538 (cond 1539 ((and lsp--throw-on-input (equal res t)) 1540 (throw 'input :interrupted)) 1541 ((booleanp res) nil) 1542 (t res))) 1543 ,@body)) 1544 1545 ;; A ‘lsp--client’ object describes the client-side behavior of a language 1546 ;; server. It is used to start individual server processes, each of which is 1547 ;; represented by a ‘lsp--workspace’ object. Client objects are normally 1548 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’. Each 1549 ;; workspace refers to exactly one client, but there can be multiple workspaces 1550 ;; for a single client. 1551 (cl-defstruct lsp--client 1552 ;; ‘language-id’ is a function that receives a buffer as a single argument 1553 ;; and should return the language identifier for that buffer. See 1554 ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem 1555 ;; for a list of language identifiers. Also consult the documentation for 1556 ;; the language server represented by this client to find out what language 1557 ;; identifiers it supports or expects. 1558 (language-id nil) 1559 1560 ;; ‘add-on?’ when set to t the server will be started no matter whether there 1561 ;; is another server handling the same mode. 1562 (add-on? nil) 1563 ;; ‘new-connection’ is a function that should start a language server process 1564 ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS). 1565 ;; COMMAND-PROCESS must be a process object representing the server process 1566 ;; just started. COMMUNICATION-PROCESS must be a process (including pipe and 1567 ;; network processes) that ‘lsp-mode’ uses to communicate with the language 1568 ;; server using the language server protocol. COMMAND-PROCESS and 1569 ;; COMMUNICATION-PROCESS may be the same process; in that case 1570 ;; ‘new-connection’ may also return that process as a single 1571 ;; object. ‘new-connection’ is called with two arguments, FILTER and 1572 ;; SENTINEL. FILTER should be used as process filter for 1573 ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for 1574 ;; COMMAND-PROCESS. 1575 (new-connection nil) 1576 1577 ;; ‘ignore-regexps’ is a list of regexps. When a data packet from the 1578 ;; language server matches any of these regexps, it will be ignored. This is 1579 ;; intended for dealing with language servers that output non-protocol data. 1580 (ignore-regexps nil) 1581 1582 ;; ‘ignore-messages’ is a list of regexps. When a message from the language 1583 ;; server matches any of these regexps, it will be ignored. This is useful 1584 ;; for filtering out unwanted messages; such as servers that send nonstandard 1585 ;; message types, or extraneous log messages. 1586 (ignore-messages nil) 1587 1588 ;; ‘notification-handlers’ is a hash table mapping notification method names 1589 ;; (strings) to functions handling the respective notifications. Upon 1590 ;; receiving a notification, ‘lsp-mode’ will call the associated handler 1591 ;; function passing two arguments, the ‘lsp--workspace’ object and the 1592 ;; deserialized notification parameters. 1593 (notification-handlers (make-hash-table :test 'equal)) 1594 1595 ;; ‘request-handlers’ is a hash table mapping request method names 1596 ;; (strings) to functions handling the respective notifications. Upon 1597 ;; receiving a request, ‘lsp-mode’ will call the associated handler function 1598 ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized 1599 ;; request parameters. 1600 (request-handlers (make-hash-table :test 'equal)) 1601 1602 ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request 1603 ;; identifiers for pending asynchronous requests to functions handling the 1604 ;; respective responses. Upon receiving a response from the language server, 1605 ;; ‘lsp-mode’ will call the associated response handler function with a 1606 ;; single argument, the deserialized response parameters. 1607 (response-handlers (make-hash-table :test 'eql)) 1608 1609 ;; ‘prefix-function’ is called for getting the prefix for completion. 1610 ;; The function takes no parameter and returns a cons (start . end) representing 1611 ;; the start and end bounds of the prefix. If it's not set, the client uses a 1612 ;; default prefix function." 1613 (prefix-function nil) 1614 1615 ;; Contains mapping of scheme to the function that is going to be used to load 1616 ;; the file. 1617 (uri-handlers (make-hash-table :test #'equal)) 1618 1619 ;; ‘action-handlers’ is a hash table mapping action to a handler function. It 1620 ;; can be used in `lsp-execute-code-action' to determine whether the action 1621 ;; current client is interested in executing the action instead of sending it 1622 ;; to the server. 1623 (action-handlers (make-hash-table :test 'equal)) 1624 1625 ;; major modes supported by the client. 1626 major-modes 1627 ;; Function that will be called to decide if this language client 1628 ;; should manage a particular buffer. The function will be passed 1629 ;; the file name and major mode to inform the decision. Setting 1630 ;; `activation-fn' will override `major-modes', if 1631 ;; present. 1632 activation-fn 1633 ;; Break the tie when major-mode is supported by multiple clients. 1634 (priority 0) 1635 ;; Unique identifier for representing the client object. 1636 server-id 1637 ;; defines whether the client supports multi root workspaces. 1638 multi-root 1639 ;; Initialization options or a function that returns initialization options. 1640 initialization-options 1641 ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or 1642 ;; completely replace, the faces used for semantic highlighting on a 1643 ;; client-by-client basis. 1644 ;; 1645 ;; It recognizes four members, all of which are optional: `:types’ and 1646 ;; `:modifiers’, respectively, should be face definition lists akin to 1647 ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be 1648 ;; merged with the default face definition list. 1649 ;; 1650 ;; Alternatively, if the plist members `:discard-default-types’ or 1651 ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers' 1652 ;; face definitions will be replaced entirely by their respective overrides. 1653 ;; 1654 ;; For example, setting `:semantic-tokens-faces-overrides' to 1655 ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from 1656 ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'. 1657 ;; 1658 ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))' 1659 ;; will also remap "macro", but on top of that associate the fictional token type 1660 ;; "not-quite-a-macro" with the face named `some-face'. 1661 ;; 1662 ;; `(:types (("macro" . font-lock-keyword-face)) 1663 ;; :modifiers (("declaration" . lsp-face-semhl-interface)) 1664 ;; :discard-default-types t 1665 ;; :discard-default-modifiers t)' 1666 ;; will discard all default face definitions, hence leaving the client with 1667 ;; only one token type "macro", mapped to `font-lock-keyword-face', and one 1668 ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'. 1669 semantic-tokens-faces-overrides 1670 ;; Provides support for registering LSP Server specific capabilities. 1671 custom-capabilities 1672 ;; Function which returns the folders that are considered to be not projects but library files. 1673 ;; The function accepts one parameter currently active workspace. 1674 ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225. 1675 library-folders-fn 1676 ;; function which will be called when opening file in the workspace to perform 1677 ;; client specific initialization. The function accepts one parameter 1678 ;; currently active workspace. 1679 before-file-open-fn 1680 ;; Function which will be called right after a workspace has been initialized. 1681 initialized-fn 1682 ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP. 1683 (remote? nil) 1684 1685 ;; ‘completion-in-comments?’ t if the client supports completion in comments. 1686 (completion-in-comments? nil) 1687 1688 ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client. 1689 (path->uri-fn nil) 1690 1691 ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client. 1692 (uri->path-fn nil) 1693 ;; Function that returns an environment structure that will be used 1694 ;; to set some environment variables when starting the language 1695 ;; server process. These environment variables enable some 1696 ;; additional features in the language server. The environment 1697 ;; structure is an alist of the form (KEY . VALUE), where KEY is a 1698 ;; string (regularly in all caps), and VALUE may be a string, a 1699 ;; boolean, or a sequence of strings. 1700 environment-fn 1701 1702 ;; ‘after-open-fn’ workspace after open specific hooks. 1703 (after-open-fn nil) 1704 1705 ;; ‘async-request-handlers’ is a hash table mapping request method names 1706 ;; (strings) to functions handling the respective requests that may take 1707 ;; time to finish. Upon receiving a request, ‘lsp-mode’ will call the 1708 ;; associated handler function passing three arguments, the ‘lsp--workspace’ 1709 ;; object, the deserialized request parameters and the callback which accept 1710 ;; result as its parameter. 1711 (async-request-handlers (make-hash-table :test 'equal)) 1712 download-server-fn 1713 download-in-progress? 1714 buffers 1715 synchronize-sections) 1716 1717 (defun lsp-clients-executable-find (find-command &rest args) 1718 "Finds an executable by invoking a search command. 1719 1720 FIND-COMMAND is the executable finder that searches for the 1721 actual language server executable. ARGS is a list of arguments to 1722 give to FIND-COMMAND to find the language server. Returns the 1723 output of FIND-COMMAND if it exits successfully, nil otherwise. 1724 1725 Typical uses include finding an executable by invoking `find' in 1726 a project, finding LLVM commands on macOS with `xcrun', or 1727 looking up project-specific language servers for projects written 1728 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv' 1729 etc." 1730 (when-let* ((find-command-path (executable-find find-command)) 1731 (executable-path 1732 (with-temp-buffer 1733 (when (zerop (apply 'call-process find-command-path nil t nil args)) 1734 (buffer-substring-no-properties (point-min) (point-max)))))) 1735 (string-trim executable-path))) 1736 1737 (defvar lsp--already-widened nil) 1738 1739 (defmacro lsp-save-restriction-and-excursion (&rest form) 1740 (declare (indent 0) (debug t)) 1741 `(if lsp--already-widened 1742 (save-excursion ,@form) 1743 (-let [lsp--already-widened t] 1744 (save-restriction 1745 (widen) 1746 (save-excursion ,@form))))) 1747 1748 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number 1749 (defun lsp--line-character-to-point (line character) 1750 "Return the point for character CHARACTER on line LINE." 1751 (or (lsp-virtual-buffer-call :line/character->point line character) 1752 (let ((inhibit-field-text-motion t)) 1753 (lsp-save-restriction-and-excursion 1754 (goto-char (point-min)) 1755 (forward-line line) 1756 ;; server may send character position beyond the current line and we 1757 ;; should fallback to line end. 1758 (-let [line-end (line-end-position)] 1759 (if (> character (- line-end (point))) 1760 line-end 1761 (forward-char character) 1762 (point))))))) 1763 1764 (lsp-defun lsp--position-to-point ((&Position :line :character)) 1765 "Convert `Position' object in PARAMS to a point." 1766 (lsp--line-character-to-point line character)) 1767 1768 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end)) 1769 (cons start end)) 1770 1771 (lsp-defun lsp--range-text ((&RangeToPoint :start :end)) 1772 (buffer-substring start end)) 1773 1774 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end))) 1775 (cond 1776 ((and 1777 (region-active-p) 1778 (<= start (region-beginning) end) 1779 (<= start (region-end) end) 1780 (or (not (= start (region-beginning))) 1781 (not (= end (region-end))))) 1782 (cons start end)) 1783 ((and (<= start (point) end) 1784 (not (region-active-p))) 1785 (cons start end)) 1786 (parent? (lsp--find-wrapping-range parent?)))) 1787 1788 (defun lsp--get-selection-range () 1789 (or 1790 (-when-let ((cache . cache-tick) lsp--document-selection-range-cache) 1791 (when (= cache-tick (buffer-modified-tick)) cache)) 1792 (let ((response (cl-first 1793 (lsp-request 1794 "textDocument/selectionRange" 1795 (list :textDocument (lsp--text-document-identifier) 1796 :positions (vector (lsp--cur-position))))))) 1797 (setq lsp--document-selection-range-cache 1798 (cons response (buffer-modified-tick))) 1799 response))) 1800 1801 (defun lsp-extend-selection () 1802 "Extend selection." 1803 (interactive) 1804 (unless (lsp-feature? "textDocument/selectionRange") 1805 (signal 'lsp-capability-not-supported (list "selectionRangeProvider"))) 1806 (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range))) 1807 (goto-char start) 1808 (set-mark (point)) 1809 (goto-char end) 1810 (exchange-point-and-mark))) 1811 1812 (defun lsp-warn (message &rest args) 1813 "Display a warning message made from (`format-message' MESSAGE ARGS...). 1814 This is equivalent to `display-warning', using `lsp-mode' as the type and 1815 `:warning' as the level." 1816 (display-warning 'lsp-mode (apply #'format-message message args))) 1817 1818 (defun lsp--get-uri-handler (scheme) 1819 "Get uri handler for SCHEME in the current workspace." 1820 (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it))) 1821 (or (lsp-workspaces) (lsp--session-workspaces (lsp-session))))) 1822 1823 (defun lsp--fix-path-casing (path) 1824 "On windows, downcases path because the windows file system is 1825 case-insensitive. 1826 1827 On other systems, returns path without change." 1828 (if (eq system-type 'windows-nt) (downcase path) path)) 1829 1830 (defun lsp--uri-to-path (uri) 1831 "Convert URI to a file path." 1832 (if-let ((fn (->> (lsp-workspaces) 1833 (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client)) 1834 (cl-first)))) 1835 (funcall fn uri) 1836 (lsp--uri-to-path-1 uri))) 1837 1838 (defun lsp-remap-path-if-needed (file-name) 1839 (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings)) 1840 (propertize (buffer-local-value 'buffer-file-name buffer) 1841 'lsp-virtual-buffer virtual-buffer) 1842 file-name)) 1843 1844 (defun lsp--uri-to-path-1 (uri) 1845 "Convert URI to a file path." 1846 (let* ((url (url-generic-parse-url (url-unhex-string uri))) 1847 (type (url-type url)) 1848 (target (url-target url)) 1849 (file 1850 (concat (decode-coding-string (url-filename url) 1851 (or locale-coding-system 'utf-8)) 1852 (when (and target 1853 (not (s-match 1854 (rx "#" (group (1+ num)) (or "," "#") 1855 (group (1+ num)) 1856 string-end) 1857 uri))) 1858 (concat "#" target)))) 1859 (file-name (if (and type (not (string= type "file"))) 1860 (if-let ((handler (lsp--get-uri-handler type))) 1861 (funcall handler uri) 1862 uri) 1863 ;; `url-generic-parse-url' is buggy on windows: 1864 ;; https://github.com/emacs-lsp/lsp-mode/pull/265 1865 (or (and (eq system-type 'windows-nt) 1866 (eq (elt file 0) ?\/) 1867 (substring file 1)) 1868 file)))) 1869 (->> file-name 1870 (concat (-some #'lsp--workspace-host-root (lsp-workspaces))) 1871 (lsp-remap-path-if-needed)))) 1872 1873 (defun lsp--buffer-uri () 1874 "Return URI of the current buffer." 1875 (or lsp-buffer-uri 1876 (plist-get lsp--virtual-buffer :buffer-uri) 1877 (lsp--path-to-uri 1878 (or (buffer-file-name) (buffer-file-name (buffer-base-buffer)))))) 1879 1880 (defun lsp-register-client-capabilities (&rest _args) 1881 "Implemented only to make `company-lsp' happy. 1882 DELETE when `lsp-mode.el' is deleted.") 1883 1884 (defconst lsp--url-path-allowed-chars 1885 (url--allowed-chars (append '(?/) url-unreserved-chars)) 1886 "`url-unreserved-chars' with additional delim ?/. 1887 This set of allowed chars is enough for hexifying local file paths.") 1888 1889 (defun lsp--path-to-uri-1 (path) 1890 (concat lsp--uri-file-prefix 1891 (--> path 1892 (expand-file-name it) 1893 (or (file-remote-p it 'localname t) it) 1894 (url-hexify-string it lsp--url-path-allowed-chars)))) 1895 1896 (defun lsp--path-to-uri (path) 1897 "Convert PATH to a uri." 1898 (if-let ((uri-fn (->> (lsp-workspaces) 1899 (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client)) 1900 (cl-first)))) 1901 (funcall uri-fn path) 1902 (lsp--path-to-uri-1 path))) 1903 1904 (defun lsp--string-match-any (regex-list str) 1905 "Return the first regex, if any, within REGEX-LIST matching STR." 1906 (--first (string-match it str) regex-list)) 1907 1908 (cl-defstruct lsp-watch 1909 (descriptors (make-hash-table :test 'equal)) 1910 root-directory) 1911 1912 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories) 1913 (let ((file-name (cl-third event)) 1914 (event-type (cl-second event))) 1915 (cond 1916 ((and (file-directory-p file-name) 1917 (equal 'created event-type) 1918 (not (lsp--string-match-any ignored-directories file-name))) 1919 1920 (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch) 1921 1922 ;; process the files that are already present in 1923 ;; the directory. 1924 (->> (directory-files-recursively file-name ".*" t) 1925 (seq-do (lambda (f) 1926 (unless (file-directory-p f) 1927 (funcall callback (list nil 'created f))))))) 1928 ((and (memq event-type '(created deleted changed)) 1929 (not (file-directory-p file-name)) 1930 (not (lsp--string-match-any ignored-files file-name))) 1931 (funcall callback event)) 1932 ((and (memq event-type '(renamed)) 1933 (not (file-directory-p file-name)) 1934 (not (lsp--string-match-any ignored-files file-name))) 1935 (funcall callback `(,(cl-first event) deleted ,(cl-third event))) 1936 (funcall callback `(,(cl-first event) created ,(cl-fourth event))))))) 1937 1938 (defun lsp--ask-about-watching-big-repo (number-of-directories dir) 1939 "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR. 1940 This is useful when there is a lot of files in a repository, as 1941 that may slow Emacs down. Returns t if the user wants to watch 1942 the entire repository, nil otherwise." 1943 (prog1 1944 (yes-or-no-p 1945 (format 1946 "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down. 1947 Do you want to watch all files in %s? " 1948 dir 1949 number-of-directories 1950 dir)) 1951 (lsp--info 1952 (concat "You can configure this warning with the `lsp-enable-file-watchers' " 1953 "and `lsp-file-watch-threshold' variables")))) 1954 1955 1956 (defun lsp--path-is-watchable-directory (path dir ignored-directories) 1957 "Figure out whether PATH (inside of DIR) is meant to have a file watcher set. 1958 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't 1959 want to watch." 1960 (let 1961 ((full-path (f-join dir path))) 1962 (and (file-accessible-directory-p full-path) 1963 (not (equal path ".")) 1964 (not (equal path "..")) 1965 (not (lsp--string-match-any ignored-directories full-path))))) 1966 1967 1968 (defun lsp--all-watchable-directories (dir ignored-directories) 1969 "Traverse DIR recursively returning a list of paths that should have watchers. 1970 IGNORED-DIRECTORIES will be used for exclusions" 1971 (let* ((dir (if (f-symlink? dir) 1972 (file-truename dir) 1973 dir))) 1974 (apply #'nconc 1975 ;; the directory itself is assumed to be part of the set 1976 (list dir) 1977 ;; collect all subdirectories that are watchable 1978 (-map 1979 (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories)) 1980 ;; but only look at subdirectories that are watchable 1981 (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories)) 1982 (directory-files dir)))))) 1983 1984 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?) 1985 "Create recursive file notification watch in DIR. 1986 CALLBACK will be called when there are changes in any of 1987 the monitored files. WATCHES is a hash table directory->file 1988 notification handle which contains all of the watch that 1989 already have been created. Watches will not be created for 1990 any directory that matches any regex in IGNORED-DIRECTORIES. 1991 Watches will not be created for any file that matches any 1992 regex in IGNORED-FILES." 1993 (let* ((dir (if (f-symlink? dir) 1994 (file-truename dir) 1995 dir)) 1996 (watch (or watch (make-lsp-watch :root-directory dir))) 1997 (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories))) 1998 (lsp-log "Creating watchers for following %s folders:\n %s" 1999 (length dirs-to-watch) 2000 (s-join "\n " dirs-to-watch)) 2001 (when (or 2002 (not warn-big-repo?) 2003 (not lsp-file-watch-threshold) 2004 (let ((number-of-directories (length dirs-to-watch))) 2005 (or 2006 (< number-of-directories lsp-file-watch-threshold) 2007 (condition-case nil 2008 (lsp--ask-about-watching-big-repo number-of-directories dir) 2009 (quit))))) 2010 (dolist (current-dir dirs-to-watch) 2011 (condition-case err 2012 (progn 2013 (puthash 2014 current-dir 2015 (file-notify-add-watch current-dir 2016 '(change) 2017 (lambda (event) 2018 (lsp--folder-watch-callback event callback watch ignored-files ignored-directories))) 2019 (lsp-watch-descriptors watch))) 2020 (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err))) 2021 (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))))) 2022 watch)) 2023 2024 (defun lsp-kill-watch (watch) 2025 "Delete WATCH." 2026 (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch)) 2027 (ht-clear! (lsp-watch-descriptors watch))) 2028 2029 (defun lsp-json-bool (val) 2030 "Convert VAL to JSON boolean." 2031 (if val t :json-false)) 2032 2033 (defmacro with-lsp-workspace (workspace &rest body) 2034 "Helper macro for invoking BODY in WORKSPACE context." 2035 (declare (debug (form body)) 2036 (indent 1)) 2037 `(let ((lsp--cur-workspace ,workspace)) ,@body)) 2038 2039 (defmacro with-lsp-workspaces (workspaces &rest body) 2040 "Helper macro for invoking BODY against multiple WORKSPACES." 2041 (declare (debug (form body)) 2042 (indent 1)) 2043 `(let ((lsp--buffer-workspaces ,workspaces)) ,@body)) 2044 2045 2046 2047 (defmacro lsp-consistency-check (package) 2048 `(defconst ,(intern (concat (symbol-name package) 2049 "-plist-value-when-compiled")) 2050 (eval-when-compile lsp-use-plists))) 2051 2052 2053 ;; loading code-workspace files 2054 2055 ;;;###autoload 2056 (defun lsp-load-vscode-workspace (file) 2057 "Load vscode workspace from FILE" 2058 (interactive "fSelect file to import: ") 2059 (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session))) 2060 2061 (let ((dir (f-dirname file))) 2062 (->> file 2063 (json-read-file) 2064 (alist-get 'folders) 2065 (-map (-lambda ((&alist 'path)) 2066 (lsp-workspace-folders-add (expand-file-name path dir))))))) 2067 2068 ;;;###autoload 2069 (defun lsp-save-vscode-workspace (file) 2070 "Save vscode workspace to FILE" 2071 (interactive "FSelect file to save to: ") 2072 2073 (let ((json-encoding-pretty-print t)) 2074 (f-write-text (json-encode 2075 `((folders . ,(->> (lsp-session) 2076 (lsp-session-folders) 2077 (--map `((path . ,it))))))) 2078 'utf-8 2079 file))) 2080 2081 2082 (defmacro lsp-foreach-workspace (&rest body) 2083 "Execute BODY for each of the current workspaces." 2084 (declare (debug (form body))) 2085 `(--map (with-lsp-workspace it ,@body) (lsp-workspaces))) 2086 2087 (defmacro when-lsp-workspace (workspace &rest body) 2088 "Helper macro for invoking BODY in WORKSPACE context if present." 2089 (declare (debug (form body)) 2090 (indent 1)) 2091 `(when-let ((lsp--cur-workspace ,workspace)) ,@body)) 2092 2093 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items)) 2094 (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read)) 2095 (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label)) 2096 items)) 2097 (result (funcall-interactively 2098 selectfunc 2099 (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels)) 2100 (choices (if (listp result) 2101 (if (equal result '("*")) 2102 itemLabels 2103 result) 2104 (list result)))) 2105 (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data)) 2106 (if (member label choices) 2107 (lsp-make-quick-pick-item :label label :picked t :user-data user-data) 2108 nil)) 2109 items))))) 2110 2111 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?)) 2112 (read-string (format "%s: " prompt) (or value? ""))) 2113 2114 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type)) 2115 "Send the server's messages to log. 2116 PARAMS - the data sent from _WORKSPACE." 2117 (funcall (cl-case type 2118 (1 'lsp--error) 2119 (2 'lsp--warn) 2120 (t 'lsp--info)) 2121 "%s" 2122 message)) 2123 2124 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type)) 2125 "Send the server's messages to log. 2126 PARAMS - the data sent from WORKSPACE." 2127 (ignore 2128 (let ((client (lsp--workspace-client workspace))) 2129 (when (or (not client) 2130 (cl-notany (-rpartial #'string-match-p message) 2131 (lsp--client-ignore-messages client))) 2132 (lsp-log "%s" (lsp--propertize message type)))))) 2133 2134 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?)) 2135 "Display a message request to user sending the user selection back to server." 2136 (let* ((message (lsp--propertize message type)) 2137 (choices (seq-map #'lsp:message-action-item-title actions?))) 2138 (if choices 2139 (completing-read (concat message " ") (seq-into choices 'list) nil t) 2140 (lsp-log message)))) 2141 2142 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?)) 2143 "Show document URI in a buffer and go to SELECTION if any." 2144 (let ((path (lsp--uri-to-path uri))) 2145 (when (f-exists? path) 2146 (with-current-buffer (find-file path) 2147 (when selection? 2148 (goto-char (lsp--position-to-point (lsp:range-start selection?)))) 2149 t)))) 2150 2151 (defcustom lsp-progress-prefix " ⌛ " 2152 "Progress prefix." 2153 :group 'lsp-mode 2154 :type 'string 2155 :package-version '(lsp-mode . "8.0.0")) 2156 2157 (defcustom lsp-progress-function #'lsp-on-progress-modeline 2158 "Function for handling the progress notifications." 2159 :group 'lsp-mode 2160 :type '(choice 2161 (const :tag "Use modeline" lsp-on-progress-modeline) 2162 (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')" 2163 lsp-on-progress-legacy) 2164 (const :tag "Ignore" ignore) 2165 (function :tag "Other function")) 2166 :package-version '(lsp-mode . "8.0.0")) 2167 2168 (defcustom lsp-request-while-no-input-may-block nil 2169 "Have `lsp-request-while-no-input` block unless `non-essential` is t." 2170 :group 'lsp-mode 2171 :type 'boolean) 2172 2173 (defun lsp--progress-status () 2174 "Returns the status of the progress for the current workspaces." 2175 (-let ((progress-status 2176 (s-join 2177 "|" 2178 (-keep 2179 (lambda (workspace) 2180 (let ((tokens (lsp--workspace-work-done-tokens workspace))) 2181 (unless (ht-empty? tokens) 2182 (mapconcat 2183 (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?)) 2184 (concat (if percentage? 2185 (if (numberp percentage?) 2186 (format "%.0f%%%% " percentage?) 2187 (format "%s%%%% " percentage?)) 2188 "") 2189 (or message? title))) 2190 (ht-values tokens) 2191 "|")))) 2192 (lsp-workspaces))))) 2193 (unless (s-blank? progress-status) 2194 (concat lsp-progress-prefix progress-status)))) 2195 2196 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value 2197 (value &as &WorkDoneProgress :kind))) 2198 "PARAMS contains the progress data. 2199 WORKSPACE is the workspace that contains the progress token." 2200 (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status)))) 2201 (pcase kind 2202 ("begin" (lsp-workspace-set-work-done-token token value workspace)) 2203 ("report" (lsp-workspace-set-work-done-token token value workspace)) 2204 ("end" (lsp-workspace-rem-work-done-token token workspace))) 2205 (force-mode-line-update)) 2206 2207 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value 2208 (value &as &WorkDoneProgress :kind))) 2209 "PARAMS contains the progress data. 2210 WORKSPACE is the workspace that contains the progress token." 2211 (pcase kind 2212 ("begin" 2213 (-let* (((&WorkDoneProgressBegin :title :percentage?) value) 2214 (reporter 2215 (if lsp-progress-via-spinner 2216 (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types)) 2217 ;; Set message as a tooltip for the spinner strings 2218 (propertized-strings 2219 (seq-map (lambda (string) (propertize string 'help-echo title)) 2220 spinner-strings)) 2221 (spinner-type (vconcat propertized-strings))) 2222 ;; The progress relates to the server as a whole, 2223 ;; display it on all buffers. 2224 (mapcar (lambda (buffer) 2225 (lsp-with-current-buffer buffer 2226 (spinner-start spinner-type)) 2227 buffer) 2228 (lsp--workspace-buffers workspace))) 2229 (if percentage? 2230 (make-progress-reporter title 0 100 percentage?) 2231 ;; No percentage, just progress 2232 (make-progress-reporter title nil nil))))) 2233 (lsp-workspace-set-work-done-token token reporter workspace))) 2234 ("report" 2235 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2236 (unless lsp-progress-via-spinner 2237 (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value))))) 2238 2239 ("end" 2240 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2241 (if lsp-progress-via-spinner 2242 (mapc (lambda (buffer) 2243 (when (lsp-buffer-live-p buffer) 2244 (lsp-with-current-buffer buffer 2245 (spinner-stop)))) 2246 reporter) 2247 (progress-reporter-done reporter)) 2248 (lsp-workspace-rem-work-done-token token workspace))))) 2249 2250 2251 ;; diagnostics 2252 2253 (defvar lsp-diagnostic-filter nil 2254 "A a function which will be called with 2255 `&PublishDiagnosticsParams' and `workspace' which can be used 2256 to filter out the diagnostics. The function should return 2257 `&PublishDiagnosticsParams'. 2258 2259 Common usecase are: 2260 1. Filter the diagnostics for a particular language server. 2261 2. Filter out the diagnostics under specific level.") 2262 2263 (defvar lsp-diagnostic-stats (ht)) 2264 2265 (defun lsp-diagnostics (&optional current-workspace?) 2266 "Return the diagnostics from all workspaces." 2267 (or (pcase (if current-workspace? 2268 (lsp-workspaces) 2269 (lsp--session-workspaces (lsp-session))) 2270 (`() ()) 2271 (`(,workspace) (lsp--workspace-diagnostics workspace)) 2272 (`,workspaces (let ((result (make-hash-table :test 'equal))) 2273 (mapc (lambda (workspace) 2274 (->> workspace 2275 (lsp--workspace-diagnostics) 2276 (maphash (lambda (file-name diagnostics) 2277 (puthash file-name 2278 (append (gethash file-name result) diagnostics) 2279 result))))) 2280 workspaces) 2281 result))) 2282 (ht))) 2283 2284 (defun lsp-diagnostics-stats-for (path) 2285 "Get diagnostics statistics for PATH. 2286 The result format is vector [_ errors warnings infos hints] or nil." 2287 (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats)) 2288 2289 (defun lsp-diagnostics--update-path (path new-stats) 2290 (let ((new-stats (copy-sequence new-stats)) 2291 (path (lsp--fix-path-casing (directory-file-name path)))) 2292 (if-let ((old-data (gethash path lsp-diagnostic-stats))) 2293 (dotimes (idx 5) 2294 (cl-callf + (aref old-data idx) 2295 (aref new-stats idx))) 2296 (puthash path new-stats lsp-diagnostic-stats)))) 2297 2298 (lsp-defun lsp--on-diagnostics-update-stats (workspace 2299 (&PublishDiagnosticsParams :uri :diagnostics)) 2300 (let ((path (lsp--fix-path-casing (lsp--uri-to-path uri))) 2301 (new-stats (make-vector 5 0))) 2302 (mapc (-lambda ((&Diagnostic :severity?)) 2303 (cl-incf (aref new-stats (or severity? 1)))) 2304 diagnostics) 2305 (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace)))) 2306 (mapc (-lambda ((&Diagnostic :severity?)) 2307 (cl-decf (aref new-stats (or severity? 1)))) 2308 old-diags)) 2309 (lsp-diagnostics--update-path path new-stats) 2310 (while (not (string= path (setf path (file-name-directory 2311 (directory-file-name path))))) 2312 (lsp-diagnostics--update-path path new-stats)))) 2313 2314 (defun lsp--on-diagnostics (workspace params) 2315 "Callback for textDocument/publishDiagnostics. 2316 interface PublishDiagnosticsParams { 2317 uri: string; 2318 diagnostics: Diagnostic[]; 2319 } 2320 PARAMS contains the diagnostics data. 2321 WORKSPACE is the workspace that contains the diagnostics." 2322 (when lsp-diagnostic-filter 2323 (setf params (funcall lsp-diagnostic-filter params workspace))) 2324 2325 (lsp--on-diagnostics-update-stats workspace params) 2326 2327 (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params) 2328 (lsp--virtual-buffer-mappings (ht)) 2329 (file (lsp--fix-path-casing (lsp--uri-to-path uri))) 2330 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2331 2332 (if (seq-empty-p diagnostics) 2333 (remhash file workspace-diagnostics) 2334 (puthash file (append diagnostics nil) workspace-diagnostics)) 2335 2336 (run-hooks 'lsp-diagnostics-updated-hook))) 2337 2338 (defun lsp-diagnostics--workspace-cleanup (workspace) 2339 (->> workspace 2340 (lsp--workspace-diagnostics) 2341 (maphash (lambda (key _) 2342 (lsp--on-diagnostics-update-stats 2343 workspace 2344 (lsp-make-publish-diagnostics-params 2345 :uri (lsp--path-to-uri key) 2346 :diagnostics []))))) 2347 (clrhash (lsp--workspace-diagnostics workspace))) 2348 2349 2350 2351 ;; textDocument/foldingRange support 2352 2353 (cl-defstruct lsp--folding-range beg end kind children) 2354 2355 (defvar-local lsp--cached-folding-ranges nil) 2356 (defvar-local lsp--cached-nested-folding-ranges nil) 2357 2358 (defun lsp--folding-range-width (range) 2359 (- (lsp--folding-range-end range) 2360 (lsp--folding-range-beg range))) 2361 2362 (defun lsp--get-folding-ranges () 2363 "Get the folding ranges for the current buffer." 2364 (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges)) 2365 (let* ((ranges (lsp-request "textDocument/foldingRange" 2366 `(:textDocument ,(lsp--text-document-identifier)))) 2367 (sorted-line-col-pairs (->> ranges 2368 (cl-mapcan (-lambda ((&FoldingRange :start-line 2369 :start-character? 2370 :end-line 2371 :end-character?)) 2372 (list (cons start-line start-character?) 2373 (cons end-line end-character?)))) 2374 (-sort #'lsp--line-col-comparator))) 2375 (line-col-to-point-map (lsp--convert-line-col-to-points-batch 2376 sorted-line-col-pairs))) 2377 (setq lsp--cached-folding-ranges 2378 (cons (buffer-chars-modified-tick) 2379 (--> ranges 2380 (seq-map (-lambda ((range &as 2381 &FoldingRange :start-line 2382 :start-character? 2383 :end-line 2384 :end-character? 2385 :kind?)) 2386 (make-lsp--folding-range 2387 :beg (ht-get line-col-to-point-map 2388 (cons start-line start-character?)) 2389 :end (ht-get line-col-to-point-map 2390 (cons end-line end-character?)) 2391 :kind kind?)) 2392 it) 2393 (seq-filter (lambda (folding-range) 2394 (< (lsp--folding-range-beg folding-range) 2395 (lsp--folding-range-end folding-range))) 2396 it) 2397 (seq-into it 'list) 2398 (delete-dups it)))))) 2399 (cdr lsp--cached-folding-ranges)) 2400 2401 (defun lsp--get-nested-folding-ranges () 2402 "Get a list of nested folding ranges for the current buffer." 2403 (-let [(tick . _) lsp--cached-folding-ranges] 2404 (if (and (eq tick (buffer-chars-modified-tick)) 2405 lsp--cached-nested-folding-ranges) 2406 lsp--cached-nested-folding-ranges 2407 (setq lsp--cached-nested-folding-ranges 2408 (lsp--folding-range-build-trees (lsp--get-folding-ranges)))))) 2409 2410 (defun lsp--folding-range-build-trees (ranges) 2411 (setq ranges (seq-sort #'lsp--range-before-p ranges)) 2412 (let* ((dummy-node (make-lsp--folding-range 2413 :beg most-negative-fixnum 2414 :end most-positive-fixnum)) 2415 (stack (list dummy-node))) 2416 (dolist (range ranges) 2417 (while (not (lsp--range-inside-p range (car stack))) 2418 (pop stack)) 2419 (push range (lsp--folding-range-children (car stack))) 2420 (push range stack)) 2421 (lsp--folding-range-children dummy-node))) 2422 2423 (defun lsp--range-inside-p (r1 r2) 2424 "Return non-nil if folding range R1 lies inside R2" 2425 (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2)) 2426 (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2)))) 2427 2428 (defun lsp--range-before-p (r1 r2) 2429 "Return non-nil if folding range R1 ends before R2" 2430 ;; Ensure r1 comes before r2 2431 (or (< (lsp--folding-range-beg r1) 2432 (lsp--folding-range-beg r2)) 2433 ;; If beg(r1) == beg(r2) make sure r2 ends first 2434 (and (= (lsp--folding-range-beg r1) 2435 (lsp--folding-range-beg r2)) 2436 (< (lsp--folding-range-end r2) 2437 (lsp--folding-range-end r1))))) 2438 2439 (defun lsp--point-inside-range-p (point range) 2440 "Return non-nil if POINT lies inside folding range RANGE." 2441 (and (>= point (lsp--folding-range-beg range)) 2442 (<= point (lsp--folding-range-end range)))) 2443 2444 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point))) 2445 "Return the innermost folding range POINT lies in." 2446 (seq-reduce (lambda (innermost-range curr-range) 2447 (if (and (lsp--point-inside-range-p point curr-range) 2448 (or (null innermost-range) 2449 (lsp--range-inside-p curr-range innermost-range))) 2450 curr-range 2451 innermost-range)) 2452 (lsp--get-folding-ranges) 2453 nil)) 2454 2455 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point))) 2456 "Return the outermost folding range POINT lies in." 2457 (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range) 2458 (let ((curr-width (lsp--folding-range-width curr-range))) 2459 (if (and (lsp--point-inside-range-p point curr-range) 2460 (or (null best-pair) 2461 (> curr-width outermost-width))) 2462 (cons curr-width curr-range) 2463 best-pair))) 2464 (lsp--get-folding-ranges) 2465 nil))) 2466 2467 (defun lsp--folding-range-at-point-bounds () 2468 (when (and lsp-enable-folding 2469 (lsp-feature? "textDocument/foldingRange")) 2470 (if-let ((range (lsp--get-current-innermost-folding-range))) 2471 (cons (lsp--folding-range-beg range) 2472 (lsp--folding-range-end range))))) 2473 (put 'lsp--folding-range 'bounds-of-thing-at-point 2474 #'lsp--folding-range-at-point-bounds) 2475 2476 (defun lsp--get-nearest-folding-range (&optional backward) 2477 (let ((point (point)) 2478 (found nil)) 2479 (while (not 2480 (or found 2481 (if backward 2482 (<= point (point-min)) 2483 (>= point (point-max))))) 2484 (if backward (cl-decf point) (cl-incf point)) 2485 (setq found (lsp--get-current-innermost-folding-range point))) 2486 found)) 2487 2488 (defun lsp--folding-range-at-point-forward-op (n) 2489 (when (and lsp-enable-folding 2490 (not (zerop n)) 2491 (lsp-feature? "textDocument/foldingRange")) 2492 (cl-block break 2493 (dotimes (_ (abs n)) 2494 (if-let ((range (lsp--get-nearest-folding-range (< n 0)))) 2495 (goto-char (if (< n 0) 2496 (lsp--folding-range-beg range) 2497 (lsp--folding-range-end range))) 2498 (cl-return-from break)))))) 2499 (put 'lsp--folding-range 'forward-op 2500 #'lsp--folding-range-at-point-forward-op) 2501 2502 (defun lsp--folding-range-at-point-beginning-op () 2503 (goto-char (car (lsp--folding-range-at-point-bounds)))) 2504 (put 'lsp--folding-range 'beginning-op 2505 #'lsp--folding-range-at-point-beginning-op) 2506 2507 (defun lsp--folding-range-at-point-end-op () 2508 (goto-char (cdr (lsp--folding-range-at-point-bounds)))) 2509 (put 'lsp--folding-range 'end-op 2510 #'lsp--folding-range-at-point-end-op) 2511 2512 (defun lsp--range-at-point-bounds () 2513 (or (lsp--folding-range-at-point-bounds) 2514 (when-let ((range (and 2515 (lsp-feature? "textDocument/hover") 2516 (->> (lsp--text-document-position-params) 2517 (lsp-request "textDocument/hover") 2518 (lsp:hover-range?))))) 2519 (lsp--range-to-region range)))) 2520 2521 ;; A more general purpose "thing", useful for applications like focus.el 2522 (put 'lsp--range 'bounds-of-thing-at-point 2523 #'lsp--range-at-point-bounds) 2524 2525 (defun lsp--log-io-p (method) 2526 "Return non nil if should log for METHOD." 2527 (and lsp-log-io 2528 (or (not lsp-log-io-allowlist-methods) 2529 (member method lsp-log-io-allowlist-methods)))) 2530 2531 2532 ;; toggles 2533 2534 (defun lsp-toggle-trace-io () 2535 "Toggle client-server protocol logging." 2536 (interactive) 2537 (setq lsp-log-io (not lsp-log-io)) 2538 (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled"))) 2539 2540 (defun lsp-toggle-signature-auto-activate () 2541 "Toggle signature auto activate." 2542 (interactive) 2543 (setq lsp-signature-auto-activate 2544 (unless lsp-signature-auto-activate '(:on-trigger-char))) 2545 (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled")) 2546 (lsp--update-signature-help-hook)) 2547 2548 (defun lsp-toggle-on-type-formatting () 2549 "Toggle on type formatting." 2550 (interactive) 2551 (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting)) 2552 (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled")) 2553 (lsp--update-on-type-formatting-hook)) 2554 2555 (defun lsp-toggle-symbol-highlight () 2556 "Toggle symbol highlighting." 2557 (interactive) 2558 (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting)) 2559 2560 (cond 2561 ((and lsp-enable-symbol-highlighting 2562 (lsp-feature? "textDocument/documentHighlight")) 2563 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t) 2564 (lsp--info "Symbol highlighting enabled in current buffer.")) 2565 ((not lsp-enable-symbol-highlighting) 2566 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 2567 (lsp--remove-overlays 'lsp-highlight) 2568 (lsp--info "Symbol highlighting disabled in current buffer.")))) 2569 2570 2571 ;; keybindings 2572 (defvar lsp--binding-descriptions nil 2573 "List of key binding/short description pair.") 2574 2575 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings) 2576 "In KEYMAP, define key sequence KEY as DEF conditionally. 2577 This is like `define-key', except the definition disappears 2578 whenever COND evaluates to nil. 2579 DESC is the short-description for the binding. 2580 BINDINGS is a list of (key def desc cond)." 2581 (declare (indent defun) 2582 (debug (form form form form form &rest sexp))) 2583 (->> (cl-list* key def desc cond bindings) 2584 (-partition 4) 2585 (-mapcat (-lambda ((key def desc cond)) 2586 `((define-key ,keymap ,key 2587 '(menu-item 2588 ,(format "maybe-%s" def) 2589 ,def 2590 :filter 2591 (lambda (item) 2592 (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer) 2593 lsp--describe-buffer) 2594 (current-buffer)) 2595 ,cond) 2596 item)))) 2597 (when (stringp ,key) 2598 (setq lsp--binding-descriptions 2599 (append lsp--binding-descriptions '(,key ,desc))))))) 2600 macroexp-progn)) 2601 2602 (defvar lsp--describe-buffer nil) 2603 2604 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus) 2605 (let ((lsp--describe-buffer buffer)) 2606 (funcall fn buffer prefix menus))) 2607 2608 (advice-add 'describe-buffer-bindings 2609 :around 2610 #'lsp-describe-buffer-bindings-advice) 2611 2612 (defun lsp--prepend-prefix (mappings) 2613 (->> mappings 2614 (-partition 2) 2615 (-mapcat (-lambda ((key description)) 2616 (list (concat lsp-keymap-prefix " " key) 2617 description))))) 2618 2619 (defvar lsp-command-map 2620 (-doto (make-sparse-keymap) 2621 (lsp-define-conditional-key 2622 ;; workspaces 2623 "wD" lsp-disconnect "disconnect" (lsp-workspaces) 2624 "wd" lsp-describe-session "describe session" t 2625 "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces) 2626 "wr" lsp-workspace-restart "restart server" (lsp-workspaces) 2627 "ws" lsp "start server" t 2628 2629 ;; formatting 2630 "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting") 2631 (lsp-feature? "textDocument/formatting")) 2632 "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting") 2633 2634 ;; folders 2635 "Fa" lsp-workspace-folders-add "add folder" t 2636 "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t 2637 "Fr" lsp-workspace-folders-remove "remove folder" t 2638 2639 ;; toggles 2640 "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature? 2641 "textDocument/publishDiagnostics") 2642 "TL" lsp-toggle-trace-io "toggle log io" t 2643 "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline) 2644 "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs) 2645 "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature? 2646 "textDocument/codeAction") 2647 "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature? 2648 "textDocument/documentSymbol") 2649 "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc) 2650 "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature? 2651 "textDocument/onTypeFormatting") 2652 "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight") 2653 "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens") 2654 "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp") 2655 2656 ;; goto 2657 "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol") 2658 "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration") 2659 "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list) 2660 "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition") 2661 "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls") 2662 (fboundp 'lsp-treemacs-call-hierarchy)) 2663 "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation") 2664 "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references") 2665 "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition") 2666 2667 ;; help 2668 "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc) 2669 (lsp-feature? "textDocument/hover")) 2670 "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover") 2671 "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp") 2672 2673 ;; refactoring 2674 "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction") 2675 "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename") 2676 2677 ;; actions 2678 "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction") 2679 "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight") 2680 "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy)) 2681 2682 ;; peeks 2683 "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition") 2684 (fboundp 'lsp-ui-peek-find-definitions)) 2685 "Gi" lsp-ui-peek-find-implementation "peek implementations" (and 2686 (fboundp 'lsp-ui-peek-find-implementation) 2687 (lsp-feature? "textDocument/implementation")) 2688 "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references) 2689 (lsp-feature? "textDocument/references")) 2690 "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp 2691 'lsp-ui-peek-find-workspace-symbol) 2692 (lsp-feature? "workspace/symbol"))))) 2693 2694 2695 ;; which-key integration 2696 2697 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key") 2698 (declare-function which-key-add-key-based-replacements "ext:which-key") 2699 2700 (defun lsp-enable-which-key-integration (&optional all-modes) 2701 "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current 2702 active `major-mode', or for all major modes when ALL-MODES is t." 2703 (cl-flet ((which-key-fn (if all-modes 2704 'which-key-add-key-based-replacements 2705 (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode)))) 2706 (apply 2707 #'which-key-fn 2708 (lsp--prepend-prefix 2709 (cl-list* 2710 "" "lsp" 2711 "w" "workspaces" 2712 "F" "folders" 2713 "=" "formatting" 2714 "T" "toggle" 2715 "g" "goto" 2716 "h" "help" 2717 "r" "refactor" 2718 "a" "code actions" 2719 "G" "peek" 2720 lsp--binding-descriptions))))) 2721 2722 2723 ;; Globbing syntax 2724 2725 ;; We port VSCode's glob-to-regexp code 2726 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts) 2727 ;; since the LSP globbing syntax seems to be the same as that of 2728 ;; VSCode. 2729 2730 (defconst lsp-globstar "**" 2731 "Globstar pattern.") 2732 2733 (defconst lsp-glob-split ?/ 2734 "The character by which we split path components in a glob 2735 pattern.") 2736 2737 (defconst lsp-path-regexp "[/\\\\]" 2738 "Forward or backslash to be used as a path separator in 2739 computed regexps.") 2740 2741 (defconst lsp-non-path-regexp "[^/\\\\]" 2742 "A regexp matching anything other than a slash.") 2743 2744 (defconst lsp-globstar-regexp 2745 (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?" 2746 lsp-path-regexp 2747 lsp-non-path-regexp lsp-path-regexp 2748 lsp-path-regexp lsp-non-path-regexp) 2749 "Globstar in regexp form.") 2750 2751 (defun lsp-split-glob-pattern (pattern split-char) 2752 "Split PATTERN at SPLIT-CHAR while respecting braces and brackets." 2753 (when pattern 2754 (let ((segments nil) 2755 (in-braces nil) 2756 (in-brackets nil) 2757 (current-segment "")) 2758 (dolist (char (string-to-list pattern)) 2759 (cl-block 'exit-point 2760 (if (eq char split-char) 2761 (when (and (null in-braces) 2762 (null in-brackets)) 2763 (push current-segment segments) 2764 (setq current-segment "") 2765 (cl-return-from 'exit-point)) 2766 (pcase char 2767 (?{ 2768 (setq in-braces t)) 2769 (?} 2770 (setq in-braces nil)) 2771 (?\[ 2772 (setq in-brackets t)) 2773 (?\] 2774 (setq in-brackets nil)))) 2775 (setq current-segment (concat current-segment 2776 (char-to-string char))))) 2777 (unless (string-empty-p current-segment) 2778 (push current-segment segments)) 2779 (nreverse segments)))) 2780 2781 (defun lsp--glob-to-regexp (pattern) 2782 "Helper function to convert a PATTERN from LSP's glob syntax to 2783 an Elisp regexp." 2784 (if (string-empty-p pattern) 2785 "" 2786 (let ((current-regexp "") 2787 (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split))) 2788 (if (-all? (lambda (segment) (eq segment lsp-globstar)) 2789 glob-segments) 2790 ".*" 2791 (let ((prev-segment-was-globstar nil)) 2792 (seq-do-indexed 2793 (lambda (segment index) 2794 (if (string-equal segment lsp-globstar) 2795 (unless prev-segment-was-globstar 2796 (setq current-regexp (concat current-regexp 2797 lsp-globstar-regexp)) 2798 (setq prev-segment-was-globstar t)) 2799 (let ((in-braces nil) 2800 (brace-val "") 2801 (in-brackets nil) 2802 (bracket-val "")) 2803 (dolist (char (string-to-list segment)) 2804 (cond 2805 ((and (not (char-equal char ?\})) 2806 in-braces) 2807 (setq brace-val (concat brace-val 2808 (char-to-string char)))) 2809 ((and in-brackets 2810 (or (not (char-equal char ?\])) 2811 (string-empty-p bracket-val))) 2812 (let ((curr (cond 2813 ((char-equal char ?-) 2814 "-") 2815 ;; NOTE: ?\^ and ?^ are different characters 2816 ((and (memq char '(?^ ?!)) 2817 (string-empty-p bracket-val)) 2818 "^") 2819 ((char-equal char lsp-glob-split) 2820 "") 2821 (t 2822 (regexp-quote (char-to-string char)))))) 2823 (setq bracket-val (concat bracket-val curr)))) 2824 (t 2825 (cl-case char 2826 (?{ 2827 (setq in-braces t)) 2828 (?\[ 2829 (setq in-brackets t)) 2830 (?} 2831 (let* ((choices (lsp-split-glob-pattern brace-val ?\,)) 2832 (brace-regexp (concat "\\(?:" 2833 (mapconcat #'lsp--glob-to-regexp choices "\\|") 2834 "\\)"))) 2835 (setq current-regexp (concat current-regexp 2836 brace-regexp)) 2837 (setq in-braces nil) 2838 (setq brace-val ""))) 2839 (?\] 2840 (setq current-regexp 2841 (concat current-regexp 2842 "[" bracket-val "]")) 2843 (setq in-brackets nil) 2844 (setq bracket-val "")) 2845 (?? 2846 (setq current-regexp 2847 (concat current-regexp 2848 lsp-non-path-regexp))) 2849 (?* 2850 (setq current-regexp 2851 (concat current-regexp 2852 lsp-non-path-regexp "*?"))) 2853 (t 2854 (setq current-regexp 2855 (concat current-regexp 2856 (regexp-quote (char-to-string char))))))))) 2857 (when (and (< index (1- (length glob-segments))) 2858 (or (not (string-equal (nth (1+ index) glob-segments) 2859 lsp-globstar)) 2860 (< (+ index 2) 2861 (length glob-segments)))) 2862 (setq current-regexp 2863 (concat current-regexp 2864 lsp-path-regexp))) 2865 (setq prev-segment-was-globstar nil)))) 2866 glob-segments) 2867 current-regexp))))) 2868 2869 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365 2870 (defun lsp-glob-unbrace-at-top-level (glob-pattern) 2871 "If GLOB-PATTERN does not start with a brace, return a singleton list 2872 containing GLOB-PATTERN. 2873 2874 If GLOB-PATTERN does start with a brace, return a list of the 2875 comma-separated globs within the top-level braces." 2876 (if (not (string-prefix-p "{" glob-pattern)) 2877 (list glob-pattern) 2878 (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,))) 2879 2880 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern) 2881 "Convert GLOB-PATTERN to a regexp wrapped with the beginning- 2882 and end-of-string meta-characters." 2883 (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'")) 2884 2885 (defun lsp-glob-to-regexps (glob-pattern) 2886 "Convert a GLOB-PATTERN to a list of Elisp regexps." 2887 (when-let* 2888 ((glob-pattern (cond ((hash-table-p glob-pattern) 2889 (ht-get glob-pattern "pattern")) 2890 ((stringp glob-pattern) glob-pattern) 2891 (t (error "Unknown glob-pattern type: %s" glob-pattern)))) 2892 (trimmed-pattern (string-trim glob-pattern)) 2893 (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern))) 2894 (seq-map #'lsp-glob-convert-to-wrapped-regexp 2895 top-level-unbraced-patterns))) 2896 2897 2898 2899 (defvar lsp-mode-menu) 2900 2901 (defun lsp-mouse-click (event) 2902 (interactive "e") 2903 (let* ((ec (event-start event)) 2904 (choice (x-popup-menu event lsp-mode-menu)) 2905 (action (lookup-key lsp-mode-menu (apply 'vector choice)))) 2906 2907 (select-window (posn-window ec)) 2908 2909 (unless (and (region-active-p) (eq action 'lsp-execute-code-action)) 2910 (goto-char (posn-point ec))) 2911 (run-with-idle-timer 2912 0.001 nil 2913 (lambda () 2914 (cl-labels ((check (value) (not (null value)))) 2915 (when choice 2916 (call-interactively action))))))) 2917 2918 (defvar lsp-mode-map 2919 (let ((map (make-sparse-keymap))) 2920 (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse) 2921 (define-key map (kbd "C-<mouse-1>") #'ignore) 2922 (define-key map (kbd "<mouse-3>") #'lsp-mouse-click) 2923 (define-key map (kbd "C-S-SPC") #'lsp-signature-activate) 2924 (when lsp-keymap-prefix 2925 (define-key map (kbd lsp-keymap-prefix) lsp-command-map)) 2926 map) 2927 "Keymap for `lsp-mode'.") 2928 2929 (define-minor-mode lsp-mode "Mode for LSP interaction." 2930 :keymap lsp-mode-map 2931 :lighter 2932 (" LSP[" 2933 (lsp--buffer-workspaces 2934 (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "][")) 2935 (:propertize "Disconnected" face warning)) 2936 "]") 2937 :group 'lsp-mode 2938 (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred)) 2939 ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp' 2940 (lsp))) 2941 2942 (defvar lsp-mode-menu 2943 (easy-menu-create-menu 2944 nil 2945 `(["Go to definition" lsp-find-definition 2946 :active (lsp-feature? "textDocument/definition")] 2947 ["Find references" lsp-find-references 2948 :active (lsp-feature? "textDocument/references")] 2949 ["Find implementations" lsp-find-implementation 2950 :active (lsp-feature? "textDocument/implementation")] 2951 ["Find declarations" lsp-find-declaration 2952 :active (lsp-feature? "textDocument/declaration")] 2953 ["Go to type declaration" lsp-find-type-definition 2954 :active (lsp-feature? "textDocument/typeDefinition")] 2955 "--" 2956 ["Describe" lsp-describe-thing-at-point] 2957 ["Code action" lsp-execute-code-action] 2958 ["Format" lsp-format-buffer] 2959 ["Highlight references" lsp-document-highlight] 2960 ["Type Hierarchy" lsp-java-type-hierarchy 2961 :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")] 2962 ["Type Hierarchy" lsp-treemacs-type-hierarchy 2963 :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")) 2964 (functionp 'lsp-treemacs-type-hierarchy) 2965 (lsp-feature? "textDocument/typeHierarchy"))] 2966 ["Call Hierarchy" lsp-treemacs-call-hierarchy 2967 :visible (and (functionp 'lsp-treemacs-call-hierarchy) 2968 (lsp-feature? "textDocument/callHierarchy"))] 2969 ["Rename" lsp-rename 2970 :active (lsp-feature? "textDocument/rename")] 2971 "--" 2972 ("Session" 2973 ["View logs" lsp-workspace-show-log] 2974 ["Describe" lsp-describe-session] 2975 ["Shutdown" lsp-shutdown-workspace] 2976 ["Restart" lsp-restart-workspace]) 2977 ("Workspace Folders" 2978 ["Add" lsp-workspace-folders-add] 2979 ["Remove" lsp-workspace-folders-remove] 2980 ["Open" lsp-workspace-folders-open]) 2981 ("Toggle features" 2982 ["Lenses" lsp-lens-mode] 2983 ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode] 2984 ["Modeline code actions" lsp-modeline-code-actions-mode] 2985 ["Modeline diagnostics" lsp-modeline-diagnostics-mode]) 2986 "---" 2987 ("Debug" 2988 :active (bound-and-true-p dap-ui-mode) 2989 :filter ,(lambda (_) 2990 (and (boundp 'dap-ui-menu-items) 2991 (nthcdr 3 dap-ui-menu-items)))))) 2992 "Menu for lsp-mode.") 2993 2994 (defalias 'make-lsp-client 'make-lsp--client) 2995 2996 (cl-defstruct lsp--registered-capability 2997 (id "") 2998 (method " ") 2999 (options nil)) 3000 3001 ;; A ‘lsp--workspace’ object represents exactly one language server process. 3002 (cl-defstruct lsp--workspace 3003 ;; the `ewoc' object for displaying I/O to and from the server 3004 (ewoc nil) 3005 3006 ;; ‘server-capabilities’ is a hash table of the language server capabilities. 3007 ;; It is the hash table representation of a LSP ServerCapabilities structure; 3008 ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize. 3009 (server-capabilities nil) 3010 3011 ;; ‘registered-server-capabilities’ is a list of hash tables that represent 3012 ;; dynamically-registered Registration objects. See 3013 ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability. 3014 (registered-server-capabilities nil) 3015 3016 ;; ‘root’ is a directory name or a directory file name for the workspace 3017 ;; root. ‘lsp-mode’ passes this directory to the ‘initialize’ method of the 3018 ;; language server; see 3019 ;; https://microsoft.github.io/language-server-protocol/specification#initialize. 3020 (root nil) 3021 3022 ;; ‘client’ is the ‘lsp--client’ object associated with this workspace. 3023 (client nil) 3024 3025 ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It 3026 ;; used to derive the file path in `lsp--uri-to-path' when using tramp 3027 ;; connection. 3028 (host-root nil) 3029 3030 ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or 3031 ;; a network connection. ‘lsp-mode’ communicates with ‘proc’ using the 3032 ;; language server protocol. ‘proc’ corresponds to the COMMUNICATION-PROCESS 3033 ;; element of the return value of the client’s ‘get-root’ field, which see. 3034 (proc nil) 3035 3036 ;; ‘proc’ is a process object; it must represent a regular process, not a 3037 ;; pipe or network process. It represents the actual server process that 3038 ;; corresponds to this workspace. ‘cmd-proc’ corresponds to the 3039 ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’ 3040 ;; field, which see. 3041 (cmd-proc nil) 3042 3043 ;; ‘buffers’ is a list of buffers associated with this workspace. 3044 (buffers nil) 3045 3046 ;; if semantic tokens is enabled, `semantic-tokens-faces' contains 3047 ;; one face (or nil) for each token type supported by the language server. 3048 (semantic-tokens-faces nil) 3049 3050 ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces' 3051 ;; contains one face (or nil) for each modifier type supported by the language 3052 ;; server 3053 (semantic-tokens-modifier-faces nil) 3054 3055 ;; Extra client capabilities provided by third-party packages using 3056 ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME 3057 ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name, 3058 ;; and CAPS is either a plist of the client capabilities, or a function that 3059 ;; takes no argument and returns a plist of the client capabilities or nil. 3060 (extra-client-capabilities nil) 3061 3062 ;; Workspace status 3063 (status nil) 3064 3065 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3066 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3067 (metadata (make-hash-table :test 'equal)) 3068 3069 ;; contains all the file notification watches that have been created for the 3070 ;; current workspace in format filePath->file notification handle. 3071 (watches (make-hash-table :test 'equal)) 3072 3073 ;; list of workspace folders 3074 (workspace-folders nil) 3075 3076 ;; ‘last-id’ the last request id for the current workspace. 3077 (last-id 0) 3078 3079 ;; ‘status-string’ allows extensions to specify custom status string based on 3080 ;; the Language Server specific messages. 3081 (status-string nil) 3082 3083 ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it 3084 ;; was stopped). 3085 shutdown-action 3086 3087 ;; ‘diagnostics’ a hashmap with workspace diagnostics. 3088 (diagnostics (make-hash-table :test 'equal)) 3089 3090 ;; contains all the workDone progress tokens that have been created 3091 ;; for the current workspace. 3092 (work-done-tokens (make-hash-table :test 'equal))) 3093 3094 3095 (cl-defstruct lsp-session 3096 ;; contains the folders that are part of the current session 3097 folders 3098 ;; contains the folders that must not be imported in the current workspace. 3099 folders-blocklist 3100 ;; contains the list of folders that must be imported in a project in case of 3101 ;; multi root LSP server. 3102 (server-id->folders (make-hash-table :test 'equal)) 3103 ;; folder to list of the servers that are associated with the folder. 3104 (folder->servers (make-hash-table :test 'equal)) 3105 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3106 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3107 (metadata (make-hash-table :test 'equal))) 3108 3109 (defun lsp-workspace-status (status-string &optional workspace) 3110 "Set current workspace status to STATUS-STRING. 3111 If WORKSPACE is not specified defaults to lsp--cur-workspace." 3112 (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string)))) 3113 (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string))) 3114 3115 (defun lsp-session-set-metadata (key value &optional _workspace) 3116 "Associate KEY with VALUE in the WORKSPACE metadata. 3117 If WORKSPACE is not provided current workspace will be used." 3118 (puthash key value (lsp-session-metadata (lsp-session)))) 3119 3120 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata) 3121 3122 (defun lsp-session-get-metadata (key &optional _workspace) 3123 "Lookup KEY in WORKSPACE metadata. 3124 If WORKSPACE is not provided current workspace will be used." 3125 (gethash key (lsp-session-metadata (lsp-session)))) 3126 3127 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata) 3128 3129 (defun lsp-workspace-set-work-done-token (token value workspace) 3130 "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens." 3131 (puthash token value (lsp--workspace-work-done-tokens workspace))) 3132 3133 (defun lsp-workspace-get-work-done-token (token workspace) 3134 "Lookup TOKEN in the WORKSPACE work-done-tokens." 3135 (gethash token (lsp--workspace-work-done-tokens workspace))) 3136 3137 (defun lsp-workspace-rem-work-done-token (token workspace) 3138 "Remove TOKEN from the WORKSPACE work-done-tokens." 3139 (remhash token (lsp--workspace-work-done-tokens workspace))) 3140 3141 3142 (defun lsp--make-notification (method &optional params) 3143 "Create notification body for method METHOD and parameters PARAMS." 3144 (list :jsonrpc "2.0" :method method :params params)) 3145 3146 (defalias 'lsp--make-request 'lsp--make-notification) 3147 (defalias 'lsp-make-request 'lsp--make-notification) 3148 3149 (defun lsp--make-response (id result) 3150 "Create response for REQUEST with RESULT." 3151 `(:jsonrpc "2.0" :id ,id :result ,result)) 3152 3153 (defun lsp-make-notification (method &optional params) 3154 "Create notification body for method METHOD and parameters PARAMS." 3155 (lsp--make-notification method params)) 3156 3157 (defmacro lsp--json-serialize (params) 3158 (if (progn 3159 (require 'json) 3160 (fboundp 'json-serialize)) 3161 `(json-serialize ,params 3162 :null-object nil 3163 :false-object :json-false) 3164 `(let ((json-false :json-false)) 3165 (json-encode ,params)))) 3166 3167 (defun lsp--make-message (params) 3168 "Create a LSP message from PARAMS, after encoding it to a JSON string." 3169 (let ((body (lsp--json-serialize params))) 3170 (concat "Content-Length: " 3171 (number-to-string (1+ (string-bytes body))) 3172 "\r\n\r\n" 3173 body 3174 "\n"))) 3175 3176 (cl-defstruct lsp--log-entry timestamp process-time type method id body) 3177 3178 (defun lsp--make-log-entry (method id body type &optional process-time) 3179 "Create an outgoing log object from BODY with method METHOD and id ID. 3180 If ID is non-nil, then the body is assumed to be a notification. 3181 TYPE can either be `incoming' or `outgoing'" 3182 (cl-assert (memq type '(incoming-req outgoing-req incoming-notif 3183 outgoing-notif incoming-resp 3184 outgoing-resp))) 3185 (make-lsp--log-entry 3186 :timestamp (format-time-string "%I:%M:%S %p") 3187 :process-time process-time 3188 :method method 3189 :id id 3190 :type type 3191 :body body)) 3192 3193 (defun lsp--log-font-lock-json (body) 3194 "Font lock JSON BODY." 3195 (with-temp-buffer 3196 (insert body) 3197 ;; We set the temp buffer file-name extension to .json and call `set-auto-mode' 3198 ;; so the users configured json mode is used which could be 3199 ;; `json-mode', `json-ts-mode', `jsonian-mode', etc. 3200 (let ((buffer-file-name "lsp-log.json")) 3201 (delay-mode-hooks 3202 (set-auto-mode) 3203 (if (fboundp 'font-lock-ensure) 3204 (font-lock-ensure) 3205 (with-no-warnings 3206 (font-lock-fontify-buffer))))) 3207 (buffer-string))) 3208 3209 (defun lsp--log-entry-pp (entry) 3210 (cl-assert (lsp--log-entry-p entry)) 3211 (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time 3212 body) 3213 entry) 3214 (json-false :json-false) 3215 (json-encoding-pretty-print t) 3216 (str nil)) 3217 (setq str 3218 (concat (format "[Trace - %s] " timestamp) 3219 (pcase type 3220 ('incoming-req (format "Received request '%s - (%s)." method id)) 3221 ('outgoing-req (format "Sending request '%s - (%s)'." method id)) 3222 3223 ('incoming-notif (format "Received notification '%s'." method)) 3224 ('outgoing-notif (format "Sending notification '%s'." method)) 3225 3226 ('incoming-resp (format "Received response '%s - (%s)' in %dms." 3227 method id process-time)) 3228 ('outgoing-resp 3229 (format 3230 "Sending response '%s - (%s)'. Processing request took %dms" 3231 method id process-time))) 3232 "\n" 3233 (if (memq type '(incoming-resp ougoing-resp)) 3234 "Result: " 3235 "Params: ") 3236 (lsp--log-font-lock-json (json-encode body)) 3237 "\n\n\n")) 3238 (setq str (propertize str 'mouse-face 'highlight 'read-only t)) 3239 (insert str))) 3240 3241 (defvar-local lsp--log-io-ewoc nil) 3242 3243 (defun lsp--get-create-io-ewoc (workspace) 3244 (if (and (lsp--workspace-ewoc workspace) 3245 (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace)))) 3246 (lsp--workspace-ewoc workspace) 3247 (with-current-buffer (lsp--get-log-buffer-create workspace) 3248 (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode)) 3249 (setq-local window-point-insertion-type t) 3250 (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t)) 3251 (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc)) 3252 (lsp--workspace-ewoc workspace))) 3253 3254 (defun lsp--ewoc-count (ewoc) 3255 (let* ((count 0) 3256 (count-fn (lambda (_) (setq count (1+ count))))) 3257 (ewoc-map count-fn ewoc) 3258 count)) 3259 3260 (defun lsp--log-entry-new (entry workspace) 3261 (let* ((ewoc (lsp--get-create-io-ewoc workspace)) 3262 (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc))) 3263 (node (if (or (eq lsp-io-messages-max t) 3264 (>= lsp-io-messages-max count)) 3265 nil 3266 (ewoc-nth ewoc (1- lsp-io-messages-max)))) 3267 (prev nil) 3268 (inhibit-read-only t)) 3269 (while node 3270 (setq prev (ewoc-prev ewoc node)) 3271 (ewoc-delete ewoc node) 3272 (setq node prev)) 3273 (ewoc-enter-last ewoc entry))) 3274 3275 (defun lsp--send-notification (body) 3276 "Send BODY as a notification to the language server." 3277 (lsp-foreach-workspace 3278 (when (lsp--log-io-p (plist-get body :method)) 3279 (lsp--log-entry-new (lsp--make-log-entry 3280 (plist-get body :method) 3281 nil (plist-get body :params) 'outgoing-notif) 3282 lsp--cur-workspace)) 3283 (lsp--send-no-wait body 3284 (lsp--workspace-proc lsp--cur-workspace)))) 3285 3286 (defalias 'lsp-send-notification 'lsp--send-notification) 3287 3288 (defun lsp-notify (method params) 3289 "Send notification METHOD with PARAMS." 3290 (lsp--send-notification (lsp--make-notification method params))) 3291 3292 (defun lsp--cur-workspace-check () 3293 "Check whether buffer lsp workspace(s) are set." 3294 (cl-assert (lsp-workspaces) nil 3295 "No language server(s) is associated with this buffer.")) 3296 3297 (defun lsp--send-request (body &optional no-wait no-merge) 3298 "Send BODY as a request to the language server, get the response. 3299 If NO-WAIT is non-nil, don't synchronously wait for a response. 3300 If NO-MERGE is non-nil, don't merge the results but return an 3301 alist mapping workspace->result." 3302 (lsp-request (plist-get body :method) 3303 (plist-get body :params) 3304 :no-wait no-wait 3305 :no-merge no-merge)) 3306 3307 (defalias 'lsp-send-request 'lsp--send-request 3308 "Send BODY as a request to the language server and return the response 3309 synchronously. 3310 \n(fn BODY)") 3311 3312 (cl-defun lsp-request (method params &key no-wait no-merge) 3313 "Send request METHOD with PARAMS. 3314 If NO-MERGE is non-nil, don't merge the results but return alist 3315 workspace->result. 3316 If NO-WAIT is non-nil send the request as notification." 3317 (if no-wait 3318 (lsp-notify method params) 3319 (let* ((send-time (float-time)) 3320 ;; max time by which we must get a response 3321 (expected-time 3322 (and 3323 lsp-response-timeout 3324 (+ send-time lsp-response-timeout))) 3325 resp-result resp-error done?) 3326 (unwind-protect 3327 (progn 3328 (lsp-request-async method params 3329 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3330 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3331 :no-merge no-merge 3332 :mode 'detached 3333 :cancel-token :sync-request) 3334 (while (not (or resp-error resp-result)) 3335 (if (functionp 'json-rpc-connection) 3336 (catch 'lsp-done (sit-for 0.01)) 3337 (catch 'lsp-done 3338 (accept-process-output 3339 nil 3340 (if expected-time (- expected-time send-time) 1)))) 3341 (setq send-time (float-time)) 3342 (when (and expected-time (< expected-time send-time)) 3343 (error "Timeout while waiting for response. Method: %s" method))) 3344 (setq done? t) 3345 (cond 3346 ((eq resp-result :finished) nil) 3347 (resp-result resp-result) 3348 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3349 ((lsp-json-error? (cl-first resp-error)) 3350 (error (lsp:json-error-message (cl-first resp-error)))))) 3351 (unless done? 3352 (lsp-cancel-request-by-token :sync-request)))))) 3353 3354 (cl-defun lsp-request-while-no-input (method params) 3355 "Send request METHOD with PARAMS and waits until there is no input. 3356 Return same value as `lsp--while-no-input' and respecting `non-essential'." 3357 (if (or non-essential (not lsp-request-while-no-input-may-block)) 3358 (let* ((send-time (float-time)) 3359 ;; max time by which we must get a response 3360 (expected-time 3361 (and 3362 lsp-response-timeout 3363 (+ send-time lsp-response-timeout))) 3364 resp-result resp-error done?) 3365 (unwind-protect 3366 (progn 3367 (lsp-request-async method params 3368 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3369 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3370 :mode 'detached 3371 :cancel-token :sync-request) 3372 (while (not (or resp-error resp-result (input-pending-p))) 3373 (catch 'lsp-done 3374 (sit-for 3375 (if expected-time (- expected-time send-time) 1))) 3376 (setq send-time (float-time)) 3377 (when (and expected-time (< expected-time send-time)) 3378 (error "Timeout while waiting for response. Method: %s" method))) 3379 (setq done? (or resp-error resp-result)) 3380 (cond 3381 ((eq resp-result :finished) nil) 3382 (resp-result resp-result) 3383 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3384 ((lsp-json-error? (cl-first resp-error)) 3385 (error (lsp:json-error-message (cl-first resp-error)))))) 3386 (unless done? 3387 (lsp-cancel-request-by-token :sync-request)) 3388 (when (and (input-pending-p) lsp--throw-on-input) 3389 (throw 'input :interrupted)))) 3390 (lsp-request method params))) 3391 3392 (defvar lsp--cancelable-requests (ht)) 3393 3394 (cl-defun lsp-request-async (method params callback 3395 &key mode error-handler cancel-handler no-merge cancel-token) 3396 "Send METHOD with PARAMS as a request to the language server. 3397 Call CALLBACK with the response received from the server 3398 asynchronously. 3399 MODE determines when the callback will be called depending on the 3400 condition of the original buffer. It could be: 3401 - `detached' which means that the callback will be executed no 3402 matter what has happened to the buffer. 3403 - `alive' - the callback will be executed only if the buffer from 3404 which the call was executed is still alive. 3405 - `current' the callback will be executed only if the original buffer 3406 is still selected. 3407 - `tick' - the callback will be executed only if the buffer was not modified. 3408 - `unchanged' - the callback will be executed only if the buffer hasn't 3409 changed and if the buffer is not modified. 3410 3411 ERROR-HANDLER will be called in case the request has failed. 3412 CANCEL-HANDLER will be called in case the request is being canceled. 3413 If NO-MERGE is non-nil, don't merge the results but return alist 3414 workspace->result. 3415 CANCEL-TOKEN is the token that can be used to cancel request." 3416 (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params) 3417 callback mode error-handler cancel-handler no-merge cancel-token)) 3418 3419 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback) 3420 (lambda (&rest _) 3421 (unless (and (equal 'post-command-hook hook) 3422 (equal (current-buffer) buf)) 3423 (lsp--request-cleanup-hooks id) 3424 (with-lsp-workspaces workspaces 3425 (lsp--cancel-request id) 3426 (when cancel-callback (funcall cancel-callback))) 3427 (lsp-log "Cancelling %s(%s) in hook %s" method id hook)))) 3428 3429 (defun lsp--create-async-callback 3430 (callback method no-merge workspaces) 3431 "Create async handler expecting COUNT results, merge them and call CALLBACK. 3432 MODE determines when the callback will be called depending on the 3433 condition of the original buffer. METHOD is the invoked method. 3434 If NO-MERGE is non-nil, don't merge the results but return alist 3435 workspace->result. ID is the request id." 3436 (let (results errors) 3437 (lambda (result) 3438 (push (cons lsp--cur-workspace result) 3439 (if (eq result :error) errors results)) 3440 (when (and (not (eq (length errors) (length workspaces))) 3441 (eq (+ (length errors) (length results)) (length workspaces))) 3442 (funcall callback 3443 (if no-merge 3444 results 3445 (lsp--merge-results (-map #'cl-rest results) method))))))) 3446 3447 (defcustom lsp-default-create-error-handler-fn nil 3448 "Default error handler customization. 3449 Handler should give METHOD as argument and return function of one argument 3450 ERROR." 3451 :type 'function 3452 :group 'lsp-mode 3453 :package-version '(lsp-mode . "9.0.0")) 3454 3455 (defun lsp--create-default-error-handler (method) 3456 "Default error handler. 3457 METHOD is the executed method." 3458 (if lsp-default-create-error-handler-fn 3459 (funcall lsp-default-create-error-handler-fn method) 3460 (lambda (error) 3461 (lsp--warn "%s" (or (lsp--error-string error) 3462 (format "%s Request has failed" method)))))) 3463 3464 (defvar lsp--request-cleanup-hooks (ht)) 3465 3466 (defun lsp--request-cleanup-hooks (request-id) 3467 (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks))) 3468 (funcall cleanup-function) 3469 (remhash request-id lsp--request-cleanup-hooks))) 3470 3471 (defun lsp-cancel-request-by-token (cancel-token) 3472 "Cancel request using CANCEL-TOKEN." 3473 (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests)) 3474 (with-lsp-workspaces workspaces 3475 (lsp--cancel-request request-id)) 3476 (remhash cancel-token lsp--cancelable-requests) 3477 (lsp--request-cleanup-hooks request-id))) 3478 3479 (defun lsp--send-request-async (body callback 3480 &optional mode error-callback cancel-callback 3481 no-merge cancel-token) 3482 "Send BODY as a request to the language server. 3483 Call CALLBACK with the response received from the server 3484 asynchronously. 3485 MODE determines when the callback will be called depending on the 3486 condition of the original buffer. It could be: 3487 - `detached' which means that the callback will be executed no 3488 matter what has happened to the buffer. 3489 - `alive' - the callback will be executed only if the buffer from 3490 which the call was executed is still alive. 3491 - `current' the callback will be executed only if the original buffer 3492 is still selected. 3493 - `tick' - the callback will be executed only if the buffer was not modified. 3494 - `unchanged' - the callback will be executed only if the buffer hasn't 3495 changed and if the buffer is not modified. 3496 3497 ERROR-CALLBACK will be called in case the request has failed. 3498 CANCEL-CALLBACK will be called in case the request is being canceled. 3499 If NO-MERGE is non-nil, don't merge the results but return alist 3500 workspace->result. 3501 CANCEL-TOKEN is the token that can be used to cancel request." 3502 (when cancel-token 3503 (lsp-cancel-request-by-token cancel-token)) 3504 3505 (if-let ((target-workspaces (lsp--find-workspaces-for body))) 3506 (let* ((start-time (current-time)) 3507 (method (plist-get body :method)) 3508 (id (cl-incf lsp-last-id)) 3509 (buf (current-buffer)) 3510 (cancel-callback (when cancel-callback 3511 (pcase mode 3512 ((or 'alive 'tick 'unchanged) 3513 (lambda () 3514 (with-current-buffer buf 3515 (funcall cancel-callback)))) 3516 (_ cancel-callback)))) 3517 ;; calculate what are the (hook . local) pairs which will cancel 3518 ;; the request 3519 (hooks (pcase mode 3520 ('alive '((kill-buffer-hook . t))) 3521 ('tick '((kill-buffer-hook . t) (after-change-functions . t))) 3522 ('unchanged '((after-change-functions . t) (post-command-hook . nil))) 3523 ('current '((post-command-hook . nil))))) 3524 ;; note: lambdas in emacs can be compared but we should make sure 3525 ;; that all of the captured arguments are the same - in our case 3526 ;; `lsp--create-request-cancel' will return the same lambda when 3527 ;; called with the same params. 3528 (cleanup-hooks 3529 (lambda () (mapc 3530 (-lambda ((hook . local)) 3531 (if local 3532 (when (buffer-live-p buf) 3533 (with-current-buffer buf 3534 (remove-hook hook 3535 (lsp--create-request-cancel 3536 id target-workspaces hook buf method cancel-callback) 3537 t))) 3538 (remove-hook hook (lsp--create-request-cancel 3539 id target-workspaces hook buf method cancel-callback)))) 3540 hooks) 3541 (remhash cancel-token lsp--cancelable-requests))) 3542 (callback (pcase mode 3543 ((or 'alive 'tick 'unchanged) (lambda (&rest args) 3544 (with-current-buffer buf 3545 (apply callback args)))) 3546 (_ callback))) 3547 (callback (lsp--create-async-callback callback 3548 method 3549 no-merge 3550 target-workspaces)) 3551 (callback (lambda (result) 3552 (lsp--request-cleanup-hooks id) 3553 (funcall callback result))) 3554 (error-callback (lsp--create-async-callback 3555 (or error-callback 3556 (lsp--create-default-error-handler method)) 3557 method 3558 nil 3559 target-workspaces)) 3560 (error-callback (lambda (error) 3561 (funcall callback :error) 3562 (lsp--request-cleanup-hooks id) 3563 (funcall error-callback error))) 3564 (body (plist-put body :id id))) 3565 3566 ;; cancel request in any of the hooks 3567 (mapc (-lambda ((hook . local)) 3568 (add-hook hook 3569 (lsp--create-request-cancel 3570 id target-workspaces hook buf method cancel-callback) 3571 nil local)) 3572 hooks) 3573 (puthash id cleanup-hooks lsp--request-cleanup-hooks) 3574 3575 (setq lsp--last-active-workspaces target-workspaces) 3576 3577 (when cancel-token 3578 (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests)) 3579 3580 (seq-doseq (workspace target-workspaces) 3581 (when (lsp--log-io-p method) 3582 (lsp--log-entry-new (lsp--make-log-entry method id 3583 (plist-get body :params) 3584 'outgoing-req) 3585 workspace)) 3586 (puthash id 3587 (list callback error-callback method start-time (current-time)) 3588 (-> workspace 3589 (lsp--workspace-client) 3590 (lsp--client-response-handlers))) 3591 (lsp--send-no-wait body (lsp--workspace-proc workspace))) 3592 body) 3593 (error "The connected server(s) does not support method %s. 3594 To find out what capabilities support your server use `M-x lsp-describe-session' 3595 and expand the capabilities section" 3596 (plist-get body :method)))) 3597 3598 ;; deprecated, use lsp-request-async. 3599 (defalias 'lsp-send-request-async 'lsp--send-request-async) 3600 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1") 3601 3602 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any 3603 ;; pending language servers. 3604 (add-hook 'kill-emacs-hook #'lsp--global-teardown) 3605 3606 (defun lsp--global-teardown () 3607 "Unload working workspaces." 3608 (lsp-foreach-workspace (lsp--shutdown-workspace))) 3609 3610 (defun lsp--shutdown-workspace (&optional restart) 3611 "Shut down the language server process for ‘lsp--cur-workspace’." 3612 (with-demoted-errors "LSP error: %S" 3613 (let ((lsp-response-timeout 0.5)) 3614 (condition-case err 3615 (lsp-request "shutdown" nil) 3616 (error (lsp--error "%s" err)))) 3617 (lsp-notify "exit" nil)) 3618 (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown)) 3619 (lsp--uninitialize-workspace)) 3620 3621 (defcustom lsp-inlay-hint-enable nil 3622 "If non-nil it will enable inlay hints." 3623 :type 'boolean 3624 :group 'lsp-mode 3625 :package-version '(lsp-mode . "9.0.0")) 3626 3627 (defun lsp--uninitialize-workspace () 3628 "Cleanup buffer state. 3629 When a workspace is shut down, by request or from just 3630 disappearing, unset all the variables related to it." 3631 (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace] 3632 (lsp-process-kill cmd-proc) 3633 (mapc (lambda (buf) 3634 (when (lsp-buffer-live-p buf) 3635 (lsp-with-current-buffer buf 3636 (lsp-managed-mode -1)))) 3637 buffers) 3638 (lsp-diagnostics--workspace-cleanup lsp--cur-workspace))) 3639 3640 (defun lsp--client-capabilities (&optional custom-capabilities) 3641 "Return the client capabilities appending CUSTOM-CAPABILITIES." 3642 (append 3643 `((general . ((positionEncodings . ["utf-32", "utf-16"]))) 3644 (workspace . ((workspaceEdit . ((documentChanges . t) 3645 (resourceOperations . ["create" "rename" "delete"]))) 3646 (applyEdit . t) 3647 (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))))) 3648 (executeCommand . ((dynamicRegistration . :json-false))) 3649 ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t))))) 3650 (workspaceFolders . t) 3651 (configuration . t) 3652 ,@(when lsp-semantic-tokens-enable 3653 `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests) 3654 lsp-semantic-tokens-honor-refresh-requests) 3655 :json-false)))))) 3656 ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t))))) 3657 ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false))))) 3658 (fileOperations . ((didCreate . :json-false) 3659 (willCreate . :json-false) 3660 (didRename . t) 3661 (willRename . t) 3662 (didDelete . :json-false) 3663 (willDelete . :json-false))))) 3664 (textDocument . ((declaration . ((dynamicRegistration . t) 3665 (linkSupport . t))) 3666 (definition . ((dynamicRegistration . t) 3667 (linkSupport . t))) 3668 (references . ((dynamicRegistration . t))) 3669 (implementation . ((dynamicRegistration . t) 3670 (linkSupport . t))) 3671 (typeDefinition . ((dynamicRegistration . t) 3672 (linkSupport . t))) 3673 (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t))) 3674 (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))) 3675 (hierarchicalDocumentSymbolSupport . t))) 3676 (formatting . ((dynamicRegistration . t))) 3677 (rangeFormatting . ((dynamicRegistration . t))) 3678 (onTypeFormatting . ((dynamicRegistration . t))) 3679 ,@(when (and lsp-semantic-tokens-enable 3680 (functionp 'lsp--semantic-tokens-capabilities)) 3681 (lsp--semantic-tokens-capabilities)) 3682 (rename . ((dynamicRegistration . t) (prepareSupport . t))) 3683 (codeAction . ((dynamicRegistration . t) 3684 (isPreferredSupport . t) 3685 (codeActionLiteralSupport . ((codeActionKind . ((valueSet . ["" 3686 "quickfix" 3687 "refactor" 3688 "refactor.extract" 3689 "refactor.inline" 3690 "refactor.rewrite" 3691 "source" 3692 "source.organizeImports"]))))) 3693 (resolveSupport . ((properties . ["edit" "command"]))) 3694 (dataSupport . t))) 3695 (completion . ((completionItem . ((snippetSupport . ,(cond 3696 ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode))) 3697 (lsp--warn (concat 3698 "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. " 3699 "You must either install yasnippet, or disable snippet support.")) 3700 :json-false) 3701 (lsp-enable-snippet t) 3702 (t :json-false))) 3703 (documentationFormat . ["markdown" "plaintext"]) 3704 ;; Remove this after jdtls support resolveSupport 3705 (resolveAdditionalTextEditsSupport . t) 3706 (insertReplaceSupport . t) 3707 (deprecatedSupport . t) 3708 (resolveSupport 3709 . ((properties . ["documentation" 3710 "detail" 3711 "additionalTextEdits" 3712 "command"]))) 3713 (insertTextModeSupport . ((valueSet . [1 2]))))) 3714 (contextSupport . t) 3715 (dynamicRegistration . t))) 3716 (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t))))) 3717 (dynamicRegistration . t))) 3718 (documentLink . ((dynamicRegistration . t) 3719 (tooltipSupport . t))) 3720 (hover . ((contentFormat . ["markdown" "plaintext"]) 3721 (dynamicRegistration . t))) 3722 ,@(when lsp-enable-folding 3723 `((foldingRange . ((dynamicRegistration . t) 3724 ,@(when lsp-folding-range-limit 3725 `((rangeLimit . ,lsp-folding-range-limit))) 3726 ,@(when lsp-folding-line-folding-only 3727 `((lineFoldingOnly . t))))))) 3728 (selectionRange . ((dynamicRegistration . t))) 3729 (callHierarchy . ((dynamicRegistration . :json-false))) 3730 (typeHierarchy . ((dynamicRegistration . t))) 3731 (publishDiagnostics . ((relatedInformation . t) 3732 (tagSupport . ((valueSet . [1 2]))) 3733 (versionSupport . t))) 3734 (linkedEditingRange . ((dynamicRegistration . t))))) 3735 (window . ((workDoneProgress . t) 3736 (showDocument . ((support . t)))))) 3737 custom-capabilities)) 3738 3739 (defun lsp-find-roots-for-workspace (workspace session) 3740 "Get all roots for the WORKSPACE." 3741 (-filter #'identity (ht-map (lambda (folder workspaces) 3742 (when (-contains? workspaces workspace) 3743 folder)) 3744 (lsp-session-folder->servers session)))) 3745 3746 (defun lsp-session-watches (&optional session) 3747 "Get watches created for SESSION." 3748 (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session)))) 3749 (-let [res (make-hash-table :test 'equal)] 3750 (puthash "__watches" res (lsp-session-metadata (or session (lsp-session)))) 3751 res))) 3752 3753 (defun lsp--file-process-event (session root-folder event) 3754 "Process file event." 3755 (let* ((changed-file (cl-third event)) 3756 (rel-changed-file (f-relative changed-file root-folder)) 3757 (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type)) 3758 (bit-position (1- event-numeric-kind)) 3759 (watch-bit (ash 1 bit-position))) 3760 (->> 3761 session 3762 lsp-session-folder->servers 3763 (gethash root-folder) 3764 (seq-do (lambda (workspace) 3765 (when (->> 3766 workspace 3767 lsp--workspace-registered-server-capabilities 3768 (-any? 3769 (lambda (capability) 3770 (and 3771 (equal (lsp--registered-capability-method capability) 3772 "workspace/didChangeWatchedFiles") 3773 (->> 3774 capability 3775 lsp--registered-capability-options 3776 (lsp:did-change-watched-files-registration-options-watchers) 3777 (seq-find 3778 (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp)) 3779 (when (or (null kind?) 3780 (> (logand kind? watch-bit) 0)) 3781 (-let [regexes (or cached-regexp 3782 (let ((regexp (lsp-glob-to-regexps glob-pattern))) 3783 (lsp-put fs-watcher :_cachedRegexp regexp) 3784 regexp))] 3785 (-any? (lambda (re) 3786 (or (string-match re changed-file) 3787 (string-match re rel-changed-file))) 3788 regexes)))))))))) 3789 (with-lsp-workspace workspace 3790 (lsp-notify 3791 "workspace/didChangeWatchedFiles" 3792 `((changes . [((type . ,event-numeric-kind) 3793 (uri . ,(lsp--path-to-uri changed-file)))])))))))))) 3794 3795 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?)) 3796 "Register capability REG." 3797 (when (and lsp-enable-file-watchers 3798 (equal method "workspace/didChangeWatchedFiles")) 3799 (-let* ((created-watches (lsp-session-watches (lsp-session))) 3800 (root-folders (cl-set-difference 3801 (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session)) 3802 (ht-keys created-watches)))) 3803 ;; create watch for each root folder without such 3804 (dolist (folder root-folders) 3805 (let* ((watch (make-lsp-watch :root-directory folder)) 3806 (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder)) 3807 (ignored-files-regex-list (car ignored-things)) 3808 (ignored-directories-regex-list (cadr ignored-things))) 3809 (puthash folder watch created-watches) 3810 (lsp-watch-root-folder (file-truename folder) 3811 (-partial #'lsp--file-process-event (lsp-session) folder) 3812 ignored-files-regex-list 3813 ignored-directories-regex-list 3814 watch 3815 t))))) 3816 3817 (push 3818 (make-lsp--registered-capability :id id :method method :options register-options?) 3819 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3820 3821 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body) 3822 "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to 3823 access dir-local variables." 3824 (declare (indent 1) (debug t)) 3825 `(with-temp-buffer 3826 ;; Set the buffer's name to something under the root so that we can hack the local variables 3827 ;; This file doesn't need to exist and will not be created due to this. 3828 (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root))) 3829 (hack-local-variables) 3830 (prog1 ,@body 3831 (setq-local buffer-file-name nil)))) 3832 3833 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root) 3834 "Return a list of the form 3835 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given 3836 WORKSPACE-ROOT." 3837 ;; The intent of this function is to provide per-root workspace-level customization of the 3838 ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables. 3839 (lsp--with-workspace-temp-buffer workspace-root 3840 (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories)))) 3841 3842 3843 (defun lsp--cleanup-hanging-watches () 3844 "Cleanup watches in case there are no more workspaces that are interested 3845 in that particular folder." 3846 (let* ((session (lsp-session)) 3847 (watches (lsp-session-watches session))) 3848 (dolist (watched-folder (ht-keys watches)) 3849 (when (-none? (lambda (workspace) 3850 (with-lsp-workspace workspace 3851 (lsp--registered-capability "workspace/didChangeWatchedFiles"))) 3852 (gethash watched-folder (lsp-session-folder->servers (lsp-session)))) 3853 (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder) 3854 (lsp-kill-watch (gethash watched-folder watches)) 3855 (remhash watched-folder watches))))) 3856 3857 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method)) 3858 "Unregister capability UNREG." 3859 (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace) 3860 (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id)) 3861 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3862 (when (equal method "workspace/didChangeWatchedFiles") 3863 (lsp--cleanup-hanging-watches))) 3864 3865 (defun lsp--server-capabilities () 3866 "Return the capabilities of the language server associated with the buffer." 3867 (->> (lsp-workspaces) 3868 (-keep #'lsp--workspace-server-capabilities) 3869 (apply #'lsp-merge))) 3870 3871 (defun lsp--send-open-close-p () 3872 "Return whether open and close notifications should be sent to the server." 3873 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3874 (or (memq sync '(1 2)) 3875 (lsp:text-document-sync-options-open-close? sync)))) 3876 3877 (defun lsp--send-will-save-p () 3878 "Return whether willSave notifications should be sent to the server." 3879 (-> (lsp--server-capabilities) 3880 (lsp:server-capabilities-text-document-sync?) 3881 (lsp:text-document-sync-options-will-save?))) 3882 3883 (defun lsp--send-will-save-wait-until-p () 3884 "Return whether willSaveWaitUntil notifications should be sent to the server." 3885 (-> (lsp--server-capabilities) 3886 (lsp:server-capabilities-text-document-sync?) 3887 (lsp:text-document-sync-options-will-save-wait-until?))) 3888 3889 (defun lsp--send-did-save-p () 3890 "Return whether didSave notifications should be sent to the server." 3891 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3892 (or (memq sync '(1 2)) 3893 (lsp:text-document-sync-options-save? sync)))) 3894 3895 (defun lsp--save-include-text-p () 3896 "Return whether save notifications should include the text document's contents." 3897 (->> (lsp--server-capabilities) 3898 (lsp:server-capabilities-text-document-sync?) 3899 (lsp:text-document-sync-options-save?) 3900 (lsp:text-document-save-registration-options-include-text?))) 3901 3902 (defun lsp--send-will-rename-files-p (path) 3903 "Return whether willRenameFiles request should be sent to the server. 3904 If any filters, checks if it applies for PATH." 3905 (let* ((will-rename (-> (lsp--server-capabilities) 3906 (lsp:server-capabilities-workspace?) 3907 (lsp:workspace-server-capabilities-file-operations?) 3908 (lsp:workspace-file-operations-will-rename?))) 3909 (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list))) 3910 (and will-rename 3911 (or (seq-empty-p filters) 3912 (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob))) 3913 (-let [regexes (lsp-glob-to-regexps glob)] 3914 (and (or (not scheme?) 3915 (string-prefix-p scheme? (lsp--path-to-uri path))) 3916 (-any? (lambda (re) 3917 (string-match re path)) 3918 regexes)))) 3919 filters))))) 3920 3921 (defun lsp--send-did-rename-files-p () 3922 "Return whether didRenameFiles notification should be sent to the server." 3923 (-> (lsp--server-capabilities) 3924 (lsp:server-capabilities-workspace?) 3925 (lsp:workspace-server-capabilities-file-operations?) 3926 (lsp:workspace-file-operations-did-rename?))) 3927 3928 (declare-function project-roots "ext:project" (project) t) 3929 (declare-function project-root "ext:project" (project) t) 3930 3931 (defun lsp--suggest-project-root () 3932 "Get project root." 3933 (or 3934 (when (featurep 'projectile) (condition-case nil 3935 (projectile-project-root) 3936 (error nil))) 3937 (when (featurep 'project) 3938 (when-let ((project (project-current))) 3939 (if (fboundp 'project-root) 3940 (project-root project) 3941 (car (with-no-warnings 3942 (project-roots project)))))) 3943 default-directory)) 3944 3945 (defun lsp--read-from-file (file) 3946 "Read FILE content." 3947 (when (file-exists-p file) 3948 (cl-first (read-from-string (f-read-text file 'utf-8))))) 3949 3950 (defun lsp--persist (file-name to-persist) 3951 "Persist TO-PERSIST in FILE-NAME. 3952 3953 This function creates the parent directories if they don't exist 3954 yet." 3955 (let ((print-length nil) 3956 (print-level nil)) 3957 ;; Create all parent directories: 3958 (make-directory (f-parent file-name) t) 3959 (f-write-text (prin1-to-string to-persist) 'utf-8 file-name))) 3960 3961 (defun lsp-workspace-folders-add (project-root) 3962 "Add PROJECT-ROOT to the list of workspace folders." 3963 (interactive 3964 (list (read-directory-name "Select folder to add: " 3965 (or (lsp--suggest-project-root) default-directory) nil t))) 3966 (cl-pushnew (lsp-f-canonical project-root) 3967 (lsp-session-folders (lsp-session)) :test 'equal) 3968 (lsp--persist-session (lsp-session)) 3969 3970 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)) 3971 3972 (defun lsp-workspace-folders-remove (project-root) 3973 "Remove PROJECT-ROOT from the list of workspace folders." 3974 (interactive (list (completing-read "Select folder to remove: " 3975 (lsp-session-folders (lsp-session)) 3976 nil t nil nil 3977 (lsp-find-session-folder (lsp-session) default-directory)))) 3978 3979 (setq project-root (lsp-f-canonical project-root)) 3980 3981 ;; send remove folder to each multiroot workspace associated with the folder 3982 (dolist (wks (->> (lsp-session) 3983 (lsp-session-folder->servers) 3984 (gethash project-root) 3985 (--filter (lsp--client-multi-root (lsp--workspace-client it))))) 3986 (with-lsp-workspace wks 3987 (lsp-notify "workspace/didChangeWorkspaceFolders" 3988 (lsp-make-did-change-workspace-folders-params 3989 :event (lsp-make-workspace-folders-change-event 3990 :removed (vector (lsp-make-workspace-folder 3991 :uri (lsp--path-to-uri project-root) 3992 :name (f-filename project-root))) 3993 :added []))))) 3994 3995 ;; turn off servers in the removed directory 3996 (let* ((session (lsp-session)) 3997 (folder->servers (lsp-session-folder->servers session)) 3998 (server-id->folders (lsp-session-server-id->folders session)) 3999 (workspaces (gethash project-root folder->servers))) 4000 4001 (remhash project-root folder->servers) 4002 4003 ;; turn off the servers without root folders 4004 (dolist (workspace workspaces) 4005 (when (--none? (-contains? it workspace) (ht-values folder->servers)) 4006 (lsp--info "Shutdown %s since folder %s is removed..." 4007 (lsp--workspace-print workspace) project-root) 4008 (with-lsp-workspace workspace (lsp--shutdown-workspace)))) 4009 4010 (setf (lsp-session-folders session) 4011 (-remove-item project-root (lsp-session-folders session))) 4012 4013 (ht-aeach (puthash key 4014 (-remove-item project-root value) 4015 server-id->folders) 4016 server-id->folders) 4017 (lsp--persist-session (lsp-session))) 4018 4019 (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root))) 4020 4021 (defun lsp-workspace-blocklist-remove (project-root) 4022 "Remove PROJECT-ROOT from the workspace blocklist." 4023 (interactive (list (completing-read "Select folder to remove:" 4024 (lsp-session-folders-blocklist (lsp-session)) 4025 nil t))) 4026 (setf (lsp-session-folders-blocklist (lsp-session)) 4027 (delete project-root 4028 (lsp-session-folders-blocklist (lsp-session)))) 4029 (lsp--persist-session (lsp-session))) 4030 4031 (define-obsolete-function-alias 'lsp-workspace-folders-switch 4032 'lsp-workspace-folders-open "lsp-mode 6.1") 4033 4034 (defun lsp-workspace-folders-open (project-root) 4035 "Open the directory located at PROJECT-ROOT" 4036 (interactive (list (completing-read "Open folder: " 4037 (lsp-session-folders (lsp-session)) 4038 nil t))) 4039 (find-file project-root)) 4040 4041 (defun lsp--maybe-enable-signature-help (trigger-characters) 4042 (let ((ch last-command-event)) 4043 (when (cl-find ch trigger-characters :key #'string-to-char) 4044 (lsp-signature-activate)))) 4045 4046 (defun lsp--on-type-formatting-handler-create () 4047 (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" ))) 4048 (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character? 4049 :first-trigger-character) provider] 4050 (lambda () 4051 (lsp--on-type-formatting first-trigger-character 4052 more-trigger-character?))))) 4053 4054 (defun lsp--update-on-type-formatting-hook (&optional cleanup?) 4055 (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create))) 4056 (cond 4057 ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?)) 4058 (add-hook 'post-self-insert-hook on-type-formatting-handler nil t)) 4059 ((or cleanup? 4060 (not lsp-enable-on-type-formatting)) 4061 (remove-hook 'post-self-insert-hook on-type-formatting-handler t))))) 4062 4063 (defun lsp--signature-help-handler-create () 4064 (-when-let ((&SignatureHelpOptions? :trigger-characters?) 4065 (lsp--capability-for-method "textDocument/signatureHelp")) 4066 (lambda () 4067 (lsp--maybe-enable-signature-help trigger-characters?)))) 4068 4069 (defun lsp--update-signature-help-hook (&optional cleanup?) 4070 (let ((signature-help-handler (lsp--signature-help-handler-create))) 4071 (cond 4072 ((and (or (equal lsp-signature-auto-activate t) 4073 (memq :on-trigger-char lsp-signature-auto-activate)) 4074 signature-help-handler) 4075 (add-hook 'post-self-insert-hook signature-help-handler nil t)) 4076 4077 ((or cleanup? 4078 (not (or (equal lsp-signature-auto-activate t) 4079 (memq :on-trigger-char lsp-signature-auto-activate)))) 4080 (remove-hook 'post-self-insert-hook signature-help-handler t))))) 4081 4082 (defun lsp--after-set-visited-file-name () 4083 (lsp-disconnect) 4084 (lsp)) 4085 4086 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27 4087 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099 4088 (defvar eldoc-documentation-default) ; CI 4089 (when (< emacs-major-version 28) 4090 (unless (boundp 'eldoc-documentation-functions) 4091 (load "eldoc")) 4092 (when (memq (default-value 'eldoc-documentation-function) '(nil ignore)) 4093 ;; actually `eldoc-documentation-strategy', but CI was failing 4094 (setq-default eldoc-documentation-function 'eldoc-documentation-default))) 4095 4096 (define-minor-mode lsp-managed-mode 4097 "Mode for source buffers managed by lsp-mode." 4098 :lighter nil 4099 (cond 4100 (lsp-managed-mode 4101 (when (lsp-feature? "textDocument/hover") 4102 (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t) 4103 (eldoc-mode 1)) 4104 4105 (add-hook 'after-change-functions #'lsp-on-change nil t) 4106 (add-hook 'after-revert-hook #'lsp-on-revert nil t) 4107 (add-hook 'after-save-hook #'lsp-on-save nil t) 4108 (add-hook 'auto-save-hook #'lsp--on-auto-save nil t) 4109 (add-hook 'before-change-functions #'lsp-before-change nil t) 4110 (add-hook 'before-save-hook #'lsp--before-save nil t) 4111 (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t) 4112 (add-hook 'post-command-hook #'lsp--post-command nil t) 4113 4114 (lsp--update-on-type-formatting-hook) 4115 (lsp--update-signature-help-hook) 4116 4117 (when lsp-enable-xref 4118 (add-hook 'xref-backend-functions #'lsp--xref-backend nil t)) 4119 4120 (lsp-configure-buffer) 4121 4122 ;; make sure we turn off lsp-mode in case major mode changes, because major 4123 ;; mode change will wipe the buffer locals. 4124 (add-hook 'change-major-mode-hook #'lsp-disconnect nil t) 4125 (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t) 4126 4127 (let ((buffer (lsp-current-buffer))) 4128 (run-with-idle-timer 4129 0.0 nil 4130 (lambda () 4131 (when (lsp-buffer-live-p buffer) 4132 (lsp-with-current-buffer buffer 4133 (lsp--on-change-debounce buffer) 4134 (lsp--on-idle buffer))))))) 4135 (t 4136 (lsp-unconfig-buffer) 4137 4138 (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t) 4139 (remove-hook 'post-command-hook #'lsp--post-command t) 4140 (remove-hook 'after-change-functions #'lsp-on-change t) 4141 (remove-hook 'after-revert-hook #'lsp-on-revert t) 4142 (remove-hook 'after-save-hook #'lsp-on-save t) 4143 (remove-hook 'auto-save-hook #'lsp--on-auto-save t) 4144 (remove-hook 'before-change-functions #'lsp-before-change t) 4145 (remove-hook 'before-save-hook #'lsp--before-save t) 4146 (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t) 4147 4148 (lsp--update-on-type-formatting-hook :cleanup) 4149 (lsp--update-signature-help-hook :cleanup) 4150 4151 (when lsp--on-idle-timer 4152 (cancel-timer lsp--on-idle-timer) 4153 (setq lsp--on-idle-timer nil)) 4154 4155 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4156 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4157 4158 (lsp--remove-overlays 'lsp-highlight) 4159 (lsp--remove-overlays 'lsp-links) 4160 4161 (remove-hook 'xref-backend-functions #'lsp--xref-backend t) 4162 (remove-hook 'change-major-mode-hook #'lsp-disconnect t) 4163 (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t) 4164 (setq-local lsp-buffer-uri nil)))) 4165 4166 (defun lsp-configure-buffer () 4167 "Configure LSP features for current buffer." 4168 ;; make sure the core is running in the context of all available workspaces 4169 ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context 4170 (let ((lsp--buffer-workspaces (cond 4171 (lsp--buffer-workspaces) 4172 (lsp--cur-workspace (list lsp--cur-workspace)))) 4173 lsp--cur-workspace) 4174 (when lsp-auto-configure 4175 (lsp--auto-configure) 4176 4177 (when (and lsp-enable-text-document-color 4178 (lsp-feature? "textDocument/documentColor")) 4179 (add-hook 'lsp-on-change-hook #'lsp--document-color nil t)) 4180 4181 (when (and lsp-enable-imenu 4182 (lsp-feature? "textDocument/documentSymbol")) 4183 (lsp-enable-imenu)) 4184 4185 (when (and lsp-enable-indentation 4186 (lsp-feature? "textDocument/rangeFormatting")) 4187 (add-function :override (local 'indent-region-function) #'lsp-format-region)) 4188 4189 (when (and lsp-enable-symbol-highlighting 4190 (lsp-feature? "textDocument/documentHighlight")) 4191 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)) 4192 4193 (when (and lsp-enable-links 4194 (lsp-feature? "textDocument/documentLink")) 4195 (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t)) 4196 4197 (when (and lsp-inlay-hint-enable 4198 (lsp-feature? "textDocument/inlayHint")) 4199 (lsp-inlay-hints-mode)) 4200 4201 (when (and lsp-enable-dap-auto-configure 4202 (functionp 'dap-mode)) 4203 (dap-auto-configure-mode 1))) 4204 (run-hooks 'lsp-configure-hook))) 4205 4206 (defun lsp-unconfig-buffer () 4207 "Unconfigure LSP features for buffer." 4208 (lsp--remove-overlays 'lsp-color) 4209 4210 (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function) 4211 (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index) 4212 (setq-local imenu-menubar-modified-tick 0) 4213 (setq-local imenu--index-alist nil) 4214 (imenu--cleanup)) 4215 4216 (remove-function (local 'indent-region-function) #'lsp-format-region) 4217 4218 (remove-hook 'lsp-on-change-hook #'lsp--document-color t) 4219 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4220 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4221 4222 (when (and lsp-enable-dap-auto-configure 4223 (functionp 'dap-mode)) 4224 (dap-auto-configure-mode -1)) 4225 4226 (run-hooks 'lsp-unconfigure-hook)) 4227 4228 (defun lsp--buffer-content () 4229 (lsp-save-restriction-and-excursion 4230 (or (lsp-virtual-buffer-call :buffer-string) 4231 (buffer-substring-no-properties (point-min) 4232 (point-max))))) 4233 4234 (defun lsp--text-document-did-open () 4235 "`document/didOpen' event." 4236 (run-hooks 'lsp-before-open-hook) 4237 (when (and lsp-auto-touch-files 4238 (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri))))) 4239 (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri)) 4240 (save-buffer)) 4241 4242 (setq lsp--cur-version (or lsp--cur-version 0)) 4243 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 4244 (lsp-notify 4245 "textDocument/didOpen" 4246 (list :textDocument 4247 (list :uri (lsp--buffer-uri) 4248 :languageId (lsp-buffer-language) 4249 :version lsp--cur-version 4250 :text (lsp--buffer-content)))) 4251 4252 (lsp-managed-mode 1) 4253 4254 (run-hooks 'lsp-after-open-hook) 4255 (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client)))) 4256 (-some-> (lsp--client-after-open-fn client) 4257 (funcall)) 4258 (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client)) 4259 (intern-soft) 4260 (run-hooks)))) 4261 4262 (defun lsp--text-document-identifier () 4263 "Make TextDocumentIdentifier." 4264 (list :uri (lsp--buffer-uri))) 4265 4266 (defun lsp--versioned-text-document-identifier () 4267 "Make VersionedTextDocumentIdentifier." 4268 (plist-put (lsp--text-document-identifier) :version lsp--cur-version)) 4269 4270 (defun lsp--cur-line (&optional point) 4271 (1- (line-number-at-pos point))) 4272 4273 (defun lsp--cur-position () 4274 "Make a Position object for the current point." 4275 (or (lsp-virtual-buffer-call :cur-position) 4276 (lsp-save-restriction-and-excursion 4277 (list :line (lsp--cur-line) 4278 :character (- (point) (line-beginning-position)))))) 4279 4280 (defun lsp--point-to-position (point) 4281 "Convert POINT to Position." 4282 (lsp-save-restriction-and-excursion 4283 (goto-char point) 4284 (lsp--cur-position))) 4285 4286 (defun lsp--range (start end) 4287 "Make Range body from START and END." 4288 ;; make sure start and end are Position objects 4289 (list :start start :end end)) 4290 4291 (defun lsp--region-to-range (start end) 4292 "Make Range object for the current region." 4293 (lsp--range (lsp--point-to-position start) 4294 (lsp--point-to-position end))) 4295 4296 (defun lsp--region-or-line () 4297 "The active region or the current line." 4298 (if (use-region-p) 4299 (lsp--region-to-range (region-beginning) (region-end)) 4300 (lsp--region-to-range (line-beginning-position) (line-end-position)))) 4301 4302 (defun lsp--check-document-changes-version (document-changes) 4303 "Verify that DOCUMENT-CHANGES have the proper version." 4304 (unless (seq-every-p 4305 (-lambda ((&TextDocumentEdit :text-document)) 4306 (or 4307 (not text-document) 4308 (let* ((filename (-> text-document 4309 lsp:versioned-text-document-identifier-uri 4310 lsp--uri-to-path)) 4311 (version (lsp:versioned-text-document-identifier-version? text-document))) 4312 (with-current-buffer (find-file-noselect filename) 4313 (or (null version) (zerop version) (= -1 version) 4314 (equal version lsp--cur-version)))))) 4315 document-changes) 4316 (error "Document changes cannot be applied due to different document version"))) 4317 4318 (defun lsp--apply-workspace-edit (workspace-edit &optional operation) 4319 "Apply the WorkspaceEdit object WORKSPACE-EDIT. 4320 OPERATION is symbol representing the source of this text edit." 4321 (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit)) 4322 (if-let ((document-changes (seq-reverse document-changes?))) 4323 (progn 4324 (lsp--check-document-changes-version document-changes) 4325 (->> document-changes 4326 (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create"))) 4327 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4328 (->> document-changes 4329 (seq-filter (-lambda ((&CreateFile :kind)) 4330 (and (or (not kind) (equal kind "edit")) 4331 (not (equal kind "create"))))) 4332 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4333 (->> document-changes 4334 (seq-filter (-lambda ((&CreateFile :kind)) 4335 (and (not (or (not kind) (equal kind "edit"))) 4336 (not (equal kind "create"))))) 4337 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))) 4338 (lsp-map 4339 (lambda (uri text-edits) 4340 (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect) 4341 (lsp--apply-text-edits text-edits operation))) 4342 changes?)))) 4343 4344 (defmacro lsp-with-filename (file &rest body) 4345 "Execute BODY with FILE as a context. 4346 Need to handle the case when FILE indicates virtual buffer." 4347 (declare (indent 1) (debug t)) 4348 `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file))) 4349 (lsp-with-current-buffer lsp--virtual-buffer 4350 ,@body) 4351 ,@body)) 4352 4353 (defun lsp--apply-text-document-edit (edit &optional operation) 4354 "Apply the TextDocumentEdit object EDIT. 4355 OPERATION is symbol representing the source of this text edit. 4356 If the file is not being visited by any buffer, it is opened with 4357 `find-file-noselect'. 4358 Because lsp-mode does not store previous document versions, the edit is only 4359 applied if the version of the textDocument matches the version of the 4360 corresponding file. 4361 4362 interface TextDocumentEdit { 4363 textDocument: VersionedTextDocumentIdentifier; 4364 edits: TextEdit[]; 4365 }" 4366 (pcase (lsp:edit-kind edit) 4367 ("create" (-let* (((&CreateFile :uri :options?) edit) 4368 (file-name (lsp--uri-to-path uri))) 4369 (mkdir (f-dirname file-name) t) 4370 (f-touch file-name) 4371 (when (lsp:create-file-options-overwrite? options?) 4372 (f-write-text "" nil file-name)) 4373 (find-file-noselect file-name))) 4374 ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit)) 4375 (f-delete (lsp--uri-to-path uri) recursive?))) 4376 ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit) 4377 (old-file-name (lsp--uri-to-path old-uri)) 4378 (new-file-name (lsp--uri-to-path new-uri)) 4379 (buf (find-buffer-visiting old-file-name))) 4380 (when buf 4381 (lsp-with-current-buffer buf 4382 (save-buffer) 4383 (lsp--text-document-did-close))) 4384 (mkdir (f-dirname new-file-name) t) 4385 (rename-file old-file-name new-file-name overwrite?) 4386 (when buf 4387 (lsp-with-current-buffer buf 4388 (set-buffer-modified-p nil) 4389 (setq lsp-buffer-uri nil) 4390 (set-visited-file-name new-file-name) 4391 (lsp))))) 4392 (_ (let ((file-name (->> edit 4393 (lsp:text-document-edit-text-document) 4394 (lsp:versioned-text-document-identifier-uri) 4395 (lsp--uri-to-path)))) 4396 (lsp-with-current-buffer (find-buffer-visiting file-name) 4397 (lsp-with-filename file-name 4398 (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation))))))) 4399 4400 (lsp-defun lsp--position-compare ((&Position :line left-line 4401 :character left-character) 4402 (&Position :line right-line 4403 :character right-character)) 4404 "Return t if position LEFT is greater than RIGHT." 4405 (if (= left-line right-line) 4406 (> left-character right-character) 4407 (> left-line right-line))) 4408 4409 (lsp-defun lsp-point-in-range? (position (&Range :start :end)) 4410 "Returns if POINT is in RANGE." 4411 (not (or (lsp--position-compare start position) 4412 (lsp--position-compare position end)))) 4413 4414 (lsp-defun lsp--position-equal ((&Position :line left-line 4415 :character left-character) 4416 (&Position :line right-line 4417 :character right-character)) 4418 "Return whether LEFT and RIGHT positions are equal." 4419 (and (= left-line right-line) 4420 (= left-character right-character))) 4421 4422 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end)) 4423 (&TextEdit :range (&Range :start right-start :end right-end))) 4424 (if (lsp--position-equal left-start right-start) 4425 (lsp--position-compare left-end right-end) 4426 (lsp--position-compare left-start right-start))) 4427 4428 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text)) 4429 "Apply the edits described in the TextEdit object in TEXT-EDIT." 4430 (setq new-text (s-replace "\r" "" (or new-text ""))) 4431 (lsp:set-text-edit-new-text edit new-text) 4432 (goto-char start) 4433 (delete-region start end) 4434 (insert new-text)) 4435 4436 ;; WORKAROUND: typescript-language might send -1 when applying code actions. 4437 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582 4438 (lsp-defun lsp--fix-point ((point &as &Position :character :line)) 4439 (-doto point 4440 (lsp:set-position-line (max 0 line)) 4441 (lsp:set-position-character (max 0 character)))) 4442 4443 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as 4444 &TextEdit 4445 :range (&Range :start :end) 4446 :new-text)) 4447 "Apply the edits described in the TextEdit object in TEXT-EDIT. 4448 The method uses `replace-buffer-contents'." 4449 (setq new-text (s-replace "\r" "" (or new-text ""))) 4450 (lsp:set-text-edit-new-text edit new-text) 4451 (-let* ((source (current-buffer)) 4452 ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start) 4453 :end (lsp--fix-point end))))) 4454 (with-temp-buffer 4455 (insert new-text) 4456 (let ((temp (current-buffer))) 4457 (with-current-buffer source 4458 (save-excursion 4459 (save-restriction 4460 (narrow-to-region beg end) 4461 4462 ;; On emacs versions < 26.2, 4463 ;; `replace-buffer-contents' is buggy - it calls 4464 ;; change functions with invalid arguments - so we 4465 ;; manually call the change functions here. 4466 ;; 4467 ;; See emacs bugs #32237, #32278: 4468 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237 4469 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278 4470 (let ((inhibit-modification-hooks t) 4471 (length (- end beg))) 4472 (run-hook-with-args 'before-change-functions 4473 beg end) 4474 (replace-buffer-contents temp) 4475 (run-hook-with-args 'after-change-functions 4476 beg (+ beg (length new-text)) 4477 length))))))))) 4478 4479 (defun lsp--to-yasnippet-snippet (snippet) 4480 "Convert LSP SNIPPET to yasnippet snippet." 4481 ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it. 4482 (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`"))) 4483 (rx "\\" (backref 1)) 4484 snippet 4485 nil nil 1)) 4486 4487 (defvar-local lsp-enable-relative-indentation nil 4488 "Enable relative indentation when insert texts, snippets ... 4489 from language server.") 4490 4491 (defun lsp--expand-snippet (snippet &optional start end expand-env) 4492 "Wrapper of `yas-expand-snippet' with all of it arguments. 4493 The snippet will be convert to LSP style and indent according to 4494 LSP server result." 4495 (require 'yasnippet nil t) 4496 (let* ((inhibit-field-text-motion t) 4497 (yas-wrap-around-region nil) 4498 (yas-indent-line 'none) 4499 (yas-also-auto-indent-first-line nil)) 4500 (yas-expand-snippet 4501 (lsp--to-yasnippet-snippet snippet) 4502 start end expand-env))) 4503 4504 (defun lsp--indent-lines (start end &optional insert-text-mode?) 4505 "Indent from START to END based on INSERT-TEXT-MODE? value. 4506 - When INSERT-TEXT-MODE? is provided 4507 - if it's `lsp/insert-text-mode-as-it', do no editor indentation. 4508 - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading 4509 whitespaces to match the line where text is inserted. 4510 - When it's not provided, using `indent-line-function' for each line." 4511 (save-excursion 4512 (goto-char end) 4513 (let* ((end-line (line-number-at-pos)) 4514 (offset (save-excursion 4515 (goto-char start) 4516 (current-indentation))) 4517 (indent-line-function 4518 (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it) 4519 #'ignore) 4520 ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation) 4521 lsp-enable-relative-indentation 4522 ;; Indenting snippets is extremely slow in `org-mode' buffers 4523 ;; since it has to calculate indentation based on SRC block 4524 ;; position. Thus we use relative indentation as default. 4525 (derived-mode-p 'org-mode)) 4526 (lambda () (save-excursion 4527 (beginning-of-line) 4528 (indent-to-column offset)))) 4529 (t indent-line-function)))) 4530 (goto-char start) 4531 (forward-line) 4532 (while (and (not (eobp)) 4533 (<= (line-number-at-pos) end-line)) 4534 (funcall indent-line-function) 4535 (forward-line))))) 4536 4537 (defun lsp--apply-text-edits (edits &optional operation) 4538 "Apply the EDITS described in the TextEdit[] object. 4539 OPERATION is symbol representing the source of this text edit." 4540 (unless (seq-empty-p edits) 4541 (atomic-change-group 4542 (run-hooks 'lsp-before-apply-edits-hook) 4543 (let* ((change-group (prepare-change-group)) 4544 (howmany (length edits)) 4545 (message (format "Applying %s edits to `%s' ..." howmany (current-buffer))) 4546 (_ (lsp--info message)) 4547 (reporter (make-progress-reporter message 0 howmany)) 4548 (done 0) 4549 (apply-edit (if (not lsp--virtual-buffer) 4550 #'lsp--apply-text-edit-replace-buffer-contents 4551 #'lsp--apply-text-edit))) 4552 (unwind-protect 4553 (->> edits 4554 ;; We sort text edits so as to apply edits that modify latter 4555 ;; parts of the document first. Furthermore, because the LSP 4556 ;; spec dictates that: "If multiple inserts have the same 4557 ;; position, the order in the array defines which edit to 4558 ;; apply first." We reverse the initial list and sort stably 4559 ;; to make sure the order among edits with the same position 4560 ;; is preserved. 4561 (nreverse) 4562 (seq-sort #'lsp--text-edit-sort-predicate) 4563 (mapc (lambda (edit) 4564 (progress-reporter-update reporter (cl-incf done)) 4565 (funcall apply-edit edit) 4566 (when (lsp:snippet-text-edit-insert-text-format? edit) 4567 (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start) 4568 :insert-text-format? :new-text) edit) 4569 (when (eq insert-text-format? lsp/insert-text-format-snippet) 4570 ;; No `save-excursion' needed since expand snippet will change point anyway 4571 (goto-char (+ start (length new-text))) 4572 (lsp--indent-lines start (point)) 4573 (lsp--expand-snippet new-text start (point))))) 4574 (run-hook-with-args 'lsp-after-apply-edits-hook operation)))) 4575 (undo-amalgamate-change-group change-group) 4576 (progress-reporter-done reporter)))))) 4577 4578 (defun lsp--create-apply-text-edits-handlers () 4579 "Create (handler cleanup-fn) for applying text edits in async request. 4580 Only works when mode is `tick or `alive." 4581 (let* (first-edited 4582 (func (lambda (start &rest _) 4583 (setq first-edited (if first-edited 4584 (min start first-edited) 4585 start))))) 4586 (add-hook 'before-change-functions func nil t) 4587 (list 4588 (lambda (edits) 4589 (if (and first-edited 4590 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end))) 4591 ;; Text edit region is overlapped 4592 (> end first-edited)) 4593 edits)) 4594 (lsp--warn "TextEdits will not be applied since document has been modified before of them.") 4595 (lsp--apply-text-edits edits 'completion-cleanup))) 4596 (lambda () 4597 (remove-hook 'before-change-functions func t))))) 4598 4599 (defun lsp--capability (cap &optional capabilities) 4600 "Get the value of capability CAP. If CAPABILITIES is non-nil, use them instead." 4601 (when (stringp cap) 4602 (setq cap (intern (concat ":" cap)))) 4603 4604 (lsp-get (or capabilities 4605 (lsp--server-capabilities)) 4606 cap)) 4607 4608 (defun lsp--registered-capability (method) 4609 "Check whether there is workspace providing METHOD." 4610 (->> (lsp-workspaces) 4611 (--keep (seq-find (lambda (reg) 4612 (equal (lsp--registered-capability-method reg) method)) 4613 (lsp--workspace-registered-server-capabilities it))) 4614 cl-first)) 4615 4616 (defun lsp--capability-for-method (method) 4617 "Get the value of capability for METHOD." 4618 (-let* ((reqs (cdr (assoc method lsp-method-requirements))) 4619 ((&plist :capability) reqs)) 4620 (or (and capability (lsp--capability capability)) 4621 (-some-> (lsp--registered-capability method) 4622 (lsp--registered-capability-options))))) 4623 4624 (defvar-local lsp--before-change-vals nil 4625 "Store the positions from the `lsp-before-change' function call, for 4626 validation and use in the `lsp-on-change' function.") 4627 4628 (defun lsp--text-document-content-change-event (start end length) 4629 "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH." 4630 ;; So (47 54 0) means add 7 chars starting at pos 47 4631 ;; must become 4632 ;; {"range":{"start":{"line":5,"character":6} 4633 ;; ,"end" :{"line":5,"character":6}} 4634 ;; ,"rangeLength":0 4635 ;; ,"text":"\nbb = 5"} 4636 ;; 4637 ;; And (47 47 7) means delete 7 chars starting at pos 47 4638 ;; must become 4639 ;; {"range":{"start":{"line":6,"character":0} 4640 ;; ,"end" :{"line":7,"character":0}} 4641 ;; ,"rangeLength":7 4642 ;; ,"text":""} 4643 ;; 4644 ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with 4645 ;; 13 chars. So it must become 4646 ;; {"range":{"start":{"line":5,"character":8} 4647 ;; ,"end" :{"line":5,"character":11}} 4648 ;; ,"rangeLength":3 4649 ;; ,"text":"new-chars-xxx"} 4650 ;; 4651 4652 ;; Adding text: 4653 ;; lsp-before-change:(start,end)=(33,33) 4654 ;; lsp-on-change:(start,end,length)=(33,34,0) 4655 ;; 4656 ;; Changing text: 4657 ;; lsp-before-change:(start,end)=(208,211) 4658 ;; lsp-on-change:(start,end,length)=(208,221,3) 4659 ;; 4660 ;; Deleting text: 4661 ;; lsp-before-change:(start,end)=(19,27) 4662 ;; lsp-on-change:(start,end,length)=(19,19,8) 4663 (if (zerop length) 4664 ;; Adding something only, work from start only 4665 `( :range ,(lsp--range 4666 (lsp--point-to-position start) 4667 (lsp--point-to-position start)) 4668 :rangeLength 0 4669 :text ,(buffer-substring-no-properties start end)) 4670 4671 (if (eq start end) 4672 ;; Deleting something only 4673 (if (lsp--bracketed-change-p start length) 4674 ;; The before-change value is bracketed, use it 4675 `( :range ,(lsp--range 4676 (lsp--point-to-position start) 4677 (plist-get lsp--before-change-vals :end-pos)) 4678 :rangeLength ,length 4679 :text "") 4680 ;; If the change is not bracketed, send a full change event instead. 4681 (lsp--full-change-event)) 4682 4683 ;; Deleting some things, adding others 4684 (if (lsp--bracketed-change-p start length) 4685 ;; The before-change value is valid, use it 4686 `( :range ,(lsp--range 4687 (lsp--point-to-position start) 4688 (plist-get lsp--before-change-vals :end-pos)) 4689 :rangeLength ,length 4690 :text ,(buffer-substring-no-properties start end)) 4691 (lsp--full-change-event))))) 4692 4693 (defun lsp--bracketed-change-p (start length) 4694 "If the before and after positions are the same, and the length 4695 is the size of the start range, we are probably good." 4696 (-let [(&plist :end before-end :start before-start) lsp--before-change-vals] 4697 (and (eq start before-start) 4698 (eq length (- before-end before-start))))) 4699 4700 (defun lsp--full-change-event () 4701 `(:text ,(lsp--buffer-content))) 4702 4703 (defun lsp-before-change (start end) 4704 "Executed before a file is changed. 4705 Added to `before-change-functions'." 4706 ;; Note: 4707 ;; 4708 ;; This variable holds a list of functions to call when Emacs is about to 4709 ;; modify a buffer. Each function gets two arguments, the beginning and end of 4710 ;; the region that is about to change, represented as integers. The buffer 4711 ;; that is about to change is always the current buffer when the function is 4712 ;; called. 4713 ;; 4714 ;; WARNING: 4715 ;; 4716 ;; Do not expect the before-change hooks and the after-change hooks be called 4717 ;; in balanced pairs around each buffer change. Also don't expect the 4718 ;; before-change hooks to be called for every chunk of text Emacs is about to 4719 ;; delete. These hooks are provided on the assumption that Lisp programs will 4720 ;; use either before- or the after-change hooks, but not both, and the 4721 ;; boundaries of the region where the changes happen might include more than 4722 ;; just the actual changed text, or even lump together several changes done 4723 ;; piecemeal. 4724 (save-match-data 4725 (lsp-save-restriction-and-excursion 4726 (setq lsp--before-change-vals 4727 (list :start start 4728 :end end 4729 :end-pos (lsp--point-to-position end)))))) 4730 4731 (defun lsp--flush-delayed-changes () 4732 (let ((inhibit-quit t)) 4733 (when lsp--delay-timer 4734 (cancel-timer lsp--delay-timer)) 4735 (mapc (-lambda ((workspace buffer document change)) 4736 (with-current-buffer buffer 4737 (with-lsp-workspace workspace 4738 (lsp-notify "textDocument/didChange" 4739 (list :textDocument document 4740 :contentChanges (vector change)))))) 4741 (prog1 (nreverse lsp--delayed-requests) 4742 (setq lsp--delayed-requests nil))))) 4743 4744 (defun lsp--workspace-sync-method (workspace) 4745 (let ((sync (-> workspace 4746 (lsp--workspace-server-capabilities) 4747 (lsp:server-capabilities-text-document-sync?)))) 4748 (if (lsp-text-document-sync-options? sync) 4749 (lsp:text-document-sync-options-change? sync) 4750 sync))) 4751 4752 (defun lsp-on-change (start end length &optional content-change-event-fn) 4753 "Executed when a file is changed. 4754 Added to `after-change-functions'." 4755 ;; Note: 4756 ;; 4757 ;; Each function receives three arguments: the beginning and end of the region 4758 ;; just changed, and the length of the text that existed before the change. 4759 ;; All three arguments are integers. The buffer that has been changed is 4760 ;; always the current buffer when the function is called. 4761 ;; 4762 ;; The length of the old text is the difference between the buffer positions 4763 ;; before and after that text as it was before the change. As for the 4764 ;; changed text, its length is simply the difference between the first two 4765 ;; arguments. 4766 ;; 4767 ;; So (47 54 0) means add 7 chars starting at pos 47 4768 ;; So (47 47 7) means delete 7 chars starting at pos 47 4769 (save-match-data 4770 (let ((inhibit-quit t) 4771 ;; make sure that `lsp-on-change' is called in multi-workspace context 4772 ;; see #2901 4773 lsp--cur-workspace) 4774 ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done 4775 ;; by auto-revert-mode) will cause this handler to get called with a nil 4776 ;; buffer-file-name. We need the buffer-file-name to send notifications; 4777 ;; so we skip handling revert-buffer-caused changes and instead handle 4778 ;; reverts separately in lsp-on-revert 4779 (when (not revert-buffer-in-progress-p) 4780 (cl-incf lsp--cur-version) 4781 (mapc 4782 (lambda (workspace) 4783 (pcase (or lsp-document-sync-method 4784 (lsp--workspace-sync-method workspace)) 4785 (1 4786 (if lsp-debounce-full-sync-notifications 4787 (setq lsp--delayed-requests 4788 (->> lsp--delayed-requests 4789 (-remove (-lambda ((_ buffer)) 4790 (equal (current-buffer) buffer))) 4791 (cons (list workspace 4792 (current-buffer) 4793 (lsp--versioned-text-document-identifier) 4794 (lsp--full-change-event))))) 4795 (with-lsp-workspace workspace 4796 (lsp-notify "textDocument/didChange" 4797 (list :contentChanges (vector (lsp--full-change-event)) 4798 :textDocument (lsp--versioned-text-document-identifier)))))) 4799 (2 4800 (with-lsp-workspace workspace 4801 (lsp-notify 4802 "textDocument/didChange" 4803 (list :textDocument (lsp--versioned-text-document-identifier) 4804 :contentChanges (vector 4805 (if content-change-event-fn 4806 (funcall content-change-event-fn start end length) 4807 (lsp--text-document-content-change-event 4808 start end length))))))))) 4809 (lsp-workspaces)) 4810 (when lsp--delay-timer (cancel-timer lsp--delay-timer)) 4811 (setq lsp--delay-timer (run-with-idle-timer 4812 lsp-debounce-full-sync-notifications-interval 4813 nil 4814 #'lsp--flush-delayed-changes)) 4815 ;; force cleanup overlays after each change 4816 (lsp--remove-overlays 'lsp-highlight) 4817 (lsp--after-change (current-buffer)) 4818 (setq lsp--signature-last-index nil 4819 lsp--signature-last nil) 4820 ;; cleanup diagnostics 4821 (when lsp-diagnostic-clean-after-change 4822 (lsp-foreach-workspace 4823 (-let [diagnostics (lsp--workspace-diagnostics lsp--cur-workspace)] 4824 (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics)))))))) 4825 4826 4827 4828 ;; facilities for on change hooks. We do not want to make lsp calls on each 4829 ;; change event so we add debounce to avoid flooding the server with events. 4830 ;; Additionally, we want to have a mechanism for stopping the server calls in 4831 ;; particular cases like, e. g. when performing completion. 4832 4833 (defvar lsp-inhibit-lsp-hooks nil 4834 "Flag to control.") 4835 4836 (defcustom lsp-on-change-hook nil 4837 "Hooks to run when buffer has changed." 4838 :type 'hook 4839 :group 'lsp-mode) 4840 4841 (defcustom lsp-idle-delay 0.500 4842 "Debounce interval for `after-change-functions'." 4843 :type 'number 4844 :group 'lsp-mode) 4845 4846 (defcustom lsp-on-idle-hook nil 4847 "Hooks to run after `lsp-idle-delay'." 4848 :type 'hook 4849 :group 'lsp-mode) 4850 4851 (defun lsp--idle-reschedule (buffer) 4852 (when lsp--on-idle-timer 4853 (cancel-timer lsp--on-idle-timer)) 4854 4855 (setq lsp--on-idle-timer (run-with-idle-timer 4856 lsp-idle-delay 4857 nil 4858 #'lsp--on-idle 4859 buffer))) 4860 4861 (defun lsp--post-command () 4862 (lsp--cleanup-highlights-if-needed) 4863 (lsp--idle-reschedule (current-buffer))) 4864 4865 (defun lsp--on-idle (buffer) 4866 "Start post command loop." 4867 (when (and (buffer-live-p buffer) 4868 (equal buffer (current-buffer)) 4869 (not lsp-inhibit-lsp-hooks) 4870 lsp-managed-mode) 4871 (run-hooks 'lsp-on-idle-hook))) 4872 4873 (defun lsp--on-change-debounce (buffer) 4874 (when (and (buffer-live-p buffer) 4875 (equal buffer (current-buffer)) 4876 (not lsp-inhibit-lsp-hooks) 4877 lsp-managed-mode) 4878 (run-hooks 'lsp-on-change-hook))) 4879 4880 (defun lsp--after-change (buffer) 4881 (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled) 4882 (lsp--semantic-tokens-refresh-if-enabled buffer)) 4883 (when lsp--on-change-timer 4884 (cancel-timer lsp--on-change-timer)) 4885 (setq lsp--on-change-timer (run-with-idle-timer 4886 lsp-idle-delay 4887 nil 4888 #'lsp--on-change-debounce 4889 buffer)) 4890 (lsp--idle-reschedule buffer)) 4891 4892 4893 (defcustom lsp-trim-trailing-whitespace t 4894 "Trim trailing whitespace on a line." 4895 :group 'lsp-mode 4896 :type 'boolean) 4897 4898 (defcustom lsp-insert-final-newline t 4899 "Insert a newline character at the end of the file if one does not exist." 4900 :group 'lsp-mode 4901 :type 'boolean) 4902 4903 (defcustom lsp-trim-final-newlines t 4904 "Trim all newlines after the final newline at the end of the file." 4905 :group 'lsp-mode 4906 :type 'boolean) 4907 4908 4909 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters) 4910 "Self insert handling. 4911 Applies on type formatting." 4912 (let ((ch last-command-event)) 4913 (when (or (eq (string-to-char first-trigger-characters) ch) 4914 (cl-find ch more-trigger-characters :key #'string-to-char)) 4915 (lsp-request-async "textDocument/onTypeFormatting" 4916 (lsp-make-document-on-type-formatting-params 4917 :text-document (lsp--text-document-identifier) 4918 :options (lsp-make-formatting-options 4919 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 4920 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 4921 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 4922 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 4923 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)) 4924 :ch (char-to-string ch) 4925 :position (lsp--cur-position)) 4926 (lambda (data) (lsp--apply-text-edits data 'format)) 4927 :mode 'tick)))) 4928 4929 4930 ;; links 4931 (defun lsp--document-links () 4932 (when (lsp-feature? "textDocument/documentLink") 4933 (lsp-request-async 4934 "textDocument/documentLink" 4935 `(:textDocument ,(lsp--text-document-identifier)) 4936 (lambda (links) 4937 (lsp--remove-overlays 'lsp-link) 4938 (seq-do 4939 (-lambda ((link &as &DocumentLink :range (&Range :start :end))) 4940 (-doto (make-button (lsp--position-to-point start) 4941 (lsp--position-to-point end) 4942 'action (lsp--document-link-keymap link) 4943 'keymap (let ((map (make-sparse-keymap))) 4944 (define-key map [M-return] 'push-button) 4945 (define-key map [mouse-2] 'push-button) 4946 map) 4947 'help-echo "mouse-2, M-RET: Visit this link") 4948 (overlay-put 'lsp-link t))) 4949 links)) 4950 :mode 'unchanged))) 4951 4952 (defun lsp--document-link-handle-target (url) 4953 (let* ((parsed-url (url-generic-parse-url (url-unhex-string url))) 4954 (type (url-type parsed-url))) 4955 (pcase type 4956 ("file" 4957 (xref-push-marker-stack) 4958 (find-file (lsp--uri-to-path url)) 4959 (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url)) 4960 (goto-char (lsp--position-to-point 4961 (lsp-make-position :character (1- (string-to-number column)) 4962 :line (1- (string-to-number line))))))) 4963 ((or "http" "https") (browse-url url)) 4964 (type (if-let ((handler (lsp--get-uri-handler type))) 4965 (funcall handler url) 4966 (signal 'lsp-file-scheme-not-supported (list url))))))) 4967 4968 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?)) 4969 (if target? 4970 (lambda (_) 4971 (interactive) 4972 (lsp--document-link-handle-target target?)) 4973 (lambda (_) 4974 (interactive) 4975 (when (lsp:document-link-registration-options-resolve-provider? 4976 (lsp--capability-for-method "textDocument/documentLink")) 4977 (lsp-request-async 4978 "documentLink/resolve" 4979 link 4980 (-lambda ((&DocumentLink :target?)) 4981 (lsp--document-link-handle-target target?))))))) 4982 4983 4984 4985 (defcustom lsp-warn-no-matched-clients t 4986 "Whether to show messages when there are no supported clients." 4987 :group 'lsp-mode 4988 :type 'boolean) 4989 4990 (defun lsp-buffer-language--configured-id () 4991 "Return nil when not registered." 4992 (->> lsp-language-id-configuration 4993 (-first 4994 (-lambda ((mode-or-pattern . language)) 4995 (cond 4996 ((and (stringp mode-or-pattern) 4997 (s-matches? mode-or-pattern (buffer-file-name))) 4998 language) 4999 ((eq mode-or-pattern major-mode) language)))) 5000 cl-rest)) 5001 5002 (defvar-local lsp--buffer-language nil 5003 "Locally cached returned value of `lsp-buffer-language'.") 5004 5005 (defun lsp-buffer-language () 5006 "Get language corresponding current buffer." 5007 (or lsp--buffer-language 5008 (let* ((configured-language (lsp-buffer-language--configured-id))) 5009 (setq lsp--buffer-language 5010 (or configured-language 5011 ;; ensure non-nil 5012 (string-remove-suffix "-mode" (symbol-name major-mode)))) 5013 (when (and lsp-warn-no-matched-clients 5014 (null configured-language)) 5015 (lsp-warn "Unable to calculate the languageId for buffer `%s'. \ 5016 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s" 5017 (buffer-name) 5018 major-mode)) 5019 lsp--buffer-language))) 5020 5021 (defun lsp-activate-on (&rest languages) 5022 "Returns language activation function. 5023 The function will return t when the `lsp-buffer-language' returns 5024 one of the LANGUAGES." 5025 (lambda (_file-name _mode) 5026 (-contains? languages (lsp-buffer-language)))) 5027 5028 (defun lsp-workspace-root (&optional path) 5029 "Find the workspace root for the current file or PATH." 5030 (-when-let* ((file-name (or path (buffer-file-name))) 5031 (file-name (lsp-f-canonical file-name))) 5032 (->> (lsp-session) 5033 (lsp-session-folders) 5034 (--filter (and (lsp--files-same-host it file-name) 5035 (or (lsp-f-ancestor-of? it file-name) 5036 (equal it file-name)))) 5037 (--max-by (> (length it) (length other)))))) 5038 5039 (defun lsp-on-revert () 5040 "Executed when a file is reverted. 5041 Added to `after-revert-hook'." 5042 (let ((n (buffer-size)) 5043 (revert-buffer-in-progress-p nil)) 5044 (lsp-on-change 0 n n))) 5045 5046 (defun lsp--text-document-did-close (&optional keep-workspace-alive) 5047 "Executed when the file is closed, added to `kill-buffer-hook'. 5048 5049 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace 5050 if it's closing the last buffer in the workspace." 5051 (lsp-foreach-workspace 5052 (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 5053 (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S" 5054 (lsp-notify "textDocument/didClose" 5055 `(:textDocument ,(lsp--text-document-identifier)))) 5056 (when (and (not lsp-keep-workspace-alive) 5057 (not keep-workspace-alive) 5058 (not (lsp--workspace-buffers lsp--cur-workspace))) 5059 (lsp--shutdown-workspace)))) 5060 5061 (defun lsp--will-save-text-document-params (reason) 5062 (list :textDocument (lsp--text-document-identifier) 5063 :reason reason)) 5064 5065 (defun lsp--before-save () 5066 "Before save handler." 5067 (with-demoted-errors "Error in ‘lsp--before-save’: %S" 5068 (let ((params (lsp--will-save-text-document-params 1))) 5069 (when (lsp--send-will-save-p) 5070 (lsp-notify "textDocument/willSave" params)) 5071 (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits) 5072 (let ((lsp-response-timeout 0.1)) 5073 (condition-case nil 5074 (lsp--apply-text-edits 5075 (lsp-request "textDocument/willSaveWaitUntil" 5076 params) 5077 'before-save) 5078 (error))))))) 5079 5080 (defun lsp--on-auto-save () 5081 "Handler for auto-save." 5082 (when (lsp--send-will-save-p) 5083 (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S" 5084 (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2))))) 5085 5086 (defun lsp--text-document-did-save () 5087 "Executed when the file is closed, added to `after-save-hook''." 5088 (when (lsp--send-did-save-p) 5089 (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’" 5090 (lsp-notify "textDocument/didSave" 5091 `( :textDocument ,(lsp--versioned-text-document-identifier) 5092 ,@(when (lsp--save-include-text-p) 5093 (list :text (lsp--buffer-content)))))))) 5094 5095 (defun lsp--text-document-position-params (&optional identifier position) 5096 "Make TextDocumentPositionParams for the current point in the current document. 5097 If IDENTIFIER and POSITION are non-nil, they will be used as the document 5098 identifier and the position respectively." 5099 (list :textDocument (or identifier (lsp--text-document-identifier)) 5100 :position (or position (lsp--cur-position)))) 5101 5102 (defun lsp--get-buffer-diagnostics () 5103 "Return buffer diagnostics." 5104 (gethash (or 5105 (plist-get lsp--virtual-buffer :buffer-file-name) 5106 (lsp--fix-path-casing (buffer-file-name))) 5107 (lsp-diagnostics t))) 5108 5109 (defun lsp-cur-line-diagnostics () 5110 "Return any diagnostics that apply to the current line." 5111 (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)] 5112 (cl-coerce (-filter 5113 (-lambda ((&Diagnostic :range (&Range :start (&Position :line)))) 5114 (and (>= line start) (<= line end))) 5115 (lsp--get-buffer-diagnostics)) 5116 'vector))) 5117 5118 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end) 5119 (right &as &Range :start right-start :end right-end)) 5120 (or (lsp-point-in-range? right-start left) 5121 (lsp-point-in-range? right-end left) 5122 (lsp-point-in-range? left-start right) 5123 (lsp-point-in-range? left-end right))) 5124 5125 (defun lsp-make-position-1 (position) 5126 (lsp-make-position :line (plist-get position :line) 5127 :character (plist-get position :character))) 5128 5129 (defun lsp-cur-possition-diagnostics () 5130 "Return any diagnostics that apply to the current line." 5131 (-let* ((start (if (use-region-p) (region-beginning) (point))) 5132 (end (if (use-region-p) (region-end) (point))) 5133 (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start)) 5134 :end (lsp-make-position-1 (lsp-point-to-position end))))) 5135 (->> (lsp--get-buffer-diagnostics) 5136 (-filter 5137 (-lambda ((&Diagnostic :range)) 5138 (lsp-range-overlapping? range current-range))) 5139 (apply 'vector)))) 5140 5141 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics) 5142 5143 (defun lsp--extract-line-from-buffer (pos) 5144 "Return the line pointed to by POS (a Position object) in the current buffer." 5145 (let* ((point (lsp--position-to-point pos)) 5146 (inhibit-field-text-motion t)) 5147 (save-excursion 5148 (goto-char point) 5149 (buffer-substring (line-beginning-position) (line-end-position))))) 5150 5151 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line) 5152 :end (end &as &Position :character end-char))) 5153 "Return a xref-item from a RANGE in FILENAME." 5154 (let* ((line (lsp--extract-line-from-buffer start)) 5155 (len (length line))) 5156 (add-face-text-property (max (min start-char len) 0) 5157 (max (min end-char len) 0) 5158 'xref-match t line) 5159 ;; LINE is nil when FILENAME is not being current visited by any buffer. 5160 (xref-make (or line filename) 5161 (xref-make-file-location 5162 filename 5163 (lsp-translate-line (1+ start-line)) 5164 (lsp-translate-column start-char))))) 5165 5166 (defun lsp--location-uri (loc) 5167 (if (lsp-location? loc) 5168 (lsp:location-uri loc) 5169 (lsp:location-link-target-uri loc))) 5170 5171 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start))) 5172 "Go to location." 5173 (let ((path (lsp--uri-to-path uri))) 5174 (if (f-exists? path) 5175 (with-current-buffer (find-file path) 5176 (goto-char (lsp--position-to-point start))) 5177 (error "There is no file %s" path)))) 5178 5179 (defun lsp--location-range (loc) 5180 (if (lsp-location? loc) 5181 (lsp:location-range loc) 5182 (lsp:location-link-target-selection-range loc))) 5183 5184 (defun lsp--locations-to-xref-items (locations) 5185 "Return a list of `xref-item' given LOCATIONS, which can be of 5186 type Location, LocationLink, Location[] or LocationLink[]." 5187 (setq locations 5188 (pcase locations 5189 ((seq (or (Location) 5190 (LocationLink))) 5191 (append locations nil)) 5192 ((or (Location) 5193 (LocationLink)) 5194 (list locations)))) 5195 5196 (cl-labels ((get-xrefs-in-file 5197 (file-locs) 5198 (-let [(filename . matches) file-locs] 5199 (condition-case err 5200 (let ((visiting (find-buffer-visiting filename)) 5201 (fn (lambda (loc) 5202 (lsp-with-filename filename 5203 (lsp--xref-make-item filename 5204 (lsp--location-range loc)))))) 5205 (if visiting 5206 (with-current-buffer visiting 5207 (seq-map fn matches)) 5208 (when (file-readable-p filename) 5209 (with-temp-buffer 5210 (insert-file-contents-literally filename) 5211 (seq-map fn matches))))) 5212 (error (lsp-warn "Failed to process xref entry for filename '%s': %s" 5213 filename (error-message-string err))) 5214 (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s" 5215 filename (error-message-string err))))))) 5216 5217 (->> locations 5218 (seq-sort #'lsp--location-before-p) 5219 (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri)) 5220 (seq-map #'get-xrefs-in-file) 5221 (apply #'nconc)))) 5222 5223 (defun lsp--location-before-p (left right) 5224 "Sort first by file, then by line, then by column." 5225 (let ((left-uri (lsp--location-uri left)) 5226 (right-uri (lsp--location-uri right))) 5227 (if (not (string= left-uri right-uri)) 5228 (string< left-uri right-uri) 5229 (-let (((&Range :start left-start) (lsp--location-range left)) 5230 ((&Range :start right-start) (lsp--location-range right))) 5231 (lsp--position-compare right-start left-start))))) 5232 5233 (defun lsp--make-reference-params (&optional td-position exclude-declaration) 5234 "Make a ReferenceParam object. 5235 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead. 5236 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." 5237 (let ((json-false :json-false)) 5238 (plist-put (or td-position (lsp--text-document-position-params)) 5239 :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration)))))) 5240 5241 (defun lsp--cancel-request (id) 5242 "Cancel request with ID in all workspaces." 5243 (lsp-foreach-workspace 5244 (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id)) 5245 (lsp-notify "$/cancelRequest" `(:id ,id)))) 5246 5247 (defvar-local lsp--hover-saved-bounds nil) 5248 5249 (defun lsp-eldoc-function (cb &rest _ignored) 5250 "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')." 5251 (if (and lsp--hover-saved-bounds 5252 (lsp--point-in-bounds-p lsp--hover-saved-bounds)) 5253 lsp--eldoc-saved-message 5254 (setq lsp--hover-saved-bounds nil 5255 lsp--eldoc-saved-message nil) 5256 (if (looking-at-p "[[:space:]\n]") 5257 (setq lsp--eldoc-saved-message nil) ; And returns nil. 5258 (when (and lsp-eldoc-enable-hover (lsp--capability :hoverProvider)) 5259 (lsp-request-async 5260 "textDocument/hover" 5261 (lsp--text-document-position-params) 5262 (-lambda ((hover &as &Hover? :range? :contents)) 5263 (setq lsp--hover-saved-bounds (when range? 5264 (lsp--range-to-region range?))) 5265 (funcall cb (setq lsp--eldoc-saved-message 5266 (when contents 5267 (lsp--render-on-hover-content 5268 contents 5269 lsp-eldoc-render-all))))) 5270 :error-handler #'ignore 5271 :mode 'tick 5272 :cancel-token :eldoc-hover))))) 5273 5274 (defun lsp--point-on-highlight? () 5275 (-some? (lambda (overlay) 5276 (overlay-get overlay 'lsp-highlight)) 5277 (overlays-at (point)))) 5278 5279 (defun lsp--cleanup-highlights-if-needed () 5280 (when (and lsp-enable-symbol-highlighting 5281 lsp--have-document-highlights 5282 (not (lsp--point-on-highlight?))) 5283 (lsp--remove-overlays 'lsp-highlight) 5284 (setq lsp--have-document-highlights nil) 5285 (lsp-cancel-request-by-token :highlights))) 5286 5287 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil 5288 "The bounds of the symbol from which `lsp--document-highlight' 5289 most recently requested highlights.") 5290 5291 (defun lsp--document-highlight () 5292 (when (lsp-feature? "textDocument/documentHighlight") 5293 (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol))) 5294 (unless (or (looking-at-p "[[:space:]\n]") 5295 (not lsp-enable-symbol-highlighting) 5296 (and lsp--have-document-highlights 5297 curr-sym-bounds 5298 (equal curr-sym-bounds 5299 lsp--symbol-bounds-of-last-highlight-invocation))) 5300 (setq lsp--symbol-bounds-of-last-highlight-invocation 5301 curr-sym-bounds) 5302 (lsp-request-async "textDocument/documentHighlight" 5303 (lsp--text-document-position-params) 5304 #'lsp--document-highlight-callback 5305 :mode 'tick 5306 :cancel-token :highlights))))) 5307 5308 (defun lsp--help-open-link (&rest _) 5309 "Open markdown link at point via mouse or keyboard." 5310 (interactive "P") 5311 (let ((buffer-list-update-hook nil)) 5312 (-let [(buffer point) (if-let* ((valid (and (listp last-input-event) 5313 (eq (car last-input-event) 'mouse-2))) 5314 (event (cadr last-input-event)) 5315 (win (posn-window event)) 5316 (buffer (window-buffer win))) 5317 `(,buffer ,(posn-point event)) 5318 `(,(current-buffer) ,(point)))] 5319 (with-current-buffer buffer 5320 (when-let* ((face (get-text-property point 'face)) 5321 (url (or (and (eq face 'markdown-link-face) 5322 (get-text-property point 'help-echo)) 5323 (and (memq face '(markdown-url-face markdown-plain-url-face)) 5324 (nth 3 (markdown-link-at-pos point)))))) 5325 (lsp--document-link-handle-target url)))))) 5326 5327 (defvar lsp-help-mode-map 5328 (-doto (make-sparse-keymap) 5329 (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link)) 5330 "Keymap for `lsp-help-mode'.") 5331 5332 (define-derived-mode lsp-help-mode help-mode "LspHelp" 5333 "Major mode for displaying lsp help.") 5334 5335 (defun lsp-describe-thing-at-point () 5336 "Display the type signature and documentation of the thing at point." 5337 (interactive) 5338 (let ((contents (-some->> (lsp--text-document-position-params) 5339 (lsp--make-request "textDocument/hover") 5340 (lsp--send-request) 5341 (lsp:hover-contents)))) 5342 (if (and contents (not (equal contents ""))) 5343 (let ((lsp-help-buf-name "*lsp-help*")) 5344 (with-current-buffer (get-buffer-create lsp-help-buf-name) 5345 (delay-mode-hooks 5346 (lsp-help-mode) 5347 (with-help-window lsp-help-buf-name 5348 (insert (string-trim-right (lsp--render-on-hover-content contents t))))) 5349 (run-mode-hooks))) 5350 (lsp--info "No content at point.")))) 5351 5352 (defun lsp--point-in-bounds-p (bounds) 5353 "Return whether the current point is within BOUNDS." 5354 (and (<= (car bounds) (point)) (< (point) (cdr bounds)))) 5355 5356 (defun lsp-get-renderer (language) 5357 "Get renderer for LANGUAGE." 5358 (lambda (str) 5359 (lsp--render-string str language))) 5360 5361 (defun lsp--setup-markdown (mode) 5362 "Setup the ‘markdown-mode’ in the frame. 5363 MODE is the mode used in the parent frame." 5364 (make-local-variable 'markdown-code-lang-modes) 5365 (dolist (mark (alist-get mode lsp-custom-markup-modes)) 5366 (add-to-list 'markdown-code-lang-modes (cons mark mode))) 5367 (setq-local markdown-fontify-code-blocks-natively t) 5368 (setq-local markdown-fontify-code-block-default-mode mode) 5369 (setq-local markdown-hide-markup t) 5370 5371 ;; Render some common HTML entities. 5372 ;; This should really happen in markdown-mode instead, 5373 ;; but it doesn't, so we do it here for now. 5374 (setq prettify-symbols-alist 5375 (cl-loop for i from 0 to 255 5376 collect (cons (format "&#x%02X;" i) i))) 5377 (push '("<" . ?<) prettify-symbols-alist) 5378 (push '(">" . ?>) prettify-symbols-alist) 5379 (push '("&" . ?&) prettify-symbols-alist) 5380 (push '(" " . ? ) prettify-symbols-alist) 5381 (setq prettify-symbols-compose-predicate 5382 (lambda (_start _end _match) t)) 5383 (prettify-symbols-mode 1)) 5384 5385 (defvar lsp-help-link-keymap 5386 (let ((map (make-sparse-keymap))) 5387 (define-key map [mouse-2] #'lsp--help-open-link) 5388 (define-key map "\r" #'lsp--help-open-link) 5389 map) 5390 "Keymap active on links in *lsp-help* mode.") 5391 5392 (defun lsp--fix-markdown-links () 5393 (let ((inhibit-read-only t) 5394 (inhibit-modification-hooks t) 5395 (prop)) 5396 (save-restriction 5397 (goto-char (point-min)) 5398 (while (setq prop (markdown-find-next-prop 'face)) 5399 (let ((end (or (next-single-property-change (car prop) 'face) 5400 (point-max)))) 5401 (when (memq (get-text-property (car prop) 'face) 5402 '(markdown-link-face 5403 markdown-url-face 5404 markdown-plain-url-face)) 5405 (add-text-properties (car prop) end 5406 (list 'button t 5407 'category 'lsp-help-link 5408 'follow-link t 5409 'keymap lsp-help-link-keymap))) 5410 (goto-char end)))))) 5411 5412 (defun lsp--buffer-string-visible () 5413 "Return visible buffer string. 5414 Stolen from `org-copy-visible'." 5415 (let ((temp (generate-new-buffer " *temp*")) 5416 (beg (point-min)) 5417 (end (point-max))) 5418 (while (/= beg end) 5419 (when (get-char-property beg 'invisible) 5420 (setq beg (next-single-char-property-change beg 'invisible nil end))) 5421 (let* ((next (next-single-char-property-change beg 'invisible nil end)) 5422 (substring (buffer-substring beg next))) 5423 (with-current-buffer temp (insert substring)) 5424 ;; (setq result (concat result substring)) 5425 (setq beg next))) 5426 (setq deactivate-mark t) 5427 (prog1 (with-current-buffer temp 5428 (s-chop-suffix "\n" (buffer-string))) 5429 (kill-buffer temp)))) 5430 5431 (defvar lsp-buffer-major-mode nil 5432 "Holds the major mode when fontification function is running. 5433 See #2588") 5434 5435 (defvar view-inhibit-help-message) 5436 5437 (defun lsp--render-markdown () 5438 "Render markdown." 5439 5440 (let ((markdown-enable-math nil)) 5441 (goto-char (point-min)) 5442 (while (re-search-forward 5443 (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/" 5444 "{" "}" "[" "]" "(" ")" 5445 "#" "+" "-" "." "!" "|")))) 5446 nil t) 5447 (replace-match (rx (backref 1)))) 5448 5449 ;; markdown-mode v2.3 does not yet provide gfm-view-mode 5450 (if (fboundp 'gfm-view-mode) 5451 (let ((view-inhibit-help-message t)) 5452 (gfm-view-mode)) 5453 (gfm-mode)) 5454 5455 (lsp--setup-markdown lsp-buffer-major-mode))) 5456 5457 (defvar lsp--display-inline-image-alist 5458 '((lsp--render-markdown 5459 (:regexp 5460 "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)" 5461 :sexp 5462 (create-image 5463 (base64-decode-string 5464 (buffer-substring-no-properties (match-beginning 1) (match-end 1))) 5465 nil t)))) 5466 "Replaced string regexp and function returning image. 5467 Each element should have the form (MODE . (PROPERTY-LIST...)). 5468 MODE (car) is function which is defined in `lsp-language-id-configuration'. 5469 Cdr should be list of PROPERTY-LIST. 5470 5471 Each PROPERTY-LIST should have properties: 5472 :regexp Regexp which determines what string is relpaced to image. 5473 You should also get information of image, by parenthesis constructs. 5474 By default, all matched string is replaced to image, but you can 5475 change index of replaced string by keyword :replaced-index. 5476 5477 :sexp Return image when evaluated. You can use information of regexp 5478 by using (match-beggining N), (match-end N) or (match-substring N). 5479 5480 In addition, each can have property: 5481 :replaced-index Determine index which is used to replace regexp to image. 5482 The value means first argument of `match-beginning' and 5483 `match-end'. If omitted, interpreted as index 0.") 5484 5485 (defcustom lsp-display-inline-image t 5486 "Showing inline image or not." 5487 :group 'lsp-mode 5488 :type 'boolean) 5489 5490 (defcustom lsp-enable-suggest-server-download t 5491 "When non-nil enable server downloading suggestions." 5492 :group 'lsp-mode 5493 :type 'boolean 5494 :package-version '(lsp-mode . "9.0.0")) 5495 5496 (defcustom lsp-auto-register-remote-clients t 5497 "When non-nil register remote when registering the local one." 5498 :group 'lsp-mode 5499 :type 'boolean 5500 :package-version '(lsp-mode . "9.0.0")) 5501 5502 (defun lsp--display-inline-image (mode) 5503 "Add image property if available." 5504 (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist)))) 5505 (when (and (display-images-p) lsp-display-inline-image) 5506 (cl-loop 5507 for plist in plist-list 5508 with regexp with replaced-index 5509 do 5510 (setq regexp (plist-get plist :regexp)) 5511 (setq replaced-index (or (plist-get plist :replaced-index) 0)) 5512 5513 (font-lock-remove-keywords nil (list regexp replaced-index)) 5514 (let ((inhibit-read-only t)) 5515 (save-excursion 5516 (goto-char (point-min)) 5517 (while (re-search-forward regexp nil t) 5518 (set-text-properties 5519 (match-beginning replaced-index) (match-end replaced-index) 5520 nil) 5521 (add-text-properties 5522 (match-beginning replaced-index) (match-end replaced-index) 5523 `(display ,(eval (plist-get plist :sexp))))))))))) 5524 5525 (defun lsp--fontlock-with-mode (str mode) 5526 "Fontlock STR with MODE." 5527 (let ((lsp-buffer-major-mode major-mode)) 5528 (with-temp-buffer 5529 (with-demoted-errors "Error during doc rendering: %s" 5530 (insert str) 5531 (delay-mode-hooks (funcall mode)) 5532 (cl-flet ((window-body-width () lsp-window-body-width)) 5533 ;; This can go wrong in some cases, and the fontification would 5534 ;; not work as expected. 5535 ;; 5536 ;; See #2984 5537 (ignore-errors (font-lock-ensure)) 5538 (lsp--display-inline-image mode) 5539 (when (eq mode 'lsp--render-markdown) 5540 (lsp--fix-markdown-links)))) 5541 (lsp--buffer-string-visible)))) 5542 5543 (defun lsp--render-string (str language) 5544 "Render STR using `major-mode' corresponding to LANGUAGE. 5545 When language is nil render as markup if `markdown-mode' is loaded." 5546 (setq str (s-replace "\r" "" (or str ""))) 5547 (if-let* ((modes (-keep (-lambda ((mode . lang)) 5548 (when (and (equal lang language) (functionp mode)) 5549 mode)) 5550 lsp-language-id-configuration)) 5551 (mode (car (or (member major-mode modes) modes)))) 5552 (lsp--fontlock-with-mode str mode) 5553 str)) 5554 5555 (defun lsp--render-element (content) 5556 "Render CONTENT element." 5557 (let ((inhibit-message t)) 5558 (or 5559 (pcase content 5560 ((MarkedString :value :language) 5561 (lsp--render-string value language)) 5562 ((MarkupContent :value :kind) 5563 (lsp--render-string value kind)) 5564 ;; plain string 5565 ((pred stringp) (lsp--render-string content "markdown")) 5566 ((pred null) "") 5567 (_ (error "Failed to handle %s" content))) 5568 ""))) 5569 5570 (defun lsp--create-unique-string-fn () 5571 (let (elements) 5572 (lambda (element) 5573 (let ((count (cl-count element elements :test #'string=))) 5574 (prog1 (if (zerop count) 5575 element 5576 (format "%s (%s)" element count)) 5577 (push element elements)))))) 5578 5579 (defun lsp--select-action (actions) 5580 "Select an action to execute from ACTIONS." 5581 (cond 5582 ((seq-empty-p actions) (signal 'lsp-no-code-actions nil)) 5583 ((and (eq (seq-length actions) 1) lsp-auto-execute-action) 5584 (lsp-seq-first actions)) 5585 (t (let ((completion-ignore-case t)) 5586 (lsp--completing-read "Select code action: " 5587 (seq-into actions 'list) 5588 (-compose (lsp--create-unique-string-fn) 5589 #'lsp:code-action-title) 5590 nil t))))) 5591 5592 (defun lsp--workspace-server-id (workspace) 5593 "Return the server ID of WORKSPACE." 5594 (-> workspace lsp--workspace-client lsp--client-server-id)) 5595 5596 (defun lsp--handle-rendered-for-echo-area (contents) 5597 "Return a single line from RENDERED, appropriate for display in the echo area." 5598 (pcase (lsp-workspaces) 5599 (`(,workspace) 5600 (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace))) 5601 ;; For projects with multiple active workspaces we also default to 5602 ;; render the first line. 5603 (_ (lsp-clients-extract-signature-on-hover contents nil)))) 5604 5605 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id) 5606 "Extract a representative line from CONTENTS, to show in the echo area." 5607 (car (s-lines (s-trim (lsp--render-element contents))))) 5608 5609 (defun lsp--render-on-hover-content (contents render-all) 5610 "Render the content received from `document/onHover' request. 5611 CONTENTS - MarkedString | MarkedString[] | MarkupContent 5612 RENDER-ALL - nil if only the signature should be rendered." 5613 (cond 5614 ((lsp-markup-content? contents) 5615 ;; MarkupContent. 5616 ;; It tends to be long and is not suitable to display fully in the echo area. 5617 ;; Just display the first line which is typically the signature. 5618 (if render-all 5619 (lsp--render-element contents) 5620 (lsp--handle-rendered-for-echo-area contents))) 5621 ((and (stringp contents) (not (string-match-p "\n" contents))) 5622 ;; If the contents is a single string containing a single line, 5623 ;; render it always. 5624 (lsp--render-element contents)) 5625 (t 5626 ;; MarkedString -> MarkedString[] 5627 (when (or (lsp-marked-string? contents) (stringp contents)) 5628 (setq contents (list contents))) 5629 ;; Consider the signature consisting of the elements who have a renderable 5630 ;; "language" property. When render-all is nil, ignore other elements. 5631 (string-join 5632 (seq-map 5633 #'lsp--render-element 5634 (if render-all 5635 contents 5636 ;; Only render contents that have an available renderer. 5637 (seq-take 5638 (seq-filter 5639 (-andfn #'lsp-marked-string? 5640 (-compose #'lsp-get-renderer #'lsp:marked-string-language)) 5641 contents) 5642 1))) 5643 (if (bound-and-true-p page-break-lines-mode) 5644 "\n\n" 5645 "\n"))))) 5646 5647 5648 5649 (defvar lsp-signature-mode-map 5650 (-doto (make-sparse-keymap) 5651 (define-key (kbd "M-n") #'lsp-signature-next) 5652 (define-key (kbd "M-p") #'lsp-signature-previous) 5653 (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs) 5654 (define-key (kbd "C-c C-k") #'lsp-signature-stop) 5655 (define-key (kbd "C-g") #'lsp-signature-stop)) 5656 "Keymap for `lsp-signature-mode'.") 5657 5658 (define-minor-mode lsp-signature-mode 5659 "Mode used to show signature popup." 5660 :keymap lsp-signature-mode-map 5661 :lighter "" 5662 :group 'lsp-mode) 5663 5664 (defun lsp-signature-stop () 5665 "Stop showing current signature help." 5666 (interactive) 5667 (lsp-cancel-request-by-token :signature) 5668 (remove-hook 'post-command-hook #'lsp-signature) 5669 (funcall lsp-signature-function nil) 5670 (lsp-signature-mode -1)) 5671 5672 (declare-function page-break-lines--update-display-tables "ext:page-break-lines") 5673 5674 (defun lsp--setup-page-break-mode-if-present () 5675 "Enable `page-break-lines-mode' in current buffer." 5676 (when (fboundp 'page-break-lines-mode) 5677 (page-break-lines-mode) 5678 ;; force page-break-lines-mode to update the display tables. 5679 (page-break-lines--update-display-tables))) 5680 5681 (defun lsp-lv-message (message) 5682 (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present) 5683 (if message 5684 (progn 5685 (setq lsp--signature-last-buffer (current-buffer)) 5686 (let ((lv-force-update t)) 5687 (lv-message "%s" message))) 5688 (lv-delete-window) 5689 (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present))) 5690 5691 (declare-function posframe-show "ext:posframe") 5692 (declare-function posframe-hide "ext:posframe") 5693 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe") 5694 5695 (defface lsp-signature-posframe 5696 '((t :inherit tooltip)) 5697 "Background and foreground for `lsp-signature-posframe'." 5698 :group 'lsp-mode) 5699 5700 (defvar lsp-signature-posframe-params 5701 (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward 5702 :height 10 5703 :width 60 5704 :border-width 1 5705 :min-width 60) 5706 "Params for signature and `posframe-show'.") 5707 5708 (defun lsp-signature-posframe (str) 5709 "Use posframe to show the STR signatureHelp string." 5710 (if str 5711 (apply #'posframe-show 5712 (with-current-buffer (get-buffer-create " *lsp-signature*") 5713 (erase-buffer) 5714 (insert str) 5715 (visual-line-mode 1) 5716 (lsp--setup-page-break-mode-if-present) 5717 (current-buffer)) 5718 (append 5719 lsp-signature-posframe-params 5720 (list :position (point) 5721 :background-color (face-attribute 'lsp-signature-posframe :background nil t) 5722 :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t) 5723 :border-color (face-attribute 'font-lock-comment-face :foreground nil t)))) 5724 (posframe-hide " *lsp-signature*"))) 5725 5726 (defun lsp--handle-signature-update (signature) 5727 (let ((message 5728 (if (lsp-signature-help? signature) 5729 (lsp--signature->message signature) 5730 (mapconcat #'lsp--signature->message signature "\n")))) 5731 (if (s-present? message) 5732 (funcall lsp-signature-function message) 5733 (lsp-signature-stop)))) 5734 5735 (defun lsp-signature-activate () 5736 "Activate signature help. 5737 It will show up only if current point has signature help." 5738 (interactive) 5739 (setq lsp--signature-last nil 5740 lsp--signature-last-index nil 5741 lsp--signature-last-buffer (current-buffer)) 5742 (add-hook 'post-command-hook #'lsp-signature) 5743 (lsp-signature-mode t)) 5744 5745 (defcustom lsp-signature-cycle t 5746 "Whether `lsp-signature-next' and prev should cycle." 5747 :type 'boolean 5748 :group 'lsp-mode) 5749 5750 (defun lsp-signature-next () 5751 "Show next signature." 5752 (interactive) 5753 (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last)))) 5754 (when (and lsp--signature-last-index 5755 lsp--signature-last 5756 (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs))) 5757 (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs)) 5758 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))) 5759 5760 (defun lsp-signature-previous () 5761 "Next signature." 5762 (interactive) 5763 (when (and lsp--signature-last-index 5764 lsp--signature-last 5765 (or lsp-signature-cycle (not (zerop lsp--signature-last-index)))) 5766 (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index) 5767 (length (lsp:signature-help-signatures lsp--signature-last)) 5768 lsp--signature-last-index))) 5769 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))) 5770 5771 (defun lsp-signature-toggle-full-docs () 5772 "Toggle full/partial signature documentation." 5773 (interactive) 5774 (let ((all? (not (numberp lsp-signature-doc-lines)))) 5775 (setq lsp-signature-doc-lines (if all? 5776 (or (car-safe lsp-signature-doc-lines) 5777 20) 5778 (list lsp-signature-doc-lines)))) 5779 (lsp-signature-activate)) 5780 5781 (defun lsp--signature->message (signature-help) 5782 "Generate eldoc message from SIGNATURE-HELP response." 5783 (setq lsp--signature-last signature-help) 5784 5785 (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help)))) 5786 (-let* (((&SignatureHelp :active-signature? 5787 :active-parameter? 5788 :signatures) signature-help) 5789 (active-signature? (or lsp--signature-last-index active-signature? 0)) 5790 (_ (setq lsp--signature-last-index active-signature?)) 5791 ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?)) 5792 (prefix (if (= (length signatures) 1) 5793 "" 5794 (concat (propertize (format " %s/%s" 5795 (1+ active-signature?) 5796 (length signatures)) 5797 'face 'success) 5798 " "))) 5799 (method-docs (when 5800 (and lsp-signature-render-documentation 5801 (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines))) 5802 (let ((docs (lsp--render-element 5803 (lsp:parameter-information-documentation? signature)))) 5804 (when (s-present? docs) 5805 (concat 5806 "\n" 5807 (if (fboundp 'page-break-lines-mode) 5808 "\n" 5809 "") 5810 (if (and (numberp lsp-signature-doc-lines) 5811 (> (length (s-lines docs)) lsp-signature-doc-lines)) 5812 (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs))) 5813 (propertize "\nTruncated..." 'face 'highlight)) 5814 docs))))))) 5815 (when (and active-parameter? (not (seq-empty-p parameters?))) 5816 (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?))) 5817 (seq-elt parameters? active-parameter?))) 5818 (selected-param-label (let ((label (lsp:parameter-information-label param))) 5819 (if (stringp label) label (append label nil)))) 5820 (start (if (stringp selected-param-label) 5821 (s-index-of selected-param-label label) 5822 (cl-first selected-param-label))) 5823 (end (if (stringp selected-param-label) 5824 (+ start (length selected-param-label)) 5825 (cl-second selected-param-label)))) 5826 (add-face-text-property start end 'eldoc-highlight-function-argument nil label))) 5827 (concat prefix label method-docs)))) 5828 5829 (defun lsp-signature () 5830 "Display signature info (based on `textDocument/signatureHelp')" 5831 (if (and lsp--signature-last-buffer 5832 (not (equal (current-buffer) lsp--signature-last-buffer))) 5833 (lsp-signature-stop) 5834 (lsp-request-async "textDocument/signatureHelp" 5835 (lsp--text-document-position-params) 5836 #'lsp--handle-signature-update 5837 :cancel-token :signature))) 5838 5839 5840 (defcustom lsp-overlay-document-color-char "■" 5841 "Display the char represent the document color in overlay" 5842 :type 'string 5843 :group 'lsp-mode) 5844 5845 ;; color presentation 5846 (defun lsp--color-create-interactive-command (color range) 5847 (lambda () 5848 (interactive) 5849 (-let [(&ColorPresentation? :text-edit? 5850 :additional-text-edits?) 5851 (lsp--completing-read 5852 "Select color presentation: " 5853 (lsp-request 5854 "textDocument/colorPresentation" 5855 `( :textDocument ,(lsp--text-document-identifier) 5856 :color ,color 5857 :range ,range)) 5858 #'lsp:color-presentation-label 5859 nil 5860 t)] 5861 (when text-edit? 5862 (lsp--apply-text-edit text-edit?)) 5863 (when additional-text-edits? 5864 (lsp--apply-text-edits additional-text-edits? 'color-presentation))))) 5865 5866 (defun lsp--number->color (number) 5867 (let ((result (format "%x" 5868 (round (* (or number 0) 255.0))))) 5869 (if (= 1 (length result)) 5870 (concat "0" result) 5871 result))) 5872 5873 (defun lsp--document-color () 5874 "Document color handler." 5875 (when (lsp-feature? "textDocument/documentColor") 5876 (lsp-request-async 5877 "textDocument/documentColor" 5878 `(:textDocument ,(lsp--text-document-identifier)) 5879 (lambda (result) 5880 (lsp--remove-overlays 'lsp-color) 5881 (seq-do 5882 (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue) 5883 :range)) 5884 (-let* (((beg . end) (lsp--range-to-region range)) 5885 (overlay (make-overlay beg end)) 5886 (command (lsp--color-create-interactive-command color range))) 5887 (overlay-put overlay 'lsp-color t) 5888 (overlay-put overlay 'evaporate t) 5889 (overlay-put overlay 5890 'before-string 5891 (propertize 5892 lsp-overlay-document-color-char 5893 'face `((:foreground ,(format 5894 "#%s%s%s" 5895 (lsp--number->color red) 5896 (lsp--number->color green) 5897 (lsp--number->color blue)))) 5898 'action command 5899 'mouse-face 'lsp-lens-mouse-face 5900 'local-map (-doto (make-sparse-keymap) 5901 (define-key [mouse-1] command)))))) 5902 result)) 5903 :mode 'unchanged 5904 :cancel-token :document-color-token))) 5905 5906 5907 5908 (defun lsp--action-trigger-parameter-hints (_command) 5909 "Handler for editor.action.triggerParameterHints." 5910 (when (member :on-server-request lsp-signature-auto-activate) 5911 (lsp-signature-activate))) 5912 5913 (defun lsp--action-trigger-suggest (_command) 5914 "Handler for editor.action.triggerSuggest." 5915 (cond 5916 ((and (bound-and-true-p company-mode) 5917 (fboundp 'company-auto-begin) 5918 (fboundp 'company-post-command)) 5919 (run-at-time 0 nil 5920 (lambda () 5921 (let ((this-command 'company-idle-begin) 5922 (company-minimum-prefix-length 0)) 5923 (company-auto-begin) 5924 (company-post-command))))) 5925 (t 5926 (completion-at-point)))) 5927 5928 (defconst lsp--default-action-handlers 5929 (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints) 5930 ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest)) 5931 "Default action handlers.") 5932 5933 (defun lsp--find-action-handler (command) 5934 "Find action handler for particular COMMAND." 5935 (or 5936 (--some (-some->> it 5937 (lsp--workspace-client) 5938 (lsp--client-action-handlers) 5939 (gethash command)) 5940 (lsp-workspaces)) 5941 (gethash command lsp--default-action-handlers))) 5942 5943 (defun lsp--text-document-code-action-params (&optional kind) 5944 "Code action params." 5945 (list :textDocument (lsp--text-document-identifier) 5946 :range (if (use-region-p) 5947 (lsp--region-to-range (region-beginning) (region-end)) 5948 (lsp--region-to-range (point) (point))) 5949 :context `( :diagnostics ,(lsp-cur-possition-diagnostics) 5950 ,@(when kind (list :only (vector kind)))))) 5951 5952 (defun lsp-code-actions-at-point (&optional kind) 5953 "Retrieve the code actions for the active region or the current line. 5954 It will filter by KIND if non nil." 5955 (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind))) 5956 5957 (defun lsp-execute-code-action-by-kind (command-kind) 5958 "Execute code action by COMMAND-KIND." 5959 (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind) 5960 (-filter (-lambda ((&CodeAction :kind?)) 5961 (and kind? (s-prefix? command-kind kind?)))) 5962 lsp--select-action))) 5963 (lsp-execute-code-action action) 5964 (signal 'lsp-no-code-actions '(command-kind)))) 5965 5966 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point) 5967 5968 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?)) 5969 "Parse and execute a code ACTION represented as a Command LSP type." 5970 (let ((server-id (->> (lsp-workspaces) 5971 (cl-first) 5972 (or lsp--cur-workspace) 5973 (lsp--workspace-client) 5974 (lsp--client-server-id)))) 5975 (condition-case nil 5976 (with-no-warnings 5977 (lsp-execute-command server-id (intern command) arguments?)) 5978 (cl-no-applicable-method 5979 (if-let ((action-handler (lsp--find-action-handler command))) 5980 (funcall action-handler action) 5981 (lsp-send-execute-command command arguments?)))))) 5982 5983 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?)) 5984 "Execute code action ACTION. For example, when text under the 5985 caret has a suggestion to apply a fix from an lsp-server, calling 5986 this function will do so. 5987 If ACTION is not set it will be selected from `lsp-code-actions-at-point'. 5988 Request codeAction/resolve for more info if server supports." 5989 (interactive (list (lsp--select-action (lsp-code-actions-at-point)))) 5990 (if (and (lsp-feature? "codeAction/resolve") 5991 (not command?) 5992 (not edit?)) 5993 (lsp--execute-code-action (lsp-request "codeAction/resolve" action)) 5994 (lsp--execute-code-action action))) 5995 5996 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?)) 5997 "Execute code action ACTION." 5998 (when edit? 5999 (lsp--apply-workspace-edit edit? 'code-action)) 6000 6001 (cond 6002 ((stringp command?) (lsp--execute-command action)) 6003 ((lsp-command? command?) (lsp--execute-command command?)))) 6004 6005 (defvar lsp--formatting-indent-alist 6006 ;; Taken from `dtrt-indent-mode' 6007 '( 6008 (ada-mode . ada-indent) ; Ada 6009 (ada-ts-mode . ada-ts-mode-indent-offset) 6010 (c++-mode . c-basic-offset) ; C++ 6011 (c++-ts-mode . c-ts-mode-indent-offset) 6012 (c-mode . c-basic-offset) ; C 6013 (c-ts-mode . c-ts-mode-indent-offset) 6014 (cperl-mode . cperl-indent-level) ; Perl 6015 (crystal-mode . crystal-indent-level) ; Crystal (Ruby) 6016 (csharp-mode . c-basic-offset) ; C# 6017 (csharp-tree-sitter-mode . csharp-tree-sitter-indent-offset) ; C# 6018 (csharp-ts-mode . csharp-ts-mode-indent-offset) ; C# (tree-sitter, Emacs29) 6019 (css-mode . css-indent-offset) ; CSS 6020 (d-mode . c-basic-offset) ; D 6021 (enh-ruby-mode . enh-ruby-indent-level) ; Ruby 6022 (erlang-mode . erlang-indent-level) ; Erlang 6023 (ess-mode . ess-indent-offset) ; ESS (R) 6024 (go-ts-mode . go-ts-mode-indent-offset) 6025 (gpr-mode . gpr-indent-offset) ; GNAT Project 6026 (gpr-ts-mode . gpr-ts-mode-indent-offset) 6027 (hack-mode . hack-indent-offset) ; Hack 6028 (java-mode . c-basic-offset) ; Java 6029 (java-ts-mode . java-ts-mode-indent-offset) 6030 (jde-mode . c-basic-offset) ; Java (JDE) 6031 (js-mode . js-indent-level) ; JavaScript 6032 (js-ts-mode . js-indent-level) 6033 (js2-mode . js2-basic-offset) ; JavaScript-IDE 6034 (js3-mode . js3-indent-level) ; JavaScript-IDE 6035 (json-mode . js-indent-level) ; JSON 6036 (json-ts-mode . json-ts-mode-indent-offset) 6037 (lua-mode . lua-indent-level) ; Lua 6038 (lua-ts-mode . lua-ts-indent-offset) 6039 (nxml-mode . nxml-child-indent) ; XML 6040 (objc-mode . c-basic-offset) ; Objective C 6041 (pascal-mode . pascal-indent-level) ; Pascal 6042 (perl-mode . perl-indent-level) ; Perl 6043 (php-mode . c-basic-offset) ; PHP 6044 (php-ts-mode . php-ts-mode-indent-offset) ; PHP 6045 (powershell-mode . powershell-indent) ; PowerShell 6046 (powershell-ts-mode . powershell-ts-mode-indent-offset) ; PowerShell 6047 (raku-mode . raku-indent-offset) ; Perl6/Raku 6048 (ruby-mode . ruby-indent-level) ; Ruby 6049 (rust-mode . rust-indent-offset) ; Rust 6050 (rust-ts-mode . rust-ts-mode-indent-offset) 6051 (rustic-mode . rustic-indent-offset) ; Rust 6052 (scala-mode . scala-indent:step) ; Scala 6053 (sgml-mode . sgml-basic-offset) ; SGML 6054 (sh-mode . sh-basic-offset) ; Shell Script 6055 (toml-ts-mode . toml-ts-mode-indent-offset) 6056 (typescript-mode . typescript-indent-level) ; Typescript 6057 (typescript-ts-mode . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29) 6058 (yaml-mode . yaml-indent-offset) ; YAML 6059 (yang-mode . c-basic-offset) ; YANG (yang-mode) 6060 6061 (default . standard-indent)) ; default fallback 6062 "A mapping from `major-mode' to its indent variable.") 6063 6064 (defun lsp--get-indent-width (mode) 6065 "Get indentation offset for MODE." 6066 (or (alist-get mode lsp--formatting-indent-alist) 6067 (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default)))) 6068 6069 (defun lsp--make-document-formatting-params () 6070 "Create document formatting params." 6071 (lsp-make-document-formatting-params 6072 :text-document (lsp--text-document-identifier) 6073 :options (lsp-make-formatting-options 6074 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 6075 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 6076 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 6077 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 6078 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)))) 6079 6080 (defun lsp-format-buffer () 6081 "Ask the server to format this document." 6082 (interactive "*") 6083 (cond ((lsp-feature? "textDocument/formatting") 6084 (let ((edits (lsp-request "textDocument/formatting" 6085 (lsp--make-document-formatting-params)))) 6086 (if (seq-empty-p edits) 6087 (lsp--info "No formatting changes provided") 6088 (lsp--apply-text-edits edits 'format)))) 6089 ((lsp-feature? "textDocument/rangeFormatting") 6090 (save-restriction 6091 (widen) 6092 (lsp-format-region (point-min) (point-max)))) 6093 (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider"))))) 6094 6095 (defun lsp-format-region (s e) 6096 "Ask the server to format the region, or if none is selected, the current line." 6097 (interactive "r") 6098 (let ((edits (lsp-request 6099 "textDocument/rangeFormatting" 6100 (lsp--make-document-range-formatting-params s e)))) 6101 (if (seq-empty-p edits) 6102 (lsp--info "No formatting changes provided") 6103 (lsp--apply-text-edits edits 'format)))) 6104 6105 (defmacro lsp-make-interactive-code-action (func-name code-action-kind) 6106 "Define an interactive function FUNC-NAME that attempts to 6107 execute a CODE-ACTION-KIND action." 6108 `(defun ,(intern (concat "lsp-" (symbol-name func-name))) () 6109 ,(format "Perform the %s code action, if available." code-action-kind) 6110 (interactive) 6111 ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to 6112 ;; auto-execute here: the user has specified exactly what they want. 6113 (let ((lsp-auto-execute-action t)) 6114 (condition-case nil 6115 (lsp-execute-code-action-by-kind ,code-action-kind) 6116 (lsp-no-code-actions 6117 (when (called-interactively-p 'any) 6118 (lsp--info ,(format "%s action not available" code-action-kind)))))))) 6119 6120 (lsp-make-interactive-code-action organize-imports "source.organizeImports") 6121 6122 (defun lsp--make-document-range-formatting-params (start end) 6123 "Make DocumentRangeFormattingParams for selected region." 6124 (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params) 6125 (lsp--region-to-range start end))) 6126 6127 (defconst lsp--highlight-kind-face 6128 '((1 . lsp-face-highlight-textual) 6129 (2 . lsp-face-highlight-read) 6130 (3 . lsp-face-highlight-write))) 6131 6132 (defun lsp--remove-overlays (name) 6133 (save-restriction 6134 (widen) 6135 (remove-overlays (point-min) (point-max) name t))) 6136 6137 (defun lsp-document-highlight () 6138 "Highlight all relevant references to the symbol under point." 6139 (interactive) 6140 (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights 6141 (setq lsp--have-document-highlights nil 6142 lsp--symbol-bounds-of-last-highlight-invocation nil) 6143 (let ((lsp-enable-symbol-highlighting t)) 6144 (lsp--document-highlight))) 6145 6146 (defun lsp--document-highlight-callback (highlights) 6147 "Create a callback to process the reply of a 6148 `textDocument/documentHighlight' message for the buffer BUF. 6149 A reference is highlighted only if it is visible in a window." 6150 (lsp--remove-overlays 'lsp-highlight) 6151 6152 (let* ((wins-visible-pos (-map (lambda (win) 6153 (cons (1- (line-number-at-pos (window-start win) t)) 6154 (1+ (line-number-at-pos (window-end win) t)))) 6155 (get-buffer-window-list nil nil 'visible)))) 6156 (setq lsp--have-document-highlights t) 6157 (-map 6158 (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line) 6159 :end (end &as &Position :line end-line)) 6160 :kind?)) 6161 (-map 6162 (-lambda ((start-window . end-window)) 6163 ;; Make the overlay only if the reference is visible 6164 (let ((start-point (lsp--position-to-point start)) 6165 (end-point (lsp--position-to-point end))) 6166 (when (and (> (1+ start-line) start-window) 6167 (< (1+ end-line) end-window) 6168 (not (and lsp-symbol-highlighting-skip-current 6169 (<= start-point (point) end-point)))) 6170 (-doto (make-overlay start-point end-point) 6171 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face))) 6172 (overlay-put 'lsp-highlight t))))) 6173 wins-visible-pos)) 6174 highlights))) 6175 6176 (defcustom lsp-symbol-kinds 6177 '((1 . "File") 6178 (2 . "Module") 6179 (3 . "Namespace") 6180 (4 . "Package") 6181 (5 . "Class") 6182 (6 . "Method") 6183 (7 . "Property") 6184 (8 . "Field") 6185 (9 . "Constructor") 6186 (10 . "Enum") 6187 (11 . "Interface") 6188 (12 . "Function") 6189 (13 . "Variable") 6190 (14 . "Constant") 6191 (15 . "String") 6192 (16 . "Number") 6193 (17 . "Boolean") 6194 (18 . "Array") 6195 (19 . "Object") 6196 (20 . "Key") 6197 (21 . "Null") 6198 (22 . "Enum Member") 6199 (23 . "Struct") 6200 (24 . "Event") 6201 (25 . "Operator") 6202 (26 . "Type Parameter")) 6203 "Alist mapping SymbolKinds to human-readable strings. 6204 Various Symbol objects in the LSP protocol have an integral type, 6205 specifying what they are. This alist maps such type integrals to 6206 readable representations of them. See 6207 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/', 6208 namespace SymbolKind." 6209 :group 'lsp-mode 6210 :type '(alist :key-type integer :value-type string)) 6211 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds) 6212 6213 (lsp-defun lsp--symbol-information-to-xref 6214 ((&SymbolInformation :kind :name 6215 :location (&Location :uri :range (&Range :start 6216 (&Position :line :character))))) 6217 "Return a `xref-item' from SYMBOL information." 6218 (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name) 6219 (xref-make-file-location (lsp--uri-to-path uri) 6220 line 6221 character))) 6222 6223 (defun lsp--get-document-symbols () 6224 "Get document symbols. 6225 6226 If the buffer has not been modified since symbols were last 6227 retrieved, simply return the latest result. 6228 6229 Else, if the request was initiated by Imenu updating its menu-bar 6230 entry, perform it asynchronously; i.e., give Imenu the latest 6231 result and then force a refresh when a new one is available. 6232 6233 Else (e.g., due to interactive use of `imenu' or `xref'), 6234 perform the request synchronously." 6235 (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick) 6236 lsp--document-symbols 6237 (let ((method "textDocument/documentSymbol") 6238 (params `(:textDocument ,(lsp--text-document-identifier))) 6239 (tick (buffer-chars-modified-tick))) 6240 (if (not lsp--document-symbols-request-async) 6241 (prog1 6242 (setq lsp--document-symbols (lsp-request method params)) 6243 (setq lsp--document-symbols-tick tick)) 6244 (lsp-request-async method params 6245 (lambda (document-symbols) 6246 (setq lsp--document-symbols document-symbols 6247 lsp--document-symbols-tick tick) 6248 (lsp--imenu-refresh)) 6249 :mode 'alive 6250 :cancel-token :document-symbols) 6251 lsp--document-symbols)))) 6252 6253 (advice-add 'imenu-update-menubar :around 6254 (lambda (oldfun &rest r) 6255 (let ((lsp--document-symbols-request-async t)) 6256 (apply oldfun r)))) 6257 6258 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position) 6259 "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION." 6260 (-let (((symbol &as &DocumentSymbol? :children?) 6261 (seq-find (-lambda ((&DocumentSymbol :range)) 6262 (lsp-point-in-range? current-position range)) 6263 document-symbols))) 6264 (if children? 6265 (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position)) 6266 (when symbol 6267 (list symbol))))) 6268 6269 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?)) 6270 "Convert a SymbolInformation to a DocumentInformation" 6271 (lsp-make-document-symbol :name name 6272 :kind kind 6273 :range (lsp:location-range location) 6274 :children? nil 6275 :deprecated? deprecated? 6276 :selection-range (lsp:location-range location) 6277 :detail? container-name?)) 6278 6279 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position) 6280 "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION." 6281 (--> symbols-informations 6282 (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range))) 6283 (when (lsp-point-in-range? current-position range) 6284 (lsp--symbol-information->document-symbol symbol))) 6285 it) 6286 (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position)) 6287 (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position))) 6288 (and (lsp--position-compare b-start-position a-start-position) 6289 (lsp--position-compare a-end-position b-end-position)))))) 6290 6291 (defun lsp--symbols->document-symbols-hierarchy (symbols) 6292 "Convert SYMBOLS to symbols-hierarchy." 6293 (when-let ((first-symbol (lsp-seq-first symbols))) 6294 (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line) 6295 :character (plist-get (lsp--cur-position) :character)))) 6296 (if (lsp-symbol-information? first-symbol) 6297 (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position) 6298 (lsp--document-symbols->document-symbols-hierarchy symbols cur-position))))) 6299 6300 (defun lsp--xref-backend () 'xref-lsp) 6301 6302 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp))) 6303 (propertize (or (thing-at-point 'symbol) "") 6304 'identifier-at-point t)) 6305 6306 (defun lsp--xref-elements-index (symbols path) 6307 (-mapcat 6308 (-lambda (sym) 6309 (pcase-exhaustive sym 6310 ((DocumentSymbol :name :children? :selection-range (Range :start)) 6311 (cons (cons (concat path name) 6312 (lsp--position-to-point start)) 6313 (lsp--xref-elements-index children? (concat path name " / ")))) 6314 ((SymbolInformation :name :location (Location :range (Range :start))) 6315 (list (cons (concat path name) 6316 (lsp--position-to-point start)))))) 6317 symbols)) 6318 6319 (defvar-local lsp--symbols-cache nil) 6320 6321 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp))) 6322 (if (lsp--find-workspaces-for "textDocument/documentSymbol") 6323 (progn 6324 (setq lsp--symbols-cache (lsp--xref-elements-index 6325 (lsp--get-document-symbols) nil)) 6326 lsp--symbols-cache) 6327 (list (propertize (or (thing-at-point 'symbol) "") 6328 'identifier-at-point t)))) 6329 6330 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier) 6331 (save-excursion 6332 (unless (get-text-property 0 'identifier-at-point identifier) 6333 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6334 (user-error "Unable to find symbol %s in current document" identifier))))) 6335 (lsp--locations-to-xref-items (lsp-request "textDocument/definition" 6336 (lsp--text-document-position-params))))) 6337 6338 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier) 6339 (save-excursion 6340 (unless (get-text-property 0 'identifier-at-point identifier) 6341 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6342 (user-error "Unable to find symbol %s" identifier))))) 6343 (lsp--locations-to-xref-items (lsp-request "textDocument/references" 6344 (lsp--make-reference-params nil lsp-references-exclude-definition))))) 6345 6346 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern) 6347 (seq-map #'lsp--symbol-information-to-xref 6348 (lsp-request "workspace/symbol" `(:query ,pattern)))) 6349 6350 (defcustom lsp-rename-use-prepare t 6351 "Whether `lsp-rename' should do a prepareRename first. 6352 For some language servers, textDocument/prepareRename might be 6353 too slow, in which case this variable may be set to nil. 6354 `lsp-rename' will then use `thing-at-point' `symbol' to determine 6355 the symbol to rename at point." 6356 :group 'lsp-mode 6357 :type 'boolean) 6358 6359 (defun lsp--get-symbol-to-rename () 6360 "Get a symbol to rename and placeholder at point. 6361 Returns a cons ((START . END) . PLACEHOLDER?), and nil if 6362 renaming is generally supported but cannot be done at point. 6363 START and END are the bounds of the identifiers being renamed, 6364 while PLACEHOLDER?, is either nil or a string suggested by the 6365 language server as the initial input of a new-name prompt." 6366 (unless (lsp-feature? "textDocument/rename") 6367 (error "The connected server(s) doesn't support renaming")) 6368 (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename")) 6369 (when-let ((response 6370 (lsp-request "textDocument/prepareRename" 6371 (lsp--text-document-position-params)))) 6372 (let* ((bounds (lsp--range-to-region 6373 (if (lsp-range? response) 6374 response 6375 (lsp:prepare-rename-result-range response)))) 6376 (placeholder 6377 (and (not (lsp-range? response)) 6378 (lsp:prepare-rename-result-placeholder response)))) 6379 (cons bounds placeholder))) 6380 (when-let ((bounds (bounds-of-thing-at-point 'symbol))) 6381 (cons bounds nil)))) 6382 6383 (defface lsp-face-rename '((t :underline t)) 6384 "Face used to highlight the identifier being renamed. 6385 Renaming can be done using `lsp-rename'." 6386 :group 'lsp-mode) 6387 6388 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face)) 6389 "Face used to display the rename placeholder in. 6390 When calling `lsp-rename' interactively, this will be the face of 6391 the new name." 6392 :group 'lsp-mode) 6393 6394 (defvar lsp-rename-history '() 6395 "History for `lsp--read-rename'.") 6396 6397 (defun lsp--read-rename (at-point) 6398 "Read a new name for a `lsp-rename' at `point' from the user. 6399 AT-POINT shall be a structure as returned by 6400 `lsp--get-symbol-to-rename'. 6401 6402 Returns a string, which should be the new name for the identifier 6403 at point. If renaming cannot be done at point (as determined from 6404 AT-POINT), throw a `user-error'. 6405 6406 This function is for use in `lsp-rename' only, and shall not be 6407 relied upon." 6408 (unless at-point 6409 (user-error "`lsp-rename' is invalid here")) 6410 (-let* ((((start . end) . placeholder?) at-point) 6411 ;; Do the `buffer-substring' first to not include `lsp-face-rename' 6412 (rename-me (buffer-substring start end)) 6413 (placeholder (or placeholder? rename-me)) 6414 (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face)) 6415 6416 overlay) 6417 ;; We need unwind protect, as the user might cancel here, causing the 6418 ;; overlay to linger. 6419 (unwind-protect 6420 (progn 6421 (setq overlay (make-overlay start end)) 6422 (overlay-put overlay 'face 'lsp-face-rename) 6423 6424 (read-string (format "Rename %s to: " rename-me) placeholder 6425 'lsp-rename-history)) 6426 (and overlay (delete-overlay overlay))))) 6427 6428 (defun lsp-rename (newname) 6429 "Rename the symbol (and all references to it) under point to NEWNAME." 6430 (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename)))) 6431 (when-let ((edits (lsp-request "textDocument/rename" 6432 `( :textDocument ,(lsp--text-document-identifier) 6433 :position ,(lsp--cur-position) 6434 :newName ,newname)))) 6435 (lsp--apply-workspace-edit edits 'rename))) 6436 6437 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?) 6438 "Advice around function `rename-file'. 6439 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?. 6440 6441 This advice sends workspace/willRenameFiles before renaming file 6442 to check if server wants to apply any workspaceEdits after renamed." 6443 (if (and lsp-apply-edits-after-file-operations 6444 (lsp--send-will-rename-files-p old-name)) 6445 (let ((params (lsp-make-rename-files-params 6446 :files (vector (lsp-make-file-rename 6447 :oldUri (lsp--path-to-uri old-name) 6448 :newUri (lsp--path-to-uri new-name)))))) 6449 (when-let ((edits (lsp-request "workspace/willRenameFiles" params))) 6450 (lsp--apply-workspace-edit edits 'rename-file) 6451 (funcall old-func old-name new-name ok-if-already-exists?) 6452 (when (lsp--send-did-rename-files-p) 6453 (lsp-notify "workspace/didRenameFiles" params)))) 6454 (funcall old-func old-name new-name ok-if-already-exists?))) 6455 6456 (advice-add 'rename-file :around #'lsp--on-rename-file) 6457 6458 (defcustom lsp-xref-force-references nil 6459 "If non-nil threat everything as references(e. g. jump if only one item.)" 6460 :group 'lsp-mode 6461 :type 'boolean) 6462 6463 (defun lsp-show-xrefs (xrefs display-action references?) 6464 (unless (region-active-p) (push-mark nil t)) 6465 (if (boundp 'xref-show-definitions-function) 6466 (with-no-warnings 6467 (xref-push-marker-stack) 6468 (funcall (if (and references? (not lsp-xref-force-references)) 6469 xref-show-xrefs-function 6470 xref-show-definitions-function) 6471 (-const xrefs) 6472 `((window . ,(selected-window)) 6473 (display-action . ,display-action) 6474 ,(if (and references? (not lsp-xref-force-references)) 6475 `(auto-jump . ,xref-auto-jump-to-first-xref) 6476 `(auto-jump . ,xref-auto-jump-to-first-definition))))) 6477 (xref--show-xrefs xrefs display-action))) 6478 6479 (cl-defmethod seq-empty-p ((ht hash-table)) 6480 "Function `seq-empty-p' for hash-table." 6481 (hash-table-empty-p ht)) 6482 6483 (cl-defun lsp-find-locations (method &optional extra &key display-action references?) 6484 "Send request named METHOD and get cross references of the symbol under point. 6485 EXTRA is a plist of extra parameters. 6486 REFERENCES? t when METHOD returns references." 6487 (let ((loc (lsp-request method 6488 (append (lsp--text-document-position-params) extra)))) 6489 (if (seq-empty-p loc) 6490 (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) "")) 6491 (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?)))) 6492 6493 (cl-defun lsp-find-declaration (&key display-action) 6494 "Find declarations of the symbol under point." 6495 (interactive) 6496 (lsp-find-locations "textDocument/declaration" nil :display-action display-action)) 6497 6498 (cl-defun lsp-find-definition (&key display-action) 6499 "Find definitions of the symbol under point." 6500 (interactive) 6501 (lsp-find-locations "textDocument/definition" nil :display-action display-action)) 6502 6503 (defun lsp-find-definition-mouse (click) 6504 "Click to start `lsp-find-definition' at clicked point." 6505 (interactive "e") 6506 (let* ((ec (event-start click)) 6507 (p1 (posn-point ec)) 6508 (w1 (posn-window ec))) 6509 (select-window w1) 6510 (goto-char p1) 6511 (lsp-find-definition))) 6512 6513 (cl-defun lsp-find-implementation (&key display-action) 6514 "Find implementations of the symbol under point." 6515 (interactive) 6516 (lsp-find-locations "textDocument/implementation" 6517 nil 6518 :display-action display-action 6519 :references? t)) 6520 6521 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action) 6522 "Find references of the symbol under point." 6523 (interactive "P") 6524 (lsp-find-locations "textDocument/references" 6525 (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition))))) 6526 :display-action display-action 6527 :references? t)) 6528 6529 (cl-defun lsp-find-type-definition (&key display-action) 6530 "Find type definitions of the symbol under point." 6531 (interactive) 6532 (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action)) 6533 6534 (defalias 'lsp-find-custom #'lsp-find-locations) 6535 (defalias 'lsp-goto-implementation #'lsp-find-implementation) 6536 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition) 6537 6538 (with-eval-after-load 'evil 6539 (evil-set-command-property 'lsp-find-definition :jump t) 6540 (evil-set-command-property 'lsp-find-implementation :jump t) 6541 (evil-set-command-property 'lsp-find-references :jump t) 6542 (evil-set-command-property 'lsp-find-type-definition :jump t)) 6543 6544 (defun lsp--workspace-method-supported? (check-command method capability workspace) 6545 (with-lsp-workspace workspace 6546 (if check-command 6547 (funcall check-command workspace) 6548 (or 6549 (when capability (lsp--capability capability)) 6550 (lsp--registered-capability method) 6551 (and (not capability) (not check-command)))))) 6552 6553 (defun lsp-disable-method-for-server (method server-id) 6554 "Disable METHOD for SERVER-ID." 6555 (cl-callf 6556 (lambda (reqs) 6557 (-let (((&plist :check-command :capability) reqs)) 6558 (list :check-command 6559 (lambda (workspace) 6560 (unless (-> workspace 6561 lsp--workspace-client 6562 lsp--client-server-id 6563 (eq server-id)) 6564 (lsp--workspace-method-supported? check-command 6565 method 6566 capability 6567 workspace)))))) 6568 (alist-get method lsp-method-requirements nil nil 'string=))) 6569 6570 (defun lsp--find-workspaces-for (msg-or-method) 6571 "Find all workspaces in the current project that can handle MSG." 6572 (let ((method (if (stringp msg-or-method) 6573 msg-or-method 6574 (plist-get msg-or-method :method)))) 6575 (-if-let (reqs (cdr (assoc method lsp-method-requirements))) 6576 (-let (((&plist :capability :check-command) reqs)) 6577 (-filter 6578 (-partial #'lsp--workspace-method-supported? 6579 check-command method capability) 6580 (lsp-workspaces))) 6581 (lsp-workspaces)))) 6582 6583 (defun lsp-can-execute-command? (command-name) 6584 "Returns non-nil if current language server(s) can execute COMMAND-NAME. 6585 The command is executed via `workspace/executeCommand'" 6586 (cl-position 6587 command-name 6588 (lsp:execute-command-options-commands 6589 (lsp:server-capabilities-execute-command-provider? 6590 (lsp--server-capabilities))) 6591 :test #'equal)) 6592 6593 (defalias 'lsp-feature? 'lsp--find-workspaces-for) 6594 6595 (cl-defmethod lsp-execute-command (_server _command _arguments) 6596 "Dispatch COMMAND execution." 6597 (signal 'cl-no-applicable-method nil)) 6598 6599 (defun lsp-workspace-command-execute (command &optional args) 6600 "Execute workspace COMMAND with ARGS." 6601 (condition-case-unless-debug err 6602 (let ((params (if args 6603 (list :command command :arguments args) 6604 (list :command command)))) 6605 (lsp-request "workspace/executeCommand" params)) 6606 (error 6607 (error "`workspace/executeCommand' with `%s' failed.\n\n%S" 6608 command err)))) 6609 6610 (defun lsp-send-execute-command (command &optional args) 6611 "Create and send a `workspace/executeCommand' message having command COMMAND 6612 and optional ARGS." 6613 (lsp-workspace-command-execute command args)) 6614 6615 (defalias 'lsp-point-to-position #'lsp--point-to-position) 6616 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier) 6617 (defalias 'lsp--send-execute-command #'lsp-send-execute-command) 6618 (defalias 'lsp-on-open #'lsp--text-document-did-open) 6619 (defalias 'lsp-on-save #'lsp--text-document-did-save) 6620 6621 (defun lsp--set-configuration (settings) 6622 "Set the SETTINGS for the lsp server." 6623 (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings))) 6624 6625 (defun lsp-current-buffer () 6626 (or lsp--virtual-buffer 6627 (current-buffer))) 6628 6629 (defun lsp-buffer-live-p (buffer-id) 6630 (if-let ((buffer-live (plist-get buffer-id :buffer-live?))) 6631 (funcall buffer-live buffer-id) 6632 (buffer-live-p buffer-id))) 6633 6634 (defun lsp--on-set-visited-file-name (old-func &rest args) 6635 "Advice around function `set-visited-file-name'. 6636 6637 This advice sends textDocument/didClose for the old file and 6638 textDocument/didOpen for the new file." 6639 (when lsp--cur-workspace 6640 (lsp--text-document-did-close t)) 6641 (prog1 (apply old-func args) 6642 (when lsp--cur-workspace 6643 (lsp--text-document-did-open)))) 6644 6645 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name) 6646 6647 (defvar lsp--flushing-delayed-changes nil) 6648 6649 (defun lsp--send-no-wait (message proc) 6650 "Send MESSAGE to PROC without waiting for further output." 6651 6652 (unless lsp--flushing-delayed-changes 6653 (let ((lsp--flushing-delayed-changes t)) 6654 (lsp--flush-delayed-changes))) 6655 (lsp-process-send proc message)) 6656 6657 (define-error 'lsp-parse-error 6658 "Error parsing message from language server" 'lsp-error) 6659 (define-error 'lsp-unknown-message-type 6660 "Unknown message type" '(lsp-error lsp-parse-error)) 6661 (define-error 'lsp-unknown-json-rpc-version 6662 "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error)) 6663 (define-error 'lsp-no-content-length 6664 "Content-Length header missing in message" '(lsp-error lsp-parse-error)) 6665 (define-error 'lsp-invalid-header-name 6666 "Invalid header name" '(lsp-error lsp-parse-error)) 6667 6668 ;; id method 6669 ;; x x request 6670 ;; x . response 6671 ;; . x notification 6672 (defun lsp--get-message-type (json-data) 6673 "Get the message type from JSON-DATA." 6674 (if (lsp:json-message-id? json-data) 6675 (if (lsp:json-message-error? json-data) 6676 'response-error 6677 (if (lsp:json-message-method? json-data) 6678 'request 6679 'response)) 6680 'notification)) 6681 6682 (defconst lsp--default-notification-handlers 6683 (ht ("window/showMessage" #'lsp--window-show-message) 6684 ("window/logMessage" #'lsp--window-log-message) 6685 ("window/showInputBox" #'lsp--window-show-input-box) 6686 ("window/showQuickPick" #'lsp--window-show-quick-pick) 6687 ("textDocument/publishDiagnostics" #'lsp--on-diagnostics) 6688 ("textDocument/diagnosticsEnd" #'ignore) 6689 ("textDocument/diagnosticsBegin" #'ignore) 6690 ("telemetry/event" #'ignore) 6691 ("$/progress" (lambda (workspace params) 6692 (funcall lsp-progress-function workspace params))))) 6693 6694 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method)) 6695 "Call the appropriate handler for NOTIFICATION." 6696 (-let ((client (lsp--workspace-client workspace))) 6697 (when (lsp--log-io-p method) 6698 (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif) 6699 lsp--cur-workspace)) 6700 (if-let ((handler (or (gethash method (lsp--client-notification-handlers client)) 6701 (gethash method lsp--default-notification-handlers)))) 6702 (funcall handler workspace params) 6703 (when (and method (not (string-prefix-p "$" method))) 6704 (lsp-warn "Unknown notification: %s" method))))) 6705 6706 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items)) 6707 "Get section configuration. 6708 PARAMS are the `workspace/configuration' request params" 6709 (->> items 6710 (-map (-lambda ((&ConfigurationItem :section?)) 6711 (-let* ((path-parts (split-string section? "\\.")) 6712 (path-without-last (s-join "." (-slice path-parts 0 -1))) 6713 (path-parts-len (length path-parts))) 6714 (cond 6715 ((<= path-parts-len 1) 6716 (ht-get (lsp-configuration-section section?) 6717 (car-safe path-parts) 6718 (ht-create))) 6719 ((> path-parts-len 1) 6720 (when-let ((section (lsp-configuration-section path-without-last)) 6721 (keys path-parts)) 6722 (while (and keys section) 6723 (setf section (ht-get section (pop keys)))) 6724 section)))))) 6725 (apply #'vector))) 6726 6727 (defun lsp--ms-since (timestamp) 6728 "Integer number of milliseconds since TIMESTAMP. Fractions discarded." 6729 (floor (* 1000 (float-time (time-since timestamp))))) 6730 6731 (defun lsp--send-request-response (workspace recv-time request response) 6732 "Send the RESPONSE for REQUEST in WORKSPACE and log if needed." 6733 (-let* (((&JSONResponse :params :method :id) request) 6734 (process (lsp--workspace-proc workspace)) 6735 (response (lsp--make-response id response)) 6736 (req-entry (and lsp-log-io 6737 (lsp--make-log-entry method id params 'incoming-req))) 6738 (resp-entry (and lsp-log-io 6739 (lsp--make-log-entry method id response 'outgoing-resp 6740 (lsp--ms-since recv-time))))) 6741 ;; Send response to the server. 6742 (when (lsp--log-io-p method) 6743 (lsp--log-entry-new req-entry workspace) 6744 (lsp--log-entry-new resp-entry workspace)) 6745 (lsp--send-no-wait response process))) 6746 6747 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method)) 6748 "Call the appropriate handler for REQUEST, and send the return value to the 6749 server. WORKSPACE is the active workspace." 6750 (-let* ((recv-time (current-time)) 6751 (client (lsp--workspace-client workspace)) 6752 (buffers (lsp--workspace-buffers workspace)) 6753 handler 6754 (response (cond 6755 ((setq handler (gethash method (lsp--client-request-handlers client) nil)) 6756 (funcall handler workspace params)) 6757 ((setq handler (gethash method (lsp--client-async-request-handlers client) nil)) 6758 (funcall handler workspace params 6759 (-partial #'lsp--send-request-response 6760 workspace recv-time request)) 6761 'delay-response) 6762 ((equal method "client/registerCapability") 6763 (mapc #'lsp--server-register-capability 6764 (lsp:registration-params-registrations params)) 6765 (mapc (lambda (buf) 6766 (when (lsp-buffer-live-p buf) 6767 (lsp-with-current-buffer buf 6768 (lsp-unconfig-buffer) 6769 (lsp-configure-buffer)))) 6770 buffers) 6771 nil) 6772 ((equal method "window/showMessageRequest") 6773 (let ((choice (lsp--window-log-message-request params))) 6774 `(:title ,choice))) 6775 ((equal method "window/showDocument") 6776 (let ((success? (lsp--window-show-document params))) 6777 (lsp-make-show-document-result :success (or success? 6778 :json-false)))) 6779 ((equal method "client/unregisterCapability") 6780 (mapc #'lsp--server-unregister-capability 6781 (lsp:unregistration-params-unregisterations params)) 6782 (mapc (lambda (buf) 6783 (when (lsp-buffer-live-p buf) 6784 (lsp-with-current-buffer buf 6785 (lsp-unconfig-buffer) 6786 (lsp-configure-buffer)))) 6787 buffers) 6788 nil) 6789 ((equal method "workspace/applyEdit") 6790 (list :applied (condition-case err 6791 (prog1 t 6792 (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested)) 6793 (error 6794 (lsp--error "Failed to apply edits with message %s" 6795 (error-message-string err)) 6796 :json-false)))) 6797 ((equal method "workspace/configuration") 6798 (with-lsp-workspace workspace 6799 (if-let ((buf (car buffers))) 6800 (lsp-with-current-buffer buf 6801 (lsp--build-workspace-configuration-response params)) 6802 (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace) 6803 (lsp--build-workspace-configuration-response params))))) 6804 ((equal method "workspace/workspaceFolders") 6805 (let ((folders (or (-> workspace 6806 (lsp--workspace-client) 6807 (lsp--client-server-id) 6808 (gethash (lsp-session-server-id->folders (lsp-session)))) 6809 (lsp-session-folders (lsp-session))))) 6810 (->> folders 6811 (-distinct) 6812 (-map (lambda (folder) 6813 (list :uri (lsp--path-to-uri folder)))) 6814 (apply #'vector)))) 6815 ((equal method "window/workDoneProgress/create") 6816 nil ;; no specific reply, no processing required 6817 ) 6818 ((equal method "workspace/semanticTokens/refresh") 6819 (when (and lsp-semantic-tokens-enable 6820 (fboundp 'lsp--semantic-tokens-on-refresh)) 6821 (lsp--semantic-tokens-on-refresh workspace)) 6822 nil) 6823 ((equal method "workspace/codeLens/refresh") 6824 (when (and lsp-lens-enable 6825 (fboundp 'lsp--lens-on-refresh)) 6826 (lsp--lens-on-refresh workspace)) 6827 nil) 6828 (t (lsp-warn "Unknown request method: %s" method) nil)))) 6829 ;; Send response to the server. 6830 (unless (eq response 'delay-response) 6831 (lsp--send-request-response workspace recv-time request response)))) 6832 6833 (lsp-defun lsp--error-string ((&JSONError :message :code)) 6834 "Format ERR as a user friendly string." 6835 (format "Error from the Language Server: %s (%s)" 6836 message 6837 (or (car (alist-get code lsp--errors)) "Unknown error"))) 6838 6839 (defun lsp--get-body-length (headers) 6840 (let ((content-length (cdr (assoc "Content-Length" headers)))) 6841 (if content-length 6842 (string-to-number content-length) 6843 6844 ;; This usually means either the server or our parser is 6845 ;; screwed up with a previous Content-Length 6846 (error "No Content-Length header")))) 6847 6848 (defun lsp--parse-header (s) 6849 "Parse string S as a LSP (KEY . VAL) header." 6850 (let ((pos (string-match "\:" s)) 6851 key val) 6852 (unless pos 6853 (signal 'lsp-invalid-header-name (list s))) 6854 (setq key (substring s 0 pos) 6855 val (s-trim-left (substring s (+ 1 pos)))) 6856 (when (equal key "Content-Length") 6857 (cl-assert (cl-loop for c across val 6858 when (or (> c ?9) (< c ?0)) return nil 6859 finally return t) 6860 nil (format "Invalid Content-Length value: %s" val))) 6861 (cons key val))) 6862 6863 (defmacro lsp--read-json (str) 6864 "Read json string STR." 6865 (if (progn 6866 (require 'json) 6867 (fboundp 'json-parse-string)) 6868 `(json-parse-string ,str 6869 :object-type (if lsp-use-plists 6870 'plist 6871 'hash-table) 6872 :null-object nil 6873 :false-object nil) 6874 `(let ((json-array-type 'vector) 6875 (json-object-type (if lsp-use-plists 6876 'plist 6877 'hash-table)) 6878 (json-false nil)) 6879 (json-read-from-string ,str)))) 6880 6881 (defmacro lsp-json-read-buffer () 6882 "Read json from the current buffer." 6883 (if (progn 6884 (require 'json) 6885 (fboundp 'json-parse-buffer)) 6886 `(json-parse-buffer :object-type (if lsp-use-plists 6887 'plist 6888 'hash-table) 6889 :null-object nil 6890 :false-object nil) 6891 `(let ((json-array-type 'vector) 6892 (json-object-type (if lsp-use-plists 6893 'plist 6894 'hash-table)) 6895 (json-false nil)) 6896 (json-read)))) 6897 6898 (defun lsp--read-json-file (file-path) 6899 "Read json file." 6900 (-> file-path 6901 (f-read-text) 6902 (lsp--read-json))) 6903 6904 (defun lsp--parser-on-message (json-data workspace) 6905 "Called when the parser P read a complete MSG from the server." 6906 (with-demoted-errors "Error processing message %S." 6907 (with-lsp-workspace workspace 6908 (let* ((client (lsp--workspace-client workspace)) 6909 (id (--when-let (lsp:json-response-id json-data) 6910 (if (stringp it) (string-to-number it) it))) 6911 (data (lsp:json-response-result json-data))) 6912 (pcase (lsp--get-message-type json-data) 6913 ('response 6914 (cl-assert id) 6915 (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))] 6916 (when (lsp--log-io-p method) 6917 (lsp--log-entry-new 6918 (lsp--make-log-entry method id data 'incoming-resp 6919 (lsp--ms-since before-send)) 6920 workspace)) 6921 (when callback 6922 (remhash id (lsp--client-response-handlers client)) 6923 (funcall callback (lsp:json-response-result json-data))))) 6924 ('response-error 6925 (cl-assert id) 6926 (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))] 6927 (when (lsp--log-io-p method) 6928 (lsp--log-entry-new 6929 (lsp--make-log-entry method id (lsp:json-response-error-error json-data) 6930 'incoming-resp (lsp--ms-since before-send)) 6931 workspace)) 6932 (when callback 6933 (remhash id (lsp--client-response-handlers client)) 6934 (funcall callback (lsp:json-response-error-error json-data))))) 6935 ('notification 6936 (lsp--on-notification workspace json-data)) 6937 ('request (lsp--on-request workspace json-data))))))) 6938 6939 (defun lsp--create-filter-function (workspace) 6940 "Make filter for the workspace." 6941 (let ((body-received 0) 6942 leftovers body-length body chunk) 6943 (lambda (_proc input) 6944 (setf chunk (if (s-blank? leftovers) 6945 input 6946 (concat leftovers input))) 6947 6948 (let (messages) 6949 (while (not (s-blank? chunk)) 6950 (if (not body-length) 6951 ;; Read headers 6952 (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk))) 6953 ;; We've got all the headers, handle them all at once: 6954 (setf body-length (lsp--get-body-length 6955 (mapcar #'lsp--parse-header 6956 (split-string 6957 (substring-no-properties chunk 6958 (or (string-match-p "Content-Length" chunk) 6959 (error "Unable to find Content-Length header.")) 6960 body-sep-pos) 6961 "\r\n"))) 6962 body-received 0 6963 leftovers nil 6964 chunk (substring-no-properties chunk (+ body-sep-pos 4))) 6965 6966 ;; Haven't found the end of the headers yet. Save everything 6967 ;; for when the next chunk arrives and await further input. 6968 (setf leftovers chunk 6969 chunk nil)) 6970 (let* ((chunk-length (string-bytes chunk)) 6971 (left-to-receive (- body-length body-received)) 6972 (this-body (if (< left-to-receive chunk-length) 6973 (prog1 (substring-no-properties chunk 0 left-to-receive) 6974 (setf chunk (substring-no-properties chunk left-to-receive))) 6975 (prog1 chunk 6976 (setf chunk nil)))) 6977 (body-bytes (string-bytes this-body))) 6978 (push this-body body) 6979 (setf body-received (+ body-received body-bytes)) 6980 (when (>= chunk-length left-to-receive) 6981 (condition-case err 6982 (with-temp-buffer 6983 (apply #'insert 6984 (nreverse 6985 (prog1 body 6986 (setf leftovers nil 6987 body-length nil 6988 body-received nil 6989 body nil)))) 6990 (decode-coding-region (point-min) 6991 (point-max) 6992 'utf-8) 6993 (goto-char (point-min)) 6994 (push (lsp-json-read-buffer) messages)) 6995 6996 (error 6997 (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s" 6998 (concat leftovers input) 6999 err))))))) 7000 (mapc (lambda (msg) 7001 (lsp--parser-on-message msg workspace)) 7002 (nreverse messages)))))) 7003 7004 (defvar-local lsp--line-col-to-point-hash-table nil 7005 "Hash table with keys (line . col) and values that are either point positions 7006 or markers.") 7007 7008 (defcustom lsp-imenu-detailed-outline t 7009 "Whether `lsp-imenu' should include signatures. 7010 This will be ignored if the server doesn't provide the necessary 7011 information, for example if it doesn't support DocumentSymbols." 7012 :group 'lsp-imenu 7013 :type 'boolean) 7014 7015 (defcustom lsp-imenu-hide-parent-details t 7016 "Whether `lsp-imenu' should hide signatures of parent nodes." 7017 :group 'lsp-imenu 7018 :type 'boolean) 7019 7020 (defface lsp-details-face '((t :height 0.8 :inherit shadow)) 7021 "Used to display additional information throughout `lsp'. 7022 Things like line numbers, signatures, ... are considered 7023 additional information. Often, additional faces are defined that 7024 inherit from this face by default, like `lsp-signature-face', and 7025 they may be customized for finer control." 7026 :group 'lsp-mode) 7027 7028 (defface lsp-signature-face '((t :inherit lsp-details-face)) 7029 "Used to display signatures in `imenu', ...." 7030 :group 'lsp-mode) 7031 7032 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?) 7033 show-detail?) 7034 "Render INPUT0, an `&DocumentSymbol', to a string. 7035 If SHOW-DETAIL? is set, make use of its `:detail?' field (often 7036 the signature)." 7037 (let ((detail (and show-detail? (s-present? detail?) 7038 (propertize (concat " " (s-trim-left detail?)) 7039 'face 'lsp-signature-face))) 7040 (name (if deprecated? 7041 (propertize name 'face 'lsp-face-semhl-deprecated) name))) 7042 (concat name detail))) 7043 7044 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?) 7045 separator) 7046 "Render a piece of SymbolInformation. 7047 Handle :deprecated?. If SEPARATOR is non-nil, the 7048 symbol's (optional) parent, SEPARATOR and the symbol itself are 7049 concatenated." 7050 (when (and separator container-name? (not (string-empty-p container-name?))) 7051 (setq name (concat name separator container-name?))) 7052 (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name)) 7053 7054 (defun lsp--symbol-to-imenu-elem (sym) 7055 "Convert SYM to imenu element. 7056 7057 SYM is a SymbolInformation message. 7058 7059 Return a cons cell (full-name . start-point)." 7060 (let ((start-point (ht-get lsp--line-col-to-point-hash-table 7061 (lsp--get-line-and-col sym)))) 7062 (cons (lsp-render-symbol-information 7063 sym (and lsp-imenu-show-container-name 7064 lsp-imenu-container-name-separator)) 7065 start-point))) 7066 7067 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?)) 7068 "Convert SYM to hierarchical imenu elements. 7069 7070 SYM is a DocumentSymbol message. 7071 7072 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if 7073 SYM doesn't have any children. Otherwise return a cons cell with 7074 an alist 7075 7076 (\"symbol-name\" . ((\"(symbol-kind)\" . start-point) 7077 cons-cells-from-children))" 7078 (let ((filtered-children (lsp--imenu-filter-symbols children?)) 7079 (signature (lsp-render-symbol sym lsp-imenu-detailed-outline))) 7080 (if (seq-empty-p filtered-children) 7081 (cons signature 7082 (ht-get lsp--line-col-to-point-hash-table 7083 (lsp--get-line-and-col sym))) 7084 (cons signature 7085 (lsp--imenu-create-hierarchical-index filtered-children))))) 7086 7087 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind)) 7088 "Determine if SYM is for the current document and is to be shown." 7089 ;; It's a SymbolInformation or DocumentSymbol, which is always in the 7090 ;; current buffer file. 7091 (and lsp-imenu-index-symbol-kinds 7092 (numberp kind) 7093 (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup)) 7094 kind 7095 0))) 7096 (not (memql (aref lsp/symbol-kind-lookup clamped-kind) 7097 lsp-imenu-index-symbol-kinds))))) 7098 7099 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind)) 7100 "The string name of the kind of SYM." 7101 (alist-get kind lsp-symbol-kinds "Other")) 7102 7103 (defun lsp--get-line-and-col (sym) 7104 "Obtain the line and column corresponding to SYM." 7105 (-let* ((location (lsp:symbol-information-location sym)) 7106 (name-range (or (and location (lsp:location-range location)) 7107 (lsp:document-symbol-selection-range sym))) 7108 ((&Range :start (&Position :line :character)) name-range)) 7109 (cons line character))) 7110 7111 (defun lsp--collect-lines-and-cols (symbols) 7112 "Return a sorted list ((line . col) ...) of the locations of SYMBOLS." 7113 (let ((stack (mapcar 'identity symbols)) 7114 line-col-list) 7115 (while stack 7116 (let ((sym (pop stack))) 7117 (push (lsp--get-line-and-col sym) line-col-list) 7118 (unless (seq-empty-p (lsp:document-symbol-children? sym)) 7119 (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack))))) 7120 (-sort #'lsp--line-col-comparator line-col-list))) 7121 7122 (defun lsp--convert-line-col-to-points-batch (line-col-list) 7123 "Convert a sorted list of positions from line-column 7124 representation to point representation." 7125 (let ((line-col-to-point-map (ht-create)) 7126 (inhibit-field-text-motion t) 7127 (curr-line 0)) 7128 (lsp-save-restriction-and-excursion 7129 (goto-char (point-min)) 7130 (cl-loop for (line . col) in line-col-list do 7131 (forward-line (- line curr-line)) 7132 (setq curr-line line) 7133 (let ((line-end (line-end-position))) 7134 (if (or (not col) (> col (- line-end (point)))) 7135 (goto-char line-end) 7136 (forward-char col))) 7137 (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers 7138 (point-marker) 7139 (point))))) 7140 line-col-to-point-map)) 7141 7142 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2)) 7143 (or (< l1 l2) 7144 (and (= l1 l2) 7145 (cond ((and c1 c2) 7146 (< c1 c2)) 7147 (c1 t))))) 7148 7149 (defun lsp-imenu-create-uncategorized-index (symbols) 7150 "Create imenu index from document SYMBOLS. 7151 This function, unlike `lsp-imenu-create-categorized-index', does 7152 not categorize by type, but instead returns an `imenu' index 7153 corresponding to the symbol hierarchy returned by the server 7154 directly." 7155 (let* ((lsp--line-col-to-point-hash-table (-> symbols 7156 lsp--collect-lines-and-cols 7157 lsp--convert-line-col-to-points-batch))) 7158 (if (lsp--imenu-hierarchical-p symbols) 7159 (lsp--imenu-create-hierarchical-index symbols) 7160 (lsp--imenu-create-non-hierarchical-index symbols)))) 7161 7162 (defcustom lsp-imenu-symbol-kinds 7163 '((1 . "Files") 7164 (2 . "Modules") 7165 (3 . "Namespaces") 7166 (4 . "Packages") 7167 (5 . "Classes") 7168 (6 . "Methods") 7169 (7 . "Properties") 7170 (8 . "Fields") 7171 (9 . "Constructors") 7172 (10 . "Enums") 7173 (11 . "Interfaces") 7174 (12 . "Functions") 7175 (13 . "Variables") 7176 (14 . "Constants") 7177 (15 . "Strings") 7178 (16 . "Numbers") 7179 (17 . "Booleans") 7180 (18 . "Arrays") 7181 (19 . "Objects") 7182 (20 . "Keys") 7183 (21 . "Nulls") 7184 (22 . "Enum Members") 7185 (23 . "Structs") 7186 (24 . "Events") 7187 (25 . "Operators") 7188 (26 . "Type Parameters")) 7189 "`lsp-symbol-kinds', but only used by `imenu'. 7190 A new variable is needed, as it is `imenu' convention to use 7191 pluralized categories, which `lsp-symbol-kinds' doesn't. If the 7192 non-pluralized names are preferred, this can be set to 7193 `lsp-symbol-kinds'." 7194 :type '(alist :key-type integer :value-type string)) 7195 7196 (defun lsp--imenu-kind->name (kind) 7197 (alist-get kind lsp-imenu-symbol-kinds "?")) 7198 7199 (defun lsp-imenu-create-top-level-categorized-index (symbols) 7200 "Create an `imenu' index categorizing SYMBOLS by type. 7201 Only root symbols are categorized. 7202 7203 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS 7204 shall be a list of DocumentSymbols or SymbolInformation." 7205 (mapcan 7206 (-lambda ((type . symbols)) 7207 (let ((cat (lsp--imenu-kind->name type)) 7208 (symbols (lsp-imenu-create-uncategorized-index symbols))) 7209 ;; If there is no :kind (this is being defensive), or we couldn't look it 7210 ;; up, just display the symbols inline, without categories. 7211 (if cat (list (cons cat symbols)) symbols))) 7212 (sort (seq-group-by #'lsp:document-symbol-kind symbols) 7213 (-lambda ((kinda) (kindb)) (< kinda kindb))))) 7214 7215 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start))) 7216 "Convert an `&DocumentSymbol' to an `imenu' entry." 7217 (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start)) 7218 7219 (defun lsp--imenu-create-categorized-index-1 (symbols) 7220 "Returns an `imenu' index from SYMBOLS categorized by type. 7221 The result looks like this: ((\"Variables\" . (...)))." 7222 (->> 7223 symbols 7224 (mapcan 7225 (-lambda ((sym &as &DocumentSymbol :kind :children?)) 7226 (if (seq-empty-p children?) 7227 (list (list kind (lsp--symbol->imenu sym))) 7228 (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline 7229 (not lsp-imenu-hide-parent-details))))) 7230 (cons 7231 (list kind (lsp--symbol->imenu sym)) 7232 (mapcar (-lambda ((type . imenu-items)) 7233 (list type (cons parent (mapcan #'cdr imenu-items)))) 7234 (-group-by #'car (lsp--imenu-create-categorized-index-1 children?)))))))) 7235 (-group-by #'car) 7236 (mapcar 7237 (-lambda ((kind . syms)) 7238 (cons kind (mapcan #'cdr syms)))))) 7239 7240 (defun lsp--imenu-create-categorized-index (symbols) 7241 (let ((syms (lsp--imenu-create-categorized-index-1 symbols))) 7242 (dolist (sym syms) 7243 (setcar sym (lsp--imenu-kind->name (car sym)))) 7244 syms)) 7245 7246 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start)))) 7247 (cons (lsp-render-symbol-information sym nil) start)) 7248 7249 (defun lsp--imenu-create-categorized-index-flat (symbols) 7250 "Create a kind-categorized index for SymbolInformation." 7251 (mapcar (-lambda ((kind . syms)) 7252 (cons (lsp--imenu-kind->name kind) 7253 (mapcan (-lambda ((parent . children)) 7254 (let ((children (mapcar #'lsp--symbol-information->imenu children))) 7255 (if parent (list (cons parent children)) children))) 7256 (-group-by #'lsp:symbol-information-container-name? syms)))) 7257 (seq-group-by #'lsp:symbol-information-kind symbols))) 7258 7259 (defun lsp-imenu-create-categorized-index (symbols) 7260 (if (lsp--imenu-hierarchical-p symbols) 7261 (lsp--imenu-create-categorized-index symbols) 7262 (lsp--imenu-create-categorized-index-flat symbols))) 7263 7264 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index 7265 "Function that should create an `imenu' index. 7266 It will be called with a list of SymbolInformation or 7267 DocumentSymbols, whose first level is already filtered. It shall 7268 then return an appropriate `imenu' index (see 7269 `imenu-create-index-function'). 7270 7271 Note that this interface is not stable, and subject to change any 7272 time." 7273 :group 'lsp-imenu 7274 :type '(radio 7275 (const :tag "Categorize by type" 7276 lsp-imenu-create-categorized-index) 7277 (const :tag "Categorize root symbols by type" 7278 lsp-imenu-create-top-level-categorized-index) 7279 (const :tag "Uncategorized, inline entries" 7280 lsp-imenu-create-uncategorized-index) 7281 (function :tag "Custom function"))) 7282 7283 (defun lsp--imenu-create-index () 7284 "Create an `imenu' index based on the language server. 7285 Respects `lsp-imenu-index-function'." 7286 (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols)))) 7287 (funcall lsp-imenu-index-function symbols))) 7288 7289 (defun lsp--imenu-filter-symbols (symbols) 7290 "Filter out unsupported symbols from SYMBOLS." 7291 (seq-remove #'lsp--symbol-ignore symbols)) 7292 7293 (defun lsp--imenu-hierarchical-p (symbols) 7294 "Determine whether any element in SYMBOLS has children." 7295 (seq-some #'lsp-document-symbol? symbols)) 7296 7297 (defun lsp--imenu-create-non-hierarchical-index (symbols) 7298 "Create imenu index for non-hierarchical SYMBOLS. 7299 7300 SYMBOLS are a list of DocumentSymbol messages. 7301 7302 Return a nested alist keyed by symbol names. e.g. 7303 7304 ((\"SomeClass\" (\"(Class)\" . 10) 7305 (\"someField (Field)\" . 20) 7306 (\"someFunction (Function)\" . 25) 7307 (\"SomeSubClass\" (\"(Class)\" . 30) 7308 (\"someSubField (Field)\" . 35)) 7309 (\"someFunction (Function)\" . 40))" 7310 (seq-map (lambda (nested-alist) 7311 (cons (car nested-alist) 7312 (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist)))) 7313 (seq-group-by #'lsp--get-symbol-type symbols))) 7314 7315 (defun lsp--imenu-create-hierarchical-index (symbols) 7316 "Create imenu index for hierarchical SYMBOLS. 7317 7318 SYMBOLS are a list of DocumentSymbol messages. 7319 7320 Return a nested alist keyed by symbol names. e.g. 7321 7322 ((\"SomeClass\" (\"(Class)\" . 10) 7323 (\"someField (Field)\" . 20) 7324 (\"someFunction (Function)\" . 25) 7325 (\"SomeSubClass\" (\"(Class)\" . 30) 7326 (\"someSubField (Field)\" . 35)) 7327 (\"someFunction (Function)\" . 40))" 7328 (seq-map #'lsp--symbol-to-hierarchical-imenu-elem 7329 (seq-sort #'lsp--imenu-symbol-lessp symbols))) 7330 7331 (defun lsp--imenu-symbol-lessp (sym1 sym2) 7332 (let* ((compare-results (mapcar (lambda (method) 7333 (funcall (alist-get method lsp--imenu-compare-function-alist) 7334 sym1 sym2)) 7335 lsp-imenu-sort-methods)) 7336 (result (seq-find (lambda (result) 7337 (not (= result 0))) 7338 compare-results 7339 0))) 7340 (and (numberp result) (< result 0)))) 7341 7342 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left) 7343 (&SymbolInformation :kind right)) 7344 "Compare SYM1 and SYM2 by kind." 7345 (- left right)) 7346 7347 (defun lsp--imenu-compare-line-col (sym1 sym2) 7348 (if (lsp--line-col-comparator 7349 (lsp--get-line-and-col sym1) 7350 (lsp--get-line-and-col sym2)) 7351 -1 7352 1)) 7353 7354 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1) 7355 (&SymbolInformation :name name2)) 7356 "Compare SYM1 and SYM2 by name." 7357 (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2)))) 7358 (if (numberp result) result 0))) 7359 7360 (defun lsp--imenu-refresh () 7361 "Force Imenu to refresh itself." 7362 (imenu--menubar-select imenu--rescan-item)) 7363 7364 (defun lsp-enable-imenu () 7365 "Use lsp-imenu for the current buffer." 7366 (imenu--cleanup) 7367 (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index) 7368 (setq-local imenu-menubar-modified-tick -1) 7369 (setq-local imenu--index-alist nil) 7370 (when menu-bar-mode 7371 (lsp--imenu-refresh))) 7372 7373 (defun lsp-resolve-final-command (command &optional test?) 7374 "Resolve final function COMMAND." 7375 (let* ((command (lsp-resolve-value command)) 7376 (command (cl-etypecase command 7377 (list 7378 (cl-assert (seq-every-p (apply-partially #'stringp) command) nil 7379 "Invalid command list") 7380 command) 7381 (string (list command))))) 7382 (if (and (file-remote-p default-directory) (not test?)) 7383 (list shell-file-name "-c" 7384 (string-join (cons "stty raw > /dev/null;" 7385 (mapcar #'shell-quote-argument command)) 7386 " ")) 7387 command))) 7388 7389 (defun lsp-server-present? (final-command) 7390 "Check whether FINAL-COMMAND is present." 7391 (let ((binary-found? (executable-find (cl-first final-command) t))) 7392 (if binary-found? 7393 (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command)) 7394 (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command))) 7395 binary-found?)) 7396 7397 (defun lsp--value-to-string (value) 7398 "Convert VALUE to a string that can be set as value in an environment 7399 variable." 7400 (cond 7401 ((stringp value) value) 7402 ((booleanp value) (if value 7403 "1" 7404 "0")) 7405 ((and (sequencep value) 7406 (seq-every-p #'stringp value)) (string-join value ":")) 7407 (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables")))) 7408 7409 (defun lsp--compute-process-environment (environment-fn) 7410 "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'. 7411 Ignore non-boolean keys whose value is nil." 7412 (let ((environment (if environment-fn 7413 (funcall environment-fn) 7414 nil))) 7415 (-flatten (cons (cl-loop for (key . value) in environment 7416 if (or (eval value) 7417 (eq (get value 'custom-type) 'boolean)) 7418 collect (concat key "=" (lsp--value-to-string 7419 (eval value)))) 7420 process-environment)))) 7421 7422 (defun lsp--default-directory-for-connection (&optional path) 7423 "Return path to be used for the working directory of a LSP process. 7424 7425 If `lsp-use-workspace-root-for-server-default-directory' is 7426 non-nil, uses `lsp-workspace-root' to find the directory 7427 corresponding to PATH, else returns `default-directory'." 7428 (if lsp-use-workspace-root-for-server-default-directory 7429 (lsp-workspace-root path) 7430 default-directory)) 7431 7432 (defun lsp--fix-remote-cmd (program) 7433 "Helper for `lsp-stdio-connection'. 7434 Originally coppied from eglot." 7435 7436 (if (file-remote-p default-directory) 7437 (list shell-file-name "-c" 7438 (string-join (cons "stty raw > /dev/null;" 7439 (mapcar #'shell-quote-argument program)) 7440 " ")) 7441 program)) 7442 7443 (defvar tramp-use-ssh-controlmaster-options) 7444 (defvar tramp-ssh-controlmaster-options) 7445 7446 (defun lsp-stdio-connection (command &optional test-command) 7447 "Returns a connection property list using COMMAND. 7448 COMMAND can be: A string, denoting the command to launch the 7449 language server. A list of strings, denoting an executable with 7450 its command line arguments. A function, that either returns a 7451 string or a list of strings. In all cases, the launched language 7452 server should send and receive messages on standard I/O. 7453 TEST-COMMAND is a function with no arguments which returns 7454 whether the command is present or not. When not specified 7455 `lsp-mode' will check whether the first element of the list 7456 returned by COMMAND is available via `executable-find'" 7457 (cl-check-type command (or string 7458 function 7459 (and list 7460 (satisfies (lambda (l) 7461 (seq-every-p (lambda (el) 7462 (stringp el)) 7463 l)))))) 7464 (list :connect (lambda (filter sentinel name environment-fn workspace) 7465 (if (and (functionp 'json-rpc-connection) 7466 (not (file-remote-p default-directory))) 7467 (lsp-json-rpc-connection workspace (lsp-resolve-final-command command)) 7468 (let ((final-command (lsp-resolve-final-command command)) 7469 (process-name (generate-new-buffer-name name)) 7470 (process-environment 7471 (lsp--compute-process-environment environment-fn))) 7472 (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name))) 7473 (default-directory (lsp--default-directory-for-connection)) 7474 (tramp-use-ssh-controlmaster-options 'suppress) 7475 (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none") 7476 (proc (make-process 7477 :name process-name 7478 :connection-type 'pipe 7479 :buffer (format "*%s*" process-name) 7480 :coding 'no-conversion 7481 :command final-command 7482 :filter filter 7483 :sentinel sentinel 7484 :stderr stderr-buf 7485 :noquery t 7486 :file-handler t))) 7487 (set-process-query-on-exit-flag proc nil) 7488 (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) 7489 (with-current-buffer (get-buffer stderr-buf) 7490 ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc. 7491 (special-mode)) 7492 (cons proc proc))))) 7493 :test? (or 7494 test-command 7495 (lambda () 7496 (lsp-server-present? (lsp-resolve-final-command command t)))))) 7497 7498 (defun lsp--open-network-stream (host port name) 7499 "Open network stream to HOST:PORT. 7500 NAME will be passed to `open-network-stream'. 7501 RETRY-COUNT is the number of the retries. 7502 SLEEP-INTERVAL is the sleep interval between each retry." 7503 (let* ((retries 0) 7504 (sleep-interval 0.01) 7505 (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval)) 7506 connection) 7507 (while (and (not connection) (< retries number-of-retries)) 7508 (condition-case err 7509 (setq connection (open-network-stream name nil host port 7510 :type 'plain 7511 :coding 'no-conversion)) 7512 (file-error 7513 (let ((inhibit-message t)) 7514 (lsp--warn "Failed to connect to %s:%s with error message %s" 7515 host 7516 port 7517 (error-message-string err)) 7518 (sleep-for sleep-interval) 7519 (cl-incf retries))))) 7520 (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port)))) 7521 7522 (defun lsp--port-available (host port) 7523 "Return non-nil if HOST and PORT are available." 7524 (condition-case _err 7525 (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain)) 7526 (file-error t))) 7527 7528 (defun lsp--find-available-port (host starting-port) 7529 "Find available port on HOST starting from STARTING-PORT." 7530 (let ((port starting-port)) 7531 (while (not (lsp--port-available host port)) 7532 (cl-incf port)) 7533 port)) 7534 7535 (defun lsp-tcp-connection (command-fn) 7536 "Returns a connection property list similar to `lsp-stdio-connection'. 7537 COMMAND-FN can only be a function that takes a single argument, a 7538 port number. It should return a command for launches a language server 7539 process listening for TCP connections on the provided port." 7540 (cl-check-type command-fn function) 7541 (list 7542 :connect (lambda (filter sentinel name environment-fn _workspace) 7543 (let* ((host "localhost") 7544 (port (lsp--find-available-port host (cl-incf lsp--tcp-port))) 7545 (command (funcall command-fn port)) 7546 (final-command (if (consp command) command (list command))) 7547 (_ (unless (lsp-server-present? final-command) 7548 (user-error (format "Couldn't find executable %s" (cl-first final-command))))) 7549 (process-environment 7550 (lsp--compute-process-environment environment-fn)) 7551 (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion 7552 :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t)) 7553 (tcp-proc (lsp--open-network-stream host port (concat name "::tcp")))) 7554 7555 ;; TODO: Same :noquery issue (see above) 7556 (set-process-query-on-exit-flag proc nil) 7557 (set-process-query-on-exit-flag tcp-proc nil) 7558 (set-process-filter tcp-proc filter) 7559 (cons tcp-proc proc))) 7560 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7561 7562 (defalias 'lsp-tcp-server 'lsp-tcp-server-command) 7563 7564 (defun lsp-tcp-server-command (command-fn) 7565 "Create tcp server connection. 7566 In this mode Emacs is TCP server and the language server connects 7567 to it. COMMAND is function with one parameter(the port) and it 7568 should return the command to start the LS server." 7569 (cl-check-type command-fn function) 7570 (list 7571 :connect (lambda (filter sentinel name environment-fn _workspace) 7572 (let* (tcp-client-connection 7573 (tcp-server (make-network-process :name (format "*tcp-server-%s*" name) 7574 :buffer (format "*tcp-server-%s*" name) 7575 :family 'ipv4 7576 :service lsp--tcp-server-port 7577 :sentinel (lambda (proc _string) 7578 (lsp-log "Language server %s is connected." name) 7579 (setf tcp-client-connection proc)) 7580 :server 't)) 7581 (port (process-contact tcp-server :service)) 7582 (final-command (funcall command-fn port)) 7583 (process-environment 7584 (lsp--compute-process-environment environment-fn)) 7585 (cmd-proc (make-process :name name 7586 :connection-type 'pipe 7587 :coding 'no-conversion 7588 :command final-command 7589 :stderr (format "*tcp-server-%s*::stderr" name) 7590 :noquery t))) 7591 (let ((retries 0)) 7592 ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds) 7593 (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds))) 7594 (lsp--info "Waiting for connection for %s, retries: %s" name retries) 7595 (sit-for 0.500) 7596 (cl-incf retries))) 7597 7598 (unless tcp-client-connection 7599 (condition-case nil (delete-process tcp-server) (error)) 7600 (condition-case nil (delete-process cmd-proc) (error)) 7601 (error "Failed to create connection to %s on port %s" name port)) 7602 (lsp--info "Successfully connected to %s" name) 7603 7604 (set-process-query-on-exit-flag cmd-proc nil) 7605 (set-process-query-on-exit-flag tcp-client-connection nil) 7606 (set-process-query-on-exit-flag tcp-server nil) 7607 7608 (set-process-filter tcp-client-connection filter) 7609 (set-process-sentinel tcp-client-connection sentinel) 7610 (cons tcp-client-connection cmd-proc))) 7611 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7612 7613 (defalias 'lsp-tramp-connection 'lsp-stdio-connection) 7614 7615 (defun lsp--auto-configure () 7616 "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed." 7617 (when (functionp 'lsp-ui-mode) 7618 (lsp-ui-mode)) 7619 7620 (if lsp-headerline-breadcrumb-enable 7621 (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode) 7622 (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)) 7623 (if lsp-modeline-code-actions-enable 7624 (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode) 7625 (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)) 7626 (if lsp-modeline-diagnostics-enable 7627 (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode) 7628 (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)) 7629 (if lsp-modeline-workspace-status-enable 7630 (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode) 7631 (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)) 7632 (if lsp-lens-enable 7633 (add-hook 'lsp-configure-hook 'lsp-lens--enable) 7634 (remove-hook 'lsp-configure-hook 'lsp-lens--enable)) 7635 (if lsp-semantic-tokens-enable 7636 (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable) 7637 (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)) 7638 7639 ;; yas-snippet config 7640 (setq-local yas-inhibit-overlay-modification-protection t)) 7641 7642 (defun lsp--restart-if-needed (workspace) 7643 "Handler restart for WORKSPACE." 7644 (when (or (eq lsp-restart 'auto-restart) 7645 (eq (lsp--workspace-shutdown-action workspace) 'restart) 7646 (and (eq lsp-restart 'interactive) 7647 (let ((query (format 7648 "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?" 7649 (lsp--workspace-print workspace)))) 7650 (y-or-n-p query)))) 7651 (--each (lsp--workspace-buffers workspace) 7652 (when (lsp-buffer-live-p it) 7653 (lsp-with-current-buffer it 7654 (if lsp--buffer-deferred 7655 (lsp-deferred) 7656 (lsp--info "Restarting LSP in buffer %s" (buffer-name)) 7657 (lsp))))))) 7658 7659 (defun lsp--update-key (table key fn) 7660 "Apply FN on value corresponding to KEY in TABLE." 7661 (let ((existing-value (gethash key table))) 7662 (if-let ((new-value (funcall fn existing-value))) 7663 (puthash key new-value table) 7664 (remhash key table)))) 7665 7666 (defun lsp--process-sentinel (workspace process exit-str) 7667 "Create the sentinel for WORKSPACE." 7668 (unless (process-live-p process) 7669 (lsp--handle-process-exit workspace exit-str))) 7670 7671 (defun lsp--handle-process-exit (workspace exit-str) 7672 (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session))) 7673 (proc (lsp--workspace-proc workspace))) 7674 (lsp--warn "%s has exited (%s)" 7675 (lsp-process-name proc) 7676 (string-trim-right (or exit-str ""))) 7677 (with-lsp-workspace workspace 7678 ;; Clean workspace related data in each of the buffers 7679 ;; in the workspace. 7680 (--each (lsp--workspace-buffers workspace) 7681 (when (lsp-buffer-live-p it) 7682 (lsp-with-current-buffer it 7683 (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces)) 7684 (lsp--uninitialize-workspace) 7685 (lsp--spinner-stop) 7686 (lsp--remove-overlays 'lsp-highlight)))) 7687 7688 ;; Cleanup session from references to the closed workspace. 7689 (--each (hash-table-keys folder->workspaces) 7690 (lsp--update-key folder->workspaces it (apply-partially 'delete workspace))) 7691 7692 (lsp-process-cleanup proc)) 7693 7694 (run-hook-with-args 'lsp-after-uninitialized-functions workspace) 7695 7696 (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown) 7697 (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace)) 7698 (lsp--restart-if-needed workspace)) 7699 (lsp--cleanup-hanging-watches))) 7700 7701 (defun lsp-workspace-folders (workspace) 7702 "Return all folders associated with WORKSPACE." 7703 (let (result) 7704 (->> (lsp-session) 7705 (lsp-session-folder->servers) 7706 (maphash (lambda (folder workspaces) 7707 (when (-contains? workspaces workspace) 7708 (push folder result))))) 7709 result)) 7710 7711 (defun lsp--start-workspace (session client-template root &optional initialization-options) 7712 "Create new workspace for CLIENT-TEMPLATE with project root ROOT. 7713 INITIALIZATION-OPTIONS are passed to initialize function. 7714 SESSION is the active session." 7715 (lsp--spinner-start) 7716 (-let* ((default-directory root) 7717 (client (copy-lsp--client client-template)) 7718 (workspace (make-lsp--workspace 7719 :root root 7720 :client client 7721 :status 'starting 7722 :buffers (list (lsp-current-buffer)) 7723 :host-root (file-remote-p root))) 7724 ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities 7725 'multi-root 'initialized-fn) client) 7726 ((proc . cmd-proc) (funcall 7727 (or (plist-get new-connection :connect) 7728 (user-error "Client %s is configured incorrectly" client)) 7729 (lsp--create-filter-function workspace) 7730 (apply-partially #'lsp--process-sentinel workspace) 7731 (format "%s" server-id) 7732 environment-fn 7733 workspace)) 7734 (workspace-folders (gethash server-id (lsp-session-server-id->folders session)))) 7735 (setf (lsp--workspace-proc workspace) proc 7736 (lsp--workspace-cmd-proc workspace) cmd-proc) 7737 7738 ;; update (lsp-session-folder->servers) depending on whether we are starting 7739 ;; multi/single folder workspace 7740 (mapc (lambda (project-root) 7741 (->> session 7742 (lsp-session-folder->servers) 7743 (gethash project-root) 7744 (cl-pushnew workspace))) 7745 (or workspace-folders (list root))) 7746 7747 (with-lsp-workspace workspace 7748 (run-hooks 'lsp-before-initialize-hook) 7749 (lsp-request-async 7750 "initialize" 7751 (append 7752 (list :processId (unless (file-remote-p (buffer-file-name)) 7753 (emacs-pid)) 7754 :rootPath (lsp-file-local-name (expand-file-name root)) 7755 :clientInfo (list :name "emacs" 7756 :version (emacs-version)) 7757 :rootUri (lsp--path-to-uri root) 7758 :capabilities (lsp--client-capabilities custom-capabilities) 7759 :initializationOptions initialization-options 7760 :workDoneToken "1") 7761 (when lsp-server-trace 7762 (list :trace lsp-server-trace)) 7763 (when multi-root 7764 (->> workspace-folders 7765 (-distinct) 7766 (-map (lambda (folder) 7767 (list :uri (lsp--path-to-uri folder) 7768 :name (f-filename folder)))) 7769 (apply 'vector) 7770 (list :workspaceFolders)))) 7771 (-lambda ((&InitializeResult :capabilities)) 7772 ;; we know that Rust Analyzer will send {} which will be parsed as null 7773 ;; when using plists 7774 (when (equal 'rust-analyzer server-id) 7775 (-> capabilities 7776 (lsp:server-capabilities-text-document-sync?) 7777 (lsp:set-text-document-sync-options-save? t))) 7778 7779 (setf (lsp--workspace-server-capabilities workspace) capabilities 7780 (lsp--workspace-status workspace) 'initialized) 7781 7782 (with-lsp-workspace workspace 7783 (lsp-notify "initialized" lsp--empty-ht)) 7784 7785 (when initialized-fn (funcall initialized-fn workspace)) 7786 7787 (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace)) 7788 (->> workspace 7789 (lsp--workspace-buffers) 7790 (mapc (lambda (buffer) 7791 (lsp-with-current-buffer buffer 7792 (lsp--open-in-workspace workspace))))) 7793 7794 (with-lsp-workspace workspace 7795 (run-hooks 'lsp-after-initialize-hook)) 7796 (lsp--info "%s initialized successfully in folders: %s" 7797 (lsp--workspace-print workspace) 7798 (lsp-workspace-folders workspace))) 7799 :mode 'detached)) 7800 workspace)) 7801 7802 (defun lsp--load-default-session () 7803 "Load default session." 7804 (setq lsp--session (or (condition-case err 7805 (lsp--read-from-file lsp-session-file) 7806 (error (lsp--error "Failed to parse the session %s, starting with clean one." 7807 (error-message-string err)) 7808 nil)) 7809 (make-lsp-session)))) 7810 7811 (defun lsp-session () 7812 "Get the session associated with the current buffer." 7813 (or lsp--session (setq lsp--session (lsp--load-default-session)))) 7814 7815 (defun lsp--client-disabled-p (buffer-major-mode client) 7816 (seq-some 7817 (lambda (entry) 7818 (pcase entry 7819 ((pred symbolp) (eq entry client)) 7820 (`(,mode . ,client-or-list) 7821 (and (eq mode buffer-major-mode) 7822 (if (listp client-or-list) 7823 (memq client client-or-list) 7824 (eq client client-or-list)))))) 7825 lsp-disabled-clients)) 7826 7827 7828 ;; download server 7829 7830 (defcustom lsp-server-install-dir (expand-file-name 7831 (locate-user-emacs-file (f-join ".cache" "lsp"))) 7832 "Directory in which the servers will be installed." 7833 :risky t 7834 :type 'directory 7835 :package-version '(lsp-mode . "6.3") 7836 :group 'lsp-mode) 7837 7838 (defcustom lsp-verify-signature t 7839 "Whether to check GPG signatures of downloaded files." 7840 :type 'boolean 7841 :package-version '(lsp-mode . "8.0.0") 7842 :group 'lsp-mode) 7843 7844 (defvar lsp--dependencies (ht)) 7845 7846 (defun lsp-dependency (name &rest definitions) 7847 "Used to specify a language server DEPENDENCY, the server 7848 executable or other required file path. Typically, the 7849 DEPENDENCY is found by locating it on the system path using 7850 `executable-find'. 7851 7852 You can explicitly call lsp-dependency in your environment to 7853 specify the absolute path to the DEPENDENCY. For example, the 7854 typescript-language-server requires both the server and the 7855 typescript compiler. If you have installed them in a team shared 7856 read-only location, you can instruct lsp-mode to use them via 7857 7858 (eval-after-load `lsp-mode 7859 `(progn 7860 (require lsp-javascript) 7861 (lsp-dependency typescript-language-server (:system ,tls-exe)) 7862 (lsp-dependency typescript (:system ,ts-js)))) 7863 7864 where tls-exe is the absolute path to the typescript-language-server 7865 executable and ts-js is the absolute path to the typescript compiler 7866 JavaScript file, tsserver.js (the *.js is required for Windows)." 7867 (ht-set lsp--dependencies name definitions)) 7868 7869 (defun lsp--server-binary-present? (client) 7870 (unless (equal (lsp--client-server-id client) 'lsp-pwsh) 7871 (condition-case () 7872 (-some-> client lsp--client-new-connection (plist-get :test?) funcall) 7873 (error nil) 7874 (args-out-of-range nil)))) 7875 7876 (define-minor-mode lsp-installation-buffer-mode 7877 "Mode used in *lsp-installation* buffers. 7878 It can be used to set-up keybindings, etc. Disabling this mode 7879 detaches the installation buffer from commands like 7880 `lsp-select-installation-buffer'." 7881 :init-value nil 7882 :lighter nil) 7883 7884 (defface lsp-installation-finished-buffer-face '((t :foreground "orange")) 7885 "Face used for finished installation buffers. 7886 Used in `lsp-select-installation-buffer'." 7887 :group 'lsp-mode) 7888 7889 (defface lsp-installation-buffer-face '((t :foreground "green")) 7890 "Face used for installation buffers still in progress. 7891 Used in `lsp-select-installation-buffer'." 7892 :group 'lsp-mode) 7893 7894 (defun lsp--installation-buffer? (buf) 7895 "Check whether BUF is an `lsp-async-start-process' buffer." 7896 (buffer-local-value 'lsp-installation-buffer-mode buf)) 7897 7898 (defun lsp-select-installation-buffer (&optional show-finished) 7899 "Interactively choose an installation buffer. 7900 If SHOW-FINISHED is set, leftover (finished) installation buffers 7901 are still shown." 7902 (interactive "P") 7903 (let ((bufs (--filter (and (lsp--installation-buffer? it) 7904 (or show-finished (get-buffer-process it))) 7905 (buffer-list)))) 7906 (pcase bufs 7907 (`nil (user-error "No installation buffers")) 7908 (`(,buf) (pop-to-buffer buf)) 7909 (bufs (pop-to-buffer (completing-read "Select installation buffer: " 7910 (--map (propertize (buffer-name it) 'face 7911 (if (get-buffer-process it) 7912 'lsp-installation-buffer-face 7913 'lsp-installation-finished-buffer-face)) 7914 bufs))))))) 7915 7916 (defun lsp-cleanup-installation-buffers () 7917 "Delete finished *lsp-installation* buffers." 7918 (interactive) 7919 (dolist (buf (buffer-list)) 7920 (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf))) 7921 (kill-buffer buf)))) 7922 7923 (defun lsp--download-status () 7924 (-some--> #'lsp--client-download-in-progress? 7925 (lsp--filter-clients it) 7926 (-map (-compose #'symbol-name #'lsp--client-server-id) it) 7927 (format "%s" it) 7928 (propertize it 'face 'success) 7929 (format " Installing following servers: %s" it) 7930 (propertize it 7931 'local-map (make-mode-line-mouse-map 7932 'mouse-1 #'lsp-select-installation-buffer) 7933 'mouse-face 'highlight))) 7934 7935 (defun lsp--install-server-internal (client &optional update?) 7936 (unless (lsp--client-download-server-fn client) 7937 (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation." 7938 (lsp--client-server-id client))) 7939 7940 (setf (lsp--client-download-in-progress? client) t) 7941 (add-to-list 'global-mode-string '(t (:eval (lsp--download-status)))) 7942 (cl-flet ((done 7943 (success? &optional error-message) 7944 ;; run with idle timer to make sure the lsp command is executed in 7945 ;; the main thread, see #2739. 7946 (run-with-timer 7947 0.0 7948 nil 7949 (lambda () 7950 (-let [(&lsp-cln 'server-id 'buffers) client] 7951 (setf (lsp--client-download-in-progress? client) nil 7952 (lsp--client-buffers client) nil) 7953 (if success? 7954 (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id 7955 (length buffers)) 7956 (lsp--error "Server %s install process failed with the following error message: %s. 7957 Check `*lsp-install*' and `*lsp-log*' buffer." 7958 server-id 7959 error-message)) 7960 (seq-do 7961 (lambda (buffer) 7962 (when (lsp-buffer-live-p buffer) 7963 (lsp-with-current-buffer buffer 7964 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 7965 global-mode-string) 7966 (when success? (lsp))))) 7967 buffers) 7968 (unless (lsp--filter-clients #'lsp--client-download-in-progress?) 7969 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 7970 global-mode-string))))))) 7971 (lsp--info "Download %s started." (lsp--client-server-id client)) 7972 (condition-case err 7973 (funcall 7974 (lsp--client-download-server-fn client) 7975 client 7976 (lambda () (done t)) 7977 (lambda (msg) (done nil msg)) 7978 update?) 7979 (error 7980 (done nil (error-message-string err)))))) 7981 7982 (defun lsp--require-packages () 7983 "Load `lsp-client-packages' if needed." 7984 (when (and lsp-auto-configure (not lsp--client-packages-required)) 7985 (seq-do (lambda (package) 7986 ;; loading client is slow and `lsp' can be called repeatedly 7987 (unless (featurep package) 7988 (require package nil t))) 7989 lsp-client-packages) 7990 (setq lsp--client-packages-required t))) 7991 7992 ;;;###autoload 7993 (defun lsp-install-server (update? &optional server-id) 7994 "Interactively install or re-install server. 7995 When prefix UPDATE? is t force installation even if the server is present." 7996 (interactive "P") 7997 (lsp--require-packages) 7998 (let* ((chosen-client (or (gethash server-id lsp-clients) 7999 (lsp--completing-read 8000 "Select server to install/re-install: " 8001 (or (->> lsp-clients 8002 (ht-values) 8003 (-filter (-andfn 8004 (-not #'lsp--client-download-in-progress?) 8005 #'lsp--client-download-server-fn))) 8006 (user-error "There are no servers with automatic installation")) 8007 (lambda (client) 8008 (let ((server-name (-> client lsp--client-server-id symbol-name))) 8009 (if (lsp--server-binary-present? client) 8010 (concat server-name " (Already installed)") 8011 server-name))) 8012 nil 8013 t))) 8014 (update? (or update? 8015 (and (not (lsp--client-download-in-progress? chosen-client)) 8016 (lsp--server-binary-present? chosen-client))))) 8017 (lsp--install-server-internal chosen-client update?))) 8018 8019 ;;;###autoload 8020 (defun lsp-uninstall-server (dir) 8021 "Delete a LSP server from `lsp-server-install-dir'." 8022 (interactive 8023 (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir)))) 8024 (unless (file-directory-p dir) 8025 (user-error "Couldn't find %s directory" dir)) 8026 (delete-directory dir 'recursive) 8027 (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir)))) 8028 8029 ;;;###autoload 8030 (defun lsp-uninstall-servers () 8031 "Uninstall all installed servers." 8032 (interactive) 8033 (let* ((dir lsp-server-install-dir) 8034 (servers (ignore-errors 8035 (directory-files dir t 8036 directory-files-no-dot-files-regexp)))) 8037 (if (or (not (file-directory-p dir)) (zerop (length servers))) 8038 (user-error "No servers to uninstall") 8039 (when (yes-or-no-p 8040 (format "Servers to uninstall: %d (%s), proceed? " 8041 (length servers) 8042 (mapconcat (lambda (server) 8043 (file-name-nondirectory (directory-file-name server))) 8044 servers " "))) 8045 (mapc #'lsp-uninstall-server servers) 8046 (message "All servers uninstalled"))))) 8047 8048 ;;;###autoload 8049 (defun lsp-update-server (&optional server-id) 8050 "Interactively update (reinstall) a server." 8051 (interactive) 8052 (lsp--require-packages) 8053 (let ((chosen-client (or (gethash server-id lsp-clients) 8054 (lsp--completing-read 8055 "Select server to update (if not on the list, probably you need to `lsp-install-server`): " 8056 (or (->> lsp-clients 8057 (ht-values) 8058 (-filter (-andfn 8059 (-not #'lsp--client-download-in-progress?) 8060 #'lsp--client-download-server-fn 8061 #'lsp--server-binary-present?))) 8062 (user-error "There are no servers to update")) 8063 (lambda (client) 8064 (-> client lsp--client-server-id symbol-name)) 8065 nil 8066 t)))) 8067 (lsp--install-server-internal chosen-client t))) 8068 8069 ;;;###autoload 8070 (defun lsp-update-servers () 8071 "Update (reinstall) all installed servers." 8072 (interactive) 8073 (lsp--require-packages) 8074 (mapc (lambda (client) (lsp--install-server-internal client t)) 8075 (-filter (-andfn 8076 (-not #'lsp--client-download-in-progress?) 8077 #'lsp--client-download-server-fn 8078 #'lsp--server-binary-present?) (hash-table-values lsp-clients)))) 8079 8080 ;;;###autoload 8081 (defun lsp-ensure-server (server-id) 8082 "Ensure server SERVER-ID" 8083 (lsp--require-packages) 8084 (if-let ((client (gethash server-id lsp-clients))) 8085 (unless (lsp--server-binary-present? client) 8086 (lsp--info "Server `%s' is not preset, installing..." server-id) 8087 (lsp-install-server nil server-id)) 8088 (warn "Unable to find server registration with id %s" server-id))) 8089 8090 (defun lsp-async-start-process (callback error-callback &rest command) 8091 "Start async process COMMAND with CALLBACK and ERROR-CALLBACK." 8092 (let ((name (cl-first command))) 8093 (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd) 8094 (not (null cmd))) 8095 command) 8096 " ") t 8097 (lambda (&rest _) 8098 (generate-new-buffer-name (format "*lsp-install: %s*" name)))) 8099 (lsp-installation-buffer-mode +1) 8100 (view-mode +1) 8101 (add-hook 8102 'compilation-finish-functions 8103 (lambda (_buf status) 8104 (if (string= "finished\n" status) 8105 (condition-case err 8106 (funcall callback) 8107 (error 8108 (funcall error-callback (error-message-string err)))) 8109 (funcall error-callback (s-trim-right status)))) 8110 nil t)))) 8111 8112 (defun lsp-resolve-value (value) 8113 "Resolve VALUE's value. 8114 If it is function - call it. 8115 If it is a variable - return it's value 8116 Otherwise returns value itself." 8117 (cond 8118 ((functionp value) (funcall value)) 8119 ((and (symbolp value) (boundp value)) (symbol-value value)) 8120 (value))) 8121 8122 (defvar lsp-deps-providers 8123 (list :npm (list :path #'lsp--npm-dependency-path 8124 :install #'lsp--npm-dependency-install) 8125 :cargo (list :path #'lsp--cargo-dependency-path 8126 :install #'lsp--cargo-dependency-install) 8127 :system (list :path #'lsp--system-path) 8128 :download (list :path #'lsp-download-path 8129 :install #'lsp-download-install))) 8130 8131 (defun lsp--system-path (path) 8132 "If PATH is absolute and exists return it as is. Otherwise, 8133 return the absolute path to the executable defined by PATH or 8134 nil." 8135 ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the 8136 ;; typescript-language-server. When lsp invokes the server, lsp needs to 8137 ;; supply the path to the typescript compiler, tsserver.js, as an argument. To 8138 ;; make code platform independent, one must pass the absolute path to the 8139 ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript 8140 ;; child process spawn command that is invoked by the 8141 ;; typescript-language-server). This is why we check for existence and not 8142 ;; that the path is executable. 8143 (let ((path (lsp-resolve-value path))) 8144 (cond 8145 ((and (f-absolute? path) 8146 (f-exists? path)) 8147 path) 8148 ((executable-find path t) path)))) 8149 8150 (defun lsp-package-path (dependency) 8151 "Path to the DEPENDENCY each of the registered providers." 8152 (let (path) 8153 (-first (-lambda ((provider . rest)) 8154 (setq path (-some-> lsp-deps-providers 8155 (plist-get provider) 8156 (plist-get :path) 8157 (apply rest)))) 8158 (gethash dependency lsp--dependencies)) 8159 path)) 8160 8161 (defun lsp-package-ensure (dependency callback error-callback) 8162 "Asynchronously ensure a package." 8163 (or (-first (-lambda ((provider . rest)) 8164 (-some-> lsp-deps-providers 8165 (plist-get provider) 8166 (plist-get :install) 8167 (apply (cl-list* callback error-callback rest)))) 8168 (gethash dependency lsp--dependencies)) 8169 (funcall error-callback (format "Unable to find a way to install %s" dependency)))) 8170 8171 8172 ;; npm handling 8173 8174 ;; https://docs.npmjs.com/files/folders#executables 8175 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys) 8176 "Return npm dependency PATH for PACKAGE." 8177 (let ((path (executable-find 8178 (f-join lsp-server-install-dir "npm" package 8179 (cond ((eq system-type 'windows-nt) "") 8180 (t "bin")) 8181 path) 8182 t))) 8183 (unless (and path (f-exists? path)) 8184 (error "The package %s is not installed. Unable to find %s" package path)) 8185 path)) 8186 8187 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys) 8188 (if-let ((npm-binary (executable-find "npm"))) 8189 (progn 8190 ;; Explicitly `make-directory' to work around NPM bug in 8191 ;; versions 7.0.0 through 7.4.1. See 8192 ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for 8193 ;; discussion. 8194 (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents) 8195 (lsp-async-start-process (lambda () 8196 (if (string-empty-p 8197 (string-trim (shell-command-to-string 8198 (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " ")))) 8199 (funcall callback) 8200 (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json"))))) 8201 (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx 8202 (when (f-dir-p default-directory) 8203 (lsp-async-start-process callback 8204 error-callback 8205 (executable-find "npx") 8206 "npm-install-peers"))))) 8207 error-callback 8208 npm-binary 8209 "-g" 8210 "--prefix" 8211 (f-join lsp-server-install-dir "npm" package) 8212 "install" 8213 package)) 8214 (lsp-log "Unable to install %s via `npm' because it is not present" package) 8215 nil)) 8216 8217 8218 ;; Cargo dependency handling 8219 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys) 8220 (let ((path (executable-find 8221 (f-join lsp-server-install-dir 8222 "cargo" 8223 package 8224 "bin" 8225 path) 8226 t))) 8227 (unless (and path (f-exists? path)) 8228 (error "The package %s is not installed. Unable to find %s" package path)) 8229 path)) 8230 8231 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys) 8232 (if-let ((cargo-binary (executable-find "cargo"))) 8233 (lsp-async-start-process 8234 callback 8235 error-callback 8236 cargo-binary 8237 "install" 8238 package 8239 (when git 8240 "--git") 8241 git 8242 "--root" 8243 (f-join lsp-server-install-dir "cargo" package)) 8244 (lsp-log "Unable to install %s via `cargo' because it is not present" package) 8245 nil)) 8246 8247 8248 8249 ;; Download URL handling 8250 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys) 8251 (let* ((url (lsp-resolve-value url)) 8252 (store-path (lsp-resolve-value store-path)) 8253 ;; (decompress (lsp-resolve-value decompress)) 8254 (download-path 8255 (pcase decompress 8256 (:gzip (concat store-path ".gz")) 8257 (:zip (concat store-path ".zip")) 8258 (:targz (concat store-path ".tar.gz")) 8259 (`nil store-path) 8260 (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'"))))) 8261 (make-thread 8262 (lambda () 8263 (condition-case err 8264 (progn 8265 (when (f-exists? download-path) 8266 (f-delete download-path)) 8267 (when (f-exists? store-path) 8268 (f-delete store-path)) 8269 (lsp--info "Starting to download %s to %s..." url download-path) 8270 (mkdir (f-parent download-path) t) 8271 (url-copy-file url download-path) 8272 (lsp--info "Finished downloading %s..." download-path) 8273 (when (and lsp-verify-signature asc-url pgp-key) 8274 (if (executable-find epg-gpg-program) 8275 (let ((asc-download-path (concat download-path ".asc")) 8276 (context (epg-make-context)) 8277 (fingerprint) 8278 (signature)) 8279 (when (f-exists? asc-download-path) 8280 (f-delete asc-download-path)) 8281 (lsp--info "Starting to download %s to %s..." asc-url asc-download-path) 8282 (url-copy-file asc-url asc-download-path) 8283 (lsp--info "Finished downloading %s..." asc-download-path) 8284 (epg-import-keys-from-string context pgp-key) 8285 (setq fingerprint (epg-import-status-fingerprint 8286 (car 8287 (epg-import-result-imports 8288 (epg-context-result-for context 'import))))) 8289 (lsp--info "Verifying signature %s..." asc-download-path) 8290 (epg-verify-file context asc-download-path download-path) 8291 (setq signature (car (epg-context-result-for context 'verify))) 8292 (unless (and 8293 (eq (epg-signature-status signature) 'good) 8294 (equal (epg-signature-fingerprint signature) fingerprint)) 8295 (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature)))) 8296 (lsp--warn "GPG is not installed, skipping the signature check."))) 8297 (when decompress 8298 (lsp--info "Decompressing %s..." download-path) 8299 (pcase decompress 8300 (:gzip 8301 (lsp-gunzip download-path)) 8302 (:zip (lsp-unzip download-path (f-parent store-path))) 8303 (:targz (lsp-tar-gz-decompress download-path (f-parent store-path)))) 8304 (lsp--info "Decompressed %s..." store-path)) 8305 (funcall callback)) 8306 (error (funcall error-callback err))))))) 8307 8308 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys) 8309 "Download URL and store it into STORE-PATH. 8310 8311 SET-EXECUTABLE? when non-nil change the executable flags of 8312 STORE-PATH to make it executable. BINARY-PATH can be specified 8313 when the binary to start does not match the name of the 8314 archive (e.g. when the archive has multiple files)" 8315 (let ((store-path (or (lsp-resolve-value binary-path) 8316 (lsp-resolve-value store-path)))) 8317 (cond 8318 ((executable-find store-path) store-path) 8319 ((and set-executable? (f-exists? store-path)) 8320 (set-file-modes store-path #o0700) 8321 store-path) 8322 ((f-exists? store-path) store-path)))) 8323 8324 (defun lsp--find-latest-gh-release-url (url regex) 8325 "Fetch the latest version in the releases given by URL by using REGEX." 8326 (let ((url-request-method "GET")) 8327 (with-current-buffer (url-retrieve-synchronously url) 8328 (goto-char (point-min)) 8329 (re-search-forward "\n\n" nil 'noerror) 8330 (delete-region (point-min) (point)) 8331 (let* ((json-result (lsp-json-read-buffer))) 8332 (message "Latest version found: %s" (lsp-get json-result :tag_name)) 8333 (--> json-result 8334 (lsp-get it :assets) 8335 (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it) 8336 (lsp-get it :browser_download_url)))))) 8337 8338 ;; unzip 8339 8340 (defconst lsp-ext-pwsh-script "powershell -noprofile -noninteractive \ 8341 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'" 8342 "Powershell script to unzip file.") 8343 8344 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'" 8345 "Unzip script to unzip file.") 8346 8347 (defcustom lsp-unzip-script (lambda () 8348 (cond ((executable-find "unzip") lsp-ext-unzip-script) 8349 ((executable-find "powershell") lsp-ext-pwsh-script) 8350 (t nil))) 8351 "The script to unzip." 8352 :group 'lsp-mode 8353 :type 'string 8354 :package-version '(lsp-mode . "8.0.0")) 8355 8356 (defun lsp-unzip (zip-file dest) 8357 "Unzip ZIP-FILE to DEST." 8358 (unless lsp-unzip-script 8359 (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'")) 8360 (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest))) 8361 8362 ;; gunzip 8363 8364 (defconst lsp-ext-gunzip-script "gzip -d %1$s" 8365 "Script to decompress a gzippped file with gzip.") 8366 8367 (defcustom lsp-gunzip-script (lambda () 8368 (cond ((executable-find "gzip") lsp-ext-gunzip-script) 8369 (t nil))) 8370 "The script to decompress a gzipped file. 8371 Should be a format string with one argument for the file to be decompressed 8372 in place." 8373 :group 'lsp-mode 8374 :type 'string 8375 :package-version '(lsp-mode . "8.0.0")) 8376 8377 (defun lsp-gunzip (gz-file) 8378 "Decompress GZ-FILE in place." 8379 (unless lsp-gunzip-script 8380 (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file)) 8381 (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file))) 8382 8383 ;; tar.gz decompression 8384 8385 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'" 8386 "Script to decompress a .tar.gz file.") 8387 8388 (defcustom lsp-tar-script (lambda () 8389 (cond ((executable-find "tar") lsp-ext-tar-script) 8390 (t nil))) 8391 "The script to decompress a .tar.gz file. 8392 Should be a format string with one argument for the file to be decompressed 8393 in place." 8394 :group 'lsp-mode 8395 :type 'string) 8396 8397 (defun lsp-tar-gz-decompress (targz-file dest) 8398 "Decompress TARGZ-FILE in DEST." 8399 (unless lsp-tar-script 8400 (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file)) 8401 (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest))) 8402 8403 8404 ;; VSCode marketplace 8405 8406 (defcustom lsp-vscode-ext-url 8407 "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s" 8408 "Vscode extension template url." 8409 :group 'lsp-mode 8410 :type 'string 8411 :package-version '(lsp-mode . "8.0.0")) 8412 8413 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform) 8414 "Return the URL to vscode extension. 8415 PUBLISHER is the extension publisher. 8416 NAME is the name of the extension. 8417 VERSION is the version of the extension. 8418 TARGETPLATFORM is the targetPlatform of the extension." 8419 (format lsp-vscode-ext-url publisher name version (or targetPlatform ""))) 8420 8421 8422 8423 ;; Queueing prompts 8424 8425 (defvar lsp--question-queue nil 8426 "List of questions yet to be asked by `lsp-ask-question'.") 8427 8428 (defun lsp-ask-question (question options callback) 8429 "Prompt the user to answer the QUESTION with one of the OPTIONS from the 8430 minibuffer. Once the user selects an option, the CALLBACK function will be 8431 called, passing the selected option to it. 8432 8433 If the user is currently being shown a question, the question will be stored in 8434 `lsp--question-queue', and will be asked once the user has answered the current 8435 question." 8436 (add-to-list 'lsp--question-queue `(("question" . ,question) 8437 ("options" . ,options) 8438 ("callback" . ,callback)) t) 8439 (when (eq (length lsp--question-queue) 1) 8440 (lsp--process-question-queue))) 8441 8442 (defun lsp--process-question-queue () 8443 "Take the first question from `lsp--question-queue', process it, then process 8444 the next question until the queue is empty." 8445 (-let* (((&alist "question" "options" "callback") (car lsp--question-queue)) 8446 (answer (completing-read question options nil t))) 8447 (pop lsp--question-queue) 8448 (funcall callback answer) 8449 (when lsp--question-queue 8450 (lsp--process-question-queue)))) 8451 8452 (defun lsp--supports-buffer? (client) 8453 (and 8454 ;; both file and client remote or both local 8455 (eq (---truthy? (file-remote-p (buffer-file-name))) 8456 (---truthy? (lsp--client-remote? client))) 8457 8458 ;; activation function or major-mode match. 8459 (if-let ((activation-fn (lsp--client-activation-fn client))) 8460 (funcall activation-fn (buffer-file-name) major-mode) 8461 (-contains? (lsp--client-major-modes client) major-mode)) 8462 8463 ;; check whether it is enabled if `lsp-enabled-clients' is not null 8464 (or (null lsp-enabled-clients) 8465 (or (member (lsp--client-server-id client) lsp-enabled-clients) 8466 (ignore (lsp--info "Client %s is not in lsp-enabled-clients" 8467 (lsp--client-server-id client))))) 8468 8469 ;; check whether it is not disabled. 8470 (not (lsp--client-disabled-p major-mode (lsp--client-server-id client))))) 8471 8472 (defun lsp--filter-clients (pred) 8473 (->> lsp-clients hash-table-values (-filter pred))) 8474 8475 (defun lsp--find-clients () 8476 "Find clients which can handle current buffer." 8477 (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 8478 #'lsp--server-binary-present?))) 8479 (lsp-log "Found the following clients for %s: %s" 8480 (buffer-file-name) 8481 (s-join ", " 8482 (-map (lambda (client) 8483 (format "(server-id %s, priority %s)" 8484 (lsp--client-server-id client) 8485 (lsp--client-priority client))) 8486 matching-clients))) 8487 (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients)) 8488 (selected-clients (if-let ((main-client (and main-clients 8489 (--max-by (> (lsp--client-priority it) 8490 (lsp--client-priority other)) 8491 main-clients)))) 8492 (cons main-client add-on-clients) 8493 add-on-clients))) 8494 (lsp-log "The following clients were selected based on priority: %s" 8495 (s-join ", " 8496 (-map (lambda (client) 8497 (format "(server-id %s, priority %s)" 8498 (lsp--client-server-id client) 8499 (lsp--client-priority client))) 8500 selected-clients))) 8501 selected-clients))) 8502 8503 (defun lsp-workspace-remove-all-folders() 8504 "Delete all lsp tracked folders." 8505 (interactive) 8506 (--each (lsp-session-folders (lsp-session)) 8507 (lsp-workspace-folders-remove it))) 8508 8509 (defun lsp-register-client (client) 8510 "Registers LSP client CLIENT." 8511 (let ((client-id (lsp--client-server-id client))) 8512 (puthash client-id client lsp-clients) 8513 (setplist (intern (format "lsp-%s-after-open-hook" client-id)) 8514 `( standard-value (nil) custom-type hook 8515 custom-package-version (lsp-mode . "7.0.1") 8516 variable-documentation ,(format "Hooks to run after `%s' server is run." client-id) 8517 custom-requests nil))) 8518 (when (and lsp-auto-register-remote-clients 8519 (not (lsp--client-remote? client))) 8520 (let ((remote-client (copy-lsp--client client))) 8521 (setf (lsp--client-remote? remote-client) t 8522 (lsp--client-server-id remote-client) (intern 8523 (format "%s-tramp" 8524 (lsp--client-server-id client))) 8525 ;; disable automatic download 8526 (lsp--client-download-server-fn remote-client) nil) 8527 (lsp-register-client remote-client)))) 8528 8529 (defun lsp--create-initialization-options (_session client) 8530 "Create initialization-options from SESSION and CLIENT. 8531 Add workspace folders depending on server being multiroot and 8532 session workspace folder configuration for the server." 8533 (let* ((initialization-options-or-fn (lsp--client-initialization-options client))) 8534 (if (functionp initialization-options-or-fn) 8535 (funcall initialization-options-or-fn) 8536 initialization-options-or-fn))) 8537 8538 (defvar lsp-client-settings (make-hash-table :test 'equal) 8539 "For internal use, any external users please use 8540 `lsp-register-custom-settings' function instead") 8541 8542 (defun lsp-register-custom-settings (props) 8543 "Register PROPS. 8544 PROPS is list of triple (path value boolean?) where PATH is the path to the 8545 property; VALUE can be a literal value, symbol to be evaluated, or either a 8546 function or lambda function to be called without arguments; BOOLEAN? is an 8547 optional flag that should be non-nil for boolean settings, when it is nil the 8548 property will be ignored if the VALUE is nil. 8549 8550 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))' 8551 \(note the double parentheses)" 8552 (mapc 8553 (-lambda ((path . rest)) 8554 (puthash path rest lsp-client-settings)) 8555 props)) 8556 8557 (defun lsp-region-text (region) 8558 "Get the text for REGION in current buffer." 8559 (-let (((start . end) (lsp--range-to-region region))) 8560 (buffer-substring-no-properties start end))) 8561 8562 (defun lsp-ht-set (tbl paths value) 8563 "Set nested hash table value. 8564 TBL - a hash table, PATHS is the path to the nested VALUE." 8565 (pcase paths 8566 (`(,path) (ht-set! tbl path value)) 8567 (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl) 8568 (let ((temp-tbl (ht))) 8569 (ht-set! tbl path temp-tbl) 8570 temp-tbl)))) 8571 (lsp-ht-set nested-tbl rst value))))) 8572 8573 ;; sections 8574 8575 (defalias 'defcustom-lsp 'lsp-defcustom) 8576 8577 (defmacro lsp-defcustom (symbol standard doc &rest args) 8578 "Defines `lsp-mode' server property." 8579 (declare (doc-string 3) (debug (name body)) 8580 (indent defun)) 8581 (let ((path (plist-get args :lsp-path))) 8582 (cl-remf args :lsp-path) 8583 `(progn 8584 (lsp-register-custom-settings 8585 (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type)))))) 8586 8587 (defcustom ,symbol ,standard ,doc 8588 :set (lambda (sym val) 8589 (lsp--set-custom-property sym val ,path)) 8590 ,@args)))) 8591 8592 (defun lsp--set-custom-property (sym val path) 8593 (set sym val) 8594 (let ((section (cl-first (s-split "\\." path)))) 8595 (mapc (lambda (workspace) 8596 (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace)) 8597 section) 8598 (with-lsp-workspace workspace 8599 (lsp--set-configuration (lsp-configuration-section section))))) 8600 (lsp--session-workspaces (lsp-session))))) 8601 8602 (defun lsp-configuration-section (section) 8603 "Get settings for SECTION." 8604 (let ((ret (ht-create))) 8605 (maphash (-lambda (path (variable boolean?)) 8606 (when (s-matches? (concat (regexp-quote section) "\\..*") path) 8607 (let* ((symbol-value (-> variable 8608 lsp-resolve-value 8609 lsp-resolve-value)) 8610 (value (if (and boolean? (not symbol-value)) 8611 :json-false 8612 symbol-value))) 8613 (when (or boolean? value) 8614 (lsp-ht-set ret (s-split "\\." path) value))))) 8615 lsp-client-settings) 8616 ret)) 8617 8618 8619 (defun lsp--start-connection (session client project-root) 8620 "Initiates connection created from CLIENT for PROJECT-ROOT. 8621 SESSION is the active session." 8622 (when (lsp--client-multi-root client) 8623 (cl-pushnew project-root (gethash (lsp--client-server-id client) 8624 (lsp-session-server-id->folders session)))) 8625 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil) 8626 8627 (unwind-protect 8628 (lsp--start-workspace session client project-root (lsp--create-initialization-options session client)) 8629 (lsp--spinner-stop))) 8630 8631 ;; lsp-log-io-mode 8632 8633 (defvar lsp-log-io-mode-map 8634 (let ((map (make-sparse-keymap))) 8635 (define-key map (kbd "M-n") #'lsp-log-io-next) 8636 (define-key map (kbd "M-p") #'lsp-log-io-prev) 8637 (define-key map (kbd "k") #'lsp--erase-log-buffer) 8638 (define-key map (kbd "K") #'lsp--erase-session-log-buffers) 8639 map) 8640 "Keymap for lsp log buffer mode.") 8641 8642 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo" 8643 "Special mode for viewing IO logs.") 8644 8645 (defun lsp-workspace-show-log (workspace) 8646 "Display the log buffer of WORKSPACE." 8647 (interactive 8648 (list (if lsp-log-io 8649 (if (eq (length (lsp-workspaces)) 1) 8650 (cl-first (lsp-workspaces)) 8651 (lsp--completing-read "Workspace: " (lsp-workspaces) 8652 #'lsp--workspace-print nil t)) 8653 (user-error "IO logging is disabled")))) 8654 (pop-to-buffer (lsp--get-log-buffer-create workspace))) 8655 8656 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log) 8657 8658 (defun lsp--get-log-buffer-create (workspace) 8659 "Return the lsp log buffer of WORKSPACE, creating a new one if needed." 8660 (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8661 (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id))) 8662 (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid)))) 8663 8664 (defun lsp--erase-log-buffer (&optional all) 8665 "Delete contents of current lsp log buffer. 8666 When ALL is t, erase all log buffers of the running session." 8667 (interactive) 8668 (let* ((workspaces (lsp--session-workspaces (lsp-session))) 8669 (current-log-buffer (current-buffer))) 8670 (dolist (w workspaces) 8671 (let ((b (lsp--get-log-buffer-create w))) 8672 (when (or all (eq b current-log-buffer)) 8673 (with-current-buffer b 8674 (let ((inhibit-read-only t)) 8675 (erase-buffer)))))))) 8676 8677 (defun lsp--erase-session-log-buffers () 8678 "Erase log buffers of the running session." 8679 (interactive) 8680 (lsp--erase-log-buffer t)) 8681 8682 (defun lsp-log-io-next (arg) 8683 "Move to next log entry." 8684 (interactive "P") 8685 (ewoc-goto-next lsp--log-io-ewoc (or arg 1))) 8686 8687 (defun lsp-log-io-prev (arg) 8688 "Move to previous log entry." 8689 (interactive "P") 8690 (ewoc-goto-prev lsp--log-io-ewoc (or arg 1))) 8691 8692 8693 8694 (cl-defmethod lsp-process-id ((process process)) 8695 (process-id process)) 8696 8697 (cl-defmethod lsp-process-name ((process process)) (process-name process)) 8698 8699 (cl-defmethod lsp-process-status ((process process)) (process-status process)) 8700 8701 (cl-defmethod lsp-process-kill ((process process)) 8702 (when (process-live-p process) 8703 (kill-process process))) 8704 8705 (cl-defmethod lsp-process-send ((process process) message) 8706 (condition-case err 8707 (process-send-string process (lsp--make-message message)) 8708 (error (lsp--error "Sending to process failed with the following error: %s" 8709 (error-message-string err))))) 8710 8711 (cl-defmethod lsp-process-cleanup (process) 8712 ;; Kill standard error buffer only if the process exited normally. 8713 ;; Leave it intact otherwise for debugging purposes. 8714 (let ((buffer (-> process process-name get-buffer))) 8715 (when (and (eq (process-status process) 'exit) 8716 (zerop (process-exit-status process)) 8717 (buffer-live-p buffer)) 8718 (kill-buffer buffer)))) 8719 8720 8721 ;; native JSONRPC 8722 8723 (declare-function json-rpc "ext:json") 8724 (declare-function json-rpc-connection "ext:json") 8725 (declare-function json-rpc-send "ext:json") 8726 (declare-function json-rpc-shutdown "ext:json") 8727 (declare-function json-rpc-stderr "ext:json") 8728 (declare-function json-rpc-pid "ext:json") 8729 8730 (defvar lsp-json-rpc-thread nil) 8731 (defvar lsp-json-rpc-queue nil) 8732 (defvar lsp-json-rpc-done nil) 8733 (defvar lsp-json-rpc-mutex (make-mutex)) 8734 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex)) 8735 8736 (defun lsp-json-rpc-process-queue () 8737 (while (not lsp-json-rpc-done) 8738 (while lsp-json-rpc-queue 8739 (-let (((proc . message) (pop lsp-json-rpc-queue))) 8740 (json-rpc-send 8741 proc message 8742 :null-object nil 8743 :false-object :json-false))) 8744 (with-mutex lsp-json-rpc-mutex 8745 (condition-wait lsp-json-rpc-condition)))) 8746 8747 (cl-defmethod lsp-process-id (process) (json-rpc-pid process)) 8748 8749 (cl-defmethod lsp-process-name (_process) "TBD") 8750 8751 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process)) 8752 8753 (cl-defmethod lsp-process-send (proc message) 8754 (unless lsp-json-rpc-thread 8755 (with-current-buffer (get-buffer-create " *json-rpc*") 8756 (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*")))) 8757 8758 (with-mutex lsp-json-rpc-mutex 8759 (setq lsp-json-rpc-queue (append lsp-json-rpc-queue 8760 (list (cons proc message)))) 8761 (condition-notify lsp-json-rpc-condition))) 8762 8763 (cl-defmethod lsp-process-cleanup (_proc)) 8764 8765 (defun lsp-json-rpc-connection (workspace command) 8766 (let ((con (apply #'json-rpc-connection command)) 8767 (object-type (if lsp-use-plists 'plist 'hash-table))) 8768 (with-current-buffer (get-buffer-create " *json-rpc*") 8769 (make-thread 8770 (lambda () 8771 (json-rpc 8772 con 8773 (lambda (result err done) 8774 (run-with-timer 8775 0.0 8776 nil 8777 (lambda () 8778 (cond 8779 (result (lsp--parser-on-message result workspace)) 8780 (err (warn "Json parsing failed with the following error: %s" err)) 8781 (done (lsp--handle-process-exit workspace "")))))) 8782 :object-type object-type 8783 :null-object nil 8784 :false-object nil)) 8785 "*json-rpc-connection*")) 8786 (cons con con))) 8787 8788 (defun lsp-json-rpc-stderr () 8789 (interactive) 8790 (--when-let (pcase (lsp-workspaces) 8791 (`nil (user-error "There are no active servers in the current buffer")) 8792 (`(,workspace) workspace) 8793 (workspaces (lsp--completing-read "Select server: " 8794 workspaces 8795 'lsp--workspace-print nil t))) 8796 (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it))) 8797 (buffer (format "*stderr-%s*" (lsp--workspace-print it)) )) 8798 (with-current-buffer (get-buffer-create buffer) 8799 (with-help-window buffer 8800 (insert content)))))) 8801 8802 8803 (defun lsp--workspace-print (workspace) 8804 "Visual representation WORKSPACE." 8805 (let* ((proc (lsp--workspace-cmd-proc workspace)) 8806 (status (lsp--workspace-status workspace)) 8807 (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8808 (pid (lsp-process-id proc))) 8809 8810 (if (eq 'initialized status) 8811 (format "%s:%s" server-id pid) 8812 (format "%s:%s/%s" server-id pid status)))) 8813 8814 (defun lsp--map-tree-widget (m) 8815 "Build `tree-widget' from a hash-table or plist M." 8816 (when (lsp-structure-p m) 8817 (let (nodes) 8818 (lsp-map (lambda (k v) 8819 (push `(tree-widget 8820 :tag ,(if (lsp-structure-p v) 8821 (format "%s:" k) 8822 (format "%s: %s" k 8823 (propertize (format "%s" v) 8824 'face 8825 'font-lock-string-face))) 8826 :open t 8827 ,@(lsp--map-tree-widget v)) 8828 nodes)) 8829 m) 8830 nodes))) 8831 8832 (defun lsp-buffer-name (buffer-id) 8833 (if-let ((buffer-name (plist-get buffer-id :buffer-name))) 8834 (funcall buffer-name buffer-id) 8835 (buffer-name buffer-id))) 8836 8837 (defun lsp--render-workspace (workspace) 8838 "Tree node representation of WORKSPACE." 8839 `(tree-widget :tag ,(lsp--workspace-print workspace) 8840 :open t 8841 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face) 8842 :open t 8843 ,@(->> workspace 8844 (lsp--workspace-buffers) 8845 (--map `(tree-widget 8846 :tag ,(when (lsp-buffer-live-p it) 8847 (let ((buffer-name (lsp-buffer-name it))) 8848 (if (lsp-with-current-buffer it buffer-read-only) 8849 (propertize buffer-name 'face 'font-lock-constant-face) 8850 buffer-name))))))) 8851 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face) 8852 ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget)))) 8853 8854 (define-derived-mode lsp-browser-mode special-mode "LspBrowser" 8855 "Define mode for displaying lsp sessions." 8856 (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t))))) 8857 8858 (defun lsp-describe-session () 8859 "Describes current `lsp-session'." 8860 (interactive) 8861 (let ((session (lsp-session)) 8862 (buf (get-buffer-create "*lsp session*")) 8863 (root (lsp-workspace-root))) 8864 (with-current-buffer buf 8865 (lsp-browser-mode) 8866 (let ((inhibit-read-only t)) 8867 (erase-buffer) 8868 (--each (lsp-session-folders session) 8869 (widget-create 8870 `(tree-widget 8871 :tag ,(propertize it 'face 'font-lock-keyword-face) 8872 :open t 8873 ,@(->> session 8874 (lsp-session-folder->servers) 8875 (gethash it) 8876 (-map 'lsp--render-workspace))))))) 8877 (pop-to-buffer buf) 8878 (goto-char (point-min)) 8879 (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag) 8880 until (or (and root (string= tag root)) (eobp)) 8881 do (goto-char (next-overlay-change (point)))))) 8882 8883 (defun lsp--session-workspaces (session) 8884 "Get all workspaces that are part of the SESSION." 8885 (-> session lsp-session-folder->servers hash-table-values -flatten -uniq)) 8886 8887 (defun lsp--find-multiroot-workspace (session client project-root) 8888 "Look for a multiroot connection in SESSION created from CLIENT for 8889 PROJECT-ROOT and BUFFER-MAJOR-MODE." 8890 (when (lsp--client-multi-root client) 8891 (-when-let (multi-root-workspace (->> session 8892 (lsp--session-workspaces) 8893 (--first (eq (-> it lsp--workspace-client lsp--client-server-id) 8894 (lsp--client-server-id client))))) 8895 (with-lsp-workspace multi-root-workspace 8896 (lsp-notify "workspace/didChangeWorkspaceFolders" 8897 (lsp-make-did-change-workspace-folders-params 8898 :event (lsp-make-workspace-folders-change-event 8899 :added (vector (lsp-make-workspace-folder 8900 :uri (lsp--path-to-uri project-root) 8901 :name (f-filename project-root))) 8902 :removed [])))) 8903 8904 (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace)) 8905 (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root)) 8906 8907 (lsp--persist-session session) 8908 8909 (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace)) 8910 (lsp--open-in-workspace multi-root-workspace) 8911 8912 multi-root-workspace))) 8913 8914 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder) 8915 "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT. 8916 IGNORE-MULTI-FOLDER to ignore multi folder server." 8917 (-map (lambda (client) 8918 (or 8919 (lsp--find-workspace session client project-root) 8920 (unless ignore-multi-folder 8921 (lsp--find-multiroot-workspace session client project-root)) 8922 (lsp--start-connection session client project-root))) 8923 clients)) 8924 8925 (defun lsp--spinner-stop () 8926 "Stop the spinner in case all of the workspaces are started." 8927 (when (--all? (eq (lsp--workspace-status it) 'initialized) 8928 lsp--buffer-workspaces) 8929 (spinner-stop))) 8930 8931 (defun lsp--open-in-workspace (workspace) 8932 "Open in existing WORKSPACE." 8933 (if (eq 'initialized (lsp--workspace-status workspace)) 8934 ;; when workspace is initialized just call document did open. 8935 (progn 8936 (with-lsp-workspace workspace 8937 (when-let ((before-document-open-fn (-> workspace 8938 lsp--workspace-client 8939 lsp--client-before-file-open-fn))) 8940 (funcall before-document-open-fn workspace)) 8941 (lsp--text-document-did-open)) 8942 (lsp--spinner-stop)) 8943 ;; when it is not initialized 8944 (lsp--spinner-start) 8945 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace)))) 8946 8947 (defun lsp--find-workspace (session client project-root) 8948 "Find server connection created with CLIENT in SESSION for PROJECT-ROOT." 8949 (when-let ((workspace (->> session 8950 (lsp-session-folder->servers) 8951 (gethash project-root) 8952 (--first (eql (-> it lsp--workspace-client lsp--client-server-id) 8953 (lsp--client-server-id client)))))) 8954 (lsp--open-in-workspace workspace) 8955 workspace)) 8956 8957 (defun lsp--read-char (prompt &optional options) 8958 "Wrapper for `read-char-from-minibuffer' if Emacs +27. 8959 Fallback to `read-key' otherwise. 8960 PROMPT is the message and OPTIONS the available options." 8961 (if (fboundp 'read-char-from-minibuffer) 8962 (read-char-from-minibuffer prompt options) 8963 (read-key prompt))) 8964 8965 (defun lsp--find-root-interactively (session) 8966 "Find project interactively. 8967 Returns nil if the project should not be added to the current SESSION." 8968 (condition-case nil 8969 (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory)) 8970 (action (lsp--read-char 8971 (format 8972 "%s is not part of any project. 8973 8974 %s ==> Import project root %s 8975 %s ==> Import project by selecting root directory interactively 8976 %s ==> Import project at current directory %s 8977 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist 8978 %s ==> Do not ask again for the current project by selecting ignore path interactively 8979 %s ==> Do nothing: ask again when opening other files from the current project 8980 8981 Select action: " 8982 (propertize (buffer-name) 'face 'bold) 8983 (propertize "i" 'face 'success) 8984 (propertize project-root-suggestion 'face 'bold) 8985 (propertize "I" 'face 'success) 8986 (propertize "." 'face 'success) 8987 (propertize default-directory 'face 'bold) 8988 (propertize "d" 'face 'warning) 8989 (propertize project-root-suggestion 'face 'bold) 8990 (propertize "D" 'face 'warning) 8991 (propertize "n" 'face 'warning)) 8992 '(?i ?\r ?I ?. ?d ?D ?n)))) 8993 (cl-case action 8994 (?i project-root-suggestion) 8995 (?\r project-root-suggestion) 8996 (?I (read-directory-name "Select workspace folder to add: " 8997 (or project-root-suggestion default-directory) 8998 nil 8999 t)) 9000 (?. default-directory) 9001 (?d (push project-root-suggestion (lsp-session-folders-blocklist session)) 9002 (lsp--persist-session session) 9003 nil) 9004 (?D (push (read-directory-name "Select folder to blocklist: " 9005 (or project-root-suggestion default-directory) 9006 nil 9007 t) 9008 (lsp-session-folders-blocklist session)) 9009 (lsp--persist-session session) 9010 nil) 9011 (t nil))) 9012 (quit))) 9013 9014 (declare-function tramp-file-name-host "ext:tramp" (file) t) 9015 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault)) 9016 9017 (defun lsp--files-same-host (f1 f2) 9018 "Predicate on whether or not two files are on the same host." 9019 (or (not (or (file-remote-p f1) (file-remote-p f2))) 9020 (and (file-remote-p f1) 9021 (file-remote-p f2) 9022 (progn (require 'tramp) 9023 (equal (tramp-file-name-host (tramp-dissect-file-name f1)) 9024 (tramp-file-name-host (tramp-dissect-file-name f2))))))) 9025 9026 (defun lsp-find-session-folder (session file-name) 9027 "Look in the current SESSION for folder containing FILE-NAME." 9028 (let ((file-name-canonical (lsp-f-canonical file-name))) 9029 (->> session 9030 (lsp-session-folders) 9031 (--filter (and (lsp--files-same-host it file-name-canonical) 9032 (or (lsp-f-same? it file-name-canonical) 9033 (and (f-dir? it) 9034 (lsp-f-ancestor-of? it file-name-canonical))))) 9035 (--max-by (> (length it) 9036 (length other)))))) 9037 9038 (defun lsp-find-workspace (server-id &optional file-name) 9039 "Find workspace for SERVER-ID for FILE-NAME." 9040 (-when-let* ((session (lsp-session)) 9041 (folder->servers (lsp-session-folder->servers session)) 9042 (workspaces (if file-name 9043 (gethash (lsp-find-session-folder session file-name) folder->servers) 9044 (lsp--session-workspaces session)))) 9045 9046 (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces))) 9047 9048 (defun lsp--calculate-root (session file-name) 9049 "Calculate project root for FILE-NAME in SESSION." 9050 (and 9051 (->> session 9052 (lsp-session-folders-blocklist) 9053 (--first (and (lsp--files-same-host it file-name) 9054 (lsp-f-ancestor-of? it file-name) 9055 (prog1 t 9056 (lsp--info "File %s is in blocklisted directory %s" file-name it)))) 9057 not) 9058 (or 9059 (when lsp-auto-guess-root 9060 (lsp--suggest-project-root)) 9061 (unless lsp-guess-root-without-session 9062 (lsp-find-session-folder session file-name)) 9063 (unless lsp-auto-guess-root 9064 (when-let ((root-folder (lsp--find-root-interactively session))) 9065 (if (or (not (f-equal? root-folder (expand-file-name "~/"))) 9066 (yes-or-no-p 9067 (concat 9068 (propertize "[WARNING] " 'face 'warning) 9069 "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: 9070 9071 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/'). 9072 2. If your file is under `~/' then create a subfolder and move that file in this folder. 9073 9074 Type `No' to go back to project selection. 9075 Type `Yes' to confirm `HOME' as project root. 9076 Type `C-g' to cancel project import process and stop `lsp'"))) 9077 root-folder 9078 (lsp--calculate-root session file-name))))))) 9079 9080 (defun lsp--try-open-in-library-workspace () 9081 "Try opening current file as library file in any of the active workspace. 9082 The library folders are defined by each client for each of the active workspace." 9083 (when-let ((workspace (->> (lsp-session) 9084 (lsp--session-workspaces) 9085 ;; Sort the last active workspaces first as they are more likely to be 9086 ;; the correct ones, especially when jumping to a definition. 9087 (-sort (lambda (a _b) 9088 (-contains? lsp--last-active-workspaces a))) 9089 (--first 9090 (and (-> it lsp--workspace-client lsp--supports-buffer?) 9091 (when-let ((library-folders-fn 9092 (-> it lsp--workspace-client lsp--client-library-folders-fn))) 9093 (-first (lambda (library-folder) 9094 (lsp-f-ancestor-of? library-folder (buffer-file-name))) 9095 (funcall library-folders-fn it)))))))) 9096 (lsp--open-in-workspace workspace) 9097 (view-mode t) 9098 (lsp--info "Opening read-only library file %s." (buffer-file-name)) 9099 (list workspace))) 9100 9101 (defun lsp--persist-session (session) 9102 "Persist SESSION to `lsp-session-file'." 9103 (lsp--persist lsp-session-file (make-lsp-session 9104 :folders (lsp-session-folders session) 9105 :folders-blocklist (lsp-session-folders-blocklist session) 9106 :server-id->folders (lsp-session-server-id->folders session)))) 9107 9108 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder) 9109 "Try create opening file as a project file. 9110 When IGNORE-MULTI-FOLDER is t the lsp mode will start new 9111 language server even if there is language server which can handle 9112 current language. When IGNORE-MULTI-FOLDER is nil current file 9113 will be opened in multi folder language server if there is 9114 such." 9115 (-let ((session (lsp-session))) 9116 (-if-let (clients (if ask-for-client 9117 (list (lsp--completing-read "Select server to start: " 9118 (ht-values lsp-clients) 9119 (-compose 'symbol-name 'lsp--client-server-id) nil t)) 9120 (lsp--find-clients))) 9121 (-if-let (project-root (-some-> session 9122 (lsp--calculate-root (buffer-file-name)) 9123 (lsp-f-canonical))) 9124 (progn 9125 ;; update project roots if needed and persist the lsp session 9126 (unless (-contains? (lsp-session-folders session) project-root) 9127 (cl-pushnew project-root (lsp-session-folders session)) 9128 (lsp--persist-session session)) 9129 (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder)) 9130 (lsp--warn "%s not in project or it is blocklisted." (buffer-name)) 9131 nil) 9132 (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode) 9133 nil))) 9134 9135 (defun lsp-shutdown-workspace () 9136 "Shutdown language server." 9137 (interactive) 9138 (--when-let (pcase (lsp-workspaces) 9139 (`nil (user-error "There are no active servers in the current buffer")) 9140 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?" 9141 (lsp--workspace-print workspace))) 9142 workspace)) 9143 (workspaces (lsp--completing-read "Select server: " 9144 workspaces 9145 'lsp--workspace-print nil t))) 9146 (lsp-workspace-shutdown it))) 9147 9148 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1") 9149 9150 (defcustom lsp-auto-select-workspace t 9151 "Shutdown or restart a single workspace. 9152 If set and the current buffer has only a single workspace 9153 associated with it, `lsp-shutdown-workspace' and 9154 `lsp-restart-workspace' will act on it without asking." 9155 :type 'boolean 9156 :group 'lsp-mode) 9157 9158 (defun lsp--read-workspace () 9159 "Ask the user to select a workspace. 9160 Errors if there are none." 9161 (pcase (lsp-workspaces) 9162 (`nil (error "No workspaces associated with the current buffer")) 9163 ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace) 9164 (workspaces (lsp--completing-read "Select workspace: " workspaces 9165 #'lsp--workspace-print nil t)))) 9166 9167 (defun lsp-workspace-shutdown (workspace) 9168 "Shut the workspace WORKSPACE and the language server associated with it" 9169 (interactive (list (lsp--read-workspace))) 9170 (lsp--warn "Stopping %s" (lsp--workspace-print workspace)) 9171 (with-lsp-workspace workspace (lsp--shutdown-workspace))) 9172 9173 (defun lsp-disconnect () 9174 "Disconnect the buffer from the language server." 9175 (interactive) 9176 (lsp--text-document-did-close t) 9177 (lsp-managed-mode -1) 9178 (lsp-mode -1) 9179 (setq lsp--buffer-workspaces nil) 9180 (lsp--info "Disconnected")) 9181 9182 (defun lsp-restart-workspace () 9183 (interactive) 9184 (--when-let (pcase (lsp-workspaces) 9185 (`nil (user-error "There are no active servers in the current buffer")) 9186 (`(,workspace) workspace) 9187 (workspaces (lsp--completing-read "Select server: " 9188 workspaces 9189 'lsp--workspace-print nil t))) 9190 (lsp-workspace-restart it))) 9191 9192 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1") 9193 9194 (defun lsp-workspace-restart (workspace) 9195 "Restart the workspace WORKSPACE and the language server associated with it" 9196 (interactive (list (lsp--read-workspace))) 9197 (lsp--warn "Restarting %s" (lsp--workspace-print workspace)) 9198 (with-lsp-workspace workspace (lsp--shutdown-workspace t))) 9199 9200 ;;;###autoload 9201 (defun lsp (&optional arg) 9202 "Entry point for the server startup. 9203 When ARG is t the lsp mode will start new language server even if 9204 there is language server which can handle current language. When 9205 ARG is nil current file will be opened in multi folder language 9206 server if there is such. When `lsp' is called with prefix 9207 argument ask the user to select which language server to start." 9208 (interactive "P") 9209 9210 (lsp--require-packages) 9211 9212 (when (buffer-file-name) 9213 (let (clients 9214 (matching-clients (lsp--filter-clients 9215 (-andfn #'lsp--supports-buffer? 9216 #'lsp--server-binary-present?)))) 9217 (cond 9218 (matching-clients 9219 (when (setq lsp--buffer-workspaces 9220 (or (and 9221 ;; Don't open as library file if file is part of a project. 9222 (not (lsp-find-session-folder (lsp-session) (buffer-file-name))) 9223 (lsp--try-open-in-library-workspace)) 9224 (lsp--try-project-root-workspaces (equal arg '(4)) 9225 (and arg (not (equal arg 1)))))) 9226 (lsp-mode 1) 9227 (when lsp-auto-configure (lsp--auto-configure)) 9228 (setq lsp-buffer-uri (lsp--buffer-uri)) 9229 (lsp--info "Connected to %s." 9230 (apply 'concat (--map (format "[%s %s]" 9231 (lsp--workspace-print it) 9232 (lsp--workspace-root it)) 9233 lsp--buffer-workspaces))))) 9234 ;; look for servers which are currently being downloaded. 9235 ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9236 #'lsp--client-download-in-progress?))) 9237 (lsp--info "There are language server(%s) installation in progress. 9238 The server(s) will be started in the buffer when it has finished." 9239 (-map #'lsp--client-server-id clients)) 9240 (seq-do (lambda (client) 9241 (cl-pushnew (current-buffer) (lsp--client-buffers client))) 9242 clients)) 9243 ;; look for servers to install 9244 ((setq clients (lsp--filter-clients 9245 (-andfn #'lsp--supports-buffer? 9246 (-const lsp-enable-suggest-server-download) 9247 #'lsp--client-download-server-fn 9248 (-not #'lsp--client-download-in-progress?)))) 9249 (let ((client (lsp--completing-read 9250 (concat "Unable to find installed server supporting this file. " 9251 "The following servers could be installed automatically: ") 9252 clients 9253 (-compose #'symbol-name #'lsp--client-server-id) 9254 nil 9255 t))) 9256 (cl-pushnew (current-buffer) (lsp--client-buffers client)) 9257 (lsp--install-server-internal client))) 9258 ;; ignore other warnings 9259 ((not lsp-warn-no-matched-clients) 9260 nil) 9261 ;; automatic installation disabled 9262 ((setq clients (unless matching-clients 9263 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9264 #'lsp--client-download-server-fn 9265 (-not (-const lsp-enable-suggest-server-download)) 9266 (-not #'lsp--server-binary-present?))))) 9267 (lsp--warn "The following servers support current file but automatic download is disabled: %s 9268 \(If you have already installed the server check *lsp-log*)." 9269 (mapconcat (lambda (client) 9270 (symbol-name (lsp--client-server-id client))) 9271 clients 9272 " "))) 9273 ;; no clients present 9274 ((setq clients (unless matching-clients 9275 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9276 (-not #'lsp--server-binary-present?))))) 9277 (lsp--warn "The following servers support current file but do not have automatic installation: %s 9278 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages. 9279 \(If you have already installed the server check *lsp-log*)." 9280 (mapconcat (lambda (client) 9281 (symbol-name (lsp--client-server-id client))) 9282 clients 9283 " "))) 9284 ;; no matches 9285 ((-> #'lsp--supports-buffer? lsp--filter-clients not) 9286 (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'. 9287 This issue might be caused by: 9288 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'. 9289 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'. 9290 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/ . 9291 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/. 9292 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients'). 9293 You can customize `lsp-warn-no-matched-clients' to disable this message." 9294 major-mode major-mode major-mode)))))) 9295 9296 (defun lsp--buffer-visible-p () 9297 "Return non nil if current buffer is visible." 9298 (or (buffer-modified-p) (get-buffer-window nil t))) 9299 9300 (defun lsp--init-if-visible () 9301 "Run `lsp' for the current buffer if the buffer is visible. 9302 Returns non nil if `lsp' was run for the buffer." 9303 (when (lsp--buffer-visible-p) 9304 (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t) 9305 (lsp) 9306 t)) 9307 9308 ;;;###autoload 9309 (defun lsp-deferred () 9310 "Entry point that defers server startup until buffer is visible. 9311 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'. 9312 This avoids overloading the server with many files when starting Emacs." 9313 ;; Workspace may not be initialized yet. Use a buffer local variable to 9314 ;; remember that we deferred loading of this buffer. 9315 (setq lsp--buffer-deferred t) 9316 (let ((buffer (current-buffer))) 9317 ;; Avoid false positives as desktop-mode restores buffers by deferring 9318 ;; visibility check until the stack clears. 9319 (run-with-idle-timer 0 nil (lambda () 9320 (when (buffer-live-p buffer) 9321 (with-current-buffer buffer 9322 (unless (lsp--init-if-visible) 9323 (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t)))))))) 9324 9325 9326 9327 (defvar lsp-file-truename-cache (ht)) 9328 9329 (defmacro lsp-with-cached-filetrue-name (&rest body) 9330 "Executes BODY caching the `file-truename' calls." 9331 `(let ((old-fn (symbol-function 'file-truename))) 9332 (unwind-protect 9333 (progn 9334 (fset 'file-truename 9335 (lambda (file-name &optional counter prev-dirs) 9336 (or (gethash file-name lsp-file-truename-cache) 9337 (puthash file-name (apply old-fn (list file-name counter prev-dirs)) 9338 lsp-file-truename-cache)))) 9339 ,@body) 9340 (fset 'file-truename old-fn)))) 9341 9342 9343 (defun lsp-virtual-buffer-call (key &rest args) 9344 (when lsp--virtual-buffer 9345 (when-let ((fn (plist-get lsp--virtual-buffer key))) 9346 (apply fn args)))) 9347 9348 (defun lsp-translate-column (column) 9349 "Translate COLUMN taking into account virtual buffers." 9350 (or (lsp-virtual-buffer-call :real->virtual-char column) 9351 column)) 9352 9353 (defun lsp-translate-line (line) 9354 "Translate LINE taking into account virtual buffers." 9355 (or (lsp-virtual-buffer-call :real->virtual-line line) 9356 line)) 9357 9358 9359 ;; lsp internal validation. 9360 9361 (defmacro lsp--doctor (&rest checks) 9362 `(-let [buf (current-buffer)] 9363 (with-current-buffer (get-buffer-create "*lsp-performance*") 9364 (with-help-window (current-buffer) 9365 ,@(-map (-lambda ((msg form)) 9366 `(insert (format "%s: %s\n" ,msg 9367 (let ((res (with-current-buffer buf 9368 ,form))) 9369 (cond 9370 ((eq res :optional) (propertize "OPTIONAL" 'face 'warning)) 9371 (res (propertize "OK" 'face 'success)) 9372 (t (propertize "ERROR" 'face 'error))))))) 9373 (-partition 2 checks)))))) 9374 9375 (define-obsolete-function-alias 'lsp-diagnose 9376 'lsp-doctor "lsp-mode 8.0.0") 9377 9378 (defun lsp-doctor () 9379 "Validate performance settings." 9380 (interactive) 9381 (lsp--doctor 9382 "Checking for Native JSON support" (functionp 'json-serialize) 9383 "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max) 9384 "Check `read-process-output-max' default has been changed from 4k" 9385 (and (boundp 'read-process-output-max) 9386 (> read-process-output-max 4096)) 9387 "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)" 9388 (condition-case _err 9389 (progn (lsp--make-message (list "a" "b")) 9390 nil) 9391 (error t)) 9392 "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000) 9393 "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) 9394 "Using emacs 28+ with native compilation?" 9395 (or (and (fboundp 'native-comp-available-p) 9396 (native-comp-available-p)) 9397 :optional))) 9398 9399 (declare-function package-version-join "ext:package") 9400 (declare-function package-desc-version "ext:package") 9401 (declare-function package--alist "ext:package") 9402 9403 (defun lsp-version () 9404 "Return string describing current version of `lsp-mode'." 9405 (interactive) 9406 (unless (featurep 'package) 9407 (require 'package)) 9408 (let ((ver (format "lsp-mode %s, Emacs %s, %s" 9409 (package-version-join 9410 (package-desc-version 9411 (car (alist-get 'lsp-mode (package--alist))))) 9412 emacs-version 9413 system-type))) 9414 (if (called-interactively-p 'interactive) 9415 (lsp--info "%s" ver) 9416 ver))) 9417 9418 9419 9420 ;; org-mode/virtual-buffer 9421 9422 (declare-function org-babel-get-src-block-info "ext:ob-core") 9423 (declare-function org-do-remove-indentation "ext:org-macs") 9424 (declare-function org-src-get-lang-mode "ext:org-src") 9425 (declare-function org-element-context "ext:org-element") 9426 9427 (defun lsp--virtual-buffer-update-position () 9428 (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range)) 9429 (funcall in-range)) 9430 lsp--virtual-buffer-connections)) 9431 (unless (equal virtual-buffer lsp--virtual-buffer) 9432 (lsp-org)) 9433 (when lsp-managed-mode 9434 (lsp-managed-mode -1) 9435 (lsp-mode -1) 9436 (setq lsp--buffer-workspaces nil) 9437 (setq lsp--virtual-buffer nil) 9438 (setq lsp-buffer-uri nil) 9439 9440 ;; force refresh of diagnostics 9441 (run-hooks 'lsp-after-diagnostics-hook)))) 9442 9443 (defun lsp-virtual-buffer-on-change (start end length) 9444 "Adjust on change event to be executed against the proper language server." 9445 (let ((max-point (max end 9446 (or (plist-get lsp--before-change-vals :end) 0) 9447 (+ start length)))) 9448 (when-let ((virtual-buffer (-first (lambda (vb) 9449 (let ((lsp--virtual-buffer vb)) 9450 (and (lsp-virtual-buffer-call :in-range start) 9451 (lsp-virtual-buffer-call :in-range max-point)))) 9452 lsp--virtual-buffer-connections))) 9453 (lsp-with-current-buffer virtual-buffer 9454 (lsp-on-change start end length 9455 (lambda (&rest _) 9456 (list :range (lsp--range (list :character 0 :line 0) 9457 lsp--virtual-buffer-point-max) 9458 :text (lsp--buffer-content)))))))) 9459 9460 (defun lsp-virtual-buffer-before-change (start _end) 9461 (when-let ((virtual-buffer (-first (lambda (vb) 9462 (lsp-with-current-buffer vb 9463 (lsp-virtual-buffer-call :in-range start))) 9464 lsp--virtual-buffer-connections))) 9465 (lsp-with-current-buffer virtual-buffer 9466 (setq lsp--virtual-buffer-point-max 9467 (lsp--point-to-position (lsp-virtual-buffer-call :last-point)))))) 9468 9469 (defun lsp-patch-on-change-event () 9470 (remove-hook 'after-change-functions #'lsp-on-change t) 9471 (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t) 9472 (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t)) 9473 9474 (defun lsp-kill-virtual-buffers () 9475 (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections)) 9476 9477 (defun lsp--move-point-in-indentation (point indentation) 9478 (save-excursion 9479 (goto-char point) 9480 (if (<= point (+ (line-beginning-position) indentation)) 9481 (line-beginning-position) 9482 point))) 9483 9484 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck") 9485 (declare-function flycheck-add-mode "ext:flycheck") 9486 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics") 9487 9488 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn) 9489 9490 (defun lsp-flycheck-add-mode (mode) 9491 "Register flycheck support for MODE." 9492 (lsp-diagnostics-lsp-checker-if-needed) 9493 (unless (flycheck-checker-supports-major-mode-p 'lsp mode) 9494 (flycheck-add-mode 'lsp mode))) 9495 9496 (defun lsp-progress-spinner-type () 9497 "Retrieve the spinner type value, if value is not a symbol of `spinner-types 9498 defaults to `progress-bar." 9499 (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar)) 9500 9501 (defun lsp-org () 9502 (interactive) 9503 (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range)) 9504 (funcall in-range)) 9505 lsp--virtual-buffer-connections)) 9506 (unless (equal lsp--virtual-buffer virtual-buffer) 9507 (setq lsp--buffer-workspaces workspaces) 9508 (setq lsp--virtual-buffer virtual-buffer) 9509 (setq lsp-buffer-uri nil) 9510 (lsp-mode 1) 9511 (lsp-managed-mode 1) 9512 (lsp-patch-on-change-event)) 9513 9514 (save-excursion 9515 (-let* (virtual-buffer 9516 (wcb (lambda (f) 9517 (with-current-buffer (plist-get virtual-buffer :buffer) 9518 (-let* (((&plist :major-mode :buffer-file-name 9519 :goto-buffer :workspaces) virtual-buffer) 9520 (lsp--virtual-buffer virtual-buffer) 9521 (lsp--buffer-workspaces workspaces)) 9522 (save-excursion 9523 (funcall goto-buffer) 9524 (funcall f)))))) 9525 ((&plist :begin :end :post-blank :language) (cl-second (org-element-context))) 9526 ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light))) 9527 9528 (file-name (if file-name 9529 (f-expand file-name) 9530 (user-error "You should specify file name in the src block header."))) 9531 (begin-marker (progn 9532 (goto-char begin) 9533 (forward-line) 9534 (set-marker (make-marker) (point)))) 9535 (end-marker (progn 9536 (goto-char end) 9537 (forward-line (1- (- post-blank))) 9538 (set-marker (make-marker) (1+ (point))))) 9539 (buf (current-buffer)) 9540 (src-block (buffer-substring-no-properties begin-marker 9541 (1- end-marker))) 9542 (indentation (with-temp-buffer 9543 (insert src-block) 9544 9545 (goto-char (point-min)) 9546 (let ((indentation (current-indentation))) 9547 (plist-put lsp--virtual-buffer :indentation indentation) 9548 (org-do-remove-indentation) 9549 (goto-char (point-min)) 9550 (- indentation (current-indentation)))))) 9551 (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t) 9552 9553 (when (fboundp 'flycheck-add-mode) 9554 (lsp-flycheck-add-mode 'org-mode)) 9555 9556 (setq lsp--virtual-buffer 9557 (list 9558 :in-range (lambda (&optional point) 9559 (<= begin-marker (or point (point)) (1- end-marker))) 9560 :goto-buffer (lambda () (goto-char begin-marker)) 9561 :buffer-string 9562 (lambda () 9563 (let ((src-block (buffer-substring-no-properties 9564 begin-marker 9565 (1- end-marker)))) 9566 (with-temp-buffer 9567 (insert src-block) 9568 9569 (goto-char (point-min)) 9570 (while (not (eobp)) 9571 (delete-region (point) (if (> (+ (point) indentation) (line-end-position)) 9572 (line-end-position) 9573 (+ (point) indentation))) 9574 (forward-line)) 9575 (buffer-substring-no-properties (point-min) 9576 (point-max))))) 9577 :buffer buf 9578 :begin begin-marker 9579 :end end-marker 9580 :indentation indentation 9581 :last-point (lambda () (1- end-marker)) 9582 :cur-position (lambda () 9583 (lsp-save-restriction-and-excursion 9584 (list :line (- (lsp--cur-line) 9585 (lsp--cur-line begin-marker)) 9586 :character (let ((character (- (point) 9587 (line-beginning-position) 9588 indentation))) 9589 (if (< character 0) 9590 0 9591 character))))) 9592 :line/character->point (-lambda (line character) 9593 (-let [inhibit-field-text-motion t] 9594 (+ indentation 9595 (lsp-save-restriction-and-excursion 9596 (goto-char begin-marker) 9597 (forward-line line) 9598 (-let [line-end (line-end-position)] 9599 (if (> character (- line-end (point))) 9600 line-end 9601 (forward-char character) 9602 (point))))))) 9603 :major-mode (org-src-get-lang-mode language) 9604 :buffer-file-name file-name 9605 :buffer-uri (lsp--path-to-uri file-name) 9606 :with-current-buffer wcb 9607 :buffer-live? (lambda (_) (buffer-live-p buf)) 9608 :buffer-name (lambda (_) 9609 (propertize (format "%s(%s:%s)%s" 9610 (buffer-name buf) 9611 begin-marker 9612 end-marker 9613 language) 9614 'face 'italic)) 9615 :real->virtual-line (lambda (line) 9616 (+ line (line-number-at-pos begin-marker) -1)) 9617 :real->virtual-char (lambda (char) (+ char indentation)) 9618 :cleanup (lambda () 9619 (set-marker begin-marker nil) 9620 (set-marker end-marker nil)))) 9621 (setf virtual-buffer lsp--virtual-buffer) 9622 (puthash file-name virtual-buffer lsp--virtual-buffer-mappings) 9623 (push virtual-buffer lsp--virtual-buffer-connections) 9624 9625 ;; TODO: tangle only connected sections 9626 (add-hook 'after-save-hook 'org-babel-tangle nil t) 9627 (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t) 9628 (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t) 9629 9630 (setq lsp--buffer-workspaces 9631 (lsp-with-current-buffer virtual-buffer 9632 (lsp) 9633 (plist-put virtual-buffer :workspaces (lsp-workspaces)) 9634 (lsp-workspaces))))))) 9635 9636 (defun lsp-virtual-buffer-disconnect (virtual-buffer) 9637 (interactive (list (or 9638 lsp--virtual-buffer 9639 (when lsp--virtual-buffer-connections 9640 (lsp--completing-read "Select virtual buffer to disconnect: " 9641 lsp--virtual-buffer-connections 9642 (-lambda ((&plist :buffer-file-name)) 9643 buffer-file-name)))))) 9644 (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer) 9645 (progn 9646 (lsp-with-current-buffer virtual-buffer 9647 (lsp--text-document-did-close)) 9648 (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections)) 9649 (when (eq virtual-buffer lsp--virtual-buffer) 9650 (setf lsp--virtual-buffer nil)) 9651 (when cleanup (funcall cleanup)) 9652 (remhash file-name lsp--virtual-buffer-mappings) 9653 9654 (lsp--virtual-buffer-update-position) 9655 (lsp--info "Disconnected from buffer %s" file-name)) 9656 (lsp--error "Nothing to disconnect from?"))) 9657 9658 9659 ;; inlay hints 9660 9661 (defface lsp-inlay-hint-face 9662 '((t :inherit font-lock-comment-face)) 9663 "The face to use for the JavaScript inlays." 9664 :group 'lsp-mode 9665 :package-version '(lsp-mode . "9.0.0")) 9666 9667 (defface lsp-inlay-hint-type-face 9668 '((t :inherit lsp-inlay-hint-face)) 9669 "Face for inlay type hints (e.g. inferred variable types)." 9670 :group 'lsp-mode 9671 :package-version '(lsp-mode . "9.0.0")) 9672 9673 (defcustom lsp-inlay-hint-type-format "%s" 9674 "Format string for variable inlays (part of the inlay face)." 9675 :type '(string :tag "String") 9676 :group 'lsp-mode 9677 :package-version '(lsp-mode . "9.0.0")) 9678 9679 (defface lsp-inlay-hint-parameter-face 9680 '((t :inherit lsp-inlay-hint-face)) 9681 "Face for inlay parameter hints (e.g. function parameter names at 9682 call-site)." 9683 :group 'lsp-mode 9684 :package-version '(lsp-mode . "9.0.0")) 9685 9686 (defcustom lsp-inlay-hint-param-format "%s" 9687 "Format string for parameter inlays (part of the inlay face)." 9688 :type '(string :tag "String") 9689 :group 'lsp-mode 9690 :package-version '(lsp-mode . "9.0.0")) 9691 9692 (defcustom lsp-update-inlay-hints-on-scroll t 9693 "If non-nil update inlay hints immediately when scrolling or 9694 modifying window sizes." 9695 :type 'boolean 9696 :package-version '(lsp-mode . "9.0.0")) 9697 9698 (defun lsp--format-inlay (text kind) 9699 (cond 9700 ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text)) 9701 ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text)) 9702 (t text))) 9703 9704 (defun lsp--face-for-inlay (kind) 9705 (cond 9706 ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face) 9707 ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face) 9708 (t 'lsp-inlay-hint-face))) 9709 9710 (defun lsp--update-inlay-hints-scroll-function (window start) 9711 (lsp-update-inlay-hints start (window-end window t))) 9712 9713 (defun lsp--update-inlay-hints () 9714 (lsp-update-inlay-hints (window-start) (window-end nil t))) 9715 9716 (defun lsp--label-from-inlay-hints-response (label) 9717 "Returns a string label built from an array of 9718 InlayHintLabelParts or the argument itself if it's already a 9719 string." 9720 (cl-typecase label 9721 (string label) 9722 (vector 9723 (string-join (mapcar (lambda (part) 9724 (-let (((&InlayHintLabelPart :value) part)) 9725 value)) 9726 label))))) 9727 9728 (defun lsp-update-inlay-hints (start end) 9729 (lsp-request-async 9730 "textDocument/inlayHint" 9731 (lsp-make-inlay-hints-params 9732 :text-document (lsp--text-document-identifier) 9733 :range (lsp-make-range :start 9734 (lsp-point-to-position start) 9735 :end 9736 (lsp-point-to-position end))) 9737 (lambda (res) 9738 (lsp--remove-overlays 'lsp-inlay-hint) 9739 (dolist (hint res) 9740 (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint) 9741 (kind (or kind? lsp/inlay-hint-kind-type-hint)) 9742 (label (lsp--label-from-inlay-hints-response label)) 9743 (pos (lsp--position-to-point position)) 9744 (overlay (make-overlay pos pos nil 'front-advance 'end-advance))) 9745 (when (stringp label) 9746 (overlay-put overlay 'lsp-inlay-hint t) 9747 (overlay-put overlay 'before-string 9748 (format "%s%s%s" 9749 (if padding-left? " " "") 9750 (propertize (lsp--format-inlay label kind) 9751 'font-lock-face (lsp--face-for-inlay kind)) 9752 (if padding-right? " " ""))))))) 9753 :mode 'tick)) 9754 9755 (define-minor-mode lsp-inlay-hints-mode 9756 "Mode for displaying inlay hints." 9757 :lighter nil 9758 (cond 9759 ((and lsp-inlay-hints-mode lsp--buffer-workspaces) 9760 (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t) 9761 (when lsp-update-inlay-hints-on-scroll 9762 (add-to-list (make-local-variable 'window-scroll-functions) 9763 #'lsp--update-inlay-hints-scroll-function))) 9764 (t 9765 (lsp--remove-overlays 'lsp-inlay-hint) 9766 (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t) 9767 (setf window-scroll-functions 9768 (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions))))) 9769 9770 9771 9772 ;;;###autoload 9773 (defun lsp-start-plain () 9774 "Start `lsp-mode' using minimal configuration using the latest `melpa' version 9775 of the packages. 9776 9777 In case the major-mode that you are using for " 9778 (interactive) 9779 (let ((start-plain (make-temp-file "plain" nil ".el"))) 9780 (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el" 9781 start-plain t) 9782 (async-shell-command 9783 (format "%s -q -l %s %s" 9784 (expand-file-name invocation-name invocation-directory) 9785 start-plain 9786 (or (buffer-file-name) "")) 9787 (generate-new-buffer " *lsp-start-plain*")))) 9788 9789 9790 9791 (provide 'lsp-mode) 9792 ;;; lsp-mode.el ends here