88
88
:type '(choice (symbol :tag " Default behaviour" 'cut )
89
89
(symbol :tag " Display all the lines with spaces" 'space )))
90
90
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." )
136
113
137
114
; ;; -------------------
138
115
; ;; OCaml-lsp extensions interface
@@ -151,12 +128,42 @@ https://github.com/ocaml/ocaml-lsp/blob/master/ocaml-lsp-server/docs/ocamllsp/sw
151
128
uris
152
129
(lsp--warn " Your version of ocaml-lsp doesn't support the switchImplIntf extension" )))
153
130
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
+
154
161
; ;; -------------------
155
162
; ;; OCaml-lsp general utilities
156
163
; ;; -------------------
157
164
158
165
(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 ."
160
167
(and lst (= (length lst) 1 )))
161
168
162
169
; ;; -------------------
@@ -193,6 +200,128 @@ If OTHER-WINDOW is not nil, open the buffer in an other window."
193
200
(selected-file (completing-read " Choose an alternate file " filenames)))
194
201
(nth (cl-position selected-file filenames :test #'string= ) uris)))))
195
202
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
+
196
325
; ;; -------------------
197
326
; ;; OCaml-lsp extensions
198
327
; ;; -------------------
@@ -206,6 +335,13 @@ If OTHER-WINDOW is not nil, open the buffer in an other window."
206
335
(unless (lsp-ocaml--load-uri uri nil )
207
336
(message " No alternate file %s could be found for %s " (f-filename uri) (buffer-name )))))
208
337
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
+
209
345
(lsp-consistency-check lsp-ocaml)
210
346
211
347
(provide 'lsp-ocaml )
0 commit comments