lsp-mode.el (433671B)
1 ;;; lsp-mode.el --- LSP mode -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2020-2024 emacs-lsp maintainers 4 5 ;; Author: Vibhav Pant, Fangrui Song, Ivan Yonchovski 6 ;; Keywords: languages 7 ;; Package-Requires: ((emacs "27.1") (dash "2.18.0") (f "0.20.0") (ht "2.3") (spinner "1.7.3") (markdown-mode "2.3") (lv "0") (eldoc "1.11")) 8 ;; Version: 9.0.1 9 10 ;; URL: https://github.com/emacs-lsp/lsp-mode 11 ;; This program is free software; you can redistribute it and/or modify 12 ;; it under the terms of the GNU General Public License as published by 13 ;; the Free Software Foundation, either version 3 of the License, or 14 ;; (at your option) any later version. 15 16 ;; This program is distributed in the hope that it will be useful, 17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 ;; GNU General Public License for more details. 20 21 ;; You should have received a copy of the GNU General Public License 22 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 23 24 ;;; Commentary: 25 26 ;; Emacs client/library for the Language Server Protocol 27 28 ;;; Code: 29 30 (require 'cl-generic) 31 (require 'cl-lib) 32 (require 'compile) 33 (require 'dash) 34 (require 'epg) 35 (require 'ewoc) 36 (require 'f) 37 (require 'filenotify) 38 (require 'files) 39 (require 'ht) 40 (require 'imenu) 41 (require 'inline) 42 (require 'json) 43 (require 'lv) 44 (require 'markdown-mode) 45 (require 'network-stream) 46 (require 'pcase) 47 (require 'rx) 48 (require 's) 49 (require 'seq) 50 (require 'spinner) 51 (require 'subr-x) 52 (require 'tree-widget) 53 (require 'url-parse) 54 (require 'url-util) 55 (require 'widget) 56 (require 'xref) 57 (require 'minibuffer) 58 (require 'help-mode) 59 (require 'lsp-protocol) 60 61 (defgroup lsp-mode nil 62 "Language Server Protocol client." 63 :group 'tools 64 :tag "Language Server (lsp-mode)") 65 66 (declare-function evil-set-command-property "ext:evil-common") 67 (declare-function projectile-project-root "ext:projectile") 68 (declare-function yas-expand-snippet "ext:yasnippet") 69 (declare-function dap-mode "ext:dap-mode") 70 (declare-function dap-auto-configure-mode "ext:dap-mode") 71 72 (defvar yas-inhibit-overlay-modification-protection) 73 (defvar yas-indent-line) 74 (defvar yas-wrap-around-region) 75 (defvar yas-also-auto-indent-first-line) 76 (defvar dap-auto-configure-mode) 77 (defvar dap-ui-menu-items) 78 (defvar company-minimum-prefix-length) 79 80 (defconst lsp--message-type-face 81 `((1 . ,compilation-error-face) 82 (2 . ,compilation-warning-face) 83 (3 . ,compilation-message-face) 84 (4 . ,compilation-info-face))) 85 86 (defconst lsp--errors 87 '((-32700 "Parse Error") 88 (-32600 "Invalid Request") 89 (-32601 "Method not Found") 90 (-32602 "Invalid Parameters") 91 (-32603 "Internal Error") 92 (-32099 "Server Start Error") 93 (-32000 "Server End Error") 94 (-32002 "Server Not Initialized") 95 (-32001 "Unknown Error Code") 96 (-32800 "Request Cancelled")) 97 "Alist of error codes to user friendly strings.") 98 99 (defconst lsp--empty-ht (make-hash-table)) 100 101 (eval-and-compile 102 (defun dash-expand:&lsp-wks (key source) 103 `(,(intern-soft (format "lsp--workspace-%s" (eval key))) ,source)) 104 105 (defun dash-expand:&lsp-cln (key source) 106 `(,(intern-soft (format "lsp--client-%s" (eval key))) ,source))) 107 108 (define-obsolete-variable-alias 'lsp-print-io 'lsp-log-io "lsp-mode 6.1") 109 110 (defcustom lsp-log-io nil 111 "If non-nil, log all messages from the language server to a *lsp-log* buffer." 112 :group 'lsp-mode 113 :type 'boolean) 114 115 (defcustom lsp-log-io-allowlist-methods '() 116 "The methods to filter before print to lsp-log-io." 117 :group 'lsp-mode 118 :type '(repeat string) 119 :package-version '(lsp-mode . "9.0.0")) 120 121 (defcustom lsp-log-max message-log-max 122 "Maximum number of lines to keep in the log buffer. 123 If nil, disable message logging. If t, log messages but don’t truncate 124 the buffer when it becomes large." 125 :group 'lsp-mode 126 :type '(choice (const :tag "Disable" nil) 127 (integer :tag "lines") 128 (const :tag "Unlimited" t)) 129 :package-version '(lsp-mode . "6.1")) 130 131 (defcustom lsp-io-messages-max t 132 "Maximum number of messages that can be locked in a `lsp-io' buffer." 133 :group 'lsp-mode 134 :type '(choice (const :tag "Unlimited" t) 135 (integer :tag "Messages")) 136 :package-version '(lsp-mode . "6.1")) 137 138 (defcustom lsp-keep-workspace-alive t 139 "If non nil keep workspace alive when the last workspace buffer is closed." 140 :group 'lsp-mode 141 :type 'boolean) 142 143 (defcustom lsp-enable-snippet t 144 "Enable/disable snippet completion support." 145 :group 'lsp-completion 146 :type 'boolean) 147 148 (defcustom lsp-enable-folding t 149 "Enable/disable code folding support." 150 :group 'lsp-mode 151 :type 'boolean 152 :package-version '(lsp-mode . "6.1")) 153 154 (define-obsolete-variable-alias 'lsp-enable-semantic-highlighting 'lsp-semantic-tokens-enable "lsp-mode 8.0.0") 155 156 (defcustom lsp-semantic-tokens-enable nil 157 "Enable/disable support for semantic tokens. 158 As defined by the Language Server Protocol 3.16." 159 :group 'lsp-semantic-tokens 160 :type 'boolean) 161 162 (defcustom lsp-folding-range-limit nil 163 "The maximum number of folding ranges to receive from the language server." 164 :group 'lsp-mode 165 :type '(choice (const :tag "No limit." nil) 166 (integer :tag "Number of lines.")) 167 :package-version '(lsp-mode . "6.1")) 168 169 (defcustom lsp-folding-line-folding-only nil 170 "If non-nil, only fold complete lines." 171 :group 'lsp-mode 172 :type 'boolean 173 :package-version '(lsp-mode . "6.1")) 174 175 (defcustom lsp-client-packages 176 '( ccls lsp-actionscript lsp-ada lsp-angular lsp-ansible lsp-asm lsp-astro 177 lsp-autotools lsp-awk lsp-bash lsp-beancount lsp-bufls lsp-clangd 178 lsp-clojure lsp-cmake lsp-cobol lsp-credo lsp-crystal lsp-csharp lsp-css 179 lsp-cucumber lsp-cypher lsp-d lsp-dart lsp-dhall lsp-docker lsp-dockerfile 180 lsp-earthly lsp-elixir lsp-elm lsp-emmet lsp-erlang lsp-eslint lsp-fortran lsp-fsharp 181 lsp-gdscript lsp-gleam lsp-glsl lsp-go lsp-golangci-lint lsp-grammarly 182 lsp-graphql lsp-groovy lsp-hack lsp-haskell lsp-haxe lsp-idris lsp-java 183 lsp-javascript lsp-jq lsp-json lsp-kotlin lsp-latex lsp-lisp lsp-ltex 184 lsp-lua lsp-magik lsp-markdown lsp-marksman lsp-mdx lsp-meson lsp-metals lsp-mint 185 lsp-mojo lsp-move lsp-mssql lsp-nginx lsp-nim lsp-nix lsp-nushell lsp-ocaml 186 lsp-openscad lsp-pascal lsp-perl lsp-perlnavigator lsp-php lsp-pls 187 lsp-purescript lsp-pwsh lsp-pyls lsp-pylsp lsp-pyright lsp-python-ms 188 lsp-qml lsp-r lsp-racket lsp-remark lsp-rf lsp-roslyn lsp-rubocop lsp-ruby-lsp 189 lsp-ruby-syntax-tree lsp-ruff-lsp lsp-rust lsp-semgrep lsp-shader 190 lsp-solargraph lsp-solidity lsp-sonarlint lsp-sorbet lsp-sourcekit 191 lsp-sql lsp-sqls lsp-steep lsp-svelte lsp-tailwindcss lsp-terraform 192 lsp-tex lsp-tilt lsp-toml lsp-trunk lsp-ttcn3 lsp-typeprof lsp-v 193 lsp-vala lsp-verilog lsp-vetur lsp-vhdl lsp-vimscript lsp-volar lsp-wgsl 194 lsp-xml lsp-yaml lsp-yang lsp-zig) 195 "List of the clients to be automatically required." 196 :group 'lsp-mode 197 :type '(repeat symbol)) 198 199 (defcustom lsp-progress-via-spinner t 200 "If non-nil, display LSP $/progress reports via a spinner in the modeline." 201 :group 'lsp-mode 202 :type 'boolean) 203 204 (defcustom lsp-progress-spinner-type 'progress-bar 205 "Holds the type of spinner to be used in the mode-line. 206 Takes a value accepted by `spinner-start'." 207 :group 'lsp-mode 208 :type `(choice :tag "Choose a spinner by name" 209 ,@(mapcar (lambda (c) (list 'const (car c))) 210 spinner-types))) 211 212 (defvar-local lsp-use-workspace-root-for-server-default-directory nil 213 "Use `lsp-workspace-root' for `default-directory' when starting LSP process.") 214 215 (defvar-local lsp--cur-workspace nil) 216 217 (defvar-local lsp--cur-version 0) 218 (defvar-local lsp--virtual-buffer-connections nil) 219 (defvar-local lsp--virtual-buffer nil) 220 (defvar lsp--virtual-buffer-mappings (ht)) 221 222 (defvar lsp--uri-file-prefix (pcase system-type 223 (`windows-nt "file:///") 224 (_ "file://")) 225 "Prefix for a file-uri.") 226 227 (defvar-local lsp-buffer-uri nil 228 "If set, return it instead of calculating it using `buffer-file-name'.") 229 230 (define-error 'lsp-error "Unknown lsp-mode error") 231 (define-error 'lsp-empty-response-error 232 "Empty response from the language server" 'lsp-error) 233 (define-error 'lsp-timed-out-error 234 "Timed out while waiting for a response from the language server" 'lsp-error) 235 (define-error 'lsp-capability-not-supported 236 "Capability not supported by the language server" 'lsp-error) 237 (define-error 'lsp-file-scheme-not-supported 238 "Unsupported file scheme" 'lsp-error) 239 (define-error 'lsp-client-already-exists-error 240 "A client with this server-id already exists" 'lsp-error) 241 (define-error 'lsp-no-code-actions 242 "No code actions" 'lsp-error) 243 244 (defcustom lsp-auto-guess-root nil 245 "Automatically guess the project root using projectile/project. 246 Do *not* use this setting unless you are familiar with `lsp-mode' 247 internals and you are sure that all of your projects are 248 following `projectile'/`project.el' conventions." 249 :group 'lsp-mode 250 :type 'boolean) 251 252 (defcustom lsp-guess-root-without-session nil 253 "Ignore the session file when calculating the project root. 254 You almost always want to set lsp-auto-guess-root too. 255 Do *not* use this setting unless you are familiar with `lsp-mode' 256 internals and you are sure that all of your projects are 257 following `projectile'/`project.el' conventions." 258 :group 'lsp-mode 259 :type 'boolean) 260 261 (defcustom lsp-restart 'interactive 262 "Defines how server-exited events must be handled." 263 :group 'lsp-mode 264 :type '(choice (const interactive) 265 (const auto-restart) 266 (const ignore))) 267 268 (defcustom lsp-session-file (expand-file-name (locate-user-emacs-file ".lsp-session-v1")) 269 "File where session information is stored." 270 :group 'lsp-mode 271 :type 'file) 272 273 (defcustom lsp-auto-configure t 274 "Auto configure `lsp-mode' main features. 275 When set to t `lsp-mode' will auto-configure completion, 276 code-actions, breadcrumb, `flycheck', `flymake', `imenu', symbol highlighting, 277 lenses, links, and so on. 278 279 For finer granularity you may use `lsp-enable-*' properties." 280 :group 'lsp-mode 281 :type 'boolean 282 :package-version '(lsp-mode . "6.1")) 283 284 (defcustom lsp-disabled-clients nil 285 "A list of disabled/blocklisted clients. 286 Each entry in the list can be either: 287 a symbol, the server-id for the LSP client, or 288 a cons pair (MAJOR-MODE . CLIENTS), where MAJOR-MODE is the major-mode, 289 and CLIENTS is either a client or a list of clients. 290 291 This option can also be used as a file- or directory-local variable to 292 disable a language server for individual files or directories/projects 293 respectively." 294 :group 'lsp-mode 295 :type '(repeat (symbol)) 296 :safe 'listp 297 :package-version '(lsp-mode . "6.1")) 298 299 (defvar lsp-clients (make-hash-table :test 'eql) 300 "Hash table server-id -> client. 301 It contains all of the clients that are currently registered.") 302 303 (defvar lsp-enabled-clients nil 304 "List of clients allowed to be used for projects. 305 When nil, all registered clients are considered candidates.") 306 307 (defvar lsp-last-id 0 308 "Last request id.") 309 310 (defcustom lsp-before-initialize-hook nil 311 "List of functions to be called before a Language Server has been initialized 312 for a new workspace." 313 :type 'hook 314 :group 'lsp-mode) 315 316 (defcustom lsp-after-initialize-hook nil 317 "List of functions to be called after a Language Server has been initialized 318 for a new workspace." 319 :type 'hook 320 :group 'lsp-mode) 321 322 (defcustom lsp-before-open-hook nil 323 "List of functions to be called before a new file with LSP support is opened." 324 :type 'hook 325 :group 'lsp-mode) 326 327 (defcustom lsp-after-open-hook nil 328 "List of functions to be called after a new file with LSP support is opened." 329 :type 'hook 330 :group 'lsp-mode) 331 332 (defcustom lsp-enable-file-watchers t 333 "If non-nil lsp-mode will watch the files in the workspace if 334 the server has requested that." 335 :type 'boolean 336 :group 'lsp-mode 337 :package-version '(lsp-mode . "6.1")) 338 ;;;###autoload(put 'lsp-enable-file-watchers 'safe-local-variable #'booleanp) 339 340 (define-obsolete-variable-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "8.0.0") 341 342 (defcustom lsp-file-watch-ignored-directories 343 '(; SCM tools 344 "[/\\\\]\\.git\\'" 345 "[/\\\\]\\.github\\'" 346 "[/\\\\]\\.gitlab\\'" 347 "[/\\\\]\\.circleci\\'" 348 "[/\\\\]\\.hg\\'" 349 "[/\\\\]\\.bzr\\'" 350 "[/\\\\]_darcs\\'" 351 "[/\\\\]\\.svn\\'" 352 "[/\\\\]_FOSSIL_\\'" 353 ;; IDE or build tools 354 "[/\\\\]\\.idea\\'" 355 "[/\\\\]\\.ensime_cache\\'" 356 "[/\\\\]\\.eunit\\'" 357 "[/\\\\]node_modules" 358 "[/\\\\]\\.yarn\\'" 359 "[/\\\\]\\.fslckout\\'" 360 "[/\\\\]\\.tox\\'" 361 "[/\\\\]\\.nox\\'" 362 "[/\\\\]dist\\'" 363 "[/\\\\]dist-newstyle\\'" 364 "[/\\\\]\\.stack-work\\'" 365 "[/\\\\]\\.bloop\\'" 366 "[/\\\\]\\.metals\\'" 367 "[/\\\\]target\\'" 368 "[/\\\\]\\.ccls-cache\\'" 369 "[/\\\\]\\.vs\\'" 370 "[/\\\\]\\.vscode\\'" 371 "[/\\\\]\\.venv\\'" 372 "[/\\\\]\\.mypy_cache\\'" 373 "[/\\\\]\\.pytest_cache\\'" 374 ;; Swift Package Manager 375 "[/\\\\]\\.build\\'" 376 ;; Python 377 "[/\\\\]__pycache__\\'" 378 ;; Autotools output 379 "[/\\\\]\\.deps\\'" 380 "[/\\\\]build-aux\\'" 381 "[/\\\\]autom4te.cache\\'" 382 "[/\\\\]\\.reference\\'" 383 ;; Bazel 384 "[/\\\\]bazel-[^/\\\\]+\\'" 385 ;; CSharp 386 "[/\\\\]\\.cache[/\\\\]lsp-csharp\\'" 387 "[/\\\\]\\.meta\\'" 388 "[/\\\\]\\.nuget\\'" 389 ;; Unity 390 "[/\\\\]Library\\'" 391 ;; Clojure 392 "[/\\\\]\\.lsp\\'" 393 "[/\\\\]\\.clj-kondo\\'" 394 "[/\\\\]\\.shadow-cljs\\'" 395 "[/\\\\]\\.babel_cache\\'" 396 "[/\\\\]\\.cpcache\\'" 397 "[/\\\\]\\checkouts\\'" 398 ;; Gradle 399 "[/\\\\]\\.gradle\\'" 400 ;; Maven 401 "[/\\\\]\\.m2\\'" 402 ;; .Net Core build-output 403 "[/\\\\]bin/Debug\\'" 404 "[/\\\\]obj\\'" 405 ;; OCaml and Dune 406 "[/\\\\]_opam\\'" 407 "[/\\\\]_build\\'" 408 ;; Elixir 409 "[/\\\\]\\.elixir_ls\\'" 410 ;; Elixir Credo 411 "[/\\\\]\\.elixir-tools\\'" 412 ;; terraform and terragrunt 413 "[/\\\\]\\.terraform\\'" 414 "[/\\\\]\\.terragrunt-cache\\'" 415 ;; nix-direnv 416 "[/\\\\]\\result" 417 "[/\\\\]\\result-bin" 418 "[/\\\\]\\.direnv\\'") 419 "List of regexps matching directory paths which won't be monitored when 420 creating file watches. Customization of this variable is only honored at 421 the global level or at a root of an lsp workspace." 422 :group 'lsp-mode 423 :type '(repeat string) 424 :package-version '(lsp-mode . "8.0.0")) 425 426 (define-obsolete-function-alias 'lsp-file-watch-ignored 'lsp-file-watch-ignored-directories "7.0.1") 427 428 (defun lsp-file-watch-ignored-directories () 429 lsp-file-watch-ignored-directories) 430 431 ;; Allow lsp-file-watch-ignored-directories as a file or directory-local variable 432 ;;;###autoload(put 'lsp-file-watch-ignored-directories 'safe-local-variable 'lsp--string-listp) 433 434 (defcustom lsp-file-watch-ignored-files 435 '( 436 ;; Flycheck tempfiles 437 "[/\\\\]flycheck_[^/\\\\]+\\'" 438 ;; lockfiles 439 "[/\\\\]\\.#[^/\\\\]+\\'" 440 ;; backup files 441 "[/\\\\][^/\\\\]+~\\'" ) 442 "List of regexps matching files for which change events will 443 not be sent to the server. 444 445 This setting has no impact on whether a file-watch is created for 446 a directory; it merely prevents notifications pertaining to 447 matched files from being sent to the server. To prevent a 448 file-watch from being created for a directory, customize 449 `lsp-file-watch-ignored-directories' 450 451 Customization of this variable is only honored at the global 452 level or at a root of an lsp workspace." 453 :group 'lsp-mode 454 :type '(repeat string) 455 :package-version '(lsp-mode . "8.0.0")) 456 457 ;; Allow lsp-file-watch-ignored-files as a file or directory-local variable 458 ;;;###autoload(put 'lsp-file-watch-ignored-files 'safe-local-variable 'lsp--string-listp) 459 460 (defcustom lsp-after-uninitialized-functions nil 461 "List of functions to be called after a Language Server has been uninitialized." 462 :type 'hook 463 :group 'lsp-mode 464 :package-version '(lsp-mode . "6.3")) 465 466 (defconst lsp--sync-full 1) 467 (defconst lsp--sync-incremental 2) 468 469 (defcustom lsp-debounce-full-sync-notifications t 470 "If non-nil debounce full sync events. 471 This flag affects only servers which do not support incremental updates." 472 :type 'boolean 473 :group 'lsp-mode 474 :package-version '(lsp-mode . "6.1")) 475 476 (defcustom lsp-debounce-full-sync-notifications-interval 1.0 477 "Time to wait before sending full sync synchronization after buffer modification." 478 :type 'float 479 :group 'lsp-mode 480 :package-version '(lsp-mode . "6.1")) 481 482 (defvar lsp--stderr-index 0) 483 484 (defvar lsp--delayed-requests nil) 485 (defvar lsp--delay-timer nil) 486 487 (defcustom lsp-document-sync-method nil 488 "How to sync the document with the language server." 489 :type '(choice (const :tag "Documents are synced by always sending the full content of the document." lsp--sync-full) 490 (const :tag "Documents are synced by always sending incremental changes to the document." lsp--sync-incremental) 491 (const :tag "Use the method recommended by the language server." nil)) 492 :group 'lsp-mode) 493 494 (defcustom lsp-auto-execute-action t 495 "Auto-execute single action." 496 :type 'boolean 497 :group 'lsp-mode) 498 499 (defcustom lsp-enable-links t 500 "If non-nil, all references to links in a file will be made clickable, if 501 supported by the language server." 502 :type 'boolean 503 :group 'lsp-mode 504 :package-version '(lsp-mode . "6.1")) 505 506 (defcustom lsp-enable-imenu t 507 "If non-nil, automatically enable `imenu' integration when server provides 508 `textDocument/documentSymbol'." 509 :type 'boolean 510 :group 'lsp-mode 511 :package-version '(lsp-mode . "6.2")) 512 513 (defcustom lsp-enable-dap-auto-configure t 514 "If non-nil, enable `dap-auto-configure-mode`." 515 :type 'boolean 516 :group 'lsp-mode 517 :package-version '(lsp-mode . "7.0")) 518 519 (defcustom lsp-eldoc-enable-hover t 520 "If non-nil, `eldoc' will display hover info when it is present." 521 :type 'boolean 522 :group 'lsp-mode) 523 524 (defcustom lsp-eldoc-render-all nil 525 "Display all of the info returned by document/onHover. 526 If this is set to nil, `eldoc' will show only the symbol information." 527 :type 'boolean 528 :group 'lsp-mode) 529 530 (define-obsolete-variable-alias 'lsp-enable-completion-at-point 531 'lsp-completion-enable "lsp-mode 7.0.1") 532 533 (defcustom lsp-completion-enable t 534 "Enable `completion-at-point' integration." 535 :type 'boolean 536 :group 'lsp-completion) 537 538 (defcustom lsp-enable-symbol-highlighting t 539 "Highlight references of the symbol at point." 540 :type 'boolean 541 :group 'lsp-mode) 542 543 (defcustom lsp-enable-xref t 544 "Enable xref integration." 545 :type 'boolean 546 :group 'lsp-mode) 547 548 (defcustom lsp-references-exclude-definition nil 549 "If non-nil, exclude declarations when finding references." 550 :type 'boolean 551 :group 'lsp-mode) 552 553 (defcustom lsp-enable-indentation t 554 "Indent regions using the file formatting functionality provided by the 555 language server." 556 :type 'boolean 557 :group 'lsp-mode) 558 559 (defcustom lsp-enable-on-type-formatting t 560 "Enable `textDocument/onTypeFormatting' integration." 561 :type 'boolean 562 :group 'lsp-mode) 563 564 (defcustom lsp-enable-text-document-color t 565 "Enable `textDocument/documentColor' integration." 566 :type 'boolean 567 :group 'lsp-mode) 568 569 (defcustom lsp-before-save-edits t 570 "If non-nil, `lsp-mode' will apply edits suggested by the language server 571 before saving a document." 572 :type 'boolean 573 :group 'lsp-mode) 574 575 (defcustom lsp-after-apply-edits-hook nil 576 "Hooks to run when text edit is applied. 577 It contains the operation source." 578 :type 'hook 579 :group 'lsp-mode 580 :package-version '(lsp-mode . "8.0.0")) 581 582 (defcustom lsp-apply-edits-after-file-operations t 583 "Whether to apply edits returned by server after file operations if any. 584 Applicable only if server supports workspace.fileOperations for operations: 585 `workspace/willRenameFiles', `workspace/willCreateFiles' and 586 `workspace/willDeleteFiles'." 587 :group 'lsp-mode 588 :type 'boolean) 589 590 (defcustom lsp-modeline-code-actions-enable t 591 "Whether to show code actions on modeline." 592 :type 'boolean 593 :group 'lsp-modeline) 594 595 (defcustom lsp-modeline-diagnostics-enable t 596 "Whether to show diagnostics on modeline." 597 :type 'boolean 598 :group 'lsp-modeline) 599 600 (defcustom lsp-modeline-workspace-status-enable t 601 "Whether to show workspace status on modeline." 602 :type 'boolean 603 :group 'lsp-modeline 604 :package-version '(lsp-mode . "8.0.0")) 605 606 (defcustom lsp-headerline-breadcrumb-enable t 607 "Whether to enable breadcrumb on headerline." 608 :type 'boolean 609 :group 'lsp-headerline) 610 611 (defcustom lsp-configure-hook nil 612 "Hooks to run when `lsp-configure-buffer' is called." 613 :type 'hook 614 :group 'lsp-mode) 615 616 (defcustom lsp-unconfigure-hook nil 617 "Hooks to run when `lsp-unconfig-buffer' is called." 618 :type 'hook 619 :group 'lsp-mode) 620 621 (defcustom lsp-after-diagnostics-hook nil 622 "Hooks to run after diagnostics are received. 623 Note: it runs only if the receiving buffer is open. Use 624 `lsp-diagnostics-updated-hook'if you want to be notified when 625 diagnostics have changed." 626 :type 'hook 627 :group 'lsp-mode) 628 629 (define-obsolete-variable-alias 'lsp-after-diagnostics-hook 630 'lsp-diagnostics-updated-hook "lsp-mode 6.4") 631 632 (defcustom lsp-diagnostics-updated-hook nil 633 "Hooks to run after diagnostics are received." 634 :type 'hook 635 :group 'lsp-mode) 636 637 (define-obsolete-variable-alias 'lsp-workspace-folders-changed-hook 638 'lsp-workspace-folders-changed-functions "lsp-mode 6.3") 639 640 (defcustom lsp-workspace-folders-changed-functions nil 641 "Hooks to run after the folders has changed. 642 The hook will receive two parameters list of added and removed folders." 643 :type 'hook 644 :group 'lsp-mode) 645 646 (define-obsolete-variable-alias 'lsp-eldoc-hook 'eldoc-documentation-functions "lsp-mode 9.0.0") 647 648 (defcustom lsp-before-apply-edits-hook nil 649 "Hooks to run before applying edits." 650 :type 'hook 651 :group 'lsp-mode) 652 653 (defgroup lsp-imenu nil 654 "LSP Imenu." 655 :group 'lsp-mode 656 :tag "LSP Imenu") 657 658 (defcustom lsp-imenu-show-container-name t 659 "Display the symbol's container name in an imenu entry." 660 :type 'boolean 661 :group 'lsp-imenu) 662 663 (defcustom lsp-imenu-container-name-separator "/" 664 "Separator string to use to separate the container name from the symbol while 665 displaying imenu entries." 666 :type 'string 667 :group 'lsp-imenu) 668 669 (defcustom lsp-imenu-sort-methods '(kind name) 670 "How to sort the imenu items. 671 672 The value is a list of `kind' `name' or `position'. Priorities 673 are determined by the index of the element." 674 :type '(repeat (choice (const name) 675 (const position) 676 (const kind))) 677 :group 'lsp-imenu) 678 679 (defcustom lsp-imenu-index-symbol-kinds nil 680 "Which symbol kinds to show in imenu." 681 :type '(repeat (choice (const :tag "Miscellaneous" nil) 682 (const :tag "File" File) 683 (const :tag "Module" Module) 684 (const :tag "Namespace" Namespace) 685 (const :tag "Package" Package) 686 (const :tag "Class" Class) 687 (const :tag "Method" Method) 688 (const :tag "Property" Property) 689 (const :tag "Field" Field) 690 (const :tag "Constructor" Constructor) 691 (const :tag "Enum" Enum) 692 (const :tag "Interface" Interface) 693 (const :tag "Function" Function) 694 (const :tag "Variable" Variable) 695 (const :tag "Constant" Constant) 696 (const :tag "String" String) 697 (const :tag "Number" Number) 698 (const :tag "Boolean" Boolean) 699 (const :tag "Array" Array) 700 (const :tag "Object" Object) 701 (const :tag "Key" Key) 702 (const :tag "Null" Null) 703 (const :tag "Enum Member" EnumMember) 704 (const :tag "Struct" Struct) 705 (const :tag "Event" Event) 706 (const :tag "Operator" Operator) 707 (const :tag "Type Parameter" TypeParameter))) 708 :group 'lsp-imenu) 709 710 ;; vibhavp: Should we use a lower value (5)? 711 (defcustom lsp-response-timeout 10 712 "Number of seconds to wait for a response from the language server before 713 timing out. Nil if no timeout." 714 :type '(choice 715 (number :tag "Seconds") 716 (const :tag "No timeout" nil)) 717 :group 'lsp-mode) 718 719 (defcustom lsp-tcp-connection-timeout 2 720 "The timeout for tcp connection in seconds." 721 :type 'number 722 :group 'lsp-mode 723 :package-version '(lsp-mode . "6.2")) 724 725 (defconst lsp--imenu-compare-function-alist 726 (list (cons 'name #'lsp--imenu-compare-name) 727 (cons 'kind #'lsp--imenu-compare-kind) 728 (cons 'position #'lsp--imenu-compare-line-col)) 729 "An alist of (METHOD . FUNCTION). 730 METHOD is one of the symbols accepted by 731 `lsp-imenu-sort-methods'. 732 733 FUNCTION takes two hash tables representing DocumentSymbol. It 734 returns a negative number, 0, or a positive number indicating 735 whether the first parameter is less than, equal to, or greater 736 than the second parameter.") 737 738 (defcustom lsp-diagnostic-clean-after-change nil 739 "When non-nil, clean the diagnostics on change. 740 741 Note that when that setting is nil, `lsp-mode' will show stale 742 diagnostics until server publishes the new set of diagnostics" 743 :type 'boolean 744 :group 'lsp-diagnostics 745 :package-version '(lsp-mode . "7.0.1")) 746 747 (defcustom lsp-server-trace nil 748 "Request tracing on the server side. 749 The actual trace output at each level depends on the language server in use. 750 Changes take effect only when a new session is started." 751 :type '(choice (const :tag "Disabled" "off") 752 (const :tag "Messages only" "messages") 753 (const :tag "Verbose" "verbose") 754 (const :tag "Default (disabled)" nil)) 755 :group 'lsp-mode 756 :package-version '(lsp-mode . "6.1")) 757 758 (defcustom lsp-auto-touch-files t 759 "If non-nil ensure the files exist before sending 760 `textDocument/didOpen' notification." 761 :type 'boolean 762 :group 'lsp-mode 763 :package-version '(lsp-mode . "9.0.0")) 764 765 (defvar lsp-language-id-configuration 766 '(("\\(^CMakeLists\\.txt\\|\\.cmake\\)\\'" . "cmake") 767 ("\\(^Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . "dockerfile") 768 ("\\.astro$" . "astro") 769 ("\\.cs\\'" . "csharp") 770 ("\\.css$" . "css") 771 ("\\.cypher$" . "cypher") 772 ("Earthfile" . "earthfile") 773 ("\\.ebuild$" . "shellscript") 774 ("\\.go\\'" . "go") 775 ("\\.html$" . "html") 776 ("\\.hx$" . "haxe") 777 ("\\.hy$" . "hy") 778 ("\\.java\\'" . "java") 779 ("\\.jq$" . "jq") 780 ("\\.js$" . "javascript") 781 ("\\.json$" . "json") 782 ("\\.jsonc$" . "jsonc") 783 ("\\.jsonnet$" . "jsonnet") 784 ("\\.jsx$" . "javascriptreact") 785 ("\\.lua$" . "lua") 786 ("\\.mdx\\'" . "mdx") 787 ("\\.nu$" . "nushell") 788 ("\\.php$" . "php") 789 ("\\.ps[dm]?1\\'" . "powershell") 790 ("\\.rs\\'" . "rust") 791 ("\\.spec\\'" . "rpm-spec") 792 ("\\.sql$" . "sql") 793 ("\\.svelte$" . "svelte") 794 ("\\.toml\\'" . "toml") 795 ("\\.ts$" . "typescript") 796 ("\\.tsx$" . "typescriptreact") 797 ("\\.ttcn3$" . "ttcn3") 798 ("\\.vue$" . "vue") 799 ("\\.xml$" . "xml") 800 ("\\ya?ml$" . "yaml") 801 ("^PKGBUILD$" . "shellscript") 802 ("^go\\.mod\\'" . "go.mod") 803 ("^settings\\.json$" . "jsonc") 804 ("^yang\\.settings$" . "jsonc") 805 ("^meson\\(_options\\.txt\\|\\.\\(build\\|format\\)\\)\\'" . "meson") 806 (ada-mode . "ada") 807 (ada-ts-mode . "ada") 808 (gpr-mode . "gpr") 809 (gpr-ts-mode . "gpr") 810 (awk-mode . "awk") 811 (awk-ts-mode . "awk") 812 (nxml-mode . "xml") 813 (sql-mode . "sql") 814 (vimrc-mode . "vim") 815 (vimscript-ts-mode . "vim") 816 (sh-mode . "shellscript") 817 (bash-ts-mode . "shellscript") 818 (ebuild-mode . "shellscript") 819 (pkgbuild-mode . "shellscript") 820 (envrc-file-mode . "shellscript") 821 (scala-mode . "scala") 822 (scala-ts-mode . "scala") 823 (julia-mode . "julia") 824 (julia-ts-mode . "julia") 825 (clojure-mode . "clojure") 826 (clojurec-mode . "clojure") 827 (clojurescript-mode . "clojurescript") 828 (clojure-ts-mode . "clojure") 829 (clojure-ts-clojurec-mode . "clojure") 830 (clojure-ts-clojurescript-mode . "clojurescript") 831 (java-mode . "java") 832 (java-ts-mode . "java") 833 (jdee-mode . "java") 834 (groovy-mode . "groovy") 835 (python-mode . "python") 836 (python-ts-mode . "python") 837 (cython-mode . "python") 838 ("\\(\\.mojo\\|\\.🔥\\)\\'" . "mojo") 839 (lsp--render-markdown . "markdown") 840 (move-mode . "move") 841 (rust-mode . "rust") 842 (rust-ts-mode . "rust") 843 (rustic-mode . "rust") 844 (kotlin-mode . "kotlin") 845 (kotlin-ts-mode . "kotlin") 846 (css-mode . "css") 847 (css-ts-mode . "css") 848 (less-mode . "less") 849 (less-css-mode . "less") 850 (lua-mode . "lua") 851 (lua-ts-mode . "lua") 852 (sass-mode . "sass") 853 (ssass-mode . "sass") 854 (scss-mode . "scss") 855 (scad-mode . "openscad") 856 (xml-mode . "xml") 857 (c-mode . "c") 858 (c-ts-mode . "c") 859 (c++-mode . "cpp") 860 (c++-ts-mode . "cpp") 861 (cuda-mode . "cuda") 862 (objc-mode . "objective-c") 863 (html-mode . "html") 864 (html-ts-mode . "html") 865 (sgml-mode . "html") 866 (mhtml-mode . "html") 867 (mint-mode . "mint") 868 (go-dot-mod-mode . "go.mod") 869 (go-mod-ts-mode . "go.mod") 870 (go-mode . "go") 871 (go-ts-mode . "go") 872 (graphql-mode . "graphql") 873 (haskell-mode . "haskell") 874 (hack-mode . "hack") 875 (php-mode . "php") 876 (php-ts-mode . "php") 877 (powershell-mode . "powershell") 878 (powershell-mode . "PowerShell") 879 (powershell-ts-mode . "powershell") 880 (json-mode . "json") 881 (json-ts-mode . "json") 882 (jsonc-mode . "jsonc") 883 (rjsx-mode . "javascript") 884 (js2-mode . "javascript") 885 (js-mode . "javascript") 886 (js-ts-mode . "javascript") 887 (typescript-mode . "typescript") 888 (typescript-ts-mode . "typescript") 889 (tsx-ts-mode . "typescriptreact") 890 (svelte-mode . "svelte") 891 (fsharp-mode . "fsharp") 892 (reason-mode . "reason") 893 (caml-mode . "ocaml") 894 (tuareg-mode . "ocaml") 895 (swift-mode . "swift") 896 (elixir-mode . "elixir") 897 (elixir-ts-mode . "elixir") 898 (heex-ts-mode . "elixir") 899 (conf-javaprop-mode . "spring-boot-properties") 900 (yaml-mode . "yaml") 901 (yaml-ts-mode . "yaml") 902 (ruby-mode . "ruby") 903 (enh-ruby-mode . "ruby") 904 (ruby-ts-mode . "ruby") 905 (feature-mode . "cucumber") 906 (fortran-mode . "fortran") 907 (f90-mode . "fortran") 908 (elm-mode . "elm") 909 (dart-mode . "dart") 910 (erlang-mode . "erlang") 911 (dockerfile-mode . "dockerfile") 912 (dockerfile-ts-mode . "dockerfile") 913 (csharp-mode . "csharp") 914 (csharp-tree-sitter-mode . "csharp") 915 (csharp-ts-mode . "csharp") 916 (plain-tex-mode . "plaintex") 917 (context-mode . "context") 918 (cypher-mode . "cypher") 919 (latex-mode . "latex") 920 (LaTeX-mode . "latex") 921 (v-mode . "v") 922 (vhdl-mode . "vhdl") 923 (vhdl-ts-mode . "vhdl") 924 (verilog-mode . "verilog") 925 (terraform-mode . "terraform") 926 (ess-julia-mode . "julia") 927 (ess-r-mode . "r") 928 (crystal-mode . "crystal") 929 (nim-mode . "nim") 930 (dhall-mode . "dhall") 931 (cmake-mode . "cmake") 932 (cmake-ts-mode . "cmake") 933 (purescript-mode . "purescript") 934 (gdscript-mode . "gdscript") 935 (gdscript-ts-mode . "gdscript") 936 (perl-mode . "perl") 937 (cperl-mode . "perl") 938 (robot-mode . "robot") 939 (racket-mode . "racket") 940 (nix-mode . "nix") 941 (nix-ts-mode . "Nix") 942 (prolog-mode . "prolog") 943 (vala-mode . "vala") 944 (actionscript-mode . "actionscript") 945 (d-mode . "d") 946 (zig-mode . "zig") 947 (text-mode . "plaintext") 948 (markdown-mode . "markdown") 949 (gfm-mode . "markdown") 950 (beancount-mode . "beancount") 951 (conf-toml-mode . "toml") 952 (toml-ts-mode . "toml") 953 (org-mode . "org") 954 (org-journal-mode . "org") 955 (nginx-mode . "nginx") 956 (magik-mode . "magik") 957 (magik-ts-mode . "magik") 958 (idris-mode . "idris") 959 (idris2-mode . "idris2") 960 (gleam-mode . "gleam") 961 (gleam-ts-mode . "gleam") 962 (graphviz-dot-mode . "dot") 963 (tiltfile-mode . "tiltfile") 964 (solidity-mode . "solidity") 965 (bibtex-mode . "bibtex") 966 (rst-mode . "restructuredtext") 967 (glsl-mode . "glsl") 968 (shader-mode . "shaderlab") 969 (wgsl-mode . "wgsl") 970 (jq-mode . "jq") 971 (jq-ts-mode . "jq") 972 (protobuf-mode . "protobuf") 973 (nushell-mode . "nushell") 974 (nushell-ts-mode . "nushell") 975 (meson-mode . "meson") 976 (yang-mode . "yang")) 977 "Language id configuration.") 978 979 (defvar lsp--last-active-workspaces nil 980 "Keep track of last active workspace. 981 We want to try the last workspace first when jumping into a library 982 directory") 983 984 (defvar lsp-method-requirements 985 '(("textDocument/callHierarchy" :capability :callHierarchyProvider) 986 ("textDocument/codeAction" :capability :codeActionProvider) 987 ("codeAction/resolve" 988 :check-command (lambda (workspace) 989 (with-lsp-workspace workspace 990 (lsp:code-action-options-resolve-provider? 991 (lsp--capability-for-method "textDocument/codeAction"))))) 992 ("textDocument/codeLens" :capability :codeLensProvider) 993 ("textDocument/completion" :capability :completionProvider) 994 ("completionItem/resolve" 995 :check-command (lambda (wk) 996 (with-lsp-workspace wk 997 (lsp:completion-options-resolve-provider? 998 (lsp--capability-for-method "textDocument/completion"))))) 999 ("textDocument/declaration" :capability :declarationProvider) 1000 ("textDocument/definition" :capability :definitionProvider) 1001 ("textDocument/documentColor" :capability :colorProvider) 1002 ("textDocument/documentLink" :capability :documentLinkProvider) 1003 ("textDocument/inlayHint" :capability :inlayHintProvider) 1004 ("textDocument/documentHighlight" :capability :documentHighlightProvider) 1005 ("textDocument/documentSymbol" :capability :documentSymbolProvider) 1006 ("textDocument/foldingRange" :capability :foldingRangeProvider) 1007 ("textDocument/formatting" :capability :documentFormattingProvider) 1008 ("textDocument/hover" :capability :hoverProvider) 1009 ("textDocument/implementation" :capability :implementationProvider) 1010 ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider) 1011 ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider) 1012 ("textDocument/prepareRename" 1013 :check-command (lambda (workspace) 1014 (with-lsp-workspace workspace 1015 (lsp:rename-options-prepare-provider? 1016 (lsp--capability-for-method "textDocument/rename"))))) 1017 ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider) 1018 ("textDocument/references" :capability :referencesProvider) 1019 ("textDocument/rename" :capability :renameProvider) 1020 ("textDocument/selectionRange" :capability :selectionRangeProvider) 1021 ("textDocument/semanticTokens" :capability :semanticTokensProvider) 1022 ("textDocument/semanticTokensFull" 1023 :check-command (lambda (workspace) 1024 (with-lsp-workspace workspace 1025 (lsp-get (lsp--capability :semanticTokensProvider) :full)))) 1026 ("textDocument/semanticTokensFull/Delta" 1027 :check-command (lambda (workspace) 1028 (with-lsp-workspace workspace 1029 (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full))) 1030 (and (not (booleanp capFull)) (lsp-get capFull :delta)))))) 1031 ("textDocument/semanticTokensRangeProvider" 1032 :check-command (lambda (workspace) 1033 (with-lsp-workspace workspace 1034 (lsp-get (lsp--capability :semanticTokensProvider) :range)))) 1035 ("textDocument/signatureHelp" :capability :signatureHelpProvider) 1036 ("textDocument/typeDefinition" :capability :typeDefinitionProvider) 1037 ("textDocument/typeHierarchy" :capability :typeHierarchyProvider) 1038 ("textDocument/diagnostic" :capability :diagnosticProvider) 1039 ("workspace/executeCommand" :capability :executeCommandProvider) 1040 ("workspace/symbol" :capability :workspaceSymbolProvider)) 1041 1042 "Map methods to requirements. 1043 It is used by request-sending functions to determine which server 1044 must be used for handling a particular message.") 1045 1046 (defconst lsp--file-change-type 1047 `((created . 1) 1048 (changed . 2) 1049 (deleted . 3))) 1050 1051 (defconst lsp--watch-kind 1052 `((create . 1) 1053 (change . 2) 1054 (delete . 4))) 1055 1056 (defvar lsp-window-body-width 40 1057 "Window body width when rendering doc.") 1058 1059 (defface lsp-face-highlight-textual 1060 '((t :inherit highlight)) 1061 "Face used for textual occurrences of symbols." 1062 :group 'lsp-mode) 1063 1064 (defface lsp-face-highlight-read 1065 '((t :inherit highlight :underline t)) 1066 "Face used for highlighting symbols being read." 1067 :group 'lsp-mode) 1068 1069 (defface lsp-face-highlight-write 1070 '((t :inherit highlight :weight bold)) 1071 "Face used for highlighting symbols being written to." 1072 :group 'lsp-mode) 1073 1074 (define-obsolete-variable-alias 'lsp-lens-auto-enable 1075 'lsp-lens-enable "lsp-mode 7.0.1") 1076 1077 (defcustom lsp-lens-enable t 1078 "Auto enable lenses if server supports." 1079 :group 'lsp-lens 1080 :type 'boolean 1081 :package-version '(lsp-mode . "6.3")) 1082 1083 (defcustom lsp-symbol-highlighting-skip-current nil 1084 "If non-nil skip current symbol when setting symbol highlights." 1085 :group 'lsp-mode 1086 :type 'boolean) 1087 1088 (defcustom lsp-file-watch-threshold 1000 1089 "Show warning if the files to watch are more than. 1090 Set to nil to disable the warning." 1091 :type 'number 1092 :group 'lsp-mode) 1093 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i)))) 1094 1095 (defvar lsp-custom-markup-modes 1096 '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic")) 1097 "Mode to uses with markdown code blocks. 1098 They are added to `markdown-code-lang-modes'") 1099 1100 (defcustom lsp-signature-render-documentation t 1101 "Display signature documentation in `eldoc'." 1102 :type 'boolean 1103 :group 'lsp-mode 1104 :package-version '(lsp-mode . "6.2")) 1105 1106 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request) 1107 "Auto activate signature conditions." 1108 :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char) 1109 (const :tag "After selected completion." :after-completion) 1110 (const :tag "When the server has sent show signature help." :on-server-request))) 1111 :group 'lsp-mode 1112 :package-version '(lsp-mode . "6.2")) 1113 1114 (defcustom lsp-signature-doc-lines 20 1115 "If number, limit the number of lines to show in the docs." 1116 :type 'number 1117 :group 'lsp-mode 1118 :package-version '(lsp-mode . "6.3")) 1119 1120 (defcustom lsp-signature-function 'lsp-lv-message 1121 "The function used for displaying signature info. 1122 It will be called with one param - the signature info. When 1123 called with nil the signature info must be cleared." 1124 :type 'function 1125 :group 'lsp-mode 1126 :package-version '(lsp-mode . "6.3")) 1127 1128 (defcustom lsp-keymap-prefix "s-l" 1129 "LSP-mode keymap prefix." 1130 :group 'lsp-mode 1131 :type 'string 1132 :package-version '(lsp-mode . "6.3")) 1133 1134 (defvar-local lsp--buffer-workspaces () 1135 "List of the buffer workspaces.") 1136 1137 (defvar-local lsp--buffer-deferred nil 1138 "Whether buffer was loaded via `lsp-deferred'.") 1139 1140 (defvar lsp--session nil 1141 "Contain the `lsp-session' for the current Emacs instance.") 1142 1143 (defvar lsp--tcp-port 10000) 1144 1145 (defvar lsp--client-packages-required nil 1146 "If nil, `lsp-client-packages' are yet to be required.") 1147 1148 (defvar lsp--tcp-server-port 0 1149 "The server socket which is opened when using `lsp-tcp-server' (a server 1150 socket is opened in Emacs and the language server connects to it). The 1151 default value of 0 ensures that a random high port is used. Set it to a positive 1152 integer to use a specific port.") 1153 1154 (defvar lsp--tcp-server-wait-seconds 10 1155 "Wait this amount of time for the client to connect to our server socket 1156 when using `lsp-tcp-server'.") 1157 1158 (defvar-local lsp--document-symbols nil 1159 "The latest document symbols.") 1160 1161 (defvar-local lsp--document-selection-range-cache nil 1162 "The document selection cache.") 1163 1164 (defvar-local lsp--document-symbols-request-async nil 1165 "If non-nil, request document symbols asynchronously.") 1166 1167 (defvar-local lsp--document-symbols-tick -1 1168 "The value of `buffer-chars-modified-tick' when document 1169 symbols were last retrieved.") 1170 1171 (defvar-local lsp--have-document-highlights nil 1172 "Set to `t' on symbol highlighting, cleared on 1173 `lsp--cleanup-highlights-if-needed'. Checking a separately 1174 defined flag is substantially faster than unconditionally 1175 calling `remove-overlays'.") 1176 1177 ;; Buffer local variable for storing number of lines. 1178 (defvar lsp--log-lines) 1179 1180 (defvar-local lsp--eldoc-saved-message nil) 1181 1182 (defvar lsp--on-change-timer nil) 1183 (defvar lsp--on-idle-timer nil) 1184 1185 (defvar-local lsp--signature-last nil) 1186 (defvar-local lsp--signature-last-index nil) 1187 (defvar lsp--signature-last-buffer nil) 1188 1189 (defvar-local lsp--virtual-buffer-point-max nil) 1190 1191 (cl-defmethod lsp-execute-command (_server _command _arguments) 1192 "Ask SERVER to execute COMMAND with ARGUMENTS.") 1193 1194 (defun lsp-elt (sequence n) 1195 "Return Nth element of SEQUENCE or nil if N is out of range." 1196 (cond 1197 ((listp sequence) (elt sequence n)) 1198 ((arrayp sequence) 1199 (and (> (length sequence) n) (aref sequence n))) 1200 (t (and (> (length sequence) n) (elt sequence n))))) 1201 1202 ;; define seq-first and seq-rest for older emacs 1203 (defun lsp-seq-first (sequence) 1204 "Return the first element of SEQUENCE." 1205 (lsp-elt sequence 0)) 1206 1207 (defun lsp-seq-rest (sequence) 1208 "Return a sequence of the elements of SEQUENCE except the first one." 1209 (seq-drop sequence 1)) 1210 1211 ;;;###autoload 1212 (defun lsp--string-listp (sequence) 1213 "Return t if all elements of SEQUENCE are strings, else nil." 1214 (not (seq-find (lambda (x) (not (stringp x))) sequence))) 1215 1216 (defun lsp--string-vector-p (candidate) 1217 "Returns true if CANDIDATE is a vector data structure and 1218 every element of it is of type string, else nil." 1219 (and 1220 (vectorp candidate) 1221 (seq-every-p #'stringp candidate))) 1222 1223 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0") 1224 1225 (defun lsp--editable-vector-match (widget value) 1226 "Function for `lsp-editable-vector' :match." 1227 ;; Value must be a list or a vector and all the members must match the type. 1228 (and (or (listp value) (vectorp value)) 1229 (length (cdr (lsp--editable-vector-match-inline widget value))))) 1230 1231 (defun lsp--editable-vector-match-inline (widget value) 1232 "Value for `lsp-editable-vector' :match-inline." 1233 (let ((type (nth 0 (widget-get widget :args))) 1234 (ok t) 1235 found) 1236 (while (and value ok) 1237 (let ((answer (widget-match-inline type value))) 1238 (if answer 1239 (let ((head (if (vectorp answer) (aref answer 0) (car answer))) 1240 (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer)))) 1241 (setq found (append found head) 1242 value tail)) 1243 (setq ok nil)))) 1244 (cons found value))) 1245 1246 (defun lsp--editable-vector-value-to-external (_widget internal-value) 1247 "Convert the internal list value to a vector." 1248 (if (listp internal-value) 1249 (apply 'vector internal-value) 1250 internal-value)) 1251 1252 (defun lsp--editable-vector-value-to-internal (_widget external-value) 1253 "Convert the external vector value to a list." 1254 (if (vectorp external-value) 1255 (append external-value nil) 1256 external-value)) 1257 1258 (define-widget 'lsp--editable-vector 'editable-list 1259 "A subclass of `editable-list' that accepts and returns a 1260 vector instead of a list." 1261 :value-to-external 'lsp--editable-vector-value-to-external 1262 :value-to-internal 'lsp--editable-vector-value-to-internal 1263 :match 'lsp--editable-vector-match 1264 :match-inline 'lsp--editable-vector-match-inline) 1265 1266 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector 1267 "A variable length homogeneous vector." 1268 :tag "Repeat" 1269 :format "%{%t%}:\n%v%i\n") 1270 1271 (define-widget 'lsp-string-vector 'lazy 1272 "A vector of zero or more elements, every element of which is a string. 1273 Appropriate for any language-specific `defcustom' that needs to 1274 serialize as a JSON array of strings. 1275 1276 Deprecated. Use `lsp-repeatable-vector' instead. " 1277 :offset 4 1278 :tag "Vector" 1279 :type '(lsp-repeatable-vector string)) 1280 1281 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0") 1282 1283 (defvar lsp--show-message t 1284 "If non-nil, show debug message from `lsp-mode'.") 1285 1286 (defun lsp--message (format &rest args) 1287 "Wrapper for `message' 1288 1289 We `inhibit-message' the message when the cursor is in the 1290 minibuffer and when emacs version is before emacs 27 due to the 1291 fact that we often use `lsp--info', `lsp--warn' and `lsp--error' 1292 in async context and the call to these function is removing the 1293 minibuffer prompt. The issue with async messages is already fixed 1294 in emacs 27. 1295 1296 See #2049" 1297 (when lsp--show-message 1298 (let ((inhibit-message (or inhibit-message 1299 (and (minibufferp) 1300 (version< emacs-version "27.0"))))) 1301 (apply #'message format args)))) 1302 1303 (defun lsp--info (format &rest args) 1304 "Display lsp info message with FORMAT with ARGS." 1305 (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args))) 1306 1307 (defun lsp--warn (format &rest args) 1308 "Display lsp warn message with FORMAT with ARGS." 1309 (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args))) 1310 1311 (defun lsp--error (format &rest args) 1312 "Display lsp error message with FORMAT with ARGS." 1313 (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args))) 1314 1315 (defun lsp-log (format &rest args) 1316 "Log message to the ’*lsp-log*’ buffer. 1317 1318 FORMAT and ARGS i the same as for `message'." 1319 (when lsp-log-max 1320 (let ((log-buffer (get-buffer "*lsp-log*")) 1321 (inhibit-read-only t)) 1322 (unless log-buffer 1323 (setq log-buffer (get-buffer-create "*lsp-log*")) 1324 (with-current-buffer log-buffer 1325 (buffer-disable-undo) 1326 (view-mode 1) 1327 (set (make-local-variable 'lsp--log-lines) 0))) 1328 (with-current-buffer log-buffer 1329 (save-excursion 1330 (let* ((message (apply 'format format args)) 1331 ;; Count newlines in message. 1332 (newlines (1+ (cl-loop with start = 0 1333 for count from 0 1334 while (string-match "\n" message start) 1335 do (setq start (match-end 0)) 1336 finally return count)))) 1337 (goto-char (point-max)) 1338 1339 ;; in case the buffer is not empty insert before last \n to preserve 1340 ;; the point position(in case it is in the end) 1341 (if (eq (point) (point-min)) 1342 (progn 1343 (insert "\n") 1344 (backward-char)) 1345 (backward-char) 1346 (insert "\n")) 1347 (insert message) 1348 1349 (setq lsp--log-lines (+ lsp--log-lines newlines)) 1350 1351 (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max)) 1352 (let ((to-delete (- lsp--log-lines lsp-log-max))) 1353 (goto-char (point-min)) 1354 (forward-line to-delete) 1355 (delete-region (point-min) (point)) 1356 (setq lsp--log-lines lsp-log-max))))))))) 1357 1358 (defalias 'lsp-message 'lsp-log) 1359 1360 (defalias 'lsp-ht 'ht) 1361 1362 (defalias 'lsp-file-local-name 'file-local-name) 1363 1364 (defun lsp-f-canonical (file-name) 1365 "Return the canonical FILE-NAME, without a trailing slash." 1366 (directory-file-name (expand-file-name file-name))) 1367 1368 (defalias 'lsp-canonical-file-name 'lsp-f-canonical) 1369 1370 (defun lsp-f-same? (path-a path-b) 1371 "Return t if PATH-A and PATH-B are references to the same file. 1372 Symlinks are not followed." 1373 (when (and (f-exists? path-a) 1374 (f-exists? path-b)) 1375 (equal 1376 (lsp-f-canonical (directory-file-name (f-expand path-a))) 1377 (lsp-f-canonical (directory-file-name (f-expand path-b)))))) 1378 1379 (defun lsp-f-parent (path) 1380 "Return the parent directory to PATH. 1381 Symlinks are not followed." 1382 (let ((parent (file-name-directory 1383 (directory-file-name (f-expand path default-directory))))) 1384 (unless (lsp-f-same? path parent) 1385 (if (f-relative? path) 1386 (f-relative parent) 1387 (directory-file-name parent))))) 1388 1389 (defun lsp-f-ancestor-of? (path-a path-b) 1390 "Return t if PATH-A is an ancestor of PATH-B. 1391 Symlinks are not followed." 1392 (unless (lsp-f-same? path-a path-b) 1393 (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator)) 1394 (lsp-f-canonical path-b)))) 1395 1396 (defun lsp--merge-results (results method) 1397 "Merge RESULTS by filtering the empty hash-tables and merging 1398 the lists according to METHOD." 1399 (pcase (--map (if (vectorp it) 1400 (append it nil) it) 1401 (-filter #'identity results)) 1402 (`() ()) 1403 ;; only one result - simply return it 1404 (`(,fst) fst) 1405 ;; multiple results merge it based on strategy 1406 (results 1407 (pcase method 1408 ("textDocument/hover" (pcase (seq-filter 1409 (-compose #'not #'lsp-empty?) 1410 results) 1411 (`(,hover) hover) 1412 (hovers (lsp-make-hover 1413 :contents 1414 (-mapcat 1415 (-lambda ((&Hover :contents)) 1416 (if (and (sequencep contents) 1417 (not (stringp contents))) 1418 (append contents ()) 1419 (list contents))) 1420 hovers))))) 1421 ("textDocument/completion" 1422 (lsp-make-completion-list 1423 :is-incomplete (seq-some 1424 #'lsp:completion-list-is-incomplete 1425 results) 1426 :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it) 1427 (lsp:completion-list-items it) 1428 it) 1429 nil)) 1430 results))) 1431 ("completionItem/resolve" 1432 (let ((item (cl-first results))) 1433 (when-let ((details (seq-filter #'identity 1434 (seq-map #'lsp:completion-item-detail? results)))) 1435 (lsp:set-completion-item-detail? 1436 item 1437 (string-join details " "))) 1438 (when-let ((docs (seq-filter #'identity 1439 (seq-map #'lsp:completion-item-documentation? results)))) 1440 (lsp:set-completion-item-documentation? 1441 item 1442 (lsp-make-markup-content 1443 :kind (or (seq-some (lambda (it) 1444 (when (equal (lsp:markup-content-kind it) 1445 lsp/markup-kind-markdown) 1446 lsp/markup-kind-markdown)) 1447 docs) 1448 lsp/markup-kind-plain-text) 1449 :value (string-join (seq-map (lambda (doc) 1450 (or (lsp:markup-content-value doc) 1451 (and (stringp doc) doc))) 1452 docs) 1453 "\n")))) 1454 (when-let ((edits (seq-filter #'identity 1455 (seq-map #'lsp:completion-item-additional-text-edits? results)))) 1456 (lsp:set-completion-item-additional-text-edits? 1457 item 1458 (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits))) 1459 item)) 1460 (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results)))))) 1461 1462 (defun lsp--spinner-start () 1463 "Start spinner indication." 1464 (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error))) 1465 1466 (defun lsp--propertize (str type) 1467 "Propertize STR as per TYPE." 1468 (propertize str 'face (alist-get type lsp--message-type-face))) 1469 1470 (defun lsp-workspaces () 1471 "Return the lsp workspaces associated with the current project." 1472 (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces)) 1473 1474 (defun lsp--completing-read (prompt collection transform-fn &optional predicate 1475 require-match initial-input 1476 hist def inherit-input-method) 1477 "Wrap `completing-read' to provide transformation function and disable sort. 1478 1479 TRANSFORM-FN will be used to transform each of the items before displaying. 1480 1481 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF 1482 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes." 1483 (let* ((col (--map (cons (funcall transform-fn it) it) collection)) 1484 (completion (completing-read prompt 1485 (lambda (string pred action) 1486 (if (eq action 'metadata) 1487 `(metadata (display-sort-function . identity)) 1488 (complete-with-action action col string pred))) 1489 predicate require-match initial-input hist 1490 def inherit-input-method))) 1491 (cdr (assoc completion col)))) 1492 1493 (defconst lsp--system-arch (lambda () 1494 (setq lsp--system-arch 1495 (pcase system-type 1496 ('windows-nt 1497 (pcase system-configuration 1498 ((rx bol "x86_64-") 'x64) 1499 (_ 'x86))) 1500 ('darwin 1501 (pcase system-configuration 1502 ((rx "aarch64-") 'arm64) 1503 (_ 'x64))) 1504 ('gnu/linux 1505 (pcase system-configuration 1506 ((rx bol "aarch64-") 'arm64) 1507 ((rx bol "x86_64") 'x64) 1508 ((rx bol (| "i386" "i886")) 'x32))) 1509 (_ 1510 (pcase system-configuration 1511 ((rx bol "x86_64") 'x64) 1512 ((rx bol (| "i386" "i886")) 'x32)))))) 1513 "Return the system architecture of `Emacs'. 1514 Special values: 1515 `x64' 64bit 1516 `x32' 32bit 1517 `arm64' ARM 64bit") 1518 1519 (defmacro lsp-with-current-buffer (buffer-id &rest body) 1520 (declare (indent 1) (debug t)) 1521 `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer))) 1522 (with-lsp-workspaces (plist-get ,buffer-id :workspaces) 1523 (funcall wcb (lambda () ,@body))) 1524 (with-current-buffer ,buffer-id 1525 ,@body))) 1526 1527 (defvar lsp--throw-on-input nil 1528 "Make `lsp-*-while-no-input' throws `input' on interrupted.") 1529 1530 (defmacro lsp--catch (tag bodyform &rest handlers) 1531 "Catch TAG thrown in BODYFORM. 1532 The return value from TAG will be handled in HANDLERS by `pcase'." 1533 (declare (debug (form form &rest (pcase-PAT body))) (indent 2)) 1534 (let ((re-sym (make-symbol "re"))) 1535 `(let ((,re-sym (catch ,tag ,bodyform))) 1536 (pcase ,re-sym 1537 ,@handlers)))) 1538 1539 (defmacro lsp--while-no-input (&rest body) 1540 "Wrap BODY in `while-no-input' and respecting `non-essential'. 1541 If `lsp--throw-on-input' is set, will throw if input is pending, else 1542 return value of `body' or nil if interrupted." 1543 (declare (debug t) (indent 0)) 1544 `(if non-essential 1545 (let ((res (while-no-input ,@body))) 1546 (cond 1547 ((and lsp--throw-on-input (equal res t)) 1548 (throw 'input :interrupted)) 1549 ((booleanp res) nil) 1550 (t res))) 1551 ,@body)) 1552 1553 ;; A ‘lsp--client’ object describes the client-side behavior of a language 1554 ;; server. It is used to start individual server processes, each of which is 1555 ;; represented by a ‘lsp--workspace’ object. Client objects are normally 1556 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’. Each 1557 ;; workspace refers to exactly one client, but there can be multiple workspaces 1558 ;; for a single client. 1559 (cl-defstruct lsp--client 1560 ;; ‘language-id’ is a function that receives a buffer as a single argument 1561 ;; and should return the language identifier for that buffer. See 1562 ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem 1563 ;; for a list of language identifiers. Also consult the documentation for 1564 ;; the language server represented by this client to find out what language 1565 ;; identifiers it supports or expects. 1566 (language-id nil) 1567 1568 ;; ‘add-on?’ when set to t the server will be started no matter whether there 1569 ;; is another server handling the same mode. 1570 (add-on? nil) 1571 ;; ‘new-connection’ is a function that should start a language server process 1572 ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS). 1573 ;; COMMAND-PROCESS must be a process object representing the server process 1574 ;; just started. COMMUNICATION-PROCESS must be a process (including pipe and 1575 ;; network processes) that ‘lsp-mode’ uses to communicate with the language 1576 ;; server using the language server protocol. COMMAND-PROCESS and 1577 ;; COMMUNICATION-PROCESS may be the same process; in that case 1578 ;; ‘new-connection’ may also return that process as a single 1579 ;; object. ‘new-connection’ is called with two arguments, FILTER and 1580 ;; SENTINEL. FILTER should be used as process filter for 1581 ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for 1582 ;; COMMAND-PROCESS. 1583 (new-connection nil) 1584 1585 ;; ‘ignore-regexps’ is a list of regexps. When a data packet from the 1586 ;; language server matches any of these regexps, it will be ignored. This is 1587 ;; intended for dealing with language servers that output non-protocol data. 1588 (ignore-regexps nil) 1589 1590 ;; ‘ignore-messages’ is a list of regexps. When a message from the language 1591 ;; server matches any of these regexps, it will be ignored. This is useful 1592 ;; for filtering out unwanted messages; such as servers that send nonstandard 1593 ;; message types, or extraneous log messages. 1594 (ignore-messages nil) 1595 1596 ;; ‘notification-handlers’ is a hash table mapping notification method names 1597 ;; (strings) to functions handling the respective notifications. Upon 1598 ;; receiving a notification, ‘lsp-mode’ will call the associated handler 1599 ;; function passing two arguments, the ‘lsp--workspace’ object and the 1600 ;; deserialized notification parameters. 1601 (notification-handlers (make-hash-table :test 'equal)) 1602 1603 ;; ‘request-handlers’ is a hash table mapping request method names 1604 ;; (strings) to functions handling the respective notifications. Upon 1605 ;; receiving a request, ‘lsp-mode’ will call the associated handler function 1606 ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized 1607 ;; request parameters. 1608 (request-handlers (make-hash-table :test 'equal)) 1609 1610 ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request 1611 ;; identifiers for pending asynchronous requests to functions handling the 1612 ;; respective responses. Upon receiving a response from the language server, 1613 ;; ‘lsp-mode’ will call the associated response handler function with a 1614 ;; single argument, the deserialized response parameters. 1615 (response-handlers (make-hash-table :test 'eql)) 1616 1617 ;; ‘prefix-function’ is called for getting the prefix for completion. 1618 ;; The function takes no parameter and returns a cons (start . end) representing 1619 ;; the start and end bounds of the prefix. If it's not set, the client uses a 1620 ;; default prefix function." 1621 (prefix-function nil) 1622 1623 ;; Contains mapping of scheme to the function that is going to be used to load 1624 ;; the file. 1625 (uri-handlers (make-hash-table :test #'equal)) 1626 1627 ;; ‘action-handlers’ is a hash table mapping action to a handler function. It 1628 ;; can be used in `lsp-execute-code-action' to determine whether the action 1629 ;; current client is interested in executing the action instead of sending it 1630 ;; to the server. 1631 (action-handlers (make-hash-table :test 'equal)) 1632 1633 ;; `action-filter' can be set to a function that modifies any incoming 1634 ;; `CodeAction' in place before it is executed. The return value is ignored. 1635 ;; This can be used to patch up broken code action requests before they are 1636 ;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an 1637 ;; example of a function that can be useful here. 1638 (action-filter nil) 1639 1640 ;; major modes supported by the client. 1641 major-modes 1642 ;; Function that will be called to decide if this language client 1643 ;; should manage a particular buffer. The function will be passed 1644 ;; the file name and major mode to inform the decision. Setting 1645 ;; `activation-fn' will override `major-modes', if 1646 ;; present. 1647 activation-fn 1648 ;; Break the tie when major-mode is supported by multiple clients. 1649 (priority 0) 1650 ;; Unique identifier for representing the client object. 1651 server-id 1652 ;; defines whether the client supports multi root workspaces. 1653 multi-root 1654 ;; Initialization options or a function that returns initialization options. 1655 initialization-options 1656 ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or 1657 ;; completely replace, the faces used for semantic highlighting on a 1658 ;; client-by-client basis. 1659 ;; 1660 ;; It recognizes four members, all of which are optional: `:types’ and 1661 ;; `:modifiers’, respectively, should be face definition lists akin to 1662 ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be 1663 ;; merged with the default face definition list. 1664 ;; 1665 ;; Alternatively, if the plist members `:discard-default-types’ or 1666 ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers' 1667 ;; face definitions will be replaced entirely by their respective overrides. 1668 ;; 1669 ;; For example, setting `:semantic-tokens-faces-overrides' to 1670 ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from 1671 ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'. 1672 ;; 1673 ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))' 1674 ;; will also remap "macro", but on top of that associate the fictional token type 1675 ;; "not-quite-a-macro" with the face named `some-face'. 1676 ;; 1677 ;; `(:types (("macro" . font-lock-keyword-face)) 1678 ;; :modifiers (("declaration" . lsp-face-semhl-interface)) 1679 ;; :discard-default-types t 1680 ;; :discard-default-modifiers t)' 1681 ;; will discard all default face definitions, hence leaving the client with 1682 ;; only one token type "macro", mapped to `font-lock-keyword-face', and one 1683 ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'. 1684 semantic-tokens-faces-overrides 1685 ;; Provides support for registering LSP Server specific capabilities. 1686 custom-capabilities 1687 ;; Function which returns the folders that are considered to be not projects but library files. 1688 ;; The function accepts one parameter currently active workspace. 1689 ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225. 1690 library-folders-fn 1691 ;; function which will be called when opening file in the workspace to perform 1692 ;; client specific initialization. The function accepts one parameter 1693 ;; currently active workspace. 1694 before-file-open-fn 1695 ;; Function which will be called right after a workspace has been initialized. 1696 initialized-fn 1697 ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP. 1698 (remote? nil) 1699 1700 ;; ‘completion-in-comments?’ t if the client supports completion in comments. 1701 (completion-in-comments? nil) 1702 1703 ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client. 1704 (path->uri-fn nil) 1705 1706 ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client. 1707 (uri->path-fn nil) 1708 ;; Function that returns an environment structure that will be used 1709 ;; to set some environment variables when starting the language 1710 ;; server process. These environment variables enable some 1711 ;; additional features in the language server. The environment 1712 ;; structure is an alist of the form (KEY . VALUE), where KEY is a 1713 ;; string (regularly in all caps), and VALUE may be a string, a 1714 ;; boolean, or a sequence of strings. 1715 environment-fn 1716 1717 ;; ‘after-open-fn’ workspace after open specific hooks. 1718 (after-open-fn nil) 1719 1720 ;; ‘async-request-handlers’ is a hash table mapping request method names 1721 ;; (strings) to functions handling the respective requests that may take 1722 ;; time to finish. Upon receiving a request, ‘lsp-mode’ will call the 1723 ;; associated handler function passing three arguments, the ‘lsp--workspace’ 1724 ;; object, the deserialized request parameters and the callback which accept 1725 ;; result as its parameter. 1726 (async-request-handlers (make-hash-table :test 'equal)) 1727 download-server-fn 1728 download-in-progress? 1729 buffers 1730 synchronize-sections) 1731 1732 (defun lsp-clients-executable-find (find-command &rest args) 1733 "Finds an executable by invoking a search command. 1734 1735 FIND-COMMAND is the executable finder that searches for the 1736 actual language server executable. ARGS is a list of arguments to 1737 give to FIND-COMMAND to find the language server. Returns the 1738 output of FIND-COMMAND if it exits successfully, nil otherwise. 1739 1740 Typical uses include finding an executable by invoking `find' in 1741 a project, finding LLVM commands on macOS with `xcrun', or 1742 looking up project-specific language servers for projects written 1743 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv' 1744 etc." 1745 (when-let* ((find-command-path (executable-find find-command)) 1746 (executable-path 1747 (with-temp-buffer 1748 (when (zerop (apply 'call-process find-command-path nil t nil args)) 1749 (buffer-substring-no-properties (point-min) (point-max)))))) 1750 (string-trim executable-path))) 1751 1752 (defvar lsp--already-widened nil) 1753 1754 (defmacro lsp-save-restriction-and-excursion (&rest form) 1755 (declare (indent 0) (debug t)) 1756 `(if lsp--already-widened 1757 (save-excursion ,@form) 1758 (-let [lsp--already-widened t] 1759 (save-restriction 1760 (widen) 1761 (save-excursion ,@form))))) 1762 1763 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number 1764 (defun lsp--line-character-to-point (line character) 1765 "Return the point for character CHARACTER on line LINE." 1766 (or (lsp-virtual-buffer-call :line/character->point line character) 1767 (let ((inhibit-field-text-motion t)) 1768 (lsp-save-restriction-and-excursion 1769 (goto-char (point-min)) 1770 (forward-line line) 1771 ;; server may send character position beyond the current line and we 1772 ;; should fallback to line end. 1773 (-let [line-end (line-end-position)] 1774 (if (> character (- line-end (point))) 1775 line-end 1776 (forward-char character) 1777 (point))))))) 1778 1779 (lsp-defun lsp--position-to-point ((&Position :line :character)) 1780 "Convert `Position' object in PARAMS to a point." 1781 (lsp--line-character-to-point line character)) 1782 1783 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end)) 1784 (cons start end)) 1785 1786 (lsp-defun lsp--range-text ((&RangeToPoint :start :end)) 1787 (buffer-substring start end)) 1788 1789 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end))) 1790 (cond 1791 ((and 1792 (region-active-p) 1793 (<= start (region-beginning) end) 1794 (<= start (region-end) end) 1795 (or (not (= start (region-beginning))) 1796 (not (= end (region-end))))) 1797 (cons start end)) 1798 ((and (<= start (point) end) 1799 (not (region-active-p))) 1800 (cons start end)) 1801 (parent? (lsp--find-wrapping-range parent?)))) 1802 1803 (defun lsp--get-selection-range () 1804 (or 1805 (-when-let ((cache . cache-tick) lsp--document-selection-range-cache) 1806 (when (= cache-tick (buffer-modified-tick)) cache)) 1807 (let ((response (cl-first 1808 (lsp-request 1809 "textDocument/selectionRange" 1810 (list :textDocument (lsp--text-document-identifier) 1811 :positions (vector (lsp--cur-position))))))) 1812 (setq lsp--document-selection-range-cache 1813 (cons response (buffer-modified-tick))) 1814 response))) 1815 1816 (defun lsp-extend-selection () 1817 "Extend selection." 1818 (interactive) 1819 (unless (lsp-feature? "textDocument/selectionRange") 1820 (signal 'lsp-capability-not-supported (list "selectionRangeProvider"))) 1821 (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range))) 1822 (goto-char start) 1823 (set-mark (point)) 1824 (goto-char end) 1825 (exchange-point-and-mark))) 1826 1827 (defun lsp-warn (message &rest args) 1828 "Display a warning message made from (`format-message' MESSAGE ARGS...). 1829 This is equivalent to `display-warning', using `lsp-mode' as the type and 1830 `:warning' as the level." 1831 (display-warning 'lsp-mode (apply #'format-message message args))) 1832 1833 (defun lsp--get-uri-handler (scheme) 1834 "Get uri handler for SCHEME in the current workspace." 1835 (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it))) 1836 (or (lsp-workspaces) (lsp--session-workspaces (lsp-session))))) 1837 1838 (defun lsp--fix-path-casing (path) 1839 "On windows, downcases path because the windows file system is 1840 case-insensitive. 1841 1842 On other systems, returns path without change." 1843 (if (eq system-type 'windows-nt) (downcase path) path)) 1844 1845 (defun lsp--uri-to-path (uri) 1846 "Convert URI to a file path." 1847 (if-let ((fn (->> (lsp-workspaces) 1848 (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client)) 1849 (cl-first)))) 1850 (funcall fn uri) 1851 (lsp--uri-to-path-1 uri))) 1852 1853 (defun lsp-remap-path-if-needed (file-name) 1854 (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings)) 1855 (propertize (buffer-local-value 'buffer-file-name buffer) 1856 'lsp-virtual-buffer virtual-buffer) 1857 file-name)) 1858 1859 (defun lsp--uri-to-path-1 (uri) 1860 "Convert URI to a file path." 1861 (let* ((url (url-generic-parse-url (url-unhex-string uri))) 1862 (type (url-type url)) 1863 (target (url-target url)) 1864 (file 1865 (concat (decode-coding-string (url-filename url) 1866 (or locale-coding-system 'utf-8)) 1867 (when (and target 1868 (not (s-match 1869 (rx "#" (group (1+ num)) (or "," "#") 1870 (group (1+ num)) 1871 string-end) 1872 uri))) 1873 (concat "#" target)))) 1874 (file-name (if (and type (not (string= type "file"))) 1875 (if-let ((handler (lsp--get-uri-handler type))) 1876 (funcall handler uri) 1877 uri) 1878 ;; `url-generic-parse-url' is buggy on windows: 1879 ;; https://github.com/emacs-lsp/lsp-mode/pull/265 1880 (or (and (eq system-type 'windows-nt) 1881 (eq (elt file 0) ?\/) 1882 (substring file 1)) 1883 file)))) 1884 (->> file-name 1885 (concat (-some #'lsp--workspace-host-root (lsp-workspaces))) 1886 (lsp-remap-path-if-needed)))) 1887 1888 (defun lsp--buffer-uri () 1889 "Return URI of the current buffer." 1890 (or lsp-buffer-uri 1891 (plist-get lsp--virtual-buffer :buffer-uri) 1892 (lsp--path-to-uri 1893 (or (buffer-file-name) (buffer-file-name (buffer-base-buffer)))))) 1894 1895 (defun lsp-register-client-capabilities (&rest _args) 1896 "Implemented only to make `company-lsp' happy. 1897 DELETE when `lsp-mode.el' is deleted.") 1898 1899 (defconst lsp--url-path-allowed-chars 1900 (url--allowed-chars (append '(?/) url-unreserved-chars)) 1901 "`url-unreserved-chars' with additional delim ?/. 1902 This set of allowed chars is enough for hexifying local file paths.") 1903 1904 (defun lsp--path-to-uri-1 (path) 1905 (concat lsp--uri-file-prefix 1906 (--> path 1907 (expand-file-name it) 1908 (or (file-remote-p it 'localname t) it) 1909 (url-hexify-string it lsp--url-path-allowed-chars)))) 1910 1911 (defun lsp--path-to-uri (path) 1912 "Convert PATH to a uri." 1913 (if-let ((uri-fn (->> (lsp-workspaces) 1914 (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client)) 1915 (cl-first)))) 1916 (funcall uri-fn path) 1917 (lsp--path-to-uri-1 path))) 1918 1919 (defun lsp--string-match-any (regex-list str) 1920 "Return the first regex, if any, within REGEX-LIST matching STR." 1921 (--first (string-match it str) regex-list)) 1922 1923 (cl-defstruct lsp-watch 1924 (descriptors (make-hash-table :test 'equal)) 1925 root-directory) 1926 1927 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories) 1928 (let ((file-name (cl-third event)) 1929 (event-type (cl-second event))) 1930 (cond 1931 ((and (file-directory-p file-name) 1932 (equal 'created event-type) 1933 (not (lsp--string-match-any ignored-directories file-name))) 1934 1935 (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch) 1936 1937 ;; process the files that are already present in 1938 ;; the directory. 1939 (->> (directory-files-recursively file-name ".*" t) 1940 (seq-do (lambda (f) 1941 (unless (file-directory-p f) 1942 (funcall callback (list nil 'created f))))))) 1943 ((and (memq event-type '(created deleted changed)) 1944 (not (file-directory-p file-name)) 1945 (not (lsp--string-match-any ignored-files file-name))) 1946 (funcall callback event)) 1947 ((and (memq event-type '(renamed)) 1948 (not (file-directory-p file-name)) 1949 (not (lsp--string-match-any ignored-files file-name))) 1950 (funcall callback `(,(cl-first event) deleted ,(cl-third event))) 1951 (funcall callback `(,(cl-first event) created ,(cl-fourth event))))))) 1952 1953 (defun lsp--ask-about-watching-big-repo (number-of-directories dir) 1954 "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR. 1955 This is useful when there is a lot of files in a repository, as 1956 that may slow Emacs down. Returns t if the user wants to watch 1957 the entire repository, nil otherwise." 1958 (prog1 1959 (yes-or-no-p 1960 (format 1961 "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down. 1962 Do you want to watch all files in %s? " 1963 dir 1964 number-of-directories 1965 dir)) 1966 (lsp--info 1967 (concat "You can configure this warning with the `lsp-enable-file-watchers' " 1968 "and `lsp-file-watch-threshold' variables")))) 1969 1970 1971 (defun lsp--path-is-watchable-directory (path dir ignored-directories) 1972 "Figure out whether PATH (inside of DIR) is meant to have a file watcher set. 1973 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't 1974 want to watch." 1975 (let 1976 ((full-path (f-join dir path))) 1977 (and (file-accessible-directory-p full-path) 1978 (not (equal path ".")) 1979 (not (equal path "..")) 1980 (not (lsp--string-match-any ignored-directories full-path))))) 1981 1982 1983 (defun lsp--all-watchable-directories (dir ignored-directories) 1984 "Traverse DIR recursively returning a list of paths that should have watchers. 1985 IGNORED-DIRECTORIES will be used for exclusions" 1986 (let* ((dir (if (f-symlink? dir) 1987 (file-truename dir) 1988 dir))) 1989 (apply #'nconc 1990 ;; the directory itself is assumed to be part of the set 1991 (list dir) 1992 ;; collect all subdirectories that are watchable 1993 (-map 1994 (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories)) 1995 ;; but only look at subdirectories that are watchable 1996 (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories)) 1997 (directory-files dir)))))) 1998 1999 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?) 2000 "Create recursive file notification watch in DIR. 2001 CALLBACK will be called when there are changes in any of 2002 the monitored files. WATCHES is a hash table directory->file 2003 notification handle which contains all of the watch that 2004 already have been created. Watches will not be created for 2005 any directory that matches any regex in IGNORED-DIRECTORIES. 2006 Watches will not be created for any file that matches any 2007 regex in IGNORED-FILES." 2008 (let* ((dir (if (f-symlink? dir) 2009 (file-truename dir) 2010 dir)) 2011 (watch (or watch (make-lsp-watch :root-directory dir))) 2012 (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories))) 2013 (lsp-log "Creating watchers for following %s folders:\n %s" 2014 (length dirs-to-watch) 2015 (s-join "\n " dirs-to-watch)) 2016 (when (or 2017 (not warn-big-repo?) 2018 (not lsp-file-watch-threshold) 2019 (let ((number-of-directories (length dirs-to-watch))) 2020 (or 2021 (< number-of-directories lsp-file-watch-threshold) 2022 (condition-case nil 2023 (lsp--ask-about-watching-big-repo number-of-directories dir) 2024 (quit))))) 2025 (dolist (current-dir dirs-to-watch) 2026 (condition-case err 2027 (progn 2028 (puthash 2029 current-dir 2030 (file-notify-add-watch current-dir 2031 '(change) 2032 (lambda (event) 2033 (lsp--folder-watch-callback event callback watch ignored-files ignored-directories))) 2034 (lsp-watch-descriptors watch))) 2035 (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err))) 2036 (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))))) 2037 watch)) 2038 2039 (defun lsp-kill-watch (watch) 2040 "Delete WATCH." 2041 (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch)) 2042 (ht-clear! (lsp-watch-descriptors watch))) 2043 2044 (defun lsp-json-bool (val) 2045 "Convert VAL to JSON boolean." 2046 (if val t :json-false)) 2047 2048 (defmacro with-lsp-workspace (workspace &rest body) 2049 "Helper macro for invoking BODY in WORKSPACE context." 2050 (declare (debug (form body)) 2051 (indent 1)) 2052 `(let ((lsp--cur-workspace ,workspace)) ,@body)) 2053 2054 (defmacro with-lsp-workspaces (workspaces &rest body) 2055 "Helper macro for invoking BODY against multiple WORKSPACES." 2056 (declare (debug (form body)) 2057 (indent 1)) 2058 `(let ((lsp--buffer-workspaces ,workspaces)) ,@body)) 2059 2060 2061 2062 (defmacro lsp-consistency-check (package) 2063 `(defconst ,(intern (concat (symbol-name package) 2064 "-plist-value-when-compiled")) 2065 (eval-when-compile lsp-use-plists))) 2066 2067 2068 ;; loading code-workspace files 2069 2070 ;;;###autoload 2071 (defun lsp-load-vscode-workspace (file) 2072 "Load vscode workspace from FILE" 2073 (interactive "fSelect file to import: ") 2074 (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session))) 2075 2076 (let ((dir (f-dirname file))) 2077 (->> file 2078 (json-read-file) 2079 (alist-get 'folders) 2080 (-map (-lambda ((&alist 'path)) 2081 (lsp-workspace-folders-add (expand-file-name path dir))))))) 2082 2083 ;;;###autoload 2084 (defun lsp-save-vscode-workspace (file) 2085 "Save vscode workspace to FILE" 2086 (interactive "FSelect file to save to: ") 2087 2088 (let ((json-encoding-pretty-print t)) 2089 (f-write-text (json-encode 2090 `((folders . ,(->> (lsp-session) 2091 (lsp-session-folders) 2092 (--map `((path . ,it))))))) 2093 'utf-8 2094 file))) 2095 2096 2097 (defmacro lsp-foreach-workspace (&rest body) 2098 "Execute BODY for each of the current workspaces." 2099 (declare (debug (form body))) 2100 `(--map (with-lsp-workspace it ,@body) (lsp-workspaces))) 2101 2102 (defmacro when-lsp-workspace (workspace &rest body) 2103 "Helper macro for invoking BODY in WORKSPACE context if present." 2104 (declare (debug (form body)) 2105 (indent 1)) 2106 `(when-let ((lsp--cur-workspace ,workspace)) ,@body)) 2107 2108 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items)) 2109 (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read)) 2110 (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label)) 2111 items)) 2112 (result (funcall-interactively 2113 selectfunc 2114 (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels)) 2115 (choices (if (listp result) 2116 (if (equal result '("*")) 2117 itemLabels 2118 result) 2119 (list result)))) 2120 (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data)) 2121 (if (member label choices) 2122 (lsp-make-quick-pick-item :label label :picked t :user-data user-data) 2123 nil)) 2124 items))))) 2125 2126 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?)) 2127 (read-string (format "%s: " prompt) (or value? ""))) 2128 2129 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type)) 2130 "Send the server's messages to log. 2131 PARAMS - the data sent from _WORKSPACE." 2132 (funcall (cl-case type 2133 (1 'lsp--error) 2134 (2 'lsp--warn) 2135 (t 'lsp--info)) 2136 "%s" 2137 message)) 2138 2139 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type)) 2140 "Send the server's messages to log. 2141 PARAMS - the data sent from WORKSPACE." 2142 (ignore 2143 (let ((client (lsp--workspace-client workspace))) 2144 (when (or (not client) 2145 (cl-notany (-rpartial #'string-match-p message) 2146 (lsp--client-ignore-messages client))) 2147 (lsp-log "%s" (lsp--propertize message type)))))) 2148 2149 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?)) 2150 "Display a message request to user sending the user selection back to server." 2151 (let* ((message (lsp--propertize message type)) 2152 (choices (seq-map #'lsp:message-action-item-title actions?))) 2153 (if choices 2154 (completing-read (concat message " ") (seq-into choices 'list) nil t) 2155 (lsp-log message)))) 2156 2157 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?)) 2158 "Show document URI in a buffer and go to SELECTION if any." 2159 (let ((path (lsp--uri-to-path uri))) 2160 (when (f-exists? path) 2161 (with-current-buffer (find-file path) 2162 (when selection? 2163 (goto-char (lsp--position-to-point (lsp:range-start selection?)))) 2164 t)))) 2165 2166 (defcustom lsp-progress-prefix "⌛ " 2167 "Progress prefix." 2168 :group 'lsp-mode 2169 :type 'string 2170 :package-version '(lsp-mode . "8.0.0")) 2171 2172 (defcustom lsp-progress-function #'lsp-on-progress-modeline 2173 "Function for handling the progress notifications." 2174 :group 'lsp-mode 2175 :type '(choice 2176 (const :tag "Use modeline" lsp-on-progress-modeline) 2177 (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')" 2178 lsp-on-progress-legacy) 2179 (const :tag "Ignore" ignore) 2180 (function :tag "Other function")) 2181 :package-version '(lsp-mode . "8.0.0")) 2182 2183 (defcustom lsp-request-while-no-input-may-block nil 2184 "Have `lsp-request-while-no-input` block unless `non-essential` is t." 2185 :group 'lsp-mode 2186 :type 'boolean) 2187 2188 (defun lsp--progress-status () 2189 "Returns the status of the progress for the current workspaces." 2190 (-let ((progress-status 2191 (s-join 2192 "|" 2193 (-keep 2194 (lambda (workspace) 2195 (let ((tokens (lsp--workspace-work-done-tokens workspace))) 2196 (unless (ht-empty? tokens) 2197 (mapconcat 2198 (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?)) 2199 (concat (if percentage? 2200 (if (numberp percentage?) 2201 (format "%.0f%%%% " percentage?) 2202 (format "%s%%%% " percentage?)) 2203 "") 2204 (or message? title))) 2205 (ht-values tokens) 2206 "|")))) 2207 (lsp-workspaces))))) 2208 (unless (s-blank? progress-status) 2209 (concat lsp-progress-prefix progress-status " ")))) 2210 2211 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value 2212 (value &as &WorkDoneProgress :kind))) 2213 "PARAMS contains the progress data. 2214 WORKSPACE is the workspace that contains the progress token." 2215 (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status)))) 2216 (pcase kind 2217 ("begin" (lsp-workspace-set-work-done-token token value workspace)) 2218 ("report" (lsp-workspace-set-work-done-token token value workspace)) 2219 ("end" (lsp-workspace-rem-work-done-token token workspace))) 2220 (force-mode-line-update)) 2221 2222 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value 2223 (value &as &WorkDoneProgress :kind))) 2224 "PARAMS contains the progress data. 2225 WORKSPACE is the workspace that contains the progress token." 2226 (pcase kind 2227 ("begin" 2228 (-let* (((&WorkDoneProgressBegin :title :percentage?) value) 2229 (reporter 2230 (if lsp-progress-via-spinner 2231 (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types)) 2232 ;; Set message as a tooltip for the spinner strings 2233 (propertized-strings 2234 (seq-map (lambda (string) (propertize string 'help-echo title)) 2235 spinner-strings)) 2236 (spinner-type (vconcat propertized-strings))) 2237 ;; The progress relates to the server as a whole, 2238 ;; display it on all buffers. 2239 (mapcar (lambda (buffer) 2240 (lsp-with-current-buffer buffer 2241 (spinner-start spinner-type)) 2242 buffer) 2243 (lsp--workspace-buffers workspace))) 2244 (if percentage? 2245 (make-progress-reporter title 0 100 percentage?) 2246 ;; No percentage, just progress 2247 (make-progress-reporter title nil nil))))) 2248 (lsp-workspace-set-work-done-token token reporter workspace))) 2249 ("report" 2250 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2251 (unless lsp-progress-via-spinner 2252 (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value))))) 2253 2254 ("end" 2255 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2256 (if lsp-progress-via-spinner 2257 (mapc (lambda (buffer) 2258 (when (lsp-buffer-live-p buffer) 2259 (lsp-with-current-buffer buffer 2260 (spinner-stop)))) 2261 reporter) 2262 (progress-reporter-done reporter)) 2263 (lsp-workspace-rem-work-done-token token workspace))))) 2264 2265 2266 ;; diagnostics 2267 2268 (defvar lsp-diagnostic-filter nil 2269 "A a function which will be called with 2270 `&PublishDiagnosticsParams' and `workspace' which can be used 2271 to filter out the diagnostics. The function should return 2272 `&PublishDiagnosticsParams'. 2273 2274 Common usecase are: 2275 1. Filter the diagnostics for a particular language server. 2276 2. Filter out the diagnostics under specific level.") 2277 2278 (defvar lsp-diagnostic-stats (ht)) 2279 2280 (defun lsp-diagnostics (&optional current-workspace?) 2281 "Return the diagnostics from all workspaces." 2282 (or (pcase (if current-workspace? 2283 (lsp-workspaces) 2284 (lsp--session-workspaces (lsp-session))) 2285 (`() ()) 2286 (`(,workspace) (lsp--workspace-diagnostics workspace)) 2287 (`,workspaces (let ((result (make-hash-table :test 'equal))) 2288 (mapc (lambda (workspace) 2289 (->> workspace 2290 (lsp--workspace-diagnostics) 2291 (maphash (lambda (file-name diagnostics) 2292 (puthash file-name 2293 (append (gethash file-name result) diagnostics) 2294 result))))) 2295 workspaces) 2296 result))) 2297 (ht))) 2298 2299 (defun lsp-diagnostics-stats-for (path) 2300 "Get diagnostics statistics for PATH. 2301 The result format is vector [_ errors warnings infos hints] or nil." 2302 (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats)) 2303 2304 (defun lsp-diagnostics--request-pull-diagnostics (workspace) 2305 "Request new diagnostics for the current file within WORKSPACE. 2306 This is only executed if the server supports pull diagnostics." 2307 (when (lsp-feature? "textDocument/diagnostic") 2308 (let ((path (lsp--fix-path-casing (buffer-file-name)))) 2309 (lsp-request-async "textDocument/diagnostic" 2310 (list :textDocument (lsp--text-document-identifier)) 2311 (-lambda ((&DocumentDiagnosticReport :kind :items?)) 2312 (lsp-diagnostics--apply-pull-diagnostics workspace path kind items?)) 2313 :mode 'tick)))) 2314 2315 (defun lsp-diagnostics--update-path (path new-stats) 2316 (let ((new-stats (copy-sequence new-stats)) 2317 (path (lsp--fix-path-casing (directory-file-name path)))) 2318 (if-let ((old-data (gethash path lsp-diagnostic-stats))) 2319 (dotimes (idx 5) 2320 (cl-callf + (aref old-data idx) 2321 (aref new-stats idx))) 2322 (puthash path new-stats lsp-diagnostic-stats)))) 2323 2324 (defun lsp-diagnostics--convert-and-update-path-stats (workspace path diagnostics) 2325 (let ((path (lsp--fix-path-casing path)) 2326 (new-stats (make-vector 5 0))) 2327 (mapc (-lambda ((&Diagnostic :severity?)) 2328 (cl-incf (aref new-stats (or severity? 1)))) 2329 diagnostics) 2330 (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace)))) 2331 (mapc (-lambda ((&Diagnostic :severity?)) 2332 (cl-decf (aref new-stats (or severity? 1)))) 2333 old-diags)) 2334 (lsp-diagnostics--update-path path new-stats) 2335 (while (not (string= path (setf path (file-name-directory 2336 (directory-file-name path))))) 2337 (lsp-diagnostics--update-path path new-stats)))) 2338 2339 (lsp-defun lsp--on-diagnostics-update-stats (workspace 2340 (&PublishDiagnosticsParams :uri :diagnostics)) 2341 (lsp-diagnostics--convert-and-update-path-stats workspace (lsp--uri-to-path uri) diagnostics)) 2342 2343 (defun lsp-diagnostics--apply-pull-diagnostics (workspace path kind diagnostics?) 2344 "Update WORKSPACE diagnostics at PATH with DIAGNOSTICS?. 2345 Depends on KIND being a \\='full\\=' update." 2346 (cond 2347 ((equal kind "full") 2348 ;; TODO support `lsp-diagnostic-filter' 2349 ;; (the params types differ from the published diagnostics response) 2350 (lsp-diagnostics--convert-and-update-path-stats workspace path diagnostics?) 2351 (-let* ((lsp--virtual-buffer-mappings (ht)) 2352 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2353 (if (seq-empty-p diagnostics?) 2354 (remhash path workspace-diagnostics) 2355 (puthash path (append diagnostics? nil) workspace-diagnostics)) 2356 (run-hooks 'lsp-diagnostics-updated-hook))) 2357 ((equal kind "unchanged") t) 2358 (t (lsp--error "Unknown pull diagnostic result kind '%s'" kind)))) 2359 2360 (defun lsp--on-diagnostics (workspace params) 2361 "Callback for textDocument/publishDiagnostics. 2362 interface PublishDiagnosticsParams { 2363 uri: string; 2364 diagnostics: Diagnostic[]; 2365 } 2366 PARAMS contains the diagnostics data. 2367 WORKSPACE is the workspace that contains the diagnostics." 2368 (when lsp-diagnostic-filter 2369 (setf params (funcall lsp-diagnostic-filter params workspace))) 2370 2371 (lsp--on-diagnostics-update-stats workspace params) 2372 2373 (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params) 2374 (lsp--virtual-buffer-mappings (ht)) 2375 (file (lsp--fix-path-casing (lsp--uri-to-path uri))) 2376 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2377 2378 (if (seq-empty-p diagnostics) 2379 (remhash file workspace-diagnostics) 2380 (puthash file (append diagnostics nil) workspace-diagnostics)) 2381 2382 (run-hooks 'lsp-diagnostics-updated-hook))) 2383 2384 (defun lsp-diagnostics--workspace-cleanup (workspace) 2385 (->> workspace 2386 (lsp--workspace-diagnostics) 2387 (maphash (lambda (key _) 2388 (lsp--on-diagnostics-update-stats 2389 workspace 2390 (lsp-make-publish-diagnostics-params 2391 :uri (lsp--path-to-uri key) 2392 :diagnostics []))))) 2393 (clrhash (lsp--workspace-diagnostics workspace))) 2394 2395 2396 2397 ;; textDocument/foldingRange support 2398 2399 (cl-defstruct lsp--folding-range beg end kind children) 2400 2401 (defvar-local lsp--cached-folding-ranges nil) 2402 (defvar-local lsp--cached-nested-folding-ranges nil) 2403 2404 (defun lsp--folding-range-width (range) 2405 (- (lsp--folding-range-end range) 2406 (lsp--folding-range-beg range))) 2407 2408 (defun lsp--get-folding-ranges () 2409 "Get the folding ranges for the current buffer." 2410 (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges)) 2411 (let* ((ranges (lsp-request "textDocument/foldingRange" 2412 `(:textDocument ,(lsp--text-document-identifier)))) 2413 (sorted-line-col-pairs (->> ranges 2414 (cl-mapcan (-lambda ((&FoldingRange :start-line 2415 :start-character? 2416 :end-line 2417 :end-character?)) 2418 (list (cons start-line start-character?) 2419 (cons end-line end-character?)))) 2420 (-sort #'lsp--line-col-comparator))) 2421 (line-col-to-point-map (lsp--convert-line-col-to-points-batch 2422 sorted-line-col-pairs))) 2423 (setq lsp--cached-folding-ranges 2424 (cons (buffer-chars-modified-tick) 2425 (--> ranges 2426 (seq-map (-lambda ((range &as 2427 &FoldingRange :start-line 2428 :start-character? 2429 :end-line 2430 :end-character? 2431 :kind?)) 2432 (make-lsp--folding-range 2433 :beg (ht-get line-col-to-point-map 2434 (cons start-line start-character?)) 2435 :end (ht-get line-col-to-point-map 2436 (cons end-line end-character?)) 2437 :kind kind?)) 2438 it) 2439 (seq-filter (lambda (folding-range) 2440 (< (lsp--folding-range-beg folding-range) 2441 (lsp--folding-range-end folding-range))) 2442 it) 2443 (seq-into it 'list) 2444 (delete-dups it)))))) 2445 (cdr lsp--cached-folding-ranges)) 2446 2447 (defun lsp--get-nested-folding-ranges () 2448 "Get a list of nested folding ranges for the current buffer." 2449 (-let [(tick . _) lsp--cached-folding-ranges] 2450 (if (and (eq tick (buffer-chars-modified-tick)) 2451 lsp--cached-nested-folding-ranges) 2452 lsp--cached-nested-folding-ranges 2453 (setq lsp--cached-nested-folding-ranges 2454 (lsp--folding-range-build-trees (lsp--get-folding-ranges)))))) 2455 2456 (defun lsp--folding-range-build-trees (ranges) 2457 (setq ranges (seq-sort #'lsp--range-before-p ranges)) 2458 (let* ((dummy-node (make-lsp--folding-range 2459 :beg most-negative-fixnum 2460 :end most-positive-fixnum)) 2461 (stack (list dummy-node))) 2462 (dolist (range ranges) 2463 (while (not (lsp--range-inside-p range (car stack))) 2464 (pop stack)) 2465 (push range (lsp--folding-range-children (car stack))) 2466 (push range stack)) 2467 (lsp--folding-range-children dummy-node))) 2468 2469 (defun lsp--range-inside-p (r1 r2) 2470 "Return non-nil if folding range R1 lies inside R2" 2471 (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2)) 2472 (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2)))) 2473 2474 (defun lsp--range-before-p (r1 r2) 2475 "Return non-nil if folding range R1 ends before R2" 2476 ;; Ensure r1 comes before r2 2477 (or (< (lsp--folding-range-beg r1) 2478 (lsp--folding-range-beg r2)) 2479 ;; If beg(r1) == beg(r2) make sure r2 ends first 2480 (and (= (lsp--folding-range-beg r1) 2481 (lsp--folding-range-beg r2)) 2482 (< (lsp--folding-range-end r2) 2483 (lsp--folding-range-end r1))))) 2484 2485 (defun lsp--point-inside-range-p (point range) 2486 "Return non-nil if POINT lies inside folding range RANGE." 2487 (and (>= point (lsp--folding-range-beg range)) 2488 (<= point (lsp--folding-range-end range)))) 2489 2490 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point))) 2491 "Return the innermost folding range POINT lies in." 2492 (seq-reduce (lambda (innermost-range curr-range) 2493 (if (and (lsp--point-inside-range-p point curr-range) 2494 (or (null innermost-range) 2495 (lsp--range-inside-p curr-range innermost-range))) 2496 curr-range 2497 innermost-range)) 2498 (lsp--get-folding-ranges) 2499 nil)) 2500 2501 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point))) 2502 "Return the outermost folding range POINT lies in." 2503 (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range) 2504 (let ((curr-width (lsp--folding-range-width curr-range))) 2505 (if (and (lsp--point-inside-range-p point curr-range) 2506 (or (null best-pair) 2507 (> curr-width outermost-width))) 2508 (cons curr-width curr-range) 2509 best-pair))) 2510 (lsp--get-folding-ranges) 2511 nil))) 2512 2513 (defun lsp--folding-range-at-point-bounds () 2514 (when (and lsp-enable-folding 2515 (lsp-feature? "textDocument/foldingRange")) 2516 (if-let ((range (lsp--get-current-innermost-folding-range))) 2517 (cons (lsp--folding-range-beg range) 2518 (lsp--folding-range-end range))))) 2519 (put 'lsp--folding-range 'bounds-of-thing-at-point 2520 #'lsp--folding-range-at-point-bounds) 2521 2522 (defun lsp--get-nearest-folding-range (&optional backward) 2523 (let ((point (point)) 2524 (found nil)) 2525 (while (not 2526 (or found 2527 (if backward 2528 (<= point (point-min)) 2529 (>= point (point-max))))) 2530 (if backward (cl-decf point) (cl-incf point)) 2531 (setq found (lsp--get-current-innermost-folding-range point))) 2532 found)) 2533 2534 (defun lsp--folding-range-at-point-forward-op (n) 2535 (when (and lsp-enable-folding 2536 (not (zerop n)) 2537 (lsp-feature? "textDocument/foldingRange")) 2538 (cl-block break 2539 (dotimes (_ (abs n)) 2540 (if-let ((range (lsp--get-nearest-folding-range (< n 0)))) 2541 (goto-char (if (< n 0) 2542 (lsp--folding-range-beg range) 2543 (lsp--folding-range-end range))) 2544 (cl-return-from break)))))) 2545 (put 'lsp--folding-range 'forward-op 2546 #'lsp--folding-range-at-point-forward-op) 2547 2548 (defun lsp--folding-range-at-point-beginning-op () 2549 (goto-char (car (lsp--folding-range-at-point-bounds)))) 2550 (put 'lsp--folding-range 'beginning-op 2551 #'lsp--folding-range-at-point-beginning-op) 2552 2553 (defun lsp--folding-range-at-point-end-op () 2554 (goto-char (cdr (lsp--folding-range-at-point-bounds)))) 2555 (put 'lsp--folding-range 'end-op 2556 #'lsp--folding-range-at-point-end-op) 2557 2558 (defun lsp--range-at-point-bounds () 2559 (or (lsp--folding-range-at-point-bounds) 2560 (when-let ((range (and 2561 (lsp-feature? "textDocument/hover") 2562 (->> (lsp--text-document-position-params) 2563 (lsp-request "textDocument/hover") 2564 (lsp:hover-range?))))) 2565 (lsp--range-to-region range)))) 2566 2567 ;; A more general purpose "thing", useful for applications like focus.el 2568 (put 'lsp--range 'bounds-of-thing-at-point 2569 #'lsp--range-at-point-bounds) 2570 2571 (defun lsp--log-io-p (method) 2572 "Return non nil if should log for METHOD." 2573 (and lsp-log-io 2574 (or (not lsp-log-io-allowlist-methods) 2575 (member method lsp-log-io-allowlist-methods)))) 2576 2577 2578 ;; toggles 2579 2580 (defun lsp-toggle-trace-io () 2581 "Toggle client-server protocol logging." 2582 (interactive) 2583 (setq lsp-log-io (not lsp-log-io)) 2584 (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled"))) 2585 2586 (defun lsp-toggle-signature-auto-activate () 2587 "Toggle signature auto activate." 2588 (interactive) 2589 (setq lsp-signature-auto-activate 2590 (unless lsp-signature-auto-activate '(:on-trigger-char))) 2591 (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled")) 2592 (lsp--update-signature-help-hook)) 2593 2594 (defun lsp-toggle-on-type-formatting () 2595 "Toggle on type formatting." 2596 (interactive) 2597 (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting)) 2598 (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled")) 2599 (lsp--update-on-type-formatting-hook)) 2600 2601 (defun lsp-toggle-symbol-highlight () 2602 "Toggle symbol highlighting." 2603 (interactive) 2604 (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting)) 2605 2606 (cond 2607 ((and lsp-enable-symbol-highlighting 2608 (lsp-feature? "textDocument/documentHighlight")) 2609 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t) 2610 (lsp--info "Symbol highlighting enabled in current buffer.")) 2611 ((not lsp-enable-symbol-highlighting) 2612 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 2613 (lsp--remove-overlays 'lsp-highlight) 2614 (lsp--info "Symbol highlighting disabled in current buffer.")))) 2615 2616 2617 ;; keybindings 2618 (defvar lsp--binding-descriptions nil 2619 "List of key binding/short description pair.") 2620 2621 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings) 2622 "In KEYMAP, define key sequence KEY as DEF conditionally. 2623 This is like `define-key', except the definition disappears 2624 whenever COND evaluates to nil. 2625 DESC is the short-description for the binding. 2626 BINDINGS is a list of (key def desc cond)." 2627 (declare (indent defun) 2628 (debug (form form form form form &rest sexp))) 2629 (->> (cl-list* key def desc cond bindings) 2630 (-partition 4) 2631 (-mapcat (-lambda ((key def desc cond)) 2632 `((define-key ,keymap ,key 2633 '(menu-item 2634 ,(format "maybe-%s" def) 2635 ,def 2636 :filter 2637 (lambda (item) 2638 (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer) 2639 lsp--describe-buffer) 2640 (current-buffer)) 2641 ,cond) 2642 item)))) 2643 (when (stringp ,key) 2644 (setq lsp--binding-descriptions 2645 (append lsp--binding-descriptions '(,key ,desc))))))) 2646 macroexp-progn)) 2647 2648 (defvar lsp--describe-buffer nil) 2649 2650 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus) 2651 (let ((lsp--describe-buffer buffer)) 2652 (funcall fn buffer prefix menus))) 2653 2654 (advice-add 'describe-buffer-bindings 2655 :around 2656 #'lsp-describe-buffer-bindings-advice) 2657 2658 (defun lsp--prepend-prefix (mappings) 2659 (->> mappings 2660 (-partition 2) 2661 (-mapcat (-lambda ((key description)) 2662 (list (concat lsp-keymap-prefix " " key) 2663 description))))) 2664 2665 (defvar lsp-command-map 2666 (-doto (make-sparse-keymap) 2667 (lsp-define-conditional-key 2668 ;; workspaces 2669 "wD" lsp-disconnect "disconnect" (lsp-workspaces) 2670 "wd" lsp-describe-session "describe session" t 2671 "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces) 2672 "wr" lsp-workspace-restart "restart server" (lsp-workspaces) 2673 "ws" lsp "start server" t 2674 2675 ;; formatting 2676 "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting") 2677 (lsp-feature? "textDocument/formatting")) 2678 "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting") 2679 2680 ;; folders 2681 "Fa" lsp-workspace-folders-add "add folder" t 2682 "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t 2683 "Fr" lsp-workspace-folders-remove "remove folder" t 2684 2685 ;; toggles 2686 "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature? 2687 "textDocument/publishDiagnostics") 2688 "TL" lsp-toggle-trace-io "toggle log io" t 2689 "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline) 2690 "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs) 2691 "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature? 2692 "textDocument/codeAction") 2693 "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature? 2694 "textDocument/documentSymbol") 2695 "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc) 2696 "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature? 2697 "textDocument/onTypeFormatting") 2698 "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight") 2699 "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens") 2700 "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp") 2701 2702 ;; goto 2703 "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol") 2704 "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration") 2705 "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list) 2706 "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition") 2707 "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls") 2708 (fboundp 'lsp-treemacs-call-hierarchy)) 2709 "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation") 2710 "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references") 2711 "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition") 2712 2713 ;; help 2714 "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc) 2715 (lsp-feature? "textDocument/hover")) 2716 "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover") 2717 "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp") 2718 2719 ;; refactoring 2720 "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction") 2721 "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename") 2722 2723 ;; actions 2724 "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction") 2725 "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight") 2726 "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy)) 2727 2728 ;; peeks 2729 "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition") 2730 (fboundp 'lsp-ui-peek-find-definitions)) 2731 "Gi" lsp-ui-peek-find-implementation "peek implementations" (and 2732 (fboundp 'lsp-ui-peek-find-implementation) 2733 (lsp-feature? "textDocument/implementation")) 2734 "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references) 2735 (lsp-feature? "textDocument/references")) 2736 "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp 2737 'lsp-ui-peek-find-workspace-symbol) 2738 (lsp-feature? "workspace/symbol"))))) 2739 2740 2741 ;; which-key integration 2742 2743 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key") 2744 (declare-function which-key-add-key-based-replacements "ext:which-key") 2745 2746 (defun lsp-enable-which-key-integration (&optional all-modes) 2747 "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current 2748 active `major-mode', or for all major modes when ALL-MODES is t." 2749 (cl-flet ((which-key-fn (if all-modes 2750 'which-key-add-key-based-replacements 2751 (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode)))) 2752 (apply 2753 #'which-key-fn 2754 (lsp--prepend-prefix 2755 (cl-list* 2756 "" "lsp" 2757 "w" "workspaces" 2758 "F" "folders" 2759 "=" "formatting" 2760 "T" "toggle" 2761 "g" "goto" 2762 "h" "help" 2763 "r" "refactor" 2764 "a" "code actions" 2765 "G" "peek" 2766 lsp--binding-descriptions))))) 2767 2768 2769 ;; Globbing syntax 2770 2771 ;; We port VSCode's glob-to-regexp code 2772 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts) 2773 ;; since the LSP globbing syntax seems to be the same as that of 2774 ;; VSCode. 2775 2776 (defconst lsp-globstar "**" 2777 "Globstar pattern.") 2778 2779 (defconst lsp-glob-split ?/ 2780 "The character by which we split path components in a glob 2781 pattern.") 2782 2783 (defconst lsp-path-regexp "[/\\\\]" 2784 "Forward or backslash to be used as a path separator in 2785 computed regexps.") 2786 2787 (defconst lsp-non-path-regexp "[^/\\\\]" 2788 "A regexp matching anything other than a slash.") 2789 2790 (defconst lsp-globstar-regexp 2791 (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?" 2792 lsp-path-regexp 2793 lsp-non-path-regexp lsp-path-regexp 2794 lsp-path-regexp lsp-non-path-regexp) 2795 "Globstar in regexp form.") 2796 2797 (defun lsp-split-glob-pattern (pattern split-char) 2798 "Split PATTERN at SPLIT-CHAR while respecting braces and brackets." 2799 (when pattern 2800 (let ((segments nil) 2801 (in-braces nil) 2802 (in-brackets nil) 2803 (current-segment "")) 2804 (dolist (char (string-to-list pattern)) 2805 (cl-block 'exit-point 2806 (if (eq char split-char) 2807 (when (and (null in-braces) 2808 (null in-brackets)) 2809 (push current-segment segments) 2810 (setq current-segment "") 2811 (cl-return-from 'exit-point)) 2812 (pcase char 2813 (?{ 2814 (setq in-braces t)) 2815 (?} 2816 (setq in-braces nil)) 2817 (?\[ 2818 (setq in-brackets t)) 2819 (?\] 2820 (setq in-brackets nil)))) 2821 (setq current-segment (concat current-segment 2822 (char-to-string char))))) 2823 (unless (string-empty-p current-segment) 2824 (push current-segment segments)) 2825 (nreverse segments)))) 2826 2827 (defun lsp--glob-to-regexp (pattern) 2828 "Helper function to convert a PATTERN from LSP's glob syntax to 2829 an Elisp regexp." 2830 (if (string-empty-p pattern) 2831 "" 2832 (let ((current-regexp "") 2833 (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split))) 2834 (if (-all? (lambda (segment) (eq segment lsp-globstar)) 2835 glob-segments) 2836 ".*" 2837 (let ((prev-segment-was-globstar nil)) 2838 (seq-do-indexed 2839 (lambda (segment index) 2840 (if (string-equal segment lsp-globstar) 2841 (unless prev-segment-was-globstar 2842 (setq current-regexp (concat current-regexp 2843 lsp-globstar-regexp)) 2844 (setq prev-segment-was-globstar t)) 2845 (let ((in-braces nil) 2846 (brace-val "") 2847 (in-brackets nil) 2848 (bracket-val "")) 2849 (dolist (char (string-to-list segment)) 2850 (cond 2851 ((and (not (char-equal char ?\})) 2852 in-braces) 2853 (setq brace-val (concat brace-val 2854 (char-to-string char)))) 2855 ((and in-brackets 2856 (or (not (char-equal char ?\])) 2857 (string-empty-p bracket-val))) 2858 (let ((curr (cond 2859 ((char-equal char ?-) 2860 "-") 2861 ;; NOTE: ?\^ and ?^ are different characters 2862 ((and (memq char '(?^ ?!)) 2863 (string-empty-p bracket-val)) 2864 "^") 2865 ((char-equal char lsp-glob-split) 2866 "") 2867 (t 2868 (regexp-quote (char-to-string char)))))) 2869 (setq bracket-val (concat bracket-val curr)))) 2870 (t 2871 (cl-case char 2872 (?{ 2873 (setq in-braces t)) 2874 (?\[ 2875 (setq in-brackets t)) 2876 (?} 2877 (let* ((choices (lsp-split-glob-pattern brace-val ?\,)) 2878 (brace-regexp (concat "\\(?:" 2879 (mapconcat #'lsp--glob-to-regexp choices "\\|") 2880 "\\)"))) 2881 (setq current-regexp (concat current-regexp 2882 brace-regexp)) 2883 (setq in-braces nil) 2884 (setq brace-val ""))) 2885 (?\] 2886 (setq current-regexp 2887 (concat current-regexp 2888 "[" bracket-val "]")) 2889 (setq in-brackets nil) 2890 (setq bracket-val "")) 2891 (?? 2892 (setq current-regexp 2893 (concat current-regexp 2894 lsp-non-path-regexp))) 2895 (?* 2896 (setq current-regexp 2897 (concat current-regexp 2898 lsp-non-path-regexp "*?"))) 2899 (t 2900 (setq current-regexp 2901 (concat current-regexp 2902 (regexp-quote (char-to-string char))))))))) 2903 (when (and (< index (1- (length glob-segments))) 2904 (or (not (string-equal (nth (1+ index) glob-segments) 2905 lsp-globstar)) 2906 (< (+ index 2) 2907 (length glob-segments)))) 2908 (setq current-regexp 2909 (concat current-regexp 2910 lsp-path-regexp))) 2911 (setq prev-segment-was-globstar nil)))) 2912 glob-segments) 2913 current-regexp))))) 2914 2915 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365 2916 (defun lsp-glob-unbrace-at-top-level (glob-pattern) 2917 "If GLOB-PATTERN does not start with a brace, return a singleton list 2918 containing GLOB-PATTERN. 2919 2920 If GLOB-PATTERN does start with a brace, return a list of the 2921 comma-separated globs within the top-level braces." 2922 (if (not (string-prefix-p "{" glob-pattern)) 2923 (list glob-pattern) 2924 (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,))) 2925 2926 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern) 2927 "Convert GLOB-PATTERN to a regexp wrapped with the beginning- 2928 and end-of-string meta-characters." 2929 (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'")) 2930 2931 (defun lsp-glob-to-regexps (glob-pattern) 2932 "Convert a GLOB-PATTERN to a list of Elisp regexps." 2933 (when-let* 2934 ((glob-pattern (cond ((hash-table-p glob-pattern) 2935 (ht-get glob-pattern "pattern")) 2936 ((stringp glob-pattern) glob-pattern) 2937 (t (error "Unknown glob-pattern type: %s" glob-pattern)))) 2938 (trimmed-pattern (string-trim glob-pattern)) 2939 (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern))) 2940 (seq-map #'lsp-glob-convert-to-wrapped-regexp 2941 top-level-unbraced-patterns))) 2942 2943 2944 2945 (defvar lsp-mode-menu) 2946 2947 (defun lsp-mouse-click (event) 2948 (interactive "e") 2949 (let* ((ec (event-start event)) 2950 (choice (x-popup-menu event lsp-mode-menu)) 2951 (action (lookup-key lsp-mode-menu (apply 'vector choice)))) 2952 2953 (select-window (posn-window ec)) 2954 2955 (unless (and (region-active-p) (eq action 'lsp-execute-code-action)) 2956 (goto-char (posn-point ec))) 2957 (run-with-idle-timer 2958 0.001 nil 2959 (lambda () 2960 (cl-labels ((check (value) (not (null value)))) 2961 (when choice 2962 (call-interactively action))))))) 2963 2964 (defvar lsp-mode-map 2965 (let ((map (make-sparse-keymap))) 2966 (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse) 2967 (define-key map (kbd "C-<mouse-1>") #'ignore) 2968 (define-key map (kbd "<mouse-3>") #'lsp-mouse-click) 2969 (define-key map (kbd "C-S-SPC") #'lsp-signature-activate) 2970 (when lsp-keymap-prefix 2971 (define-key map (kbd lsp-keymap-prefix) lsp-command-map)) 2972 map) 2973 "Keymap for `lsp-mode'.") 2974 2975 (define-minor-mode lsp-mode "Mode for LSP interaction." 2976 :keymap lsp-mode-map 2977 :lighter 2978 (" LSP[" 2979 (lsp--buffer-workspaces 2980 (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "][")) 2981 (:propertize "Disconnected" face warning)) 2982 "]") 2983 :group 'lsp-mode 2984 (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred)) 2985 ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp' 2986 (lsp))) 2987 2988 (defvar lsp-mode-menu 2989 (easy-menu-create-menu 2990 nil 2991 `(["Go to definition" lsp-find-definition 2992 :active (lsp-feature? "textDocument/definition")] 2993 ["Find references" lsp-find-references 2994 :active (lsp-feature? "textDocument/references")] 2995 ["Find implementations" lsp-find-implementation 2996 :active (lsp-feature? "textDocument/implementation")] 2997 ["Find declarations" lsp-find-declaration 2998 :active (lsp-feature? "textDocument/declaration")] 2999 ["Go to type declaration" lsp-find-type-definition 3000 :active (lsp-feature? "textDocument/typeDefinition")] 3001 "--" 3002 ["Describe" lsp-describe-thing-at-point] 3003 ["Code action" lsp-execute-code-action] 3004 ["Format" lsp-format-buffer] 3005 ["Highlight references" lsp-document-highlight] 3006 ["Type Hierarchy" lsp-java-type-hierarchy 3007 :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")] 3008 ["Type Hierarchy" lsp-treemacs-type-hierarchy 3009 :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")) 3010 (functionp 'lsp-treemacs-type-hierarchy) 3011 (lsp-feature? "textDocument/typeHierarchy"))] 3012 ["Call Hierarchy" lsp-treemacs-call-hierarchy 3013 :visible (and (functionp 'lsp-treemacs-call-hierarchy) 3014 (lsp-feature? "textDocument/callHierarchy"))] 3015 ["Rename" lsp-rename 3016 :active (lsp-feature? "textDocument/rename")] 3017 "--" 3018 ("Session" 3019 ["View logs" lsp-workspace-show-log] 3020 ["Describe" lsp-describe-session] 3021 ["Shutdown" lsp-shutdown-workspace] 3022 ["Restart" lsp-restart-workspace]) 3023 ("Workspace Folders" 3024 ["Add" lsp-workspace-folders-add] 3025 ["Remove" lsp-workspace-folders-remove] 3026 ["Open" lsp-workspace-folders-open]) 3027 ("Toggle features" 3028 ["Lenses" lsp-lens-mode] 3029 ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode] 3030 ["Modeline code actions" lsp-modeline-code-actions-mode] 3031 ["Modeline diagnostics" lsp-modeline-diagnostics-mode]) 3032 "---" 3033 ("Debug" 3034 :active (bound-and-true-p dap-ui-mode) 3035 :filter ,(lambda (_) 3036 (and (boundp 'dap-ui-menu-items) 3037 (nthcdr 3 dap-ui-menu-items)))))) 3038 "Menu for lsp-mode.") 3039 3040 (defalias 'make-lsp-client 'make-lsp--client) 3041 3042 (cl-defstruct lsp--registered-capability 3043 (id "") 3044 (method " ") 3045 (options nil)) 3046 3047 ;; A ‘lsp--workspace’ object represents exactly one language server process. 3048 (cl-defstruct lsp--workspace 3049 ;; the `ewoc' object for displaying I/O to and from the server 3050 (ewoc nil) 3051 3052 ;; ‘server-capabilities’ is a hash table of the language server capabilities. 3053 ;; It is the hash table representation of a LSP ServerCapabilities structure; 3054 ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize. 3055 (server-capabilities nil) 3056 3057 ;; ‘registered-server-capabilities’ is a list of hash tables that represent 3058 ;; dynamically-registered Registration objects. See 3059 ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability. 3060 (registered-server-capabilities nil) 3061 3062 ;; ‘root’ is a directory name or a directory file name for the workspace 3063 ;; root. ‘lsp-mode’ passes this directory to the ‘initialize’ method of the 3064 ;; language server; see 3065 ;; https://microsoft.github.io/language-server-protocol/specification#initialize. 3066 (root nil) 3067 3068 ;; ‘client’ is the ‘lsp--client’ object associated with this workspace. 3069 (client nil) 3070 3071 ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It 3072 ;; used to derive the file path in `lsp--uri-to-path' when using tramp 3073 ;; connection. 3074 (host-root nil) 3075 3076 ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or 3077 ;; a network connection. ‘lsp-mode’ communicates with ‘proc’ using the 3078 ;; language server protocol. ‘proc’ corresponds to the COMMUNICATION-PROCESS 3079 ;; element of the return value of the client’s ‘get-root’ field, which see. 3080 (proc nil) 3081 3082 ;; ‘proc’ is a process object; it must represent a regular process, not a 3083 ;; pipe or network process. It represents the actual server process that 3084 ;; corresponds to this workspace. ‘cmd-proc’ corresponds to the 3085 ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’ 3086 ;; field, which see. 3087 (cmd-proc nil) 3088 3089 ;; ‘buffers’ is a list of buffers associated with this workspace. 3090 (buffers nil) 3091 3092 ;; if semantic tokens is enabled, `semantic-tokens-faces' contains 3093 ;; one face (or nil) for each token type supported by the language server. 3094 (semantic-tokens-faces nil) 3095 3096 ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces' 3097 ;; contains one face (or nil) for each modifier type supported by the language 3098 ;; server 3099 (semantic-tokens-modifier-faces nil) 3100 3101 ;; Extra client capabilities provided by third-party packages using 3102 ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME 3103 ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name, 3104 ;; and CAPS is either a plist of the client capabilities, or a function that 3105 ;; takes no argument and returns a plist of the client capabilities or nil. 3106 (extra-client-capabilities nil) 3107 3108 ;; Workspace status 3109 (status nil) 3110 3111 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3112 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3113 (metadata (make-hash-table :test 'equal)) 3114 3115 ;; contains all the file notification watches that have been created for the 3116 ;; current workspace in format filePath->file notification handle. 3117 (watches (make-hash-table :test 'equal)) 3118 3119 ;; list of workspace folders 3120 (workspace-folders nil) 3121 3122 ;; ‘last-id’ the last request id for the current workspace. 3123 (last-id 0) 3124 3125 ;; ‘status-string’ allows extensions to specify custom status string based on 3126 ;; the Language Server specific messages. 3127 (status-string nil) 3128 3129 ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it 3130 ;; was stopped). 3131 shutdown-action 3132 3133 ;; ‘diagnostics’ a hashmap with workspace diagnostics. 3134 (diagnostics (make-hash-table :test 'equal)) 3135 3136 ;; contains all the workDone progress tokens that have been created 3137 ;; for the current workspace. 3138 (work-done-tokens (make-hash-table :test 'equal))) 3139 3140 3141 (cl-defstruct lsp-session 3142 ;; contains the folders that are part of the current session 3143 folders 3144 ;; contains the folders that must not be imported in the current workspace. 3145 folders-blocklist 3146 ;; contains the list of folders that must be imported in a project in case of 3147 ;; multi root LSP server. 3148 (server-id->folders (make-hash-table :test 'equal)) 3149 ;; folder to list of the servers that are associated with the folder. 3150 (folder->servers (make-hash-table :test 'equal)) 3151 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3152 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3153 (metadata (make-hash-table :test 'equal))) 3154 3155 (defun lsp-workspace-status (status-string &optional workspace) 3156 "Set current workspace status to STATUS-STRING. 3157 If WORKSPACE is not specified defaults to lsp--cur-workspace." 3158 (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string)))) 3159 (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string))) 3160 3161 (defun lsp-session-set-metadata (key value &optional _workspace) 3162 "Associate KEY with VALUE in the WORKSPACE metadata. 3163 If WORKSPACE is not provided current workspace will be used." 3164 (puthash key value (lsp-session-metadata (lsp-session)))) 3165 3166 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata) 3167 3168 (defun lsp-session-get-metadata (key &optional _workspace) 3169 "Lookup KEY in WORKSPACE metadata. 3170 If WORKSPACE is not provided current workspace will be used." 3171 (gethash key (lsp-session-metadata (lsp-session)))) 3172 3173 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata) 3174 3175 (defun lsp-workspace-set-work-done-token (token value workspace) 3176 "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens." 3177 (puthash token value (lsp--workspace-work-done-tokens workspace))) 3178 3179 (defun lsp-workspace-get-work-done-token (token workspace) 3180 "Lookup TOKEN in the WORKSPACE work-done-tokens." 3181 (gethash token (lsp--workspace-work-done-tokens workspace))) 3182 3183 (defun lsp-workspace-rem-work-done-token (token workspace) 3184 "Remove TOKEN from the WORKSPACE work-done-tokens." 3185 (remhash token (lsp--workspace-work-done-tokens workspace))) 3186 3187 3188 (defun lsp--make-notification (method &optional params) 3189 "Create notification body for method METHOD and parameters PARAMS." 3190 (list :jsonrpc "2.0" :method method :params params)) 3191 3192 (defalias 'lsp--make-request 'lsp--make-notification) 3193 (defalias 'lsp-make-request 'lsp--make-notification) 3194 3195 (defun lsp--make-response (id result) 3196 "Create response for REQUEST with RESULT." 3197 `(:jsonrpc "2.0" :id ,id :result ,result)) 3198 3199 (defun lsp-make-notification (method &optional params) 3200 "Create notification body for method METHOD and parameters PARAMS." 3201 (lsp--make-notification method params)) 3202 3203 (defmacro lsp--json-serialize (params) 3204 (if (progn 3205 (require 'json) 3206 (fboundp 'json-serialize)) 3207 `(json-serialize ,params 3208 :null-object nil 3209 :false-object :json-false) 3210 `(let ((json-false :json-false)) 3211 (json-encode ,params)))) 3212 3213 (defun lsp--make-message (params) 3214 "Create a LSP message from PARAMS, after encoding it to a JSON string." 3215 (let ((body (lsp--json-serialize params))) 3216 (concat "Content-Length: " 3217 (number-to-string (1+ (string-bytes body))) 3218 "\r\n\r\n" 3219 body 3220 "\n"))) 3221 3222 (cl-defstruct lsp--log-entry timestamp process-time type method id body) 3223 3224 (defun lsp--make-log-entry (method id body type &optional process-time) 3225 "Create an outgoing log object from BODY with method METHOD and id ID. 3226 If ID is non-nil, then the body is assumed to be a notification. 3227 TYPE can either be `incoming' or `outgoing'" 3228 (cl-assert (memq type '(incoming-req outgoing-req incoming-notif 3229 outgoing-notif incoming-resp 3230 outgoing-resp))) 3231 (make-lsp--log-entry 3232 :timestamp (format-time-string "%I:%M:%S %p") 3233 :process-time process-time 3234 :method method 3235 :id id 3236 :type type 3237 :body body)) 3238 3239 (defun lsp--log-font-lock-json (body) 3240 "Font lock JSON BODY." 3241 (with-temp-buffer 3242 (insert body) 3243 ;; We set the temp buffer file-name extension to .json and call `set-auto-mode' 3244 ;; so the users configured json mode is used which could be 3245 ;; `json-mode', `json-ts-mode', `jsonian-mode', etc. 3246 (let ((buffer-file-name "lsp-log.json")) 3247 (delay-mode-hooks 3248 (set-auto-mode) 3249 (if (fboundp 'font-lock-ensure) 3250 (font-lock-ensure) 3251 (with-no-warnings 3252 (font-lock-fontify-buffer))))) 3253 (buffer-string))) 3254 3255 (defun lsp--log-entry-pp (entry) 3256 (cl-assert (lsp--log-entry-p entry)) 3257 (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time 3258 body) 3259 entry) 3260 (json-false :json-false) 3261 (json-encoding-pretty-print t) 3262 (str nil)) 3263 (setq str 3264 (concat (format "[Trace - %s] " timestamp) 3265 (pcase type 3266 ('incoming-req (format "Received request '%s - (%s)." method id)) 3267 ('outgoing-req (format "Sending request '%s - (%s)'." method id)) 3268 3269 ('incoming-notif (format "Received notification '%s'." method)) 3270 ('outgoing-notif (format "Sending notification '%s'." method)) 3271 3272 ('incoming-resp (format "Received response '%s - (%s)' in %dms." 3273 method id process-time)) 3274 ('outgoing-resp 3275 (format 3276 "Sending response '%s - (%s)'. Processing request took %dms" 3277 method id process-time))) 3278 "\n" 3279 (if (memq type '(incoming-resp ougoing-resp)) 3280 "Result: " 3281 "Params: ") 3282 (lsp--log-font-lock-json (json-encode body)) 3283 "\n\n\n")) 3284 (setq str (propertize str 'mouse-face 'highlight 'read-only t)) 3285 (insert str))) 3286 3287 (defvar-local lsp--log-io-ewoc nil) 3288 3289 (defun lsp--get-create-io-ewoc (workspace) 3290 (if (and (lsp--workspace-ewoc workspace) 3291 (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace)))) 3292 (lsp--workspace-ewoc workspace) 3293 (with-current-buffer (lsp--get-log-buffer-create workspace) 3294 (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode)) 3295 (setq-local window-point-insertion-type t) 3296 (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t)) 3297 (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc)) 3298 (lsp--workspace-ewoc workspace))) 3299 3300 (defun lsp--ewoc-count (ewoc) 3301 (let* ((count 0) 3302 (count-fn (lambda (_) (setq count (1+ count))))) 3303 (ewoc-map count-fn ewoc) 3304 count)) 3305 3306 (defun lsp--log-entry-new (entry workspace) 3307 (let* ((ewoc (lsp--get-create-io-ewoc workspace)) 3308 (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc))) 3309 (node (if (or (eq lsp-io-messages-max t) 3310 (>= lsp-io-messages-max count)) 3311 nil 3312 (ewoc-nth ewoc (1- lsp-io-messages-max)))) 3313 (prev nil) 3314 (inhibit-read-only t)) 3315 (while node 3316 (setq prev (ewoc-prev ewoc node)) 3317 (ewoc-delete ewoc node) 3318 (setq node prev)) 3319 (ewoc-enter-last ewoc entry))) 3320 3321 (defun lsp--send-notification (body) 3322 "Send BODY as a notification to the language server." 3323 (lsp-foreach-workspace 3324 (when (lsp--log-io-p (plist-get body :method)) 3325 (lsp--log-entry-new (lsp--make-log-entry 3326 (plist-get body :method) 3327 nil (plist-get body :params) 'outgoing-notif) 3328 lsp--cur-workspace)) 3329 (lsp--send-no-wait body 3330 (lsp--workspace-proc lsp--cur-workspace)))) 3331 3332 (defalias 'lsp-send-notification 'lsp--send-notification) 3333 3334 (defun lsp-notify (method params) 3335 "Send notification METHOD with PARAMS." 3336 (lsp--send-notification (lsp--make-notification method params))) 3337 3338 (defun lsp--cur-workspace-check () 3339 "Check whether buffer lsp workspace(s) are set." 3340 (cl-assert (lsp-workspaces) nil 3341 "No language server(s) is associated with this buffer.")) 3342 3343 (defun lsp--send-request (body &optional no-wait no-merge) 3344 "Send BODY as a request to the language server, get the response. 3345 If NO-WAIT is non-nil, don't synchronously wait for a response. 3346 If NO-MERGE is non-nil, don't merge the results but return an 3347 alist mapping workspace->result." 3348 (lsp-request (plist-get body :method) 3349 (plist-get body :params) 3350 :no-wait no-wait 3351 :no-merge no-merge)) 3352 3353 (defalias 'lsp-send-request 'lsp--send-request 3354 "Send BODY as a request to the language server and return the response 3355 synchronously. 3356 \n(fn BODY)") 3357 3358 (cl-defun lsp-request (method params &key no-wait no-merge) 3359 "Send request METHOD with PARAMS. 3360 If NO-MERGE is non-nil, don't merge the results but return alist 3361 workspace->result. 3362 If NO-WAIT is non-nil send the request as notification." 3363 (if no-wait 3364 (lsp-notify method params) 3365 (let* ((send-time (float-time)) 3366 ;; max time by which we must get a response 3367 (expected-time 3368 (and 3369 lsp-response-timeout 3370 (+ send-time lsp-response-timeout))) 3371 resp-result resp-error done?) 3372 (unwind-protect 3373 (progn 3374 (lsp-request-async method params 3375 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3376 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3377 :no-merge no-merge 3378 :mode 'detached 3379 :cancel-token :sync-request) 3380 (while (not (or resp-error resp-result)) 3381 (if (functionp 'json-rpc-connection) 3382 (catch 'lsp-done (sit-for 0.01)) 3383 (catch 'lsp-done 3384 (accept-process-output 3385 nil 3386 (if expected-time (- expected-time send-time) 1)))) 3387 (setq send-time (float-time)) 3388 (when (and expected-time (< expected-time send-time)) 3389 (error "Timeout while waiting for response. Method: %s" method))) 3390 (setq done? t) 3391 (cond 3392 ((eq resp-result :finished) nil) 3393 (resp-result resp-result) 3394 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3395 ((lsp-json-error? (cl-first resp-error)) 3396 (error (lsp:json-error-message (cl-first resp-error)))))) 3397 (unless done? 3398 (lsp-cancel-request-by-token :sync-request)))))) 3399 3400 (cl-defun lsp-request-while-no-input (method params) 3401 "Send request METHOD with PARAMS and waits until there is no input. 3402 Return same value as `lsp--while-no-input' and respecting `non-essential'." 3403 (if (or non-essential (not lsp-request-while-no-input-may-block)) 3404 (let* ((send-time (float-time)) 3405 ;; max time by which we must get a response 3406 (expected-time 3407 (and 3408 lsp-response-timeout 3409 (+ send-time lsp-response-timeout))) 3410 resp-result resp-error done?) 3411 (unwind-protect 3412 (progn 3413 (lsp-request-async method params 3414 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3415 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3416 :mode 'detached 3417 :cancel-token :sync-request) 3418 (while (not (or resp-error resp-result (input-pending-p))) 3419 (catch 'lsp-done 3420 (sit-for 3421 (if expected-time (- expected-time send-time) 1))) 3422 (setq send-time (float-time)) 3423 (when (and expected-time (< expected-time send-time)) 3424 (error "Timeout while waiting for response. Method: %s" method))) 3425 (setq done? (or resp-error resp-result)) 3426 (cond 3427 ((eq resp-result :finished) nil) 3428 (resp-result resp-result) 3429 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3430 ((lsp-json-error? (cl-first resp-error)) 3431 (error (lsp:json-error-message (cl-first resp-error)))))) 3432 (unless done? 3433 (lsp-cancel-request-by-token :sync-request)) 3434 (when (and (input-pending-p) lsp--throw-on-input) 3435 (throw 'input :interrupted)))) 3436 (lsp-request method params))) 3437 3438 (defvar lsp--cancelable-requests (ht)) 3439 3440 (cl-defun lsp-request-async (method params callback 3441 &key mode error-handler cancel-handler no-merge cancel-token) 3442 "Send METHOD with PARAMS as a request to the language server. 3443 Call CALLBACK with the response received from the server 3444 asynchronously. 3445 MODE determines when the callback will be called depending on the 3446 condition of the original buffer. It could be: 3447 - `detached' which means that the callback will be executed no 3448 matter what has happened to the buffer. 3449 - `alive' - the callback will be executed only if the buffer from 3450 which the call was executed is still alive. 3451 - `current' the callback will be executed only if the original buffer 3452 is still selected. 3453 - `tick' - the callback will be executed only if the buffer was not modified. 3454 - `unchanged' - the callback will be executed only if the buffer hasn't 3455 changed and if the buffer is not modified. 3456 3457 ERROR-HANDLER will be called in case the request has failed. 3458 CANCEL-HANDLER will be called in case the request is being canceled. 3459 If NO-MERGE is non-nil, don't merge the results but return alist 3460 workspace->result. 3461 CANCEL-TOKEN is the token that can be used to cancel request." 3462 (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params) 3463 callback mode error-handler cancel-handler no-merge cancel-token)) 3464 3465 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback) 3466 (lambda (&rest _) 3467 (unless (and (equal 'post-command-hook hook) 3468 (equal (current-buffer) buf)) 3469 (lsp--request-cleanup-hooks id) 3470 (with-lsp-workspaces workspaces 3471 (lsp--cancel-request id) 3472 (when cancel-callback (funcall cancel-callback))) 3473 (lsp-log "Cancelling %s(%s) in hook %s" method id hook)))) 3474 3475 (defun lsp--create-async-callback 3476 (callback method no-merge workspaces) 3477 "Create async handler expecting COUNT results, merge them and call CALLBACK. 3478 MODE determines when the callback will be called depending on the 3479 condition of the original buffer. METHOD is the invoked method. 3480 If NO-MERGE is non-nil, don't merge the results but return alist 3481 workspace->result. ID is the request id." 3482 (let (results errors) 3483 (lambda (result) 3484 (push (cons lsp--cur-workspace result) 3485 (if (eq result :error) errors results)) 3486 (when (and (not (eq (length errors) (length workspaces))) 3487 (eq (+ (length errors) (length results)) (length workspaces))) 3488 (funcall callback 3489 (if no-merge 3490 results 3491 (lsp--merge-results (-map #'cl-rest results) method))))))) 3492 3493 (defcustom lsp-default-create-error-handler-fn nil 3494 "Default error handler customization. 3495 Handler should give METHOD as argument and return function of one argument 3496 ERROR." 3497 :type 'function 3498 :group 'lsp-mode 3499 :package-version '(lsp-mode . "9.0.0")) 3500 3501 (defun lsp--create-default-error-handler (method) 3502 "Default error handler. 3503 METHOD is the executed method." 3504 (if lsp-default-create-error-handler-fn 3505 (funcall lsp-default-create-error-handler-fn method) 3506 (lambda (error) 3507 (lsp--warn "%s" (or (lsp--error-string error) 3508 (format "%s Request has failed" method)))))) 3509 3510 (defvar lsp--request-cleanup-hooks (ht)) 3511 3512 (defun lsp--request-cleanup-hooks (request-id) 3513 (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks))) 3514 (funcall cleanup-function) 3515 (remhash request-id lsp--request-cleanup-hooks))) 3516 3517 (defun lsp-cancel-request-by-token (cancel-token) 3518 "Cancel request using CANCEL-TOKEN." 3519 (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests)) 3520 (with-lsp-workspaces workspaces 3521 (lsp--cancel-request request-id)) 3522 (remhash cancel-token lsp--cancelable-requests) 3523 (lsp--request-cleanup-hooks request-id))) 3524 3525 (defun lsp--send-request-async (body callback 3526 &optional mode error-callback cancel-callback 3527 no-merge cancel-token) 3528 "Send BODY as a request to the language server. 3529 Call CALLBACK with the response received from the server 3530 asynchronously. 3531 MODE determines when the callback will be called depending on the 3532 condition of the original buffer. It could be: 3533 - `detached' which means that the callback will be executed no 3534 matter what has happened to the buffer. 3535 - `alive' - the callback will be executed only if the buffer from 3536 which the call was executed is still alive. 3537 - `current' the callback will be executed only if the original buffer 3538 is still selected. 3539 - `tick' - the callback will be executed only if the buffer was not modified. 3540 - `unchanged' - the callback will be executed only if the buffer hasn't 3541 changed and if the buffer is not modified. 3542 3543 ERROR-CALLBACK will be called in case the request has failed. 3544 CANCEL-CALLBACK will be called in case the request is being canceled. 3545 If NO-MERGE is non-nil, don't merge the results but return alist 3546 workspace->result. 3547 CANCEL-TOKEN is the token that can be used to cancel request." 3548 (when cancel-token 3549 (lsp-cancel-request-by-token cancel-token)) 3550 3551 (if-let ((target-workspaces (lsp--find-workspaces-for body))) 3552 (let* ((start-time (current-time)) 3553 (method (plist-get body :method)) 3554 (id (cl-incf lsp-last-id)) 3555 (buf (current-buffer)) 3556 (cancel-callback (when cancel-callback 3557 (pcase mode 3558 ((or 'alive 'tick 'unchanged) 3559 (lambda () 3560 (with-current-buffer buf 3561 (funcall cancel-callback)))) 3562 (_ cancel-callback)))) 3563 ;; calculate what are the (hook . local) pairs which will cancel 3564 ;; the request 3565 (hooks (pcase mode 3566 ('alive '((kill-buffer-hook . t))) 3567 ('tick '((kill-buffer-hook . t) (after-change-functions . t))) 3568 ('unchanged '((after-change-functions . t) (post-command-hook . nil))) 3569 ('current '((post-command-hook . nil))))) 3570 ;; note: lambdas in emacs can be compared but we should make sure 3571 ;; that all of the captured arguments are the same - in our case 3572 ;; `lsp--create-request-cancel' will return the same lambda when 3573 ;; called with the same params. 3574 (cleanup-hooks 3575 (lambda () (mapc 3576 (-lambda ((hook . local)) 3577 (if local 3578 (when (buffer-live-p buf) 3579 (with-current-buffer buf 3580 (remove-hook hook 3581 (lsp--create-request-cancel 3582 id target-workspaces hook buf method cancel-callback) 3583 t))) 3584 (remove-hook hook (lsp--create-request-cancel 3585 id target-workspaces hook buf method cancel-callback)))) 3586 hooks) 3587 (remhash cancel-token lsp--cancelable-requests))) 3588 (callback (pcase mode 3589 ((or 'alive 'tick 'unchanged) (lambda (&rest args) 3590 (with-current-buffer buf 3591 (apply callback args)))) 3592 (_ callback))) 3593 (callback (lsp--create-async-callback callback 3594 method 3595 no-merge 3596 target-workspaces)) 3597 (callback (lambda (result) 3598 (lsp--request-cleanup-hooks id) 3599 (funcall callback result))) 3600 (error-callback (lsp--create-async-callback 3601 (or error-callback 3602 (lsp--create-default-error-handler method)) 3603 method 3604 nil 3605 target-workspaces)) 3606 (error-callback (lambda (error) 3607 (funcall callback :error) 3608 (lsp--request-cleanup-hooks id) 3609 (funcall error-callback error))) 3610 (body (plist-put body :id id))) 3611 3612 ;; cancel request in any of the hooks 3613 (mapc (-lambda ((hook . local)) 3614 (add-hook hook 3615 (lsp--create-request-cancel 3616 id target-workspaces hook buf method cancel-callback) 3617 nil local)) 3618 hooks) 3619 (puthash id cleanup-hooks lsp--request-cleanup-hooks) 3620 3621 (setq lsp--last-active-workspaces target-workspaces) 3622 3623 (when cancel-token 3624 (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests)) 3625 3626 (seq-doseq (workspace target-workspaces) 3627 (when (lsp--log-io-p method) 3628 (lsp--log-entry-new (lsp--make-log-entry method id 3629 (plist-get body :params) 3630 'outgoing-req) 3631 workspace)) 3632 (puthash id 3633 (list callback error-callback method start-time (current-time)) 3634 (-> workspace 3635 (lsp--workspace-client) 3636 (lsp--client-response-handlers))) 3637 (lsp--send-no-wait body (lsp--workspace-proc workspace))) 3638 body) 3639 (error "The connected server(s) does not support method %s. 3640 To find out what capabilities support your server use `M-x lsp-describe-session' 3641 and expand the capabilities section" 3642 (plist-get body :method)))) 3643 3644 ;; deprecated, use lsp-request-async. 3645 (defalias 'lsp-send-request-async 'lsp--send-request-async) 3646 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1") 3647 3648 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any 3649 ;; pending language servers. 3650 (add-hook 'kill-emacs-hook #'lsp--global-teardown) 3651 3652 (defun lsp--global-teardown () 3653 "Unload working workspaces." 3654 (lsp-foreach-workspace (lsp--shutdown-workspace))) 3655 3656 (defun lsp--shutdown-workspace (&optional restart) 3657 "Shut down the language server process for ‘lsp--cur-workspace’." 3658 (with-demoted-errors "LSP error: %S" 3659 (let ((lsp-response-timeout 0.5)) 3660 (condition-case err 3661 (lsp-request "shutdown" nil) 3662 (error (lsp--error "%s" err)))) 3663 (lsp-notify "exit" nil)) 3664 (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown)) 3665 (lsp--uninitialize-workspace)) 3666 3667 (defcustom lsp-inlay-hint-enable nil 3668 "If non-nil it will enable inlay hints." 3669 :type 'boolean 3670 :group 'lsp-mode 3671 :package-version '(lsp-mode . "9.0.0")) 3672 3673 (defun lsp--uninitialize-workspace () 3674 "Cleanup buffer state. 3675 When a workspace is shut down, by request or from just 3676 disappearing, unset all the variables related to it." 3677 (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace] 3678 (lsp-process-kill cmd-proc) 3679 (mapc (lambda (buf) 3680 (when (lsp-buffer-live-p buf) 3681 (lsp-with-current-buffer buf 3682 (lsp-managed-mode -1)))) 3683 buffers) 3684 (lsp-diagnostics--workspace-cleanup lsp--cur-workspace))) 3685 3686 (defun lsp--client-capabilities (&optional custom-capabilities) 3687 "Return the client capabilities appending CUSTOM-CAPABILITIES." 3688 (append 3689 `((general . ((positionEncodings . ["utf-32", "utf-16"]))) 3690 (workspace . ((workspaceEdit . ((documentChanges . t) 3691 (resourceOperations . ["create" "rename" "delete"]))) 3692 (applyEdit . t) 3693 (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))))) 3694 (executeCommand . ((dynamicRegistration . :json-false))) 3695 ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t))))) 3696 (workspaceFolders . t) 3697 (configuration . t) 3698 ,@(when lsp-semantic-tokens-enable 3699 `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests) 3700 lsp-semantic-tokens-honor-refresh-requests) 3701 :json-false)))))) 3702 ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t))))) 3703 ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false))))) 3704 (fileOperations . ((didCreate . :json-false) 3705 (willCreate . :json-false) 3706 (didRename . t) 3707 (willRename . t) 3708 (didDelete . :json-false) 3709 (willDelete . :json-false))))) 3710 (textDocument . ((declaration . ((dynamicRegistration . t) 3711 (linkSupport . t))) 3712 (definition . ((dynamicRegistration . t) 3713 (linkSupport . t))) 3714 (references . ((dynamicRegistration . t))) 3715 (implementation . ((dynamicRegistration . t) 3716 (linkSupport . t))) 3717 (typeDefinition . ((dynamicRegistration . t) 3718 (linkSupport . t))) 3719 (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t))) 3720 (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))) 3721 (hierarchicalDocumentSymbolSupport . t))) 3722 (formatting . ((dynamicRegistration . t))) 3723 (rangeFormatting . ((dynamicRegistration . t))) 3724 (onTypeFormatting . ((dynamicRegistration . t))) 3725 ,@(when (and lsp-semantic-tokens-enable 3726 (functionp 'lsp--semantic-tokens-capabilities)) 3727 (lsp--semantic-tokens-capabilities)) 3728 (rename . ((dynamicRegistration . t) (prepareSupport . t))) 3729 (codeAction . ((dynamicRegistration . t) 3730 (isPreferredSupport . t) 3731 (codeActionLiteralSupport . ((codeActionKind . ((valueSet . ["" 3732 "quickfix" 3733 "refactor" 3734 "refactor.extract" 3735 "refactor.inline" 3736 "refactor.rewrite" 3737 "source" 3738 "source.organizeImports"]))))) 3739 (resolveSupport . ((properties . ["edit" "command"]))) 3740 (dataSupport . t))) 3741 (completion . ((completionItem . ((snippetSupport . ,(cond 3742 ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode))) 3743 (lsp--warn (concat 3744 "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. " 3745 "You must either install yasnippet, or disable snippet support.")) 3746 :json-false) 3747 (lsp-enable-snippet t) 3748 (t :json-false))) 3749 (documentationFormat . ["markdown" "plaintext"]) 3750 ;; Remove this after jdtls support resolveSupport 3751 (resolveAdditionalTextEditsSupport . t) 3752 (insertReplaceSupport . t) 3753 (deprecatedSupport . t) 3754 (resolveSupport 3755 . ((properties . ["documentation" 3756 "detail" 3757 "additionalTextEdits" 3758 "command"]))) 3759 (insertTextModeSupport . ((valueSet . [1 2]))))) 3760 (contextSupport . t) 3761 (dynamicRegistration . t))) 3762 (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t))))) 3763 (dynamicRegistration . t))) 3764 (documentLink . ((dynamicRegistration . t) 3765 (tooltipSupport . t))) 3766 (hover . ((contentFormat . ["markdown" "plaintext"]) 3767 (dynamicRegistration . t))) 3768 ,@(when lsp-enable-folding 3769 `((foldingRange . ((dynamicRegistration . t) 3770 ,@(when lsp-folding-range-limit 3771 `((rangeLimit . ,lsp-folding-range-limit))) 3772 ,@(when lsp-folding-line-folding-only 3773 `((lineFoldingOnly . t))))))) 3774 (selectionRange . ((dynamicRegistration . t))) 3775 (callHierarchy . ((dynamicRegistration . :json-false))) 3776 (typeHierarchy . ((dynamicRegistration . t))) 3777 (publishDiagnostics . ((relatedInformation . t) 3778 (tagSupport . ((valueSet . [1 2]))) 3779 (versionSupport . t))) 3780 (diagnostic . ((dynamicRegistration . :json-false) 3781 (relatedDocumentSupport . :json-false))) 3782 (linkedEditingRange . ((dynamicRegistration . t))))) 3783 (window . ((workDoneProgress . t) 3784 (showDocument . ((support . t)))))) 3785 custom-capabilities)) 3786 3787 (defun lsp-find-roots-for-workspace (workspace session) 3788 "Get all roots for the WORKSPACE." 3789 (-filter #'identity (ht-map (lambda (folder workspaces) 3790 (when (-contains? workspaces workspace) 3791 folder)) 3792 (lsp-session-folder->servers session)))) 3793 3794 (defun lsp-session-watches (&optional session) 3795 "Get watches created for SESSION." 3796 (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session)))) 3797 (-let [res (make-hash-table :test 'equal)] 3798 (puthash "__watches" res (lsp-session-metadata (or session (lsp-session)))) 3799 res))) 3800 3801 (defun lsp--file-process-event (session root-folder event) 3802 "Process file event." 3803 (let* ((changed-file (cl-third event)) 3804 (rel-changed-file (f-relative changed-file root-folder)) 3805 (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type)) 3806 (bit-position (1- event-numeric-kind)) 3807 (watch-bit (ash 1 bit-position))) 3808 (->> 3809 session 3810 lsp-session-folder->servers 3811 (gethash root-folder) 3812 (seq-do (lambda (workspace) 3813 (when (->> 3814 workspace 3815 lsp--workspace-registered-server-capabilities 3816 (-any? 3817 (lambda (capability) 3818 (and 3819 (equal (lsp--registered-capability-method capability) 3820 "workspace/didChangeWatchedFiles") 3821 (->> 3822 capability 3823 lsp--registered-capability-options 3824 (lsp:did-change-watched-files-registration-options-watchers) 3825 (seq-find 3826 (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp)) 3827 (when (or (null kind?) 3828 (> (logand kind? watch-bit) 0)) 3829 (-let [regexes (or cached-regexp 3830 (let ((regexp (lsp-glob-to-regexps glob-pattern))) 3831 (lsp-put fs-watcher :_cachedRegexp regexp) 3832 regexp))] 3833 (-any? (lambda (re) 3834 (or (string-match re changed-file) 3835 (string-match re rel-changed-file))) 3836 regexes)))))))))) 3837 (with-lsp-workspace workspace 3838 (lsp-notify 3839 "workspace/didChangeWatchedFiles" 3840 `((changes . [((type . ,event-numeric-kind) 3841 (uri . ,(lsp--path-to-uri changed-file)))])))))))))) 3842 3843 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?)) 3844 "Register capability REG." 3845 (when (and lsp-enable-file-watchers 3846 (equal method "workspace/didChangeWatchedFiles")) 3847 (-let* ((created-watches (lsp-session-watches (lsp-session))) 3848 (root-folders (cl-set-difference 3849 (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session)) 3850 (ht-keys created-watches)))) 3851 ;; create watch for each root folder without such 3852 (dolist (folder root-folders) 3853 (let* ((watch (make-lsp-watch :root-directory folder)) 3854 (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder)) 3855 (ignored-files-regex-list (car ignored-things)) 3856 (ignored-directories-regex-list (cadr ignored-things))) 3857 (puthash folder watch created-watches) 3858 (lsp-watch-root-folder (file-truename folder) 3859 (-partial #'lsp--file-process-event (lsp-session) folder) 3860 ignored-files-regex-list 3861 ignored-directories-regex-list 3862 watch 3863 t))))) 3864 3865 (push 3866 (make-lsp--registered-capability :id id :method method :options register-options?) 3867 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3868 3869 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body) 3870 "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to 3871 access dir-local variables." 3872 (declare (indent 1) (debug t)) 3873 `(with-temp-buffer 3874 ;; Set the buffer's name to something under the root so that we can hack the local variables 3875 ;; This file doesn't need to exist and will not be created due to this. 3876 (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root))) 3877 (hack-local-variables) 3878 (prog1 ,@body 3879 (setq-local buffer-file-name nil)))) 3880 3881 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root) 3882 "Return a list of the form 3883 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given 3884 WORKSPACE-ROOT." 3885 ;; The intent of this function is to provide per-root workspace-level customization of the 3886 ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables. 3887 (lsp--with-workspace-temp-buffer workspace-root 3888 (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories)))) 3889 3890 3891 (defun lsp--cleanup-hanging-watches () 3892 "Cleanup watches in case there are no more workspaces that are interested 3893 in that particular folder." 3894 (let* ((session (lsp-session)) 3895 (watches (lsp-session-watches session))) 3896 (dolist (watched-folder (ht-keys watches)) 3897 (when (-none? (lambda (workspace) 3898 (with-lsp-workspace workspace 3899 (lsp--registered-capability "workspace/didChangeWatchedFiles"))) 3900 (gethash watched-folder (lsp-session-folder->servers (lsp-session)))) 3901 (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder) 3902 (lsp-kill-watch (gethash watched-folder watches)) 3903 (remhash watched-folder watches))))) 3904 3905 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method)) 3906 "Unregister capability UNREG." 3907 (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace) 3908 (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id)) 3909 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3910 (when (equal method "workspace/didChangeWatchedFiles") 3911 (lsp--cleanup-hanging-watches))) 3912 3913 (defun lsp--server-capabilities () 3914 "Return the capabilities of the language server associated with the buffer." 3915 (->> (lsp-workspaces) 3916 (-keep #'lsp--workspace-server-capabilities) 3917 (apply #'lsp-merge))) 3918 3919 (defun lsp--send-open-close-p () 3920 "Return whether open and close notifications should be sent to the server." 3921 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3922 (or (memq sync '(1 2)) 3923 (lsp:text-document-sync-options-open-close? sync)))) 3924 3925 (defun lsp--send-will-save-p () 3926 "Return whether willSave notifications should be sent to the server." 3927 (-> (lsp--server-capabilities) 3928 (lsp:server-capabilities-text-document-sync?) 3929 (lsp:text-document-sync-options-will-save?))) 3930 3931 (defun lsp--send-will-save-wait-until-p () 3932 "Return whether willSaveWaitUntil notifications should be sent to the server." 3933 (-> (lsp--server-capabilities) 3934 (lsp:server-capabilities-text-document-sync?) 3935 (lsp:text-document-sync-options-will-save-wait-until?))) 3936 3937 (defun lsp--send-did-save-p () 3938 "Return whether didSave notifications should be sent to the server." 3939 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3940 (or (memq sync '(1 2)) 3941 (lsp:text-document-sync-options-save? sync)))) 3942 3943 (defun lsp--save-include-text-p () 3944 "Return whether save notifications should include the text document's contents." 3945 (->> (lsp--server-capabilities) 3946 (lsp:server-capabilities-text-document-sync?) 3947 (lsp:text-document-sync-options-save?) 3948 (lsp:text-document-save-registration-options-include-text?))) 3949 3950 (defun lsp--send-will-rename-files-p (path) 3951 "Return whether willRenameFiles request should be sent to the server. 3952 If any filters, checks if it applies for PATH." 3953 (let* ((will-rename (-> (lsp--server-capabilities) 3954 (lsp:server-capabilities-workspace?) 3955 (lsp:workspace-server-capabilities-file-operations?) 3956 (lsp:workspace-file-operations-will-rename?))) 3957 (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list))) 3958 (and will-rename 3959 (or (seq-empty-p filters) 3960 (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob))) 3961 (-let [regexes (lsp-glob-to-regexps glob)] 3962 (and (or (not scheme?) 3963 (string-prefix-p scheme? (lsp--path-to-uri path))) 3964 (-any? (lambda (re) 3965 (string-match re path)) 3966 regexes)))) 3967 filters))))) 3968 3969 (defun lsp--send-did-rename-files-p () 3970 "Return whether didRenameFiles notification should be sent to the server." 3971 (-> (lsp--server-capabilities) 3972 (lsp:server-capabilities-workspace?) 3973 (lsp:workspace-server-capabilities-file-operations?) 3974 (lsp:workspace-file-operations-did-rename?))) 3975 3976 (declare-function project-roots "ext:project" (project) t) 3977 (declare-function project-root "ext:project" (project) t) 3978 3979 (defun lsp--suggest-project-root () 3980 "Get project root." 3981 (or 3982 (when (fboundp 'projectile-project-root) 3983 (condition-case nil 3984 (projectile-project-root) 3985 (error nil))) 3986 (when (fboundp 'project-current) 3987 (when-let ((project (project-current))) 3988 (if (fboundp 'project-root) 3989 (project-root project) 3990 (car (with-no-warnings 3991 (project-roots project)))))) 3992 default-directory)) 3993 3994 (defun lsp--read-from-file (file) 3995 "Read FILE content." 3996 (when (file-exists-p file) 3997 (cl-first (read-from-string (f-read-text file 'utf-8))))) 3998 3999 (defun lsp--persist (file-name to-persist) 4000 "Persist TO-PERSIST in FILE-NAME. 4001 4002 This function creates the parent directories if they don't exist 4003 yet." 4004 (let ((print-length nil) 4005 (print-level nil)) 4006 ;; Create all parent directories: 4007 (make-directory (f-parent file-name) t) 4008 (f-write-text (prin1-to-string to-persist) 'utf-8 file-name))) 4009 4010 (defun lsp-workspace-folders-add (project-root) 4011 "Add PROJECT-ROOT to the list of workspace folders." 4012 (interactive 4013 (list (read-directory-name "Select folder to add: " 4014 (or (lsp--suggest-project-root) default-directory) nil t))) 4015 (cl-pushnew (lsp-f-canonical project-root) 4016 (lsp-session-folders (lsp-session)) :test 'equal) 4017 (lsp--persist-session (lsp-session)) 4018 4019 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)) 4020 4021 (defun lsp-workspace-folders-remove (project-root) 4022 "Remove PROJECT-ROOT from the list of workspace folders." 4023 (interactive (list (completing-read "Select folder to remove: " 4024 (lsp-session-folders (lsp-session)) 4025 nil t nil nil 4026 (lsp-find-session-folder (lsp-session) default-directory)))) 4027 4028 (setq project-root (lsp-f-canonical project-root)) 4029 4030 ;; send remove folder to each multiroot workspace associated with the folder 4031 (dolist (wks (->> (lsp-session) 4032 (lsp-session-folder->servers) 4033 (gethash project-root) 4034 (--filter (lsp--client-multi-root (lsp--workspace-client it))))) 4035 (with-lsp-workspace wks 4036 (lsp-notify "workspace/didChangeWorkspaceFolders" 4037 (lsp-make-did-change-workspace-folders-params 4038 :event (lsp-make-workspace-folders-change-event 4039 :removed (vector (lsp-make-workspace-folder 4040 :uri (lsp--path-to-uri project-root) 4041 :name (f-filename project-root))) 4042 :added []))))) 4043 4044 ;; turn off servers in the removed directory 4045 (let* ((session (lsp-session)) 4046 (folder->servers (lsp-session-folder->servers session)) 4047 (server-id->folders (lsp-session-server-id->folders session)) 4048 (workspaces (gethash project-root folder->servers))) 4049 4050 (remhash project-root folder->servers) 4051 4052 ;; turn off the servers without root folders 4053 (dolist (workspace workspaces) 4054 (when (--none? (-contains? it workspace) (ht-values folder->servers)) 4055 (lsp--info "Shutdown %s since folder %s is removed..." 4056 (lsp--workspace-print workspace) project-root) 4057 (with-lsp-workspace workspace (lsp--shutdown-workspace)))) 4058 4059 (setf (lsp-session-folders session) 4060 (-remove-item project-root (lsp-session-folders session))) 4061 4062 (ht-aeach (puthash key 4063 (-remove-item project-root value) 4064 server-id->folders) 4065 server-id->folders) 4066 (lsp--persist-session (lsp-session))) 4067 4068 (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root))) 4069 4070 (defun lsp-workspace-blocklist-remove (project-root) 4071 "Remove PROJECT-ROOT from the workspace blocklist." 4072 (interactive (list (completing-read "Select folder to remove:" 4073 (lsp-session-folders-blocklist (lsp-session)) 4074 nil t))) 4075 (setf (lsp-session-folders-blocklist (lsp-session)) 4076 (delete project-root 4077 (lsp-session-folders-blocklist (lsp-session)))) 4078 (lsp--persist-session (lsp-session))) 4079 4080 (define-obsolete-function-alias 'lsp-workspace-folders-switch 4081 'lsp-workspace-folders-open "lsp-mode 6.1") 4082 4083 (defun lsp-workspace-folders-open (project-root) 4084 "Open the directory located at PROJECT-ROOT" 4085 (interactive (list (completing-read "Open folder: " 4086 (lsp-session-folders (lsp-session)) 4087 nil t))) 4088 (find-file project-root)) 4089 4090 (defun lsp--maybe-enable-signature-help (trigger-characters) 4091 (let ((ch last-command-event)) 4092 (when (cl-find ch trigger-characters :key #'string-to-char) 4093 (lsp-signature-activate)))) 4094 4095 (defun lsp--on-type-formatting-handler-create () 4096 (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" ))) 4097 (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character? 4098 :first-trigger-character) provider] 4099 (lambda () 4100 (lsp--on-type-formatting first-trigger-character 4101 more-trigger-character?))))) 4102 4103 (defun lsp--update-on-type-formatting-hook (&optional cleanup?) 4104 (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create))) 4105 (cond 4106 ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?)) 4107 (add-hook 'post-self-insert-hook on-type-formatting-handler nil t)) 4108 ((or cleanup? 4109 (not lsp-enable-on-type-formatting)) 4110 (remove-hook 'post-self-insert-hook on-type-formatting-handler t))))) 4111 4112 (defun lsp--signature-help-handler-create () 4113 (-when-let ((&SignatureHelpOptions? :trigger-characters?) 4114 (lsp--capability-for-method "textDocument/signatureHelp")) 4115 (lambda () 4116 (lsp--maybe-enable-signature-help trigger-characters?)))) 4117 4118 (defun lsp--update-signature-help-hook (&optional cleanup?) 4119 (let ((signature-help-handler (lsp--signature-help-handler-create))) 4120 (cond 4121 ((and (or (equal lsp-signature-auto-activate t) 4122 (memq :on-trigger-char lsp-signature-auto-activate)) 4123 signature-help-handler) 4124 (add-hook 'post-self-insert-hook signature-help-handler nil t)) 4125 4126 ((or cleanup? 4127 (not (or (equal lsp-signature-auto-activate t) 4128 (memq :on-trigger-char lsp-signature-auto-activate)))) 4129 (remove-hook 'post-self-insert-hook signature-help-handler t))))) 4130 4131 (defun lsp--after-set-visited-file-name () 4132 (lsp-disconnect) 4133 (lsp)) 4134 4135 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27 4136 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099 4137 (defvar eldoc-documentation-default) ; CI 4138 (when (< emacs-major-version 28) 4139 (unless (boundp 'eldoc-documentation-functions) 4140 (load "eldoc" nil 'nomessage)) 4141 (when (memq (default-value 'eldoc-documentation-function) '(nil ignore)) 4142 ;; actually `eldoc-documentation-strategy', but CI was failing 4143 (setq-default eldoc-documentation-function 'eldoc-documentation-default))) 4144 4145 (define-minor-mode lsp-managed-mode 4146 "Mode for source buffers managed by lsp-mode." 4147 :lighter nil 4148 (cond 4149 (lsp-managed-mode 4150 (when (lsp-feature? "textDocument/hover") 4151 (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t) 4152 (eldoc-mode 1)) 4153 4154 (add-hook 'after-change-functions #'lsp-on-change nil t) 4155 (add-hook 'after-revert-hook #'lsp-on-revert nil t) 4156 (add-hook 'after-save-hook #'lsp-on-save nil t) 4157 (add-hook 'auto-save-hook #'lsp--on-auto-save nil t) 4158 (add-hook 'before-change-functions #'lsp-before-change nil t) 4159 (add-hook 'before-save-hook #'lsp--before-save nil t) 4160 (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t) 4161 (add-hook 'post-command-hook #'lsp--post-command nil t) 4162 4163 (lsp--update-on-type-formatting-hook) 4164 (lsp--update-signature-help-hook) 4165 4166 (when lsp-enable-xref 4167 (add-hook 'xref-backend-functions #'lsp--xref-backend nil t)) 4168 4169 (lsp-configure-buffer) 4170 4171 ;; make sure we turn off lsp-mode in case major mode changes, because major 4172 ;; mode change will wipe the buffer locals. 4173 (add-hook 'change-major-mode-hook #'lsp-disconnect nil t) 4174 (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t) 4175 4176 (let ((buffer (lsp-current-buffer))) 4177 (run-with-idle-timer 4178 0.0 nil 4179 (lambda () 4180 (when (lsp-buffer-live-p buffer) 4181 (lsp-with-current-buffer buffer 4182 (lsp--on-change-debounce buffer) 4183 (lsp--on-idle buffer))))))) 4184 (t 4185 (lsp-unconfig-buffer) 4186 4187 (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t) 4188 (remove-hook 'post-command-hook #'lsp--post-command t) 4189 (remove-hook 'after-change-functions #'lsp-on-change t) 4190 (remove-hook 'after-revert-hook #'lsp-on-revert t) 4191 (remove-hook 'after-save-hook #'lsp-on-save t) 4192 (remove-hook 'auto-save-hook #'lsp--on-auto-save t) 4193 (remove-hook 'before-change-functions #'lsp-before-change t) 4194 (remove-hook 'before-save-hook #'lsp--before-save t) 4195 (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t) 4196 4197 (lsp--update-on-type-formatting-hook :cleanup) 4198 (lsp--update-signature-help-hook :cleanup) 4199 4200 (when lsp--on-idle-timer 4201 (cancel-timer lsp--on-idle-timer) 4202 (setq lsp--on-idle-timer nil)) 4203 4204 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4205 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4206 4207 (lsp--remove-overlays 'lsp-highlight) 4208 (lsp--remove-overlays 'lsp-links) 4209 4210 (remove-hook 'xref-backend-functions #'lsp--xref-backend t) 4211 (remove-hook 'change-major-mode-hook #'lsp-disconnect t) 4212 (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t) 4213 (setq-local lsp-buffer-uri nil)))) 4214 4215 (defun lsp-configure-buffer () 4216 "Configure LSP features for current buffer." 4217 ;; make sure the core is running in the context of all available workspaces 4218 ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context 4219 (let ((lsp--buffer-workspaces (cond 4220 (lsp--buffer-workspaces) 4221 (lsp--cur-workspace (list lsp--cur-workspace)))) 4222 lsp--cur-workspace) 4223 (when lsp-auto-configure 4224 (lsp--auto-configure) 4225 4226 (when (and lsp-enable-text-document-color 4227 (lsp-feature? "textDocument/documentColor")) 4228 (add-hook 'lsp-on-change-hook #'lsp--document-color nil t)) 4229 4230 (when (and lsp-enable-imenu 4231 (lsp-feature? "textDocument/documentSymbol")) 4232 (lsp-enable-imenu)) 4233 4234 (when (and lsp-enable-indentation 4235 (lsp-feature? "textDocument/rangeFormatting")) 4236 (add-function :override (local 'indent-region-function) #'lsp-format-region)) 4237 4238 (when (and lsp-enable-symbol-highlighting 4239 (lsp-feature? "textDocument/documentHighlight")) 4240 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)) 4241 4242 (when (and lsp-enable-links 4243 (lsp-feature? "textDocument/documentLink")) 4244 (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t)) 4245 4246 (when (and lsp-inlay-hint-enable 4247 (lsp-feature? "textDocument/inlayHint")) 4248 (lsp-inlay-hints-mode)) 4249 4250 (when (and lsp-enable-dap-auto-configure 4251 (functionp 'dap-mode)) 4252 (dap-auto-configure-mode 1))) 4253 (run-hooks 'lsp-configure-hook))) 4254 4255 (defun lsp-unconfig-buffer () 4256 "Unconfigure LSP features for buffer." 4257 (lsp--remove-overlays 'lsp-color) 4258 4259 (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function) 4260 (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index) 4261 (setq-local imenu-menubar-modified-tick 0) 4262 (setq-local imenu--index-alist nil) 4263 (imenu--cleanup)) 4264 4265 (remove-function (local 'indent-region-function) #'lsp-format-region) 4266 4267 (remove-hook 'lsp-on-change-hook #'lsp--document-color t) 4268 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4269 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4270 4271 (when (and lsp-enable-dap-auto-configure 4272 (functionp 'dap-mode)) 4273 (dap-auto-configure-mode -1)) 4274 4275 (run-hooks 'lsp-unconfigure-hook)) 4276 4277 (defun lsp--buffer-content () 4278 (lsp-save-restriction-and-excursion 4279 (or (lsp-virtual-buffer-call :buffer-string) 4280 (buffer-substring-no-properties (point-min) 4281 (point-max))))) 4282 4283 (defun lsp--text-document-did-open () 4284 "`document/didOpen' event." 4285 (run-hooks 'lsp-before-open-hook) 4286 (when (and lsp-auto-touch-files 4287 (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri))))) 4288 (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri)) 4289 (save-buffer)) 4290 4291 (setq lsp--cur-version (or lsp--cur-version 0)) 4292 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 4293 (lsp-notify 4294 "textDocument/didOpen" 4295 (list :textDocument 4296 (list :uri (lsp--buffer-uri) 4297 :languageId (lsp-buffer-language) 4298 :version lsp--cur-version 4299 :text (lsp--buffer-content)))) 4300 4301 (lsp-managed-mode 1) 4302 4303 (lsp-diagnostics--request-pull-diagnostics lsp--cur-workspace) 4304 4305 (run-hooks 'lsp-after-open-hook) 4306 (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client)))) 4307 (-some-> (lsp--client-after-open-fn client) 4308 (funcall)) 4309 (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client)) 4310 (intern-soft) 4311 (run-hooks)))) 4312 4313 (defun lsp--text-document-identifier () 4314 "Make TextDocumentIdentifier." 4315 (list :uri (lsp--buffer-uri))) 4316 4317 (defun lsp--versioned-text-document-identifier () 4318 "Make VersionedTextDocumentIdentifier." 4319 (plist-put (lsp--text-document-identifier) :version lsp--cur-version)) 4320 4321 (defun lsp--cur-line (&optional point) 4322 (1- (line-number-at-pos point))) 4323 4324 (defun lsp--cur-position () 4325 "Make a Position object for the current point." 4326 (or (lsp-virtual-buffer-call :cur-position) 4327 (lsp-save-restriction-and-excursion 4328 (list :line (lsp--cur-line) 4329 :character (- (point) (line-beginning-position)))))) 4330 4331 (defun lsp--point-to-position (point) 4332 "Convert POINT to Position." 4333 (lsp-save-restriction-and-excursion 4334 (goto-char point) 4335 (lsp--cur-position))) 4336 4337 (defun lsp--range (start end) 4338 "Make Range body from START and END." 4339 ;; make sure start and end are Position objects 4340 (list :start start :end end)) 4341 4342 (defun lsp--region-to-range (start end) 4343 "Make Range object for the current region." 4344 (lsp--range (lsp--point-to-position start) 4345 (lsp--point-to-position end))) 4346 4347 (defun lsp--region-or-line () 4348 "The active region or the current line." 4349 (if (use-region-p) 4350 (lsp--region-to-range (region-beginning) (region-end)) 4351 (lsp--region-to-range (line-beginning-position) (line-end-position)))) 4352 4353 (defun lsp--check-document-changes-version (document-changes) 4354 "Verify that DOCUMENT-CHANGES have the proper version." 4355 (unless (seq-every-p 4356 (-lambda ((&TextDocumentEdit :text-document)) 4357 (or 4358 (not text-document) 4359 (let* ((filename (-> text-document 4360 lsp:versioned-text-document-identifier-uri 4361 lsp--uri-to-path)) 4362 (version (lsp:versioned-text-document-identifier-version? text-document))) 4363 (with-current-buffer (find-file-noselect filename) 4364 (or (null version) (zerop version) (= -1 version) 4365 (equal version lsp--cur-version)))))) 4366 document-changes) 4367 (error "Document changes cannot be applied due to different document version"))) 4368 4369 (defun lsp--apply-workspace-edit (workspace-edit &optional operation) 4370 "Apply the WorkspaceEdit object WORKSPACE-EDIT. 4371 OPERATION is symbol representing the source of this text edit." 4372 (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit)) 4373 (if-let ((document-changes (seq-reverse document-changes?))) 4374 (progn 4375 (lsp--check-document-changes-version document-changes) 4376 (->> document-changes 4377 (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create"))) 4378 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4379 (->> document-changes 4380 (seq-filter (-lambda ((&CreateFile :kind)) 4381 (and (or (not kind) (equal kind "edit")) 4382 (not (equal kind "create"))))) 4383 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4384 (->> document-changes 4385 (seq-filter (-lambda ((&CreateFile :kind)) 4386 (and (not (or (not kind) (equal kind "edit"))) 4387 (not (equal kind "create"))))) 4388 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))) 4389 (lsp-map 4390 (lambda (uri text-edits) 4391 (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect) 4392 (lsp--apply-text-edits text-edits operation))) 4393 changes?)))) 4394 4395 (defmacro lsp-with-filename (file &rest body) 4396 "Execute BODY with FILE as a context. 4397 Need to handle the case when FILE indicates virtual buffer." 4398 (declare (indent 1) (debug t)) 4399 `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file))) 4400 (lsp-with-current-buffer lsp--virtual-buffer 4401 ,@body) 4402 ,@body)) 4403 4404 (defun lsp--apply-text-document-edit (edit &optional operation) 4405 "Apply the TextDocumentEdit object EDIT. 4406 OPERATION is symbol representing the source of this text edit. 4407 If the file is not being visited by any buffer, it is opened with 4408 `find-file-noselect'. 4409 Because lsp-mode does not store previous document versions, the edit is only 4410 applied if the version of the textDocument matches the version of the 4411 corresponding file. 4412 4413 interface TextDocumentEdit { 4414 textDocument: VersionedTextDocumentIdentifier; 4415 edits: TextEdit[]; 4416 }" 4417 (pcase (lsp:edit-kind edit) 4418 ("create" (-let* (((&CreateFile :uri :options?) edit) 4419 (file-name (lsp--uri-to-path uri))) 4420 (mkdir (f-dirname file-name) t) 4421 (f-touch file-name) 4422 (when (lsp:create-file-options-overwrite? options?) 4423 (f-write-text "" nil file-name)) 4424 (find-file-noselect file-name))) 4425 ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit)) 4426 (f-delete (lsp--uri-to-path uri) recursive?))) 4427 ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit) 4428 (old-file-name (lsp--uri-to-path old-uri)) 4429 (new-file-name (lsp--uri-to-path new-uri)) 4430 (buf (find-buffer-visiting old-file-name))) 4431 (when buf 4432 (lsp-with-current-buffer buf 4433 (save-buffer) 4434 (lsp--text-document-did-close))) 4435 (mkdir (f-dirname new-file-name) t) 4436 (rename-file old-file-name new-file-name overwrite?) 4437 (when buf 4438 (lsp-with-current-buffer buf 4439 (set-buffer-modified-p nil) 4440 (setq lsp-buffer-uri nil) 4441 (set-visited-file-name new-file-name) 4442 (lsp))))) 4443 (_ (let ((file-name (->> edit 4444 (lsp:text-document-edit-text-document) 4445 (lsp:versioned-text-document-identifier-uri) 4446 (lsp--uri-to-path)))) 4447 (lsp-with-current-buffer (find-buffer-visiting file-name) 4448 (lsp-with-filename file-name 4449 (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation))))))) 4450 4451 (lsp-defun lsp--position-compare ((&Position :line left-line 4452 :character left-character) 4453 (&Position :line right-line 4454 :character right-character)) 4455 "Return t if position LEFT is greater than RIGHT." 4456 (if (= left-line right-line) 4457 (> left-character right-character) 4458 (> left-line right-line))) 4459 4460 (lsp-defun lsp-point-in-range? (position (&Range :start :end)) 4461 "Returns if POINT is in RANGE." 4462 (not (or (lsp--position-compare start position) 4463 (lsp--position-compare position end)))) 4464 4465 (lsp-defun lsp--position-equal ((&Position :line left-line 4466 :character left-character) 4467 (&Position :line right-line 4468 :character right-character)) 4469 "Return whether LEFT and RIGHT positions are equal." 4470 (and (= left-line right-line) 4471 (= left-character right-character))) 4472 4473 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end)) 4474 (&TextEdit :range (&Range :start right-start :end right-end))) 4475 (if (lsp--position-equal left-start right-start) 4476 (lsp--position-compare left-end right-end) 4477 (lsp--position-compare left-start right-start))) 4478 4479 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text)) 4480 "Apply the edits described in the TextEdit object in TEXT-EDIT." 4481 (setq new-text (s-replace "\r" "" (or new-text ""))) 4482 (lsp:set-text-edit-new-text edit new-text) 4483 (goto-char start) 4484 (delete-region start end) 4485 (insert new-text)) 4486 4487 ;; WORKAROUND: typescript-language might send -1 when applying code actions. 4488 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582 4489 (lsp-defun lsp--fix-point ((point &as &Position :character :line)) 4490 (-doto point 4491 (lsp:set-position-line (max 0 line)) 4492 (lsp:set-position-character (max 0 character)))) 4493 4494 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as 4495 &TextEdit 4496 :range (&Range :start :end) 4497 :new-text)) 4498 "Apply the edits described in the TextEdit object in TEXT-EDIT. 4499 The method uses `replace-buffer-contents'." 4500 (setq new-text (s-replace "\r" "" (or new-text ""))) 4501 (lsp:set-text-edit-new-text edit new-text) 4502 (-let* ((source (current-buffer)) 4503 ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start) 4504 :end (lsp--fix-point end))))) 4505 (with-temp-buffer 4506 (insert new-text) 4507 (let ((temp (current-buffer))) 4508 (with-current-buffer source 4509 (save-excursion 4510 (save-restriction 4511 (narrow-to-region beg end) 4512 4513 ;; On emacs versions < 26.2, 4514 ;; `replace-buffer-contents' is buggy - it calls 4515 ;; change functions with invalid arguments - so we 4516 ;; manually call the change functions here. 4517 ;; 4518 ;; See emacs bugs #32237, #32278: 4519 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237 4520 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278 4521 (let ((inhibit-modification-hooks t) 4522 (length (- end beg))) 4523 (run-hook-with-args 'before-change-functions 4524 beg end) 4525 (replace-buffer-contents temp) 4526 (run-hook-with-args 'after-change-functions 4527 beg (+ beg (length new-text)) 4528 length))))))))) 4529 4530 (defun lsp--to-yasnippet-snippet (snippet) 4531 "Convert LSP SNIPPET to yasnippet snippet." 4532 ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it. 4533 (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`"))) 4534 (rx "\\" (backref 1)) 4535 snippet 4536 nil nil 1)) 4537 4538 (defvar-local lsp-enable-relative-indentation nil 4539 "Enable relative indentation when insert texts, snippets ... 4540 from language server.") 4541 4542 (defun lsp--expand-snippet (snippet &optional start end expand-env) 4543 "Wrapper of `yas-expand-snippet' with all of it arguments. 4544 The snippet will be convert to LSP style and indent according to 4545 LSP server result." 4546 (require 'yasnippet nil t) 4547 (let* ((inhibit-field-text-motion t) 4548 (yas-wrap-around-region nil) 4549 (yas-indent-line 'none) 4550 (yas-also-auto-indent-first-line nil)) 4551 (yas-expand-snippet 4552 (lsp--to-yasnippet-snippet snippet) 4553 start end expand-env))) 4554 4555 (defun lsp--indent-lines (start end &optional insert-text-mode?) 4556 "Indent from START to END based on INSERT-TEXT-MODE? value. 4557 - When INSERT-TEXT-MODE? is provided 4558 - if it's `lsp/insert-text-mode-as-it', do no editor indentation. 4559 - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading 4560 whitespaces to match the line where text is inserted. 4561 - When it's not provided, using `indent-line-function' for each line." 4562 (save-excursion 4563 (goto-char end) 4564 (let* ((end-line (line-number-at-pos)) 4565 (offset (save-excursion 4566 (goto-char start) 4567 (current-indentation))) 4568 (indent-line-function 4569 (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it) 4570 #'ignore) 4571 ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation) 4572 lsp-enable-relative-indentation 4573 ;; Indenting snippets is extremely slow in `org-mode' buffers 4574 ;; since it has to calculate indentation based on SRC block 4575 ;; position. Thus we use relative indentation as default. 4576 (derived-mode-p 'org-mode)) 4577 (lambda () (save-excursion 4578 (beginning-of-line) 4579 (indent-to-column offset)))) 4580 (t indent-line-function)))) 4581 (goto-char start) 4582 (forward-line) 4583 (while (and (not (eobp)) 4584 (<= (line-number-at-pos) end-line)) 4585 (funcall indent-line-function) 4586 (forward-line))))) 4587 4588 (defun lsp--apply-text-edits (edits &optional operation) 4589 "Apply the EDITS described in the TextEdit[] object. 4590 OPERATION is symbol representing the source of this text edit." 4591 (unless (seq-empty-p edits) 4592 (atomic-change-group 4593 (run-hooks 'lsp-before-apply-edits-hook) 4594 (let* ((change-group (prepare-change-group)) 4595 (howmany (length edits)) 4596 (message (format "Applying %s edits to `%s' ..." howmany (current-buffer))) 4597 (_ (lsp--info message)) 4598 (reporter (make-progress-reporter message 0 howmany)) 4599 (done 0) 4600 (apply-edit (if (not lsp--virtual-buffer) 4601 #'lsp--apply-text-edit-replace-buffer-contents 4602 #'lsp--apply-text-edit))) 4603 (unwind-protect 4604 (->> edits 4605 ;; We sort text edits so as to apply edits that modify latter 4606 ;; parts of the document first. Furthermore, because the LSP 4607 ;; spec dictates that: "If multiple inserts have the same 4608 ;; position, the order in the array defines which edit to 4609 ;; apply first." We reverse the initial list and sort stably 4610 ;; to make sure the order among edits with the same position 4611 ;; is preserved. 4612 (nreverse) 4613 (seq-sort #'lsp--text-edit-sort-predicate) 4614 (mapc (lambda (edit) 4615 (progress-reporter-update reporter (cl-incf done)) 4616 (funcall apply-edit edit) 4617 (when (lsp:snippet-text-edit-insert-text-format? edit) 4618 (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start) 4619 :insert-text-format? :new-text) edit) 4620 (when (eq insert-text-format? lsp/insert-text-format-snippet) 4621 ;; No `save-excursion' needed since expand snippet will change point anyway 4622 (goto-char (+ start (length new-text))) 4623 (lsp--indent-lines start (point)) 4624 (lsp--expand-snippet new-text start (point))))) 4625 (run-hook-with-args 'lsp-after-apply-edits-hook operation)))) 4626 (undo-amalgamate-change-group change-group) 4627 (progress-reporter-done reporter)))))) 4628 4629 (defun lsp--create-apply-text-edits-handlers () 4630 "Create (handler cleanup-fn) for applying text edits in async request. 4631 Only works when mode is `tick or `alive." 4632 (let* (first-edited 4633 (func (lambda (start &rest _) 4634 (setq first-edited (if first-edited 4635 (min start first-edited) 4636 start))))) 4637 (add-hook 'before-change-functions func nil t) 4638 (list 4639 (lambda (edits) 4640 (if (and first-edited 4641 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end))) 4642 ;; Text edit region is overlapped 4643 (> end first-edited)) 4644 edits)) 4645 (lsp--warn "TextEdits will not be applied since document has been modified before of them.") 4646 (lsp--apply-text-edits edits 'completion-cleanup))) 4647 (lambda () 4648 (remove-hook 'before-change-functions func t))))) 4649 4650 (defun lsp--capability (cap &optional capabilities) 4651 "Get the value of capability CAP. If CAPABILITIES is non-nil, use them instead." 4652 (when (stringp cap) 4653 (setq cap (intern (concat ":" cap)))) 4654 4655 (lsp-get (or capabilities 4656 (lsp--server-capabilities)) 4657 cap)) 4658 4659 (defun lsp--registered-capability (method) 4660 "Check whether there is workspace providing METHOD." 4661 (->> (lsp-workspaces) 4662 (--keep (seq-find (lambda (reg) 4663 (equal (lsp--registered-capability-method reg) method)) 4664 (lsp--workspace-registered-server-capabilities it))) 4665 cl-first)) 4666 4667 (defun lsp--capability-for-method (method) 4668 "Get the value of capability for METHOD." 4669 (-let* ((reqs (cdr (assoc method lsp-method-requirements))) 4670 ((&plist :capability) reqs)) 4671 (or (and capability (lsp--capability capability)) 4672 (-some-> (lsp--registered-capability method) 4673 (lsp--registered-capability-options))))) 4674 4675 (defvar-local lsp--before-change-vals nil 4676 "Store the positions from the `lsp-before-change' function call, for 4677 validation and use in the `lsp-on-change' function.") 4678 4679 (defun lsp--text-document-content-change-event (start end length) 4680 "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH." 4681 ;; So (47 54 0) means add 7 chars starting at pos 47 4682 ;; must become 4683 ;; {"range":{"start":{"line":5,"character":6} 4684 ;; ,"end" :{"line":5,"character":6}} 4685 ;; ,"rangeLength":0 4686 ;; ,"text":"\nbb = 5"} 4687 ;; 4688 ;; And (47 47 7) means delete 7 chars starting at pos 47 4689 ;; must become 4690 ;; {"range":{"start":{"line":6,"character":0} 4691 ;; ,"end" :{"line":7,"character":0}} 4692 ;; ,"rangeLength":7 4693 ;; ,"text":""} 4694 ;; 4695 ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with 4696 ;; 13 chars. So it must become 4697 ;; {"range":{"start":{"line":5,"character":8} 4698 ;; ,"end" :{"line":5,"character":11}} 4699 ;; ,"rangeLength":3 4700 ;; ,"text":"new-chars-xxx"} 4701 ;; 4702 4703 ;; Adding text: 4704 ;; lsp-before-change:(start,end)=(33,33) 4705 ;; lsp-on-change:(start,end,length)=(33,34,0) 4706 ;; 4707 ;; Changing text: 4708 ;; lsp-before-change:(start,end)=(208,211) 4709 ;; lsp-on-change:(start,end,length)=(208,221,3) 4710 ;; 4711 ;; Deleting text: 4712 ;; lsp-before-change:(start,end)=(19,27) 4713 ;; lsp-on-change:(start,end,length)=(19,19,8) 4714 (if (zerop length) 4715 ;; Adding something only, work from start only 4716 `( :range ,(lsp--range 4717 (lsp--point-to-position start) 4718 (lsp--point-to-position start)) 4719 :rangeLength 0 4720 :text ,(buffer-substring-no-properties start end)) 4721 4722 (if (eq start end) 4723 ;; Deleting something only 4724 (if (lsp--bracketed-change-p start length) 4725 ;; The before-change value is bracketed, use it 4726 `( :range ,(lsp--range 4727 (lsp--point-to-position start) 4728 (plist-get lsp--before-change-vals :end-pos)) 4729 :rangeLength ,length 4730 :text "") 4731 ;; If the change is not bracketed, send a full change event instead. 4732 (lsp--full-change-event)) 4733 4734 ;; Deleting some things, adding others 4735 (if (lsp--bracketed-change-p start length) 4736 ;; The before-change value is valid, use it 4737 `( :range ,(lsp--range 4738 (lsp--point-to-position start) 4739 (plist-get lsp--before-change-vals :end-pos)) 4740 :rangeLength ,length 4741 :text ,(buffer-substring-no-properties start end)) 4742 (lsp--full-change-event))))) 4743 4744 (defun lsp--bracketed-change-p (start length) 4745 "If the before and after positions are the same, and the length 4746 is the size of the start range, we are probably good." 4747 (-let [(&plist :end before-end :start before-start) lsp--before-change-vals] 4748 (and (eq start before-start) 4749 (eq length (- before-end before-start))))) 4750 4751 (defun lsp--full-change-event () 4752 `(:text ,(lsp--buffer-content))) 4753 4754 (defun lsp-before-change (start end) 4755 "Executed before a file is changed. 4756 Added to `before-change-functions'." 4757 ;; Note: 4758 ;; 4759 ;; This variable holds a list of functions to call when Emacs is about to 4760 ;; modify a buffer. Each function gets two arguments, the beginning and end of 4761 ;; the region that is about to change, represented as integers. The buffer 4762 ;; that is about to change is always the current buffer when the function is 4763 ;; called. 4764 ;; 4765 ;; WARNING: 4766 ;; 4767 ;; Do not expect the before-change hooks and the after-change hooks be called 4768 ;; in balanced pairs around each buffer change. Also don't expect the 4769 ;; before-change hooks to be called for every chunk of text Emacs is about to 4770 ;; delete. These hooks are provided on the assumption that Lisp programs will 4771 ;; use either before- or the after-change hooks, but not both, and the 4772 ;; boundaries of the region where the changes happen might include more than 4773 ;; just the actual changed text, or even lump together several changes done 4774 ;; piecemeal. 4775 (save-match-data 4776 (lsp-save-restriction-and-excursion 4777 (setq lsp--before-change-vals 4778 (list :start start 4779 :end end 4780 :end-pos (lsp--point-to-position end)))))) 4781 4782 (defun lsp--flush-delayed-changes () 4783 (let ((inhibit-quit t)) 4784 (when lsp--delay-timer 4785 (cancel-timer lsp--delay-timer)) 4786 (mapc (-lambda ((workspace buffer document change)) 4787 (with-current-buffer buffer 4788 (with-lsp-workspace workspace 4789 (lsp-notify "textDocument/didChange" 4790 (list :textDocument document 4791 :contentChanges (vector change)))))) 4792 (prog1 (nreverse lsp--delayed-requests) 4793 (setq lsp--delayed-requests nil))))) 4794 4795 (defun lsp--workspace-sync-method (workspace) 4796 (let ((sync (-> workspace 4797 (lsp--workspace-server-capabilities) 4798 (lsp:server-capabilities-text-document-sync?)))) 4799 (if (lsp-text-document-sync-options? sync) 4800 (lsp:text-document-sync-options-change? sync) 4801 sync))) 4802 4803 (defun lsp-on-change (start end length &optional content-change-event-fn) 4804 "Executed when a file is changed. 4805 Added to `after-change-functions'." 4806 ;; Note: 4807 ;; 4808 ;; Each function receives three arguments: the beginning and end of the region 4809 ;; just changed, and the length of the text that existed before the change. 4810 ;; All three arguments are integers. The buffer that has been changed is 4811 ;; always the current buffer when the function is called. 4812 ;; 4813 ;; The length of the old text is the difference between the buffer positions 4814 ;; before and after that text as it was before the change. As for the 4815 ;; changed text, its length is simply the difference between the first two 4816 ;; arguments. 4817 ;; 4818 ;; So (47 54 0) means add 7 chars starting at pos 47 4819 ;; So (47 47 7) means delete 7 chars starting at pos 47 4820 (save-match-data 4821 (let ((inhibit-quit t) 4822 ;; make sure that `lsp-on-change' is called in multi-workspace context 4823 ;; see #2901 4824 lsp--cur-workspace) 4825 ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done 4826 ;; by auto-revert-mode) will cause this handler to get called with a nil 4827 ;; buffer-file-name. We need the buffer-file-name to send notifications; 4828 ;; so we skip handling revert-buffer-caused changes and instead handle 4829 ;; reverts separately in lsp-on-revert 4830 (when (not revert-buffer-in-progress-p) 4831 (cl-incf lsp--cur-version) 4832 (mapc 4833 (lambda (workspace) 4834 (pcase (or lsp-document-sync-method 4835 (lsp--workspace-sync-method workspace)) 4836 (1 4837 (if lsp-debounce-full-sync-notifications 4838 (setq lsp--delayed-requests 4839 (->> lsp--delayed-requests 4840 (-remove (-lambda ((_ buffer)) 4841 (equal (current-buffer) buffer))) 4842 (cons (list workspace 4843 (current-buffer) 4844 (lsp--versioned-text-document-identifier) 4845 (lsp--full-change-event))))) 4846 (with-lsp-workspace workspace 4847 (lsp-notify "textDocument/didChange" 4848 (list :contentChanges (vector (lsp--full-change-event)) 4849 :textDocument (lsp--versioned-text-document-identifier))) 4850 (lsp-diagnostics--request-pull-diagnostics workspace)))) 4851 (2 4852 (with-lsp-workspace workspace 4853 (lsp-notify 4854 "textDocument/didChange" 4855 (list :textDocument (lsp--versioned-text-document-identifier) 4856 :contentChanges (vector 4857 (if content-change-event-fn 4858 (funcall content-change-event-fn start end length) 4859 (lsp--text-document-content-change-event 4860 start end length))))) 4861 (lsp-diagnostics--request-pull-diagnostics workspace))))) 4862 (lsp-workspaces)) 4863 (when lsp--delay-timer (cancel-timer lsp--delay-timer)) 4864 (setq lsp--delay-timer (run-with-idle-timer 4865 lsp-debounce-full-sync-notifications-interval 4866 nil 4867 #'lsp--flush-delayed-changes)) 4868 ;; force cleanup overlays after each change 4869 (lsp--remove-overlays 'lsp-highlight) 4870 (lsp--after-change (current-buffer)))))) 4871 4872 4873 4874 ;; facilities for on change hooks. We do not want to make lsp calls on each 4875 ;; change event so we add debounce to avoid flooding the server with events. 4876 ;; Additionally, we want to have a mechanism for stopping the server calls in 4877 ;; particular cases like, e. g. when performing completion. 4878 4879 (defvar lsp-inhibit-lsp-hooks nil 4880 "Flag to control.") 4881 4882 (defcustom lsp-on-change-hook nil 4883 "Hooks to run when buffer has changed." 4884 :type 'hook 4885 :group 'lsp-mode) 4886 4887 (defcustom lsp-idle-delay 0.500 4888 "Debounce interval for `after-change-functions'." 4889 :type 'number 4890 :group 'lsp-mode) 4891 4892 (defcustom lsp-on-idle-hook nil 4893 "Hooks to run after `lsp-idle-delay'." 4894 :type 'hook 4895 :group 'lsp-mode) 4896 4897 (defun lsp--idle-reschedule (buffer) 4898 (when lsp--on-idle-timer 4899 (cancel-timer lsp--on-idle-timer)) 4900 4901 (setq lsp--on-idle-timer (run-with-idle-timer 4902 lsp-idle-delay 4903 nil 4904 #'lsp--on-idle 4905 buffer))) 4906 4907 (defun lsp--post-command () 4908 (lsp--cleanup-highlights-if-needed) 4909 (lsp--idle-reschedule (current-buffer))) 4910 4911 (defun lsp--on-idle (buffer) 4912 "Start post command loop." 4913 (when (and (buffer-live-p buffer) 4914 (equal buffer (current-buffer)) 4915 (not lsp-inhibit-lsp-hooks) 4916 lsp-managed-mode) 4917 (run-hooks 'lsp-on-idle-hook))) 4918 4919 (defun lsp--on-change-debounce (buffer) 4920 (when (and (buffer-live-p buffer) 4921 (equal buffer (current-buffer)) 4922 (not lsp-inhibit-lsp-hooks) 4923 lsp-managed-mode) 4924 (run-hooks 'lsp-on-change-hook))) 4925 4926 (defun lsp--after-change (buffer) 4927 "Called after most textDocument/didChange events." 4928 (setq lsp--signature-last-index nil 4929 lsp--signature-last nil) 4930 4931 ;; cleanup diagnostics 4932 (when lsp-diagnostic-clean-after-change 4933 (dolist (workspace (lsp-workspaces)) 4934 (-let [diagnostics (lsp--workspace-diagnostics workspace)] 4935 (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics)))) 4936 4937 (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled) 4938 (lsp--semantic-tokens-refresh-if-enabled buffer)) 4939 (when lsp--on-change-timer 4940 (cancel-timer lsp--on-change-timer)) 4941 (setq lsp--on-change-timer (run-with-idle-timer 4942 lsp-idle-delay 4943 nil 4944 #'lsp--on-change-debounce 4945 buffer)) 4946 (lsp--idle-reschedule buffer)) 4947 4948 4949 (defcustom lsp-trim-trailing-whitespace t 4950 "Trim trailing whitespace on a line." 4951 :group 'lsp-mode 4952 :type 'boolean) 4953 4954 (defcustom lsp-insert-final-newline t 4955 "Insert a newline character at the end of the file if one does not exist." 4956 :group 'lsp-mode 4957 :type 'boolean) 4958 4959 (defcustom lsp-trim-final-newlines t 4960 "Trim all newlines after the final newline at the end of the file." 4961 :group 'lsp-mode 4962 :type 'boolean) 4963 4964 4965 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters) 4966 "Self insert handling. 4967 Applies on type formatting." 4968 (let ((ch last-command-event)) 4969 (when (or (eq (string-to-char first-trigger-characters) ch) 4970 (cl-find ch more-trigger-characters :key #'string-to-char)) 4971 (lsp-request-async "textDocument/onTypeFormatting" 4972 (lsp-make-document-on-type-formatting-params 4973 :text-document (lsp--text-document-identifier) 4974 :options (lsp-make-formatting-options 4975 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 4976 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 4977 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 4978 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 4979 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)) 4980 :ch (char-to-string ch) 4981 :position (lsp--cur-position)) 4982 (lambda (data) (lsp--apply-text-edits data 'format)) 4983 :mode 'tick)))) 4984 4985 4986 ;; links 4987 (defun lsp--document-links () 4988 (when (lsp-feature? "textDocument/documentLink") 4989 (lsp-request-async 4990 "textDocument/documentLink" 4991 `(:textDocument ,(lsp--text-document-identifier)) 4992 (lambda (links) 4993 (lsp--remove-overlays 'lsp-link) 4994 (seq-do 4995 (-lambda ((link &as &DocumentLink :range (&Range :start :end))) 4996 (-doto (make-button (lsp--position-to-point start) 4997 (lsp--position-to-point end) 4998 'action (lsp--document-link-keymap link) 4999 'keymap (let ((map (make-sparse-keymap))) 5000 (define-key map [M-return] 'push-button) 5001 (define-key map [mouse-2] 'push-button) 5002 map) 5003 'help-echo "mouse-2, M-RET: Visit this link") 5004 (overlay-put 'lsp-link t))) 5005 links)) 5006 :mode 'unchanged))) 5007 5008 (defun lsp--document-link-handle-target (url) 5009 (let* ((parsed-url (url-generic-parse-url (url-unhex-string url))) 5010 (type (url-type parsed-url))) 5011 (pcase type 5012 ("file" 5013 (xref-push-marker-stack) 5014 (find-file (lsp--uri-to-path url)) 5015 (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url)) 5016 (goto-char (lsp--position-to-point 5017 (lsp-make-position :character (1- (string-to-number column)) 5018 :line (1- (string-to-number line))))))) 5019 ((or "http" "https") (browse-url url)) 5020 (type (if-let ((handler (lsp--get-uri-handler type))) 5021 (funcall handler url) 5022 (signal 'lsp-file-scheme-not-supported (list url))))))) 5023 5024 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?)) 5025 (if target? 5026 (lambda (_) 5027 (interactive) 5028 (lsp--document-link-handle-target target?)) 5029 (lambda (_) 5030 (interactive) 5031 (when (lsp:document-link-registration-options-resolve-provider? 5032 (lsp--capability-for-method "textDocument/documentLink")) 5033 (lsp-request-async 5034 "documentLink/resolve" 5035 link 5036 (-lambda ((&DocumentLink :target?)) 5037 (lsp--document-link-handle-target target?))))))) 5038 5039 5040 5041 (defcustom lsp-warn-no-matched-clients t 5042 "Whether to show messages when there are no supported clients." 5043 :group 'lsp-mode 5044 :type 'boolean) 5045 5046 (defun lsp-buffer-language--configured-id () 5047 "Return nil when not registered." 5048 (->> lsp-language-id-configuration 5049 (-first 5050 (-lambda ((mode-or-pattern . language)) 5051 (cond 5052 ((and (stringp mode-or-pattern) 5053 (s-matches? mode-or-pattern (buffer-file-name))) 5054 language) 5055 ((eq mode-or-pattern major-mode) language)))) 5056 cl-rest)) 5057 5058 (defvar-local lsp--buffer-language nil 5059 "Locally cached returned value of `lsp-buffer-language'.") 5060 5061 (defun lsp-buffer-language () 5062 "Get language corresponding current buffer." 5063 (or lsp--buffer-language 5064 (let* ((configured-language (lsp-buffer-language--configured-id))) 5065 (setq lsp--buffer-language 5066 (or configured-language 5067 ;; ensure non-nil 5068 (string-remove-suffix "-mode" (symbol-name major-mode)))) 5069 (when (and lsp-warn-no-matched-clients 5070 (null configured-language)) 5071 (lsp-warn "Unable to calculate the languageId for buffer `%s'. \ 5072 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s" 5073 (buffer-name) 5074 major-mode)) 5075 lsp--buffer-language))) 5076 5077 (defun lsp-activate-on (&rest languages) 5078 "Returns language activation function. 5079 The function will return t when the `lsp-buffer-language' returns 5080 one of the LANGUAGES." 5081 (lambda (_file-name _mode) 5082 (-contains? languages (lsp-buffer-language)))) 5083 5084 (defun lsp-workspace-root (&optional path) 5085 "Find the workspace root for the current file or PATH." 5086 (-when-let* ((file-name (or path (buffer-file-name))) 5087 (file-name (lsp-f-canonical file-name))) 5088 (->> (lsp-session) 5089 (lsp-session-folders) 5090 (--filter (and (lsp--files-same-host it file-name) 5091 (or (lsp-f-ancestor-of? it file-name) 5092 (equal it file-name)))) 5093 (--max-by (> (length it) (length other)))))) 5094 5095 (defun lsp-on-revert () 5096 "Executed when a file is reverted. 5097 Added to `after-revert-hook'." 5098 (let ((n (buffer-size)) 5099 (revert-buffer-in-progress-p nil)) 5100 (lsp-on-change 0 n n))) 5101 5102 (defun lsp--text-document-did-close (&optional keep-workspace-alive) 5103 "Executed when the file is closed, added to `kill-buffer-hook'. 5104 5105 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace 5106 if it's closing the last buffer in the workspace." 5107 (lsp-foreach-workspace 5108 (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 5109 (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S" 5110 (lsp-notify "textDocument/didClose" 5111 `(:textDocument ,(lsp--text-document-identifier)))) 5112 (when (and (not lsp-keep-workspace-alive) 5113 (not keep-workspace-alive) 5114 (not (lsp--workspace-buffers lsp--cur-workspace))) 5115 (lsp--shutdown-workspace)))) 5116 5117 (defun lsp--will-save-text-document-params (reason) 5118 (list :textDocument (lsp--text-document-identifier) 5119 :reason reason)) 5120 5121 (defun lsp--before-save () 5122 "Before save handler." 5123 (with-demoted-errors "Error in ‘lsp--before-save’: %S" 5124 (let ((params (lsp--will-save-text-document-params 1))) 5125 (when (lsp--send-will-save-p) 5126 (lsp-notify "textDocument/willSave" params)) 5127 (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits) 5128 (let ((lsp-response-timeout 0.1)) 5129 (condition-case nil 5130 (lsp--apply-text-edits 5131 (lsp-request "textDocument/willSaveWaitUntil" 5132 params) 5133 'before-save) 5134 (error))))))) 5135 5136 (defun lsp--on-auto-save () 5137 "Handler for auto-save." 5138 (when (lsp--send-will-save-p) 5139 (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S" 5140 (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2))))) 5141 5142 (defun lsp--text-document-did-save () 5143 "Executed when the file is closed, added to `after-save-hook''." 5144 (when (lsp--send-did-save-p) 5145 (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’" 5146 (lsp-notify "textDocument/didSave" 5147 `( :textDocument ,(lsp--versioned-text-document-identifier) 5148 ,@(when (lsp--save-include-text-p) 5149 (list :text (lsp--buffer-content)))))))) 5150 5151 (defun lsp--text-document-position-params (&optional identifier position) 5152 "Make TextDocumentPositionParams for the current point in the current document. 5153 If IDENTIFIER and POSITION are non-nil, they will be used as the document 5154 identifier and the position respectively." 5155 (list :textDocument (or identifier (lsp--text-document-identifier)) 5156 :position (or position (lsp--cur-position)))) 5157 5158 (defun lsp--get-buffer-diagnostics () 5159 "Return buffer diagnostics." 5160 (gethash (or 5161 (plist-get lsp--virtual-buffer :buffer-file-name) 5162 (lsp--fix-path-casing (buffer-file-name))) 5163 (lsp-diagnostics t))) 5164 5165 (defun lsp-cur-line-diagnostics () 5166 "Return any diagnostics that apply to the current line." 5167 (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)] 5168 (cl-coerce (-filter 5169 (-lambda ((&Diagnostic :range (&Range :start (&Position :line)))) 5170 (and (>= line start) (<= line end))) 5171 (lsp--get-buffer-diagnostics)) 5172 'vector))) 5173 5174 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end) 5175 (right &as &Range :start right-start :end right-end)) 5176 (or (lsp-point-in-range? right-start left) 5177 (lsp-point-in-range? right-end left) 5178 (lsp-point-in-range? left-start right) 5179 (lsp-point-in-range? left-end right))) 5180 5181 (defun lsp-make-position-1 (position) 5182 (lsp-make-position :line (plist-get position :line) 5183 :character (plist-get position :character))) 5184 5185 (defun lsp-cur-possition-diagnostics () 5186 "Return any diagnostics that apply to the current line." 5187 (-let* ((start (if (use-region-p) (region-beginning) (point))) 5188 (end (if (use-region-p) (region-end) (point))) 5189 (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start)) 5190 :end (lsp-make-position-1 (lsp-point-to-position end))))) 5191 (->> (lsp--get-buffer-diagnostics) 5192 (-filter 5193 (-lambda ((&Diagnostic :range)) 5194 (lsp-range-overlapping? range current-range))) 5195 (apply 'vector)))) 5196 5197 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics) 5198 5199 (defun lsp--extract-line-from-buffer (pos) 5200 "Return the line pointed to by POS (a Position object) in the current buffer." 5201 (let* ((point (lsp--position-to-point pos)) 5202 (inhibit-field-text-motion t)) 5203 (save-excursion 5204 (goto-char point) 5205 (buffer-substring (line-beginning-position) (line-end-position))))) 5206 5207 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line) 5208 :end (end &as &Position :character end-char))) 5209 "Return a xref-item from a RANGE in FILENAME." 5210 (let* ((line (lsp--extract-line-from-buffer start)) 5211 (len (length line))) 5212 (add-face-text-property (max (min start-char len) 0) 5213 (max (min end-char len) 0) 5214 'xref-match t line) 5215 ;; LINE is nil when FILENAME is not being current visited by any buffer. 5216 (xref-make-match (or line filename) 5217 (xref-make-file-location 5218 filename 5219 (lsp-translate-line (1+ start-line)) 5220 (lsp-translate-column start-char)) 5221 (- end-char start-char)))) 5222 5223 (defun lsp--location-uri (loc) 5224 (if (lsp-location? loc) 5225 (lsp:location-uri loc) 5226 (lsp:location-link-target-uri loc))) 5227 5228 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start))) 5229 "Go to location." 5230 (let ((path (lsp--uri-to-path uri))) 5231 (if (f-exists? path) 5232 (with-current-buffer (find-file path) 5233 (goto-char (lsp--position-to-point start))) 5234 (error "There is no file %s" path)))) 5235 5236 (defun lsp--location-range (loc) 5237 (if (lsp-location? loc) 5238 (lsp:location-range loc) 5239 (lsp:location-link-target-selection-range loc))) 5240 5241 (defun lsp--locations-to-xref-items (locations) 5242 "Return a list of `xref-item' given LOCATIONS, which can be of 5243 type Location, LocationLink, Location[] or LocationLink[]." 5244 (setq locations 5245 (pcase locations 5246 ((seq (or (Location) 5247 (LocationLink))) 5248 (append locations nil)) 5249 ((or (Location) 5250 (LocationLink)) 5251 (list locations)))) 5252 5253 (cl-labels ((get-xrefs-in-file 5254 (file-locs) 5255 (-let [(filename . matches) file-locs] 5256 (condition-case err 5257 (let ((visiting (find-buffer-visiting filename)) 5258 (fn (lambda (loc) 5259 (lsp-with-filename filename 5260 (lsp--xref-make-item filename 5261 (lsp--location-range loc)))))) 5262 (if visiting 5263 (with-current-buffer visiting 5264 (seq-map fn matches)) 5265 (when (file-readable-p filename) 5266 (with-temp-buffer 5267 (insert-file-contents-literally filename) 5268 (seq-map fn matches))))) 5269 (error (lsp-warn "Failed to process xref entry for filename '%s': %s" 5270 filename (error-message-string err))) 5271 (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s" 5272 filename (error-message-string err))))))) 5273 5274 (->> locations 5275 (seq-sort #'lsp--location-before-p) 5276 (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri)) 5277 (seq-map #'get-xrefs-in-file) 5278 (apply #'nconc)))) 5279 5280 (defun lsp--location-before-p (left right) 5281 "Sort first by file, then by line, then by column." 5282 (let ((left-uri (lsp--location-uri left)) 5283 (right-uri (lsp--location-uri right))) 5284 (if (not (string= left-uri right-uri)) 5285 (string< left-uri right-uri) 5286 (-let (((&Range :start left-start) (lsp--location-range left)) 5287 ((&Range :start right-start) (lsp--location-range right))) 5288 (lsp--position-compare right-start left-start))))) 5289 5290 (defun lsp--make-reference-params (&optional td-position exclude-declaration) 5291 "Make a ReferenceParam object. 5292 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead. 5293 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." 5294 (let ((json-false :json-false)) 5295 (plist-put (or td-position (lsp--text-document-position-params)) 5296 :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration)))))) 5297 5298 (defun lsp--cancel-request (id) 5299 "Cancel request with ID in all workspaces." 5300 (lsp-foreach-workspace 5301 (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id)) 5302 (lsp-notify "$/cancelRequest" `(:id ,id)))) 5303 5304 (defvar-local lsp--hover-saved-bounds nil) 5305 5306 (defun lsp-eldoc-function (cb &rest _ignored) 5307 "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')." 5308 (if (and lsp--hover-saved-bounds 5309 (lsp--point-in-bounds-p lsp--hover-saved-bounds)) 5310 lsp--eldoc-saved-message 5311 (setq lsp--hover-saved-bounds nil 5312 lsp--eldoc-saved-message nil) 5313 (if (looking-at-p "[[:space:]\n]") 5314 (setq lsp--eldoc-saved-message nil) ; And returns nil. 5315 (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover")) 5316 (lsp-request-async 5317 "textDocument/hover" 5318 (lsp--text-document-position-params) 5319 (-lambda ((hover &as &Hover? :range? :contents)) 5320 (setq lsp--hover-saved-bounds (when range? 5321 (lsp--range-to-region range?))) 5322 (funcall cb (setq lsp--eldoc-saved-message 5323 (when contents 5324 (lsp--render-on-hover-content 5325 contents 5326 lsp-eldoc-render-all))))) 5327 :error-handler #'ignore 5328 :mode 'tick 5329 :cancel-token :eldoc-hover))))) 5330 5331 (defun lsp--point-on-highlight? () 5332 (-some? (lambda (overlay) 5333 (overlay-get overlay 'lsp-highlight)) 5334 (overlays-at (point)))) 5335 5336 (defun lsp--cleanup-highlights-if-needed () 5337 (when (and lsp-enable-symbol-highlighting 5338 lsp--have-document-highlights 5339 (not (lsp--point-on-highlight?))) 5340 (lsp--remove-overlays 'lsp-highlight) 5341 (setq lsp--have-document-highlights nil) 5342 (lsp-cancel-request-by-token :highlights))) 5343 5344 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil 5345 "The bounds of the symbol from which `lsp--document-highlight' 5346 most recently requested highlights.") 5347 5348 (defun lsp--document-highlight () 5349 (when (lsp-feature? "textDocument/documentHighlight") 5350 (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol))) 5351 (unless (or (looking-at-p "[[:space:]\n]") 5352 (not lsp-enable-symbol-highlighting) 5353 (and lsp--have-document-highlights 5354 curr-sym-bounds 5355 (equal curr-sym-bounds 5356 lsp--symbol-bounds-of-last-highlight-invocation))) 5357 (setq lsp--symbol-bounds-of-last-highlight-invocation 5358 curr-sym-bounds) 5359 (lsp-request-async "textDocument/documentHighlight" 5360 (lsp--text-document-position-params) 5361 #'lsp--document-highlight-callback 5362 :mode 'tick 5363 :cancel-token :highlights))))) 5364 5365 (defun lsp--help-open-link (&rest _) 5366 "Open markdown link at point via mouse or keyboard." 5367 (interactive "P") 5368 (let ((buffer-list-update-hook nil)) 5369 (-let [(buffer point) (if-let* ((valid (and (listp last-input-event) 5370 (eq (car last-input-event) 'mouse-2))) 5371 (event (cadr last-input-event)) 5372 (win (posn-window event)) 5373 (buffer (window-buffer win))) 5374 `(,buffer ,(posn-point event)) 5375 `(,(current-buffer) ,(point)))] 5376 (with-current-buffer buffer 5377 (when-let* ((face (get-text-property point 'face)) 5378 (url (or (and (eq face 'markdown-link-face) 5379 (get-text-property point 'help-echo)) 5380 (and (memq face '(markdown-url-face markdown-plain-url-face)) 5381 (nth 3 (markdown-link-at-pos point)))))) 5382 (lsp--document-link-handle-target url)))))) 5383 5384 (defvar lsp-help-mode-map 5385 (-doto (make-sparse-keymap) 5386 (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link)) 5387 "Keymap for `lsp-help-mode'.") 5388 5389 (define-derived-mode lsp-help-mode help-mode "LspHelp" 5390 "Major mode for displaying lsp help.") 5391 5392 (defun lsp-describe-thing-at-point () 5393 "Display the type signature and documentation of the thing at point." 5394 (interactive) 5395 (let ((contents (-some->> (lsp--text-document-position-params) 5396 (lsp--make-request "textDocument/hover") 5397 (lsp--send-request) 5398 (lsp:hover-contents)))) 5399 (if (and contents (not (equal contents ""))) 5400 (let ((lsp-help-buf-name "*lsp-help*")) 5401 (with-current-buffer (get-buffer-create lsp-help-buf-name) 5402 (delay-mode-hooks 5403 (lsp-help-mode) 5404 (with-help-window lsp-help-buf-name 5405 (insert (string-trim-right (lsp--render-on-hover-content contents t))))) 5406 (run-mode-hooks))) 5407 (lsp--info "No content at point.")))) 5408 5409 (defun lsp--point-in-bounds-p (bounds) 5410 "Return whether the current point is within BOUNDS." 5411 (and (<= (car bounds) (point)) (< (point) (cdr bounds)))) 5412 5413 (defun lsp-get-renderer (language) 5414 "Get renderer for LANGUAGE." 5415 (lambda (str) 5416 (lsp--render-string str language))) 5417 5418 (defun lsp--setup-markdown (mode) 5419 "Setup the ‘markdown-mode’ in the frame. 5420 MODE is the mode used in the parent frame." 5421 (make-local-variable 'markdown-code-lang-modes) 5422 (dolist (mark (alist-get mode lsp-custom-markup-modes)) 5423 (add-to-list 'markdown-code-lang-modes (cons mark mode))) 5424 (setq-local markdown-fontify-code-blocks-natively t) 5425 (setq-local markdown-fontify-code-block-default-mode mode) 5426 (setq-local markdown-hide-markup t) 5427 5428 ;; Render some common HTML entities. 5429 ;; This should really happen in markdown-mode instead, 5430 ;; but it doesn't, so we do it here for now. 5431 (setq prettify-symbols-alist 5432 (cl-loop for i from 0 to 255 5433 collect (cons (format "&#x%02X;" i) i))) 5434 (push '("<" . ?<) prettify-symbols-alist) 5435 (push '(">" . ?>) prettify-symbols-alist) 5436 (push '("&" . ?&) prettify-symbols-alist) 5437 (push '(" " . ? ) prettify-symbols-alist) 5438 (setq prettify-symbols-compose-predicate 5439 (lambda (_start _end _match) t)) 5440 (prettify-symbols-mode 1)) 5441 5442 (defvar lsp-help-link-keymap 5443 (let ((map (make-sparse-keymap))) 5444 (define-key map [mouse-2] #'lsp--help-open-link) 5445 (define-key map "\r" #'lsp--help-open-link) 5446 map) 5447 "Keymap active on links in *lsp-help* mode.") 5448 5449 (defun lsp--fix-markdown-links () 5450 (let ((inhibit-read-only t) 5451 (inhibit-modification-hooks t) 5452 (prop)) 5453 (save-restriction 5454 (goto-char (point-min)) 5455 (while (setq prop (markdown-find-next-prop 'face)) 5456 (let ((end (or (next-single-property-change (car prop) 'face) 5457 (point-max)))) 5458 (when (memq (get-text-property (car prop) 'face) 5459 '(markdown-link-face 5460 markdown-url-face 5461 markdown-plain-url-face)) 5462 (add-text-properties (car prop) end 5463 (list 'button t 5464 'category 'lsp-help-link 5465 'follow-link t 5466 'keymap lsp-help-link-keymap))) 5467 (goto-char end)))))) 5468 5469 (defun lsp--buffer-string-visible () 5470 "Return visible buffer string. 5471 Stolen from `org-copy-visible'." 5472 (let ((temp (generate-new-buffer " *temp*")) 5473 (beg (point-min)) 5474 (end (point-max))) 5475 (while (/= beg end) 5476 (when (get-char-property beg 'invisible) 5477 (setq beg (next-single-char-property-change beg 'invisible nil end))) 5478 (let* ((next (next-single-char-property-change beg 'invisible nil end)) 5479 (substring (buffer-substring beg next))) 5480 (with-current-buffer temp (insert substring)) 5481 ;; (setq result (concat result substring)) 5482 (setq beg next))) 5483 (setq deactivate-mark t) 5484 (prog1 (with-current-buffer temp 5485 (s-chop-suffix "\n" (buffer-string))) 5486 (kill-buffer temp)))) 5487 5488 (defvar lsp-buffer-major-mode nil 5489 "Holds the major mode when fontification function is running. 5490 See #2588") 5491 5492 (defvar view-inhibit-help-message) 5493 5494 (defun lsp--render-markdown () 5495 "Render markdown." 5496 5497 (let ((markdown-enable-math nil)) 5498 (goto-char (point-min)) 5499 (while (re-search-forward 5500 (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/" 5501 "{" "}" "[" "]" "(" ")" 5502 "#" "+" "-" "." "!" "|")))) 5503 nil t) 5504 (replace-match (rx (backref 1)))) 5505 5506 ;; markdown-mode v2.3 does not yet provide gfm-view-mode 5507 (if (fboundp 'gfm-view-mode) 5508 (let ((view-inhibit-help-message t)) 5509 (gfm-view-mode)) 5510 (gfm-mode)) 5511 5512 (lsp--setup-markdown lsp-buffer-major-mode))) 5513 5514 (defvar lsp--display-inline-image-alist 5515 '((lsp--render-markdown 5516 (:regexp 5517 "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)" 5518 :sexp 5519 (create-image 5520 (base64-decode-string 5521 (buffer-substring-no-properties (match-beginning 1) (match-end 1))) 5522 nil t)))) 5523 "Replaced string regexp and function returning image. 5524 Each element should have the form (MODE . (PROPERTY-LIST...)). 5525 MODE (car) is function which is defined in `lsp-language-id-configuration'. 5526 Cdr should be list of PROPERTY-LIST. 5527 5528 Each PROPERTY-LIST should have properties: 5529 :regexp Regexp which determines what string is relpaced to image. 5530 You should also get information of image, by parenthesis constructs. 5531 By default, all matched string is replaced to image, but you can 5532 change index of replaced string by keyword :replaced-index. 5533 5534 :sexp Return image when evaluated. You can use information of regexp 5535 by using (match-beggining N), (match-end N) or (match-substring N). 5536 5537 In addition, each can have property: 5538 :replaced-index Determine index which is used to replace regexp to image. 5539 The value means first argument of `match-beginning' and 5540 `match-end'. If omitted, interpreted as index 0.") 5541 5542 (defcustom lsp-display-inline-image t 5543 "Showing inline image or not." 5544 :group 'lsp-mode 5545 :type 'boolean) 5546 5547 (defcustom lsp-enable-suggest-server-download t 5548 "When non-nil enable server downloading suggestions." 5549 :group 'lsp-mode 5550 :type 'boolean 5551 :package-version '(lsp-mode . "9.0.0")) 5552 5553 (defcustom lsp-auto-register-remote-clients t 5554 "When non-nil register remote when registering the local one." 5555 :group 'lsp-mode 5556 :type 'boolean 5557 :package-version '(lsp-mode . "9.0.0")) 5558 5559 (defun lsp--display-inline-image (mode) 5560 "Add image property if available." 5561 (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist)))) 5562 (when (and (display-images-p) lsp-display-inline-image) 5563 (cl-loop 5564 for plist in plist-list 5565 with regexp with replaced-index 5566 do 5567 (setq regexp (plist-get plist :regexp)) 5568 (setq replaced-index (or (plist-get plist :replaced-index) 0)) 5569 5570 (font-lock-remove-keywords nil (list regexp replaced-index)) 5571 (let ((inhibit-read-only t)) 5572 (save-excursion 5573 (goto-char (point-min)) 5574 (while (re-search-forward regexp nil t) 5575 (set-text-properties 5576 (match-beginning replaced-index) (match-end replaced-index) 5577 nil) 5578 (add-text-properties 5579 (match-beginning replaced-index) (match-end replaced-index) 5580 `(display ,(eval (plist-get plist :sexp))))))))))) 5581 5582 (defun lsp--fontlock-with-mode (str mode) 5583 "Fontlock STR with MODE." 5584 (let ((lsp-buffer-major-mode major-mode)) 5585 (with-temp-buffer 5586 (with-demoted-errors "Error during doc rendering: %s" 5587 (insert str) 5588 (delay-mode-hooks (funcall mode)) 5589 (cl-flet ((window-body-width () lsp-window-body-width)) 5590 ;; This can go wrong in some cases, and the fontification would 5591 ;; not work as expected. 5592 ;; 5593 ;; See #2984 5594 (ignore-errors (font-lock-ensure)) 5595 (lsp--display-inline-image mode) 5596 (when (eq mode 'lsp--render-markdown) 5597 (lsp--fix-markdown-links)))) 5598 (lsp--buffer-string-visible)))) 5599 5600 (defun lsp--render-string (str language) 5601 "Render STR using `major-mode' corresponding to LANGUAGE. 5602 When language is nil render as markup if `markdown-mode' is loaded." 5603 (setq str (s-replace "\r" "" (or str ""))) 5604 (if-let* ((modes (-keep (-lambda ((mode . lang)) 5605 (when (and (equal lang language) (functionp mode)) 5606 mode)) 5607 lsp-language-id-configuration)) 5608 (mode (car (or (member major-mode modes) modes)))) 5609 (lsp--fontlock-with-mode str mode) 5610 str)) 5611 5612 (defun lsp--render-element (content) 5613 "Render CONTENT element." 5614 (let ((inhibit-message t)) 5615 (or 5616 (pcase content 5617 ((MarkedString :value :language) 5618 (lsp--render-string value language)) 5619 ((MarkupContent :value :kind) 5620 (lsp--render-string value kind)) 5621 ;; plain string 5622 ((pred stringp) (lsp--render-string content "markdown")) 5623 ((pred null) "") 5624 (_ (error "Failed to handle %s" content))) 5625 ""))) 5626 5627 (defun lsp--create-unique-string-fn () 5628 (let (elements) 5629 (lambda (element) 5630 (let ((count (cl-count element elements :test #'string=))) 5631 (prog1 (if (zerop count) 5632 element 5633 (format "%s (%s)" element count)) 5634 (push element elements)))))) 5635 5636 (defun lsp--select-action (actions) 5637 "Select an action to execute from ACTIONS." 5638 (cond 5639 ((seq-empty-p actions) (signal 'lsp-no-code-actions nil)) 5640 ((and (eq (seq-length actions) 1) lsp-auto-execute-action) 5641 (lsp-seq-first actions)) 5642 (t (let ((completion-ignore-case t)) 5643 (lsp--completing-read "Select code action: " 5644 (seq-into actions 'list) 5645 (-compose (lsp--create-unique-string-fn) 5646 #'lsp:code-action-title) 5647 nil t))))) 5648 5649 (defun lsp--workspace-server-id (workspace) 5650 "Return the server ID of WORKSPACE." 5651 (-> workspace lsp--workspace-client lsp--client-server-id)) 5652 5653 (defun lsp--handle-rendered-for-echo-area (contents) 5654 "Return a single line from RENDERED, appropriate for display in the echo area." 5655 (pcase (lsp-workspaces) 5656 (`(,workspace) 5657 (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace))) 5658 ;; For projects with multiple active workspaces we also default to 5659 ;; render the first line. 5660 (_ (lsp-clients-extract-signature-on-hover contents nil)))) 5661 5662 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id) 5663 "Extract a representative line from CONTENTS, to show in the echo area." 5664 (car (s-lines (s-trim (lsp--render-element contents))))) 5665 5666 (defun lsp--render-on-hover-content (contents render-all) 5667 "Render the content received from `document/onHover' request. 5668 CONTENTS - MarkedString | MarkedString[] | MarkupContent 5669 RENDER-ALL - nil if only the signature should be rendered." 5670 (cond 5671 ((lsp-markup-content? contents) 5672 ;; MarkupContent. 5673 ;; It tends to be long and is not suitable to display fully in the echo area. 5674 ;; Just display the first line which is typically the signature. 5675 (if render-all 5676 (lsp--render-element contents) 5677 (lsp--handle-rendered-for-echo-area contents))) 5678 ((and (stringp contents) (not (string-match-p "\n" contents))) 5679 ;; If the contents is a single string containing a single line, 5680 ;; render it always. 5681 (lsp--render-element contents)) 5682 (t 5683 ;; MarkedString -> MarkedString[] 5684 (when (or (lsp-marked-string? contents) (stringp contents)) 5685 (setq contents (list contents))) 5686 ;; Consider the signature consisting of the elements who have a renderable 5687 ;; "language" property. When render-all is nil, ignore other elements. 5688 (string-join 5689 (seq-map 5690 #'lsp--render-element 5691 (if render-all 5692 contents 5693 ;; Only render contents that have an available renderer. 5694 (seq-take 5695 (seq-filter 5696 (-andfn #'lsp-marked-string? 5697 (-compose #'lsp-get-renderer #'lsp:marked-string-language)) 5698 contents) 5699 1))) 5700 (if (bound-and-true-p page-break-lines-mode) 5701 "\n\n" 5702 "\n"))))) 5703 5704 5705 5706 (defvar lsp-signature-mode-map 5707 (-doto (make-sparse-keymap) 5708 (define-key (kbd "M-n") #'lsp-signature-next) 5709 (define-key (kbd "M-p") #'lsp-signature-previous) 5710 (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs) 5711 (define-key (kbd "C-c C-k") #'lsp-signature-stop) 5712 (define-key (kbd "C-g") #'lsp-signature-stop)) 5713 "Keymap for `lsp-signature-mode'.") 5714 5715 (define-minor-mode lsp-signature-mode 5716 "Mode used to show signature popup." 5717 :keymap lsp-signature-mode-map 5718 :lighter "" 5719 :group 'lsp-mode) 5720 5721 (defun lsp-signature-stop () 5722 "Stop showing current signature help." 5723 (interactive) 5724 (lsp-cancel-request-by-token :signature) 5725 (remove-hook 'post-command-hook #'lsp-signature) 5726 (funcall lsp-signature-function nil) 5727 (lsp-signature-mode -1)) 5728 5729 (declare-function page-break-lines--update-display-tables "ext:page-break-lines") 5730 5731 (defun lsp--setup-page-break-mode-if-present () 5732 "Enable `page-break-lines-mode' in current buffer." 5733 (when (fboundp 'page-break-lines-mode) 5734 (page-break-lines-mode) 5735 ;; force page-break-lines-mode to update the display tables. 5736 (page-break-lines--update-display-tables))) 5737 5738 (defun lsp-lv-message (message) 5739 (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present) 5740 (if message 5741 (progn 5742 (setq lsp--signature-last-buffer (current-buffer)) 5743 (let ((lv-force-update t)) 5744 (lv-message "%s" message))) 5745 (lv-delete-window) 5746 (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present))) 5747 5748 (declare-function posframe-show "ext:posframe") 5749 (declare-function posframe-hide "ext:posframe") 5750 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe") 5751 5752 (defface lsp-signature-posframe 5753 '((t :inherit tooltip)) 5754 "Background and foreground for `lsp-signature-posframe'." 5755 :group 'lsp-mode) 5756 5757 (defvar lsp-signature-posframe-params 5758 (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward 5759 :height 10 5760 :width 60 5761 :border-width 1 5762 :min-width 60) 5763 "Params for signature and `posframe-show'.") 5764 5765 (defun lsp-signature-posframe (str) 5766 "Use posframe to show the STR signatureHelp string." 5767 (if str 5768 (apply #'posframe-show 5769 (with-current-buffer (get-buffer-create " *lsp-signature*") 5770 (erase-buffer) 5771 (insert str) 5772 (visual-line-mode 1) 5773 (lsp--setup-page-break-mode-if-present) 5774 (current-buffer)) 5775 (append 5776 lsp-signature-posframe-params 5777 (list :position (point) 5778 :background-color (face-attribute 'lsp-signature-posframe :background nil t) 5779 :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t) 5780 :border-color (face-attribute 'font-lock-comment-face :foreground nil t)))) 5781 (posframe-hide " *lsp-signature*"))) 5782 5783 (defun lsp--handle-signature-update (signature) 5784 (let ((message 5785 (if (lsp-signature-help? signature) 5786 (lsp--signature->message signature) 5787 (mapconcat #'lsp--signature->message signature "\n")))) 5788 (if (s-present? message) 5789 (funcall lsp-signature-function message) 5790 (lsp-signature-stop)))) 5791 5792 (defun lsp-signature-activate () 5793 "Activate signature help. 5794 It will show up only if current point has signature help." 5795 (interactive) 5796 (setq lsp--signature-last nil 5797 lsp--signature-last-index nil 5798 lsp--signature-last-buffer (current-buffer)) 5799 (add-hook 'post-command-hook #'lsp-signature) 5800 (lsp-signature-mode t)) 5801 5802 (defcustom lsp-signature-cycle t 5803 "Whether `lsp-signature-next' and prev should cycle." 5804 :type 'boolean 5805 :group 'lsp-mode) 5806 5807 (defun lsp-signature-next () 5808 "Show next signature." 5809 (interactive) 5810 (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last)))) 5811 (when (and lsp--signature-last-index 5812 lsp--signature-last 5813 (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs))) 5814 (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs)) 5815 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))) 5816 5817 (defun lsp-signature-previous () 5818 "Next signature." 5819 (interactive) 5820 (when (and lsp--signature-last-index 5821 lsp--signature-last 5822 (or lsp-signature-cycle (not (zerop lsp--signature-last-index)))) 5823 (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index) 5824 (length (lsp:signature-help-signatures lsp--signature-last)) 5825 lsp--signature-last-index))) 5826 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))) 5827 5828 (defun lsp-signature-toggle-full-docs () 5829 "Toggle full/partial signature documentation." 5830 (interactive) 5831 (let ((all? (not (numberp lsp-signature-doc-lines)))) 5832 (setq lsp-signature-doc-lines (if all? 5833 (or (car-safe lsp-signature-doc-lines) 5834 20) 5835 (list lsp-signature-doc-lines)))) 5836 (lsp-signature-activate)) 5837 5838 (defun lsp--signature->message (signature-help) 5839 "Generate eldoc message from SIGNATURE-HELP response." 5840 (setq lsp--signature-last signature-help) 5841 5842 (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help)))) 5843 (-let* (((&SignatureHelp :active-signature? 5844 :active-parameter? 5845 :signatures) signature-help) 5846 (active-signature? (or lsp--signature-last-index active-signature? 0)) 5847 (_ (setq lsp--signature-last-index active-signature?)) 5848 ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?)) 5849 (prefix (if (= (length signatures) 1) 5850 "" 5851 (concat (propertize (format " %s/%s" 5852 (1+ active-signature?) 5853 (length signatures)) 5854 'face 'success) 5855 " "))) 5856 (method-docs (when 5857 (and lsp-signature-render-documentation 5858 (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines))) 5859 (let ((docs (lsp--render-element 5860 (lsp:parameter-information-documentation? signature)))) 5861 (when (s-present? docs) 5862 (concat 5863 "\n" 5864 (if (fboundp 'page-break-lines-mode) 5865 "\n" 5866 "") 5867 (if (and (numberp lsp-signature-doc-lines) 5868 (> (length (s-lines docs)) lsp-signature-doc-lines)) 5869 (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs))) 5870 (propertize "\nTruncated..." 'face 'highlight)) 5871 docs))))))) 5872 (when (and active-parameter? (not (seq-empty-p parameters?))) 5873 (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?))) 5874 (seq-elt parameters? active-parameter?))) 5875 (selected-param-label (let ((label (lsp:parameter-information-label param))) 5876 (if (stringp label) label (append label nil)))) 5877 (start (if (stringp selected-param-label) 5878 (s-index-of selected-param-label label) 5879 (cl-first selected-param-label))) 5880 (end (if (stringp selected-param-label) 5881 (+ start (length selected-param-label)) 5882 (cl-second selected-param-label)))) 5883 (add-face-text-property start end 'eldoc-highlight-function-argument nil label))) 5884 (concat prefix label method-docs)))) 5885 5886 (defun lsp-signature () 5887 "Display signature info (based on `textDocument/signatureHelp')" 5888 (if (and lsp--signature-last-buffer 5889 (not (equal (current-buffer) lsp--signature-last-buffer))) 5890 (lsp-signature-stop) 5891 (lsp-request-async "textDocument/signatureHelp" 5892 (lsp--text-document-position-params) 5893 #'lsp--handle-signature-update 5894 :cancel-token :signature))) 5895 5896 5897 (defcustom lsp-overlay-document-color-char "■" 5898 "Display the char represent the document color in overlay" 5899 :type 'string 5900 :group 'lsp-mode) 5901 5902 ;; color presentation 5903 (defun lsp--color-create-interactive-command (color range) 5904 (lambda () 5905 (interactive) 5906 (-let [(&ColorPresentation? :text-edit? 5907 :additional-text-edits?) 5908 (lsp--completing-read 5909 "Select color presentation: " 5910 (lsp-request 5911 "textDocument/colorPresentation" 5912 `( :textDocument ,(lsp--text-document-identifier) 5913 :color ,color 5914 :range ,range)) 5915 #'lsp:color-presentation-label 5916 nil 5917 t)] 5918 (when text-edit? 5919 (lsp--apply-text-edit text-edit?)) 5920 (when additional-text-edits? 5921 (lsp--apply-text-edits additional-text-edits? 'color-presentation))))) 5922 5923 (defun lsp--number->color (number) 5924 (let ((result (format "%x" 5925 (round (* (or number 0) 255.0))))) 5926 (if (= 1 (length result)) 5927 (concat "0" result) 5928 result))) 5929 5930 (defun lsp--document-color () 5931 "Document color handler." 5932 (when (lsp-feature? "textDocument/documentColor") 5933 (lsp-request-async 5934 "textDocument/documentColor" 5935 `(:textDocument ,(lsp--text-document-identifier)) 5936 (lambda (result) 5937 (lsp--remove-overlays 'lsp-color) 5938 (seq-do 5939 (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue) 5940 :range)) 5941 (-let* (((beg . end) (lsp--range-to-region range)) 5942 (overlay (make-overlay beg end)) 5943 (command (lsp--color-create-interactive-command color range))) 5944 (overlay-put overlay 'lsp-color t) 5945 (overlay-put overlay 'evaporate t) 5946 (overlay-put overlay 5947 'before-string 5948 (propertize 5949 lsp-overlay-document-color-char 5950 'face `((:foreground ,(format 5951 "#%s%s%s" 5952 (lsp--number->color red) 5953 (lsp--number->color green) 5954 (lsp--number->color blue)))) 5955 'action command 5956 'mouse-face 'lsp-lens-mouse-face 5957 'local-map (-doto (make-sparse-keymap) 5958 (define-key [mouse-1] command)))))) 5959 result)) 5960 :mode 'unchanged 5961 :cancel-token :document-color-token))) 5962 5963 5964 5965 (defun lsp--action-trigger-parameter-hints (_command) 5966 "Handler for editor.action.triggerParameterHints." 5967 (when (member :on-server-request lsp-signature-auto-activate) 5968 (lsp-signature-activate))) 5969 5970 (defun lsp--action-trigger-suggest (_command) 5971 "Handler for editor.action.triggerSuggest." 5972 (cond 5973 ((and (bound-and-true-p company-mode) 5974 (fboundp 'company-auto-begin) 5975 (fboundp 'company-post-command)) 5976 (run-at-time 0 nil 5977 (lambda () 5978 (let ((this-command 'company-idle-begin) 5979 (company-minimum-prefix-length 0)) 5980 (company-auto-begin) 5981 (company-post-command))))) 5982 (t 5983 (completion-at-point)))) 5984 5985 (defconst lsp--default-action-handlers 5986 (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints) 5987 ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest)) 5988 "Default action handlers.") 5989 5990 (defun lsp--find-action-handler (command) 5991 "Find action handler for particular COMMAND." 5992 (or 5993 (--some (-some->> it 5994 (lsp--workspace-client) 5995 (lsp--client-action-handlers) 5996 (gethash command)) 5997 (lsp-workspaces)) 5998 (gethash command lsp--default-action-handlers))) 5999 6000 (defun lsp--text-document-code-action-params (&optional kind) 6001 "Code action params." 6002 (list :textDocument (lsp--text-document-identifier) 6003 :range (if (use-region-p) 6004 (lsp--region-to-range (region-beginning) (region-end)) 6005 (lsp--region-to-range (point) (point))) 6006 :context `( :diagnostics ,(lsp-cur-possition-diagnostics) 6007 ,@(when kind (list :only (vector kind)))))) 6008 6009 (defun lsp-code-actions-at-point (&optional kind) 6010 "Retrieve the code actions for the active region or the current line. 6011 It will filter by KIND if non nil." 6012 (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind))) 6013 6014 (defun lsp-execute-code-action-by-kind (command-kind) 6015 "Execute code action by COMMAND-KIND." 6016 (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind) 6017 (-filter (-lambda ((&CodeAction :kind?)) 6018 (and kind? (s-prefix? command-kind kind?)))) 6019 lsp--select-action))) 6020 (lsp-execute-code-action action) 6021 (signal 'lsp-no-code-actions '(command-kind)))) 6022 6023 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point) 6024 6025 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?)) 6026 "Parse and execute a code ACTION represented as a Command LSP type." 6027 (let ((server-id (->> (lsp-workspaces) 6028 (cl-first) 6029 (or lsp--cur-workspace) 6030 (lsp--workspace-client) 6031 (lsp--client-server-id)))) 6032 (condition-case nil 6033 (with-no-warnings 6034 (lsp-execute-command server-id (intern command) arguments?)) 6035 (cl-no-applicable-method 6036 (if-let ((action-handler (lsp--find-action-handler command))) 6037 (funcall action-handler action) 6038 (lsp-send-execute-command command arguments?)))))) 6039 6040 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?)) 6041 "Execute code action ACTION. For example, when text under the 6042 caret has a suggestion to apply a fix from an lsp-server, calling 6043 this function will do so. 6044 If ACTION is not set it will be selected from `lsp-code-actions-at-point'. 6045 Request codeAction/resolve for more info if server supports." 6046 (interactive (list (lsp--select-action (lsp-code-actions-at-point)))) 6047 (if (and (lsp-feature? "codeAction/resolve") 6048 (not command?) 6049 (not edit?)) 6050 (lsp--execute-code-action (lsp-request "codeAction/resolve" action)) 6051 (lsp--execute-code-action action))) 6052 6053 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?)) 6054 "Execute code action ACTION." 6055 (when edit? 6056 (lsp--apply-workspace-edit edit? 'code-action)) 6057 6058 (cond 6059 ((stringp command?) (lsp--execute-command action)) 6060 ((lsp-command? command?) (progn 6061 (when-let ((action-filter (->> (lsp-workspaces) 6062 (cl-first) 6063 (or lsp--cur-workspace) 6064 (lsp--workspace-client) 6065 (lsp--client-action-filter)))) 6066 (funcall action-filter command?)) 6067 (lsp--execute-command command?))))) 6068 6069 (lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments) 6070 "Patch incorrect boolean argument values in the provided `CodeAction' command 6071 in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values 6072 in this list can be either symbols or lists of symbols that 6073 represent paths to boolean arguments in code actions: 6074 6075 > (lsp-fix-code-action-booleans command `(:foo :bar (:some :nested :boolean))) 6076 6077 When there are available code actions, the server sends 6078 `lsp-mode' a list of possible command names and arguments as 6079 JSON. `lsp-mode' parses all boolean false values as `nil'. As a 6080 result code action arguments containing falsy values don't 6081 roundtrip correctly because `lsp-mode' will end up sending null 6082 values back to the client. This list makes it possible to 6083 selectively transform `nil' values back into `:json-false'." 6084 (seq-doseq (path boolean-action-arguments) 6085 (seq-doseq (args arguments?) 6086 (lsp--fix-nested-boolean args (if (listp path) path (list path)))))) 6087 6088 (defun lsp--fix-nested-boolean (structure path) 6089 "Traverse STRUCTURE using the paths from the PATH list, changing the value to 6090 `:json-false' if it was `nil'. PATH should be a list containing 6091 one or more symbols, and STRUCTURE should be compatible with 6092 `lsp-member?', `lsp-get', and `lsp-put'." 6093 (let ((key (car path)) 6094 (rest (cdr path))) 6095 (if (null rest) 6096 ;; `lsp-put' returns `nil' both when the key doesn't exist and when the 6097 ;; value is `nil', so we need to explicitly check its presence here 6098 (when (and (lsp-member? structure key) (not (lsp-get structure key))) 6099 (lsp-put structure key :json-false)) 6100 ;; If `key' does not exist, then we'll silently ignore it 6101 (when-let ((child (lsp-get structure key))) 6102 (lsp--fix-nested-boolean child rest))))) 6103 6104 (defvar lsp--formatting-indent-alist 6105 ;; Taken from `dtrt-indent-mode' 6106 '( 6107 (ada-mode . ada-indent) ; Ada 6108 (ada-ts-mode . ada-ts-mode-indent-offset) 6109 (c++-mode . c-basic-offset) ; C++ 6110 (c++-ts-mode . c-ts-mode-indent-offset) 6111 (c-mode . c-basic-offset) ; C 6112 (c-ts-mode . c-ts-mode-indent-offset) 6113 (cperl-mode . cperl-indent-level) ; Perl 6114 (crystal-mode . crystal-indent-level) ; Crystal (Ruby) 6115 (csharp-mode . c-basic-offset) ; C# 6116 (csharp-tree-sitter-mode . csharp-tree-sitter-indent-offset) ; C# 6117 (csharp-ts-mode . csharp-ts-mode-indent-offset) ; C# (tree-sitter, Emacs29) 6118 (css-mode . css-indent-offset) ; CSS 6119 (d-mode . c-basic-offset) ; D 6120 (enh-ruby-mode . enh-ruby-indent-level) ; Ruby 6121 (erlang-mode . erlang-indent-level) ; Erlang 6122 (ess-mode . ess-indent-offset) ; ESS (R) 6123 (go-ts-mode . go-ts-mode-indent-offset) 6124 (gpr-mode . gpr-indent-offset) ; GNAT Project 6125 (gpr-ts-mode . gpr-ts-mode-indent-offset) 6126 (hack-mode . hack-indent-offset) ; Hack 6127 (java-mode . c-basic-offset) ; Java 6128 (java-ts-mode . java-ts-mode-indent-offset) 6129 (jde-mode . c-basic-offset) ; Java (JDE) 6130 (js-mode . js-indent-level) ; JavaScript 6131 (js-ts-mode . js-indent-level) 6132 (js2-mode . js2-basic-offset) ; JavaScript-IDE 6133 (js3-mode . js3-indent-level) ; JavaScript-IDE 6134 (json-mode . js-indent-level) ; JSON 6135 (json-ts-mode . json-ts-mode-indent-offset) 6136 (lua-mode . lua-indent-level) ; Lua 6137 (lua-ts-mode . lua-ts-indent-offset) 6138 (nxml-mode . nxml-child-indent) ; XML 6139 (objc-mode . c-basic-offset) ; Objective C 6140 (pascal-mode . pascal-indent-level) ; Pascal 6141 (perl-mode . perl-indent-level) ; Perl 6142 (php-mode . c-basic-offset) ; PHP 6143 (php-ts-mode . php-ts-mode-indent-offset) ; PHP 6144 (powershell-mode . powershell-indent) ; PowerShell 6145 (powershell-ts-mode . powershell-ts-mode-indent-offset) ; PowerShell 6146 (raku-mode . raku-indent-offset) ; Perl6/Raku 6147 (ruby-mode . ruby-indent-level) ; Ruby 6148 (rust-mode . rust-indent-offset) ; Rust 6149 (rust-ts-mode . rust-ts-mode-indent-offset) 6150 (rustic-mode . rustic-indent-offset) ; Rust 6151 (scala-mode . scala-indent:step) ; Scala 6152 (sgml-mode . sgml-basic-offset) ; SGML 6153 (sh-mode . sh-basic-offset) ; Shell Script 6154 (toml-ts-mode . toml-ts-mode-indent-offset) 6155 (typescript-mode . typescript-indent-level) ; Typescript 6156 (typescript-ts-mode . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29) 6157 (yaml-mode . yaml-indent-offset) ; YAML 6158 (yang-mode . c-basic-offset) ; YANG (yang-mode) 6159 6160 (default . standard-indent)) ; default fallback 6161 "A mapping from `major-mode' to its indent variable.") 6162 6163 (defun lsp--get-indent-width (mode) 6164 "Get indentation offset for MODE." 6165 (or (alist-get mode lsp--formatting-indent-alist) 6166 (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default)))) 6167 6168 (defun lsp--make-document-formatting-params () 6169 "Create document formatting params." 6170 (lsp-make-document-formatting-params 6171 :text-document (lsp--text-document-identifier) 6172 :options (lsp-make-formatting-options 6173 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 6174 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 6175 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 6176 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 6177 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)))) 6178 6179 (defun lsp-format-buffer () 6180 "Ask the server to format this document." 6181 (interactive "*") 6182 (cond ((lsp-feature? "textDocument/formatting") 6183 (let ((edits (lsp-request "textDocument/formatting" 6184 (lsp--make-document-formatting-params)))) 6185 (if (seq-empty-p edits) 6186 (lsp--info "No formatting changes provided") 6187 (lsp--apply-text-edits edits 'format)))) 6188 ((lsp-feature? "textDocument/rangeFormatting") 6189 (save-restriction 6190 (widen) 6191 (lsp-format-region (point-min) (point-max)))) 6192 (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider"))))) 6193 6194 (defun lsp-format-region (s e) 6195 "Ask the server to format the region, or if none is selected, the current line." 6196 (interactive "r") 6197 (let ((edits (lsp-request 6198 "textDocument/rangeFormatting" 6199 (lsp--make-document-range-formatting-params s e)))) 6200 (if (seq-empty-p edits) 6201 (lsp--info "No formatting changes provided") 6202 (lsp--apply-text-edits edits 'format)))) 6203 6204 (defmacro lsp-make-interactive-code-action (func-name code-action-kind) 6205 "Define an interactive function FUNC-NAME that attempts to 6206 execute a CODE-ACTION-KIND action." 6207 `(defun ,(intern (concat "lsp-" (symbol-name func-name))) () 6208 ,(format "Perform the %s code action, if available." code-action-kind) 6209 (interactive) 6210 ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to 6211 ;; auto-execute here: the user has specified exactly what they want. 6212 (let ((lsp-auto-execute-action t)) 6213 (condition-case nil 6214 (lsp-execute-code-action-by-kind ,code-action-kind) 6215 (lsp-no-code-actions 6216 (when (called-interactively-p 'any) 6217 (lsp--info ,(format "%s action not available" code-action-kind)))))))) 6218 6219 (lsp-make-interactive-code-action organize-imports "source.organizeImports") 6220 6221 (defun lsp--make-document-range-formatting-params (start end) 6222 "Make DocumentRangeFormattingParams for selected region." 6223 (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params) 6224 (lsp--region-to-range start end))) 6225 6226 (defconst lsp--highlight-kind-face 6227 '((1 . lsp-face-highlight-textual) 6228 (2 . lsp-face-highlight-read) 6229 (3 . lsp-face-highlight-write))) 6230 6231 (defun lsp--remove-overlays (name) 6232 (save-restriction 6233 (widen) 6234 (remove-overlays (point-min) (point-max) name t))) 6235 6236 (defun lsp-document-highlight () 6237 "Highlight all relevant references to the symbol under point." 6238 (interactive) 6239 (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights 6240 (setq lsp--have-document-highlights nil 6241 lsp--symbol-bounds-of-last-highlight-invocation nil) 6242 (let ((lsp-enable-symbol-highlighting t)) 6243 (lsp--document-highlight))) 6244 6245 (defun lsp--document-highlight-callback (highlights) 6246 "Create a callback to process the reply of a 6247 `textDocument/documentHighlight' message for the buffer BUF. 6248 A reference is highlighted only if it is visible in a window." 6249 (lsp--remove-overlays 'lsp-highlight) 6250 6251 (let* ((wins-visible-pos (-map (lambda (win) 6252 (cons (1- (line-number-at-pos (window-start win) t)) 6253 (1+ (line-number-at-pos (window-end win) t)))) 6254 (get-buffer-window-list nil nil 'visible)))) 6255 (setq lsp--have-document-highlights t) 6256 (-map 6257 (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line) 6258 :end (end &as &Position :line end-line)) 6259 :kind?)) 6260 (-map 6261 (-lambda ((start-window . end-window)) 6262 ;; Make the overlay only if the reference is visible 6263 (let ((start-point (lsp--position-to-point start)) 6264 (end-point (lsp--position-to-point end))) 6265 (when (and (> (1+ start-line) start-window) 6266 (< (1+ end-line) end-window) 6267 (not (and lsp-symbol-highlighting-skip-current 6268 (<= start-point (point) end-point)))) 6269 (-doto (make-overlay start-point end-point) 6270 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face))) 6271 (overlay-put 'lsp-highlight t))))) 6272 wins-visible-pos)) 6273 highlights))) 6274 6275 (defcustom lsp-symbol-kinds 6276 '((1 . "File") 6277 (2 . "Module") 6278 (3 . "Namespace") 6279 (4 . "Package") 6280 (5 . "Class") 6281 (6 . "Method") 6282 (7 . "Property") 6283 (8 . "Field") 6284 (9 . "Constructor") 6285 (10 . "Enum") 6286 (11 . "Interface") 6287 (12 . "Function") 6288 (13 . "Variable") 6289 (14 . "Constant") 6290 (15 . "String") 6291 (16 . "Number") 6292 (17 . "Boolean") 6293 (18 . "Array") 6294 (19 . "Object") 6295 (20 . "Key") 6296 (21 . "Null") 6297 (22 . "Enum Member") 6298 (23 . "Struct") 6299 (24 . "Event") 6300 (25 . "Operator") 6301 (26 . "Type Parameter")) 6302 "Alist mapping SymbolKinds to human-readable strings. 6303 Various Symbol objects in the LSP protocol have an integral type, 6304 specifying what they are. This alist maps such type integrals to 6305 readable representations of them. See 6306 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/', 6307 namespace SymbolKind." 6308 :group 'lsp-mode 6309 :type '(alist :key-type integer :value-type string)) 6310 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds) 6311 6312 (lsp-defun lsp--symbol-information-to-xref 6313 ((&SymbolInformation :kind :name 6314 :location (&Location :uri :range (&Range :start 6315 (&Position :line :character))))) 6316 "Return a `xref-item' from SYMBOL information." 6317 (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name) 6318 (xref-make-file-location (lsp--uri-to-path uri) 6319 line 6320 character))) 6321 6322 (defun lsp--get-document-symbols () 6323 "Get document symbols. 6324 6325 If the buffer has not been modified since symbols were last 6326 retrieved, simply return the latest result. 6327 6328 Else, if the request was initiated by Imenu updating its menu-bar 6329 entry, perform it asynchronously; i.e., give Imenu the latest 6330 result and then force a refresh when a new one is available. 6331 6332 Else (e.g., due to interactive use of `imenu' or `xref'), 6333 perform the request synchronously." 6334 (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick) 6335 lsp--document-symbols 6336 (let ((method "textDocument/documentSymbol") 6337 (params `(:textDocument ,(lsp--text-document-identifier))) 6338 (tick (buffer-chars-modified-tick))) 6339 (if (not lsp--document-symbols-request-async) 6340 (prog1 6341 (setq lsp--document-symbols (lsp-request method params)) 6342 (setq lsp--document-symbols-tick tick)) 6343 (lsp-request-async method params 6344 (lambda (document-symbols) 6345 (setq lsp--document-symbols document-symbols 6346 lsp--document-symbols-tick tick) 6347 (lsp--imenu-refresh)) 6348 :mode 'alive 6349 :cancel-token :document-symbols) 6350 lsp--document-symbols)))) 6351 6352 (advice-add 'imenu-update-menubar :around 6353 (lambda (oldfun &rest r) 6354 (let ((lsp--document-symbols-request-async t)) 6355 (apply oldfun r)))) 6356 6357 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position) 6358 "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION." 6359 (-let (((symbol &as &DocumentSymbol? :children?) 6360 (seq-find (-lambda ((&DocumentSymbol :range)) 6361 (lsp-point-in-range? current-position range)) 6362 document-symbols))) 6363 (if children? 6364 (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position)) 6365 (when symbol 6366 (list symbol))))) 6367 6368 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?)) 6369 "Convert a SymbolInformation to a DocumentInformation" 6370 (lsp-make-document-symbol :name name 6371 :kind kind 6372 :range (lsp:location-range location) 6373 :children? nil 6374 :deprecated? deprecated? 6375 :selection-range (lsp:location-range location) 6376 :detail? container-name?)) 6377 6378 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position) 6379 "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION." 6380 (--> symbols-informations 6381 (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range))) 6382 (when (lsp-point-in-range? current-position range) 6383 (lsp--symbol-information->document-symbol symbol))) 6384 it) 6385 (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position)) 6386 (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position))) 6387 (and (lsp--position-compare b-start-position a-start-position) 6388 (lsp--position-compare a-end-position b-end-position)))))) 6389 6390 (defun lsp--symbols->document-symbols-hierarchy (symbols) 6391 "Convert SYMBOLS to symbols-hierarchy." 6392 (when-let ((first-symbol (lsp-seq-first symbols))) 6393 (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line) 6394 :character (plist-get (lsp--cur-position) :character)))) 6395 (if (lsp-symbol-information? first-symbol) 6396 (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position) 6397 (lsp--document-symbols->document-symbols-hierarchy symbols cur-position))))) 6398 6399 (defun lsp--xref-backend () 'xref-lsp) 6400 6401 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp))) 6402 (propertize (or (thing-at-point 'symbol) "") 6403 'identifier-at-point t)) 6404 6405 (defun lsp--xref-elements-index (symbols path) 6406 (-mapcat 6407 (-lambda (sym) 6408 (pcase-exhaustive sym 6409 ((DocumentSymbol :name :children? :selection-range (Range :start)) 6410 (cons (cons (concat path name) 6411 (lsp--position-to-point start)) 6412 (lsp--xref-elements-index children? (concat path name " / ")))) 6413 ((SymbolInformation :name :location (Location :range (Range :start))) 6414 (list (cons (concat path name) 6415 (lsp--position-to-point start)))))) 6416 symbols)) 6417 6418 (defvar-local lsp--symbols-cache nil) 6419 6420 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp))) 6421 (if (lsp--find-workspaces-for "textDocument/documentSymbol") 6422 (progn 6423 (setq lsp--symbols-cache (lsp--xref-elements-index 6424 (lsp--get-document-symbols) nil)) 6425 lsp--symbols-cache) 6426 (list (propertize (or (thing-at-point 'symbol) "") 6427 'identifier-at-point t)))) 6428 6429 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier) 6430 (save-excursion 6431 (unless (get-text-property 0 'identifier-at-point identifier) 6432 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6433 (user-error "Unable to find symbol %s in current document" identifier))))) 6434 (lsp--locations-to-xref-items (lsp-request "textDocument/definition" 6435 (lsp--text-document-position-params))))) 6436 6437 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier) 6438 (save-excursion 6439 (unless (get-text-property 0 'identifier-at-point identifier) 6440 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6441 (user-error "Unable to find symbol %s" identifier))))) 6442 (lsp--locations-to-xref-items (lsp-request "textDocument/references" 6443 (lsp--make-reference-params nil lsp-references-exclude-definition))))) 6444 6445 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern) 6446 (seq-map #'lsp--symbol-information-to-xref 6447 (lsp-request "workspace/symbol" `(:query ,pattern)))) 6448 6449 (defcustom lsp-rename-use-prepare t 6450 "Whether `lsp-rename' should do a prepareRename first. 6451 For some language servers, textDocument/prepareRename might be 6452 too slow, in which case this variable may be set to nil. 6453 `lsp-rename' will then use `thing-at-point' `symbol' to determine 6454 the symbol to rename at point." 6455 :group 'lsp-mode 6456 :type 'boolean) 6457 6458 (defun lsp--get-symbol-to-rename () 6459 "Get a symbol to rename and placeholder at point. 6460 Returns a cons ((START . END) . PLACEHOLDER?), and nil if 6461 renaming is generally supported but cannot be done at point. 6462 START and END are the bounds of the identifiers being renamed, 6463 while PLACEHOLDER?, is either nil or a string suggested by the 6464 language server as the initial input of a new-name prompt." 6465 (unless (lsp-feature? "textDocument/rename") 6466 (error "The connected server(s) doesn't support renaming")) 6467 (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename")) 6468 (when-let ((response 6469 (lsp-request "textDocument/prepareRename" 6470 (lsp--text-document-position-params)))) 6471 (let* ((bounds (lsp--range-to-region 6472 (if (lsp-range? response) 6473 response 6474 (lsp:prepare-rename-result-range response)))) 6475 (placeholder 6476 (and (not (lsp-range? response)) 6477 (lsp:prepare-rename-result-placeholder response)))) 6478 (cons bounds placeholder))) 6479 (when-let ((bounds (bounds-of-thing-at-point 'symbol))) 6480 (cons bounds nil)))) 6481 6482 (defface lsp-face-rename '((t :underline t)) 6483 "Face used to highlight the identifier being renamed. 6484 Renaming can be done using `lsp-rename'." 6485 :group 'lsp-mode) 6486 6487 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face)) 6488 "Face used to display the rename placeholder in. 6489 When calling `lsp-rename' interactively, this will be the face of 6490 the new name." 6491 :group 'lsp-mode) 6492 6493 (defvar lsp-rename-history '() 6494 "History for `lsp--read-rename'.") 6495 6496 (defun lsp--read-rename (at-point) 6497 "Read a new name for a `lsp-rename' at `point' from the user. 6498 AT-POINT shall be a structure as returned by 6499 `lsp--get-symbol-to-rename'. 6500 6501 Returns a string, which should be the new name for the identifier 6502 at point. If renaming cannot be done at point (as determined from 6503 AT-POINT), throw a `user-error'. 6504 6505 This function is for use in `lsp-rename' only, and shall not be 6506 relied upon." 6507 (unless at-point 6508 (user-error "`lsp-rename' is invalid here")) 6509 (-let* ((((start . end) . placeholder?) at-point) 6510 ;; Do the `buffer-substring' first to not include `lsp-face-rename' 6511 (rename-me (buffer-substring start end)) 6512 (placeholder (or placeholder? rename-me)) 6513 (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face)) 6514 6515 overlay) 6516 ;; We need unwind protect, as the user might cancel here, causing the 6517 ;; overlay to linger. 6518 (unwind-protect 6519 (progn 6520 (setq overlay (make-overlay start end)) 6521 (overlay-put overlay 'face 'lsp-face-rename) 6522 6523 (read-string (format "Rename %s to: " rename-me) placeholder 6524 'lsp-rename-history)) 6525 (and overlay (delete-overlay overlay))))) 6526 6527 (defun lsp-rename (newname) 6528 "Rename the symbol (and all references to it) under point to NEWNAME." 6529 (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename)))) 6530 (when-let ((edits (lsp-request "textDocument/rename" 6531 `( :textDocument ,(lsp--text-document-identifier) 6532 :position ,(lsp--cur-position) 6533 :newName ,newname)))) 6534 (lsp--apply-workspace-edit edits 'rename))) 6535 6536 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?) 6537 "Advice around function `rename-file'. 6538 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?. 6539 6540 This advice sends workspace/willRenameFiles before renaming file 6541 to check if server wants to apply any workspaceEdits after renamed." 6542 (if (and lsp-apply-edits-after-file-operations 6543 (lsp--send-will-rename-files-p old-name)) 6544 (let ((params (lsp-make-rename-files-params 6545 :files (vector (lsp-make-file-rename 6546 :oldUri (lsp--path-to-uri old-name) 6547 :newUri (lsp--path-to-uri new-name)))))) 6548 (when-let ((edits (lsp-request "workspace/willRenameFiles" params))) 6549 (lsp--apply-workspace-edit edits 'rename-file) 6550 (funcall old-func old-name new-name ok-if-already-exists?) 6551 (when (lsp--send-did-rename-files-p) 6552 (lsp-notify "workspace/didRenameFiles" params)))) 6553 (funcall old-func old-name new-name ok-if-already-exists?))) 6554 6555 (advice-add 'rename-file :around #'lsp--on-rename-file) 6556 6557 (defcustom lsp-xref-force-references nil 6558 "If non-nil threat everything as references(e. g. jump if only one item.)" 6559 :group 'lsp-mode 6560 :type 'boolean) 6561 6562 (defun lsp-show-xrefs (xrefs display-action references?) 6563 (unless (region-active-p) (push-mark nil t)) 6564 (if (boundp 'xref-show-definitions-function) 6565 (with-no-warnings 6566 (xref-push-marker-stack) 6567 (funcall (if (and references? (not lsp-xref-force-references)) 6568 xref-show-xrefs-function 6569 xref-show-definitions-function) 6570 (-const xrefs) 6571 `((window . ,(selected-window)) 6572 (display-action . ,display-action) 6573 ,(if (and references? (not lsp-xref-force-references)) 6574 `(auto-jump . ,xref-auto-jump-to-first-xref) 6575 `(auto-jump . ,xref-auto-jump-to-first-definition))))) 6576 (xref--show-xrefs xrefs display-action))) 6577 6578 (cl-defmethod seq-empty-p ((ht hash-table)) 6579 "Function `seq-empty-p' for hash-table." 6580 (hash-table-empty-p ht)) 6581 6582 (cl-defun lsp-find-locations (method &optional extra &key display-action references?) 6583 "Send request named METHOD and get cross references of the symbol under point. 6584 EXTRA is a plist of extra parameters. 6585 REFERENCES? t when METHOD returns references." 6586 (let ((loc (lsp-request method 6587 (append (lsp--text-document-position-params) extra)))) 6588 (if (seq-empty-p loc) 6589 (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) "")) 6590 (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?)))) 6591 6592 (cl-defun lsp-find-declaration (&key display-action) 6593 "Find declarations of the symbol under point." 6594 (interactive) 6595 (lsp-find-locations "textDocument/declaration" nil :display-action display-action)) 6596 6597 (cl-defun lsp-find-definition (&key display-action) 6598 "Find definitions of the symbol under point." 6599 (interactive) 6600 (lsp-find-locations "textDocument/definition" nil :display-action display-action)) 6601 6602 (defun lsp-find-definition-mouse (click) 6603 "Click to start `lsp-find-definition' at clicked point." 6604 (interactive "e") 6605 (let* ((ec (event-start click)) 6606 (p1 (posn-point ec)) 6607 (w1 (posn-window ec))) 6608 (select-window w1) 6609 (goto-char p1) 6610 (lsp-find-definition))) 6611 6612 (cl-defun lsp-find-implementation (&key display-action) 6613 "Find implementations of the symbol under point." 6614 (interactive) 6615 (lsp-find-locations "textDocument/implementation" 6616 nil 6617 :display-action display-action 6618 :references? t)) 6619 6620 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action) 6621 "Find references of the symbol under point." 6622 (interactive "P") 6623 (lsp-find-locations "textDocument/references" 6624 (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition))))) 6625 :display-action display-action 6626 :references? t)) 6627 6628 (cl-defun lsp-find-type-definition (&key display-action) 6629 "Find type definitions of the symbol under point." 6630 (interactive) 6631 (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action)) 6632 6633 (defalias 'lsp-find-custom #'lsp-find-locations) 6634 (defalias 'lsp-goto-implementation #'lsp-find-implementation) 6635 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition) 6636 6637 (with-eval-after-load 'evil 6638 (evil-set-command-property 'lsp-find-definition :jump t) 6639 (evil-set-command-property 'lsp-find-implementation :jump t) 6640 (evil-set-command-property 'lsp-find-references :jump t) 6641 (evil-set-command-property 'lsp-find-type-definition :jump t)) 6642 6643 (defun lsp--workspace-method-supported? (check-command method capability workspace) 6644 (with-lsp-workspace workspace 6645 (if check-command 6646 (funcall check-command workspace) 6647 (or 6648 (when capability (lsp--capability capability)) 6649 (lsp--registered-capability method) 6650 (and (not capability) (not check-command)))))) 6651 6652 (defun lsp-disable-method-for-server (method server-id) 6653 "Disable METHOD for SERVER-ID." 6654 (cl-callf 6655 (lambda (reqs) 6656 (-let (((&plist :check-command :capability) reqs)) 6657 (list :check-command 6658 (lambda (workspace) 6659 (unless (-> workspace 6660 lsp--workspace-client 6661 lsp--client-server-id 6662 (eq server-id)) 6663 (lsp--workspace-method-supported? check-command 6664 method 6665 capability 6666 workspace)))))) 6667 (alist-get method lsp-method-requirements nil nil 'string=))) 6668 6669 (defun lsp--find-workspaces-for (msg-or-method) 6670 "Find all workspaces in the current project that can handle MSG." 6671 (let ((method (if (stringp msg-or-method) 6672 msg-or-method 6673 (plist-get msg-or-method :method)))) 6674 (-if-let (reqs (cdr (assoc method lsp-method-requirements))) 6675 (-let (((&plist :capability :check-command) reqs)) 6676 (-filter 6677 (-partial #'lsp--workspace-method-supported? 6678 check-command method capability) 6679 (lsp-workspaces))) 6680 (lsp-workspaces)))) 6681 6682 (defun lsp-can-execute-command? (command-name) 6683 "Returns non-nil if current language server(s) can execute COMMAND-NAME. 6684 The command is executed via `workspace/executeCommand'" 6685 (cl-position 6686 command-name 6687 (lsp:execute-command-options-commands 6688 (lsp:server-capabilities-execute-command-provider? 6689 (lsp--server-capabilities))) 6690 :test #'equal)) 6691 6692 (defalias 'lsp-feature? 'lsp--find-workspaces-for) 6693 6694 (cl-defmethod lsp-execute-command (_server _command _arguments) 6695 "Dispatch COMMAND execution." 6696 (signal 'cl-no-applicable-method nil)) 6697 6698 (defun lsp-workspace-command-execute (command &optional args) 6699 "Execute workspace COMMAND with ARGS." 6700 (condition-case-unless-debug err 6701 (let ((params (if args 6702 (list :command command :arguments args) 6703 (list :command command)))) 6704 (lsp-request "workspace/executeCommand" params)) 6705 (error 6706 (error "`workspace/executeCommand' with `%s' failed.\n\n%S" 6707 command err)))) 6708 6709 (defun lsp-send-execute-command (command &optional args) 6710 "Create and send a `workspace/executeCommand' message having command COMMAND 6711 and optional ARGS." 6712 (lsp-workspace-command-execute command args)) 6713 6714 (defalias 'lsp-point-to-position #'lsp--point-to-position) 6715 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier) 6716 (defalias 'lsp--send-execute-command #'lsp-send-execute-command) 6717 (defalias 'lsp-on-open #'lsp--text-document-did-open) 6718 (defalias 'lsp-on-save #'lsp--text-document-did-save) 6719 6720 (defun lsp--set-configuration (settings) 6721 "Set the SETTINGS for the lsp server." 6722 (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings))) 6723 6724 (defun lsp-current-buffer () 6725 (or lsp--virtual-buffer 6726 (current-buffer))) 6727 6728 (defun lsp-buffer-live-p (buffer-id) 6729 (if-let ((buffer-live (plist-get buffer-id :buffer-live?))) 6730 (funcall buffer-live buffer-id) 6731 (buffer-live-p buffer-id))) 6732 6733 (defun lsp--on-set-visited-file-name (old-func &rest args) 6734 "Advice around function `set-visited-file-name'. 6735 6736 This advice sends textDocument/didClose for the old file and 6737 textDocument/didOpen for the new file." 6738 (when lsp--cur-workspace 6739 (lsp--text-document-did-close t)) 6740 (prog1 (apply old-func args) 6741 (when lsp--cur-workspace 6742 (lsp--text-document-did-open)))) 6743 6744 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name) 6745 6746 (defcustom lsp-flush-delayed-changes-before-next-message t 6747 "If non-nil send the document changes update before sending other messages. 6748 6749 If nil, and `lsp-debounce-full-sync-notifications' is non-nil, 6750 change notifications will be throttled by 6751 `lsp-debounce-full-sync-notifications-interval' regardless of 6752 other messages." 6753 :group 'lsp-mode 6754 :type 'boolean) 6755 6756 (defvar lsp--not-flushing-delayed-changes t) 6757 6758 (defun lsp--send-no-wait (message proc) 6759 "Send MESSAGE to PROC without waiting for further output." 6760 6761 (when (and lsp--not-flushing-delayed-changes 6762 lsp-flush-delayed-changes-before-next-message) 6763 (let ((lsp--not-flushing-delayed-changes nil)) 6764 (lsp--flush-delayed-changes))) 6765 (lsp-process-send proc message)) 6766 6767 (define-error 'lsp-parse-error 6768 "Error parsing message from language server" 'lsp-error) 6769 (define-error 'lsp-unknown-message-type 6770 "Unknown message type" '(lsp-error lsp-parse-error)) 6771 (define-error 'lsp-unknown-json-rpc-version 6772 "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error)) 6773 (define-error 'lsp-no-content-length 6774 "Content-Length header missing in message" '(lsp-error lsp-parse-error)) 6775 (define-error 'lsp-invalid-header-name 6776 "Invalid header name" '(lsp-error lsp-parse-error)) 6777 6778 ;; id method 6779 ;; x x request 6780 ;; x . response 6781 ;; . x notification 6782 (defun lsp--get-message-type (json-data) 6783 "Get the message type from JSON-DATA." 6784 (if (lsp:json-message-id? json-data) 6785 (if (lsp:json-message-error? json-data) 6786 'response-error 6787 (if (lsp:json-message-method? json-data) 6788 'request 6789 'response)) 6790 'notification)) 6791 6792 (defconst lsp--default-notification-handlers 6793 (ht ("window/showMessage" #'lsp--window-show-message) 6794 ("window/logMessage" #'lsp--window-log-message) 6795 ("window/showInputBox" #'lsp--window-show-input-box) 6796 ("window/showQuickPick" #'lsp--window-show-quick-pick) 6797 ("textDocument/publishDiagnostics" #'lsp--on-diagnostics) 6798 ("textDocument/diagnosticsEnd" #'ignore) 6799 ("textDocument/diagnosticsBegin" #'ignore) 6800 ("telemetry/event" #'ignore) 6801 ("$/progress" (lambda (workspace params) 6802 (funcall lsp-progress-function workspace params))))) 6803 6804 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method)) 6805 "Call the appropriate handler for NOTIFICATION." 6806 (-let ((client (lsp--workspace-client workspace))) 6807 (when (lsp--log-io-p method) 6808 (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif) 6809 lsp--cur-workspace)) 6810 (if-let ((handler (or (gethash method (lsp--client-notification-handlers client)) 6811 (gethash method lsp--default-notification-handlers)))) 6812 (funcall handler workspace params) 6813 (when (and method (not (string-prefix-p "$" method))) 6814 (lsp-warn "Unknown notification: %s" method))))) 6815 6816 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items)) 6817 "Get section configuration. 6818 PARAMS are the `workspace/configuration' request params" 6819 (->> items 6820 (-map (-lambda ((&ConfigurationItem :section?)) 6821 (-let* ((path-parts (split-string section? "\\.")) 6822 (path-without-last (s-join "." (-slice path-parts 0 -1))) 6823 (path-parts-len (length path-parts))) 6824 (cond 6825 ((<= path-parts-len 1) 6826 (ht-get (lsp-configuration-section section?) 6827 (car-safe path-parts) 6828 (ht-create))) 6829 ((> path-parts-len 1) 6830 (when-let ((section (lsp-configuration-section path-without-last)) 6831 (keys path-parts)) 6832 (while (and keys section) 6833 (setf section (ht-get section (pop keys)))) 6834 section)))))) 6835 (apply #'vector))) 6836 6837 (defun lsp--ms-since (timestamp) 6838 "Integer number of milliseconds since TIMESTAMP. Fractions discarded." 6839 (floor (* 1000 (float-time (time-since timestamp))))) 6840 6841 (defun lsp--send-request-response (workspace recv-time request response) 6842 "Send the RESPONSE for REQUEST in WORKSPACE and log if needed." 6843 (-let* (((&JSONResponse :params :method :id) request) 6844 (process (lsp--workspace-proc workspace)) 6845 (response (lsp--make-response id response)) 6846 (req-entry (and lsp-log-io 6847 (lsp--make-log-entry method id params 'incoming-req))) 6848 (resp-entry (and lsp-log-io 6849 (lsp--make-log-entry method id response 'outgoing-resp 6850 (lsp--ms-since recv-time))))) 6851 ;; Send response to the server. 6852 (when (lsp--log-io-p method) 6853 (lsp--log-entry-new req-entry workspace) 6854 (lsp--log-entry-new resp-entry workspace)) 6855 (lsp--send-no-wait response process))) 6856 6857 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method)) 6858 "Call the appropriate handler for REQUEST, and send the return value to the 6859 server. WORKSPACE is the active workspace." 6860 (-let* ((recv-time (current-time)) 6861 (client (lsp--workspace-client workspace)) 6862 (buffers (lsp--workspace-buffers workspace)) 6863 handler 6864 (response (cond 6865 ((setq handler (gethash method (lsp--client-request-handlers client) nil)) 6866 (funcall handler workspace params)) 6867 ((setq handler (gethash method (lsp--client-async-request-handlers client) nil)) 6868 (funcall handler workspace params 6869 (-partial #'lsp--send-request-response 6870 workspace recv-time request)) 6871 'delay-response) 6872 ((equal method "client/registerCapability") 6873 (mapc #'lsp--server-register-capability 6874 (lsp:registration-params-registrations params)) 6875 (mapc (lambda (buf) 6876 (when (lsp-buffer-live-p buf) 6877 (lsp-with-current-buffer buf 6878 (lsp-unconfig-buffer) 6879 (lsp-configure-buffer)))) 6880 buffers) 6881 nil) 6882 ((equal method "window/showMessageRequest") 6883 (let ((choice (lsp--window-log-message-request params))) 6884 `(:title ,choice))) 6885 ((equal method "window/showDocument") 6886 (let ((success? (lsp--window-show-document params))) 6887 (lsp-make-show-document-result :success (or success? 6888 :json-false)))) 6889 ((equal method "client/unregisterCapability") 6890 (mapc #'lsp--server-unregister-capability 6891 (lsp:unregistration-params-unregisterations params)) 6892 (mapc (lambda (buf) 6893 (when (lsp-buffer-live-p buf) 6894 (lsp-with-current-buffer buf 6895 (lsp-unconfig-buffer) 6896 (lsp-configure-buffer)))) 6897 buffers) 6898 nil) 6899 ((equal method "workspace/applyEdit") 6900 (list :applied (condition-case err 6901 (prog1 t 6902 (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested)) 6903 (error 6904 (lsp--error "Failed to apply edits with message %s" 6905 (error-message-string err)) 6906 :json-false)))) 6907 ((equal method "workspace/configuration") 6908 (with-lsp-workspace workspace 6909 (if-let ((buf (car buffers))) 6910 (lsp-with-current-buffer buf 6911 (lsp--build-workspace-configuration-response params)) 6912 (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace) 6913 (lsp--build-workspace-configuration-response params))))) 6914 ((equal method "workspace/workspaceFolders") 6915 (let ((folders (or (-> workspace 6916 (lsp--workspace-client) 6917 (lsp--client-server-id) 6918 (gethash (lsp-session-server-id->folders (lsp-session)))) 6919 (lsp-session-folders (lsp-session))))) 6920 (->> folders 6921 (-distinct) 6922 (-map (lambda (folder) 6923 (list :uri (lsp--path-to-uri folder)))) 6924 (apply #'vector)))) 6925 ((equal method "window/workDoneProgress/create") 6926 nil ;; no specific reply, no processing required 6927 ) 6928 ((equal method "workspace/semanticTokens/refresh") 6929 (when (and lsp-semantic-tokens-enable 6930 (fboundp 'lsp--semantic-tokens-on-refresh)) 6931 (lsp--semantic-tokens-on-refresh workspace)) 6932 nil) 6933 ((equal method "workspace/codeLens/refresh") 6934 (when (and lsp-lens-enable 6935 (fboundp 'lsp--lens-on-refresh)) 6936 (lsp--lens-on-refresh workspace)) 6937 nil) 6938 (t (lsp-warn "Unknown request method: %s" method) nil)))) 6939 ;; Send response to the server. 6940 (unless (eq response 'delay-response) 6941 (lsp--send-request-response workspace recv-time request response)))) 6942 6943 (lsp-defun lsp--error-string ((&JSONError :message :code)) 6944 "Format ERR as a user friendly string." 6945 (format "Error from the Language Server: %s (%s)" 6946 message 6947 (or (car (alist-get code lsp--errors)) "Unknown error"))) 6948 6949 (defun lsp--get-body-length (headers) 6950 (let ((content-length (cdr (assoc "Content-Length" headers)))) 6951 (if content-length 6952 (string-to-number content-length) 6953 6954 ;; This usually means either the server or our parser is 6955 ;; screwed up with a previous Content-Length 6956 (error "No Content-Length header")))) 6957 6958 (defun lsp--parse-header (s) 6959 "Parse string S as a LSP (KEY . VAL) header." 6960 (let ((pos (string-match "\:" s)) 6961 key val) 6962 (unless pos 6963 (signal 'lsp-invalid-header-name (list s))) 6964 (setq key (substring s 0 pos) 6965 val (s-trim-left (substring s (+ 1 pos)))) 6966 (when (equal key "Content-Length") 6967 (cl-assert (cl-loop for c across val 6968 when (or (> c ?9) (< c ?0)) return nil 6969 finally return t) 6970 nil (format "Invalid Content-Length value: %s" val))) 6971 (cons key val))) 6972 6973 (defmacro lsp--read-json (str) 6974 "Read json string STR." 6975 (if (progn 6976 (require 'json) 6977 (fboundp 'json-parse-string)) 6978 `(json-parse-string ,str 6979 :object-type (if lsp-use-plists 6980 'plist 6981 'hash-table) 6982 :null-object nil 6983 :false-object nil) 6984 `(let ((json-array-type 'vector) 6985 (json-object-type (if lsp-use-plists 6986 'plist 6987 'hash-table)) 6988 (json-false nil)) 6989 (json-read-from-string ,str)))) 6990 6991 (defmacro lsp-json-read-buffer () 6992 "Read json from the current buffer." 6993 (if (progn 6994 (require 'json) 6995 (fboundp 'json-parse-buffer)) 6996 `(json-parse-buffer :object-type (if lsp-use-plists 6997 'plist 6998 'hash-table) 6999 :null-object nil 7000 :false-object nil) 7001 `(let ((json-array-type 'vector) 7002 (json-object-type (if lsp-use-plists 7003 'plist 7004 'hash-table)) 7005 (json-false nil)) 7006 (json-read)))) 7007 7008 (defun lsp--read-json-file (file-path) 7009 "Read json file." 7010 (-> file-path 7011 (f-read-text) 7012 (lsp--read-json))) 7013 7014 (defun lsp--parser-on-message (json-data workspace) 7015 "Called when the parser P read a complete MSG from the server." 7016 (with-demoted-errors "Error processing message %S." 7017 (with-lsp-workspace workspace 7018 (let* ((client (lsp--workspace-client workspace)) 7019 (id (--when-let (lsp:json-response-id json-data) 7020 (if (stringp it) (string-to-number it) it))) 7021 (data (lsp:json-response-result json-data))) 7022 (pcase (lsp--get-message-type json-data) 7023 ('response 7024 (cl-assert id) 7025 (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))] 7026 (when (lsp--log-io-p method) 7027 (lsp--log-entry-new 7028 (lsp--make-log-entry method id data 'incoming-resp 7029 (lsp--ms-since before-send)) 7030 workspace)) 7031 (when callback 7032 (remhash id (lsp--client-response-handlers client)) 7033 (funcall callback (lsp:json-response-result json-data))))) 7034 ('response-error 7035 (cl-assert id) 7036 (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))] 7037 (when (lsp--log-io-p method) 7038 (lsp--log-entry-new 7039 (lsp--make-log-entry method id (lsp:json-response-error-error json-data) 7040 'incoming-resp (lsp--ms-since before-send)) 7041 workspace)) 7042 (when callback 7043 (remhash id (lsp--client-response-handlers client)) 7044 (funcall callback (lsp:json-response-error-error json-data))))) 7045 ('notification 7046 (lsp--on-notification workspace json-data)) 7047 ('request (lsp--on-request workspace json-data))))))) 7048 7049 (defun lsp--create-filter-function (workspace) 7050 "Make filter for the workspace." 7051 (let ((body-received 0) 7052 leftovers body-length body chunk) 7053 (lambda (_proc input) 7054 (setf chunk (if (s-blank? leftovers) 7055 input 7056 (concat leftovers input))) 7057 7058 (let (messages) 7059 (while (not (s-blank? chunk)) 7060 (if (not body-length) 7061 ;; Read headers 7062 (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk))) 7063 ;; We've got all the headers, handle them all at once: 7064 (setf body-length (lsp--get-body-length 7065 (mapcar #'lsp--parse-header 7066 (split-string 7067 (substring-no-properties chunk 7068 (or (string-match-p "Content-Length" chunk) 7069 (error "Unable to find Content-Length header.")) 7070 body-sep-pos) 7071 "\r\n"))) 7072 body-received 0 7073 leftovers nil 7074 chunk (substring-no-properties chunk (+ body-sep-pos 4))) 7075 7076 ;; Haven't found the end of the headers yet. Save everything 7077 ;; for when the next chunk arrives and await further input. 7078 (setf leftovers chunk 7079 chunk nil)) 7080 (let* ((chunk-length (string-bytes chunk)) 7081 (left-to-receive (- body-length body-received)) 7082 (this-body (if (< left-to-receive chunk-length) 7083 (prog1 (substring-no-properties chunk 0 left-to-receive) 7084 (setf chunk (substring-no-properties chunk left-to-receive))) 7085 (prog1 chunk 7086 (setf chunk nil)))) 7087 (body-bytes (string-bytes this-body))) 7088 (push this-body body) 7089 (setf body-received (+ body-received body-bytes)) 7090 (when (>= chunk-length left-to-receive) 7091 (condition-case err 7092 (with-temp-buffer 7093 (apply #'insert 7094 (nreverse 7095 (prog1 body 7096 (setf leftovers nil 7097 body-length nil 7098 body-received nil 7099 body nil)))) 7100 (decode-coding-region (point-min) 7101 (point-max) 7102 'utf-8) 7103 (goto-char (point-min)) 7104 (push (lsp-json-read-buffer) messages)) 7105 7106 (error 7107 (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s" 7108 (concat leftovers input) 7109 err))))))) 7110 (mapc (lambda (msg) 7111 (lsp--parser-on-message msg workspace)) 7112 (nreverse messages)))))) 7113 7114 (defvar-local lsp--line-col-to-point-hash-table nil 7115 "Hash table with keys (line . col) and values that are either point positions 7116 or markers.") 7117 7118 (defcustom lsp-imenu-detailed-outline t 7119 "Whether `lsp-imenu' should include signatures. 7120 This will be ignored if the server doesn't provide the necessary 7121 information, for example if it doesn't support DocumentSymbols." 7122 :group 'lsp-imenu 7123 :type 'boolean) 7124 7125 (defcustom lsp-imenu-hide-parent-details t 7126 "Whether `lsp-imenu' should hide signatures of parent nodes." 7127 :group 'lsp-imenu 7128 :type 'boolean) 7129 7130 (defface lsp-details-face '((t :height 0.8 :inherit shadow)) 7131 "Used to display additional information throughout `lsp'. 7132 Things like line numbers, signatures, ... are considered 7133 additional information. Often, additional faces are defined that 7134 inherit from this face by default, like `lsp-signature-face', and 7135 they may be customized for finer control." 7136 :group 'lsp-mode) 7137 7138 (defface lsp-signature-face '((t :inherit lsp-details-face)) 7139 "Used to display signatures in `imenu', ...." 7140 :group 'lsp-mode) 7141 7142 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?) 7143 show-detail?) 7144 "Render INPUT0, an `&DocumentSymbol', to a string. 7145 If SHOW-DETAIL? is set, make use of its `:detail?' field (often 7146 the signature)." 7147 (let ((detail (and show-detail? (s-present? detail?) 7148 (propertize (concat " " (s-trim-left detail?)) 7149 'face 'lsp-signature-face))) 7150 (name (if deprecated? 7151 (propertize name 'face 'lsp-face-semhl-deprecated) name))) 7152 (concat name detail))) 7153 7154 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?) 7155 separator) 7156 "Render a piece of SymbolInformation. 7157 Handle :deprecated?. If SEPARATOR is non-nil, the 7158 symbol's (optional) parent, SEPARATOR and the symbol itself are 7159 concatenated." 7160 (when (and separator container-name? (not (string-empty-p container-name?))) 7161 (setq name (concat name separator container-name?))) 7162 (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name)) 7163 7164 (defun lsp--symbol-to-imenu-elem (sym) 7165 "Convert SYM to imenu element. 7166 7167 SYM is a SymbolInformation message. 7168 7169 Return a cons cell (full-name . start-point)." 7170 (let ((start-point (ht-get lsp--line-col-to-point-hash-table 7171 (lsp--get-line-and-col sym)))) 7172 (cons (lsp-render-symbol-information 7173 sym (and lsp-imenu-show-container-name 7174 lsp-imenu-container-name-separator)) 7175 start-point))) 7176 7177 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?)) 7178 "Convert SYM to hierarchical imenu elements. 7179 7180 SYM is a DocumentSymbol message. 7181 7182 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if 7183 SYM doesn't have any children. Otherwise return a cons cell with 7184 an alist 7185 7186 (\"symbol-name\" . ((\"(symbol-kind)\" . start-point) 7187 cons-cells-from-children))" 7188 (let ((filtered-children (lsp--imenu-filter-symbols children?)) 7189 (signature (lsp-render-symbol sym lsp-imenu-detailed-outline))) 7190 (if (seq-empty-p filtered-children) 7191 (cons signature 7192 (ht-get lsp--line-col-to-point-hash-table 7193 (lsp--get-line-and-col sym))) 7194 (cons signature 7195 (lsp--imenu-create-hierarchical-index filtered-children))))) 7196 7197 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind)) 7198 "Determine if SYM is for the current document and is to be shown." 7199 ;; It's a SymbolInformation or DocumentSymbol, which is always in the 7200 ;; current buffer file. 7201 (and lsp-imenu-index-symbol-kinds 7202 (numberp kind) 7203 (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup)) 7204 kind 7205 0))) 7206 (not (memql (aref lsp/symbol-kind-lookup clamped-kind) 7207 lsp-imenu-index-symbol-kinds))))) 7208 7209 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind)) 7210 "The string name of the kind of SYM." 7211 (alist-get kind lsp-symbol-kinds "Other")) 7212 7213 (defun lsp--get-line-and-col (sym) 7214 "Obtain the line and column corresponding to SYM." 7215 (-let* ((location (lsp:symbol-information-location sym)) 7216 (name-range (or (and location (lsp:location-range location)) 7217 (lsp:document-symbol-selection-range sym))) 7218 ((&Range :start (&Position :line :character)) name-range)) 7219 (cons line character))) 7220 7221 (defun lsp--collect-lines-and-cols (symbols) 7222 "Return a sorted list ((line . col) ...) of the locations of SYMBOLS." 7223 (let ((stack (mapcar 'identity symbols)) 7224 line-col-list) 7225 (while stack 7226 (let ((sym (pop stack))) 7227 (push (lsp--get-line-and-col sym) line-col-list) 7228 (unless (seq-empty-p (lsp:document-symbol-children? sym)) 7229 (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack))))) 7230 (-sort #'lsp--line-col-comparator line-col-list))) 7231 7232 (defun lsp--convert-line-col-to-points-batch (line-col-list) 7233 "Convert a sorted list of positions from line-column 7234 representation to point representation." 7235 (let ((line-col-to-point-map (ht-create)) 7236 (inhibit-field-text-motion t) 7237 (curr-line 0)) 7238 (lsp-save-restriction-and-excursion 7239 (goto-char (point-min)) 7240 (cl-loop for (line . col) in line-col-list do 7241 (forward-line (- line curr-line)) 7242 (setq curr-line line) 7243 (let ((line-end (line-end-position))) 7244 (if (or (not col) (> col (- line-end (point)))) 7245 (goto-char line-end) 7246 (forward-char col))) 7247 (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers 7248 (point-marker) 7249 (point))))) 7250 line-col-to-point-map)) 7251 7252 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2)) 7253 (or (< l1 l2) 7254 (and (= l1 l2) 7255 (cond ((and c1 c2) 7256 (< c1 c2)) 7257 (c1 t))))) 7258 7259 (defun lsp-imenu-create-uncategorized-index (symbols) 7260 "Create imenu index from document SYMBOLS. 7261 This function, unlike `lsp-imenu-create-categorized-index', does 7262 not categorize by type, but instead returns an `imenu' index 7263 corresponding to the symbol hierarchy returned by the server 7264 directly." 7265 (let* ((lsp--line-col-to-point-hash-table (-> symbols 7266 lsp--collect-lines-and-cols 7267 lsp--convert-line-col-to-points-batch))) 7268 (if (lsp--imenu-hierarchical-p symbols) 7269 (lsp--imenu-create-hierarchical-index symbols) 7270 (lsp--imenu-create-non-hierarchical-index symbols)))) 7271 7272 (defcustom lsp-imenu-symbol-kinds 7273 '((1 . "Files") 7274 (2 . "Modules") 7275 (3 . "Namespaces") 7276 (4 . "Packages") 7277 (5 . "Classes") 7278 (6 . "Methods") 7279 (7 . "Properties") 7280 (8 . "Fields") 7281 (9 . "Constructors") 7282 (10 . "Enums") 7283 (11 . "Interfaces") 7284 (12 . "Functions") 7285 (13 . "Variables") 7286 (14 . "Constants") 7287 (15 . "Strings") 7288 (16 . "Numbers") 7289 (17 . "Booleans") 7290 (18 . "Arrays") 7291 (19 . "Objects") 7292 (20 . "Keys") 7293 (21 . "Nulls") 7294 (22 . "Enum Members") 7295 (23 . "Structs") 7296 (24 . "Events") 7297 (25 . "Operators") 7298 (26 . "Type Parameters")) 7299 "`lsp-symbol-kinds', but only used by `imenu'. 7300 A new variable is needed, as it is `imenu' convention to use 7301 pluralized categories, which `lsp-symbol-kinds' doesn't. If the 7302 non-pluralized names are preferred, this can be set to 7303 `lsp-symbol-kinds'." 7304 :type '(alist :key-type integer :value-type string)) 7305 7306 (defun lsp--imenu-kind->name (kind) 7307 (alist-get kind lsp-imenu-symbol-kinds "?")) 7308 7309 (defun lsp-imenu-create-top-level-categorized-index (symbols) 7310 "Create an `imenu' index categorizing SYMBOLS by type. 7311 Only root symbols are categorized. 7312 7313 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS 7314 shall be a list of DocumentSymbols or SymbolInformation." 7315 (mapcan 7316 (-lambda ((type . symbols)) 7317 (let ((cat (lsp--imenu-kind->name type)) 7318 (symbols (lsp-imenu-create-uncategorized-index symbols))) 7319 ;; If there is no :kind (this is being defensive), or we couldn't look it 7320 ;; up, just display the symbols inline, without categories. 7321 (if cat (list (cons cat symbols)) symbols))) 7322 (sort (seq-group-by #'lsp:document-symbol-kind symbols) 7323 (-lambda ((kinda) (kindb)) (< kinda kindb))))) 7324 7325 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start))) 7326 "Convert an `&DocumentSymbol' to an `imenu' entry." 7327 (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start)) 7328 7329 (defun lsp--imenu-create-categorized-index-1 (symbols) 7330 "Returns an `imenu' index from SYMBOLS categorized by type. 7331 The result looks like this: ((\"Variables\" . (...)))." 7332 (->> 7333 symbols 7334 (mapcan 7335 (-lambda ((sym &as &DocumentSymbol :kind :children?)) 7336 (if (seq-empty-p children?) 7337 (list (list kind (lsp--symbol->imenu sym))) 7338 (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline 7339 (not lsp-imenu-hide-parent-details))))) 7340 (cons 7341 (list kind (lsp--symbol->imenu sym)) 7342 (mapcar (-lambda ((type . imenu-items)) 7343 (list type (cons parent (mapcan #'cdr imenu-items)))) 7344 (-group-by #'car (lsp--imenu-create-categorized-index-1 children?)))))))) 7345 (-group-by #'car) 7346 (mapcar 7347 (-lambda ((kind . syms)) 7348 (cons kind (mapcan #'cdr syms)))))) 7349 7350 (defun lsp--imenu-create-categorized-index (symbols) 7351 (let ((syms (lsp--imenu-create-categorized-index-1 symbols))) 7352 (dolist (sym syms) 7353 (setcar sym (lsp--imenu-kind->name (car sym)))) 7354 syms)) 7355 7356 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start)))) 7357 (cons (lsp-render-symbol-information sym nil) start)) 7358 7359 (defun lsp--imenu-create-categorized-index-flat (symbols) 7360 "Create a kind-categorized index for SymbolInformation." 7361 (mapcar (-lambda ((kind . syms)) 7362 (cons (lsp--imenu-kind->name kind) 7363 (mapcan (-lambda ((parent . children)) 7364 (let ((children (mapcar #'lsp--symbol-information->imenu children))) 7365 (if parent (list (cons parent children)) children))) 7366 (-group-by #'lsp:symbol-information-container-name? syms)))) 7367 (seq-group-by #'lsp:symbol-information-kind symbols))) 7368 7369 (defun lsp-imenu-create-categorized-index (symbols) 7370 (if (lsp--imenu-hierarchical-p symbols) 7371 (lsp--imenu-create-categorized-index symbols) 7372 (lsp--imenu-create-categorized-index-flat symbols))) 7373 7374 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index 7375 "Function that should create an `imenu' index. 7376 It will be called with a list of SymbolInformation or 7377 DocumentSymbols, whose first level is already filtered. It shall 7378 then return an appropriate `imenu' index (see 7379 `imenu-create-index-function'). 7380 7381 Note that this interface is not stable, and subject to change any 7382 time." 7383 :group 'lsp-imenu 7384 :type '(radio 7385 (const :tag "Categorize by type" 7386 lsp-imenu-create-categorized-index) 7387 (const :tag "Categorize root symbols by type" 7388 lsp-imenu-create-top-level-categorized-index) 7389 (const :tag "Uncategorized, inline entries" 7390 lsp-imenu-create-uncategorized-index) 7391 (function :tag "Custom function"))) 7392 7393 (defun lsp--imenu-create-index () 7394 "Create an `imenu' index based on the language server. 7395 Respects `lsp-imenu-index-function'." 7396 (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols)))) 7397 (funcall lsp-imenu-index-function symbols))) 7398 7399 (defun lsp--imenu-filter-symbols (symbols) 7400 "Filter out unsupported symbols from SYMBOLS." 7401 (seq-remove #'lsp--symbol-ignore symbols)) 7402 7403 (defun lsp--imenu-hierarchical-p (symbols) 7404 "Determine whether any element in SYMBOLS has children." 7405 (seq-some #'lsp-document-symbol? symbols)) 7406 7407 (defun lsp--imenu-create-non-hierarchical-index (symbols) 7408 "Create imenu index for non-hierarchical SYMBOLS. 7409 7410 SYMBOLS are a list of DocumentSymbol messages. 7411 7412 Return a nested alist keyed by symbol names. e.g. 7413 7414 ((\"SomeClass\" (\"(Class)\" . 10) 7415 (\"someField (Field)\" . 20) 7416 (\"someFunction (Function)\" . 25) 7417 (\"SomeSubClass\" (\"(Class)\" . 30) 7418 (\"someSubField (Field)\" . 35)) 7419 (\"someFunction (Function)\" . 40))" 7420 (seq-map (lambda (nested-alist) 7421 (cons (car nested-alist) 7422 (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist)))) 7423 (seq-group-by #'lsp--get-symbol-type symbols))) 7424 7425 (defun lsp--imenu-create-hierarchical-index (symbols) 7426 "Create imenu index for hierarchical SYMBOLS. 7427 7428 SYMBOLS are a list of DocumentSymbol messages. 7429 7430 Return a nested alist keyed by symbol names. e.g. 7431 7432 ((\"SomeClass\" (\"(Class)\" . 10) 7433 (\"someField (Field)\" . 20) 7434 (\"someFunction (Function)\" . 25) 7435 (\"SomeSubClass\" (\"(Class)\" . 30) 7436 (\"someSubField (Field)\" . 35)) 7437 (\"someFunction (Function)\" . 40))" 7438 (seq-map #'lsp--symbol-to-hierarchical-imenu-elem 7439 (seq-sort #'lsp--imenu-symbol-lessp symbols))) 7440 7441 (defun lsp--imenu-symbol-lessp (sym1 sym2) 7442 (let* ((compare-results (mapcar (lambda (method) 7443 (funcall (alist-get method lsp--imenu-compare-function-alist) 7444 sym1 sym2)) 7445 lsp-imenu-sort-methods)) 7446 (result (seq-find (lambda (result) 7447 (not (= result 0))) 7448 compare-results 7449 0))) 7450 (and (numberp result) (< result 0)))) 7451 7452 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left) 7453 (&SymbolInformation :kind right)) 7454 "Compare SYM1 and SYM2 by kind." 7455 (- left right)) 7456 7457 (defun lsp--imenu-compare-line-col (sym1 sym2) 7458 (if (lsp--line-col-comparator 7459 (lsp--get-line-and-col sym1) 7460 (lsp--get-line-and-col sym2)) 7461 -1 7462 1)) 7463 7464 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1) 7465 (&SymbolInformation :name name2)) 7466 "Compare SYM1 and SYM2 by name." 7467 (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2)))) 7468 (if (numberp result) result 0))) 7469 7470 (defun lsp--imenu-refresh () 7471 "Force Imenu to refresh itself." 7472 (imenu--menubar-select imenu--rescan-item)) 7473 7474 (defun lsp-enable-imenu () 7475 "Use lsp-imenu for the current buffer." 7476 (imenu--cleanup) 7477 (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index) 7478 (setq-local imenu-menubar-modified-tick -1) 7479 (setq-local imenu--index-alist nil) 7480 (when menu-bar-mode 7481 (lsp--imenu-refresh))) 7482 7483 (defun lsp-resolve-final-command (command &optional test?) 7484 "Resolve final function COMMAND." 7485 (let* ((command (lsp-resolve-value command)) 7486 (command (cl-etypecase command 7487 (list 7488 (cl-assert (seq-every-p (apply-partially #'stringp) command) nil 7489 "Invalid command list") 7490 command) 7491 (string (list command))))) 7492 (if (and (file-remote-p default-directory) (not test?)) 7493 (list shell-file-name "-c" 7494 (string-join (cons "stty raw > /dev/null;" 7495 (mapcar #'shell-quote-argument command)) 7496 " ")) 7497 command))) 7498 7499 (defun lsp-server-present? (final-command) 7500 "Check whether FINAL-COMMAND is present." 7501 (let ((binary-found? (executable-find (cl-first final-command) t))) 7502 (if binary-found? 7503 (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command)) 7504 (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command))) 7505 binary-found?)) 7506 7507 (defun lsp--value-to-string (value) 7508 "Convert VALUE to a string that can be set as value in an environment 7509 variable." 7510 (cond 7511 ((stringp value) value) 7512 ((booleanp value) (if value 7513 "1" 7514 "0")) 7515 ((and (sequencep value) 7516 (seq-every-p #'stringp value)) (string-join value ":")) 7517 (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables")))) 7518 7519 (defun lsp--compute-process-environment (environment-fn) 7520 "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'. 7521 Ignore non-boolean keys whose value is nil." 7522 (let ((environment (if environment-fn 7523 (funcall environment-fn) 7524 nil))) 7525 (-flatten (cons (cl-loop for (key . value) in environment 7526 if (or (eval value) 7527 (eq (get value 'custom-type) 'boolean)) 7528 collect (concat key "=" (lsp--value-to-string 7529 (eval value)))) 7530 process-environment)))) 7531 7532 (defun lsp--default-directory-for-connection (&optional path) 7533 "Return path to be used for the working directory of a LSP process. 7534 7535 If `lsp-use-workspace-root-for-server-default-directory' is 7536 non-nil, uses `lsp-workspace-root' to find the directory 7537 corresponding to PATH, else returns `default-directory'." 7538 (if lsp-use-workspace-root-for-server-default-directory 7539 (lsp-workspace-root path) 7540 default-directory)) 7541 7542 (defun lsp--fix-remote-cmd (program) 7543 "Helper for `lsp-stdio-connection'. 7544 Originally coppied from eglot." 7545 7546 (if (file-remote-p default-directory) 7547 (list shell-file-name "-c" 7548 (string-join (cons "stty raw > /dev/null;" 7549 (mapcar #'shell-quote-argument program)) 7550 " ")) 7551 program)) 7552 7553 (defvar tramp-use-ssh-controlmaster-options) 7554 (defvar tramp-ssh-controlmaster-options) 7555 7556 (defun lsp-stdio-connection (command &optional test-command) 7557 "Returns a connection property list using COMMAND. 7558 COMMAND can be: A string, denoting the command to launch the 7559 language server. A list of strings, denoting an executable with 7560 its command line arguments. A function, that either returns a 7561 string or a list of strings. In all cases, the launched language 7562 server should send and receive messages on standard I/O. 7563 TEST-COMMAND is a function with no arguments which returns 7564 whether the command is present or not. When not specified 7565 `lsp-mode' will check whether the first element of the list 7566 returned by COMMAND is available via `executable-find'" 7567 (cl-check-type command (or string 7568 function 7569 (and list 7570 (satisfies (lambda (l) 7571 (seq-every-p (lambda (el) 7572 (stringp el)) 7573 l)))))) 7574 (list :connect (lambda (filter sentinel name environment-fn workspace) 7575 (if (and (functionp 'json-rpc-connection) 7576 (not (file-remote-p default-directory))) 7577 (lsp-json-rpc-connection workspace (lsp-resolve-final-command command)) 7578 (let ((final-command (lsp-resolve-final-command command)) 7579 (process-name (generate-new-buffer-name name)) 7580 (process-environment 7581 (lsp--compute-process-environment environment-fn))) 7582 (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name))) 7583 (default-directory (lsp--default-directory-for-connection)) 7584 (tramp-use-ssh-controlmaster-options 'suppress) 7585 (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none") 7586 (proc (make-process 7587 :name process-name 7588 :connection-type 'pipe 7589 :buffer (format "*%s*" process-name) 7590 :coding 'no-conversion 7591 :command final-command 7592 :filter filter 7593 :sentinel sentinel 7594 :stderr stderr-buf 7595 :noquery t 7596 :file-handler t))) 7597 (set-process-query-on-exit-flag proc nil) 7598 (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) 7599 (with-current-buffer (get-buffer stderr-buf) 7600 ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc. 7601 (special-mode)) 7602 (cons proc proc))))) 7603 :test? (or 7604 test-command 7605 (lambda () 7606 (lsp-server-present? (lsp-resolve-final-command command t)))))) 7607 7608 (defun lsp--open-network-stream (host port name) 7609 "Open network stream to HOST:PORT. 7610 NAME will be passed to `open-network-stream'. 7611 RETRY-COUNT is the number of the retries. 7612 SLEEP-INTERVAL is the sleep interval between each retry." 7613 (let* ((retries 0) 7614 (sleep-interval 0.01) 7615 (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval)) 7616 connection) 7617 (while (and (not connection) (< retries number-of-retries)) 7618 (condition-case err 7619 (setq connection (open-network-stream name nil host port 7620 :type 'plain 7621 :coding 'no-conversion)) 7622 (file-error 7623 (let ((inhibit-message t)) 7624 (lsp--warn "Failed to connect to %s:%s with error message %s" 7625 host 7626 port 7627 (error-message-string err)) 7628 (sleep-for sleep-interval) 7629 (cl-incf retries))))) 7630 (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port)))) 7631 7632 (defun lsp--port-available (host port) 7633 "Return non-nil if HOST and PORT are available." 7634 (condition-case _err 7635 (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain)) 7636 (file-error t))) 7637 7638 (defun lsp--find-available-port (host starting-port) 7639 "Find available port on HOST starting from STARTING-PORT." 7640 (let ((port starting-port)) 7641 (while (not (lsp--port-available host port)) 7642 (cl-incf port)) 7643 port)) 7644 7645 (defun lsp-tcp-connection (command-fn) 7646 "Returns a connection property list similar to `lsp-stdio-connection'. 7647 COMMAND-FN can only be a function that takes a single argument, a 7648 port number. It should return a command for launches a language server 7649 process listening for TCP connections on the provided port." 7650 (cl-check-type command-fn function) 7651 (list 7652 :connect (lambda (filter sentinel name environment-fn _workspace) 7653 (let* ((host "localhost") 7654 (port (lsp--find-available-port host (cl-incf lsp--tcp-port))) 7655 (command (funcall command-fn port)) 7656 (final-command (if (consp command) command (list command))) 7657 (_ (unless (lsp-server-present? final-command) 7658 (user-error (format "Couldn't find executable %s" (cl-first final-command))))) 7659 (process-environment 7660 (lsp--compute-process-environment environment-fn)) 7661 (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion 7662 :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t)) 7663 (tcp-proc (lsp--open-network-stream host port (concat name "::tcp")))) 7664 7665 ;; TODO: Same :noquery issue (see above) 7666 (set-process-query-on-exit-flag proc nil) 7667 (set-process-query-on-exit-flag tcp-proc nil) 7668 (set-process-filter tcp-proc filter) 7669 (cons tcp-proc proc))) 7670 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7671 7672 (defalias 'lsp-tcp-server 'lsp-tcp-server-command) 7673 7674 (defun lsp-tcp-server-command (command-fn) 7675 "Create tcp server connection. 7676 In this mode Emacs is TCP server and the language server connects 7677 to it. COMMAND is function with one parameter(the port) and it 7678 should return the command to start the LS server." 7679 (cl-check-type command-fn function) 7680 (list 7681 :connect (lambda (filter sentinel name environment-fn _workspace) 7682 (let* (tcp-client-connection 7683 (tcp-server (make-network-process :name (format "*tcp-server-%s*" name) 7684 :buffer (format "*tcp-server-%s*" name) 7685 :family 'ipv4 7686 :service lsp--tcp-server-port 7687 :sentinel (lambda (proc _string) 7688 (lsp-log "Language server %s is connected." name) 7689 (setf tcp-client-connection proc)) 7690 :server 't)) 7691 (port (process-contact tcp-server :service)) 7692 (final-command (funcall command-fn port)) 7693 (process-environment 7694 (lsp--compute-process-environment environment-fn)) 7695 (cmd-proc (make-process :name name 7696 :connection-type 'pipe 7697 :coding 'no-conversion 7698 :command final-command 7699 :stderr (format "*tcp-server-%s*::stderr" name) 7700 :noquery t))) 7701 (let ((retries 0)) 7702 ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds) 7703 (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds))) 7704 (lsp--info "Waiting for connection for %s, retries: %s" name retries) 7705 (sit-for 0.500) 7706 (cl-incf retries))) 7707 7708 (unless tcp-client-connection 7709 (condition-case nil (delete-process tcp-server) (error)) 7710 (condition-case nil (delete-process cmd-proc) (error)) 7711 (error "Failed to create connection to %s on port %s" name port)) 7712 (lsp--info "Successfully connected to %s" name) 7713 7714 (set-process-query-on-exit-flag cmd-proc nil) 7715 (set-process-query-on-exit-flag tcp-client-connection nil) 7716 (set-process-query-on-exit-flag tcp-server nil) 7717 7718 (set-process-filter tcp-client-connection filter) 7719 (set-process-sentinel tcp-client-connection sentinel) 7720 (cons tcp-client-connection cmd-proc))) 7721 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7722 7723 (defalias 'lsp-tramp-connection 'lsp-stdio-connection) 7724 7725 (defun lsp--auto-configure () 7726 "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed." 7727 (when (functionp 'lsp-ui-mode) 7728 (lsp-ui-mode)) 7729 7730 (if lsp-headerline-breadcrumb-enable 7731 (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode) 7732 (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)) 7733 (if lsp-modeline-code-actions-enable 7734 (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode) 7735 (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)) 7736 (if lsp-modeline-diagnostics-enable 7737 (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode) 7738 (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)) 7739 (if lsp-modeline-workspace-status-enable 7740 (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode) 7741 (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)) 7742 (if lsp-lens-enable 7743 (add-hook 'lsp-configure-hook 'lsp-lens--enable) 7744 (remove-hook 'lsp-configure-hook 'lsp-lens--enable)) 7745 (if lsp-semantic-tokens-enable 7746 (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable) 7747 (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)) 7748 7749 ;; yas-snippet config 7750 (setq-local yas-inhibit-overlay-modification-protection t)) 7751 7752 (defun lsp--restart-if-needed (workspace) 7753 "Handler restart for WORKSPACE." 7754 (when (or (eq lsp-restart 'auto-restart) 7755 (eq (lsp--workspace-shutdown-action workspace) 'restart) 7756 (and (eq lsp-restart 'interactive) 7757 (let ((query (format 7758 "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?" 7759 (lsp--workspace-print workspace)))) 7760 (y-or-n-p query)))) 7761 (--each (lsp--workspace-buffers workspace) 7762 (when (lsp-buffer-live-p it) 7763 (lsp-with-current-buffer it 7764 (if lsp--buffer-deferred 7765 (lsp-deferred) 7766 (lsp--info "Restarting LSP in buffer %s" (buffer-name)) 7767 (lsp))))))) 7768 7769 (defun lsp--update-key (table key fn) 7770 "Apply FN on value corresponding to KEY in TABLE." 7771 (let ((existing-value (gethash key table))) 7772 (if-let ((new-value (funcall fn existing-value))) 7773 (puthash key new-value table) 7774 (remhash key table)))) 7775 7776 (defun lsp--process-sentinel (workspace process exit-str) 7777 "Create the sentinel for WORKSPACE." 7778 (unless (process-live-p process) 7779 (lsp--handle-process-exit workspace exit-str))) 7780 7781 (defun lsp--handle-process-exit (workspace exit-str) 7782 (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session))) 7783 (proc (lsp--workspace-proc workspace))) 7784 (lsp--warn "%s has exited (%s)" 7785 (lsp-process-name proc) 7786 (string-trim-right (or exit-str ""))) 7787 (with-lsp-workspace workspace 7788 ;; Clean workspace related data in each of the buffers 7789 ;; in the workspace. 7790 (--each (lsp--workspace-buffers workspace) 7791 (when (lsp-buffer-live-p it) 7792 (lsp-with-current-buffer it 7793 (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces)) 7794 (lsp--uninitialize-workspace) 7795 (lsp--spinner-stop) 7796 (lsp--remove-overlays 'lsp-highlight)))) 7797 7798 ;; Cleanup session from references to the closed workspace. 7799 (--each (hash-table-keys folder->workspaces) 7800 (lsp--update-key folder->workspaces it (apply-partially 'delete workspace))) 7801 7802 (lsp-process-cleanup proc)) 7803 7804 (run-hook-with-args 'lsp-after-uninitialized-functions workspace) 7805 7806 (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown) 7807 (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace)) 7808 (lsp--restart-if-needed workspace)) 7809 (lsp--cleanup-hanging-watches))) 7810 7811 (defun lsp-workspace-folders (workspace) 7812 "Return all folders associated with WORKSPACE." 7813 (let (result) 7814 (->> (lsp-session) 7815 (lsp-session-folder->servers) 7816 (maphash (lambda (folder workspaces) 7817 (when (-contains? workspaces workspace) 7818 (push folder result))))) 7819 result)) 7820 7821 (defun lsp--start-workspace (session client-template root &optional initialization-options) 7822 "Create new workspace for CLIENT-TEMPLATE with project root ROOT. 7823 INITIALIZATION-OPTIONS are passed to initialize function. 7824 SESSION is the active session." 7825 (lsp--spinner-start) 7826 (-let* ((default-directory root) 7827 (client (copy-lsp--client client-template)) 7828 (workspace (make-lsp--workspace 7829 :root root 7830 :client client 7831 :status 'starting 7832 :buffers (list (lsp-current-buffer)) 7833 :host-root (file-remote-p root))) 7834 ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities 7835 'multi-root 'initialized-fn) client) 7836 ((proc . cmd-proc) (funcall 7837 (or (plist-get new-connection :connect) 7838 (user-error "Client %s is configured incorrectly" client)) 7839 (lsp--create-filter-function workspace) 7840 (apply-partially #'lsp--process-sentinel workspace) 7841 (format "%s" server-id) 7842 environment-fn 7843 workspace)) 7844 (workspace-folders (gethash server-id (lsp-session-server-id->folders session)))) 7845 (setf (lsp--workspace-proc workspace) proc 7846 (lsp--workspace-cmd-proc workspace) cmd-proc) 7847 7848 ;; update (lsp-session-folder->servers) depending on whether we are starting 7849 ;; multi/single folder workspace 7850 (mapc (lambda (project-root) 7851 (->> session 7852 (lsp-session-folder->servers) 7853 (gethash project-root) 7854 (cl-pushnew workspace))) 7855 (or workspace-folders (list root))) 7856 7857 (with-lsp-workspace workspace 7858 (run-hooks 'lsp-before-initialize-hook) 7859 (lsp-request-async 7860 "initialize" 7861 (append 7862 (list :processId (unless (file-remote-p (buffer-file-name)) 7863 (emacs-pid)) 7864 :rootPath (lsp-file-local-name (expand-file-name root)) 7865 :clientInfo (list :name "emacs" 7866 :version (emacs-version)) 7867 :rootUri (lsp--path-to-uri root) 7868 :capabilities (lsp--client-capabilities custom-capabilities) 7869 :initializationOptions initialization-options 7870 :workDoneToken "1") 7871 (when lsp-server-trace 7872 (list :trace lsp-server-trace)) 7873 (when multi-root 7874 (->> workspace-folders 7875 (-distinct) 7876 (-map (lambda (folder) 7877 (list :uri (lsp--path-to-uri folder) 7878 :name (f-filename folder)))) 7879 (apply 'vector) 7880 (list :workspaceFolders)))) 7881 (-lambda ((&InitializeResult :capabilities)) 7882 ;; we know that Rust Analyzer will send {} which will be parsed as null 7883 ;; when using plists 7884 (when (equal 'rust-analyzer server-id) 7885 (-> capabilities 7886 (lsp:server-capabilities-text-document-sync?) 7887 (lsp:set-text-document-sync-options-save? t))) 7888 7889 (setf (lsp--workspace-server-capabilities workspace) capabilities 7890 (lsp--workspace-status workspace) 'initialized) 7891 7892 (with-lsp-workspace workspace 7893 (lsp-notify "initialized" lsp--empty-ht)) 7894 7895 (when initialized-fn (funcall initialized-fn workspace)) 7896 7897 (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace)) 7898 (->> workspace 7899 (lsp--workspace-buffers) 7900 (mapc (lambda (buffer) 7901 (lsp-with-current-buffer buffer 7902 (lsp--open-in-workspace workspace))))) 7903 7904 (with-lsp-workspace workspace 7905 (run-hooks 'lsp-after-initialize-hook)) 7906 (lsp--info "%s initialized successfully in folders: %s" 7907 (lsp--workspace-print workspace) 7908 (lsp-workspace-folders workspace))) 7909 :mode 'detached)) 7910 workspace)) 7911 7912 (defun lsp--load-default-session () 7913 "Load default session." 7914 (setq lsp--session (or (condition-case err 7915 (lsp--read-from-file lsp-session-file) 7916 (error (lsp--error "Failed to parse the session %s, starting with clean one." 7917 (error-message-string err)) 7918 nil)) 7919 (make-lsp-session)))) 7920 7921 (defun lsp-session () 7922 "Get the session associated with the current buffer." 7923 (or lsp--session (setq lsp--session (lsp--load-default-session)))) 7924 7925 (defun lsp--client-disabled-p (buffer-major-mode client) 7926 (seq-some 7927 (lambda (entry) 7928 (pcase entry 7929 ((pred symbolp) (eq entry client)) 7930 (`(,mode . ,client-or-list) 7931 (and (eq mode buffer-major-mode) 7932 (if (listp client-or-list) 7933 (memq client client-or-list) 7934 (eq client client-or-list)))))) 7935 lsp-disabled-clients)) 7936 7937 7938 ;; download server 7939 7940 (defcustom lsp-server-install-dir (expand-file-name 7941 (locate-user-emacs-file (f-join ".cache" "lsp"))) 7942 "Directory in which the servers will be installed." 7943 :risky t 7944 :type 'directory 7945 :package-version '(lsp-mode . "6.3") 7946 :group 'lsp-mode) 7947 7948 (defcustom lsp-verify-signature t 7949 "Whether to check GPG signatures of downloaded files." 7950 :type 'boolean 7951 :package-version '(lsp-mode . "8.0.0") 7952 :group 'lsp-mode) 7953 7954 (defvar lsp--dependencies (ht)) 7955 7956 (defun lsp-dependency (name &rest definitions) 7957 "Used to specify a language server DEPENDENCY, the server 7958 executable or other required file path. Typically, the 7959 DEPENDENCY is found by locating it on the system path using 7960 `executable-find'. 7961 7962 You can explicitly call lsp-dependency in your environment to 7963 specify the absolute path to the DEPENDENCY. For example, the 7964 typescript-language-server requires both the server and the 7965 typescript compiler. If you have installed them in a team shared 7966 read-only location, you can instruct lsp-mode to use them via 7967 7968 (eval-after-load `lsp-mode 7969 `(progn 7970 (require lsp-javascript) 7971 (lsp-dependency typescript-language-server (:system ,tls-exe)) 7972 (lsp-dependency typescript (:system ,ts-js)))) 7973 7974 where tls-exe is the absolute path to the typescript-language-server 7975 executable and ts-js is the absolute path to the typescript compiler 7976 JavaScript file, tsserver.js (the *.js is required for Windows)." 7977 (ht-set lsp--dependencies name definitions)) 7978 7979 (defun lsp--server-binary-present? (client) 7980 (unless (equal (lsp--client-server-id client) 'lsp-pwsh) 7981 (condition-case () 7982 (-some-> client lsp--client-new-connection (plist-get :test?) funcall) 7983 (error nil) 7984 (args-out-of-range nil)))) 7985 7986 (define-minor-mode lsp-installation-buffer-mode 7987 "Mode used in *lsp-installation* buffers. 7988 It can be used to set-up keybindings, etc. Disabling this mode 7989 detaches the installation buffer from commands like 7990 `lsp-select-installation-buffer'." 7991 :init-value nil 7992 :lighter nil) 7993 7994 (defface lsp-installation-finished-buffer-face '((t :foreground "orange")) 7995 "Face used for finished installation buffers. 7996 Used in `lsp-select-installation-buffer'." 7997 :group 'lsp-mode) 7998 7999 (defface lsp-installation-buffer-face '((t :foreground "green")) 8000 "Face used for installation buffers still in progress. 8001 Used in `lsp-select-installation-buffer'." 8002 :group 'lsp-mode) 8003 8004 (defun lsp--installation-buffer? (buf) 8005 "Check whether BUF is an `lsp-async-start-process' buffer." 8006 (buffer-local-value 'lsp-installation-buffer-mode buf)) 8007 8008 (defun lsp-select-installation-buffer (&optional show-finished) 8009 "Interactively choose an installation buffer. 8010 If SHOW-FINISHED is set, leftover (finished) installation buffers 8011 are still shown." 8012 (interactive "P") 8013 (let ((bufs (--filter (and (lsp--installation-buffer? it) 8014 (or show-finished (get-buffer-process it))) 8015 (buffer-list)))) 8016 (pcase bufs 8017 (`nil (user-error "No installation buffers")) 8018 (`(,buf) (pop-to-buffer buf)) 8019 (bufs (pop-to-buffer (completing-read "Select installation buffer: " 8020 (--map (propertize (buffer-name it) 'face 8021 (if (get-buffer-process it) 8022 'lsp-installation-buffer-face 8023 'lsp-installation-finished-buffer-face)) 8024 bufs))))))) 8025 8026 (defun lsp-cleanup-installation-buffers () 8027 "Delete finished *lsp-installation* buffers." 8028 (interactive) 8029 (dolist (buf (buffer-list)) 8030 (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf))) 8031 (kill-buffer buf)))) 8032 8033 (defun lsp--download-status () 8034 (-some--> #'lsp--client-download-in-progress? 8035 (lsp--filter-clients it) 8036 (-map (-compose #'symbol-name #'lsp--client-server-id) it) 8037 (format "%s" it) 8038 (propertize it 'face 'success) 8039 (format " Installing following servers: %s" it) 8040 (propertize it 8041 'local-map (make-mode-line-mouse-map 8042 'mouse-1 #'lsp-select-installation-buffer) 8043 'mouse-face 'highlight))) 8044 8045 (defun lsp--install-server-internal (client &optional update?) 8046 (unless (lsp--client-download-server-fn client) 8047 (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation." 8048 (lsp--client-server-id client))) 8049 8050 (setf (lsp--client-download-in-progress? client) t) 8051 (add-to-list 'global-mode-string '(t (:eval (lsp--download-status)))) 8052 (cl-flet ((done 8053 (success? &optional error-message) 8054 ;; run with idle timer to make sure the lsp command is executed in 8055 ;; the main thread, see #2739. 8056 (run-with-timer 8057 0.0 8058 nil 8059 (lambda () 8060 (-let [(&lsp-cln 'server-id 'buffers) client] 8061 (setf (lsp--client-download-in-progress? client) nil 8062 (lsp--client-buffers client) nil) 8063 (if success? 8064 (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id 8065 (length buffers)) 8066 (lsp--error "Server %s install process failed with the following error message: %s. 8067 Check `*lsp-install*' and `*lsp-log*' buffer." 8068 server-id 8069 error-message)) 8070 (seq-do 8071 (lambda (buffer) 8072 (when (lsp-buffer-live-p buffer) 8073 (lsp-with-current-buffer buffer 8074 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8075 global-mode-string) 8076 (when success? (lsp))))) 8077 buffers) 8078 (unless (lsp--filter-clients #'lsp--client-download-in-progress?) 8079 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8080 global-mode-string))))))) 8081 (lsp--info "Download %s started." (lsp--client-server-id client)) 8082 (condition-case err 8083 (funcall 8084 (lsp--client-download-server-fn client) 8085 client 8086 (lambda () (done t)) 8087 (lambda (msg) (done nil msg)) 8088 update?) 8089 (error 8090 (done nil (error-message-string err)))))) 8091 8092 (defun lsp--require-packages () 8093 "Load `lsp-client-packages' if needed." 8094 (when (and lsp-auto-configure (not lsp--client-packages-required)) 8095 (seq-do (lambda (package) 8096 ;; loading client is slow and `lsp' can be called repeatedly 8097 (unless (featurep package) 8098 (require package nil t))) 8099 lsp-client-packages) 8100 (setq lsp--client-packages-required t))) 8101 8102 ;;;###autoload 8103 (defun lsp-install-server (update? &optional server-id) 8104 "Interactively install or re-install server. 8105 When prefix UPDATE? is t force installation even if the server is present." 8106 (interactive "P") 8107 (lsp--require-packages) 8108 (let* ((chosen-client (or (gethash server-id lsp-clients) 8109 (lsp--completing-read 8110 "Select server to install/re-install: " 8111 (or (->> lsp-clients 8112 (ht-values) 8113 (-filter (-andfn 8114 (-not #'lsp--client-download-in-progress?) 8115 #'lsp--client-download-server-fn))) 8116 (user-error "There are no servers with automatic installation")) 8117 (lambda (client) 8118 (let ((server-name (-> client lsp--client-server-id symbol-name))) 8119 (if (lsp--server-binary-present? client) 8120 (concat server-name " (Already installed)") 8121 server-name))) 8122 nil 8123 t))) 8124 (update? (or update? 8125 (and (not (lsp--client-download-in-progress? chosen-client)) 8126 (lsp--server-binary-present? chosen-client))))) 8127 (lsp--install-server-internal chosen-client update?))) 8128 8129 ;;;###autoload 8130 (defun lsp-uninstall-server (dir) 8131 "Delete a LSP server from `lsp-server-install-dir'." 8132 (interactive 8133 (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir)))) 8134 (unless (file-directory-p dir) 8135 (user-error "Couldn't find %s directory" dir)) 8136 (delete-directory dir 'recursive) 8137 (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir)))) 8138 8139 ;;;###autoload 8140 (defun lsp-uninstall-servers () 8141 "Uninstall all installed servers." 8142 (interactive) 8143 (let* ((dir lsp-server-install-dir) 8144 (servers (ignore-errors 8145 (directory-files dir t 8146 directory-files-no-dot-files-regexp)))) 8147 (if (or (not (file-directory-p dir)) (zerop (length servers))) 8148 (user-error "No servers to uninstall") 8149 (when (yes-or-no-p 8150 (format "Servers to uninstall: %d (%s), proceed? " 8151 (length servers) 8152 (mapconcat (lambda (server) 8153 (file-name-nondirectory (directory-file-name server))) 8154 servers " "))) 8155 (mapc #'lsp-uninstall-server servers) 8156 (message "All servers uninstalled"))))) 8157 8158 ;;;###autoload 8159 (defun lsp-update-server (&optional server-id) 8160 "Interactively update (reinstall) a server." 8161 (interactive) 8162 (lsp--require-packages) 8163 (let ((chosen-client (or (gethash server-id lsp-clients) 8164 (lsp--completing-read 8165 "Select server to update (if not on the list, probably you need to `lsp-install-server`): " 8166 (or (->> lsp-clients 8167 (ht-values) 8168 (-filter (-andfn 8169 (-not #'lsp--client-download-in-progress?) 8170 #'lsp--client-download-server-fn 8171 #'lsp--server-binary-present?))) 8172 (user-error "There are no servers to update")) 8173 (lambda (client) 8174 (-> client lsp--client-server-id symbol-name)) 8175 nil 8176 t)))) 8177 (lsp--install-server-internal chosen-client t))) 8178 8179 ;;;###autoload 8180 (defun lsp-update-servers () 8181 "Update (reinstall) all installed servers." 8182 (interactive) 8183 (lsp--require-packages) 8184 (mapc (lambda (client) (lsp--install-server-internal client t)) 8185 (-filter (-andfn 8186 (-not #'lsp--client-download-in-progress?) 8187 #'lsp--client-download-server-fn 8188 #'lsp--server-binary-present?) (hash-table-values lsp-clients)))) 8189 8190 ;;;###autoload 8191 (defun lsp-ensure-server (server-id) 8192 "Ensure server SERVER-ID" 8193 (lsp--require-packages) 8194 (if-let ((client (gethash server-id lsp-clients))) 8195 (unless (lsp--server-binary-present? client) 8196 (lsp--info "Server `%s' is not preset, installing..." server-id) 8197 (lsp-install-server nil server-id)) 8198 (warn "Unable to find server registration with id %s" server-id))) 8199 8200 (defun lsp-async-start-process (callback error-callback &rest command) 8201 "Start async process COMMAND with CALLBACK and ERROR-CALLBACK." 8202 (let ((name (cl-first command))) 8203 (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd) 8204 (not (null cmd))) 8205 command) 8206 " ") t 8207 (lambda (&rest _) 8208 (generate-new-buffer-name (format "*lsp-install: %s*" name)))) 8209 (lsp-installation-buffer-mode +1) 8210 (view-mode +1) 8211 (add-hook 8212 'compilation-finish-functions 8213 (lambda (_buf status) 8214 (if (string= "finished\n" status) 8215 (condition-case err 8216 (funcall callback) 8217 (error 8218 (funcall error-callback (error-message-string err)))) 8219 (funcall error-callback (s-trim-right status)))) 8220 nil t)))) 8221 8222 (defun lsp-resolve-value (value) 8223 "Resolve VALUE's value. 8224 If it is function - call it. 8225 If it is a variable - return it's value 8226 Otherwise returns value itself." 8227 (cond 8228 ((functionp value) (funcall value)) 8229 ((and (symbolp value) (boundp value)) (symbol-value value)) 8230 (value))) 8231 8232 (defvar lsp-deps-providers 8233 (list :npm (list :path #'lsp--npm-dependency-path 8234 :install #'lsp--npm-dependency-install) 8235 :cargo (list :path #'lsp--cargo-dependency-path 8236 :install #'lsp--cargo-dependency-install) 8237 :system (list :path #'lsp--system-path) 8238 :download (list :path #'lsp-download-path 8239 :install #'lsp-download-install))) 8240 8241 (defun lsp--system-path (path) 8242 "If PATH is absolute and exists return it as is. Otherwise, 8243 return the absolute path to the executable defined by PATH or 8244 nil." 8245 ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the 8246 ;; typescript-language-server. When lsp invokes the server, lsp needs to 8247 ;; supply the path to the typescript compiler, tsserver.js, as an argument. To 8248 ;; make code platform independent, one must pass the absolute path to the 8249 ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript 8250 ;; child process spawn command that is invoked by the 8251 ;; typescript-language-server). This is why we check for existence and not 8252 ;; that the path is executable. 8253 (let ((path (lsp-resolve-value path))) 8254 (cond 8255 ((and (f-absolute? path) 8256 (f-exists? path)) 8257 path) 8258 ((executable-find path t) path)))) 8259 8260 (defun lsp-package-path (dependency) 8261 "Path to the DEPENDENCY each of the registered providers." 8262 (let (path) 8263 (-first (-lambda ((provider . rest)) 8264 (setq path (-some-> lsp-deps-providers 8265 (plist-get provider) 8266 (plist-get :path) 8267 (apply rest)))) 8268 (gethash dependency lsp--dependencies)) 8269 path)) 8270 8271 (defun lsp-package-ensure (dependency callback error-callback) 8272 "Asynchronously ensure a package." 8273 (or (-first (-lambda ((provider . rest)) 8274 (-some-> lsp-deps-providers 8275 (plist-get provider) 8276 (plist-get :install) 8277 (apply (cl-list* callback error-callback rest)))) 8278 (gethash dependency lsp--dependencies)) 8279 (funcall error-callback (format "Unable to find a way to install %s" dependency)))) 8280 8281 8282 ;; npm handling 8283 8284 ;; https://docs.npmjs.com/files/folders#executables 8285 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys) 8286 "Return npm dependency PATH for PACKAGE." 8287 (let ((path (executable-find 8288 (f-join lsp-server-install-dir "npm" package 8289 (cond ((eq system-type 'windows-nt) "") 8290 (t "bin")) 8291 path) 8292 t))) 8293 (unless (and path (f-exists? path)) 8294 (error "The package %s is not installed. Unable to find %s" package path)) 8295 path)) 8296 8297 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys) 8298 (if-let ((npm-binary (executable-find "npm"))) 8299 (progn 8300 ;; Explicitly `make-directory' to work around NPM bug in 8301 ;; versions 7.0.0 through 7.4.1. See 8302 ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for 8303 ;; discussion. 8304 (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents) 8305 (lsp-async-start-process (lambda () 8306 (if (string-empty-p 8307 (string-trim (shell-command-to-string 8308 (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " ")))) 8309 (funcall callback) 8310 (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json"))))) 8311 (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx 8312 (when (f-dir-p default-directory) 8313 (lsp-async-start-process callback 8314 error-callback 8315 (executable-find "npx") 8316 "npm-install-peers"))))) 8317 error-callback 8318 npm-binary 8319 "-g" 8320 "--prefix" 8321 (f-join lsp-server-install-dir "npm" package) 8322 "install" 8323 package)) 8324 (lsp-log "Unable to install %s via `npm' because it is not present" package) 8325 nil)) 8326 8327 8328 ;; Cargo dependency handling 8329 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys) 8330 (let ((path (executable-find 8331 (f-join lsp-server-install-dir 8332 "cargo" 8333 package 8334 "bin" 8335 path) 8336 t))) 8337 (unless (and path (f-exists? path)) 8338 (error "The package %s is not installed. Unable to find %s" package path)) 8339 path)) 8340 8341 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys) 8342 (if-let ((cargo-binary (executable-find "cargo"))) 8343 (lsp-async-start-process 8344 callback 8345 error-callback 8346 cargo-binary 8347 "install" 8348 package 8349 (when git 8350 "--git") 8351 git 8352 "--root" 8353 (f-join lsp-server-install-dir "cargo" package)) 8354 (lsp-log "Unable to install %s via `cargo' because it is not present" package) 8355 nil)) 8356 8357 8358 8359 ;; Download URL handling 8360 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys) 8361 (let* ((url (lsp-resolve-value url)) 8362 (store-path (lsp-resolve-value store-path)) 8363 ;; (decompress (lsp-resolve-value decompress)) 8364 (download-path 8365 (pcase decompress 8366 (:gzip (concat store-path ".gz")) 8367 (:zip (concat store-path ".zip")) 8368 (:targz (concat store-path ".tar.gz")) 8369 (`nil store-path) 8370 (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'"))))) 8371 (make-thread 8372 (lambda () 8373 (condition-case err 8374 (progn 8375 (when (f-exists? download-path) 8376 (f-delete download-path)) 8377 (when (f-exists? store-path) 8378 (f-delete store-path)) 8379 (lsp--info "Starting to download %s to %s..." url download-path) 8380 (mkdir (f-parent download-path) t) 8381 (url-copy-file url download-path) 8382 (lsp--info "Finished downloading %s..." download-path) 8383 (when (and lsp-verify-signature asc-url pgp-key) 8384 (if (executable-find epg-gpg-program) 8385 (let ((asc-download-path (concat download-path ".asc")) 8386 (context (epg-make-context)) 8387 (fingerprint) 8388 (signature)) 8389 (when (f-exists? asc-download-path) 8390 (f-delete asc-download-path)) 8391 (lsp--info "Starting to download %s to %s..." asc-url asc-download-path) 8392 (url-copy-file asc-url asc-download-path) 8393 (lsp--info "Finished downloading %s..." asc-download-path) 8394 (epg-import-keys-from-string context pgp-key) 8395 (setq fingerprint (epg-import-status-fingerprint 8396 (car 8397 (epg-import-result-imports 8398 (epg-context-result-for context 'import))))) 8399 (lsp--info "Verifying signature %s..." asc-download-path) 8400 (epg-verify-file context asc-download-path download-path) 8401 (setq signature (car (epg-context-result-for context 'verify))) 8402 (unless (and 8403 (eq (epg-signature-status signature) 'good) 8404 (equal (epg-signature-fingerprint signature) fingerprint)) 8405 (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature)))) 8406 (lsp--warn "GPG is not installed, skipping the signature check."))) 8407 (when decompress 8408 (lsp--info "Decompressing %s..." download-path) 8409 (pcase decompress 8410 (:gzip 8411 (lsp-gunzip download-path)) 8412 (:zip (lsp-unzip download-path (f-parent store-path))) 8413 (:targz (lsp-tar-gz-decompress download-path (f-parent store-path)))) 8414 (lsp--info "Decompressed %s..." store-path)) 8415 (funcall callback)) 8416 (error (funcall error-callback err))))))) 8417 8418 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys) 8419 "Download URL and store it into STORE-PATH. 8420 8421 SET-EXECUTABLE? when non-nil change the executable flags of 8422 STORE-PATH to make it executable. BINARY-PATH can be specified 8423 when the binary to start does not match the name of the 8424 archive (e.g. when the archive has multiple files)" 8425 (let ((store-path (or (lsp-resolve-value binary-path) 8426 (lsp-resolve-value store-path)))) 8427 (cond 8428 ((executable-find store-path) store-path) 8429 ((and set-executable? (f-exists? store-path)) 8430 (set-file-modes store-path #o0700) 8431 store-path) 8432 ((f-exists? store-path) store-path)))) 8433 8434 (defun lsp--find-latest-gh-release-url (url regex) 8435 "Fetch the latest version in the releases given by URL by using REGEX." 8436 (let ((url-request-method "GET")) 8437 (with-current-buffer (url-retrieve-synchronously url) 8438 (goto-char (point-min)) 8439 (re-search-forward "\n\n" nil 'noerror) 8440 (delete-region (point-min) (point)) 8441 (let* ((json-result (lsp-json-read-buffer))) 8442 (message "Latest version found: %s" (lsp-get json-result :tag_name)) 8443 (--> json-result 8444 (lsp-get it :assets) 8445 (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it) 8446 (lsp-get it :browser_download_url)))))) 8447 8448 ;; unzip 8449 8450 (defconst lsp-ext-pwsh-script "powershell -noprofile -noninteractive \ 8451 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'" 8452 "Powershell script to unzip file.") 8453 8454 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'" 8455 "Unzip script to unzip file.") 8456 8457 (defcustom lsp-unzip-script (lambda () 8458 (cond ((executable-find "unzip") lsp-ext-unzip-script) 8459 ((executable-find "powershell") lsp-ext-pwsh-script) 8460 (t nil))) 8461 "The script to unzip." 8462 :group 'lsp-mode 8463 :type 'string 8464 :package-version '(lsp-mode . "8.0.0")) 8465 8466 (defun lsp-unzip (zip-file dest) 8467 "Unzip ZIP-FILE to DEST." 8468 (unless lsp-unzip-script 8469 (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'")) 8470 (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest))) 8471 8472 ;; gunzip 8473 8474 (defconst lsp-ext-gunzip-script "gzip -d %1$s" 8475 "Script to decompress a gzippped file with gzip.") 8476 8477 (defcustom lsp-gunzip-script (lambda () 8478 (cond ((executable-find "gzip") lsp-ext-gunzip-script) 8479 (t nil))) 8480 "The script to decompress a gzipped file. 8481 Should be a format string with one argument for the file to be decompressed 8482 in place." 8483 :group 'lsp-mode 8484 :type 'string 8485 :package-version '(lsp-mode . "8.0.0")) 8486 8487 (defun lsp-gunzip (gz-file) 8488 "Decompress GZ-FILE in place." 8489 (unless lsp-gunzip-script 8490 (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file)) 8491 (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file))) 8492 8493 ;; tar.gz decompression 8494 8495 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'" 8496 "Script to decompress a .tar.gz file.") 8497 8498 (defcustom lsp-tar-script (lambda () 8499 (cond ((executable-find "tar") lsp-ext-tar-script) 8500 (t nil))) 8501 "The script to decompress a .tar.gz file. 8502 Should be a format string with one argument for the file to be decompressed 8503 in place." 8504 :group 'lsp-mode 8505 :type 'string) 8506 8507 (defun lsp-tar-gz-decompress (targz-file dest) 8508 "Decompress TARGZ-FILE in DEST." 8509 (unless lsp-tar-script 8510 (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file)) 8511 (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest))) 8512 8513 8514 ;; VSCode marketplace 8515 8516 (defcustom lsp-vscode-ext-url 8517 "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s" 8518 "Vscode extension template url." 8519 :group 'lsp-mode 8520 :type 'string 8521 :package-version '(lsp-mode . "8.0.0")) 8522 8523 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform) 8524 "Return the URL to vscode extension. 8525 PUBLISHER is the extension publisher. 8526 NAME is the name of the extension. 8527 VERSION is the version of the extension. 8528 TARGETPLATFORM is the targetPlatform of the extension." 8529 (format lsp-vscode-ext-url publisher name version (or targetPlatform ""))) 8530 8531 8532 8533 ;; Queueing prompts 8534 8535 (defvar lsp--question-queue nil 8536 "List of questions yet to be asked by `lsp-ask-question'.") 8537 8538 (defun lsp-ask-question (question options callback) 8539 "Prompt the user to answer the QUESTION with one of the OPTIONS from the 8540 minibuffer. Once the user selects an option, the CALLBACK function will be 8541 called, passing the selected option to it. 8542 8543 If the user is currently being shown a question, the question will be stored in 8544 `lsp--question-queue', and will be asked once the user has answered the current 8545 question." 8546 (add-to-list 'lsp--question-queue `(("question" . ,question) 8547 ("options" . ,options) 8548 ("callback" . ,callback)) t) 8549 (when (eq (length lsp--question-queue) 1) 8550 (lsp--process-question-queue))) 8551 8552 (defun lsp--process-question-queue () 8553 "Take the first question from `lsp--question-queue', process it, then process 8554 the next question until the queue is empty." 8555 (-let* (((&alist "question" "options" "callback") (car lsp--question-queue)) 8556 (answer (completing-read question options nil t))) 8557 (pop lsp--question-queue) 8558 (funcall callback answer) 8559 (when lsp--question-queue 8560 (lsp--process-question-queue)))) 8561 8562 (defun lsp--supports-buffer? (client) 8563 (and 8564 ;; both file and client remote or both local 8565 (eq (---truthy? (file-remote-p (buffer-file-name))) 8566 (---truthy? (lsp--client-remote? client))) 8567 8568 ;; activation function or major-mode match. 8569 (if-let ((activation-fn (lsp--client-activation-fn client))) 8570 (funcall activation-fn (buffer-file-name) major-mode) 8571 (-contains? (lsp--client-major-modes client) major-mode)) 8572 8573 ;; check whether it is enabled if `lsp-enabled-clients' is not null 8574 (or (null lsp-enabled-clients) 8575 (or (member (lsp--client-server-id client) lsp-enabled-clients) 8576 (ignore (lsp--info "Client %s is not in lsp-enabled-clients" 8577 (lsp--client-server-id client))))) 8578 8579 ;; check whether it is not disabled. 8580 (not (lsp--client-disabled-p major-mode (lsp--client-server-id client))))) 8581 8582 (defun lsp--filter-clients (pred) 8583 (->> lsp-clients hash-table-values (-filter pred))) 8584 8585 (defun lsp--find-clients () 8586 "Find clients which can handle current buffer." 8587 (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 8588 #'lsp--server-binary-present?))) 8589 (lsp-log "Found the following clients for %s: %s" 8590 (buffer-file-name) 8591 (s-join ", " 8592 (-map (lambda (client) 8593 (format "(server-id %s, priority %s)" 8594 (lsp--client-server-id client) 8595 (lsp--client-priority client))) 8596 matching-clients))) 8597 (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients)) 8598 (selected-clients (if-let ((main-client (and main-clients 8599 (--max-by (> (lsp--client-priority it) 8600 (lsp--client-priority other)) 8601 main-clients)))) 8602 (cons main-client add-on-clients) 8603 add-on-clients))) 8604 (lsp-log "The following clients were selected based on priority: %s" 8605 (s-join ", " 8606 (-map (lambda (client) 8607 (format "(server-id %s, priority %s)" 8608 (lsp--client-server-id client) 8609 (lsp--client-priority client))) 8610 selected-clients))) 8611 selected-clients))) 8612 8613 (defun lsp-workspace-remove-all-folders() 8614 "Delete all lsp tracked folders." 8615 (interactive) 8616 (--each (lsp-session-folders (lsp-session)) 8617 (lsp-workspace-folders-remove it))) 8618 8619 (defun lsp-register-client (client) 8620 "Registers LSP client CLIENT." 8621 (let ((client-id (lsp--client-server-id client))) 8622 (puthash client-id client lsp-clients) 8623 (setplist (intern (format "lsp-%s-after-open-hook" client-id)) 8624 `( standard-value (nil) custom-type hook 8625 custom-package-version (lsp-mode . "7.0.1") 8626 variable-documentation ,(format "Hooks to run after `%s' server is run." client-id) 8627 custom-requests nil))) 8628 (when (and lsp-auto-register-remote-clients 8629 (not (lsp--client-remote? client))) 8630 (let ((remote-client (copy-lsp--client client))) 8631 (setf (lsp--client-remote? remote-client) t 8632 (lsp--client-server-id remote-client) (intern 8633 (format "%s-tramp" 8634 (lsp--client-server-id client))) 8635 ;; disable automatic download 8636 (lsp--client-download-server-fn remote-client) nil) 8637 (lsp-register-client remote-client)))) 8638 8639 (defun lsp--create-initialization-options (_session client) 8640 "Create initialization-options from SESSION and CLIENT. 8641 Add workspace folders depending on server being multiroot and 8642 session workspace folder configuration for the server." 8643 (let* ((initialization-options-or-fn (lsp--client-initialization-options client))) 8644 (if (functionp initialization-options-or-fn) 8645 (funcall initialization-options-or-fn) 8646 initialization-options-or-fn))) 8647 8648 (defvar lsp-client-settings (make-hash-table :test 'equal) 8649 "For internal use, any external users please use 8650 `lsp-register-custom-settings' function instead") 8651 8652 (defun lsp-register-custom-settings (props) 8653 "Register PROPS. 8654 PROPS is list of triple (path value boolean?) where PATH is the path to the 8655 property; VALUE can be a literal value, symbol to be evaluated, or either a 8656 function or lambda function to be called without arguments; BOOLEAN? is an 8657 optional flag that should be non-nil for boolean settings, when it is nil the 8658 property will be ignored if the VALUE is nil. 8659 8660 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))' 8661 \(note the double parentheses)" 8662 (mapc 8663 (-lambda ((path . rest)) 8664 (puthash path rest lsp-client-settings)) 8665 props)) 8666 8667 (defun lsp-region-text (region) 8668 "Get the text for REGION in current buffer." 8669 (-let (((start . end) (lsp--range-to-region region))) 8670 (buffer-substring-no-properties start end))) 8671 8672 (defun lsp-ht-set (tbl paths value) 8673 "Set nested hash table value. 8674 TBL - a hash table, PATHS is the path to the nested VALUE." 8675 (pcase paths 8676 (`(,path) (ht-set! tbl path value)) 8677 (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl) 8678 (let ((temp-tbl (ht))) 8679 (ht-set! tbl path temp-tbl) 8680 temp-tbl)))) 8681 (lsp-ht-set nested-tbl rst value))))) 8682 8683 ;; sections 8684 8685 (defalias 'defcustom-lsp 'lsp-defcustom) 8686 8687 (defmacro lsp-defcustom (symbol standard doc &rest args) 8688 "Defines `lsp-mode' server property." 8689 (declare (doc-string 3) (debug (name body)) 8690 (indent defun)) 8691 (let ((path (plist-get args :lsp-path)) 8692 (setter (intern (concat (symbol-name symbol) "--set")))) 8693 (cl-remf args :lsp-path) 8694 `(progn 8695 (lsp-register-custom-settings 8696 (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type)))))) 8697 8698 (defcustom ,symbol ,standard ,doc ,@args) 8699 8700 ;; Use a variable watcher instead of registering a `defcustom' 8701 ;; setter since `hack-local-variables' is not aware of custom 8702 ;; setters and won't invoke them. 8703 8704 (defun ,setter (sym val op _where) 8705 (when (eq op 'set) 8706 (lsp--set-custom-property sym val ,path))) 8707 8708 (add-variable-watcher ',symbol #',setter)))) 8709 8710 (defun lsp--set-custom-property (sym val path) 8711 (set sym val) 8712 (let ((section (cl-first (s-split "\\." path)))) 8713 (mapc (lambda (workspace) 8714 (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace)) 8715 section) 8716 (with-lsp-workspace workspace 8717 (lsp--set-configuration (lsp-configuration-section section))))) 8718 (lsp--session-workspaces (lsp-session))))) 8719 8720 (defun lsp-configuration-section (section) 8721 "Get settings for SECTION." 8722 (let ((ret (ht-create))) 8723 (maphash (-lambda (path (variable boolean?)) 8724 (when (s-matches? (concat (regexp-quote section) "\\..*") path) 8725 (let* ((symbol-value (-> variable 8726 lsp-resolve-value 8727 lsp-resolve-value)) 8728 (value (if (and boolean? (not symbol-value)) 8729 :json-false 8730 symbol-value))) 8731 (when (or boolean? value) 8732 (lsp-ht-set ret (s-split "\\." path) value))))) 8733 lsp-client-settings) 8734 ret)) 8735 8736 8737 (defun lsp--start-connection (session client project-root) 8738 "Initiates connection created from CLIENT for PROJECT-ROOT. 8739 SESSION is the active session." 8740 (when (lsp--client-multi-root client) 8741 (cl-pushnew project-root (gethash (lsp--client-server-id client) 8742 (lsp-session-server-id->folders session)))) 8743 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil) 8744 8745 (unwind-protect 8746 (lsp--start-workspace session client project-root (lsp--create-initialization-options session client)) 8747 (lsp--spinner-stop))) 8748 8749 ;; lsp-log-io-mode 8750 8751 (defvar lsp-log-io-mode-map 8752 (let ((map (make-sparse-keymap))) 8753 (define-key map (kbd "M-n") #'lsp-log-io-next) 8754 (define-key map (kbd "M-p") #'lsp-log-io-prev) 8755 (define-key map (kbd "k") #'lsp--erase-log-buffer) 8756 (define-key map (kbd "K") #'lsp--erase-session-log-buffers) 8757 map) 8758 "Keymap for lsp log buffer mode.") 8759 8760 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo" 8761 "Special mode for viewing IO logs.") 8762 8763 (defun lsp-workspace-show-log (workspace) 8764 "Display the log buffer of WORKSPACE." 8765 (interactive 8766 (list (if lsp-log-io 8767 (if (eq (length (lsp-workspaces)) 1) 8768 (cl-first (lsp-workspaces)) 8769 (lsp--completing-read "Workspace: " (lsp-workspaces) 8770 #'lsp--workspace-print nil t)) 8771 (user-error "IO logging is disabled")))) 8772 (pop-to-buffer (lsp--get-log-buffer-create workspace))) 8773 8774 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log) 8775 8776 (defun lsp--get-log-buffer-create (workspace) 8777 "Return the lsp log buffer of WORKSPACE, creating a new one if needed." 8778 (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8779 (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id))) 8780 (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid)))) 8781 8782 (defun lsp--erase-log-buffer (&optional all) 8783 "Delete contents of current lsp log buffer. 8784 When ALL is t, erase all log buffers of the running session." 8785 (interactive) 8786 (let* ((workspaces (lsp--session-workspaces (lsp-session))) 8787 (current-log-buffer (current-buffer))) 8788 (dolist (w workspaces) 8789 (let ((b (lsp--get-log-buffer-create w))) 8790 (when (or all (eq b current-log-buffer)) 8791 (with-current-buffer b 8792 (let ((inhibit-read-only t)) 8793 (erase-buffer)))))))) 8794 8795 (defun lsp--erase-session-log-buffers () 8796 "Erase log buffers of the running session." 8797 (interactive) 8798 (lsp--erase-log-buffer t)) 8799 8800 (defun lsp-log-io-next (arg) 8801 "Move to next log entry." 8802 (interactive "P") 8803 (ewoc-goto-next lsp--log-io-ewoc (or arg 1))) 8804 8805 (defun lsp-log-io-prev (arg) 8806 "Move to previous log entry." 8807 (interactive "P") 8808 (ewoc-goto-prev lsp--log-io-ewoc (or arg 1))) 8809 8810 8811 8812 (cl-defmethod lsp-process-id ((process process)) 8813 (process-id process)) 8814 8815 (cl-defmethod lsp-process-name ((process process)) (process-name process)) 8816 8817 (cl-defmethod lsp-process-status ((process process)) (process-status process)) 8818 8819 (cl-defmethod lsp-process-kill ((process process)) 8820 (when (process-live-p process) 8821 (kill-process process))) 8822 8823 (cl-defmethod lsp-process-send ((process process) message) 8824 (condition-case err 8825 (process-send-string process (lsp--make-message message)) 8826 (error (lsp--error "Sending to process failed with the following error: %s" 8827 (error-message-string err))))) 8828 8829 (cl-defmethod lsp-process-cleanup (process) 8830 ;; Kill standard error buffer only if the process exited normally. 8831 ;; Leave it intact otherwise for debugging purposes. 8832 (let ((buffer (-> process process-name get-buffer))) 8833 (when (and (eq (process-status process) 'exit) 8834 (zerop (process-exit-status process)) 8835 (buffer-live-p buffer)) 8836 (kill-buffer buffer)))) 8837 8838 8839 ;; native JSONRPC 8840 8841 (declare-function json-rpc "ext:json") 8842 (declare-function json-rpc-connection "ext:json") 8843 (declare-function json-rpc-send "ext:json") 8844 (declare-function json-rpc-shutdown "ext:json") 8845 (declare-function json-rpc-stderr "ext:json") 8846 (declare-function json-rpc-pid "ext:json") 8847 8848 (defvar lsp-json-rpc-thread nil) 8849 (defvar lsp-json-rpc-queue nil) 8850 (defvar lsp-json-rpc-done nil) 8851 (defvar lsp-json-rpc-mutex (make-mutex)) 8852 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex)) 8853 8854 (defun lsp-json-rpc-process-queue () 8855 (while (not lsp-json-rpc-done) 8856 (while lsp-json-rpc-queue 8857 (-let (((proc . message) (pop lsp-json-rpc-queue))) 8858 (json-rpc-send 8859 proc message 8860 :null-object nil 8861 :false-object :json-false))) 8862 (with-mutex lsp-json-rpc-mutex 8863 (condition-wait lsp-json-rpc-condition)))) 8864 8865 (cl-defmethod lsp-process-id (process) (json-rpc-pid process)) 8866 8867 (cl-defmethod lsp-process-name (_process) "TBD") 8868 8869 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process)) 8870 8871 (cl-defmethod lsp-process-send (proc message) 8872 (unless lsp-json-rpc-thread 8873 (with-current-buffer (get-buffer-create " *json-rpc*") 8874 (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*")))) 8875 8876 (with-mutex lsp-json-rpc-mutex 8877 (setq lsp-json-rpc-queue (append lsp-json-rpc-queue 8878 (list (cons proc message)))) 8879 (condition-notify lsp-json-rpc-condition))) 8880 8881 (cl-defmethod lsp-process-cleanup (_proc)) 8882 8883 (defun lsp-json-rpc-connection (workspace command) 8884 (let ((con (apply #'json-rpc-connection command)) 8885 (object-type (if lsp-use-plists 'plist 'hash-table))) 8886 (with-current-buffer (get-buffer-create " *json-rpc*") 8887 (make-thread 8888 (lambda () 8889 (json-rpc 8890 con 8891 (lambda (result err done) 8892 (run-with-timer 8893 0.0 8894 nil 8895 (lambda () 8896 (cond 8897 (result (lsp--parser-on-message result workspace)) 8898 (err (warn "Json parsing failed with the following error: %s" err)) 8899 (done (lsp--handle-process-exit workspace "")))))) 8900 :object-type object-type 8901 :null-object nil 8902 :false-object nil)) 8903 "*json-rpc-connection*")) 8904 (cons con con))) 8905 8906 (defun lsp-json-rpc-stderr () 8907 (interactive) 8908 (--when-let (pcase (lsp-workspaces) 8909 (`nil (user-error "There are no active servers in the current buffer")) 8910 (`(,workspace) workspace) 8911 (workspaces (lsp--completing-read "Select server: " 8912 workspaces 8913 'lsp--workspace-print nil t))) 8914 (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it))) 8915 (buffer (format "*stderr-%s*" (lsp--workspace-print it)) )) 8916 (with-current-buffer (get-buffer-create buffer) 8917 (with-help-window buffer 8918 (insert content)))))) 8919 8920 8921 (defun lsp--workspace-print (workspace) 8922 "Visual representation WORKSPACE." 8923 (let* ((proc (lsp--workspace-cmd-proc workspace)) 8924 (status (lsp--workspace-status workspace)) 8925 (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8926 (pid (lsp-process-id proc))) 8927 8928 (if (eq 'initialized status) 8929 (format "%s:%s" server-id pid) 8930 (format "%s:%s/%s" server-id pid status)))) 8931 8932 (defun lsp--map-tree-widget (m) 8933 "Build `tree-widget' from a hash-table or plist M." 8934 (when (lsp-structure-p m) 8935 (let (nodes) 8936 (lsp-map (lambda (k v) 8937 (push `(tree-widget 8938 :tag ,(if (lsp-structure-p v) 8939 (format "%s:" k) 8940 (format "%s: %s" k 8941 (propertize (format "%s" v) 8942 'face 8943 'font-lock-string-face))) 8944 :open t 8945 ,@(lsp--map-tree-widget v)) 8946 nodes)) 8947 m) 8948 nodes))) 8949 8950 (defun lsp-buffer-name (buffer-id) 8951 (if-let ((buffer-name (plist-get buffer-id :buffer-name))) 8952 (funcall buffer-name buffer-id) 8953 (buffer-name buffer-id))) 8954 8955 (defun lsp--render-workspace (workspace) 8956 "Tree node representation of WORKSPACE." 8957 `(tree-widget :tag ,(lsp--workspace-print workspace) 8958 :open t 8959 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face) 8960 :open t 8961 ,@(->> workspace 8962 (lsp--workspace-buffers) 8963 (--map `(tree-widget 8964 :tag ,(when (lsp-buffer-live-p it) 8965 (let ((buffer-name (lsp-buffer-name it))) 8966 (if (lsp-with-current-buffer it buffer-read-only) 8967 (propertize buffer-name 'face 'font-lock-constant-face) 8968 buffer-name))))))) 8969 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face) 8970 ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget)))) 8971 8972 (define-derived-mode lsp-browser-mode special-mode "LspBrowser" 8973 "Define mode for displaying lsp sessions." 8974 (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t))))) 8975 8976 (defun lsp-describe-session () 8977 "Describes current `lsp-session'." 8978 (interactive) 8979 (let ((session (lsp-session)) 8980 (buf (get-buffer-create "*lsp session*")) 8981 (root (lsp-workspace-root))) 8982 (with-current-buffer buf 8983 (lsp-browser-mode) 8984 (let ((inhibit-read-only t)) 8985 (erase-buffer) 8986 (--each (lsp-session-folders session) 8987 (widget-create 8988 `(tree-widget 8989 :tag ,(propertize it 'face 'font-lock-keyword-face) 8990 :open t 8991 ,@(->> session 8992 (lsp-session-folder->servers) 8993 (gethash it) 8994 (-map 'lsp--render-workspace))))))) 8995 (pop-to-buffer buf) 8996 (goto-char (point-min)) 8997 (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag) 8998 until (or (and root (string= tag root)) (eobp)) 8999 do (goto-char (next-overlay-change (point)))))) 9000 9001 (defun lsp--session-workspaces (session) 9002 "Get all workspaces that are part of the SESSION." 9003 (-> session lsp-session-folder->servers hash-table-values -flatten -uniq)) 9004 9005 (defun lsp--find-multiroot-workspace (session client project-root) 9006 "Look for a multiroot connection in SESSION created from CLIENT for 9007 PROJECT-ROOT and BUFFER-MAJOR-MODE." 9008 (when (lsp--client-multi-root client) 9009 (-when-let (multi-root-workspace (->> session 9010 (lsp--session-workspaces) 9011 (--first (eq (-> it lsp--workspace-client lsp--client-server-id) 9012 (lsp--client-server-id client))))) 9013 (with-lsp-workspace multi-root-workspace 9014 (lsp-notify "workspace/didChangeWorkspaceFolders" 9015 (lsp-make-did-change-workspace-folders-params 9016 :event (lsp-make-workspace-folders-change-event 9017 :added (vector (lsp-make-workspace-folder 9018 :uri (lsp--path-to-uri project-root) 9019 :name (f-filename project-root))) 9020 :removed [])))) 9021 9022 (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace)) 9023 (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root)) 9024 9025 (lsp--persist-session session) 9026 9027 (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace)) 9028 (lsp--open-in-workspace multi-root-workspace) 9029 9030 multi-root-workspace))) 9031 9032 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder) 9033 "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT. 9034 IGNORE-MULTI-FOLDER to ignore multi folder server." 9035 (-map (lambda (client) 9036 (or 9037 (lsp--find-workspace session client project-root) 9038 (unless ignore-multi-folder 9039 (lsp--find-multiroot-workspace session client project-root)) 9040 (lsp--start-connection session client project-root))) 9041 clients)) 9042 9043 (defun lsp--spinner-stop () 9044 "Stop the spinner in case all of the workspaces are started." 9045 (when (--all? (eq (lsp--workspace-status it) 'initialized) 9046 lsp--buffer-workspaces) 9047 (spinner-stop))) 9048 9049 (defun lsp--open-in-workspace (workspace) 9050 "Open in existing WORKSPACE." 9051 (if (eq 'initialized (lsp--workspace-status workspace)) 9052 ;; when workspace is initialized just call document did open. 9053 (progn 9054 (with-lsp-workspace workspace 9055 (when-let ((before-document-open-fn (-> workspace 9056 lsp--workspace-client 9057 lsp--client-before-file-open-fn))) 9058 (funcall before-document-open-fn workspace)) 9059 (lsp--text-document-did-open)) 9060 (lsp--spinner-stop)) 9061 ;; when it is not initialized 9062 (lsp--spinner-start) 9063 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace)))) 9064 9065 (defun lsp--find-workspace (session client project-root) 9066 "Find server connection created with CLIENT in SESSION for PROJECT-ROOT." 9067 (when-let ((workspace (->> session 9068 (lsp-session-folder->servers) 9069 (gethash project-root) 9070 (--first (eql (-> it lsp--workspace-client lsp--client-server-id) 9071 (lsp--client-server-id client)))))) 9072 (lsp--open-in-workspace workspace) 9073 workspace)) 9074 9075 (defun lsp--read-char (prompt &optional options) 9076 "Wrapper for `read-char-from-minibuffer' if Emacs +27. 9077 Fallback to `read-key' otherwise. 9078 PROMPT is the message and OPTIONS the available options." 9079 (if (fboundp 'read-char-from-minibuffer) 9080 (read-char-from-minibuffer prompt options) 9081 (read-key prompt))) 9082 9083 (defun lsp--find-root-interactively (session) 9084 "Find project interactively. 9085 Returns nil if the project should not be added to the current SESSION." 9086 (condition-case nil 9087 (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory)) 9088 (action (lsp--read-char 9089 (format 9090 "%s is not part of any project. 9091 9092 %s ==> Import project root %s 9093 %s ==> Import project by selecting root directory interactively 9094 %s ==> Import project at current directory %s 9095 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist 9096 %s ==> Do not ask again for the current project by selecting ignore path interactively 9097 %s ==> Do nothing: ask again when opening other files from the current project 9098 9099 Select action: " 9100 (propertize (buffer-name) 'face 'bold) 9101 (propertize "i" 'face 'success) 9102 (propertize project-root-suggestion 'face 'bold) 9103 (propertize "I" 'face 'success) 9104 (propertize "." 'face 'success) 9105 (propertize default-directory 'face 'bold) 9106 (propertize "d" 'face 'warning) 9107 (propertize project-root-suggestion 'face 'bold) 9108 (propertize "D" 'face 'warning) 9109 (propertize "n" 'face 'warning)) 9110 '(?i ?\r ?I ?. ?d ?D ?n)))) 9111 (cl-case action 9112 (?i project-root-suggestion) 9113 (?\r project-root-suggestion) 9114 (?I (read-directory-name "Select workspace folder to add: " 9115 (or project-root-suggestion default-directory) 9116 nil 9117 t)) 9118 (?. default-directory) 9119 (?d (push project-root-suggestion (lsp-session-folders-blocklist session)) 9120 (lsp--persist-session session) 9121 nil) 9122 (?D (push (read-directory-name "Select folder to blocklist: " 9123 (or project-root-suggestion default-directory) 9124 nil 9125 t) 9126 (lsp-session-folders-blocklist session)) 9127 (lsp--persist-session session) 9128 nil) 9129 (t nil))) 9130 (quit))) 9131 9132 (declare-function tramp-file-name-host "ext:tramp" (file) t) 9133 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault)) 9134 9135 (defun lsp--files-same-host (f1 f2) 9136 "Predicate on whether or not two files are on the same host." 9137 (or (not (or (file-remote-p f1) (file-remote-p f2))) 9138 (and (file-remote-p f1) 9139 (file-remote-p f2) 9140 (progn (require 'tramp) 9141 (equal (tramp-file-name-host (tramp-dissect-file-name f1)) 9142 (tramp-file-name-host (tramp-dissect-file-name f2))))))) 9143 9144 (defun lsp-find-session-folder (session file-name) 9145 "Look in the current SESSION for folder containing FILE-NAME." 9146 (let ((file-name-canonical (lsp-f-canonical file-name))) 9147 (->> session 9148 (lsp-session-folders) 9149 (--filter (and (lsp--files-same-host it file-name-canonical) 9150 (or (lsp-f-same? it file-name-canonical) 9151 (and (f-dir? it) 9152 (lsp-f-ancestor-of? it file-name-canonical))))) 9153 (--max-by (> (length it) 9154 (length other)))))) 9155 9156 (defun lsp-find-workspace (server-id &optional file-name) 9157 "Find workspace for SERVER-ID for FILE-NAME." 9158 (-when-let* ((session (lsp-session)) 9159 (folder->servers (lsp-session-folder->servers session)) 9160 (workspaces (if file-name 9161 (gethash (lsp-find-session-folder session file-name) folder->servers) 9162 (lsp--session-workspaces session)))) 9163 9164 (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces))) 9165 9166 (defun lsp--calculate-root (session file-name) 9167 "Calculate project root for FILE-NAME in SESSION." 9168 (and 9169 (->> session 9170 (lsp-session-folders-blocklist) 9171 (--first (and (lsp--files-same-host it file-name) 9172 (lsp-f-ancestor-of? it file-name) 9173 (prog1 t 9174 (lsp--info "File %s is in blocklisted directory %s" file-name it)))) 9175 not) 9176 (or 9177 (when lsp-auto-guess-root 9178 (lsp--suggest-project-root)) 9179 (unless lsp-guess-root-without-session 9180 (lsp-find-session-folder session file-name)) 9181 (unless lsp-auto-guess-root 9182 (when-let ((root-folder (lsp--find-root-interactively session))) 9183 (if (or (not (f-equal? root-folder (expand-file-name "~/"))) 9184 (yes-or-no-p 9185 (concat 9186 (propertize "[WARNING] " 'face 'warning) 9187 "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: 9188 9189 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/'). 9190 2. If your file is under `~/' then create a subfolder and move that file in this folder. 9191 9192 Type `No' to go back to project selection. 9193 Type `Yes' to confirm `HOME' as project root. 9194 Type `C-g' to cancel project import process and stop `lsp'"))) 9195 root-folder 9196 (lsp--calculate-root session file-name))))))) 9197 9198 (defun lsp--try-open-in-library-workspace () 9199 "Try opening current file as library file in any of the active workspace. 9200 The library folders are defined by each client for each of the active workspace." 9201 (when-let ((workspace (->> (lsp-session) 9202 (lsp--session-workspaces) 9203 ;; Sort the last active workspaces first as they are more likely to be 9204 ;; the correct ones, especially when jumping to a definition. 9205 (-sort (lambda (a _b) 9206 (-contains? lsp--last-active-workspaces a))) 9207 (--first 9208 (and (-> it lsp--workspace-client lsp--supports-buffer?) 9209 (when-let ((library-folders-fn 9210 (-> it lsp--workspace-client lsp--client-library-folders-fn))) 9211 (-first (lambda (library-folder) 9212 (lsp-f-ancestor-of? library-folder (buffer-file-name))) 9213 (funcall library-folders-fn it)))))))) 9214 (lsp--open-in-workspace workspace) 9215 (view-mode t) 9216 (lsp--info "Opening read-only library file %s." (buffer-file-name)) 9217 (list workspace))) 9218 9219 (defun lsp--persist-session (session) 9220 "Persist SESSION to `lsp-session-file'." 9221 (lsp--persist lsp-session-file (make-lsp-session 9222 :folders (lsp-session-folders session) 9223 :folders-blocklist (lsp-session-folders-blocklist session) 9224 :server-id->folders (lsp-session-server-id->folders session)))) 9225 9226 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder) 9227 "Try create opening file as a project file. 9228 When IGNORE-MULTI-FOLDER is t the lsp mode will start new 9229 language server even if there is language server which can handle 9230 current language. When IGNORE-MULTI-FOLDER is nil current file 9231 will be opened in multi folder language server if there is 9232 such." 9233 (-let ((session (lsp-session))) 9234 (-if-let (clients (if ask-for-client 9235 (list (lsp--completing-read "Select server to start: " 9236 (ht-values lsp-clients) 9237 (-compose 'symbol-name 'lsp--client-server-id) nil t)) 9238 (lsp--find-clients))) 9239 (-if-let (project-root (-some-> session 9240 (lsp--calculate-root (buffer-file-name)) 9241 (lsp-f-canonical))) 9242 (progn 9243 ;; update project roots if needed and persist the lsp session 9244 (unless (-contains? (lsp-session-folders session) project-root) 9245 (cl-pushnew project-root (lsp-session-folders session)) 9246 (lsp--persist-session session)) 9247 (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder)) 9248 (lsp--warn "%s not in project or it is blocklisted." (buffer-name)) 9249 nil) 9250 (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode) 9251 nil))) 9252 9253 (defun lsp-shutdown-workspace () 9254 "Shutdown language server." 9255 (interactive) 9256 (--when-let (pcase (lsp-workspaces) 9257 (`nil (user-error "There are no active servers in the current buffer")) 9258 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?" 9259 (lsp--workspace-print workspace))) 9260 workspace)) 9261 (workspaces (lsp--completing-read "Select server: " 9262 workspaces 9263 'lsp--workspace-print nil t))) 9264 (lsp-workspace-shutdown it))) 9265 9266 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1") 9267 9268 (defcustom lsp-auto-select-workspace t 9269 "Shutdown or restart a single workspace. 9270 If set and the current buffer has only a single workspace 9271 associated with it, `lsp-shutdown-workspace' and 9272 `lsp-restart-workspace' will act on it without asking." 9273 :type 'boolean 9274 :group 'lsp-mode) 9275 9276 (defun lsp--read-workspace () 9277 "Ask the user to select a workspace. 9278 Errors if there are none." 9279 (pcase (lsp-workspaces) 9280 (`nil (error "No workspaces associated with the current buffer")) 9281 ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace) 9282 (workspaces (lsp--completing-read "Select workspace: " workspaces 9283 #'lsp--workspace-print nil t)))) 9284 9285 (defun lsp-workspace-shutdown (workspace) 9286 "Shut the workspace WORKSPACE and the language server associated with it" 9287 (interactive (list (lsp--read-workspace))) 9288 (lsp--warn "Stopping %s" (lsp--workspace-print workspace)) 9289 (with-lsp-workspace workspace (lsp--shutdown-workspace))) 9290 9291 (defun lsp-disconnect () 9292 "Disconnect the buffer from the language server." 9293 (interactive) 9294 (lsp--text-document-did-close t) 9295 (lsp-managed-mode -1) 9296 (lsp-mode -1) 9297 (setq lsp--buffer-workspaces nil) 9298 (lsp--info "Disconnected")) 9299 9300 (defun lsp-restart-workspace () 9301 (interactive) 9302 (--when-let (pcase (lsp-workspaces) 9303 (`nil (user-error "There are no active servers in the current buffer")) 9304 (`(,workspace) workspace) 9305 (workspaces (lsp--completing-read "Select server: " 9306 workspaces 9307 'lsp--workspace-print nil t))) 9308 (lsp-workspace-restart it))) 9309 9310 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1") 9311 9312 (defun lsp-workspace-restart (workspace) 9313 "Restart the workspace WORKSPACE and the language server associated with it" 9314 (interactive (list (lsp--read-workspace))) 9315 (lsp--warn "Restarting %s" (lsp--workspace-print workspace)) 9316 (with-lsp-workspace workspace (lsp--shutdown-workspace t))) 9317 9318 ;;;###autoload 9319 (defun lsp (&optional arg) 9320 "Entry point for the server startup. 9321 When ARG is t the lsp mode will start new language server even if 9322 there is language server which can handle current language. When 9323 ARG is nil current file will be opened in multi folder language 9324 server if there is such. When `lsp' is called with prefix 9325 argument ask the user to select which language server to start." 9326 (interactive "P") 9327 9328 (lsp--require-packages) 9329 9330 (when (buffer-file-name) 9331 (let (clients 9332 (matching-clients (lsp--filter-clients 9333 (-andfn #'lsp--supports-buffer? 9334 #'lsp--server-binary-present?)))) 9335 (cond 9336 (matching-clients 9337 (when (setq lsp--buffer-workspaces 9338 (or (and 9339 ;; Don't open as library file if file is part of a project. 9340 (not (lsp-find-session-folder (lsp-session) (buffer-file-name))) 9341 (lsp--try-open-in-library-workspace)) 9342 (lsp--try-project-root-workspaces (equal arg '(4)) 9343 (and arg (not (equal arg 1)))))) 9344 (lsp-mode 1) 9345 (when lsp-auto-configure (lsp--auto-configure)) 9346 (setq lsp-buffer-uri (lsp--buffer-uri)) 9347 (lsp--info "Connected to %s." 9348 (apply 'concat (--map (format "[%s %s]" 9349 (lsp--workspace-print it) 9350 (lsp--workspace-root it)) 9351 lsp--buffer-workspaces))))) 9352 ;; look for servers which are currently being downloaded. 9353 ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9354 #'lsp--client-download-in-progress?))) 9355 (lsp--info "There are language server(%s) installation in progress. 9356 The server(s) will be started in the buffer when it has finished." 9357 (-map #'lsp--client-server-id clients)) 9358 (seq-do (lambda (client) 9359 (cl-pushnew (current-buffer) (lsp--client-buffers client))) 9360 clients)) 9361 ;; look for servers to install 9362 ((setq clients (lsp--filter-clients 9363 (-andfn #'lsp--supports-buffer? 9364 (-const lsp-enable-suggest-server-download) 9365 #'lsp--client-download-server-fn 9366 (-not #'lsp--client-download-in-progress?)))) 9367 (let ((client (lsp--completing-read 9368 (concat "Unable to find installed server supporting this file. " 9369 "The following servers could be installed automatically: ") 9370 clients 9371 (-compose #'symbol-name #'lsp--client-server-id) 9372 nil 9373 t))) 9374 (cl-pushnew (current-buffer) (lsp--client-buffers client)) 9375 (lsp--install-server-internal client))) 9376 ;; ignore other warnings 9377 ((not lsp-warn-no-matched-clients) 9378 nil) 9379 ;; automatic installation disabled 9380 ((setq clients (unless matching-clients 9381 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9382 #'lsp--client-download-server-fn 9383 (-not (-const lsp-enable-suggest-server-download)) 9384 (-not #'lsp--server-binary-present?))))) 9385 (lsp--warn "The following servers support current file but automatic download is disabled: %s 9386 \(If you have already installed the server check *lsp-log*)." 9387 (mapconcat (lambda (client) 9388 (symbol-name (lsp--client-server-id client))) 9389 clients 9390 " "))) 9391 ;; no clients present 9392 ((setq clients (unless matching-clients 9393 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9394 (-not #'lsp--server-binary-present?))))) 9395 (lsp--warn "The following servers support current file but do not have automatic installation: %s 9396 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages. 9397 \(If you have already installed the server check *lsp-log*)." 9398 (mapconcat (lambda (client) 9399 (symbol-name (lsp--client-server-id client))) 9400 clients 9401 " "))) 9402 ;; no matches 9403 ((-> #'lsp--supports-buffer? lsp--filter-clients not) 9404 (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'. 9405 This issue might be caused by: 9406 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'. 9407 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'. 9408 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/ . 9409 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/. 9410 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients'). 9411 You can customize `lsp-warn-no-matched-clients' to disable this message." 9412 major-mode major-mode major-mode)))))) 9413 9414 (defun lsp--buffer-visible-p () 9415 "Return non nil if current buffer is visible." 9416 (or (buffer-modified-p) (get-buffer-window nil t))) 9417 9418 (defun lsp--init-if-visible () 9419 "Run `lsp' for the current buffer if the buffer is visible. 9420 Returns non nil if `lsp' was run for the buffer." 9421 (when (lsp--buffer-visible-p) 9422 (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t) 9423 (lsp) 9424 t)) 9425 9426 ;;;###autoload 9427 (defun lsp-deferred () 9428 "Entry point that defers server startup until buffer is visible. 9429 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'. 9430 This avoids overloading the server with many files when starting Emacs." 9431 ;; Workspace may not be initialized yet. Use a buffer local variable to 9432 ;; remember that we deferred loading of this buffer. 9433 (setq lsp--buffer-deferred t) 9434 (let ((buffer (current-buffer))) 9435 ;; Avoid false positives as desktop-mode restores buffers by deferring 9436 ;; visibility check until the stack clears. 9437 (run-with-idle-timer 0 nil (lambda () 9438 (when (buffer-live-p buffer) 9439 (with-current-buffer buffer 9440 (unless (lsp--init-if-visible) 9441 (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t)))))))) 9442 9443 9444 9445 (defvar lsp-file-truename-cache (ht)) 9446 9447 (defmacro lsp-with-cached-filetrue-name (&rest body) 9448 "Executes BODY caching the `file-truename' calls." 9449 `(let ((old-fn (symbol-function 'file-truename))) 9450 (unwind-protect 9451 (progn 9452 (fset 'file-truename 9453 (lambda (file-name &optional counter prev-dirs) 9454 (or (gethash file-name lsp-file-truename-cache) 9455 (puthash file-name (apply old-fn (list file-name counter prev-dirs)) 9456 lsp-file-truename-cache)))) 9457 ,@body) 9458 (fset 'file-truename old-fn)))) 9459 9460 9461 (defun lsp-virtual-buffer-call (key &rest args) 9462 (when lsp--virtual-buffer 9463 (when-let ((fn (plist-get lsp--virtual-buffer key))) 9464 (apply fn args)))) 9465 9466 (defun lsp-translate-column (column) 9467 "Translate COLUMN taking into account virtual buffers." 9468 (or (lsp-virtual-buffer-call :real->virtual-char column) 9469 column)) 9470 9471 (defun lsp-translate-line (line) 9472 "Translate LINE taking into account virtual buffers." 9473 (or (lsp-virtual-buffer-call :real->virtual-line line) 9474 line)) 9475 9476 9477 ;; lsp internal validation. 9478 9479 (defmacro lsp--doctor (&rest checks) 9480 `(-let [buf (current-buffer)] 9481 (with-current-buffer (get-buffer-create "*lsp-performance*") 9482 (with-help-window (current-buffer) 9483 ,@(-map (-lambda ((msg form)) 9484 `(insert (format "%s: %s\n" ,msg 9485 (let ((res (with-current-buffer buf 9486 ,form))) 9487 (cond 9488 ((eq res :optional) (propertize "OPTIONAL" 'face 'warning)) 9489 (res (propertize "OK" 'face 'success)) 9490 (t (propertize "ERROR" 'face 'error))))))) 9491 (-partition 2 checks)))))) 9492 9493 (define-obsolete-function-alias 'lsp-diagnose 9494 'lsp-doctor "lsp-mode 8.0.0") 9495 9496 (defun lsp-doctor () 9497 "Validate performance settings." 9498 (interactive) 9499 (lsp--doctor 9500 "Checking for Native JSON support" (functionp 'json-serialize) 9501 "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max) 9502 "Check `read-process-output-max' default has been changed from 4k" 9503 (and (boundp 'read-process-output-max) 9504 (> read-process-output-max 4096)) 9505 "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)" 9506 (condition-case _err 9507 (progn (lsp--make-message (list "a" "b")) 9508 nil) 9509 (error t)) 9510 "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000) 9511 "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) 9512 "Using emacs 28+ with native compilation?" 9513 (or (and (fboundp 'native-comp-available-p) 9514 (native-comp-available-p)) 9515 :optional))) 9516 9517 (declare-function package-version-join "ext:package") 9518 (declare-function package-desc-version "ext:package") 9519 (declare-function package--alist "ext:package") 9520 9521 (defun lsp-version () 9522 "Return string describing current version of `lsp-mode'." 9523 (interactive) 9524 (unless (featurep 'package) 9525 (require 'package)) 9526 (let ((ver (format "lsp-mode %s, Emacs %s, %s" 9527 (package-version-join 9528 (package-desc-version 9529 (car (alist-get 'lsp-mode (package--alist))))) 9530 emacs-version 9531 system-type))) 9532 (if (called-interactively-p 'interactive) 9533 (lsp--info "%s" ver) 9534 ver))) 9535 9536 9537 9538 ;; org-mode/virtual-buffer 9539 9540 (declare-function org-babel-get-src-block-info "ext:ob-core") 9541 (declare-function org-do-remove-indentation "ext:org-macs") 9542 (declare-function org-src-get-lang-mode "ext:org-src") 9543 (declare-function org-element-context "ext:org-element") 9544 9545 (defun lsp--virtual-buffer-update-position () 9546 (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range)) 9547 (funcall in-range)) 9548 lsp--virtual-buffer-connections)) 9549 (unless (equal virtual-buffer lsp--virtual-buffer) 9550 (lsp-org)) 9551 (when lsp-managed-mode 9552 (lsp-managed-mode -1) 9553 (lsp-mode -1) 9554 (setq lsp--buffer-workspaces nil) 9555 (setq lsp--virtual-buffer nil) 9556 (setq lsp-buffer-uri nil) 9557 9558 ;; force refresh of diagnostics 9559 (run-hooks 'lsp-after-diagnostics-hook)))) 9560 9561 (defun lsp-virtual-buffer-on-change (start end length) 9562 "Adjust on change event to be executed against the proper language server." 9563 (let ((max-point (max end 9564 (or (plist-get lsp--before-change-vals :end) 0) 9565 (+ start length)))) 9566 (when-let ((virtual-buffer (-first (lambda (vb) 9567 (let ((lsp--virtual-buffer vb)) 9568 (and (lsp-virtual-buffer-call :in-range start) 9569 (lsp-virtual-buffer-call :in-range max-point)))) 9570 lsp--virtual-buffer-connections))) 9571 (lsp-with-current-buffer virtual-buffer 9572 (lsp-on-change start end length 9573 (lambda (&rest _) 9574 (list :range (lsp--range (list :character 0 :line 0) 9575 lsp--virtual-buffer-point-max) 9576 :text (lsp--buffer-content)))))))) 9577 9578 (defun lsp-virtual-buffer-before-change (start _end) 9579 (when-let ((virtual-buffer (-first (lambda (vb) 9580 (lsp-with-current-buffer vb 9581 (lsp-virtual-buffer-call :in-range start))) 9582 lsp--virtual-buffer-connections))) 9583 (lsp-with-current-buffer virtual-buffer 9584 (setq lsp--virtual-buffer-point-max 9585 (lsp--point-to-position (lsp-virtual-buffer-call :last-point)))))) 9586 9587 (defun lsp-patch-on-change-event () 9588 (remove-hook 'after-change-functions #'lsp-on-change t) 9589 (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t) 9590 (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t)) 9591 9592 (defun lsp-kill-virtual-buffers () 9593 (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections)) 9594 9595 (defun lsp--move-point-in-indentation (point indentation) 9596 (save-excursion 9597 (goto-char point) 9598 (if (<= point (+ (line-beginning-position) indentation)) 9599 (line-beginning-position) 9600 point))) 9601 9602 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck") 9603 (declare-function flycheck-add-mode "ext:flycheck") 9604 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics") 9605 9606 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn) 9607 9608 (defun lsp-flycheck-add-mode (mode) 9609 "Register flycheck support for MODE." 9610 (lsp-diagnostics-lsp-checker-if-needed) 9611 (unless (flycheck-checker-supports-major-mode-p 'lsp mode) 9612 (flycheck-add-mode 'lsp mode))) 9613 9614 (defun lsp-progress-spinner-type () 9615 "Retrieve the spinner type value, if value is not a symbol of `spinner-types 9616 defaults to `progress-bar." 9617 (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar)) 9618 9619 (defun lsp-org () 9620 (interactive) 9621 (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range)) 9622 (funcall in-range)) 9623 lsp--virtual-buffer-connections)) 9624 (unless (equal lsp--virtual-buffer virtual-buffer) 9625 (setq lsp--buffer-workspaces workspaces) 9626 (setq lsp--virtual-buffer virtual-buffer) 9627 (setq lsp-buffer-uri nil) 9628 (lsp-mode 1) 9629 (lsp-managed-mode 1) 9630 (lsp-patch-on-change-event)) 9631 9632 (save-excursion 9633 (-let* (virtual-buffer 9634 (wcb (lambda (f) 9635 (with-current-buffer (plist-get virtual-buffer :buffer) 9636 (-let* (((&plist :major-mode :buffer-file-name 9637 :goto-buffer :workspaces) virtual-buffer) 9638 (lsp--virtual-buffer virtual-buffer) 9639 (lsp--buffer-workspaces workspaces)) 9640 (save-excursion 9641 (funcall goto-buffer) 9642 (funcall f)))))) 9643 ((&plist :begin :end :post-blank :language) (cl-second (org-element-context))) 9644 ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light))) 9645 9646 (file-name (if file-name 9647 (f-expand file-name) 9648 (user-error "You should specify file name in the src block header."))) 9649 (begin-marker (progn 9650 (goto-char begin) 9651 (forward-line) 9652 (set-marker (make-marker) (point)))) 9653 (end-marker (progn 9654 (goto-char end) 9655 (forward-line (1- (- post-blank))) 9656 (set-marker (make-marker) (1+ (point))))) 9657 (buf (current-buffer)) 9658 (src-block (buffer-substring-no-properties begin-marker 9659 (1- end-marker))) 9660 (indentation (with-temp-buffer 9661 (insert src-block) 9662 9663 (goto-char (point-min)) 9664 (let ((indentation (current-indentation))) 9665 (plist-put lsp--virtual-buffer :indentation indentation) 9666 (org-do-remove-indentation) 9667 (goto-char (point-min)) 9668 (- indentation (current-indentation)))))) 9669 (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t) 9670 9671 (when (fboundp 'flycheck-add-mode) 9672 (lsp-flycheck-add-mode 'org-mode)) 9673 9674 (setq lsp--virtual-buffer 9675 (list 9676 :in-range (lambda (&optional point) 9677 (<= begin-marker (or point (point)) (1- end-marker))) 9678 :goto-buffer (lambda () (goto-char begin-marker)) 9679 :buffer-string 9680 (lambda () 9681 (let ((src-block (buffer-substring-no-properties 9682 begin-marker 9683 (1- end-marker)))) 9684 (with-temp-buffer 9685 (insert src-block) 9686 9687 (goto-char (point-min)) 9688 (while (not (eobp)) 9689 (delete-region (point) (if (> (+ (point) indentation) (line-end-position)) 9690 (line-end-position) 9691 (+ (point) indentation))) 9692 (forward-line)) 9693 (buffer-substring-no-properties (point-min) 9694 (point-max))))) 9695 :buffer buf 9696 :begin begin-marker 9697 :end end-marker 9698 :indentation indentation 9699 :last-point (lambda () (1- end-marker)) 9700 :cur-position (lambda () 9701 (lsp-save-restriction-and-excursion 9702 (list :line (- (lsp--cur-line) 9703 (lsp--cur-line begin-marker)) 9704 :character (let ((character (- (point) 9705 (line-beginning-position) 9706 indentation))) 9707 (if (< character 0) 9708 0 9709 character))))) 9710 :line/character->point (-lambda (line character) 9711 (-let [inhibit-field-text-motion t] 9712 (+ indentation 9713 (lsp-save-restriction-and-excursion 9714 (goto-char begin-marker) 9715 (forward-line line) 9716 (-let [line-end (line-end-position)] 9717 (if (> character (- line-end (point))) 9718 line-end 9719 (forward-char character) 9720 (point))))))) 9721 :major-mode (org-src-get-lang-mode language) 9722 :buffer-file-name file-name 9723 :buffer-uri (lsp--path-to-uri file-name) 9724 :with-current-buffer wcb 9725 :buffer-live? (lambda (_) (buffer-live-p buf)) 9726 :buffer-name (lambda (_) 9727 (propertize (format "%s(%s:%s)%s" 9728 (buffer-name buf) 9729 begin-marker 9730 end-marker 9731 language) 9732 'face 'italic)) 9733 :real->virtual-line (lambda (line) 9734 (+ line (line-number-at-pos begin-marker) -1)) 9735 :real->virtual-char (lambda (char) (+ char indentation)) 9736 :cleanup (lambda () 9737 (set-marker begin-marker nil) 9738 (set-marker end-marker nil)))) 9739 (setf virtual-buffer lsp--virtual-buffer) 9740 (puthash file-name virtual-buffer lsp--virtual-buffer-mappings) 9741 (push virtual-buffer lsp--virtual-buffer-connections) 9742 9743 ;; TODO: tangle only connected sections 9744 (add-hook 'after-save-hook 'org-babel-tangle nil t) 9745 (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t) 9746 (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t) 9747 9748 (setq lsp--buffer-workspaces 9749 (lsp-with-current-buffer virtual-buffer 9750 (lsp) 9751 (plist-put virtual-buffer :workspaces (lsp-workspaces)) 9752 (lsp-workspaces))))))) 9753 9754 (defun lsp-virtual-buffer-disconnect (virtual-buffer) 9755 (interactive (list (or 9756 lsp--virtual-buffer 9757 (when lsp--virtual-buffer-connections 9758 (lsp--completing-read "Select virtual buffer to disconnect: " 9759 lsp--virtual-buffer-connections 9760 (-lambda ((&plist :buffer-file-name)) 9761 buffer-file-name)))))) 9762 (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer) 9763 (progn 9764 (lsp-with-current-buffer virtual-buffer 9765 (lsp--text-document-did-close)) 9766 (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections)) 9767 (when (eq virtual-buffer lsp--virtual-buffer) 9768 (setf lsp--virtual-buffer nil)) 9769 (when cleanup (funcall cleanup)) 9770 (remhash file-name lsp--virtual-buffer-mappings) 9771 9772 (lsp--virtual-buffer-update-position) 9773 (lsp--info "Disconnected from buffer %s" file-name)) 9774 (lsp--error "Nothing to disconnect from?"))) 9775 9776 9777 ;; inlay hints 9778 9779 (defface lsp-inlay-hint-face 9780 '((t :inherit font-lock-comment-face)) 9781 "The face to use for the JavaScript inlays." 9782 :group 'lsp-mode 9783 :package-version '(lsp-mode . "9.0.0")) 9784 9785 (defface lsp-inlay-hint-type-face 9786 '((t :inherit lsp-inlay-hint-face)) 9787 "Face for inlay type hints (e.g. inferred variable types)." 9788 :group 'lsp-mode 9789 :package-version '(lsp-mode . "9.0.0")) 9790 9791 (defcustom lsp-inlay-hint-type-format "%s" 9792 "Format string for variable inlays (part of the inlay face)." 9793 :type '(string :tag "String") 9794 :group 'lsp-mode 9795 :package-version '(lsp-mode . "9.0.0")) 9796 9797 (defface lsp-inlay-hint-parameter-face 9798 '((t :inherit lsp-inlay-hint-face)) 9799 "Face for inlay parameter hints (e.g. function parameter names at 9800 call-site)." 9801 :group 'lsp-mode 9802 :package-version '(lsp-mode . "9.0.0")) 9803 9804 (defcustom lsp-inlay-hint-param-format "%s" 9805 "Format string for parameter inlays (part of the inlay face)." 9806 :type '(string :tag "String") 9807 :group 'lsp-mode 9808 :package-version '(lsp-mode . "9.0.0")) 9809 9810 (defcustom lsp-update-inlay-hints-on-scroll t 9811 "If non-nil update inlay hints immediately when scrolling or 9812 modifying window sizes." 9813 :type 'boolean 9814 :package-version '(lsp-mode . "9.0.0")) 9815 9816 (defun lsp--format-inlay (text kind) 9817 (cond 9818 ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text)) 9819 ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text)) 9820 (t text))) 9821 9822 (defun lsp--face-for-inlay (kind) 9823 (cond 9824 ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face) 9825 ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face) 9826 (t 'lsp-inlay-hint-face))) 9827 9828 (defun lsp--update-inlay-hints-scroll-function (window start) 9829 (lsp-update-inlay-hints start (window-end window t))) 9830 9831 (defun lsp--update-inlay-hints () 9832 (lsp-update-inlay-hints (window-start) (window-end nil t))) 9833 9834 (defun lsp--label-from-inlay-hints-response (label) 9835 "Returns a string label built from an array of 9836 InlayHintLabelParts or the argument itself if it's already a 9837 string." 9838 (cl-typecase label 9839 (string label) 9840 (vector 9841 (string-join (mapcar (lambda (part) 9842 (-let (((&InlayHintLabelPart :value) part)) 9843 value)) 9844 label))))) 9845 9846 (defun lsp-update-inlay-hints (start end) 9847 (lsp-request-async 9848 "textDocument/inlayHint" 9849 (lsp-make-inlay-hints-params 9850 :text-document (lsp--text-document-identifier) 9851 :range (lsp-make-range :start 9852 (lsp-point-to-position start) 9853 :end 9854 (lsp-point-to-position end))) 9855 (lambda (res) 9856 (lsp--remove-overlays 'lsp-inlay-hint) 9857 (dolist (hint res) 9858 (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint) 9859 (kind (or kind? lsp/inlay-hint-kind-type-hint)) 9860 (label (lsp--label-from-inlay-hints-response label)) 9861 (pos (lsp--position-to-point position)) 9862 (overlay (make-overlay pos pos nil 'front-advance 'end-advance))) 9863 (when (stringp label) 9864 (overlay-put overlay 'lsp-inlay-hint t) 9865 (overlay-put overlay 'before-string 9866 (format "%s%s%s" 9867 (if padding-left? " " "") 9868 (propertize (lsp--format-inlay label kind) 9869 'font-lock-face (lsp--face-for-inlay kind)) 9870 (if padding-right? " " ""))))))) 9871 :mode 'tick)) 9872 9873 (define-minor-mode lsp-inlay-hints-mode 9874 "Mode for displaying inlay hints." 9875 :lighter nil 9876 (cond 9877 ((and lsp-inlay-hints-mode lsp--buffer-workspaces) 9878 (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t) 9879 (when lsp-update-inlay-hints-on-scroll 9880 (add-to-list (make-local-variable 'window-scroll-functions) 9881 #'lsp--update-inlay-hints-scroll-function))) 9882 (t 9883 (lsp--remove-overlays 'lsp-inlay-hint) 9884 (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t) 9885 (setf window-scroll-functions 9886 (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions))))) 9887 9888 9889 9890 ;;;###autoload 9891 (defun lsp-start-plain () 9892 "Start `lsp-mode' using minimal configuration using the latest `melpa' version 9893 of the packages. 9894 9895 In case the major-mode that you are using for " 9896 (interactive) 9897 (let ((start-plain (make-temp-file "plain" nil ".el"))) 9898 (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el" 9899 start-plain t) 9900 (async-shell-command 9901 (format "%s -q -l %s %s" 9902 (expand-file-name invocation-name invocation-directory) 9903 start-plain 9904 (or (buffer-file-name) "")) 9905 (generate-new-buffer " *lsp-start-plain*")))) 9906 9907 9908 9909 (provide 'lsp-mode) 9910 ;;; lsp-mode.el ends here