Skip to content

Commit 2f98fe1

Browse files
authored
OCaml: Add implIntf (#4732)
*This is the first of a series of PR I aim to do to improve the ocaml-lsp experience in lsp-mode* This PR exposes `lsp-ocaml-find-alternate-file` that allows to find the file interface corresponding to an implementation or the implementation corresponding to an interface. This function is quite simple for the moment since I first needed to make sure that the LSP requests were working as expected. Following PRs will add more possibilities
1 parent 32899ce commit 2f98fe1

File tree

3 files changed

+95
-3
lines changed

3 files changed

+95
-3
lines changed

clients/lsp-ocaml.el

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
;;; Code:
2626

2727
(require 'lsp-mode)
28+
(require 'find-file)
2829

2930
(defgroup lsp-ocaml nil
3031
"LSP support for OCaml, using ocaml-language-server."
@@ -60,10 +61,14 @@
6061
(define-obsolete-variable-alias 'lsp-merlin 'lsp-ocaml-lsp-server "lsp-mode 6.1")
6162
(define-obsolete-variable-alias 'lsp-merlin-command 'lsp-ocaml-lsp-server-command "lsp-mode 6.1")
6263

64+
;;; -------------------
65+
;;; OCaml-lsp custom variables
66+
;;; -------------------
67+
6368
(defcustom lsp-ocaml-lsp-server-command
6469
'("ocamllsp")
65-
"Command to start ocaml-language-server."
66-
:group 'lsp-ocaml
70+
"Command to start ocaml-lsp-server."
71+
:group 'lsp-ocaml-lsp-server
6772
:type '(choice
6873
(string :tag "Single string value")
6974
(repeat :tag "List of string values"
@@ -79,7 +84,7 @@
7984

8085
(defcustom lsp-cut-signature 'space
8186
"If non-nil, signatures returned on hover will not be split on newline."
82-
:group 'lsp-ocaml
87+
:group 'lsp-ocaml-lsp-server
8388
:type '(choice (symbol :tag "Default behaviour" 'cut)
8489
(symbol :tag "Display all the lines with spaces" 'space)))
8590

@@ -129,6 +134,78 @@ An example of function using STORABLE is:
129134
ntype)))
130135
type)))
131136

137+
;;; -------------------
138+
;;; OCaml-lsp extensions interface
139+
;;; -------------------
140+
141+
;;; The following functions are used to create an interface between custom OCaml-lsp requests and lsp-mode
142+
143+
(defun lsp-ocaml--switch-impl-intf ()
144+
"Switch to the file(s) that the current file can switch to.
145+
146+
OCaml-lsp custom protocol documented here
147+
https://github.com/ocaml/ocaml-lsp/blob/master/ocaml-lsp-server/docs/ocamllsp/switchImplIntf-spec.md"
148+
(-if-let* ((params (lsp-make-ocaml-lsp-switch-impl-intf-params
149+
:uri (lsp--buffer-uri)))
150+
(uris (lsp-request "ocamllsp/switchImplIntf" params)))
151+
uris
152+
(lsp--warn "Your version of ocaml-lsp doesn't support the switchImplIntf extension")))
153+
154+
;;; -------------------
155+
;;; OCaml-lsp general utilities
156+
;;; -------------------
157+
158+
(defun lsp-ocaml--has-one-element-p (lst)
159+
"Returns t if LST contains only one element."
160+
(and lst (= (length lst) 1)))
161+
162+
;;; -------------------
163+
;;; OCaml-lsp URI utilities
164+
;;; -------------------
165+
166+
(defun lsp-ocaml--load-uri (uri &optional other-window)
167+
"Check if URI exists and open its buffer or create a new one.
168+
169+
If OTHER-WINDOW is not nil, open the buffer in an other window."
170+
(let ((path (lsp--uri-to-path uri)))
171+
(cond
172+
173+
;; A buffer already exists with PATH
174+
((bufferp (get-file-buffer path))
175+
(ff-switch-to-buffer (get-file-buffer path) other-window)
176+
path)
177+
178+
;; PATH is an existing file
179+
((file-exists-p path)
180+
(ff-find-file path other-window nil)
181+
path)
182+
183+
;; PATH is not an existing file
184+
(t
185+
nil))))
186+
187+
(defun lsp-ocaml--find-alternate-uri ()
188+
"Return the URI corresponding to the alternate file if there's only one or prompt for a choice."
189+
(let ((uris (lsp-ocaml--switch-impl-intf)))
190+
(if (lsp-ocaml--has-one-element-p uris)
191+
(car uris)
192+
(let* ((filenames (mapcar #'f-filename uris))
193+
(selected-file (completing-read "Choose an alternate file " filenames)))
194+
(nth (cl-position selected-file filenames :test #'string=) uris)))))
195+
196+
;;; -------------------
197+
;;; OCaml-lsp extensions
198+
;;; -------------------
199+
200+
;;; The following functions are interactive implementations of the OCaml-lsp requests
201+
202+
(defun lsp-ocaml-find-alternate-file ()
203+
"Return the URI corresponding to the alternate file if there's only one or prompt for a choice."
204+
(interactive)
205+
(let ((uri (lsp-ocaml--find-alternate-uri)))
206+
(unless (lsp-ocaml--load-uri uri nil)
207+
(message "No alternate file %s could be found for %s" (f-filename uri) (buffer-name)))))
208+
132209
(lsp-consistency-check lsp-ocaml)
133210

134211
(provide 'lsp-ocaml)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
author: mattiasdrp
3+
template: comment.html
4+
root_file: docs/manual-language-docs/lsp-ocaml.md
5+
---
6+
7+
## ocaml-lsp-server
8+
9+
### Commands
10+
11+
#### `lsp-ocaml-find-alternate-file`
12+
13+
Find the interface corresponding to an implementation or the implementation corresponding to an interface.

lsp-protocol.el

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,8 @@ See `-let' for a description of the destructuring mechanism."
433433
(lsp-interface (csharp-ls:CSharpMetadata (:textDocument))
434434
(csharp-ls:CSharpMetadataResponse (:source :projectName :assemblyName :symbolName)))
435435

436+
(lsp-interface (ocaml-lsp:SwitchImplIntfParams (:uri) nil))
437+
436438
(lsp-interface (rls:Cmd (:args :binary :env :cwd) nil))
437439

438440
(lsp-interface (rust-analyzer:AnalyzerStatusParams (:textDocument))

0 commit comments

Comments
 (0)