lsp-csharp.el (26677B)
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 124 (defcustom lsp-csharp-omnisharp-enable-decompilation-support 125 nil 126 "Decompile bytecode when browsing method metadata for types in assemblies. 127 Otherwise only declarations for the methods are visible (the default)." 128 :group 'lsp-csharp 129 :type 'boolean) 130 131 (defcustom lsp-csharp-csharpls-use-dotnet-tool t 132 "Whether to use a dotnet tool version of the expected C# 133 language server; only available for csharp-ls" 134 :group 'lsp-csharp 135 :type 'boolean 136 :risky t) 137 138 (defcustom lsp-csharp-csharpls-use-local-tool nil 139 "Whether to use csharp-ls as a global or local dotnet tool. 140 141 Note: this variable has no effect if 142 lsp-csharp-csharpls-use-dotnet-tool is nil." 143 :group 'lsp-csharp 144 :type 'boolean 145 :risky t) 146 147 (lsp-dependency 148 'omnisharp-roslyn 149 `(:download :url lsp-csharp-omnisharp-roslyn-download-url 150 :decompress :zip 151 :store-path lsp-csharp-omnisharp-roslyn-store-path 152 :binary-path lsp-csharp-omnisharp-roslyn-binary-path 153 :set-executable? t) 154 '(:system "OmniSharp")) 155 156 (defun lsp-csharp--omnisharp-download-server (_client callback error-callback _update?) 157 "Download zip package for omnisharp-roslyn and install it. 158 Will invoke CALLBACK on success, ERROR-CALLBACK on error." 159 (lsp-package-ensure 'omnisharp-roslyn callback error-callback)) 160 161 (defun lsp-csharp--language-server-path () 162 "Resolve path to use to start the server." 163 (let ((executable-name (if (eq system-type 'windows-nt) 164 "OmniSharp.exe" 165 "OmniSharp"))) 166 (or (and lsp-csharp-server-path 167 (executable-find lsp-csharp-server-path)) 168 (executable-find executable-name) 169 (lsp-package-path 'omnisharp-roslyn)))) 170 171 (defun lsp-csharp-open-project-file () 172 "Open corresponding project file (.csproj) for the current file." 173 (interactive) 174 (-let* ((project-info-req (lsp-make-omnisharp-project-information-request :file-name (buffer-file-name))) 175 (project-info (lsp-request "o#/project" project-info-req)) 176 ((&omnisharp:ProjectInformation :ms-build-project) project-info) 177 ((&omnisharp:MsBuildProject :path) ms-build-project)) 178 (find-file path))) 179 180 (defun lsp-csharp--get-buffer-code-elements () 181 "Retrieve code structure by calling into the /v2/codestructure endpoint. 182 Returns :elements from omnisharp:CodeStructureResponse." 183 (-let* ((code-structure (lsp-request "o#/v2/codestructure" 184 (lsp-make-omnisharp-code-structure-request :file-name (buffer-file-name)))) 185 ((&omnisharp:CodeStructureResponse :elements) code-structure)) 186 elements)) 187 188 (defun lsp-csharp--inspect-code-elements-recursively (fn elements) 189 "Invoke FN for every omnisharp:CodeElement found recursively in ELEMENTS." 190 (seq-each 191 (lambda (el) 192 (funcall fn el) 193 (-let (((&omnisharp:CodeElement :children) el)) 194 (lsp-csharp--inspect-code-elements-recursively fn children))) 195 elements)) 196 197 (defun lsp-csharp--collect-code-elements-recursively (predicate elements) 198 "Flatten the omnisharp:CodeElement tree in ELEMENTS matching PREDICATE." 199 (let ((results nil)) 200 (lsp-csharp--inspect-code-elements-recursively (lambda (el) 201 (when (funcall predicate el) 202 (setq results (cons el results)))) 203 elements) 204 results)) 205 206 (lsp-defun lsp-csharp--l-c-within-range (l c (&omnisharp:Range :start :end)) 207 "Determine if L (line) and C (column) are within RANGE." 208 (-let* (((&omnisharp:Point :line start-l :column start-c) start) 209 ((&omnisharp:Point :line end-l :column end-c) end)) 210 (or (and (= l start-l) (>= c start-c) (or (> end-l start-l) (<= c end-c))) 211 (and (> l start-l) (< l end-l)) 212 (and (= l end-l) (<= c end-c))))) 213 214 (defun lsp-csharp--code-element-stack-on-l-c (l c elements) 215 "Return omnisharp:CodeElement stack at L (line) and C (column) in ELEMENTS tree." 216 (when-let ((matching-element (seq-find (lambda (el) 217 (-when-let* (((&omnisharp:CodeElement :ranges) el) 218 ((&omnisharp:RangeList :full?) ranges)) 219 (lsp-csharp--l-c-within-range l c full?))) 220 elements))) 221 (-let (((&omnisharp:CodeElement :children) matching-element)) 222 (cons matching-element (lsp-csharp--code-element-stack-on-l-c l c children))))) 223 224 (defun lsp-csharp--code-element-stack-at-point () 225 "Return omnisharp:CodeElement stack at point as a list." 226 (let ((pos-line (plist-get (lsp--cur-position) :line)) 227 (pos-col (plist-get (lsp--cur-position) :character))) 228 (lsp-csharp--code-element-stack-on-l-c pos-line 229 pos-col 230 (lsp-csharp--get-buffer-code-elements)))) 231 232 (lsp-defun lsp-csharp--code-element-test-method-p (element) 233 "Return test method name and test framework for a given ELEMENT." 234 (when element 235 (-when-let* (((&omnisharp:CodeElement :properties) element) 236 ((&omnisharp:CodeElementProperties :test-method-name? :test-framework?) properties)) 237 (list test-method-name? test-framework?)))) 238 239 (defun lsp-csharp--reset-test-buffer (present-buffer) 240 "Create new or reuse an existing test result output buffer. 241 PRESENT-BUFFER will make the buffer be presented to the user." 242 (with-current-buffer (get-buffer-create lsp-csharp-test-run-buffer-name) 243 (compilation-mode) 244 (read-only-mode) 245 (let ((inhibit-read-only t)) 246 (erase-buffer))) 247 248 (when present-buffer 249 (display-buffer lsp-csharp-test-run-buffer-name))) 250 251 (defun lsp-csharp--start-tests (test-method-framework test-method-names) 252 "Run test(s) identified by TEST-METHOD-NAMES using TEST-METHOD-FRAMEWORK." 253 (if (and test-method-framework test-method-names) 254 (let ((request-message (lsp-make-omnisharp-run-tests-in-class-request 255 :file-name (buffer-file-name) 256 :test-frameworkname test-method-framework 257 :method-names (vconcat test-method-names)))) 258 (lsp-csharp--reset-test-buffer t) 259 (lsp-session-set-metadata "last-test-method-framework" test-method-framework) 260 (lsp-session-set-metadata "last-test-method-names" test-method-names) 261 (lsp-request-async "o#/v2/runtestsinclass" 262 request-message 263 (-lambda ((&omnisharp:RunTestResponse)) 264 (message "lsp-csharp: Test run has started")))) 265 (message "lsp-csharp: No test methods to run"))) 266 267 (defun lsp-csharp--test-message (message) 268 "Emit a MESSAGE to lsp-csharp test run buffer." 269 (when-let ((existing-buffer (get-buffer lsp-csharp-test-run-buffer-name)) 270 (inhibit-read-only t)) 271 (with-current-buffer existing-buffer 272 (save-excursion 273 (goto-char (point-max)) 274 (insert message "\n"))))) 275 276 (defun lsp-csharp-run-test-at-point () 277 "Start test run at current point (if any)." 278 (interactive) 279 (let* ((stack (lsp-csharp--code-element-stack-at-point)) 280 (element-on-point (car (last stack))) 281 (test-method (lsp-csharp--code-element-test-method-p element-on-point)) 282 (test-method-name (car test-method)) 283 (test-method-framework (car (cdr test-method)))) 284 (lsp-csharp--start-tests test-method-framework (list test-method-name)))) 285 286 (defun lsp-csharp-run-all-tests-in-buffer () 287 "Run all test methods in the current buffer." 288 (interactive) 289 (let* ((elements (lsp-csharp--get-buffer-code-elements)) 290 (test-methods (lsp-csharp--collect-code-elements-recursively 'lsp-csharp--code-element-test-method-p elements)) 291 (test-method-framework (car (cdr (lsp-csharp--code-element-test-method-p (car test-methods))))) 292 (test-method-names (mapcar (lambda (method) 293 (car (lsp-csharp--code-element-test-method-p method))) 294 test-methods))) 295 (lsp-csharp--start-tests test-method-framework test-method-names))) 296 297 (defun lsp-csharp-run-test-in-buffer () 298 "Run selected test in current buffer." 299 (interactive) 300 (when-let* ((elements (lsp-csharp--get-buffer-code-elements)) 301 (test-methods (lsp-csharp--collect-code-elements-recursively 'lsp-csharp--code-element-test-method-p elements)) 302 (test-method-framework (car (cdr (lsp-csharp--code-element-test-method-p (car test-methods))))) 303 (test-method-names (mapcar (lambda (method) 304 (car (lsp-csharp--code-element-test-method-p method))) 305 test-methods)) 306 (selected-test-method-name (lsp--completing-read "Select test:" test-method-names 'identity))) 307 (lsp-csharp--start-tests test-method-framework (list selected-test-method-name)))) 308 309 (defun lsp-csharp-run-last-tests () 310 "Re-run test(s) that were run last time." 311 (interactive) 312 (if-let ((last-test-method-framework (lsp-session-get-metadata "last-test-method-framework")) 313 (last-test-method-names (lsp-session-get-metadata "last-test-method-names"))) 314 (lsp-csharp--start-tests last-test-method-framework last-test-method-names) 315 (message "lsp-csharp: No test method(s) found to be ran previously on this workspace"))) 316 317 (lsp-defun lsp-csharp--handle-os-error (_workspace (&omnisharp:ErrorMessage :file-name :text)) 318 "Handle the `o#/error' (interop) notification displaying a message." 319 (lsp-warn "%s: %s" file-name text)) 320 321 (lsp-defun lsp-csharp--handle-os-testmessage (_workspace (&omnisharp:TestMessageEvent :message)) 322 "Handle the `o#/testmessage and display test message on test output buffer." 323 (lsp-csharp--test-message message)) 324 325 (lsp-defun lsp-csharp--handle-os-testcompleted (_workspace (&omnisharp:DotNetTestResult 326 :method-name 327 :outcome 328 :error-message 329 :error-stack-trace 330 :standard-output 331 :standard-error)) 332 "Handle the `o#/testcompleted' message from the server. 333 334 Will display the results of the test on the lsp-csharp test output buffer." 335 (let ((passed (string-equal "passed" outcome))) 336 (lsp-csharp--test-message 337 (format "[%s] %s " 338 (propertize (upcase outcome) 'font-lock-face (if passed 'success 'error)) 339 method-name)) 340 341 (unless passed 342 (lsp-csharp--test-message error-message) 343 344 (when error-stack-trace 345 (lsp-csharp--test-message error-stack-trace)) 346 347 (unless (seq-empty-p standard-output) 348 (lsp-csharp--test-message "STANDARD OUTPUT:") 349 (seq-doseq (stdout-line standard-output) 350 (lsp-csharp--test-message stdout-line))) 351 352 (unless (seq-empty-p standard-error) 353 (lsp-csharp--test-message "STANDARD ERROR:") 354 (seq-doseq (stderr-line standard-error) 355 (lsp-csharp--test-message stderr-line)))))) 356 357 (lsp-defun lsp-csharp--action-client-find-references ((&Command :arguments?)) 358 "Read first argument from ACTION as Location and display xrefs for that location 359 using the `textDocument/references' request." 360 (-if-let* (((&Location :uri :range) (lsp-seq-first arguments?)) 361 ((&Range :start range-start) range) 362 (find-refs-params (append (lsp--text-document-position-params (list :uri uri) range-start) 363 (list :context (list :includeDeclaration json-false)))) 364 (locations-found (lsp-request "textDocument/references" find-refs-params))) 365 (lsp-show-xrefs (lsp--locations-to-xref-items locations-found) nil t) 366 (message "No references found"))) 367 368 (defun lsp-csharp--omnisharp-path->qualified-name (path) 369 "Convert PATH to qualified-namespace-like name." 370 (replace-regexp-in-string 371 (regexp-quote "/") 372 "." 373 path)) 374 375 (defun lsp-csharp--omnisharp-metadata-uri-handler (uri) 376 "Handle `file:/(metadata)' URI from omnisharp-roslyn server. 377 378 The URI is parsed and then `o#/metadata' request is issued to retrieve 379 metadata from the server. A cache file is created on project root dir that 380 stores this metadata and filename is returned so lsp-mode can display this file." 381 (string-match lsp-csharp--omnisharp-metadata-uri-re uri) 382 (-when-let* ((project-name (lsp-csharp--omnisharp-path->qualified-name (url-unhex-string (match-string 1 uri)))) 383 (assembly-name (lsp-csharp--omnisharp-path->qualified-name (url-unhex-string (match-string 2 uri)))) 384 (type-name (lsp-csharp--omnisharp-path->qualified-name (url-unhex-string (match-string 3 uri)))) 385 (metadata-req (lsp-make-omnisharp-metadata-request :project-name project-name 386 :assembly-name assembly-name 387 :type-name type-name)) 388 (metadata (lsp-request "o#/metadata" metadata-req)) 389 ((&omnisharp:MetadataResponse :source-name :source) metadata) 390 (filename (f-join ".cache" 391 "lsp-csharp" 392 "metadata" 393 "Project" project-name 394 "Assembly" assembly-name 395 "Symbol" (concat type-name ".cs"))) 396 (file-location (expand-file-name filename (lsp--suggest-project-root))) 397 (metadata-file-location (concat file-location ".metadata-uri")) 398 (path (f-dirname file-location))) 399 400 (unless (find-buffer-visiting file-location) 401 (unless (file-directory-p path) 402 (make-directory path t)) 403 404 (with-temp-file metadata-file-location 405 (insert uri)) 406 407 (with-temp-file file-location 408 (insert source))) 409 410 file-location)) 411 412 (defun lsp-csharp--omnisharp-uri->path-fn (uri) 413 "Custom implementation of lsp--uri-to-path function to glue omnisharp's 414 metadata uri." 415 (if (string-match-p lsp-csharp--omnisharp-metadata-uri-re uri) 416 (lsp-csharp--omnisharp-metadata-uri-handler uri) 417 (lsp--uri-to-path-1 uri))) 418 419 (defun lsp-csharp--omnisharp-environment-fn () 420 "Build environment structure for current values of lsp-csharp customizables. 421 See https://github.com/OmniSharp/omnisharp-roslyn/wiki/Configuration-Options" 422 `(("OMNISHARP_RoslynExtensionsOptions:enableDecompilationSupport" . ,(if lsp-csharp-omnisharp-enable-decompilation-support "true" "false")))) 423 424 (lsp-register-client 425 (make-lsp-client :new-connection 426 (lsp-stdio-connection 427 #'(lambda () 428 (append 429 (list (lsp-csharp--language-server-path) "-lsp") 430 (when lsp-csharp-solution-file 431 (list "-s" (expand-file-name lsp-csharp-solution-file))))) 432 #'(lambda () 433 (when-let ((binary (lsp-csharp--language-server-path))) 434 (f-exists? binary)))) 435 :activation-fn (lsp-activate-on "csharp") 436 :server-id 'omnisharp 437 :priority -1 438 :uri->path-fn #'lsp-csharp--omnisharp-uri->path-fn 439 :environment-fn #'lsp-csharp--omnisharp-environment-fn 440 :action-handlers (ht ("omnisharp/client/findReferences" 'lsp-csharp--action-client-find-references)) 441 :notification-handlers (ht ("o#/projectadded" 'ignore) 442 ("o#/projectchanged" 'ignore) 443 ("o#/projectremoved" 'ignore) 444 ("o#/packagerestorestarted" 'ignore) 445 ("o#/msbuildprojectdiagnostics" 'ignore) 446 ("o#/packagerestorefinished" 'ignore) 447 ("o#/unresolveddependencies" 'ignore) 448 ("o#/error" 'lsp-csharp--handle-os-error) 449 ("o#/testmessage" 'lsp-csharp--handle-os-testmessage) 450 ("o#/testcompleted" 'lsp-csharp--handle-os-testcompleted) 451 ("o#/projectconfiguration" 'ignore) 452 ("o#/projectdiagnosticstatus" 'ignore) 453 ("o#/backgrounddiagnosticstatus" 'ignore)) 454 :download-server-fn #'lsp-csharp--omnisharp-download-server)) 455 456 ;; 457 ;; Alternative "csharp-ls" language server support 458 ;; see https://github.com/razzmatazz/csharp-language-server 459 ;; 460 (lsp-defun lsp-csharp--cls-metadata-uri-handler (uri) 461 "Handle `csharp:/(metadata)' uri from csharp-ls server. 462 463 `csharp/metadata' request is issued to retrieve metadata from the server. 464 A cache file is created on project root dir that stores this metadata and 465 filename is returned so lsp-mode can display this file." 466 467 (-when-let* ((metadata-req (lsp-make-csharp-ls-c-sharp-metadata 468 :text-document (lsp-make-text-document-identifier :uri uri))) 469 (metadata (lsp-request "csharp/metadata" metadata-req)) 470 ((&csharp-ls:CSharpMetadataResponse :project-name 471 :assembly-name 472 :symbol-name 473 :source) metadata) 474 (filename (f-join ".cache" 475 "lsp-csharp" 476 "metadata" 477 "projects" project-name 478 "assemblies" assembly-name 479 (concat symbol-name ".cs"))) 480 (file-location (expand-file-name filename (lsp-workspace-root))) 481 (metadata-file-location (concat file-location ".metadata-uri")) 482 (path (f-dirname file-location))) 483 484 (unless (file-exists-p file-location) 485 (unless (file-directory-p path) 486 (make-directory path t)) 487 488 (with-temp-file metadata-file-location 489 (insert uri)) 490 491 (with-temp-file file-location 492 (insert source))) 493 494 file-location)) 495 496 (defun lsp-csharp--cls-before-file-open (_workspace) 497 "Set `lsp-buffer-uri' variable after C# file is open from *.metadata-uri file." 498 499 (let ((metadata-file-name (concat buffer-file-name ".metadata-uri"))) 500 (setq-local lsp-buffer-uri 501 (when (file-exists-p metadata-file-name) 502 (with-temp-buffer (insert-file-contents metadata-file-name) 503 (buffer-string)))))) 504 505 (defun lsp-csharp--cls-find-executable () 506 (or (when lsp-csharp-csharpls-use-dotnet-tool 507 (if lsp-csharp-csharpls-use-local-tool 508 (list "dotnet" "tool" "run" "csharp-ls") 509 (list "csharp-ls"))) 510 (executable-find "csharp-ls") 511 (f-join (or (getenv "USERPROFILE") (getenv "HOME")) 512 ".dotnet" "tools" "csharp-ls"))) 513 514 (defun lsp-csharp--cls-make-launch-cmd () 515 "Return command line to invoke csharp-ls." 516 517 ;; emacs-28.1 on macOS has an issue 518 ;; that it launches processes using posix_spawn but does not reset sigmask properly 519 ;; thus causing dotnet runtime to lockup awaiting a SIGCHLD signal that never comes 520 ;; from subprocesses that quit 521 ;; 522 ;; as a workaround we will wrap csharp-ls invocation in "/bin/ksh -c" on macos 523 ;; so it launches with proper sigmask 524 ;; 525 ;; see https://lists.gnu.org/archive/html/emacs-devel/2022-02/msg00461.html 526 527 (let ((startup-wrapper (cond ((and (eq 'darwin system-type) 528 (version= "28.1" emacs-version)) 529 (list "/bin/ksh" "-c")) 530 531 (t nil))) 532 533 (csharp-ls-exec (lsp-csharp--cls-find-executable)) 534 535 (solution-file-params (when lsp-csharp-solution-file 536 (list "-s" lsp-csharp-solution-file)))) 537 (append startup-wrapper 538 (if (listp csharp-ls-exec) 539 csharp-ls-exec 540 (list csharp-ls-exec)) 541 solution-file-params))) 542 543 (defun lsp-csharp--cls-test-csharp-ls-present () 544 "Return non-nil if dotnet tool csharp-ls is installed as a dotnet tool." 545 (string-match-p "csharp-ls" 546 (shell-command-to-string 547 (if lsp-csharp-csharpls-use-local-tool 548 "dotnet tool list" 549 "dotnet tool list -g")))) 550 551 (defun lsp-csharp--cls-download-server (_client callback error-callback update?) 552 "Install/update csharp-ls language server using `dotnet tool'. 553 554 Will invoke CALLBACK or ERROR-CALLBACK based on result. 555 Will update if UPDATE? is t" 556 (lsp-async-start-process 557 callback 558 error-callback 559 "dotnet" "tool" (if update? "update" "install") (if lsp-csharp-csharpls-use-local-tool "" "-g") "csharp-ls")) 560 561 (lsp-register-client 562 (make-lsp-client :new-connection (lsp-stdio-connection #'lsp-csharp--cls-make-launch-cmd) 563 :priority -2 564 :server-id 'csharp-ls 565 :activation-fn (lsp-activate-on "csharp") 566 :before-file-open-fn #'lsp-csharp--cls-before-file-open 567 :uri-handlers (ht ("csharp" #'lsp-csharp--cls-metadata-uri-handler)) 568 :download-server-fn #'lsp-csharp--cls-download-server)) 569 570 (lsp-consistency-check lsp-csharp) 571 572 (provide 'lsp-csharp) 573 ;;; lsp-csharp.el ends here