Skip to content

Commit

Permalink
Add go-to definition in metadata (#4441)
Browse files Browse the repository at this point in the history
* feat(lsp-csharp): Add goto to metadata definition

Now if we try to find the definition of a symbol that is defined
externally, it will try to load a stripped-down version of the file where
is defined and show it in a temporal file.

* feat(lsp-csharp): Add option for decompilation on goto definition.

* Add entry to the CHANGELOG

* refactor(lsp-csharp): Follow razzmat's suggestion.

I renamed functionallity only used by omnisharp to be prefixed with
lsp-csharp--omnisharp-

I'm a little bit torn with lsp-csharp--path->qualified-name being prefixed
like that, but only omnisharp is using it.

* style(lsp-csharp): Fix compilation warnings and other misc stuff.
  • Loading branch information
cheerio-pixel authored May 29, 2024
1 parent fceda19 commit 750043c
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.org
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* Add support for GNAT Project (~gpr-mode~, ~gpr-ts-mode~).
* Add SQL support
* Add support for Meson build system. (~meson-mode~).
* Add support for go to definition for external files (.dll) in CSharp projects for OmniSharp server.

** 9.0.0
* Add language server config for QML (Qt Modeling Language) using qmlls.
* Add new configuration options for lsp-html. Now able to toggle documentation hovers. Custom data is no longer experimental, and is now a vector.
Expand Down
72 changes: 72 additions & 0 deletions clients/lsp-csharp.el
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ Version 1.34.3 minimum is required."
:link '(url-link "https://github.com/OmniSharp/omnisharp-roslyn")
:package-version '(lsp-mode . "9.0.0"))

(defconst lsp-csharp--omnisharp-metadata-uri-re
"^file:///%24metadata%24/Project/\\(.+\\)/Assembly/\\(.+\\)/Symbol/\\(.+\\)\.cs$"
"Regular expression matching omnisharp's metadata uri.
Group 1 contains the Project name
Group 2 contains the Assembly name
Group 3 contains the Type name")

(defcustom lsp-csharp-server-install-dir
(f-join lsp-server-install-dir "omnisharp-roslyn/")
"Installation directory for OmniSharp Roslyn server."
Expand Down Expand Up @@ -113,6 +120,13 @@ Usually this is to be set in your .dir-locals.el on the project root directory."
:group 'lsp-csharp-omnisharp
:type 'file)

(defcustom lsp-csharp-omnisharp-enable-decompilation-support
nil
"Decompile bytecode when browsing method metadata for types in assemblies.
Otherwise only declarations for the methods are visible (the default)."
:group 'lsp-csharp
:type 'boolean)

(lsp-dependency
'omnisharp-roslyn
`(:download :url lsp-csharp-omnisharp-roslyn-download-url
Expand Down Expand Up @@ -334,6 +348,62 @@ using the `textDocument/references' request."
(lsp-show-xrefs (lsp--locations-to-xref-items locations-found) nil t)
(message "No references found")))

(defun lsp-csharp--omnisharp-path->qualified-name (path)
"Convert PATH to qualified-namespace-like name."
(replace-regexp-in-string
(regexp-quote "/")
"."
path))

(defun lsp-csharp--omnisharp-metadata-uri-handler (uri)
"Handle `file:/(metadata)' URI from omnisharp-roslyn server.
The URI is parsed and then `o#/metadata' request is issued to retrieve
metadata from the server. A cache file is created on project root dir that
stores this metadata and filename is returned so lsp-mode can display this file."
(string-match lsp-csharp--omnisharp-metadata-uri-re uri)
(-when-let* ((project-name (lsp-csharp--omnisharp-path->qualified-name (url-unhex-string (match-string 1 uri))))
(assembly-name (lsp-csharp--omnisharp-path->qualified-name (url-unhex-string (match-string 2 uri))))
(type-name (lsp-csharp--omnisharp-path->qualified-name (url-unhex-string (match-string 3 uri))))
(metadata-req (lsp-make-omnisharp-metadata-request :project-name project-name
:assembly-name assembly-name
:type-name type-name))
(metadata (lsp-request "o#/metadata" metadata-req))
((&omnisharp:MetadataResponse :source-name :source) metadata)
(filename (f-join ".cache"
"lsp-csharp"
"metadata"
"Project" project-name
"Assembly" assembly-name
"Symbol" (concat type-name ".cs")))
(file-location (expand-file-name filename (lsp--suggest-project-root)))
(metadata-file-location (concat file-location ".metadata-uri"))
(path (f-dirname file-location)))

(unless (find-buffer-visiting file-location)
(unless (file-directory-p path)
(make-directory path t))

(with-temp-file metadata-file-location
(insert uri))

(with-temp-file file-location
(insert source)))

file-location))

(defun lsp-csharp--omnisharp-uri->path-fn (uri)
"Custom implementation of lsp--uri-to-path function to glue omnisharp's
metadata uri."
(if (string-match-p lsp-csharp--omnisharp-metadata-uri-re uri)
(lsp-csharp--omnisharp-metadata-uri-handler uri)
(lsp--uri-to-path-1 uri)))

(defun lsp-csharp--omnisharp-environment-fn ()
"Build environment structure for current values of lsp-csharp customizables.
See https://github.com/OmniSharp/omnisharp-roslyn/wiki/Configuration-Options"
`(("OMNISHARP_RoslynExtensionsOptions:enableDecompilationSupport" . ,(if lsp-csharp-omnisharp-enable-decompilation-support "true" "false"))))

(lsp-register-client
(make-lsp-client :new-connection
(lsp-stdio-connection
Expand All @@ -348,6 +418,8 @@ using the `textDocument/references' request."
:activation-fn (lsp-activate-on "csharp")
:server-id 'omnisharp
:priority -1
:uri->path-fn #'lsp-csharp--omnisharp-uri->path-fn
:environment-fn #'lsp-csharp--omnisharp-environment-fn
:action-handlers (ht ("omnisharp/client/findReferences" 'lsp-csharp--action-client-find-references))
:notification-handlers (ht ("o#/projectadded" 'ignore)
("o#/projectchanged" 'ignore)
Expand Down
4 changes: 3 additions & 1 deletion lsp-protocol.el
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,9 @@ See `-let' for a description of the destructuring mechanism."
(omnisharp:RunTestsInClassRequest (:MethodNames :RunSettings :TestFrameworkname :TargetFrameworkVersion :NoBuild :Line :Column :Buffer :FileName))
(omnisharp:RunTestResponse (:Results :Pass :Failure :ContextHadNoTests))
(omnisharp:TestMessageEvent (:MessageLevel :Message))
(omnisharp:DotNetTestResult (:MethodName :Outcome :ErrorMessage :ErrorStackTrace :StandardOutput :StandardError)))
(omnisharp:DotNetTestResult (:MethodName :Outcome :ErrorMessage :ErrorStackTrace :StandardOutput :StandardError))
(omnisharp:MetadataRequest (:AssemblyName :TypeName :ProjectName :VersionNumber :Language))
(omnisharp:MetadataResponse (:SourceName :Source)))

(lsp-interface (csharp-ls:CSharpMetadata (:textDocument))
(csharp-ls:CSharpMetadataResponse (:source :projectName :assemblyName :symbolName)))
Expand Down

0 comments on commit 750043c

Please sign in to comment.