Skip to content

Commit

Permalink
Merge pull request #346 from razzmatazz/master
Browse files Browse the repository at this point in the history
Implementtion for go-to-definition in metadata
  • Loading branch information
razzmatazz committed Aug 4, 2017
2 parents 74a5833 + 00bd7a1 commit bf0edf7
Show file tree
Hide file tree
Showing 13 changed files with 235 additions and 51 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@
# backup files by emacs
/test/MinimalProject/obj/*
/test/MinimalProject/KeystrokeTest.cs
/travis-stuff/omnisharp-roslyn/
*~
*~
85 changes: 77 additions & 8 deletions omnisharp-navigation-actions.el
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,83 @@
"Jump to the definition of the symbol under point. With prefix
argument, use another window."
(interactive "P")
(omnisharp--send-command-to-server
"gotodefinition"
(omnisharp--get-request-object)
(lambda (response)
(if (null (omnisharp--get-filename response))
(message
"Cannot go to definition as none was returned by the API.")
(omnisharp-go-to-file-line-and-column response other-window)))))
(let ((gotodefinition-request (append
'((WantMetadata . t))
(omnisharp--get-request-object))))
(omnisharp--send-command-to-server
"gotodefinition"
gotodefinition-request
(lambda (response)
(omnisharp--prepare-metadata-buffer-if-needed
(omnisharp--get-filename response)
(cdr (assoc 'MetadataSource response))
(lambda (buffer filename)
(omnisharp-go-to-file-line-and-column response
other-window
buffer)))))))

(defun omnisharp--prepare-metadata-buffer-if-needed (filename
metadata-source
callback)
"Prepares metadata buffer if required (if FILENAME is missing and
METADATA-SOURCE is available) and then invokes CALLBACK with either
buffer or FILENAME of the file containing the definition.
Metadata buffer is made readonly and both omnisharp-mode and csharp-mode's
are enabled on this buffer."
(cond
;; when gotodefinition returns FileName for the same
;; metadata buffer as we're in:
;; just return current buffer
((and (boundp 'omnisharp--metadata-source)
(string-equal filename omnisharp--metadata-source))
(funcall callback (current-buffer) nil))

;; when gotodefinition returns an actual filename on the filesystem:
;; navigate to this file
(filename
(funcall callback nil filename))

;; when gotodefinition returns metadata reference:
;; in this case we need to invoke /metadata endpoint to fetch
;; generated C# source for this type from the server (unless we
;; have it already in an existing buffer)
(metadata-source
(let* ((metadata-buffer-name (omnisharp--make-metadata-buffer-name
metadata-source))
(existing-metadata-buffer (get-buffer metadata-buffer-name)))
(if existing-metadata-buffer
;; ok, we have this buffer for this metadata source loaded already
(funcall callback existing-metadata-buffer nil)

;; otherwise we need to actually retrieve metadata-generated source
;; and create a buffer for this type
(omnisharp--send-command-to-server
"metadata"
metadata-source
(lambda (response)
(let ((source (cdr (assoc 'Source response)))
(source-name (cdr (assoc 'SourceName response)))
(new-metadata-buffer (get-buffer-create metadata-buffer-name)))
(with-current-buffer new-metadata-buffer
(insert source)
(csharp-mode)
(omnisharp-mode)
(toggle-read-only 1)
(setq-local omnisharp--metadata-source source-name))
(funcall callback new-metadata-buffer nil)))))))
(t
(message
"Cannot go to definition as none was returned by the API."))))

(defun omnisharp--make-metadata-buffer-name (metadata-source)
"Builds unique buffer name for the given MetadataSource object.
This buffer name assumed to be stable and unique."

(let ((assembly-name (cdr (assoc 'AssemblyName metadata-source)))
(type-name (cdr (assoc 'TypeName metadata-source)))
(project-name (cdr (assoc 'ProjectName metadata-source))))
(concat "*omnisharp-metadata:" project-name ":" assembly-name ":" type-name "*")))

(defun omnisharp-go-to-definition-other-window ()
"Do `omnisharp-go-to-definition' displaying the result in a different window."
Expand Down
2 changes: 1 addition & 1 deletion omnisharp-server-installation.el
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.

(defcustom omnisharp-expected-server-version "1.19.0"
(defcustom omnisharp-expected-server-version "1.22.0"
"Version of the omnisharp-roslyn server that this omnisharp-emacs package
is built for. Also used to select version for automatic server installation."
:group 'omnisharp
Expand Down
68 changes: 44 additions & 24 deletions omnisharp.el
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ server backend."
(setq eldoc-documentation-function 'omnisharp-eldoc-function))))

;;
;; interactive functions that are to be available via autoload
;; (all autoloaded f-ns should go in omnisharp.el in order to make
;; below are all the interactive functions that are to be available via autoload
;;
;; (autoloaded f-ns should go in omnisharp.el in order to make
;; internal file dependencies easier to manage when autoloading)
;;

Expand Down Expand Up @@ -330,15 +331,22 @@ some cases. Work around this."
"Construct a Request object based on the current buffer contents."
(let* ((line-number (number-to-string (line-number-at-pos)))
(column-number (number-to-string (omnisharp--current-column)))
(buffer-contents (omnisharp--get-current-buffer-contents))
(buffer-contents (if (boundp 'omnisharp--metadata-source)
nil
(omnisharp--get-current-buffer-contents)))
(filename-tmp (or buffer-file-name ""))
(params `((Line . ,line-number)
(Column . ,column-number)
(Buffer . ,buffer-contents))))
(if (/= 0 (length filename-tmp))
(cons (omnisharp--to-filename filename-tmp)
params)
params)))
(cond
((boundp 'omnisharp--metadata-source)
(cons `(FileName . ,omnisharp--metadata-source)
params))
((/= 0 (length filename-tmp))
(cons (omnisharp--to-filename filename-tmp)
params))
(t
params))))

(defun omnisharp--get-typelookup-request-object ()
"Construct a Request object for typelookup endpoint based on the current buffer contents."
Expand All @@ -363,15 +371,20 @@ not work on all platforms."
params)))

(defun omnisharp-go-to-file-line-and-column (json-result
&optional other-window)
&optional other-window
buffer)
"Open file :FileName at :Line and :Column. If filename is not given,
defaults to the current file. This function works for a
QuickFix class json result."
QuickFix class json result.
Switches to BUFFER instead of :FileName when buffer is set."
(omnisharp-go-to-file-line-and-column-worker
(cdr (assoc 'Line json-result))
(- (cdr (assoc 'Column json-result)) 1)
(omnisharp--get-filename json-result)
other-window))
other-window
nil
buffer))

(defun omnisharp--go-to-line-and-column (line column)
(goto-char (point-min))
Expand All @@ -382,18 +395,20 @@ QuickFix class json result."
column
&optional filename
other-window
dont-save-old-pos)
"Open file filename at line and column. If filename is not given,
defaults to the current file. Saves the current location into the tag
ring so that the user may return with (pop-tag-mark).
dont-save-old-pos
buffer)
"Open filename at line and column. Switches to BUFFER if provided,
otherwise defaults to the current file if filename is not given.
Saves the current location into the tag ring so that the user may
return with (pop-tag-mark).
If DONT-SAVE-OLD-POS is specified, will not save current position to
find-tag-marker-ring. This is so this function may be used without
messing with the ring."

(let ((position-before-jumping (point-marker)))
(when filename
(omnisharp--find-file-possibly-in-other-window filename
(when (or buffer filename)
(omnisharp--find-file-possibly-in-other-window (or buffer filename)
other-window))

;; calling goto-line directly results in a compiler warning.
Expand Down Expand Up @@ -423,18 +438,23 @@ record that position. Otherwise record the current position."
(ring-insert find-tag-marker-ring marker))

(defun omnisharp--find-file-possibly-in-other-window
(filename &optional other-window)
"Open a buffer editing FILENAME. If no buffer for that filename
(file &optional other-window)
"Open a buffer editing FILE. If no buffer for that filename
exists, a new one is created.
If the optional argument OTHER-WINDOW is non-nil, uses another
window."
window.
FILE can be a buffer in which case that buffer is selected."

(cond
((omnisharp--buffer-exists-for-file-name filename)
((or (bufferp file)
(omnisharp--buffer-exists-for-file-name file))
(let ((target-buffer-to-switch-to
(--first (string= (buffer-file-name it)
filename)
(buffer-list))))
(if (bufferp file)
file
(--first (string= (buffer-file-name it)
file)
(buffer-list)))))
(if other-window
(pop-to-buffer target-buffer-to-switch-to)
(pop-to-buffer-same-window target-buffer-to-switch-to))))
Expand All @@ -443,7 +463,7 @@ window."
(funcall (if other-window
'find-file-other-window
'find-file)
filename))))
file))))

(defun omnisharp--vector-to-list (vector)
(append vector nil))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

(it "navigates to the only implementation when only one found"
(ot--buffer-contents-and-point-at-$
"public class Base$Class {}"
"public class SomeClass : BaseClass {}")
"public interface IInter$face {}"
"public class SomeClass : IInterface {}")

(ot--evaluate-and-wait-for-server-response "(omnisharp-find-implementations)")
(ot--point-should-be-on-a-line-containing "public class SomeClass : BaseClass {}"))
(ot--point-should-be-on-a-line-containing "public class SomeClass : IInterface {}"))

(it "shows a list of implementations when more than one found"
(ot--buffer-contents-and-point-at-$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@

(it "navigates to the only implementation when only one found"
(ot--buffer-contents-and-point-at-$
"public class Base$Class {}"
"public class SomeClass : BaseClass {}")
"public interface IInter$face {}"
"public class SomeClass : IInterface {}")

(ot--evaluate-and-wait-for-server-response "(omnisharp-find-implementations-with-ido)")
(ot--point-should-be-on-a-line-containing "public class SomeClass : BaseClass {}"))
(ot--point-should-be-on-a-line-containing "public class SomeClass : IInterface {}"))

(it "lets the user choose one with ido when more than one found"
(ot--buffer-contents-and-point-at-$
"public class Base$Class {}"
"public class SomeClass : BaseClass {}"
"public class SomeClass2 : BaseClass {}")
"public interface IInter$face {}"
"public class SomeClass : IInterface {}"
"public class SomeClass2 : IInterface {}")

(ot--keyboard-input
(ot--meta-x-command "omnisharp-find-implementations-with-ido")
;; choose the first item
(ot--press-key "RET"))

(ot--point-should-be-on-a-line-containing "public class SomeClass : BaseClass {}")))
(ot--point-should-be-on-a-line-containing "public class SomeClass : IInterface {}")))
14 changes: 13 additions & 1 deletion test/buttercup-tests/navigation/find-symbols-test.el
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
;; License: GNU General Public License version 3, or (at your option) any later version
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.


(describe "Navigate to solution member (find symbols)"
(it "moves point to selected type"
Expand Down
36 changes: 34 additions & 2 deletions test/buttercup-tests/navigation/go-to-definition-test.el
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
;; License: GNU General Public License version 3, or (at your option) any later version
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.


(describe "Go to definition"
(it "goes to definition in the same file"
Expand Down Expand Up @@ -47,5 +59,25 @@
(omnisharp--wait-until-request-completed (omnisharp-go-to-definition-other-window))

(ot--switch-to-the-window-in-the-buffer "MyClass.cs")
(ot--point-should-be-on-a-line-containing "public class MyClass")))
(ot--point-should-be-on-a-line-containing "public class MyClass"))

(it "goes to a member defined in metadata"
(ot--open-the-minimal-project-source-file "MyClass.cs")
(ot--buffer-contents-and-point-at-$
"using System;"
"namespace minimal"
"{"
" public class MyClass"
" {"
" public st$ring foo;"
" }"
"}")

(omnisharp-go-to-definition)
(ot--wait-until-all-requests-completed)

;; TODO: for some reason I need to set current buffer from window list
;; with with-current-buffer..
(with-current-buffer (car (mapcar #'window-buffer (window-list)))
(ot--i-should-be-in-buffer-name "*omnisharp-metadata:MinimalProject:System.Runtime:System.String*")
(ot--point-should-be-on-a-line-containing "public sealed class String"))))
14 changes: 13 additions & 1 deletion test/buttercup-tests/navigation/imenu-test.el
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
;; License: GNU General Public License version 3, or (at your option) any later version
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.


(describe "Imenu integration"
(it "builds an index of members in the current file"
Expand Down
14 changes: 13 additions & 1 deletion test/buttercup-tests/navigation/navigate-to-region-test.el
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
;; License: GNU General Public License version 3, or (at your option) any later version
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.


(describe "Navigate to region"
(it "asks the user for a region in the current file to navigate to and goes there"
Expand Down
Loading

0 comments on commit bf0edf7

Please sign in to comment.