lsp-mode.el (430695B)
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-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 (fortran-mode . "fortran") 906 (f90-mode . "fortran") 907 (elm-mode . "elm") 908 (dart-mode . "dart") 909 (erlang-mode . "erlang") 910 (dockerfile-mode . "dockerfile") 911 (dockerfile-ts-mode . "dockerfile") 912 (csharp-mode . "csharp") 913 (csharp-tree-sitter-mode . "csharp") 914 (csharp-ts-mode . "csharp") 915 (plain-tex-mode . "plaintex") 916 (context-mode . "context") 917 (cypher-mode . "cypher") 918 (latex-mode . "latex") 919 (LaTeX-mode . "latex") 920 (v-mode . "v") 921 (vhdl-mode . "vhdl") 922 (vhdl-ts-mode . "vhdl") 923 (verilog-mode . "verilog") 924 (terraform-mode . "terraform") 925 (ess-julia-mode . "julia") 926 (ess-r-mode . "r") 927 (crystal-mode . "crystal") 928 (nim-mode . "nim") 929 (dhall-mode . "dhall") 930 (cmake-mode . "cmake") 931 (cmake-ts-mode . "cmake") 932 (purescript-mode . "purescript") 933 (gdscript-mode . "gdscript") 934 (gdscript-ts-mode . "gdscript") 935 (perl-mode . "perl") 936 (cperl-mode . "perl") 937 (robot-mode . "robot") 938 (racket-mode . "racket") 939 (nix-mode . "nix") 940 (nix-ts-mode . "Nix") 941 (prolog-mode . "prolog") 942 (vala-mode . "vala") 943 (actionscript-mode . "actionscript") 944 (d-mode . "d") 945 (zig-mode . "zig") 946 (text-mode . "plaintext") 947 (markdown-mode . "markdown") 948 (gfm-mode . "markdown") 949 (beancount-mode . "beancount") 950 (conf-toml-mode . "toml") 951 (toml-ts-mode . "toml") 952 (org-mode . "org") 953 (org-journal-mode . "org") 954 (nginx-mode . "nginx") 955 (magik-mode . "magik") 956 (magik-ts-mode . "magik") 957 (idris-mode . "idris") 958 (idris2-mode . "idris2") 959 (gleam-mode . "gleam") 960 (gleam-ts-mode . "gleam") 961 (graphviz-dot-mode . "dot") 962 (tiltfile-mode . "tiltfile") 963 (solidity-mode . "solidity") 964 (bibtex-mode . "bibtex") 965 (rst-mode . "restructuredtext") 966 (glsl-mode . "glsl") 967 (shader-mode . "shaderlab") 968 (wgsl-mode . "wgsl") 969 (jq-mode . "jq") 970 (jq-ts-mode . "jq") 971 (protobuf-mode . "protobuf") 972 (nushell-mode . "nushell") 973 (nushell-ts-mode . "nushell") 974 (meson-mode . "meson") 975 (yang-mode . "yang")) 976 "Language id configuration.") 977 978 (defvar lsp--last-active-workspaces nil 979 "Keep track of last active workspace. 980 We want to try the last workspace first when jumping into a library 981 directory") 982 983 (defvar lsp-method-requirements 984 '(("textDocument/callHierarchy" :capability :callHierarchyProvider) 985 ("textDocument/codeAction" :capability :codeActionProvider) 986 ("codeAction/resolve" 987 :check-command (lambda (workspace) 988 (with-lsp-workspace workspace 989 (lsp:code-action-options-resolve-provider? 990 (lsp--capability-for-method "textDocument/codeAction"))))) 991 ("textDocument/codeLens" :capability :codeLensProvider) 992 ("textDocument/completion" :capability :completionProvider) 993 ("completionItem/resolve" 994 :check-command (lambda (wk) 995 (with-lsp-workspace wk 996 (lsp:completion-options-resolve-provider? 997 (lsp--capability-for-method "textDocument/completion"))))) 998 ("textDocument/declaration" :capability :declarationProvider) 999 ("textDocument/definition" :capability :definitionProvider) 1000 ("textDocument/documentColor" :capability :colorProvider) 1001 ("textDocument/documentLink" :capability :documentLinkProvider) 1002 ("textDocument/inlayHint" :capability :inlayHintProvider) 1003 ("textDocument/documentHighlight" :capability :documentHighlightProvider) 1004 ("textDocument/documentSymbol" :capability :documentSymbolProvider) 1005 ("textDocument/foldingRange" :capability :foldingRangeProvider) 1006 ("textDocument/formatting" :capability :documentFormattingProvider) 1007 ("textDocument/hover" :capability :hoverProvider) 1008 ("textDocument/implementation" :capability :implementationProvider) 1009 ("textDocument/linkedEditingRange" :capability :linkedEditingRangeProvider) 1010 ("textDocument/onTypeFormatting" :capability :documentOnTypeFormattingProvider) 1011 ("textDocument/prepareRename" 1012 :check-command (lambda (workspace) 1013 (with-lsp-workspace workspace 1014 (lsp:rename-options-prepare-provider? 1015 (lsp--capability-for-method "textDocument/rename"))))) 1016 ("textDocument/rangeFormatting" :capability :documentRangeFormattingProvider) 1017 ("textDocument/references" :capability :referencesProvider) 1018 ("textDocument/rename" :capability :renameProvider) 1019 ("textDocument/selectionRange" :capability :selectionRangeProvider) 1020 ("textDocument/semanticTokens" :capability :semanticTokensProvider) 1021 ("textDocument/semanticTokensFull" 1022 :check-command (lambda (workspace) 1023 (with-lsp-workspace workspace 1024 (lsp-get (lsp--capability :semanticTokensProvider) :full)))) 1025 ("textDocument/semanticTokensFull/Delta" 1026 :check-command (lambda (workspace) 1027 (with-lsp-workspace workspace 1028 (let ((capFull (lsp-get (lsp--capability :semanticTokensProvider) :full))) 1029 (and (not (booleanp capFull)) (lsp-get capFull :delta)))))) 1030 ("textDocument/semanticTokensRangeProvider" 1031 :check-command (lambda (workspace) 1032 (with-lsp-workspace workspace 1033 (lsp-get (lsp--capability :semanticTokensProvider) :range)))) 1034 ("textDocument/signatureHelp" :capability :signatureHelpProvider) 1035 ("textDocument/typeDefinition" :capability :typeDefinitionProvider) 1036 ("textDocument/typeHierarchy" :capability :typeHierarchyProvider) 1037 ("workspace/executeCommand" :capability :executeCommandProvider) 1038 ("workspace/symbol" :capability :workspaceSymbolProvider)) 1039 1040 "Map methods to requirements. 1041 It is used by request-sending functions to determine which server 1042 must be used for handling a particular message.") 1043 1044 (defconst lsp--file-change-type 1045 `((created . 1) 1046 (changed . 2) 1047 (deleted . 3))) 1048 1049 (defconst lsp--watch-kind 1050 `((create . 1) 1051 (change . 2) 1052 (delete . 4))) 1053 1054 (defvar lsp-window-body-width 40 1055 "Window body width when rendering doc.") 1056 1057 (defface lsp-face-highlight-textual 1058 '((t :inherit highlight)) 1059 "Face used for textual occurrences of symbols." 1060 :group 'lsp-mode) 1061 1062 (defface lsp-face-highlight-read 1063 '((t :inherit highlight :underline t)) 1064 "Face used for highlighting symbols being read." 1065 :group 'lsp-mode) 1066 1067 (defface lsp-face-highlight-write 1068 '((t :inherit highlight :weight bold)) 1069 "Face used for highlighting symbols being written to." 1070 :group 'lsp-mode) 1071 1072 (define-obsolete-variable-alias 'lsp-lens-auto-enable 1073 'lsp-lens-enable "lsp-mode 7.0.1") 1074 1075 (defcustom lsp-lens-enable t 1076 "Auto enable lenses if server supports." 1077 :group 'lsp-lens 1078 :type 'boolean 1079 :package-version '(lsp-mode . "6.3")) 1080 1081 (defcustom lsp-symbol-highlighting-skip-current nil 1082 "If non-nil skip current symbol when setting symbol highlights." 1083 :group 'lsp-mode 1084 :type 'boolean) 1085 1086 (defcustom lsp-file-watch-threshold 1000 1087 "Show warning if the files to watch are more than. 1088 Set to nil to disable the warning." 1089 :type 'number 1090 :group 'lsp-mode) 1091 ;;;###autoload(put 'lsp-file-watch-threshold 'safe-local-variable (lambda (i) (or (numberp i) (not i)))) 1092 1093 (defvar lsp-custom-markup-modes 1094 '((rust-mode "no_run" "rust,no_run" "rust,ignore" "rust,should_panic")) 1095 "Mode to uses with markdown code blocks. 1096 They are added to `markdown-code-lang-modes'") 1097 1098 (defcustom lsp-signature-render-documentation t 1099 "Display signature documentation in `eldoc'." 1100 :type 'boolean 1101 :group 'lsp-mode 1102 :package-version '(lsp-mode . "6.2")) 1103 1104 (defcustom lsp-signature-auto-activate '(:on-trigger-char :on-server-request) 1105 "Auto activate signature conditions." 1106 :type '(repeat (choice (const :tag "On trigger chars pressed." :on-trigger-char) 1107 (const :tag "After selected completion." :after-completion) 1108 (const :tag "When the server has sent show signature help." :on-server-request))) 1109 :group 'lsp-mode 1110 :package-version '(lsp-mode . "6.2")) 1111 1112 (defcustom lsp-signature-doc-lines 20 1113 "If number, limit the number of lines to show in the docs." 1114 :type 'number 1115 :group 'lsp-mode 1116 :package-version '(lsp-mode . "6.3")) 1117 1118 (defcustom lsp-signature-function 'lsp-lv-message 1119 "The function used for displaying signature info. 1120 It will be called with one param - the signature info. When 1121 called with nil the signature info must be cleared." 1122 :type 'function 1123 :group 'lsp-mode 1124 :package-version '(lsp-mode . "6.3")) 1125 1126 (defcustom lsp-keymap-prefix "s-l" 1127 "LSP-mode keymap prefix." 1128 :group 'lsp-mode 1129 :type 'string 1130 :package-version '(lsp-mode . "6.3")) 1131 1132 (defvar-local lsp--buffer-workspaces () 1133 "List of the buffer workspaces.") 1134 1135 (defvar-local lsp--buffer-deferred nil 1136 "Whether buffer was loaded via `lsp-deferred'.") 1137 1138 (defvar lsp--session nil 1139 "Contain the `lsp-session' for the current Emacs instance.") 1140 1141 (defvar lsp--tcp-port 10000) 1142 1143 (defvar lsp--client-packages-required nil 1144 "If nil, `lsp-client-packages' are yet to be required.") 1145 1146 (defvar lsp--tcp-server-port 0 1147 "The server socket which is opened when using `lsp-tcp-server' (a server 1148 socket is opened in Emacs and the language server connects to it). The 1149 default value of 0 ensures that a random high port is used. Set it to a positive 1150 integer to use a specific port.") 1151 1152 (defvar lsp--tcp-server-wait-seconds 10 1153 "Wait this amount of time for the client to connect to our server socket 1154 when using `lsp-tcp-server'.") 1155 1156 (defvar-local lsp--document-symbols nil 1157 "The latest document symbols.") 1158 1159 (defvar-local lsp--document-selection-range-cache nil 1160 "The document selection cache.") 1161 1162 (defvar-local lsp--document-symbols-request-async nil 1163 "If non-nil, request document symbols asynchronously.") 1164 1165 (defvar-local lsp--document-symbols-tick -1 1166 "The value of `buffer-chars-modified-tick' when document 1167 symbols were last retrieved.") 1168 1169 (defvar-local lsp--have-document-highlights nil 1170 "Set to `t' on symbol highlighting, cleared on 1171 `lsp--cleanup-highlights-if-needed'. Checking a separately 1172 defined flag is substantially faster than unconditionally 1173 calling `remove-overlays'.") 1174 1175 ;; Buffer local variable for storing number of lines. 1176 (defvar lsp--log-lines) 1177 1178 (defvar-local lsp--eldoc-saved-message nil) 1179 1180 (defvar lsp--on-change-timer nil) 1181 (defvar lsp--on-idle-timer nil) 1182 1183 (defvar-local lsp--signature-last nil) 1184 (defvar-local lsp--signature-last-index nil) 1185 (defvar lsp--signature-last-buffer nil) 1186 1187 (defvar-local lsp--virtual-buffer-point-max nil) 1188 1189 (cl-defmethod lsp-execute-command (_server _command _arguments) 1190 "Ask SERVER to execute COMMAND with ARGUMENTS.") 1191 1192 (defun lsp-elt (sequence n) 1193 "Return Nth element of SEQUENCE or nil if N is out of range." 1194 (cond 1195 ((listp sequence) (elt sequence n)) 1196 ((arrayp sequence) 1197 (and (> (length sequence) n) (aref sequence n))) 1198 (t (and (> (length sequence) n) (elt sequence n))))) 1199 1200 ;; define seq-first and seq-rest for older emacs 1201 (defun lsp-seq-first (sequence) 1202 "Return the first element of SEQUENCE." 1203 (lsp-elt sequence 0)) 1204 1205 (defun lsp-seq-rest (sequence) 1206 "Return a sequence of the elements of SEQUENCE except the first one." 1207 (seq-drop sequence 1)) 1208 1209 ;;;###autoload 1210 (defun lsp--string-listp (sequence) 1211 "Return t if all elements of SEQUENCE are strings, else nil." 1212 (not (seq-find (lambda (x) (not (stringp x))) sequence))) 1213 1214 (defun lsp--string-vector-p (candidate) 1215 "Returns true if CANDIDATE is a vector data structure and 1216 every element of it is of type string, else nil." 1217 (and 1218 (vectorp candidate) 1219 (seq-every-p #'stringp candidate))) 1220 1221 (make-obsolete 'lsp--string-vector-p nil "lsp-mode 8.0.0") 1222 1223 (defun lsp--editable-vector-match (widget value) 1224 "Function for `lsp-editable-vector' :match." 1225 ;; Value must be a list or a vector and all the members must match the type. 1226 (and (or (listp value) (vectorp value)) 1227 (length (cdr (lsp--editable-vector-match-inline widget value))))) 1228 1229 (defun lsp--editable-vector-match-inline (widget value) 1230 "Value for `lsp-editable-vector' :match-inline." 1231 (let ((type (nth 0 (widget-get widget :args))) 1232 (ok t) 1233 found) 1234 (while (and value ok) 1235 (let ((answer (widget-match-inline type value))) 1236 (if answer 1237 (let ((head (if (vectorp answer) (aref answer 0) (car answer))) 1238 (tail (if (vectorp answer) (seq-drop 1 answer) (cdr answer)))) 1239 (setq found (append found head) 1240 value tail)) 1241 (setq ok nil)))) 1242 (cons found value))) 1243 1244 (defun lsp--editable-vector-value-to-external (_widget internal-value) 1245 "Convert the internal list value to a vector." 1246 (if (listp internal-value) 1247 (apply 'vector internal-value) 1248 internal-value)) 1249 1250 (defun lsp--editable-vector-value-to-internal (_widget external-value) 1251 "Convert the external vector value to a list." 1252 (if (vectorp external-value) 1253 (append external-value nil) 1254 external-value)) 1255 1256 (define-widget 'lsp--editable-vector 'editable-list 1257 "A subclass of `editable-list' that accepts and returns a 1258 vector instead of a list." 1259 :value-to-external 'lsp--editable-vector-value-to-external 1260 :value-to-internal 'lsp--editable-vector-value-to-internal 1261 :match 'lsp--editable-vector-match 1262 :match-inline 'lsp--editable-vector-match-inline) 1263 1264 (define-widget 'lsp-repeatable-vector 'lsp--editable-vector 1265 "A variable length homogeneous vector." 1266 :tag "Repeat" 1267 :format "%{%t%}:\n%v%i\n") 1268 1269 (define-widget 'lsp-string-vector 'lazy 1270 "A vector of zero or more elements, every element of which is a string. 1271 Appropriate for any language-specific `defcustom' that needs to 1272 serialize as a JSON array of strings. 1273 1274 Deprecated. Use `lsp-repeatable-vector' instead. " 1275 :offset 4 1276 :tag "Vector" 1277 :type '(lsp-repeatable-vector string)) 1278 1279 (make-obsolete 'lsp-string-vector nil "lsp-mode 8.0.0") 1280 1281 (defvar lsp--show-message t 1282 "If non-nil, show debug message from `lsp-mode'.") 1283 1284 (defun lsp--message (format &rest args) 1285 "Wrapper for `message' 1286 1287 We `inhibit-message' the message when the cursor is in the 1288 minibuffer and when emacs version is before emacs 27 due to the 1289 fact that we often use `lsp--info', `lsp--warn' and `lsp--error' 1290 in async context and the call to these function is removing the 1291 minibuffer prompt. The issue with async messages is already fixed 1292 in emacs 27. 1293 1294 See #2049" 1295 (when lsp--show-message 1296 (let ((inhibit-message (or inhibit-message 1297 (and (minibufferp) 1298 (version< emacs-version "27.0"))))) 1299 (apply #'message format args)))) 1300 1301 (defun lsp--info (format &rest args) 1302 "Display lsp info message with FORMAT with ARGS." 1303 (lsp--message "%s :: %s" (propertize "LSP" 'face 'success) (apply #'format format args))) 1304 1305 (defun lsp--warn (format &rest args) 1306 "Display lsp warn message with FORMAT with ARGS." 1307 (lsp--message "%s :: %s" (propertize "LSP" 'face 'warning) (apply #'format format args))) 1308 1309 (defun lsp--error (format &rest args) 1310 "Display lsp error message with FORMAT with ARGS." 1311 (lsp--message "%s :: %s" (propertize "LSP" 'face 'error) (apply #'format format args))) 1312 1313 (defun lsp-log (format &rest args) 1314 "Log message to the ’*lsp-log*’ buffer. 1315 1316 FORMAT and ARGS i the same as for `message'." 1317 (when lsp-log-max 1318 (let ((log-buffer (get-buffer "*lsp-log*")) 1319 (inhibit-read-only t)) 1320 (unless log-buffer 1321 (setq log-buffer (get-buffer-create "*lsp-log*")) 1322 (with-current-buffer log-buffer 1323 (buffer-disable-undo) 1324 (view-mode 1) 1325 (set (make-local-variable 'lsp--log-lines) 0))) 1326 (with-current-buffer log-buffer 1327 (save-excursion 1328 (let* ((message (apply 'format format args)) 1329 ;; Count newlines in message. 1330 (newlines (1+ (cl-loop with start = 0 1331 for count from 0 1332 while (string-match "\n" message start) 1333 do (setq start (match-end 0)) 1334 finally return count)))) 1335 (goto-char (point-max)) 1336 1337 ;; in case the buffer is not empty insert before last \n to preserve 1338 ;; the point position(in case it is in the end) 1339 (if (eq (point) (point-min)) 1340 (progn 1341 (insert "\n") 1342 (backward-char)) 1343 (backward-char) 1344 (insert "\n")) 1345 (insert message) 1346 1347 (setq lsp--log-lines (+ lsp--log-lines newlines)) 1348 1349 (when (and (integerp lsp-log-max) (> lsp--log-lines lsp-log-max)) 1350 (let ((to-delete (- lsp--log-lines lsp-log-max))) 1351 (goto-char (point-min)) 1352 (forward-line to-delete) 1353 (delete-region (point-min) (point)) 1354 (setq lsp--log-lines lsp-log-max))))))))) 1355 1356 (defalias 'lsp-message 'lsp-log) 1357 1358 (defalias 'lsp-ht 'ht) 1359 1360 (defalias 'lsp-file-local-name 'file-local-name) 1361 1362 (defun lsp-f-canonical (file-name) 1363 "Return the canonical FILE-NAME, without a trailing slash." 1364 (directory-file-name (expand-file-name file-name))) 1365 1366 (defalias 'lsp-canonical-file-name 'lsp-f-canonical) 1367 1368 (defun lsp-f-same? (path-a path-b) 1369 "Return t if PATH-A and PATH-B are references to the same file. 1370 Symlinks are not followed." 1371 (when (and (f-exists? path-a) 1372 (f-exists? path-b)) 1373 (equal 1374 (lsp-f-canonical (directory-file-name (f-expand path-a))) 1375 (lsp-f-canonical (directory-file-name (f-expand path-b)))))) 1376 1377 (defun lsp-f-parent (path) 1378 "Return the parent directory to PATH. 1379 Symlinks are not followed." 1380 (let ((parent (file-name-directory 1381 (directory-file-name (f-expand path default-directory))))) 1382 (unless (lsp-f-same? path parent) 1383 (if (f-relative? path) 1384 (f-relative parent) 1385 (directory-file-name parent))))) 1386 1387 (defun lsp-f-ancestor-of? (path-a path-b) 1388 "Return t if PATH-A is an ancestor of PATH-B. 1389 Symlinks are not followed." 1390 (unless (lsp-f-same? path-a path-b) 1391 (s-prefix? (concat (lsp-f-canonical path-a) (f-path-separator)) 1392 (lsp-f-canonical path-b)))) 1393 1394 (defun lsp--merge-results (results method) 1395 "Merge RESULTS by filtering the empty hash-tables and merging 1396 the lists according to METHOD." 1397 (pcase (--map (if (vectorp it) 1398 (append it nil) it) 1399 (-filter #'identity results)) 1400 (`() ()) 1401 ;; only one result - simply return it 1402 (`(,fst) fst) 1403 ;; multiple results merge it based on strategy 1404 (results 1405 (pcase method 1406 ("textDocument/hover" (pcase (seq-filter 1407 (-compose #'not #'lsp-empty?) 1408 results) 1409 (`(,hover) hover) 1410 (hovers (lsp-make-hover 1411 :contents 1412 (-mapcat 1413 (-lambda ((&Hover :contents)) 1414 (if (and (sequencep contents) 1415 (not (stringp contents))) 1416 (append contents ()) 1417 (list contents))) 1418 hovers))))) 1419 ("textDocument/completion" 1420 (lsp-make-completion-list 1421 :is-incomplete (seq-some 1422 #'lsp:completion-list-is-incomplete 1423 results) 1424 :items (cl-mapcan (lambda (it) (append (if (lsp-completion-list? it) 1425 (lsp:completion-list-items it) 1426 it) 1427 nil)) 1428 results))) 1429 ("completionItem/resolve" 1430 (let ((item (cl-first results))) 1431 (when-let ((details (seq-filter #'identity 1432 (seq-map #'lsp:completion-item-detail? results)))) 1433 (lsp:set-completion-item-detail? 1434 item 1435 (string-join details " "))) 1436 (when-let ((docs (seq-filter #'identity 1437 (seq-map #'lsp:completion-item-documentation? results)))) 1438 (lsp:set-completion-item-documentation? 1439 item 1440 (lsp-make-markup-content 1441 :kind (or (seq-some (lambda (it) 1442 (when (equal (lsp:markup-content-kind it) 1443 lsp/markup-kind-markdown) 1444 lsp/markup-kind-markdown)) 1445 docs) 1446 lsp/markup-kind-plain-text) 1447 :value (string-join (seq-map (lambda (doc) 1448 (or (lsp:markup-content-value doc) 1449 (and (stringp doc) doc))) 1450 docs) 1451 "\n")))) 1452 (when-let ((edits (seq-filter #'identity 1453 (seq-map #'lsp:completion-item-additional-text-edits? results)))) 1454 (lsp:set-completion-item-additional-text-edits? 1455 item 1456 (cl-mapcan (lambda (it) (if (seqp it) it (list it))) edits))) 1457 item)) 1458 (_ (cl-mapcan (lambda (it) (if (seqp it) it (list it))) results)))))) 1459 1460 (defun lsp--spinner-start () 1461 "Start spinner indication." 1462 (condition-case _err (spinner-start (lsp-progress-spinner-type)) (error))) 1463 1464 (defun lsp--propertize (str type) 1465 "Propertize STR as per TYPE." 1466 (propertize str 'face (alist-get type lsp--message-type-face))) 1467 1468 (defun lsp-workspaces () 1469 "Return the lsp workspaces associated with the current project." 1470 (if lsp--cur-workspace (list lsp--cur-workspace) lsp--buffer-workspaces)) 1471 1472 (defun lsp--completing-read (prompt collection transform-fn &optional predicate 1473 require-match initial-input 1474 hist def inherit-input-method) 1475 "Wrap `completing-read' to provide transformation function and disable sort. 1476 1477 TRANSFORM-FN will be used to transform each of the items before displaying. 1478 1479 PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF 1480 INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes." 1481 (let* ((col (--map (cons (funcall transform-fn it) it) collection)) 1482 (completion (completing-read prompt 1483 (lambda (string pred action) 1484 (if (eq action 'metadata) 1485 `(metadata (display-sort-function . identity)) 1486 (complete-with-action action col string pred))) 1487 predicate require-match initial-input hist 1488 def inherit-input-method))) 1489 (cdr (assoc completion col)))) 1490 1491 (defconst lsp--system-arch (lambda () 1492 (setq lsp--system-arch 1493 (pcase system-type 1494 ('windows-nt 1495 (pcase system-configuration 1496 ((rx bol "x86_64-") 'x64) 1497 (_ 'x86))) 1498 ('darwin 1499 (pcase system-configuration 1500 ((rx "aarch64-") 'arm64) 1501 (_ 'x64))) 1502 ('gnu/linux 1503 (pcase system-configuration 1504 ((rx bol "x86_64") 'x64) 1505 ((rx bol (| "i386" "i886")) 'x32))) 1506 (_ 1507 (pcase system-configuration 1508 ((rx bol "x86_64") 'x64) 1509 ((rx bol (| "i386" "i886")) 'x32)))))) 1510 "Return the system architecture of `Emacs'. 1511 Special values: 1512 `x64' 64bit 1513 `x32' 32bit 1514 `arm64' ARM 64bit") 1515 1516 (defmacro lsp-with-current-buffer (buffer-id &rest body) 1517 (declare (indent 1) (debug t)) 1518 `(if-let ((wcb (plist-get ,buffer-id :with-current-buffer))) 1519 (with-lsp-workspaces (plist-get ,buffer-id :workspaces) 1520 (funcall wcb (lambda () ,@body))) 1521 (with-current-buffer ,buffer-id 1522 ,@body))) 1523 1524 (defvar lsp--throw-on-input nil 1525 "Make `lsp-*-while-no-input' throws `input' on interrupted.") 1526 1527 (defmacro lsp--catch (tag bodyform &rest handlers) 1528 "Catch TAG thrown in BODYFORM. 1529 The return value from TAG will be handled in HANDLERS by `pcase'." 1530 (declare (debug (form form &rest (pcase-PAT body))) (indent 2)) 1531 (let ((re-sym (make-symbol "re"))) 1532 `(let ((,re-sym (catch ,tag ,bodyform))) 1533 (pcase ,re-sym 1534 ,@handlers)))) 1535 1536 (defmacro lsp--while-no-input (&rest body) 1537 "Wrap BODY in `while-no-input' and respecting `non-essential'. 1538 If `lsp--throw-on-input' is set, will throw if input is pending, else 1539 return value of `body' or nil if interrupted." 1540 (declare (debug t) (indent 0)) 1541 `(if non-essential 1542 (let ((res (while-no-input ,@body))) 1543 (cond 1544 ((and lsp--throw-on-input (equal res t)) 1545 (throw 'input :interrupted)) 1546 ((booleanp res) nil) 1547 (t res))) 1548 ,@body)) 1549 1550 ;; A ‘lsp--client’ object describes the client-side behavior of a language 1551 ;; server. It is used to start individual server processes, each of which is 1552 ;; represented by a ‘lsp--workspace’ object. Client objects are normally 1553 ;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’. Each 1554 ;; workspace refers to exactly one client, but there can be multiple workspaces 1555 ;; for a single client. 1556 (cl-defstruct lsp--client 1557 ;; ‘language-id’ is a function that receives a buffer as a single argument 1558 ;; and should return the language identifier for that buffer. See 1559 ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem 1560 ;; for a list of language identifiers. Also consult the documentation for 1561 ;; the language server represented by this client to find out what language 1562 ;; identifiers it supports or expects. 1563 (language-id nil) 1564 1565 ;; ‘add-on?’ when set to t the server will be started no matter whether there 1566 ;; is another server handling the same mode. 1567 (add-on? nil) 1568 ;; ‘new-connection’ is a function that should start a language server process 1569 ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS). 1570 ;; COMMAND-PROCESS must be a process object representing the server process 1571 ;; just started. COMMUNICATION-PROCESS must be a process (including pipe and 1572 ;; network processes) that ‘lsp-mode’ uses to communicate with the language 1573 ;; server using the language server protocol. COMMAND-PROCESS and 1574 ;; COMMUNICATION-PROCESS may be the same process; in that case 1575 ;; ‘new-connection’ may also return that process as a single 1576 ;; object. ‘new-connection’ is called with two arguments, FILTER and 1577 ;; SENTINEL. FILTER should be used as process filter for 1578 ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for 1579 ;; COMMAND-PROCESS. 1580 (new-connection nil) 1581 1582 ;; ‘ignore-regexps’ is a list of regexps. When a data packet from the 1583 ;; language server matches any of these regexps, it will be ignored. This is 1584 ;; intended for dealing with language servers that output non-protocol data. 1585 (ignore-regexps nil) 1586 1587 ;; ‘ignore-messages’ is a list of regexps. When a message from the language 1588 ;; server matches any of these regexps, it will be ignored. This is useful 1589 ;; for filtering out unwanted messages; such as servers that send nonstandard 1590 ;; message types, or extraneous log messages. 1591 (ignore-messages nil) 1592 1593 ;; ‘notification-handlers’ is a hash table mapping notification method names 1594 ;; (strings) to functions handling the respective notifications. Upon 1595 ;; receiving a notification, ‘lsp-mode’ will call the associated handler 1596 ;; function passing two arguments, the ‘lsp--workspace’ object and the 1597 ;; deserialized notification parameters. 1598 (notification-handlers (make-hash-table :test 'equal)) 1599 1600 ;; ‘request-handlers’ is a hash table mapping request method names 1601 ;; (strings) to functions handling the respective notifications. Upon 1602 ;; receiving a request, ‘lsp-mode’ will call the associated handler function 1603 ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized 1604 ;; request parameters. 1605 (request-handlers (make-hash-table :test 'equal)) 1606 1607 ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request 1608 ;; identifiers for pending asynchronous requests to functions handling the 1609 ;; respective responses. Upon receiving a response from the language server, 1610 ;; ‘lsp-mode’ will call the associated response handler function with a 1611 ;; single argument, the deserialized response parameters. 1612 (response-handlers (make-hash-table :test 'eql)) 1613 1614 ;; ‘prefix-function’ is called for getting the prefix for completion. 1615 ;; The function takes no parameter and returns a cons (start . end) representing 1616 ;; the start and end bounds of the prefix. If it's not set, the client uses a 1617 ;; default prefix function." 1618 (prefix-function nil) 1619 1620 ;; Contains mapping of scheme to the function that is going to be used to load 1621 ;; the file. 1622 (uri-handlers (make-hash-table :test #'equal)) 1623 1624 ;; ‘action-handlers’ is a hash table mapping action to a handler function. It 1625 ;; can be used in `lsp-execute-code-action' to determine whether the action 1626 ;; current client is interested in executing the action instead of sending it 1627 ;; to the server. 1628 (action-handlers (make-hash-table :test 'equal)) 1629 1630 ;; `action-filter' can be set to a function that modifies any incoming 1631 ;; `CodeAction' in place before it is executed. The return value is ignored. 1632 ;; This can be used to patch up broken code action requests before they are 1633 ;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an 1634 ;; example of a function that can be useful here. 1635 (action-filter nil) 1636 1637 ;; major modes supported by the client. 1638 major-modes 1639 ;; Function that will be called to decide if this language client 1640 ;; should manage a particular buffer. The function will be passed 1641 ;; the file name and major mode to inform the decision. Setting 1642 ;; `activation-fn' will override `major-modes', if 1643 ;; present. 1644 activation-fn 1645 ;; Break the tie when major-mode is supported by multiple clients. 1646 (priority 0) 1647 ;; Unique identifier for representing the client object. 1648 server-id 1649 ;; defines whether the client supports multi root workspaces. 1650 multi-root 1651 ;; Initialization options or a function that returns initialization options. 1652 initialization-options 1653 ;; `semantic-tokens-faces-overrides’ is a plist that can be used to extend, or 1654 ;; completely replace, the faces used for semantic highlighting on a 1655 ;; client-by-client basis. 1656 ;; 1657 ;; It recognizes four members, all of which are optional: `:types’ and 1658 ;; `:modifiers’, respectively, should be face definition lists akin to 1659 ;; `:lsp-semantic-token-faces’. If specified, each of these face lists will be 1660 ;; merged with the default face definition list. 1661 ;; 1662 ;; Alternatively, if the plist members `:discard-default-types’ or 1663 ;; `:discard-default-modifiers' are non-nil, the default `:type' or `:modifiers' 1664 ;; face definitions will be replaced entirely by their respective overrides. 1665 ;; 1666 ;; For example, setting `:semantic-tokens-faces-overrides' to 1667 ;; `(:types (("macro" . font-lock-keyword-face)))' will remap "macro" tokens from 1668 ;; their default face `lsp-face-semhl-macro' to `font-lock-keyword-face'. 1669 ;; 1670 ;; `(:types (("macro" . font-lock-keyword-face) ("not-quite-a-macro" . some-face)))' 1671 ;; will also remap "macro", but on top of that associate the fictional token type 1672 ;; "not-quite-a-macro" with the face named `some-face'. 1673 ;; 1674 ;; `(:types (("macro" . font-lock-keyword-face)) 1675 ;; :modifiers (("declaration" . lsp-face-semhl-interface)) 1676 ;; :discard-default-types t 1677 ;; :discard-default-modifiers t)' 1678 ;; will discard all default face definitions, hence leaving the client with 1679 ;; only one token type "macro", mapped to `font-lock-keyword-face', and one 1680 ;; modifier type "declaration", mapped to `lsp-face-semhl-interface'. 1681 semantic-tokens-faces-overrides 1682 ;; Provides support for registering LSP Server specific capabilities. 1683 custom-capabilities 1684 ;; Function which returns the folders that are considered to be not projects but library files. 1685 ;; The function accepts one parameter currently active workspace. 1686 ;; See: https://github.com/emacs-lsp/lsp-mode/issues/225. 1687 library-folders-fn 1688 ;; function which will be called when opening file in the workspace to perform 1689 ;; client specific initialization. The function accepts one parameter 1690 ;; currently active workspace. 1691 before-file-open-fn 1692 ;; Function which will be called right after a workspace has been initialized. 1693 initialized-fn 1694 ;; ‘remote?’ indicate whether the client can be used for LSP server over TRAMP. 1695 (remote? nil) 1696 1697 ;; ‘completion-in-comments?’ t if the client supports completion in comments. 1698 (completion-in-comments? nil) 1699 1700 ;; ‘path->uri-fn’ the function to use for path->uri conversion for the client. 1701 (path->uri-fn nil) 1702 1703 ;; ‘uri->path-fn’ the function to use for uri->path conversion for the client. 1704 (uri->path-fn nil) 1705 ;; Function that returns an environment structure that will be used 1706 ;; to set some environment variables when starting the language 1707 ;; server process. These environment variables enable some 1708 ;; additional features in the language server. The environment 1709 ;; structure is an alist of the form (KEY . VALUE), where KEY is a 1710 ;; string (regularly in all caps), and VALUE may be a string, a 1711 ;; boolean, or a sequence of strings. 1712 environment-fn 1713 1714 ;; ‘after-open-fn’ workspace after open specific hooks. 1715 (after-open-fn nil) 1716 1717 ;; ‘async-request-handlers’ is a hash table mapping request method names 1718 ;; (strings) to functions handling the respective requests that may take 1719 ;; time to finish. Upon receiving a request, ‘lsp-mode’ will call the 1720 ;; associated handler function passing three arguments, the ‘lsp--workspace’ 1721 ;; object, the deserialized request parameters and the callback which accept 1722 ;; result as its parameter. 1723 (async-request-handlers (make-hash-table :test 'equal)) 1724 download-server-fn 1725 download-in-progress? 1726 buffers 1727 synchronize-sections) 1728 1729 (defun lsp-clients-executable-find (find-command &rest args) 1730 "Finds an executable by invoking a search command. 1731 1732 FIND-COMMAND is the executable finder that searches for the 1733 actual language server executable. ARGS is a list of arguments to 1734 give to FIND-COMMAND to find the language server. Returns the 1735 output of FIND-COMMAND if it exits successfully, nil otherwise. 1736 1737 Typical uses include finding an executable by invoking `find' in 1738 a project, finding LLVM commands on macOS with `xcrun', or 1739 looking up project-specific language servers for projects written 1740 in the various dynamic languages, e.g. `nvm', `pyenv' and `rbenv' 1741 etc." 1742 (when-let* ((find-command-path (executable-find find-command)) 1743 (executable-path 1744 (with-temp-buffer 1745 (when (zerop (apply 'call-process find-command-path nil t nil args)) 1746 (buffer-substring-no-properties (point-min) (point-max)))))) 1747 (string-trim executable-path))) 1748 1749 (defvar lsp--already-widened nil) 1750 1751 (defmacro lsp-save-restriction-and-excursion (&rest form) 1752 (declare (indent 0) (debug t)) 1753 `(if lsp--already-widened 1754 (save-excursion ,@form) 1755 (-let [lsp--already-widened t] 1756 (save-restriction 1757 (widen) 1758 (save-excursion ,@form))))) 1759 1760 ;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number 1761 (defun lsp--line-character-to-point (line character) 1762 "Return the point for character CHARACTER on line LINE." 1763 (or (lsp-virtual-buffer-call :line/character->point line character) 1764 (let ((inhibit-field-text-motion t)) 1765 (lsp-save-restriction-and-excursion 1766 (goto-char (point-min)) 1767 (forward-line line) 1768 ;; server may send character position beyond the current line and we 1769 ;; should fallback to line end. 1770 (-let [line-end (line-end-position)] 1771 (if (> character (- line-end (point))) 1772 line-end 1773 (forward-char character) 1774 (point))))))) 1775 1776 (lsp-defun lsp--position-to-point ((&Position :line :character)) 1777 "Convert `Position' object in PARAMS to a point." 1778 (lsp--line-character-to-point line character)) 1779 1780 (lsp-defun lsp--range-to-region ((&RangeToPoint :start :end)) 1781 (cons start end)) 1782 1783 (lsp-defun lsp--range-text ((&RangeToPoint :start :end)) 1784 (buffer-substring start end)) 1785 1786 (lsp-defun lsp--find-wrapping-range ((&SelectionRange :parent? :range (&RangeToPoint :start :end))) 1787 (cond 1788 ((and 1789 (region-active-p) 1790 (<= start (region-beginning) end) 1791 (<= start (region-end) end) 1792 (or (not (= start (region-beginning))) 1793 (not (= end (region-end))))) 1794 (cons start end)) 1795 ((and (<= start (point) end) 1796 (not (region-active-p))) 1797 (cons start end)) 1798 (parent? (lsp--find-wrapping-range parent?)))) 1799 1800 (defun lsp--get-selection-range () 1801 (or 1802 (-when-let ((cache . cache-tick) lsp--document-selection-range-cache) 1803 (when (= cache-tick (buffer-modified-tick)) cache)) 1804 (let ((response (cl-first 1805 (lsp-request 1806 "textDocument/selectionRange" 1807 (list :textDocument (lsp--text-document-identifier) 1808 :positions (vector (lsp--cur-position))))))) 1809 (setq lsp--document-selection-range-cache 1810 (cons response (buffer-modified-tick))) 1811 response))) 1812 1813 (defun lsp-extend-selection () 1814 "Extend selection." 1815 (interactive) 1816 (unless (lsp-feature? "textDocument/selectionRange") 1817 (signal 'lsp-capability-not-supported (list "selectionRangeProvider"))) 1818 (-when-let ((start . end) (lsp--find-wrapping-range (lsp--get-selection-range))) 1819 (goto-char start) 1820 (set-mark (point)) 1821 (goto-char end) 1822 (exchange-point-and-mark))) 1823 1824 (defun lsp-warn (message &rest args) 1825 "Display a warning message made from (`format-message' MESSAGE ARGS...). 1826 This is equivalent to `display-warning', using `lsp-mode' as the type and 1827 `:warning' as the level." 1828 (display-warning 'lsp-mode (apply #'format-message message args))) 1829 1830 (defun lsp--get-uri-handler (scheme) 1831 "Get uri handler for SCHEME in the current workspace." 1832 (--some (gethash scheme (lsp--client-uri-handlers (lsp--workspace-client it))) 1833 (or (lsp-workspaces) (lsp--session-workspaces (lsp-session))))) 1834 1835 (defun lsp--fix-path-casing (path) 1836 "On windows, downcases path because the windows file system is 1837 case-insensitive. 1838 1839 On other systems, returns path without change." 1840 (if (eq system-type 'windows-nt) (downcase path) path)) 1841 1842 (defun lsp--uri-to-path (uri) 1843 "Convert URI to a file path." 1844 (if-let ((fn (->> (lsp-workspaces) 1845 (-keep (-compose #'lsp--client-uri->path-fn #'lsp--workspace-client)) 1846 (cl-first)))) 1847 (funcall fn uri) 1848 (lsp--uri-to-path-1 uri))) 1849 1850 (defun lsp-remap-path-if-needed (file-name) 1851 (-if-let ((virtual-buffer &as &plist :buffer) (gethash file-name lsp--virtual-buffer-mappings)) 1852 (propertize (buffer-local-value 'buffer-file-name buffer) 1853 'lsp-virtual-buffer virtual-buffer) 1854 file-name)) 1855 1856 (defun lsp--uri-to-path-1 (uri) 1857 "Convert URI to a file path." 1858 (let* ((url (url-generic-parse-url (url-unhex-string uri))) 1859 (type (url-type url)) 1860 (target (url-target url)) 1861 (file 1862 (concat (decode-coding-string (url-filename url) 1863 (or locale-coding-system 'utf-8)) 1864 (when (and target 1865 (not (s-match 1866 (rx "#" (group (1+ num)) (or "," "#") 1867 (group (1+ num)) 1868 string-end) 1869 uri))) 1870 (concat "#" target)))) 1871 (file-name (if (and type (not (string= type "file"))) 1872 (if-let ((handler (lsp--get-uri-handler type))) 1873 (funcall handler uri) 1874 uri) 1875 ;; `url-generic-parse-url' is buggy on windows: 1876 ;; https://github.com/emacs-lsp/lsp-mode/pull/265 1877 (or (and (eq system-type 'windows-nt) 1878 (eq (elt file 0) ?\/) 1879 (substring file 1)) 1880 file)))) 1881 (->> file-name 1882 (concat (-some #'lsp--workspace-host-root (lsp-workspaces))) 1883 (lsp-remap-path-if-needed)))) 1884 1885 (defun lsp--buffer-uri () 1886 "Return URI of the current buffer." 1887 (or lsp-buffer-uri 1888 (plist-get lsp--virtual-buffer :buffer-uri) 1889 (lsp--path-to-uri 1890 (or (buffer-file-name) (buffer-file-name (buffer-base-buffer)))))) 1891 1892 (defun lsp-register-client-capabilities (&rest _args) 1893 "Implemented only to make `company-lsp' happy. 1894 DELETE when `lsp-mode.el' is deleted.") 1895 1896 (defconst lsp--url-path-allowed-chars 1897 (url--allowed-chars (append '(?/) url-unreserved-chars)) 1898 "`url-unreserved-chars' with additional delim ?/. 1899 This set of allowed chars is enough for hexifying local file paths.") 1900 1901 (defun lsp--path-to-uri-1 (path) 1902 (concat lsp--uri-file-prefix 1903 (--> path 1904 (expand-file-name it) 1905 (or (file-remote-p it 'localname t) it) 1906 (url-hexify-string it lsp--url-path-allowed-chars)))) 1907 1908 (defun lsp--path-to-uri (path) 1909 "Convert PATH to a uri." 1910 (if-let ((uri-fn (->> (lsp-workspaces) 1911 (-keep (-compose #'lsp--client-path->uri-fn #'lsp--workspace-client)) 1912 (cl-first)))) 1913 (funcall uri-fn path) 1914 (lsp--path-to-uri-1 path))) 1915 1916 (defun lsp--string-match-any (regex-list str) 1917 "Return the first regex, if any, within REGEX-LIST matching STR." 1918 (--first (string-match it str) regex-list)) 1919 1920 (cl-defstruct lsp-watch 1921 (descriptors (make-hash-table :test 'equal)) 1922 root-directory) 1923 1924 (defun lsp--folder-watch-callback (event callback watch ignored-files ignored-directories) 1925 (let ((file-name (cl-third event)) 1926 (event-type (cl-second event))) 1927 (cond 1928 ((and (file-directory-p file-name) 1929 (equal 'created event-type) 1930 (not (lsp--string-match-any ignored-directories file-name))) 1931 1932 (lsp-watch-root-folder (file-truename file-name) callback ignored-files ignored-directories watch) 1933 1934 ;; process the files that are already present in 1935 ;; the directory. 1936 (->> (directory-files-recursively file-name ".*" t) 1937 (seq-do (lambda (f) 1938 (unless (file-directory-p f) 1939 (funcall callback (list nil 'created f))))))) 1940 ((and (memq event-type '(created deleted changed)) 1941 (not (file-directory-p file-name)) 1942 (not (lsp--string-match-any ignored-files file-name))) 1943 (funcall callback event)) 1944 ((and (memq event-type '(renamed)) 1945 (not (file-directory-p file-name)) 1946 (not (lsp--string-match-any ignored-files file-name))) 1947 (funcall callback `(,(cl-first event) deleted ,(cl-third event))) 1948 (funcall callback `(,(cl-first event) created ,(cl-fourth event))))))) 1949 1950 (defun lsp--ask-about-watching-big-repo (number-of-directories dir) 1951 "Ask the user if they want to watch NUMBER-OF-DIRECTORIES from a repository DIR. 1952 This is useful when there is a lot of files in a repository, as 1953 that may slow Emacs down. Returns t if the user wants to watch 1954 the entire repository, nil otherwise." 1955 (prog1 1956 (yes-or-no-p 1957 (format 1958 "Watching all the files in %s would require adding watches to %s directories, so watching the repo may slow Emacs down. 1959 Do you want to watch all files in %s? " 1960 dir 1961 number-of-directories 1962 dir)) 1963 (lsp--info 1964 (concat "You can configure this warning with the `lsp-enable-file-watchers' " 1965 "and `lsp-file-watch-threshold' variables")))) 1966 1967 1968 (defun lsp--path-is-watchable-directory (path dir ignored-directories) 1969 "Figure out whether PATH (inside of DIR) is meant to have a file watcher set. 1970 IGNORED-DIRECTORIES is a list of regexes to filter out directories we don't 1971 want to watch." 1972 (let 1973 ((full-path (f-join dir path))) 1974 (and (file-accessible-directory-p full-path) 1975 (not (equal path ".")) 1976 (not (equal path "..")) 1977 (not (lsp--string-match-any ignored-directories full-path))))) 1978 1979 1980 (defun lsp--all-watchable-directories (dir ignored-directories) 1981 "Traverse DIR recursively returning a list of paths that should have watchers. 1982 IGNORED-DIRECTORIES will be used for exclusions" 1983 (let* ((dir (if (f-symlink? dir) 1984 (file-truename dir) 1985 dir))) 1986 (apply #'nconc 1987 ;; the directory itself is assumed to be part of the set 1988 (list dir) 1989 ;; collect all subdirectories that are watchable 1990 (-map 1991 (lambda (path) (lsp--all-watchable-directories (f-join dir path) ignored-directories)) 1992 ;; but only look at subdirectories that are watchable 1993 (-filter (lambda (path) (lsp--path-is-watchable-directory path dir ignored-directories)) 1994 (directory-files dir)))))) 1995 1996 (defun lsp-watch-root-folder (dir callback ignored-files ignored-directories &optional watch warn-big-repo?) 1997 "Create recursive file notification watch in DIR. 1998 CALLBACK will be called when there are changes in any of 1999 the monitored files. WATCHES is a hash table directory->file 2000 notification handle which contains all of the watch that 2001 already have been created. Watches will not be created for 2002 any directory that matches any regex in IGNORED-DIRECTORIES. 2003 Watches will not be created for any file that matches any 2004 regex in IGNORED-FILES." 2005 (let* ((dir (if (f-symlink? dir) 2006 (file-truename dir) 2007 dir)) 2008 (watch (or watch (make-lsp-watch :root-directory dir))) 2009 (dirs-to-watch (lsp--all-watchable-directories dir ignored-directories))) 2010 (lsp-log "Creating watchers for following %s folders:\n %s" 2011 (length dirs-to-watch) 2012 (s-join "\n " dirs-to-watch)) 2013 (when (or 2014 (not warn-big-repo?) 2015 (not lsp-file-watch-threshold) 2016 (let ((number-of-directories (length dirs-to-watch))) 2017 (or 2018 (< number-of-directories lsp-file-watch-threshold) 2019 (condition-case nil 2020 (lsp--ask-about-watching-big-repo number-of-directories dir) 2021 (quit))))) 2022 (dolist (current-dir dirs-to-watch) 2023 (condition-case err 2024 (progn 2025 (puthash 2026 current-dir 2027 (file-notify-add-watch current-dir 2028 '(change) 2029 (lambda (event) 2030 (lsp--folder-watch-callback event callback watch ignored-files ignored-directories))) 2031 (lsp-watch-descriptors watch))) 2032 (error (lsp-log "Failed to create a watch for %s: message" (error-message-string err))) 2033 (file-missing (lsp-log "Failed to create a watch for %s: message" (error-message-string err)))))) 2034 watch)) 2035 2036 (defun lsp-kill-watch (watch) 2037 "Delete WATCH." 2038 (-> watch lsp-watch-descriptors hash-table-values (-each #'file-notify-rm-watch)) 2039 (ht-clear! (lsp-watch-descriptors watch))) 2040 2041 (defun lsp-json-bool (val) 2042 "Convert VAL to JSON boolean." 2043 (if val t :json-false)) 2044 2045 (defmacro with-lsp-workspace (workspace &rest body) 2046 "Helper macro for invoking BODY in WORKSPACE context." 2047 (declare (debug (form body)) 2048 (indent 1)) 2049 `(let ((lsp--cur-workspace ,workspace)) ,@body)) 2050 2051 (defmacro with-lsp-workspaces (workspaces &rest body) 2052 "Helper macro for invoking BODY against multiple WORKSPACES." 2053 (declare (debug (form body)) 2054 (indent 1)) 2055 `(let ((lsp--buffer-workspaces ,workspaces)) ,@body)) 2056 2057 2058 2059 (defmacro lsp-consistency-check (package) 2060 `(defconst ,(intern (concat (symbol-name package) 2061 "-plist-value-when-compiled")) 2062 (eval-when-compile lsp-use-plists))) 2063 2064 2065 ;; loading code-workspace files 2066 2067 ;;;###autoload 2068 (defun lsp-load-vscode-workspace (file) 2069 "Load vscode workspace from FILE" 2070 (interactive "fSelect file to import: ") 2071 (mapc #'lsp-workspace-folders-remove (lsp-session-folders (lsp-session))) 2072 2073 (let ((dir (f-dirname file))) 2074 (->> file 2075 (json-read-file) 2076 (alist-get 'folders) 2077 (-map (-lambda ((&alist 'path)) 2078 (lsp-workspace-folders-add (expand-file-name path dir))))))) 2079 2080 ;;;###autoload 2081 (defun lsp-save-vscode-workspace (file) 2082 "Save vscode workspace to FILE" 2083 (interactive "FSelect file to save to: ") 2084 2085 (let ((json-encoding-pretty-print t)) 2086 (f-write-text (json-encode 2087 `((folders . ,(->> (lsp-session) 2088 (lsp-session-folders) 2089 (--map `((path . ,it))))))) 2090 'utf-8 2091 file))) 2092 2093 2094 (defmacro lsp-foreach-workspace (&rest body) 2095 "Execute BODY for each of the current workspaces." 2096 (declare (debug (form body))) 2097 `(--map (with-lsp-workspace it ,@body) (lsp-workspaces))) 2098 2099 (defmacro when-lsp-workspace (workspace &rest body) 2100 "Helper macro for invoking BODY in WORKSPACE context if present." 2101 (declare (debug (form body)) 2102 (indent 1)) 2103 `(when-let ((lsp--cur-workspace ,workspace)) ,@body)) 2104 2105 (lsp-defun lsp--window-show-quick-pick (_workspace (&ShowQuickPickParams :place-holder :can-pick-many :items)) 2106 (if-let* ((selectfunc (if can-pick-many #'completing-read-multiple #'completing-read)) 2107 (itemLabels (seq-map (-lambda ((item &as &QuickPickItem :label)) (format "%s" label)) 2108 items)) 2109 (result (funcall-interactively 2110 selectfunc 2111 (format "%s%s " place-holder (if can-pick-many " (* for all)" "")) itemLabels)) 2112 (choices (if (listp result) 2113 (if (equal result '("*")) 2114 itemLabels 2115 result) 2116 (list result)))) 2117 (vconcat (seq-filter #'identity (seq-map (-lambda ((item &as &QuickPickItem :label :user-data)) 2118 (if (member label choices) 2119 (lsp-make-quick-pick-item :label label :picked t :user-data user-data) 2120 nil)) 2121 items))))) 2122 2123 (lsp-defun lsp--window-show-input-box (_workspace (&ShowInputBoxParams :prompt :value?)) 2124 (read-string (format "%s: " prompt) (or value? ""))) 2125 2126 (lsp-defun lsp--window-show-message (_workspace (&ShowMessageRequestParams :message :type)) 2127 "Send the server's messages to log. 2128 PARAMS - the data sent from _WORKSPACE." 2129 (funcall (cl-case type 2130 (1 'lsp--error) 2131 (2 'lsp--warn) 2132 (t 'lsp--info)) 2133 "%s" 2134 message)) 2135 2136 (lsp-defun lsp--window-log-message (workspace (&ShowMessageRequestParams :message :type)) 2137 "Send the server's messages to log. 2138 PARAMS - the data sent from WORKSPACE." 2139 (ignore 2140 (let ((client (lsp--workspace-client workspace))) 2141 (when (or (not client) 2142 (cl-notany (-rpartial #'string-match-p message) 2143 (lsp--client-ignore-messages client))) 2144 (lsp-log "%s" (lsp--propertize message type)))))) 2145 2146 (lsp-defun lsp--window-log-message-request ((&ShowMessageRequestParams :message :type :actions?)) 2147 "Display a message request to user sending the user selection back to server." 2148 (let* ((message (lsp--propertize message type)) 2149 (choices (seq-map #'lsp:message-action-item-title actions?))) 2150 (if choices 2151 (completing-read (concat message " ") (seq-into choices 'list) nil t) 2152 (lsp-log message)))) 2153 2154 (lsp-defun lsp--window-show-document ((&ShowDocumentParams :uri :selection?)) 2155 "Show document URI in a buffer and go to SELECTION if any." 2156 (let ((path (lsp--uri-to-path uri))) 2157 (when (f-exists? path) 2158 (with-current-buffer (find-file path) 2159 (when selection? 2160 (goto-char (lsp--position-to-point (lsp:range-start selection?)))) 2161 t)))) 2162 2163 (defcustom lsp-progress-prefix "⌛ " 2164 "Progress prefix." 2165 :group 'lsp-mode 2166 :type 'string 2167 :package-version '(lsp-mode . "8.0.0")) 2168 2169 (defcustom lsp-progress-function #'lsp-on-progress-modeline 2170 "Function for handling the progress notifications." 2171 :group 'lsp-mode 2172 :type '(choice 2173 (const :tag "Use modeline" lsp-on-progress-modeline) 2174 (const :tag "Legacy(uses either `progress-reporter' or `spinner' based on `lsp-progress-via-spinner')" 2175 lsp-on-progress-legacy) 2176 (const :tag "Ignore" ignore) 2177 (function :tag "Other function")) 2178 :package-version '(lsp-mode . "8.0.0")) 2179 2180 (defcustom lsp-request-while-no-input-may-block nil 2181 "Have `lsp-request-while-no-input` block unless `non-essential` is t." 2182 :group 'lsp-mode 2183 :type 'boolean) 2184 2185 (defun lsp--progress-status () 2186 "Returns the status of the progress for the current workspaces." 2187 (-let ((progress-status 2188 (s-join 2189 "|" 2190 (-keep 2191 (lambda (workspace) 2192 (let ((tokens (lsp--workspace-work-done-tokens workspace))) 2193 (unless (ht-empty? tokens) 2194 (mapconcat 2195 (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?)) 2196 (concat (if percentage? 2197 (if (numberp percentage?) 2198 (format "%.0f%%%% " percentage?) 2199 (format "%s%%%% " percentage?)) 2200 "") 2201 (or message? title))) 2202 (ht-values tokens) 2203 "|")))) 2204 (lsp-workspaces))))) 2205 (unless (s-blank? progress-status) 2206 (concat lsp-progress-prefix progress-status " ")))) 2207 2208 (lsp-defun lsp-on-progress-modeline (workspace (&ProgressParams :token :value 2209 (value &as &WorkDoneProgress :kind))) 2210 "PARAMS contains the progress data. 2211 WORKSPACE is the workspace that contains the progress token." 2212 (add-to-list 'global-mode-string '(t (:eval (lsp--progress-status)))) 2213 (pcase kind 2214 ("begin" (lsp-workspace-set-work-done-token token value workspace)) 2215 ("report" (lsp-workspace-set-work-done-token token value workspace)) 2216 ("end" (lsp-workspace-rem-work-done-token token workspace))) 2217 (force-mode-line-update)) 2218 2219 (lsp-defun lsp-on-progress-legacy (workspace (&ProgressParams :token :value 2220 (value &as &WorkDoneProgress :kind))) 2221 "PARAMS contains the progress data. 2222 WORKSPACE is the workspace that contains the progress token." 2223 (pcase kind 2224 ("begin" 2225 (-let* (((&WorkDoneProgressBegin :title :percentage?) value) 2226 (reporter 2227 (if lsp-progress-via-spinner 2228 (let* ((spinner-strings (alist-get (lsp-progress-spinner-type) spinner-types)) 2229 ;; Set message as a tooltip for the spinner strings 2230 (propertized-strings 2231 (seq-map (lambda (string) (propertize string 'help-echo title)) 2232 spinner-strings)) 2233 (spinner-type (vconcat propertized-strings))) 2234 ;; The progress relates to the server as a whole, 2235 ;; display it on all buffers. 2236 (mapcar (lambda (buffer) 2237 (lsp-with-current-buffer buffer 2238 (spinner-start spinner-type)) 2239 buffer) 2240 (lsp--workspace-buffers workspace))) 2241 (if percentage? 2242 (make-progress-reporter title 0 100 percentage?) 2243 ;; No percentage, just progress 2244 (make-progress-reporter title nil nil))))) 2245 (lsp-workspace-set-work-done-token token reporter workspace))) 2246 ("report" 2247 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2248 (unless lsp-progress-via-spinner 2249 (progress-reporter-update reporter (lsp:work-done-progress-report-percentage? value))))) 2250 2251 ("end" 2252 (when-let ((reporter (lsp-workspace-get-work-done-token token workspace))) 2253 (if lsp-progress-via-spinner 2254 (mapc (lambda (buffer) 2255 (when (lsp-buffer-live-p buffer) 2256 (lsp-with-current-buffer buffer 2257 (spinner-stop)))) 2258 reporter) 2259 (progress-reporter-done reporter)) 2260 (lsp-workspace-rem-work-done-token token workspace))))) 2261 2262 2263 ;; diagnostics 2264 2265 (defvar lsp-diagnostic-filter nil 2266 "A a function which will be called with 2267 `&PublishDiagnosticsParams' and `workspace' which can be used 2268 to filter out the diagnostics. The function should return 2269 `&PublishDiagnosticsParams'. 2270 2271 Common usecase are: 2272 1. Filter the diagnostics for a particular language server. 2273 2. Filter out the diagnostics under specific level.") 2274 2275 (defvar lsp-diagnostic-stats (ht)) 2276 2277 (defun lsp-diagnostics (&optional current-workspace?) 2278 "Return the diagnostics from all workspaces." 2279 (or (pcase (if current-workspace? 2280 (lsp-workspaces) 2281 (lsp--session-workspaces (lsp-session))) 2282 (`() ()) 2283 (`(,workspace) (lsp--workspace-diagnostics workspace)) 2284 (`,workspaces (let ((result (make-hash-table :test 'equal))) 2285 (mapc (lambda (workspace) 2286 (->> workspace 2287 (lsp--workspace-diagnostics) 2288 (maphash (lambda (file-name diagnostics) 2289 (puthash file-name 2290 (append (gethash file-name result) diagnostics) 2291 result))))) 2292 workspaces) 2293 result))) 2294 (ht))) 2295 2296 (defun lsp-diagnostics-stats-for (path) 2297 "Get diagnostics statistics for PATH. 2298 The result format is vector [_ errors warnings infos hints] or nil." 2299 (gethash (lsp--fix-path-casing path) lsp-diagnostic-stats)) 2300 2301 (defun lsp-diagnostics--update-path (path new-stats) 2302 (let ((new-stats (copy-sequence new-stats)) 2303 (path (lsp--fix-path-casing (directory-file-name path)))) 2304 (if-let ((old-data (gethash path lsp-diagnostic-stats))) 2305 (dotimes (idx 5) 2306 (cl-callf + (aref old-data idx) 2307 (aref new-stats idx))) 2308 (puthash path new-stats lsp-diagnostic-stats)))) 2309 2310 (lsp-defun lsp--on-diagnostics-update-stats (workspace 2311 (&PublishDiagnosticsParams :uri :diagnostics)) 2312 (let ((path (lsp--fix-path-casing (lsp--uri-to-path uri))) 2313 (new-stats (make-vector 5 0))) 2314 (mapc (-lambda ((&Diagnostic :severity?)) 2315 (cl-incf (aref new-stats (or severity? 1)))) 2316 diagnostics) 2317 (when-let ((old-diags (gethash path (lsp--workspace-diagnostics workspace)))) 2318 (mapc (-lambda ((&Diagnostic :severity?)) 2319 (cl-decf (aref new-stats (or severity? 1)))) 2320 old-diags)) 2321 (lsp-diagnostics--update-path path new-stats) 2322 (while (not (string= path (setf path (file-name-directory 2323 (directory-file-name path))))) 2324 (lsp-diagnostics--update-path path new-stats)))) 2325 2326 (defun lsp--on-diagnostics (workspace params) 2327 "Callback for textDocument/publishDiagnostics. 2328 interface PublishDiagnosticsParams { 2329 uri: string; 2330 diagnostics: Diagnostic[]; 2331 } 2332 PARAMS contains the diagnostics data. 2333 WORKSPACE is the workspace that contains the diagnostics." 2334 (when lsp-diagnostic-filter 2335 (setf params (funcall lsp-diagnostic-filter params workspace))) 2336 2337 (lsp--on-diagnostics-update-stats workspace params) 2338 2339 (-let* (((&PublishDiagnosticsParams :uri :diagnostics) params) 2340 (lsp--virtual-buffer-mappings (ht)) 2341 (file (lsp--fix-path-casing (lsp--uri-to-path uri))) 2342 (workspace-diagnostics (lsp--workspace-diagnostics workspace))) 2343 2344 (if (seq-empty-p diagnostics) 2345 (remhash file workspace-diagnostics) 2346 (puthash file (append diagnostics nil) workspace-diagnostics)) 2347 2348 (run-hooks 'lsp-diagnostics-updated-hook))) 2349 2350 (defun lsp-diagnostics--workspace-cleanup (workspace) 2351 (->> workspace 2352 (lsp--workspace-diagnostics) 2353 (maphash (lambda (key _) 2354 (lsp--on-diagnostics-update-stats 2355 workspace 2356 (lsp-make-publish-diagnostics-params 2357 :uri (lsp--path-to-uri key) 2358 :diagnostics []))))) 2359 (clrhash (lsp--workspace-diagnostics workspace))) 2360 2361 2362 2363 ;; textDocument/foldingRange support 2364 2365 (cl-defstruct lsp--folding-range beg end kind children) 2366 2367 (defvar-local lsp--cached-folding-ranges nil) 2368 (defvar-local lsp--cached-nested-folding-ranges nil) 2369 2370 (defun lsp--folding-range-width (range) 2371 (- (lsp--folding-range-end range) 2372 (lsp--folding-range-beg range))) 2373 2374 (defun lsp--get-folding-ranges () 2375 "Get the folding ranges for the current buffer." 2376 (unless (eq (buffer-chars-modified-tick) (car lsp--cached-folding-ranges)) 2377 (let* ((ranges (lsp-request "textDocument/foldingRange" 2378 `(:textDocument ,(lsp--text-document-identifier)))) 2379 (sorted-line-col-pairs (->> ranges 2380 (cl-mapcan (-lambda ((&FoldingRange :start-line 2381 :start-character? 2382 :end-line 2383 :end-character?)) 2384 (list (cons start-line start-character?) 2385 (cons end-line end-character?)))) 2386 (-sort #'lsp--line-col-comparator))) 2387 (line-col-to-point-map (lsp--convert-line-col-to-points-batch 2388 sorted-line-col-pairs))) 2389 (setq lsp--cached-folding-ranges 2390 (cons (buffer-chars-modified-tick) 2391 (--> ranges 2392 (seq-map (-lambda ((range &as 2393 &FoldingRange :start-line 2394 :start-character? 2395 :end-line 2396 :end-character? 2397 :kind?)) 2398 (make-lsp--folding-range 2399 :beg (ht-get line-col-to-point-map 2400 (cons start-line start-character?)) 2401 :end (ht-get line-col-to-point-map 2402 (cons end-line end-character?)) 2403 :kind kind?)) 2404 it) 2405 (seq-filter (lambda (folding-range) 2406 (< (lsp--folding-range-beg folding-range) 2407 (lsp--folding-range-end folding-range))) 2408 it) 2409 (seq-into it 'list) 2410 (delete-dups it)))))) 2411 (cdr lsp--cached-folding-ranges)) 2412 2413 (defun lsp--get-nested-folding-ranges () 2414 "Get a list of nested folding ranges for the current buffer." 2415 (-let [(tick . _) lsp--cached-folding-ranges] 2416 (if (and (eq tick (buffer-chars-modified-tick)) 2417 lsp--cached-nested-folding-ranges) 2418 lsp--cached-nested-folding-ranges 2419 (setq lsp--cached-nested-folding-ranges 2420 (lsp--folding-range-build-trees (lsp--get-folding-ranges)))))) 2421 2422 (defun lsp--folding-range-build-trees (ranges) 2423 (setq ranges (seq-sort #'lsp--range-before-p ranges)) 2424 (let* ((dummy-node (make-lsp--folding-range 2425 :beg most-negative-fixnum 2426 :end most-positive-fixnum)) 2427 (stack (list dummy-node))) 2428 (dolist (range ranges) 2429 (while (not (lsp--range-inside-p range (car stack))) 2430 (pop stack)) 2431 (push range (lsp--folding-range-children (car stack))) 2432 (push range stack)) 2433 (lsp--folding-range-children dummy-node))) 2434 2435 (defun lsp--range-inside-p (r1 r2) 2436 "Return non-nil if folding range R1 lies inside R2" 2437 (and (>= (lsp--folding-range-beg r1) (lsp--folding-range-beg r2)) 2438 (<= (lsp--folding-range-end r1) (lsp--folding-range-end r2)))) 2439 2440 (defun lsp--range-before-p (r1 r2) 2441 "Return non-nil if folding range R1 ends before R2" 2442 ;; Ensure r1 comes before r2 2443 (or (< (lsp--folding-range-beg r1) 2444 (lsp--folding-range-beg r2)) 2445 ;; If beg(r1) == beg(r2) make sure r2 ends first 2446 (and (= (lsp--folding-range-beg r1) 2447 (lsp--folding-range-beg r2)) 2448 (< (lsp--folding-range-end r2) 2449 (lsp--folding-range-end r1))))) 2450 2451 (defun lsp--point-inside-range-p (point range) 2452 "Return non-nil if POINT lies inside folding range RANGE." 2453 (and (>= point (lsp--folding-range-beg range)) 2454 (<= point (lsp--folding-range-end range)))) 2455 2456 (cl-defun lsp--get-current-innermost-folding-range (&optional (point (point))) 2457 "Return the innermost folding range POINT lies in." 2458 (seq-reduce (lambda (innermost-range curr-range) 2459 (if (and (lsp--point-inside-range-p point curr-range) 2460 (or (null innermost-range) 2461 (lsp--range-inside-p curr-range innermost-range))) 2462 curr-range 2463 innermost-range)) 2464 (lsp--get-folding-ranges) 2465 nil)) 2466 2467 (cl-defun lsp--get-current-outermost-folding-range (&optional (point (point))) 2468 "Return the outermost folding range POINT lies in." 2469 (cdr (seq-reduce (-lambda ((best-pair &as outermost-width . _) curr-range) 2470 (let ((curr-width (lsp--folding-range-width curr-range))) 2471 (if (and (lsp--point-inside-range-p point curr-range) 2472 (or (null best-pair) 2473 (> curr-width outermost-width))) 2474 (cons curr-width curr-range) 2475 best-pair))) 2476 (lsp--get-folding-ranges) 2477 nil))) 2478 2479 (defun lsp--folding-range-at-point-bounds () 2480 (when (and lsp-enable-folding 2481 (lsp-feature? "textDocument/foldingRange")) 2482 (if-let ((range (lsp--get-current-innermost-folding-range))) 2483 (cons (lsp--folding-range-beg range) 2484 (lsp--folding-range-end range))))) 2485 (put 'lsp--folding-range 'bounds-of-thing-at-point 2486 #'lsp--folding-range-at-point-bounds) 2487 2488 (defun lsp--get-nearest-folding-range (&optional backward) 2489 (let ((point (point)) 2490 (found nil)) 2491 (while (not 2492 (or found 2493 (if backward 2494 (<= point (point-min)) 2495 (>= point (point-max))))) 2496 (if backward (cl-decf point) (cl-incf point)) 2497 (setq found (lsp--get-current-innermost-folding-range point))) 2498 found)) 2499 2500 (defun lsp--folding-range-at-point-forward-op (n) 2501 (when (and lsp-enable-folding 2502 (not (zerop n)) 2503 (lsp-feature? "textDocument/foldingRange")) 2504 (cl-block break 2505 (dotimes (_ (abs n)) 2506 (if-let ((range (lsp--get-nearest-folding-range (< n 0)))) 2507 (goto-char (if (< n 0) 2508 (lsp--folding-range-beg range) 2509 (lsp--folding-range-end range))) 2510 (cl-return-from break)))))) 2511 (put 'lsp--folding-range 'forward-op 2512 #'lsp--folding-range-at-point-forward-op) 2513 2514 (defun lsp--folding-range-at-point-beginning-op () 2515 (goto-char (car (lsp--folding-range-at-point-bounds)))) 2516 (put 'lsp--folding-range 'beginning-op 2517 #'lsp--folding-range-at-point-beginning-op) 2518 2519 (defun lsp--folding-range-at-point-end-op () 2520 (goto-char (cdr (lsp--folding-range-at-point-bounds)))) 2521 (put 'lsp--folding-range 'end-op 2522 #'lsp--folding-range-at-point-end-op) 2523 2524 (defun lsp--range-at-point-bounds () 2525 (or (lsp--folding-range-at-point-bounds) 2526 (when-let ((range (and 2527 (lsp-feature? "textDocument/hover") 2528 (->> (lsp--text-document-position-params) 2529 (lsp-request "textDocument/hover") 2530 (lsp:hover-range?))))) 2531 (lsp--range-to-region range)))) 2532 2533 ;; A more general purpose "thing", useful for applications like focus.el 2534 (put 'lsp--range 'bounds-of-thing-at-point 2535 #'lsp--range-at-point-bounds) 2536 2537 (defun lsp--log-io-p (method) 2538 "Return non nil if should log for METHOD." 2539 (and lsp-log-io 2540 (or (not lsp-log-io-allowlist-methods) 2541 (member method lsp-log-io-allowlist-methods)))) 2542 2543 2544 ;; toggles 2545 2546 (defun lsp-toggle-trace-io () 2547 "Toggle client-server protocol logging." 2548 (interactive) 2549 (setq lsp-log-io (not lsp-log-io)) 2550 (lsp--info "Server logging %s." (if lsp-log-io "enabled" "disabled"))) 2551 2552 (defun lsp-toggle-signature-auto-activate () 2553 "Toggle signature auto activate." 2554 (interactive) 2555 (setq lsp-signature-auto-activate 2556 (unless lsp-signature-auto-activate '(:on-trigger-char))) 2557 (lsp--info "Signature autoactivate %s." (if lsp-signature-auto-activate "enabled" "disabled")) 2558 (lsp--update-signature-help-hook)) 2559 2560 (defun lsp-toggle-on-type-formatting () 2561 "Toggle on type formatting." 2562 (interactive) 2563 (setq lsp-enable-on-type-formatting (not lsp-enable-on-type-formatting)) 2564 (lsp--info "On type formatting is %s." (if lsp-enable-on-type-formatting "enabled" "disabled")) 2565 (lsp--update-on-type-formatting-hook)) 2566 2567 (defun lsp-toggle-symbol-highlight () 2568 "Toggle symbol highlighting." 2569 (interactive) 2570 (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting)) 2571 2572 (cond 2573 ((and lsp-enable-symbol-highlighting 2574 (lsp-feature? "textDocument/documentHighlight")) 2575 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t) 2576 (lsp--info "Symbol highlighting enabled in current buffer.")) 2577 ((not lsp-enable-symbol-highlighting) 2578 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 2579 (lsp--remove-overlays 'lsp-highlight) 2580 (lsp--info "Symbol highlighting disabled in current buffer.")))) 2581 2582 2583 ;; keybindings 2584 (defvar lsp--binding-descriptions nil 2585 "List of key binding/short description pair.") 2586 2587 (defmacro lsp-define-conditional-key (keymap key def desc cond &rest bindings) 2588 "In KEYMAP, define key sequence KEY as DEF conditionally. 2589 This is like `define-key', except the definition disappears 2590 whenever COND evaluates to nil. 2591 DESC is the short-description for the binding. 2592 BINDINGS is a list of (key def desc cond)." 2593 (declare (indent defun) 2594 (debug (form form form form form &rest sexp))) 2595 (->> (cl-list* key def desc cond bindings) 2596 (-partition 4) 2597 (-mapcat (-lambda ((key def desc cond)) 2598 `((define-key ,keymap ,key 2599 '(menu-item 2600 ,(format "maybe-%s" def) 2601 ,def 2602 :filter 2603 (lambda (item) 2604 (when (with-current-buffer (or (when (buffer-live-p lsp--describe-buffer) 2605 lsp--describe-buffer) 2606 (current-buffer)) 2607 ,cond) 2608 item)))) 2609 (when (stringp ,key) 2610 (setq lsp--binding-descriptions 2611 (append lsp--binding-descriptions '(,key ,desc))))))) 2612 macroexp-progn)) 2613 2614 (defvar lsp--describe-buffer nil) 2615 2616 (defun lsp-describe-buffer-bindings-advice (fn buffer &optional prefix menus) 2617 (let ((lsp--describe-buffer buffer)) 2618 (funcall fn buffer prefix menus))) 2619 2620 (advice-add 'describe-buffer-bindings 2621 :around 2622 #'lsp-describe-buffer-bindings-advice) 2623 2624 (defun lsp--prepend-prefix (mappings) 2625 (->> mappings 2626 (-partition 2) 2627 (-mapcat (-lambda ((key description)) 2628 (list (concat lsp-keymap-prefix " " key) 2629 description))))) 2630 2631 (defvar lsp-command-map 2632 (-doto (make-sparse-keymap) 2633 (lsp-define-conditional-key 2634 ;; workspaces 2635 "wD" lsp-disconnect "disconnect" (lsp-workspaces) 2636 "wd" lsp-describe-session "describe session" t 2637 "wq" lsp-workspace-shutdown "shutdown server" (lsp-workspaces) 2638 "wr" lsp-workspace-restart "restart server" (lsp-workspaces) 2639 "ws" lsp "start server" t 2640 2641 ;; formatting 2642 "==" lsp-format-buffer "format buffer" (or (lsp-feature? "textDocument/rangeFormatting") 2643 (lsp-feature? "textDocument/formatting")) 2644 "=r" lsp-format-region "format region" (lsp-feature? "textDocument/rangeFormatting") 2645 2646 ;; folders 2647 "Fa" lsp-workspace-folders-add "add folder" t 2648 "Fb" lsp-workspace-blocklist-remove "un-blocklist folder" t 2649 "Fr" lsp-workspace-folders-remove "remove folder" t 2650 2651 ;; toggles 2652 "TD" lsp-modeline-diagnostics-mode "toggle modeline diagnostics" (lsp-feature? 2653 "textDocument/publishDiagnostics") 2654 "TL" lsp-toggle-trace-io "toggle log io" t 2655 "TS" lsp-ui-sideline-mode "toggle sideline" (featurep 'lsp-ui-sideline) 2656 "TT" lsp-treemacs-sync-mode "toggle treemacs integration" (featurep 'lsp-treemacs) 2657 "Ta" lsp-modeline-code-actions-mode "toggle modeline code actions" (lsp-feature? 2658 "textDocument/codeAction") 2659 "Tb" lsp-headerline-breadcrumb-mode "toggle breadcrumb" (lsp-feature? 2660 "textDocument/documentSymbol") 2661 "Td" lsp-ui-doc-mode "toggle documentation popup" (featurep 'lsp-ui-doc) 2662 "Tf" lsp-toggle-on-type-formatting "toggle on type formatting" (lsp-feature? 2663 "textDocument/onTypeFormatting") 2664 "Th" lsp-toggle-symbol-highlight "toggle highlighting" (lsp-feature? "textDocument/documentHighlight") 2665 "Tl" lsp-lens-mode "toggle lenses" (lsp-feature? "textDocument/codeLens") 2666 "Ts" lsp-toggle-signature-auto-activate "toggle signature" (lsp-feature? "textDocument/signatureHelp") 2667 2668 ;; goto 2669 "ga" xref-find-apropos "find symbol in workspace" (lsp-feature? "workspace/symbol") 2670 "gd" lsp-find-declaration "find declarations" (lsp-feature? "textDocument/declaration") 2671 "ge" lsp-treemacs-errors-list "show errors" (fboundp 'lsp-treemacs-errors-list) 2672 "gg" lsp-find-definition "find definitions" (lsp-feature? "textDocument/definition") 2673 "gh" lsp-treemacs-call-hierarchy "call hierarchy" (and (lsp-feature? "callHierarchy/incomingCalls") 2674 (fboundp 'lsp-treemacs-call-hierarchy)) 2675 "gi" lsp-find-implementation "find implementations" (lsp-feature? "textDocument/implementation") 2676 "gr" lsp-find-references "find references" (lsp-feature? "textDocument/references") 2677 "gt" lsp-find-type-definition "find type definition" (lsp-feature? "textDocument/typeDefinition") 2678 2679 ;; help 2680 "hg" lsp-ui-doc-glance "glance symbol" (and (featurep 'lsp-ui-doc) 2681 (lsp-feature? "textDocument/hover")) 2682 "hh" lsp-describe-thing-at-point "describe symbol at point" (lsp-feature? "textDocument/hover") 2683 "hs" lsp-signature-activate "signature help" (lsp-feature? "textDocument/signatureHelp") 2684 2685 ;; refactoring 2686 "ro" lsp-organize-imports "organize imports" (lsp-feature? "textDocument/codeAction") 2687 "rr" lsp-rename "rename" (lsp-feature? "textDocument/rename") 2688 2689 ;; actions 2690 "aa" lsp-execute-code-action "code actions" (lsp-feature? "textDocument/codeAction") 2691 "ah" lsp-document-highlight "highlight symbol" (lsp-feature? "textDocument/documentHighlight") 2692 "al" lsp-avy-lens "lens" (and (bound-and-true-p lsp-lens-mode) (featurep 'avy)) 2693 2694 ;; peeks 2695 "Gg" lsp-ui-peek-find-definitions "peek definitions" (and (lsp-feature? "textDocument/definition") 2696 (fboundp 'lsp-ui-peek-find-definitions)) 2697 "Gi" lsp-ui-peek-find-implementation "peek implementations" (and 2698 (fboundp 'lsp-ui-peek-find-implementation) 2699 (lsp-feature? "textDocument/implementation")) 2700 "Gr" lsp-ui-peek-find-references "peek references" (and (fboundp 'lsp-ui-peek-find-references) 2701 (lsp-feature? "textDocument/references")) 2702 "Gs" lsp-ui-peek-find-workspace-symbol "peek workspace symbol" (and (fboundp 2703 'lsp-ui-peek-find-workspace-symbol) 2704 (lsp-feature? "workspace/symbol"))))) 2705 2706 2707 ;; which-key integration 2708 2709 (declare-function which-key-add-major-mode-key-based-replacements "ext:which-key") 2710 (declare-function which-key-add-key-based-replacements "ext:which-key") 2711 2712 (defun lsp-enable-which-key-integration (&optional all-modes) 2713 "Adds descriptions for `lsp-mode-map' to `which-key-mode' for the current 2714 active `major-mode', or for all major modes when ALL-MODES is t." 2715 (cl-flet ((which-key-fn (if all-modes 2716 'which-key-add-key-based-replacements 2717 (apply-partially 'which-key-add-major-mode-key-based-replacements major-mode)))) 2718 (apply 2719 #'which-key-fn 2720 (lsp--prepend-prefix 2721 (cl-list* 2722 "" "lsp" 2723 "w" "workspaces" 2724 "F" "folders" 2725 "=" "formatting" 2726 "T" "toggle" 2727 "g" "goto" 2728 "h" "help" 2729 "r" "refactor" 2730 "a" "code actions" 2731 "G" "peek" 2732 lsp--binding-descriptions))))) 2733 2734 2735 ;; Globbing syntax 2736 2737 ;; We port VSCode's glob-to-regexp code 2738 ;; (https://github.com/Microsoft/vscode/blob/466da1c9013c624140f6d1473b23a870abc82d44/src/vs/base/common/glob.ts) 2739 ;; since the LSP globbing syntax seems to be the same as that of 2740 ;; VSCode. 2741 2742 (defconst lsp-globstar "**" 2743 "Globstar pattern.") 2744 2745 (defconst lsp-glob-split ?/ 2746 "The character by which we split path components in a glob 2747 pattern.") 2748 2749 (defconst lsp-path-regexp "[/\\\\]" 2750 "Forward or backslash to be used as a path separator in 2751 computed regexps.") 2752 2753 (defconst lsp-non-path-regexp "[^/\\\\]" 2754 "A regexp matching anything other than a slash.") 2755 2756 (defconst lsp-globstar-regexp 2757 (format "\\(?:%s\\|%s+%s\\|%s%s+\\)*?" 2758 lsp-path-regexp 2759 lsp-non-path-regexp lsp-path-regexp 2760 lsp-path-regexp lsp-non-path-regexp) 2761 "Globstar in regexp form.") 2762 2763 (defun lsp-split-glob-pattern (pattern split-char) 2764 "Split PATTERN at SPLIT-CHAR while respecting braces and brackets." 2765 (when pattern 2766 (let ((segments nil) 2767 (in-braces nil) 2768 (in-brackets nil) 2769 (current-segment "")) 2770 (dolist (char (string-to-list pattern)) 2771 (cl-block 'exit-point 2772 (if (eq char split-char) 2773 (when (and (null in-braces) 2774 (null in-brackets)) 2775 (push current-segment segments) 2776 (setq current-segment "") 2777 (cl-return-from 'exit-point)) 2778 (pcase char 2779 (?{ 2780 (setq in-braces t)) 2781 (?} 2782 (setq in-braces nil)) 2783 (?\[ 2784 (setq in-brackets t)) 2785 (?\] 2786 (setq in-brackets nil)))) 2787 (setq current-segment (concat current-segment 2788 (char-to-string char))))) 2789 (unless (string-empty-p current-segment) 2790 (push current-segment segments)) 2791 (nreverse segments)))) 2792 2793 (defun lsp--glob-to-regexp (pattern) 2794 "Helper function to convert a PATTERN from LSP's glob syntax to 2795 an Elisp regexp." 2796 (if (string-empty-p pattern) 2797 "" 2798 (let ((current-regexp "") 2799 (glob-segments (lsp-split-glob-pattern pattern lsp-glob-split))) 2800 (if (-all? (lambda (segment) (eq segment lsp-globstar)) 2801 glob-segments) 2802 ".*" 2803 (let ((prev-segment-was-globstar nil)) 2804 (seq-do-indexed 2805 (lambda (segment index) 2806 (if (string-equal segment lsp-globstar) 2807 (unless prev-segment-was-globstar 2808 (setq current-regexp (concat current-regexp 2809 lsp-globstar-regexp)) 2810 (setq prev-segment-was-globstar t)) 2811 (let ((in-braces nil) 2812 (brace-val "") 2813 (in-brackets nil) 2814 (bracket-val "")) 2815 (dolist (char (string-to-list segment)) 2816 (cond 2817 ((and (not (char-equal char ?\})) 2818 in-braces) 2819 (setq brace-val (concat brace-val 2820 (char-to-string char)))) 2821 ((and in-brackets 2822 (or (not (char-equal char ?\])) 2823 (string-empty-p bracket-val))) 2824 (let ((curr (cond 2825 ((char-equal char ?-) 2826 "-") 2827 ;; NOTE: ?\^ and ?^ are different characters 2828 ((and (memq char '(?^ ?!)) 2829 (string-empty-p bracket-val)) 2830 "^") 2831 ((char-equal char lsp-glob-split) 2832 "") 2833 (t 2834 (regexp-quote (char-to-string char)))))) 2835 (setq bracket-val (concat bracket-val curr)))) 2836 (t 2837 (cl-case char 2838 (?{ 2839 (setq in-braces t)) 2840 (?\[ 2841 (setq in-brackets t)) 2842 (?} 2843 (let* ((choices (lsp-split-glob-pattern brace-val ?\,)) 2844 (brace-regexp (concat "\\(?:" 2845 (mapconcat #'lsp--glob-to-regexp choices "\\|") 2846 "\\)"))) 2847 (setq current-regexp (concat current-regexp 2848 brace-regexp)) 2849 (setq in-braces nil) 2850 (setq brace-val ""))) 2851 (?\] 2852 (setq current-regexp 2853 (concat current-regexp 2854 "[" bracket-val "]")) 2855 (setq in-brackets nil) 2856 (setq bracket-val "")) 2857 (?? 2858 (setq current-regexp 2859 (concat current-regexp 2860 lsp-non-path-regexp))) 2861 (?* 2862 (setq current-regexp 2863 (concat current-regexp 2864 lsp-non-path-regexp "*?"))) 2865 (t 2866 (setq current-regexp 2867 (concat current-regexp 2868 (regexp-quote (char-to-string char))))))))) 2869 (when (and (< index (1- (length glob-segments))) 2870 (or (not (string-equal (nth (1+ index) glob-segments) 2871 lsp-globstar)) 2872 (< (+ index 2) 2873 (length glob-segments)))) 2874 (setq current-regexp 2875 (concat current-regexp 2876 lsp-path-regexp))) 2877 (setq prev-segment-was-globstar nil)))) 2878 glob-segments) 2879 current-regexp))))) 2880 2881 ;; See https://github.com/emacs-lsp/lsp-mode/issues/2365 2882 (defun lsp-glob-unbrace-at-top-level (glob-pattern) 2883 "If GLOB-PATTERN does not start with a brace, return a singleton list 2884 containing GLOB-PATTERN. 2885 2886 If GLOB-PATTERN does start with a brace, return a list of the 2887 comma-separated globs within the top-level braces." 2888 (if (not (string-prefix-p "{" glob-pattern)) 2889 (list glob-pattern) 2890 (lsp-split-glob-pattern (substring glob-pattern 1 -1) ?\,))) 2891 2892 (defun lsp-glob-convert-to-wrapped-regexp (glob-pattern) 2893 "Convert GLOB-PATTERN to a regexp wrapped with the beginning- 2894 and end-of-string meta-characters." 2895 (concat "\\`" (lsp--glob-to-regexp (string-trim glob-pattern)) "\\'")) 2896 2897 (defun lsp-glob-to-regexps (glob-pattern) 2898 "Convert a GLOB-PATTERN to a list of Elisp regexps." 2899 (when-let* 2900 ((glob-pattern (cond ((hash-table-p glob-pattern) 2901 (ht-get glob-pattern "pattern")) 2902 ((stringp glob-pattern) glob-pattern) 2903 (t (error "Unknown glob-pattern type: %s" glob-pattern)))) 2904 (trimmed-pattern (string-trim glob-pattern)) 2905 (top-level-unbraced-patterns (lsp-glob-unbrace-at-top-level trimmed-pattern))) 2906 (seq-map #'lsp-glob-convert-to-wrapped-regexp 2907 top-level-unbraced-patterns))) 2908 2909 2910 2911 (defvar lsp-mode-menu) 2912 2913 (defun lsp-mouse-click (event) 2914 (interactive "e") 2915 (let* ((ec (event-start event)) 2916 (choice (x-popup-menu event lsp-mode-menu)) 2917 (action (lookup-key lsp-mode-menu (apply 'vector choice)))) 2918 2919 (select-window (posn-window ec)) 2920 2921 (unless (and (region-active-p) (eq action 'lsp-execute-code-action)) 2922 (goto-char (posn-point ec))) 2923 (run-with-idle-timer 2924 0.001 nil 2925 (lambda () 2926 (cl-labels ((check (value) (not (null value)))) 2927 (when choice 2928 (call-interactively action))))))) 2929 2930 (defvar lsp-mode-map 2931 (let ((map (make-sparse-keymap))) 2932 (define-key map (kbd "C-<down-mouse-1>") #'lsp-find-definition-mouse) 2933 (define-key map (kbd "C-<mouse-1>") #'ignore) 2934 (define-key map (kbd "<mouse-3>") #'lsp-mouse-click) 2935 (define-key map (kbd "C-S-SPC") #'lsp-signature-activate) 2936 (when lsp-keymap-prefix 2937 (define-key map (kbd lsp-keymap-prefix) lsp-command-map)) 2938 map) 2939 "Keymap for `lsp-mode'.") 2940 2941 (define-minor-mode lsp-mode "Mode for LSP interaction." 2942 :keymap lsp-mode-map 2943 :lighter 2944 (" LSP[" 2945 (lsp--buffer-workspaces 2946 (:eval (mapconcat #'lsp--workspace-print lsp--buffer-workspaces "][")) 2947 (:propertize "Disconnected" face warning)) 2948 "]") 2949 :group 'lsp-mode 2950 (when (and lsp-mode (not lsp--buffer-workspaces) (not lsp--buffer-deferred)) 2951 ;; fire up `lsp' when someone calls `lsp-mode' instead of `lsp' 2952 (lsp))) 2953 2954 (defvar lsp-mode-menu 2955 (easy-menu-create-menu 2956 nil 2957 `(["Go to definition" lsp-find-definition 2958 :active (lsp-feature? "textDocument/definition")] 2959 ["Find references" lsp-find-references 2960 :active (lsp-feature? "textDocument/references")] 2961 ["Find implementations" lsp-find-implementation 2962 :active (lsp-feature? "textDocument/implementation")] 2963 ["Find declarations" lsp-find-declaration 2964 :active (lsp-feature? "textDocument/declaration")] 2965 ["Go to type declaration" lsp-find-type-definition 2966 :active (lsp-feature? "textDocument/typeDefinition")] 2967 "--" 2968 ["Describe" lsp-describe-thing-at-point] 2969 ["Code action" lsp-execute-code-action] 2970 ["Format" lsp-format-buffer] 2971 ["Highlight references" lsp-document-highlight] 2972 ["Type Hierarchy" lsp-java-type-hierarchy 2973 :visible (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")] 2974 ["Type Hierarchy" lsp-treemacs-type-hierarchy 2975 :visible (and (not (lsp-can-execute-command? "java.navigate.resolveTypeHierarchy")) 2976 (functionp 'lsp-treemacs-type-hierarchy) 2977 (lsp-feature? "textDocument/typeHierarchy"))] 2978 ["Call Hierarchy" lsp-treemacs-call-hierarchy 2979 :visible (and (functionp 'lsp-treemacs-call-hierarchy) 2980 (lsp-feature? "textDocument/callHierarchy"))] 2981 ["Rename" lsp-rename 2982 :active (lsp-feature? "textDocument/rename")] 2983 "--" 2984 ("Session" 2985 ["View logs" lsp-workspace-show-log] 2986 ["Describe" lsp-describe-session] 2987 ["Shutdown" lsp-shutdown-workspace] 2988 ["Restart" lsp-restart-workspace]) 2989 ("Workspace Folders" 2990 ["Add" lsp-workspace-folders-add] 2991 ["Remove" lsp-workspace-folders-remove] 2992 ["Open" lsp-workspace-folders-open]) 2993 ("Toggle features" 2994 ["Lenses" lsp-lens-mode] 2995 ["Headerline breadcrumb" lsp-headerline-breadcrumb-mode] 2996 ["Modeline code actions" lsp-modeline-code-actions-mode] 2997 ["Modeline diagnostics" lsp-modeline-diagnostics-mode]) 2998 "---" 2999 ("Debug" 3000 :active (bound-and-true-p dap-ui-mode) 3001 :filter ,(lambda (_) 3002 (and (boundp 'dap-ui-menu-items) 3003 (nthcdr 3 dap-ui-menu-items)))))) 3004 "Menu for lsp-mode.") 3005 3006 (defalias 'make-lsp-client 'make-lsp--client) 3007 3008 (cl-defstruct lsp--registered-capability 3009 (id "") 3010 (method " ") 3011 (options nil)) 3012 3013 ;; A ‘lsp--workspace’ object represents exactly one language server process. 3014 (cl-defstruct lsp--workspace 3015 ;; the `ewoc' object for displaying I/O to and from the server 3016 (ewoc nil) 3017 3018 ;; ‘server-capabilities’ is a hash table of the language server capabilities. 3019 ;; It is the hash table representation of a LSP ServerCapabilities structure; 3020 ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize. 3021 (server-capabilities nil) 3022 3023 ;; ‘registered-server-capabilities’ is a list of hash tables that represent 3024 ;; dynamically-registered Registration objects. See 3025 ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability. 3026 (registered-server-capabilities nil) 3027 3028 ;; ‘root’ is a directory name or a directory file name for the workspace 3029 ;; root. ‘lsp-mode’ passes this directory to the ‘initialize’ method of the 3030 ;; language server; see 3031 ;; https://microsoft.github.io/language-server-protocol/specification#initialize. 3032 (root nil) 3033 3034 ;; ‘client’ is the ‘lsp--client’ object associated with this workspace. 3035 (client nil) 3036 3037 ;; ‘host-root’ contains the host root info as derived from `file-remote-p'. It 3038 ;; used to derive the file path in `lsp--uri-to-path' when using tramp 3039 ;; connection. 3040 (host-root nil) 3041 3042 ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or 3043 ;; a network connection. ‘lsp-mode’ communicates with ‘proc’ using the 3044 ;; language server protocol. ‘proc’ corresponds to the COMMUNICATION-PROCESS 3045 ;; element of the return value of the client’s ‘get-root’ field, which see. 3046 (proc nil) 3047 3048 ;; ‘proc’ is a process object; it must represent a regular process, not a 3049 ;; pipe or network process. It represents the actual server process that 3050 ;; corresponds to this workspace. ‘cmd-proc’ corresponds to the 3051 ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’ 3052 ;; field, which see. 3053 (cmd-proc nil) 3054 3055 ;; ‘buffers’ is a list of buffers associated with this workspace. 3056 (buffers nil) 3057 3058 ;; if semantic tokens is enabled, `semantic-tokens-faces' contains 3059 ;; one face (or nil) for each token type supported by the language server. 3060 (semantic-tokens-faces nil) 3061 3062 ;; If semantic highlighting is enabled, `semantic-tokens-modifier-faces' 3063 ;; contains one face (or nil) for each modifier type supported by the language 3064 ;; server 3065 (semantic-tokens-modifier-faces nil) 3066 3067 ;; Extra client capabilities provided by third-party packages using 3068 ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME 3069 ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name, 3070 ;; and CAPS is either a plist of the client capabilities, or a function that 3071 ;; takes no argument and returns a plist of the client capabilities or nil. 3072 (extra-client-capabilities nil) 3073 3074 ;; Workspace status 3075 (status nil) 3076 3077 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3078 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3079 (metadata (make-hash-table :test 'equal)) 3080 3081 ;; contains all the file notification watches that have been created for the 3082 ;; current workspace in format filePath->file notification handle. 3083 (watches (make-hash-table :test 'equal)) 3084 3085 ;; list of workspace folders 3086 (workspace-folders nil) 3087 3088 ;; ‘last-id’ the last request id for the current workspace. 3089 (last-id 0) 3090 3091 ;; ‘status-string’ allows extensions to specify custom status string based on 3092 ;; the Language Server specific messages. 3093 (status-string nil) 3094 3095 ;; ‘shutdown-action’ flag used to mark that workspace should not be restarted (e.g. it 3096 ;; was stopped). 3097 shutdown-action 3098 3099 ;; ‘diagnostics’ a hashmap with workspace diagnostics. 3100 (diagnostics (make-hash-table :test 'equal)) 3101 3102 ;; contains all the workDone progress tokens that have been created 3103 ;; for the current workspace. 3104 (work-done-tokens (make-hash-table :test 'equal))) 3105 3106 3107 (cl-defstruct lsp-session 3108 ;; contains the folders that are part of the current session 3109 folders 3110 ;; contains the folders that must not be imported in the current workspace. 3111 folders-blocklist 3112 ;; contains the list of folders that must be imported in a project in case of 3113 ;; multi root LSP server. 3114 (server-id->folders (make-hash-table :test 'equal)) 3115 ;; folder to list of the servers that are associated with the folder. 3116 (folder->servers (make-hash-table :test 'equal)) 3117 ;; ‘metadata’ is a generic storage for workspace specific data. It is 3118 ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata' 3119 (metadata (make-hash-table :test 'equal))) 3120 3121 (defun lsp-workspace-status (status-string &optional workspace) 3122 "Set current workspace status to STATUS-STRING. 3123 If WORKSPACE is not specified defaults to lsp--cur-workspace." 3124 (let ((status-string (when status-string (replace-regexp-in-string "%" "%%" status-string)))) 3125 (setf (lsp--workspace-status-string (or workspace lsp--cur-workspace)) status-string))) 3126 3127 (defun lsp-session-set-metadata (key value &optional _workspace) 3128 "Associate KEY with VALUE in the WORKSPACE metadata. 3129 If WORKSPACE is not provided current workspace will be used." 3130 (puthash key value (lsp-session-metadata (lsp-session)))) 3131 3132 (defalias 'lsp-workspace-set-metadata 'lsp-session-set-metadata) 3133 3134 (defun lsp-session-get-metadata (key &optional _workspace) 3135 "Lookup KEY in WORKSPACE metadata. 3136 If WORKSPACE is not provided current workspace will be used." 3137 (gethash key (lsp-session-metadata (lsp-session)))) 3138 3139 (defalias 'lsp-workspace-get-metadata 'lsp-session-get-metadata) 3140 3141 (defun lsp-workspace-set-work-done-token (token value workspace) 3142 "Associate TOKEN with VALUE in the WORKSPACE work-done-tokens." 3143 (puthash token value (lsp--workspace-work-done-tokens workspace))) 3144 3145 (defun lsp-workspace-get-work-done-token (token workspace) 3146 "Lookup TOKEN in the WORKSPACE work-done-tokens." 3147 (gethash token (lsp--workspace-work-done-tokens workspace))) 3148 3149 (defun lsp-workspace-rem-work-done-token (token workspace) 3150 "Remove TOKEN from the WORKSPACE work-done-tokens." 3151 (remhash token (lsp--workspace-work-done-tokens workspace))) 3152 3153 3154 (defun lsp--make-notification (method &optional params) 3155 "Create notification body for method METHOD and parameters PARAMS." 3156 (list :jsonrpc "2.0" :method method :params params)) 3157 3158 (defalias 'lsp--make-request 'lsp--make-notification) 3159 (defalias 'lsp-make-request 'lsp--make-notification) 3160 3161 (defun lsp--make-response (id result) 3162 "Create response for REQUEST with RESULT." 3163 `(:jsonrpc "2.0" :id ,id :result ,result)) 3164 3165 (defun lsp-make-notification (method &optional params) 3166 "Create notification body for method METHOD and parameters PARAMS." 3167 (lsp--make-notification method params)) 3168 3169 (defmacro lsp--json-serialize (params) 3170 (if (progn 3171 (require 'json) 3172 (fboundp 'json-serialize)) 3173 `(json-serialize ,params 3174 :null-object nil 3175 :false-object :json-false) 3176 `(let ((json-false :json-false)) 3177 (json-encode ,params)))) 3178 3179 (defun lsp--make-message (params) 3180 "Create a LSP message from PARAMS, after encoding it to a JSON string." 3181 (let ((body (lsp--json-serialize params))) 3182 (concat "Content-Length: " 3183 (number-to-string (1+ (string-bytes body))) 3184 "\r\n\r\n" 3185 body 3186 "\n"))) 3187 3188 (cl-defstruct lsp--log-entry timestamp process-time type method id body) 3189 3190 (defun lsp--make-log-entry (method id body type &optional process-time) 3191 "Create an outgoing log object from BODY with method METHOD and id ID. 3192 If ID is non-nil, then the body is assumed to be a notification. 3193 TYPE can either be `incoming' or `outgoing'" 3194 (cl-assert (memq type '(incoming-req outgoing-req incoming-notif 3195 outgoing-notif incoming-resp 3196 outgoing-resp))) 3197 (make-lsp--log-entry 3198 :timestamp (format-time-string "%I:%M:%S %p") 3199 :process-time process-time 3200 :method method 3201 :id id 3202 :type type 3203 :body body)) 3204 3205 (defun lsp--log-font-lock-json (body) 3206 "Font lock JSON BODY." 3207 (with-temp-buffer 3208 (insert body) 3209 ;; We set the temp buffer file-name extension to .json and call `set-auto-mode' 3210 ;; so the users configured json mode is used which could be 3211 ;; `json-mode', `json-ts-mode', `jsonian-mode', etc. 3212 (let ((buffer-file-name "lsp-log.json")) 3213 (delay-mode-hooks 3214 (set-auto-mode) 3215 (if (fboundp 'font-lock-ensure) 3216 (font-lock-ensure) 3217 (with-no-warnings 3218 (font-lock-fontify-buffer))))) 3219 (buffer-string))) 3220 3221 (defun lsp--log-entry-pp (entry) 3222 (cl-assert (lsp--log-entry-p entry)) 3223 (pcase-let (((cl-struct lsp--log-entry timestamp method id type process-time 3224 body) 3225 entry) 3226 (json-false :json-false) 3227 (json-encoding-pretty-print t) 3228 (str nil)) 3229 (setq str 3230 (concat (format "[Trace - %s] " timestamp) 3231 (pcase type 3232 ('incoming-req (format "Received request '%s - (%s)." method id)) 3233 ('outgoing-req (format "Sending request '%s - (%s)'." method id)) 3234 3235 ('incoming-notif (format "Received notification '%s'." method)) 3236 ('outgoing-notif (format "Sending notification '%s'." method)) 3237 3238 ('incoming-resp (format "Received response '%s - (%s)' in %dms." 3239 method id process-time)) 3240 ('outgoing-resp 3241 (format 3242 "Sending response '%s - (%s)'. Processing request took %dms" 3243 method id process-time))) 3244 "\n" 3245 (if (memq type '(incoming-resp ougoing-resp)) 3246 "Result: " 3247 "Params: ") 3248 (lsp--log-font-lock-json (json-encode body)) 3249 "\n\n\n")) 3250 (setq str (propertize str 'mouse-face 'highlight 'read-only t)) 3251 (insert str))) 3252 3253 (defvar-local lsp--log-io-ewoc nil) 3254 3255 (defun lsp--get-create-io-ewoc (workspace) 3256 (if (and (lsp--workspace-ewoc workspace) 3257 (buffer-live-p (ewoc-buffer (lsp--workspace-ewoc workspace)))) 3258 (lsp--workspace-ewoc workspace) 3259 (with-current-buffer (lsp--get-log-buffer-create workspace) 3260 (unless (eq 'lsp-log-io-mode major-mode) (lsp-log-io-mode)) 3261 (setq-local window-point-insertion-type t) 3262 (setq lsp--log-io-ewoc (ewoc-create #'lsp--log-entry-pp nil nil t)) 3263 (setf (lsp--workspace-ewoc workspace) lsp--log-io-ewoc)) 3264 (lsp--workspace-ewoc workspace))) 3265 3266 (defun lsp--ewoc-count (ewoc) 3267 (let* ((count 0) 3268 (count-fn (lambda (_) (setq count (1+ count))))) 3269 (ewoc-map count-fn ewoc) 3270 count)) 3271 3272 (defun lsp--log-entry-new (entry workspace) 3273 (let* ((ewoc (lsp--get-create-io-ewoc workspace)) 3274 (count (and (not (eq lsp-io-messages-max t)) (lsp--ewoc-count ewoc))) 3275 (node (if (or (eq lsp-io-messages-max t) 3276 (>= lsp-io-messages-max count)) 3277 nil 3278 (ewoc-nth ewoc (1- lsp-io-messages-max)))) 3279 (prev nil) 3280 (inhibit-read-only t)) 3281 (while node 3282 (setq prev (ewoc-prev ewoc node)) 3283 (ewoc-delete ewoc node) 3284 (setq node prev)) 3285 (ewoc-enter-last ewoc entry))) 3286 3287 (defun lsp--send-notification (body) 3288 "Send BODY as a notification to the language server." 3289 (lsp-foreach-workspace 3290 (when (lsp--log-io-p (plist-get body :method)) 3291 (lsp--log-entry-new (lsp--make-log-entry 3292 (plist-get body :method) 3293 nil (plist-get body :params) 'outgoing-notif) 3294 lsp--cur-workspace)) 3295 (lsp--send-no-wait body 3296 (lsp--workspace-proc lsp--cur-workspace)))) 3297 3298 (defalias 'lsp-send-notification 'lsp--send-notification) 3299 3300 (defun lsp-notify (method params) 3301 "Send notification METHOD with PARAMS." 3302 (lsp--send-notification (lsp--make-notification method params))) 3303 3304 (defun lsp--cur-workspace-check () 3305 "Check whether buffer lsp workspace(s) are set." 3306 (cl-assert (lsp-workspaces) nil 3307 "No language server(s) is associated with this buffer.")) 3308 3309 (defun lsp--send-request (body &optional no-wait no-merge) 3310 "Send BODY as a request to the language server, get the response. 3311 If NO-WAIT is non-nil, don't synchronously wait for a response. 3312 If NO-MERGE is non-nil, don't merge the results but return an 3313 alist mapping workspace->result." 3314 (lsp-request (plist-get body :method) 3315 (plist-get body :params) 3316 :no-wait no-wait 3317 :no-merge no-merge)) 3318 3319 (defalias 'lsp-send-request 'lsp--send-request 3320 "Send BODY as a request to the language server and return the response 3321 synchronously. 3322 \n(fn BODY)") 3323 3324 (cl-defun lsp-request (method params &key no-wait no-merge) 3325 "Send request METHOD with PARAMS. 3326 If NO-MERGE is non-nil, don't merge the results but return alist 3327 workspace->result. 3328 If NO-WAIT is non-nil send the request as notification." 3329 (if no-wait 3330 (lsp-notify method params) 3331 (let* ((send-time (float-time)) 3332 ;; max time by which we must get a response 3333 (expected-time 3334 (and 3335 lsp-response-timeout 3336 (+ send-time lsp-response-timeout))) 3337 resp-result resp-error done?) 3338 (unwind-protect 3339 (progn 3340 (lsp-request-async method params 3341 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3342 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3343 :no-merge no-merge 3344 :mode 'detached 3345 :cancel-token :sync-request) 3346 (while (not (or resp-error resp-result)) 3347 (if (functionp 'json-rpc-connection) 3348 (catch 'lsp-done (sit-for 0.01)) 3349 (catch 'lsp-done 3350 (accept-process-output 3351 nil 3352 (if expected-time (- expected-time send-time) 1)))) 3353 (setq send-time (float-time)) 3354 (when (and expected-time (< expected-time send-time)) 3355 (error "Timeout while waiting for response. Method: %s" method))) 3356 (setq done? t) 3357 (cond 3358 ((eq resp-result :finished) nil) 3359 (resp-result resp-result) 3360 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3361 ((lsp-json-error? (cl-first resp-error)) 3362 (error (lsp:json-error-message (cl-first resp-error)))))) 3363 (unless done? 3364 (lsp-cancel-request-by-token :sync-request)))))) 3365 3366 (cl-defun lsp-request-while-no-input (method params) 3367 "Send request METHOD with PARAMS and waits until there is no input. 3368 Return same value as `lsp--while-no-input' and respecting `non-essential'." 3369 (if (or non-essential (not lsp-request-while-no-input-may-block)) 3370 (let* ((send-time (float-time)) 3371 ;; max time by which we must get a response 3372 (expected-time 3373 (and 3374 lsp-response-timeout 3375 (+ send-time lsp-response-timeout))) 3376 resp-result resp-error done?) 3377 (unwind-protect 3378 (progn 3379 (lsp-request-async method params 3380 (lambda (res) (setf resp-result (or res :finished)) (throw 'lsp-done '_)) 3381 :error-handler (lambda (err) (setf resp-error err) (throw 'lsp-done '_)) 3382 :mode 'detached 3383 :cancel-token :sync-request) 3384 (while (not (or resp-error resp-result (input-pending-p))) 3385 (catch 'lsp-done 3386 (sit-for 3387 (if expected-time (- expected-time send-time) 1))) 3388 (setq send-time (float-time)) 3389 (when (and expected-time (< expected-time send-time)) 3390 (error "Timeout while waiting for response. Method: %s" method))) 3391 (setq done? (or resp-error resp-result)) 3392 (cond 3393 ((eq resp-result :finished) nil) 3394 (resp-result resp-result) 3395 ((lsp-json-error? resp-error) (error (lsp:json-error-message resp-error))) 3396 ((lsp-json-error? (cl-first resp-error)) 3397 (error (lsp:json-error-message (cl-first resp-error)))))) 3398 (unless done? 3399 (lsp-cancel-request-by-token :sync-request)) 3400 (when (and (input-pending-p) lsp--throw-on-input) 3401 (throw 'input :interrupted)))) 3402 (lsp-request method params))) 3403 3404 (defvar lsp--cancelable-requests (ht)) 3405 3406 (cl-defun lsp-request-async (method params callback 3407 &key mode error-handler cancel-handler no-merge cancel-token) 3408 "Send METHOD with PARAMS as a request to the language server. 3409 Call CALLBACK with the response received from the server 3410 asynchronously. 3411 MODE determines when the callback will be called depending on the 3412 condition of the original buffer. It could be: 3413 - `detached' which means that the callback will be executed no 3414 matter what has happened to the buffer. 3415 - `alive' - the callback will be executed only if the buffer from 3416 which the call was executed is still alive. 3417 - `current' the callback will be executed only if the original buffer 3418 is still selected. 3419 - `tick' - the callback will be executed only if the buffer was not modified. 3420 - `unchanged' - the callback will be executed only if the buffer hasn't 3421 changed and if the buffer is not modified. 3422 3423 ERROR-HANDLER will be called in case the request has failed. 3424 CANCEL-HANDLER will be called in case the request is being canceled. 3425 If NO-MERGE is non-nil, don't merge the results but return alist 3426 workspace->result. 3427 CANCEL-TOKEN is the token that can be used to cancel request." 3428 (lsp--send-request-async `(:jsonrpc "2.0" :method ,method :params ,params) 3429 callback mode error-handler cancel-handler no-merge cancel-token)) 3430 3431 (defun lsp--create-request-cancel (id workspaces hook buf method cancel-callback) 3432 (lambda (&rest _) 3433 (unless (and (equal 'post-command-hook hook) 3434 (equal (current-buffer) buf)) 3435 (lsp--request-cleanup-hooks id) 3436 (with-lsp-workspaces workspaces 3437 (lsp--cancel-request id) 3438 (when cancel-callback (funcall cancel-callback))) 3439 (lsp-log "Cancelling %s(%s) in hook %s" method id hook)))) 3440 3441 (defun lsp--create-async-callback 3442 (callback method no-merge workspaces) 3443 "Create async handler expecting COUNT results, merge them and call CALLBACK. 3444 MODE determines when the callback will be called depending on the 3445 condition of the original buffer. METHOD is the invoked method. 3446 If NO-MERGE is non-nil, don't merge the results but return alist 3447 workspace->result. ID is the request id." 3448 (let (results errors) 3449 (lambda (result) 3450 (push (cons lsp--cur-workspace result) 3451 (if (eq result :error) errors results)) 3452 (when (and (not (eq (length errors) (length workspaces))) 3453 (eq (+ (length errors) (length results)) (length workspaces))) 3454 (funcall callback 3455 (if no-merge 3456 results 3457 (lsp--merge-results (-map #'cl-rest results) method))))))) 3458 3459 (defcustom lsp-default-create-error-handler-fn nil 3460 "Default error handler customization. 3461 Handler should give METHOD as argument and return function of one argument 3462 ERROR." 3463 :type 'function 3464 :group 'lsp-mode 3465 :package-version '(lsp-mode . "9.0.0")) 3466 3467 (defun lsp--create-default-error-handler (method) 3468 "Default error handler. 3469 METHOD is the executed method." 3470 (if lsp-default-create-error-handler-fn 3471 (funcall lsp-default-create-error-handler-fn method) 3472 (lambda (error) 3473 (lsp--warn "%s" (or (lsp--error-string error) 3474 (format "%s Request has failed" method)))))) 3475 3476 (defvar lsp--request-cleanup-hooks (ht)) 3477 3478 (defun lsp--request-cleanup-hooks (request-id) 3479 (when-let ((cleanup-function (gethash request-id lsp--request-cleanup-hooks))) 3480 (funcall cleanup-function) 3481 (remhash request-id lsp--request-cleanup-hooks))) 3482 3483 (defun lsp-cancel-request-by-token (cancel-token) 3484 "Cancel request using CANCEL-TOKEN." 3485 (-when-let ((request-id . workspaces) (gethash cancel-token lsp--cancelable-requests)) 3486 (with-lsp-workspaces workspaces 3487 (lsp--cancel-request request-id)) 3488 (remhash cancel-token lsp--cancelable-requests) 3489 (lsp--request-cleanup-hooks request-id))) 3490 3491 (defun lsp--send-request-async (body callback 3492 &optional mode error-callback cancel-callback 3493 no-merge cancel-token) 3494 "Send BODY as a request to the language server. 3495 Call CALLBACK with the response received from the server 3496 asynchronously. 3497 MODE determines when the callback will be called depending on the 3498 condition of the original buffer. It could be: 3499 - `detached' which means that the callback will be executed no 3500 matter what has happened to the buffer. 3501 - `alive' - the callback will be executed only if the buffer from 3502 which the call was executed is still alive. 3503 - `current' the callback will be executed only if the original buffer 3504 is still selected. 3505 - `tick' - the callback will be executed only if the buffer was not modified. 3506 - `unchanged' - the callback will be executed only if the buffer hasn't 3507 changed and if the buffer is not modified. 3508 3509 ERROR-CALLBACK will be called in case the request has failed. 3510 CANCEL-CALLBACK will be called in case the request is being canceled. 3511 If NO-MERGE is non-nil, don't merge the results but return alist 3512 workspace->result. 3513 CANCEL-TOKEN is the token that can be used to cancel request." 3514 (when cancel-token 3515 (lsp-cancel-request-by-token cancel-token)) 3516 3517 (if-let ((target-workspaces (lsp--find-workspaces-for body))) 3518 (let* ((start-time (current-time)) 3519 (method (plist-get body :method)) 3520 (id (cl-incf lsp-last-id)) 3521 (buf (current-buffer)) 3522 (cancel-callback (when cancel-callback 3523 (pcase mode 3524 ((or 'alive 'tick 'unchanged) 3525 (lambda () 3526 (with-current-buffer buf 3527 (funcall cancel-callback)))) 3528 (_ cancel-callback)))) 3529 ;; calculate what are the (hook . local) pairs which will cancel 3530 ;; the request 3531 (hooks (pcase mode 3532 ('alive '((kill-buffer-hook . t))) 3533 ('tick '((kill-buffer-hook . t) (after-change-functions . t))) 3534 ('unchanged '((after-change-functions . t) (post-command-hook . nil))) 3535 ('current '((post-command-hook . nil))))) 3536 ;; note: lambdas in emacs can be compared but we should make sure 3537 ;; that all of the captured arguments are the same - in our case 3538 ;; `lsp--create-request-cancel' will return the same lambda when 3539 ;; called with the same params. 3540 (cleanup-hooks 3541 (lambda () (mapc 3542 (-lambda ((hook . local)) 3543 (if local 3544 (when (buffer-live-p buf) 3545 (with-current-buffer buf 3546 (remove-hook hook 3547 (lsp--create-request-cancel 3548 id target-workspaces hook buf method cancel-callback) 3549 t))) 3550 (remove-hook hook (lsp--create-request-cancel 3551 id target-workspaces hook buf method cancel-callback)))) 3552 hooks) 3553 (remhash cancel-token lsp--cancelable-requests))) 3554 (callback (pcase mode 3555 ((or 'alive 'tick 'unchanged) (lambda (&rest args) 3556 (with-current-buffer buf 3557 (apply callback args)))) 3558 (_ callback))) 3559 (callback (lsp--create-async-callback callback 3560 method 3561 no-merge 3562 target-workspaces)) 3563 (callback (lambda (result) 3564 (lsp--request-cleanup-hooks id) 3565 (funcall callback result))) 3566 (error-callback (lsp--create-async-callback 3567 (or error-callback 3568 (lsp--create-default-error-handler method)) 3569 method 3570 nil 3571 target-workspaces)) 3572 (error-callback (lambda (error) 3573 (funcall callback :error) 3574 (lsp--request-cleanup-hooks id) 3575 (funcall error-callback error))) 3576 (body (plist-put body :id id))) 3577 3578 ;; cancel request in any of the hooks 3579 (mapc (-lambda ((hook . local)) 3580 (add-hook hook 3581 (lsp--create-request-cancel 3582 id target-workspaces hook buf method cancel-callback) 3583 nil local)) 3584 hooks) 3585 (puthash id cleanup-hooks lsp--request-cleanup-hooks) 3586 3587 (setq lsp--last-active-workspaces target-workspaces) 3588 3589 (when cancel-token 3590 (puthash cancel-token (cons id target-workspaces) lsp--cancelable-requests)) 3591 3592 (seq-doseq (workspace target-workspaces) 3593 (when (lsp--log-io-p method) 3594 (lsp--log-entry-new (lsp--make-log-entry method id 3595 (plist-get body :params) 3596 'outgoing-req) 3597 workspace)) 3598 (puthash id 3599 (list callback error-callback method start-time (current-time)) 3600 (-> workspace 3601 (lsp--workspace-client) 3602 (lsp--client-response-handlers))) 3603 (lsp--send-no-wait body (lsp--workspace-proc workspace))) 3604 body) 3605 (error "The connected server(s) does not support method %s. 3606 To find out what capabilities support your server use `M-x lsp-describe-session' 3607 and expand the capabilities section" 3608 (plist-get body :method)))) 3609 3610 ;; deprecated, use lsp-request-async. 3611 (defalias 'lsp-send-request-async 'lsp--send-request-async) 3612 (make-obsolete 'lsp-send-request-async 'lsp-request-async "lsp-mode 7.0.1") 3613 3614 ;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any 3615 ;; pending language servers. 3616 (add-hook 'kill-emacs-hook #'lsp--global-teardown) 3617 3618 (defun lsp--global-teardown () 3619 "Unload working workspaces." 3620 (lsp-foreach-workspace (lsp--shutdown-workspace))) 3621 3622 (defun lsp--shutdown-workspace (&optional restart) 3623 "Shut down the language server process for ‘lsp--cur-workspace’." 3624 (with-demoted-errors "LSP error: %S" 3625 (let ((lsp-response-timeout 0.5)) 3626 (condition-case err 3627 (lsp-request "shutdown" nil) 3628 (error (lsp--error "%s" err)))) 3629 (lsp-notify "exit" nil)) 3630 (setf (lsp--workspace-shutdown-action lsp--cur-workspace) (or (and restart 'restart) 'shutdown)) 3631 (lsp--uninitialize-workspace)) 3632 3633 (defcustom lsp-inlay-hint-enable nil 3634 "If non-nil it will enable inlay hints." 3635 :type 'boolean 3636 :group 'lsp-mode 3637 :package-version '(lsp-mode . "9.0.0")) 3638 3639 (defun lsp--uninitialize-workspace () 3640 "Cleanup buffer state. 3641 When a workspace is shut down, by request or from just 3642 disappearing, unset all the variables related to it." 3643 (-let [(&lsp-wks 'cmd-proc 'buffers) lsp--cur-workspace] 3644 (lsp-process-kill cmd-proc) 3645 (mapc (lambda (buf) 3646 (when (lsp-buffer-live-p buf) 3647 (lsp-with-current-buffer buf 3648 (lsp-managed-mode -1)))) 3649 buffers) 3650 (lsp-diagnostics--workspace-cleanup lsp--cur-workspace))) 3651 3652 (defun lsp--client-capabilities (&optional custom-capabilities) 3653 "Return the client capabilities appending CUSTOM-CAPABILITIES." 3654 (append 3655 `((general . ((positionEncodings . ["utf-32", "utf-16"]))) 3656 (workspace . ((workspaceEdit . ((documentChanges . t) 3657 (resourceOperations . ["create" "rename" "delete"]))) 3658 (applyEdit . t) 3659 (symbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))))) 3660 (executeCommand . ((dynamicRegistration . :json-false))) 3661 ,@(when lsp-enable-file-watchers '((didChangeWatchedFiles . ((dynamicRegistration . t))))) 3662 (workspaceFolders . t) 3663 (configuration . t) 3664 ,@(when lsp-semantic-tokens-enable 3665 `((semanticTokens . ((refreshSupport . ,(or (and (boundp 'lsp-semantic-tokens-honor-refresh-requests) 3666 lsp-semantic-tokens-honor-refresh-requests) 3667 :json-false)))))) 3668 ,@(when lsp-lens-enable '((codeLens . ((refreshSupport . t))))) 3669 ,@(when lsp-inlay-hint-enable '((inlayHint . ((refreshSupport . :json-false))))) 3670 (fileOperations . ((didCreate . :json-false) 3671 (willCreate . :json-false) 3672 (didRename . t) 3673 (willRename . t) 3674 (didDelete . :json-false) 3675 (willDelete . :json-false))))) 3676 (textDocument . ((declaration . ((dynamicRegistration . t) 3677 (linkSupport . t))) 3678 (definition . ((dynamicRegistration . t) 3679 (linkSupport . t))) 3680 (references . ((dynamicRegistration . t))) 3681 (implementation . ((dynamicRegistration . t) 3682 (linkSupport . t))) 3683 (typeDefinition . ((dynamicRegistration . t) 3684 (linkSupport . t))) 3685 (synchronization . ((willSave . t) (didSave . t) (willSaveWaitUntil . t))) 3686 (documentSymbol . ((symbolKind . ((valueSet . ,(apply 'vector (number-sequence 1 26))))) 3687 (hierarchicalDocumentSymbolSupport . t))) 3688 (formatting . ((dynamicRegistration . t))) 3689 (rangeFormatting . ((dynamicRegistration . t))) 3690 (onTypeFormatting . ((dynamicRegistration . t))) 3691 ,@(when (and lsp-semantic-tokens-enable 3692 (functionp 'lsp--semantic-tokens-capabilities)) 3693 (lsp--semantic-tokens-capabilities)) 3694 (rename . ((dynamicRegistration . t) (prepareSupport . t))) 3695 (codeAction . ((dynamicRegistration . t) 3696 (isPreferredSupport . t) 3697 (codeActionLiteralSupport . ((codeActionKind . ((valueSet . ["" 3698 "quickfix" 3699 "refactor" 3700 "refactor.extract" 3701 "refactor.inline" 3702 "refactor.rewrite" 3703 "source" 3704 "source.organizeImports"]))))) 3705 (resolveSupport . ((properties . ["edit" "command"]))) 3706 (dataSupport . t))) 3707 (completion . ((completionItem . ((snippetSupport . ,(cond 3708 ((and lsp-enable-snippet (not (fboundp 'yas-minor-mode))) 3709 (lsp--warn (concat 3710 "Yasnippet is not installed, but `lsp-enable-snippet' is set to `t'. " 3711 "You must either install yasnippet, or disable snippet support.")) 3712 :json-false) 3713 (lsp-enable-snippet t) 3714 (t :json-false))) 3715 (documentationFormat . ["markdown" "plaintext"]) 3716 ;; Remove this after jdtls support resolveSupport 3717 (resolveAdditionalTextEditsSupport . t) 3718 (insertReplaceSupport . t) 3719 (deprecatedSupport . t) 3720 (resolveSupport 3721 . ((properties . ["documentation" 3722 "detail" 3723 "additionalTextEdits" 3724 "command"]))) 3725 (insertTextModeSupport . ((valueSet . [1 2]))))) 3726 (contextSupport . t) 3727 (dynamicRegistration . t))) 3728 (signatureHelp . ((signatureInformation . ((parameterInformation . ((labelOffsetSupport . t))))) 3729 (dynamicRegistration . t))) 3730 (documentLink . ((dynamicRegistration . t) 3731 (tooltipSupport . t))) 3732 (hover . ((contentFormat . ["markdown" "plaintext"]) 3733 (dynamicRegistration . t))) 3734 ,@(when lsp-enable-folding 3735 `((foldingRange . ((dynamicRegistration . t) 3736 ,@(when lsp-folding-range-limit 3737 `((rangeLimit . ,lsp-folding-range-limit))) 3738 ,@(when lsp-folding-line-folding-only 3739 `((lineFoldingOnly . t))))))) 3740 (selectionRange . ((dynamicRegistration . t))) 3741 (callHierarchy . ((dynamicRegistration . :json-false))) 3742 (typeHierarchy . ((dynamicRegistration . t))) 3743 (publishDiagnostics . ((relatedInformation . t) 3744 (tagSupport . ((valueSet . [1 2]))) 3745 (versionSupport . t))) 3746 (linkedEditingRange . ((dynamicRegistration . t))))) 3747 (window . ((workDoneProgress . t) 3748 (showDocument . ((support . t)))))) 3749 custom-capabilities)) 3750 3751 (defun lsp-find-roots-for-workspace (workspace session) 3752 "Get all roots for the WORKSPACE." 3753 (-filter #'identity (ht-map (lambda (folder workspaces) 3754 (when (-contains? workspaces workspace) 3755 folder)) 3756 (lsp-session-folder->servers session)))) 3757 3758 (defun lsp-session-watches (&optional session) 3759 "Get watches created for SESSION." 3760 (or (gethash "__watches" (lsp-session-metadata (or session (lsp-session)))) 3761 (-let [res (make-hash-table :test 'equal)] 3762 (puthash "__watches" res (lsp-session-metadata (or session (lsp-session)))) 3763 res))) 3764 3765 (defun lsp--file-process-event (session root-folder event) 3766 "Process file event." 3767 (let* ((changed-file (cl-third event)) 3768 (rel-changed-file (f-relative changed-file root-folder)) 3769 (event-numeric-kind (alist-get (cl-second event) lsp--file-change-type)) 3770 (bit-position (1- event-numeric-kind)) 3771 (watch-bit (ash 1 bit-position))) 3772 (->> 3773 session 3774 lsp-session-folder->servers 3775 (gethash root-folder) 3776 (seq-do (lambda (workspace) 3777 (when (->> 3778 workspace 3779 lsp--workspace-registered-server-capabilities 3780 (-any? 3781 (lambda (capability) 3782 (and 3783 (equal (lsp--registered-capability-method capability) 3784 "workspace/didChangeWatchedFiles") 3785 (->> 3786 capability 3787 lsp--registered-capability-options 3788 (lsp:did-change-watched-files-registration-options-watchers) 3789 (seq-find 3790 (-lambda ((fs-watcher &as &FileSystemWatcher :glob-pattern :kind? :_cachedRegexp cached-regexp)) 3791 (when (or (null kind?) 3792 (> (logand kind? watch-bit) 0)) 3793 (-let [regexes (or cached-regexp 3794 (let ((regexp (lsp-glob-to-regexps glob-pattern))) 3795 (lsp-put fs-watcher :_cachedRegexp regexp) 3796 regexp))] 3797 (-any? (lambda (re) 3798 (or (string-match re changed-file) 3799 (string-match re rel-changed-file))) 3800 regexes)))))))))) 3801 (with-lsp-workspace workspace 3802 (lsp-notify 3803 "workspace/didChangeWatchedFiles" 3804 `((changes . [((type . ,event-numeric-kind) 3805 (uri . ,(lsp--path-to-uri changed-file)))])))))))))) 3806 3807 (lsp-defun lsp--server-register-capability ((&Registration :method :id :register-options?)) 3808 "Register capability REG." 3809 (when (and lsp-enable-file-watchers 3810 (equal method "workspace/didChangeWatchedFiles")) 3811 (-let* ((created-watches (lsp-session-watches (lsp-session))) 3812 (root-folders (cl-set-difference 3813 (lsp-find-roots-for-workspace lsp--cur-workspace (lsp-session)) 3814 (ht-keys created-watches)))) 3815 ;; create watch for each root folder without such 3816 (dolist (folder root-folders) 3817 (let* ((watch (make-lsp-watch :root-directory folder)) 3818 (ignored-things (lsp--get-ignored-regexes-for-workspace-root folder)) 3819 (ignored-files-regex-list (car ignored-things)) 3820 (ignored-directories-regex-list (cadr ignored-things))) 3821 (puthash folder watch created-watches) 3822 (lsp-watch-root-folder (file-truename folder) 3823 (-partial #'lsp--file-process-event (lsp-session) folder) 3824 ignored-files-regex-list 3825 ignored-directories-regex-list 3826 watch 3827 t))))) 3828 3829 (push 3830 (make-lsp--registered-capability :id id :method method :options register-options?) 3831 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3832 3833 (defmacro lsp--with-workspace-temp-buffer (workspace-root &rest body) 3834 "With a temp-buffer under `WORKSPACE-ROOT' and evaluate `BODY', useful to 3835 access dir-local variables." 3836 (declare (indent 1) (debug t)) 3837 `(with-temp-buffer 3838 ;; Set the buffer's name to something under the root so that we can hack the local variables 3839 ;; This file doesn't need to exist and will not be created due to this. 3840 (setq-local buffer-file-name (expand-file-name "lsp-mode-temp" (expand-file-name ,workspace-root))) 3841 (hack-local-variables) 3842 (prog1 ,@body 3843 (setq-local buffer-file-name nil)))) 3844 3845 (defun lsp--get-ignored-regexes-for-workspace-root (workspace-root) 3846 "Return a list of the form 3847 (lsp-file-watch-ignored-files lsp-file-watch-ignored-directories) for the given 3848 WORKSPACE-ROOT." 3849 ;; The intent of this function is to provide per-root workspace-level customization of the 3850 ;; lsp-file-watch-ignored-directories and lsp-file-watch-ignored-files variables. 3851 (lsp--with-workspace-temp-buffer workspace-root 3852 (list lsp-file-watch-ignored-files (lsp-file-watch-ignored-directories)))) 3853 3854 3855 (defun lsp--cleanup-hanging-watches () 3856 "Cleanup watches in case there are no more workspaces that are interested 3857 in that particular folder." 3858 (let* ((session (lsp-session)) 3859 (watches (lsp-session-watches session))) 3860 (dolist (watched-folder (ht-keys watches)) 3861 (when (-none? (lambda (workspace) 3862 (with-lsp-workspace workspace 3863 (lsp--registered-capability "workspace/didChangeWatchedFiles"))) 3864 (gethash watched-folder (lsp-session-folder->servers (lsp-session)))) 3865 (lsp-log "Cleaning up watches for folder %s. There is no workspace watching this folder..." watched-folder) 3866 (lsp-kill-watch (gethash watched-folder watches)) 3867 (remhash watched-folder watches))))) 3868 3869 (lsp-defun lsp--server-unregister-capability ((&Unregistration :id :method)) 3870 "Unregister capability UNREG." 3871 (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace) 3872 (seq-remove (lambda (e) (equal (lsp--registered-capability-id e) id)) 3873 (lsp--workspace-registered-server-capabilities lsp--cur-workspace))) 3874 (when (equal method "workspace/didChangeWatchedFiles") 3875 (lsp--cleanup-hanging-watches))) 3876 3877 (defun lsp--server-capabilities () 3878 "Return the capabilities of the language server associated with the buffer." 3879 (->> (lsp-workspaces) 3880 (-keep #'lsp--workspace-server-capabilities) 3881 (apply #'lsp-merge))) 3882 3883 (defun lsp--send-open-close-p () 3884 "Return whether open and close notifications should be sent to the server." 3885 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3886 (or (memq sync '(1 2)) 3887 (lsp:text-document-sync-options-open-close? sync)))) 3888 3889 (defun lsp--send-will-save-p () 3890 "Return whether willSave notifications should be sent to the server." 3891 (-> (lsp--server-capabilities) 3892 (lsp:server-capabilities-text-document-sync?) 3893 (lsp:text-document-sync-options-will-save?))) 3894 3895 (defun lsp--send-will-save-wait-until-p () 3896 "Return whether willSaveWaitUntil notifications should be sent to the server." 3897 (-> (lsp--server-capabilities) 3898 (lsp:server-capabilities-text-document-sync?) 3899 (lsp:text-document-sync-options-will-save-wait-until?))) 3900 3901 (defun lsp--send-did-save-p () 3902 "Return whether didSave notifications should be sent to the server." 3903 (let ((sync (lsp:server-capabilities-text-document-sync? (lsp--server-capabilities)))) 3904 (or (memq sync '(1 2)) 3905 (lsp:text-document-sync-options-save? sync)))) 3906 3907 (defun lsp--save-include-text-p () 3908 "Return whether save notifications should include the text document's contents." 3909 (->> (lsp--server-capabilities) 3910 (lsp:server-capabilities-text-document-sync?) 3911 (lsp:text-document-sync-options-save?) 3912 (lsp:text-document-save-registration-options-include-text?))) 3913 3914 (defun lsp--send-will-rename-files-p (path) 3915 "Return whether willRenameFiles request should be sent to the server. 3916 If any filters, checks if it applies for PATH." 3917 (let* ((will-rename (-> (lsp--server-capabilities) 3918 (lsp:server-capabilities-workspace?) 3919 (lsp:workspace-server-capabilities-file-operations?) 3920 (lsp:workspace-file-operations-will-rename?))) 3921 (filters (seq-into (lsp:file-operation-registration-options-filters will-rename) 'list))) 3922 (and will-rename 3923 (or (seq-empty-p filters) 3924 (-any? (-lambda ((&FileOperationFilter :scheme? :pattern (&FileOperationPattern :glob))) 3925 (-let [regexes (lsp-glob-to-regexps glob)] 3926 (and (or (not scheme?) 3927 (string-prefix-p scheme? (lsp--path-to-uri path))) 3928 (-any? (lambda (re) 3929 (string-match re path)) 3930 regexes)))) 3931 filters))))) 3932 3933 (defun lsp--send-did-rename-files-p () 3934 "Return whether didRenameFiles notification should be sent to the server." 3935 (-> (lsp--server-capabilities) 3936 (lsp:server-capabilities-workspace?) 3937 (lsp:workspace-server-capabilities-file-operations?) 3938 (lsp:workspace-file-operations-did-rename?))) 3939 3940 (declare-function project-roots "ext:project" (project) t) 3941 (declare-function project-root "ext:project" (project) t) 3942 3943 (defun lsp--suggest-project-root () 3944 "Get project root." 3945 (or 3946 (when (fboundp 'projectile-project-root) 3947 (condition-case nil 3948 (projectile-project-root) 3949 (error nil))) 3950 (when (fboundp 'project-current) 3951 (when-let ((project (project-current))) 3952 (if (fboundp 'project-root) 3953 (project-root project) 3954 (car (with-no-warnings 3955 (project-roots project)))))) 3956 default-directory)) 3957 3958 (defun lsp--read-from-file (file) 3959 "Read FILE content." 3960 (when (file-exists-p file) 3961 (cl-first (read-from-string (f-read-text file 'utf-8))))) 3962 3963 (defun lsp--persist (file-name to-persist) 3964 "Persist TO-PERSIST in FILE-NAME. 3965 3966 This function creates the parent directories if they don't exist 3967 yet." 3968 (let ((print-length nil) 3969 (print-level nil)) 3970 ;; Create all parent directories: 3971 (make-directory (f-parent file-name) t) 3972 (f-write-text (prin1-to-string to-persist) 'utf-8 file-name))) 3973 3974 (defun lsp-workspace-folders-add (project-root) 3975 "Add PROJECT-ROOT to the list of workspace folders." 3976 (interactive 3977 (list (read-directory-name "Select folder to add: " 3978 (or (lsp--suggest-project-root) default-directory) nil t))) 3979 (cl-pushnew (lsp-f-canonical project-root) 3980 (lsp-session-folders (lsp-session)) :test 'equal) 3981 (lsp--persist-session (lsp-session)) 3982 3983 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil)) 3984 3985 (defun lsp-workspace-folders-remove (project-root) 3986 "Remove PROJECT-ROOT from the list of workspace folders." 3987 (interactive (list (completing-read "Select folder to remove: " 3988 (lsp-session-folders (lsp-session)) 3989 nil t nil nil 3990 (lsp-find-session-folder (lsp-session) default-directory)))) 3991 3992 (setq project-root (lsp-f-canonical project-root)) 3993 3994 ;; send remove folder to each multiroot workspace associated with the folder 3995 (dolist (wks (->> (lsp-session) 3996 (lsp-session-folder->servers) 3997 (gethash project-root) 3998 (--filter (lsp--client-multi-root (lsp--workspace-client it))))) 3999 (with-lsp-workspace wks 4000 (lsp-notify "workspace/didChangeWorkspaceFolders" 4001 (lsp-make-did-change-workspace-folders-params 4002 :event (lsp-make-workspace-folders-change-event 4003 :removed (vector (lsp-make-workspace-folder 4004 :uri (lsp--path-to-uri project-root) 4005 :name (f-filename project-root))) 4006 :added []))))) 4007 4008 ;; turn off servers in the removed directory 4009 (let* ((session (lsp-session)) 4010 (folder->servers (lsp-session-folder->servers session)) 4011 (server-id->folders (lsp-session-server-id->folders session)) 4012 (workspaces (gethash project-root folder->servers))) 4013 4014 (remhash project-root folder->servers) 4015 4016 ;; turn off the servers without root folders 4017 (dolist (workspace workspaces) 4018 (when (--none? (-contains? it workspace) (ht-values folder->servers)) 4019 (lsp--info "Shutdown %s since folder %s is removed..." 4020 (lsp--workspace-print workspace) project-root) 4021 (with-lsp-workspace workspace (lsp--shutdown-workspace)))) 4022 4023 (setf (lsp-session-folders session) 4024 (-remove-item project-root (lsp-session-folders session))) 4025 4026 (ht-aeach (puthash key 4027 (-remove-item project-root value) 4028 server-id->folders) 4029 server-id->folders) 4030 (lsp--persist-session (lsp-session))) 4031 4032 (run-hook-with-args 'lsp-workspace-folders-changed-functions nil (list project-root))) 4033 4034 (defun lsp-workspace-blocklist-remove (project-root) 4035 "Remove PROJECT-ROOT from the workspace blocklist." 4036 (interactive (list (completing-read "Select folder to remove:" 4037 (lsp-session-folders-blocklist (lsp-session)) 4038 nil t))) 4039 (setf (lsp-session-folders-blocklist (lsp-session)) 4040 (delete project-root 4041 (lsp-session-folders-blocklist (lsp-session)))) 4042 (lsp--persist-session (lsp-session))) 4043 4044 (define-obsolete-function-alias 'lsp-workspace-folders-switch 4045 'lsp-workspace-folders-open "lsp-mode 6.1") 4046 4047 (defun lsp-workspace-folders-open (project-root) 4048 "Open the directory located at PROJECT-ROOT" 4049 (interactive (list (completing-read "Open folder: " 4050 (lsp-session-folders (lsp-session)) 4051 nil t))) 4052 (find-file project-root)) 4053 4054 (defun lsp--maybe-enable-signature-help (trigger-characters) 4055 (let ((ch last-command-event)) 4056 (when (cl-find ch trigger-characters :key #'string-to-char) 4057 (lsp-signature-activate)))) 4058 4059 (defun lsp--on-type-formatting-handler-create () 4060 (when-let ((provider (lsp--capability-for-method "textDocument/onTypeFormatting" ))) 4061 (-let [(&DocumentOnTypeFormattingOptions :more-trigger-character? 4062 :first-trigger-character) provider] 4063 (lambda () 4064 (lsp--on-type-formatting first-trigger-character 4065 more-trigger-character?))))) 4066 4067 (defun lsp--update-on-type-formatting-hook (&optional cleanup?) 4068 (let ((on-type-formatting-handler (lsp--on-type-formatting-handler-create))) 4069 (cond 4070 ((and lsp-enable-on-type-formatting on-type-formatting-handler (not cleanup?)) 4071 (add-hook 'post-self-insert-hook on-type-formatting-handler nil t)) 4072 ((or cleanup? 4073 (not lsp-enable-on-type-formatting)) 4074 (remove-hook 'post-self-insert-hook on-type-formatting-handler t))))) 4075 4076 (defun lsp--signature-help-handler-create () 4077 (-when-let ((&SignatureHelpOptions? :trigger-characters?) 4078 (lsp--capability-for-method "textDocument/signatureHelp")) 4079 (lambda () 4080 (lsp--maybe-enable-signature-help trigger-characters?)))) 4081 4082 (defun lsp--update-signature-help-hook (&optional cleanup?) 4083 (let ((signature-help-handler (lsp--signature-help-handler-create))) 4084 (cond 4085 ((and (or (equal lsp-signature-auto-activate t) 4086 (memq :on-trigger-char lsp-signature-auto-activate)) 4087 signature-help-handler) 4088 (add-hook 'post-self-insert-hook signature-help-handler nil t)) 4089 4090 ((or cleanup? 4091 (not (or (equal lsp-signature-auto-activate t) 4092 (memq :on-trigger-char lsp-signature-auto-activate)))) 4093 (remove-hook 'post-self-insert-hook signature-help-handler t))))) 4094 4095 (defun lsp--after-set-visited-file-name () 4096 (lsp-disconnect) 4097 (lsp)) 4098 4099 ;; TODO remove those eldoc workarounds when dropping support for Emacs 27 4100 ;; https://github.com/emacs-lsp/lsp-mode/issues/3295#issuecomment-1308994099 4101 (defvar eldoc-documentation-default) ; CI 4102 (when (< emacs-major-version 28) 4103 (unless (boundp 'eldoc-documentation-functions) 4104 (load "eldoc" nil 'nomessage)) 4105 (when (memq (default-value 'eldoc-documentation-function) '(nil ignore)) 4106 ;; actually `eldoc-documentation-strategy', but CI was failing 4107 (setq-default eldoc-documentation-function 'eldoc-documentation-default))) 4108 4109 (define-minor-mode lsp-managed-mode 4110 "Mode for source buffers managed by lsp-mode." 4111 :lighter nil 4112 (cond 4113 (lsp-managed-mode 4114 (when (lsp-feature? "textDocument/hover") 4115 (add-hook 'eldoc-documentation-functions #'lsp-eldoc-function nil t) 4116 (eldoc-mode 1)) 4117 4118 (add-hook 'after-change-functions #'lsp-on-change nil t) 4119 (add-hook 'after-revert-hook #'lsp-on-revert nil t) 4120 (add-hook 'after-save-hook #'lsp-on-save nil t) 4121 (add-hook 'auto-save-hook #'lsp--on-auto-save nil t) 4122 (add-hook 'before-change-functions #'lsp-before-change nil t) 4123 (add-hook 'before-save-hook #'lsp--before-save nil t) 4124 (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t) 4125 (add-hook 'post-command-hook #'lsp--post-command nil t) 4126 4127 (lsp--update-on-type-formatting-hook) 4128 (lsp--update-signature-help-hook) 4129 4130 (when lsp-enable-xref 4131 (add-hook 'xref-backend-functions #'lsp--xref-backend nil t)) 4132 4133 (lsp-configure-buffer) 4134 4135 ;; make sure we turn off lsp-mode in case major mode changes, because major 4136 ;; mode change will wipe the buffer locals. 4137 (add-hook 'change-major-mode-hook #'lsp-disconnect nil t) 4138 (add-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name nil t) 4139 4140 (let ((buffer (lsp-current-buffer))) 4141 (run-with-idle-timer 4142 0.0 nil 4143 (lambda () 4144 (when (lsp-buffer-live-p buffer) 4145 (lsp-with-current-buffer buffer 4146 (lsp--on-change-debounce buffer) 4147 (lsp--on-idle buffer))))))) 4148 (t 4149 (lsp-unconfig-buffer) 4150 4151 (remove-hook 'eldoc-documentation-functions #'lsp-eldoc-function t) 4152 (remove-hook 'post-command-hook #'lsp--post-command t) 4153 (remove-hook 'after-change-functions #'lsp-on-change t) 4154 (remove-hook 'after-revert-hook #'lsp-on-revert t) 4155 (remove-hook 'after-save-hook #'lsp-on-save t) 4156 (remove-hook 'auto-save-hook #'lsp--on-auto-save t) 4157 (remove-hook 'before-change-functions #'lsp-before-change t) 4158 (remove-hook 'before-save-hook #'lsp--before-save t) 4159 (remove-hook 'kill-buffer-hook #'lsp--text-document-did-close t) 4160 4161 (lsp--update-on-type-formatting-hook :cleanup) 4162 (lsp--update-signature-help-hook :cleanup) 4163 4164 (when lsp--on-idle-timer 4165 (cancel-timer lsp--on-idle-timer) 4166 (setq lsp--on-idle-timer nil)) 4167 4168 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4169 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4170 4171 (lsp--remove-overlays 'lsp-highlight) 4172 (lsp--remove-overlays 'lsp-links) 4173 4174 (remove-hook 'xref-backend-functions #'lsp--xref-backend t) 4175 (remove-hook 'change-major-mode-hook #'lsp-disconnect t) 4176 (remove-hook 'after-set-visited-file-name-hook #'lsp--after-set-visited-file-name t) 4177 (setq-local lsp-buffer-uri nil)))) 4178 4179 (defun lsp-configure-buffer () 4180 "Configure LSP features for current buffer." 4181 ;; make sure the core is running in the context of all available workspaces 4182 ;; to avoid misconfiguration in case we are running in `with-lsp-workspace' context 4183 (let ((lsp--buffer-workspaces (cond 4184 (lsp--buffer-workspaces) 4185 (lsp--cur-workspace (list lsp--cur-workspace)))) 4186 lsp--cur-workspace) 4187 (when lsp-auto-configure 4188 (lsp--auto-configure) 4189 4190 (when (and lsp-enable-text-document-color 4191 (lsp-feature? "textDocument/documentColor")) 4192 (add-hook 'lsp-on-change-hook #'lsp--document-color nil t)) 4193 4194 (when (and lsp-enable-imenu 4195 (lsp-feature? "textDocument/documentSymbol")) 4196 (lsp-enable-imenu)) 4197 4198 (when (and lsp-enable-indentation 4199 (lsp-feature? "textDocument/rangeFormatting")) 4200 (add-function :override (local 'indent-region-function) #'lsp-format-region)) 4201 4202 (when (and lsp-enable-symbol-highlighting 4203 (lsp-feature? "textDocument/documentHighlight")) 4204 (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)) 4205 4206 (when (and lsp-enable-links 4207 (lsp-feature? "textDocument/documentLink")) 4208 (add-hook 'lsp-on-idle-hook #'lsp--document-links nil t)) 4209 4210 (when (and lsp-inlay-hint-enable 4211 (lsp-feature? "textDocument/inlayHint")) 4212 (lsp-inlay-hints-mode)) 4213 4214 (when (and lsp-enable-dap-auto-configure 4215 (functionp 'dap-mode)) 4216 (dap-auto-configure-mode 1))) 4217 (run-hooks 'lsp-configure-hook))) 4218 4219 (defun lsp-unconfig-buffer () 4220 "Unconfigure LSP features for buffer." 4221 (lsp--remove-overlays 'lsp-color) 4222 4223 (when (advice-function-member-p 'lsp--imenu-create-index imenu-create-index-function) 4224 (remove-function (local 'imenu-create-index-function) #'lsp--imenu-create-index) 4225 (setq-local imenu-menubar-modified-tick 0) 4226 (setq-local imenu--index-alist nil) 4227 (imenu--cleanup)) 4228 4229 (remove-function (local 'indent-region-function) #'lsp-format-region) 4230 4231 (remove-hook 'lsp-on-change-hook #'lsp--document-color t) 4232 (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t) 4233 (remove-hook 'lsp-on-idle-hook #'lsp--document-links t) 4234 4235 (when (and lsp-enable-dap-auto-configure 4236 (functionp 'dap-mode)) 4237 (dap-auto-configure-mode -1)) 4238 4239 (run-hooks 'lsp-unconfigure-hook)) 4240 4241 (defun lsp--buffer-content () 4242 (lsp-save-restriction-and-excursion 4243 (or (lsp-virtual-buffer-call :buffer-string) 4244 (buffer-substring-no-properties (point-min) 4245 (point-max))))) 4246 4247 (defun lsp--text-document-did-open () 4248 "`document/didOpen' event." 4249 (run-hooks 'lsp-before-open-hook) 4250 (when (and lsp-auto-touch-files 4251 (not (f-exists? (lsp--uri-to-path (lsp--buffer-uri))))) 4252 (lsp--info "Saving file '%s' because it is not present on the disk." (lsp--buffer-uri)) 4253 (save-buffer)) 4254 4255 (setq lsp--cur-version (or lsp--cur-version 0)) 4256 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 4257 (lsp-notify 4258 "textDocument/didOpen" 4259 (list :textDocument 4260 (list :uri (lsp--buffer-uri) 4261 :languageId (lsp-buffer-language) 4262 :version lsp--cur-version 4263 :text (lsp--buffer-content)))) 4264 4265 (lsp-managed-mode 1) 4266 4267 (run-hooks 'lsp-after-open-hook) 4268 (when-let ((client (-some-> lsp--cur-workspace (lsp--workspace-client)))) 4269 (-some-> (lsp--client-after-open-fn client) 4270 (funcall)) 4271 (-some-> (format "lsp-%s-after-open-hook" (lsp--client-server-id client)) 4272 (intern-soft) 4273 (run-hooks)))) 4274 4275 (defun lsp--text-document-identifier () 4276 "Make TextDocumentIdentifier." 4277 (list :uri (lsp--buffer-uri))) 4278 4279 (defun lsp--versioned-text-document-identifier () 4280 "Make VersionedTextDocumentIdentifier." 4281 (plist-put (lsp--text-document-identifier) :version lsp--cur-version)) 4282 4283 (defun lsp--cur-line (&optional point) 4284 (1- (line-number-at-pos point))) 4285 4286 (defun lsp--cur-position () 4287 "Make a Position object for the current point." 4288 (or (lsp-virtual-buffer-call :cur-position) 4289 (lsp-save-restriction-and-excursion 4290 (list :line (lsp--cur-line) 4291 :character (- (point) (line-beginning-position)))))) 4292 4293 (defun lsp--point-to-position (point) 4294 "Convert POINT to Position." 4295 (lsp-save-restriction-and-excursion 4296 (goto-char point) 4297 (lsp--cur-position))) 4298 4299 (defun lsp--range (start end) 4300 "Make Range body from START and END." 4301 ;; make sure start and end are Position objects 4302 (list :start start :end end)) 4303 4304 (defun lsp--region-to-range (start end) 4305 "Make Range object for the current region." 4306 (lsp--range (lsp--point-to-position start) 4307 (lsp--point-to-position end))) 4308 4309 (defun lsp--region-or-line () 4310 "The active region or the current line." 4311 (if (use-region-p) 4312 (lsp--region-to-range (region-beginning) (region-end)) 4313 (lsp--region-to-range (line-beginning-position) (line-end-position)))) 4314 4315 (defun lsp--check-document-changes-version (document-changes) 4316 "Verify that DOCUMENT-CHANGES have the proper version." 4317 (unless (seq-every-p 4318 (-lambda ((&TextDocumentEdit :text-document)) 4319 (or 4320 (not text-document) 4321 (let* ((filename (-> text-document 4322 lsp:versioned-text-document-identifier-uri 4323 lsp--uri-to-path)) 4324 (version (lsp:versioned-text-document-identifier-version? text-document))) 4325 (with-current-buffer (find-file-noselect filename) 4326 (or (null version) (zerop version) (= -1 version) 4327 (equal version lsp--cur-version)))))) 4328 document-changes) 4329 (error "Document changes cannot be applied due to different document version"))) 4330 4331 (defun lsp--apply-workspace-edit (workspace-edit &optional operation) 4332 "Apply the WorkspaceEdit object WORKSPACE-EDIT. 4333 OPERATION is symbol representing the source of this text edit." 4334 (-let (((&WorkspaceEdit :document-changes? :changes?) workspace-edit)) 4335 (if-let ((document-changes (seq-reverse document-changes?))) 4336 (progn 4337 (lsp--check-document-changes-version document-changes) 4338 (->> document-changes 4339 (seq-filter (-lambda ((&CreateFile :kind)) (equal kind "create"))) 4340 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4341 (->> document-changes 4342 (seq-filter (-lambda ((&CreateFile :kind)) 4343 (and (or (not kind) (equal kind "edit")) 4344 (not (equal kind "create"))))) 4345 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation)))) 4346 (->> document-changes 4347 (seq-filter (-lambda ((&CreateFile :kind)) 4348 (and (not (or (not kind) (equal kind "edit"))) 4349 (not (equal kind "create"))))) 4350 (seq-do (lambda (change) (lsp--apply-text-document-edit change operation))))) 4351 (lsp-map 4352 (lambda (uri text-edits) 4353 (with-current-buffer (-> uri lsp--uri-to-path find-file-noselect) 4354 (lsp--apply-text-edits text-edits operation))) 4355 changes?)))) 4356 4357 (defmacro lsp-with-filename (file &rest body) 4358 "Execute BODY with FILE as a context. 4359 Need to handle the case when FILE indicates virtual buffer." 4360 (declare (indent 1) (debug t)) 4361 `(if-let ((lsp--virtual-buffer (get-text-property 0 'lsp-virtual-buffer ,file))) 4362 (lsp-with-current-buffer lsp--virtual-buffer 4363 ,@body) 4364 ,@body)) 4365 4366 (defun lsp--apply-text-document-edit (edit &optional operation) 4367 "Apply the TextDocumentEdit object EDIT. 4368 OPERATION is symbol representing the source of this text edit. 4369 If the file is not being visited by any buffer, it is opened with 4370 `find-file-noselect'. 4371 Because lsp-mode does not store previous document versions, the edit is only 4372 applied if the version of the textDocument matches the version of the 4373 corresponding file. 4374 4375 interface TextDocumentEdit { 4376 textDocument: VersionedTextDocumentIdentifier; 4377 edits: TextEdit[]; 4378 }" 4379 (pcase (lsp:edit-kind edit) 4380 ("create" (-let* (((&CreateFile :uri :options?) edit) 4381 (file-name (lsp--uri-to-path uri))) 4382 (mkdir (f-dirname file-name) t) 4383 (f-touch file-name) 4384 (when (lsp:create-file-options-overwrite? options?) 4385 (f-write-text "" nil file-name)) 4386 (find-file-noselect file-name))) 4387 ("delete" (-let (((&DeleteFile :uri :options? (&DeleteFileOptions? :recursive?)) edit)) 4388 (f-delete (lsp--uri-to-path uri) recursive?))) 4389 ("rename" (-let* (((&RenameFile :old-uri :new-uri :options? (&RenameFileOptions? :overwrite?)) edit) 4390 (old-file-name (lsp--uri-to-path old-uri)) 4391 (new-file-name (lsp--uri-to-path new-uri)) 4392 (buf (find-buffer-visiting old-file-name))) 4393 (when buf 4394 (lsp-with-current-buffer buf 4395 (save-buffer) 4396 (lsp--text-document-did-close))) 4397 (mkdir (f-dirname new-file-name) t) 4398 (rename-file old-file-name new-file-name overwrite?) 4399 (when buf 4400 (lsp-with-current-buffer buf 4401 (set-buffer-modified-p nil) 4402 (setq lsp-buffer-uri nil) 4403 (set-visited-file-name new-file-name) 4404 (lsp))))) 4405 (_ (let ((file-name (->> edit 4406 (lsp:text-document-edit-text-document) 4407 (lsp:versioned-text-document-identifier-uri) 4408 (lsp--uri-to-path)))) 4409 (lsp-with-current-buffer (find-buffer-visiting file-name) 4410 (lsp-with-filename file-name 4411 (lsp--apply-text-edits (lsp:text-document-edit-edits edit) operation))))))) 4412 4413 (lsp-defun lsp--position-compare ((&Position :line left-line 4414 :character left-character) 4415 (&Position :line right-line 4416 :character right-character)) 4417 "Return t if position LEFT is greater than RIGHT." 4418 (if (= left-line right-line) 4419 (> left-character right-character) 4420 (> left-line right-line))) 4421 4422 (lsp-defun lsp-point-in-range? (position (&Range :start :end)) 4423 "Returns if POINT is in RANGE." 4424 (not (or (lsp--position-compare start position) 4425 (lsp--position-compare position end)))) 4426 4427 (lsp-defun lsp--position-equal ((&Position :line left-line 4428 :character left-character) 4429 (&Position :line right-line 4430 :character right-character)) 4431 "Return whether LEFT and RIGHT positions are equal." 4432 (and (= left-line right-line) 4433 (= left-character right-character))) 4434 4435 (lsp-defun lsp--text-edit-sort-predicate ((&TextEdit :range (&Range :start left-start :end left-end)) 4436 (&TextEdit :range (&Range :start right-start :end right-end))) 4437 (if (lsp--position-equal left-start right-start) 4438 (lsp--position-compare left-end right-end) 4439 (lsp--position-compare left-start right-start))) 4440 4441 (lsp-defun lsp--apply-text-edit ((edit &as &TextEdit :range (&RangeToPoint :start :end) :new-text)) 4442 "Apply the edits described in the TextEdit object in TEXT-EDIT." 4443 (setq new-text (s-replace "\r" "" (or new-text ""))) 4444 (lsp:set-text-edit-new-text edit new-text) 4445 (goto-char start) 4446 (delete-region start end) 4447 (insert new-text)) 4448 4449 ;; WORKAROUND: typescript-language might send -1 when applying code actions. 4450 ;; see https://github.com/emacs-lsp/lsp-mode/issues/1582 4451 (lsp-defun lsp--fix-point ((point &as &Position :character :line)) 4452 (-doto point 4453 (lsp:set-position-line (max 0 line)) 4454 (lsp:set-position-character (max 0 character)))) 4455 4456 (lsp-defun lsp--apply-text-edit-replace-buffer-contents ((edit &as 4457 &TextEdit 4458 :range (&Range :start :end) 4459 :new-text)) 4460 "Apply the edits described in the TextEdit object in TEXT-EDIT. 4461 The method uses `replace-buffer-contents'." 4462 (setq new-text (s-replace "\r" "" (or new-text ""))) 4463 (lsp:set-text-edit-new-text edit new-text) 4464 (-let* ((source (current-buffer)) 4465 ((beg . end) (lsp--range-to-region (lsp-make-range :start (lsp--fix-point start) 4466 :end (lsp--fix-point end))))) 4467 (with-temp-buffer 4468 (insert new-text) 4469 (let ((temp (current-buffer))) 4470 (with-current-buffer source 4471 (save-excursion 4472 (save-restriction 4473 (narrow-to-region beg end) 4474 4475 ;; On emacs versions < 26.2, 4476 ;; `replace-buffer-contents' is buggy - it calls 4477 ;; change functions with invalid arguments - so we 4478 ;; manually call the change functions here. 4479 ;; 4480 ;; See emacs bugs #32237, #32278: 4481 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237 4482 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278 4483 (let ((inhibit-modification-hooks t) 4484 (length (- end beg))) 4485 (run-hook-with-args 'before-change-functions 4486 beg end) 4487 (replace-buffer-contents temp) 4488 (run-hook-with-args 'after-change-functions 4489 beg (+ beg (length new-text)) 4490 length))))))))) 4491 4492 (defun lsp--to-yasnippet-snippet (snippet) 4493 "Convert LSP SNIPPET to yasnippet snippet." 4494 ;; LSP snippet doesn't escape "{" and "`", but yasnippet requires escaping it. 4495 (replace-regexp-in-string (rx (or bos (not (any "$" "\\"))) (group (or "{" "`"))) 4496 (rx "\\" (backref 1)) 4497 snippet 4498 nil nil 1)) 4499 4500 (defvar-local lsp-enable-relative-indentation nil 4501 "Enable relative indentation when insert texts, snippets ... 4502 from language server.") 4503 4504 (defun lsp--expand-snippet (snippet &optional start end expand-env) 4505 "Wrapper of `yas-expand-snippet' with all of it arguments. 4506 The snippet will be convert to LSP style and indent according to 4507 LSP server result." 4508 (require 'yasnippet nil t) 4509 (let* ((inhibit-field-text-motion t) 4510 (yas-wrap-around-region nil) 4511 (yas-indent-line 'none) 4512 (yas-also-auto-indent-first-line nil)) 4513 (yas-expand-snippet 4514 (lsp--to-yasnippet-snippet snippet) 4515 start end expand-env))) 4516 4517 (defun lsp--indent-lines (start end &optional insert-text-mode?) 4518 "Indent from START to END based on INSERT-TEXT-MODE? value. 4519 - When INSERT-TEXT-MODE? is provided 4520 - if it's `lsp/insert-text-mode-as-it', do no editor indentation. 4521 - if it's `lsp/insert-text-mode-adjust-indentation', adjust leading 4522 whitespaces to match the line where text is inserted. 4523 - When it's not provided, using `indent-line-function' for each line." 4524 (save-excursion 4525 (goto-char end) 4526 (let* ((end-line (line-number-at-pos)) 4527 (offset (save-excursion 4528 (goto-char start) 4529 (current-indentation))) 4530 (indent-line-function 4531 (cond ((equal insert-text-mode? lsp/insert-text-mode-as-it) 4532 #'ignore) 4533 ((or (equal insert-text-mode? lsp/insert-text-mode-adjust-indentation) 4534 lsp-enable-relative-indentation 4535 ;; Indenting snippets is extremely slow in `org-mode' buffers 4536 ;; since it has to calculate indentation based on SRC block 4537 ;; position. Thus we use relative indentation as default. 4538 (derived-mode-p 'org-mode)) 4539 (lambda () (save-excursion 4540 (beginning-of-line) 4541 (indent-to-column offset)))) 4542 (t indent-line-function)))) 4543 (goto-char start) 4544 (forward-line) 4545 (while (and (not (eobp)) 4546 (<= (line-number-at-pos) end-line)) 4547 (funcall indent-line-function) 4548 (forward-line))))) 4549 4550 (defun lsp--apply-text-edits (edits &optional operation) 4551 "Apply the EDITS described in the TextEdit[] object. 4552 OPERATION is symbol representing the source of this text edit." 4553 (unless (seq-empty-p edits) 4554 (atomic-change-group 4555 (run-hooks 'lsp-before-apply-edits-hook) 4556 (let* ((change-group (prepare-change-group)) 4557 (howmany (length edits)) 4558 (message (format "Applying %s edits to `%s' ..." howmany (current-buffer))) 4559 (_ (lsp--info message)) 4560 (reporter (make-progress-reporter message 0 howmany)) 4561 (done 0) 4562 (apply-edit (if (not lsp--virtual-buffer) 4563 #'lsp--apply-text-edit-replace-buffer-contents 4564 #'lsp--apply-text-edit))) 4565 (unwind-protect 4566 (->> edits 4567 ;; We sort text edits so as to apply edits that modify latter 4568 ;; parts of the document first. Furthermore, because the LSP 4569 ;; spec dictates that: "If multiple inserts have the same 4570 ;; position, the order in the array defines which edit to 4571 ;; apply first." We reverse the initial list and sort stably 4572 ;; to make sure the order among edits with the same position 4573 ;; is preserved. 4574 (nreverse) 4575 (seq-sort #'lsp--text-edit-sort-predicate) 4576 (mapc (lambda (edit) 4577 (progress-reporter-update reporter (cl-incf done)) 4578 (funcall apply-edit edit) 4579 (when (lsp:snippet-text-edit-insert-text-format? edit) 4580 (-when-let ((&SnippetTextEdit :range (&RangeToPoint :start) 4581 :insert-text-format? :new-text) edit) 4582 (when (eq insert-text-format? lsp/insert-text-format-snippet) 4583 ;; No `save-excursion' needed since expand snippet will change point anyway 4584 (goto-char (+ start (length new-text))) 4585 (lsp--indent-lines start (point)) 4586 (lsp--expand-snippet new-text start (point))))) 4587 (run-hook-with-args 'lsp-after-apply-edits-hook operation)))) 4588 (undo-amalgamate-change-group change-group) 4589 (progress-reporter-done reporter)))))) 4590 4591 (defun lsp--create-apply-text-edits-handlers () 4592 "Create (handler cleanup-fn) for applying text edits in async request. 4593 Only works when mode is `tick or `alive." 4594 (let* (first-edited 4595 (func (lambda (start &rest _) 4596 (setq first-edited (if first-edited 4597 (min start first-edited) 4598 start))))) 4599 (add-hook 'before-change-functions func nil t) 4600 (list 4601 (lambda (edits) 4602 (if (and first-edited 4603 (seq-find (-lambda ((&TextEdit :range (&RangeToPoint :end))) 4604 ;; Text edit region is overlapped 4605 (> end first-edited)) 4606 edits)) 4607 (lsp--warn "TextEdits will not be applied since document has been modified before of them.") 4608 (lsp--apply-text-edits edits 'completion-cleanup))) 4609 (lambda () 4610 (remove-hook 'before-change-functions func t))))) 4611 4612 (defun lsp--capability (cap &optional capabilities) 4613 "Get the value of capability CAP. If CAPABILITIES is non-nil, use them instead." 4614 (when (stringp cap) 4615 (setq cap (intern (concat ":" cap)))) 4616 4617 (lsp-get (or capabilities 4618 (lsp--server-capabilities)) 4619 cap)) 4620 4621 (defun lsp--registered-capability (method) 4622 "Check whether there is workspace providing METHOD." 4623 (->> (lsp-workspaces) 4624 (--keep (seq-find (lambda (reg) 4625 (equal (lsp--registered-capability-method reg) method)) 4626 (lsp--workspace-registered-server-capabilities it))) 4627 cl-first)) 4628 4629 (defun lsp--capability-for-method (method) 4630 "Get the value of capability for METHOD." 4631 (-let* ((reqs (cdr (assoc method lsp-method-requirements))) 4632 ((&plist :capability) reqs)) 4633 (or (and capability (lsp--capability capability)) 4634 (-some-> (lsp--registered-capability method) 4635 (lsp--registered-capability-options))))) 4636 4637 (defvar-local lsp--before-change-vals nil 4638 "Store the positions from the `lsp-before-change' function call, for 4639 validation and use in the `lsp-on-change' function.") 4640 4641 (defun lsp--text-document-content-change-event (start end length) 4642 "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH." 4643 ;; So (47 54 0) means add 7 chars starting at pos 47 4644 ;; must become 4645 ;; {"range":{"start":{"line":5,"character":6} 4646 ;; ,"end" :{"line":5,"character":6}} 4647 ;; ,"rangeLength":0 4648 ;; ,"text":"\nbb = 5"} 4649 ;; 4650 ;; And (47 47 7) means delete 7 chars starting at pos 47 4651 ;; must become 4652 ;; {"range":{"start":{"line":6,"character":0} 4653 ;; ,"end" :{"line":7,"character":0}} 4654 ;; ,"rangeLength":7 4655 ;; ,"text":""} 4656 ;; 4657 ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with 4658 ;; 13 chars. So it must become 4659 ;; {"range":{"start":{"line":5,"character":8} 4660 ;; ,"end" :{"line":5,"character":11}} 4661 ;; ,"rangeLength":3 4662 ;; ,"text":"new-chars-xxx"} 4663 ;; 4664 4665 ;; Adding text: 4666 ;; lsp-before-change:(start,end)=(33,33) 4667 ;; lsp-on-change:(start,end,length)=(33,34,0) 4668 ;; 4669 ;; Changing text: 4670 ;; lsp-before-change:(start,end)=(208,211) 4671 ;; lsp-on-change:(start,end,length)=(208,221,3) 4672 ;; 4673 ;; Deleting text: 4674 ;; lsp-before-change:(start,end)=(19,27) 4675 ;; lsp-on-change:(start,end,length)=(19,19,8) 4676 (if (zerop length) 4677 ;; Adding something only, work from start only 4678 `( :range ,(lsp--range 4679 (lsp--point-to-position start) 4680 (lsp--point-to-position start)) 4681 :rangeLength 0 4682 :text ,(buffer-substring-no-properties start end)) 4683 4684 (if (eq start end) 4685 ;; Deleting something only 4686 (if (lsp--bracketed-change-p start length) 4687 ;; The before-change value is bracketed, use it 4688 `( :range ,(lsp--range 4689 (lsp--point-to-position start) 4690 (plist-get lsp--before-change-vals :end-pos)) 4691 :rangeLength ,length 4692 :text "") 4693 ;; If the change is not bracketed, send a full change event instead. 4694 (lsp--full-change-event)) 4695 4696 ;; Deleting some things, adding others 4697 (if (lsp--bracketed-change-p start length) 4698 ;; The before-change value is valid, use it 4699 `( :range ,(lsp--range 4700 (lsp--point-to-position start) 4701 (plist-get lsp--before-change-vals :end-pos)) 4702 :rangeLength ,length 4703 :text ,(buffer-substring-no-properties start end)) 4704 (lsp--full-change-event))))) 4705 4706 (defun lsp--bracketed-change-p (start length) 4707 "If the before and after positions are the same, and the length 4708 is the size of the start range, we are probably good." 4709 (-let [(&plist :end before-end :start before-start) lsp--before-change-vals] 4710 (and (eq start before-start) 4711 (eq length (- before-end before-start))))) 4712 4713 (defun lsp--full-change-event () 4714 `(:text ,(lsp--buffer-content))) 4715 4716 (defun lsp-before-change (start end) 4717 "Executed before a file is changed. 4718 Added to `before-change-functions'." 4719 ;; Note: 4720 ;; 4721 ;; This variable holds a list of functions to call when Emacs is about to 4722 ;; modify a buffer. Each function gets two arguments, the beginning and end of 4723 ;; the region that is about to change, represented as integers. The buffer 4724 ;; that is about to change is always the current buffer when the function is 4725 ;; called. 4726 ;; 4727 ;; WARNING: 4728 ;; 4729 ;; Do not expect the before-change hooks and the after-change hooks be called 4730 ;; in balanced pairs around each buffer change. Also don't expect the 4731 ;; before-change hooks to be called for every chunk of text Emacs is about to 4732 ;; delete. These hooks are provided on the assumption that Lisp programs will 4733 ;; use either before- or the after-change hooks, but not both, and the 4734 ;; boundaries of the region where the changes happen might include more than 4735 ;; just the actual changed text, or even lump together several changes done 4736 ;; piecemeal. 4737 (save-match-data 4738 (lsp-save-restriction-and-excursion 4739 (setq lsp--before-change-vals 4740 (list :start start 4741 :end end 4742 :end-pos (lsp--point-to-position end)))))) 4743 4744 (defun lsp--flush-delayed-changes () 4745 (let ((inhibit-quit t)) 4746 (when lsp--delay-timer 4747 (cancel-timer lsp--delay-timer)) 4748 (mapc (-lambda ((workspace buffer document change)) 4749 (with-current-buffer buffer 4750 (with-lsp-workspace workspace 4751 (lsp-notify "textDocument/didChange" 4752 (list :textDocument document 4753 :contentChanges (vector change)))))) 4754 (prog1 (nreverse lsp--delayed-requests) 4755 (setq lsp--delayed-requests nil))))) 4756 4757 (defun lsp--workspace-sync-method (workspace) 4758 (let ((sync (-> workspace 4759 (lsp--workspace-server-capabilities) 4760 (lsp:server-capabilities-text-document-sync?)))) 4761 (if (lsp-text-document-sync-options? sync) 4762 (lsp:text-document-sync-options-change? sync) 4763 sync))) 4764 4765 (defun lsp-on-change (start end length &optional content-change-event-fn) 4766 "Executed when a file is changed. 4767 Added to `after-change-functions'." 4768 ;; Note: 4769 ;; 4770 ;; Each function receives three arguments: the beginning and end of the region 4771 ;; just changed, and the length of the text that existed before the change. 4772 ;; All three arguments are integers. The buffer that has been changed is 4773 ;; always the current buffer when the function is called. 4774 ;; 4775 ;; The length of the old text is the difference between the buffer positions 4776 ;; before and after that text as it was before the change. As for the 4777 ;; changed text, its length is simply the difference between the first two 4778 ;; arguments. 4779 ;; 4780 ;; So (47 54 0) means add 7 chars starting at pos 47 4781 ;; So (47 47 7) means delete 7 chars starting at pos 47 4782 (save-match-data 4783 (let ((inhibit-quit t) 4784 ;; make sure that `lsp-on-change' is called in multi-workspace context 4785 ;; see #2901 4786 lsp--cur-workspace) 4787 ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done 4788 ;; by auto-revert-mode) will cause this handler to get called with a nil 4789 ;; buffer-file-name. We need the buffer-file-name to send notifications; 4790 ;; so we skip handling revert-buffer-caused changes and instead handle 4791 ;; reverts separately in lsp-on-revert 4792 (when (not revert-buffer-in-progress-p) 4793 (cl-incf lsp--cur-version) 4794 (mapc 4795 (lambda (workspace) 4796 (pcase (or lsp-document-sync-method 4797 (lsp--workspace-sync-method workspace)) 4798 (1 4799 (if lsp-debounce-full-sync-notifications 4800 (setq lsp--delayed-requests 4801 (->> lsp--delayed-requests 4802 (-remove (-lambda ((_ buffer)) 4803 (equal (current-buffer) buffer))) 4804 (cons (list workspace 4805 (current-buffer) 4806 (lsp--versioned-text-document-identifier) 4807 (lsp--full-change-event))))) 4808 (with-lsp-workspace workspace 4809 (lsp-notify "textDocument/didChange" 4810 (list :contentChanges (vector (lsp--full-change-event)) 4811 :textDocument (lsp--versioned-text-document-identifier)))))) 4812 (2 4813 (with-lsp-workspace workspace 4814 (lsp-notify 4815 "textDocument/didChange" 4816 (list :textDocument (lsp--versioned-text-document-identifier) 4817 :contentChanges (vector 4818 (if content-change-event-fn 4819 (funcall content-change-event-fn start end length) 4820 (lsp--text-document-content-change-event 4821 start end length))))))))) 4822 (lsp-workspaces)) 4823 (when lsp--delay-timer (cancel-timer lsp--delay-timer)) 4824 (setq lsp--delay-timer (run-with-idle-timer 4825 lsp-debounce-full-sync-notifications-interval 4826 nil 4827 #'lsp--flush-delayed-changes)) 4828 ;; force cleanup overlays after each change 4829 (lsp--remove-overlays 'lsp-highlight) 4830 (lsp--after-change (current-buffer)) 4831 (setq lsp--signature-last-index nil 4832 lsp--signature-last nil) 4833 ;; cleanup diagnostics 4834 (when lsp-diagnostic-clean-after-change 4835 (lsp-foreach-workspace 4836 (-let [diagnostics (lsp--workspace-diagnostics lsp--cur-workspace)] 4837 (remhash (lsp--fix-path-casing (buffer-file-name)) diagnostics)))))))) 4838 4839 4840 4841 ;; facilities for on change hooks. We do not want to make lsp calls on each 4842 ;; change event so we add debounce to avoid flooding the server with events. 4843 ;; Additionally, we want to have a mechanism for stopping the server calls in 4844 ;; particular cases like, e. g. when performing completion. 4845 4846 (defvar lsp-inhibit-lsp-hooks nil 4847 "Flag to control.") 4848 4849 (defcustom lsp-on-change-hook nil 4850 "Hooks to run when buffer has changed." 4851 :type 'hook 4852 :group 'lsp-mode) 4853 4854 (defcustom lsp-idle-delay 0.500 4855 "Debounce interval for `after-change-functions'." 4856 :type 'number 4857 :group 'lsp-mode) 4858 4859 (defcustom lsp-on-idle-hook nil 4860 "Hooks to run after `lsp-idle-delay'." 4861 :type 'hook 4862 :group 'lsp-mode) 4863 4864 (defun lsp--idle-reschedule (buffer) 4865 (when lsp--on-idle-timer 4866 (cancel-timer lsp--on-idle-timer)) 4867 4868 (setq lsp--on-idle-timer (run-with-idle-timer 4869 lsp-idle-delay 4870 nil 4871 #'lsp--on-idle 4872 buffer))) 4873 4874 (defun lsp--post-command () 4875 (lsp--cleanup-highlights-if-needed) 4876 (lsp--idle-reschedule (current-buffer))) 4877 4878 (defun lsp--on-idle (buffer) 4879 "Start post command loop." 4880 (when (and (buffer-live-p buffer) 4881 (equal buffer (current-buffer)) 4882 (not lsp-inhibit-lsp-hooks) 4883 lsp-managed-mode) 4884 (run-hooks 'lsp-on-idle-hook))) 4885 4886 (defun lsp--on-change-debounce (buffer) 4887 (when (and (buffer-live-p buffer) 4888 (equal buffer (current-buffer)) 4889 (not lsp-inhibit-lsp-hooks) 4890 lsp-managed-mode) 4891 (run-hooks 'lsp-on-change-hook))) 4892 4893 (defun lsp--after-change (buffer) 4894 (when (fboundp 'lsp--semantic-tokens-refresh-if-enabled) 4895 (lsp--semantic-tokens-refresh-if-enabled buffer)) 4896 (when lsp--on-change-timer 4897 (cancel-timer lsp--on-change-timer)) 4898 (setq lsp--on-change-timer (run-with-idle-timer 4899 lsp-idle-delay 4900 nil 4901 #'lsp--on-change-debounce 4902 buffer)) 4903 (lsp--idle-reschedule buffer)) 4904 4905 4906 (defcustom lsp-trim-trailing-whitespace t 4907 "Trim trailing whitespace on a line." 4908 :group 'lsp-mode 4909 :type 'boolean) 4910 4911 (defcustom lsp-insert-final-newline t 4912 "Insert a newline character at the end of the file if one does not exist." 4913 :group 'lsp-mode 4914 :type 'boolean) 4915 4916 (defcustom lsp-trim-final-newlines t 4917 "Trim all newlines after the final newline at the end of the file." 4918 :group 'lsp-mode 4919 :type 'boolean) 4920 4921 4922 (defun lsp--on-type-formatting (first-trigger-characters more-trigger-characters) 4923 "Self insert handling. 4924 Applies on type formatting." 4925 (let ((ch last-command-event)) 4926 (when (or (eq (string-to-char first-trigger-characters) ch) 4927 (cl-find ch more-trigger-characters :key #'string-to-char)) 4928 (lsp-request-async "textDocument/onTypeFormatting" 4929 (lsp-make-document-on-type-formatting-params 4930 :text-document (lsp--text-document-identifier) 4931 :options (lsp-make-formatting-options 4932 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 4933 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 4934 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 4935 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 4936 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)) 4937 :ch (char-to-string ch) 4938 :position (lsp--cur-position)) 4939 (lambda (data) (lsp--apply-text-edits data 'format)) 4940 :mode 'tick)))) 4941 4942 4943 ;; links 4944 (defun lsp--document-links () 4945 (when (lsp-feature? "textDocument/documentLink") 4946 (lsp-request-async 4947 "textDocument/documentLink" 4948 `(:textDocument ,(lsp--text-document-identifier)) 4949 (lambda (links) 4950 (lsp--remove-overlays 'lsp-link) 4951 (seq-do 4952 (-lambda ((link &as &DocumentLink :range (&Range :start :end))) 4953 (-doto (make-button (lsp--position-to-point start) 4954 (lsp--position-to-point end) 4955 'action (lsp--document-link-keymap link) 4956 'keymap (let ((map (make-sparse-keymap))) 4957 (define-key map [M-return] 'push-button) 4958 (define-key map [mouse-2] 'push-button) 4959 map) 4960 'help-echo "mouse-2, M-RET: Visit this link") 4961 (overlay-put 'lsp-link t))) 4962 links)) 4963 :mode 'unchanged))) 4964 4965 (defun lsp--document-link-handle-target (url) 4966 (let* ((parsed-url (url-generic-parse-url (url-unhex-string url))) 4967 (type (url-type parsed-url))) 4968 (pcase type 4969 ("file" 4970 (xref-push-marker-stack) 4971 (find-file (lsp--uri-to-path url)) 4972 (-when-let ((_ line column) (s-match (rx "#" (group (1+ num)) (or "," "#") (group (1+ num))) url)) 4973 (goto-char (lsp--position-to-point 4974 (lsp-make-position :character (1- (string-to-number column)) 4975 :line (1- (string-to-number line))))))) 4976 ((or "http" "https") (browse-url url)) 4977 (type (if-let ((handler (lsp--get-uri-handler type))) 4978 (funcall handler url) 4979 (signal 'lsp-file-scheme-not-supported (list url))))))) 4980 4981 (lsp-defun lsp--document-link-keymap ((link &as &DocumentLink :target?)) 4982 (if target? 4983 (lambda (_) 4984 (interactive) 4985 (lsp--document-link-handle-target target?)) 4986 (lambda (_) 4987 (interactive) 4988 (when (lsp:document-link-registration-options-resolve-provider? 4989 (lsp--capability-for-method "textDocument/documentLink")) 4990 (lsp-request-async 4991 "documentLink/resolve" 4992 link 4993 (-lambda ((&DocumentLink :target?)) 4994 (lsp--document-link-handle-target target?))))))) 4995 4996 4997 4998 (defcustom lsp-warn-no-matched-clients t 4999 "Whether to show messages when there are no supported clients." 5000 :group 'lsp-mode 5001 :type 'boolean) 5002 5003 (defun lsp-buffer-language--configured-id () 5004 "Return nil when not registered." 5005 (->> lsp-language-id-configuration 5006 (-first 5007 (-lambda ((mode-or-pattern . language)) 5008 (cond 5009 ((and (stringp mode-or-pattern) 5010 (s-matches? mode-or-pattern (buffer-file-name))) 5011 language) 5012 ((eq mode-or-pattern major-mode) language)))) 5013 cl-rest)) 5014 5015 (defvar-local lsp--buffer-language nil 5016 "Locally cached returned value of `lsp-buffer-language'.") 5017 5018 (defun lsp-buffer-language () 5019 "Get language corresponding current buffer." 5020 (or lsp--buffer-language 5021 (let* ((configured-language (lsp-buffer-language--configured-id))) 5022 (setq lsp--buffer-language 5023 (or configured-language 5024 ;; ensure non-nil 5025 (string-remove-suffix "-mode" (symbol-name major-mode)))) 5026 (when (and lsp-warn-no-matched-clients 5027 (null configured-language)) 5028 (lsp-warn "Unable to calculate the languageId for buffer `%s'. \ 5029 Take a look at `lsp-language-id-configuration'. The `major-mode' is %s" 5030 (buffer-name) 5031 major-mode)) 5032 lsp--buffer-language))) 5033 5034 (defun lsp-activate-on (&rest languages) 5035 "Returns language activation function. 5036 The function will return t when the `lsp-buffer-language' returns 5037 one of the LANGUAGES." 5038 (lambda (_file-name _mode) 5039 (-contains? languages (lsp-buffer-language)))) 5040 5041 (defun lsp-workspace-root (&optional path) 5042 "Find the workspace root for the current file or PATH." 5043 (-when-let* ((file-name (or path (buffer-file-name))) 5044 (file-name (lsp-f-canonical file-name))) 5045 (->> (lsp-session) 5046 (lsp-session-folders) 5047 (--filter (and (lsp--files-same-host it file-name) 5048 (or (lsp-f-ancestor-of? it file-name) 5049 (equal it file-name)))) 5050 (--max-by (> (length it) (length other)))))) 5051 5052 (defun lsp-on-revert () 5053 "Executed when a file is reverted. 5054 Added to `after-revert-hook'." 5055 (let ((n (buffer-size)) 5056 (revert-buffer-in-progress-p nil)) 5057 (lsp-on-change 0 n n))) 5058 5059 (defun lsp--text-document-did-close (&optional keep-workspace-alive) 5060 "Executed when the file is closed, added to `kill-buffer-hook'. 5061 5062 If KEEP-WORKSPACE-ALIVE is non-nil, do not shutdown the workspace 5063 if it's closing the last buffer in the workspace." 5064 (lsp-foreach-workspace 5065 (cl-callf2 delq (lsp-current-buffer) (lsp--workspace-buffers lsp--cur-workspace)) 5066 (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S" 5067 (lsp-notify "textDocument/didClose" 5068 `(:textDocument ,(lsp--text-document-identifier)))) 5069 (when (and (not lsp-keep-workspace-alive) 5070 (not keep-workspace-alive) 5071 (not (lsp--workspace-buffers lsp--cur-workspace))) 5072 (lsp--shutdown-workspace)))) 5073 5074 (defun lsp--will-save-text-document-params (reason) 5075 (list :textDocument (lsp--text-document-identifier) 5076 :reason reason)) 5077 5078 (defun lsp--before-save () 5079 "Before save handler." 5080 (with-demoted-errors "Error in ‘lsp--before-save’: %S" 5081 (let ((params (lsp--will-save-text-document-params 1))) 5082 (when (lsp--send-will-save-p) 5083 (lsp-notify "textDocument/willSave" params)) 5084 (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits) 5085 (let ((lsp-response-timeout 0.1)) 5086 (condition-case nil 5087 (lsp--apply-text-edits 5088 (lsp-request "textDocument/willSaveWaitUntil" 5089 params) 5090 'before-save) 5091 (error))))))) 5092 5093 (defun lsp--on-auto-save () 5094 "Handler for auto-save." 5095 (when (lsp--send-will-save-p) 5096 (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S" 5097 (lsp-notify "textDocument/willSave" (lsp--will-save-text-document-params 2))))) 5098 5099 (defun lsp--text-document-did-save () 5100 "Executed when the file is closed, added to `after-save-hook''." 5101 (when (lsp--send-did-save-p) 5102 (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’" 5103 (lsp-notify "textDocument/didSave" 5104 `( :textDocument ,(lsp--versioned-text-document-identifier) 5105 ,@(when (lsp--save-include-text-p) 5106 (list :text (lsp--buffer-content)))))))) 5107 5108 (defun lsp--text-document-position-params (&optional identifier position) 5109 "Make TextDocumentPositionParams for the current point in the current document. 5110 If IDENTIFIER and POSITION are non-nil, they will be used as the document 5111 identifier and the position respectively." 5112 (list :textDocument (or identifier (lsp--text-document-identifier)) 5113 :position (or position (lsp--cur-position)))) 5114 5115 (defun lsp--get-buffer-diagnostics () 5116 "Return buffer diagnostics." 5117 (gethash (or 5118 (plist-get lsp--virtual-buffer :buffer-file-name) 5119 (lsp--fix-path-casing (buffer-file-name))) 5120 (lsp-diagnostics t))) 5121 5122 (defun lsp-cur-line-diagnostics () 5123 "Return any diagnostics that apply to the current line." 5124 (-let [(&plist :start (&plist :line start) :end (&plist :line end)) (lsp--region-or-line)] 5125 (cl-coerce (-filter 5126 (-lambda ((&Diagnostic :range (&Range :start (&Position :line)))) 5127 (and (>= line start) (<= line end))) 5128 (lsp--get-buffer-diagnostics)) 5129 'vector))) 5130 5131 (lsp-defun lsp-range-overlapping?((left &as &Range :start left-start :end left-end) 5132 (right &as &Range :start right-start :end right-end)) 5133 (or (lsp-point-in-range? right-start left) 5134 (lsp-point-in-range? right-end left) 5135 (lsp-point-in-range? left-start right) 5136 (lsp-point-in-range? left-end right))) 5137 5138 (defun lsp-make-position-1 (position) 5139 (lsp-make-position :line (plist-get position :line) 5140 :character (plist-get position :character))) 5141 5142 (defun lsp-cur-possition-diagnostics () 5143 "Return any diagnostics that apply to the current line." 5144 (-let* ((start (if (use-region-p) (region-beginning) (point))) 5145 (end (if (use-region-p) (region-end) (point))) 5146 (current-range (lsp-make-range :start (lsp-make-position-1 (lsp-point-to-position start)) 5147 :end (lsp-make-position-1 (lsp-point-to-position end))))) 5148 (->> (lsp--get-buffer-diagnostics) 5149 (-filter 5150 (-lambda ((&Diagnostic :range)) 5151 (lsp-range-overlapping? range current-range))) 5152 (apply 'vector)))) 5153 5154 (defalias 'lsp--cur-line-diagnotics 'lsp-cur-line-diagnostics) 5155 5156 (defun lsp--extract-line-from-buffer (pos) 5157 "Return the line pointed to by POS (a Position object) in the current buffer." 5158 (let* ((point (lsp--position-to-point pos)) 5159 (inhibit-field-text-motion t)) 5160 (save-excursion 5161 (goto-char point) 5162 (buffer-substring (line-beginning-position) (line-end-position))))) 5163 5164 (lsp-defun lsp--xref-make-item (filename (&Range :start (start &as &Position :character start-char :line start-line) 5165 :end (end &as &Position :character end-char))) 5166 "Return a xref-item from a RANGE in FILENAME." 5167 (let* ((line (lsp--extract-line-from-buffer start)) 5168 (len (length line))) 5169 (add-face-text-property (max (min start-char len) 0) 5170 (max (min end-char len) 0) 5171 'xref-match t line) 5172 ;; LINE is nil when FILENAME is not being current visited by any buffer. 5173 (xref-make-match (or line filename) 5174 (xref-make-file-location 5175 filename 5176 (lsp-translate-line (1+ start-line)) 5177 (lsp-translate-column start-char)) 5178 (- end-char start-char)))) 5179 5180 (defun lsp--location-uri (loc) 5181 (if (lsp-location? loc) 5182 (lsp:location-uri loc) 5183 (lsp:location-link-target-uri loc))) 5184 5185 (lsp-defun lsp-goto-location ((loc &as &Location :uri :range (&Range :start))) 5186 "Go to location." 5187 (let ((path (lsp--uri-to-path uri))) 5188 (if (f-exists? path) 5189 (with-current-buffer (find-file path) 5190 (goto-char (lsp--position-to-point start))) 5191 (error "There is no file %s" path)))) 5192 5193 (defun lsp--location-range (loc) 5194 (if (lsp-location? loc) 5195 (lsp:location-range loc) 5196 (lsp:location-link-target-selection-range loc))) 5197 5198 (defun lsp--locations-to-xref-items (locations) 5199 "Return a list of `xref-item' given LOCATIONS, which can be of 5200 type Location, LocationLink, Location[] or LocationLink[]." 5201 (setq locations 5202 (pcase locations 5203 ((seq (or (Location) 5204 (LocationLink))) 5205 (append locations nil)) 5206 ((or (Location) 5207 (LocationLink)) 5208 (list locations)))) 5209 5210 (cl-labels ((get-xrefs-in-file 5211 (file-locs) 5212 (-let [(filename . matches) file-locs] 5213 (condition-case err 5214 (let ((visiting (find-buffer-visiting filename)) 5215 (fn (lambda (loc) 5216 (lsp-with-filename filename 5217 (lsp--xref-make-item filename 5218 (lsp--location-range loc)))))) 5219 (if visiting 5220 (with-current-buffer visiting 5221 (seq-map fn matches)) 5222 (when (file-readable-p filename) 5223 (with-temp-buffer 5224 (insert-file-contents-literally filename) 5225 (seq-map fn matches))))) 5226 (error (lsp-warn "Failed to process xref entry for filename '%s': %s" 5227 filename (error-message-string err))) 5228 (file-error (lsp-warn "Failed to process xref entry, file-error, '%s': %s" 5229 filename (error-message-string err))))))) 5230 5231 (->> locations 5232 (seq-sort #'lsp--location-before-p) 5233 (seq-group-by (-compose #'lsp--uri-to-path #'lsp--location-uri)) 5234 (seq-map #'get-xrefs-in-file) 5235 (apply #'nconc)))) 5236 5237 (defun lsp--location-before-p (left right) 5238 "Sort first by file, then by line, then by column." 5239 (let ((left-uri (lsp--location-uri left)) 5240 (right-uri (lsp--location-uri right))) 5241 (if (not (string= left-uri right-uri)) 5242 (string< left-uri right-uri) 5243 (-let (((&Range :start left-start) (lsp--location-range left)) 5244 ((&Range :start right-start) (lsp--location-range right))) 5245 (lsp--position-compare right-start left-start))))) 5246 5247 (defun lsp--make-reference-params (&optional td-position exclude-declaration) 5248 "Make a ReferenceParam object. 5249 If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead. 5250 If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." 5251 (let ((json-false :json-false)) 5252 (plist-put (or td-position (lsp--text-document-position-params)) 5253 :context `(:includeDeclaration ,(lsp-json-bool (not exclude-declaration)))))) 5254 5255 (defun lsp--cancel-request (id) 5256 "Cancel request with ID in all workspaces." 5257 (lsp-foreach-workspace 5258 (->> lsp--cur-workspace lsp--workspace-client lsp--client-response-handlers (remhash id)) 5259 (lsp-notify "$/cancelRequest" `(:id ,id)))) 5260 5261 (defvar-local lsp--hover-saved-bounds nil) 5262 5263 (defun lsp-eldoc-function (cb &rest _ignored) 5264 "`lsp-mode' eldoc function to display hover info (based on `textDocument/hover')." 5265 (if (and lsp--hover-saved-bounds 5266 (lsp--point-in-bounds-p lsp--hover-saved-bounds)) 5267 lsp--eldoc-saved-message 5268 (setq lsp--hover-saved-bounds nil 5269 lsp--eldoc-saved-message nil) 5270 (if (looking-at-p "[[:space:]\n]") 5271 (setq lsp--eldoc-saved-message nil) ; And returns nil. 5272 (when (and lsp-eldoc-enable-hover (lsp-feature? "textDocument/hover")) 5273 (lsp-request-async 5274 "textDocument/hover" 5275 (lsp--text-document-position-params) 5276 (-lambda ((hover &as &Hover? :range? :contents)) 5277 (setq lsp--hover-saved-bounds (when range? 5278 (lsp--range-to-region range?))) 5279 (funcall cb (setq lsp--eldoc-saved-message 5280 (when contents 5281 (lsp--render-on-hover-content 5282 contents 5283 lsp-eldoc-render-all))))) 5284 :error-handler #'ignore 5285 :mode 'tick 5286 :cancel-token :eldoc-hover))))) 5287 5288 (defun lsp--point-on-highlight? () 5289 (-some? (lambda (overlay) 5290 (overlay-get overlay 'lsp-highlight)) 5291 (overlays-at (point)))) 5292 5293 (defun lsp--cleanup-highlights-if-needed () 5294 (when (and lsp-enable-symbol-highlighting 5295 lsp--have-document-highlights 5296 (not (lsp--point-on-highlight?))) 5297 (lsp--remove-overlays 'lsp-highlight) 5298 (setq lsp--have-document-highlights nil) 5299 (lsp-cancel-request-by-token :highlights))) 5300 5301 (defvar-local lsp--symbol-bounds-of-last-highlight-invocation nil 5302 "The bounds of the symbol from which `lsp--document-highlight' 5303 most recently requested highlights.") 5304 5305 (defun lsp--document-highlight () 5306 (when (lsp-feature? "textDocument/documentHighlight") 5307 (let ((curr-sym-bounds (bounds-of-thing-at-point 'symbol))) 5308 (unless (or (looking-at-p "[[:space:]\n]") 5309 (not lsp-enable-symbol-highlighting) 5310 (and lsp--have-document-highlights 5311 curr-sym-bounds 5312 (equal curr-sym-bounds 5313 lsp--symbol-bounds-of-last-highlight-invocation))) 5314 (setq lsp--symbol-bounds-of-last-highlight-invocation 5315 curr-sym-bounds) 5316 (lsp-request-async "textDocument/documentHighlight" 5317 (lsp--text-document-position-params) 5318 #'lsp--document-highlight-callback 5319 :mode 'tick 5320 :cancel-token :highlights))))) 5321 5322 (defun lsp--help-open-link (&rest _) 5323 "Open markdown link at point via mouse or keyboard." 5324 (interactive "P") 5325 (let ((buffer-list-update-hook nil)) 5326 (-let [(buffer point) (if-let* ((valid (and (listp last-input-event) 5327 (eq (car last-input-event) 'mouse-2))) 5328 (event (cadr last-input-event)) 5329 (win (posn-window event)) 5330 (buffer (window-buffer win))) 5331 `(,buffer ,(posn-point event)) 5332 `(,(current-buffer) ,(point)))] 5333 (with-current-buffer buffer 5334 (when-let* ((face (get-text-property point 'face)) 5335 (url (or (and (eq face 'markdown-link-face) 5336 (get-text-property point 'help-echo)) 5337 (and (memq face '(markdown-url-face markdown-plain-url-face)) 5338 (nth 3 (markdown-link-at-pos point)))))) 5339 (lsp--document-link-handle-target url)))))) 5340 5341 (defvar lsp-help-mode-map 5342 (-doto (make-sparse-keymap) 5343 (define-key [remap markdown-follow-link-at-point] #'lsp--help-open-link)) 5344 "Keymap for `lsp-help-mode'.") 5345 5346 (define-derived-mode lsp-help-mode help-mode "LspHelp" 5347 "Major mode for displaying lsp help.") 5348 5349 (defun lsp-describe-thing-at-point () 5350 "Display the type signature and documentation of the thing at point." 5351 (interactive) 5352 (let ((contents (-some->> (lsp--text-document-position-params) 5353 (lsp--make-request "textDocument/hover") 5354 (lsp--send-request) 5355 (lsp:hover-contents)))) 5356 (if (and contents (not (equal contents ""))) 5357 (let ((lsp-help-buf-name "*lsp-help*")) 5358 (with-current-buffer (get-buffer-create lsp-help-buf-name) 5359 (delay-mode-hooks 5360 (lsp-help-mode) 5361 (with-help-window lsp-help-buf-name 5362 (insert (string-trim-right (lsp--render-on-hover-content contents t))))) 5363 (run-mode-hooks))) 5364 (lsp--info "No content at point.")))) 5365 5366 (defun lsp--point-in-bounds-p (bounds) 5367 "Return whether the current point is within BOUNDS." 5368 (and (<= (car bounds) (point)) (< (point) (cdr bounds)))) 5369 5370 (defun lsp-get-renderer (language) 5371 "Get renderer for LANGUAGE." 5372 (lambda (str) 5373 (lsp--render-string str language))) 5374 5375 (defun lsp--setup-markdown (mode) 5376 "Setup the ‘markdown-mode’ in the frame. 5377 MODE is the mode used in the parent frame." 5378 (make-local-variable 'markdown-code-lang-modes) 5379 (dolist (mark (alist-get mode lsp-custom-markup-modes)) 5380 (add-to-list 'markdown-code-lang-modes (cons mark mode))) 5381 (setq-local markdown-fontify-code-blocks-natively t) 5382 (setq-local markdown-fontify-code-block-default-mode mode) 5383 (setq-local markdown-hide-markup t) 5384 5385 ;; Render some common HTML entities. 5386 ;; This should really happen in markdown-mode instead, 5387 ;; but it doesn't, so we do it here for now. 5388 (setq prettify-symbols-alist 5389 (cl-loop for i from 0 to 255 5390 collect (cons (format "&#x%02X;" i) i))) 5391 (push '("<" . ?<) prettify-symbols-alist) 5392 (push '(">" . ?>) prettify-symbols-alist) 5393 (push '("&" . ?&) prettify-symbols-alist) 5394 (push '(" " . ? ) prettify-symbols-alist) 5395 (setq prettify-symbols-compose-predicate 5396 (lambda (_start _end _match) t)) 5397 (prettify-symbols-mode 1)) 5398 5399 (defvar lsp-help-link-keymap 5400 (let ((map (make-sparse-keymap))) 5401 (define-key map [mouse-2] #'lsp--help-open-link) 5402 (define-key map "\r" #'lsp--help-open-link) 5403 map) 5404 "Keymap active on links in *lsp-help* mode.") 5405 5406 (defun lsp--fix-markdown-links () 5407 (let ((inhibit-read-only t) 5408 (inhibit-modification-hooks t) 5409 (prop)) 5410 (save-restriction 5411 (goto-char (point-min)) 5412 (while (setq prop (markdown-find-next-prop 'face)) 5413 (let ((end (or (next-single-property-change (car prop) 'face) 5414 (point-max)))) 5415 (when (memq (get-text-property (car prop) 'face) 5416 '(markdown-link-face 5417 markdown-url-face 5418 markdown-plain-url-face)) 5419 (add-text-properties (car prop) end 5420 (list 'button t 5421 'category 'lsp-help-link 5422 'follow-link t 5423 'keymap lsp-help-link-keymap))) 5424 (goto-char end)))))) 5425 5426 (defun lsp--buffer-string-visible () 5427 "Return visible buffer string. 5428 Stolen from `org-copy-visible'." 5429 (let ((temp (generate-new-buffer " *temp*")) 5430 (beg (point-min)) 5431 (end (point-max))) 5432 (while (/= beg end) 5433 (when (get-char-property beg 'invisible) 5434 (setq beg (next-single-char-property-change beg 'invisible nil end))) 5435 (let* ((next (next-single-char-property-change beg 'invisible nil end)) 5436 (substring (buffer-substring beg next))) 5437 (with-current-buffer temp (insert substring)) 5438 ;; (setq result (concat result substring)) 5439 (setq beg next))) 5440 (setq deactivate-mark t) 5441 (prog1 (with-current-buffer temp 5442 (s-chop-suffix "\n" (buffer-string))) 5443 (kill-buffer temp)))) 5444 5445 (defvar lsp-buffer-major-mode nil 5446 "Holds the major mode when fontification function is running. 5447 See #2588") 5448 5449 (defvar view-inhibit-help-message) 5450 5451 (defun lsp--render-markdown () 5452 "Render markdown." 5453 5454 (let ((markdown-enable-math nil)) 5455 (goto-char (point-min)) 5456 (while (re-search-forward 5457 (rx (and "\\" (group (or "\\" "`" "*" "_" ":" "/" 5458 "{" "}" "[" "]" "(" ")" 5459 "#" "+" "-" "." "!" "|")))) 5460 nil t) 5461 (replace-match (rx (backref 1)))) 5462 5463 ;; markdown-mode v2.3 does not yet provide gfm-view-mode 5464 (if (fboundp 'gfm-view-mode) 5465 (let ((view-inhibit-help-message t)) 5466 (gfm-view-mode)) 5467 (gfm-mode)) 5468 5469 (lsp--setup-markdown lsp-buffer-major-mode))) 5470 5471 (defvar lsp--display-inline-image-alist 5472 '((lsp--render-markdown 5473 (:regexp 5474 "!\\[.*?\\](data:image/[a-zA-Z]+;base64,\\([A-Za-z0-9+/\n]+?=*?\\)\\(|[^)]+\\)?)" 5475 :sexp 5476 (create-image 5477 (base64-decode-string 5478 (buffer-substring-no-properties (match-beginning 1) (match-end 1))) 5479 nil t)))) 5480 "Replaced string regexp and function returning image. 5481 Each element should have the form (MODE . (PROPERTY-LIST...)). 5482 MODE (car) is function which is defined in `lsp-language-id-configuration'. 5483 Cdr should be list of PROPERTY-LIST. 5484 5485 Each PROPERTY-LIST should have properties: 5486 :regexp Regexp which determines what string is relpaced to image. 5487 You should also get information of image, by parenthesis constructs. 5488 By default, all matched string is replaced to image, but you can 5489 change index of replaced string by keyword :replaced-index. 5490 5491 :sexp Return image when evaluated. You can use information of regexp 5492 by using (match-beggining N), (match-end N) or (match-substring N). 5493 5494 In addition, each can have property: 5495 :replaced-index Determine index which is used to replace regexp to image. 5496 The value means first argument of `match-beginning' and 5497 `match-end'. If omitted, interpreted as index 0.") 5498 5499 (defcustom lsp-display-inline-image t 5500 "Showing inline image or not." 5501 :group 'lsp-mode 5502 :type 'boolean) 5503 5504 (defcustom lsp-enable-suggest-server-download t 5505 "When non-nil enable server downloading suggestions." 5506 :group 'lsp-mode 5507 :type 'boolean 5508 :package-version '(lsp-mode . "9.0.0")) 5509 5510 (defcustom lsp-auto-register-remote-clients t 5511 "When non-nil register remote when registering the local one." 5512 :group 'lsp-mode 5513 :type 'boolean 5514 :package-version '(lsp-mode . "9.0.0")) 5515 5516 (defun lsp--display-inline-image (mode) 5517 "Add image property if available." 5518 (let ((plist-list (cdr (assq mode lsp--display-inline-image-alist)))) 5519 (when (and (display-images-p) lsp-display-inline-image) 5520 (cl-loop 5521 for plist in plist-list 5522 with regexp with replaced-index 5523 do 5524 (setq regexp (plist-get plist :regexp)) 5525 (setq replaced-index (or (plist-get plist :replaced-index) 0)) 5526 5527 (font-lock-remove-keywords nil (list regexp replaced-index)) 5528 (let ((inhibit-read-only t)) 5529 (save-excursion 5530 (goto-char (point-min)) 5531 (while (re-search-forward regexp nil t) 5532 (set-text-properties 5533 (match-beginning replaced-index) (match-end replaced-index) 5534 nil) 5535 (add-text-properties 5536 (match-beginning replaced-index) (match-end replaced-index) 5537 `(display ,(eval (plist-get plist :sexp))))))))))) 5538 5539 (defun lsp--fontlock-with-mode (str mode) 5540 "Fontlock STR with MODE." 5541 (let ((lsp-buffer-major-mode major-mode)) 5542 (with-temp-buffer 5543 (with-demoted-errors "Error during doc rendering: %s" 5544 (insert str) 5545 (delay-mode-hooks (funcall mode)) 5546 (cl-flet ((window-body-width () lsp-window-body-width)) 5547 ;; This can go wrong in some cases, and the fontification would 5548 ;; not work as expected. 5549 ;; 5550 ;; See #2984 5551 (ignore-errors (font-lock-ensure)) 5552 (lsp--display-inline-image mode) 5553 (when (eq mode 'lsp--render-markdown) 5554 (lsp--fix-markdown-links)))) 5555 (lsp--buffer-string-visible)))) 5556 5557 (defun lsp--render-string (str language) 5558 "Render STR using `major-mode' corresponding to LANGUAGE. 5559 When language is nil render as markup if `markdown-mode' is loaded." 5560 (setq str (s-replace "\r" "" (or str ""))) 5561 (if-let* ((modes (-keep (-lambda ((mode . lang)) 5562 (when (and (equal lang language) (functionp mode)) 5563 mode)) 5564 lsp-language-id-configuration)) 5565 (mode (car (or (member major-mode modes) modes)))) 5566 (lsp--fontlock-with-mode str mode) 5567 str)) 5568 5569 (defun lsp--render-element (content) 5570 "Render CONTENT element." 5571 (let ((inhibit-message t)) 5572 (or 5573 (pcase content 5574 ((MarkedString :value :language) 5575 (lsp--render-string value language)) 5576 ((MarkupContent :value :kind) 5577 (lsp--render-string value kind)) 5578 ;; plain string 5579 ((pred stringp) (lsp--render-string content "markdown")) 5580 ((pred null) "") 5581 (_ (error "Failed to handle %s" content))) 5582 ""))) 5583 5584 (defun lsp--create-unique-string-fn () 5585 (let (elements) 5586 (lambda (element) 5587 (let ((count (cl-count element elements :test #'string=))) 5588 (prog1 (if (zerop count) 5589 element 5590 (format "%s (%s)" element count)) 5591 (push element elements)))))) 5592 5593 (defun lsp--select-action (actions) 5594 "Select an action to execute from ACTIONS." 5595 (cond 5596 ((seq-empty-p actions) (signal 'lsp-no-code-actions nil)) 5597 ((and (eq (seq-length actions) 1) lsp-auto-execute-action) 5598 (lsp-seq-first actions)) 5599 (t (let ((completion-ignore-case t)) 5600 (lsp--completing-read "Select code action: " 5601 (seq-into actions 'list) 5602 (-compose (lsp--create-unique-string-fn) 5603 #'lsp:code-action-title) 5604 nil t))))) 5605 5606 (defun lsp--workspace-server-id (workspace) 5607 "Return the server ID of WORKSPACE." 5608 (-> workspace lsp--workspace-client lsp--client-server-id)) 5609 5610 (defun lsp--handle-rendered-for-echo-area (contents) 5611 "Return a single line from RENDERED, appropriate for display in the echo area." 5612 (pcase (lsp-workspaces) 5613 (`(,workspace) 5614 (lsp-clients-extract-signature-on-hover contents (lsp--workspace-server-id workspace))) 5615 ;; For projects with multiple active workspaces we also default to 5616 ;; render the first line. 5617 (_ (lsp-clients-extract-signature-on-hover contents nil)))) 5618 5619 (cl-defmethod lsp-clients-extract-signature-on-hover (contents _server-id) 5620 "Extract a representative line from CONTENTS, to show in the echo area." 5621 (car (s-lines (s-trim (lsp--render-element contents))))) 5622 5623 (defun lsp--render-on-hover-content (contents render-all) 5624 "Render the content received from `document/onHover' request. 5625 CONTENTS - MarkedString | MarkedString[] | MarkupContent 5626 RENDER-ALL - nil if only the signature should be rendered." 5627 (cond 5628 ((lsp-markup-content? contents) 5629 ;; MarkupContent. 5630 ;; It tends to be long and is not suitable to display fully in the echo area. 5631 ;; Just display the first line which is typically the signature. 5632 (if render-all 5633 (lsp--render-element contents) 5634 (lsp--handle-rendered-for-echo-area contents))) 5635 ((and (stringp contents) (not (string-match-p "\n" contents))) 5636 ;; If the contents is a single string containing a single line, 5637 ;; render it always. 5638 (lsp--render-element contents)) 5639 (t 5640 ;; MarkedString -> MarkedString[] 5641 (when (or (lsp-marked-string? contents) (stringp contents)) 5642 (setq contents (list contents))) 5643 ;; Consider the signature consisting of the elements who have a renderable 5644 ;; "language" property. When render-all is nil, ignore other elements. 5645 (string-join 5646 (seq-map 5647 #'lsp--render-element 5648 (if render-all 5649 contents 5650 ;; Only render contents that have an available renderer. 5651 (seq-take 5652 (seq-filter 5653 (-andfn #'lsp-marked-string? 5654 (-compose #'lsp-get-renderer #'lsp:marked-string-language)) 5655 contents) 5656 1))) 5657 (if (bound-and-true-p page-break-lines-mode) 5658 "\n\n" 5659 "\n"))))) 5660 5661 5662 5663 (defvar lsp-signature-mode-map 5664 (-doto (make-sparse-keymap) 5665 (define-key (kbd "M-n") #'lsp-signature-next) 5666 (define-key (kbd "M-p") #'lsp-signature-previous) 5667 (define-key (kbd "M-a") #'lsp-signature-toggle-full-docs) 5668 (define-key (kbd "C-c C-k") #'lsp-signature-stop) 5669 (define-key (kbd "C-g") #'lsp-signature-stop)) 5670 "Keymap for `lsp-signature-mode'.") 5671 5672 (define-minor-mode lsp-signature-mode 5673 "Mode used to show signature popup." 5674 :keymap lsp-signature-mode-map 5675 :lighter "" 5676 :group 'lsp-mode) 5677 5678 (defun lsp-signature-stop () 5679 "Stop showing current signature help." 5680 (interactive) 5681 (lsp-cancel-request-by-token :signature) 5682 (remove-hook 'post-command-hook #'lsp-signature) 5683 (funcall lsp-signature-function nil) 5684 (lsp-signature-mode -1)) 5685 5686 (declare-function page-break-lines--update-display-tables "ext:page-break-lines") 5687 5688 (defun lsp--setup-page-break-mode-if-present () 5689 "Enable `page-break-lines-mode' in current buffer." 5690 (when (fboundp 'page-break-lines-mode) 5691 (page-break-lines-mode) 5692 ;; force page-break-lines-mode to update the display tables. 5693 (page-break-lines--update-display-tables))) 5694 5695 (defun lsp-lv-message (message) 5696 (add-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present) 5697 (if message 5698 (progn 5699 (setq lsp--signature-last-buffer (current-buffer)) 5700 (let ((lv-force-update t)) 5701 (lv-message "%s" message))) 5702 (lv-delete-window) 5703 (remove-hook 'lv-window-hook #'lsp--setup-page-break-mode-if-present))) 5704 5705 (declare-function posframe-show "ext:posframe") 5706 (declare-function posframe-hide "ext:posframe") 5707 (declare-function posframe-poshandler-point-bottom-left-corner-upward "ext:posframe") 5708 5709 (defface lsp-signature-posframe 5710 '((t :inherit tooltip)) 5711 "Background and foreground for `lsp-signature-posframe'." 5712 :group 'lsp-mode) 5713 5714 (defvar lsp-signature-posframe-params 5715 (list :poshandler #'posframe-poshandler-point-bottom-left-corner-upward 5716 :height 10 5717 :width 60 5718 :border-width 1 5719 :min-width 60) 5720 "Params for signature and `posframe-show'.") 5721 5722 (defun lsp-signature-posframe (str) 5723 "Use posframe to show the STR signatureHelp string." 5724 (if str 5725 (apply #'posframe-show 5726 (with-current-buffer (get-buffer-create " *lsp-signature*") 5727 (erase-buffer) 5728 (insert str) 5729 (visual-line-mode 1) 5730 (lsp--setup-page-break-mode-if-present) 5731 (current-buffer)) 5732 (append 5733 lsp-signature-posframe-params 5734 (list :position (point) 5735 :background-color (face-attribute 'lsp-signature-posframe :background nil t) 5736 :foreground-color (face-attribute 'lsp-signature-posframe :foreground nil t) 5737 :border-color (face-attribute 'font-lock-comment-face :foreground nil t)))) 5738 (posframe-hide " *lsp-signature*"))) 5739 5740 (defun lsp--handle-signature-update (signature) 5741 (let ((message 5742 (if (lsp-signature-help? signature) 5743 (lsp--signature->message signature) 5744 (mapconcat #'lsp--signature->message signature "\n")))) 5745 (if (s-present? message) 5746 (funcall lsp-signature-function message) 5747 (lsp-signature-stop)))) 5748 5749 (defun lsp-signature-activate () 5750 "Activate signature help. 5751 It will show up only if current point has signature help." 5752 (interactive) 5753 (setq lsp--signature-last nil 5754 lsp--signature-last-index nil 5755 lsp--signature-last-buffer (current-buffer)) 5756 (add-hook 'post-command-hook #'lsp-signature) 5757 (lsp-signature-mode t)) 5758 5759 (defcustom lsp-signature-cycle t 5760 "Whether `lsp-signature-next' and prev should cycle." 5761 :type 'boolean 5762 :group 'lsp-mode) 5763 5764 (defun lsp-signature-next () 5765 "Show next signature." 5766 (interactive) 5767 (let ((nsigs (length (lsp:signature-help-signatures lsp--signature-last)))) 5768 (when (and lsp--signature-last-index 5769 lsp--signature-last 5770 (or lsp-signature-cycle (< (1+ lsp--signature-last-index) nsigs))) 5771 (setq lsp--signature-last-index (% (1+ lsp--signature-last-index) nsigs)) 5772 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last))))) 5773 5774 (defun lsp-signature-previous () 5775 "Next signature." 5776 (interactive) 5777 (when (and lsp--signature-last-index 5778 lsp--signature-last 5779 (or lsp-signature-cycle (not (zerop lsp--signature-last-index)))) 5780 (setq lsp--signature-last-index (1- (if (zerop lsp--signature-last-index) 5781 (length (lsp:signature-help-signatures lsp--signature-last)) 5782 lsp--signature-last-index))) 5783 (funcall lsp-signature-function (lsp--signature->message lsp--signature-last)))) 5784 5785 (defun lsp-signature-toggle-full-docs () 5786 "Toggle full/partial signature documentation." 5787 (interactive) 5788 (let ((all? (not (numberp lsp-signature-doc-lines)))) 5789 (setq lsp-signature-doc-lines (if all? 5790 (or (car-safe lsp-signature-doc-lines) 5791 20) 5792 (list lsp-signature-doc-lines)))) 5793 (lsp-signature-activate)) 5794 5795 (defun lsp--signature->message (signature-help) 5796 "Generate eldoc message from SIGNATURE-HELP response." 5797 (setq lsp--signature-last signature-help) 5798 5799 (when (and signature-help (not (seq-empty-p (lsp:signature-help-signatures signature-help)))) 5800 (-let* (((&SignatureHelp :active-signature? 5801 :active-parameter? 5802 :signatures) signature-help) 5803 (active-signature? (or lsp--signature-last-index active-signature? 0)) 5804 (_ (setq lsp--signature-last-index active-signature?)) 5805 ((signature &as &SignatureInformation? :label :parameters?) (seq-elt signatures active-signature?)) 5806 (prefix (if (= (length signatures) 1) 5807 "" 5808 (concat (propertize (format " %s/%s" 5809 (1+ active-signature?) 5810 (length signatures)) 5811 'face 'success) 5812 " "))) 5813 (method-docs (when 5814 (and lsp-signature-render-documentation 5815 (or (not (numberp lsp-signature-doc-lines)) (< 0 lsp-signature-doc-lines))) 5816 (let ((docs (lsp--render-element 5817 (lsp:parameter-information-documentation? signature)))) 5818 (when (s-present? docs) 5819 (concat 5820 "\n" 5821 (if (fboundp 'page-break-lines-mode) 5822 "\n" 5823 "") 5824 (if (and (numberp lsp-signature-doc-lines) 5825 (> (length (s-lines docs)) lsp-signature-doc-lines)) 5826 (concat (s-join "\n" (-take lsp-signature-doc-lines (s-lines docs))) 5827 (propertize "\nTruncated..." 'face 'highlight)) 5828 docs))))))) 5829 (when (and active-parameter? (not (seq-empty-p parameters?))) 5830 (-when-let* ((param (when (and (< -1 active-parameter? (length parameters?))) 5831 (seq-elt parameters? active-parameter?))) 5832 (selected-param-label (let ((label (lsp:parameter-information-label param))) 5833 (if (stringp label) label (append label nil)))) 5834 (start (if (stringp selected-param-label) 5835 (s-index-of selected-param-label label) 5836 (cl-first selected-param-label))) 5837 (end (if (stringp selected-param-label) 5838 (+ start (length selected-param-label)) 5839 (cl-second selected-param-label)))) 5840 (add-face-text-property start end 'eldoc-highlight-function-argument nil label))) 5841 (concat prefix label method-docs)))) 5842 5843 (defun lsp-signature () 5844 "Display signature info (based on `textDocument/signatureHelp')" 5845 (if (and lsp--signature-last-buffer 5846 (not (equal (current-buffer) lsp--signature-last-buffer))) 5847 (lsp-signature-stop) 5848 (lsp-request-async "textDocument/signatureHelp" 5849 (lsp--text-document-position-params) 5850 #'lsp--handle-signature-update 5851 :cancel-token :signature))) 5852 5853 5854 (defcustom lsp-overlay-document-color-char "■" 5855 "Display the char represent the document color in overlay" 5856 :type 'string 5857 :group 'lsp-mode) 5858 5859 ;; color presentation 5860 (defun lsp--color-create-interactive-command (color range) 5861 (lambda () 5862 (interactive) 5863 (-let [(&ColorPresentation? :text-edit? 5864 :additional-text-edits?) 5865 (lsp--completing-read 5866 "Select color presentation: " 5867 (lsp-request 5868 "textDocument/colorPresentation" 5869 `( :textDocument ,(lsp--text-document-identifier) 5870 :color ,color 5871 :range ,range)) 5872 #'lsp:color-presentation-label 5873 nil 5874 t)] 5875 (when text-edit? 5876 (lsp--apply-text-edit text-edit?)) 5877 (when additional-text-edits? 5878 (lsp--apply-text-edits additional-text-edits? 'color-presentation))))) 5879 5880 (defun lsp--number->color (number) 5881 (let ((result (format "%x" 5882 (round (* (or number 0) 255.0))))) 5883 (if (= 1 (length result)) 5884 (concat "0" result) 5885 result))) 5886 5887 (defun lsp--document-color () 5888 "Document color handler." 5889 (when (lsp-feature? "textDocument/documentColor") 5890 (lsp-request-async 5891 "textDocument/documentColor" 5892 `(:textDocument ,(lsp--text-document-identifier)) 5893 (lambda (result) 5894 (lsp--remove-overlays 'lsp-color) 5895 (seq-do 5896 (-lambda ((&ColorInformation :color (color &as &Color :red :green :blue) 5897 :range)) 5898 (-let* (((beg . end) (lsp--range-to-region range)) 5899 (overlay (make-overlay beg end)) 5900 (command (lsp--color-create-interactive-command color range))) 5901 (overlay-put overlay 'lsp-color t) 5902 (overlay-put overlay 'evaporate t) 5903 (overlay-put overlay 5904 'before-string 5905 (propertize 5906 lsp-overlay-document-color-char 5907 'face `((:foreground ,(format 5908 "#%s%s%s" 5909 (lsp--number->color red) 5910 (lsp--number->color green) 5911 (lsp--number->color blue)))) 5912 'action command 5913 'mouse-face 'lsp-lens-mouse-face 5914 'local-map (-doto (make-sparse-keymap) 5915 (define-key [mouse-1] command)))))) 5916 result)) 5917 :mode 'unchanged 5918 :cancel-token :document-color-token))) 5919 5920 5921 5922 (defun lsp--action-trigger-parameter-hints (_command) 5923 "Handler for editor.action.triggerParameterHints." 5924 (when (member :on-server-request lsp-signature-auto-activate) 5925 (lsp-signature-activate))) 5926 5927 (defun lsp--action-trigger-suggest (_command) 5928 "Handler for editor.action.triggerSuggest." 5929 (cond 5930 ((and (bound-and-true-p company-mode) 5931 (fboundp 'company-auto-begin) 5932 (fboundp 'company-post-command)) 5933 (run-at-time 0 nil 5934 (lambda () 5935 (let ((this-command 'company-idle-begin) 5936 (company-minimum-prefix-length 0)) 5937 (company-auto-begin) 5938 (company-post-command))))) 5939 (t 5940 (completion-at-point)))) 5941 5942 (defconst lsp--default-action-handlers 5943 (ht ("editor.action.triggerParameterHints" #'lsp--action-trigger-parameter-hints) 5944 ("editor.action.triggerSuggest" #'lsp--action-trigger-suggest)) 5945 "Default action handlers.") 5946 5947 (defun lsp--find-action-handler (command) 5948 "Find action handler for particular COMMAND." 5949 (or 5950 (--some (-some->> it 5951 (lsp--workspace-client) 5952 (lsp--client-action-handlers) 5953 (gethash command)) 5954 (lsp-workspaces)) 5955 (gethash command lsp--default-action-handlers))) 5956 5957 (defun lsp--text-document-code-action-params (&optional kind) 5958 "Code action params." 5959 (list :textDocument (lsp--text-document-identifier) 5960 :range (if (use-region-p) 5961 (lsp--region-to-range (region-beginning) (region-end)) 5962 (lsp--region-to-range (point) (point))) 5963 :context `( :diagnostics ,(lsp-cur-possition-diagnostics) 5964 ,@(when kind (list :only (vector kind)))))) 5965 5966 (defun lsp-code-actions-at-point (&optional kind) 5967 "Retrieve the code actions for the active region or the current line. 5968 It will filter by KIND if non nil." 5969 (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind))) 5970 5971 (defun lsp-execute-code-action-by-kind (command-kind) 5972 "Execute code action by COMMAND-KIND." 5973 (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind) 5974 (-filter (-lambda ((&CodeAction :kind?)) 5975 (and kind? (s-prefix? command-kind kind?)))) 5976 lsp--select-action))) 5977 (lsp-execute-code-action action) 5978 (signal 'lsp-no-code-actions '(command-kind)))) 5979 5980 (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point) 5981 5982 (lsp-defun lsp--execute-command ((action &as &Command :command :arguments?)) 5983 "Parse and execute a code ACTION represented as a Command LSP type." 5984 (let ((server-id (->> (lsp-workspaces) 5985 (cl-first) 5986 (or lsp--cur-workspace) 5987 (lsp--workspace-client) 5988 (lsp--client-server-id)))) 5989 (condition-case nil 5990 (with-no-warnings 5991 (lsp-execute-command server-id (intern command) arguments?)) 5992 (cl-no-applicable-method 5993 (if-let ((action-handler (lsp--find-action-handler command))) 5994 (funcall action-handler action) 5995 (lsp-send-execute-command command arguments?)))))) 5996 5997 (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?)) 5998 "Execute code action ACTION. For example, when text under the 5999 caret has a suggestion to apply a fix from an lsp-server, calling 6000 this function will do so. 6001 If ACTION is not set it will be selected from `lsp-code-actions-at-point'. 6002 Request codeAction/resolve for more info if server supports." 6003 (interactive (list (lsp--select-action (lsp-code-actions-at-point)))) 6004 (if (and (lsp-feature? "codeAction/resolve") 6005 (not command?) 6006 (not edit?)) 6007 (lsp--execute-code-action (lsp-request "codeAction/resolve" action)) 6008 (lsp--execute-code-action action))) 6009 6010 (lsp-defun lsp--execute-code-action ((action &as &CodeAction :command? :edit?)) 6011 "Execute code action ACTION." 6012 (when edit? 6013 (lsp--apply-workspace-edit edit? 'code-action)) 6014 6015 (cond 6016 ((stringp command?) (lsp--execute-command action)) 6017 ((lsp-command? command?) (progn 6018 (when-let ((action-filter (->> (lsp-workspaces) 6019 (cl-first) 6020 (or lsp--cur-workspace) 6021 (lsp--workspace-client) 6022 (lsp--client-action-filter)))) 6023 (funcall action-filter command?)) 6024 (lsp--execute-command command?))))) 6025 6026 (lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments) 6027 "Patch incorrect boolean argument values in the provided `CodeAction' command 6028 in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values 6029 in this list can be either symbols or lists of symbols that 6030 represent paths to boolean arguments in code actions: 6031 6032 > (lsp-fix-code-action-booleans command `(:foo :bar (:some :nested :boolean))) 6033 6034 When there are available code actions, the server sends 6035 `lsp-mode' a list of possible command names and arguments as 6036 JSON. `lsp-mode' parses all boolean false values as `nil'. As a 6037 result code action arguments containing falsy values don't 6038 roundtrip correctly because `lsp-mode' will end up sending null 6039 values back to the client. This list makes it possible to 6040 selectively transform `nil' values back into `:json-false'." 6041 (seq-doseq (path boolean-action-arguments) 6042 (seq-doseq (args arguments?) 6043 (lsp--fix-nested-boolean args (if (listp path) path (list path)))))) 6044 6045 (defun lsp--fix-nested-boolean (structure path) 6046 "Traverse STRUCTURE using the paths from the PATH list, changing the value to 6047 `:json-false' if it was `nil'. PATH should be a list containing 6048 one or more symbols, and STRUCTURE should be compatible with 6049 `lsp-member?', `lsp-get', and `lsp-put'." 6050 (let ((key (car path)) 6051 (rest (cdr path))) 6052 (if (null rest) 6053 ;; `lsp-put' returns `nil' both when the key doesn't exist and when the 6054 ;; value is `nil', so we need to explicitly check its presence here 6055 (when (and (lsp-member? structure key) (not (lsp-get structure key))) 6056 (lsp-put structure key :json-false)) 6057 ;; If `key' does not exist, then we'll silently ignore it 6058 (when-let ((child (lsp-get structure key))) 6059 (lsp--fix-nested-boolean child rest))))) 6060 6061 (defvar lsp--formatting-indent-alist 6062 ;; Taken from `dtrt-indent-mode' 6063 '( 6064 (ada-mode . ada-indent) ; Ada 6065 (ada-ts-mode . ada-ts-mode-indent-offset) 6066 (c++-mode . c-basic-offset) ; C++ 6067 (c++-ts-mode . c-ts-mode-indent-offset) 6068 (c-mode . c-basic-offset) ; C 6069 (c-ts-mode . c-ts-mode-indent-offset) 6070 (cperl-mode . cperl-indent-level) ; Perl 6071 (crystal-mode . crystal-indent-level) ; Crystal (Ruby) 6072 (csharp-mode . c-basic-offset) ; C# 6073 (csharp-tree-sitter-mode . csharp-tree-sitter-indent-offset) ; C# 6074 (csharp-ts-mode . csharp-ts-mode-indent-offset) ; C# (tree-sitter, Emacs29) 6075 (css-mode . css-indent-offset) ; CSS 6076 (d-mode . c-basic-offset) ; D 6077 (enh-ruby-mode . enh-ruby-indent-level) ; Ruby 6078 (erlang-mode . erlang-indent-level) ; Erlang 6079 (ess-mode . ess-indent-offset) ; ESS (R) 6080 (go-ts-mode . go-ts-mode-indent-offset) 6081 (gpr-mode . gpr-indent-offset) ; GNAT Project 6082 (gpr-ts-mode . gpr-ts-mode-indent-offset) 6083 (hack-mode . hack-indent-offset) ; Hack 6084 (java-mode . c-basic-offset) ; Java 6085 (java-ts-mode . java-ts-mode-indent-offset) 6086 (jde-mode . c-basic-offset) ; Java (JDE) 6087 (js-mode . js-indent-level) ; JavaScript 6088 (js-ts-mode . js-indent-level) 6089 (js2-mode . js2-basic-offset) ; JavaScript-IDE 6090 (js3-mode . js3-indent-level) ; JavaScript-IDE 6091 (json-mode . js-indent-level) ; JSON 6092 (json-ts-mode . json-ts-mode-indent-offset) 6093 (lua-mode . lua-indent-level) ; Lua 6094 (lua-ts-mode . lua-ts-indent-offset) 6095 (nxml-mode . nxml-child-indent) ; XML 6096 (objc-mode . c-basic-offset) ; Objective C 6097 (pascal-mode . pascal-indent-level) ; Pascal 6098 (perl-mode . perl-indent-level) ; Perl 6099 (php-mode . c-basic-offset) ; PHP 6100 (php-ts-mode . php-ts-mode-indent-offset) ; PHP 6101 (powershell-mode . powershell-indent) ; PowerShell 6102 (powershell-ts-mode . powershell-ts-mode-indent-offset) ; PowerShell 6103 (raku-mode . raku-indent-offset) ; Perl6/Raku 6104 (ruby-mode . ruby-indent-level) ; Ruby 6105 (rust-mode . rust-indent-offset) ; Rust 6106 (rust-ts-mode . rust-ts-mode-indent-offset) 6107 (rustic-mode . rustic-indent-offset) ; Rust 6108 (scala-mode . scala-indent:step) ; Scala 6109 (sgml-mode . sgml-basic-offset) ; SGML 6110 (sh-mode . sh-basic-offset) ; Shell Script 6111 (toml-ts-mode . toml-ts-mode-indent-offset) 6112 (typescript-mode . typescript-indent-level) ; Typescript 6113 (typescript-ts-mode . typescript-ts-mode-indent-offset) ; Typescript (tree-sitter, Emacs29) 6114 (yaml-mode . yaml-indent-offset) ; YAML 6115 (yang-mode . c-basic-offset) ; YANG (yang-mode) 6116 6117 (default . standard-indent)) ; default fallback 6118 "A mapping from `major-mode' to its indent variable.") 6119 6120 (defun lsp--get-indent-width (mode) 6121 "Get indentation offset for MODE." 6122 (or (alist-get mode lsp--formatting-indent-alist) 6123 (lsp--get-indent-width (or (get mode 'derived-mode-parent) 'default)))) 6124 6125 (defun lsp--make-document-formatting-params () 6126 "Create document formatting params." 6127 (lsp-make-document-formatting-params 6128 :text-document (lsp--text-document-identifier) 6129 :options (lsp-make-formatting-options 6130 :tab-size (symbol-value (lsp--get-indent-width major-mode)) 6131 :insert-spaces (lsp-json-bool (not indent-tabs-mode)) 6132 :trim-trailing-whitespace? (lsp-json-bool lsp-trim-trailing-whitespace) 6133 :insert-final-newline? (lsp-json-bool lsp-insert-final-newline) 6134 :trim-final-newlines? (lsp-json-bool lsp-trim-final-newlines)))) 6135 6136 (defun lsp-format-buffer () 6137 "Ask the server to format this document." 6138 (interactive "*") 6139 (cond ((lsp-feature? "textDocument/formatting") 6140 (let ((edits (lsp-request "textDocument/formatting" 6141 (lsp--make-document-formatting-params)))) 6142 (if (seq-empty-p edits) 6143 (lsp--info "No formatting changes provided") 6144 (lsp--apply-text-edits edits 'format)))) 6145 ((lsp-feature? "textDocument/rangeFormatting") 6146 (save-restriction 6147 (widen) 6148 (lsp-format-region (point-min) (point-max)))) 6149 (t (signal 'lsp-capability-not-supported (list "documentFormattingProvider"))))) 6150 6151 (defun lsp-format-region (s e) 6152 "Ask the server to format the region, or if none is selected, the current line." 6153 (interactive "r") 6154 (let ((edits (lsp-request 6155 "textDocument/rangeFormatting" 6156 (lsp--make-document-range-formatting-params s e)))) 6157 (if (seq-empty-p edits) 6158 (lsp--info "No formatting changes provided") 6159 (lsp--apply-text-edits edits 'format)))) 6160 6161 (defmacro lsp-make-interactive-code-action (func-name code-action-kind) 6162 "Define an interactive function FUNC-NAME that attempts to 6163 execute a CODE-ACTION-KIND action." 6164 `(defun ,(intern (concat "lsp-" (symbol-name func-name))) () 6165 ,(format "Perform the %s code action, if available." code-action-kind) 6166 (interactive) 6167 ;; Even when `lsp-auto-execute-action' is nil, it still makes sense to 6168 ;; auto-execute here: the user has specified exactly what they want. 6169 (let ((lsp-auto-execute-action t)) 6170 (condition-case nil 6171 (lsp-execute-code-action-by-kind ,code-action-kind) 6172 (lsp-no-code-actions 6173 (when (called-interactively-p 'any) 6174 (lsp--info ,(format "%s action not available" code-action-kind)))))))) 6175 6176 (lsp-make-interactive-code-action organize-imports "source.organizeImports") 6177 6178 (defun lsp--make-document-range-formatting-params (start end) 6179 "Make DocumentRangeFormattingParams for selected region." 6180 (lsp:set-document-range-formatting-params-range (lsp--make-document-formatting-params) 6181 (lsp--region-to-range start end))) 6182 6183 (defconst lsp--highlight-kind-face 6184 '((1 . lsp-face-highlight-textual) 6185 (2 . lsp-face-highlight-read) 6186 (3 . lsp-face-highlight-write))) 6187 6188 (defun lsp--remove-overlays (name) 6189 (save-restriction 6190 (widen) 6191 (remove-overlays (point-min) (point-max) name t))) 6192 6193 (defun lsp-document-highlight () 6194 "Highlight all relevant references to the symbol under point." 6195 (interactive) 6196 (lsp--remove-overlays 'lsp-highlight) ;; clear any previous highlights 6197 (setq lsp--have-document-highlights nil 6198 lsp--symbol-bounds-of-last-highlight-invocation nil) 6199 (let ((lsp-enable-symbol-highlighting t)) 6200 (lsp--document-highlight))) 6201 6202 (defun lsp--document-highlight-callback (highlights) 6203 "Create a callback to process the reply of a 6204 `textDocument/documentHighlight' message for the buffer BUF. 6205 A reference is highlighted only if it is visible in a window." 6206 (lsp--remove-overlays 'lsp-highlight) 6207 6208 (let* ((wins-visible-pos (-map (lambda (win) 6209 (cons (1- (line-number-at-pos (window-start win) t)) 6210 (1+ (line-number-at-pos (window-end win) t)))) 6211 (get-buffer-window-list nil nil 'visible)))) 6212 (setq lsp--have-document-highlights t) 6213 (-map 6214 (-lambda ((&DocumentHighlight :range (&Range :start (start &as &Position :line start-line) 6215 :end (end &as &Position :line end-line)) 6216 :kind?)) 6217 (-map 6218 (-lambda ((start-window . end-window)) 6219 ;; Make the overlay only if the reference is visible 6220 (let ((start-point (lsp--position-to-point start)) 6221 (end-point (lsp--position-to-point end))) 6222 (when (and (> (1+ start-line) start-window) 6223 (< (1+ end-line) end-window) 6224 (not (and lsp-symbol-highlighting-skip-current 6225 (<= start-point (point) end-point)))) 6226 (-doto (make-overlay start-point end-point) 6227 (overlay-put 'face (cdr (assq (or kind? 1) lsp--highlight-kind-face))) 6228 (overlay-put 'lsp-highlight t))))) 6229 wins-visible-pos)) 6230 highlights))) 6231 6232 (defcustom lsp-symbol-kinds 6233 '((1 . "File") 6234 (2 . "Module") 6235 (3 . "Namespace") 6236 (4 . "Package") 6237 (5 . "Class") 6238 (6 . "Method") 6239 (7 . "Property") 6240 (8 . "Field") 6241 (9 . "Constructor") 6242 (10 . "Enum") 6243 (11 . "Interface") 6244 (12 . "Function") 6245 (13 . "Variable") 6246 (14 . "Constant") 6247 (15 . "String") 6248 (16 . "Number") 6249 (17 . "Boolean") 6250 (18 . "Array") 6251 (19 . "Object") 6252 (20 . "Key") 6253 (21 . "Null") 6254 (22 . "Enum Member") 6255 (23 . "Struct") 6256 (24 . "Event") 6257 (25 . "Operator") 6258 (26 . "Type Parameter")) 6259 "Alist mapping SymbolKinds to human-readable strings. 6260 Various Symbol objects in the LSP protocol have an integral type, 6261 specifying what they are. This alist maps such type integrals to 6262 readable representations of them. See 6263 `https://microsoft.github.io/language-server-protocol/specifications/specification-current/', 6264 namespace SymbolKind." 6265 :group 'lsp-mode 6266 :type '(alist :key-type integer :value-type string)) 6267 (defalias 'lsp--symbol-kind 'lsp-symbol-kinds) 6268 6269 (lsp-defun lsp--symbol-information-to-xref 6270 ((&SymbolInformation :kind :name 6271 :location (&Location :uri :range (&Range :start 6272 (&Position :line :character))))) 6273 "Return a `xref-item' from SYMBOL information." 6274 (xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name) 6275 (xref-make-file-location (lsp--uri-to-path uri) 6276 line 6277 character))) 6278 6279 (defun lsp--get-document-symbols () 6280 "Get document symbols. 6281 6282 If the buffer has not been modified since symbols were last 6283 retrieved, simply return the latest result. 6284 6285 Else, if the request was initiated by Imenu updating its menu-bar 6286 entry, perform it asynchronously; i.e., give Imenu the latest 6287 result and then force a refresh when a new one is available. 6288 6289 Else (e.g., due to interactive use of `imenu' or `xref'), 6290 perform the request synchronously." 6291 (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick) 6292 lsp--document-symbols 6293 (let ((method "textDocument/documentSymbol") 6294 (params `(:textDocument ,(lsp--text-document-identifier))) 6295 (tick (buffer-chars-modified-tick))) 6296 (if (not lsp--document-symbols-request-async) 6297 (prog1 6298 (setq lsp--document-symbols (lsp-request method params)) 6299 (setq lsp--document-symbols-tick tick)) 6300 (lsp-request-async method params 6301 (lambda (document-symbols) 6302 (setq lsp--document-symbols document-symbols 6303 lsp--document-symbols-tick tick) 6304 (lsp--imenu-refresh)) 6305 :mode 'alive 6306 :cancel-token :document-symbols) 6307 lsp--document-symbols)))) 6308 6309 (advice-add 'imenu-update-menubar :around 6310 (lambda (oldfun &rest r) 6311 (let ((lsp--document-symbols-request-async t)) 6312 (apply oldfun r)))) 6313 6314 (defun lsp--document-symbols->document-symbols-hierarchy (document-symbols current-position) 6315 "Convert DOCUMENT-SYMBOLS to symbols hierarchy on CURRENT-POSITION." 6316 (-let (((symbol &as &DocumentSymbol? :children?) 6317 (seq-find (-lambda ((&DocumentSymbol :range)) 6318 (lsp-point-in-range? current-position range)) 6319 document-symbols))) 6320 (if children? 6321 (cons symbol (lsp--document-symbols->document-symbols-hierarchy children? current-position)) 6322 (when symbol 6323 (list symbol))))) 6324 6325 (lsp-defun lsp--symbol-information->document-symbol ((&SymbolInformation :name :kind :location :container-name? :deprecated?)) 6326 "Convert a SymbolInformation to a DocumentInformation" 6327 (lsp-make-document-symbol :name name 6328 :kind kind 6329 :range (lsp:location-range location) 6330 :children? nil 6331 :deprecated? deprecated? 6332 :selection-range (lsp:location-range location) 6333 :detail? container-name?)) 6334 6335 (defun lsp--symbols-informations->document-symbols-hierarchy (symbols-informations current-position) 6336 "Convert SYMBOLS-INFORMATIONS to symbols hierarchy on CURRENT-POSITION." 6337 (--> symbols-informations 6338 (-keep (-lambda ((symbol &as &SymbolInformation :location (&Location :range))) 6339 (when (lsp-point-in-range? current-position range) 6340 (lsp--symbol-information->document-symbol symbol))) 6341 it) 6342 (sort it (-lambda ((&DocumentSymbol :range (&Range :start a-start-position :end a-end-position)) 6343 (&DocumentSymbol :range (&Range :start b-start-position :end b-end-position))) 6344 (and (lsp--position-compare b-start-position a-start-position) 6345 (lsp--position-compare a-end-position b-end-position)))))) 6346 6347 (defun lsp--symbols->document-symbols-hierarchy (symbols) 6348 "Convert SYMBOLS to symbols-hierarchy." 6349 (when-let ((first-symbol (lsp-seq-first symbols))) 6350 (let ((cur-position (lsp-make-position :line (plist-get (lsp--cur-position) :line) 6351 :character (plist-get (lsp--cur-position) :character)))) 6352 (if (lsp-symbol-information? first-symbol) 6353 (lsp--symbols-informations->document-symbols-hierarchy symbols cur-position) 6354 (lsp--document-symbols->document-symbols-hierarchy symbols cur-position))))) 6355 6356 (defun lsp--xref-backend () 'xref-lsp) 6357 6358 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp))) 6359 (propertize (or (thing-at-point 'symbol) "") 6360 'identifier-at-point t)) 6361 6362 (defun lsp--xref-elements-index (symbols path) 6363 (-mapcat 6364 (-lambda (sym) 6365 (pcase-exhaustive sym 6366 ((DocumentSymbol :name :children? :selection-range (Range :start)) 6367 (cons (cons (concat path name) 6368 (lsp--position-to-point start)) 6369 (lsp--xref-elements-index children? (concat path name " / ")))) 6370 ((SymbolInformation :name :location (Location :range (Range :start))) 6371 (list (cons (concat path name) 6372 (lsp--position-to-point start)))))) 6373 symbols)) 6374 6375 (defvar-local lsp--symbols-cache nil) 6376 6377 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp))) 6378 (if (lsp--find-workspaces-for "textDocument/documentSymbol") 6379 (progn 6380 (setq lsp--symbols-cache (lsp--xref-elements-index 6381 (lsp--get-document-symbols) nil)) 6382 lsp--symbols-cache) 6383 (list (propertize (or (thing-at-point 'symbol) "") 6384 'identifier-at-point t)))) 6385 6386 (cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier) 6387 (save-excursion 6388 (unless (get-text-property 0 'identifier-at-point identifier) 6389 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6390 (user-error "Unable to find symbol %s in current document" identifier))))) 6391 (lsp--locations-to-xref-items (lsp-request "textDocument/definition" 6392 (lsp--text-document-position-params))))) 6393 6394 (cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier) 6395 (save-excursion 6396 (unless (get-text-property 0 'identifier-at-point identifier) 6397 (goto-char (cl-rest (or (assoc identifier lsp--symbols-cache) 6398 (user-error "Unable to find symbol %s" identifier))))) 6399 (lsp--locations-to-xref-items (lsp-request "textDocument/references" 6400 (lsp--make-reference-params nil lsp-references-exclude-definition))))) 6401 6402 (cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern) 6403 (seq-map #'lsp--symbol-information-to-xref 6404 (lsp-request "workspace/symbol" `(:query ,pattern)))) 6405 6406 (defcustom lsp-rename-use-prepare t 6407 "Whether `lsp-rename' should do a prepareRename first. 6408 For some language servers, textDocument/prepareRename might be 6409 too slow, in which case this variable may be set to nil. 6410 `lsp-rename' will then use `thing-at-point' `symbol' to determine 6411 the symbol to rename at point." 6412 :group 'lsp-mode 6413 :type 'boolean) 6414 6415 (defun lsp--get-symbol-to-rename () 6416 "Get a symbol to rename and placeholder at point. 6417 Returns a cons ((START . END) . PLACEHOLDER?), and nil if 6418 renaming is generally supported but cannot be done at point. 6419 START and END are the bounds of the identifiers being renamed, 6420 while PLACEHOLDER?, is either nil or a string suggested by the 6421 language server as the initial input of a new-name prompt." 6422 (unless (lsp-feature? "textDocument/rename") 6423 (error "The connected server(s) doesn't support renaming")) 6424 (if (and lsp-rename-use-prepare (lsp-feature? "textDocument/prepareRename")) 6425 (when-let ((response 6426 (lsp-request "textDocument/prepareRename" 6427 (lsp--text-document-position-params)))) 6428 (let* ((bounds (lsp--range-to-region 6429 (if (lsp-range? response) 6430 response 6431 (lsp:prepare-rename-result-range response)))) 6432 (placeholder 6433 (and (not (lsp-range? response)) 6434 (lsp:prepare-rename-result-placeholder response)))) 6435 (cons bounds placeholder))) 6436 (when-let ((bounds (bounds-of-thing-at-point 'symbol))) 6437 (cons bounds nil)))) 6438 6439 (defface lsp-face-rename '((t :underline t)) 6440 "Face used to highlight the identifier being renamed. 6441 Renaming can be done using `lsp-rename'." 6442 :group 'lsp-mode) 6443 6444 (defface lsp-rename-placeholder-face '((t :inherit font-lock-variable-name-face)) 6445 "Face used to display the rename placeholder in. 6446 When calling `lsp-rename' interactively, this will be the face of 6447 the new name." 6448 :group 'lsp-mode) 6449 6450 (defvar lsp-rename-history '() 6451 "History for `lsp--read-rename'.") 6452 6453 (defun lsp--read-rename (at-point) 6454 "Read a new name for a `lsp-rename' at `point' from the user. 6455 AT-POINT shall be a structure as returned by 6456 `lsp--get-symbol-to-rename'. 6457 6458 Returns a string, which should be the new name for the identifier 6459 at point. If renaming cannot be done at point (as determined from 6460 AT-POINT), throw a `user-error'. 6461 6462 This function is for use in `lsp-rename' only, and shall not be 6463 relied upon." 6464 (unless at-point 6465 (user-error "`lsp-rename' is invalid here")) 6466 (-let* ((((start . end) . placeholder?) at-point) 6467 ;; Do the `buffer-substring' first to not include `lsp-face-rename' 6468 (rename-me (buffer-substring start end)) 6469 (placeholder (or placeholder? rename-me)) 6470 (placeholder (propertize placeholder 'face 'lsp-rename-placeholder-face)) 6471 6472 overlay) 6473 ;; We need unwind protect, as the user might cancel here, causing the 6474 ;; overlay to linger. 6475 (unwind-protect 6476 (progn 6477 (setq overlay (make-overlay start end)) 6478 (overlay-put overlay 'face 'lsp-face-rename) 6479 6480 (read-string (format "Rename %s to: " rename-me) placeholder 6481 'lsp-rename-history)) 6482 (and overlay (delete-overlay overlay))))) 6483 6484 (defun lsp-rename (newname) 6485 "Rename the symbol (and all references to it) under point to NEWNAME." 6486 (interactive (list (lsp--read-rename (lsp--get-symbol-to-rename)))) 6487 (when-let ((edits (lsp-request "textDocument/rename" 6488 `( :textDocument ,(lsp--text-document-identifier) 6489 :position ,(lsp--cur-position) 6490 :newName ,newname)))) 6491 (lsp--apply-workspace-edit edits 'rename))) 6492 6493 (defun lsp--on-rename-file (old-func old-name new-name &optional ok-if-already-exists?) 6494 "Advice around function `rename-file'. 6495 Applies OLD-FUNC with OLD-NAME, NEW-NAME and OK-IF-ALREADY-EXISTS?. 6496 6497 This advice sends workspace/willRenameFiles before renaming file 6498 to check if server wants to apply any workspaceEdits after renamed." 6499 (if (and lsp-apply-edits-after-file-operations 6500 (lsp--send-will-rename-files-p old-name)) 6501 (let ((params (lsp-make-rename-files-params 6502 :files (vector (lsp-make-file-rename 6503 :oldUri (lsp--path-to-uri old-name) 6504 :newUri (lsp--path-to-uri new-name)))))) 6505 (when-let ((edits (lsp-request "workspace/willRenameFiles" params))) 6506 (lsp--apply-workspace-edit edits 'rename-file) 6507 (funcall old-func old-name new-name ok-if-already-exists?) 6508 (when (lsp--send-did-rename-files-p) 6509 (lsp-notify "workspace/didRenameFiles" params)))) 6510 (funcall old-func old-name new-name ok-if-already-exists?))) 6511 6512 (advice-add 'rename-file :around #'lsp--on-rename-file) 6513 6514 (defcustom lsp-xref-force-references nil 6515 "If non-nil threat everything as references(e. g. jump if only one item.)" 6516 :group 'lsp-mode 6517 :type 'boolean) 6518 6519 (defun lsp-show-xrefs (xrefs display-action references?) 6520 (unless (region-active-p) (push-mark nil t)) 6521 (if (boundp 'xref-show-definitions-function) 6522 (with-no-warnings 6523 (xref-push-marker-stack) 6524 (funcall (if (and references? (not lsp-xref-force-references)) 6525 xref-show-xrefs-function 6526 xref-show-definitions-function) 6527 (-const xrefs) 6528 `((window . ,(selected-window)) 6529 (display-action . ,display-action) 6530 ,(if (and references? (not lsp-xref-force-references)) 6531 `(auto-jump . ,xref-auto-jump-to-first-xref) 6532 `(auto-jump . ,xref-auto-jump-to-first-definition))))) 6533 (xref--show-xrefs xrefs display-action))) 6534 6535 (cl-defmethod seq-empty-p ((ht hash-table)) 6536 "Function `seq-empty-p' for hash-table." 6537 (hash-table-empty-p ht)) 6538 6539 (cl-defun lsp-find-locations (method &optional extra &key display-action references?) 6540 "Send request named METHOD and get cross references of the symbol under point. 6541 EXTRA is a plist of extra parameters. 6542 REFERENCES? t when METHOD returns references." 6543 (let ((loc (lsp-request method 6544 (append (lsp--text-document-position-params) extra)))) 6545 (if (seq-empty-p loc) 6546 (lsp--error "Not found for: %s" (or (thing-at-point 'symbol t) "")) 6547 (lsp-show-xrefs (lsp--locations-to-xref-items loc) display-action references?)))) 6548 6549 (cl-defun lsp-find-declaration (&key display-action) 6550 "Find declarations of the symbol under point." 6551 (interactive) 6552 (lsp-find-locations "textDocument/declaration" nil :display-action display-action)) 6553 6554 (cl-defun lsp-find-definition (&key display-action) 6555 "Find definitions of the symbol under point." 6556 (interactive) 6557 (lsp-find-locations "textDocument/definition" nil :display-action display-action)) 6558 6559 (defun lsp-find-definition-mouse (click) 6560 "Click to start `lsp-find-definition' at clicked point." 6561 (interactive "e") 6562 (let* ((ec (event-start click)) 6563 (p1 (posn-point ec)) 6564 (w1 (posn-window ec))) 6565 (select-window w1) 6566 (goto-char p1) 6567 (lsp-find-definition))) 6568 6569 (cl-defun lsp-find-implementation (&key display-action) 6570 "Find implementations of the symbol under point." 6571 (interactive) 6572 (lsp-find-locations "textDocument/implementation" 6573 nil 6574 :display-action display-action 6575 :references? t)) 6576 6577 (cl-defun lsp-find-references (&optional exclude-declaration &key display-action) 6578 "Find references of the symbol under point." 6579 (interactive "P") 6580 (lsp-find-locations "textDocument/references" 6581 (list :context `(:includeDeclaration ,(lsp-json-bool (not (or exclude-declaration lsp-references-exclude-definition))))) 6582 :display-action display-action 6583 :references? t)) 6584 6585 (cl-defun lsp-find-type-definition (&key display-action) 6586 "Find type definitions of the symbol under point." 6587 (interactive) 6588 (lsp-find-locations "textDocument/typeDefinition" nil :display-action display-action)) 6589 6590 (defalias 'lsp-find-custom #'lsp-find-locations) 6591 (defalias 'lsp-goto-implementation #'lsp-find-implementation) 6592 (defalias 'lsp-goto-type-definition #'lsp-find-type-definition) 6593 6594 (with-eval-after-load 'evil 6595 (evil-set-command-property 'lsp-find-definition :jump t) 6596 (evil-set-command-property 'lsp-find-implementation :jump t) 6597 (evil-set-command-property 'lsp-find-references :jump t) 6598 (evil-set-command-property 'lsp-find-type-definition :jump t)) 6599 6600 (defun lsp--workspace-method-supported? (check-command method capability workspace) 6601 (with-lsp-workspace workspace 6602 (if check-command 6603 (funcall check-command workspace) 6604 (or 6605 (when capability (lsp--capability capability)) 6606 (lsp--registered-capability method) 6607 (and (not capability) (not check-command)))))) 6608 6609 (defun lsp-disable-method-for-server (method server-id) 6610 "Disable METHOD for SERVER-ID." 6611 (cl-callf 6612 (lambda (reqs) 6613 (-let (((&plist :check-command :capability) reqs)) 6614 (list :check-command 6615 (lambda (workspace) 6616 (unless (-> workspace 6617 lsp--workspace-client 6618 lsp--client-server-id 6619 (eq server-id)) 6620 (lsp--workspace-method-supported? check-command 6621 method 6622 capability 6623 workspace)))))) 6624 (alist-get method lsp-method-requirements nil nil 'string=))) 6625 6626 (defun lsp--find-workspaces-for (msg-or-method) 6627 "Find all workspaces in the current project that can handle MSG." 6628 (let ((method (if (stringp msg-or-method) 6629 msg-or-method 6630 (plist-get msg-or-method :method)))) 6631 (-if-let (reqs (cdr (assoc method lsp-method-requirements))) 6632 (-let (((&plist :capability :check-command) reqs)) 6633 (-filter 6634 (-partial #'lsp--workspace-method-supported? 6635 check-command method capability) 6636 (lsp-workspaces))) 6637 (lsp-workspaces)))) 6638 6639 (defun lsp-can-execute-command? (command-name) 6640 "Returns non-nil if current language server(s) can execute COMMAND-NAME. 6641 The command is executed via `workspace/executeCommand'" 6642 (cl-position 6643 command-name 6644 (lsp:execute-command-options-commands 6645 (lsp:server-capabilities-execute-command-provider? 6646 (lsp--server-capabilities))) 6647 :test #'equal)) 6648 6649 (defalias 'lsp-feature? 'lsp--find-workspaces-for) 6650 6651 (cl-defmethod lsp-execute-command (_server _command _arguments) 6652 "Dispatch COMMAND execution." 6653 (signal 'cl-no-applicable-method nil)) 6654 6655 (defun lsp-workspace-command-execute (command &optional args) 6656 "Execute workspace COMMAND with ARGS." 6657 (condition-case-unless-debug err 6658 (let ((params (if args 6659 (list :command command :arguments args) 6660 (list :command command)))) 6661 (lsp-request "workspace/executeCommand" params)) 6662 (error 6663 (error "`workspace/executeCommand' with `%s' failed.\n\n%S" 6664 command err)))) 6665 6666 (defun lsp-send-execute-command (command &optional args) 6667 "Create and send a `workspace/executeCommand' message having command COMMAND 6668 and optional ARGS." 6669 (lsp-workspace-command-execute command args)) 6670 6671 (defalias 'lsp-point-to-position #'lsp--point-to-position) 6672 (defalias 'lsp-text-document-identifier #'lsp--text-document-identifier) 6673 (defalias 'lsp--send-execute-command #'lsp-send-execute-command) 6674 (defalias 'lsp-on-open #'lsp--text-document-did-open) 6675 (defalias 'lsp-on-save #'lsp--text-document-did-save) 6676 6677 (defun lsp--set-configuration (settings) 6678 "Set the SETTINGS for the lsp server." 6679 (lsp-notify "workspace/didChangeConfiguration" `(:settings ,settings))) 6680 6681 (defun lsp-current-buffer () 6682 (or lsp--virtual-buffer 6683 (current-buffer))) 6684 6685 (defun lsp-buffer-live-p (buffer-id) 6686 (if-let ((buffer-live (plist-get buffer-id :buffer-live?))) 6687 (funcall buffer-live buffer-id) 6688 (buffer-live-p buffer-id))) 6689 6690 (defun lsp--on-set-visited-file-name (old-func &rest args) 6691 "Advice around function `set-visited-file-name'. 6692 6693 This advice sends textDocument/didClose for the old file and 6694 textDocument/didOpen for the new file." 6695 (when lsp--cur-workspace 6696 (lsp--text-document-did-close t)) 6697 (prog1 (apply old-func args) 6698 (when lsp--cur-workspace 6699 (lsp--text-document-did-open)))) 6700 6701 (advice-add 'set-visited-file-name :around #'lsp--on-set-visited-file-name) 6702 6703 (defvar lsp--flushing-delayed-changes nil) 6704 6705 (defun lsp--send-no-wait (message proc) 6706 "Send MESSAGE to PROC without waiting for further output." 6707 6708 (unless lsp--flushing-delayed-changes 6709 (let ((lsp--flushing-delayed-changes t)) 6710 (lsp--flush-delayed-changes))) 6711 (lsp-process-send proc message)) 6712 6713 (define-error 'lsp-parse-error 6714 "Error parsing message from language server" 'lsp-error) 6715 (define-error 'lsp-unknown-message-type 6716 "Unknown message type" '(lsp-error lsp-parse-error)) 6717 (define-error 'lsp-unknown-json-rpc-version 6718 "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error)) 6719 (define-error 'lsp-no-content-length 6720 "Content-Length header missing in message" '(lsp-error lsp-parse-error)) 6721 (define-error 'lsp-invalid-header-name 6722 "Invalid header name" '(lsp-error lsp-parse-error)) 6723 6724 ;; id method 6725 ;; x x request 6726 ;; x . response 6727 ;; . x notification 6728 (defun lsp--get-message-type (json-data) 6729 "Get the message type from JSON-DATA." 6730 (if (lsp:json-message-id? json-data) 6731 (if (lsp:json-message-error? json-data) 6732 'response-error 6733 (if (lsp:json-message-method? json-data) 6734 'request 6735 'response)) 6736 'notification)) 6737 6738 (defconst lsp--default-notification-handlers 6739 (ht ("window/showMessage" #'lsp--window-show-message) 6740 ("window/logMessage" #'lsp--window-log-message) 6741 ("window/showInputBox" #'lsp--window-show-input-box) 6742 ("window/showQuickPick" #'lsp--window-show-quick-pick) 6743 ("textDocument/publishDiagnostics" #'lsp--on-diagnostics) 6744 ("textDocument/diagnosticsEnd" #'ignore) 6745 ("textDocument/diagnosticsBegin" #'ignore) 6746 ("telemetry/event" #'ignore) 6747 ("$/progress" (lambda (workspace params) 6748 (funcall lsp-progress-function workspace params))))) 6749 6750 (lsp-defun lsp--on-notification (workspace (&JSONNotification :params :method)) 6751 "Call the appropriate handler for NOTIFICATION." 6752 (-let ((client (lsp--workspace-client workspace))) 6753 (when (lsp--log-io-p method) 6754 (lsp--log-entry-new (lsp--make-log-entry method nil params 'incoming-notif) 6755 lsp--cur-workspace)) 6756 (if-let ((handler (or (gethash method (lsp--client-notification-handlers client)) 6757 (gethash method lsp--default-notification-handlers)))) 6758 (funcall handler workspace params) 6759 (when (and method (not (string-prefix-p "$" method))) 6760 (lsp-warn "Unknown notification: %s" method))))) 6761 6762 (lsp-defun lsp--build-workspace-configuration-response ((&ConfigurationParams :items)) 6763 "Get section configuration. 6764 PARAMS are the `workspace/configuration' request params" 6765 (->> items 6766 (-map (-lambda ((&ConfigurationItem :section?)) 6767 (-let* ((path-parts (split-string section? "\\.")) 6768 (path-without-last (s-join "." (-slice path-parts 0 -1))) 6769 (path-parts-len (length path-parts))) 6770 (cond 6771 ((<= path-parts-len 1) 6772 (ht-get (lsp-configuration-section section?) 6773 (car-safe path-parts) 6774 (ht-create))) 6775 ((> path-parts-len 1) 6776 (when-let ((section (lsp-configuration-section path-without-last)) 6777 (keys path-parts)) 6778 (while (and keys section) 6779 (setf section (ht-get section (pop keys)))) 6780 section)))))) 6781 (apply #'vector))) 6782 6783 (defun lsp--ms-since (timestamp) 6784 "Integer number of milliseconds since TIMESTAMP. Fractions discarded." 6785 (floor (* 1000 (float-time (time-since timestamp))))) 6786 6787 (defun lsp--send-request-response (workspace recv-time request response) 6788 "Send the RESPONSE for REQUEST in WORKSPACE and log if needed." 6789 (-let* (((&JSONResponse :params :method :id) request) 6790 (process (lsp--workspace-proc workspace)) 6791 (response (lsp--make-response id response)) 6792 (req-entry (and lsp-log-io 6793 (lsp--make-log-entry method id params 'incoming-req))) 6794 (resp-entry (and lsp-log-io 6795 (lsp--make-log-entry method id response 'outgoing-resp 6796 (lsp--ms-since recv-time))))) 6797 ;; Send response to the server. 6798 (when (lsp--log-io-p method) 6799 (lsp--log-entry-new req-entry workspace) 6800 (lsp--log-entry-new resp-entry workspace)) 6801 (lsp--send-no-wait response process))) 6802 6803 (lsp-defun lsp--on-request (workspace (request &as &JSONRequest :params :method)) 6804 "Call the appropriate handler for REQUEST, and send the return value to the 6805 server. WORKSPACE is the active workspace." 6806 (-let* ((recv-time (current-time)) 6807 (client (lsp--workspace-client workspace)) 6808 (buffers (lsp--workspace-buffers workspace)) 6809 handler 6810 (response (cond 6811 ((setq handler (gethash method (lsp--client-request-handlers client) nil)) 6812 (funcall handler workspace params)) 6813 ((setq handler (gethash method (lsp--client-async-request-handlers client) nil)) 6814 (funcall handler workspace params 6815 (-partial #'lsp--send-request-response 6816 workspace recv-time request)) 6817 'delay-response) 6818 ((equal method "client/registerCapability") 6819 (mapc #'lsp--server-register-capability 6820 (lsp:registration-params-registrations params)) 6821 (mapc (lambda (buf) 6822 (when (lsp-buffer-live-p buf) 6823 (lsp-with-current-buffer buf 6824 (lsp-unconfig-buffer) 6825 (lsp-configure-buffer)))) 6826 buffers) 6827 nil) 6828 ((equal method "window/showMessageRequest") 6829 (let ((choice (lsp--window-log-message-request params))) 6830 `(:title ,choice))) 6831 ((equal method "window/showDocument") 6832 (let ((success? (lsp--window-show-document params))) 6833 (lsp-make-show-document-result :success (or success? 6834 :json-false)))) 6835 ((equal method "client/unregisterCapability") 6836 (mapc #'lsp--server-unregister-capability 6837 (lsp:unregistration-params-unregisterations params)) 6838 (mapc (lambda (buf) 6839 (when (lsp-buffer-live-p buf) 6840 (lsp-with-current-buffer buf 6841 (lsp-unconfig-buffer) 6842 (lsp-configure-buffer)))) 6843 buffers) 6844 nil) 6845 ((equal method "workspace/applyEdit") 6846 (list :applied (condition-case err 6847 (prog1 t 6848 (lsp--apply-workspace-edit (lsp:apply-workspace-edit-params-edit params) 'server-requested)) 6849 (error 6850 (lsp--error "Failed to apply edits with message %s" 6851 (error-message-string err)) 6852 :json-false)))) 6853 ((equal method "workspace/configuration") 6854 (with-lsp-workspace workspace 6855 (if-let ((buf (car buffers))) 6856 (lsp-with-current-buffer buf 6857 (lsp--build-workspace-configuration-response params)) 6858 (lsp--with-workspace-temp-buffer (lsp--workspace-root workspace) 6859 (lsp--build-workspace-configuration-response params))))) 6860 ((equal method "workspace/workspaceFolders") 6861 (let ((folders (or (-> workspace 6862 (lsp--workspace-client) 6863 (lsp--client-server-id) 6864 (gethash (lsp-session-server-id->folders (lsp-session)))) 6865 (lsp-session-folders (lsp-session))))) 6866 (->> folders 6867 (-distinct) 6868 (-map (lambda (folder) 6869 (list :uri (lsp--path-to-uri folder)))) 6870 (apply #'vector)))) 6871 ((equal method "window/workDoneProgress/create") 6872 nil ;; no specific reply, no processing required 6873 ) 6874 ((equal method "workspace/semanticTokens/refresh") 6875 (when (and lsp-semantic-tokens-enable 6876 (fboundp 'lsp--semantic-tokens-on-refresh)) 6877 (lsp--semantic-tokens-on-refresh workspace)) 6878 nil) 6879 ((equal method "workspace/codeLens/refresh") 6880 (when (and lsp-lens-enable 6881 (fboundp 'lsp--lens-on-refresh)) 6882 (lsp--lens-on-refresh workspace)) 6883 nil) 6884 (t (lsp-warn "Unknown request method: %s" method) nil)))) 6885 ;; Send response to the server. 6886 (unless (eq response 'delay-response) 6887 (lsp--send-request-response workspace recv-time request response)))) 6888 6889 (lsp-defun lsp--error-string ((&JSONError :message :code)) 6890 "Format ERR as a user friendly string." 6891 (format "Error from the Language Server: %s (%s)" 6892 message 6893 (or (car (alist-get code lsp--errors)) "Unknown error"))) 6894 6895 (defun lsp--get-body-length (headers) 6896 (let ((content-length (cdr (assoc "Content-Length" headers)))) 6897 (if content-length 6898 (string-to-number content-length) 6899 6900 ;; This usually means either the server or our parser is 6901 ;; screwed up with a previous Content-Length 6902 (error "No Content-Length header")))) 6903 6904 (defun lsp--parse-header (s) 6905 "Parse string S as a LSP (KEY . VAL) header." 6906 (let ((pos (string-match "\:" s)) 6907 key val) 6908 (unless pos 6909 (signal 'lsp-invalid-header-name (list s))) 6910 (setq key (substring s 0 pos) 6911 val (s-trim-left (substring s (+ 1 pos)))) 6912 (when (equal key "Content-Length") 6913 (cl-assert (cl-loop for c across val 6914 when (or (> c ?9) (< c ?0)) return nil 6915 finally return t) 6916 nil (format "Invalid Content-Length value: %s" val))) 6917 (cons key val))) 6918 6919 (defmacro lsp--read-json (str) 6920 "Read json string STR." 6921 (if (progn 6922 (require 'json) 6923 (fboundp 'json-parse-string)) 6924 `(json-parse-string ,str 6925 :object-type (if lsp-use-plists 6926 'plist 6927 'hash-table) 6928 :null-object nil 6929 :false-object nil) 6930 `(let ((json-array-type 'vector) 6931 (json-object-type (if lsp-use-plists 6932 'plist 6933 'hash-table)) 6934 (json-false nil)) 6935 (json-read-from-string ,str)))) 6936 6937 (defmacro lsp-json-read-buffer () 6938 "Read json from the current buffer." 6939 (if (progn 6940 (require 'json) 6941 (fboundp 'json-parse-buffer)) 6942 `(json-parse-buffer :object-type (if lsp-use-plists 6943 'plist 6944 'hash-table) 6945 :null-object nil 6946 :false-object nil) 6947 `(let ((json-array-type 'vector) 6948 (json-object-type (if lsp-use-plists 6949 'plist 6950 'hash-table)) 6951 (json-false nil)) 6952 (json-read)))) 6953 6954 (defun lsp--read-json-file (file-path) 6955 "Read json file." 6956 (-> file-path 6957 (f-read-text) 6958 (lsp--read-json))) 6959 6960 (defun lsp--parser-on-message (json-data workspace) 6961 "Called when the parser P read a complete MSG from the server." 6962 (with-demoted-errors "Error processing message %S." 6963 (with-lsp-workspace workspace 6964 (let* ((client (lsp--workspace-client workspace)) 6965 (id (--when-let (lsp:json-response-id json-data) 6966 (if (stringp it) (string-to-number it) it))) 6967 (data (lsp:json-response-result json-data))) 6968 (pcase (lsp--get-message-type json-data) 6969 ('response 6970 (cl-assert id) 6971 (-let [(callback _ method _ before-send) (gethash id (lsp--client-response-handlers client))] 6972 (when (lsp--log-io-p method) 6973 (lsp--log-entry-new 6974 (lsp--make-log-entry method id data 'incoming-resp 6975 (lsp--ms-since before-send)) 6976 workspace)) 6977 (when callback 6978 (remhash id (lsp--client-response-handlers client)) 6979 (funcall callback (lsp:json-response-result json-data))))) 6980 ('response-error 6981 (cl-assert id) 6982 (-let [(_ callback method _ before-send) (gethash id (lsp--client-response-handlers client))] 6983 (when (lsp--log-io-p method) 6984 (lsp--log-entry-new 6985 (lsp--make-log-entry method id (lsp:json-response-error-error json-data) 6986 'incoming-resp (lsp--ms-since before-send)) 6987 workspace)) 6988 (when callback 6989 (remhash id (lsp--client-response-handlers client)) 6990 (funcall callback (lsp:json-response-error-error json-data))))) 6991 ('notification 6992 (lsp--on-notification workspace json-data)) 6993 ('request (lsp--on-request workspace json-data))))))) 6994 6995 (defun lsp--create-filter-function (workspace) 6996 "Make filter for the workspace." 6997 (let ((body-received 0) 6998 leftovers body-length body chunk) 6999 (lambda (_proc input) 7000 (setf chunk (if (s-blank? leftovers) 7001 input 7002 (concat leftovers input))) 7003 7004 (let (messages) 7005 (while (not (s-blank? chunk)) 7006 (if (not body-length) 7007 ;; Read headers 7008 (if-let ((body-sep-pos (string-match-p "\r\n\r\n" chunk))) 7009 ;; We've got all the headers, handle them all at once: 7010 (setf body-length (lsp--get-body-length 7011 (mapcar #'lsp--parse-header 7012 (split-string 7013 (substring-no-properties chunk 7014 (or (string-match-p "Content-Length" chunk) 7015 (error "Unable to find Content-Length header.")) 7016 body-sep-pos) 7017 "\r\n"))) 7018 body-received 0 7019 leftovers nil 7020 chunk (substring-no-properties chunk (+ body-sep-pos 4))) 7021 7022 ;; Haven't found the end of the headers yet. Save everything 7023 ;; for when the next chunk arrives and await further input. 7024 (setf leftovers chunk 7025 chunk nil)) 7026 (let* ((chunk-length (string-bytes chunk)) 7027 (left-to-receive (- body-length body-received)) 7028 (this-body (if (< left-to-receive chunk-length) 7029 (prog1 (substring-no-properties chunk 0 left-to-receive) 7030 (setf chunk (substring-no-properties chunk left-to-receive))) 7031 (prog1 chunk 7032 (setf chunk nil)))) 7033 (body-bytes (string-bytes this-body))) 7034 (push this-body body) 7035 (setf body-received (+ body-received body-bytes)) 7036 (when (>= chunk-length left-to-receive) 7037 (condition-case err 7038 (with-temp-buffer 7039 (apply #'insert 7040 (nreverse 7041 (prog1 body 7042 (setf leftovers nil 7043 body-length nil 7044 body-received nil 7045 body nil)))) 7046 (decode-coding-region (point-min) 7047 (point-max) 7048 'utf-8) 7049 (goto-char (point-min)) 7050 (push (lsp-json-read-buffer) messages)) 7051 7052 (error 7053 (lsp-warn "Failed to parse the following chunk:\n'''\n%s\n'''\nwith message %s" 7054 (concat leftovers input) 7055 err))))))) 7056 (mapc (lambda (msg) 7057 (lsp--parser-on-message msg workspace)) 7058 (nreverse messages)))))) 7059 7060 (defvar-local lsp--line-col-to-point-hash-table nil 7061 "Hash table with keys (line . col) and values that are either point positions 7062 or markers.") 7063 7064 (defcustom lsp-imenu-detailed-outline t 7065 "Whether `lsp-imenu' should include signatures. 7066 This will be ignored if the server doesn't provide the necessary 7067 information, for example if it doesn't support DocumentSymbols." 7068 :group 'lsp-imenu 7069 :type 'boolean) 7070 7071 (defcustom lsp-imenu-hide-parent-details t 7072 "Whether `lsp-imenu' should hide signatures of parent nodes." 7073 :group 'lsp-imenu 7074 :type 'boolean) 7075 7076 (defface lsp-details-face '((t :height 0.8 :inherit shadow)) 7077 "Used to display additional information throughout `lsp'. 7078 Things like line numbers, signatures, ... are considered 7079 additional information. Often, additional faces are defined that 7080 inherit from this face by default, like `lsp-signature-face', and 7081 they may be customized for finer control." 7082 :group 'lsp-mode) 7083 7084 (defface lsp-signature-face '((t :inherit lsp-details-face)) 7085 "Used to display signatures in `imenu', ...." 7086 :group 'lsp-mode) 7087 7088 (lsp-defun lsp-render-symbol ((&DocumentSymbol :name :detail? :deprecated?) 7089 show-detail?) 7090 "Render INPUT0, an `&DocumentSymbol', to a string. 7091 If SHOW-DETAIL? is set, make use of its `:detail?' field (often 7092 the signature)." 7093 (let ((detail (and show-detail? (s-present? detail?) 7094 (propertize (concat " " (s-trim-left detail?)) 7095 'face 'lsp-signature-face))) 7096 (name (if deprecated? 7097 (propertize name 'face 'lsp-face-semhl-deprecated) name))) 7098 (concat name detail))) 7099 7100 (lsp-defun lsp-render-symbol-information ((&SymbolInformation :name :deprecated? :container-name?) 7101 separator) 7102 "Render a piece of SymbolInformation. 7103 Handle :deprecated?. If SEPARATOR is non-nil, the 7104 symbol's (optional) parent, SEPARATOR and the symbol itself are 7105 concatenated." 7106 (when (and separator container-name? (not (string-empty-p container-name?))) 7107 (setq name (concat name separator container-name?))) 7108 (if deprecated? (propertize name 'face 'lsp-face-semhl-deprecated) name)) 7109 7110 (defun lsp--symbol-to-imenu-elem (sym) 7111 "Convert SYM to imenu element. 7112 7113 SYM is a SymbolInformation message. 7114 7115 Return a cons cell (full-name . start-point)." 7116 (let ((start-point (ht-get lsp--line-col-to-point-hash-table 7117 (lsp--get-line-and-col sym)))) 7118 (cons (lsp-render-symbol-information 7119 sym (and lsp-imenu-show-container-name 7120 lsp-imenu-container-name-separator)) 7121 start-point))) 7122 7123 (lsp-defun lsp--symbol-to-hierarchical-imenu-elem ((sym &as &DocumentSymbol :children?)) 7124 "Convert SYM to hierarchical imenu elements. 7125 7126 SYM is a DocumentSymbol message. 7127 7128 Return cons cell (\"symbol-name (symbol-kind)\" . start-point) if 7129 SYM doesn't have any children. Otherwise return a cons cell with 7130 an alist 7131 7132 (\"symbol-name\" . ((\"(symbol-kind)\" . start-point) 7133 cons-cells-from-children))" 7134 (let ((filtered-children (lsp--imenu-filter-symbols children?)) 7135 (signature (lsp-render-symbol sym lsp-imenu-detailed-outline))) 7136 (if (seq-empty-p filtered-children) 7137 (cons signature 7138 (ht-get lsp--line-col-to-point-hash-table 7139 (lsp--get-line-and-col sym))) 7140 (cons signature 7141 (lsp--imenu-create-hierarchical-index filtered-children))))) 7142 7143 (lsp-defun lsp--symbol-ignore ((&SymbolInformation :kind)) 7144 "Determine if SYM is for the current document and is to be shown." 7145 ;; It's a SymbolInformation or DocumentSymbol, which is always in the 7146 ;; current buffer file. 7147 (and lsp-imenu-index-symbol-kinds 7148 (numberp kind) 7149 (let ((clamped-kind (if (< 0 kind (length lsp/symbol-kind-lookup)) 7150 kind 7151 0))) 7152 (not (memql (aref lsp/symbol-kind-lookup clamped-kind) 7153 lsp-imenu-index-symbol-kinds))))) 7154 7155 (lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind)) 7156 "The string name of the kind of SYM." 7157 (alist-get kind lsp-symbol-kinds "Other")) 7158 7159 (defun lsp--get-line-and-col (sym) 7160 "Obtain the line and column corresponding to SYM." 7161 (-let* ((location (lsp:symbol-information-location sym)) 7162 (name-range (or (and location (lsp:location-range location)) 7163 (lsp:document-symbol-selection-range sym))) 7164 ((&Range :start (&Position :line :character)) name-range)) 7165 (cons line character))) 7166 7167 (defun lsp--collect-lines-and-cols (symbols) 7168 "Return a sorted list ((line . col) ...) of the locations of SYMBOLS." 7169 (let ((stack (mapcar 'identity symbols)) 7170 line-col-list) 7171 (while stack 7172 (let ((sym (pop stack))) 7173 (push (lsp--get-line-and-col sym) line-col-list) 7174 (unless (seq-empty-p (lsp:document-symbol-children? sym)) 7175 (setf stack (nconc (lsp--imenu-filter-symbols (lsp:document-symbol-children? sym)) stack))))) 7176 (-sort #'lsp--line-col-comparator line-col-list))) 7177 7178 (defun lsp--convert-line-col-to-points-batch (line-col-list) 7179 "Convert a sorted list of positions from line-column 7180 representation to point representation." 7181 (let ((line-col-to-point-map (ht-create)) 7182 (inhibit-field-text-motion t) 7183 (curr-line 0)) 7184 (lsp-save-restriction-and-excursion 7185 (goto-char (point-min)) 7186 (cl-loop for (line . col) in line-col-list do 7187 (forward-line (- line curr-line)) 7188 (setq curr-line line) 7189 (let ((line-end (line-end-position))) 7190 (if (or (not col) (> col (- line-end (point)))) 7191 (goto-char line-end) 7192 (forward-char col))) 7193 (ht-set! line-col-to-point-map (cons line col) (if imenu-use-markers 7194 (point-marker) 7195 (point))))) 7196 line-col-to-point-map)) 7197 7198 (cl-defun lsp--line-col-comparator ((l1 . c1) (l2 . c2)) 7199 (or (< l1 l2) 7200 (and (= l1 l2) 7201 (cond ((and c1 c2) 7202 (< c1 c2)) 7203 (c1 t))))) 7204 7205 (defun lsp-imenu-create-uncategorized-index (symbols) 7206 "Create imenu index from document SYMBOLS. 7207 This function, unlike `lsp-imenu-create-categorized-index', does 7208 not categorize by type, but instead returns an `imenu' index 7209 corresponding to the symbol hierarchy returned by the server 7210 directly." 7211 (let* ((lsp--line-col-to-point-hash-table (-> symbols 7212 lsp--collect-lines-and-cols 7213 lsp--convert-line-col-to-points-batch))) 7214 (if (lsp--imenu-hierarchical-p symbols) 7215 (lsp--imenu-create-hierarchical-index symbols) 7216 (lsp--imenu-create-non-hierarchical-index symbols)))) 7217 7218 (defcustom lsp-imenu-symbol-kinds 7219 '((1 . "Files") 7220 (2 . "Modules") 7221 (3 . "Namespaces") 7222 (4 . "Packages") 7223 (5 . "Classes") 7224 (6 . "Methods") 7225 (7 . "Properties") 7226 (8 . "Fields") 7227 (9 . "Constructors") 7228 (10 . "Enums") 7229 (11 . "Interfaces") 7230 (12 . "Functions") 7231 (13 . "Variables") 7232 (14 . "Constants") 7233 (15 . "Strings") 7234 (16 . "Numbers") 7235 (17 . "Booleans") 7236 (18 . "Arrays") 7237 (19 . "Objects") 7238 (20 . "Keys") 7239 (21 . "Nulls") 7240 (22 . "Enum Members") 7241 (23 . "Structs") 7242 (24 . "Events") 7243 (25 . "Operators") 7244 (26 . "Type Parameters")) 7245 "`lsp-symbol-kinds', but only used by `imenu'. 7246 A new variable is needed, as it is `imenu' convention to use 7247 pluralized categories, which `lsp-symbol-kinds' doesn't. If the 7248 non-pluralized names are preferred, this can be set to 7249 `lsp-symbol-kinds'." 7250 :type '(alist :key-type integer :value-type string)) 7251 7252 (defun lsp--imenu-kind->name (kind) 7253 (alist-get kind lsp-imenu-symbol-kinds "?")) 7254 7255 (defun lsp-imenu-create-top-level-categorized-index (symbols) 7256 "Create an `imenu' index categorizing SYMBOLS by type. 7257 Only root symbols are categorized. 7258 7259 See `lsp-symbol-kinds' to customize the category naming. SYMBOLS 7260 shall be a list of DocumentSymbols or SymbolInformation." 7261 (mapcan 7262 (-lambda ((type . symbols)) 7263 (let ((cat (lsp--imenu-kind->name type)) 7264 (symbols (lsp-imenu-create-uncategorized-index symbols))) 7265 ;; If there is no :kind (this is being defensive), or we couldn't look it 7266 ;; up, just display the symbols inline, without categories. 7267 (if cat (list (cons cat symbols)) symbols))) 7268 (sort (seq-group-by #'lsp:document-symbol-kind symbols) 7269 (-lambda ((kinda) (kindb)) (< kinda kindb))))) 7270 7271 (lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start))) 7272 "Convert an `&DocumentSymbol' to an `imenu' entry." 7273 (cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start)) 7274 7275 (defun lsp--imenu-create-categorized-index-1 (symbols) 7276 "Returns an `imenu' index from SYMBOLS categorized by type. 7277 The result looks like this: ((\"Variables\" . (...)))." 7278 (->> 7279 symbols 7280 (mapcan 7281 (-lambda ((sym &as &DocumentSymbol :kind :children?)) 7282 (if (seq-empty-p children?) 7283 (list (list kind (lsp--symbol->imenu sym))) 7284 (let ((parent (lsp-render-symbol sym (and lsp-imenu-detailed-outline 7285 (not lsp-imenu-hide-parent-details))))) 7286 (cons 7287 (list kind (lsp--symbol->imenu sym)) 7288 (mapcar (-lambda ((type . imenu-items)) 7289 (list type (cons parent (mapcan #'cdr imenu-items)))) 7290 (-group-by #'car (lsp--imenu-create-categorized-index-1 children?)))))))) 7291 (-group-by #'car) 7292 (mapcar 7293 (-lambda ((kind . syms)) 7294 (cons kind (mapcan #'cdr syms)))))) 7295 7296 (defun lsp--imenu-create-categorized-index (symbols) 7297 (let ((syms (lsp--imenu-create-categorized-index-1 symbols))) 7298 (dolist (sym syms) 7299 (setcar sym (lsp--imenu-kind->name (car sym)))) 7300 syms)) 7301 7302 (lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start)))) 7303 (cons (lsp-render-symbol-information sym nil) start)) 7304 7305 (defun lsp--imenu-create-categorized-index-flat (symbols) 7306 "Create a kind-categorized index for SymbolInformation." 7307 (mapcar (-lambda ((kind . syms)) 7308 (cons (lsp--imenu-kind->name kind) 7309 (mapcan (-lambda ((parent . children)) 7310 (let ((children (mapcar #'lsp--symbol-information->imenu children))) 7311 (if parent (list (cons parent children)) children))) 7312 (-group-by #'lsp:symbol-information-container-name? syms)))) 7313 (seq-group-by #'lsp:symbol-information-kind symbols))) 7314 7315 (defun lsp-imenu-create-categorized-index (symbols) 7316 (if (lsp--imenu-hierarchical-p symbols) 7317 (lsp--imenu-create-categorized-index symbols) 7318 (lsp--imenu-create-categorized-index-flat symbols))) 7319 7320 (defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index 7321 "Function that should create an `imenu' index. 7322 It will be called with a list of SymbolInformation or 7323 DocumentSymbols, whose first level is already filtered. It shall 7324 then return an appropriate `imenu' index (see 7325 `imenu-create-index-function'). 7326 7327 Note that this interface is not stable, and subject to change any 7328 time." 7329 :group 'lsp-imenu 7330 :type '(radio 7331 (const :tag "Categorize by type" 7332 lsp-imenu-create-categorized-index) 7333 (const :tag "Categorize root symbols by type" 7334 lsp-imenu-create-top-level-categorized-index) 7335 (const :tag "Uncategorized, inline entries" 7336 lsp-imenu-create-uncategorized-index) 7337 (function :tag "Custom function"))) 7338 7339 (defun lsp--imenu-create-index () 7340 "Create an `imenu' index based on the language server. 7341 Respects `lsp-imenu-index-function'." 7342 (let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols)))) 7343 (funcall lsp-imenu-index-function symbols))) 7344 7345 (defun lsp--imenu-filter-symbols (symbols) 7346 "Filter out unsupported symbols from SYMBOLS." 7347 (seq-remove #'lsp--symbol-ignore symbols)) 7348 7349 (defun lsp--imenu-hierarchical-p (symbols) 7350 "Determine whether any element in SYMBOLS has children." 7351 (seq-some #'lsp-document-symbol? symbols)) 7352 7353 (defun lsp--imenu-create-non-hierarchical-index (symbols) 7354 "Create imenu index for non-hierarchical SYMBOLS. 7355 7356 SYMBOLS are a list of DocumentSymbol messages. 7357 7358 Return a nested alist keyed by symbol names. e.g. 7359 7360 ((\"SomeClass\" (\"(Class)\" . 10) 7361 (\"someField (Field)\" . 20) 7362 (\"someFunction (Function)\" . 25) 7363 (\"SomeSubClass\" (\"(Class)\" . 30) 7364 (\"someSubField (Field)\" . 35)) 7365 (\"someFunction (Function)\" . 40))" 7366 (seq-map (lambda (nested-alist) 7367 (cons (car nested-alist) 7368 (seq-map #'lsp--symbol-to-imenu-elem (cdr nested-alist)))) 7369 (seq-group-by #'lsp--get-symbol-type symbols))) 7370 7371 (defun lsp--imenu-create-hierarchical-index (symbols) 7372 "Create imenu index for hierarchical SYMBOLS. 7373 7374 SYMBOLS are a list of DocumentSymbol messages. 7375 7376 Return a nested alist keyed by symbol names. e.g. 7377 7378 ((\"SomeClass\" (\"(Class)\" . 10) 7379 (\"someField (Field)\" . 20) 7380 (\"someFunction (Function)\" . 25) 7381 (\"SomeSubClass\" (\"(Class)\" . 30) 7382 (\"someSubField (Field)\" . 35)) 7383 (\"someFunction (Function)\" . 40))" 7384 (seq-map #'lsp--symbol-to-hierarchical-imenu-elem 7385 (seq-sort #'lsp--imenu-symbol-lessp symbols))) 7386 7387 (defun lsp--imenu-symbol-lessp (sym1 sym2) 7388 (let* ((compare-results (mapcar (lambda (method) 7389 (funcall (alist-get method lsp--imenu-compare-function-alist) 7390 sym1 sym2)) 7391 lsp-imenu-sort-methods)) 7392 (result (seq-find (lambda (result) 7393 (not (= result 0))) 7394 compare-results 7395 0))) 7396 (and (numberp result) (< result 0)))) 7397 7398 (lsp-defun lsp--imenu-compare-kind ((&SymbolInformation :kind left) 7399 (&SymbolInformation :kind right)) 7400 "Compare SYM1 and SYM2 by kind." 7401 (- left right)) 7402 7403 (defun lsp--imenu-compare-line-col (sym1 sym2) 7404 (if (lsp--line-col-comparator 7405 (lsp--get-line-and-col sym1) 7406 (lsp--get-line-and-col sym2)) 7407 -1 7408 1)) 7409 7410 (lsp-defun lsp--imenu-compare-name ((&SymbolInformation :name name1) 7411 (&SymbolInformation :name name2)) 7412 "Compare SYM1 and SYM2 by name." 7413 (let ((result (compare-strings name1 0 (length name1) name2 0 (length name2)))) 7414 (if (numberp result) result 0))) 7415 7416 (defun lsp--imenu-refresh () 7417 "Force Imenu to refresh itself." 7418 (imenu--menubar-select imenu--rescan-item)) 7419 7420 (defun lsp-enable-imenu () 7421 "Use lsp-imenu for the current buffer." 7422 (imenu--cleanup) 7423 (add-function :override (local 'imenu-create-index-function) #'lsp--imenu-create-index) 7424 (setq-local imenu-menubar-modified-tick -1) 7425 (setq-local imenu--index-alist nil) 7426 (when menu-bar-mode 7427 (lsp--imenu-refresh))) 7428 7429 (defun lsp-resolve-final-command (command &optional test?) 7430 "Resolve final function COMMAND." 7431 (let* ((command (lsp-resolve-value command)) 7432 (command (cl-etypecase command 7433 (list 7434 (cl-assert (seq-every-p (apply-partially #'stringp) command) nil 7435 "Invalid command list") 7436 command) 7437 (string (list command))))) 7438 (if (and (file-remote-p default-directory) (not test?)) 7439 (list shell-file-name "-c" 7440 (string-join (cons "stty raw > /dev/null;" 7441 (mapcar #'shell-quote-argument command)) 7442 " ")) 7443 command))) 7444 7445 (defun lsp-server-present? (final-command) 7446 "Check whether FINAL-COMMAND is present." 7447 (let ((binary-found? (executable-find (cl-first final-command) t))) 7448 (if binary-found? 7449 (lsp-log "Command \"%s\" is present on the path." (s-join " " final-command)) 7450 (lsp-log "Command \"%s\" is not present on the path." (s-join " " final-command))) 7451 binary-found?)) 7452 7453 (defun lsp--value-to-string (value) 7454 "Convert VALUE to a string that can be set as value in an environment 7455 variable." 7456 (cond 7457 ((stringp value) value) 7458 ((booleanp value) (if value 7459 "1" 7460 "0")) 7461 ((and (sequencep value) 7462 (seq-every-p #'stringp value)) (string-join value ":")) 7463 (t (user-error "Only strings, booleans, and sequences of strings are supported as environment variables")))) 7464 7465 (defun lsp--compute-process-environment (environment-fn) 7466 "Append a list of KEY=VALUE from the alist ENVIRONMENT to `process-environment'. 7467 Ignore non-boolean keys whose value is nil." 7468 (let ((environment (if environment-fn 7469 (funcall environment-fn) 7470 nil))) 7471 (-flatten (cons (cl-loop for (key . value) in environment 7472 if (or (eval value) 7473 (eq (get value 'custom-type) 'boolean)) 7474 collect (concat key "=" (lsp--value-to-string 7475 (eval value)))) 7476 process-environment)))) 7477 7478 (defun lsp--default-directory-for-connection (&optional path) 7479 "Return path to be used for the working directory of a LSP process. 7480 7481 If `lsp-use-workspace-root-for-server-default-directory' is 7482 non-nil, uses `lsp-workspace-root' to find the directory 7483 corresponding to PATH, else returns `default-directory'." 7484 (if lsp-use-workspace-root-for-server-default-directory 7485 (lsp-workspace-root path) 7486 default-directory)) 7487 7488 (defun lsp--fix-remote-cmd (program) 7489 "Helper for `lsp-stdio-connection'. 7490 Originally coppied from eglot." 7491 7492 (if (file-remote-p default-directory) 7493 (list shell-file-name "-c" 7494 (string-join (cons "stty raw > /dev/null;" 7495 (mapcar #'shell-quote-argument program)) 7496 " ")) 7497 program)) 7498 7499 (defvar tramp-use-ssh-controlmaster-options) 7500 (defvar tramp-ssh-controlmaster-options) 7501 7502 (defun lsp-stdio-connection (command &optional test-command) 7503 "Returns a connection property list using COMMAND. 7504 COMMAND can be: A string, denoting the command to launch the 7505 language server. A list of strings, denoting an executable with 7506 its command line arguments. A function, that either returns a 7507 string or a list of strings. In all cases, the launched language 7508 server should send and receive messages on standard I/O. 7509 TEST-COMMAND is a function with no arguments which returns 7510 whether the command is present or not. When not specified 7511 `lsp-mode' will check whether the first element of the list 7512 returned by COMMAND is available via `executable-find'" 7513 (cl-check-type command (or string 7514 function 7515 (and list 7516 (satisfies (lambda (l) 7517 (seq-every-p (lambda (el) 7518 (stringp el)) 7519 l)))))) 7520 (list :connect (lambda (filter sentinel name environment-fn workspace) 7521 (if (and (functionp 'json-rpc-connection) 7522 (not (file-remote-p default-directory))) 7523 (lsp-json-rpc-connection workspace (lsp-resolve-final-command command)) 7524 (let ((final-command (lsp-resolve-final-command command)) 7525 (process-name (generate-new-buffer-name name)) 7526 (process-environment 7527 (lsp--compute-process-environment environment-fn))) 7528 (let* ((stderr-buf (get-buffer-create (format "*%s::stderr*" process-name))) 7529 (default-directory (lsp--default-directory-for-connection)) 7530 (tramp-use-ssh-controlmaster-options 'suppress) 7531 (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none") 7532 (proc (make-process 7533 :name process-name 7534 :connection-type 'pipe 7535 :buffer (format "*%s*" process-name) 7536 :coding 'no-conversion 7537 :command final-command 7538 :filter filter 7539 :sentinel sentinel 7540 :stderr stderr-buf 7541 :noquery t 7542 :file-handler t))) 7543 (set-process-query-on-exit-flag proc nil) 7544 (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) 7545 (with-current-buffer (get-buffer stderr-buf) 7546 ;; Make the *NAME::stderr* buffer buffer-read-only, q to bury, etc. 7547 (special-mode)) 7548 (cons proc proc))))) 7549 :test? (or 7550 test-command 7551 (lambda () 7552 (lsp-server-present? (lsp-resolve-final-command command t)))))) 7553 7554 (defun lsp--open-network-stream (host port name) 7555 "Open network stream to HOST:PORT. 7556 NAME will be passed to `open-network-stream'. 7557 RETRY-COUNT is the number of the retries. 7558 SLEEP-INTERVAL is the sleep interval between each retry." 7559 (let* ((retries 0) 7560 (sleep-interval 0.01) 7561 (number-of-retries (/ lsp-tcp-connection-timeout sleep-interval)) 7562 connection) 7563 (while (and (not connection) (< retries number-of-retries)) 7564 (condition-case err 7565 (setq connection (open-network-stream name nil host port 7566 :type 'plain 7567 :coding 'no-conversion)) 7568 (file-error 7569 (let ((inhibit-message t)) 7570 (lsp--warn "Failed to connect to %s:%s with error message %s" 7571 host 7572 port 7573 (error-message-string err)) 7574 (sleep-for sleep-interval) 7575 (cl-incf retries))))) 7576 (or connection (error "Port %s was never taken. Consider increasing `lsp-tcp-connection-timeout'." port)))) 7577 7578 (defun lsp--port-available (host port) 7579 "Return non-nil if HOST and PORT are available." 7580 (condition-case _err 7581 (delete-process (open-network-stream "*connection-test*" nil host port :type 'plain)) 7582 (file-error t))) 7583 7584 (defun lsp--find-available-port (host starting-port) 7585 "Find available port on HOST starting from STARTING-PORT." 7586 (let ((port starting-port)) 7587 (while (not (lsp--port-available host port)) 7588 (cl-incf port)) 7589 port)) 7590 7591 (defun lsp-tcp-connection (command-fn) 7592 "Returns a connection property list similar to `lsp-stdio-connection'. 7593 COMMAND-FN can only be a function that takes a single argument, a 7594 port number. It should return a command for launches a language server 7595 process listening for TCP connections on the provided port." 7596 (cl-check-type command-fn function) 7597 (list 7598 :connect (lambda (filter sentinel name environment-fn _workspace) 7599 (let* ((host "localhost") 7600 (port (lsp--find-available-port host (cl-incf lsp--tcp-port))) 7601 (command (funcall command-fn port)) 7602 (final-command (if (consp command) command (list command))) 7603 (_ (unless (lsp-server-present? final-command) 7604 (user-error (format "Couldn't find executable %s" (cl-first final-command))))) 7605 (process-environment 7606 (lsp--compute-process-environment environment-fn)) 7607 (proc (make-process :name name :connection-type 'pipe :coding 'no-conversion 7608 :command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t)) 7609 (tcp-proc (lsp--open-network-stream host port (concat name "::tcp")))) 7610 7611 ;; TODO: Same :noquery issue (see above) 7612 (set-process-query-on-exit-flag proc nil) 7613 (set-process-query-on-exit-flag tcp-proc nil) 7614 (set-process-filter tcp-proc filter) 7615 (cons tcp-proc proc))) 7616 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7617 7618 (defalias 'lsp-tcp-server 'lsp-tcp-server-command) 7619 7620 (defun lsp-tcp-server-command (command-fn) 7621 "Create tcp server connection. 7622 In this mode Emacs is TCP server and the language server connects 7623 to it. COMMAND is function with one parameter(the port) and it 7624 should return the command to start the LS server." 7625 (cl-check-type command-fn function) 7626 (list 7627 :connect (lambda (filter sentinel name environment-fn _workspace) 7628 (let* (tcp-client-connection 7629 (tcp-server (make-network-process :name (format "*tcp-server-%s*" name) 7630 :buffer (format "*tcp-server-%s*" name) 7631 :family 'ipv4 7632 :service lsp--tcp-server-port 7633 :sentinel (lambda (proc _string) 7634 (lsp-log "Language server %s is connected." name) 7635 (setf tcp-client-connection proc)) 7636 :server 't)) 7637 (port (process-contact tcp-server :service)) 7638 (final-command (funcall command-fn port)) 7639 (process-environment 7640 (lsp--compute-process-environment environment-fn)) 7641 (cmd-proc (make-process :name name 7642 :connection-type 'pipe 7643 :coding 'no-conversion 7644 :command final-command 7645 :stderr (format "*tcp-server-%s*::stderr" name) 7646 :noquery t))) 7647 (let ((retries 0)) 7648 ;; wait for the client to connect (we sit-for 500 ms, so have to double lsp--tcp-server-wait-seconds) 7649 (while (and (not tcp-client-connection) (< retries (* 2 lsp--tcp-server-wait-seconds))) 7650 (lsp--info "Waiting for connection for %s, retries: %s" name retries) 7651 (sit-for 0.500) 7652 (cl-incf retries))) 7653 7654 (unless tcp-client-connection 7655 (condition-case nil (delete-process tcp-server) (error)) 7656 (condition-case nil (delete-process cmd-proc) (error)) 7657 (error "Failed to create connection to %s on port %s" name port)) 7658 (lsp--info "Successfully connected to %s" name) 7659 7660 (set-process-query-on-exit-flag cmd-proc nil) 7661 (set-process-query-on-exit-flag tcp-client-connection nil) 7662 (set-process-query-on-exit-flag tcp-server nil) 7663 7664 (set-process-filter tcp-client-connection filter) 7665 (set-process-sentinel tcp-client-connection sentinel) 7666 (cons tcp-client-connection cmd-proc))) 7667 :test? (lambda () (lsp-server-present? (funcall command-fn 0))))) 7668 7669 (defalias 'lsp-tramp-connection 'lsp-stdio-connection) 7670 7671 (defun lsp--auto-configure () 7672 "Autoconfigure `company', `flycheck', `lsp-ui', etc if they are installed." 7673 (when (functionp 'lsp-ui-mode) 7674 (lsp-ui-mode)) 7675 7676 (if lsp-headerline-breadcrumb-enable 7677 (add-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode) 7678 (remove-hook 'lsp-configure-hook 'lsp-headerline-breadcrumb-mode)) 7679 (if lsp-modeline-code-actions-enable 7680 (add-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode) 7681 (remove-hook 'lsp-configure-hook 'lsp-modeline-code-actions-mode)) 7682 (if lsp-modeline-diagnostics-enable 7683 (add-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode) 7684 (remove-hook 'lsp-configure-hook 'lsp-modeline-diagnostics-mode)) 7685 (if lsp-modeline-workspace-status-enable 7686 (add-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode) 7687 (remove-hook 'lsp-configure-hook 'lsp-modeline-workspace-status-mode)) 7688 (if lsp-lens-enable 7689 (add-hook 'lsp-configure-hook 'lsp-lens--enable) 7690 (remove-hook 'lsp-configure-hook 'lsp-lens--enable)) 7691 (if lsp-semantic-tokens-enable 7692 (add-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable) 7693 (remove-hook 'lsp-configure-hook 'lsp-semantic-tokens--enable)) 7694 7695 ;; yas-snippet config 7696 (setq-local yas-inhibit-overlay-modification-protection t)) 7697 7698 (defun lsp--restart-if-needed (workspace) 7699 "Handler restart for WORKSPACE." 7700 (when (or (eq lsp-restart 'auto-restart) 7701 (eq (lsp--workspace-shutdown-action workspace) 'restart) 7702 (and (eq lsp-restart 'interactive) 7703 (let ((query (format 7704 "Server %s exited (check corresponding stderr buffer for details). Do you want to restart it?" 7705 (lsp--workspace-print workspace)))) 7706 (y-or-n-p query)))) 7707 (--each (lsp--workspace-buffers workspace) 7708 (when (lsp-buffer-live-p it) 7709 (lsp-with-current-buffer it 7710 (if lsp--buffer-deferred 7711 (lsp-deferred) 7712 (lsp--info "Restarting LSP in buffer %s" (buffer-name)) 7713 (lsp))))))) 7714 7715 (defun lsp--update-key (table key fn) 7716 "Apply FN on value corresponding to KEY in TABLE." 7717 (let ((existing-value (gethash key table))) 7718 (if-let ((new-value (funcall fn existing-value))) 7719 (puthash key new-value table) 7720 (remhash key table)))) 7721 7722 (defun lsp--process-sentinel (workspace process exit-str) 7723 "Create the sentinel for WORKSPACE." 7724 (unless (process-live-p process) 7725 (lsp--handle-process-exit workspace exit-str))) 7726 7727 (defun lsp--handle-process-exit (workspace exit-str) 7728 (let* ((folder->workspaces (lsp-session-folder->servers (lsp-session))) 7729 (proc (lsp--workspace-proc workspace))) 7730 (lsp--warn "%s has exited (%s)" 7731 (lsp-process-name proc) 7732 (string-trim-right (or exit-str ""))) 7733 (with-lsp-workspace workspace 7734 ;; Clean workspace related data in each of the buffers 7735 ;; in the workspace. 7736 (--each (lsp--workspace-buffers workspace) 7737 (when (lsp-buffer-live-p it) 7738 (lsp-with-current-buffer it 7739 (setq lsp--buffer-workspaces (delete workspace lsp--buffer-workspaces)) 7740 (lsp--uninitialize-workspace) 7741 (lsp--spinner-stop) 7742 (lsp--remove-overlays 'lsp-highlight)))) 7743 7744 ;; Cleanup session from references to the closed workspace. 7745 (--each (hash-table-keys folder->workspaces) 7746 (lsp--update-key folder->workspaces it (apply-partially 'delete workspace))) 7747 7748 (lsp-process-cleanup proc)) 7749 7750 (run-hook-with-args 'lsp-after-uninitialized-functions workspace) 7751 7752 (if (eq (lsp--workspace-shutdown-action workspace) 'shutdown) 7753 (lsp--info "Workspace %s shutdown." (lsp--workspace-print workspace)) 7754 (lsp--restart-if-needed workspace)) 7755 (lsp--cleanup-hanging-watches))) 7756 7757 (defun lsp-workspace-folders (workspace) 7758 "Return all folders associated with WORKSPACE." 7759 (let (result) 7760 (->> (lsp-session) 7761 (lsp-session-folder->servers) 7762 (maphash (lambda (folder workspaces) 7763 (when (-contains? workspaces workspace) 7764 (push folder result))))) 7765 result)) 7766 7767 (defun lsp--start-workspace (session client-template root &optional initialization-options) 7768 "Create new workspace for CLIENT-TEMPLATE with project root ROOT. 7769 INITIALIZATION-OPTIONS are passed to initialize function. 7770 SESSION is the active session." 7771 (lsp--spinner-start) 7772 (-let* ((default-directory root) 7773 (client (copy-lsp--client client-template)) 7774 (workspace (make-lsp--workspace 7775 :root root 7776 :client client 7777 :status 'starting 7778 :buffers (list (lsp-current-buffer)) 7779 :host-root (file-remote-p root))) 7780 ((&lsp-cln 'server-id 'environment-fn 'new-connection 'custom-capabilities 7781 'multi-root 'initialized-fn) client) 7782 ((proc . cmd-proc) (funcall 7783 (or (plist-get new-connection :connect) 7784 (user-error "Client %s is configured incorrectly" client)) 7785 (lsp--create-filter-function workspace) 7786 (apply-partially #'lsp--process-sentinel workspace) 7787 (format "%s" server-id) 7788 environment-fn 7789 workspace)) 7790 (workspace-folders (gethash server-id (lsp-session-server-id->folders session)))) 7791 (setf (lsp--workspace-proc workspace) proc 7792 (lsp--workspace-cmd-proc workspace) cmd-proc) 7793 7794 ;; update (lsp-session-folder->servers) depending on whether we are starting 7795 ;; multi/single folder workspace 7796 (mapc (lambda (project-root) 7797 (->> session 7798 (lsp-session-folder->servers) 7799 (gethash project-root) 7800 (cl-pushnew workspace))) 7801 (or workspace-folders (list root))) 7802 7803 (with-lsp-workspace workspace 7804 (run-hooks 'lsp-before-initialize-hook) 7805 (lsp-request-async 7806 "initialize" 7807 (append 7808 (list :processId (unless (file-remote-p (buffer-file-name)) 7809 (emacs-pid)) 7810 :rootPath (lsp-file-local-name (expand-file-name root)) 7811 :clientInfo (list :name "emacs" 7812 :version (emacs-version)) 7813 :rootUri (lsp--path-to-uri root) 7814 :capabilities (lsp--client-capabilities custom-capabilities) 7815 :initializationOptions initialization-options 7816 :workDoneToken "1") 7817 (when lsp-server-trace 7818 (list :trace lsp-server-trace)) 7819 (when multi-root 7820 (->> workspace-folders 7821 (-distinct) 7822 (-map (lambda (folder) 7823 (list :uri (lsp--path-to-uri folder) 7824 :name (f-filename folder)))) 7825 (apply 'vector) 7826 (list :workspaceFolders)))) 7827 (-lambda ((&InitializeResult :capabilities)) 7828 ;; we know that Rust Analyzer will send {} which will be parsed as null 7829 ;; when using plists 7830 (when (equal 'rust-analyzer server-id) 7831 (-> capabilities 7832 (lsp:server-capabilities-text-document-sync?) 7833 (lsp:set-text-document-sync-options-save? t))) 7834 7835 (setf (lsp--workspace-server-capabilities workspace) capabilities 7836 (lsp--workspace-status workspace) 'initialized) 7837 7838 (with-lsp-workspace workspace 7839 (lsp-notify "initialized" lsp--empty-ht)) 7840 7841 (when initialized-fn (funcall initialized-fn workspace)) 7842 7843 (cl-callf2 -filter #'lsp-buffer-live-p (lsp--workspace-buffers workspace)) 7844 (->> workspace 7845 (lsp--workspace-buffers) 7846 (mapc (lambda (buffer) 7847 (lsp-with-current-buffer buffer 7848 (lsp--open-in-workspace workspace))))) 7849 7850 (with-lsp-workspace workspace 7851 (run-hooks 'lsp-after-initialize-hook)) 7852 (lsp--info "%s initialized successfully in folders: %s" 7853 (lsp--workspace-print workspace) 7854 (lsp-workspace-folders workspace))) 7855 :mode 'detached)) 7856 workspace)) 7857 7858 (defun lsp--load-default-session () 7859 "Load default session." 7860 (setq lsp--session (or (condition-case err 7861 (lsp--read-from-file lsp-session-file) 7862 (error (lsp--error "Failed to parse the session %s, starting with clean one." 7863 (error-message-string err)) 7864 nil)) 7865 (make-lsp-session)))) 7866 7867 (defun lsp-session () 7868 "Get the session associated with the current buffer." 7869 (or lsp--session (setq lsp--session (lsp--load-default-session)))) 7870 7871 (defun lsp--client-disabled-p (buffer-major-mode client) 7872 (seq-some 7873 (lambda (entry) 7874 (pcase entry 7875 ((pred symbolp) (eq entry client)) 7876 (`(,mode . ,client-or-list) 7877 (and (eq mode buffer-major-mode) 7878 (if (listp client-or-list) 7879 (memq client client-or-list) 7880 (eq client client-or-list)))))) 7881 lsp-disabled-clients)) 7882 7883 7884 ;; download server 7885 7886 (defcustom lsp-server-install-dir (expand-file-name 7887 (locate-user-emacs-file (f-join ".cache" "lsp"))) 7888 "Directory in which the servers will be installed." 7889 :risky t 7890 :type 'directory 7891 :package-version '(lsp-mode . "6.3") 7892 :group 'lsp-mode) 7893 7894 (defcustom lsp-verify-signature t 7895 "Whether to check GPG signatures of downloaded files." 7896 :type 'boolean 7897 :package-version '(lsp-mode . "8.0.0") 7898 :group 'lsp-mode) 7899 7900 (defvar lsp--dependencies (ht)) 7901 7902 (defun lsp-dependency (name &rest definitions) 7903 "Used to specify a language server DEPENDENCY, the server 7904 executable or other required file path. Typically, the 7905 DEPENDENCY is found by locating it on the system path using 7906 `executable-find'. 7907 7908 You can explicitly call lsp-dependency in your environment to 7909 specify the absolute path to the DEPENDENCY. For example, the 7910 typescript-language-server requires both the server and the 7911 typescript compiler. If you have installed them in a team shared 7912 read-only location, you can instruct lsp-mode to use them via 7913 7914 (eval-after-load `lsp-mode 7915 `(progn 7916 (require lsp-javascript) 7917 (lsp-dependency typescript-language-server (:system ,tls-exe)) 7918 (lsp-dependency typescript (:system ,ts-js)))) 7919 7920 where tls-exe is the absolute path to the typescript-language-server 7921 executable and ts-js is the absolute path to the typescript compiler 7922 JavaScript file, tsserver.js (the *.js is required for Windows)." 7923 (ht-set lsp--dependencies name definitions)) 7924 7925 (defun lsp--server-binary-present? (client) 7926 (unless (equal (lsp--client-server-id client) 'lsp-pwsh) 7927 (condition-case () 7928 (-some-> client lsp--client-new-connection (plist-get :test?) funcall) 7929 (error nil) 7930 (args-out-of-range nil)))) 7931 7932 (define-minor-mode lsp-installation-buffer-mode 7933 "Mode used in *lsp-installation* buffers. 7934 It can be used to set-up keybindings, etc. Disabling this mode 7935 detaches the installation buffer from commands like 7936 `lsp-select-installation-buffer'." 7937 :init-value nil 7938 :lighter nil) 7939 7940 (defface lsp-installation-finished-buffer-face '((t :foreground "orange")) 7941 "Face used for finished installation buffers. 7942 Used in `lsp-select-installation-buffer'." 7943 :group 'lsp-mode) 7944 7945 (defface lsp-installation-buffer-face '((t :foreground "green")) 7946 "Face used for installation buffers still in progress. 7947 Used in `lsp-select-installation-buffer'." 7948 :group 'lsp-mode) 7949 7950 (defun lsp--installation-buffer? (buf) 7951 "Check whether BUF is an `lsp-async-start-process' buffer." 7952 (buffer-local-value 'lsp-installation-buffer-mode buf)) 7953 7954 (defun lsp-select-installation-buffer (&optional show-finished) 7955 "Interactively choose an installation buffer. 7956 If SHOW-FINISHED is set, leftover (finished) installation buffers 7957 are still shown." 7958 (interactive "P") 7959 (let ((bufs (--filter (and (lsp--installation-buffer? it) 7960 (or show-finished (get-buffer-process it))) 7961 (buffer-list)))) 7962 (pcase bufs 7963 (`nil (user-error "No installation buffers")) 7964 (`(,buf) (pop-to-buffer buf)) 7965 (bufs (pop-to-buffer (completing-read "Select installation buffer: " 7966 (--map (propertize (buffer-name it) 'face 7967 (if (get-buffer-process it) 7968 'lsp-installation-buffer-face 7969 'lsp-installation-finished-buffer-face)) 7970 bufs))))))) 7971 7972 (defun lsp-cleanup-installation-buffers () 7973 "Delete finished *lsp-installation* buffers." 7974 (interactive) 7975 (dolist (buf (buffer-list)) 7976 (when (and (lsp--installation-buffer? buf) (not (get-buffer-process buf))) 7977 (kill-buffer buf)))) 7978 7979 (defun lsp--download-status () 7980 (-some--> #'lsp--client-download-in-progress? 7981 (lsp--filter-clients it) 7982 (-map (-compose #'symbol-name #'lsp--client-server-id) it) 7983 (format "%s" it) 7984 (propertize it 'face 'success) 7985 (format " Installing following servers: %s" it) 7986 (propertize it 7987 'local-map (make-mode-line-mouse-map 7988 'mouse-1 #'lsp-select-installation-buffer) 7989 'mouse-face 'highlight))) 7990 7991 (defun lsp--install-server-internal (client &optional update?) 7992 (unless (lsp--client-download-server-fn client) 7993 (user-error "There is no automatic installation for `%s', you have to install it manually following lsp-mode's documentation." 7994 (lsp--client-server-id client))) 7995 7996 (setf (lsp--client-download-in-progress? client) t) 7997 (add-to-list 'global-mode-string '(t (:eval (lsp--download-status)))) 7998 (cl-flet ((done 7999 (success? &optional error-message) 8000 ;; run with idle timer to make sure the lsp command is executed in 8001 ;; the main thread, see #2739. 8002 (run-with-timer 8003 0.0 8004 nil 8005 (lambda () 8006 (-let [(&lsp-cln 'server-id 'buffers) client] 8007 (setf (lsp--client-download-in-progress? client) nil 8008 (lsp--client-buffers client) nil) 8009 (if success? 8010 (lsp--info "Server %s downloaded, auto-starting in %s buffers." server-id 8011 (length buffers)) 8012 (lsp--error "Server %s install process failed with the following error message: %s. 8013 Check `*lsp-install*' and `*lsp-log*' buffer." 8014 server-id 8015 error-message)) 8016 (seq-do 8017 (lambda (buffer) 8018 (when (lsp-buffer-live-p buffer) 8019 (lsp-with-current-buffer buffer 8020 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8021 global-mode-string) 8022 (when success? (lsp))))) 8023 buffers) 8024 (unless (lsp--filter-clients #'lsp--client-download-in-progress?) 8025 (cl-callf2 -remove-item '(t (:eval (lsp--download-status))) 8026 global-mode-string))))))) 8027 (lsp--info "Download %s started." (lsp--client-server-id client)) 8028 (condition-case err 8029 (funcall 8030 (lsp--client-download-server-fn client) 8031 client 8032 (lambda () (done t)) 8033 (lambda (msg) (done nil msg)) 8034 update?) 8035 (error 8036 (done nil (error-message-string err)))))) 8037 8038 (defun lsp--require-packages () 8039 "Load `lsp-client-packages' if needed." 8040 (when (and lsp-auto-configure (not lsp--client-packages-required)) 8041 (seq-do (lambda (package) 8042 ;; loading client is slow and `lsp' can be called repeatedly 8043 (unless (featurep package) 8044 (require package nil t))) 8045 lsp-client-packages) 8046 (setq lsp--client-packages-required t))) 8047 8048 ;;;###autoload 8049 (defun lsp-install-server (update? &optional server-id) 8050 "Interactively install or re-install server. 8051 When prefix UPDATE? is t force installation even if the server is present." 8052 (interactive "P") 8053 (lsp--require-packages) 8054 (let* ((chosen-client (or (gethash server-id lsp-clients) 8055 (lsp--completing-read 8056 "Select server to install/re-install: " 8057 (or (->> lsp-clients 8058 (ht-values) 8059 (-filter (-andfn 8060 (-not #'lsp--client-download-in-progress?) 8061 #'lsp--client-download-server-fn))) 8062 (user-error "There are no servers with automatic installation")) 8063 (lambda (client) 8064 (let ((server-name (-> client lsp--client-server-id symbol-name))) 8065 (if (lsp--server-binary-present? client) 8066 (concat server-name " (Already installed)") 8067 server-name))) 8068 nil 8069 t))) 8070 (update? (or update? 8071 (and (not (lsp--client-download-in-progress? chosen-client)) 8072 (lsp--server-binary-present? chosen-client))))) 8073 (lsp--install-server-internal chosen-client update?))) 8074 8075 ;;;###autoload 8076 (defun lsp-uninstall-server (dir) 8077 "Delete a LSP server from `lsp-server-install-dir'." 8078 (interactive 8079 (list (read-directory-name "Uninstall LSP server: " (f-slash lsp-server-install-dir)))) 8080 (unless (file-directory-p dir) 8081 (user-error "Couldn't find %s directory" dir)) 8082 (delete-directory dir 'recursive) 8083 (message "Server `%s' uninstalled." (file-name-nondirectory (directory-file-name dir)))) 8084 8085 ;;;###autoload 8086 (defun lsp-uninstall-servers () 8087 "Uninstall all installed servers." 8088 (interactive) 8089 (let* ((dir lsp-server-install-dir) 8090 (servers (ignore-errors 8091 (directory-files dir t 8092 directory-files-no-dot-files-regexp)))) 8093 (if (or (not (file-directory-p dir)) (zerop (length servers))) 8094 (user-error "No servers to uninstall") 8095 (when (yes-or-no-p 8096 (format "Servers to uninstall: %d (%s), proceed? " 8097 (length servers) 8098 (mapconcat (lambda (server) 8099 (file-name-nondirectory (directory-file-name server))) 8100 servers " "))) 8101 (mapc #'lsp-uninstall-server servers) 8102 (message "All servers uninstalled"))))) 8103 8104 ;;;###autoload 8105 (defun lsp-update-server (&optional server-id) 8106 "Interactively update (reinstall) a server." 8107 (interactive) 8108 (lsp--require-packages) 8109 (let ((chosen-client (or (gethash server-id lsp-clients) 8110 (lsp--completing-read 8111 "Select server to update (if not on the list, probably you need to `lsp-install-server`): " 8112 (or (->> lsp-clients 8113 (ht-values) 8114 (-filter (-andfn 8115 (-not #'lsp--client-download-in-progress?) 8116 #'lsp--client-download-server-fn 8117 #'lsp--server-binary-present?))) 8118 (user-error "There are no servers to update")) 8119 (lambda (client) 8120 (-> client lsp--client-server-id symbol-name)) 8121 nil 8122 t)))) 8123 (lsp--install-server-internal chosen-client t))) 8124 8125 ;;;###autoload 8126 (defun lsp-update-servers () 8127 "Update (reinstall) all installed servers." 8128 (interactive) 8129 (lsp--require-packages) 8130 (mapc (lambda (client) (lsp--install-server-internal client t)) 8131 (-filter (-andfn 8132 (-not #'lsp--client-download-in-progress?) 8133 #'lsp--client-download-server-fn 8134 #'lsp--server-binary-present?) (hash-table-values lsp-clients)))) 8135 8136 ;;;###autoload 8137 (defun lsp-ensure-server (server-id) 8138 "Ensure server SERVER-ID" 8139 (lsp--require-packages) 8140 (if-let ((client (gethash server-id lsp-clients))) 8141 (unless (lsp--server-binary-present? client) 8142 (lsp--info "Server `%s' is not preset, installing..." server-id) 8143 (lsp-install-server nil server-id)) 8144 (warn "Unable to find server registration with id %s" server-id))) 8145 8146 (defun lsp-async-start-process (callback error-callback &rest command) 8147 "Start async process COMMAND with CALLBACK and ERROR-CALLBACK." 8148 (let ((name (cl-first command))) 8149 (with-current-buffer (compilation-start (mapconcat #'shell-quote-argument (-filter (lambda (cmd) 8150 (not (null cmd))) 8151 command) 8152 " ") t 8153 (lambda (&rest _) 8154 (generate-new-buffer-name (format "*lsp-install: %s*" name)))) 8155 (lsp-installation-buffer-mode +1) 8156 (view-mode +1) 8157 (add-hook 8158 'compilation-finish-functions 8159 (lambda (_buf status) 8160 (if (string= "finished\n" status) 8161 (condition-case err 8162 (funcall callback) 8163 (error 8164 (funcall error-callback (error-message-string err)))) 8165 (funcall error-callback (s-trim-right status)))) 8166 nil t)))) 8167 8168 (defun lsp-resolve-value (value) 8169 "Resolve VALUE's value. 8170 If it is function - call it. 8171 If it is a variable - return it's value 8172 Otherwise returns value itself." 8173 (cond 8174 ((functionp value) (funcall value)) 8175 ((and (symbolp value) (boundp value)) (symbol-value value)) 8176 (value))) 8177 8178 (defvar lsp-deps-providers 8179 (list :npm (list :path #'lsp--npm-dependency-path 8180 :install #'lsp--npm-dependency-install) 8181 :cargo (list :path #'lsp--cargo-dependency-path 8182 :install #'lsp--cargo-dependency-install) 8183 :system (list :path #'lsp--system-path) 8184 :download (list :path #'lsp-download-path 8185 :install #'lsp-download-install))) 8186 8187 (defun lsp--system-path (path) 8188 "If PATH is absolute and exists return it as is. Otherwise, 8189 return the absolute path to the executable defined by PATH or 8190 nil." 8191 ;; For node.js 'sub-packages' PATH may point to a *.js file. Consider the 8192 ;; typescript-language-server. When lsp invokes the server, lsp needs to 8193 ;; supply the path to the typescript compiler, tsserver.js, as an argument. To 8194 ;; make code platform independent, one must pass the absolute path to the 8195 ;; tsserver.js file (Windows requires a *.js file - see help on the JavaScript 8196 ;; child process spawn command that is invoked by the 8197 ;; typescript-language-server). This is why we check for existence and not 8198 ;; that the path is executable. 8199 (let ((path (lsp-resolve-value path))) 8200 (cond 8201 ((and (f-absolute? path) 8202 (f-exists? path)) 8203 path) 8204 ((executable-find path t) path)))) 8205 8206 (defun lsp-package-path (dependency) 8207 "Path to the DEPENDENCY each of the registered providers." 8208 (let (path) 8209 (-first (-lambda ((provider . rest)) 8210 (setq path (-some-> lsp-deps-providers 8211 (plist-get provider) 8212 (plist-get :path) 8213 (apply rest)))) 8214 (gethash dependency lsp--dependencies)) 8215 path)) 8216 8217 (defun lsp-package-ensure (dependency callback error-callback) 8218 "Asynchronously ensure a package." 8219 (or (-first (-lambda ((provider . rest)) 8220 (-some-> lsp-deps-providers 8221 (plist-get provider) 8222 (plist-get :install) 8223 (apply (cl-list* callback error-callback rest)))) 8224 (gethash dependency lsp--dependencies)) 8225 (funcall error-callback (format "Unable to find a way to install %s" dependency)))) 8226 8227 8228 ;; npm handling 8229 8230 ;; https://docs.npmjs.com/files/folders#executables 8231 (cl-defun lsp--npm-dependency-path (&key package path &allow-other-keys) 8232 "Return npm dependency PATH for PACKAGE." 8233 (let ((path (executable-find 8234 (f-join lsp-server-install-dir "npm" package 8235 (cond ((eq system-type 'windows-nt) "") 8236 (t "bin")) 8237 path) 8238 t))) 8239 (unless (and path (f-exists? path)) 8240 (error "The package %s is not installed. Unable to find %s" package path)) 8241 path)) 8242 8243 (cl-defun lsp--npm-dependency-install (callback error-callback &key package &allow-other-keys) 8244 (if-let ((npm-binary (executable-find "npm"))) 8245 (progn 8246 ;; Explicitly `make-directory' to work around NPM bug in 8247 ;; versions 7.0.0 through 7.4.1. See 8248 ;; https://github.com/emacs-lsp/lsp-mode/issues/2364 for 8249 ;; discussion. 8250 (make-directory (f-join lsp-server-install-dir "npm" package "lib") 'parents) 8251 (lsp-async-start-process (lambda () 8252 (if (string-empty-p 8253 (string-trim (shell-command-to-string 8254 (mapconcat #'shell-quote-argument `(,npm-binary "view" ,package "peerDependencies") " ")))) 8255 (funcall callback) 8256 (let ((default-directory (f-dirname (car (last (directory-files-recursively (f-join lsp-server-install-dir "npm" package) "package.json"))))) 8257 (process-environment (append '("npm_config_yes=true") process-environment))) ;; Disable prompting for older versions of npx 8258 (when (f-dir-p default-directory) 8259 (lsp-async-start-process callback 8260 error-callback 8261 (executable-find "npx") 8262 "npm-install-peers"))))) 8263 error-callback 8264 npm-binary 8265 "-g" 8266 "--prefix" 8267 (f-join lsp-server-install-dir "npm" package) 8268 "install" 8269 package)) 8270 (lsp-log "Unable to install %s via `npm' because it is not present" package) 8271 nil)) 8272 8273 8274 ;; Cargo dependency handling 8275 (cl-defun lsp--cargo-dependency-path (&key package path &allow-other-keys) 8276 (let ((path (executable-find 8277 (f-join lsp-server-install-dir 8278 "cargo" 8279 package 8280 "bin" 8281 path) 8282 t))) 8283 (unless (and path (f-exists? path)) 8284 (error "The package %s is not installed. Unable to find %s" package path)) 8285 path)) 8286 8287 (cl-defun lsp--cargo-dependency-install (callback error-callback &key package git &allow-other-keys) 8288 (if-let ((cargo-binary (executable-find "cargo"))) 8289 (lsp-async-start-process 8290 callback 8291 error-callback 8292 cargo-binary 8293 "install" 8294 package 8295 (when git 8296 "--git") 8297 git 8298 "--root" 8299 (f-join lsp-server-install-dir "cargo" package)) 8300 (lsp-log "Unable to install %s via `cargo' because it is not present" package) 8301 nil)) 8302 8303 8304 8305 ;; Download URL handling 8306 (cl-defun lsp-download-install (callback error-callback &key url asc-url pgp-key store-path decompress &allow-other-keys) 8307 (let* ((url (lsp-resolve-value url)) 8308 (store-path (lsp-resolve-value store-path)) 8309 ;; (decompress (lsp-resolve-value decompress)) 8310 (download-path 8311 (pcase decompress 8312 (:gzip (concat store-path ".gz")) 8313 (:zip (concat store-path ".zip")) 8314 (:targz (concat store-path ".tar.gz")) 8315 (`nil store-path) 8316 (_ (error ":decompress must be `:gzip', `:zip', `:targz' or `nil'"))))) 8317 (make-thread 8318 (lambda () 8319 (condition-case err 8320 (progn 8321 (when (f-exists? download-path) 8322 (f-delete download-path)) 8323 (when (f-exists? store-path) 8324 (f-delete store-path)) 8325 (lsp--info "Starting to download %s to %s..." url download-path) 8326 (mkdir (f-parent download-path) t) 8327 (url-copy-file url download-path) 8328 (lsp--info "Finished downloading %s..." download-path) 8329 (when (and lsp-verify-signature asc-url pgp-key) 8330 (if (executable-find epg-gpg-program) 8331 (let ((asc-download-path (concat download-path ".asc")) 8332 (context (epg-make-context)) 8333 (fingerprint) 8334 (signature)) 8335 (when (f-exists? asc-download-path) 8336 (f-delete asc-download-path)) 8337 (lsp--info "Starting to download %s to %s..." asc-url asc-download-path) 8338 (url-copy-file asc-url asc-download-path) 8339 (lsp--info "Finished downloading %s..." asc-download-path) 8340 (epg-import-keys-from-string context pgp-key) 8341 (setq fingerprint (epg-import-status-fingerprint 8342 (car 8343 (epg-import-result-imports 8344 (epg-context-result-for context 'import))))) 8345 (lsp--info "Verifying signature %s..." asc-download-path) 8346 (epg-verify-file context asc-download-path download-path) 8347 (setq signature (car (epg-context-result-for context 'verify))) 8348 (unless (and 8349 (eq (epg-signature-status signature) 'good) 8350 (equal (epg-signature-fingerprint signature) fingerprint)) 8351 (error "Failed to verify GPG signature: %s" (epg-signature-to-string signature)))) 8352 (lsp--warn "GPG is not installed, skipping the signature check."))) 8353 (when decompress 8354 (lsp--info "Decompressing %s..." download-path) 8355 (pcase decompress 8356 (:gzip 8357 (lsp-gunzip download-path)) 8358 (:zip (lsp-unzip download-path (f-parent store-path))) 8359 (:targz (lsp-tar-gz-decompress download-path (f-parent store-path)))) 8360 (lsp--info "Decompressed %s..." store-path)) 8361 (funcall callback)) 8362 (error (funcall error-callback err))))))) 8363 8364 (cl-defun lsp-download-path (&key store-path binary-path set-executable? &allow-other-keys) 8365 "Download URL and store it into STORE-PATH. 8366 8367 SET-EXECUTABLE? when non-nil change the executable flags of 8368 STORE-PATH to make it executable. BINARY-PATH can be specified 8369 when the binary to start does not match the name of the 8370 archive (e.g. when the archive has multiple files)" 8371 (let ((store-path (or (lsp-resolve-value binary-path) 8372 (lsp-resolve-value store-path)))) 8373 (cond 8374 ((executable-find store-path) store-path) 8375 ((and set-executable? (f-exists? store-path)) 8376 (set-file-modes store-path #o0700) 8377 store-path) 8378 ((f-exists? store-path) store-path)))) 8379 8380 (defun lsp--find-latest-gh-release-url (url regex) 8381 "Fetch the latest version in the releases given by URL by using REGEX." 8382 (let ((url-request-method "GET")) 8383 (with-current-buffer (url-retrieve-synchronously url) 8384 (goto-char (point-min)) 8385 (re-search-forward "\n\n" nil 'noerror) 8386 (delete-region (point-min) (point)) 8387 (let* ((json-result (lsp-json-read-buffer))) 8388 (message "Latest version found: %s" (lsp-get json-result :tag_name)) 8389 (--> json-result 8390 (lsp-get it :assets) 8391 (seq-find (lambda (entry) (string-match-p regex (lsp-get entry :name))) it) 8392 (lsp-get it :browser_download_url)))))) 8393 8394 ;; unzip 8395 8396 (defconst lsp-ext-pwsh-script "powershell -noprofile -noninteractive \ 8397 -nologo -ex bypass -command Expand-Archive -path '%s' -dest '%s'" 8398 "Powershell script to unzip file.") 8399 8400 (defconst lsp-ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq -o %1$s -d %2$s'" 8401 "Unzip script to unzip file.") 8402 8403 (defcustom lsp-unzip-script (lambda () 8404 (cond ((executable-find "unzip") lsp-ext-unzip-script) 8405 ((executable-find "powershell") lsp-ext-pwsh-script) 8406 (t nil))) 8407 "The script to unzip." 8408 :group 'lsp-mode 8409 :type 'string 8410 :package-version '(lsp-mode . "8.0.0")) 8411 8412 (defun lsp-unzip (zip-file dest) 8413 "Unzip ZIP-FILE to DEST." 8414 (unless lsp-unzip-script 8415 (error "Unable to find `unzip' or `powershell' on the path, please customize `lsp-unzip-script'")) 8416 (shell-command (format (lsp-resolve-value lsp-unzip-script) zip-file dest))) 8417 8418 ;; gunzip 8419 8420 (defconst lsp-ext-gunzip-script "gzip -d %1$s" 8421 "Script to decompress a gzippped file with gzip.") 8422 8423 (defcustom lsp-gunzip-script (lambda () 8424 (cond ((executable-find "gzip") lsp-ext-gunzip-script) 8425 (t nil))) 8426 "The script to decompress a gzipped file. 8427 Should be a format string with one argument for the file to be decompressed 8428 in place." 8429 :group 'lsp-mode 8430 :type 'string 8431 :package-version '(lsp-mode . "8.0.0")) 8432 8433 (defun lsp-gunzip (gz-file) 8434 "Decompress GZ-FILE in place." 8435 (unless lsp-gunzip-script 8436 (error "Unable to find `gzip' on the path, please either customize `lsp-gunzip-script' or manually decompress %s" gz-file)) 8437 (shell-command (format (lsp-resolve-value lsp-gunzip-script) gz-file))) 8438 8439 ;; tar.gz decompression 8440 8441 (defconst lsp-ext-tar-script "bash -c 'mkdir -p %2$s; tar xf %1$s --directory=%2$s'" 8442 "Script to decompress a .tar.gz file.") 8443 8444 (defcustom lsp-tar-script (lambda () 8445 (cond ((executable-find "tar") lsp-ext-tar-script) 8446 (t nil))) 8447 "The script to decompress a .tar.gz file. 8448 Should be a format string with one argument for the file to be decompressed 8449 in place." 8450 :group 'lsp-mode 8451 :type 'string) 8452 8453 (defun lsp-tar-gz-decompress (targz-file dest) 8454 "Decompress TARGZ-FILE in DEST." 8455 (unless lsp-tar-script 8456 (error "Unable to find `tar' on the path, please either customize `lsp-tar-script' or manually decompress %s" targz-file)) 8457 (shell-command (format (lsp-resolve-value lsp-tar-script) targz-file dest))) 8458 8459 8460 ;; VSCode marketplace 8461 8462 (defcustom lsp-vscode-ext-url 8463 "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage%s" 8464 "Vscode extension template url." 8465 :group 'lsp-mode 8466 :type 'string 8467 :package-version '(lsp-mode . "8.0.0")) 8468 8469 (defun lsp-vscode-extension-url (publisher name version &optional targetPlatform) 8470 "Return the URL to vscode extension. 8471 PUBLISHER is the extension publisher. 8472 NAME is the name of the extension. 8473 VERSION is the version of the extension. 8474 TARGETPLATFORM is the targetPlatform of the extension." 8475 (format lsp-vscode-ext-url publisher name version (or targetPlatform ""))) 8476 8477 8478 8479 ;; Queueing prompts 8480 8481 (defvar lsp--question-queue nil 8482 "List of questions yet to be asked by `lsp-ask-question'.") 8483 8484 (defun lsp-ask-question (question options callback) 8485 "Prompt the user to answer the QUESTION with one of the OPTIONS from the 8486 minibuffer. Once the user selects an option, the CALLBACK function will be 8487 called, passing the selected option to it. 8488 8489 If the user is currently being shown a question, the question will be stored in 8490 `lsp--question-queue', and will be asked once the user has answered the current 8491 question." 8492 (add-to-list 'lsp--question-queue `(("question" . ,question) 8493 ("options" . ,options) 8494 ("callback" . ,callback)) t) 8495 (when (eq (length lsp--question-queue) 1) 8496 (lsp--process-question-queue))) 8497 8498 (defun lsp--process-question-queue () 8499 "Take the first question from `lsp--question-queue', process it, then process 8500 the next question until the queue is empty." 8501 (-let* (((&alist "question" "options" "callback") (car lsp--question-queue)) 8502 (answer (completing-read question options nil t))) 8503 (pop lsp--question-queue) 8504 (funcall callback answer) 8505 (when lsp--question-queue 8506 (lsp--process-question-queue)))) 8507 8508 (defun lsp--supports-buffer? (client) 8509 (and 8510 ;; both file and client remote or both local 8511 (eq (---truthy? (file-remote-p (buffer-file-name))) 8512 (---truthy? (lsp--client-remote? client))) 8513 8514 ;; activation function or major-mode match. 8515 (if-let ((activation-fn (lsp--client-activation-fn client))) 8516 (funcall activation-fn (buffer-file-name) major-mode) 8517 (-contains? (lsp--client-major-modes client) major-mode)) 8518 8519 ;; check whether it is enabled if `lsp-enabled-clients' is not null 8520 (or (null lsp-enabled-clients) 8521 (or (member (lsp--client-server-id client) lsp-enabled-clients) 8522 (ignore (lsp--info "Client %s is not in lsp-enabled-clients" 8523 (lsp--client-server-id client))))) 8524 8525 ;; check whether it is not disabled. 8526 (not (lsp--client-disabled-p major-mode (lsp--client-server-id client))))) 8527 8528 (defun lsp--filter-clients (pred) 8529 (->> lsp-clients hash-table-values (-filter pred))) 8530 8531 (defun lsp--find-clients () 8532 "Find clients which can handle current buffer." 8533 (-when-let (matching-clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 8534 #'lsp--server-binary-present?))) 8535 (lsp-log "Found the following clients for %s: %s" 8536 (buffer-file-name) 8537 (s-join ", " 8538 (-map (lambda (client) 8539 (format "(server-id %s, priority %s)" 8540 (lsp--client-server-id client) 8541 (lsp--client-priority client))) 8542 matching-clients))) 8543 (-let* (((add-on-clients main-clients) (-separate #'lsp--client-add-on? matching-clients)) 8544 (selected-clients (if-let ((main-client (and main-clients 8545 (--max-by (> (lsp--client-priority it) 8546 (lsp--client-priority other)) 8547 main-clients)))) 8548 (cons main-client add-on-clients) 8549 add-on-clients))) 8550 (lsp-log "The following clients were selected based on priority: %s" 8551 (s-join ", " 8552 (-map (lambda (client) 8553 (format "(server-id %s, priority %s)" 8554 (lsp--client-server-id client) 8555 (lsp--client-priority client))) 8556 selected-clients))) 8557 selected-clients))) 8558 8559 (defun lsp-workspace-remove-all-folders() 8560 "Delete all lsp tracked folders." 8561 (interactive) 8562 (--each (lsp-session-folders (lsp-session)) 8563 (lsp-workspace-folders-remove it))) 8564 8565 (defun lsp-register-client (client) 8566 "Registers LSP client CLIENT." 8567 (let ((client-id (lsp--client-server-id client))) 8568 (puthash client-id client lsp-clients) 8569 (setplist (intern (format "lsp-%s-after-open-hook" client-id)) 8570 `( standard-value (nil) custom-type hook 8571 custom-package-version (lsp-mode . "7.0.1") 8572 variable-documentation ,(format "Hooks to run after `%s' server is run." client-id) 8573 custom-requests nil))) 8574 (when (and lsp-auto-register-remote-clients 8575 (not (lsp--client-remote? client))) 8576 (let ((remote-client (copy-lsp--client client))) 8577 (setf (lsp--client-remote? remote-client) t 8578 (lsp--client-server-id remote-client) (intern 8579 (format "%s-tramp" 8580 (lsp--client-server-id client))) 8581 ;; disable automatic download 8582 (lsp--client-download-server-fn remote-client) nil) 8583 (lsp-register-client remote-client)))) 8584 8585 (defun lsp--create-initialization-options (_session client) 8586 "Create initialization-options from SESSION and CLIENT. 8587 Add workspace folders depending on server being multiroot and 8588 session workspace folder configuration for the server." 8589 (let* ((initialization-options-or-fn (lsp--client-initialization-options client))) 8590 (if (functionp initialization-options-or-fn) 8591 (funcall initialization-options-or-fn) 8592 initialization-options-or-fn))) 8593 8594 (defvar lsp-client-settings (make-hash-table :test 'equal) 8595 "For internal use, any external users please use 8596 `lsp-register-custom-settings' function instead") 8597 8598 (defun lsp-register-custom-settings (props) 8599 "Register PROPS. 8600 PROPS is list of triple (path value boolean?) where PATH is the path to the 8601 property; VALUE can be a literal value, symbol to be evaluated, or either a 8602 function or lambda function to be called without arguments; BOOLEAN? is an 8603 optional flag that should be non-nil for boolean settings, when it is nil the 8604 property will be ignored if the VALUE is nil. 8605 8606 Example: `(lsp-register-custom-settings `((\"foo.bar.buzz.enabled\" t t)))' 8607 \(note the double parentheses)" 8608 (mapc 8609 (-lambda ((path . rest)) 8610 (puthash path rest lsp-client-settings)) 8611 props)) 8612 8613 (defun lsp-region-text (region) 8614 "Get the text for REGION in current buffer." 8615 (-let (((start . end) (lsp--range-to-region region))) 8616 (buffer-substring-no-properties start end))) 8617 8618 (defun lsp-ht-set (tbl paths value) 8619 "Set nested hash table value. 8620 TBL - a hash table, PATHS is the path to the nested VALUE." 8621 (pcase paths 8622 (`(,path) (ht-set! tbl path value)) 8623 (`(,path . ,rst) (let ((nested-tbl (or (gethash path tbl) 8624 (let ((temp-tbl (ht))) 8625 (ht-set! tbl path temp-tbl) 8626 temp-tbl)))) 8627 (lsp-ht-set nested-tbl rst value))))) 8628 8629 ;; sections 8630 8631 (defalias 'defcustom-lsp 'lsp-defcustom) 8632 8633 (defmacro lsp-defcustom (symbol standard doc &rest args) 8634 "Defines `lsp-mode' server property." 8635 (declare (doc-string 3) (debug (name body)) 8636 (indent defun)) 8637 (let ((path (plist-get args :lsp-path))) 8638 (cl-remf args :lsp-path) 8639 `(progn 8640 (lsp-register-custom-settings 8641 (quote ((,path ,symbol ,(equal ''boolean (plist-get args :type)))))) 8642 8643 (defcustom ,symbol ,standard ,doc 8644 :set (lambda (sym val) 8645 (lsp--set-custom-property sym val ,path)) 8646 ,@args)))) 8647 8648 (defun lsp--set-custom-property (sym val path) 8649 (set sym val) 8650 (let ((section (cl-first (s-split "\\." path)))) 8651 (mapc (lambda (workspace) 8652 (when (-contains? (lsp--client-synchronize-sections (lsp--workspace-client workspace)) 8653 section) 8654 (with-lsp-workspace workspace 8655 (lsp--set-configuration (lsp-configuration-section section))))) 8656 (lsp--session-workspaces (lsp-session))))) 8657 8658 (defun lsp-configuration-section (section) 8659 "Get settings for SECTION." 8660 (let ((ret (ht-create))) 8661 (maphash (-lambda (path (variable boolean?)) 8662 (when (s-matches? (concat (regexp-quote section) "\\..*") path) 8663 (let* ((symbol-value (-> variable 8664 lsp-resolve-value 8665 lsp-resolve-value)) 8666 (value (if (and boolean? (not symbol-value)) 8667 :json-false 8668 symbol-value))) 8669 (when (or boolean? value) 8670 (lsp-ht-set ret (s-split "\\." path) value))))) 8671 lsp-client-settings) 8672 ret)) 8673 8674 8675 (defun lsp--start-connection (session client project-root) 8676 "Initiates connection created from CLIENT for PROJECT-ROOT. 8677 SESSION is the active session." 8678 (when (lsp--client-multi-root client) 8679 (cl-pushnew project-root (gethash (lsp--client-server-id client) 8680 (lsp-session-server-id->folders session)))) 8681 (run-hook-with-args 'lsp-workspace-folders-changed-functions (list project-root) nil) 8682 8683 (unwind-protect 8684 (lsp--start-workspace session client project-root (lsp--create-initialization-options session client)) 8685 (lsp--spinner-stop))) 8686 8687 ;; lsp-log-io-mode 8688 8689 (defvar lsp-log-io-mode-map 8690 (let ((map (make-sparse-keymap))) 8691 (define-key map (kbd "M-n") #'lsp-log-io-next) 8692 (define-key map (kbd "M-p") #'lsp-log-io-prev) 8693 (define-key map (kbd "k") #'lsp--erase-log-buffer) 8694 (define-key map (kbd "K") #'lsp--erase-session-log-buffers) 8695 map) 8696 "Keymap for lsp log buffer mode.") 8697 8698 (define-derived-mode lsp-log-io-mode special-mode "LspLogIo" 8699 "Special mode for viewing IO logs.") 8700 8701 (defun lsp-workspace-show-log (workspace) 8702 "Display the log buffer of WORKSPACE." 8703 (interactive 8704 (list (if lsp-log-io 8705 (if (eq (length (lsp-workspaces)) 1) 8706 (cl-first (lsp-workspaces)) 8707 (lsp--completing-read "Workspace: " (lsp-workspaces) 8708 #'lsp--workspace-print nil t)) 8709 (user-error "IO logging is disabled")))) 8710 (pop-to-buffer (lsp--get-log-buffer-create workspace))) 8711 8712 (defalias 'lsp-switch-to-io-log-buffer 'lsp-workspace-show-log) 8713 8714 (defun lsp--get-log-buffer-create (workspace) 8715 "Return the lsp log buffer of WORKSPACE, creating a new one if needed." 8716 (let* ((server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8717 (pid (-> workspace lsp--workspace-cmd-proc lsp-process-id))) 8718 (get-buffer-create (format "*lsp-log: %s:%s*" server-id pid)))) 8719 8720 (defun lsp--erase-log-buffer (&optional all) 8721 "Delete contents of current lsp log buffer. 8722 When ALL is t, erase all log buffers of the running session." 8723 (interactive) 8724 (let* ((workspaces (lsp--session-workspaces (lsp-session))) 8725 (current-log-buffer (current-buffer))) 8726 (dolist (w workspaces) 8727 (let ((b (lsp--get-log-buffer-create w))) 8728 (when (or all (eq b current-log-buffer)) 8729 (with-current-buffer b 8730 (let ((inhibit-read-only t)) 8731 (erase-buffer)))))))) 8732 8733 (defun lsp--erase-session-log-buffers () 8734 "Erase log buffers of the running session." 8735 (interactive) 8736 (lsp--erase-log-buffer t)) 8737 8738 (defun lsp-log-io-next (arg) 8739 "Move to next log entry." 8740 (interactive "P") 8741 (ewoc-goto-next lsp--log-io-ewoc (or arg 1))) 8742 8743 (defun lsp-log-io-prev (arg) 8744 "Move to previous log entry." 8745 (interactive "P") 8746 (ewoc-goto-prev lsp--log-io-ewoc (or arg 1))) 8747 8748 8749 8750 (cl-defmethod lsp-process-id ((process process)) 8751 (process-id process)) 8752 8753 (cl-defmethod lsp-process-name ((process process)) (process-name process)) 8754 8755 (cl-defmethod lsp-process-status ((process process)) (process-status process)) 8756 8757 (cl-defmethod lsp-process-kill ((process process)) 8758 (when (process-live-p process) 8759 (kill-process process))) 8760 8761 (cl-defmethod lsp-process-send ((process process) message) 8762 (condition-case err 8763 (process-send-string process (lsp--make-message message)) 8764 (error (lsp--error "Sending to process failed with the following error: %s" 8765 (error-message-string err))))) 8766 8767 (cl-defmethod lsp-process-cleanup (process) 8768 ;; Kill standard error buffer only if the process exited normally. 8769 ;; Leave it intact otherwise for debugging purposes. 8770 (let ((buffer (-> process process-name get-buffer))) 8771 (when (and (eq (process-status process) 'exit) 8772 (zerop (process-exit-status process)) 8773 (buffer-live-p buffer)) 8774 (kill-buffer buffer)))) 8775 8776 8777 ;; native JSONRPC 8778 8779 (declare-function json-rpc "ext:json") 8780 (declare-function json-rpc-connection "ext:json") 8781 (declare-function json-rpc-send "ext:json") 8782 (declare-function json-rpc-shutdown "ext:json") 8783 (declare-function json-rpc-stderr "ext:json") 8784 (declare-function json-rpc-pid "ext:json") 8785 8786 (defvar lsp-json-rpc-thread nil) 8787 (defvar lsp-json-rpc-queue nil) 8788 (defvar lsp-json-rpc-done nil) 8789 (defvar lsp-json-rpc-mutex (make-mutex)) 8790 (defvar lsp-json-rpc-condition (make-condition-variable lsp-json-rpc-mutex)) 8791 8792 (defun lsp-json-rpc-process-queue () 8793 (while (not lsp-json-rpc-done) 8794 (while lsp-json-rpc-queue 8795 (-let (((proc . message) (pop lsp-json-rpc-queue))) 8796 (json-rpc-send 8797 proc message 8798 :null-object nil 8799 :false-object :json-false))) 8800 (with-mutex lsp-json-rpc-mutex 8801 (condition-wait lsp-json-rpc-condition)))) 8802 8803 (cl-defmethod lsp-process-id (process) (json-rpc-pid process)) 8804 8805 (cl-defmethod lsp-process-name (_process) "TBD") 8806 8807 (cl-defmethod lsp-process-kill (process) (json-rpc-shutdown process)) 8808 8809 (cl-defmethod lsp-process-send (proc message) 8810 (unless lsp-json-rpc-thread 8811 (with-current-buffer (get-buffer-create " *json-rpc*") 8812 (setq lsp-json-rpc-thread (make-thread #'lsp-json-rpc-process-queue "*json-rpc-queue*")))) 8813 8814 (with-mutex lsp-json-rpc-mutex 8815 (setq lsp-json-rpc-queue (append lsp-json-rpc-queue 8816 (list (cons proc message)))) 8817 (condition-notify lsp-json-rpc-condition))) 8818 8819 (cl-defmethod lsp-process-cleanup (_proc)) 8820 8821 (defun lsp-json-rpc-connection (workspace command) 8822 (let ((con (apply #'json-rpc-connection command)) 8823 (object-type (if lsp-use-plists 'plist 'hash-table))) 8824 (with-current-buffer (get-buffer-create " *json-rpc*") 8825 (make-thread 8826 (lambda () 8827 (json-rpc 8828 con 8829 (lambda (result err done) 8830 (run-with-timer 8831 0.0 8832 nil 8833 (lambda () 8834 (cond 8835 (result (lsp--parser-on-message result workspace)) 8836 (err (warn "Json parsing failed with the following error: %s" err)) 8837 (done (lsp--handle-process-exit workspace "")))))) 8838 :object-type object-type 8839 :null-object nil 8840 :false-object nil)) 8841 "*json-rpc-connection*")) 8842 (cons con con))) 8843 8844 (defun lsp-json-rpc-stderr () 8845 (interactive) 8846 (--when-let (pcase (lsp-workspaces) 8847 (`nil (user-error "There are no active servers in the current buffer")) 8848 (`(,workspace) workspace) 8849 (workspaces (lsp--completing-read "Select server: " 8850 workspaces 8851 'lsp--workspace-print nil t))) 8852 (let ((content (json-rpc-stderr (lsp--workspace-cmd-proc it))) 8853 (buffer (format "*stderr-%s*" (lsp--workspace-print it)) )) 8854 (with-current-buffer (get-buffer-create buffer) 8855 (with-help-window buffer 8856 (insert content)))))) 8857 8858 8859 (defun lsp--workspace-print (workspace) 8860 "Visual representation WORKSPACE." 8861 (let* ((proc (lsp--workspace-cmd-proc workspace)) 8862 (status (lsp--workspace-status workspace)) 8863 (server-id (-> workspace lsp--workspace-client lsp--client-server-id symbol-name)) 8864 (pid (lsp-process-id proc))) 8865 8866 (if (eq 'initialized status) 8867 (format "%s:%s" server-id pid) 8868 (format "%s:%s/%s" server-id pid status)))) 8869 8870 (defun lsp--map-tree-widget (m) 8871 "Build `tree-widget' from a hash-table or plist M." 8872 (when (lsp-structure-p m) 8873 (let (nodes) 8874 (lsp-map (lambda (k v) 8875 (push `(tree-widget 8876 :tag ,(if (lsp-structure-p v) 8877 (format "%s:" k) 8878 (format "%s: %s" k 8879 (propertize (format "%s" v) 8880 'face 8881 'font-lock-string-face))) 8882 :open t 8883 ,@(lsp--map-tree-widget v)) 8884 nodes)) 8885 m) 8886 nodes))) 8887 8888 (defun lsp-buffer-name (buffer-id) 8889 (if-let ((buffer-name (plist-get buffer-id :buffer-name))) 8890 (funcall buffer-name buffer-id) 8891 (buffer-name buffer-id))) 8892 8893 (defun lsp--render-workspace (workspace) 8894 "Tree node representation of WORKSPACE." 8895 `(tree-widget :tag ,(lsp--workspace-print workspace) 8896 :open t 8897 (tree-widget :tag ,(propertize "Buffers" 'face 'font-lock-function-name-face) 8898 :open t 8899 ,@(->> workspace 8900 (lsp--workspace-buffers) 8901 (--map `(tree-widget 8902 :tag ,(when (lsp-buffer-live-p it) 8903 (let ((buffer-name (lsp-buffer-name it))) 8904 (if (lsp-with-current-buffer it buffer-read-only) 8905 (propertize buffer-name 'face 'font-lock-constant-face) 8906 buffer-name))))))) 8907 (tree-widget :tag ,(propertize "Capabilities" 'face 'font-lock-function-name-face) 8908 ,@(-> workspace lsp--workspace-server-capabilities lsp--map-tree-widget)))) 8909 8910 (define-derived-mode lsp-browser-mode special-mode "LspBrowser" 8911 "Define mode for displaying lsp sessions." 8912 (setq-local display-buffer-base-action '(nil . ((inhibit-same-window . t))))) 8913 8914 (defun lsp-describe-session () 8915 "Describes current `lsp-session'." 8916 (interactive) 8917 (let ((session (lsp-session)) 8918 (buf (get-buffer-create "*lsp session*")) 8919 (root (lsp-workspace-root))) 8920 (with-current-buffer buf 8921 (lsp-browser-mode) 8922 (let ((inhibit-read-only t)) 8923 (erase-buffer) 8924 (--each (lsp-session-folders session) 8925 (widget-create 8926 `(tree-widget 8927 :tag ,(propertize it 'face 'font-lock-keyword-face) 8928 :open t 8929 ,@(->> session 8930 (lsp-session-folder->servers) 8931 (gethash it) 8932 (-map 'lsp--render-workspace))))))) 8933 (pop-to-buffer buf) 8934 (goto-char (point-min)) 8935 (cl-loop for tag = (widget-get (widget-get (widget-at) :node) :tag) 8936 until (or (and root (string= tag root)) (eobp)) 8937 do (goto-char (next-overlay-change (point)))))) 8938 8939 (defun lsp--session-workspaces (session) 8940 "Get all workspaces that are part of the SESSION." 8941 (-> session lsp-session-folder->servers hash-table-values -flatten -uniq)) 8942 8943 (defun lsp--find-multiroot-workspace (session client project-root) 8944 "Look for a multiroot connection in SESSION created from CLIENT for 8945 PROJECT-ROOT and BUFFER-MAJOR-MODE." 8946 (when (lsp--client-multi-root client) 8947 (-when-let (multi-root-workspace (->> session 8948 (lsp--session-workspaces) 8949 (--first (eq (-> it lsp--workspace-client lsp--client-server-id) 8950 (lsp--client-server-id client))))) 8951 (with-lsp-workspace multi-root-workspace 8952 (lsp-notify "workspace/didChangeWorkspaceFolders" 8953 (lsp-make-did-change-workspace-folders-params 8954 :event (lsp-make-workspace-folders-change-event 8955 :added (vector (lsp-make-workspace-folder 8956 :uri (lsp--path-to-uri project-root) 8957 :name (f-filename project-root))) 8958 :removed [])))) 8959 8960 (->> session (lsp-session-folder->servers) (gethash project-root) (cl-pushnew multi-root-workspace)) 8961 (->> session (lsp-session-server-id->folders) (gethash (lsp--client-server-id client)) (cl-pushnew project-root)) 8962 8963 (lsp--persist-session session) 8964 8965 (lsp--info "Opened folder %s in workspace %s" project-root (lsp--workspace-print multi-root-workspace)) 8966 (lsp--open-in-workspace multi-root-workspace) 8967 8968 multi-root-workspace))) 8969 8970 (defun lsp--ensure-lsp-servers (session clients project-root ignore-multi-folder) 8971 "Ensure that SESSION contain server CLIENTS created for PROJECT-ROOT. 8972 IGNORE-MULTI-FOLDER to ignore multi folder server." 8973 (-map (lambda (client) 8974 (or 8975 (lsp--find-workspace session client project-root) 8976 (unless ignore-multi-folder 8977 (lsp--find-multiroot-workspace session client project-root)) 8978 (lsp--start-connection session client project-root))) 8979 clients)) 8980 8981 (defun lsp--spinner-stop () 8982 "Stop the spinner in case all of the workspaces are started." 8983 (when (--all? (eq (lsp--workspace-status it) 'initialized) 8984 lsp--buffer-workspaces) 8985 (spinner-stop))) 8986 8987 (defun lsp--open-in-workspace (workspace) 8988 "Open in existing WORKSPACE." 8989 (if (eq 'initialized (lsp--workspace-status workspace)) 8990 ;; when workspace is initialized just call document did open. 8991 (progn 8992 (with-lsp-workspace workspace 8993 (when-let ((before-document-open-fn (-> workspace 8994 lsp--workspace-client 8995 lsp--client-before-file-open-fn))) 8996 (funcall before-document-open-fn workspace)) 8997 (lsp--text-document-did-open)) 8998 (lsp--spinner-stop)) 8999 ;; when it is not initialized 9000 (lsp--spinner-start) 9001 (cl-pushnew (lsp-current-buffer) (lsp--workspace-buffers workspace)))) 9002 9003 (defun lsp--find-workspace (session client project-root) 9004 "Find server connection created with CLIENT in SESSION for PROJECT-ROOT." 9005 (when-let ((workspace (->> session 9006 (lsp-session-folder->servers) 9007 (gethash project-root) 9008 (--first (eql (-> it lsp--workspace-client lsp--client-server-id) 9009 (lsp--client-server-id client)))))) 9010 (lsp--open-in-workspace workspace) 9011 workspace)) 9012 9013 (defun lsp--read-char (prompt &optional options) 9014 "Wrapper for `read-char-from-minibuffer' if Emacs +27. 9015 Fallback to `read-key' otherwise. 9016 PROMPT is the message and OPTIONS the available options." 9017 (if (fboundp 'read-char-from-minibuffer) 9018 (read-char-from-minibuffer prompt options) 9019 (read-key prompt))) 9020 9021 (defun lsp--find-root-interactively (session) 9022 "Find project interactively. 9023 Returns nil if the project should not be added to the current SESSION." 9024 (condition-case nil 9025 (let* ((project-root-suggestion (or (lsp--suggest-project-root) default-directory)) 9026 (action (lsp--read-char 9027 (format 9028 "%s is not part of any project. 9029 9030 %s ==> Import project root %s 9031 %s ==> Import project by selecting root directory interactively 9032 %s ==> Import project at current directory %s 9033 %s ==> Do not ask again for the current project by adding %s to lsp-session-folders-blocklist 9034 %s ==> Do not ask again for the current project by selecting ignore path interactively 9035 %s ==> Do nothing: ask again when opening other files from the current project 9036 9037 Select action: " 9038 (propertize (buffer-name) 'face 'bold) 9039 (propertize "i" 'face 'success) 9040 (propertize project-root-suggestion 'face 'bold) 9041 (propertize "I" 'face 'success) 9042 (propertize "." 'face 'success) 9043 (propertize default-directory 'face 'bold) 9044 (propertize "d" 'face 'warning) 9045 (propertize project-root-suggestion 'face 'bold) 9046 (propertize "D" 'face 'warning) 9047 (propertize "n" 'face 'warning)) 9048 '(?i ?\r ?I ?. ?d ?D ?n)))) 9049 (cl-case action 9050 (?i project-root-suggestion) 9051 (?\r project-root-suggestion) 9052 (?I (read-directory-name "Select workspace folder to add: " 9053 (or project-root-suggestion default-directory) 9054 nil 9055 t)) 9056 (?. default-directory) 9057 (?d (push project-root-suggestion (lsp-session-folders-blocklist session)) 9058 (lsp--persist-session session) 9059 nil) 9060 (?D (push (read-directory-name "Select folder to blocklist: " 9061 (or project-root-suggestion default-directory) 9062 nil 9063 t) 9064 (lsp-session-folders-blocklist session)) 9065 (lsp--persist-session session) 9066 nil) 9067 (t nil))) 9068 (quit))) 9069 9070 (declare-function tramp-file-name-host "ext:tramp" (file) t) 9071 (declare-function tramp-dissect-file-name "ext:tramp" (file &optional nodefault)) 9072 9073 (defun lsp--files-same-host (f1 f2) 9074 "Predicate on whether or not two files are on the same host." 9075 (or (not (or (file-remote-p f1) (file-remote-p f2))) 9076 (and (file-remote-p f1) 9077 (file-remote-p f2) 9078 (progn (require 'tramp) 9079 (equal (tramp-file-name-host (tramp-dissect-file-name f1)) 9080 (tramp-file-name-host (tramp-dissect-file-name f2))))))) 9081 9082 (defun lsp-find-session-folder (session file-name) 9083 "Look in the current SESSION for folder containing FILE-NAME." 9084 (let ((file-name-canonical (lsp-f-canonical file-name))) 9085 (->> session 9086 (lsp-session-folders) 9087 (--filter (and (lsp--files-same-host it file-name-canonical) 9088 (or (lsp-f-same? it file-name-canonical) 9089 (and (f-dir? it) 9090 (lsp-f-ancestor-of? it file-name-canonical))))) 9091 (--max-by (> (length it) 9092 (length other)))))) 9093 9094 (defun lsp-find-workspace (server-id &optional file-name) 9095 "Find workspace for SERVER-ID for FILE-NAME." 9096 (-when-let* ((session (lsp-session)) 9097 (folder->servers (lsp-session-folder->servers session)) 9098 (workspaces (if file-name 9099 (gethash (lsp-find-session-folder session file-name) folder->servers) 9100 (lsp--session-workspaces session)))) 9101 9102 (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) workspaces))) 9103 9104 (defun lsp--calculate-root (session file-name) 9105 "Calculate project root for FILE-NAME in SESSION." 9106 (and 9107 (->> session 9108 (lsp-session-folders-blocklist) 9109 (--first (and (lsp--files-same-host it file-name) 9110 (lsp-f-ancestor-of? it file-name) 9111 (prog1 t 9112 (lsp--info "File %s is in blocklisted directory %s" file-name it)))) 9113 not) 9114 (or 9115 (when lsp-auto-guess-root 9116 (lsp--suggest-project-root)) 9117 (unless lsp-guess-root-without-session 9118 (lsp-find-session-folder session file-name)) 9119 (unless lsp-auto-guess-root 9120 (when-let ((root-folder (lsp--find-root-interactively session))) 9121 (if (or (not (f-equal? root-folder (expand-file-name "~/"))) 9122 (yes-or-no-p 9123 (concat 9124 (propertize "[WARNING] " 'face 'warning) 9125 "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: 9126 9127 1. Use `I' option from the interactive project import to select subfolder(e. g. `~/foo/bar' instead of `~/'). 9128 2. If your file is under `~/' then create a subfolder and move that file in this folder. 9129 9130 Type `No' to go back to project selection. 9131 Type `Yes' to confirm `HOME' as project root. 9132 Type `C-g' to cancel project import process and stop `lsp'"))) 9133 root-folder 9134 (lsp--calculate-root session file-name))))))) 9135 9136 (defun lsp--try-open-in-library-workspace () 9137 "Try opening current file as library file in any of the active workspace. 9138 The library folders are defined by each client for each of the active workspace." 9139 (when-let ((workspace (->> (lsp-session) 9140 (lsp--session-workspaces) 9141 ;; Sort the last active workspaces first as they are more likely to be 9142 ;; the correct ones, especially when jumping to a definition. 9143 (-sort (lambda (a _b) 9144 (-contains? lsp--last-active-workspaces a))) 9145 (--first 9146 (and (-> it lsp--workspace-client lsp--supports-buffer?) 9147 (when-let ((library-folders-fn 9148 (-> it lsp--workspace-client lsp--client-library-folders-fn))) 9149 (-first (lambda (library-folder) 9150 (lsp-f-ancestor-of? library-folder (buffer-file-name))) 9151 (funcall library-folders-fn it)))))))) 9152 (lsp--open-in-workspace workspace) 9153 (view-mode t) 9154 (lsp--info "Opening read-only library file %s." (buffer-file-name)) 9155 (list workspace))) 9156 9157 (defun lsp--persist-session (session) 9158 "Persist SESSION to `lsp-session-file'." 9159 (lsp--persist lsp-session-file (make-lsp-session 9160 :folders (lsp-session-folders session) 9161 :folders-blocklist (lsp-session-folders-blocklist session) 9162 :server-id->folders (lsp-session-server-id->folders session)))) 9163 9164 (defun lsp--try-project-root-workspaces (ask-for-client ignore-multi-folder) 9165 "Try create opening file as a project file. 9166 When IGNORE-MULTI-FOLDER is t the lsp mode will start new 9167 language server even if there is language server which can handle 9168 current language. When IGNORE-MULTI-FOLDER is nil current file 9169 will be opened in multi folder language server if there is 9170 such." 9171 (-let ((session (lsp-session))) 9172 (-if-let (clients (if ask-for-client 9173 (list (lsp--completing-read "Select server to start: " 9174 (ht-values lsp-clients) 9175 (-compose 'symbol-name 'lsp--client-server-id) nil t)) 9176 (lsp--find-clients))) 9177 (-if-let (project-root (-some-> session 9178 (lsp--calculate-root (buffer-file-name)) 9179 (lsp-f-canonical))) 9180 (progn 9181 ;; update project roots if needed and persist the lsp session 9182 (unless (-contains? (lsp-session-folders session) project-root) 9183 (cl-pushnew project-root (lsp-session-folders session)) 9184 (lsp--persist-session session)) 9185 (lsp--ensure-lsp-servers session clients project-root ignore-multi-folder)) 9186 (lsp--warn "%s not in project or it is blocklisted." (buffer-name)) 9187 nil) 9188 (lsp--warn "No LSP server for %s(check *lsp-log*)." major-mode) 9189 nil))) 9190 9191 (defun lsp-shutdown-workspace () 9192 "Shutdown language server." 9193 (interactive) 9194 (--when-let (pcase (lsp-workspaces) 9195 (`nil (user-error "There are no active servers in the current buffer")) 9196 (`(,workspace) (when (y-or-n-p (format "Are you sure you want to stop the server %s?" 9197 (lsp--workspace-print workspace))) 9198 workspace)) 9199 (workspaces (lsp--completing-read "Select server: " 9200 workspaces 9201 'lsp--workspace-print nil t))) 9202 (lsp-workspace-shutdown it))) 9203 9204 (make-obsolete 'lsp-shutdown-workspace 'lsp-workspace-shutdown "lsp-mode 6.1") 9205 9206 (defcustom lsp-auto-select-workspace t 9207 "Shutdown or restart a single workspace. 9208 If set and the current buffer has only a single workspace 9209 associated with it, `lsp-shutdown-workspace' and 9210 `lsp-restart-workspace' will act on it without asking." 9211 :type 'boolean 9212 :group 'lsp-mode) 9213 9214 (defun lsp--read-workspace () 9215 "Ask the user to select a workspace. 9216 Errors if there are none." 9217 (pcase (lsp-workspaces) 9218 (`nil (error "No workspaces associated with the current buffer")) 9219 ((and `(,workspace) (guard lsp-auto-select-workspace)) workspace) 9220 (workspaces (lsp--completing-read "Select workspace: " workspaces 9221 #'lsp--workspace-print nil t)))) 9222 9223 (defun lsp-workspace-shutdown (workspace) 9224 "Shut the workspace WORKSPACE and the language server associated with it" 9225 (interactive (list (lsp--read-workspace))) 9226 (lsp--warn "Stopping %s" (lsp--workspace-print workspace)) 9227 (with-lsp-workspace workspace (lsp--shutdown-workspace))) 9228 9229 (defun lsp-disconnect () 9230 "Disconnect the buffer from the language server." 9231 (interactive) 9232 (lsp--text-document-did-close t) 9233 (lsp-managed-mode -1) 9234 (lsp-mode -1) 9235 (setq lsp--buffer-workspaces nil) 9236 (lsp--info "Disconnected")) 9237 9238 (defun lsp-restart-workspace () 9239 (interactive) 9240 (--when-let (pcase (lsp-workspaces) 9241 (`nil (user-error "There are no active servers in the current buffer")) 9242 (`(,workspace) workspace) 9243 (workspaces (lsp--completing-read "Select server: " 9244 workspaces 9245 'lsp--workspace-print nil t))) 9246 (lsp-workspace-restart it))) 9247 9248 (make-obsolete 'lsp-restart-workspace 'lsp-workspace-restart "lsp-mode 6.1") 9249 9250 (defun lsp-workspace-restart (workspace) 9251 "Restart the workspace WORKSPACE and the language server associated with it" 9252 (interactive (list (lsp--read-workspace))) 9253 (lsp--warn "Restarting %s" (lsp--workspace-print workspace)) 9254 (with-lsp-workspace workspace (lsp--shutdown-workspace t))) 9255 9256 ;;;###autoload 9257 (defun lsp (&optional arg) 9258 "Entry point for the server startup. 9259 When ARG is t the lsp mode will start new language server even if 9260 there is language server which can handle current language. When 9261 ARG is nil current file will be opened in multi folder language 9262 server if there is such. When `lsp' is called with prefix 9263 argument ask the user to select which language server to start." 9264 (interactive "P") 9265 9266 (lsp--require-packages) 9267 9268 (when (buffer-file-name) 9269 (let (clients 9270 (matching-clients (lsp--filter-clients 9271 (-andfn #'lsp--supports-buffer? 9272 #'lsp--server-binary-present?)))) 9273 (cond 9274 (matching-clients 9275 (when (setq lsp--buffer-workspaces 9276 (or (and 9277 ;; Don't open as library file if file is part of a project. 9278 (not (lsp-find-session-folder (lsp-session) (buffer-file-name))) 9279 (lsp--try-open-in-library-workspace)) 9280 (lsp--try-project-root-workspaces (equal arg '(4)) 9281 (and arg (not (equal arg 1)))))) 9282 (lsp-mode 1) 9283 (when lsp-auto-configure (lsp--auto-configure)) 9284 (setq lsp-buffer-uri (lsp--buffer-uri)) 9285 (lsp--info "Connected to %s." 9286 (apply 'concat (--map (format "[%s %s]" 9287 (lsp--workspace-print it) 9288 (lsp--workspace-root it)) 9289 lsp--buffer-workspaces))))) 9290 ;; look for servers which are currently being downloaded. 9291 ((setq clients (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9292 #'lsp--client-download-in-progress?))) 9293 (lsp--info "There are language server(%s) installation in progress. 9294 The server(s) will be started in the buffer when it has finished." 9295 (-map #'lsp--client-server-id clients)) 9296 (seq-do (lambda (client) 9297 (cl-pushnew (current-buffer) (lsp--client-buffers client))) 9298 clients)) 9299 ;; look for servers to install 9300 ((setq clients (lsp--filter-clients 9301 (-andfn #'lsp--supports-buffer? 9302 (-const lsp-enable-suggest-server-download) 9303 #'lsp--client-download-server-fn 9304 (-not #'lsp--client-download-in-progress?)))) 9305 (let ((client (lsp--completing-read 9306 (concat "Unable to find installed server supporting this file. " 9307 "The following servers could be installed automatically: ") 9308 clients 9309 (-compose #'symbol-name #'lsp--client-server-id) 9310 nil 9311 t))) 9312 (cl-pushnew (current-buffer) (lsp--client-buffers client)) 9313 (lsp--install-server-internal client))) 9314 ;; ignore other warnings 9315 ((not lsp-warn-no-matched-clients) 9316 nil) 9317 ;; automatic installation disabled 9318 ((setq clients (unless matching-clients 9319 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9320 #'lsp--client-download-server-fn 9321 (-not (-const lsp-enable-suggest-server-download)) 9322 (-not #'lsp--server-binary-present?))))) 9323 (lsp--warn "The following servers support current file but automatic download is disabled: %s 9324 \(If you have already installed the server check *lsp-log*)." 9325 (mapconcat (lambda (client) 9326 (symbol-name (lsp--client-server-id client))) 9327 clients 9328 " "))) 9329 ;; no clients present 9330 ((setq clients (unless matching-clients 9331 (lsp--filter-clients (-andfn #'lsp--supports-buffer? 9332 (-not #'lsp--server-binary-present?))))) 9333 (lsp--warn "The following servers support current file but do not have automatic installation: %s 9334 You may find the installation instructions at https://emacs-lsp.github.io/lsp-mode/page/languages. 9335 \(If you have already installed the server check *lsp-log*)." 9336 (mapconcat (lambda (client) 9337 (symbol-name (lsp--client-server-id client))) 9338 clients 9339 " "))) 9340 ;; no matches 9341 ((-> #'lsp--supports-buffer? lsp--filter-clients not) 9342 (lsp--error "There are no language servers supporting current mode `%s' registered with `lsp-mode'. 9343 This issue might be caused by: 9344 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'. 9345 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'. 9346 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/ . 9347 4. You are over `tramp'. In this case follow https://emacs-lsp.github.io/lsp-mode/page/remote/. 9348 5. You have disabled the `lsp-mode' clients for that file. (Check `lsp-enabled-clients' and `lsp-disabled-clients'). 9349 You can customize `lsp-warn-no-matched-clients' to disable this message." 9350 major-mode major-mode major-mode)))))) 9351 9352 (defun lsp--buffer-visible-p () 9353 "Return non nil if current buffer is visible." 9354 (or (buffer-modified-p) (get-buffer-window nil t))) 9355 9356 (defun lsp--init-if-visible () 9357 "Run `lsp' for the current buffer if the buffer is visible. 9358 Returns non nil if `lsp' was run for the buffer." 9359 (when (lsp--buffer-visible-p) 9360 (remove-hook 'window-configuration-change-hook #'lsp--init-if-visible t) 9361 (lsp) 9362 t)) 9363 9364 ;;;###autoload 9365 (defun lsp-deferred () 9366 "Entry point that defers server startup until buffer is visible. 9367 `lsp-deferred' will wait until the buffer is visible before invoking `lsp'. 9368 This avoids overloading the server with many files when starting Emacs." 9369 ;; Workspace may not be initialized yet. Use a buffer local variable to 9370 ;; remember that we deferred loading of this buffer. 9371 (setq lsp--buffer-deferred t) 9372 (let ((buffer (current-buffer))) 9373 ;; Avoid false positives as desktop-mode restores buffers by deferring 9374 ;; visibility check until the stack clears. 9375 (run-with-idle-timer 0 nil (lambda () 9376 (when (buffer-live-p buffer) 9377 (with-current-buffer buffer 9378 (unless (lsp--init-if-visible) 9379 (add-hook 'window-configuration-change-hook #'lsp--init-if-visible nil t)))))))) 9380 9381 9382 9383 (defvar lsp-file-truename-cache (ht)) 9384 9385 (defmacro lsp-with-cached-filetrue-name (&rest body) 9386 "Executes BODY caching the `file-truename' calls." 9387 `(let ((old-fn (symbol-function 'file-truename))) 9388 (unwind-protect 9389 (progn 9390 (fset 'file-truename 9391 (lambda (file-name &optional counter prev-dirs) 9392 (or (gethash file-name lsp-file-truename-cache) 9393 (puthash file-name (apply old-fn (list file-name counter prev-dirs)) 9394 lsp-file-truename-cache)))) 9395 ,@body) 9396 (fset 'file-truename old-fn)))) 9397 9398 9399 (defun lsp-virtual-buffer-call (key &rest args) 9400 (when lsp--virtual-buffer 9401 (when-let ((fn (plist-get lsp--virtual-buffer key))) 9402 (apply fn args)))) 9403 9404 (defun lsp-translate-column (column) 9405 "Translate COLUMN taking into account virtual buffers." 9406 (or (lsp-virtual-buffer-call :real->virtual-char column) 9407 column)) 9408 9409 (defun lsp-translate-line (line) 9410 "Translate LINE taking into account virtual buffers." 9411 (or (lsp-virtual-buffer-call :real->virtual-line line) 9412 line)) 9413 9414 9415 ;; lsp internal validation. 9416 9417 (defmacro lsp--doctor (&rest checks) 9418 `(-let [buf (current-buffer)] 9419 (with-current-buffer (get-buffer-create "*lsp-performance*") 9420 (with-help-window (current-buffer) 9421 ,@(-map (-lambda ((msg form)) 9422 `(insert (format "%s: %s\n" ,msg 9423 (let ((res (with-current-buffer buf 9424 ,form))) 9425 (cond 9426 ((eq res :optional) (propertize "OPTIONAL" 'face 'warning)) 9427 (res (propertize "OK" 'face 'success)) 9428 (t (propertize "ERROR" 'face 'error))))))) 9429 (-partition 2 checks)))))) 9430 9431 (define-obsolete-function-alias 'lsp-diagnose 9432 'lsp-doctor "lsp-mode 8.0.0") 9433 9434 (defun lsp-doctor () 9435 "Validate performance settings." 9436 (interactive) 9437 (lsp--doctor 9438 "Checking for Native JSON support" (functionp 'json-serialize) 9439 "Check emacs supports `read-process-output-max'" (boundp 'read-process-output-max) 9440 "Check `read-process-output-max' default has been changed from 4k" 9441 (and (boundp 'read-process-output-max) 9442 (> read-process-output-max 4096)) 9443 "Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available)" 9444 (condition-case _err 9445 (progn (lsp--make-message (list "a" "b")) 9446 nil) 9447 (error t)) 9448 "`gc-cons-threshold' increased?" (> gc-cons-threshold 800000) 9449 "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) 9450 "Using emacs 28+ with native compilation?" 9451 (or (and (fboundp 'native-comp-available-p) 9452 (native-comp-available-p)) 9453 :optional))) 9454 9455 (declare-function package-version-join "ext:package") 9456 (declare-function package-desc-version "ext:package") 9457 (declare-function package--alist "ext:package") 9458 9459 (defun lsp-version () 9460 "Return string describing current version of `lsp-mode'." 9461 (interactive) 9462 (unless (featurep 'package) 9463 (require 'package)) 9464 (let ((ver (format "lsp-mode %s, Emacs %s, %s" 9465 (package-version-join 9466 (package-desc-version 9467 (car (alist-get 'lsp-mode (package--alist))))) 9468 emacs-version 9469 system-type))) 9470 (if (called-interactively-p 'interactive) 9471 (lsp--info "%s" ver) 9472 ver))) 9473 9474 9475 9476 ;; org-mode/virtual-buffer 9477 9478 (declare-function org-babel-get-src-block-info "ext:ob-core") 9479 (declare-function org-do-remove-indentation "ext:org-macs") 9480 (declare-function org-src-get-lang-mode "ext:org-src") 9481 (declare-function org-element-context "ext:org-element") 9482 9483 (defun lsp--virtual-buffer-update-position () 9484 (-if-let (virtual-buffer (-first (-lambda ((&plist :in-range)) 9485 (funcall in-range)) 9486 lsp--virtual-buffer-connections)) 9487 (unless (equal virtual-buffer lsp--virtual-buffer) 9488 (lsp-org)) 9489 (when lsp-managed-mode 9490 (lsp-managed-mode -1) 9491 (lsp-mode -1) 9492 (setq lsp--buffer-workspaces nil) 9493 (setq lsp--virtual-buffer nil) 9494 (setq lsp-buffer-uri nil) 9495 9496 ;; force refresh of diagnostics 9497 (run-hooks 'lsp-after-diagnostics-hook)))) 9498 9499 (defun lsp-virtual-buffer-on-change (start end length) 9500 "Adjust on change event to be executed against the proper language server." 9501 (let ((max-point (max end 9502 (or (plist-get lsp--before-change-vals :end) 0) 9503 (+ start length)))) 9504 (when-let ((virtual-buffer (-first (lambda (vb) 9505 (let ((lsp--virtual-buffer vb)) 9506 (and (lsp-virtual-buffer-call :in-range start) 9507 (lsp-virtual-buffer-call :in-range max-point)))) 9508 lsp--virtual-buffer-connections))) 9509 (lsp-with-current-buffer virtual-buffer 9510 (lsp-on-change start end length 9511 (lambda (&rest _) 9512 (list :range (lsp--range (list :character 0 :line 0) 9513 lsp--virtual-buffer-point-max) 9514 :text (lsp--buffer-content)))))))) 9515 9516 (defun lsp-virtual-buffer-before-change (start _end) 9517 (when-let ((virtual-buffer (-first (lambda (vb) 9518 (lsp-with-current-buffer vb 9519 (lsp-virtual-buffer-call :in-range start))) 9520 lsp--virtual-buffer-connections))) 9521 (lsp-with-current-buffer virtual-buffer 9522 (setq lsp--virtual-buffer-point-max 9523 (lsp--point-to-position (lsp-virtual-buffer-call :last-point)))))) 9524 9525 (defun lsp-patch-on-change-event () 9526 (remove-hook 'after-change-functions #'lsp-on-change t) 9527 (add-hook 'after-change-functions #'lsp-virtual-buffer-on-change nil t) 9528 (add-hook 'before-change-functions #'lsp-virtual-buffer-before-change nil t)) 9529 9530 (defun lsp-kill-virtual-buffers () 9531 (mapc #'lsp-virtual-buffer-disconnect lsp--virtual-buffer-connections)) 9532 9533 (defun lsp--move-point-in-indentation (point indentation) 9534 (save-excursion 9535 (goto-char point) 9536 (if (<= point (+ (line-beginning-position) indentation)) 9537 (line-beginning-position) 9538 point))) 9539 9540 (declare-function flycheck-checker-supports-major-mode-p "ext:flycheck") 9541 (declare-function flycheck-add-mode "ext:flycheck") 9542 (declare-function lsp-diagnostics-lsp-checker-if-needed "lsp-diagnostics") 9543 9544 (defalias 'lsp-client-download-server-fn 'lsp--client-download-server-fn) 9545 9546 (defun lsp-flycheck-add-mode (mode) 9547 "Register flycheck support for MODE." 9548 (lsp-diagnostics-lsp-checker-if-needed) 9549 (unless (flycheck-checker-supports-major-mode-p 'lsp mode) 9550 (flycheck-add-mode 'lsp mode))) 9551 9552 (defun lsp-progress-spinner-type () 9553 "Retrieve the spinner type value, if value is not a symbol of `spinner-types 9554 defaults to `progress-bar." 9555 (or (car (assoc lsp-progress-spinner-type spinner-types)) 'progress-bar)) 9556 9557 (defun lsp-org () 9558 (interactive) 9559 (-if-let ((virtual-buffer &as &plist :workspaces) (-first (-lambda ((&plist :in-range)) 9560 (funcall in-range)) 9561 lsp--virtual-buffer-connections)) 9562 (unless (equal lsp--virtual-buffer virtual-buffer) 9563 (setq lsp--buffer-workspaces workspaces) 9564 (setq lsp--virtual-buffer virtual-buffer) 9565 (setq lsp-buffer-uri nil) 9566 (lsp-mode 1) 9567 (lsp-managed-mode 1) 9568 (lsp-patch-on-change-event)) 9569 9570 (save-excursion 9571 (-let* (virtual-buffer 9572 (wcb (lambda (f) 9573 (with-current-buffer (plist-get virtual-buffer :buffer) 9574 (-let* (((&plist :major-mode :buffer-file-name 9575 :goto-buffer :workspaces) virtual-buffer) 9576 (lsp--virtual-buffer virtual-buffer) 9577 (lsp--buffer-workspaces workspaces)) 9578 (save-excursion 9579 (funcall goto-buffer) 9580 (funcall f)))))) 9581 ((&plist :begin :end :post-blank :language) (cl-second (org-element-context))) 9582 ((&alist :tangle file-name) (cl-third (org-babel-get-src-block-info 'light))) 9583 9584 (file-name (if file-name 9585 (f-expand file-name) 9586 (user-error "You should specify file name in the src block header."))) 9587 (begin-marker (progn 9588 (goto-char begin) 9589 (forward-line) 9590 (set-marker (make-marker) (point)))) 9591 (end-marker (progn 9592 (goto-char end) 9593 (forward-line (1- (- post-blank))) 9594 (set-marker (make-marker) (1+ (point))))) 9595 (buf (current-buffer)) 9596 (src-block (buffer-substring-no-properties begin-marker 9597 (1- end-marker))) 9598 (indentation (with-temp-buffer 9599 (insert src-block) 9600 9601 (goto-char (point-min)) 9602 (let ((indentation (current-indentation))) 9603 (plist-put lsp--virtual-buffer :indentation indentation) 9604 (org-do-remove-indentation) 9605 (goto-char (point-min)) 9606 (- indentation (current-indentation)))))) 9607 (add-hook 'post-command-hook #'lsp--virtual-buffer-update-position nil t) 9608 9609 (when (fboundp 'flycheck-add-mode) 9610 (lsp-flycheck-add-mode 'org-mode)) 9611 9612 (setq lsp--virtual-buffer 9613 (list 9614 :in-range (lambda (&optional point) 9615 (<= begin-marker (or point (point)) (1- end-marker))) 9616 :goto-buffer (lambda () (goto-char begin-marker)) 9617 :buffer-string 9618 (lambda () 9619 (let ((src-block (buffer-substring-no-properties 9620 begin-marker 9621 (1- end-marker)))) 9622 (with-temp-buffer 9623 (insert src-block) 9624 9625 (goto-char (point-min)) 9626 (while (not (eobp)) 9627 (delete-region (point) (if (> (+ (point) indentation) (line-end-position)) 9628 (line-end-position) 9629 (+ (point) indentation))) 9630 (forward-line)) 9631 (buffer-substring-no-properties (point-min) 9632 (point-max))))) 9633 :buffer buf 9634 :begin begin-marker 9635 :end end-marker 9636 :indentation indentation 9637 :last-point (lambda () (1- end-marker)) 9638 :cur-position (lambda () 9639 (lsp-save-restriction-and-excursion 9640 (list :line (- (lsp--cur-line) 9641 (lsp--cur-line begin-marker)) 9642 :character (let ((character (- (point) 9643 (line-beginning-position) 9644 indentation))) 9645 (if (< character 0) 9646 0 9647 character))))) 9648 :line/character->point (-lambda (line character) 9649 (-let [inhibit-field-text-motion t] 9650 (+ indentation 9651 (lsp-save-restriction-and-excursion 9652 (goto-char begin-marker) 9653 (forward-line line) 9654 (-let [line-end (line-end-position)] 9655 (if (> character (- line-end (point))) 9656 line-end 9657 (forward-char character) 9658 (point))))))) 9659 :major-mode (org-src-get-lang-mode language) 9660 :buffer-file-name file-name 9661 :buffer-uri (lsp--path-to-uri file-name) 9662 :with-current-buffer wcb 9663 :buffer-live? (lambda (_) (buffer-live-p buf)) 9664 :buffer-name (lambda (_) 9665 (propertize (format "%s(%s:%s)%s" 9666 (buffer-name buf) 9667 begin-marker 9668 end-marker 9669 language) 9670 'face 'italic)) 9671 :real->virtual-line (lambda (line) 9672 (+ line (line-number-at-pos begin-marker) -1)) 9673 :real->virtual-char (lambda (char) (+ char indentation)) 9674 :cleanup (lambda () 9675 (set-marker begin-marker nil) 9676 (set-marker end-marker nil)))) 9677 (setf virtual-buffer lsp--virtual-buffer) 9678 (puthash file-name virtual-buffer lsp--virtual-buffer-mappings) 9679 (push virtual-buffer lsp--virtual-buffer-connections) 9680 9681 ;; TODO: tangle only connected sections 9682 (add-hook 'after-save-hook 'org-babel-tangle nil t) 9683 (add-hook 'lsp-after-open-hook #'lsp-patch-on-change-event nil t) 9684 (add-hook 'kill-buffer-hook #'lsp-kill-virtual-buffers nil t) 9685 9686 (setq lsp--buffer-workspaces 9687 (lsp-with-current-buffer virtual-buffer 9688 (lsp) 9689 (plist-put virtual-buffer :workspaces (lsp-workspaces)) 9690 (lsp-workspaces))))))) 9691 9692 (defun lsp-virtual-buffer-disconnect (virtual-buffer) 9693 (interactive (list (or 9694 lsp--virtual-buffer 9695 (when lsp--virtual-buffer-connections 9696 (lsp--completing-read "Select virtual buffer to disconnect: " 9697 lsp--virtual-buffer-connections 9698 (-lambda ((&plist :buffer-file-name)) 9699 buffer-file-name)))))) 9700 (-if-let ((&plist :buffer-file-name file-name :cleanup) virtual-buffer) 9701 (progn 9702 (lsp-with-current-buffer virtual-buffer 9703 (lsp--text-document-did-close)) 9704 (setq lsp--virtual-buffer-connections (-remove-item virtual-buffer lsp--virtual-buffer-connections)) 9705 (when (eq virtual-buffer lsp--virtual-buffer) 9706 (setf lsp--virtual-buffer nil)) 9707 (when cleanup (funcall cleanup)) 9708 (remhash file-name lsp--virtual-buffer-mappings) 9709 9710 (lsp--virtual-buffer-update-position) 9711 (lsp--info "Disconnected from buffer %s" file-name)) 9712 (lsp--error "Nothing to disconnect from?"))) 9713 9714 9715 ;; inlay hints 9716 9717 (defface lsp-inlay-hint-face 9718 '((t :inherit font-lock-comment-face)) 9719 "The face to use for the JavaScript inlays." 9720 :group 'lsp-mode 9721 :package-version '(lsp-mode . "9.0.0")) 9722 9723 (defface lsp-inlay-hint-type-face 9724 '((t :inherit lsp-inlay-hint-face)) 9725 "Face for inlay type hints (e.g. inferred variable types)." 9726 :group 'lsp-mode 9727 :package-version '(lsp-mode . "9.0.0")) 9728 9729 (defcustom lsp-inlay-hint-type-format "%s" 9730 "Format string for variable inlays (part of the inlay face)." 9731 :type '(string :tag "String") 9732 :group 'lsp-mode 9733 :package-version '(lsp-mode . "9.0.0")) 9734 9735 (defface lsp-inlay-hint-parameter-face 9736 '((t :inherit lsp-inlay-hint-face)) 9737 "Face for inlay parameter hints (e.g. function parameter names at 9738 call-site)." 9739 :group 'lsp-mode 9740 :package-version '(lsp-mode . "9.0.0")) 9741 9742 (defcustom lsp-inlay-hint-param-format "%s" 9743 "Format string for parameter inlays (part of the inlay face)." 9744 :type '(string :tag "String") 9745 :group 'lsp-mode 9746 :package-version '(lsp-mode . "9.0.0")) 9747 9748 (defcustom lsp-update-inlay-hints-on-scroll t 9749 "If non-nil update inlay hints immediately when scrolling or 9750 modifying window sizes." 9751 :type 'boolean 9752 :package-version '(lsp-mode . "9.0.0")) 9753 9754 (defun lsp--format-inlay (text kind) 9755 (cond 9756 ((eql kind lsp/inlay-hint-kind-type-hint) (format lsp-inlay-hint-type-format text)) 9757 ((eql kind lsp/inlay-hint-kind-parameter-hint) (format lsp-inlay-hint-param-format text)) 9758 (t text))) 9759 9760 (defun lsp--face-for-inlay (kind) 9761 (cond 9762 ((eql kind lsp/inlay-hint-kind-type-hint) 'lsp-inlay-hint-type-face) 9763 ((eql kind lsp/inlay-hint-kind-parameter-hint) 'lsp-inlay-hint-parameter-face) 9764 (t 'lsp-inlay-hint-face))) 9765 9766 (defun lsp--update-inlay-hints-scroll-function (window start) 9767 (lsp-update-inlay-hints start (window-end window t))) 9768 9769 (defun lsp--update-inlay-hints () 9770 (lsp-update-inlay-hints (window-start) (window-end nil t))) 9771 9772 (defun lsp--label-from-inlay-hints-response (label) 9773 "Returns a string label built from an array of 9774 InlayHintLabelParts or the argument itself if it's already a 9775 string." 9776 (cl-typecase label 9777 (string label) 9778 (vector 9779 (string-join (mapcar (lambda (part) 9780 (-let (((&InlayHintLabelPart :value) part)) 9781 value)) 9782 label))))) 9783 9784 (defun lsp-update-inlay-hints (start end) 9785 (lsp-request-async 9786 "textDocument/inlayHint" 9787 (lsp-make-inlay-hints-params 9788 :text-document (lsp--text-document-identifier) 9789 :range (lsp-make-range :start 9790 (lsp-point-to-position start) 9791 :end 9792 (lsp-point-to-position end))) 9793 (lambda (res) 9794 (lsp--remove-overlays 'lsp-inlay-hint) 9795 (dolist (hint res) 9796 (-let* (((&InlayHint :label :position :kind? :padding-left? :padding-right?) hint) 9797 (kind (or kind? lsp/inlay-hint-kind-type-hint)) 9798 (label (lsp--label-from-inlay-hints-response label)) 9799 (pos (lsp--position-to-point position)) 9800 (overlay (make-overlay pos pos nil 'front-advance 'end-advance))) 9801 (when (stringp label) 9802 (overlay-put overlay 'lsp-inlay-hint t) 9803 (overlay-put overlay 'before-string 9804 (format "%s%s%s" 9805 (if padding-left? " " "") 9806 (propertize (lsp--format-inlay label kind) 9807 'font-lock-face (lsp--face-for-inlay kind)) 9808 (if padding-right? " " ""))))))) 9809 :mode 'tick)) 9810 9811 (define-minor-mode lsp-inlay-hints-mode 9812 "Mode for displaying inlay hints." 9813 :lighter nil 9814 (cond 9815 ((and lsp-inlay-hints-mode lsp--buffer-workspaces) 9816 (add-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints nil t) 9817 (when lsp-update-inlay-hints-on-scroll 9818 (add-to-list (make-local-variable 'window-scroll-functions) 9819 #'lsp--update-inlay-hints-scroll-function))) 9820 (t 9821 (lsp--remove-overlays 'lsp-inlay-hint) 9822 (remove-hook 'lsp-on-idle-hook #'lsp--update-inlay-hints t) 9823 (setf window-scroll-functions 9824 (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions))))) 9825 9826 9827 9828 ;;;###autoload 9829 (defun lsp-start-plain () 9830 "Start `lsp-mode' using minimal configuration using the latest `melpa' version 9831 of the packages. 9832 9833 In case the major-mode that you are using for " 9834 (interactive) 9835 (let ((start-plain (make-temp-file "plain" nil ".el"))) 9836 (url-copy-file "https://raw.githubusercontent.com/emacs-lsp/lsp-mode/master/scripts/lsp-start-plain.el" 9837 start-plain t) 9838 (async-shell-command 9839 (format "%s -q -l %s %s" 9840 (expand-file-name invocation-name invocation-directory) 9841 start-plain 9842 (or (buffer-file-name) "")) 9843 (generate-new-buffer " *lsp-start-plain*")))) 9844 9845 9846 9847 (provide 'lsp-mode) 9848 ;;; lsp-mode.el ends here