8888 :type '(choice (symbol :tag " Default behaviour" 'cut )
8989 (symbol :tag " Display all the lines with spaces" 'space )))
9090
91- (cl-defmethod lsp-clients-extract-signature-on-hover (contents (_server-id (eql ocaml-lsp-server)) &optional storable)
92- " Extract a representative line from OCaml's CONTENTS, to show in the echo area.
93- This function splits the content between the signature
94- and the documentation to display the signature
95- and truncate it if it's too wide.
96- The STORABLE argument is used if you want to use this
97- function to get the type and, for example, kill and yank it.
98-
99- An example of function using STORABLE is:
100-
101- (defun mdrp/lsp-get-type-and-kill ()
102- (interactive)
103- (let ((contents (-some->> (lsp--text-document-position-params)
104- (lsp--make-request \" textDocument/hover\" )
105- (lsp--send-request)
106- (lsp:hover-contents))))
107- (let ((contents (and contents
108- (lsp--render-on-hover-content
109- contents
110- t))))
111- (let ((contents
112- (pcase (lsp-workspaces)
113- (`(,workspace)
114- (lsp-clients-extract-signature-on-hover
115- contents
116- (lsp--workspace-server-id workspace)
117- t))
118- (lsp-clients-extract-signature-on-hover
119- contents
120- nil)
121- )))
122- (message \" Copied %s to kill-ring\" contents)
123- (kill-new contents)))))"
124- (let ((type (s-trim (lsp--render-element (lsp-make-marked-string
125- :language " ocaml"
126- :value (car (s-split " ---" (lsp--render-element contents))))))))
127- (if (equal nil storable)
128- (if (eq lsp-cut-signature 'cut )
129- (car (s-lines type))
130- ; ; else lsp-cut-signature is 'space
131- (let ((ntype (s-replace " \n " " " type)))
132- (if (>= (length ntype) (frame-width ))
133- (concat (substring ntype 0 (- (frame-width ) 4 )) " ..." )
134- ntype)))
135- type)))
91+ (defcustom lsp-ocaml-markupkind 'markdown
92+ " Preferred markup format."
93+ :group 'lsp-ocaml-lsp-server
94+ :type '(choice (symbol :tag " Markdown" 'markdown )
95+ (symbol :tag " Plain text" 'plaintext )))
96+
97+ (defcustom lsp-ocaml-enclosing-type-verbosity 1
98+ " Number of expansions of aliases in answers."
99+ :group 'lsp-ocaml-lsp-server
100+ :type 'int )
101+
102+ (defcustom lsp-ocaml-enclosing-type-cycle nil
103+ " When growing up or down the enclosings of a type, cycle when reaching one bound."
104+ :group 'lsp-ocaml-server
105+ :type 'boolean )
106+
107+ ; ;; -------------------
108+ ; ;; OCaml-lsp faces
109+ ; ;; -------------------
110+
111+ (defface lsp-ocaml-highlight-region-face '((t (:inherit region )))
112+ " Face used to highlight a region." )
136113
137114; ;; -------------------
138115; ;; OCaml-lsp extensions interface
@@ -151,12 +128,42 @@ https://github.com/ocaml/ocaml-lsp/blob/master/ocaml-lsp-server/docs/ocamllsp/sw
151128 uris
152129 (lsp--warn " Your version of ocaml-lsp doesn't support the switchImplIntf extension" )))
153130
131+ (defun lsp-ocaml--type-enclosing (verbosity index )
132+ " Get the type of the identifier at point.
133+
134+ VERBOSITY and INDEX use is described in the OCaml-lsp protocol documented here
135+ https://github.com/ocaml/ocaml-lsp/blob/master/ocaml-lsp-server/docs/ocamllsp/typeEnclosing-spec.md"
136+ (-if-let* ((params (lsp-make-ocaml-lsp-type-enclosing-params
137+ :uri (lsp--buffer-uri)
138+ :at (lsp--cur-position)
139+ :index index
140+ :verbosity verbosity))
141+ (result (lsp-request " ocamllsp/typeEnclosing" params)))
142+ result
143+ (lsp--warn " Your version of ocaml-lsp doesn't support the typeEnclosing extension" )))
144+
145+ (defun lsp-ocaml--get-documentation (identifier content-format )
146+ " Get the documentation of IDENTIFIER or the identifier at point if IDENTIFIER is nil.
147+
148+ CONTENT-FORMAT is `Markdown' or `Plaintext' .
149+ OCaml-lsp protocol documented here
150+ https://github.com/ocaml/ocaml-lsp/blob/master/ocaml-lsp-server/docs/ocamllsp/getDocumentation-spec.md"
151+ (-if-let* ((position (if identifier nil (lsp--cur-position)))
152+ ((&TextDocumentPositionParams :text-document :position ) (lsp--text-document-position-params identifier position))
153+ (params (lsp-make-ocaml-lsp-get-documentation-params
154+ :textDocument text-document
155+ :position position
156+ :contentFormat content-format)))
157+ ; ; Don't exit if the request returns nil, an identifier can have no documentation
158+ (lsp-request " ocamllsp/getDocumentation" params)
159+ (lsp--warn " Your version of ocaml-lsp doesn't support the getDocumentation extension" )))
160+
154161; ;; -------------------
155162; ;; OCaml-lsp general utilities
156163; ;; -------------------
157164
158165(defun lsp-ocaml--has-one-element-p (lst )
159- " Returns t if LST contains only one element ."
166+ " Return t if LST is a singleton ."
160167 (and lst (= (length lst) 1 )))
161168
162169; ;; -------------------
@@ -193,6 +200,128 @@ If OTHER-WINDOW is not nil, open the buffer in an other window."
193200 (selected-file (completing-read " Choose an alternate file " filenames)))
194201 (nth (cl-position selected-file filenames :test #'string= ) uris)))))
195202
203+ ; ;; -------------------
204+ ; ;; OCaml-lsp type enclosing utilities
205+ ; ;; ------------------
206+
207+ (defvar-local lsp-ocaml--type-enclosing-verbosity lsp-ocaml-enclosing-type-verbosity)
208+ (defvar-local lsp-ocaml--type-enclosing-index 0 )
209+ (defvar-local lsp-ocaml--type-enclosing-saved-type nil )
210+ (defvar-local lsp-ocaml--type-enclosing-type-enclosings nil )
211+
212+ (defun lsp-ocaml--init-type-enclosing-config ()
213+ " Create a new config for the type enclosing requests."
214+ (setq lsp-ocaml--type-enclosing-verbosity lsp-ocaml-enclosing-type-verbosity)
215+ (setq lsp-ocaml--type-enclosing-index 0 )
216+ (setq lsp-ocaml--type-enclosing-saved-type nil )
217+ (setq lsp-ocaml--type-enclosing-type-enclosings nil ))
218+
219+ (defun lsp-ocaml--highlight-current-type (range )
220+ " Highlight RANGE.
221+
222+ RANGE is (:start (:character .. :line ..)) :end (:character .. :line ..)"
223+ (remove-overlays nil nil 'face 'lsp-ocaml-highlight-region-face )
224+ (let* ((point-min (lsp--position-to-point (cl-getf range :start )))
225+ (point-max (lsp--position-to-point (cl-getf range :end )))
226+ (overlay (make-overlay point-min point-max)))
227+ (overlay-put overlay 'face 'lsp-ocaml-highlight-region-face )
228+ (unwind-protect (sit-for 10 ) (delete-overlay overlay))))
229+
230+ (defun lsp-ocaml--display-type (markupkind type doc )
231+ " Display TYPE in MARKUPKIND with its DOC attached.
232+
233+ If TYPE is a single-line that represents a module type, reformat it."
234+ (let* (; ; Regroup the type and documentation at point
235+ (single-linep (not (string-match-p " \n " type)))
236+ (new-type (if single-linep (string-replace " val " " \n val " type) type))
237+ (new-type (if single-linep (string-replace " end" " \n end" new-type) type))
238+ (contents `(:kind , markupkind
239+ :value ,(mapconcat #'identity `(" ```ocaml" , new-type " ```" " ***" , doc ) " \n " ))))
240+ (lsp--display-contents contents)))
241+
242+ ; ;; -------------------
243+ ; ;; OCaml-lsp type enclosing transient map
244+ ; ;; -------------------
245+
246+ (defvar lsp-ocaml-type-enclosing-map
247+ (let ((keymap (make-sparse-keymap )))
248+ (define-key keymap (kbd " C-<up>" ) #'lsp-ocaml-type-enclosing-go-up )
249+ (define-key keymap (kbd " C-<down>" ) #'lsp-ocaml-type-enclosing-go-down )
250+ (define-key keymap (kbd " C-w" ) #'lsp-ocaml-type-enclosing-copy )
251+ (define-key keymap (kbd " C-t" ) #'lsp-ocaml-type-enclosing-increase-verbosity )
252+ (define-key keymap (kbd " C-<right>" ) #'lsp-ocaml-type-enclosing-increase-verbosity )
253+ (define-key keymap (kbd " C-<left>" ) #'lsp-ocaml-type-enclosing-decrease-verbosity )
254+ keymap)
255+ " Keymap for OCaml-lsp type enclosing transient mode." )
256+
257+ (defun lsp-ocaml-type-enclosing-go-up ()
258+ " Go up the type's enclosing."
259+ (interactive )
260+ (when lsp-ocaml--type-enclosing-type-enclosings
261+ (setq lsp-ocaml--type-enclosing-index
262+ (if lsp-ocaml-enclosing-type-cycle
263+ (mod (1+ lsp-ocaml--type-enclosing-index)
264+ (length lsp-ocaml--type-enclosing-type-enclosings))
265+ (min (1+ lsp-ocaml--type-enclosing-index)
266+ (1- (length lsp-ocaml--type-enclosing-type-enclosings))))))
267+ (lsp-ocaml--get-and-display-type-enclosing))
268+
269+ (defun lsp-ocaml-type-enclosing-go-down ()
270+ " Go down the type's enclosing."
271+ (interactive )
272+ (when lsp-ocaml--type-enclosing-type-enclosings
273+ (setq lsp-ocaml--type-enclosing-index
274+ (if lsp-ocaml-enclosing-type-cycle
275+ (mod (1- lsp-ocaml--type-enclosing-index)
276+ (length lsp-ocaml--type-enclosing-type-enclosings))
277+ (max (1- lsp-ocaml--type-enclosing-index) 0 ))))
278+ (lsp-ocaml--get-and-display-type-enclosing))
279+
280+ (defun lsp-ocaml-type-enclosing-decrease-verbosity ()
281+ " Decreases the number of expansions of aliases in answer."
282+ (interactive )
283+ (let ((verbosity (max 0 (1- lsp-ocaml--type-enclosing-verbosity))))
284+ (setq lsp-ocaml--type-enclosing-verbosity verbosity))
285+ (lsp-ocaml--get-and-display-type-enclosing))
286+
287+ (defun lsp-ocaml-type-enclosing-increase-verbosity ()
288+ " Increases the number of expansions of aliases in answer."
289+ (interactive )
290+ (let ((verbosity (1+ lsp-ocaml--type-enclosing-verbosity)))
291+ (setq lsp-ocaml--type-enclosing-verbosity verbosity))
292+ (lsp-ocaml--get-and-display-type-enclosing t ))
293+
294+ (defun lsp-ocaml-type-enclosing-copy ()
295+ " Copy the type of the saved enclosing type to the `kill-ring' ."
296+ (interactive )
297+ (when lsp-ocaml--type-enclosing-saved-type
298+ (message " Copied `%s' to kill-ring "
299+ lsp-ocaml--type-enclosing-saved-type)
300+ (kill-new lsp-ocaml--type-enclosing-saved-type)))
301+
302+ (defun lsp-ocaml--get-and-display-type-enclosing (&optional increased-verbosity )
303+ " Compute the type enclosing request.
304+
305+ If INCREASED-VERBOSITY is t, if the computed type is the same as the previous
306+ one, decrease the verbosity.
307+ This allows to make sure that we don't increase infinitely the verbosity."
308+ (-let* ((verbosity lsp-ocaml--type-enclosing-verbosity)
309+ (index lsp-ocaml--type-enclosing-index)
310+ (type_result (lsp-ocaml--type-enclosing verbosity index))
311+ ((&ocaml-lsp:TypeEnclosingResult :index :type :enclosings ) type_result)
312+ ; ; Get documentation informations
313+ (markupkind (symbol-name lsp-ocaml-markupkind))
314+ (doc_result (lsp-ocaml--get-documentation nil markupkind))
315+ (doc (cl-getf (cl-getf doc_result :doc ) :value )))
316+ (when (and increased-verbosity
317+ (string= type lsp-ocaml--type-enclosing-saved-type))
318+ (setq lsp-ocaml--type-enclosing-verbosity (1- verbosity)))
319+ (setq lsp-ocaml--type-enclosing-saved-type type)
320+ (setq lsp-ocaml--type-enclosing-type-enclosings enclosings)
321+ (lsp-ocaml--display-type markupkind type doc)
322+ (lsp-ocaml--highlight-current-type (aref enclosings index))
323+ type))
324+
196325; ;; -------------------
197326; ;; OCaml-lsp extensions
198327; ;; -------------------
@@ -206,6 +335,13 @@ If OTHER-WINDOW is not nil, open the buffer in an other window."
206335 (unless (lsp-ocaml--load-uri uri nil )
207336 (message " No alternate file %s could be found for %s " (f-filename uri) (buffer-name )))))
208337
338+ (defun lsp-ocaml-type-enclosing ()
339+ " Returns the type of the indent at point."
340+ (interactive )
341+ (lsp-ocaml--init-type-enclosing-config)
342+ (when-let* ((type (lsp-ocaml--get-and-display-type-enclosing)))
343+ (set-transient-map lsp-ocaml-type-enclosing-map t )))
344+
209345(lsp-consistency-check lsp-ocaml)
210346
211347(provide 'lsp-ocaml )
0 commit comments