lsp-csharp.el (25541B)
1 ;;; lsp-csharp.el --- description -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2019 Jostein Kjønigsen, Saulius Menkevicius 4 5 ;; Author: Saulius Menkevicius <saulius.menkevicius@fastmail.com> 6 ;; Keywords: 7 8 ;; This program is free software; you can redistribute it and/or modify 9 ;; it under the terms of the GNU General Public License as published by 10 ;; the Free Software Foundation, either version 3 of the License, or 11 ;; (at your option) any later version. 12 13 ;; This program is distributed in the hope that it will be useful, 14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 ;; GNU General Public License for more details. 17 18 ;; You should have received a copy of the GNU General Public License 19 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 20 21 ;;; Commentary: 22 23 ;; lsp-csharp client 24 25 ;;; Code: 26 27 (require 'lsp-mode) 28 (require 'gnutls) 29 (require 'f) 30 31 (defgroup lsp-csharp nil 32 "LSP support for C#, using the Omnisharp Language Server. 33 Version 1.34.3 minimum is required." 34 :group 'lsp-mode 35 :link '(url-link "https://github.com/OmniSharp/omnisharp-roslyn")) 36 37 (defgroup lsp-csharp-omnisharp nil 38 "LSP support for C#, using the Omnisharp Language Server. 39 Version 1.34.3 minimum is required." 40 :group 'lsp-mode 41 :link '(url-link "https://github.com/OmniSharp/omnisharp-roslyn") 42 :package-version '(lsp-mode . "9.0.0")) 43 44 (defconst lsp-csharp--omnisharp-metadata-uri-re 45 "^file:///%24metadata%24/Project/\\(.+\\)/Assembly/\\(.+\\)/Symbol/\\(.+\\)\.cs$" 46 "Regular expression matching omnisharp's metadata uri. 47 Group 1 contains the Project name 48 Group 2 contains the Assembly name 49 Group 3 contains the Type name") 50 51 (defcustom lsp-csharp-server-install-dir 52 (f-join lsp-server-install-dir "omnisharp-roslyn/") 53 "Installation directory for OmniSharp Roslyn server." 54 :group 'lsp-csharp-omnisharp 55 :type 'directory) 56 57 (defcustom lsp-csharp-server-path 58 nil 59 "The path to the OmniSharp Roslyn language-server binary. 60 Set this if you have the binary installed or have it built yourself." 61 :group 'lsp-csharp-omnisharp 62 :type '(string :tag "Single string value or nil")) 63 64 (defcustom lsp-csharp-test-run-buffer-name 65 "*lsp-csharp test run*" 66 "The name of buffer used for outputting lsp-csharp test run results." 67 :group 'lsp-csharp-omnisharp 68 :type 'string) 69 70 (defcustom lsp-csharp-solution-file 71 nil 72 "Solution to load when starting the server. 73 Usually this is to be set in your .dir-locals.el on the project root directory." 74 :group 'lsp-csharp-omnisharp 75 :type 'string) 76 77 (defcustom lsp-csharp-omnisharp-roslyn-download-url 78 (concat "https://github.com/omnisharp/omnisharp-roslyn/releases/latest/download/" 79 (cond ((eq system-type 'windows-nt) 80 ; On Windows we're trying to avoid a crash starting 64bit .NET PE binaries in 81 ; Emacs by using x86 version of omnisharp-roslyn on older (<= 26.4) versions 82 ; of Emacs. See https://lists.nongnu.org/archive/html/bug-gnu-emacs/2017-06/msg00893.html" 83 (if (and (string-match "^x86_64-.*" system-configuration) 84 (version<= "26.4" emacs-version)) 85 "omnisharp-win-x64.zip" 86 "omnisharp-win-x86.zip")) 87 88 ((eq system-type 'darwin) 89 (if (string-match "aarch64-.*" system-configuration) 90 "omnisharp-osx-arm64-net6.0.zip" 91 "omnisharp-osx-x64-net6.0.zip")) 92 93 ((and (eq system-type 'gnu/linux) 94 (or (eq (string-match "^x86_64" system-configuration) 0) 95 (eq (string-match "^i[3-6]86" system-configuration) 0))) 96 "omnisharp-linux-x64-net6.0.zip") 97 98 (t "omnisharp-mono.zip"))) 99 "Automatic download url for omnisharp-roslyn." 100 :group 'lsp-csharp-omnisharp 101 :type 'string) 102 103 (defcustom lsp-csharp-omnisharp-roslyn-store-path 104 (f-join lsp-csharp-server-install-dir "latest" "omnisharp-roslyn.zip") 105 "The path where omnisharp-roslyn .zip archive will be stored." 106 :group 'lsp-csharp-omnisharp 107 :type 'file) 108 109 (defcustom lsp-csharp-omnisharp-roslyn-binary-path 110 (f-join lsp-csharp-server-install-dir "latest" (if (eq system-type 'windows-nt) 111 "OmniSharp.exe" 112 "OmniSharp")) 113 "The path where omnisharp-roslyn binary after will be stored." 114 :group 'lsp-csharp-omnisharp 115 :type 'file) 116 117 (defcustom lsp-csharp-omnisharp-roslyn-server-dir 118 (f-join lsp-csharp-server-install-dir "latest" "omnisharp-roslyn") 119 "The path where omnisharp-roslyn .zip archive will be extracted." 120 :group 'lsp-csharp-omnisharp 121 :type 'file) 122 123 (defcustom lsp-csharp-omnisharp-enable-decompilation-support 124 nil 125 "Decompile bytecode when browsing method metadata for types in assemblies. 126 Otherwise only declarations for the methods are visible (the default)." 127 :group 'lsp-csharp 128 :type 'boolean) 129 130 (lsp-dependency 131 'omnisharp-roslyn 132 `(:download :url lsp-csharp-omnisharp-roslyn-download-url 133 :decompress :zip 134 :store-path lsp-csharp-omnisharp-roslyn-store-path 135 :binary-path lsp-csharp-omnisharp-roslyn-binary-path 136 :set-executable? t) 137 '(:system "OmniSharp")) 138 139 (defun lsp-csharp--omnisharp-download-server (_client callback error-callback _update?) 140 "Download zip package for omnisharp-roslyn and install it. 141 Will invoke CALLBACK on success, ERROR-CALLBACK on error." 142 (lsp-package-ensure 'omnisharp-roslyn callback error-callback)) 143 144 (defun lsp-csharp--language-server-path () 145 "Resolve path to use to start the server." 146 (let ((executable-name (if (eq system-type 'windows-nt) 147 "OmniSharp.exe" 148 "OmniSharp"))) 149 (or (and lsp-csharp-server-path 150 (executable-find lsp-csharp-server-path)) 151 (executable-find executable-name) 152 (lsp-package-path 'omnisharp-roslyn)))) 153 154 (defun lsp-csharp-open-project-file () 155 "Open corresponding project file (.csproj) for the current file." 156 (interactive) 157 (-let* ((project-info-req (lsp-make-omnisharp-project-information-request :file-name (buffer-file-name))) 158 (project-info (lsp-request "o#/project" project-info-req)) 159 ((&omnisharp:ProjectInformation :ms-build-project) project-info) 160 ((&omnisharp:MsBuildProject :path) ms-build-project)) 161 (find-file path))) 162 163 (defun lsp-csharp--get-buffer-code-elements () 164 "Retrieve code structure by calling into the /v2/codestructure endpoint. 165 Returns :elements from omnisharp:CodeStructureResponse." 166 (-let* ((code-structure (lsp-request "o#/v2/codestructure" 167 (lsp-make-omnisharp-code-structure-request :file-name (buffer-file-name)))) 168 ((&omnisharp:CodeStructureResponse :elements) code-structure)) 169 elements)) 170 171 (defun lsp-csharp--inspect-code-elements-recursively (fn elements) 172 "Invoke FN for every omnisharp:CodeElement found recursively in ELEMENTS." 173 (seq-each 174 (lambda (el) 175 (funcall fn el) 176 (-let (((&omnisharp:CodeElement :children) el)) 177 (lsp-csharp--inspect-code-elements-recursively fn children))) 178 elements)) 179 180 (defun lsp-csharp--collect-code-elements-recursively (predicate elements) 181 "Flatten the omnisharp:CodeElement tree in ELEMENTS matching PREDICATE." 182 (let ((results nil)) 183 (lsp-csharp--inspect-code-elements-recursively (lambda (el) 184 (when (funcall predicate el) 185 (setq results (cons el results)))) 186 elements) 187 results)) 188 189 (lsp-defun lsp-csharp--l-c-within-range (l c (&omnisharp:Range :start :end)) 190 "Determine if L (line) and C (column) are within RANGE." 191 (-let* (((&omnisharp:Point :line start-l :column start-c) start) 192 ((&omnisharp:Point :line end-l :column end-c) end)) 193 (or (and (= l start-l) (>= c start-c) (or (> end-l start-l) (<= c end-c))) 194 (and (> l start-l) (< l end-l)) 195 (and (= l end-l) (<= c end-c))))) 196 197 (defun lsp-csharp--code-element-stack-on-l-c (l c elements) 198 "Return omnisharp:CodeElement stack at L (line) and C (column) in ELEMENTS tree." 199 (when-let ((matching-element (seq-find (lambda (el) 200 (-when-let* (((&omnisharp:CodeElement :ranges) el) 201 ((&omnisharp:RangeList :full?) ranges)) 202 (lsp-csharp--l-c-within-range l c full?))) 203 elements))) 204 (-let (((&omnisharp:CodeElement :children) matching-element)) 205 (cons matching-element (lsp-csharp--code-element-stack-on-l-c l c children))))) 206 207 (defun lsp-csharp--code-element-stack-at-point () 208 "Return omnisharp:CodeElement stack at point as a list." 209 (let ((pos-line (plist-get (lsp--cur-position) :line)) 210 (pos-col (plist-get (lsp--cur-position) :character))) 211 (lsp-csharp--code-element-stack-on-l-c pos-line 212 pos-col 213 (lsp-csharp--get-buffer-code-elements)))) 214 215 (lsp-defun lsp-csharp--code-element-test-method-p (element) 216 "Return test method name and test framework for a given ELEMENT." 217 (when element 218 (-when-let* (((&omnisharp:CodeElement :properties) element) 219 ((&omnisharp:CodeElementProperties :test-method-name? :test-framework?) properties)) 220 (list test-method-name? test-framework?)))) 221 222 (defun lsp-csharp--reset-test-buffer (present-buffer) 223 "Create new or reuse an existing test result output buffer. 224 PRESENT-BUFFER will make the buffer be presented to the user." 225 (with-current-buffer (get-buffer-create lsp-csharp-test-run-buffer-name) 226 (compilation-mode) 227 (read-only-mode) 228 (let ((inhibit-read-only t)) 229 (erase-buffer))) 230 231 (when present-buffer 232 (display-buffer lsp-csharp-test-run-buffer-name))) 233 234 (defun lsp-csharp--start-tests (test-method-framework test-method-names) 235 "Run test(s) identified by TEST-METHOD-NAMES using TEST-METHOD-FRAMEWORK." 236 (if (and test-method-framework test-method-names) 237 (let ((request-message (lsp-make-omnisharp-run-tests-in-class-request 238 :file-name (buffer-file-name) 239 :test-frameworkname test-method-framework 240 :method-names (vconcat test-method-names)))) 241 (lsp-csharp--reset-test-buffer t) 242 (lsp-session-set-metadata "last-test-method-framework" test-method-framework) 243 (lsp-session-set-metadata "last-test-method-names" test-method-names) 244 (lsp-request-async "o#/v2/runtestsinclass" 245 request-message 246 (-lambda ((&omnisharp:RunTestResponse)) 247 (message "lsp-csharp: Test run has started")))) 248 (message "lsp-csharp: No test methods to run"))) 249 250 (defun lsp-csharp--test-message (message) 251 "Emit a MESSAGE to lsp-csharp test run buffer." 252 (when-let ((existing-buffer (get-buffer lsp-csharp-test-run-buffer-name)) 253 (inhibit-read-only t)) 254 (with-current-buffer existing-buffer 255 (save-excursion 256 (goto-char (point-max)) 257 (insert message "\n"))))) 258 259 (defun lsp-csharp-run-test-at-point () 260 "Start test run at current point (if any)." 261 (interactive) 262 (let* ((stack (lsp-csharp--code-element-stack-at-point)) 263 (element-on-point (car (last stack))) 264 (test-method (lsp-csharp--code-element-test-method-p element-on-point)) 265 (test-method-name (car test-method)) 266 (test-method-framework (car (cdr test-method)))) 267 (lsp-csharp--start-tests test-method-framework (list test-method-name)))) 268 269 (defun lsp-csharp-run-all-tests-in-buffer () 270 "Run all test methods in the current buffer." 271 (interactive) 272 (let* ((elements (lsp-csharp--get-buffer-code-elements)) 273 (test-methods (lsp-csharp--collect-code-elements-recursively 'lsp-csharp--code-element-test-method-p elements)) 274 (test-method-framework (car (cdr (lsp-csharp--code-element-test-method-p (car test-methods))))) 275 (test-method-names (mapcar (lambda (method) 276 (car (lsp-csharp--code-element-test-method-p method))) 277 test-methods))) 278 (lsp-csharp--start-tests test-method-framework test-method-names))) 279 280 (defun lsp-csharp-run-test-in-buffer () 281 "Run selected test in current buffer." 282 (interactive) 283 (when-let* ((elements (lsp-csharp--get-buffer-code-elements)) 284 (test-methods (lsp-csharp--collect-code-elements-recursively 'lsp-csharp--code-element-test-method-p elements)) 285 (test-method-framework (car (cdr (lsp-csharp--code-element-test-method-p (car test-methods))))) 286 (test-method-names (mapcar (lambda (method) 287 (car (lsp-csharp--code-element-test-method-p method))) 288 test-methods)) 289 (selected-test-method-name (lsp--completing-read "Select test:" test-method-names 'identity))) 290 (lsp-csharp--start-tests test-method-framework (list selected-test-method-name)))) 291 292 (defun lsp-csharp-run-last-tests () 293 "Re-run test(s) that were run last time." 294 (interactive) 295 (if-let ((last-test-method-framework (lsp-session-get-metadata "last-test-method-framework")) 296 (last-test-method-names (lsp-session-get-metadata "last-test-method-names"))) 297 (lsp-csharp--start-tests last-test-method-framework last-test-method-names) 298 (message "lsp-csharp: No test method(s) found to be ran previously on this workspace"))) 299 300 (lsp-defun lsp-csharp--handle-os-error (_workspace (&omnisharp:ErrorMessage :file-name :text)) 301 "Handle the `o#/error' (interop) notification displaying a message." 302 (lsp-warn "%s: %s" file-name text)) 303 304 (lsp-defun lsp-csharp--handle-os-testmessage (_workspace (&omnisharp:TestMessageEvent :message)) 305 "Handle the `o#/testmessage and display test message on test output buffer." 306 (lsp-csharp--test-message message)) 307 308 (lsp-defun lsp-csharp--handle-os-testcompleted (_workspace (&omnisharp:DotNetTestResult 309 :method-name 310 :outcome 311 :error-message 312 :error-stack-trace 313 :standard-output 314 :standard-error)) 315 "Handle the `o#/testcompleted' message from the server. 316 317 Will display the results of the test on the lsp-csharp test output buffer." 318 (let ((passed (string-equal "passed" outcome))) 319 (lsp-csharp--test-message 320 (format "[%s] %s " 321 (propertize (upcase outcome) 'font-lock-face (if passed 'success 'error)) 322 method-name)) 323 324 (unless passed 325 (lsp-csharp--test-message error-message) 326 327 (when error-stack-trace 328 (lsp-csharp--test-message error-stack-trace)) 329 330 (unless (seq-empty-p standard-output) 331 (lsp-csharp--test-message "STANDARD OUTPUT:") 332 (seq-doseq (stdout-line standard-output) 333 (lsp-csharp--test-message stdout-line))) 334 335 (unless (seq-empty-p standard-error) 336 (lsp-csharp--test-message "STANDARD ERROR:") 337 (seq-doseq (stderr-line standard-error) 338 (lsp-csharp--test-message stderr-line)))))) 339 340 (lsp-defun lsp-csharp--action-client-find-references ((&Command :arguments?)) 341 "Read first argument from ACTION as Location and display xrefs for that location 342 using the `textDocument/references' request." 343 (-if-let* (((&Location :uri :range) (lsp-seq-first arguments?)) 344 ((&Range :start range-start) range) 345 (find-refs-params (append (lsp--text-document-position-params (list :uri uri) range-start) 346 (list :context (list :includeDeclaration json-false)))) 347 (locations-found (lsp-request "textDocument/references" find-refs-params))) 348 (lsp-show-xrefs (lsp--locations-to-xref-items locations-found) nil t) 349 (message "No references found"))) 350 351 (defun lsp-csharp--omnisharp-path->qualified-name (path) 352 "Convert PATH to qualified-namespace-like name." 353 (replace-regexp-in-string 354 (regexp-quote "/") 355 "." 356 path)) 357 358 (defun lsp-csharp--omnisharp-metadata-uri-handler (uri) 359 "Handle `file:/(metadata)' URI from omnisharp-roslyn server. 360 361 The URI is parsed and then `o#/metadata' request is issued to retrieve 362 metadata from the server. A cache file is created on project root dir that 363 stores this metadata and filename is returned so lsp-mode can display this file." 364 (string-match lsp-csharp--omnisharp-metadata-uri-re uri) 365 (-when-let* ((project-name (lsp-csharp--omnisharp-path->qualified-name (url-unhex-string (match-string 1 uri)))) 366 (assembly-name (lsp-csharp--omnisharp-path->qualified-name (url-unhex-string (match-string 2 uri)))) 367 (type-name (lsp-csharp--omnisharp-path->qualified-name (url-unhex-string (match-string 3 uri)))) 368 (metadata-req (lsp-make-omnisharp-metadata-request :project-name project-name 369 :assembly-name assembly-name 370 :type-name type-name)) 371 (metadata (lsp-request "o#/metadata" metadata-req)) 372 ((&omnisharp:MetadataResponse :source-name :source) metadata) 373 (filename (f-join ".cache" 374 "lsp-csharp" 375 "metadata" 376 "Project" project-name 377 "Assembly" assembly-name 378 "Symbol" (concat type-name ".cs"))) 379 (file-location (expand-file-name filename (lsp--suggest-project-root))) 380 (metadata-file-location (concat file-location ".metadata-uri")) 381 (path (f-dirname file-location))) 382 383 (unless (find-buffer-visiting file-location) 384 (unless (file-directory-p path) 385 (make-directory path t)) 386 387 (with-temp-file metadata-file-location 388 (insert uri)) 389 390 (with-temp-file file-location 391 (insert source))) 392 393 file-location)) 394 395 (defun lsp-csharp--omnisharp-uri->path-fn (uri) 396 "Custom implementation of lsp--uri-to-path function to glue omnisharp's 397 metadata uri." 398 (if (string-match-p lsp-csharp--omnisharp-metadata-uri-re uri) 399 (lsp-csharp--omnisharp-metadata-uri-handler uri) 400 (lsp--uri-to-path-1 uri))) 401 402 (defun lsp-csharp--omnisharp-environment-fn () 403 "Build environment structure for current values of lsp-csharp customizables. 404 See https://github.com/OmniSharp/omnisharp-roslyn/wiki/Configuration-Options" 405 `(("OMNISHARP_RoslynExtensionsOptions:enableDecompilationSupport" . ,(if lsp-csharp-omnisharp-enable-decompilation-support "true" "false")))) 406 407 (lsp-register-client 408 (make-lsp-client :new-connection 409 (lsp-stdio-connection 410 #'(lambda () 411 (append 412 (list (lsp-csharp--language-server-path) "-lsp") 413 (when lsp-csharp-solution-file 414 (list "-s" (expand-file-name lsp-csharp-solution-file))))) 415 #'(lambda () 416 (when-let ((binary (lsp-csharp--language-server-path))) 417 (f-exists? binary)))) 418 :activation-fn (lsp-activate-on "csharp") 419 :server-id 'omnisharp 420 :priority -1 421 :uri->path-fn #'lsp-csharp--omnisharp-uri->path-fn 422 :environment-fn #'lsp-csharp--omnisharp-environment-fn 423 :action-handlers (ht ("omnisharp/client/findReferences" 'lsp-csharp--action-client-find-references)) 424 :notification-handlers (ht ("o#/projectadded" 'ignore) 425 ("o#/projectchanged" 'ignore) 426 ("o#/projectremoved" 'ignore) 427 ("o#/packagerestorestarted" 'ignore) 428 ("o#/msbuildprojectdiagnostics" 'ignore) 429 ("o#/packagerestorefinished" 'ignore) 430 ("o#/unresolveddependencies" 'ignore) 431 ("o#/error" 'lsp-csharp--handle-os-error) 432 ("o#/testmessage" 'lsp-csharp--handle-os-testmessage) 433 ("o#/testcompleted" 'lsp-csharp--handle-os-testcompleted) 434 ("o#/projectconfiguration" 'ignore) 435 ("o#/projectdiagnosticstatus" 'ignore) 436 ("o#/backgrounddiagnosticstatus" 'ignore)) 437 :download-server-fn #'lsp-csharp--omnisharp-download-server)) 438 439 ;; 440 ;; Alternative "csharp-ls" language server support 441 ;; see https://github.com/razzmatazz/csharp-language-server 442 ;; 443 (lsp-defun lsp-csharp--cls-metadata-uri-handler (uri) 444 "Handle `csharp:/(metadata)' uri from csharp-ls server. 445 446 `csharp/metadata' request is issued to retrieve metadata from the server. 447 A cache file is created on project root dir that stores this metadata and 448 filename is returned so lsp-mode can display this file." 449 450 (-when-let* ((metadata-req (lsp-make-csharp-ls-c-sharp-metadata 451 :text-document (lsp-make-text-document-identifier :uri uri))) 452 (metadata (lsp-request "csharp/metadata" metadata-req)) 453 ((&csharp-ls:CSharpMetadataResponse :project-name 454 :assembly-name 455 :symbol-name 456 :source) metadata) 457 (filename (f-join ".cache" 458 "lsp-csharp" 459 "metadata" 460 "projects" project-name 461 "assemblies" assembly-name 462 (concat symbol-name ".cs"))) 463 (file-location (expand-file-name filename (lsp-workspace-root))) 464 (metadata-file-location (concat file-location ".metadata-uri")) 465 (path (f-dirname file-location))) 466 467 (unless (file-exists-p file-location) 468 (unless (file-directory-p path) 469 (make-directory path t)) 470 471 (with-temp-file metadata-file-location 472 (insert uri)) 473 474 (with-temp-file file-location 475 (insert source))) 476 477 file-location)) 478 479 (defun lsp-csharp--cls-before-file-open (_workspace) 480 "Set `lsp-buffer-uri' variable after C# file is open from *.metadata-uri file." 481 482 (let ((metadata-file-name (concat buffer-file-name ".metadata-uri"))) 483 (setq-local lsp-buffer-uri 484 (when (file-exists-p metadata-file-name) 485 (with-temp-buffer (insert-file-contents metadata-file-name) 486 (buffer-string)))))) 487 488 (defun lsp-csharp--cls-make-launch-cmd () 489 "Return command line to invoke csharp-ls." 490 491 ;; emacs-28.1 on macOS has an issue 492 ;; that it launches processes using posix_spawn but does not reset sigmask properly 493 ;; thus causing dotnet runtime to lockup awaiting a SIGCHLD signal that never comes 494 ;; from subprocesses that quit 495 ;; 496 ;; as a workaround we will wrap csharp-ls invocation in "/bin/ksh -c" on macos 497 ;; so it launches with proper sigmask 498 ;; 499 ;; see https://lists.gnu.org/archive/html/emacs-devel/2022-02/msg00461.html 500 501 (let ((startup-wrapper (cond ((and (eq 'darwin system-type) 502 (version= "28.1" emacs-version)) 503 (list "/bin/ksh" "-c")) 504 505 (t nil))) 506 507 (csharp-ls-exec (or (executable-find "csharp-ls") 508 (f-join (or (getenv "USERPROFILE") (getenv "HOME")) 509 ".dotnet" "tools" "csharp-ls"))) 510 511 (solution-file-params (when lsp-csharp-solution-file 512 (list "-s" lsp-csharp-solution-file)))) 513 (append startup-wrapper 514 (list csharp-ls-exec) 515 solution-file-params))) 516 517 (defun lsp-csharp--cls-download-server (_client callback error-callback update?) 518 "Install/update csharp-ls language server using `dotnet tool'. 519 520 Will invoke CALLBACK or ERROR-CALLBACK based on result. 521 Will update if UPDATE? is t" 522 (lsp-async-start-process 523 callback 524 error-callback 525 "dotnet" "tool" (if update? "update" "install") "-g" "csharp-ls")) 526 527 (lsp-register-client 528 (make-lsp-client :new-connection (lsp-stdio-connection #'lsp-csharp--cls-make-launch-cmd) 529 :priority -2 530 :server-id 'csharp-ls 531 :activation-fn (lsp-activate-on "csharp") 532 :before-file-open-fn #'lsp-csharp--cls-before-file-open 533 :uri-handlers (ht ("csharp" #'lsp-csharp--cls-metadata-uri-handler)) 534 :download-server-fn #'lsp-csharp--cls-download-server)) 535 536 (lsp-consistency-check lsp-csharp) 537 538 (provide 'lsp-csharp) 539 ;;; lsp-csharp.el ends here