lsp-mode.el (427902B)
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-meson lsp-metals lsp-mint 185 lsp-mojo lsp-move lsp-mssql lsp-nginx lsp-nim lsp-nix lsp-nushell lsp-ocaml 186 lsp-openscad lsp-pascal lsp-perl lsp-perlnavigator lsp-php lsp-pls 187 lsp-purescript lsp-pwsh lsp-pyls lsp-pylsp lsp-pyright lsp-python-ms 188 lsp-qml lsp-r lsp-racket lsp-remark lsp-rf lsp-rubocop lsp-ruby-lsp 189 lsp-ruby-syntax-tree lsp-ruff-lsp lsp-rust lsp-semgrep lsp-shader 190 lsp-solargraph lsp-solidity lsp-sonarlint lsp-sorbet lsp-sourcekit 191 lsp-sql lsp-sqls lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform 192 lsp-tex lsp-tilt lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-v 193 lsp-vala lsp-verilog lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl 194 lsp-xml lsp-yaml lsp-yang lsp-zig) 195 "List of the clients to be automatically required." 196 :group 'lsp-mode 197 :type '(repeat symbol)) 198 199 (defcustom lsp-progress-via-spinner t 200 "If non-nil, display LSP $/progress reports via a spinner in the modeline." 201 :group 'lsp-mode 202 :type 'boolean) 203 204 (defcustom lsp-progress-spinner-type 'progress-bar 205 "Holds the type of spinner to be used in the mode-line. 206 Takes a value accepted by `spinner-start'." 207 :group 'lsp-mode 208 :type `(choice :tag "Choose a spinner by name" 209 ,@(mapcar (lambda (c) (list 'const (car c))) 210 spinner-types))) 211 212 (defvar-local lsp-use-workspace-root-for-server-default-directory nil 213 "Use `lsp-workspace-root' for `default-directory' when starting LSP process.") 214 215 (defvar-local lsp--cur-workspace nil) 216 217 (defvar-local lsp--cur-version 0) 218 (defvar-local lsp--virtual-buffer-connections nil) 219 (defvar-local lsp--virtual-buffer nil) 220 (defvar lsp--virtual-buffer-mappings (ht)) 221 222 (defvar lsp--uri-file-prefix (pcase system-type 223 (`windows-nt "file:///") 224 (_ "file://")) 225 "Prefix for a file-uri.") 226 227 (defvar-local lsp-buffer-uri nil 228 "If set, return it instead of calculating it using `buffer-file-name'.") 229 230 (define-error 'lsp-error "Unknown lsp-mode error") 231 (define-error 'lsp-empty-response-error 232 "Empty response from the language server" 'lsp-error) 233 (define-error 'lsp-timed-out-error 234 "Timed out while waiting for a response from the language server" 'lsp-error) 235 (define-error 'lsp-capability-not-supported 236 "Capability not supported by the language server" 'lsp-error) 237 (define-error 'lsp-file-scheme-not-supported 238 "Unsupported file scheme" 'lsp-error) 239 (define-error 'lsp-client-already-exists-error 240 "A client with this server-id already exists" 'lsp-error) 241 (define-error 'lsp-no-code-actions 242 "No code actions" 'lsp-error) 243 244 (defcustom lsp-auto-guess-root nil 245 "Automatically guess the project root using projectile/project. 246 Do *not* use this setting unless you are familiar with `lsp-mode' 247 internals and you are sure that all of your projects are 248 following `projectile'/`project.el' conventions." 249 :group 'lsp-mode 250 :type 'boolean) 251 252 (defcustom lsp-guess-root-without-session nil 253 "Ignore the session file when calculating the project root. 254 You almost always want to set lsp-auto-guess-root too. 255 Do *not* use this setting unless you are familiar with `lsp-mode' 256 internals and you are sure that all of your projects are 257 following `projectile'/`project.el' conventions." 258 :group 'lsp-mode 259 :type 'boolean) 260 261 (defcustom lsp-restart 'interactive 262 "Defines how server-exited events must be handled." 263 :group 'lsp-mode 264 :type '(choice (const interactive) 265 (const auto-restart) 266 (const ignore))) 267 268 (defcustom lsp-session-file (expand-file-name (locate-user-emacs-file ".lsp-session-v1")) 269 "File where session information is stored." 270 :group 'lsp-mode 271 :type 'file) 272 273 (defcustom lsp-auto-configure t 274 "Auto configure `lsp-mode' main features. 275 When set to t `lsp-mode' will auto-configure completion, 276 code-actions, breadcrumb, `flycheck', `flymake', `imenu', symbol highlighting, 277 lenses, links, and so on. 278 279 For finer granularity you may use `lsp-enable-*' properties." 280 :group 'lsp-mode 281 :type 'boolean 282 :package-version '(lsp-mode . "6.1")) 283 284 (defcustom lsp-disabled-clients nil 285 "A list of disabled/blocklisted clients. 286 Each entry in the list can be either: 287 a symbol, the server-id for the LSP client, or 288 a cons pair (MAJOR-MODE . CLIENTS), where MAJOR-MODE is the major-mode, 289 and CLIENTS is either a client or a list of clients. 290 291 This option can also be used as a file- or directory-local variable to 292 disable a language server for individual files or directories/projects 293 respectively." 294 :group 'lsp-mode 295 :type '(repeat (symbol)) 296 :safe 'listp 297 :package-version '(lsp-mode . "6.1")) 298 299 (defvar lsp-clients (make-hash-table :test 'eql) 300 "Hash table server-id -> client. 301 It contains all of the clients that are currently registered.") 302 303 (defvar lsp-enabled-clients nil 304 "List of clients allowed to be used for projects. 305 When nil, all registered clients are considered candidates.") 306 307 (defvar lsp-last-id 0 308 "Last request id.") 309 310 (defcustom lsp-before-initialize-hook nil 311 "List of functions to be called before a Language Server has been initialized 312 for a new workspace." 313 :type 'hook 314 :group 'lsp-mode) 315 316 (defcustom lsp-after-initialize-hook nil 317 "List of functions to be called after a Language Server has been initialized 318 for a new workspace." 319 :type 'hook 320 :group 'lsp-mode) 321 322 (defcustom lsp-before-open-hook nil 323 "List of functions to be called before a new file with LSP support is opened." 324 :type 'hook 325 :group 'lsp-mode) 326 327 (defcustom lsp-after-open-hook nil 328 "List of functions to be called after a new file with LSP support is opened." 329 :type 'hook 330 :group 'lsp-mode) 331 332 (defcustom lsp-enable-file-watchers t 333 "If non-nil lsp-mode will watch the files in the workspace if 334 the server has requested that." 335 :type 'boolean 336 :group 'lsp-mode 337 :package-version '(lsp-mode . "6.1")) 338 ;;;###autoload(put 'lsp-enable-file-watchers 'safe-local-variable #'booleanp) 339 340 (define-obsolete-variable-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "8.0.0") 341 342 (defcustom lsp-file-watch-ignored-directories 343 '(; SCM tools 344 "[/\\\\]\\.git\\'" 345 "[/\\\\]\\.github\\'" 346 "[/\\\\]\\.gitlab\\'" 347 "[/\\\\]\\.circleci\\'" 348 "[/\\\\]\\.hg\\'" 349 "[/\\\\]\\.bzr\\'" 350 "[/\\\\]_darcs\\'" 351 "[/\\\\]\\.svn\\'" 352 "[/\\\\]_FOSSIL_\\'" 353 ;; IDE or build tools 354 "[/\\\\]\\.idea\\'" 355 "[/\\\\]\\.ensime_cache\\'" 356 "[/\\\\]\\.eunit\\'" 357 "[/\\\\]node_modules" 358 "[/\\\\]\\.yarn\\'" 359 "[/\\\\]\\.fslckout\\'" 360 "[/\\\\]\\.tox\\'" 361 "[/\\\\]\\.nox\\'" 362 "[/\\\\]dist\\'" 363 "[/\\\\]dist-newstyle\\'" 364 "[/\\\\]\\.stack-work\\'" 365 "[/\\\\]\\.bloop\\'" 366 "[/\\\\]\\.metals\\'" 367 "[/\\\\]target\\'" 368 "[/\\\\]\\.ccls-cache\\'" 369 "[/\\\\]\\.vs\\'" 370 "[/\\\\]\\.vscode\\'" 371 "[/\\\\]\\.venv\\'" 372 "[/\\\\]\\.mypy_cache\\'" 373 "[/\\\\]\\.pytest_cache\\'" 374 ;; Swift Package Manager 375 "[/\\\\]\\.build\\'" 376 ;; Python 377 "[/\\\\]__pycache__\\'" 378 ;; Autotools output 379 "[/\\\\]\\.deps\\'" 380 "[/\\\\]build-aux\\'" 381 "[/\\\\]autom4te.cache\\'" 382 "[/\\\\]\\.reference\\'" 383 ;; Bazel 384 "[/\\\\]bazel-[^/\\\\]+\\'" 385 ;; CSharp 386 "[/\\\\]\\.cache[/\\\\]lsp-csharp\\'" 387 "[/\\\\]\\.meta\\'" 388 "[/\\\\]\\.nuget\\'" 389 ;; Unity 390 "[/\\\\]Library\\'" 391 ;; Clojure 392 "[/\\\\]\\.lsp\\'" 393 "[/\\\\]\\.clj-kondo\\'" 394 "[/\\\\]\\.shadow-cljs\\'" 395 "[/\\\\]\\.babel_cache\\'" 396 "[/\\\\]\\.cpcache\\'" 397 "[/\\\\]\\checkouts\\'" 398 ;; Gradle 399 "[/\\\\]\\.gradle\\'" 400 ;; Maven 401 "[/\\\\]\\.m2\\'" 402 ;; .Net Core build-output 403 "[/\\\\]bin/Debug\\'" 404 "[/\\\\]obj\\'" 405 ;; OCaml and Dune 406 "[/\\\\]_opam\\'" 407 "[/\\\\]_build\\'" 408 ;; Elixir 409 "[/\\\\]\\.elixir_ls\\'" 410 ;; Elixir Credo 411 "[/\\\\]\\.elixir-tools\\'" 412 ;; terraform and terragrunt 413 "[/\\\\]\\.terraform\\'" 414 "[/\\\\]\\.terragrunt-cache\\'" 415 ;; nix-direnv 416 "[/\\\\]\\result" 417 "[/\\\\]\\result-bin" 418 "[/\\\\]\\.direnv\\'") 419 "List of regexps matching directory paths which won't be monitored when 420 creating file watches. Customization of this variable is only honored at 421 the global level or at a root of an lsp workspace." 422 :group 'lsp-mode 423 :type '(repeat string) 424 :package-version '(lsp-mode . "8.0.0")) 425 426 (define-obsolete-function-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "7.0.1") 427 428 (defun lsp-file-watch-ignored-directories () 429 lsp-file-watch-ignored-directories) 430 431 ;; Allow lsp-file-watch-ignored-directories as a file or directory-local variable 432 ;;;###autoload(put 'lsp-file-watch-ignored-directories 'safe-local-variable 'lsp--string-listp) 433 434 (defcustom lsp-file-watch-ignored-files 435 '( 436 ;; Flycheck tempfiles 437 "[/\\\\]flycheck_[^/\\\\]+\\'" 438 ;; lockfiles 439 "[/\\\\]\\.#[^/\\\\]+\\'" 440 ;; backup files 441 "[/\\\\][^/\\\\]+~\\'" ) 442 "List of regexps matching files for which change events will 443 not be sent to the server. 444 445 This setting has no impact on whether a file-watch is created for 446 a directory; it merely prevents notifications pertaining to 447 matched files from being sent to the server. To prevent a 448 file-watch from being created for a directory, customize 449 `lsp-file-watch-ignored-directories' 450 451 Customization of this variable is only honored at the global 452 level or at a root of an lsp workspace." 453 :group 'lsp-mode 454 :type '(repeat string) 455 :package-version '(lsp-mode . "8.0.0")) 456 457 ;; Allow lsp-file-watch-ignored-files as a file or directory-local variable 458 ;;;###autoload(put 'lsp-file-watch-ignored-files 'safe-local-variable 'lsp--string-listp) 459 460 (defcustom lsp-after-uninitialized-functions nil 461 "List of functions to be called after a Language Server has been uninitialized." 462 :type 'hook 463 :group 'lsp-mode 464 :package-version '(lsp-mode . "6.3")) 465 466 (defconst lsp--sync-full 1) 467 (defconst lsp--sync-incremental 2) 468 469 (defcustom lsp-debounce-full-sync-notifications t 470 "If non-nil debounce full sync events. 471 This flag affects only servers which do not support incremental updates." 472 :type 'boolean 473 :group 'lsp-mode 474 :package-version '(lsp-mode . "6.1")) 475 476 (defcustom lsp-debounce-full-sync-notifications-interval 1.0 477 "Time to wait before sending full sync synchronization after buffer modification." 478 :type 'float 479 :group 'lsp-mode 480 :package-version '(lsp-mode . "6.1")) 481 482 (defvar lsp--stderr-index 0) 483 484 (defvar lsp--delayed-requests nil) 485 (defvar lsp--delay-timer nil) 486 487 (defcustom lsp-document-sync-method nil 488 "How to sync the document with the language server." 489 :type '(choice (const :tag "Documents are synced by always sending the full content of the document." lsp--sync-full) 490 (const :tag "Documents are synced by always sending incremental changes to the document." lsp--sync-incremental) 491 (const :tag "Use the method recommended by the language server." nil)) 492 :group 'lsp-mode) 493 494 (defcustom lsp-auto-execute-action t 495 "Auto-execute single action." 496 :type 'boolean 497 :group 'lsp-mode) 498 499 (defcustom lsp-enable-links t 500 "If non-nil, all references to links in a file will be made clickable, if 501 supported by the language server." 502 :type 'boolean 503 :group 'lsp-mode 504 :package-version '(lsp-mode . "6.1")) 505 506 (defcustom lsp-enable-imenu t 507 "If non-nil, automatically enable `imenu' integration when server provides 508 `textDocument/documentSymbol'." 509 :type 'boolean 510 :group 'lsp-mode 511 :package-version '(lsp-mode . "6.2")) 512 513 (defcustom lsp-enable-dap-auto-configure t 514 "If non-nil, enable `dap-auto-configure-mode`." 515 :type 'boolean 516 :group 'lsp-mode 517 :package-version '(lsp-mode . "7.0")) 518 519 (defcustom lsp-eldoc-enable-hover t 520 "If non-nil, `eldoc' will display hover info when it is present." 521 :type 'boolean 522 :group 'lsp-mode) 523 524 (defcustom lsp-eldoc-render-all nil 525 "Display all of the info returned by document/onHover. 526 If this is set to nil, `eldoc' will show only the symbol information." 527 :type 'boolean 528 :group 'lsp-mode) 529 530 (define-obsolete-variable-alias 'lsp-enable-completion-at-point 531 'lsp-completion-enable "lsp-mode 7.0.1") 532 533 (defcustom lsp-completion-enable t 534 "Enable `completion-at-point' integration." 535 :type 'boolean 536 :group 'lsp-completion) 537 538 (defcustom lsp-enable-symbol-highlighting t 539 "Highlight references of the symbol at point." 540 :type 'boolean 541 :group 'lsp-mode) 542 543 (defcustom lsp-enable-xref t 544 "Enable xref integration." 545 :type 'boolean 546 :group 'lsp-mode) 547 548 (defcustom lsp-references-exclude-definition nil 549 "If non-nil, exclude declarations when finding references." 550 :type 'boolean 551 :group 'lsp-mode) 552 553 (defcustom lsp-enable-indentation t 554 "Indent regions using the file formatting functionality provided by the 555 language server." 556 :type 'boolean 557 :group 'lsp-mode) 558 559 (defcustom lsp-enable-on-type-formatting t 560 "Enable `textDocument/onTypeFormatting' integration." 561 :type 'boolean 562 :group 'lsp-mode) 563 564 (defcustom lsp-enable-text-document-color t 565 "Enable `textDocument/documentColor' integration." 566 :type 'boolean 567 :group 'lsp-mode) 568 569 (defcustom lsp-before-save-edits t 570 "If non-nil, `lsp-mode' will apply edits suggested by the language server 571 before saving a document." 572 :type 'boolean 573 :group 'lsp-mode) 574 575 (defcustom lsp-after-apply-edits-hook nil 576 "Hooks to run when text edit is applied. 577 It contains the operation source." 578 :type 'hook 579 :group 'lsp-mode 580 :package-version '(lsp-mode . "8.0.0")) 581 582 (defcustom lsp-apply-edits-after-file-operations t 583 "Whether to apply edits returned by server after file operations if any. 584 Applicable only if server supports workspace.fileOperations for operations: 585 `workspace/willRenameFiles', `workspace/willCreateFiles' and 586 `workspace/willDeleteFiles'." 587 :group 'lsp-mode 588 :type 'boolean) 589 590 (defcustom lsp-modeline-code-actions-enable t 591 "Whether to show code actions on modeline." 592 :type 'boolean 593 :group 'lsp-modeline) 594 595 (defcustom lsp-modeline-diagnostics-enable t 596 "Whether to show diagnostics on modeline." 597 :type 'boolean 598 :group 'lsp-modeline) 599 600 (defcustom lsp-modeline-workspace-status-enable t 601 "Whether to show workspace status on modeline." 602 :type 'boolean 603 :group 'lsp-modeline 604 :package-version '(lsp-mode . "8.0.0")) 605 606 (defcustom lsp-headerline-breadcrumb-enable t 607 "Whether to enable breadcrumb on headerline." 608 :type 'boolean 609 :group 'lsp-headerline) 610 611 (defcustom lsp-configure-hook nil 612 "Hooks to run when `lsp-configure-buffer' is called." 613 :type 'hook 614 :group 'lsp-mode) 615 616 (defcustom lsp-unconfigure-hook nil 617 "Hooks to run when `lsp-unconfig-buffer' is called." 618 :type 'hook 619 :group 'lsp-mode) 620 621 (defcustom lsp-after-diagnostics-hook nil 622 "Hooks to run after diagnostics are received. 623 Note: it runs only if the receiving buffer is open. Use 624 `lsp-diagnostics-updated-hook'if you want to be notified when 625 diagnostics have changed." 626 :type 'hook 627 :group 'lsp-mode) 628 629 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook 630 'lsp-diagnostics-updated-hook "lsp-mode 6.4") 631 632 (defcustom lsp-diagnostics-updated-hook nil 633 "Hooks to run after diagnostics are received." 634 :type 'hook 635 :group 'lsp-mode) 636 637 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook 638 'lsp-workspace-folders-changed-functions "lsp-mode 6.3") 639 640 (defcustom lsp-workspace-folders-changed-functions nil 641 "Hooks to run after the folders has changed. 642 The hook will receive two parameters list of added and removed folders." 643 :type 'hook 644 :group 'lsp-mode) 645 646 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0") 647 648 (defcustom lsp-before-apply-edits-hook nil 649 "Hooks to run before applying edits." 650 :type 'hook 651 :group 'lsp-mode) 652 653 (defgroup lsp-imenu nil 654 "LSP Imenu." 655 :group 'lsp-mode 656 :tag "LSP Imenu") 657 658 (defcustom lsp-imenu-show-container-name t 659 "Display the symbol's container name in an imenu entry." 660 :type 'boolean 661 :group 'lsp-imenu) 662 663 (defcustom lsp-imenu-container-name-separator "/" 664 "Separator string to use to separate the container name from the symbol while 665 displaying imenu entries." 666 :type 'string 667 :group 'lsp-imenu) 668 669 (defcustom lsp-imenu-sort-methods '(kind name) 670 "How to sort the imenu items. 671 672 The value is a list of `kind' `name' or `position'. Priorities 673 are determined by the index of the element." 674 :type '(repeat (choice (const name) 675 (const position) 676 (const kind))) 677 :group 'lsp-imenu) 678 679 (defcustom lsp-imenu-index-symbol-kinds nil 680 "Which symbol kinds to show in imenu." 681 :type '(repeat (choice (const :tag "Miscellaneous" nil) 682 (const :tag "File" File) 683 (const :tag "Module" Module) 684 (const :tag "Namespace" Namespace) 685 (const :tag "Package" Package) 686 (const :tag "Class" Class) 687 (const :tag "Method" Method) 688 (const :tag "Property" Property) 689 (const :tag "Field" Field) 690 (const :tag "Constructor" Constructor) 691 (const :tag "Enum" Enum) 692 (const :tag "Interface" Interface) 693 (const :tag "Function" Function) 694 (const :tag "Variable" Variable) 695 (const :tag "Constant" Constant) 696 (const :tag "String" String) 697 (const :tag "Number" Number) 698 (const :tag "Boolean" Boolean) 699 (const :tag "Array" Array) 700 (const :tag "Object" Object) 701 (const :tag "Key" Key) 702 (const :tag "Null" Null) 703 (const :tag "Enum Member" EnumMember) 704 (const :tag "Struct" Struct) 705 (const :tag "Event" Event) 706 (const :tag "Operator" Operator) 707 (const :tag "Type Parameter" TypeParameter))) 708 :group 'lsp-imenu) 709 710 ;; vibhavp: Should we use a lower value (5)? 711 (defcustom lsp-response-timeout 10 712 "Number of seconds to wait for a response from the language server before 713 timing out. Nil if no timeout." 714 :type '(choice 715 (number :tag "Seconds") 716 (const :tag "No timeout" nil)) 717 :group 'lsp-mode) 718 719 (defcustom lsp-tcp-connection-timeout 2 720 "The timeout for tcp connection in seconds." 721 :type 'number 722 :group 'lsp-mode 723 :package-version '(lsp-mode . "6.2")) 724 725 (defconst lsp--imenu-compare-function-alist 726 (list (cons 'name #'lsp--imenu-compare-name) 727 (cons 'kind #'lsp--imenu-compare-kind) 728 (cons 'position #'lsp--imenu-compare-line-col)) 729 "An alist of (METHOD . FUNCTION). 730 METHOD is one of the symbols accepted by 731 `lsp-imenu-sort-methods'. 732 733 FUNCTION takes two hash tables representing DocumentSymbol. It 734 returns a negative number, 0, or a positive number indicating 735 whether the first parameter is less than, equal to, or greater 736 than the second parameter.") 737 738 (defcustom lsp-diagnostic-clean-after-change nil 739 "When non-nil, clean the diagnostics on change. 740 741 Note that when that setting is nil, `lsp-mode' will show stale 742 diagnostics until server publishes the new set of diagnostics" 743 :type 'boolean 744 :group 'lsp-diagnostics 745 :package-version '(lsp-mode . "7.0.1")) 746 747 (defcustom lsp-server-trace nil 748 "Request tracing on the server side. 749 The actual trace output at each level depends on the language server in use. 750 Changes take effect only when a new session is started." 751 :type '(choice (const :tag "Disabled" "off") 752 (const :tag "Messages only" "messages") 753 (const :tag "Verbose" "verbose") 754 (const :tag "Default (disabled)" nil)) 755 :group 'lsp-mode 756 :package-version '(lsp-mode . "6.1")) 757 758 (defcustom lsp-auto-touch-files t 759 "If non-nil ensure the files exist before sending 760 `textDocument/didOpen' notification." 761 :type 'boolean 762 :group 'lsp-mode 763 :package-version '(lsp-mode . "9.0.0")) 764 765 (defvar lsp-language-id-configuration 766 '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake") 767 ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile") 768 ("\\.astro$" . "astro") 769 ("\\.cs\\'" . "csharp") 770 ("\\.css$" . "css") 771 ("\\.cypher$" . "cypher") 772 ("\\.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 ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson") 805 (ada-mode . "ada") 806 (ada-ts-mode . "ada") 807 (gpr-mode . "gpr") 808 (gpr-ts-mode . "gpr") 809 (awk-mode . "awk") 810 (awk-ts-mode . "awk") 811 (nxml-mode . "xml") 812 (sql-mode . "sql") 813 (vimrc-mode . "vim") 814 (vimscript-ts-mode . "vim") 815 (sh-mode . "shellscript") 816 (bash-ts-mode . "shellscript") 817 (ebuild-mode . "shellscript") 818 (pkgbuild-mode . "shellscript") 819 (envrc-file-mode . "shellscript") 820 (scala-mode . "scala") 821 (scala-ts-mode . "scala") 822 (julia-mode . "julia") 823 (julia-ts-mode . "julia") 824 (clojure-mode . "clojure") 825 (clojurec-mode . "clojure") 826 (clojurescript-mode . "clojurescript") 827 (clojure-ts-mode . "clojure") 828 (clojure-ts-clojurec-mode . "clojure") 829 (clojure-ts-clojurescript-mode . "clojurescript") 830 (java-mode . "java") 831 (java-ts-mode . "java") 832 (jdee-mode . "java") 833 (groovy-mode . "groovy") 834 (python-mode . "python") 835 (python-ts-mode . "python") 836 (cython-mode . "python") 837 ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo") 838 (lsp--render-markdown . "markdown") 839 (move-mode . "move") 840 (rust-mode . "rust") 841 (rust-ts-mode . "rust") 842 (rustic-mode . "rust") 843 (kotlin-mode . "kotlin") 844 (kotlin-ts-mode . "kotlin") 845 (css-mode . "css") 846 (css-ts-mode . "css") 847 (less-mode . "less") 848 (less-css-mode . "less") 849 (lua-mode . "lua") 850 (lua-ts-mode . "lua") 851 (sass-mode . "sass") 852 (ssass-mode . "sass") 853 (scss-mode . "scss") 854 (scad-mode . "openscad") 855 (xml-mode . "xml") 856 (c-mode . "c") 857 (c-ts-mode . "c") 858 (c++-mode . "cpp") 859 (c++-ts-mode . "cpp") 860 (cuda-mode . "cuda") 861 (objc-mode . "objective-c") 862 (html-mode . "html") 863 (html-ts-mode . "html") 864 (sgml-mode . "html") 865 (mhtml-mode . "html") 866 (mint-mode . "mint") 867 (go-dot-mod-mode . "go.mod") 868 (go-mod-ts-mode . "go.mod") 869 (go-mode . "go") 870 (go-ts-mode . "go") 871 (graphql-mode . "graphql") 872 (haskell-mode . "haskell") 873 (hack-mode . "hack") 874 (php-mode . "php") 875 (php-ts-mode . "php") 876 (powershell-mode . "powershell") 877 (powershell-mode . "PowerShell") 878 (powershell-ts-mode . "powershell") 879 (json-mode . "json") 880 (json-ts-mode . "json") 881 (jsonc-mode . "jsonc") 882 (rjsx-mode . "javascript") 883 (js2-mode . "javascript") 884 (js-mode . "javascript") 885 (js-ts-mode . "javascript") 886 (typescript-mode . "typescript") 887 (typescript-ts-mode . "typescript") 888 (tsx-ts-mode . "typescriptreact") 889 (svelte-mode . "svelte") 890 (fsharp-mode . "fsharp") 891 (reason-mode . "reason") 892 (caml-mode . "ocaml") 893 (tuareg-mode . "ocaml") 894 (swift-mode . "swift") 895 (elixir-mode . "elixir") 896 (elixir-ts-mode . "elixir") 897 (heex-ts-mode . "elixir") 898 (conf-javaprop-mode . "spring-boot-properties") 899 (yaml-mode . "yaml") 900 (yaml-ts-mode . "yaml") 901 (ruby-mode . "ruby") 902 (enh-ruby-mode . "ruby") 903 (ruby-ts-mode . "ruby") 904 (fortran-mode . "fortran") 905 (f90-mode . "fortran") 906 (elm-mode . "elm") 907 (dart-mode . "dart") 908 (erlang-mode . "erlang") 909 (dockerfile-mode . "dockerfile") 910 (dockerfile-ts-mode . "dockerfile") 911 (csharp-mode . "csharp") 912 (csharp-tree-sitter-mode . "csharp") 913 (csharp-ts-mode . "csharp") 914 (plain-tex-mode . "plaintex") 915 (context-mode . "context") 916 (cypher-mode . "cypher") 917 (latex-mode . "latex") 918 (LaTeX-mode . "latex") 919 (v-mode . "v") 920 (vhdl-mode . "vhdl") 921 (vhdl-ts-mode . "vhdl") 922 (verilog-mode . "verilog") 923 (terraform-mode . "terraform") 924 (ess-julia-mode . "julia") 925 (ess-r-mode . "r") 926 (crystal-mode . "crystal") 927 (nim-mode . "nim") 928 (dhall-mode . "dhall") 929 (cmake-mode . "cmake") 930 (cmake-ts-mode . "cmake") 931 (purescript-mode . "purescript") 932 (gdscript-mode . "gdscript") 933 (gdscript-ts-mode . "gdscript") 934 (perl-mode . "perl") 935 (cperl-mode . "perl") 936 (robot-mode . "robot") 937 (racket-mode . "racket") 938 (nix-mode . "nix") 939 (nix-ts-mode . "Nix") 940 (prolog-mode . "prolog") 941 (vala-mode . "vala") 942 (actionscript-mode . "actionscript") 943 (d-mode . "d") 944 (zig-mode . "zig") 945 (text-mode . "plaintext") 946 (markdown-mode . "markdown") 947 (gfm-mode . "markdown") 948 (beancount-mode . "beancount") 949 (conf-toml-mode . "toml") 950 (toml-ts-mode . "toml") 951 (org-mode . "org") 952 (org-journal-mode . "org") 953 (nginx-mode . "nginx") 954 (magik-mode . "magik") 955 (magik-ts-mode . "magik") 956 (idris-mode . "idris") 957 (idris2-mode . "idris2") 958 (gleam-mode . "gleam") 959 (graphviz-dot-mode . "dot") 960 (tiltfile-mode . "tiltfile") 961 (solidity-mode . "solidity") 962 (bibtex-mode . "bibtex") 963 (rst-mode . "restructuredtext") 964 (glsl-mode . "glsl") 965 (shader-mode . "shaderlab") 966 (wgsl-mode . "wgsl") 967 (jq-mode . "jq") 968 (jq-ts-mode . "jq") 969 (protobuf-mode . "protobuf") 970 (nushell-mode . "nushell") 971 (nushell-ts-mode . "nushell") 972 (meson-mode . "meson") 973 (yang-mode . "yang")) 974 "Language id configuration.") 975 976 (defvar lsp--last-active-workspaces nil 977 "Keep track of last active workspace. 978 We want to try the last workspace first when jumping into a library 979 directory") 980 981 (defvar lsp-method-requirements 982 '(("textDocument/callHierarchy" :capability :callHierarchyProvider) 983 ("textDocument/codeAction" :capability :codeActionProvider) 984 ("codeAction/resolve" 985 :check-command (lambda (workspace) 986 (with-lsp-workspace workspace 987 (lsp:code-action-options-resolve-provider? 988 (lsp--capability-for-method "textDocument/codeAction"))))) 989 ("textDocument/codeLens" :capability :codeLensProvider) 990 ("textDocument/completion" :capability :completionProvider) 991 ("completionItem/resolve" 992 :check-command (lambda (wk) 993 (with-lsp-workspace wk 994 (lsp:completion-options-resolve-provider? 995 (lsp--capability-for-method "textDocument/completion"))))) 996 ("textDocument/declaration" :capability :declarationProvider) 997 ("textDocument/definition" :capability :definitionProvider) 998 ("textDocument/documentColor" :capability :colorProvider) 999 ("textDocument/documentLink" :capability :documentLinkProvider) 1000 ("textDocument/inlayHint" :capability :inlayHintProvider) 1001 ("textDocument/documentHighlight" :capability :documentHighlightProvider) 1002 ("textDocument/documentSymbol" :capability :documentSymbolProvider) 1003 ("textDocument/foldingRange" :capability :foldingRangeProvider) 1004 ("textDocument/formatting" :capability :documentFormattingProvider) 1005 ("textDocument/hover" :capability :hoverProvider) 1006 ("textDocument/implementation" :capability :implementationProvider) 1007 ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider) 1008 ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider) 1009 ("textDocument/prepareRename" 1010 :check-command (lambda (workspace) 1011 (with-lsp-workspace workspace 1012 (lsp:rename-options-prepare-provider? 1013 (lsp--capability-for-method "textDocument/rename"))))) 1014 ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider) 1015 ("textDocument/references" :capability :referencesProvider) 1016 ("textDocument/rename" :capability :renameProvider) 1017 ("textDocument/selectionRange" :capability :selectionRangeProvider) 1018 ("textDocument/semanticTokens" :capability :semanticTokensProvider) 1019 ("textDocument/semanticTokensFull" 1020 :check-command (lambda (workspace) 1021 (with-lsp-workspace workspace 1022 (lsp-get (lsp--capability :semanticTokensProvider) :full)))) 1023 ("textDocument/semanticTokensFull/Delta" 1024 :check-command (lambda (workspace) 1025 (with-lsp-workspace workspace 1026 (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full))) 1027 (and (not (booleanp capFull)) (lsp-get capFull :delta)))))) 1028 ("textDocument/semanticTokensRangeProvider" 1029 :check-command (lambda (workspace) 1030 (with-lsp-workspace workspace 1031 (lsp-get (lsp--capability :semanticTokensProvider) :range)))) 1032 ("textDocument/signatureHelp" :capability :signatureHelpProvider) 1033 ("textDocument/typeDefinition" :capability :typeDefinitionProvider) 1034 ("textDocument/typeHierarchy" :capability :typeHierarchyProvider) 1035 ("workspace/executeCommand" :capability :executeCommandProvider) 1036 ("workspace/symbol" :capability :workspaceSymbolProvider)) 1037 1038 "Map methods to requirements. 1039 It is used by request-sending functions to determine which server 1040 must be used for handling a particular message.") 1041 1042 (defconst lsp--file-change-type 1043 `((created . 1) 1044 (changed . 2) 1045 (deleted . 3))) 1046 1047 (defconst lsp--watch-kind 1048 `((create . 1) 1049 (change . 2) 1050 (delete . 4))) 1051 1052 (defvar lsp-window-body-width 40 1053 "Window body width when rendering doc.") 1054 1055 (defface lsp-face-highlight-textual 1056 '((t :inherit highlight)) 1057 "Face used for textual occurrences of symbols." 1058 :group 'lsp-mode) 1059 1060 (defface lsp-face-highlight-read 1061 '((t :inherit highlight :underline t)) 1062 "Face used for highlighting symbols being read." 1063 :group 'lsp-mode) 1064 1065 (defface lsp-face-highlight-write 1066 '((t :inherit highlight :weight bold)) 1067 "Face used for highlighting symbols being written to." 1068 :group 'lsp-mode) 1069 1070 (define-obsolete-variable-alias 'lsp-lens-auto-enable 1071 'lsp-lens-enable "lsp-mode 7.0.1") 1072 1073 (defcustom lsp-lens-enable t 1074 "Auto enable lenses if server supports." 1075 :group 'lsp-lens 1076 :type 'boolean 1077 :package-version '(lsp-mode . "6.3")) 1078 1079 (defcustom lsp-symbol-highlighting-skip-current nil 1080 "If non-nil skip current symbol when setting symbol highlights." 1081 :group 'lsp-mode 1082 :type 'boolean) 1083 1084 (defcustom lsp-file-watch-threshold 1000 1085 "Show warning if the files to watch are more than. 1086 Set to nil to disable the warning." 1087 :type 'number 1088 :group 'lsp-mode) 1089 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i)))) 1090 1091 (defvar lsp-custom-markup-modes 1092 '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic")) 1093 "Mode to uses with markdown code blocks. 1094 They are added to `markdown-code-lang-modes'") 1095 1096 (defcustom lsp-signature-render-documentation t 1097 "Display signature documentation in `eldoc'." 1098 :type 'boolean 1099 :group 'lsp-mode 1100 :package-version '(lsp-mode . "6.2")) 1101 1102 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request) 1103 "Auto activate signature conditions." 1104 :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char) 1105 (const :tag "After selected completion." :after-completion) 1106 (const :tag "When the server has sent show signature help." :on-server-request))) 1107 :group 'lsp-mode 1108 :package-version '(lsp-mode . "6.2")) 1109 1110 (defcustom lsp-signature-doc-lines 20 1111 "If number, limit the number of lines to show in the docs." 1112 :type 'number 1113 :group 'lsp-mode 1114 :package-version '(lsp-mode . "6.3")) 1115 1116 (defcustom lsp-signature-function 'lsp-lv-message 1117 "The function used for displaying signature info. 1118 It will be called with one param - the signature info. When 1119 called with nil the signature info must be cleared." 1120 :type 'function 1121 :group 'lsp-mode 1122 :package-version '(lsp-mode . "6.3")) 1123 1124 (defcustom lsp-keymap-prefix "s-l" 1125 "LSP-mode keymap prefix." 1126 :group 'lsp-mode 1127 :type 'string 1128 :package-version '(lsp-mode . "6.3")) 1129 1130 (defvar-local lsp--buffer-workspaces () 1131 "List of the buffer workspaces.") 1132 1133 (defvar-local lsp--buffer-deferred nil 1134 "Whether buffer was loaded via `lsp-deferred'.") 1135 1136 (defvar lsp--session nil 1137 "Contain the `lsp-session' for the current Emacs instance.") 1138 1139 (defvar lsp--tcp-port 10000) 1140 1141 (defvar lsp--client-packages-required nil 1142 "If nil, `lsp-client-packages' are yet to be required.") 1143 1144 (defvar lsp--tcp-server-port 0 1145 "The server socket which is opened when using `lsp-tcp-server' (a server 1146 socket is opened in Emacs and the language server connects to it). The 1147 default value of 0 ensures that a random high port is used. Set it to a positive 1148 integer to use a specific port.") 1149 1150 (defvar lsp--tcp-server-wait-seconds 10 1151 "Wait this amount of time for the client to connect to our server socket 1152 when using `lsp-tcp-server'.") 1153 1154 (defvar-local lsp--document-symbols nil 1155 "The latest document symbols.") 1156 1157 (defvar-local lsp--document-selection-range-cache nil 1158 "The document selection cache.") 1159 1160 (defvar-local lsp--document-symbols-request-async nil 1161 "If non-nil, request document symbols asynchronously.") 1162 1163 (defvar-local lsp--document-symbols-tick -1 1164 "The value of `buffer-chars-modified-tick' when document 1165 symbols were last retrieved.") 1166 1167 (defvar-local lsp--have-document-highlights nil 1168 "Set to `t' on symbol highlighting, cleared on 1169 `lsp--cleanup-highlights-if-needed'. Checking a separately 1170 defined flag is substantially faster than unconditionally 1171 calling `remove-overlays'.") 1172 1173 ;; Buffer local variable for storing number of lines. 1174 (defvar lsp--log-lines) 1175 1176 (defvar-local lsp--eldoc-saved-message nil) 1177 1178 (defvar lsp--on-change-timer nil) 1179 (defvar lsp--on-idle-timer nil) 1180 1181 (defvar-local lsp--signature-last nil) 1182 (defvar-local lsp--signature-last-index nil) 1183 (defvar lsp--signature-last-buffer nil) 1184 1185 (defvar-local lsp--virtual-buffer-point-max nil) 1186 1187 (cl-defmethod lsp-execute-command (_server _command _arguments) 1188 "Ask SERVER to execute COMMAND with ARGUMENTS.") 1189 1190 (defun lsp-elt (sequence n) 1191 "Return Nth element of SEQUENCE or nil if N is out of range." 1192 (cond 1193 ((listp sequence) (elt sequence n)) 1194 ((arrayp sequence) 1195 (and (> (length sequence) n) (aref sequence n))) 1196 (t (and (> (length sequence) n) (elt sequence n))))) 1197 1198 ;; define seq-first and seq-rest for older emacs 1199 (defun lsp-seq-first (sequence) 1200 "Return the first element of SEQUENCE." 1201 (lsp-elt sequence 0)) 1202 1203 (defun lsp-seq-rest (sequence) 1204 "Return a sequence of the elements of SEQUENCE except the first one." 1205 (seq-drop sequence 1)) 1206 1207 ;;;###autoload 1208 (defun lsp--string-listp (sequence) 1209 "Return t if all elements of SEQUENCE are strings, else nil." 1210 (not (seq-find (lambda (x) (not (stringp x))) sequence))) 1211 1212 (defun lsp--string-vector-p (candidate) 1213 "Returns true if CANDIDATE is a vector data structure and 1214 every element of it is of type string, else nil." 1215 (and 1216 (vectorp candidate) 1217 (seq-every-p #'stringp candidate))) 1218 1219 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0") 1220 1221 (defun lsp--editable-vector-match (widget value) 1222 "Function for `lsp-editable-vector' :match." 1223 ;; Value must be a list or a vector and all the members must match the type. 1224 (and (or (listp value) (vectorp value)) 1225 (length (cdr (lsp--editable-vector-match-inline widget value))))) 1226 1227 (defun lsp--editable-vector-match-inline (widget value) 1228 "Value for `lsp-editable-vector' :match-inline." 1229 (let ((type (nth 0 (widget-get widget :args))) 1230 (ok t) 1231 found) 1232 (while (and value ok) 1233 (let ((answer (widget-match-inline type value))) 1234 (if answer 1235 (let ((head (if (vectorp answer) (aref answer 0) (car answer))) 1236 (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer)))) 1237 (setq found (append found head) 1238 value tail)) 1239 (setq ok nil)))) 1240 (cons found value))) 1241 1242 (defun lsp--editable-vector-value-to-external (_widget internal-value) 1243 "Convert the internal list value to a vector." 1244 (if (listp internal-value) 1245 (apply 'vector internal-value) 1246 internal-value)) 1247 1248 (defun lsp--editable-vector-value-to-internal (_widget external-value) 1249 "Convert the external vector value to a list." 1250 (if (vectorp external-value) 1251 (append external-value nil) 1252 external-value)) 1253 1254 (define-widget 'lsp--editable-vector 'editable-list 1255 "A subclass of `editable-list' that accepts and returns a 1256 vector instead of a list." 1257 :value-to-external 'lsp--editable-vector-value-to-external 1258 :value-to-internal 'lsp--editable-vector-value-to-internal 1259 :match 'lsp--editable-vector-match 1260 :match-inline 'lsp--editable-vector-match-inline) 1261 1262 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector 1263 "A variable length homogeneous vector." 1264 :tag "Repeat" 1265 :format "%{%t%}:\n%v%i\n") 1266 1267 (define-widget 'lsp-string-vector 'lazy 1268 "A vector of zero or more elements, every element of which is a string. 1269 Appropriate for any language-specific `defcustom' that needs to 1270 serialize as a JSON array of strings. 1271 1272 Deprecated. Use `lsp-repeatable-vector' instead. " 1273 :offset 4 1274 :tag "Vector" 1275 :type '(lsp-repeatable-vector string)) 1276 1277 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0") 1278 1279 (defvar lsp--show-message t 1280 "If non-nil, show debug message from `lsp-mode'.") 1281 1282 (defun lsp--message (format &rest args) 1283 "Wrapper for `message' 1284 1285 We `inhibit-message' the message when the cursor is in the 1286 minibuffer and when emacs version is before emacs 27 due to the 1287 fact that we often use `lsp--info', `lsp--warn' and `lsp--error' 1288 in async context and the call to these function is removing the 1289 minibuffer prompt. The issue with async messages is already fixed 1290 in emacs 27. 1291 1292 See #2049" 1293 (when lsp--show-message 1294 (let ((inhibit-message (or inhibit-message 1295 (and (minibufferp) 1296 (version< emacs-version "27.0"))))) 1297 (apply #'message format args)))) 1298 1299 (defun lsp--info (format &rest args) 1300 "Display lsp info message with FORMAT with ARGS." 1301 (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args))) 1302 1303 (defun lsp--warn (format &rest args) 1304 "Display lsp warn message with FORMAT with ARGS." 1305 (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args))) 1306 1307 (defun lsp--error (format &rest args) 1308 "Display lsp error message with FORMAT with ARGS." 1309 (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args))) 1310 1311 (defun lsp-log (format &rest args) 1312 "Log message to the ’*lsp-log*’ buffer. 1313 1314 FORMAT and ARGS i the same as for `message'." 1315 (when lsp-log-max 1316 (let ((log-buffer (get-buffer "*lsp-log*")) 1317 (inhibit-read-only t)) 1318 (unless log-buffer 1319 (setq log-buffer (get-buffer-create "*lsp-log*")) 1320 (with-current-buffer log-buffer 1321 (buffer-disable-undo) 1322 (view-mode 1) 1323 (set (make-local-variable 'lsp--log-lines) 0))) 1324 (with-current-buffer log-buffer 1325 (save-excursion 1326 (let* ((message (apply 'format format args)) 1327 ;; Count newlines in message. 1328 (newlines (1+ (cl-loop with start = 0 1329 for count from 0 1330 while (string-match "\n" message start) 1331 do (setq start (match-end 0)) 1332 finally return count)))) 1333 (goto-char (point-max)) 1334 1335 ;; in case the buffer is not empty insert before last \n to preserve 1336 ;; the point position(in case it is in the end) 1337 (if (eq (point) (point-min)) 1338 (progn 1339 (insert "\n") 1340 (backward-char)) 1341 (backward-char) 1342 (insert "\n")) 1343 (insert message) 1344 1345 (setq lsp--log-lines (+ lsp--log-lines newlines)) 1346 1347 (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max)) 1348 (let ((to-delete (- lsp--log-lines lsp-log-max))) 1349 (goto-char (point-min)) 1350 (forward-line to-delete) 1351 (delete-region (point-min) (point)) 1352 (setq lsp--log-lines lsp-log-max))))))))) 1353 1354 (defalias 'lsp-message 'lsp-log) 1355 1356 (defalias 'lsp-ht 'ht) 1357 1358 (defalias 'lsp-file-local-name 'file-local-name) 1359 1360 (defun lsp-f-canonical (file-name) 1361 "Return the canonical FILE-NAME, without a trailing slash." 1362 (directory-file-name (expand-file-name file-name))) 1363 1364 (defalias 'lsp-canonical-file-name 'lsp-f-canonical) 1365 1366 (defun lsp-f-same? (path-a path-b) 1367 "Return t if PATH-A and PATH-B are references to the same file. 1368 Symlinks are not followed." 1369 (when (and (f-exists? path-a) 1370 (f-exists? path-b)) 1371 (equal 1372 (lsp-f-canonical (directory-file-name (f-expand path-a))) 1373 (lsp-f-canonical (directory-file-name (f-expand path-b)))))) 1374 1375 (defun lsp-f-parent (path) 1376 "Return the parent directory to PATH. 1377 Symlinks are not followed." 1378 (let ((parent (file-name-directory 1379 (directory-file-name (f-expand path default-directory))))) 1380 (unless (lsp-f-same? path parent) 1381 (if (f-relative? path) 1382 (f-relative parent) 1383 (directory-file-name parent))))) 1384 1385 (defun lsp-f-ancestor-of? (path-a path-b) 1386 "Return t if PATH-A is an ancestor of PATH-B. 1387 Symlinks are not followed." 1388 (unless (lsp-f-same? path-a path-b) 1389 (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator)) 1390 (lsp-f-canonical path-b)))) 1391 1392 (defun lsp--merge-results (results method) 1393 "Merge RESULTS by filtering the empty hash-tables and merging 1394 the lists according to METHOD." 1395 (pcase (--map (if (vectorp it) 1396 (append it nil) it) 1397 (-filter #'identity results)) 1398 (`() ()) 1399 ;; only one result - simply return it 1400 (`(,fst) fst) 1401 ;; multiple results merge it based on strategy 1402 (results 1403 (pcase method 1404 ("textDocument/hover" (pcase (seq-filter 1405 (-compose #'not #'lsp-empty?) 1406 results) 1407 (`(,hover) hover) 1408 (hovers (lsp-make-hover 1409 :contents 1410 (-mapcat 1411 (-lambda ((&Hover :contents)) 1412 (if (and (sequencep contents) 1413 (not (stringp contents))) 1414 (append contents ()) 1415 (list contents))) 1416 hovers))))) 1417 ("textDocument/completion" 1418 (lsp-make-completion-list 1419 :is-incomplete (seq-some 1420 #'lsp:completion-list-is-incomplete 1421 results) 1422 :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it) 1423 (lsp:completion-list-items it) 1424 it) 1425 nil)) 1426 results))) 1427 ("completionItem/resolve" 1428 (let ((item (cl-first results))) 1429 (when-let ((details (seq-filter #'identity 1430 (seq-map #'lsp:completion-item-detail? results)))) 1431 (lsp:set-completion-item-detail? 1432 item 1433 (string-join details " "))) 1434 (when-let ((docs (seq-filter #'identity 1435 (seq-map #'lsp:completion-item-documentation? results)))) 1436 (lsp:set-completion-item-documentation? 1437 item 1438 (lsp-make-markup-content 1439 :kind (or (seq-some (lambda (it) 1440 (when (equal (lsp:markup-content-kind it) 1441 lsp/markup-kind-markdown) 1442 lsp/markup-kind-markdown)) 1443 docs) 1444 lsp/markup-kind-plain-text) 1445 :value (string-join (seq-map (lambda (doc) 1446 (or (lsp:markup-content-value doc) 1447 (and (stringp doc) doc))) 1448 docs) 1449 "\n")))) 1450 (when-let ((edits (seq-filter #'identity 1451 (seq-map #'lsp:completion-item-additional-text-edits? results)))) 1452 (lsp:set-completion-item-additional-text-edits? 1453 item 1454 (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits))) 1455 item)) 1456 (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results)))))) 1457 1458 (defun lsp--spinner-start () 1459 "Start spinner indication." 1460 (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error))) 1461 1462 (defun lsp--propertize (str type) 1463 "Propertize STR as per TYPE." 1464 (propertize str 'face (alist-get type lsp--message-type-face))) 1465 1466 (defun lsp-workspaces () 1467 "Return the lsp workspaces associated with the current project." 1468 (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces)) 1469 1470 (defun lsp--completing-read (prompt collection transform-fn &optional predicate 1471 require-match initial-input 1472 hist def inherit-input-method) 1473 "Wrap `completing-read' to provide transformation function and disable sort. 1474 1475 TRANSFORM-FN will be used to transform each of the items before displaying. 1476 1477 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF 1478 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes." 1479 (let* ((col (--map (cons (funcall transform-fn it) it) collection)) 1480 (completion (completing-read prompt 1481 (lambda (string pred action) 1482 (if (eq action 'metadata) 1483 `(metadata (display-sort-function . identity)) 1484 (complete-with-action action col string pred))) 1485 predicate require-match initial-input hist 1486 def inherit-input-method))) 1487 (cdr (assoc completion col)))) 1488 1489 (defconst lsp--system-arch (lambda () 1490 (setq lsp--system-arch 1491 (pcase system-type 1492 ('windows-nt 1493 (pcase system-configuration 1494 ((rx bol "x86_64-") 'x64) 1495 (_ 'x86))) 1496 ('darwin 1497 (pcase system-configuration 1498 ((rx "aarch64-") 'arm64) 1499 (_ 'x64))) 1500 ('gnu/linux 1501 (pcase system-configuration 1502 ((rx bol "x86_64") 'x64) 1503 ((rx bol (| "i386" "i886")) 'x32))) 1504 (_ 1505 (pcase system-configuration 1506 ((rx bol "x86_64") 'x64) 1507 ((rx bol (| "i386" "i886")) 'x32)))))) 1508 "Return the system architecture of `Emacs'. 1509 Special values: 1510 `x64' 64bit 1511 `x32' 32bit 1512 `arm64' ARM 64bit") 1513 1514 (defmacro lsp-with-current-buffer (buffer-id &rest body) 1515 (declare (indent 1) (debug t)) 1516 `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer))) 1517 (with-lsp-workspaces (plist-get ,buffer-id :workspaces) 1518 (funcall wcb (lambda () ,@body))) 1519 (with-current-buffer ,buffer-id 1520 ,@body))) 1521 1522 (defvar lsp--throw-on-input nil 1523 "Make `lsp-*-while-no-input' throws `input' on interrupted.") 1524 1525 (defmacro lsp--catch (tag bodyform &rest handlers) 1526 "Catch TAG thrown in BODYFORM. 1527 The return value from TAG will be handled in HANDLERS by `pcase'." 1528 (declare (debug (form form &rest (pcase-PAT body))) (indent 2)) 1529 (let ((re-sym (make-symbol "re"))) 1530 `(let ((,re-sym (catch ,tag ,bodyform))) 1531 (pcase ,re-sym 1532 ,@handlers)))) 1533 1534 (defmacro lsp--while-no-input (&rest body) 1535 "Wrap BODY in `while-no-input' and respecting `non-essential'. 1536 If `lsp--throw-on-input' is set, will throw if input is pending, else 1537 return value of `body' or nil if interrupted." 1538 (declare (debug t) (indent 0)) 1539 `(if non-essential 1540 (let ((res (while-no-input ,@body))) 1541 (cond 1542 ((and lsp--throw-on-input (equal res t)) 1543 (throw 'input :interrupted)) 1544 ((booleanp res) nil) 1545 (t res))) 1546 ,@body)) 1547 1548 ;; A ‘lsp--client’ object describes the client-side behavior of a language 1549 ;; server. It is used to start individual server processes, each of which is 1550 ;; represented by a ‘lsp--workspace’ object. Client objects are normally 1551 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’. Each 1552 ;; workspace refers to exactly one client, but there can be multiple workspaces 1553 ;; for a single client. 1554 (cl-defstruct lsp--client 1555 ;; ‘language-id’ is a function that receives a buffer as a single argument 1556 ;; and should return the language identifier for that buffer. See 1557 ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem 1558 ;; for a list of language identifiers. Also consult the documentation for 1559 ;; the language server represented by this client to find out what language 1560 ;; identifiers it supports or expects. 1561 (language-id nil) 1562 1563 ;; ‘add-on?’ when set to t the server will be started no matter whether there 1564 ;; is another server handling the same mode. 1565 (add-on? nil) 1566 ;; ‘new-connection’ is a function that should start a language server process 1567 ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS). 1568 ;; COMMAND-PROCESS must be a process object representing the server process 1569 ;; just started. COMMUNICATION-PROCESS must be a process (including pipe and 1570 ;; network processes) that ‘lsp-mode’ uses to communicate with the language 1571 ;; server using the language server protocol. COMMAND-PROCESS and 1572 ;; COMMUNICATION-PROCESS may be the same process; in that case 1573 ;; ‘new-connection’ may also return that process as a single 1574 ;; object. ‘new-connection’ is called with two arguments, FILTER and 1575 ;; SENTINEL. FILTER should be used as process filter for 1576 ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for 1577 ;; COMMAND-PROCESS. 1578 (new-connection nil) 1579 1580 ;; ‘ignore-regexps’ is a list of regexps. When a data packet from the 1581 ;; language server matches any of these regexps, it will be ignored. This is 1582 ;; intended for dealing with language servers that output non-protocol data. 1583 (ignore-regexps nil) 1584 1585 ;; ‘ignore-messages’ is a list of regexps. When a message from the language 1586 ;; server matches any of these regexps, it will be ignored. This is useful 1587 ;; for filtering out unwanted messages; such as servers that send nonstandard 1588 ;; message types, or extraneous log messages. 1589 (ignore-messages nil) 1590 1591 ;; ‘notification-handlers’ is a hash table mapping notification method names 1592 ;; (strings) to functions handling the respective notifications. Upon 1593 ;; receiving a notification, ‘lsp-mode’ will call the associated handler 1594 ;; function passing two arguments, the ‘lsp--workspace’ object and the 1595 ;; deserialized notification parameters. 1596 (notification-handlers (make-hash-table :test 'equal)) 1597 1598 ;; ‘request-handlers’ is a hash table mapping request method names 1599 ;; (strings) to functions handling the respective notifications. Upon 1600 ;; receiving a request, ‘lsp-mode’ will call the associated handler function 1601 ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized 1602 ;; request parameters. 1603 (request-handlers (make-hash-table :test 'equal)) 1604 1605 ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request 1606 ;; identifiers for pending asynchronous requests to functions handling the 1607 ;; respective responses. Upon receiving a response from the language server, 1608 ;; ‘lsp-mode’ will call the associated response handler function with a 1609 ;; single argument, the deserialized response parameters. 1610 (response-handlers (make-hash-table :test 'eql)) 1611 1612 ;; ‘prefix-function’ is called for getting the prefix for completion. 1613 ;; The function takes no parameter and returns a cons (start . end) representing 1614 ;; the start and end bounds of the prefix. If it's not set, the client uses a 1615 ;; default prefix function." 1616 (prefix-function nil) 1617 1618 ;; Contains mapping of scheme to the function that is going to be used to load 1619 ;; the file. 1620 (uri-handlers (make-hash-table :test #'equal)) 1621 1622 ;; ‘action-handlers’ is a hash table mapping action to a handler function. It 1623 ;; can be used in `lsp-execute-code-action' to determine whether the action 1624 ;; current client is interested in executing the action instead of sending it 1625 ;; to the server. 1626 (action-handlers (make-hash-table :test 'equal)) 1627 1628 ;; major modes supported by the client. 1629 major-modes 1630 ;; Function that will be called to decide if this language client 1631 ;; should manage a particular buffer. The function will be passed 1632 ;; the file name and major mode to inform the decision. Setting 1633 ;; `activation-fn' will override `major-modes', if 1634 ;; present. 1635 activation-fn 1636 ;; Break the tie when major-mode is supported by multiple clients. 1637 (priority 0) 1638 ;; Unique identifier for representing the client object. 1639 server-id 1640 ;; defines whether the client supports multi root workspaces. 1641 multi-root 1642 ;; Initialization options or a function that returns initialization options. 1643 initialization-options 1644 ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or 1645 ;; completely replace, the faces used for semantic highlighting on a 1646 ;; client-by-client basis. 1647 ;; 1648 ;; It recognizes four members, all of which are optional: `:types’ and 1649 ;; `:modifiers’, respectively, should be face definition lists akin to 1650 ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be 1651 ;; merged with the default face definition list. 1652 ;; 1653 ;; Alternatively, if the plist members `:discard-default-types’ or 1654 ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers' 1655 ;; face definitions will be replaced entirely by their respective overrides. 1656 ;; 1657 ;; For example, setting `:semantic-tokens-faces-overrides' to 1658 ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from 1659 ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'. 1660 ;; 1661 ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))' 1662 ;; will also remap "macro", but on top of that associate the fictional token type 1663 ;; "not-quite-a-macro" with the face named `some-face'. 1664 ;; 1665 ;; `(:types (("macro" . font-lock-keyword-face)) 1666 ;; :modifiers (("declaration" . lsp-face-semhl-interface)) 1667 ;; :discard-default-types t 1668 ;; :discard-default-modifiers t)' 1669 ;; will discard all default face definitions, hence leaving the client with 1670 ;; only one token type "macro", mapped to `font-lock-keyword-face', and one 1671 ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'. 1672 semantic-tokens-faces-overrides 1673 ;; Provides support for registering LSP Server specific capabilities. 1674 custom-capabilities 1675 ;; Function which returns the folders that are considered to be not projects but library files. 1676 ;; The function accepts one parameter currently active workspace. 1677 ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225. 1678 library-folders-fn 1679 ;; function which will be called when opening file in the workspace to perform 1680 ;; client specific initialization. The function accepts one parameter 1681 ;; currently active workspace. 1682 before-file-open-fn 1683 ;; Function which will be called right after a workspace has been initialized. 1684 initialized-fn 1685 ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP. 1686 (remote? nil) 1687 1688 ;; ‘completion-in-comments?’ t if the client supports completion in comments. 1689 (completion-in-comments? nil) 1690 1691 ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client. 1692 (path->uri-fn nil) 1693 1694 ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client. 1695 (uri->path-fn nil) 1696 ;; Function that returns an environment structure that will be used 1697 ;; to set some environment variables when starting the language 1698 ;; server process. These environment variables enable some 1699 ;; additional features in the language server. The environment 1700 ;; structure is an alist of the form (KEY . VALUE), where KEY is a 1701 ;; string (regularly in all caps), and VALUE may be a string, a 1702 ;; boolean, or a sequence of strings. 1703 environment-fn 1704 1705 ;; ‘after-open-fn’ workspace after open specific hooks. 1706 (after-open-fn nil) 1707 1708 ;; ‘async-request-handlers’ is a hash table mapping request method names 1709 ;; (strings) to functions handling the respective requests that may take 1710 ;; time to finish. Upon receiving a request, ‘lsp-mode’ will call the 1711 ;; associated handler function passing three arguments, the ‘lsp--workspace’ 1712 ;; object, the deserialized request parameters and the callback which accept 1713 ;; result as its parameter. 1714 (async-request-handlers (make-hash-table :test 'equal)) 1715 download-server-fn 1716 download-in-progress? 1717 buffers 1718 synchronize-sections) 1719 1720 (defun lsp-clients-executable-find (find-command &rest args) 1721 "Finds an executable by invoking a search command. 1722 1723 FIND-COMMAND is the executable finder that searches for the 1724 actual language server executable. ARGS is a list of arguments to 1725 give to FIND-COMMAND to find the language server. Returns the 1726 output of FIND-COMMAND if it exits successfully, nil otherwise. 1727 1728 Typical uses include finding an executable by invoking `find' in 1729 a project, finding LLVM commands on macOS with `xcrun', or 1730 looking up project-specific language servers for projects written 1731 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv' 1732 etc." 1733 (when-let* ((find-command-path (executable-find find-command)) 1734 (executable-path 1735 (with-temp-buffer 1736 (when (zerop (apply 'call-process find-command-path nil t nil args)) 1737 (buffer-substring-no-properties (point-min) (point-max)))))) 1738 (string-trim executable-path))) 1739 1740 (defvar lsp--already-widened nil) 1741 1742 (defmacro lsp-save-restriction-and-excursion (&rest form) 1743 (declare (indent 0) (debug t)) 1744 `(if lsp--already-widened 1745 (save-excursion ,@form) 1746 (-let [lsp--already-widened t] 1747 (save-restriction 1748 (widen) 1749 (save-excursion ,@form))))) 1750 1751 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number 1752 (defun lsp--line-character-to-point (line character) 1753 "Return the point for character CHARACTER on line LINE." 1754 (or (lsp-virtual-buffer-call :line/character->point line character) 1755 (let ((inhibit-field-text-motion t)) 1756 (lsp-save-restriction-and-excursion 1757 (goto-char (point-min)) 1758 (forward-line line) 1759 ;; server may send character position beyond the current line and we 1760 ;; should fallback to line end. 1761 (-let [line-end (line-end-position)] 1762 (if (> character (- line-end (point))) 1763 line-end 1764 (forward-char character) 1765 (point))))))) 1766 1767 (lsp-defun lsp--position-to-point ((&Position :line :character)) 1768 "Convert `Position' object in PARAMS to a point." 1769 (lsp--line-character-to-point line character)) 1770 1771 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end)) 1772 (cons start end)) 1773 1774 (lsp-defun lsp--range-text ((&RangeToPoint :start :end)) 1775 (buffer-substring start end)) 1776 1777 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end))) 1778 (cond 1779 ((and 1780 (region-active-p) 1781 (<= start (region-beginning) end) 1782 (<= start (region-end) end) 1783 (or (not (= start (region-beginning))) 1784 (not (= end (region-end))))) 1785 (cons start end)) 1786 ((and (<= start (point) end) 1787 (not (region-active-p))) 1788 (cons start end)) 1789 (parent? (lsp--find-wrapping-range parent?)))) 1790 1791 (defun lsp--get-selection-range () 1792 (or 1793 (-when-let ((cache . cache-tick) lsp--document-selection-range-cache) 1794 (when (= cache-tick (buffer-modified-tick)) cache)) 1795 (let ((response (cl-first 1796 (lsp-request 1797 "textDocument/selectionRange" 1798 (list :textDocument (lsp--text-document-identifier) 1799 :positions (vector (lsp--cur-position))))))) 1800 (setq lsp--document-selection-range-cache 1801 (cons response (buffer-modified-tick))) 1802 response))) 1803 1804 (defun lsp-extend-selection () 1805 "Extend selection." 1806 (interactive) 1807 (unless (lsp-feature? "textDocument/selectionRange") 1808 (signal 'lsp-capability-not-supported (list "selectionRangeProvider"))) 1809 (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range))) 1810 (goto-char start) 1811 (set-mark (point)) 1812 (goto-char end) 1813 (exchange-point-and-mark))) 1814 1815 (defun lsp-warn (message &rest args) 1816 "Display a warning message made from (`format-message' MESSAGE ARGS...). 1817 This is equivalent to `display-warning', using `lsp-mode' as the type and 1818 `:warning' as the level." 1819 (display-warning 'lsp-mode (apply #'format-message message args))) 1820 1821 (defun lsp--get-uri-handler (scheme) 1822 "Get uri handler for SCHEME in the current workspace." 1823 (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it))) 1824 (or (lsp-workspaces) (lsp--session-workspaces (lsp-session))))) 1825 1826 (defun lsp--fix-path-casing (path) 1827 "On windows, downcases path because the windows file system is 1828 case-insensitive. 1829 1830 On other systems, returns path without change." 1831 (if (eq system-type 'windows-nt) (downcase path) path)) 1832 1833 (defun lsp--uri-to-path (uri) 1834 "Convert URI to a file path." 1835 (if-let ((fn (->> (lsp-workspaces) 1836 (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client)) 1837 (cl-first)))) 1838 (funcall fn uri) 1839 (lsp--uri-to-path-1 uri))) 1840 1841 (defun lsp-remap-path-if-needed (file-name) 1842 (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings)) 1843 (propertize (buffer-local-value 'buffer-file-name buffer) 1844 'lsp-virtual-buffer virtual-buffer) 1845 file-name)) 1846 1847 (defun lsp--uri-to-path-1 (uri) 1848 "Convert URI to a file path." 1849 (let* ((url (url-generic-parse-url (url-unhex-string uri))) 1850 (type (url-type url)) 1851 (target (url-target url)) 1852 (file 1853 (concat (decode-coding-string (url-filename url) 1854 (or locale-coding-system 'utf-8)) 1855 (when (and target 1856 (not (s-match 1857 (rx "#" (group (1+ num)) (or "," "#") 1858 (group (1+ num)) 1859 string-end) 1860 uri))) 1861 (concat "#" target)))) 1862 (file-name (if (and type (not (string= type "file"))) 1863 (if-let ((handler (lsp--get-uri-handler type))) 1864 (funcall handler uri) 1865 uri) 1866 ;; `url-generic-parse-url' is buggy on windows: 1867 ;; https://github.com/emacs-lsp/lsp-mode/pull/265 1868 (or (and (eq system-type 'windows-nt) 1869 (eq (elt file 0) ?\/) 1870 (substring file 1)) 1871 file)))) 1872 (->> file-name 1873 (concat (-some #'lsp--workspace-host-root (lsp-workspaces))) 1874 (lsp-remap-path-if-needed)))) 1875 1876 (defun lsp--buffer-uri () 1877 "Return URI of the current buffer." 1878 (or lsp-buffer-uri 1879 (plist-get lsp--virtual-buffer :buffer-uri) 1880 (lsp--path-to-uri 1881 (or (buffer-file-name) (buffer-file-name (buffer-base-buffer)))))) 1882 1883 (defun lsp-register-client-capabilities (&rest _args) 1884 "Implemented only to make `company-lsp' happy. 1885 DELETE when `lsp-mode.el' is deleted.") 1886 1887 (defconst lsp--url-path-allowed-chars 1888 (url--allowed-chars (append '(?/) url-unreserved-chars)) 1889 "`url-unreserved-chars' with additional delim ?/. 1890 This set of allowed chars is enough for hexifying local file paths.") 1891 1892 (defun lsp--path-to-uri-1 (path) 1893 (concat lsp--uri-file-prefix 1894 (--> path 1895 (expand-file-name it) 1896 (or (file-remote-p it 'localname t) it) 1897 (url-hexify-string it lsp--url-path-allowed-chars)))) 1898 1899 (defun lsp--path-to-uri (path) 1900 "Convert PATH to a uri." 1901 (if-let ((uri-fn (->> (lsp-workspaces) 1902 (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client)) 1903 (cl-first)))) 1904 (funcall uri-fn path) 1905 (lsp--path-to-uri-1 path))) 1906 1907 (defun lsp--string-match-any (regex-list str) 1908 "Return the first regex, if any, within REGEX-LIST matching STR." 1909 (--first (string-match it str) regex-list)) 1910 1911 (cl-defstruct lsp-watch 1912 (descriptors (make-hash-table :test 'equal)) 1913 root-directory) 1914 1915 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories) 1916 (let ((file-name (cl-third event)) 1917 (event-type (cl-second event))) 1918 (cond 1919 ((and (file-directory-p file-name) 1920 (equal 'created event-type) 1921 (not (lsp--string-match-any ignored-directories file-name))) 1922 1923 (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch) 1924 1925 ;; process the files that are already present in 1926 ;; the directory. 1927 (->> (directory-files-recursively file-name ".*" t) 1928 (seq-do (lambda (f) 1929 (unless (file-directory-p f) 1930 (funcall callback (list nil 'created f))))))) 1931 ((and (memq event-type '(created deleted changed)) 1932 (not (file-directory-p file-name)) 1933 (not (lsp--string-match-any ignored-files file-name))) 1934 (funcall callback event)) 1935 ((and (memq event-type '(renamed)) 1936 (not (file-directory-p file-name)) 1937 (not (lsp--string-match-any ignored-files file-name))) 1938 (funcall callback `(,(cl-first event) deleted ,(cl-third event))) 1939 (funcall callback `(,(cl-first event) created ,(cl-fourth event))))))) 1940 1941 (defun lsp--ask-about-watching-big-repo (number-of-directories dir) 1942 "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR. 1943 This is useful when there is a lot of files in a repository, as 1944 that may slow Emacs down. Returns t if the user wants to watch 1945 the entire repository, nil otherwise." 1946 (prog1 1947 (yes-or-no-p 1948 (format 1949 "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down. 1950 Do you want to watch all files in %s? " 1951 dir 1952 number-of-directories 1953 dir)) 1954 (lsp--info 1955 (concat "You can configure this warning with the `lsp-enable-file-watchers' " 1956 "and `lsp-file-watch-threshold' variables")))) 1957 1958 1959 (defun lsp--path-is-watchable-directory (path dir ignored-directories) 1960 "Figure out whether PATH (inside of DIR) is meant to have a file watcher set. 1961 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't 1962 want to watch." 1963 (let 1964 ((full-path (f-join dir path))) 1965 (and (file-accessible-directory-p full-path) 1966 (not (equal path ".")) 1967 (not (equal path "..")) 1968 (not (lsp--string-match-any ignored-directories full-path))))) 1969 1970 1971 (defun lsp--all-watchable-directories (dir ignored-directories) 1972 "Traverse DIR recursively returning a list of paths that should have watchers. 1973 IGNORED-DIRECTORIES will be used for exclusions" 1974 (let* ((dir (if (f-symlink? dir) 1975 (file-truename dir) 1976 dir))) 1977 (apply #'nconc 1978 ;; the directory itself is assumed to be part of the set 1979 (list dir) 1980 ;; collect all subdirectories that are watchable 1981 (-map 1982 (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories)) 1983 ;; but only look at subdirectories that are watchable 1984 (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories)) 1985 (directory-files dir)))))) 1986 1987 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?) 1988 "Create recursive file notification watch in DIR. 1989 CALLBACK will be called when there are changes in any of 1990 the monitored files. WATCHES is a hash table directory->file 1991 notification handle which contains all of the watch that 1992 already have been created. Watches will not be created for 1993 any directory that matches any regex in IGNORED-DIRECTORIES. 1994 Watches will not be created for any file that matches any 1995 regex in IGNORED-FILES." 1996 (let* ((dir (if (f-symlink? dir) 1997 (file-truename dir) 1998 dir)) 1999 (watch (or watch (make-lsp-watch :root-directory dir))) 2000 (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories))) 2001 (lsp-log "Creating watchers for following %s folders:\n %s" 2002 (length dirs-to-watch) 2003 (s-join "\n " dirs-to-watch)) 2004 (when (or 2005 (not warn-big-repo?) 2006 (not lsp-file-watch-threshold) 2007 (let ((number-of-directories (length dirs-to-watch))) 2008 (or 2009 (< number-of-directories lsp-file-watch-threshold) 2010 (condition-case nil 2011 (lsp--ask-about-watching-big-repo number-of-directories dir) 2012 (quit))))) 2013 (dolist (current-dir dirs-to-watch) 2014 (condition-case err 2015 (progn 2016 (puthash 2017 current-dir 2018 (file-notify-add-watch current-dir 2019 '(change) 2020 (lambda (event) 2021 (lsp--folder-watch-callback event callback watch ignored-files ignored-directories))) 2022 (lsp-watch-descriptors watch))) 2023 (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err))) 2024 (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))))) 2025 watch)) 2026 2027 (defun lsp-kill-watch (watch) 2028 "Delete WATCH." 2029 (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch)) 2030 (ht-clear! (lsp-watch-descriptors watch))) 2031 2032 (defun lsp-json-bool (val) 2033 "Convert VAL to JSON boolean." 2034 (if val t :json-false)) 2035 2036 (defmacro with-lsp-workspace (workspace &rest body) 2037 "Helper macro for invoking BODY in WORKSPACE context." 2038 (declare (debug (form body)) 2039 (indent 1)) 2040 `(let ((lsp--cur-workspace ,workspace)) ,@body)) 2041 2042 (defmacro with-lsp-workspaces (workspaces &rest body) 2043 "Helper macro for invoking BODY against multiple WORKSPACES." 2044 (declare (debug (form body)) 2045 (indent 1)) 2046 `(let ((lsp--buffer-workspaces ,workspaces)) ,@body)) 2047 2048 2049 2050 (defmacro lsp-consistency-check (package) 2051 `(defconst ,(intern (concat (symbol-name package) 2052 "-plist-value-when-compiled")) 2053 (eval-when-compile lsp-use-plists))) 2054 2055 2056 ;; loading code-workspace files 2057 2058 ;;;###autoload 2059 (defun lsp-load-vscode-workspace (file) 2060 "Load vscode workspace from FILE" 2061 (interactive "fSelect file to import: ") 2062 (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session))) 2063 2064 (let ((dir (f-dirname file))) 2065 (->> file 2066 (json-read-file) 2067 (alist-get 'folders) 2068 (-map (-lambda ((&alist 'path)) 2069 (lsp-workspace-folders-add (expand-file-name path dir))))))) 2070 2071 ;;;###autoload 2072 (defun lsp-save-vscode-workspace (file) 2073 "Save vscode workspace to FILE" 2074 (interactive "FSelect file to save to: ") 2075 2076 (let ((json-encoding-pretty-print t)) 2077 (f-write-text (json-encode 2078 `((folders . ,(->> (lsp-session) 2079 (lsp-session-folders) 2080 (--map `((path . ,it))))))) 2081 'utf-8 2082 file))) 2083 2084 2085 (defmacro lsp-foreach-workspace (&rest body) 2086 "Execute BODY for each of the current workspaces." 2087 (declare (debug (form body))) 2088 `(--map (with-lsp-workspace it ,@body) (lsp-workspaces))) 2089 2090 (defmacro when-lsp-workspace (workspace &rest body) 2091 "Helper macro for invoking BODY in WORKSPACE context if present." 2092 (declare (debug (form body)) 2093 (indent 1)) 2094 `(when-let ((lsp--cur-workspace ,workspace)) ,@body)) 2095 2096 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items)) 2097 (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read)) 2098 (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label)) 2099 items)) 2100 (result (funcall-interactively 2101 selectfunc 2102 (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels)) 2103 (choices (if (listp result) 2104 (if (equal result '("*")) 2105 itemLabels 2106 result) 2107 (list result)))) 2108 (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data)) 2109 (if (member label choices) 2110 (lsp-make-quick-pick-item :label label :picked t :user-data user-data) 2111 nil)) 2112 items))))) 2113 2114 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?)) 2115 (read-string (format "%s: " prompt) (or value? ""))) 2116 2117 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type)) 2118 "Send the server's messages to log. 2119 PARAMS - the data sent from _WORKSPACE." 2120 (funcall (cl-case type 2121 (1 'lsp--error) 2122 (2 'lsp--warn) 2123 (t 'lsp--info)) 2124 "%s" 2125 message)) 2126 2127 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type)) 2128 "Send the server's messages to log. 2129 PARAMS - the data sent from WORKSPACE." 2130 (ignore 2131 (let ((client (lsp--workspace-client workspace))) 2132 (when (or (not client) 2133 (cl-notany (-rpartial #'string-match-p message) 2134 (lsp--client-ignore-messages client))) 2135 (lsp-log "%s" (lsp--propertize message type)))))) 2136 2137 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?)) 2138 "Display a message request to user sending the user selection back to server." 2139 (let* ((message (lsp--propertize message type)) 2140 (choices (seq-map #'lsp:message-action-item-title actions?))) 2141 (if choices 2142 (completing-read (concat message " ") (seq-into choices 'list) nil t) 2143 (lsp-log message)))) 2144 2145 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?)) 2146 "Show document URI in a buffer and go to SELECTION if any." 2147 (let ((path (lsp--uri-to-path uri))) 2148 (when (f-exists? path) 2149 (with-current-buffer (find-file path) 2150 (when selection? 2151 (goto-char (lsp--position-to-point (lsp:range-start selection?)))) 2152 t)))) 2153 2154 (defcustom lsp-progress-prefix " ⌛ " 2155 "Progress prefix." 2156 :group 'lsp-mode 2157 :type 'string 2158 :package-version '(lsp-mode . "8.0.0")) 2159 2160 (defcustom lsp-progress-function #'lsp-on-progress-modeline 2161 "Function for handling the progress notifications." 2162 :group 'lsp-mode 2163 :type '(choice 2164 (const :tag "Use modeline" lsp-on-progress-modeline) 2165 (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')" 2166 lsp-on-progress-legacy) 2167 (const :tag "Ignore" ignore) 2168 (function :tag "Other function")) 2169 :package-version '(lsp-mode . "8.0.0")) 2170 2171 (defcustom lsp-request-while-no-input-may-block nil 2172 "Have `lsp-request-while-no-input` block unless `non-essential` is t." 2173 :group 'lsp-mode 2174 :type 'boolean) 2175 2176 (defun lsp--progress-status () 2177 "Returns the status of the progress for the current workspaces." 2178 (-let ((progress-status 2179 (s-join 2180 "|" 2181 (-keep 2182 (lambda (workspace) 2183 (let ((tokens (lsp--workspace-work-done-tokens workspace))) 2184 (unless (ht-empty? tokens) 2185 (mapconcat 2186 (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?)) 2187 (concat (if percentage? 2188 (if (numberp percentage?) 2189 (format "%.0f%%%% " percentage?) 2190 (format "%s%%%% " percentage?)) 2191 "") 2192 (or message? title))) 2193 (ht-values tokens) 2194 "|")))) 2195 (lsp-workspaces))))) 2196 (unless (s-blank? progress-status) 2197 (concat lsp-progress-prefix progress-status)))) 2198 2199 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value 2200 (value &as &WorkDoneProgress :kind))) 2201 "PARAMS contains the progress data. 2202 WORKSPACE is the workspace that contains the progress token." 2203 (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status)))) 2204 (pcase kind 2205 ("begin" (lsp-workspace-set-work-done-token token value workspace)) 2206 ("report" (lsp-workspace-set-work-done-token token value workspace)) 2207 ("end" (lsp-workspace-rem-work-done-token token workspace))) 2208 (force-mode-line-update)) 2209 2210 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value 2211 (value &as &WorkDoneProgress :kind))) 2212 "PARAMS contains the progress data. 2213 WORKSPACE is the workspace that contains the progress token." 2214 (pcase kind 2215 ("begin" 2216 (-let* (((&WorkDoneProgressBegin :title :percentage?) value) 2217 (reporter 2218 (if lsp-progress-via-spinner 2219 (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types)) 2220 ;; Set message as a tooltip for the spinner strings 2221 (propertized-strings 2222 (seq-map (lambda (string) (propertize string 'help-echo title)) 2223 spinner-strings)) 2224 (spinner-type (vconcat propertized-strings))) 2225 ;; The progress relates to the server as a whole, 2226 ;; display it on all buffers. 2227 (mapcar (lambda (buffer) 2228 (lsp-with-current-buffer buffer 2229 (spinner-start spinner-type)) 2230 buffer) 2231 (lsp--workspace-buffers workspace))) 2232 (if percentage? 2233 (make-progress-reporter title 0 100 percentage?) 2234 ;; No percentage, just progress 2235 (make-progress-reporter title nil nil))))) 2236 (lsp-workspace-set-work-done-token token reporter workspace))) 2237 ("report" 2238 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2239 (unless lsp-progress-via-spinner 2240 (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value))))) 2241 2242 ("end" 2243 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2244 (if lsp-progress-via-spinner 2245 (mapc (lambda (buffer) 2246 (when (lsp-buffer-live-p buffer) 2247 (lsp-with-current-buffer buffer 2248 (spinner-stop)))) 2249 reporter) 2250 (progress-reporter-done reporter)) 2251 (lsp-workspace-rem-work-done-token token workspace))))) 2252 2253 2254 ;; diagnostics 2255 2256 (defvar lsp-diagnostic-filter nil 2257 "A a function which will be called with 2258 `&PublishDiagnosticsParams' and `workspace' which can be used 2259 to filter out the diagnostics. The function should return 2260 `&PublishDiagnosticsParams'. 2261 2262 Common usecase are: 2263 1. Filter the diagnostics for a particular language server. 2264 2. Filter out the diagnostics under specific level.") 2265 2266 (defvar lsp-diagnostic-stats (ht)) 2267 2268 (defun lsp-diagnostics (&optional current-workspace?) 2269 "Return the diagnostics from all workspaces." 2270 (or (pcase (if current-workspace? 2271 (lsp-workspaces) 2272 (lsp--session-workspaces (lsp-session))) 2273 (`() ()) 2274 (`(,workspace) (lsp--workspace-diagnostics workspace)) 2275 (`,workspaces (let ((result (make-hash-table :test 'equal))) 2276 (mapc (lambda (workspace) 2277 (->> workspace 2278 (lsp--workspace-diagnostics) 2279 (maphash (lambda (file-name diagnostics) 2280 (puthash file-name 2281 (append (gethash file-name result) diagnostics) 2282 result))))) 2283 workspaces) 2284 result))) 2285 (ht))) 2286 2287 (defun lsp-diagnostics-stats-for (path) 2288 "Get diagnostics statistics for PATH. 2289 The result format is vector [_ errors warnings infos hints] or nil." 2290 (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats)) 2291 2292 (defun lsp-diagnostics--update-path (path new-stats) 2293 (let ((new-stats (copy-sequence new-stats)) 2294 (path (lsp--fix-path-casing (directory-file-name path)))) 2295 (if-let ((old-data (gethash path lsp-diagnostic-stats))) 2296 (dotimes (idx 5) 2297 (cl-callf + (aref old-data idx) 2298 (aref new-stats idx))) 2299 (puthash path new-stats lsp-diagnostic-stats)))) 2300 2301 (lsp-defun lsp--on-diagnostics-update-stats (workspace 2302 (&PublishDiagnosticsParams :uri :diagnostics)) 2303 (let ((path (lsp--fix-path-casing (lsp--uri-to-path uri))) 2304 (new-stats (make-vector 5 0))) 2305 (mapc (-lambda ((&Diagnostic :severity?)) 2306 (cl-incf (aref new-stats (or severity? 1)))) 2307 diagnostics) 2308 (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace)))) 2309 (mapc (-lambda ((&Diagnostic :severity?)) 2310 (cl-decf (aref new-stats (or severity? 1)))) 2311 old-diags)) 2312 (lsp-diagnostics--update-path path new-stats) 2313 (while (not (string= path (setf path (file-name-directory 2314 (directory-file-name path))))) 2315 (lsp-diagnostics--update-path path new-stats)))) 2316 2317 (defun lsp--on-diagnostics (workspace params) 2318 "Callback for textDocument/publishDiagnostics. 2319 interface PublishDiagnosticsParams { 2320 uri: string; 2321 diagnostics: Diagnostic[]; 2322 } 2323 PARAMS contains the diagnostics data. 2324 WORKSPACE is the workspace that contains the diagnostics." 2325 (when lsp-diagnostic-filter 2326 (setf params (funcall lsp-diagnostic-filter params workspace))) 2327 2328 (lsp--on-diagnostics-update-stats workspace params) 2329 2330 (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params) 2331 (lsp--virtual-buffer-mappings (ht)) 2332 (file (lsp--fix-path-casing (lsp--uri-to-path uri))) 2333 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2334 2335 (if (seq-empty-p diagnostics) 2336 (remhash file workspace-diagnostics) 2337 (puthash file (append diagnostics nil) workspace-diagnostics)) 2338 2339 (run-hooks 'lsp-diagnostics-updated-hook))) 2340 2341 (defun lsp-diagnostics--workspace-cleanup (workspace) 2342 (->> workspace 2343 (lsp--workspace-diagnostics) 2344 (maphash (lambda (key _) 2345 (lsp--on-diagnostics-update-stats 2346 workspace 2347 (lsp-make-publish-diagnostics-params 2348 :uri (lsp--path-to-uri key) 2349 :diagnostics []))))) 2350 (clrhash (lsp--workspace-diagnostics workspace))) 2351 2352 2353 2354 ;; textDocument/foldingRange support 2355 2356 (cl-defstruct lsp--folding-range beg end kind children) 2357 2358 (defvar-local lsp--cached-folding-ranges nil) 2359 (defvar-local lsp--cached-nested-folding-ranges nil) 2360 2361 (defun lsp--folding-range-width (range) 2362 (- (lsp--folding-range-end range) 2363 (lsp--folding-range-beg range))) 2364 2365 (defun lsp--get-folding-ranges () 2366 "Get the folding ranges for the current buffer." 2367 (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges)) 2368 (let* ((ranges (lsp-request "textDocument/foldingRange" 2369 `(:textDocument ,(lsp--text-document-identifier)))) 2370 (sorted-line-col-pairs (->> ranges 2371 (cl-mapcan (-lambda ((&FoldingRange :start-line 2372 :start-character? 2373 :end-line 2374 :end-character?)) 2375 (list (cons start-line start-character?) 2376 (cons end-line end-character?)))) 2377 (-sort #'lsp--line-col-comparator))) 2378 (line-col-to-point-map (lsp--convert-line-col-to-points-batch 2379 sorted-line-col-pairs))) 2380 (setq lsp--cached-folding-ranges 2381 (cons (buffer-chars-modified-tick) 2382 (--> ranges 2383 (seq-map (-lambda ((range &as 2384 &FoldingRange :start-line 2385 :start-character? 2386 :end-line 2387 :end-character? 2388 :kind?)) 2389 (make-lsp--folding-range 2390 :beg (ht-get line-col-to-point-map 2391 (cons start-line start-character?)) 2392 :end (ht-get line-col-to-point-map 2393 (cons end-line end-character?)) 2394 :kind kind?)) 2395 it) 2396 (seq-filter (lambda (folding-range) 2397 (< (lsp--folding-range-beg folding-range) 2398 (lsp--folding-range-end folding-range))) 2399 it) 2400 (seq-into it 'list) 2401 (delete-dups it)))))) 2402 (cdr lsp--cached-folding-ranges)) 2403 2404 (defun lsp--get-nested-folding-ranges () 2405 "Get a list of nested folding ranges for the current buffer." 2406 (-let [(tick . _) lsp--cached-folding-ranges] 2407 (if (and (eq tick (buffer-chars-modified-tick)) 2408 lsp--cached-nested-folding-ranges) 2409 lsp--cached-nested-folding-ranges 2410 (setq lsp--cached-nested-folding-ranges 2411 (lsp--folding-range-build-trees (lsp--get-folding-ranges)))))) 2412 2413 (defun lsp--folding-range-build-trees (ranges) 2414 (setq ranges (seq-sort #'lsp--range-before-p ranges)) 2415 (let* ((dummy-node (make-lsp--folding-range 2416 :beg most-negative-fixnum 2417 :end most-positive-fixnum)) 2418 (stack (list dummy-node))) 2419 (dolist (range ranges) 2420 (while (not (lsp--range-inside-p range (car stack))) 2421 (pop stack)) 2422 (push range (lsp--folding-range-children (car stack))) 2423 (push range stack)) 2424 (lsp--folding-range-children dummy-node))) 2425 2426 (defun lsp--range-inside-p (r1 r2) 2427 "Return non-nil if folding range R1 lies inside R2" 2428 (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2)) 2429 (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2)))) 2430 2431 (defun lsp--range-before-p (r1 r2) 2432 "Return non-nil if folding range R1 ends before R2" 2433 ;; Ensure r1 comes before r2 2434 (or (< (lsp--folding-range-beg r1) 2435 (lsp--folding-range-beg r2)) 2436 ;; If beg(r1) == beg(r2) make sure r2 ends first 2437 (and (= (lsp--folding-range-beg r1) 2438 (lsp--folding-range-beg r2)) 2439 (< (lsp--folding-range-end r2) 2440 (lsp--folding-range-end r1))))) 2441 2442 (defun lsp--point-inside-range-p (point range) 2443 "Return non-nil if POINT lies inside folding range RANGE." 2444 (and (>= point (lsp--folding-range-beg range)) 2445 (<= point (lsp--folding-range-end range)))) 2446 2447 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point))) 2448 "Return the innermost folding range POINT lies in." 2449 (seq-reduce (lambda (innermost-range curr-range) 2450 (if (and (lsp--point-inside-range-p point curr-range) 2451 (or (null innermost-range) 2452 (lsp--range-inside-p curr-range innermost-range))) 2453 curr-range 2454 innermost-range)) 2455 (lsp--get-folding-ranges) 2456 nil)) 2457 2458 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point))) 2459 "Return the outermost folding range POINT lies in." 2460 (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range) 2461 (let ((curr-width (lsp--folding-range-width curr-range))) 2462 (if (and (lsp--point-inside-range-p point curr-range) 2463 (or (null best-pair) 2464 (> curr-width outermost-width))) 2465 (cons curr-width curr-range) 2466 best-pair))) 2467 (lsp--get-folding-ranges) 2468 nil))) 2469 2470 (defun lsp--folding-range-at-point-bounds () 2471 (when (and lsp-enable-folding 2472 (lsp-feature? "textDocument/foldingRange")) 2473 (if-let ((range (lsp--get-current-innermost-folding-range))) 2474 (cons (lsp--folding-range-beg range) 2475 (lsp--folding-range-end range))))) 2476 (put 'lsp--folding-range 'bounds-of-thing-at-point 2477 #'lsp--folding-range-at-point-bounds) 2478 2479 (defun lsp--get-nearest-folding-range (&optional backward) 2480 (let ((point (point)) 2481 (found nil)) 2482 (while (not 2483 (or found 2484 (if backward 2485 (<= point (point-min)) 2486 (>= point (point-max))))) 2487 (if backward (cl-decf point) (cl-incf point)) 2488 (setq found (lsp--get-current-innermost-folding-range point))) 2489 found)) 2490 2491 (defun lsp--folding-range-at-point-forward-op (n) 2492 (when (and lsp-enable-folding 2493 (not (zerop n)) 2494 (lsp-feature? "textDocument/foldingRange")) 2495 (cl-block break 2496 (dotimes (_ (abs n)) 2497 (if-let ((range (lsp--get-nearest-folding-range (< n 0)))) 2498 (goto-char (if (< n 0) 2499 (lsp--folding-range-beg range) 2500 (lsp--folding-range-end range))) 2501 (cl-return-from break)))))) 2502 (put 'lsp--folding-range 'forward-op 2503 #'lsp--folding-range-at-point-forward-op) 2504 2505 (defun lsp--folding-range-at-point-beginning-op () 2506 (goto-char (car (lsp--folding-range-at-point-bounds)))) 2507 (put 'lsp--folding-range 'beginning-op 2508 #'lsp--folding-range-at-point-beginning-op) 2509 2510 (defun lsp--folding-range-at-point-end-op () 2511 (goto-char (cdr (lsp--folding-range-at-point-bounds)))) 2512 (put 'lsp--folding-range 'end-op 2513 #'lsp--folding-range-at-point-end-op) 2514 2515 (defun lsp--range-at-point-bounds () 2516 (or (lsp--folding-range-at-point-bounds) 2517 (when-let ((range (and 2518 (lsp-feature? "textDocument/hover") 2519 (->> (lsp--text-document-position-params) 2520 (lsp-request "textDocument/hover") 2521 (lsp:hover-range?))))) 2522 (lsp--range-to-region range)))) 2523 2524 ;; A more general purpose "thing", useful for applications like focus.el 2525 (put 'lsp--range 'bounds-of-thing-at-point 2526 #'lsp--range-at-point-bounds) 2527 2528 (defun lsp--log-io-p (method) 2529 "Return non nil if should log for METHOD." 2530 (and lsp-log-io 2531 (or (not lsp-log-io-allowlist-methods) 2532 (member method lsp-log-io-allowlist-methods)))) 2533 2534 2535 ;; toggles 2536 2537 (defun lsp-toggle-trace-io () 2538 "Toggle client-server protocol logging." 2539 (interactive) 2540 (setq lsp-log-io (not lsp-log-io)) 2541 (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled"))) 2542 2543 (defun lsp-toggle-signature-auto-activate () 2544 "Toggle signature auto activate." 2545 (interactive) 2546 (setq lsp-signature-auto-activate 2547 (unless lsp-signature-auto-activate '(:on-trigger-char))) 2548 (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled")) 2549 (lsp--update-signature-help-hook)) 2550 2551 (defun lsp-toggle-on-type-formatting () 2552 "Toggle on type formatting." 2553 (interactive) 2554 (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting)) 2555 (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled")) 2556 (lsp--update-on-type-formatting-hook)) 2557 2558 (defun lsp-toggle-symbol-highlight () 2559 "Toggle symbol highlighting." 2560 (interactive) 2561 (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting)) 2562 2563 (cond 2564 ((and lsp-enable-symbol-highlighting 2565 (lsp-feature? "textDocument/documentHighlight")) 2566 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t) 2567 (lsp--info "Symbol highlighting enabled in current buffer.")) 2568 ((not lsp-enable-symbol-highlighting) 2569 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 2570 (lsp--remove-overlays 'lsp-highlight) 2571 (lsp--info "Symbol highlighting disabled in current buffer.")))) 2572 2573 2574 ;; keybindings 2575 (defvar lsp--binding-descriptions nil 2576 "List of key binding/short description pair.") 2577 2578 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings) 2579 "In KEYMAP, define key sequence KEY as DEF conditionally. 2580 This is like `define-key', except the definition disappears 2581 whenever COND evaluates to nil. 2582 DESC is the short-description for the binding. 2583 BINDINGS is a list of (key def desc cond)." 2584 (declare (indent defun) 2585 (debug (form form form form form &rest sexp))) 2586 (->> (cl-list* key def desc cond bindings) 2587 (-partition 4) 2588 (-mapcat (-lambda ((key def desc cond)) 2589 `((define-key ,keymap ,key 2590 '(menu-item 2591 ,(format "maybe-%s" def) 2592 ,def 2593 :filter 2594 (lambda (item) 2595 (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer) 2596 lsp--describe-buffer) 2597 (current-buffer)) 2598 ,cond) 2599 item)))) 2600 (when (stringp ,key) 2601 (setq lsp--binding-descriptions 2602 (append lsp--binding-descriptions '(,key ,desc))))))) 2603 macroexp-progn)) 2604 2605 (defvar lsp--describe-buffer nil) 2606 2607 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus) 2608 (let ((lsp--describe-buffer buffer)) 2609 (funcall fn buffer prefix menus))) 2610 2611 (advice-add 'describe-buffer-bindings 2612 :around 2613 #'lsp-describe-buffer-bindings-advice) 2614 2615 (defun lsp--prepend-prefix (mappings) 2616 (->> mappings 2617 (-partition 2) 2618 (-mapcat (-lambda ((key description)) 2619 (list (concat lsp-keymap-prefix " " key) 2620 description))))) 2621 2622 (defvar lsp-command-map 2623 (-doto (make-sparse-keymap) 2624 (lsp-define-conditional-key 2625 ;; workspaces 2626 "wD" lsp-disconnect "disconnect" (lsp-workspaces) 2627 "wd" lsp-describe-session "describe session" t 2628 "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces) 2629 "wr" lsp-workspace-restart "restart server" (lsp-workspaces) 2630 "ws" lsp "start server" t 2631 2632 ;; formatting 2633 "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting") 2634 (lsp-feature? "textDocument/formatting")) 2635 "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting") 2636 2637 ;; folders 2638 "Fa" lsp-workspace-folders-add "add folder" t 2639 "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t 2640 "Fr" lsp-workspace-folders-remove "remove folder" t 2641 2642 ;; toggles 2643 "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature? 2644 "textDocument/publishDiagnostics") 2645 "TL" lsp-toggle-trace-io "toggle log io" t 2646 "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline) 2647 "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs) 2648 "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature? 2649 "textDocument/codeAction") 2650 "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature? 2651 "textDocument/documentSymbol") 2652 "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc) 2653 "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature? 2654 "textDocument/onTypeFormatting") 2655 "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight") 2656 "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens") 2657 "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp") 2658 2659 ;; goto 2660 "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol") 2661 "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration") 2662 "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list) 2663 "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition") 2664 "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls") 2665 (fboundp 'lsp-treemacs-call-hierarchy)) 2666 "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation") 2667 "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references") 2668 "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition") 2669 2670 ;; help 2671 "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc) 2672 (lsp-feature? "textDocument/hover")) 2673 "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover") 2674 "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp") 2675 2676 ;; refactoring 2677 "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction") 2678 "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename") 2679 2680 ;; actions 2681 "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction") 2682 "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight") 2683 "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy)) 2684 2685 ;; peeks 2686 "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition") 2687 (fboundp 'lsp-ui-peek-find-definitions)) 2688 "Gi" lsp-ui-peek-find-implementation "peek implementations" (and 2689 (fboundp 'lsp-ui-peek-find-implementation) 2690 (lsp-feature? "textDocument/implementation")) 2691 "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references) 2692 (lsp-feature? "textDocument/references")) 2693 "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp 2694 'lsp-ui-peek-find-workspace-symbol) 2695 (lsp-feature? "workspace/symbol"))))) 2696 2697 2698 ;; which-key integration 2699 2700 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key") 2701 (declare-function which-key-add-key-based-replacements "ext:which-key") 2702 2703 (defun lsp-enable-which-key-integration (&optional all-modes) 2704 "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current 2705 active `major-mode', or for all major modes when ALL-MODES is t." 2706 (cl-flet ((which-key-fn (if all-modes 2707 'which-key-add-key-based-replacements 2708 (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode)))) 2709 (apply 2710 #'which-key-fn 2711 (lsp--prepend-prefix 2712 (cl-list* 2713 "" "lsp" 2714 "w" "workspaces" 2715 "F" "folders" 2716 "=" "formatting" 2717 "T" "toggle" 2718 "g" "goto" 2719 "h" "help" 2720 "r" "refactor" 2721 "a" "code actions" 2722 "G" "peek" 2723 lsp--binding-descriptions))))) 2724 2725 2726 ;; Globbing syntax 2727 2728 ;; We port VSCode's glob-to-regexp code 2729 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts) 2730 ;; since the LSP globbing syntax seems to be the same as that of 2731 ;; VSCode. 2732 2733 (defconst lsp-globstar "**" 2734 "Globstar pattern.") 2735 2736 (defconst lsp-glob-split ?/ 2737 "The character by which we split path components in a glob 2738 pattern.") 2739 2740 (defconst lsp-path-regexp "[/\\\\]" 2741 "Forward or backslash to be used as a path separator in 2742 computed regexps.") 2743 2744 (defconst lsp-non-path-regexp "[^/\\\\]" 2745 "A regexp matching anything other than a slash.") 2746 2747 (defconst lsp-globstar-regexp 2748 (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?" 2749 lsp-path-regexp 2750 lsp-non-path-regexp lsp-path-regexp 2751 lsp-path-regexp lsp-non-path-regexp) 2752 "Globstar in regexp form.") 2753 2754 (defun lsp-split-glob-pattern (pattern split-char) 2755 "Split PATTERN at SPLIT-CHAR while respecting braces and brackets." 2756 (when pattern 2757 (let ((segments nil) 2758 (in-braces nil) 2759 (in-brackets nil) 2760 (current-segment "")) 2761 (dolist (char (string-to-list pattern)) 2762 (cl-block 'exit-point 2763 (if (eq char split-char) 2764 (when (and (null in-braces) 2765 (null in-brackets)) 2766 (push current-segment segments) 2767 (setq current-segment "") 2768 (cl-return-from 'exit-point)) 2769 (pcase char 2770 (?{ 2771 (setq in-braces t)) 2772 (?} 2773 (setq in-braces nil)) 2774 (?\[ 2775 (setq in-brackets t)) 2776 (?\] 2777 (setq in-brackets nil)))) 2778 (setq current-segment (concat current-segment 2779 (char-to-string char))))) 2780 (unless (string-empty-p current-segment) 2781 (push current-segment segments)) 2782 (nreverse segments)))) 2783 2784 (defun lsp--glob-to-regexp (pattern) 2785 "Helper function to convert a PATTERN from LSP's glob syntax to 2786 an Elisp regexp." 2787 (if (string-empty-p pattern) 2788 "" 2789 (let ((current-regexp "") 2790 (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split))) 2791 (if (-all? (lambda (segment) (eq segment lsp-globstar)) 2792 glob-segments) 2793 ".*" 2794 (let ((prev-segment-was-globstar nil)) 2795 (seq-do-indexed 2796 (lambda (segment index) 2797 (if (string-equal segment lsp-globstar) 2798 (unless prev-segment-was-globstar 2799 (setq current-regexp (concat current-regexp 2800 lsp-globstar-regexp)) 2801 (setq prev-segment-was-globstar t)) 2802 (let ((in-braces nil) 2803 (brace-val "") 2804 (in-brackets nil) 2805 (bracket-val "")) 2806 (dolist (char (string-to-list segment)) 2807 (cond 2808 ((and (not (char-equal char ?\})) 2809 in-braces) 2810 (setq brace-val (concat brace-val 2811 (char-to-string char)))) 2812 ((and in-brackets 2813 (or (not (char-equal char ?\])) 2814 (string-empty-p bracket-val))) 2815 (let ((curr (cond 2816 ((char-equal char ?-) 2817 "-") 2818 ;; NOTE: ?\^ and ?^ are different characters 2819 ((and (memq char '(?^ ?!)) 2820 (string-empty-p bracket-val)) 2821 "^") 2822 ((char-equal char lsp-glob-split) 2823 "") 2824 (t 2825 (regexp-quote (char-to-string char)))))) 2826 (setq bracket-val (concat bracket-val curr)))) 2827 (t 2828 (cl-case char 2829 (?{ 2830 (setq in-braces t)) 2831 (?\[ 2832 (setq in-brackets t)) 2833 (?} 2834 (let* ((choices (lsp-split-glob-pattern brace-val ?\,)) 2835 (brace-regexp (concat "\\(?:" 2836 (mapconcat #'lsp--glob-to-regexp choices "\\|") 2837 "\\)"))) 2838 (setq current-regexp (concat current-regexp 2839 brace-regexp)) 2840 (setq in-braces nil) 2841 (setq brace-val ""))) 2842 (?\] 2843 (setq current-regexp 2844 (concat current-regexp 2845 "[" bracket-val "]")) 2846 (setq in-brackets nil) 2847 (setq bracket-val "")) 2848 (?? 2849 (setq current-regexp 2850 (concat current-regexp 2851 lsp-non-path-regexp))) 2852 (?* 2853 (setq current-regexp 2854 (concat current-regexp 2855 lsp-non-path-regexp "*?"))) 2856 (t 2857 (setq current-regexp 2858 (concat current-regexp 2859 (regexp-quote (char-to-string char))))))))) 2860 (when (and (< index (1- (length glob-segments))) 2861 (or (not (string-equal (nth (1+ index) glob-segments) 2862 lsp-globstar)) 2863 (< (+ index 2) 2864 (length glob-segments)))) 2865 (setq current-regexp 2866 (concat current-regexp 2867 lsp-path-regexp))) 2868 (setq prev-segment-was-globstar nil)))) 2869 glob-segments) 2870 current-regexp))))) 2871 2872 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365 2873 (defun lsp-glob-unbrace-at-top-level (glob-pattern) 2874 "If GLOB-PATTERN does not start with a brace, return a singleton list 2875 containing GLOB-PATTERN. 2876 2877 If GLOB-PATTERN does start with a brace, return a list of the 2878 comma-separated globs within the top-level braces." 2879 (if (not (string-prefix-p "{" glob-pattern)) 2880 (list glob-pattern) 2881 (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,))) 2882 2883 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern) 2884 "Convert GLOB-PATTERN to a regexp wrapped with the beginning- 2885 and end-of-string meta-characters." 2886 (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'")) 2887 2888 (defun lsp-glob-to-regexps (glob-pattern) 2889 "Convert a GLOB-PATTERN to a list of Elisp regexps." 2890 (when-let* 2891 ((glob-pattern (cond ((hash-table-p glob-pattern) 2892 (ht-get glob-pattern "pattern")) 2893 ((stringp glob-pattern) glob-pattern) 2894 (t (error "Unknown glob-pattern type: %s" glob-pattern)))) 2895 (trimmed-pattern (string-trim glob-pattern)) 2896 (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern))) 2897 (seq-map #'lsp-glob-convert-to-wrapped-regexp 2898 top-level-unbraced-patterns))) 2899 2900 2901 2902 (defvar lsp-mode-menu) 2903 2904 (defun lsp-mouse-click (event) 2905 (interactive "e") 2906 (let* ((ec (event-start event)) 2907 (choice (x-popup-menu event lsp-mode-menu)) 2908 (action (lookup-key lsp-mode-menu (apply 'vector choice)))) 2909 2910 (select-window (posn-window ec)) 2911 2912 (unless (and (region-active-p) (eq action 'lsp-execute-code-action)) 2913 (goto-char (posn-point ec))) 2914 (run-with-idle-timer 2915 0.001 nil 2916 (lambda () 2917 (cl-labels ((check (value) (not (null value)))) 2918 (when choice 2919 (call-interactively action))))))) 2920 2921 (defvar lsp-mode-map 2922 (let ((map (make-sparse-keymap))) 2923 (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse) 2924 (define-key map (kbd "C-<mouse-1>") #'ignore) 2925 (define-key map (kbd "<mouse-3>") #'lsp-mouse-click) 2926 (define-key map (kbd "C-S-SPC") #'lsp-signature-activate) 2927 (when lsp-keymap-prefix 2928 (define-key map (kbd lsp-keymap-prefix) lsp-command-map)) 2929 map) 2930 "Keymap for `lsp-mode'.") 2931 2932 (define-minor-mode lsp-mode "Mode for LSP interaction." 2933 :keymap lsp-mode-map 2934 :lighter 2935 (" LSP[" 2936 (lsp--buffer-workspaces 2937 (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "][")) 2938 (:propertize "Disconnected" face warning)) 2939 "]") 2940 :group 'lsp-mode 2941 (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred)) 2942 ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp' 2943 (lsp))) 2944 2945 (defvar lsp-mode-menu 2946 (easy-menu-create-menu 2947 nil 2948 `(["Go to definition" lsp-find-definition 2949 :active (lsp-feature? "textDocument/definition")] 2950 ["Find references" lsp-find-references 2951 :active (lsp-feature? "textDocument/references")] 2952 ["Find implementations" lsp-find-implementation 2953 :active (lsp-feature? "textDocument/implementation")] 2954 ["Find declarations" lsp-find-declaration 2955 :active (lsp-feature? "textDocument/declaration")] 2956 ["Go to type declaration" lsp-find-type-definition 2957 :active (lsp-feature? "textDocument/typeDefinition")] 2958 "--" 2959 ["Describe" lsp-describe-thing-at-point] 2960 ["Code action" lsp-execute-code-action] 2961 ["Format" lsp-format-buffer] 2962 ["Highlight references" lsp-document-highlight] 2963 ["Type Hierarchy" lsp-java-type-hierarchy 2964 :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")] 2965 ["Type Hierarchy" lsp-treemacs-type-hierarchy 2966 :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")) 2967 (functionp 'lsp-treemacs-type-hierarchy) 2968 (lsp-feature? "textDocument/typeHierarchy"))] 2969 ["Call Hierarchy" lsp-treemacs-call-hierarchy 2970 :visible (and (functionp 'lsp-treemacs-call-hierarchy) 2971 (lsp-feature? "textDocument/callHierarchy"))] 2972 ["Rename" lsp-rename 2973 :active (lsp-feature? "textDocument/rename")] 2974 "--" 2975 ("Session" 2976 ["View logs" lsp-workspace-show-log] 2977 ["Describe" lsp-describe-session] 2978 ["Shutdown" lsp-shutdown-workspace] 2979 ["Restart" lsp-restart-workspace]) 2980 ("Workspace Folders" 2981 ["Add" lsp-workspace-folders-add] 2982 ["Remove" lsp-workspace-folders-remove] 2983 ["Open" lsp-workspace-folders-open]) 2984 ("Toggle features" 2985 ["Lenses" lsp-lens-mode] 2986 ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode] 2987 ["Modeline code actions" lsp-modeline-code-actions-mode] 2988 ["Modeline diagnostics" lsp-modeline-diagnostics-mode]) 2989 "---" 2990 ("Debug" 2991 :active (bound-and-true-p dap-ui-mode) 2992 :filter ,(lambda (_) 2993 (and (boundp 'dap-ui-menu-items) 2994 (nthcdr 3 dap-ui-menu-items)))))) 2995 "Menu for lsp-mode.") 2996 2997 (defalias 'make-lsp-client 'make-lsp--client) 2998 2999 (cl-defstruct lsp--registered-capability 3000 (id "") 3001 (method " ") 3002 (options nil)) 3003 3004 ;; A ‘lsp--workspace’ object represents exactly one language server process. 3005 (cl-defstruct lsp--workspace 3006 ;; the `ewoc' object for displaying I/O to and from the server 3007 (ewoc nil) 3008 3009 ;; ‘server-capabilities’ is a hash table of the language server capabilities. 3010 ;; It is the hash table representation of a LSP ServerCapabilities structure; 3011 ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize. 3012 (server-capabilities nil) 3013 3014 ;; ‘registered-server-capabilities’ is a list of hash tables that represent 3015 ;; dynamically-registered Registration objects. See 3016 ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability. 3017 (registered-server-capabilities nil) 3018 3019 ;; ‘root’ is a directory name or a directory file name for the workspace 3020 ;; root. ‘lsp-mode’ passes this directory to the ‘initialize’ method of the 3021 ;; language server; see 3022 ;; https://microsoft.github.io/language-server-protocol/specification#initialize. 3023 (root nil) 3024 3025 ;; ‘client’ is the ‘lsp--client’ object associated with this workspace. 3026 (client nil) 3027 3028 ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It 3029 ;; used to derive the file path in `lsp--uri-to-path' when using tramp 3030 ;; connection. 3031 (host-root nil) 3032 3033 ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or 3034 ;; a network connection. ‘lsp-mode’ communicates with ‘proc’ using the 3035 ;; language server protocol. ‘proc’ corresponds to the COMMUNICATION-PROCESS 3036 ;; element of the return value of the client’s ‘get-root’ field, which see. 3037 (proc nil) 3038 3039 ;; ‘proc’ is a process object; it must represent a regular process, not a 3040 ;; pipe or network process. It represents the actual server process that 3041 ;; corresponds to this workspace. ‘cmd-proc’ corresponds to the 3042 ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’ 3043 ;; field, which see. 3044 (cmd-proc nil) 3045 3046 ;; ‘buffers’ is a list of buffers associated with this workspace. 3047 (buffers nil) 3048 3049 ;; if semantic tokens is enabled, `semantic-tokens-faces' contains 3050 ;; one face (or nil) for each token type supported by the language server. 3051 (semantic-tokens-faces nil) 3052 3053 ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces' 3054 ;; contains one face (or nil) for each modifier type supported by the language 3055 ;; server 3056 (semantic-tokens-modifier-faces nil) 3057 3058 ;; Extra client capabilities provided by third-party packages using 3059 ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME 3060 ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name, 3061 ;; and CAPS is either a plist of the client capabilities, or a function that 3062 ;; takes no argument and returns a plist of the client capabilities or nil. 3063 (extra-client-capabilities nil) 3064 3065 ;; Workspace status 3066 (status nil) 3067 3068 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3069 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3070 (metadata (make-hash-table :test 'equal)) 3071 3072 ;; contains all the file notification watches that have been created for the 3073 ;; current workspace in format filePath->file notification handle. 3074 (watches (make-hash-table :test 'equal)) 3075 3076 ;; list of workspace folders 3077 (workspace-folders nil) 3078 3079 ;; ‘last-id’ the last request id for the current workspace. 3080 (last-id 0) 3081 3082 ;; ‘status-string’ allows extensions to specify custom status string based on 3083 ;; the Language Server specific messages. 3084 (status-string nil) 3085 3086 ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it 3087 ;; was stopped). 3088 shutdown-action 3089 3090 ;; ‘diagnostics’ a hashmap with workspace diagnostics. 3091 (diagnostics (make-hash-table :test 'equal)) 3092 3093 ;; contains all the workDone progress tokens that have been created 3094 ;; for the current workspace. 3095 (work-done-tokens (make-hash-table :test 'equal))) 3096 3097 3098 (cl-defstruct lsp-session 3099 ;; contains the folders that are part of the current session 3100 folders 3101 ;; contains the folders that must not be imported in the current workspace. 3102 folders-blocklist 3103 ;; contains the list of folders that must be imported in a project in case of 3104 ;; multi root LSP server. 3105 (server-id->folders (make-hash-table :test 'equal)) 3106 ;; folder to list of the servers that are associated with the folder. 3107 (folder->servers (make-hash-table :test 'equal)) 3108 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3109 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3110 (metadata (make-hash-table :test 'equal))) 3111 3112 (defun lsp-workspace-status (status-string &optional workspace) 3113 "Set current workspace status to STATUS-STRING. 3114 If WORKSPACE is not specified defaults to lsp--cur-workspace." 3115 (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string)))) 3116 (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string))) 3117 3118 (defun lsp-session-set-metadata (key value &optional _workspace) 3119 "Associate KEY with VALUE in the WORKSPACE metadata. 3120 If WORKSPACE is not provided current workspace will be used." 3121 (puthash key value (lsp-session-metadata (lsp-session)))) 3122 3123 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata) 3124 3125 (defun lsp-session-get-metadata (key &optional _workspace) 3126 "Lookup KEY in WORKSPACE metadata. 3127 If WORKSPACE is not provided current workspace will be used." 3128 (gethash key (lsp-session-metadata (lsp-session)))) 3129 3130 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata) 3131 3132 (defun lsp-workspace-set-work-done-token (token value workspace) 3133 "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens." 3134 (puthash token value (lsp--workspace-work-done-tokens workspace))) 3135 3136 (defun lsp-workspace-get-work-done-token (token workspace) 3137 "Lookup TOKEN in the WORKSPACE work-done-tokens." 3138 (gethash token (lsp--workspace-work-done-tokens workspace))) 3139 3140 (defun lsp-workspace-rem-work-done-token (token workspace) 3141 "Remove TOKEN from the WORKSPACE work-done-tokens." 3142 (remhash token (lsp--workspace-work-done-tokens workspace))) 3143 3144 3145 (defun lsp--make-notification (method &optional params) 3146 "Create notification body for method METHOD and parameters PARAMS." 3147 (list :jsonrpc "2.0" :method method :params params)) 3148 3149 (defalias 'lsp--make-request 'lsp--make-notification) 3150 (defalias 'lsp-make-request 'lsp--make-notification) 3151 3152 (defun lsp--make-response (id result) 3153 "Create response for REQUEST with RESULT." 3154 `(:jsonrpc "2.0" :id ,id :result ,result)) 3155 3156 (defun lsp-make-notification (method &optional params) 3157 "Create notification body for method METHOD and parameters PARAMS." 3158 (lsp--make-notification method params)) 3159 3160 (defmacro lsp--json-serialize (params) 3161 (if (progn 3162 (require 'json) 3163 (fboundp 'json-serialize)) 3164 `(json-serialize ,params 3165 :null-object nil 3166 :false-object :json-false) 3167 `(let ((json-false :json-false)) 3168 (json-encode ,params)))) 3169 3170 (defun lsp--make-message (params) 3171 "Create a LSP message from PARAMS, after encoding it to a JSON string." 3172 (let ((body (lsp--json-serialize params))) 3173 (concat "Content-Length: " 3174 (number-to-string (1+ (string-bytes body))) 3175 "\r\n\r\n" 3176 body 3177 "\n"))) 3178 3179 (cl-defstruct lsp--log-entry timestamp process-time type method id body) 3180 3181 (defun lsp--make-log-entry (method id body type &optional process-time) 3182 "Create an outgoing log object from BODY with method METHOD and id ID. 3183 If ID is non-nil, then the body is assumed to be a notification. 3184 TYPE can either be `incoming' or `outgoing'" 3185 (cl-assert (memq type '(incoming-req outgoing-req incoming-notif 3186 outgoing-notif incoming-resp 3187 outgoing-resp))) 3188 (make-lsp--log-entry 3189 :timestamp (format-time-string "%I:%M:%S %p") 3190 :process-time process-time 3191 :method method 3192 :id id 3193 :type type 3194 :body body)) 3195 3196 (defun lsp--log-font-lock-json (body) 3197 "Font lock JSON BODY." 3198 (with-temp-buffer 3199 (insert body) 3200 ;; We set the temp buffer file-name extension to .json and call `set-auto-mode' 3201 ;; so the users configured json mode is used which could be 3202 ;; `json-mode', `json-ts-mode', `jsonian-mode', etc. 3203 (let ((buffer-file-name "lsp-log.json")) 3204 (delay-mode-hooks 3205 (set-auto-mode) 3206 (if (fboundp 'font-lock-ensure) 3207 (font-lock-ensure) 3208 (with-no-warnings 3209 (font-lock-fontify-buffer))))) 3210 (buffer-string))) 3211 3212 (defun lsp--log-entry-pp (entry) 3213 (cl-assert (lsp--log-entry-p entry)) 3214 (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time 3215 body) 3216 entry) 3217 (json-false :json-false) 3218 (json-encoding-pretty-print t) 3219 (str nil)) 3220 (setq str 3221 (concat (format "[Trace - %s] " timestamp) 3222 (pcase type 3223 ('incoming-req (format "Received request '%s - (%s)." method id)) 3224 ('outgoing-req (format "Sending request '%s - (%s)'." method id)) 3225 3226 ('incoming-notif (format "Received notification '%s'." method)) 3227 ('outgoing-notif (format "Sending notification '%s'." method)) 3228 3229 ('incoming-resp (format "Received response '%s - (%s)' in %dms." 3230 method id process-time)) 3231 ('outgoing-resp 3232 (format 3233 "Sending response '%s - (%s)'. Processing request took %dms" 3234 method id process-time))) 3235 "\n" 3236 (if (memq type '(incoming-resp ougoing-resp)) 3237 "Result: " 3238 "Params: ") 3239 (lsp--log-font-lock-json (json-encode body)) 3240 "\n\n\n")) 3241 (setq str (propertize str 'mouse-face 'highlight 'read-only t)) 3242 (insert str))) 3243 3244 (defvar-local lsp--log-io-ewoc nil) 3245 3246 (defun lsp--get-create-io-ewoc (workspace) 3247 (if (and (lsp--workspace-ewoc workspace) 3248 (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace)))) 3249 (lsp--workspace-ewoc workspace) 3250 (with-current-buffer (lsp--get-log-buffer-create workspace) 3251 (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode)) 3252 (setq-local window-point-insertion-type t) 3253 (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t)) 3254 (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc)) 3255 (lsp--workspace-ewoc workspace))) 3256 3257 (defun lsp--ewoc-count (ewoc) 3258 (let* ((count 0) 3259 (count-fn (lambda (_) (setq count (1+ count))))) 3260 (ewoc-map count-fn ewoc) 3261 count)) 3262 3263 (defun lsp--log-entry-new (entry workspace) 3264 (let* ((ewoc (lsp--get-create-io-ewoc workspace)) 3265 (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc))) 3266 (node (if (or (eq lsp-io-messages-max t) 3267 (>= lsp-io-messages-max count)) 3268 nil 3269 (ewoc-nth ewoc (1- lsp-io-messages-max)))) 3270 (prev nil) 3271 (inhibit-read-only t)) 3272 (while node 3273 (setq prev (ewoc-prev ewoc node)) 3274 (ewoc-delete ewoc node) 3275 (setq node prev)) 3276 (ewoc-enter-last ewoc entry))) 3277 3278 (defun lsp--send-notification (body) 3279 "Send BODY as a notification to the language server." 3280 (lsp-foreach-workspace 3281 (when (lsp--log-io-p (plist-get body :method)) 3282 (lsp--log-entry-new (lsp--make-log-entry 3283 (plist-get body :method) 3284 nil (plist-get body :params) 'outgoing-notif) 3285 lsp--cur-workspace)) 3286 (lsp--send-no-wait body 3287 (lsp--workspace-proc lsp--cur-workspace)))) 3288 3289 (defalias 'lsp-send-notification 'lsp--send-notification) 3290 3291 (defun lsp-notify (method params) 3292 "Send notification METHOD with PARAMS." 3293 (lsp--send-notification (lsp--make-notification method params))) 3294 3295 (defun lsp--cur-workspace-check () 3296 "Check whether buffer lsp workspace(s) are set." 3297 (cl-assert (lsp-workspaces) nil 3298 "No language server(s) is associated with this buffer.")) 3299 3300 (defun lsp--send-request (body &optional no-wait no-merge) 3301 "Send BODY as a request to the language server, get the response. 3302 If NO-WAIT is non-nil, don't synchronously wait for a response. 3303 If NO-MERGE is non-nil, don't merge the results but return an 3304 alist mapping workspace->result." 3305 (lsp-request (plist-get body :method) 3306 (plist-get body :params) 3307 :no-wait no-wait 3308 :no-merge no-merge)) 3309 3310 (defalias 'lsp-send-request 'lsp--send-request 3311 "Send BODY as a request to the language server and return the response 3312 synchronously. 3313 \n(fn BODY)") 3314 3315 (cl-defun lsp-request (method params &key no-wait no-merge) 3316 "Send request METHOD with PARAMS. 3317 If NO-MERGE is non-nil, don't merge the results but return alist 3318 workspace->result. 3319 If NO-WAIT is non-nil send the request as notification." 3320 (if no-wait 3321 (lsp-notify method params) 3322 (let* ((send-time (float-time)) 3323 ;; max time by which we must get a response 3324 (expected-time 3325 (and 3326 lsp-response-timeout 3327 (+ send-time lsp-response-timeout))) 3328 resp-result resp-error done?) 3329 (unwind-protect 3330 (progn 3331 (lsp-request-async method params 3332 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3333 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3334 :no-merge no-merge 3335 :mode 'detached 3336 :cancel-token :sync-request) 3337 (while (not (or resp-error resp-result)) 3338 (if (functionp 'json-rpc-connection) 3339 (catch 'lsp-done (sit-for 0.01)) 3340 (catch 'lsp-done 3341 (accept-process-output 3342 nil 3343 (if expected-time (- expected-time send-time) 1)))) 3344 (setq send-time (float-time)) 3345 (when (and expected-time (< expected-time send-time)) 3346 (error "Timeout while waiting for response. Method: %s" method))) 3347 (setq done? t) 3348 (cond 3349 ((eq resp-result :finished) nil) 3350 (resp-result resp-result) 3351 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3352 ((lsp-json-error? (cl-first resp-error)) 3353 (error (lsp:json-error-message (cl-first resp-error)))))) 3354 (unless done? 3355 (lsp-cancel-request-by-token :sync-request)))))) 3356 3357 (cl-defun lsp-request-while-no-input (method params) 3358 "Send request METHOD with PARAMS and waits until there is no input. 3359 Return same value as `lsp--while-no-input' and respecting `non-essential'." 3360 (if (or non-essential (not lsp-request-while-no-input-may-block)) 3361 (let* ((send-time (float-time)) 3362 ;; max time by which we must get a response 3363 (expected-time 3364 (and 3365 lsp-response-timeout 3366 (+ send-time lsp-response-timeout))) 3367 resp-result resp-error done?) 3368 (unwind-protect 3369 (progn 3370 (lsp-request-async method params 3371 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3372 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3373 :mode 'detached 3374 :cancel-token :sync-request) 3375 (while (not (or resp-error resp-result (input-pending-p))) 3376 (catch 'lsp-done 3377 (sit-for 3378 (if expected-time (- expected-time send-time) 1))) 3379 (setq send-time (float-time)) 3380 (when (and expected-time (< expected-time send-time)) 3381 (error "Timeout while waiting for response. Method: %s" method))) 3382 (setq done? (or resp-error resp-result)) 3383 (cond 3384 ((eq resp-result :finished) nil) 3385 (resp-result resp-result) 3386 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3387 ((lsp-json-error? (cl-first resp-error)) 3388 (error (lsp:json-error-message (cl-first resp-error)))))) 3389 (unless done? 3390 (lsp-cancel-request-by-token :sync-request)) 3391 (when (and (input-pending-p) lsp--throw-on-input) 3392 (throw 'input :interrupted)))) 3393 (lsp-request method params))) 3394 3395 (defvar lsp--cancelable-requests (ht)) 3396 3397 (cl-defun lsp-request-async (method params callback 3398 &key mode error-handler cancel-handler no-merge cancel-token) 3399 "Send METHOD with PARAMS as a request to the language server. 3400 Call CALLBACK with the response received from the server 3401 asynchronously. 3402 MODE determines when the callback will be called depending on the 3403 condition of the original buffer. It could be: 3404 - `detached' which means that the callback will be executed no 3405 matter what has happened to the buffer. 3406 - `alive' - the callback will be executed only if the buffer from 3407 which the call was executed is still alive. 3408 - `current' the callback will be executed only if the original buffer 3409 is still selected. 3410 - `tick' - the callback will be executed only if the buffer was not modified. 3411 - `unchanged' - the callback will be executed only if the buffer hasn't 3412 changed and if the buffer is not modified. 3413 3414 ERROR-HANDLER will be called in case the request has failed. 3415 CANCEL-HANDLER will be called in case the request is being canceled. 3416 If NO-MERGE is non-nil, don't merge the results but return alist 3417 workspace->result. 3418 CANCEL-TOKEN is the token that can be used to cancel request." 3419 (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params) 3420 callback mode error-handler cancel-handler no-merge cancel-token)) 3421 3422 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback) 3423 (lambda (&rest _) 3424 (unless (and (equal 'post-command-hook hook) 3425 (equal (current-buffer) buf)) 3426 (lsp--request-cleanup-hooks id) 3427 (with-lsp-workspaces workspaces 3428 (lsp--cancel-request id) 3429 (when cancel-callback (funcall cancel-callback))) 3430 (lsp-log "Cancelling %s(%s) in hook %s" method id hook)))) 3431 3432 (defun lsp--create-async-callback 3433 (callback method no-merge workspaces) 3434 "Create async handler expecting COUNT results, merge them and call CALLBACK. 3435 MODE determines when the callback will be called depending on the 3436 condition of the original buffer. METHOD is the invoked method. 3437 If NO-MERGE is non-nil, don't merge the results but return alist 3438 workspace->result. ID is the request id." 3439 (let (results errors) 3440 (lambda (result) 3441 (push (cons lsp--cur-workspace result) 3442 (if (eq result :error) errors results)) 3443 (when (and (not (eq (length errors) (length workspaces))) 3444 (eq (+ (length errors) (length results)) (length workspaces))) 3445 (funcall callback 3446 (if no-merge 3447 results 3448 (lsp--merge-results (-map #'cl-rest results) method))))))) 3449 3450 (defcustom lsp-default-create-error-handler-fn nil 3451 "Default error handler customization. 3452 Handler should give METHOD as argument and return function of one argument 3453 ERROR." 3454 :type 'function 3455 :group 'lsp-mode 3456 :package-version '(lsp-mode . "9.0.0")) 3457 3458 (defun lsp--create-default-error-handler (method) 3459 "Default error handler. 3460 METHOD is the executed method." 3461 (if lsp-default-create-error-handler-fn 3462 (funcall lsp-default-create-error-handler-fn method) 3463 (lambda (error) 3464 (lsp--warn "%s" (or (lsp--error-string error) 3465 (format "%s Request has failed" method)))))) 3466 3467 (defvar lsp--request-cleanup-hooks (ht)) 3468 3469 (defun lsp--request-cleanup-hooks (request-id) 3470 (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks))) 3471 (funcall cleanup-function) 3472 (remhash request-id lsp--request-cleanup-hooks))) 3473 3474 (defun lsp-cancel-request-by-token (cancel-token) 3475 "Cancel request using CANCEL-TOKEN." 3476 (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests)) 3477 (with-lsp-workspaces workspaces 3478 (lsp--cancel-request request-id)) 3479 (remhash cancel-token lsp--cancelable-requests) 3480 (lsp--request-cleanup-hooks request-id))) 3481 3482 (defun lsp--send-request-async (body callback 3483 &optional mode error-callback cancel-callback 3484 no-merge cancel-token) 3485 "Send BODY as a request to the language server. 3486 Call CALLBACK with the response received from the server 3487 asynchronously. 3488 MODE determines when the callback will be called depending on the 3489 condition of the original buffer. It could be: 3490 - `detached' which means that the callback will be executed no 3491 matter what has happened to the buffer. 3492 - `alive' - the callback will be executed only if the buffer from 3493 which the call was executed is still alive. 3494 - `current' the callback will be executed only if the original buffer 3495 is still selected. 3496 - `tick' - the callback will be executed only if the buffer was not modified. 3497 - `unchanged' - the callback will be executed only if the buffer hasn't 3498 changed and if the buffer is not modified. 3499 3500 ERROR-CALLBACK will be called in case the request has failed. 3501 CANCEL-CALLBACK will be called in case the request is being canceled. 3502 If NO-MERGE is non-nil, don't merge the results but return alist 3503 workspace->result. 3504 CANCEL-TOKEN is the token that can be used to cancel request." 3505 (when cancel-token 3506 (lsp-cancel-request-by-token cancel-token)) 3507 3508 (if-let ((target-workspaces (lsp--find-workspaces-for body))) 3509 (let* ((start-time (current-time)) 3510 (method (plist-get body :method)) 3511 (id (cl-incf lsp-last-id)) 3512 (buf (current-buffer)) 3513 (cancel-callback (when cancel-callback 3514 (pcase mode 3515 ((or 'alive 'tick 'unchanged) 3516 (lambda () 3517 (with-current-buffer buf 3518 (funcall cancel-callback)))) 3519 (_ cancel-callback)))) 3520 ;; calculate what are the (hook . local) pairs which will cancel 3521 ;; the request 3522 (hooks (pcase mode 3523 ('alive '((kill-buffer-hook . t))) 3524 ('tick '((kill-buffer-hook . t) (after-change-functions . t))) 3525 ('unchanged '((after-change-functions . t) (post-command-hook . nil))) 3526 ('current '((post-command-hook . nil))))) 3527 ;; note: lambdas in emacs can be compared but we should make sure 3528 ;; that all of the captured arguments are the same - in our case 3529 ;; `lsp--create-request-cancel' will return the same lambda when 3530 ;; called with the same params. 3531 (cleanup-hooks 3532 (lambda () (mapc 3533 (-lambda ((hook . local)) 3534 (if local 3535 (when (buffer-live-p buf) 3536 (with-current-buffer buf 3537 (remove-hook hook 3538 (lsp--create-request-cancel 3539 id target-workspaces hook buf method cancel-callback) 3540 t))) 3541 (remove-hook hook (lsp--create-request-cancel 3542 id target-workspaces hook buf method cancel-callback)))) 3543 hooks) 3544 (remhash cancel-token lsp--cancelable-requests))) 3545 (callback (pcase mode 3546 ((or 'alive 'tick 'unchanged) (lambda (&rest args) 3547 (with-current-buffer buf 3548 (apply callback args)))) 3549 (_ callback))) 3550 (callback (lsp--create-async-callback callback 3551 method 3552 no-merge 3553 target-workspaces)) 3554 (callback (lambda (result) 3555 (lsp--request-cleanup-hooks id) 3556 (funcall callback result))) 3557 (error-callback (lsp--create-async-callback 3558 (or error-callback 3559 (lsp--create-default-error-handler method)) 3560 method 3561 nil 3562 target-workspaces)) 3563 (error-callback (lambda (error) 3564 (funcall callback :error) 3565 (lsp--request-cleanup-hooks id) 3566 (funcall error-callback error))) 3567 (body (plist-put body :id id))) 3568 3569 ;; cancel request in any of the hooks 3570 (mapc (-lambda ((hook . local)) 3571 (add-hook hook 3572 (lsp--create-request-cancel 3573 id target-workspaces hook buf method cancel-callback) 3574 nil local)) 3575 hooks) 3576 (puthash id cleanup-hooks lsp--request-cleanup-hooks) 3577 3578 (setq lsp--last-active-workspaces target-workspaces) 3579 3580 (when cancel-token 3581 (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests)) 3582 3583 (seq-doseq (workspace target-workspaces) 3584 (when (lsp--log-io-p method) 3585 (lsp--log-entry-new (lsp--make-log-entry method id 3586 (plist-get body :params) 3587 'outgoing-req) 3588 workspace)) 3589 (puthash id 3590 (list callback error-callback method start-time (current-time)) 3591 (-> workspace 3592 (lsp--workspace-client) 3593 (lsp--client-response-handlers))) 3594 (lsp--send-no-wait body (lsp--workspace-proc workspace))) 3595 body) 3596 (error "The connected server(s) does not support method %s. 3597 To find out what capabilities support your server use `M-x lsp-describe-session' 3598 and expand the capabilities section" 3599 (plist-get body :method)))) 3600 3601 ;; deprecated, use lsp-request-async. 3602 (defalias 'lsp-send-request-async 'lsp--send-request-async) 3603 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1") 3604 3605 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any 3606 ;; pending language servers. 3607 (add-hook 'kill-emacs-hook #'lsp--global-teardown) 3608 3609 (defun lsp--global-teardown () 3610 "Unload working workspaces." 3611 (lsp-foreach-workspace (lsp--shutdown-workspace))) 3612 3613 (defun lsp--shutdown-workspace (&optional restart) 3614 "Shut down the language server process for ‘lsp--cur-workspace’." 3615 (with-demoted-errors "LSP error: %S" 3616 (let ((lsp-response-timeout 0.5)) 3617 (condition-case err 3618 (lsp-request "shutdown" nil) 3619 (error (lsp--error "%s" err)))) 3620 (lsp-notify "exit" nil)) 3621 (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown)) 3622 (lsp--uninitialize-workspace)) 3623 3624 (defcustom lsp-inlay-hint-enable nil 3625 "If non-nil it will enable inlay hints." 3626 :type 'boolean 3627 :group 'lsp-mode 3628 :package-version '(lsp-mode . "9.0.0")) 3629 3630 (defun lsp--uninitialize-workspace () 3631 "Cleanup buffer state. 3632 When a workspace is shut down, by request or from just 3633 disappearing, unset all the variables related to it." 3634 (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace] 3635 (lsp-process-kill cmd-proc) 3636 (mapc (lambda (buf) 3637 (when (lsp-buffer-live-p buf) 3638 (lsp-with-current-buffer buf 3639 (lsp-managed-mode -1)))) 3640 buffers) 3641 (lsp-diagnostics--workspace-cleanup lsp--cur-workspace))) 3642 3643 (defun lsp--client-capabilities (&optional custom-capabilities) 3644 "Return the client capabilities appending CUSTOM-CAPABILITIES." 3645 (append 3646 `((general . ((positionEncodings . ["utf-32", "utf-16"]))) 3647 (workspace . ((workspaceEdit . ((documentChanges . t) 3648 (resourceOperations . ["create" "rename" "delete"]))) 3649 (applyEdit . t) 3650 (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))))) 3651 (executeCommand . ((dynamicRegistration . :json-false))) 3652 ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t))))) 3653 (workspaceFolders . t) 3654 (configuration . t) 3655 ,@(when lsp-semantic-tokens-enable 3656 `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests) 3657 lsp-semantic-tokens-honor-refresh-requests) 3658 :json-false)))))) 3659 ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t))))) 3660 ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false))))) 3661 (fileOperations . ((didCreate . :json-false) 3662 (willCreate . :json-false) 3663 (didRename . t) 3664 (willRename . t) 3665 (didDelete . :json-false) 3666 (willDelete . :json-false))))) 3667 (textDocument . ((declaration . ((dynamicRegistration . t) 3668 (linkSupport . t))) 3669 (definition . ((dynamicRegistration . t) 3670 (linkSupport . t))) 3671 (references . ((dynamicRegistration . t))) 3672 (implementation . ((dynamicRegistration . t) 3673 (linkSupport . t))) 3674 (typeDefinition . ((dynamicRegistration . t) 3675 (linkSupport . t))) 3676 (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t))) 3677 (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))) 3678 (hierarchicalDocumentSymbolSupport . t))) 3679 (formatting . ((dynamicRegistration . t))) 3680 (rangeFormatting . ((dynamicRegistration . t))) 3681 (onTypeFormatting . ((dynamicRegistration . t))) 3682 ,@(when (and lsp-semantic-tokens-enable 3683 (functionp 'lsp--semantic-tokens-capabilities)) 3684 (lsp--semantic-tokens-capabilities)) 3685 (rename . ((dynamicRegistration . t) (prepareSupport . t))) 3686 (codeAction . ((dynamicRegistration . t) 3687 (isPreferredSupport . t) 3688 (codeActionLiteralSupport . ((codeActionKind . ((valueSet . ["" 3689 "quickfix" 3690 "refactor" 3691 "refactor.extract" 3692 "refactor.inline" 3693 "refactor.rewrite" 3694 "source" 3695 "source.organizeImports"]))))) 3696 (resolveSupport . ((properties . ["edit" "command"]))) 3697 (dataSupport . t))) 3698 (completion . ((completionItem . ((snippetSupport . ,(cond 3699 ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode))) 3700 (lsp--warn (concat 3701 "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. " 3702 "You must either install yasnippet, or disable snippet support.")) 3703 :json-false) 3704 (lsp-enable-snippet t) 3705 (t :json-false))) 3706 (documentationFormat . ["markdown" "plaintext"]) 3707 ;; Remove this after jdtls support resolveSupport 3708 (resolveAdditionalTextEditsSupport . t) 3709 (insertReplaceSupport . t) 3710 (deprecatedSupport . t) 3711 (resolveSupport 3712 . ((properties . ["documentation" 3713 "detail" 3714 "additionalTextEdits" 3715 "command"]))) 3716 (insertTextModeSupport . ((valueSet . [1 2]))))) 3717 (contextSupport . t) 3718 (dynamicRegistration . t))) 3719 (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t))))) 3720 (dynamicRegistration . t))) 3721 (documentLink . ((dynamicRegistration . t) 3722 (tooltipSupport . t))) 3723 (hover . ((contentFormat . ["markdown" "plaintext"]) 3724 (dynamicRegistration . t))) 3725 ,@(when lsp-enable-folding 3726 `((foldingRange . ((dynamicRegistration . t) 3727 ,@(when lsp-folding-range-limit 3728 `((rangeLimit . ,lsp-folding-range-limit))) 3729 ,@(when lsp-folding-line-folding-only 3730 `((lineFoldingOnly . t))))))) 3731 (selectionRange . ((dynamicRegistration . t))) 3732 (callHierarchy . ((dynamicRegistration . :json-false))) 3733 (typeHierarchy . ((dynamicRegistration . t))) 3734 (publishDiagnostics . ((relatedInformation . t) 3735 (tagSupport . ((valueSet . [1 2]))) 3736 (versionSupport . t))) 3737 (linkedEditingRange . ((dynamicRegistration . t))))) 3738 (window . ((workDoneProgress . t) 3739 (showDocument . ((support . t)))))) 3740 custom-capabilities)) 3741 3742 (defun lsp-find-roots-for-workspace (workspace session) 3743 "Get all roots for the WORKSPACE." 3744 (-filter #'identity (ht-map (lambda (folder workspaces) 3745 (when (-contains? workspaces workspace) 3746 folder)) 3747 (lsp-session-folder->servers session)))) 3748 3749 (defun lsp-session-watches (&optional session) 3750 "Get watches created for SESSION." 3751 (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session)))) 3752 (-let [res (make-hash-table :test 'equal)] 3753 (puthash "__watches" res (lsp-session-metadata (or session (lsp-session)))) 3754 res))) 3755 3756 (defun lsp--file-process-event (session root-folder event) 3757 "Process file event." 3758 (let* ((changed-file (cl-third event)) 3759 (rel-changed-file (f-relative changed-file root-folder)) 3760 (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type)) 3761 (bit-position (1- event-numeric-kind)) 3762 (watch-bit (ash 1 bit-position))) 3763 (->> 3764 session 3765 lsp-session-folder->servers 3766 (gethash root-folder) 3767 (seq-do (lambda (workspace) 3768 (when (->> 3769 workspace 3770 lsp--workspace-registered-server-capabilities 3771 (-any? 3772 (lambda (capability) 3773 (and 3774 (equal (lsp--registered-capability-method capability) 3775 "workspace/didChangeWatchedFiles") 3776 (->> 3777 capability 3778 lsp--registered-capability-options 3779 (lsp:did-change-watched-files-registration-options-watchers) 3780 (seq-find 3781 (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp)) 3782 (when (or (null kind?) 3783 (> (logand kind? watch-bit) 0)) 3784 (-let [regexes (or cached-regexp 3785 (let ((regexp (lsp-glob-to-regexps glob-pattern))) 3786 (lsp-put fs-watcher :_cachedRegexp regexp) 3787 regexp))] 3788 (-any? (lambda (re) 3789 (or (string-match re changed-file) 3790 (string-match re rel-changed-file))) 3791 regexes)))))))))) 3792 (with-lsp-workspace workspace 3793 (lsp-notify 3794 "workspace/didChangeWatchedFiles" 3795 `((changes . [((type . ,event-numeric-kind) 3796 (uri . ,(lsp--path-to-uri changed-file)))])))))))))) 3797 3798 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?)) 3799 "Register capability REG." 3800 (when (and lsp-enable-file-watchers 3801 (equal method "workspace/didChangeWatchedFiles")) 3802 (-let* ((created-watches (lsp-session-watches (lsp-session))) 3803 (root-folders (cl-set-difference 3804 (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session)) 3805 (ht-keys created-watches)))) 3806 ;; create watch for each root folder without such 3807 (dolist (folder root-folders) 3808 (let* ((watch (make-lsp-watch :root-directory folder)) 3809 (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder)) 3810 (ignored-files-regex-list (car ignored-things)) 3811 (ignored-directories-regex-list (cadr ignored-things))) 3812 (puthash folder watch created-watches) 3813 (lsp-watch-root-folder (file-truename folder) 3814 (-partial #'lsp--file-process-event (lsp-session) folder) 3815 ignored-files-regex-list 3816 ignored-directories-regex-list 3817 watch 3818 t))))) 3819 3820 (push 3821 (make-lsp--registered-capability :id id :method method :options register-options?) 3822 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3823 3824 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body) 3825 "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to 3826 access dir-local variables." 3827 (declare (indent 1) (debug t)) 3828 `(with-temp-buffer 3829 ;; Set the buffer's name to something under the root so that we can hack the local variables 3830 ;; This file doesn't need to exist and will not be created due to this. 3831 (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root))) 3832 (hack-local-variables) 3833 (prog1 ,@body 3834 (setq-local buffer-file-name nil)))) 3835 3836 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root) 3837 "Return a list of the form 3838 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given 3839 WORKSPACE-ROOT." 3840 ;; The intent of this function is to provide per-root workspace-level customization of the 3841 ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables. 3842 (lsp--with-workspace-temp-buffer workspace-root 3843 (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories)))) 3844 3845 3846 (defun lsp--cleanup-hanging-watches () 3847 "Cleanup watches in case there are no more workspaces that are interested 3848 in that particular folder." 3849 (let* ((session (lsp-session)) 3850 (watches (lsp-session-watches session))) 3851 (dolist (watched-folder (ht-keys watches)) 3852 (when (-none? (lambda (workspace) 3853 (with-lsp-workspace workspace 3854 (lsp--registered-capability "workspace/didChangeWatchedFiles"))) 3855 (gethash watched-folder (lsp-session-folder->servers (lsp-session)))) 3856 (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder) 3857 (lsp-kill-watch (gethash watched-folder watches)) 3858 (remhash watched-folder watches))))) 3859 3860 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method)) 3861 "Unregister capability UNREG." 3862 (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace) 3863 (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id)) 3864 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3865 (when (equal method "workspace/didChangeWatchedFiles") 3866 (lsp--cleanup-hanging-watches))) 3867 3868 (defun lsp--server-capabilities () 3869 "Return the capabilities of the language server associated with the buffer." 3870 (->> (lsp-workspaces) 3871 (-keep #'lsp--workspace-server-capabilities) 3872 (apply #'lsp-merge))) 3873 3874 (defun lsp--send-open-close-p () 3875 "Return whether open and close notifications should be sent to the server." 3876 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3877 (or (memq sync '(1 2)) 3878 (lsp:text-document-sync-options-open-close? sync)))) 3879 3880 (defun lsp--send-will-save-p () 3881 "Return whether willSave notifications should be sent to the server." 3882 (-> (lsp--server-capabilities) 3883 (lsp:server-capabilities-text-document-sync?) 3884 (lsp:text-document-sync-options-will-save?))) 3885 3886 (defun lsp--send-will-save-wait-until-p () 3887 "Return whether willSaveWaitUntil notifications should be sent to the server." 3888 (-> (lsp--server-capabilities) 3889 (lsp:server-capabilities-text-document-sync?) 3890 (lsp:text-document-sync-options-will-save-wait-until?))) 3891 3892 (defun lsp--send-did-save-p () 3893 "Return whether didSave notifications should be sent to the server." 3894 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3895 (or (memq sync '(1 2)) 3896 (lsp:text-document-sync-options-save? sync)))) 3897 3898 (defun lsp--save-include-text-p () 3899 "Return whether save notifications should include the text document's contents." 3900 (->> (lsp--server-capabilities) 3901 (lsp:server-capabilities-text-document-sync?) 3902 (lsp:text-document-sync-options-save?) 3903 (lsp:text-document-save-registration-options-include-text?))) 3904 3905 (defun lsp--send-will-rename-files-p (path) 3906 "Return whether willRenameFiles request should be sent to the server. 3907 If any filters, checks if it applies for PATH." 3908 (let* ((will-rename (-> (lsp--server-capabilities) 3909 (lsp:server-capabilities-workspace?) 3910 (lsp:workspace-server-capabilities-file-operations?) 3911 (lsp:workspace-file-operations-will-rename?))) 3912 (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list))) 3913 (and will-rename 3914 (or (seq-empty-p filters) 3915 (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob))) 3916 (-let [regexes (lsp-glob-to-regexps glob)] 3917 (and (or (not scheme?) 3918 (string-prefix-p scheme? (lsp--path-to-uri path))) 3919 (-any? (lambda (re) 3920 (string-match re path)) 3921 regexes)))) 3922 filters))))) 3923 3924 (defun lsp--send-did-rename-files-p () 3925 "Return whether didRenameFiles notification should be sent to the server." 3926 (-> (lsp--server-capabilities) 3927 (lsp:server-capabilities-workspace?) 3928 (lsp:workspace-server-capabilities-file-operations?) 3929 (lsp:workspace-file-operations-did-rename?))) 3930 3931 (declare-function project-roots "ext:project" (project) t) 3932 (declare-function project-root "ext:project" (project) t) 3933 3934 (defun lsp--suggest-project-root () 3935 "Get project root." 3936 (or 3937 (when (fboundp 'projectile-project-root) 3938 (condition-case nil 3939 (projectile-project-root) 3940 (error nil))) 3941 (when (fboundp 'project-current) 3942 (when-let ((project (project-current))) 3943 (if (fboundp 'project-root) 3944 (project-root project) 3945 (car (with-no-warnings 3946 (project-roots project)))))) 3947 default-directory)) 3948 3949 (defun lsp--read-from-file (file) 3950 "Read FILE content." 3951 (when (file-exists-p file) 3952 (cl-first (read-from-string (f-read-text file 'utf-8))))) 3953 3954 (defun lsp--persist (file-name to-persist) 3955 "Persist TO-PERSIST in FILE-NAME. 3956 3957 This function creates the parent directories if they don't exist 3958 yet." 3959 (let ((print-length nil) 3960 (print-level nil)) 3961 ;; Create all parent directories: 3962 (make-directory (f-parent file-name) t) 3963 (f-write-text (prin1-to-string to-persist) 'utf-8 file-name))) 3964 3965 (defun lsp-workspace-folders-add (project-root) 3966 "Add PROJECT-ROOT to the list of workspace folders." 3967 (interactive 3968 (list (read-directory-name "Select folder to add: " 3969 (or (lsp--suggest-project-root) default-directory) nil t))) 3970 (cl-pushnew (lsp-f-canonical project-root) 3971 (lsp-session-folders (lsp-session)) :test 'equal) 3972 (lsp--persist-session (lsp-session)) 3973 3974 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)) 3975 3976 (defun lsp-workspace-folders-remove (project-root) 3977 "Remove PROJECT-ROOT from the list of workspace folders." 3978 (interactive (list (completing-read "Select folder to remove: " 3979 (lsp-session-folders (lsp-session)) 3980 nil t nil nil 3981 (lsp-find-session-folder (lsp-session) default-directory)))) 3982 3983 (setq project-root (lsp-f-canonical project-root)) 3984 3985 ;; send remove folder to each multiroot workspace associated with the folder 3986 (dolist (wks (->> (lsp-session) 3987 (lsp-session-folder->servers) 3988 (gethash project-root) 3989 (--filter (lsp--client-multi-root (lsp--workspace-client it))))) 3990 (with-lsp-workspace wks 3991 (lsp-notify "workspace/didChangeWorkspaceFolders" 3992 (lsp-make-did-change-workspace-folders-params 3993 :event (lsp-make-workspace-folders-change-event 3994 :removed (vector (lsp-make-workspace-folder 3995 :uri (lsp--path-to-uri project-root) 3996 :name (f-filename project-root))) 3997 :added []))))) 3998 3999 ;; turn off servers in the removed directory 4000 (let* ((session (lsp-session)) 4001 (folder->servers (lsp-session-folder->servers session)) 4002 (server-id->folders (lsp-session-server-id->folders session)) 4003 (workspaces (gethash project-root folder->servers))) 4004 4005 (remhash project-root folder->servers) 4006 4007 ;; turn off the servers without root folders 4008 (dolist (workspace workspaces) 4009 (when (--none? (-contains? it workspace) (ht-values folder->servers)) 4010 (lsp--info "Shutdown %s since folder %s is removed..." 4011 (lsp--workspace-print workspace) project-root) 4012 (with-lsp-workspace workspace (lsp--shutdown-workspace)))) 4013 4014 (setf (lsp-session-folders session) 4015 (-remove-item project-root (lsp-session-folders session))) 4016 4017 (ht-aeach (puthash key 4018 (-remove-item project-root value) 4019 server-id->folders) 4020 server-id->folders) 4021 (lsp--persist-session (lsp-session))) 4022 4023 (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root))) 4024 4025 (defun lsp-workspace-blocklist-remove (project-root) 4026 "Remove PROJECT-ROOT from the workspace blocklist." 4027 (interactive (list (completing-read "Select folder to remove:" 4028 (lsp-session-folders-blocklist (lsp-session)) 4029 nil t))) 4030 (setf (lsp-session-folders-blocklist (lsp-session)) 4031 (delete project-root 4032 (lsp-session-folders-blocklist (lsp-session)))) 4033 (lsp--persist-session (lsp-session))) 4034 4035 (define-obsolete-function-alias 'lsp-workspace-folders-switch 4036 'lsp-workspace-folders-open "lsp-mode 6.1") 4037 4038 (defun lsp-workspace-folders-open (project-root) 4039 "Open the directory located at PROJECT-ROOT" 4040 (interactive (list (completing-read "Open folder: " 4041 (lsp-session-folders (lsp-session)) 4042 nil t))) 4043 (find-file project-root)) 4044 4045 (defun lsp--maybe-enable-signature-help (trigger-characters) 4046 (let ((ch last-command-event)) 4047 (when (cl-find ch trigger-characters :key #'string-to-char) 4048 (lsp-signature-activate)))) 4049 4050 (defun lsp--on-type-formatting-handler-create () 4051 (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" ))) 4052 (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character? 4053 :first-trigger-character) provider] 4054 (lambda () 4055 (lsp--on-type-formatting first-trigger-character 4056 more-trigger-character?))))) 4057 4058 (defun lsp--update-on-type-formatting-hook (&optional cleanup?) 4059 (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create))) 4060 (cond 4061 ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?)) 4062 (add-hook 'post-self-insert-hook on-type-formatting-handler nil t)) 4063 ((or cleanup? 4064 (not lsp-enable-on-type-formatting)) 4065 (remove-hook 'post-self-insert-hook on-type-formatting-handler t))))) 4066 4067 (defun lsp--signature-help-handler-create () 4068 (-when-let ((&SignatureHelpOptions? :trigger-characters?) 4069 (lsp--capability-for-method "textDocument/signatureHelp")) 4070 (lambda () 4071 (lsp--maybe-enable-signature-help trigger-characters?)))) 4072 4073 (defun lsp--update-signature-help-hook (&optional cleanup?) 4074 (let ((signature-help-handler (lsp--signature-help-handler-create))) 4075 (cond 4076 ((and (or (equal lsp-signature-auto-activate t) 4077 (memq :on-trigger-char lsp-signature-auto-activate)) 4078 signature-help-handler) 4079 (add-hook 'post-self-insert-hook signature-help-handler nil t)) 4080 4081 ((or cleanup? 4082 (not (or (equal lsp-signature-auto-activate t) 4083 (memq :on-trigger-char lsp-signature-auto-activate)))) 4084 (remove-hook 'post-self-insert-hook signature-help-handler t))))) 4085 4086 (defun lsp--after-set-visited-file-name () 4087 (lsp-disconnect) 4088 (lsp)) 4089 4090 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27 4091 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099 4092 (defvar eldoc-documentation-default) ; CI 4093 (when (< emacs-major-version 28) 4094 (unless (boundp 'eldoc-documentation-functions) 4095 (load "eldoc" nil 'nomessage)) 4096 (when (memq (default-value 'eldoc-documentation-function) '(nil ignore)) 4097 ;; actually `eldoc-documentation-strategy', but CI was failing 4098 (setq-default eldoc-documentation-function 'eldoc-documentation-default))) 4099 4100 (define-minor-mode lsp-managed-mode 4101 "Mode for source buffers managed by lsp-mode." 4102 :lighter nil 4103 (cond 4104 (lsp-managed-mode 4105 (when (lsp-feature? "textDocument/hover") 4106 (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t) 4107 (eldoc-mode 1)) 4108 4109 (add-hook 'after-change-functions #'lsp-on-change nil t) 4110 (add-hook 'after-revert-hook #'lsp-on-revert nil t) 4111 (add-hook 'after-save-hook #'lsp-on-save nil t) 4112 (add-hook 'auto-save-hook #'lsp--on-auto-save nil t) 4113 (add-hook 'before-change-functions #'lsp-before-change nil t) 4114 (add-hook 'before-save-hook #'lsp--before-save nil t) 4115 (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t) 4116 (add-hook 'post-command-hook #'lsp--post-command nil t) 4117 4118 (lsp--update-on-type-formatting-hook) 4119 (lsp--update-signature-help-hook) 4120 4121 (when lsp-enable-xref 4122 (add-hook 'xref-backend-functions #'lsp--xref-backend nil t)) 4123 4124 (lsp-configure-buffer) 4125 4126 ;; make sure we turn off lsp-mode in case major mode changes, because major 4127 ;; mode change will wipe the buffer locals. 4128 (add-hook 'change-major-mode-hook #'lsp-disconnect nil t) 4129 (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t) 4130 4131 (let ((buffer (lsp-current-buffer))) 4132 (run-with-idle-timer 4133 0.0 nil 4134 (lambda () 4135 (when (lsp-buffer-live-p buffer) 4136 (lsp-with-current-buffer buffer 4137 (lsp--on-change-debounce buffer) 4138 (lsp--on-idle buffer))))))) 4139 (t 4140 (lsp-unconfig-buffer) 4141 4142 (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t) 4143 (remove-hook 'post-command-hook #'lsp--post-command t) 4144 (remove-hook 'after-change-functions #'lsp-on-change t) 4145 (remove-hook 'after-revert-hook #'lsp-on-revert t) 4146 (remove-hook 'after-save-hook #'lsp-on-save t) 4147 (remove-hook 'auto-save-hook #'lsp--on-auto-save t) 4148 (remove-hook 'before-change-functions #'lsp-before-change t) 4149 (remove-hook 'before-save-hook #'lsp--before-save t) 4150 (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t) 4151 4152 (lsp--update-on-type-formatting-hook :cleanup) 4153 (lsp--update-signature-help-hook :cleanup) 4154 4155 (when lsp--on-idle-timer 4156 (cancel-timer lsp--on-idle-timer) 4157 (setq lsp--on-idle-timer nil)) 4158 4159 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4160 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4161 4162 (lsp--remove-overlays 'lsp-highlight) 4163 (lsp--remove-overlays 'lsp-links) 4164 4165 (remove-hook 'xref-backend-functions #'lsp--xref-backend t) 4166 (remove-hook 'change-major-mode-hook #'lsp-disconnect t) 4167 (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t) 4168 (setq-local lsp-buffer-uri nil)))) 4169 4170 (defun lsp-configure-buffer () 4171 "Configure LSP features for current buffer." 4172 ;; make sure the core is running in the context of all available workspaces 4173 ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context 4174 (let ((lsp--buffer-workspaces (cond 4175 (lsp--buffer-workspaces) 4176 (lsp--cur-workspace (list lsp--cur-workspace)))) 4177 lsp--cur-workspace) 4178 (when lsp-auto-configure 4179 (lsp--auto-configure) 4180 4181 (when (and lsp-enable-text-document-color 4182 (lsp-feature? "textDocument/documentColor")) 4183 (add-hook 'lsp-on-change-hook #'lsp--document-color nil t)) 4184 4185 (when (and lsp-enable-imenu 4186 (lsp-feature? "textDocument/documentSymbol")) 4187 (lsp-enable-imenu)) 4188 4189 (when (and lsp-enable-indentation 4190 (lsp-feature? "textDocument/rangeFormatting")) 4191 (add-function :override (local 'indent-region-function) #'lsp-format-region)) 4192 4193 (when (and lsp-enable-symbol-highlighting 4194 (lsp-feature? "textDocument/documentHighlight")) 4195 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)) 4196 4197 (when (and lsp-enable-links 4198 (lsp-feature? "textDocument/documentLink")) 4199 (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t)) 4200 4201 (when (and lsp-inlay-hint-enable 4202 (lsp-feature? "textDocument/inlayHint")) 4203 (lsp-inlay-hints-mode)) 4204 4205 (when (and lsp-enable-dap-auto-configure 4206 (functionp 'dap-mode)) 4207 (dap-auto-configure-mode 1))) 4208 (run-hooks 'lsp-configure-hook))) 4209 4210 (defun lsp-unconfig-buffer () 4211 "Unconfigure LSP features for buffer." 4212 (lsp--remove-overlays 'lsp-color) 4213 4214 (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function) 4215 (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index) 4216 (setq-local imenu-menubar-modified-tick 0) 4217 (setq-local imenu--index-alist nil) 4218 (imenu--cleanup)) 4219 4220 (remove-function (local 'indent-region-function) #'lsp-format-region) 4221 4222 (remove-hook 'lsp-on-change-hook #'lsp--document-color t) 4223 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4224 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4225 4226 (when (and lsp-enable-dap-auto-configure 4227 (functionp 'dap-mode)) 4228 (dap-auto-configure-mode -1)) 4229 4230 (run-hooks 'lsp-unconfigure-hook)) 4231 4232 (defun lsp--buffer-content () 4233 (lsp-save-restriction-and-excursion 4234 (or (lsp-virtual-buffer-call :buffer-string) 4235 (buffer-substring-no-properties (point-min) 4236 (point-max))))) 4237 4238 (defun lsp--text-document-did-open () 4239 "`document/didOpen' event." 4240 (run-hooks 'lsp-before-open-hook) 4241 (when (and lsp-auto-touch-files 4242 (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri))))) 4243 (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri)) 4244 (save-buffer)) 4245 4246 (setq lsp--cur-version (or lsp--cur-version 0)) 4247 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 4248 (lsp-notify 4249 "textDocument/didOpen" 4250 (list :textDocument 4251 (list :uri (lsp--buffer-uri) 4252 :languageId (lsp-buffer-language) 4253 :version lsp--cur-version 4254 :text (lsp--buffer-content)))) 4255 4256 (lsp-managed-mode 1) 4257 4258 (run-hooks 'lsp-after-open-hook) 4259 (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client)))) 4260 (-some-> (lsp--client-after-open-fn client) 4261 (funcall)) 4262 (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client)) 4263 (intern-soft) 4264 (run-hooks)))) 4265 4266 (defun lsp--text-document-identifier () 4267 "Make TextDocumentIdentifier." 4268 (list :uri (lsp--buffer-uri))) 4269 4270 (defun lsp--versioned-text-document-identifier () 4271 "Make VersionedTextDocumentIdentifier." 4272 (plist-put (lsp--text-document-identifier) :version lsp--cur-version)) 4273 4274 (defun lsp--cur-line (&optional point) 4275 (1- (line-number-at-pos point))) 4276 4277 (defun lsp--cur-position () 4278 "Make a Position object for the current point." 4279 (or (lsp-virtual-buffer-call :cur-position) 4280 (lsp-save-restriction-and-excursion 4281 (list :line (lsp--cur-line) 4282 :character (- (point) (line-beginning-position)))))) 4283 4284 (defun lsp--point-to-position (point) 4285 "Convert POINT to Position." 4286 (lsp-save-restriction-and-excursion 4287 (goto-char point) 4288 (lsp--cur-position))) 4289 4290 (defun lsp--range (start end) 4291 "Make Range body from START and END." 4292 ;; make sure start and end are Position objects 4293 (list :start start :end end)) 4294 4295 (defun lsp--region-to-range (start end) 4296 "Make Range object for the current region." 4297 (lsp--range (lsp--point-to-position start) 4298 (lsp--point-to-position end))) 4299 4300 (defun lsp--region-or-line () 4301 "The active region or the current line." 4302 (if (use-region-p) 4303 (lsp--region-to-range (region-beginning) (region-end)) 4304 (lsp--region-to-range (line-beginning-position) (line-end-position)))) 4305 4306 (defun lsp--check-document-changes-version (document-changes) 4307 "Verify that DOCUMENT-CHANGES have the proper version." 4308 (unless (seq-every-p 4309 (-lambda ((&TextDocumentEdit :text-document)) 4310 (or 4311 (not text-document) 4312 (let* ((filename (-> text-document 4313 lsp:versioned-text-document-identifier-uri 4314 lsp--uri-to-path)) 4315 (version (lsp:versioned-text-document-identifier-version? text-document))) 4316 (with-current-buffer (find-file-noselect filename) 4317 (or (null version) (zerop version) (= -1 version) 4318 (equal version lsp--cur-version)))))) 4319 document-changes) 4320 (error "Document changes cannot be applied due to different document version"))) 4321 4322 (defun lsp--apply-workspace-edit (workspace-edit &optional operation) 4323 "Apply the WorkspaceEdit object WORKSPACE-EDIT. 4324 OPERATION is symbol representing the source of this text edit." 4325 (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit)) 4326 (if-let ((document-changes (seq-reverse document-changes?))) 4327 (progn 4328 (lsp--check-document-changes-version document-changes) 4329 (->> document-changes 4330 (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create"))) 4331 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4332 (->> document-changes 4333 (seq-filter (-lambda ((&CreateFile :kind)) 4334 (and (or (not kind) (equal kind "edit")) 4335 (not (equal kind "create"))))) 4336 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4337 (->> document-changes 4338 (seq-filter (-lambda ((&CreateFile :kind)) 4339 (and (not (or (not kind) (equal kind "edit"))) 4340 (not (equal kind "create"))))) 4341 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))) 4342 (lsp-map 4343 (lambda (uri text-edits) 4344 (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect) 4345 (lsp--apply-text-edits text-edits operation))) 4346 changes?)))) 4347 4348 (defmacro lsp-with-filename (file &rest body) 4349 "Execute BODY with FILE as a context. 4350 Need to handle the case when FILE indicates virtual buffer." 4351 (declare (indent 1) (debug t)) 4352 `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file))) 4353 (lsp-with-current-buffer lsp--virtual-buffer 4354 ,@body) 4355 ,@body)) 4356 4357 (defun lsp--apply-text-document-edit (edit &optional operation) 4358 "Apply the TextDocumentEdit object EDIT. 4359 OPERATION is symbol representing the source of this text edit. 4360 If the file is not being visited by any buffer, it is opened with 4361 `find-file-noselect'. 4362 Because lsp-mode does not store previous document versions, the edit is only 4363 applied if the version of the textDocument matches the version of the 4364 corresponding file. 4365 4366 interface TextDocumentEdit { 4367 textDocument: VersionedTextDocumentIdentifier; 4368 edits: TextEdit[]; 4369 }" 4370 (pcase (lsp:edit-kind edit) 4371 ("create" (-let* (((&CreateFile :uri :options?) edit) 4372 (file-name (lsp--uri-to-path uri))) 4373 (mkdir (f-dirname file-name) t) 4374 (f-touch file-name) 4375 (when (lsp:create-file-options-overwrite? options?) 4376 (f-write-text "" nil file-name)) 4377 (find-file-noselect file-name))) 4378 ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit)) 4379 (f-delete (lsp--uri-to-path uri) recursive?))) 4380 ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit) 4381 (old-file-name (lsp--uri-to-path old-uri)) 4382 (new-file-name (lsp--uri-to-path new-uri)) 4383 (buf (find-buffer-visiting old-file-name))) 4384 (when buf 4385 (lsp-with-current-buffer buf 4386 (save-buffer) 4387 (lsp--text-document-did-close))) 4388 (mkdir (f-dirname new-file-name) t) 4389 (rename-file old-file-name new-file-name overwrite?) 4390 (when buf 4391 (lsp-with-current-buffer buf 4392 (set-buffer-modified-p nil) 4393 (setq lsp-buffer-uri nil) 4394 (set-visited-file-name new-file-name) 4395 (lsp))))) 4396 (_ (let ((file-name (->> edit 4397 (lsp:text-document-edit-text-document) 4398 (lsp:versioned-text-document-identifier-uri) 4399 (lsp--uri-to-path)))) 4400 (lsp-with-current-buffer (find-buffer-visiting file-name) 4401 (lsp-with-filename file-name 4402 (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation))))))) 4403 4404 (lsp-defun lsp--position-compare ((&Position :line left-line 4405 :character left-character) 4406 (&Position :line right-line 4407 :character right-character)) 4408 "Return t if position LEFT is greater than RIGHT." 4409 (if (= left-line right-line) 4410 (> left-character right-character) 4411 (> left-line right-line))) 4412 4413 (lsp-defun lsp-point-in-range? (position (&Range :start :end)) 4414 "Returns if POINT is in RANGE." 4415 (not (or (lsp--position-compare start position) 4416 (lsp--position-compare position end)))) 4417 4418 (lsp-defun lsp--position-equal ((&Position :line left-line 4419 :character left-character) 4420 (&Position :line right-line 4421 :character right-character)) 4422 "Return whether LEFT and RIGHT positions are equal." 4423 (and (= left-line right-line) 4424 (= left-character right-character))) 4425 4426 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end)) 4427 (&TextEdit :range (&Range :start right-start :end right-end))) 4428 (if (lsp--position-equal left-start right-start) 4429 (lsp--position-compare left-end right-end) 4430 (lsp--position-compare left-start right-start))) 4431 4432 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text)) 4433 "Apply the edits described in the TextEdit object in TEXT-EDIT." 4434 (setq new-text (s-replace "\r" "" (or new-text ""))) 4435 (lsp:set-text-edit-new-text edit new-text) 4436 (goto-char start) 4437 (delete-region start end) 4438 (insert new-text)) 4439 4440 ;; WORKAROUND: typescript-language might send -1 when applying code actions. 4441 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582 4442 (lsp-defun lsp--fix-point ((point &as &Position :character :line)) 4443 (-doto point 4444 (lsp:set-position-line (max 0 line)) 4445 (lsp:set-position-character (max 0 character)))) 4446 4447 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as 4448 &TextEdit 4449 :range (&Range :start :end) 4450 :new-text)) 4451 "Apply the edits described in the TextEdit object in TEXT-EDIT. 4452 The method uses `replace-buffer-contents'." 4453 (setq new-text (s-replace "\r" "" (or new-text ""))) 4454 (lsp:set-text-edit-new-text edit new-text) 4455 (-let* ((source (current-buffer)) 4456 ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start) 4457 :end (lsp--fix-point end))))) 4458 (with-temp-buffer 4459 (insert new-text) 4460 (let ((temp (current-buffer))) 4461 (with-current-buffer source 4462 (save-excursion 4463 (save-restriction 4464 (narrow-to-region beg end) 4465 4466 ;; On emacs versions < 26.2, 4467 ;; `replace-buffer-contents' is buggy - it calls 4468 ;; change functions with invalid arguments - so we 4469 ;; manually call the change functions here. 4470 ;; 4471 ;; See emacs bugs #32237, #32278: 4472 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237 4473 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278 4474 (let ((inhibit-modification-hooks t) 4475 (length (- end beg))) 4476 (run-hook-with-args 'before-change-functions 4477 beg end) 4478 (replace-buffer-contents temp) 4479 (run-hook-with-args 'after-change-functions 4480 beg (+ beg (length new-text)) 4481 length))))))))) 4482 4483 (defun lsp--to-yasnippet-snippet (snippet) 4484 "Convert LSP SNIPPET to yasnippet snippet." 4485 ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it. 4486 (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`"))) 4487 (rx "\\" (backref 1)) 4488 snippet 4489 nil nil 1)) 4490 4491 (defvar-local lsp-enable-relative-indentation nil 4492 "Enable relative indentation when insert texts, snippets ... 4493 from language server.") 4494 4495 (defun lsp--expand-snippet (snippet &optional start end expand-env) 4496 "Wrapper of `yas-expand-snippet' with all of it arguments. 4497 The snippet will be convert to LSP style and indent according to 4498 LSP server result." 4499 (require 'yasnippet nil t) 4500 (let* ((inhibit-field-text-motion t) 4501 (yas-wrap-around-region nil) 4502 (yas-indent-line 'none) 4503 (yas-also-auto-indent-first-line nil)) 4504 (yas-expand-snippet 4505 (lsp--to-yasnippet-snippet snippet) 4506 start end expand-env))) 4507 4508 (defun lsp--indent-lines (start end &optional insert-text-mode?) 4509 "Indent from START to END based on INSERT-TEXT-MODE? value. 4510 - When INSERT-TEXT-MODE? is provided 4511 - if it's `lsp/insert-text-mode-as-it', do no editor indentation. 4512 - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading 4513 whitespaces to match the line where text is inserted. 4514 - When it's not provided, using `indent-line-function' for each line." 4515 (save-excursion 4516 (goto-char end) 4517 (let* ((end-line (line-number-at-pos)) 4518 (offset (save-excursion 4519 (goto-char start) 4520 (current-indentation))) 4521 (indent-line-function 4522 (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it) 4523 #'ignore) 4524 ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation) 4525 lsp-enable-relative-indentation 4526 ;; Indenting snippets is extremely slow in `org-mode' buffers 4527 ;; since it has to calculate indentation based on SRC block 4528 ;; position. Thus we use relative indentation as default. 4529 (derived-mode-p 'org-mode)) 4530 (lambda () (save-excursion 4531 (beginning-of-line) 4532 (indent-to-column offset)))) 4533 (t indent-line-function)))) 4534 (goto-char start) 4535 (forward-line) 4536 (while (and (not (eobp)) 4537 (<= (line-number-at-pos) end-line)) 4538 (funcall indent-line-function) 4539 (forward-line))))) 4540 4541 (defun lsp--apply-text-edits (edits &optional operation) 4542 "Apply the EDITS described in the TextEdit[] object. 4543 OPERATION is symbol representing the source of this text edit." 4544 (unless (seq-empty-p edits) 4545 (atomic-change-group 4546 (run-hooks 'lsp-before-apply-edits-hook) 4547 (let* ((change-group (prepare-change-group)) 4548 (howmany (length edits)) 4549 (message (format "Applying %s edits to `%s' ..." howmany (current-buffer))) 4550 (_ (lsp--info message)) 4551 (reporter (make-progress-reporter message 0 howmany)) 4552 (done 0) 4553 (apply-edit (if (not lsp--virtual-buffer) 4554 #'lsp--apply-text-edit-replace-buffer-contents 4555 #'lsp--apply-text-edit))) 4556 (unwind-protect 4557 (->> edits 4558 ;; We sort text edits so as to apply edits that modify latter 4559 ;; parts of the document first. Furthermore, because the LSP 4560 ;; spec dictates that: "If multiple inserts have the same 4561 ;; position, the order in the array defines which edit to 4562 ;; apply first." We reverse the initial list and sort stably 4563 ;; to make sure the order among edits with the same position 4564 ;; is preserved. 4565 (nreverse) 4566 (seq-sort #'lsp--text-edit-sort-predicate) 4567 (mapc (lambda (edit) 4568 (progress-reporter-update reporter (cl-incf done)) 4569 (funcall apply-edit edit) 4570 (when (lsp:snippet-text-edit-insert-text-format? edit) 4571 (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start) 4572 :insert-text-format? :new-text) edit) 4573 (when (eq insert-text-format? lsp/insert-text-format-snippet) 4574 ;; No `save-excursion' needed since expand snippet will change point anyway 4575 (goto-char (+ start (length new-text))) 4576 (lsp--indent-lines start (point)) 4577 (lsp--expand-snippet new-text start (point))))) 4578 (run-hook-with-args 'lsp-after-apply-edits-hook operation)))) 4579 (undo-amalgamate-change-group change-group) 4580 (progress-reporter-done reporter)))))) 4581 4582 (defun lsp--create-apply-text-edits-handlers () 4583 "Create (handler cleanup-fn) for applying text edits in async request. 4584 Only works when mode is `tick or `alive." 4585 (let* (first-edited 4586 (func (lambda (start &rest _) 4587 (setq first-edited (if first-edited 4588 (min start first-edited) 4589 start))))) 4590 (add-hook 'before-change-functions func nil t) 4591 (list 4592 (lambda (edits) 4593 (if (and first-edited 4594 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end))) 4595 ;; Text edit region is overlapped 4596 (> end first-edited)) 4597 edits)) 4598 (lsp--warn "TextEdits will not be applied since document has been modified before of them.") 4599 (lsp--apply-text-edits edits 'completion-cleanup))) 4600 (lambda () 4601 (remove-hook 'before-change-functions func t))))) 4602 4603 (defun lsp--capability (cap &optional capabilities) 4604 "Get the value of capability CAP. If CAPABILITIES is non-nil, use them instead." 4605 (when (stringp cap) 4606 (setq cap (intern (concat ":" cap)))) 4607 4608 (lsp-get (or capabilities 4609 (lsp--server-capabilities)) 4610 cap)) 4611 4612 (defun lsp--registered-capability (method) 4613 "Check whether there is workspace providing METHOD." 4614 (->> (lsp-workspaces) 4615 (--keep (seq-find (lambda (reg) 4616 (equal (lsp--registered-capability-method reg) method)) 4617 (lsp--workspace-registered-server-capabilities it))) 4618 cl-first)) 4619 4620 (defun lsp--capability-for-method (method) 4621 "Get the value of capability for METHOD." 4622 (-let* ((reqs (cdr (assoc method lsp-method-requirements))) 4623 ((&plist :capability) reqs)) 4624 (or (and capability (lsp--capability capability)) 4625 (-some-> (lsp--registered-capability method) 4626 (lsp--registered-capability-options))))) 4627 4628 (defvar-local lsp--before-change-vals nil 4629 "Store the positions from the `lsp-before-change' function call, for 4630 validation and use in the `lsp-on-change' function.") 4631 4632 (defun lsp--text-document-content-change-event (start end length) 4633 "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH." 4634 ;; So (47 54 0) means add 7 chars starting at pos 47 4635 ;; must become 4636 ;; {"range":{"start":{"line":5,"character":6} 4637 ;; ,"end" :{"line":5,"character":6}} 4638 ;; ,"rangeLength":0 4639 ;; ,"text":"\nbb = 5"} 4640 ;; 4641 ;; And (47 47 7) means delete 7 chars starting at pos 47 4642 ;; must become 4643 ;; {"range":{"start":{"line":6,"character":0} 4644 ;; ,"end" :{"line":7,"character":0}} 4645 ;; ,"rangeLength":7 4646 ;; ,"text":""} 4647 ;; 4648 ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with 4649 ;; 13 chars. So it must become 4650 ;; {"range":{"start":{"line":5,"character":8} 4651 ;; ,"end" :{"line":5,"character":11}} 4652 ;; ,"rangeLength":3 4653 ;; ,"text":"new-chars-xxx"} 4654 ;; 4655 4656 ;; Adding text: 4657 ;; lsp-before-change:(start,end)=(33,33) 4658 ;; lsp-on-change:(start,end,length)=(33,34,0) 4659 ;; 4660 ;; Changing text: 4661 ;; lsp-before-change:(start,end)=(208,211) 4662 ;; lsp-on-change:(start,end,length)=(208,221,3) 4663 ;; 4664 ;; Deleting text: 4665 ;; lsp-before-change:(start,end)=(19,27) 4666 ;; lsp-on-change:(start,end,length)=(19,19,8) 4667 (if (zerop length) 4668 ;; Adding something only, work from start only 4669 `( :range ,(lsp--range 4670 (lsp--point-to-position start) 4671 (lsp--point-to-position start)) 4672 :rangeLength 0 4673 :text ,(buffer-substring-no-properties start end)) 4674 4675 (if (eq start end) 4676 ;; Deleting something only 4677 (if (lsp--bracketed-change-p start length) 4678 ;; The before-change value is bracketed, use it 4679 `( :range ,(lsp--range 4680 (lsp--point-to-position start) 4681 (plist-get lsp--before-change-vals :end-pos)) 4682 :rangeLength ,length 4683 :text "") 4684 ;; If the change is not bracketed, send a full change event instead. 4685 (lsp--full-change-event)) 4686 4687 ;; Deleting some things, adding others 4688 (if (lsp--bracketed-change-p start length) 4689 ;; The before-change value is valid, use it 4690 `( :range ,(lsp--range 4691 (lsp--point-to-position start) 4692 (plist-get lsp--before-change-vals :end-pos)) 4693 :rangeLength ,length 4694 :text ,(buffer-substring-no-properties start end)) 4695 (lsp--full-change-event))))) 4696 4697 (defun lsp--bracketed-change-p (start length) 4698 "If the before and after positions are the same, and the length 4699 is the size of the start range, we are probably good." 4700 (-let [(&plist :end before-end :start before-start) lsp--before-change-vals] 4701 (and (eq start before-start) 4702 (eq length (- before-end before-start))))) 4703 4704 (defun lsp--full-change-event () 4705 `(:text ,(lsp--buffer-content))) 4706 4707 (defun lsp-before-change (start end) 4708 "Executed before a file is changed. 4709 Added to `before-change-functions'." 4710 ;; Note: 4711 ;; 4712 ;; This variable holds a list of functions to call when Emacs is about to 4713 ;; modify a buffer. Each function gets two arguments, the beginning and end of 4714 ;; the region that is about to change, represented as integers. The buffer 4715 ;; that is about to change is always the current buffer when the function is 4716 ;; called. 4717 ;; 4718 ;; WARNING: 4719 ;; 4720 ;; Do not expect the before-change hooks and the after-change hooks be called 4721 ;; in balanced pairs around each buffer change. Also don't expect the 4722 ;; before-change hooks to be called for every chunk of text Emacs is about to 4723 ;; delete. These hooks are provided on the assumption that Lisp programs will 4724 ;; use either before- or the after-change hooks, but not both, and the 4725 ;; boundaries of the region where the changes happen might include more than 4726 ;; just the actual changed text, or even lump together several changes done 4727 ;; piecemeal. 4728 (save-match-data 4729 (lsp-save-restriction-and-excursion 4730 (setq lsp--before-change-vals 4731 (list :start start 4732 :end end 4733 :end-pos (lsp--point-to-position end)))))) 4734 4735 (defun lsp--flush-delayed-changes () 4736 (let ((inhibit-quit t)) 4737 (when lsp--delay-timer 4738 (cancel-timer lsp--delay-timer)) 4739 (mapc (-lambda ((workspace buffer document change)) 4740 (with-current-buffer buffer 4741 (with-lsp-workspace workspace 4742 (lsp-notify "textDocument/didChange" 4743 (list :textDocument document 4744 :contentChanges (vector change)))))) 4745 (prog1 (nreverse lsp--delayed-requests) 4746 (setq lsp--delayed-requests nil))))) 4747 4748 (defun lsp--workspace-sync-method (workspace) 4749 (let ((sync (-> workspace 4750 (lsp--workspace-server-capabilities) 4751 (lsp:server-capabilities-text-document-sync?)))) 4752 (if (lsp-text-document-sync-options? sync) 4753 (lsp:text-document-sync-options-change? sync) 4754 sync))) 4755 4756 (defun lsp-on-change (start end length &optional content-change-event-fn) 4757 "Executed when a file is changed. 4758 Added to `after-change-functions'." 4759 ;; Note: 4760 ;; 4761 ;; Each function receives three arguments: the beginning and end of the region 4762 ;; just changed, and the length of the text that existed before the change. 4763 ;; All three arguments are integers. The buffer that has been changed is 4764 ;; always the current buffer when the function is called. 4765 ;; 4766 ;; The length of the old text is the difference between the buffer positions 4767 ;; before and after that text as it was before the change. As for the 4768 ;; changed text, its length is simply the difference between the first two 4769 ;; arguments. 4770 ;; 4771 ;; So (47 54 0) means add 7 chars starting at pos 47 4772 ;; So (47 47 7) means delete 7 chars starting at pos 47 4773 (save-match-data 4774 (let ((inhibit-quit t) 4775 ;; make sure that `lsp-on-change' is called in multi-workspace context 4776 ;; see #2901 4777 lsp--cur-workspace) 4778 ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done 4779 ;; by auto-revert-mode) will cause this handler to get called with a nil 4780 ;; buffer-file-name. We need the buffer-file-name to send notifications; 4781 ;; so we skip handling revert-buffer-caused changes and instead handle 4782 ;; reverts separately in lsp-on-revert 4783 (when (not revert-buffer-in-progress-p) 4784 (cl-incf lsp--cur-version) 4785 (mapc 4786 (lambda (workspace) 4787 (pcase (or lsp-document-sync-method 4788 (lsp--workspace-sync-method workspace)) 4789 (1 4790 (if lsp-debounce-full-sync-notifications 4791 (setq lsp--delayed-requests 4792 (->> lsp--delayed-requests 4793 (-remove (-lambda ((_ buffer)) 4794 (equal (current-buffer) buffer))) 4795 (cons (list workspace 4796 (current-buffer) 4797 (lsp--versioned-text-document-identifier) 4798 (lsp--full-change-event))))) 4799 (with-lsp-workspace workspace 4800 (lsp-notify "textDocument/didChange" 4801 (list :contentChanges (vector (lsp--full-change-event)) 4802 :textDocument (lsp--versioned-text-document-identifier)))))) 4803 (2 4804 (with-lsp-workspace workspace 4805 (lsp-notify 4806 "textDocument/didChange" 4807 (list :textDocument (lsp--versioned-text-document-identifier) 4808 :contentChanges (vector 4809 (if content-change-event-fn 4810 (funcall content-change-event-fn start end length) 4811 (lsp--text-document-content-change-event 4812 start end length))))))))) 4813 (lsp-workspaces)) 4814 (when lsp--delay-timer (cancel-timer lsp--delay-timer)) 4815 (setq lsp--delay-timer (run-with-idle-timer 4816 lsp-debounce-full-sync-notifications-interval 4817 nil 4818 #'lsp--flush-delayed-changes)) 4819 ;; force cleanup overlays after each change 4820 (lsp--remove-overlays 'lsp-highlight) 4821 (lsp--after-change (current-buffer)) 4822 (setq lsp--signature-last-index nil 4823 lsp--signature-last nil) 4824 ;; cleanup diagnostics 4825 (when lsp-diagnostic-clean-after-change 4826 (lsp-foreach-workspace 4827 (-let [diagnostics (lsp--workspace-diagnostics lsp--cur-workspace)] 4828 (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics)))))))) 4829 4830 4831 4832 ;; facilities for on change hooks. We do not want to make lsp calls on each 4833 ;; change event so we add debounce to avoid flooding the server with events. 4834 ;; Additionally, we want to have a mechanism for stopping the server calls in 4835 ;; particular cases like, e. g. when performing completion. 4836 4837 (defvar lsp-inhibit-lsp-hooks nil 4838 "Flag to control.") 4839 4840 (defcustom lsp-on-change-hook nil 4841 "Hooks to run when buffer has changed." 4842 :type 'hook 4843 :group 'lsp-mode) 4844 4845 (defcustom lsp-idle-delay 0.500 4846 "Debounce interval for `after-change-functions'." 4847 :type 'number 4848 :group 'lsp-mode) 4849 4850 (defcustom lsp-on-idle-hook nil 4851 "Hooks to run after `lsp-idle-delay'." 4852 :type 'hook 4853 :group 'lsp-mode) 4854 4855 (defun lsp--idle-reschedule (buffer) 4856 (when lsp--on-idle-timer 4857 (cancel-timer lsp--on-idle-timer)) 4858 4859 (setq lsp--on-idle-timer (run-with-idle-timer 4860 lsp-idle-delay 4861 nil 4862 #'lsp--on-idle 4863 buffer))) 4864 4865 (defun lsp--post-command () 4866 (lsp--cleanup-highlights-if-needed) 4867 (lsp--idle-reschedule (current-buffer))) 4868 4869 (defun lsp--on-idle (buffer) 4870 "Start post command loop." 4871 (when (and (buffer-live-p buffer) 4872 (equal buffer (current-buffer)) 4873 (not lsp-inhibit-lsp-hooks) 4874 lsp-managed-mode) 4875 (run-hooks 'lsp-on-idle-hook))) 4876 4877 (defun lsp--on-change-debounce (buffer) 4878 (when (and (buffer-live-p buffer) 4879 (equal buffer (current-buffer)) 4880 (not lsp-inhibit-lsp-hooks) 4881 lsp-managed-mode) 4882 (run-hooks 'lsp-on-change-hook))) 4883 4884 (defun lsp--after-change (buffer) 4885 (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled) 4886 (lsp--semantic-tokens-refresh-if-enabled buffer)) 4887 (when lsp--on-change-timer 4888 (cancel-timer lsp--on-change-timer)) 4889 (setq lsp--on-change-timer (run-with-idle-timer 4890 lsp-idle-delay 4891 nil 4892 #'lsp--on-change-debounce 4893 buffer)) 4894 (lsp--idle-reschedule buffer)) 4895 4896 4897 (defcustom lsp-trim-trailing-whitespace t 4898 "Trim trailing whitespace on a line." 4899 :group 'lsp-mode 4900 :type 'boolean) 4901 4902 (defcustom lsp-insert-final-newline t 4903 "Insert a newline character at the end of the file if one does not exist." 4904 :group 'lsp-mode 4905 :type 'boolean) 4906 4907 (defcustom lsp-trim-final-newlines t 4908 "Trim all newlines after the final newline at the end of the file." 4909 :group 'lsp-mode 4910 :type 'boolean) 4911 4912 4913 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters) 4914 "Self insert handling. 4915 Applies on type formatting." 4916 (let ((ch last-command-event)) 4917 (when (or (eq (string-to-char first-trigger-characters) ch) 4918 (cl-find ch more-trigger-characters :key #'string-to-char)) 4919 (lsp-request-async "textDocument/onTypeFormatting" 4920 (lsp-make-document-on-type-formatting-params 4921 :text-document (lsp--text-document-identifier) 4922 :options (lsp-make-formatting-options 4923 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 4924 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 4925 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 4926 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 4927 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)) 4928 :ch (char-to-string ch) 4929 :position (lsp--cur-position)) 4930 (lambda (data) (lsp--apply-text-edits data 'format)) 4931 :mode 'tick)))) 4932 4933 4934 ;; links 4935 (defun lsp--document-links () 4936 (when (lsp-feature? "textDocument/documentLink") 4937 (lsp-request-async 4938 "textDocument/documentLink" 4939 `(:textDocument ,(lsp--text-document-identifier)) 4940 (lambda (links) 4941 (lsp--remove-overlays 'lsp-link) 4942 (seq-do 4943 (-lambda ((link &as &DocumentLink :range (&Range :start :end))) 4944 (-doto (make-button (lsp--position-to-point start) 4945 (lsp--position-to-point end) 4946 'action (lsp--document-link-keymap link) 4947 'keymap (let ((map (make-sparse-keymap))) 4948 (define-key map [M-return] 'push-button) 4949 (define-key map [mouse-2] 'push-button) 4950 map) 4951 'help-echo "mouse-2, M-RET: Visit this link") 4952 (overlay-put 'lsp-link t))) 4953 links)) 4954 :mode 'unchanged))) 4955 4956 (defun lsp--document-link-handle-target (url) 4957 (let* ((parsed-url (url-generic-parse-url (url-unhex-string url))) 4958 (type (url-type parsed-url))) 4959 (pcase type 4960 ("file" 4961 (xref-push-marker-stack) 4962 (find-file (lsp--uri-to-path url)) 4963 (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url)) 4964 (goto-char (lsp--position-to-point 4965 (lsp-make-position :character (1- (string-to-number column)) 4966 :line (1- (string-to-number line))))))) 4967 ((or "http" "https") (browse-url url)) 4968 (type (if-let ((handler (lsp--get-uri-handler type))) 4969 (funcall handler url) 4970 (signal 'lsp-file-scheme-not-supported (list url))))))) 4971 4972 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?)) 4973 (if target? 4974 (lambda (_) 4975 (interactive) 4976 (lsp--document-link-handle-target target?)) 4977 (lambda (_) 4978 (interactive) 4979 (when (lsp:document-link-registration-options-resolve-provider? 4980 (lsp--capability-for-method "textDocument/documentLink")) 4981 (lsp-request-async 4982 "documentLink/resolve" 4983 link 4984 (-lambda ((&DocumentLink :target?)) 4985 (lsp--document-link-handle-target target?))))))) 4986 4987 4988 4989 (defcustom lsp-warn-no-matched-clients t 4990 "Whether to show messages when there are no supported clients." 4991 :group 'lsp-mode 4992 :type 'boolean) 4993 4994 (defun lsp-buffer-language--configured-id () 4995 "Return nil when not registered." 4996 (->> lsp-language-id-configuration 4997 (-first 4998 (-lambda ((mode-or-pattern . language)) 4999 (cond 5000 ((and (stringp mode-or-pattern) 5001 (s-matches? mode-or-pattern (buffer-file-name))) 5002 language) 5003 ((eq mode-or-pattern major-mode) language)))) 5004 cl-rest)) 5005 5006 (defvar-local lsp--buffer-language nil 5007 "Locally cached returned value of `lsp-buffer-language'.") 5008 5009 (defun lsp-buffer-language () 5010 "Get language corresponding current buffer." 5011 (or lsp--buffer-language 5012 (let* ((configured-language (lsp-buffer-language--configured-id))) 5013 (setq lsp--buffer-language 5014 (or configured-language 5015 ;; ensure non-nil 5016 (string-remove-suffix "-mode" (symbol-name major-mode)))) 5017 (when (and lsp-warn-no-matched-clients 5018 (null configured-language)) 5019 (lsp-warn "Unable to calculate the languageId for buffer `%s'. \ 5020 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s" 5021 (buffer-name) 5022 major-mode)) 5023 lsp--buffer-language))) 5024 5025 (defun lsp-activate-on (&rest languages) 5026 "Returns language activation function. 5027 The function will return t when the `lsp-buffer-language' returns 5028 one of the LANGUAGES." 5029 (lambda (_file-name _mode) 5030 (-contains? languages (lsp-buffer-language)))) 5031 5032 (defun lsp-workspace-root (&optional path) 5033 "Find the workspace root for the current file or PATH." 5034 (-when-let* ((file-name (or path (buffer-file-name))) 5035 (file-name (lsp-f-canonical file-name))) 5036 (->> (lsp-session) 5037 (lsp-session-folders) 5038 (--filter (and (lsp--files-same-host it file-name) 5039 (or (lsp-f-ancestor-of? it file-name) 5040 (equal it file-name)))) 5041 (--max-by (> (length it) (length other)))))) 5042 5043 (defun lsp-on-revert () 5044 "Executed when a file is reverted. 5045 Added to `after-revert-hook'." 5046 (let ((n (buffer-size)) 5047 (revert-buffer-in-progress-p nil)) 5048 (lsp-on-change 0 n n))) 5049 5050 (defun lsp--text-document-did-close (&optional keep-workspace-alive) 5051 "Executed when the file is closed, added to `kill-buffer-hook'. 5052 5053 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace 5054 if it's closing the last buffer in the workspace." 5055 (lsp-foreach-workspace 5056 (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 5057 (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S" 5058 (lsp-notify "textDocument/didClose" 5059 `(:textDocument ,(lsp--text-document-identifier)))) 5060 (when (and (not lsp-keep-workspace-alive) 5061 (not keep-workspace-alive) 5062 (not (lsp--workspace-buffers lsp--cur-workspace))) 5063 (lsp--shutdown-workspace)))) 5064 5065 (defun lsp--will-save-text-document-params (reason) 5066 (list :textDocument (lsp--text-document-identifier) 5067 :reason reason)) 5068 5069 (defun lsp--before-save () 5070 "Before save handler." 5071 (with-demoted-errors "Error in ‘lsp--before-save’: %S" 5072 (let ((params (lsp--will-save-text-document-params 1))) 5073 (when (lsp--send-will-save-p) 5074 (lsp-notify "textDocument/willSave" params)) 5075 (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits) 5076 (let ((lsp-response-timeout 0.1)) 5077 (condition-case nil 5078 (lsp--apply-text-edits 5079 (lsp-request "textDocument/willSaveWaitUntil" 5080 params) 5081 'before-save) 5082 (error))))))) 5083 5084 (defun lsp--on-auto-save () 5085 "Handler for auto-save." 5086 (when (lsp--send-will-save-p) 5087 (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S" 5088 (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2))))) 5089 5090 (defun lsp--text-document-did-save () 5091 "Executed when the file is closed, added to `after-save-hook''." 5092 (when (lsp--send-did-save-p) 5093 (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’" 5094 (lsp-notify "textDocument/didSave" 5095 `( :textDocument ,(lsp--versioned-text-document-identifier) 5096 ,@(when (lsp--save-include-text-p) 5097 (list :text (lsp--buffer-content)))))))) 5098 5099 (defun lsp--text-document-position-params (&optional identifier position) 5100 "Make TextDocumentPositionParams for the current point in the current document. 5101 If IDENTIFIER and POSITION are non-nil, they will be used as the document 5102 identifier and the position respectively." 5103 (list :textDocument (or identifier (lsp--text-document-identifier)) 5104 :position (or position (lsp--cur-position)))) 5105 5106 (defun lsp--get-buffer-diagnostics () 5107 "Return buffer diagnostics." 5108 (gethash (or 5109 (plist-get lsp--virtual-buffer :buffer-file-name) 5110 (lsp--fix-path-casing (buffer-file-name))) 5111 (lsp-diagnostics t))) 5112 5113 (defun lsp-cur-line-diagnostics () 5114 "Return any diagnostics that apply to the current line." 5115 (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)] 5116 (cl-coerce (-filter 5117 (-lambda ((&Diagnostic :range (&Range :start (&Position :line)))) 5118 (and (>= line start) (<= line end))) 5119 (lsp--get-buffer-diagnostics)) 5120 'vector))) 5121 5122 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end) 5123 (right &as &Range :start right-start :end right-end)) 5124 (or (lsp-point-in-range? right-start left) 5125 (lsp-point-in-range? right-end left) 5126 (lsp-point-in-range? left-start right) 5127 (lsp-point-in-range? left-end right))) 5128 5129 (defun lsp-make-position-1 (position) 5130 (lsp-make-position :line (plist-get position :line) 5131 :character (plist-get position :character))) 5132 5133 (defun lsp-cur-possition-diagnostics () 5134 "Return any diagnostics that apply to the current line." 5135 (-let* ((start (if (use-region-p) (region-beginning) (point))) 5136 (end (if (use-region-p) (region-end) (point))) 5137 (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start)) 5138 :end (lsp-make-position-1 (lsp-point-to-position end))))) 5139 (->> (lsp--get-buffer-diagnostics) 5140 (-filter 5141 (-lambda ((&Diagnostic :range)) 5142 (lsp-range-overlapping? range current-range))) 5143 (apply 'vector)))) 5144 5145 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics) 5146 5147 (defun lsp--extract-line-from-buffer (pos) 5148 "Return the line pointed to by POS (a Position object) in the current buffer." 5149 (let* ((point (lsp--position-to-point pos)) 5150 (inhibit-field-text-motion t)) 5151 (save-excursion 5152 (goto-char point) 5153 (buffer-substring (line-beginning-position) (line-end-position))))) 5154 5155 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line) 5156 :end (end &as &Position :character end-char))) 5157 "Return a xref-item from a RANGE in FILENAME." 5158 (let* ((line (lsp--extract-line-from-buffer start)) 5159 (len (length line))) 5160 (add-face-text-property (max (min start-char len) 0) 5161 (max (min end-char len) 0) 5162 'xref-match t line) 5163 ;; LINE is nil when FILENAME is not being current visited by any buffer. 5164 (xref-make-match (or line filename) 5165 (xref-make-file-location 5166 filename 5167 (lsp-translate-line (1+ start-line)) 5168 (lsp-translate-column start-char)) 5169 (- end-char start-char)))) 5170 5171 (defun lsp--location-uri (loc) 5172 (if (lsp-location? loc) 5173 (lsp:location-uri loc) 5174 (lsp:location-link-target-uri loc))) 5175 5176 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start))) 5177 "Go to location." 5178 (let ((path (lsp--uri-to-path uri))) 5179 (if (f-exists? path) 5180 (with-current-buffer (find-file path) 5181 (goto-char (lsp--position-to-point start))) 5182 (error "There is no file %s" path)))) 5183 5184 (defun lsp--location-range (loc) 5185 (if (lsp-location? loc) 5186 (lsp:location-range loc) 5187 (lsp:location-link-target-selection-range loc))) 5188 5189 (defun lsp--locations-to-xref-items (locations) 5190 "Return a list of `xref-item' given LOCATIONS, which can be of 5191 type Location, LocationLink, Location[] or LocationLink[]." 5192 (setq locations 5193 (pcase locations 5194 ((seq (or (Location) 5195 (LocationLink))) 5196 (append locations nil)) 5197 ((or (Location) 5198 (LocationLink)) 5199 (list locations)))) 5200 5201 (cl-labels ((get-xrefs-in-file 5202 (file-locs) 5203 (-let [(filename . matches) file-locs] 5204 (condition-case err 5205 (let ((visiting (find-buffer-visiting filename)) 5206 (fn (lambda (loc) 5207 (lsp-with-filename filename 5208 (lsp--xref-make-item filename 5209 (lsp--location-range loc)))))) 5210 (if visiting 5211 (with-current-buffer visiting 5212 (seq-map fn matches)) 5213 (when (file-readable-p filename) 5214 (with-temp-buffer 5215 (insert-file-contents-literally filename) 5216 (seq-map fn matches))))) 5217 (error (lsp-warn "Failed to process xref entry for filename '%s': %s" 5218 filename (error-message-string err))) 5219 (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s" 5220 filename (error-message-string err))))))) 5221 5222 (->> locations 5223 (seq-sort #'lsp--location-before-p) 5224 (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri)) 5225 (seq-map #'get-xrefs-in-file) 5226 (apply #'nconc)))) 5227 5228 (defun lsp--location-before-p (left right) 5229 "Sort first by file, then by line, then by column." 5230 (let ((left-uri (lsp--location-uri left)) 5231 (right-uri (lsp--location-uri right))) 5232 (if (not (string= left-uri right-uri)) 5233 (string< left-uri right-uri) 5234 (-let (((&Range :start left-start) (lsp--location-range left)) 5235 ((&Range :start right-start) (lsp--location-range right))) 5236 (lsp--position-compare right-start left-start))))) 5237 5238 (defun lsp--make-reference-params (&optional td-position exclude-declaration) 5239 "Make a ReferenceParam object. 5240 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead. 5241 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." 5242 (let ((json-false :json-false)) 5243 (plist-put (or td-position (lsp--text-document-position-params)) 5244 :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration)))))) 5245 5246 (defun lsp--cancel-request (id) 5247 "Cancel request with ID in all workspaces." 5248 (lsp-foreach-workspace 5249 (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id)) 5250 (lsp-notify "$/cancelRequest" `(:id ,id)))) 5251 5252 (defvar-local lsp--hover-saved-bounds nil) 5253 5254 (defun lsp-eldoc-function (cb &rest _ignored) 5255 "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')." 5256 (if (and lsp--hover-saved-bounds 5257 (lsp--point-in-bounds-p lsp--hover-saved-bounds)) 5258 lsp--eldoc-saved-message 5259 (setq lsp--hover-saved-bounds nil 5260 lsp--eldoc-saved-message nil) 5261 (if (looking-at-p "[[:space:]\n]") 5262 (setq lsp--eldoc-saved-message nil) ; And returns nil. 5263 (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover")) 5264 (lsp-request-async 5265 "textDocument/hover" 5266 (lsp--text-document-position-params) 5267 (-lambda ((hover &as &Hover? :range? :contents)) 5268 (setq lsp--hover-saved-bounds (when range? 5269 (lsp--range-to-region range?))) 5270 (funcall cb (setq lsp--eldoc-saved-message 5271 (when contents 5272 (lsp--render-on-hover-content 5273 contents 5274 lsp-eldoc-render-all))))) 5275 :error-handler #'ignore 5276 :mode 'tick 5277 :cancel-token :eldoc-hover))))) 5278 5279 (defun lsp--point-on-highlight? () 5280 (-some? (lambda (overlay) 5281 (overlay-get overlay 'lsp-highlight)) 5282 (overlays-at (point)))) 5283 5284 (defun lsp--cleanup-highlights-if-needed () 5285 (when (and lsp-enable-symbol-highlighting 5286 lsp--have-document-highlights 5287 (not (lsp--point-on-highlight?))) 5288 (lsp--remove-overlays 'lsp-highlight) 5289 (setq lsp--have-document-highlights nil) 5290 (lsp-cancel-request-by-token :highlights))) 5291 5292 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil 5293 "The bounds of the symbol from which `lsp--document-highlight' 5294 most recently requested highlights.") 5295 5296 (defun lsp--document-highlight () 5297 (when (lsp-feature? "textDocument/documentHighlight") 5298 (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol))) 5299 (unless (or (looking-at-p "[[:space:]\n]") 5300 (not lsp-enable-symbol-highlighting) 5301 (and lsp--have-document-highlights 5302 curr-sym-bounds 5303 (equal curr-sym-bounds 5304 lsp--symbol-bounds-of-last-highlight-invocation))) 5305 (setq lsp--symbol-bounds-of-last-highlight-invocation 5306 curr-sym-bounds) 5307 (lsp-request-async "textDocument/documentHighlight" 5308 (lsp--text-document-position-params) 5309 #'lsp--document-highlight-callback 5310 :mode 'tick 5311 :cancel-token :highlights))))) 5312 5313 (defun lsp--help-open-link (&rest _) 5314 "Open markdown link at point via mouse or keyboard." 5315 (interactive "P") 5316 (let ((buffer-list-update-hook nil)) 5317 (-let [(buffer point) (if-let* ((valid (and (listp last-input-event) 5318 (eq (car last-input-event) 'mouse-2))) 5319 (event (cadr last-input-event)) 5320 (win (posn-window event)) 5321 (buffer (window-buffer win))) 5322 `(,buffer ,(posn-point event)) 5323 `(,(current-buffer) ,(point)))] 5324 (with-current-buffer buffer 5325 (when-let* ((face (get-text-property point 'face)) 5326 (url (or (and (eq face 'markdown-link-face) 5327 (get-text-property point 'help-echo)) 5328 (and (memq face '(markdown-url-face markdown-plain-url-face)) 5329 (nth 3 (markdown-link-at-pos point)))))) 5330 (lsp--document-link-handle-target url)))))) 5331 5332 (defvar lsp-help-mode-map 5333 (-doto (make-sparse-keymap) 5334 (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link)) 5335 "Keymap for `lsp-help-mode'.") 5336 5337 (define-derived-mode lsp-help-mode help-mode "LspHelp" 5338 "Major mode for displaying lsp help.") 5339 5340 (defun lsp-describe-thing-at-point () 5341 "Display the type signature and documentation of the thing at point." 5342 (interactive) 5343 (let ((contents (-some->> (lsp--text-document-position-params) 5344 (lsp--make-request "textDocument/hover") 5345 (lsp--send-request) 5346 (lsp:hover-contents)))) 5347 (if (and contents (not (equal contents ""))) 5348 (let ((lsp-help-buf-name "*lsp-help*")) 5349 (with-current-buffer (get-buffer-create lsp-help-buf-name) 5350 (delay-mode-hooks 5351 (lsp-help-mode) 5352 (with-help-window lsp-help-buf-name 5353 (insert (string-trim-right (lsp--render-on-hover-content contents t))))) 5354 (run-mode-hooks))) 5355 (lsp--info "No content at point.")))) 5356 5357 (defun lsp--point-in-bounds-p (bounds) 5358 "Return whether the current point is within BOUNDS." 5359 (and (<= (car bounds) (point)) (< (point) (cdr bounds)))) 5360 5361 (defun lsp-get-renderer (language) 5362 "Get renderer for LANGUAGE." 5363 (lambda (str) 5364 (lsp--render-string str language))) 5365 5366 (defun lsp--setup-markdown (mode) 5367 "Setup the ‘markdown-mode’ in the frame. 5368 MODE is the mode used in the parent frame." 5369 (make-local-variable 'markdown-code-lang-modes) 5370 (dolist (mark (alist-get mode lsp-custom-markup-modes)) 5371 (add-to-list 'markdown-code-lang-modes (cons mark mode))) 5372 (setq-local markdown-fontify-code-blocks-natively t) 5373 (setq-local markdown-fontify-code-block-default-mode mode) 5374 (setq-local markdown-hide-markup t) 5375 5376 ;; Render some common HTML entities. 5377 ;; This should really happen in markdown-mode instead, 5378 ;; but it doesn't, so we do it here for now. 5379 (setq prettify-symbols-alist 5380 (cl-loop for i from 0 to 255 5381 collect (cons (format "&#x%02X;" i) i))) 5382 (push '("<" . ?<) prettify-symbols-alist) 5383 (push '(">" . ?>) prettify-symbols-alist) 5384 (push '("&" . ?&) prettify-symbols-alist) 5385 (push '(" " . ? ) prettify-symbols-alist) 5386 (setq prettify-symbols-compose-predicate 5387 (lambda (_start _end _match) t)) 5388 (prettify-symbols-mode 1)) 5389 5390 (defvar lsp-help-link-keymap 5391 (let ((map (make-sparse-keymap))) 5392 (define-key map [mouse-2] #'lsp--help-open-link) 5393 (define-key map "\r" #'lsp--help-open-link) 5394 map) 5395 "Keymap active on links in *lsp-help* mode.") 5396 5397 (defun lsp--fix-markdown-links () 5398 (let ((inhibit-read-only t) 5399 (inhibit-modification-hooks t) 5400 (prop)) 5401 (save-restriction 5402 (goto-char (point-min)) 5403 (while (setq prop (markdown-find-next-prop 'face)) 5404 (let ((end (or (next-single-property-change (car prop) 'face) 5405 (point-max)))) 5406 (when (memq (get-text-property (car prop) 'face) 5407 '(markdown-link-face 5408 markdown-url-face 5409 markdown-plain-url-face)) 5410 (add-text-properties (car prop) end 5411 (list 'button t 5412 'category 'lsp-help-link 5413 'follow-link t 5414 'keymap lsp-help-link-keymap))) 5415 (goto-char end)))))) 5416 5417 (defun lsp--buffer-string-visible () 5418 "Return visible buffer string. 5419 Stolen from `org-copy-visible'." 5420 (let ((temp (generate-new-buffer " *temp*")) 5421 (beg (point-min)) 5422 (end (point-max))) 5423 (while (/= beg end) 5424 (when (get-char-property beg 'invisible) 5425 (setq beg (next-single-char-property-change beg 'invisible nil end))) 5426 (let* ((next (next-single-char-property-change beg 'invisible nil end)) 5427 (substring (buffer-substring beg next))) 5428 (with-current-buffer temp (insert substring)) 5429 ;; (setq result (concat result substring)) 5430 (setq beg next))) 5431 (setq deactivate-mark t) 5432 (prog1 (with-current-buffer temp 5433 (s-chop-suffix "\n" (buffer-string))) 5434 (kill-buffer temp)))) 5435 5436 (defvar lsp-buffer-major-mode nil 5437 "Holds the major mode when fontification function is running. 5438 See #2588") 5439 5440 (defvar view-inhibit-help-message) 5441 5442 (defun lsp--render-markdown () 5443 "Render markdown." 5444 5445 (let ((markdown-enable-math nil)) 5446 (goto-char (point-min)) 5447 (while (re-search-forward 5448 (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/" 5449 "{" "}" "[" "]" "(" ")" 5450 "#" "+" "-" "." "!" "|")))) 5451 nil t) 5452 (replace-match (rx (backref 1)))) 5453 5454 ;; markdown-mode v2.3 does not yet provide gfm-view-mode 5455 (if (fboundp 'gfm-view-mode) 5456 (let ((view-inhibit-help-message t)) 5457 (gfm-view-mode)) 5458 (gfm-mode)) 5459 5460 (lsp--setup-markdown lsp-buffer-major-mode))) 5461 5462 (defvar lsp--display-inline-image-alist 5463 '((lsp--render-markdown 5464 (:regexp 5465 "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)" 5466 :sexp 5467 (create-image 5468 (base64-decode-string 5469 (buffer-substring-no-properties (match-beginning 1) (match-end 1))) 5470 nil t)))) 5471 "Replaced string regexp and function returning image. 5472 Each element should have the form (MODE . (PROPERTY-LIST...)). 5473 MODE (car) is function which is defined in `lsp-language-id-configuration'. 5474 Cdr should be list of PROPERTY-LIST. 5475 5476 Each PROPERTY-LIST should have properties: 5477 :regexp Regexp which determines what string is relpaced to image. 5478 You should also get information of image, by parenthesis constructs. 5479 By default, all matched string is replaced to image, but you can 5480 change index of replaced string by keyword :replaced-index. 5481 5482 :sexp Return image when evaluated. You can use information of regexp 5483 by using (match-beggining N), (match-end N) or (match-substring N). 5484 5485 In addition, each can have property: 5486 :replaced-index Determine index which is used to replace regexp to image. 5487 The value means first argument of `match-beginning' and 5488 `match-end'. If omitted, interpreted as index 0.") 5489 5490 (defcustom lsp-display-inline-image t 5491 "Showing inline image or not." 5492 :group 'lsp-mode 5493 :type 'boolean) 5494 5495 (defcustom lsp-enable-suggest-server-download t 5496 "When non-nil enable server downloading suggestions." 5497 :group 'lsp-mode 5498 :type 'boolean 5499 :package-version '(lsp-mode . "9.0.0")) 5500 5501 (defcustom lsp-auto-register-remote-clients t 5502 "When non-nil register remote when registering the local one." 5503 :group 'lsp-mode 5504 :type 'boolean 5505 :package-version '(lsp-mode . "9.0.0")) 5506 5507 (defun lsp--display-inline-image (mode) 5508 "Add image property if available." 5509 (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist)))) 5510 (when (and (display-images-p) lsp-display-inline-image) 5511 (cl-loop 5512 for plist in plist-list 5513 with regexp with replaced-index 5514 do 5515 (setq regexp (plist-get plist :regexp)) 5516 (setq replaced-index (or (plist-get plist :replaced-index) 0)) 5517 5518 (font-lock-remove-keywords nil (list regexp replaced-index)) 5519 (let ((inhibit-read-only t)) 5520 (save-excursion 5521 (goto-char (point-min)) 5522 (while (re-search-forward regexp nil t) 5523 (set-text-properties 5524 (match-beginning replaced-index) (match-end replaced-index) 5525 nil) 5526 (add-text-properties 5527 (match-beginning replaced-index) (match-end replaced-index) 5528 `(display ,(eval (plist-get plist :sexp))))))))))) 5529 5530 (defun lsp--fontlock-with-mode (str mode) 5531 "Fontlock STR with MODE." 5532 (let ((lsp-buffer-major-mode major-mode)) 5533 (with-temp-buffer 5534 (with-demoted-errors "Error during doc rendering: %s" 5535 (insert str) 5536 (delay-mode-hooks (funcall mode)) 5537 (cl-flet ((window-body-width () lsp-window-body-width)) 5538 ;; This can go wrong in some cases, and the fontification would 5539 ;; not work as expected. 5540 ;; 5541 ;; See #2984 5542 (ignore-errors (font-lock-ensure)) 5543 (lsp--display-inline-image mode) 5544 (when (eq mode 'lsp--render-markdown) 5545 (lsp--fix-markdown-links)))) 5546 (lsp--buffer-string-visible)))) 5547 5548 (defun lsp--render-string (str language) 5549 "Render STR using `major-mode' corresponding to LANGUAGE. 5550 When language is nil render as markup if `markdown-mode' is loaded." 5551 (setq str (s-replace "\r" "" (or str ""))) 5552 (if-let* ((modes (-keep (-lambda ((mode . lang)) 5553 (when (and (equal lang language) (functionp mode)) 5554 mode)) 5555 lsp-language-id-configuration)) 5556 (mode (car (or (member major-mode modes) modes)))) 5557 (lsp--fontlock-with-mode str mode) 5558 str)) 5559 5560 (defun lsp--render-element (content) 5561 "Render CONTENT element." 5562 (let ((inhibit-message t)) 5563 (or 5564 (pcase content 5565 ((MarkedString :value :language) 5566 (lsp--render-string value language)) 5567 ((MarkupContent :value :kind) 5568 (lsp--render-string value kind)) 5569 ;; plain string 5570 ((pred stringp) (lsp--render-string content "markdown")) 5571 ((pred null) "") 5572 (_ (error "Failed to handle %s" content))) 5573 ""))) 5574 5575 (defun lsp--create-unique-string-fn () 5576 (let (elements) 5577 (lambda (element) 5578 (let ((count (cl-count element elements :test #'string=))) 5579 (prog1 (if (zerop count) 5580 element 5581 (format "%s (%s)" element count)) 5582 (push element elements)))))) 5583 5584 (defun lsp--select-action (actions) 5585 "Select an action to execute from ACTIONS." 5586 (cond 5587 ((seq-empty-p actions) (signal 'lsp-no-code-actions nil)) 5588 ((and (eq (seq-length actions) 1) lsp-auto-execute-action) 5589 (lsp-seq-first actions)) 5590 (t (let ((completion-ignore-case t)) 5591 (lsp--completing-read "Select code action: " 5592 (seq-into actions 'list) 5593 (-compose (lsp--create-unique-string-fn) 5594 #'lsp:code-action-title) 5595 nil t))))) 5596 5597 (defun lsp--workspace-server-id (workspace) 5598 "Return the server ID of WORKSPACE." 5599 (-> workspace lsp--workspace-client lsp--client-server-id)) 5600 5601 (defun lsp--handle-rendered-for-echo-area (contents) 5602 "Return a single line from RENDERED, appropriate for display in the echo area." 5603 (pcase (lsp-workspaces) 5604 (`(,workspace) 5605 (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace))) 5606 ;; For projects with multiple active workspaces we also default to 5607 ;; render the first line. 5608 (_ (lsp-clients-extract-signature-on-hover contents nil)))) 5609 5610 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id) 5611 "Extract a representative line from CONTENTS, to show in the echo area." 5612 (car (s-lines (s-trim (lsp--render-element contents))))) 5613 5614 (defun lsp--render-on-hover-content (contents render-all) 5615 "Render the content received from `document/onHover' request. 5616 CONTENTS - MarkedString | MarkedString[] | MarkupContent 5617 RENDER-ALL - nil if only the signature should be rendered." 5618 (cond 5619 ((lsp-markup-content? contents) 5620 ;; MarkupContent. 5621 ;; It tends to be long and is not suitable to display fully in the echo area. 5622 ;; Just display the first line which is typically the signature. 5623 (if render-all 5624 (lsp--render-element contents) 5625 (lsp--handle-rendered-for-echo-area contents))) 5626 ((and (stringp contents) (not (string-match-p "\n" contents))) 5627 ;; If the contents is a single string containing a single line, 5628 ;; render it always. 5629 (lsp--render-element contents)) 5630 (t 5631 ;; MarkedString -> MarkedString[] 5632 (when (or (lsp-marked-string? contents) (stringp contents)) 5633 (setq contents (list contents))) 5634 ;; Consider the signature consisting of the elements who have a renderable 5635 ;; "language" property. When render-all is nil, ignore other elements. 5636 (string-join 5637 (seq-map 5638 #'lsp--render-element 5639 (if render-all 5640 contents 5641 ;; Only render contents that have an available renderer. 5642 (seq-take 5643 (seq-filter 5644 (-andfn #'lsp-marked-string? 5645 (-compose #'lsp-get-renderer #'lsp:marked-string-language)) 5646 contents) 5647 1))) 5648 (if (bound-and-true-p page-break-lines-mode) 5649 "\n\n" 5650 "\n"))))) 5651 5652 5653 5654 (defvar lsp-signature-mode-map 5655 (-doto (make-sparse-keymap) 5656 (define-key (kbd "M-n") #'lsp-signature-next) 5657 (define-key (kbd "M-p") #'lsp-signature-previous) 5658 (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs) 5659 (define-key (kbd "C-c C-k") #'lsp-signature-stop) 5660 (define-key (kbd "C-g") #'lsp-signature-stop)) 5661 "Keymap for `lsp-signature-mode'.") 5662 5663 (define-minor-mode lsp-signature-mode 5664 "Mode used to show signature popup." 5665 :keymap lsp-signature-mode-map 5666 :lighter "" 5667 :group 'lsp-mode) 5668 5669 (defun lsp-signature-stop () 5670 "Stop showing current signature help." 5671 (interactive) 5672 (lsp-cancel-request-by-token :signature) 5673 (remove-hook 'post-command-hook #'lsp-signature) 5674 (funcall lsp-signature-function nil) 5675 (lsp-signature-mode -1)) 5676 5677 (declare-function page-break-lines--update-display-tables "ext:page-break-lines") 5678 5679 (defun lsp--setup-page-break-mode-if-present () 5680 "Enable `page-break-lines-mode' in current buffer." 5681 (when (fboundp 'page-break-lines-mode) 5682 (page-break-lines-mode) 5683 ;; force page-break-lines-mode to update the display tables. 5684 (page-break-lines--update-display-tables))) 5685 5686 (defun lsp-lv-message (message) 5687 (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present) 5688 (if message 5689 (progn 5690 (setq lsp--signature-last-buffer (current-buffer)) 5691 (let ((lv-force-update t)) 5692 (lv-message "%s" message))) 5693 (lv-delete-window) 5694 (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present))) 5695 5696 (declare-function posframe-show "ext:posframe") 5697 (declare-function posframe-hide "ext:posframe") 5698 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe") 5699 5700 (defface lsp-signature-posframe 5701 '((t :inherit tooltip)) 5702 "Background and foreground for `lsp-signature-posframe'." 5703 :group 'lsp-mode) 5704 5705 (defvar lsp-signature-posframe-params 5706 (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward 5707 :height 10 5708 :width 60 5709 :border-width 1 5710 :min-width 60) 5711 "Params for signature and `posframe-show'.") 5712 5713 (defun lsp-signature-posframe (str) 5714 "Use posframe to show the STR signatureHelp string." 5715 (if str 5716 (apply #'posframe-show 5717 (with-current-buffer (get-buffer-create " *lsp-signature*") 5718 (erase-buffer) 5719 (insert str) 5720 (visual-line-mode 1) 5721 (lsp--setup-page-break-mode-if-present) 5722 (current-buffer)) 5723 (append 5724 lsp-signature-posframe-params 5725 (list :position (point) 5726 :background-color (face-attribute 'lsp-signature-posframe :background nil t) 5727 :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t) 5728 :border-color (face-attribute 'font-lock-comment-face :foreground nil t)))) 5729 (posframe-hide " *lsp-signature*"))) 5730 5731 (defun lsp--handle-signature-update (signature) 5732 (let ((message 5733 (if (lsp-signature-help? signature) 5734 (lsp--signature->message signature) 5735 (mapconcat #'lsp--signature->message signature "\n")))) 5736 (if (s-present? message) 5737 (funcall lsp-signature-function message) 5738 (lsp-signature-stop)))) 5739 5740 (defun lsp-signature-activate () 5741 "Activate signature help. 5742 It will show up only if current point has signature help." 5743 (interactive) 5744 (setq lsp--signature-last nil 5745 lsp--signature-last-index nil 5746 lsp--signature-last-buffer (current-buffer)) 5747 (add-hook 'post-command-hook #'lsp-signature) 5748 (lsp-signature-mode t)) 5749 5750 (defcustom lsp-signature-cycle t 5751 "Whether `lsp-signature-next' and prev should cycle." 5752 :type 'boolean 5753 :group 'lsp-mode) 5754 5755 (defun lsp-signature-next () 5756 "Show next signature." 5757 (interactive) 5758 (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last)))) 5759 (when (and lsp--signature-last-index 5760 lsp--signature-last 5761 (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs))) 5762 (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs)) 5763 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))) 5764 5765 (defun lsp-signature-previous () 5766 "Next signature." 5767 (interactive) 5768 (when (and lsp--signature-last-index 5769 lsp--signature-last 5770 (or lsp-signature-cycle (not (zerop lsp--signature-last-index)))) 5771 (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index) 5772 (length (lsp:signature-help-signatures lsp--signature-last)) 5773 lsp--signature-last-index))) 5774 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))) 5775 5776 (defun lsp-signature-toggle-full-docs () 5777 "Toggle full/partial signature documentation." 5778 (interactive) 5779 (let ((all? (not (numberp lsp-signature-doc-lines)))) 5780 (setq lsp-signature-doc-lines (if all? 5781 (or (car-safe lsp-signature-doc-lines) 5782 20) 5783 (list lsp-signature-doc-lines)))) 5784 (lsp-signature-activate)) 5785 5786 (defun lsp--signature->message (signature-help) 5787 "Generate eldoc message from SIGNATURE-HELP response." 5788 (setq lsp--signature-last signature-help) 5789 5790 (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help)))) 5791 (-let* (((&SignatureHelp :active-signature? 5792 :active-parameter? 5793 :signatures) signature-help) 5794 (active-signature? (or lsp--signature-last-index active-signature? 0)) 5795 (_ (setq lsp--signature-last-index active-signature?)) 5796 ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?)) 5797 (prefix (if (= (length signatures) 1) 5798 "" 5799 (concat (propertize (format " %s/%s" 5800 (1+ active-signature?) 5801 (length signatures)) 5802 'face 'success) 5803 " "))) 5804 (method-docs (when 5805 (and lsp-signature-render-documentation 5806 (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines))) 5807 (let ((docs (lsp--render-element 5808 (lsp:parameter-information-documentation? signature)))) 5809 (when (s-present? docs) 5810 (concat 5811 "\n" 5812 (if (fboundp 'page-break-lines-mode) 5813 "\n" 5814 "") 5815 (if (and (numberp lsp-signature-doc-lines) 5816 (> (length (s-lines docs)) lsp-signature-doc-lines)) 5817 (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs))) 5818 (propertize "\nTruncated..." 'face 'highlight)) 5819 docs))))))) 5820 (when (and active-parameter? (not (seq-empty-p parameters?))) 5821 (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?))) 5822 (seq-elt parameters? active-parameter?))) 5823 (selected-param-label (let ((label (lsp:parameter-information-label param))) 5824 (if (stringp label) label (append label nil)))) 5825 (start (if (stringp selected-param-label) 5826 (s-index-of selected-param-label label) 5827 (cl-first selected-param-label))) 5828 (end (if (stringp selected-param-label) 5829 (+ start (length selected-param-label)) 5830 (cl-second selected-param-label)))) 5831 (add-face-text-property start end 'eldoc-highlight-function-argument nil label))) 5832 (concat prefix label method-docs)))) 5833 5834 (defun lsp-signature () 5835 "Display signature info (based on `textDocument/signatureHelp')" 5836 (if (and lsp--signature-last-buffer 5837 (not (equal (current-buffer) lsp--signature-last-buffer))) 5838 (lsp-signature-stop) 5839 (lsp-request-async "textDocument/signatureHelp" 5840 (lsp--text-document-position-params) 5841 #'lsp--handle-signature-update 5842 :cancel-token :signature))) 5843 5844 5845 (defcustom lsp-overlay-document-color-char "■" 5846 "Display the char represent the document color in overlay" 5847 :type 'string 5848 :group 'lsp-mode) 5849 5850 ;; color presentation 5851 (defun lsp--color-create-interactive-command (color range) 5852 (lambda () 5853 (interactive) 5854 (-let [(&ColorPresentation? :text-edit? 5855 :additional-text-edits?) 5856 (lsp--completing-read 5857 "Select color presentation: " 5858 (lsp-request 5859 "textDocument/colorPresentation" 5860 `( :textDocument ,(lsp--text-document-identifier) 5861 :color ,color 5862 :range ,range)) 5863 #'lsp:color-presentation-label 5864 nil 5865 t)] 5866 (when text-edit? 5867 (lsp--apply-text-edit text-edit?)) 5868 (when additional-text-edits? 5869 (lsp--apply-text-edits additional-text-edits? 'color-presentation))))) 5870 5871 (defun lsp--number->color (number) 5872 (let ((result (format "%x" 5873 (round (* (or number 0) 255.0))))) 5874 (if (= 1 (length result)) 5875 (concat "0" result) 5876 result))) 5877 5878 (defun lsp--document-color () 5879 "Document color handler." 5880 (when (lsp-feature? "textDocument/documentColor") 5881 (lsp-request-async 5882 "textDocument/documentColor" 5883 `(:textDocument ,(lsp--text-document-identifier)) 5884 (lambda (result) 5885 (lsp--remove-overlays 'lsp-color) 5886 (seq-do 5887 (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue) 5888 :range)) 5889 (-let* (((beg . end) (lsp--range-to-region range)) 5890 (overlay (make-overlay beg end)) 5891 (command (lsp--color-create-interactive-command color range))) 5892 (overlay-put overlay 'lsp-color t) 5893 (overlay-put overlay 'evaporate t) 5894 (overlay-put overlay 5895 'before-string 5896 (propertize 5897 lsp-overlay-document-color-char 5898 'face `((:foreground ,(format 5899 "#%s%s%s" 5900 (lsp--number->color red) 5901 (lsp--number->color green) 5902 (lsp--number->color blue)))) 5903 'action command 5904 'mouse-face 'lsp-lens-mouse-face 5905 'local-map (-doto (make-sparse-keymap) 5906 (define-key [mouse-1] command)))))) 5907 result)) 5908 :mode 'unchanged 5909 :cancel-token :document-color-token))) 5910 5911 5912 5913 (defun lsp--action-trigger-parameter-hints (_command) 5914 "Handler for editor.action.triggerParameterHints." 5915 (when (member :on-server-request lsp-signature-auto-activate) 5916 (lsp-signature-activate))) 5917 5918 (defun lsp--action-trigger-suggest (_command) 5919 "Handler for editor.action.triggerSuggest." 5920 (cond 5921 ((and (bound-and-true-p company-mode) 5922 (fboundp 'company-auto-begin) 5923 (fboundp 'company-post-command)) 5924 (run-at-time 0 nil 5925 (lambda () 5926 (let ((this-command 'company-idle-begin) 5927 (company-minimum-prefix-length 0)) 5928 (company-auto-begin) 5929 (company-post-command))))) 5930 (t 5931 (completion-at-point)))) 5932 5933 (defconst lsp--default-action-handlers 5934 (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints) 5935 ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest)) 5936 "Default action handlers.") 5937 5938 (defun lsp--find-action-handler (command) 5939 "Find action handler for particular COMMAND." 5940 (or 5941 (--some (-some->> it 5942 (lsp--workspace-client) 5943 (lsp--client-action-handlers) 5944 (gethash command)) 5945 (lsp-workspaces)) 5946 (gethash command lsp--default-action-handlers))) 5947 5948 (defun lsp--text-document-code-action-params (&optional kind) 5949 "Code action params." 5950 (list :textDocument (lsp--text-document-identifier) 5951 :range (if (use-region-p) 5952 (lsp--region-to-range (region-beginning) (region-end)) 5953 (lsp--region-to-range (point) (point))) 5954 :context `( :diagnostics ,(lsp-cur-possition-diagnostics) 5955 ,@(when kind (list :only (vector kind)))))) 5956 5957 (defun lsp-code-actions-at-point (&optional kind) 5958 "Retrieve the code actions for the active region or the current line. 5959 It will filter by KIND if non nil." 5960 (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind))) 5961 5962 (defun lsp-execute-code-action-by-kind (command-kind) 5963 "Execute code action by COMMAND-KIND." 5964 (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind) 5965 (-filter (-lambda ((&CodeAction :kind?)) 5966 (and kind? (s-prefix? command-kind kind?)))) 5967 lsp--select-action))) 5968 (lsp-execute-code-action action) 5969 (signal 'lsp-no-code-actions '(command-kind)))) 5970 5971 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point) 5972 5973 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?)) 5974 "Parse and execute a code ACTION represented as a Command LSP type." 5975 (let ((server-id (->> (lsp-workspaces) 5976 (cl-first) 5977 (or lsp--cur-workspace) 5978 (lsp--workspace-client) 5979 (lsp--client-server-id)))) 5980 (condition-case nil 5981 (with-no-warnings 5982 (lsp-execute-command server-id (intern command) arguments?)) 5983 (cl-no-applicable-method 5984 (if-let ((action-handler (lsp--find-action-handler command))) 5985 (funcall action-handler action) 5986 (lsp-send-execute-command command arguments?)))))) 5987 5988 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?)) 5989 "Execute code action ACTION. For example, when text under the 5990 caret has a suggestion to apply a fix from an lsp-server, calling 5991 this function will do so. 5992 If ACTION is not set it will be selected from `lsp-code-actions-at-point'. 5993 Request codeAction/resolve for more info if server supports." 5994 (interactive (list (lsp--select-action (lsp-code-actions-at-point)))) 5995 (if (and (lsp-feature? "codeAction/resolve") 5996 (not command?) 5997 (not edit?)) 5998 (lsp--execute-code-action (lsp-request "codeAction/resolve" action)) 5999 (lsp--execute-code-action action))) 6000 6001 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?)) 6002 "Execute code action ACTION." 6003 (when edit? 6004 (lsp--apply-workspace-edit edit? 'code-action)) 6005 6006 (cond 6007 ((stringp command?) (lsp--execute-command action)) 6008 ((lsp-command? command?) (lsp--execute-command command?)))) 6009 6010 (defvar lsp--formatting-indent-alist 6011 ;; Taken from `dtrt-indent-mode' 6012 '( 6013 (ada-mode . ada-indent) ; Ada 6014 (ada-ts-mode . ada-ts-mode-indent-offset) 6015 (c++-mode . c-basic-offset) ; C++ 6016 (c++-ts-mode . c-ts-mode-indent-offset) 6017 (c-mode . c-basic-offset) ; C 6018 (c-ts-mode . c-ts-mode-indent-offset) 6019 (cperl-mode . cperl-indent-level) ; Perl 6020 (crystal-mode . crystal-indent-level) ; Crystal (Ruby) 6021 (csharp-mode . c-basic-offset) ; C# 6022 (csharp-tree-sitter-mode . csharp-tree-sitter-indent-offset) ; C# 6023 (csharp-ts-mode . csharp-ts-mode-indent-offset) ; C# (tree-sitter, Emacs29) 6024 (css-mode . css-indent-offset) ; CSS 6025 (d-mode . c-basic-offset) ; D 6026 (enh-ruby-mode . enh-ruby-indent-level) ; Ruby 6027 (erlang-mode . erlang-indent-level) ; Erlang 6028 (ess-mode . ess-indent-offset) ; ESS (R) 6029 (go-ts-mode . go-ts-mode-indent-offset) 6030 (gpr-mode . gpr-indent-offset) ; GNAT Project 6031 (gpr-ts-mode . gpr-ts-mode-indent-offset) 6032 (hack-mode . hack-indent-offset) ; Hack 6033 (java-mode . c-basic-offset) ; Java 6034 (java-ts-mode . java-ts-mode-indent-offset) 6035 (jde-mode . c-basic-offset) ; Java (JDE) 6036 (js-mode . js-indent-level) ; JavaScript 6037 (js-ts-mode . js-indent-level) 6038 (js2-mode . js2-basic-offset) ; JavaScript-IDE 6039 (js3-mode . js3-indent-level) ; JavaScript-IDE 6040 (json-mode . js-indent-level) ; JSON 6041 (json-ts-mode . json-ts-mode-indent-offset) 6042 (lua-mode . lua-indent-level) ; Lua 6043 (lua-ts-mode . lua-ts-indent-offset) 6044 (nxml-mode . nxml-child-indent) ; XML 6045 (objc-mode . c-basic-offset) ; Objective C 6046 (pascal-mode . pascal-indent-level) ; Pascal 6047 (perl-mode . perl-indent-level) ; Perl 6048 (php-mode . c-basic-offset) ; PHP 6049 (php-ts-mode . php-ts-mode-indent-offset) ; PHP 6050 (powershell-mode . powershell-indent) ; PowerShell 6051 (powershell-ts-mode . powershell-ts-mode-indent-offset) ; PowerShell 6052 (raku-mode . raku-indent-offset) ; Perl6/Raku 6053 (ruby-mode . ruby-indent-level) ; Ruby 6054 (rust-mode . rust-indent-offset) ; Rust 6055 (rust-ts-mode . rust-ts-mode-indent-offset) 6056 (rustic-mode . rustic-indent-offset) ; Rust 6057 (scala-mode . scala-indent:step) ; Scala 6058 (sgml-mode . sgml-basic-offset) ; SGML 6059 (sh-mode . sh-basic-offset) ; Shell Script 6060 (toml-ts-mode . toml-ts-mode-indent-offset) 6061 (typescript-mode . typescript-indent-level) ; Typescript 6062 (typescript-ts-mode . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29) 6063 (yaml-mode . yaml-indent-offset) ; YAML 6064 (yang-mode . c-basic-offset) ; YANG (yang-mode) 6065 6066 (default . standard-indent)) ; default fallback 6067 "A mapping from `major-mode' to its indent variable.") 6068 6069 (defun lsp--get-indent-width (mode) 6070 "Get indentation offset for MODE." 6071 (or (alist-get mode lsp--formatting-indent-alist) 6072 (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default)))) 6073 6074 (defun lsp--make-document-formatting-params () 6075 "Create document formatting params." 6076 (lsp-make-document-formatting-params 6077 :text-document (lsp--text-document-identifier) 6078 :options (lsp-make-formatting-options 6079 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 6080 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 6081 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 6082 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 6083 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)))) 6084 6085 (defun lsp-format-buffer () 6086 "Ask the server to format this document." 6087 (interactive "*") 6088 (cond ((lsp-feature? "textDocument/formatting") 6089 (let ((edits (lsp-request "textDocument/formatting" 6090 (lsp--make-document-formatting-params)))) 6091 (if (seq-empty-p edits) 6092 (lsp--info "No formatting changes provided") 6093 (lsp--apply-text-edits edits 'format)))) 6094 ((lsp-feature? "textDocument/rangeFormatting") 6095 (save-restriction 6096 (widen) 6097 (lsp-format-region (point-min) (point-max)))) 6098 (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider"))))) 6099 6100 (defun lsp-format-region (s e) 6101 "Ask the server to format the region, or if none is selected, the current line." 6102 (interactive "r") 6103 (let ((edits (lsp-request 6104 "textDocument/rangeFormatting" 6105 (lsp--make-document-range-formatting-params s e)))) 6106 (if (seq-empty-p edits) 6107 (lsp--info "No formatting changes provided") 6108 (lsp--apply-text-edits edits 'format)))) 6109 6110 (defmacro lsp-make-interactive-code-action (func-name code-action-kind) 6111 "Define an interactive function FUNC-NAME that attempts to 6112 execute a CODE-ACTION-KIND action." 6113 `(defun ,(intern (concat "lsp-" (symbol-name func-name))) () 6114 ,(format "Perform the %s code action, if available." code-action-kind) 6115 (interactive) 6116 ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to 6117 ;; auto-execute here: the user has specified exactly what they want. 6118 (let ((lsp-auto-execute-action t)) 6119 (condition-case nil 6120 (lsp-execute-code-action-by-kind ,code-action-kind) 6121 (lsp-no-code-actions 6122 (when (called-interactively-p 'any) 6123 (lsp--info ,(format "%s action not available" code-action-kind)))))))) 6124 6125 (lsp-make-interactive-code-action organize-imports "source.organizeImports") 6126 6127 (defun lsp--make-document-range-formatting-params (start end) 6128 "Make DocumentRangeFormattingParams for selected region." 6129 (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params) 6130 (lsp--region-to-range start end))) 6131 6132 (defconst lsp--highlight-kind-face 6133 '((1 . lsp-face-highlight-textual) 6134 (2 . lsp-face-highlight-read) 6135 (3 . lsp-face-highlight-write))) 6136 6137 (defun lsp--remove-overlays (name) 6138 (save-restriction 6139 (widen) 6140 (remove-overlays (point-min) (point-max) name t))) 6141 6142 (defun lsp-document-highlight () 6143 "Highlight all relevant references to the symbol under point." 6144 (interactive) 6145 (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights 6146 (setq lsp--have-document-highlights nil 6147 lsp--symbol-bounds-of-last-highlight-invocation nil) 6148 (let ((lsp-enable-symbol-highlighting t)) 6149 (lsp--document-highlight))) 6150 6151 (defun lsp--document-highlight-callback (highlights) 6152 "Create a callback to process the reply of a 6153 `textDocument/documentHighlight' message for the buffer BUF. 6154 A reference is highlighted only if it is visible in a window." 6155 (lsp--remove-overlays 'lsp-highlight) 6156 6157 (let* ((wins-visible-pos (-map (lambda (win) 6158 (cons (1- (line-number-at-pos (window-start win) t)) 6159 (1+ (line-number-at-pos (window-end win) t)))) 6160 (get-buffer-window-list nil nil 'visible)))) 6161 (setq lsp--have-document-highlights t) 6162 (-map 6163 (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line) 6164 :end (end &as &Position :line end-line)) 6165 :kind?)) 6166 (-map 6167 (-lambda ((start-window . end-window)) 6168 ;; Make the overlay only if the reference is visible 6169 (let ((start-point (lsp--position-to-point start)) 6170 (end-point (lsp--position-to-point end))) 6171 (when (and (> (1+ start-line) start-window) 6172 (< (1+ end-line) end-window) 6173 (not (and lsp-symbol-highlighting-skip-current 6174 (<= start-point (point) end-point)))) 6175 (-doto (make-overlay start-point end-point) 6176 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face))) 6177 (overlay-put 'lsp-highlight t))))) 6178 wins-visible-pos)) 6179 highlights))) 6180 6181 (defcustom lsp-symbol-kinds 6182 '((1 . "File") 6183 (2 . "Module") 6184 (3 . "Namespace") 6185 (4 . "Package") 6186 (5 . "Class") 6187 (6 . "Method") 6188 (7 . "Property") 6189 (8 . "Field") 6190 (9 . "Constructor") 6191 (10 . "Enum") 6192 (11 . "Interface") 6193 (12 . "Function") 6194 (13 . "Variable") 6195 (14 . "Constant") 6196 (15 . "String") 6197 (16 . "Number") 6198 (17 . "Boolean") 6199 (18 . "Array") 6200 (19 . "Object") 6201 (20 . "Key") 6202 (21 . "Null") 6203 (22 . "Enum Member") 6204 (23 . "Struct") 6205 (24 . "Event") 6206 (25 . "Operator") 6207 (26 . "Type Parameter")) 6208 "Alist mapping SymbolKinds to human-readable strings. 6209 Various Symbol objects in the LSP protocol have an integral type, 6210 specifying what they are. This alist maps such type integrals to 6211 readable representations of them. See 6212 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/', 6213 namespace SymbolKind." 6214 :group 'lsp-mode 6215 :type '(alist :key-type integer :value-type string)) 6216 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds) 6217 6218 (lsp-defun lsp--symbol-information-to-xref 6219 ((&SymbolInformation :kind :name 6220 :location (&Location :uri :range (&Range :start 6221 (&Position :line :character))))) 6222 "Return a `xref-item' from SYMBOL information." 6223 (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name) 6224 (xref-make-file-location (lsp--uri-to-path uri) 6225 line 6226 character))) 6227 6228 (defun lsp--get-document-symbols () 6229 "Get document symbols. 6230 6231 If the buffer has not been modified since symbols were last 6232 retrieved, simply return the latest result. 6233 6234 Else, if the request was initiated by Imenu updating its menu-bar 6235 entry, perform it asynchronously; i.e., give Imenu the latest 6236 result and then force a refresh when a new one is available. 6237 6238 Else (e.g., due to interactive use of `imenu' or `xref'), 6239 perform the request synchronously." 6240 (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick) 6241 lsp--document-symbols 6242 (let ((method "textDocument/documentSymbol") 6243 (params `(:textDocument ,(lsp--text-document-identifier))) 6244 (tick (buffer-chars-modified-tick))) 6245 (if (not lsp--document-symbols-request-async) 6246 (prog1 6247 (setq lsp--document-symbols (lsp-request method params)) 6248 (setq lsp--document-symbols-tick tick)) 6249 (lsp-request-async method params 6250 (lambda (document-symbols) 6251 (setq lsp--document-symbols document-symbols 6252 lsp--document-symbols-tick tick) 6253 (lsp--imenu-refresh)) 6254 :mode 'alive 6255 :cancel-token :document-symbols) 6256 lsp--document-symbols)))) 6257 6258 (advice-add 'imenu-update-menubar :around 6259 (lambda (oldfun &rest r) 6260 (let ((lsp--document-symbols-request-async t)) 6261 (apply oldfun r)))) 6262 6263 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position) 6264 "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION." 6265 (-let (((symbol &as &DocumentSymbol? :children?) 6266 (seq-find (-lambda ((&DocumentSymbol :range)) 6267 (lsp-point-in-range? current-position range)) 6268 document-symbols))) 6269 (if children? 6270 (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position)) 6271 (when symbol 6272 (list symbol))))) 6273 6274 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?)) 6275 "Convert a SymbolInformation to a DocumentInformation" 6276 (lsp-make-document-symbol :name name 6277 :kind kind 6278 :range (lsp:location-range location) 6279 :children? nil 6280 :deprecated? deprecated? 6281 :selection-range (lsp:location-range location) 6282 :detail? container-name?)) 6283 6284 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position) 6285 "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION." 6286 (--> symbols-informations 6287 (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range))) 6288 (when (lsp-point-in-range? current-position range) 6289 (lsp--symbol-information->document-symbol symbol))) 6290 it) 6291 (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position)) 6292 (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position))) 6293 (and (lsp--position-compare b-start-position a-start-position) 6294 (lsp--position-compare a-end-position b-end-position)))))) 6295 6296 (defun lsp--symbols->document-symbols-hierarchy (symbols) 6297 "Convert SYMBOLS to symbols-hierarchy." 6298 (when-let ((first-symbol (lsp-seq-first symbols))) 6299 (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line) 6300 :character (plist-get (lsp--cur-position) :character)))) 6301 (if (lsp-symbol-information? first-symbol) 6302 (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position) 6303 (lsp--document-symbols->document-symbols-hierarchy symbols cur-position))))) 6304 6305 (defun lsp--xref-backend () 'xref-lsp) 6306 6307 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp))) 6308 (propertize (or (thing-at-point 'symbol) "") 6309 'identifier-at-point t)) 6310 6311 (defun lsp--xref-elements-index (symbols path) 6312 (-mapcat 6313 (-lambda (sym) 6314 (pcase-exhaustive sym 6315 ((DocumentSymbol :name :children? :selection-range (Range :start)) 6316 (cons (cons (concat path name) 6317 (lsp--position-to-point start)) 6318 (lsp--xref-elements-index children? (concat path name " / ")))) 6319 ((SymbolInformation :name :location (Location :range (Range :start))) 6320 (list (cons (concat path name) 6321 (lsp--position-to-point start)))))) 6322 symbols)) 6323 6324 (defvar-local lsp--symbols-cache nil) 6325 6326 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp))) 6327 (if (lsp--find-workspaces-for "textDocument/documentSymbol") 6328 (progn 6329 (setq lsp--symbols-cache (lsp--xref-elements-index 6330 (lsp--get-document-symbols) nil)) 6331 lsp--symbols-cache) 6332 (list (propertize (or (thing-at-point 'symbol) "") 6333 'identifier-at-point t)))) 6334 6335 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier) 6336 (save-excursion 6337 (unless (get-text-property 0 'identifier-at-point identifier) 6338 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6339 (user-error "Unable to find symbol %s in current document" identifier))))) 6340 (lsp--locations-to-xref-items (lsp-request "textDocument/definition" 6341 (lsp--text-document-position-params))))) 6342 6343 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier) 6344 (save-excursion 6345 (unless (get-text-property 0 'identifier-at-point identifier) 6346 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6347 (user-error "Unable to find symbol %s" identifier))))) 6348 (lsp--locations-to-xref-items (lsp-request "textDocument/references" 6349 (lsp--make-reference-params nil lsp-references-exclude-definition))))) 6350 6351 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern) 6352 (seq-map #'lsp--symbol-information-to-xref 6353 (lsp-request "workspace/symbol" `(:query ,pattern)))) 6354 6355 (defcustom lsp-rename-use-prepare t 6356 "Whether `lsp-rename' should do a prepareRename first. 6357 For some language servers, textDocument/prepareRename might be 6358 too slow, in which case this variable may be set to nil. 6359 `lsp-rename' will then use `thing-at-point' `symbol' to determine 6360 the symbol to rename at point." 6361 :group 'lsp-mode 6362 :type 'boolean) 6363 6364 (defun lsp--get-symbol-to-rename () 6365 "Get a symbol to rename and placeholder at point. 6366 Returns a cons ((START . END) . PLACEHOLDER?), and nil if 6367 renaming is generally supported but cannot be done at point. 6368 START and END are the bounds of the identifiers being renamed, 6369 while PLACEHOLDER?, is either nil or a string suggested by the 6370 language server as the initial input of a new-name prompt." 6371 (unless (lsp-feature? "textDocument/rename") 6372 (error "The connected server(s) doesn't support renaming")) 6373 (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename")) 6374 (when-let ((response 6375 (lsp-request "textDocument/prepareRename" 6376 (lsp--text-document-position-params)))) 6377 (let* ((bounds (lsp--range-to-region 6378 (if (lsp-range? response) 6379 response 6380 (lsp:prepare-rename-result-range response)))) 6381 (placeholder 6382 (and (not (lsp-range? response)) 6383 (lsp:prepare-rename-result-placeholder response)))) 6384 (cons bounds placeholder))) 6385 (when-let ((bounds (bounds-of-thing-at-point 'symbol))) 6386 (cons bounds nil)))) 6387 6388 (defface lsp-face-rename '((t :underline t)) 6389 "Face used to highlight the identifier being renamed. 6390 Renaming can be done using `lsp-rename'." 6391 :group 'lsp-mode) 6392 6393 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face)) 6394 "Face used to display the rename placeholder in. 6395 When calling `lsp-rename' interactively, this will be the face of 6396 the new name." 6397 :group 'lsp-mode) 6398 6399 (defvar lsp-rename-history '() 6400 "History for `lsp--read-rename'.") 6401 6402 (defun lsp--read-rename (at-point) 6403 "Read a new name for a `lsp-rename' at `point' from the user. 6404 AT-POINT shall be a structure as returned by 6405 `lsp--get-symbol-to-rename'. 6406 6407 Returns a string, which should be the new name for the identifier 6408 at point. If renaming cannot be done at point (as determined from 6409 AT-POINT), throw a `user-error'. 6410 6411 This function is for use in `lsp-rename' only, and shall not be 6412 relied upon." 6413 (unless at-point 6414 (user-error "`lsp-rename' is invalid here")) 6415 (-let* ((((start . end) . placeholder?) at-point) 6416 ;; Do the `buffer-substring' first to not include `lsp-face-rename' 6417 (rename-me (buffer-substring start end)) 6418 (placeholder (or placeholder? rename-me)) 6419 (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face)) 6420 6421 overlay) 6422 ;; We need unwind protect, as the user might cancel here, causing the 6423 ;; overlay to linger. 6424 (unwind-protect 6425 (progn 6426 (setq overlay (make-overlay start end)) 6427 (overlay-put overlay 'face 'lsp-face-rename) 6428 6429 (read-string (format "Rename %s to: " rename-me) placeholder 6430 'lsp-rename-history)) 6431 (and overlay (delete-overlay overlay))))) 6432 6433 (defun lsp-rename (newname) 6434 "Rename the symbol (and all references to it) under point to NEWNAME." 6435 (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename)))) 6436 (when-let ((edits (lsp-request "textDocument/rename" 6437 `( :textDocument ,(lsp--text-document-identifier) 6438 :position ,(lsp--cur-position) 6439 :newName ,newname)))) 6440 (lsp--apply-workspace-edit edits 'rename))) 6441 6442 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?) 6443 "Advice around function `rename-file'. 6444 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?. 6445 6446 This advice sends workspace/willRenameFiles before renaming file 6447 to check if server wants to apply any workspaceEdits after renamed." 6448 (if (and lsp-apply-edits-after-file-operations 6449 (lsp--send-will-rename-files-p old-name)) 6450 (let ((params (lsp-make-rename-files-params 6451 :files (vector (lsp-make-file-rename 6452 :oldUri (lsp--path-to-uri old-name) 6453 :newUri (lsp--path-to-uri new-name)))))) 6454 (when-let ((edits (lsp-request "workspace/willRenameFiles" params))) 6455 (lsp--apply-workspace-edit edits 'rename-file) 6456 (funcall old-func old-name new-name ok-if-already-exists?) 6457 (when (lsp--send-did-rename-files-p) 6458 (lsp-notify "workspace/didRenameFiles" params)))) 6459 (funcall old-func old-name new-name ok-if-already-exists?))) 6460 6461 (advice-add 'rename-file :around #'lsp--on-rename-file) 6462 6463 (defcustom lsp-xref-force-references nil 6464 "If non-nil threat everything as references(e. g. jump if only one item.)" 6465 :group 'lsp-mode 6466 :type 'boolean) 6467 6468 (defun lsp-show-xrefs (xrefs display-action references?) 6469 (unless (region-active-p) (push-mark nil t)) 6470 (if (boundp 'xref-show-definitions-function) 6471 (with-no-warnings 6472 (xref-push-marker-stack) 6473 (funcall (if (and references? (not lsp-xref-force-references)) 6474 xref-show-xrefs-function 6475 xref-show-definitions-function) 6476 (-const xrefs) 6477 `((window . ,(selected-window)) 6478 (display-action . ,display-action) 6479 ,(if (and references? (not lsp-xref-force-references)) 6480 `(auto-jump . ,xref-auto-jump-to-first-xref) 6481 `(auto-jump . ,xref-auto-jump-to-first-definition))))) 6482 (xref--show-xrefs xrefs display-action))) 6483 6484 (cl-defmethod seq-empty-p ((ht hash-table)) 6485 "Function `seq-empty-p' for hash-table." 6486 (hash-table-empty-p ht)) 6487 6488 (cl-defun lsp-find-locations (method &optional extra &key display-action references?) 6489 "Send request named METHOD and get cross references of the symbol under point. 6490 EXTRA is a plist of extra parameters. 6491 REFERENCES? t when METHOD returns references." 6492 (let ((loc (lsp-request method 6493 (append (lsp--text-document-position-params) extra)))) 6494 (if (seq-empty-p loc) 6495 (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) "")) 6496 (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?)))) 6497 6498 (cl-defun lsp-find-declaration (&key display-action) 6499 "Find declarations of the symbol under point." 6500 (interactive) 6501 (lsp-find-locations "textDocument/declaration" nil :display-action display-action)) 6502 6503 (cl-defun lsp-find-definition (&key display-action) 6504 "Find definitions of the symbol under point." 6505 (interactive) 6506 (lsp-find-locations "textDocument/definition" nil :display-action display-action)) 6507 6508 (defun lsp-find-definition-mouse (click) 6509 "Click to start `lsp-find-definition' at clicked point." 6510 (interactive "e") 6511 (let* ((ec (event-start click)) 6512 (p1 (posn-point ec)) 6513 (w1 (posn-window ec))) 6514 (select-window w1) 6515 (goto-char p1) 6516 (lsp-find-definition))) 6517 6518 (cl-defun lsp-find-implementation (&key display-action) 6519 "Find implementations of the symbol under point." 6520 (interactive) 6521 (lsp-find-locations "textDocument/implementation" 6522 nil 6523 :display-action display-action 6524 :references? t)) 6525 6526 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action) 6527 "Find references of the symbol under point." 6528 (interactive "P") 6529 (lsp-find-locations "textDocument/references" 6530 (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition))))) 6531 :display-action display-action 6532 :references? t)) 6533 6534 (cl-defun lsp-find-type-definition (&key display-action) 6535 "Find type definitions of the symbol under point." 6536 (interactive) 6537 (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action)) 6538 6539 (defalias 'lsp-find-custom #'lsp-find-locations) 6540 (defalias 'lsp-goto-implementation #'lsp-find-implementation) 6541 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition) 6542 6543 (with-eval-after-load 'evil 6544 (evil-set-command-property 'lsp-find-definition :jump t) 6545 (evil-set-command-property 'lsp-find-implementation :jump t) 6546 (evil-set-command-property 'lsp-find-references :jump t) 6547 (evil-set-command-property 'lsp-find-type-definition :jump t)) 6548 6549 (defun lsp--workspace-method-supported? (check-command method capability workspace) 6550 (with-lsp-workspace workspace 6551 (if check-command 6552 (funcall check-command workspace) 6553 (or 6554 (when capability (lsp--capability capability)) 6555 (lsp--registered-capability method) 6556 (and (not capability) (not check-command)))))) 6557 6558 (defun lsp-disable-method-for-server (method server-id) 6559 "Disable METHOD for SERVER-ID." 6560 (cl-callf 6561 (lambda (reqs) 6562 (-let (((&plist :check-command :capability) reqs)) 6563 (list :check-command 6564 (lambda (workspace) 6565 (unless (-> workspace 6566 lsp--workspace-client 6567 lsp--client-server-id 6568 (eq server-id)) 6569 (lsp--workspace-method-supported? check-command 6570 method 6571 capability 6572 workspace)))))) 6573 (alist-get method lsp-method-requirements nil nil 'string=))) 6574 6575 (defun lsp--find-workspaces-for (msg-or-method) 6576 "Find all workspaces in the current project that can handle MSG." 6577 (let ((method (if (stringp msg-or-method) 6578 msg-or-method 6579 (plist-get msg-or-method :method)))) 6580 (-if-let (reqs (cdr (assoc method lsp-method-requirements))) 6581 (-let (((&plist :capability :check-command) reqs)) 6582 (-filter 6583 (-partial #'lsp--workspace-method-supported? 6584 check-command method capability) 6585 (lsp-workspaces))) 6586 (lsp-workspaces)))) 6587 6588 (defun lsp-can-execute-command? (command-name) 6589 "Returns non-nil if current language server(s) can execute COMMAND-NAME. 6590 The command is executed via `workspace/executeCommand'" 6591 (cl-position 6592 command-name 6593 (lsp:execute-command-options-commands 6594 (lsp:server-capabilities-execute-command-provider? 6595 (lsp--server-capabilities))) 6596 :test #'equal)) 6597 6598 (defalias 'lsp-feature? 'lsp--find-workspaces-for) 6599 6600 (cl-defmethod lsp-execute-command (_server _command _arguments) 6601 "Dispatch COMMAND execution." 6602 (signal 'cl-no-applicable-method nil)) 6603 6604 (defun lsp-workspace-command-execute (command &optional args) 6605 "Execute workspace COMMAND with ARGS." 6606 (condition-case-unless-debug err 6607 (let ((params (if args 6608 (list :command command :arguments args) 6609 (list :command command)))) 6610 (lsp-request "workspace/executeCommand" params)) 6611 (error 6612 (error "`workspace/executeCommand' with `%s' failed.\n\n%S" 6613 command err)))) 6614 6615 (defun lsp-send-execute-command (command &optional args) 6616 "Create and send a `workspace/executeCommand' message having command COMMAND 6617 and optional ARGS." 6618 (lsp-workspace-command-execute command args)) 6619 6620 (defalias 'lsp-point-to-position #'lsp--point-to-position) 6621 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier) 6622 (defalias 'lsp--send-execute-command #'lsp-send-execute-command) 6623 (defalias 'lsp-on-open #'lsp--text-document-did-open) 6624 (defalias 'lsp-on-save #'lsp--text-document-did-save) 6625 6626 (defun lsp--set-configuration (settings) 6627 "Set the SETTINGS for the lsp server." 6628 (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings))) 6629 6630 (defun lsp-current-buffer () 6631 (or lsp--virtual-buffer 6632 (current-buffer))) 6633 6634 (defun lsp-buffer-live-p (buffer-id) 6635 (if-let ((buffer-live (plist-get buffer-id :buffer-live?))) 6636 (funcall buffer-live buffer-id) 6637 (buffer-live-p buffer-id))) 6638 6639 (defun lsp--on-set-visited-file-name (old-func &rest args) 6640 "Advice around function `set-visited-file-name'. 6641 6642 This advice sends textDocument/didClose for the old file and 6643 textDocument/didOpen for the new file." 6644 (when lsp--cur-workspace 6645 (lsp--text-document-did-close t)) 6646 (prog1 (apply old-func args) 6647 (when lsp--cur-workspace 6648 (lsp--text-document-did-open)))) 6649 6650 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name) 6651 6652 (defvar lsp--flushing-delayed-changes nil) 6653 6654 (defun lsp--send-no-wait (message proc) 6655 "Send MESSAGE to PROC without waiting for further output." 6656 6657 (unless lsp--flushing-delayed-changes 6658 (let ((lsp--flushing-delayed-changes t)) 6659 (lsp--flush-delayed-changes))) 6660 (lsp-process-send proc message)) 6661 6662 (define-error 'lsp-parse-error 6663 "Error parsing message from language server" 'lsp-error) 6664 (define-error 'lsp-unknown-message-type 6665 "Unknown message type" '(lsp-error lsp-parse-error)) 6666 (define-error 'lsp-unknown-json-rpc-version 6667 "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error)) 6668 (define-error 'lsp-no-content-length 6669 "Content-Length header missing in message" '(lsp-error lsp-parse-error)) 6670 (define-error 'lsp-invalid-header-name 6671 "Invalid header name" '(lsp-error lsp-parse-error)) 6672 6673 ;; id method 6674 ;; x x request 6675 ;; x . response 6676 ;; . x notification 6677 (defun lsp--get-message-type (json-data) 6678 "Get the message type from JSON-DATA." 6679 (if (lsp:json-message-id? json-data) 6680 (if (lsp:json-message-error? json-data) 6681 'response-error 6682 (if (lsp:json-message-method? json-data) 6683 'request 6684 'response)) 6685 'notification)) 6686 6687 (defconst lsp--default-notification-handlers 6688 (ht ("window/showMessage" #'lsp--window-show-message) 6689 ("window/logMessage" #'lsp--window-log-message) 6690 ("window/showInputBox" #'lsp--window-show-input-box) 6691 ("window/showQuickPick" #'lsp--window-show-quick-pick) 6692 ("textDocument/publishDiagnostics" #'lsp--on-diagnostics) 6693 ("textDocument/diagnosticsEnd" #'ignore) 6694 ("textDocument/diagnosticsBegin" #'ignore) 6695 ("telemetry/event" #'ignore) 6696 ("$/progress" (lambda (workspace params) 6697 (funcall lsp-progress-function workspace params))))) 6698 6699 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method)) 6700 "Call the appropriate handler for NOTIFICATION." 6701 (-let ((client (lsp--workspace-client workspace))) 6702 (when (lsp--log-io-p method) 6703 (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif) 6704 lsp--cur-workspace)) 6705 (if-let ((handler (or (gethash method (lsp--client-notification-handlers client)) 6706 (gethash method lsp--default-notification-handlers)))) 6707 (funcall handler workspace params) 6708 (when (and method (not (string-prefix-p "$" method))) 6709 (lsp-warn "Unknown notification: %s" method))))) 6710 6711 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items)) 6712 "Get section configuration. 6713 PARAMS are the `workspace/configuration' request params" 6714 (->> items 6715 (-map (-lambda ((&ConfigurationItem :section?)) 6716 (-let* ((path-parts (split-string section? "\\.")) 6717 (path-without-last (s-join "." (-slice path-parts 0 -1))) 6718 (path-parts-len (length path-parts))) 6719 (cond 6720 ((<= path-parts-len 1) 6721 (ht-get (lsp-configuration-section section?) 6722 (car-safe path-parts) 6723 (ht-create))) 6724 ((> path-parts-len 1) 6725 (when-let ((section (lsp-configuration-section path-without-last)) 6726 (keys path-parts)) 6727 (while (and keys section) 6728 (setf section (ht-get section (pop keys)))) 6729 section)))))) 6730 (apply #'vector))) 6731 6732 (defun lsp--ms-since (timestamp) 6733 "Integer number of milliseconds since TIMESTAMP. Fractions discarded." 6734 (floor (* 1000 (float-time (time-since timestamp))))) 6735 6736 (defun lsp--send-request-response (workspace recv-time request response) 6737 "Send the RESPONSE for REQUEST in WORKSPACE and log if needed." 6738 (-let* (((&JSONResponse :params :method :id) request) 6739 (process (lsp--workspace-proc workspace)) 6740 (response (lsp--make-response id response)) 6741 (req-entry (and lsp-log-io 6742 (lsp--make-log-entry method id params 'incoming-req))) 6743 (resp-entry (and lsp-log-io 6744 (lsp--make-log-entry method id response 'outgoing-resp 6745 (lsp--ms-since recv-time))))) 6746 ;; Send response to the server. 6747 (when (lsp--log-io-p method) 6748 (lsp--log-entry-new req-entry workspace) 6749 (lsp--log-entry-new resp-entry workspace)) 6750 (lsp--send-no-wait response process))) 6751 6752 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method)) 6753 "Call the appropriate handler for REQUEST, and send the return value to the 6754 server. WORKSPACE is the active workspace." 6755 (-let* ((recv-time (current-time)) 6756 (client (lsp--workspace-client workspace)) 6757 (buffers (lsp--workspace-buffers workspace)) 6758 handler 6759 (response (cond 6760 ((setq handler (gethash method (lsp--client-request-handlers client) nil)) 6761 (funcall handler workspace params)) 6762 ((setq handler (gethash method (lsp--client-async-request-handlers client) nil)) 6763 (funcall handler workspace params 6764 (-partial #'lsp--send-request-response 6765 workspace recv-time request)) 6766 'delay-response) 6767 ((equal method "client/registerCapability") 6768 (mapc #'lsp--server-register-capability 6769 (lsp:registration-params-registrations params)) 6770 (mapc (lambda (buf) 6771 (when (lsp-buffer-live-p buf) 6772 (lsp-with-current-buffer buf 6773 (lsp-unconfig-buffer) 6774 (lsp-configure-buffer)))) 6775 buffers) 6776 nil) 6777 ((equal method "window/showMessageRequest") 6778 (let ((choice (lsp--window-log-message-request params))) 6779 `(:title ,choice))) 6780 ((equal method "window/showDocument") 6781 (let ((success? (lsp--window-show-document params))) 6782 (lsp-make-show-document-result :success (or success? 6783 :json-false)))) 6784 ((equal method "client/unregisterCapability") 6785 (mapc #'lsp--server-unregister-capability 6786 (lsp:unregistration-params-unregisterations params)) 6787 (mapc (lambda (buf) 6788 (when (lsp-buffer-live-p buf) 6789 (lsp-with-current-buffer buf 6790 (lsp-unconfig-buffer) 6791 (lsp-configure-buffer)))) 6792 buffers) 6793 nil) 6794 ((equal method "workspace/applyEdit") 6795 (list :applied (condition-case err 6796 (prog1 t 6797 (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested)) 6798 (error 6799 (lsp--error "Failed to apply edits with message %s" 6800 (error-message-string err)) 6801 :json-false)))) 6802 ((equal method "workspace/configuration") 6803 (with-lsp-workspace workspace 6804 (if-let ((buf (car buffers))) 6805 (lsp-with-current-buffer buf 6806 (lsp--build-workspace-configuration-response params)) 6807 (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace) 6808 (lsp--build-workspace-configuration-response params))))) 6809 ((equal method "workspace/workspaceFolders") 6810 (let ((folders (or (-> workspace 6811 (lsp--workspace-client) 6812 (lsp--client-server-id) 6813 (gethash (lsp-session-server-id->folders (lsp-session)))) 6814 (lsp-session-folders (lsp-session))))) 6815 (->> folders 6816 (-distinct) 6817 (-map (lambda (folder) 6818 (list :uri (lsp--path-to-uri folder)))) 6819 (apply #'vector)))) 6820 ((equal method "window/workDoneProgress/create") 6821 nil ;; no specific reply, no processing required 6822 ) 6823 ((equal method "workspace/semanticTokens/refresh") 6824 (when (and lsp-semantic-tokens-enable 6825 (fboundp 'lsp--semantic-tokens-on-refresh)) 6826 (lsp--semantic-tokens-on-refresh workspace)) 6827 nil) 6828 ((equal method "workspace/codeLens/refresh") 6829 (when (and lsp-lens-enable 6830 (fboundp 'lsp--lens-on-refresh)) 6831 (lsp--lens-on-refresh workspace)) 6832 nil) 6833 (t (lsp-warn "Unknown request method: %s" method) nil)))) 6834 ;; Send response to the server. 6835 (unless (eq response 'delay-response) 6836 (lsp--send-request-response workspace recv-time request response)))) 6837 6838 (lsp-defun lsp--error-string ((&JSONError :message :code)) 6839 "Format ERR as a user friendly string." 6840 (format "Error from the Language Server: %s (%s)" 6841 message 6842 (or (car (alist-get code lsp--errors)) "Unknown error"))) 6843 6844 (defun lsp--get-body-length (headers) 6845 (let ((content-length (cdr (assoc "Content-Length" headers)))) 6846 (if content-length 6847 (string-to-number content-length) 6848 6849 ;; This usually means either the server or our parser is 6850 ;; screwed up with a previous Content-Length 6851 (error "No Content-Length header")))) 6852 6853 (defun lsp--parse-header (s) 6854 "Parse string S as a LSP (KEY . VAL) header." 6855 (let ((pos (string-match "\:" s)) 6856 key val) 6857 (unless pos 6858 (signal 'lsp-invalid-header-name (list s))) 6859 (setq key (substring s 0 pos) 6860 val (s-trim-left (substring s (+ 1 pos)))) 6861 (when (equal key "Content-Length") 6862 (cl-assert (cl-loop for c across val 6863 when (or (> c ?9) (< c ?0)) return nil 6864 finally return t) 6865 nil (format "Invalid Content-Length value: %s" val))) 6866 (cons key val))) 6867 6868 (defmacro lsp--read-json (str) 6869 "Read json string STR." 6870 (if (progn 6871 (require 'json) 6872 (fboundp 'json-parse-string)) 6873 `(json-parse-string ,str 6874 :object-type (if lsp-use-plists 6875 'plist 6876 'hash-table) 6877 :null-object nil 6878 :false-object nil) 6879 `(let ((json-array-type 'vector) 6880 (json-object-type (if lsp-use-plists 6881 'plist 6882 'hash-table)) 6883 (json-false nil)) 6884 (json-read-from-string ,str)))) 6885 6886 (defmacro lsp-json-read-buffer () 6887 "Read json from the current buffer." 6888 (if (progn 6889 (require 'json) 6890 (fboundp 'json-parse-buffer)) 6891 `(json-parse-buffer :object-type (if lsp-use-plists 6892 'plist 6893 'hash-table) 6894 :null-object nil 6895 :false-object nil) 6896 `(let ((json-array-type 'vector) 6897 (json-object-type (if lsp-use-plists 6898 'plist 6899 'hash-table)) 6900 (json-false nil)) 6901 (json-read)))) 6902 6903 (defun lsp--read-json-file (file-path) 6904 "Read json file." 6905 (-> file-path 6906 (f-read-text) 6907 (lsp--read-json))) 6908 6909 (defun lsp--parser-on-message (json-data workspace) 6910 "Called when the parser P read a complete MSG from the server." 6911 (with-demoted-errors "Error processing message %S." 6912 (with-lsp-workspace workspace 6913 (let* ((client (lsp--workspace-client workspace)) 6914 (id (--when-let (lsp:json-response-id json-data) 6915 (if (stringp it) (string-to-number it) it))) 6916 (data (lsp:json-response-result json-data))) 6917 (pcase (lsp--get-message-type json-data) 6918 ('response 6919 (cl-assert id) 6920 (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))] 6921 (when (lsp--log-io-p method) 6922 (lsp--log-entry-new 6923 (lsp--make-log-entry method id data 'incoming-resp 6924 (lsp--ms-since before-send)) 6925 workspace)) 6926 (when callback 6927 (remhash id (lsp--client-response-handlers client)) 6928 (funcall callback (lsp:json-response-result json-data))))) 6929 ('response-error 6930 (cl-assert id) 6931 (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))] 6932 (when (lsp--log-io-p method) 6933 (lsp--log-entry-new 6934 (lsp--make-log-entry method id (lsp:json-response-error-error json-data) 6935 'incoming-resp (lsp--ms-since before-send)) 6936 workspace)) 6937 (when callback 6938 (remhash id (lsp--client-response-handlers client)) 6939 (funcall callback (lsp:json-response-error-error json-data))))) 6940 ('notification 6941 (lsp--on-notification workspace json-data)) 6942 ('request (lsp--on-request workspace json-data))))))) 6943 6944 (defun lsp--create-filter-function (workspace) 6945 "Make filter for the workspace." 6946 (let ((body-received 0) 6947 leftovers body-length body chunk) 6948 (lambda (_proc input) 6949 (setf chunk (if (s-blank? leftovers) 6950 input 6951 (concat leftovers input))) 6952 6953 (let (messages) 6954 (while (not (s-blank? chunk)) 6955 (if (not body-length) 6956 ;; Read headers 6957 (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk))) 6958 ;; We've got all the headers, handle them all at once: 6959 (setf body-length (lsp--get-body-length 6960 (mapcar #'lsp--parse-header 6961 (split-string 6962 (substring-no-properties chunk 6963 (or (string-match-p "Content-Length" chunk) 6964 (error "Unable to find Content-Length header.")) 6965 body-sep-pos) 6966 "\r\n"))) 6967 body-received 0 6968 leftovers nil 6969 chunk (substring-no-properties chunk (+ body-sep-pos 4))) 6970 6971 ;; Haven't found the end of the headers yet. Save everything 6972 ;; for when the next chunk arrives and await further input. 6973 (setf leftovers chunk 6974 chunk nil)) 6975 (let* ((chunk-length (string-bytes chunk)) 6976 (left-to-receive (- body-length body-received)) 6977 (this-body (if (< left-to-receive chunk-length) 6978 (prog1 (substring-no-properties chunk 0 left-to-receive) 6979 (setf chunk (substring-no-properties chunk left-to-receive))) 6980 (prog1 chunk 6981 (setf chunk nil)))) 6982 (body-bytes (string-bytes this-body))) 6983 (push this-body body) 6984 (setf body-received (+ body-received body-bytes)) 6985 (when (>= chunk-length left-to-receive) 6986 (condition-case err 6987 (with-temp-buffer 6988 (apply #'insert 6989 (nreverse 6990 (prog1 body 6991 (setf leftovers nil 6992 body-length nil 6993 body-received nil 6994 body nil)))) 6995 (decode-coding-region (point-min) 6996 (point-max) 6997 'utf-8) 6998 (goto-char (point-min)) 6999 (push (lsp-json-read-buffer) messages)) 7000 7001 (error 7002 (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s" 7003 (concat leftovers input) 7004 err))))))) 7005 (mapc (lambda (msg) 7006 (lsp--parser-on-message msg workspace)) 7007 (nreverse messages)))))) 7008 7009 (defvar-local lsp--line-col-to-point-hash-table nil 7010 "Hash table with keys (line . col) and values that are either point positions 7011 or markers.") 7012 7013 (defcustom lsp-imenu-detailed-outline t 7014 "Whether `lsp-imenu' should include signatures. 7015 This will be ignored if the server doesn't provide the necessary 7016 information, for example if it doesn't support DocumentSymbols." 7017 :group 'lsp-imenu 7018 :type 'boolean) 7019 7020 (defcustom lsp-imenu-hide-parent-details t 7021 "Whether `lsp-imenu' should hide signatures of parent nodes." 7022 :group 'lsp-imenu 7023 :type 'boolean) 7024 7025 (defface lsp-details-face '((t :height 0.8 :inherit shadow)) 7026 "Used to display additional information throughout `lsp'. 7027 Things like line numbers, signatures, ... are considered 7028 additional information. Often, additional faces are defined that 7029 inherit from this face by default, like `lsp-signature-face', and 7030 they may be customized for finer control." 7031 :group 'lsp-mode) 7032 7033 (defface lsp-signature-face '((t :inherit lsp-details-face)) 7034 "Used to display signatures in `imenu', ...." 7035 :group 'lsp-mode) 7036 7037 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?) 7038 show-detail?) 7039 "Render INPUT0, an `&DocumentSymbol', to a string. 7040 If SHOW-DETAIL? is set, make use of its `:detail?' field (often 7041 the signature)." 7042 (let ((detail (and show-detail? (s-present? detail?) 7043 (propertize (concat " " (s-trim-left detail?)) 7044 'face 'lsp-signature-face))) 7045 (name (if deprecated? 7046 (propertize name 'face 'lsp-face-semhl-deprecated) name))) 7047 (concat name detail))) 7048 7049 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?) 7050 separator) 7051 "Render a piece of SymbolInformation. 7052 Handle :deprecated?. If SEPARATOR is non-nil, the 7053 symbol's (optional) parent, SEPARATOR and the symbol itself are 7054 concatenated." 7055 (when (and separator container-name? (not (string-empty-p container-name?))) 7056 (setq name (concat name separator container-name?))) 7057 (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name)) 7058 7059 (defun lsp--symbol-to-imenu-elem (sym) 7060 "Convert SYM to imenu element. 7061 7062 SYM is a SymbolInformation message. 7063 7064 Return a cons cell (full-name . start-point)." 7065 (let ((start-point (ht-get lsp--line-col-to-point-hash-table 7066 (lsp--get-line-and-col sym)))) 7067 (cons (lsp-render-symbol-information 7068 sym (and lsp-imenu-show-container-name 7069 lsp-imenu-container-name-separator)) 7070 start-point))) 7071 7072 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?)) 7073 "Convert SYM to hierarchical imenu elements. 7074 7075 SYM is a DocumentSymbol message. 7076 7077 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if 7078 SYM doesn't have any children. Otherwise return a cons cell with 7079 an alist 7080 7081 (\"symbol-name\" . ((\"(symbol-kind)\" . start-point) 7082 cons-cells-from-children))" 7083 (let ((filtered-children (lsp--imenu-filter-symbols children?)) 7084 (signature (lsp-render-symbol sym lsp-imenu-detailed-outline))) 7085 (if (seq-empty-p filtered-children) 7086 (cons signature 7087 (ht-get lsp--line-col-to-point-hash-table 7088 (lsp--get-line-and-col sym))) 7089 (cons signature 7090 (lsp--imenu-create-hierarchical-index filtered-children))))) 7091 7092 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind)) 7093 "Determine if SYM is for the current document and is to be shown." 7094 ;; It's a SymbolInformation or DocumentSymbol, which is always in the 7095 ;; current buffer file. 7096 (and lsp-imenu-index-symbol-kinds 7097 (numberp kind) 7098 (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup)) 7099 kind 7100 0))) 7101 (not (memql (aref lsp/symbol-kind-lookup clamped-kind) 7102 lsp-imenu-index-symbol-kinds))))) 7103 7104 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind)) 7105 "The string name of the kind of SYM." 7106 (alist-get kind lsp-symbol-kinds "Other")) 7107 7108 (defun lsp--get-line-and-col (sym) 7109 "Obtain the line and column corresponding to SYM." 7110 (-let* ((location (lsp:symbol-information-location sym)) 7111 (name-range (or (and location (lsp:location-range location)) 7112 (lsp:document-symbol-selection-range sym))) 7113 ((&Range :start (&Position :line :character)) name-range)) 7114 (cons line character))) 7115 7116 (defun lsp--collect-lines-and-cols (symbols) 7117 "Return a sorted list ((line . col) ...) of the locations of SYMBOLS." 7118 (let ((stack (mapcar 'identity symbols)) 7119 line-col-list) 7120 (while stack 7121 (let ((sym (pop stack))) 7122 (push (lsp--get-line-and-col sym) line-col-list) 7123 (unless (seq-empty-p (lsp:document-symbol-children? sym)) 7124 (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack))))) 7125 (-sort #'lsp--line-col-comparator line-col-list))) 7126 7127 (defun lsp--convert-line-col-to-points-batch (line-col-list) 7128 "Convert a sorted list of positions from line-column 7129 representation to point representation." 7130 (let ((line-col-to-point-map (ht-create)) 7131 (inhibit-field-text-motion t) 7132 (curr-line 0)) 7133 (lsp-save-restriction-and-excursion 7134 (goto-char (point-min)) 7135 (cl-loop for (line . col) in line-col-list do 7136 (forward-line (- line curr-line)) 7137 (setq curr-line line) 7138 (let ((line-end (line-end-position))) 7139 (if (or (not col) (> col (- line-end (point)))) 7140 (goto-char line-end) 7141 (forward-char col))) 7142 (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers 7143 (point-marker) 7144 (point))))) 7145 line-col-to-point-map)) 7146 7147 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2)) 7148 (or (< l1 l2) 7149 (and (= l1 l2) 7150 (cond ((and c1 c2) 7151 (< c1 c2)) 7152 (c1 t))))) 7153 7154 (defun lsp-imenu-create-uncategorized-index (symbols) 7155 "Create imenu index from document SYMBOLS. 7156 This function, unlike `lsp-imenu-create-categorized-index', does 7157 not categorize by type, but instead returns an `imenu' index 7158 corresponding to the symbol hierarchy returned by the server 7159 directly." 7160 (let* ((lsp--line-col-to-point-hash-table (-> symbols 7161 lsp--collect-lines-and-cols 7162 lsp--convert-line-col-to-points-batch))) 7163 (if (lsp--imenu-hierarchical-p symbols) 7164 (lsp--imenu-create-hierarchical-index symbols) 7165 (lsp--imenu-create-non-hierarchical-index symbols)))) 7166 7167 (defcustom lsp-imenu-symbol-kinds 7168 '((1 . "Files") 7169 (2 . "Modules") 7170 (3 . "Namespaces") 7171 (4 . "Packages") 7172 (5 . "Classes") 7173 (6 . "Methods") 7174 (7 . "Properties") 7175 (8 . "Fields") 7176 (9 . "Constructors") 7177 (10 . "Enums") 7178 (11 . "Interfaces") 7179 (12 . "Functions") 7180 (13 . "Variables") 7181 (14 . "Constants") 7182 (15 . "Strings") 7183 (16 . "Numbers") 7184 (17 . "Booleans") 7185 (18 . "Arrays") 7186 (19 . "Objects") 7187 (20 . "Keys") 7188 (21 . "Nulls") 7189 (22 . "Enum Members") 7190 (23 . "Structs") 7191 (24 . "Events") 7192 (25 . "Operators") 7193 (26 . "Type Parameters")) 7194 "`lsp-symbol-kinds', but only used by `imenu'. 7195 A new variable is needed, as it is `imenu' convention to use 7196 pluralized categories, which `lsp-symbol-kinds' doesn't. If the 7197 non-pluralized names are preferred, this can be set to 7198 `lsp-symbol-kinds'." 7199 :type '(alist :key-type integer :value-type string)) 7200 7201 (defun lsp--imenu-kind->name (kind) 7202 (alist-get kind lsp-imenu-symbol-kinds "?")) 7203 7204 (defun lsp-imenu-create-top-level-categorized-index (symbols) 7205 "Create an `imenu' index categorizing SYMBOLS by type. 7206 Only root symbols are categorized. 7207 7208 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS 7209 shall be a list of DocumentSymbols or SymbolInformation." 7210 (mapcan 7211 (-lambda ((type . symbols)) 7212 (let ((cat (lsp--imenu-kind->name type)) 7213 (symbols (lsp-imenu-create-uncategorized-index symbols))) 7214 ;; If there is no :kind (this is being defensive), or we couldn't look it 7215 ;; up, just display the symbols inline, without categories. 7216 (if cat (list (cons cat symbols)) symbols))) 7217 (sort (seq-group-by #'lsp:document-symbol-kind symbols) 7218 (-lambda ((kinda) (kindb)) (< kinda kindb))))) 7219 7220 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start))) 7221 "Convert an `&DocumentSymbol' to an `imenu' entry." 7222 (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start)) 7223 7224 (defun lsp--imenu-create-categorized-index-1 (symbols) 7225 "Returns an `imenu' index from SYMBOLS categorized by type. 7226 The result looks like this: ((\"Variables\" . (...)))." 7227 (->> 7228 symbols 7229 (mapcan 7230 (-lambda ((sym &as &DocumentSymbol :kind :children?)) 7231 (if (seq-empty-p children?) 7232 (list (list kind (lsp--symbol->imenu sym))) 7233 (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline 7234 (not lsp-imenu-hide-parent-details))))) 7235 (cons 7236 (list kind (lsp--symbol->imenu sym)) 7237 (mapcar (-lambda ((type . imenu-items)) 7238 (list type (cons parent (mapcan #'cdr imenu-items)))) 7239 (-group-by #'car (lsp--imenu-create-categorized-index-1 children?)))))))) 7240 (-group-by #'car) 7241 (mapcar 7242 (-lambda ((kind . syms)) 7243 (cons kind (mapcan #'cdr syms)))))) 7244 7245 (defun lsp--imenu-create-categorized-index (symbols) 7246 (let ((syms (lsp--imenu-create-categorized-index-1 symbols))) 7247 (dolist (sym syms) 7248 (setcar sym (lsp--imenu-kind->name (car sym)))) 7249 syms)) 7250 7251 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start)))) 7252 (cons (lsp-render-symbol-information sym nil) start)) 7253 7254 (defun lsp--imenu-create-categorized-index-flat (symbols) 7255 "Create a kind-categorized index for SymbolInformation." 7256 (mapcar (-lambda ((kind . syms)) 7257 (cons (lsp--imenu-kind->name kind) 7258 (mapcan (-lambda ((parent . children)) 7259 (let ((children (mapcar #'lsp--symbol-information->imenu children))) 7260 (if parent (list (cons parent children)) children))) 7261 (-group-by #'lsp:symbol-information-container-name? syms)))) 7262 (seq-group-by #'lsp:symbol-information-kind symbols))) 7263 7264 (defun lsp-imenu-create-categorized-index (symbols) 7265 (if (lsp--imenu-hierarchical-p symbols) 7266 (lsp--imenu-create-categorized-index symbols) 7267 (lsp--imenu-create-categorized-index-flat symbols))) 7268 7269 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index 7270 "Function that should create an `imenu' index. 7271 It will be called with a list of SymbolInformation or 7272 DocumentSymbols, whose first level is already filtered. It shall 7273 then return an appropriate `imenu' index (see 7274 `imenu-create-index-function'). 7275 7276 Note that this interface is not stable, and subject to change any 7277 time." 7278 :group 'lsp-imenu 7279 :type '(radio 7280 (const :tag "Categorize by type" 7281 lsp-imenu-create-categorized-index) 7282 (const :tag "Categorize root symbols by type" 7283 lsp-imenu-create-top-level-categorized-index) 7284 (const :tag "Uncategorized, inline entries" 7285 lsp-imenu-create-uncategorized-index) 7286 (function :tag "Custom function"))) 7287 7288 (defun lsp--imenu-create-index () 7289 "Create an `imenu' index based on the language server. 7290 Respects `lsp-imenu-index-function'." 7291 (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols)))) 7292 (funcall lsp-imenu-index-function symbols))) 7293 7294 (defun lsp--imenu-filter-symbols (symbols) 7295 "Filter out unsupported symbols from SYMBOLS." 7296 (seq-remove #'lsp--symbol-ignore symbols)) 7297 7298 (defun lsp--imenu-hierarchical-p (symbols) 7299 "Determine whether any element in SYMBOLS has children." 7300 (seq-some #'lsp-document-symbol? symbols)) 7301 7302 (defun lsp--imenu-create-non-hierarchical-index (symbols) 7303 "Create imenu index for non-hierarchical SYMBOLS. 7304 7305 SYMBOLS are a list of DocumentSymbol messages. 7306 7307 Return a nested alist keyed by symbol names. e.g. 7308 7309 ((\"SomeClass\" (\"(Class)\" . 10) 7310 (\"someField (Field)\" . 20) 7311 (\"someFunction (Function)\" . 25) 7312 (\"SomeSubClass\" (\"(Class)\" . 30) 7313 (\"someSubField (Field)\" . 35)) 7314 (\"someFunction (Function)\" . 40))" 7315 (seq-map (lambda (nested-alist) 7316 (cons (car nested-alist) 7317 (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist)))) 7318 (seq-group-by #'lsp--get-symbol-type symbols))) 7319 7320 (defun lsp--imenu-create-hierarchical-index (symbols) 7321 "Create imenu index for hierarchical SYMBOLS. 7322 7323 SYMBOLS are a list of DocumentSymbol messages. 7324 7325 Return a nested alist keyed by symbol names. e.g. 7326 7327 ((\"SomeClass\" (\"(Class)\" . 10) 7328 (\"someField (Field)\" . 20) 7329 (\"someFunction (Function)\" . 25) 7330 (\"SomeSubClass\" (\"(Class)\" . 30) 7331 (\"someSubField (Field)\" . 35)) 7332 (\"someFunction (Function)\" . 40))" 7333 (seq-map #'lsp--symbol-to-hierarchical-imenu-elem 7334 (seq-sort #'lsp--imenu-symbol-lessp symbols))) 7335 7336 (defun lsp--imenu-symbol-lessp (sym1 sym2) 7337 (let* ((compare-results (mapcar (lambda (method) 7338 (funcall (alist-get method lsp--imenu-compare-function-alist) 7339 sym1 sym2)) 7340 lsp-imenu-sort-methods)) 7341 (result (seq-find (lambda (result) 7342 (not (= result 0))) 7343 compare-results 7344 0))) 7345 (and (numberp result) (< result 0)))) 7346 7347 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left) 7348 (&SymbolInformation :kind right)) 7349 "Compare SYM1 and SYM2 by kind." 7350 (- left right)) 7351 7352 (defun lsp--imenu-compare-line-col (sym1 sym2) 7353 (if (lsp--line-col-comparator 7354 (lsp--get-line-and-col sym1) 7355 (lsp--get-line-and-col sym2)) 7356 -1 7357 1)) 7358 7359 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1) 7360 (&SymbolInformation :name name2)) 7361 "Compare SYM1 and SYM2 by name." 7362 (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2)))) 7363 (if (numberp result) result 0))) 7364 7365 (defun lsp--imenu-refresh () 7366 "Force Imenu to refresh itself." 7367 (imenu--menubar-select imenu--rescan-item)) 7368 7369 (defun lsp-enable-imenu () 7370 "Use lsp-imenu for the current buffer." 7371 (imenu--cleanup) 7372 (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index) 7373 (setq-local imenu-menubar-modified-tick -1) 7374 (setq-local imenu--index-alist nil) 7375 (when menu-bar-mode 7376 (lsp--imenu-refresh))) 7377 7378 (defun lsp-resolve-final-command (command &optional test?) 7379 "Resolve final function COMMAND." 7380 (let* ((command (lsp-resolve-value command)) 7381 (command (cl-etypecase command 7382 (list 7383 (cl-assert (seq-every-p (apply-partially #'stringp) command) nil 7384 "Invalid command list") 7385 command) 7386 (string (list command))))) 7387 (if (and (file-remote-p default-directory) (not test?)) 7388 (list shell-file-name "-c" 7389 (string-join (cons "stty raw > /dev/null;" 7390 (mapcar #'shell-quote-argument command)) 7391 " ")) 7392 command))) 7393 7394 (defun lsp-server-present? (final-command) 7395 "Check whether FINAL-COMMAND is present." 7396 (let ((binary-found? (executable-find (cl-first final-command) t))) 7397 (if binary-found? 7398 (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command)) 7399 (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command))) 7400 binary-found?)) 7401 7402 (defun lsp--value-to-string (value) 7403 "Convert VALUE to a string that can be set as value in an environment 7404 variable." 7405 (cond 7406 ((stringp value) value) 7407 ((booleanp value) (if value 7408 "1" 7409 "0")) 7410 ((and (sequencep value) 7411 (seq-every-p #'stringp value)) (string-join value ":")) 7412 (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables")))) 7413 7414 (defun lsp--compute-process-environment (environment-fn) 7415 "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'. 7416 Ignore non-boolean keys whose value is nil." 7417 (let ((environment (if environment-fn 7418 (funcall environment-fn) 7419 nil))) 7420 (-flatten (cons (cl-loop for (key . value) in environment 7421 if (or (eval value) 7422 (eq (get value 'custom-type) 'boolean)) 7423 collect (concat key "=" (lsp--value-to-string 7424 (eval value)))) 7425 process-environment)))) 7426 7427 (defun lsp--default-directory-for-connection (&optional path) 7428 "Return path to be used for the working directory of a LSP process. 7429 7430 If `lsp-use-workspace-root-for-server-default-directory' is 7431 non-nil, uses `lsp-workspace-root' to find the directory 7432 corresponding to PATH, else returns `default-directory'." 7433 (if lsp-use-workspace-root-for-server-default-directory 7434 (lsp-workspace-root path) 7435 default-directory)) 7436 7437 (defun lsp--fix-remote-cmd (program) 7438 "Helper for `lsp-stdio-connection'. 7439 Originally coppied from eglot." 7440 7441 (if (file-remote-p default-directory) 7442 (list shell-file-name "-c" 7443 (string-join (cons "stty raw > /dev/null;" 7444 (mapcar #'shell-quote-argument program)) 7445 " ")) 7446 program)) 7447 7448 (defvar tramp-use-ssh-controlmaster-options) 7449 (defvar tramp-ssh-controlmaster-options) 7450 7451 (defun lsp-stdio-connection (command &optional test-command) 7452 "Returns a connection property list using COMMAND. 7453 COMMAND can be: A string, denoting the command to launch the 7454 language server. A list of strings, denoting an executable with 7455 its command line arguments. A function, that either returns a 7456 string or a list of strings. In all cases, the launched language 7457 server should send and receive messages on standard I/O. 7458 TEST-COMMAND is a function with no arguments which returns 7459 whether the command is present or not. When not specified 7460 `lsp-mode' will check whether the first element of the list 7461 returned by COMMAND is available via `executable-find'" 7462 (cl-check-type command (or string 7463 function 7464 (and list 7465 (satisfies (lambda (l) 7466 (seq-every-p (lambda (el) 7467 (stringp el)) 7468 l)))))) 7469 (list :connect (lambda (filter sentinel name environment-fn workspace) 7470 (if (and (functionp 'json-rpc-connection) 7471 (not (file-remote-p default-directory))) 7472 (lsp-json-rpc-connection workspace (lsp-resolve-final-command command)) 7473 (let ((final-command (lsp-resolve-final-command command)) 7474 (process-name (generate-new-buffer-name name)) 7475 (process-environment 7476 (lsp--compute-process-environment environment-fn))) 7477 (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name))) 7478 (default-directory (lsp--default-directory-for-connection)) 7479 (tramp-use-ssh-controlmaster-options 'suppress) 7480 (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none") 7481 (proc (make-process 7482 :name process-name 7483 :connection-type 'pipe 7484 :buffer (format "*%s*" process-name) 7485 :coding 'no-conversion 7486 :command final-command 7487 :filter filter 7488 :sentinel sentinel 7489 :stderr stderr-buf 7490 :noquery t 7491 :file-handler t))) 7492 (set-process-query-on-exit-flag proc nil) 7493 (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) 7494 (with-current-buffer (get-buffer stderr-buf) 7495 ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc. 7496 (special-mode)) 7497 (cons proc proc))))) 7498 :test? (or 7499 test-command 7500 (lambda () 7501 (lsp-server-present? (lsp-resolve-final-command command t)))))) 7502 7503 (defun lsp--open-network-stream (host port name) 7504 "Open network stream to HOST:PORT. 7505 NAME will be passed to `open-network-stream'. 7506 RETRY-COUNT is the number of the retries. 7507 SLEEP-INTERVAL is the sleep interval between each retry." 7508 (let* ((retries 0) 7509 (sleep-interval 0.01) 7510 (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval)) 7511 connection) 7512 (while (and (not connection) (< retries number-of-retries)) 7513 (condition-case err 7514 (setq connection (open-network-stream name nil host port 7515 :type 'plain 7516 :coding 'no-conversion)) 7517 (file-error 7518 (let ((inhibit-message t)) 7519 (lsp--warn "Failed to connect to %s:%s with error message %s" 7520 host 7521 port 7522 (error-message-string err)) 7523 (sleep-for sleep-interval) 7524 (cl-incf retries))))) 7525 (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port)))) 7526 7527 (defun lsp--port-available (host port) 7528 "Return non-nil if HOST and PORT are available." 7529 (condition-case _err 7530 (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain)) 7531 (file-error t))) 7532 7533 (defun lsp--find-available-port (host starting-port) 7534 "Find available port on HOST starting from STARTING-PORT." 7535 (let ((port starting-port)) 7536 (while (not (lsp--port-available host port)) 7537 (cl-incf port)) 7538 port)) 7539 7540 (defun lsp-tcp-connection (command-fn) 7541 "Returns a connection property list similar to `lsp-stdio-connection'. 7542 COMMAND-FN can only be a function that takes a single argument, a 7543 port number. It should return a command for launches a language server 7544 process listening for TCP connections on the provided port." 7545 (cl-check-type command-fn function) 7546 (list 7547 :connect (lambda (filter sentinel name environment-fn _workspace) 7548 (let* ((host "localhost") 7549 (port (lsp--find-available-port host (cl-incf lsp--tcp-port))) 7550 (command (funcall command-fn port)) 7551 (final-command (if (consp command) command (list command))) 7552 (_ (unless (lsp-server-present? final-command) 7553 (user-error (format "Couldn't find executable %s" (cl-first final-command))))) 7554 (process-environment 7555 (lsp--compute-process-environment environment-fn)) 7556 (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion 7557 :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t)) 7558 (tcp-proc (lsp--open-network-stream host port (concat name "::tcp")))) 7559 7560 ;; TODO: Same :noquery issue (see above) 7561 (set-process-query-on-exit-flag proc nil) 7562 (set-process-query-on-exit-flag tcp-proc nil) 7563 (set-process-filter tcp-proc filter) 7564 (cons tcp-proc proc))) 7565 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7566 7567 (defalias 'lsp-tcp-server 'lsp-tcp-server-command) 7568 7569 (defun lsp-tcp-server-command (command-fn) 7570 "Create tcp server connection. 7571 In this mode Emacs is TCP server and the language server connects 7572 to it. COMMAND is function with one parameter(the port) and it 7573 should return the command to start the LS server." 7574 (cl-check-type command-fn function) 7575 (list 7576 :connect (lambda (filter sentinel name environment-fn _workspace) 7577 (let* (tcp-client-connection 7578 (tcp-server (make-network-process :name (format "*tcp-server-%s*" name) 7579 :buffer (format "*tcp-server-%s*" name) 7580 :family 'ipv4 7581 :service lsp--tcp-server-port 7582 :sentinel (lambda (proc _string) 7583 (lsp-log "Language server %s is connected." name) 7584 (setf tcp-client-connection proc)) 7585 :server 't)) 7586 (port (process-contact tcp-server :service)) 7587 (final-command (funcall command-fn port)) 7588 (process-environment 7589 (lsp--compute-process-environment environment-fn)) 7590 (cmd-proc (make-process :name name 7591 :connection-type 'pipe 7592 :coding 'no-conversion 7593 :command final-command 7594 :stderr (format "*tcp-server-%s*::stderr" name) 7595 :noquery t))) 7596 (let ((retries 0)) 7597 ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds) 7598 (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds))) 7599 (lsp--info "Waiting for connection for %s, retries: %s" name retries) 7600 (sit-for 0.500) 7601 (cl-incf retries))) 7602 7603 (unless tcp-client-connection 7604 (condition-case nil (delete-process tcp-server) (error)) 7605 (condition-case nil (delete-process cmd-proc) (error)) 7606 (error "Failed to create connection to %s on port %s" name port)) 7607 (lsp--info "Successfully connected to %s" name) 7608 7609 (set-process-query-on-exit-flag cmd-proc nil) 7610 (set-process-query-on-exit-flag tcp-client-connection nil) 7611 (set-process-query-on-exit-flag tcp-server nil) 7612 7613 (set-process-filter tcp-client-connection filter) 7614 (set-process-sentinel tcp-client-connection sentinel) 7615 (cons tcp-client-connection cmd-proc))) 7616 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7617 7618 (defalias 'lsp-tramp-connection 'lsp-stdio-connection) 7619 7620 (defun lsp--auto-configure () 7621 "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed." 7622 (when (functionp 'lsp-ui-mode) 7623 (lsp-ui-mode)) 7624 7625 (if lsp-headerline-breadcrumb-enable 7626 (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode) 7627 (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)) 7628 (if lsp-modeline-code-actions-enable 7629 (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode) 7630 (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)) 7631 (if lsp-modeline-diagnostics-enable 7632 (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode) 7633 (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)) 7634 (if lsp-modeline-workspace-status-enable 7635 (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode) 7636 (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)) 7637 (if lsp-lens-enable 7638 (add-hook 'lsp-configure-hook 'lsp-lens--enable) 7639 (remove-hook 'lsp-configure-hook 'lsp-lens--enable)) 7640 (if lsp-semantic-tokens-enable 7641 (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable) 7642 (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)) 7643 7644 ;; yas-snippet config 7645 (setq-local yas-inhibit-overlay-modification-protection t)) 7646 7647 (defun lsp--restart-if-needed (workspace) 7648 "Handler restart for WORKSPACE." 7649 (when (or (eq lsp-restart 'auto-restart) 7650 (eq (lsp--workspace-shutdown-action workspace) 'restart) 7651 (and (eq lsp-restart 'interactive) 7652 (let ((query (format 7653 "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?" 7654 (lsp--workspace-print workspace)))) 7655 (y-or-n-p query)))) 7656 (--each (lsp--workspace-buffers workspace) 7657 (when (lsp-buffer-live-p it) 7658 (lsp-with-current-buffer it 7659 (if lsp--buffer-deferred 7660 (lsp-deferred) 7661 (lsp--info "Restarting LSP in buffer %s" (buffer-name)) 7662 (lsp))))))) 7663 7664 (defun lsp--update-key (table key fn) 7665 "Apply FN on value corresponding to KEY in TABLE." 7666 (let ((existing-value (gethash key table))) 7667 (if-let ((new-value (funcall fn existing-value))) 7668 (puthash key new-value table) 7669 (remhash key table)))) 7670 7671 (defun lsp--process-sentinel (workspace process exit-str) 7672 "Create the sentinel for WORKSPACE." 7673 (unless (process-live-p process) 7674 (lsp--handle-process-exit workspace exit-str))) 7675 7676 (defun lsp--handle-process-exit (workspace exit-str) 7677 (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session))) 7678 (proc (lsp--workspace-proc workspace))) 7679 (lsp--warn "%s has exited (%s)" 7680 (lsp-process-name proc) 7681 (string-trim-right (or exit-str ""))) 7682 (with-lsp-workspace workspace 7683 ;; Clean workspace related data in each of the buffers 7684 ;; in the workspace. 7685 (--each (lsp--workspace-buffers workspace) 7686 (when (lsp-buffer-live-p it) 7687 (lsp-with-current-buffer it 7688 (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces)) 7689 (lsp--uninitialize-workspace) 7690 (lsp--spinner-stop) 7691 (lsp--remove-overlays 'lsp-highlight)))) 7692 7693 ;; Cleanup session from references to the closed workspace. 7694 (--each (hash-table-keys folder->workspaces) 7695 (lsp--update-key folder->workspaces it (apply-partially 'delete workspace))) 7696 7697 (lsp-process-cleanup proc)) 7698 7699 (run-hook-with-args 'lsp-after-uninitialized-functions workspace) 7700 7701 (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown) 7702 (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace)) 7703 (lsp--restart-if-needed workspace)) 7704 (lsp--cleanup-hanging-watches))) 7705 7706 (defun lsp-workspace-folders (workspace) 7707 "Return all folders associated with WORKSPACE." 7708 (let (result) 7709 (->> (lsp-session) 7710 (lsp-session-folder->servers) 7711 (maphash (lambda (folder workspaces) 7712 (when (-contains? workspaces workspace) 7713 (push folder result))))) 7714 result)) 7715 7716 (defun lsp--start-workspace (session client-template root &optional initialization-options) 7717 "Create new workspace for CLIENT-TEMPLATE with project root ROOT. 7718 INITIALIZATION-OPTIONS are passed to initialize function. 7719 SESSION is the active session." 7720 (lsp--spinner-start) 7721 (-let* ((default-directory root) 7722 (client (copy-lsp--client client-template)) 7723 (workspace (make-lsp--workspace 7724 :root root 7725 :client client 7726 :status 'starting 7727 :buffers (list (lsp-current-buffer)) 7728 :host-root (file-remote-p root))) 7729 ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities 7730 'multi-root 'initialized-fn) client) 7731 ((proc . cmd-proc) (funcall 7732 (or (plist-get new-connection :connect) 7733 (user-error "Client %s is configured incorrectly" client)) 7734 (lsp--create-filter-function workspace) 7735 (apply-partially #'lsp--process-sentinel workspace) 7736 (format "%s" server-id) 7737 environment-fn 7738 workspace)) 7739 (workspace-folders (gethash server-id (lsp-session-server-id->folders session)))) 7740 (setf (lsp--workspace-proc workspace) proc 7741 (lsp--workspace-cmd-proc workspace) cmd-proc) 7742 7743 ;; update (lsp-session-folder->servers) depending on whether we are starting 7744 ;; multi/single folder workspace 7745 (mapc (lambda (project-root) 7746 (->> session 7747 (lsp-session-folder->servers) 7748 (gethash project-root) 7749 (cl-pushnew workspace))) 7750 (or workspace-folders (list root))) 7751 7752 (with-lsp-workspace workspace 7753 (run-hooks 'lsp-before-initialize-hook) 7754 (lsp-request-async 7755 "initialize" 7756 (append 7757 (list :processId (unless (file-remote-p (buffer-file-name)) 7758 (emacs-pid)) 7759 :rootPath (lsp-file-local-name (expand-file-name root)) 7760 :clientInfo (list :name "emacs" 7761 :version (emacs-version)) 7762 :rootUri (lsp--path-to-uri root) 7763 :capabilities (lsp--client-capabilities custom-capabilities) 7764 :initializationOptions initialization-options 7765 :workDoneToken "1") 7766 (when lsp-server-trace 7767 (list :trace lsp-server-trace)) 7768 (when multi-root 7769 (->> workspace-folders 7770 (-distinct) 7771 (-map (lambda (folder) 7772 (list :uri (lsp--path-to-uri folder) 7773 :name (f-filename folder)))) 7774 (apply 'vector) 7775 (list :workspaceFolders)))) 7776 (-lambda ((&InitializeResult :capabilities)) 7777 ;; we know that Rust Analyzer will send {} which will be parsed as null 7778 ;; when using plists 7779 (when (equal 'rust-analyzer server-id) 7780 (-> capabilities 7781 (lsp:server-capabilities-text-document-sync?) 7782 (lsp:set-text-document-sync-options-save? t))) 7783 7784 (setf (lsp--workspace-server-capabilities workspace) capabilities 7785 (lsp--workspace-status workspace) 'initialized) 7786 7787 (with-lsp-workspace workspace 7788 (lsp-notify "initialized" lsp--empty-ht)) 7789 7790 (when initialized-fn (funcall initialized-fn workspace)) 7791 7792 (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace)) 7793 (->> workspace 7794 (lsp--workspace-buffers) 7795 (mapc (lambda (buffer) 7796 (lsp-with-current-buffer buffer 7797 (lsp--open-in-workspace workspace))))) 7798 7799 (with-lsp-workspace workspace 7800 (run-hooks 'lsp-after-initialize-hook)) 7801 (lsp--info "%s initialized successfully in folders: %s" 7802 (lsp--workspace-print workspace) 7803 (lsp-workspace-folders workspace))) 7804 :mode 'detached)) 7805 workspace)) 7806 7807 (defun lsp--load-default-session () 7808 "Load default session." 7809 (setq lsp--session (or (condition-case err 7810 (lsp--read-from-file lsp-session-file) 7811 (error (lsp--error "Failed to parse the session %s, starting with clean one." 7812 (error-message-string err)) 7813 nil)) 7814 (make-lsp-session)))) 7815 7816 (defun lsp-session () 7817 "Get the session associated with the current buffer." 7818 (or lsp--session (setq lsp--session (lsp--load-default-session)))) 7819 7820 (defun lsp--client-disabled-p (buffer-major-mode client) 7821 (seq-some 7822 (lambda (entry) 7823 (pcase entry 7824 ((pred symbolp) (eq entry client)) 7825 (`(,mode . ,client-or-list) 7826 (and (eq mode buffer-major-mode) 7827 (if (listp client-or-list) 7828 (memq client client-or-list) 7829 (eq client client-or-list)))))) 7830 lsp-disabled-clients)) 7831 7832 7833 ;; download server 7834 7835 (defcustom lsp-server-install-dir (expand-file-name 7836 (locate-user-emacs-file (f-join ".cache" "lsp"))) 7837 "Directory in which the servers will be installed." 7838 :risky t 7839 :type 'directory 7840 :package-version '(lsp-mode . "6.3") 7841 :group 'lsp-mode) 7842 7843 (defcustom lsp-verify-signature t 7844 "Whether to check GPG signatures of downloaded files." 7845 :type 'boolean 7846 :package-version '(lsp-mode . "8.0.0") 7847 :group 'lsp-mode) 7848 7849 (defvar lsp--dependencies (ht)) 7850 7851 (defun lsp-dependency (name &rest definitions) 7852 "Used to specify a language server DEPENDENCY, the server 7853 executable or other required file path. Typically, the 7854 DEPENDENCY is found by locating it on the system path using 7855 `executable-find'. 7856 7857 You can explicitly call lsp-dependency in your environment to 7858 specify the absolute path to the DEPENDENCY. For example, the 7859 typescript-language-server requires both the server and the 7860 typescript compiler. If you have installed them in a team shared 7861 read-only location, you can instruct lsp-mode to use them via 7862 7863 (eval-after-load `lsp-mode 7864 `(progn 7865 (require lsp-javascript) 7866 (lsp-dependency typescript-language-server (:system ,tls-exe)) 7867 (lsp-dependency typescript (:system ,ts-js)))) 7868 7869 where tls-exe is the absolute path to the typescript-language-server 7870 executable and ts-js is the absolute path to the typescript compiler 7871 JavaScript file, tsserver.js (the *.js is required for Windows)." 7872 (ht-set lsp--dependencies name definitions)) 7873 7874 (defun lsp--server-binary-present? (client) 7875 (unless (equal (lsp--client-server-id client) 'lsp-pwsh) 7876 (condition-case () 7877 (-some-> client lsp--client-new-connection (plist-get :test?) funcall) 7878 (error nil) 7879 (args-out-of-range nil)))) 7880 7881 (define-minor-mode lsp-installation-buffer-mode 7882 "Mode used in *lsp-installation* buffers. 7883 It can be used to set-up keybindings, etc. Disabling this mode 7884 detaches the installation buffer from commands like 7885 `lsp-select-installation-buffer'." 7886 :init-value nil 7887 :lighter nil) 7888 7889 (defface lsp-installation-finished-buffer-face '((t :foreground "orange")) 7890 "Face used for finished installation buffers. 7891 Used in `lsp-select-installation-buffer'." 7892 :group 'lsp-mode) 7893 7894 (defface lsp-installation-buffer-face '((t :foreground "green")) 7895 "Face used for installation buffers still in progress. 7896 Used in `lsp-select-installation-buffer'." 7897 :group 'lsp-mode) 7898 7899 (defun lsp--installation-buffer? (buf) 7900 "Check whether BUF is an `lsp-async-start-process' buffer." 7901 (buffer-local-value 'lsp-installation-buffer-mode buf)) 7902 7903 (defun lsp-select-installation-buffer (&optional show-finished) 7904 "Interactively choose an installation buffer. 7905 If SHOW-FINISHED is set, leftover (finished) installation buffers 7906 are still shown." 7907 (interactive "P") 7908 (let ((bufs (--filter (and (lsp--installation-buffer? it) 7909 (or show-finished (get-buffer-process it))) 7910 (buffer-list)))) 7911 (pcase bufs 7912 (`nil (user-error "No installation buffers")) 7913 (`(,buf) (pop-to-buffer buf)) 7914 (bufs (pop-to-buffer (completing-read "Select installation buffer: " 7915 (--map (propertize (buffer-name it) 'face 7916 (if (get-buffer-process it) 7917 'lsp-installation-buffer-face 7918 'lsp-installation-finished-buffer-face)) 7919 bufs))))))) 7920 7921 (defun lsp-cleanup-installation-buffers () 7922 "Delete finished *lsp-installation* buffers." 7923 (interactive) 7924 (dolist (buf (buffer-list)) 7925 (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf))) 7926 (kill-buffer buf)))) 7927 7928 (defun lsp--download-status () 7929 (-some--> #'lsp--client-download-in-progress? 7930 (lsp--filter-clients it) 7931 (-map (-compose #'symbol-name #'lsp--client-server-id) it) 7932 (format "%s" it) 7933 (propertize it 'face 'success) 7934 (format " Installing following servers: %s" it) 7935 (propertize it 7936 'local-map (make-mode-line-mouse-map 7937 'mouse-1 #'lsp-select-installation-buffer) 7938 'mouse-face 'highlight))) 7939 7940 (defun lsp--install-server-internal (client &optional update?) 7941 (unless (lsp--client-download-server-fn client) 7942 (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation." 7943 (lsp--client-server-id client))) 7944 7945 (setf (lsp--client-download-in-progress? client) t) 7946 (add-to-list 'global-mode-string '(t (:eval (lsp--download-status)))) 7947 (cl-flet ((done 7948 (success? &optional error-message) 7949 ;; run with idle timer to make sure the lsp command is executed in 7950 ;; the main thread, see #2739. 7951 (run-with-timer 7952 0.0 7953 nil 7954 (lambda () 7955 (-let [(&lsp-cln 'server-id 'buffers) client] 7956 (setf (lsp--client-download-in-progress? client) nil 7957 (lsp--client-buffers client) nil) 7958 (if success? 7959 (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id 7960 (length buffers)) 7961 (lsp--error "Server %s install process failed with the following error message: %s. 7962 Check `*lsp-install*' and `*lsp-log*' buffer." 7963 server-id 7964 error-message)) 7965 (seq-do 7966 (lambda (buffer) 7967 (when (lsp-buffer-live-p buffer) 7968 (lsp-with-current-buffer buffer 7969 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 7970 global-mode-string) 7971 (when success? (lsp))))) 7972 buffers) 7973 (unless (lsp--filter-clients #'lsp--client-download-in-progress?) 7974 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 7975 global-mode-string))))))) 7976 (lsp--info "Download %s started." (lsp--client-server-id client)) 7977 (condition-case err 7978 (funcall 7979 (lsp--client-download-server-fn client) 7980 client 7981 (lambda () (done t)) 7982 (lambda (msg) (done nil msg)) 7983 update?) 7984 (error 7985 (done nil (error-message-string err)))))) 7986 7987 (defun lsp--require-packages () 7988 "Load `lsp-client-packages' if needed." 7989 (when (and lsp-auto-configure (not lsp--client-packages-required)) 7990 (seq-do (lambda (package) 7991 ;; loading client is slow and `lsp' can be called repeatedly 7992 (unless (featurep package) 7993 (require package nil t))) 7994 lsp-client-packages) 7995 (setq lsp--client-packages-required t))) 7996 7997 ;;;###autoload 7998 (defun lsp-install-server (update? &optional server-id) 7999 "Interactively install or re-install server. 8000 When prefix UPDATE? is t force installation even if the server is present." 8001 (interactive "P") 8002 (lsp--require-packages) 8003 (let* ((chosen-client (or (gethash server-id lsp-clients) 8004 (lsp--completing-read 8005 "Select server to install/re-install: " 8006 (or (->> lsp-clients 8007 (ht-values) 8008 (-filter (-andfn 8009 (-not #'lsp--client-download-in-progress?) 8010 #'lsp--client-download-server-fn))) 8011 (user-error "There are no servers with automatic installation")) 8012 (lambda (client) 8013 (let ((server-name (-> client lsp--client-server-id symbol-name))) 8014 (if (lsp--server-binary-present? client) 8015 (concat server-name " (Already installed)") 8016 server-name))) 8017 nil 8018 t))) 8019 (update? (or update? 8020 (and (not (lsp--client-download-in-progress? chosen-client)) 8021 (lsp--server-binary-present? chosen-client))))) 8022 (lsp--install-server-internal chosen-client update?))) 8023 8024 ;;;###autoload 8025 (defun lsp-uninstall-server (dir) 8026 "Delete a LSP server from `lsp-server-install-dir'." 8027 (interactive 8028 (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir)))) 8029 (unless (file-directory-p dir) 8030 (user-error "Couldn't find %s directory" dir)) 8031 (delete-directory dir 'recursive) 8032 (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir)))) 8033 8034 ;;;###autoload 8035 (defun lsp-uninstall-servers () 8036 "Uninstall all installed servers." 8037 (interactive) 8038 (let* ((dir lsp-server-install-dir) 8039 (servers (ignore-errors 8040 (directory-files dir t 8041 directory-files-no-dot-files-regexp)))) 8042 (if (or (not (file-directory-p dir)) (zerop (length servers))) 8043 (user-error "No servers to uninstall") 8044 (when (yes-or-no-p 8045 (format "Servers to uninstall: %d (%s), proceed? " 8046 (length servers) 8047 (mapconcat (lambda (server) 8048 (file-name-nondirectory (directory-file-name server))) 8049 servers " "))) 8050 (mapc #'lsp-uninstall-server servers) 8051 (message "All servers uninstalled"))))) 8052 8053 ;;;###autoload 8054 (defun lsp-update-server (&optional server-id) 8055 "Interactively update (reinstall) a server." 8056 (interactive) 8057 (lsp--require-packages) 8058 (let ((chosen-client (or (gethash server-id lsp-clients) 8059 (lsp--completing-read 8060 "Select server to update (if not on the list, probably you need to `lsp-install-server`): " 8061 (or (->> lsp-clients 8062 (ht-values) 8063 (-filter (-andfn 8064 (-not #'lsp--client-download-in-progress?) 8065 #'lsp--client-download-server-fn 8066 #'lsp--server-binary-present?))) 8067 (user-error "There are no servers to update")) 8068 (lambda (client) 8069 (-> client lsp--client-server-id symbol-name)) 8070 nil 8071 t)))) 8072 (lsp--install-server-internal chosen-client t))) 8073 8074 ;;;###autoload 8075 (defun lsp-update-servers () 8076 "Update (reinstall) all installed servers." 8077 (interactive) 8078 (lsp--require-packages) 8079 (mapc (lambda (client) (lsp--install-server-internal client t)) 8080 (-filter (-andfn 8081 (-not #'lsp--client-download-in-progress?) 8082 #'lsp--client-download-server-fn 8083 #'lsp--server-binary-present?) (hash-table-values lsp-clients)))) 8084 8085 ;;;###autoload 8086 (defun lsp-ensure-server (server-id) 8087 "Ensure server SERVER-ID" 8088 (lsp--require-packages) 8089 (if-let ((client (gethash server-id lsp-clients))) 8090 (unless (lsp--server-binary-present? client) 8091 (lsp--info "Server `%s' is not preset, installing..." server-id) 8092 (lsp-install-server nil server-id)) 8093 (warn "Unable to find server registration with id %s" server-id))) 8094 8095 (defun lsp-async-start-process (callback error-callback &rest command) 8096 "Start async process COMMAND with CALLBACK and ERROR-CALLBACK." 8097 (let ((name (cl-first command))) 8098 (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd) 8099 (not (null cmd))) 8100 command) 8101 " ") t 8102 (lambda (&rest _) 8103 (generate-new-buffer-name (format "*lsp-install: %s*" name)))) 8104 (lsp-installation-buffer-mode +1) 8105 (view-mode +1) 8106 (add-hook 8107 'compilation-finish-functions 8108 (lambda (_buf status) 8109 (if (string= "finished\n" status) 8110 (condition-case err 8111 (funcall callback) 8112 (error 8113 (funcall error-callback (error-message-string err)))) 8114 (funcall error-callback (s-trim-right status)))) 8115 nil t)))) 8116 8117 (defun lsp-resolve-value (value) 8118 "Resolve VALUE's value. 8119 If it is function - call it. 8120 If it is a variable - return it's value 8121 Otherwise returns value itself." 8122 (cond 8123 ((functionp value) (funcall value)) 8124 ((and (symbolp value) (boundp value)) (symbol-value value)) 8125 (value))) 8126 8127 (defvar lsp-deps-providers 8128 (list :npm (list :path #'lsp--npm-dependency-path 8129 :install #'lsp--npm-dependency-install) 8130 :cargo (list :path #'lsp--cargo-dependency-path 8131 :install #'lsp--cargo-dependency-install) 8132 :system (list :path #'lsp--system-path) 8133 :download (list :path #'lsp-download-path 8134 :install #'lsp-download-install))) 8135 8136 (defun lsp--system-path (path) 8137 "If PATH is absolute and exists return it as is. Otherwise, 8138 return the absolute path to the executable defined by PATH or 8139 nil." 8140 ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the 8141 ;; typescript-language-server. When lsp invokes the server, lsp needs to 8142 ;; supply the path to the typescript compiler, tsserver.js, as an argument. To 8143 ;; make code platform independent, one must pass the absolute path to the 8144 ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript 8145 ;; child process spawn command that is invoked by the 8146 ;; typescript-language-server). This is why we check for existence and not 8147 ;; that the path is executable. 8148 (let ((path (lsp-resolve-value path))) 8149 (cond 8150 ((and (f-absolute? path) 8151 (f-exists? path)) 8152 path) 8153 ((executable-find path t) path)))) 8154 8155 (defun lsp-package-path (dependency) 8156 "Path to the DEPENDENCY each of the registered providers." 8157 (let (path) 8158 (-first (-lambda ((provider . rest)) 8159 (setq path (-some-> lsp-deps-providers 8160 (plist-get provider) 8161 (plist-get :path) 8162 (apply rest)))) 8163 (gethash dependency lsp--dependencies)) 8164 path)) 8165 8166 (defun lsp-package-ensure (dependency callback error-callback) 8167 "Asynchronously ensure a package." 8168 (or (-first (-lambda ((provider . rest)) 8169 (-some-> lsp-deps-providers 8170 (plist-get provider) 8171 (plist-get :install) 8172 (apply (cl-list* callback error-callback rest)))) 8173 (gethash dependency lsp--dependencies)) 8174 (funcall error-callback (format "Unable to find a way to install %s" dependency)))) 8175 8176 8177 ;; npm handling 8178 8179 ;; https://docs.npmjs.com/files/folders#executables 8180 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys) 8181 "Return npm dependency PATH for PACKAGE." 8182 (let ((path (executable-find 8183 (f-join lsp-server-install-dir "npm" package 8184 (cond ((eq system-type 'windows-nt) "") 8185 (t "bin")) 8186 path) 8187 t))) 8188 (unless (and path (f-exists? path)) 8189 (error "The package %s is not installed. Unable to find %s" package path)) 8190 path)) 8191 8192 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys) 8193 (if-let ((npm-binary (executable-find "npm"))) 8194 (progn 8195 ;; Explicitly `make-directory' to work around NPM bug in 8196 ;; versions 7.0.0 through 7.4.1. See 8197 ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for 8198 ;; discussion. 8199 (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents) 8200 (lsp-async-start-process (lambda () 8201 (if (string-empty-p 8202 (string-trim (shell-command-to-string 8203 (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " ")))) 8204 (funcall callback) 8205 (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json"))))) 8206 (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx 8207 (when (f-dir-p default-directory) 8208 (lsp-async-start-process callback 8209 error-callback 8210 (executable-find "npx") 8211 "npm-install-peers"))))) 8212 error-callback 8213 npm-binary 8214 "-g" 8215 "--prefix" 8216 (f-join lsp-server-install-dir "npm" package) 8217 "install" 8218 package)) 8219 (lsp-log "Unable to install %s via `npm' because it is not present" package) 8220 nil)) 8221 8222 8223 ;; Cargo dependency handling 8224 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys) 8225 (let ((path (executable-find 8226 (f-join lsp-server-install-dir 8227 "cargo" 8228 package 8229 "bin" 8230 path) 8231 t))) 8232 (unless (and path (f-exists? path)) 8233 (error "The package %s is not installed. Unable to find %s" package path)) 8234 path)) 8235 8236 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys) 8237 (if-let ((cargo-binary (executable-find "cargo"))) 8238 (lsp-async-start-process 8239 callback 8240 error-callback 8241 cargo-binary 8242 "install" 8243 package 8244 (when git 8245 "--git") 8246 git 8247 "--root" 8248 (f-join lsp-server-install-dir "cargo" package)) 8249 (lsp-log "Unable to install %s via `cargo' because it is not present" package) 8250 nil)) 8251 8252 8253 8254 ;; Download URL handling 8255 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys) 8256 (let* ((url (lsp-resolve-value url)) 8257 (store-path (lsp-resolve-value store-path)) 8258 ;; (decompress (lsp-resolve-value decompress)) 8259 (download-path 8260 (pcase decompress 8261 (:gzip (concat store-path ".gz")) 8262 (:zip (concat store-path ".zip")) 8263 (:targz (concat store-path ".tar.gz")) 8264 (`nil store-path) 8265 (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'"))))) 8266 (make-thread 8267 (lambda () 8268 (condition-case err 8269 (progn 8270 (when (f-exists? download-path) 8271 (f-delete download-path)) 8272 (when (f-exists? store-path) 8273 (f-delete store-path)) 8274 (lsp--info "Starting to download %s to %s..." url download-path) 8275 (mkdir (f-parent download-path) t) 8276 (url-copy-file url download-path) 8277 (lsp--info "Finished downloading %s..." download-path) 8278 (when (and lsp-verify-signature asc-url pgp-key) 8279 (if (executable-find epg-gpg-program) 8280 (let ((asc-download-path (concat download-path ".asc")) 8281 (context (epg-make-context)) 8282 (fingerprint) 8283 (signature)) 8284 (when (f-exists? asc-download-path) 8285 (f-delete asc-download-path)) 8286 (lsp--info "Starting to download %s to %s..." asc-url asc-download-path) 8287 (url-copy-file asc-url asc-download-path) 8288 (lsp--info "Finished downloading %s..." asc-download-path) 8289 (epg-import-keys-from-string context pgp-key) 8290 (setq fingerprint (epg-import-status-fingerprint 8291 (car 8292 (epg-import-result-imports 8293 (epg-context-result-for context 'import))))) 8294 (lsp--info "Verifying signature %s..." asc-download-path) 8295 (epg-verify-file context asc-download-path download-path) 8296 (setq signature (car (epg-context-result-for context 'verify))) 8297 (unless (and 8298 (eq (epg-signature-status signature) 'good) 8299 (equal (epg-signature-fingerprint signature) fingerprint)) 8300 (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature)))) 8301 (lsp--warn "GPG is not installed, skipping the signature check."))) 8302 (when decompress 8303 (lsp--info "Decompressing %s..." download-path) 8304 (pcase decompress 8305 (:gzip 8306 (lsp-gunzip download-path)) 8307 (:zip (lsp-unzip download-path (f-parent store-path))) 8308 (:targz (lsp-tar-gz-decompress download-path (f-parent store-path)))) 8309 (lsp--info "Decompressed %s..." store-path)) 8310 (funcall callback)) 8311 (error (funcall error-callback err))))))) 8312 8313 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys) 8314 "Download URL and store it into STORE-PATH. 8315 8316 SET-EXECUTABLE? when non-nil change the executable flags of 8317 STORE-PATH to make it executable. BINARY-PATH can be specified 8318 when the binary to start does not match the name of the 8319 archive (e.g. when the archive has multiple files)" 8320 (let ((store-path (or (lsp-resolve-value binary-path) 8321 (lsp-resolve-value store-path)))) 8322 (cond 8323 ((executable-find store-path) store-path) 8324 ((and set-executable? (f-exists? store-path)) 8325 (set-file-modes store-path #o0700) 8326 store-path) 8327 ((f-exists? store-path) store-path)))) 8328 8329 (defun lsp--find-latest-gh-release-url (url regex) 8330 "Fetch the latest version in the releases given by URL by using REGEX." 8331 (let ((url-request-method "GET")) 8332 (with-current-buffer (url-retrieve-synchronously url) 8333 (goto-char (point-min)) 8334 (re-search-forward "\n\n" nil 'noerror) 8335 (delete-region (point-min) (point)) 8336 (let* ((json-result (lsp-json-read-buffer))) 8337 (message "Latest version found: %s" (lsp-get json-result :tag_name)) 8338 (--> json-result 8339 (lsp-get it :assets) 8340 (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it) 8341 (lsp-get it :browser_download_url)))))) 8342 8343 ;; unzip 8344 8345 (defconst lsp-ext-pwsh-script "powershell -noprofile -noninteractive \ 8346 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'" 8347 "Powershell script to unzip file.") 8348 8349 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'" 8350 "Unzip script to unzip file.") 8351 8352 (defcustom lsp-unzip-script (lambda () 8353 (cond ((executable-find "unzip") lsp-ext-unzip-script) 8354 ((executable-find "powershell") lsp-ext-pwsh-script) 8355 (t nil))) 8356 "The script to unzip." 8357 :group 'lsp-mode 8358 :type 'string 8359 :package-version '(lsp-mode . "8.0.0")) 8360 8361 (defun lsp-unzip (zip-file dest) 8362 "Unzip ZIP-FILE to DEST." 8363 (unless lsp-unzip-script 8364 (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'")) 8365 (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest))) 8366 8367 ;; gunzip 8368 8369 (defconst lsp-ext-gunzip-script "gzip -d %1$s" 8370 "Script to decompress a gzippped file with gzip.") 8371 8372 (defcustom lsp-gunzip-script (lambda () 8373 (cond ((executable-find "gzip") lsp-ext-gunzip-script) 8374 (t nil))) 8375 "The script to decompress a gzipped file. 8376 Should be a format string with one argument for the file to be decompressed 8377 in place." 8378 :group 'lsp-mode 8379 :type 'string 8380 :package-version '(lsp-mode . "8.0.0")) 8381 8382 (defun lsp-gunzip (gz-file) 8383 "Decompress GZ-FILE in place." 8384 (unless lsp-gunzip-script 8385 (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file)) 8386 (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file))) 8387 8388 ;; tar.gz decompression 8389 8390 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'" 8391 "Script to decompress a .tar.gz file.") 8392 8393 (defcustom lsp-tar-script (lambda () 8394 (cond ((executable-find "tar") lsp-ext-tar-script) 8395 (t nil))) 8396 "The script to decompress a .tar.gz file. 8397 Should be a format string with one argument for the file to be decompressed 8398 in place." 8399 :group 'lsp-mode 8400 :type 'string) 8401 8402 (defun lsp-tar-gz-decompress (targz-file dest) 8403 "Decompress TARGZ-FILE in DEST." 8404 (unless lsp-tar-script 8405 (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file)) 8406 (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest))) 8407 8408 8409 ;; VSCode marketplace 8410 8411 (defcustom lsp-vscode-ext-url 8412 "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s" 8413 "Vscode extension template url." 8414 :group 'lsp-mode 8415 :type 'string 8416 :package-version '(lsp-mode . "8.0.0")) 8417 8418 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform) 8419 "Return the URL to vscode extension. 8420 PUBLISHER is the extension publisher. 8421 NAME is the name of the extension. 8422 VERSION is the version of the extension. 8423 TARGETPLATFORM is the targetPlatform of the extension." 8424 (format lsp-vscode-ext-url publisher name version (or targetPlatform ""))) 8425 8426 8427 8428 ;; Queueing prompts 8429 8430 (defvar lsp--question-queue nil 8431 "List of questions yet to be asked by `lsp-ask-question'.") 8432 8433 (defun lsp-ask-question (question options callback) 8434 "Prompt the user to answer the QUESTION with one of the OPTIONS from the 8435 minibuffer. Once the user selects an option, the CALLBACK function will be 8436 called, passing the selected option to it. 8437 8438 If the user is currently being shown a question, the question will be stored in 8439 `lsp--question-queue', and will be asked once the user has answered the current 8440 question." 8441 (add-to-list 'lsp--question-queue `(("question" . ,question) 8442 ("options" . ,options) 8443 ("callback" . ,callback)) t) 8444 (when (eq (length lsp--question-queue) 1) 8445 (lsp--process-question-queue))) 8446 8447 (defun lsp--process-question-queue () 8448 "Take the first question from `lsp--question-queue', process it, then process 8449 the next question until the queue is empty." 8450 (-let* (((&alist "question" "options" "callback") (car lsp--question-queue)) 8451 (answer (completing-read question options nil t))) 8452 (pop lsp--question-queue) 8453 (funcall callback answer) 8454 (when lsp--question-queue 8455 (lsp--process-question-queue)))) 8456 8457 (defun lsp--supports-buffer? (client) 8458 (and 8459 ;; both file and client remote or both local 8460 (eq (---truthy? (file-remote-p (buffer-file-name))) 8461 (---truthy? (lsp--client-remote? client))) 8462 8463 ;; activation function or major-mode match. 8464 (if-let ((activation-fn (lsp--client-activation-fn client))) 8465 (funcall activation-fn (buffer-file-name) major-mode) 8466 (-contains? (lsp--client-major-modes client) major-mode)) 8467 8468 ;; check whether it is enabled if `lsp-enabled-clients' is not null 8469 (or (null lsp-enabled-clients) 8470 (or (member (lsp--client-server-id client) lsp-enabled-clients) 8471 (ignore (lsp--info "Client %s is not in lsp-enabled-clients" 8472 (lsp--client-server-id client))))) 8473 8474 ;; check whether it is not disabled. 8475 (not (lsp--client-disabled-p major-mode (lsp--client-server-id client))))) 8476 8477 (defun lsp--filter-clients (pred) 8478 (->> lsp-clients hash-table-values (-filter pred))) 8479 8480 (defun lsp--find-clients () 8481 "Find clients which can handle current buffer." 8482 (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 8483 #'lsp--server-binary-present?))) 8484 (lsp-log "Found the following clients for %s: %s" 8485 (buffer-file-name) 8486 (s-join ", " 8487 (-map (lambda (client) 8488 (format "(server-id %s, priority %s)" 8489 (lsp--client-server-id client) 8490 (lsp--client-priority client))) 8491 matching-clients))) 8492 (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients)) 8493 (selected-clients (if-let ((main-client (and main-clients 8494 (--max-by (> (lsp--client-priority it) 8495 (lsp--client-priority other)) 8496 main-clients)))) 8497 (cons main-client add-on-clients) 8498 add-on-clients))) 8499 (lsp-log "The following clients were selected based on priority: %s" 8500 (s-join ", " 8501 (-map (lambda (client) 8502 (format "(server-id %s, priority %s)" 8503 (lsp--client-server-id client) 8504 (lsp--client-priority client))) 8505 selected-clients))) 8506 selected-clients))) 8507 8508 (defun lsp-workspace-remove-all-folders() 8509 "Delete all lsp tracked folders." 8510 (interactive) 8511 (--each (lsp-session-folders (lsp-session)) 8512 (lsp-workspace-folders-remove it))) 8513 8514 (defun lsp-register-client (client) 8515 "Registers LSP client CLIENT." 8516 (let ((client-id (lsp--client-server-id client))) 8517 (puthash client-id client lsp-clients) 8518 (setplist (intern (format "lsp-%s-after-open-hook" client-id)) 8519 `( standard-value (nil) custom-type hook 8520 custom-package-version (lsp-mode . "7.0.1") 8521 variable-documentation ,(format "Hooks to run after `%s' server is run." client-id) 8522 custom-requests nil))) 8523 (when (and lsp-auto-register-remote-clients 8524 (not (lsp--client-remote? client))) 8525 (let ((remote-client (copy-lsp--client client))) 8526 (setf (lsp--client-remote? remote-client) t 8527 (lsp--client-server-id remote-client) (intern 8528 (format "%s-tramp" 8529 (lsp--client-server-id client))) 8530 ;; disable automatic download 8531 (lsp--client-download-server-fn remote-client) nil) 8532 (lsp-register-client remote-client)))) 8533 8534 (defun lsp--create-initialization-options (_session client) 8535 "Create initialization-options from SESSION and CLIENT. 8536 Add workspace folders depending on server being multiroot and 8537 session workspace folder configuration for the server." 8538 (let* ((initialization-options-or-fn (lsp--client-initialization-options client))) 8539 (if (functionp initialization-options-or-fn) 8540 (funcall initialization-options-or-fn) 8541 initialization-options-or-fn))) 8542 8543 (defvar lsp-client-settings (make-hash-table :test 'equal) 8544 "For internal use, any external users please use 8545 `lsp-register-custom-settings' function instead") 8546 8547 (defun lsp-register-custom-settings (props) 8548 "Register PROPS. 8549 PROPS is list of triple (path value boolean?) where PATH is the path to the 8550 property; VALUE can be a literal value, symbol to be evaluated, or either a 8551 function or lambda function to be called without arguments; BOOLEAN? is an 8552 optional flag that should be non-nil for boolean settings, when it is nil the 8553 property will be ignored if the VALUE is nil. 8554 8555 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))' 8556 \(note the double parentheses)" 8557 (mapc 8558 (-lambda ((path . rest)) 8559 (puthash path rest lsp-client-settings)) 8560 props)) 8561 8562 (defun lsp-region-text (region) 8563 "Get the text for REGION in current buffer." 8564 (-let (((start . end) (lsp--range-to-region region))) 8565 (buffer-substring-no-properties start end))) 8566 8567 (defun lsp-ht-set (tbl paths value) 8568 "Set nested hash table value. 8569 TBL - a hash table, PATHS is the path to the nested VALUE." 8570 (pcase paths 8571 (`(,path) (ht-set! tbl path value)) 8572 (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl) 8573 (let ((temp-tbl (ht))) 8574 (ht-set! tbl path temp-tbl) 8575 temp-tbl)))) 8576 (lsp-ht-set nested-tbl rst value))))) 8577 8578 ;; sections 8579 8580 (defalias 'defcustom-lsp 'lsp-defcustom) 8581 8582 (defmacro lsp-defcustom (symbol standard doc &rest args) 8583 "Defines `lsp-mode' server property." 8584 (declare (doc-string 3) (debug (name body)) 8585 (indent defun)) 8586 (let ((path (plist-get args :lsp-path))) 8587 (cl-remf args :lsp-path) 8588 `(progn 8589 (lsp-register-custom-settings 8590 (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type)))))) 8591 8592 (defcustom ,symbol ,standard ,doc 8593 :set (lambda (sym val) 8594 (lsp--set-custom-property sym val ,path)) 8595 ,@args)))) 8596 8597 (defun lsp--set-custom-property (sym val path) 8598 (set sym val) 8599 (let ((section (cl-first (s-split "\\." path)))) 8600 (mapc (lambda (workspace) 8601 (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace)) 8602 section) 8603 (with-lsp-workspace workspace 8604 (lsp--set-configuration (lsp-configuration-section section))))) 8605 (lsp--session-workspaces (lsp-session))))) 8606 8607 (defun lsp-configuration-section (section) 8608 "Get settings for SECTION." 8609 (let ((ret (ht-create))) 8610 (maphash (-lambda (path (variable boolean?)) 8611 (when (s-matches? (concat (regexp-quote section) "\\..*") path) 8612 (let* ((symbol-value (-> variable 8613 lsp-resolve-value 8614 lsp-resolve-value)) 8615 (value (if (and boolean? (not symbol-value)) 8616 :json-false 8617 symbol-value))) 8618 (when (or boolean? value) 8619 (lsp-ht-set ret (s-split "\\." path) value))))) 8620 lsp-client-settings) 8621 ret)) 8622 8623 8624 (defun lsp--start-connection (session client project-root) 8625 "Initiates connection created from CLIENT for PROJECT-ROOT. 8626 SESSION is the active session." 8627 (when (lsp--client-multi-root client) 8628 (cl-pushnew project-root (gethash (lsp--client-server-id client) 8629 (lsp-session-server-id->folders session)))) 8630 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil) 8631 8632 (unwind-protect 8633 (lsp--start-workspace session client project-root (lsp--create-initialization-options session client)) 8634 (lsp--spinner-stop))) 8635 8636 ;; lsp-log-io-mode 8637 8638 (defvar lsp-log-io-mode-map 8639 (let ((map (make-sparse-keymap))) 8640 (define-key map (kbd "M-n") #'lsp-log-io-next) 8641 (define-key map (kbd "M-p") #'lsp-log-io-prev) 8642 (define-key map (kbd "k") #'lsp--erase-log-buffer) 8643 (define-key map (kbd "K") #'lsp--erase-session-log-buffers) 8644 map) 8645 "Keymap for lsp log buffer mode.") 8646 8647 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo" 8648 "Special mode for viewing IO logs.") 8649 8650 (defun lsp-workspace-show-log (workspace) 8651 "Display the log buffer of WORKSPACE." 8652 (interactive 8653 (list (if lsp-log-io 8654 (if (eq (length (lsp-workspaces)) 1) 8655 (cl-first (lsp-workspaces)) 8656 (lsp--completing-read "Workspace: " (lsp-workspaces) 8657 #'lsp--workspace-print nil t)) 8658 (user-error "IO logging is disabled")))) 8659 (pop-to-buffer (lsp--get-log-buffer-create workspace))) 8660 8661 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log) 8662 8663 (defun lsp--get-log-buffer-create (workspace) 8664 "Return the lsp log buffer of WORKSPACE, creating a new one if needed." 8665 (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8666 (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id))) 8667 (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid)))) 8668 8669 (defun lsp--erase-log-buffer (&optional all) 8670 "Delete contents of current lsp log buffer. 8671 When ALL is t, erase all log buffers of the running session." 8672 (interactive) 8673 (let* ((workspaces (lsp--session-workspaces (lsp-session))) 8674 (current-log-buffer (current-buffer))) 8675 (dolist (w workspaces) 8676 (let ((b (lsp--get-log-buffer-create w))) 8677 (when (or all (eq b current-log-buffer)) 8678 (with-current-buffer b 8679 (let ((inhibit-read-only t)) 8680 (erase-buffer)))))))) 8681 8682 (defun lsp--erase-session-log-buffers () 8683 "Erase log buffers of the running session." 8684 (interactive) 8685 (lsp--erase-log-buffer t)) 8686 8687 (defun lsp-log-io-next (arg) 8688 "Move to next log entry." 8689 (interactive "P") 8690 (ewoc-goto-next lsp--log-io-ewoc (or arg 1))) 8691 8692 (defun lsp-log-io-prev (arg) 8693 "Move to previous log entry." 8694 (interactive "P") 8695 (ewoc-goto-prev lsp--log-io-ewoc (or arg 1))) 8696 8697 8698 8699 (cl-defmethod lsp-process-id ((process process)) 8700 (process-id process)) 8701 8702 (cl-defmethod lsp-process-name ((process process)) (process-name process)) 8703 8704 (cl-defmethod lsp-process-status ((process process)) (process-status process)) 8705 8706 (cl-defmethod lsp-process-kill ((process process)) 8707 (when (process-live-p process) 8708 (kill-process process))) 8709 8710 (cl-defmethod lsp-process-send ((process process) message) 8711 (condition-case err 8712 (process-send-string process (lsp--make-message message)) 8713 (error (lsp--error "Sending to process failed with the following error: %s" 8714 (error-message-string err))))) 8715 8716 (cl-defmethod lsp-process-cleanup (process) 8717 ;; Kill standard error buffer only if the process exited normally. 8718 ;; Leave it intact otherwise for debugging purposes. 8719 (let ((buffer (-> process process-name get-buffer))) 8720 (when (and (eq (process-status process) 'exit) 8721 (zerop (process-exit-status process)) 8722 (buffer-live-p buffer)) 8723 (kill-buffer buffer)))) 8724 8725 8726 ;; native JSONRPC 8727 8728 (declare-function json-rpc "ext:json") 8729 (declare-function json-rpc-connection "ext:json") 8730 (declare-function json-rpc-send "ext:json") 8731 (declare-function json-rpc-shutdown "ext:json") 8732 (declare-function json-rpc-stderr "ext:json") 8733 (declare-function json-rpc-pid "ext:json") 8734 8735 (defvar lsp-json-rpc-thread nil) 8736 (defvar lsp-json-rpc-queue nil) 8737 (defvar lsp-json-rpc-done nil) 8738 (defvar lsp-json-rpc-mutex (make-mutex)) 8739 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex)) 8740 8741 (defun lsp-json-rpc-process-queue () 8742 (while (not lsp-json-rpc-done) 8743 (while lsp-json-rpc-queue 8744 (-let (((proc . message) (pop lsp-json-rpc-queue))) 8745 (json-rpc-send 8746 proc message 8747 :null-object nil 8748 :false-object :json-false))) 8749 (with-mutex lsp-json-rpc-mutex 8750 (condition-wait lsp-json-rpc-condition)))) 8751 8752 (cl-defmethod lsp-process-id (process) (json-rpc-pid process)) 8753 8754 (cl-defmethod lsp-process-name (_process) "TBD") 8755 8756 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process)) 8757 8758 (cl-defmethod lsp-process-send (proc message) 8759 (unless lsp-json-rpc-thread 8760 (with-current-buffer (get-buffer-create " *json-rpc*") 8761 (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*")))) 8762 8763 (with-mutex lsp-json-rpc-mutex 8764 (setq lsp-json-rpc-queue (append lsp-json-rpc-queue 8765 (list (cons proc message)))) 8766 (condition-notify lsp-json-rpc-condition))) 8767 8768 (cl-defmethod lsp-process-cleanup (_proc)) 8769 8770 (defun lsp-json-rpc-connection (workspace command) 8771 (let ((con (apply #'json-rpc-connection command)) 8772 (object-type (if lsp-use-plists 'plist 'hash-table))) 8773 (with-current-buffer (get-buffer-create " *json-rpc*") 8774 (make-thread 8775 (lambda () 8776 (json-rpc 8777 con 8778 (lambda (result err done) 8779 (run-with-timer 8780 0.0 8781 nil 8782 (lambda () 8783 (cond 8784 (result (lsp--parser-on-message result workspace)) 8785 (err (warn "Json parsing failed with the following error: %s" err)) 8786 (done (lsp--handle-process-exit workspace "")))))) 8787 :object-type object-type 8788 :null-object nil 8789 :false-object nil)) 8790 "*json-rpc-connection*")) 8791 (cons con con))) 8792 8793 (defun lsp-json-rpc-stderr () 8794 (interactive) 8795 (--when-let (pcase (lsp-workspaces) 8796 (`nil (user-error "There are no active servers in the current buffer")) 8797 (`(,workspace) workspace) 8798 (workspaces (lsp--completing-read "Select server: " 8799 workspaces 8800 'lsp--workspace-print nil t))) 8801 (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it))) 8802 (buffer (format "*stderr-%s*" (lsp--workspace-print it)) )) 8803 (with-current-buffer (get-buffer-create buffer) 8804 (with-help-window buffer 8805 (insert content)))))) 8806 8807 8808 (defun lsp--workspace-print (workspace) 8809 "Visual representation WORKSPACE." 8810 (let* ((proc (lsp--workspace-cmd-proc workspace)) 8811 (status (lsp--workspace-status workspace)) 8812 (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8813 (pid (lsp-process-id proc))) 8814 8815 (if (eq 'initialized status) 8816 (format "%s:%s" server-id pid) 8817 (format "%s:%s/%s" server-id pid status)))) 8818 8819 (defun lsp--map-tree-widget (m) 8820 "Build `tree-widget' from a hash-table or plist M." 8821 (when (lsp-structure-p m) 8822 (let (nodes) 8823 (lsp-map (lambda (k v) 8824 (push `(tree-widget 8825 :tag ,(if (lsp-structure-p v) 8826 (format "%s:" k) 8827 (format "%s: %s" k 8828 (propertize (format "%s" v) 8829 'face 8830 'font-lock-string-face))) 8831 :open t 8832 ,@(lsp--map-tree-widget v)) 8833 nodes)) 8834 m) 8835 nodes))) 8836 8837 (defun lsp-buffer-name (buffer-id) 8838 (if-let ((buffer-name (plist-get buffer-id :buffer-name))) 8839 (funcall buffer-name buffer-id) 8840 (buffer-name buffer-id))) 8841 8842 (defun lsp--render-workspace (workspace) 8843 "Tree node representation of WORKSPACE." 8844 `(tree-widget :tag ,(lsp--workspace-print workspace) 8845 :open t 8846 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face) 8847 :open t 8848 ,@(->> workspace 8849 (lsp--workspace-buffers) 8850 (--map `(tree-widget 8851 :tag ,(when (lsp-buffer-live-p it) 8852 (let ((buffer-name (lsp-buffer-name it))) 8853 (if (lsp-with-current-buffer it buffer-read-only) 8854 (propertize buffer-name 'face 'font-lock-constant-face) 8855 buffer-name))))))) 8856 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face) 8857 ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget)))) 8858 8859 (define-derived-mode lsp-browser-mode special-mode "LspBrowser" 8860 "Define mode for displaying lsp sessions." 8861 (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t))))) 8862 8863 (defun lsp-describe-session () 8864 "Describes current `lsp-session'." 8865 (interactive) 8866 (let ((session (lsp-session)) 8867 (buf (get-buffer-create "*lsp session*")) 8868 (root (lsp-workspace-root))) 8869 (with-current-buffer buf 8870 (lsp-browser-mode) 8871 (let ((inhibit-read-only t)) 8872 (erase-buffer) 8873 (--each (lsp-session-folders session) 8874 (widget-create 8875 `(tree-widget 8876 :tag ,(propertize it 'face 'font-lock-keyword-face) 8877 :open t 8878 ,@(->> session 8879 (lsp-session-folder->servers) 8880 (gethash it) 8881 (-map 'lsp--render-workspace))))))) 8882 (pop-to-buffer buf) 8883 (goto-char (point-min)) 8884 (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag) 8885 until (or (and root (string= tag root)) (eobp)) 8886 do (goto-char (next-overlay-change (point)))))) 8887 8888 (defun lsp--session-workspaces (session) 8889 "Get all workspaces that are part of the SESSION." 8890 (-> session lsp-session-folder->servers hash-table-values -flatten -uniq)) 8891 8892 (defun lsp--find-multiroot-workspace (session client project-root) 8893 "Look for a multiroot connection in SESSION created from CLIENT for 8894 PROJECT-ROOT and BUFFER-MAJOR-MODE." 8895 (when (lsp--client-multi-root client) 8896 (-when-let (multi-root-workspace (->> session 8897 (lsp--session-workspaces) 8898 (--first (eq (-> it lsp--workspace-client lsp--client-server-id) 8899 (lsp--client-server-id client))))) 8900 (with-lsp-workspace multi-root-workspace 8901 (lsp-notify "workspace/didChangeWorkspaceFolders" 8902 (lsp-make-did-change-workspace-folders-params 8903 :event (lsp-make-workspace-folders-change-event 8904 :added (vector (lsp-make-workspace-folder 8905 :uri (lsp--path-to-uri project-root) 8906 :name (f-filename project-root))) 8907 :removed [])))) 8908 8909 (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace)) 8910 (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root)) 8911 8912 (lsp--persist-session session) 8913 8914 (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace)) 8915 (lsp--open-in-workspace multi-root-workspace) 8916 8917 multi-root-workspace))) 8918 8919 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder) 8920 "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT. 8921 IGNORE-MULTI-FOLDER to ignore multi folder server." 8922 (-map (lambda (client) 8923 (or 8924 (lsp--find-workspace session client project-root) 8925 (unless ignore-multi-folder 8926 (lsp--find-multiroot-workspace session client project-root)) 8927 (lsp--start-connection session client project-root))) 8928 clients)) 8929 8930 (defun lsp--spinner-stop () 8931 "Stop the spinner in case all of the workspaces are started." 8932 (when (--all? (eq (lsp--workspace-status it) 'initialized) 8933 lsp--buffer-workspaces) 8934 (spinner-stop))) 8935 8936 (defun lsp--open-in-workspace (workspace) 8937 "Open in existing WORKSPACE." 8938 (if (eq 'initialized (lsp--workspace-status workspace)) 8939 ;; when workspace is initialized just call document did open. 8940 (progn 8941 (with-lsp-workspace workspace 8942 (when-let ((before-document-open-fn (-> workspace 8943 lsp--workspace-client 8944 lsp--client-before-file-open-fn))) 8945 (funcall before-document-open-fn workspace)) 8946 (lsp--text-document-did-open)) 8947 (lsp--spinner-stop)) 8948 ;; when it is not initialized 8949 (lsp--spinner-start) 8950 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace)))) 8951 8952 (defun lsp--find-workspace (session client project-root) 8953 "Find server connection created with CLIENT in SESSION for PROJECT-ROOT." 8954 (when-let ((workspace (->> session 8955 (lsp-session-folder->servers) 8956 (gethash project-root) 8957 (--first (eql (-> it lsp--workspace-client lsp--client-server-id) 8958 (lsp--client-server-id client)))))) 8959 (lsp--open-in-workspace workspace) 8960 workspace)) 8961 8962 (defun lsp--read-char (prompt &optional options) 8963 "Wrapper for `read-char-from-minibuffer' if Emacs +27. 8964 Fallback to `read-key' otherwise. 8965 PROMPT is the message and OPTIONS the available options." 8966 (if (fboundp 'read-char-from-minibuffer) 8967 (read-char-from-minibuffer prompt options) 8968 (read-key prompt))) 8969 8970 (defun lsp--find-root-interactively (session) 8971 "Find project interactively. 8972 Returns nil if the project should not be added to the current SESSION." 8973 (condition-case nil 8974 (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory)) 8975 (action (lsp--read-char 8976 (format 8977 "%s is not part of any project. 8978 8979 %s ==> Import project root %s 8980 %s ==> Import project by selecting root directory interactively 8981 %s ==> Import project at current directory %s 8982 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist 8983 %s ==> Do not ask again for the current project by selecting ignore path interactively 8984 %s ==> Do nothing: ask again when opening other files from the current project 8985 8986 Select action: " 8987 (propertize (buffer-name) 'face 'bold) 8988 (propertize "i" 'face 'success) 8989 (propertize project-root-suggestion 'face 'bold) 8990 (propertize "I" 'face 'success) 8991 (propertize "." 'face 'success) 8992 (propertize default-directory 'face 'bold) 8993 (propertize "d" 'face 'warning) 8994 (propertize project-root-suggestion 'face 'bold) 8995 (propertize "D" 'face 'warning) 8996 (propertize "n" 'face 'warning)) 8997 '(?i ?\r ?I ?. ?d ?D ?n)))) 8998 (cl-case action 8999 (?i project-root-suggestion) 9000 (?\r project-root-suggestion) 9001 (?I (read-directory-name "Select workspace folder to add: " 9002 (or project-root-suggestion default-directory) 9003 nil 9004 t)) 9005 (?. default-directory) 9006 (?d (push project-root-suggestion (lsp-session-folders-blocklist session)) 9007 (lsp--persist-session session) 9008 nil) 9009 (?D (push (read-directory-name "Select folder to blocklist: " 9010 (or project-root-suggestion default-directory) 9011 nil 9012 t) 9013 (lsp-session-folders-blocklist session)) 9014 (lsp--persist-session session) 9015 nil) 9016 (t nil))) 9017 (quit))) 9018 9019 (declare-function tramp-file-name-host "ext:tramp" (file) t) 9020 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault)) 9021 9022 (defun lsp--files-same-host (f1 f2) 9023 "Predicate on whether or not two files are on the same host." 9024 (or (not (or (file-remote-p f1) (file-remote-p f2))) 9025 (and (file-remote-p f1) 9026 (file-remote-p f2) 9027 (progn (require 'tramp) 9028 (equal (tramp-file-name-host (tramp-dissect-file-name f1)) 9029 (tramp-file-name-host (tramp-dissect-file-name f2))))))) 9030 9031 (defun lsp-find-session-folder (session file-name) 9032 "Look in the current SESSION for folder containing FILE-NAME." 9033 (let ((file-name-canonical (lsp-f-canonical file-name))) 9034 (->> session 9035 (lsp-session-folders) 9036 (--filter (and (lsp--files-same-host it file-name-canonical) 9037 (or (lsp-f-same? it file-name-canonical) 9038 (and (f-dir? it) 9039 (lsp-f-ancestor-of? it file-name-canonical))))) 9040 (--max-by (> (length it) 9041 (length other)))))) 9042 9043 (defun lsp-find-workspace (server-id &optional file-name) 9044 "Find workspace for SERVER-ID for FILE-NAME." 9045 (-when-let* ((session (lsp-session)) 9046 (folder->servers (lsp-session-folder->servers session)) 9047 (workspaces (if file-name 9048 (gethash (lsp-find-session-folder session file-name) folder->servers) 9049 (lsp--session-workspaces session)))) 9050 9051 (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces))) 9052 9053 (defun lsp--calculate-root (session file-name) 9054 "Calculate project root for FILE-NAME in SESSION." 9055 (and 9056 (->> session 9057 (lsp-session-folders-blocklist) 9058 (--first (and (lsp--files-same-host it file-name) 9059 (lsp-f-ancestor-of? it file-name) 9060 (prog1 t 9061 (lsp--info "File %s is in blocklisted directory %s" file-name it)))) 9062 not) 9063 (or 9064 (when lsp-auto-guess-root 9065 (lsp--suggest-project-root)) 9066 (unless lsp-guess-root-without-session 9067 (lsp-find-session-folder session file-name)) 9068 (unless lsp-auto-guess-root 9069 (when-let ((root-folder (lsp--find-root-interactively session))) 9070 (if (or (not (f-equal? root-folder (expand-file-name "~/"))) 9071 (yes-or-no-p 9072 (concat 9073 (propertize "[WARNING] " 'face 'warning) 9074 "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: 9075 9076 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/'). 9077 2. If your file is under `~/' then create a subfolder and move that file in this folder. 9078 9079 Type `No' to go back to project selection. 9080 Type `Yes' to confirm `HOME' as project root. 9081 Type `C-g' to cancel project import process and stop `lsp'"))) 9082 root-folder 9083 (lsp--calculate-root session file-name))))))) 9084 9085 (defun lsp--try-open-in-library-workspace () 9086 "Try opening current file as library file in any of the active workspace. 9087 The library folders are defined by each client for each of the active workspace." 9088 (when-let ((workspace (->> (lsp-session) 9089 (lsp--session-workspaces) 9090 ;; Sort the last active workspaces first as they are more likely to be 9091 ;; the correct ones, especially when jumping to a definition. 9092 (-sort (lambda (a _b) 9093 (-contains? lsp--last-active-workspaces a))) 9094 (--first 9095 (and (-> it lsp--workspace-client lsp--supports-buffer?) 9096 (when-let ((library-folders-fn 9097 (-> it lsp--workspace-client lsp--client-library-folders-fn))) 9098 (-first (lambda (library-folder) 9099 (lsp-f-ancestor-of? library-folder (buffer-file-name))) 9100 (funcall library-folders-fn it)))))))) 9101 (lsp--open-in-workspace workspace) 9102 (view-mode t) 9103 (lsp--info "Opening read-only library file %s." (buffer-file-name)) 9104 (list workspace))) 9105 9106 (defun lsp--persist-session (session) 9107 "Persist SESSION to `lsp-session-file'." 9108 (lsp--persist lsp-session-file (make-lsp-session 9109 :folders (lsp-session-folders session) 9110 :folders-blocklist (lsp-session-folders-blocklist session) 9111 :server-id->folders (lsp-session-server-id->folders session)))) 9112 9113 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder) 9114 "Try create opening file as a project file. 9115 When IGNORE-MULTI-FOLDER is t the lsp mode will start new 9116 language server even if there is language server which can handle 9117 current language. When IGNORE-MULTI-FOLDER is nil current file 9118 will be opened in multi folder language server if there is 9119 such." 9120 (-let ((session (lsp-session))) 9121 (-if-let (clients (if ask-for-client 9122 (list (lsp--completing-read "Select server to start: " 9123 (ht-values lsp-clients) 9124 (-compose 'symbol-name 'lsp--client-server-id) nil t)) 9125 (lsp--find-clients))) 9126 (-if-let (project-root (-some-> session 9127 (lsp--calculate-root (buffer-file-name)) 9128 (lsp-f-canonical))) 9129 (progn 9130 ;; update project roots if needed and persist the lsp session 9131 (unless (-contains? (lsp-session-folders session) project-root) 9132 (cl-pushnew project-root (lsp-session-folders session)) 9133 (lsp--persist-session session)) 9134 (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder)) 9135 (lsp--warn "%s not in project or it is blocklisted." (buffer-name)) 9136 nil) 9137 (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode) 9138 nil))) 9139 9140 (defun lsp-shutdown-workspace () 9141 "Shutdown language server." 9142 (interactive) 9143 (--when-let (pcase (lsp-workspaces) 9144 (`nil (user-error "There are no active servers in the current buffer")) 9145 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?" 9146 (lsp--workspace-print workspace))) 9147 workspace)) 9148 (workspaces (lsp--completing-read "Select server: " 9149 workspaces 9150 'lsp--workspace-print nil t))) 9151 (lsp-workspace-shutdown it))) 9152 9153 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1") 9154 9155 (defcustom lsp-auto-select-workspace t 9156 "Shutdown or restart a single workspace. 9157 If set and the current buffer has only a single workspace 9158 associated with it, `lsp-shutdown-workspace' and 9159 `lsp-restart-workspace' will act on it without asking." 9160 :type 'boolean 9161 :group 'lsp-mode) 9162 9163 (defun lsp--read-workspace () 9164 "Ask the user to select a workspace. 9165 Errors if there are none." 9166 (pcase (lsp-workspaces) 9167 (`nil (error "No workspaces associated with the current buffer")) 9168 ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace) 9169 (workspaces (lsp--completing-read "Select workspace: " workspaces 9170 #'lsp--workspace-print nil t)))) 9171 9172 (defun lsp-workspace-shutdown (workspace) 9173 "Shut the workspace WORKSPACE and the language server associated with it" 9174 (interactive (list (lsp--read-workspace))) 9175 (lsp--warn "Stopping %s" (lsp--workspace-print workspace)) 9176 (with-lsp-workspace workspace (lsp--shutdown-workspace))) 9177 9178 (defun lsp-disconnect () 9179 "Disconnect the buffer from the language server." 9180 (interactive) 9181 (lsp--text-document-did-close t) 9182 (lsp-managed-mode -1) 9183 (lsp-mode -1) 9184 (setq lsp--buffer-workspaces nil) 9185 (lsp--info "Disconnected")) 9186 9187 (defun lsp-restart-workspace () 9188 (interactive) 9189 (--when-let (pcase (lsp-workspaces) 9190 (`nil (user-error "There are no active servers in the current buffer")) 9191 (`(,workspace) workspace) 9192 (workspaces (lsp--completing-read "Select server: " 9193 workspaces 9194 'lsp--workspace-print nil t))) 9195 (lsp-workspace-restart it))) 9196 9197 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1") 9198 9199 (defun lsp-workspace-restart (workspace) 9200 "Restart the workspace WORKSPACE and the language server associated with it" 9201 (interactive (list (lsp--read-workspace))) 9202 (lsp--warn "Restarting %s" (lsp--workspace-print workspace)) 9203 (with-lsp-workspace workspace (lsp--shutdown-workspace t))) 9204 9205 ;;;###autoload 9206 (defun lsp (&optional arg) 9207 "Entry point for the server startup. 9208 When ARG is t the lsp mode will start new language server even if 9209 there is language server which can handle current language. When 9210 ARG is nil current file will be opened in multi folder language 9211 server if there is such. When `lsp' is called with prefix 9212 argument ask the user to select which language server to start." 9213 (interactive "P") 9214 9215 (lsp--require-packages) 9216 9217 (when (buffer-file-name) 9218 (let (clients 9219 (matching-clients (lsp--filter-clients 9220 (-andfn #'lsp--supports-buffer? 9221 #'lsp--server-binary-present?)))) 9222 (cond 9223 (matching-clients 9224 (when (setq lsp--buffer-workspaces 9225 (or (and 9226 ;; Don't open as library file if file is part of a project. 9227 (not (lsp-find-session-folder (lsp-session) (buffer-file-name))) 9228 (lsp--try-open-in-library-workspace)) 9229 (lsp--try-project-root-workspaces (equal arg '(4)) 9230 (and arg (not (equal arg 1)))))) 9231 (lsp-mode 1) 9232 (when lsp-auto-configure (lsp--auto-configure)) 9233 (setq lsp-buffer-uri (lsp--buffer-uri)) 9234 (lsp--info "Connected to %s." 9235 (apply 'concat (--map (format "[%s %s]" 9236 (lsp--workspace-print it) 9237 (lsp--workspace-root it)) 9238 lsp--buffer-workspaces))))) 9239 ;; look for servers which are currently being downloaded. 9240 ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9241 #'lsp--client-download-in-progress?))) 9242 (lsp--info "There are language server(%s) installation in progress. 9243 The server(s) will be started in the buffer when it has finished." 9244 (-map #'lsp--client-server-id clients)) 9245 (seq-do (lambda (client) 9246 (cl-pushnew (current-buffer) (lsp--client-buffers client))) 9247 clients)) 9248 ;; look for servers to install 9249 ((setq clients (lsp--filter-clients 9250 (-andfn #'lsp--supports-buffer? 9251 (-const lsp-enable-suggest-server-download) 9252 #'lsp--client-download-server-fn 9253 (-not #'lsp--client-download-in-progress?)))) 9254 (let ((client (lsp--completing-read 9255 (concat "Unable to find installed server supporting this file. " 9256 "The following servers could be installed automatically: ") 9257 clients 9258 (-compose #'symbol-name #'lsp--client-server-id) 9259 nil 9260 t))) 9261 (cl-pushnew (current-buffer) (lsp--client-buffers client)) 9262 (lsp--install-server-internal client))) 9263 ;; ignore other warnings 9264 ((not lsp-warn-no-matched-clients) 9265 nil) 9266 ;; automatic installation disabled 9267 ((setq clients (unless matching-clients 9268 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9269 #'lsp--client-download-server-fn 9270 (-not (-const lsp-enable-suggest-server-download)) 9271 (-not #'lsp--server-binary-present?))))) 9272 (lsp--warn "The following servers support current file but automatic download is disabled: %s 9273 \(If you have already installed the server check *lsp-log*)." 9274 (mapconcat (lambda (client) 9275 (symbol-name (lsp--client-server-id client))) 9276 clients 9277 " "))) 9278 ;; no clients present 9279 ((setq clients (unless matching-clients 9280 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9281 (-not #'lsp--server-binary-present?))))) 9282 (lsp--warn "The following servers support current file but do not have automatic installation: %s 9283 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages. 9284 \(If you have already installed the server check *lsp-log*)." 9285 (mapconcat (lambda (client) 9286 (symbol-name (lsp--client-server-id client))) 9287 clients 9288 " "))) 9289 ;; no matches 9290 ((-> #'lsp--supports-buffer? lsp--filter-clients not) 9291 (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'. 9292 This issue might be caused by: 9293 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'. 9294 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'. 9295 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/ . 9296 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/. 9297 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients'). 9298 You can customize `lsp-warn-no-matched-clients' to disable this message." 9299 major-mode major-mode major-mode)))))) 9300 9301 (defun lsp--buffer-visible-p () 9302 "Return non nil if current buffer is visible." 9303 (or (buffer-modified-p) (get-buffer-window nil t))) 9304 9305 (defun lsp--init-if-visible () 9306 "Run `lsp' for the current buffer if the buffer is visible. 9307 Returns non nil if `lsp' was run for the buffer." 9308 (when (lsp--buffer-visible-p) 9309 (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t) 9310 (lsp) 9311 t)) 9312 9313 ;;;###autoload 9314 (defun lsp-deferred () 9315 "Entry point that defers server startup until buffer is visible. 9316 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'. 9317 This avoids overloading the server with many files when starting Emacs." 9318 ;; Workspace may not be initialized yet. Use a buffer local variable to 9319 ;; remember that we deferred loading of this buffer. 9320 (setq lsp--buffer-deferred t) 9321 (let ((buffer (current-buffer))) 9322 ;; Avoid false positives as desktop-mode restores buffers by deferring 9323 ;; visibility check until the stack clears. 9324 (run-with-idle-timer 0 nil (lambda () 9325 (when (buffer-live-p buffer) 9326 (with-current-buffer buffer 9327 (unless (lsp--init-if-visible) 9328 (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t)))))))) 9329 9330 9331 9332 (defvar lsp-file-truename-cache (ht)) 9333 9334 (defmacro lsp-with-cached-filetrue-name (&rest body) 9335 "Executes BODY caching the `file-truename' calls." 9336 `(let ((old-fn (symbol-function 'file-truename))) 9337 (unwind-protect 9338 (progn 9339 (fset 'file-truename 9340 (lambda (file-name &optional counter prev-dirs) 9341 (or (gethash file-name lsp-file-truename-cache) 9342 (puthash file-name (apply old-fn (list file-name counter prev-dirs)) 9343 lsp-file-truename-cache)))) 9344 ,@body) 9345 (fset 'file-truename old-fn)))) 9346 9347 9348 (defun lsp-virtual-buffer-call (key &rest args) 9349 (when lsp--virtual-buffer 9350 (when-let ((fn (plist-get lsp--virtual-buffer key))) 9351 (apply fn args)))) 9352 9353 (defun lsp-translate-column (column) 9354 "Translate COLUMN taking into account virtual buffers." 9355 (or (lsp-virtual-buffer-call :real->virtual-char column) 9356 column)) 9357 9358 (defun lsp-translate-line (line) 9359 "Translate LINE taking into account virtual buffers." 9360 (or (lsp-virtual-buffer-call :real->virtual-line line) 9361 line)) 9362 9363 9364 ;; lsp internal validation. 9365 9366 (defmacro lsp--doctor (&rest checks) 9367 `(-let [buf (current-buffer)] 9368 (with-current-buffer (get-buffer-create "*lsp-performance*") 9369 (with-help-window (current-buffer) 9370 ,@(-map (-lambda ((msg form)) 9371 `(insert (format "%s: %s\n" ,msg 9372 (let ((res (with-current-buffer buf 9373 ,form))) 9374 (cond 9375 ((eq res :optional) (propertize "OPTIONAL" 'face 'warning)) 9376 (res (propertize "OK" 'face 'success)) 9377 (t (propertize "ERROR" 'face 'error))))))) 9378 (-partition 2 checks)))))) 9379 9380 (define-obsolete-function-alias 'lsp-diagnose 9381 'lsp-doctor "lsp-mode 8.0.0") 9382 9383 (defun lsp-doctor () 9384 "Validate performance settings." 9385 (interactive) 9386 (lsp--doctor 9387 "Checking for Native JSON support" (functionp 'json-serialize) 9388 "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max) 9389 "Check `read-process-output-max' default has been changed from 4k" 9390 (and (boundp 'read-process-output-max) 9391 (> read-process-output-max 4096)) 9392 "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)" 9393 (condition-case _err 9394 (progn (lsp--make-message (list "a" "b")) 9395 nil) 9396 (error t)) 9397 "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000) 9398 "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) 9399 "Using emacs 28+ with native compilation?" 9400 (or (and (fboundp 'native-comp-available-p) 9401 (native-comp-available-p)) 9402 :optional))) 9403 9404 (declare-function package-version-join "ext:package") 9405 (declare-function package-desc-version "ext:package") 9406 (declare-function package--alist "ext:package") 9407 9408 (defun lsp-version () 9409 "Return string describing current version of `lsp-mode'." 9410 (interactive) 9411 (unless (featurep 'package) 9412 (require 'package)) 9413 (let ((ver (format "lsp-mode %s, Emacs %s, %s" 9414 (package-version-join 9415 (package-desc-version 9416 (car (alist-get 'lsp-mode (package--alist))))) 9417 emacs-version 9418 system-type))) 9419 (if (called-interactively-p 'interactive) 9420 (lsp--info "%s" ver) 9421 ver))) 9422 9423 9424 9425 ;; org-mode/virtual-buffer 9426 9427 (declare-function org-babel-get-src-block-info "ext:ob-core") 9428 (declare-function org-do-remove-indentation "ext:org-macs") 9429 (declare-function org-src-get-lang-mode "ext:org-src") 9430 (declare-function org-element-context "ext:org-element") 9431 9432 (defun lsp--virtual-buffer-update-position () 9433 (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range)) 9434 (funcall in-range)) 9435 lsp--virtual-buffer-connections)) 9436 (unless (equal virtual-buffer lsp--virtual-buffer) 9437 (lsp-org)) 9438 (when lsp-managed-mode 9439 (lsp-managed-mode -1) 9440 (lsp-mode -1) 9441 (setq lsp--buffer-workspaces nil) 9442 (setq lsp--virtual-buffer nil) 9443 (setq lsp-buffer-uri nil) 9444 9445 ;; force refresh of diagnostics 9446 (run-hooks 'lsp-after-diagnostics-hook)))) 9447 9448 (defun lsp-virtual-buffer-on-change (start end length) 9449 "Adjust on change event to be executed against the proper language server." 9450 (let ((max-point (max end 9451 (or (plist-get lsp--before-change-vals :end) 0) 9452 (+ start length)))) 9453 (when-let ((virtual-buffer (-first (lambda (vb) 9454 (let ((lsp--virtual-buffer vb)) 9455 (and (lsp-virtual-buffer-call :in-range start) 9456 (lsp-virtual-buffer-call :in-range max-point)))) 9457 lsp--virtual-buffer-connections))) 9458 (lsp-with-current-buffer virtual-buffer 9459 (lsp-on-change start end length 9460 (lambda (&rest _) 9461 (list :range (lsp--range (list :character 0 :line 0) 9462 lsp--virtual-buffer-point-max) 9463 :text (lsp--buffer-content)))))))) 9464 9465 (defun lsp-virtual-buffer-before-change (start _end) 9466 (when-let ((virtual-buffer (-first (lambda (vb) 9467 (lsp-with-current-buffer vb 9468 (lsp-virtual-buffer-call :in-range start))) 9469 lsp--virtual-buffer-connections))) 9470 (lsp-with-current-buffer virtual-buffer 9471 (setq lsp--virtual-buffer-point-max 9472 (lsp--point-to-position (lsp-virtual-buffer-call :last-point)))))) 9473 9474 (defun lsp-patch-on-change-event () 9475 (remove-hook 'after-change-functions #'lsp-on-change t) 9476 (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t) 9477 (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t)) 9478 9479 (defun lsp-kill-virtual-buffers () 9480 (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections)) 9481 9482 (defun lsp--move-point-in-indentation (point indentation) 9483 (save-excursion 9484 (goto-char point) 9485 (if (<= point (+ (line-beginning-position) indentation)) 9486 (line-beginning-position) 9487 point))) 9488 9489 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck") 9490 (declare-function flycheck-add-mode "ext:flycheck") 9491 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics") 9492 9493 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn) 9494 9495 (defun lsp-flycheck-add-mode (mode) 9496 "Register flycheck support for MODE." 9497 (lsp-diagnostics-lsp-checker-if-needed) 9498 (unless (flycheck-checker-supports-major-mode-p 'lsp mode) 9499 (flycheck-add-mode 'lsp mode))) 9500 9501 (defun lsp-progress-spinner-type () 9502 "Retrieve the spinner type value, if value is not a symbol of `spinner-types 9503 defaults to `progress-bar." 9504 (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar)) 9505 9506 (defun lsp-org () 9507 (interactive) 9508 (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range)) 9509 (funcall in-range)) 9510 lsp--virtual-buffer-connections)) 9511 (unless (equal lsp--virtual-buffer virtual-buffer) 9512 (setq lsp--buffer-workspaces workspaces) 9513 (setq lsp--virtual-buffer virtual-buffer) 9514 (setq lsp-buffer-uri nil) 9515 (lsp-mode 1) 9516 (lsp-managed-mode 1) 9517 (lsp-patch-on-change-event)) 9518 9519 (save-excursion 9520 (-let* (virtual-buffer 9521 (wcb (lambda (f) 9522 (with-current-buffer (plist-get virtual-buffer :buffer) 9523 (-let* (((&plist :major-mode :buffer-file-name 9524 :goto-buffer :workspaces) virtual-buffer) 9525 (lsp--virtual-buffer virtual-buffer) 9526 (lsp--buffer-workspaces workspaces)) 9527 (save-excursion 9528 (funcall goto-buffer) 9529 (funcall f)))))) 9530 ((&plist :begin :end :post-blank :language) (cl-second (org-element-context))) 9531 ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light))) 9532 9533 (file-name (if file-name 9534 (f-expand file-name) 9535 (user-error "You should specify file name in the src block header."))) 9536 (begin-marker (progn 9537 (goto-char begin) 9538 (forward-line) 9539 (set-marker (make-marker) (point)))) 9540 (end-marker (progn 9541 (goto-char end) 9542 (forward-line (1- (- post-blank))) 9543 (set-marker (make-marker) (1+ (point))))) 9544 (buf (current-buffer)) 9545 (src-block (buffer-substring-no-properties begin-marker 9546 (1- end-marker))) 9547 (indentation (with-temp-buffer 9548 (insert src-block) 9549 9550 (goto-char (point-min)) 9551 (let ((indentation (current-indentation))) 9552 (plist-put lsp--virtual-buffer :indentation indentation) 9553 (org-do-remove-indentation) 9554 (goto-char (point-min)) 9555 (- indentation (current-indentation)))))) 9556 (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t) 9557 9558 (when (fboundp 'flycheck-add-mode) 9559 (lsp-flycheck-add-mode 'org-mode)) 9560 9561 (setq lsp--virtual-buffer 9562 (list 9563 :in-range (lambda (&optional point) 9564 (<= begin-marker (or point (point)) (1- end-marker))) 9565 :goto-buffer (lambda () (goto-char begin-marker)) 9566 :buffer-string 9567 (lambda () 9568 (let ((src-block (buffer-substring-no-properties 9569 begin-marker 9570 (1- end-marker)))) 9571 (with-temp-buffer 9572 (insert src-block) 9573 9574 (goto-char (point-min)) 9575 (while (not (eobp)) 9576 (delete-region (point) (if (> (+ (point) indentation) (line-end-position)) 9577 (line-end-position) 9578 (+ (point) indentation))) 9579 (forward-line)) 9580 (buffer-substring-no-properties (point-min) 9581 (point-max))))) 9582 :buffer buf 9583 :begin begin-marker 9584 :end end-marker 9585 :indentation indentation 9586 :last-point (lambda () (1- end-marker)) 9587 :cur-position (lambda () 9588 (lsp-save-restriction-and-excursion 9589 (list :line (- (lsp--cur-line) 9590 (lsp--cur-line begin-marker)) 9591 :character (let ((character (- (point) 9592 (line-beginning-position) 9593 indentation))) 9594 (if (< character 0) 9595 0 9596 character))))) 9597 :line/character->point (-lambda (line character) 9598 (-let [inhibit-field-text-motion t] 9599 (+ indentation 9600 (lsp-save-restriction-and-excursion 9601 (goto-char begin-marker) 9602 (forward-line line) 9603 (-let [line-end (line-end-position)] 9604 (if (> character (- line-end (point))) 9605 line-end 9606 (forward-char character) 9607 (point))))))) 9608 :major-mode (org-src-get-lang-mode language) 9609 :buffer-file-name file-name 9610 :buffer-uri (lsp--path-to-uri file-name) 9611 :with-current-buffer wcb 9612 :buffer-live? (lambda (_) (buffer-live-p buf)) 9613 :buffer-name (lambda (_) 9614 (propertize (format "%s(%s:%s)%s" 9615 (buffer-name buf) 9616 begin-marker 9617 end-marker 9618 language) 9619 'face 'italic)) 9620 :real->virtual-line (lambda (line) 9621 (+ line (line-number-at-pos begin-marker) -1)) 9622 :real->virtual-char (lambda (char) (+ char indentation)) 9623 :cleanup (lambda () 9624 (set-marker begin-marker nil) 9625 (set-marker end-marker nil)))) 9626 (setf virtual-buffer lsp--virtual-buffer) 9627 (puthash file-name virtual-buffer lsp--virtual-buffer-mappings) 9628 (push virtual-buffer lsp--virtual-buffer-connections) 9629 9630 ;; TODO: tangle only connected sections 9631 (add-hook 'after-save-hook 'org-babel-tangle nil t) 9632 (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t) 9633 (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t) 9634 9635 (setq lsp--buffer-workspaces 9636 (lsp-with-current-buffer virtual-buffer 9637 (lsp) 9638 (plist-put virtual-buffer :workspaces (lsp-workspaces)) 9639 (lsp-workspaces))))))) 9640 9641 (defun lsp-virtual-buffer-disconnect (virtual-buffer) 9642 (interactive (list (or 9643 lsp--virtual-buffer 9644 (when lsp--virtual-buffer-connections 9645 (lsp--completing-read "Select virtual buffer to disconnect: " 9646 lsp--virtual-buffer-connections 9647 (-lambda ((&plist :buffer-file-name)) 9648 buffer-file-name)))))) 9649 (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer) 9650 (progn 9651 (lsp-with-current-buffer virtual-buffer 9652 (lsp--text-document-did-close)) 9653 (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections)) 9654 (when (eq virtual-buffer lsp--virtual-buffer) 9655 (setf lsp--virtual-buffer nil)) 9656 (when cleanup (funcall cleanup)) 9657 (remhash file-name lsp--virtual-buffer-mappings) 9658 9659 (lsp--virtual-buffer-update-position) 9660 (lsp--info "Disconnected from buffer %s" file-name)) 9661 (lsp--error "Nothing to disconnect from?"))) 9662 9663 9664 ;; inlay hints 9665 9666 (defface lsp-inlay-hint-face 9667 '((t :inherit font-lock-comment-face)) 9668 "The face to use for the JavaScript inlays." 9669 :group 'lsp-mode 9670 :package-version '(lsp-mode . "9.0.0")) 9671 9672 (defface lsp-inlay-hint-type-face 9673 '((t :inherit lsp-inlay-hint-face)) 9674 "Face for inlay type hints (e.g. inferred variable types)." 9675 :group 'lsp-mode 9676 :package-version '(lsp-mode . "9.0.0")) 9677 9678 (defcustom lsp-inlay-hint-type-format "%s" 9679 "Format string for variable inlays (part of the inlay face)." 9680 :type '(string :tag "String") 9681 :group 'lsp-mode 9682 :package-version '(lsp-mode . "9.0.0")) 9683 9684 (defface lsp-inlay-hint-parameter-face 9685 '((t :inherit lsp-inlay-hint-face)) 9686 "Face for inlay parameter hints (e.g. function parameter names at 9687 call-site)." 9688 :group 'lsp-mode 9689 :package-version '(lsp-mode . "9.0.0")) 9690 9691 (defcustom lsp-inlay-hint-param-format "%s" 9692 "Format string for parameter inlays (part of the inlay face)." 9693 :type '(string :tag "String") 9694 :group 'lsp-mode 9695 :package-version '(lsp-mode . "9.0.0")) 9696 9697 (defcustom lsp-update-inlay-hints-on-scroll t 9698 "If non-nil update inlay hints immediately when scrolling or 9699 modifying window sizes." 9700 :type 'boolean 9701 :package-version '(lsp-mode . "9.0.0")) 9702 9703 (defun lsp--format-inlay (text kind) 9704 (cond 9705 ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text)) 9706 ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text)) 9707 (t text))) 9708 9709 (defun lsp--face-for-inlay (kind) 9710 (cond 9711 ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face) 9712 ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face) 9713 (t 'lsp-inlay-hint-face))) 9714 9715 (defun lsp--update-inlay-hints-scroll-function (window start) 9716 (lsp-update-inlay-hints start (window-end window t))) 9717 9718 (defun lsp--update-inlay-hints () 9719 (lsp-update-inlay-hints (window-start) (window-end nil t))) 9720 9721 (defun lsp--label-from-inlay-hints-response (label) 9722 "Returns a string label built from an array of 9723 InlayHintLabelParts or the argument itself if it's already a 9724 string." 9725 (cl-typecase label 9726 (string label) 9727 (vector 9728 (string-join (mapcar (lambda (part) 9729 (-let (((&InlayHintLabelPart :value) part)) 9730 value)) 9731 label))))) 9732 9733 (defun lsp-update-inlay-hints (start end) 9734 (lsp-request-async 9735 "textDocument/inlayHint" 9736 (lsp-make-inlay-hints-params 9737 :text-document (lsp--text-document-identifier) 9738 :range (lsp-make-range :start 9739 (lsp-point-to-position start) 9740 :end 9741 (lsp-point-to-position end))) 9742 (lambda (res) 9743 (lsp--remove-overlays 'lsp-inlay-hint) 9744 (dolist (hint res) 9745 (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint) 9746 (kind (or kind? lsp/inlay-hint-kind-type-hint)) 9747 (label (lsp--label-from-inlay-hints-response label)) 9748 (pos (lsp--position-to-point position)) 9749 (overlay (make-overlay pos pos nil 'front-advance 'end-advance))) 9750 (when (stringp label) 9751 (overlay-put overlay 'lsp-inlay-hint t) 9752 (overlay-put overlay 'before-string 9753 (format "%s%s%s" 9754 (if padding-left? " " "") 9755 (propertize (lsp--format-inlay label kind) 9756 'font-lock-face (lsp--face-for-inlay kind)) 9757 (if padding-right? " " ""))))))) 9758 :mode 'tick)) 9759 9760 (define-minor-mode lsp-inlay-hints-mode 9761 "Mode for displaying inlay hints." 9762 :lighter nil 9763 (cond 9764 ((and lsp-inlay-hints-mode lsp--buffer-workspaces) 9765 (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t) 9766 (when lsp-update-inlay-hints-on-scroll 9767 (add-to-list (make-local-variable 'window-scroll-functions) 9768 #'lsp--update-inlay-hints-scroll-function))) 9769 (t 9770 (lsp--remove-overlays 'lsp-inlay-hint) 9771 (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t) 9772 (setf window-scroll-functions 9773 (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions))))) 9774 9775 9776 9777 ;;;###autoload 9778 (defun lsp-start-plain () 9779 "Start `lsp-mode' using minimal configuration using the latest `melpa' version 9780 of the packages. 9781 9782 In case the major-mode that you are using for " 9783 (interactive) 9784 (let ((start-plain (make-temp-file "plain" nil ".el"))) 9785 (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el" 9786 start-plain t) 9787 (async-shell-command 9788 (format "%s -q -l %s %s" 9789 (expand-file-name invocation-name invocation-directory) 9790 start-plain 9791 (or (buffer-file-name) "")) 9792 (generate-new-buffer " *lsp-start-plain*")))) 9793 9794 9795 9796 (provide 'lsp-mode) 9797 ;;; lsp-mode.el ends here