Skip to content

Commit 43e6032

Browse files
authored
Merge pull request #22 from tarides/fixes-and-improvement-before-release
Fixes and improvement before release
2 parents 7e23b2a + b2102fc commit 43e6032

File tree

5 files changed

+140
-21
lines changed

5 files changed

+140
-21
lines changed

CHANGES.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
ocaml-eglot 1.0.0
2+
=================
3+
Fri Jan 17 04:50:35 PM CET 2025
4+
5+
- First release of `ocaml-eglot`
6+
([#1](https://github.com/tarides/ocaml-eglot/pull/1),
7+
[#2](https://github.com/tarides/ocaml-eglot/pull/2),
8+
[#3](https://github.com/tarides/ocaml-eglot/pull/3),
9+
[#4](https://github.com/tarides/ocaml-eglot/pull/4),
10+
[#5](https://github.com/tarides/ocaml-eglot/pull/5),
11+
[#6](https://github.com/tarides/ocaml-eglot/pull/6),
12+
[#7](https://github.com/tarides/ocaml-eglot/pull/7),
13+
[#8](https://github.com/tarides/ocaml-eglot/pull/8),
14+
[#9](https://github.com/tarides/ocaml-eglot/pull/9),
15+
[#10](https://github.com/tarides/ocaml-eglot/pull/10),
16+
[#11](https://github.com/tarides/ocaml-eglot/pull/11),
17+
[#12](https://github.com/tarides/ocaml-eglot/pull/12),
18+
[#13](https://github.com/tarides/ocaml-eglot/pull/13),
19+
[#14](https://github.com/tarides/ocaml-eglot/pull/14),
20+
[#16](https://github.com/tarides/ocaml-eglot/pull/16),
21+
[#18](https://github.com/tarides/ocaml-eglot/pull/18),
22+
[#19](https://github.com/tarides/ocaml-eglot/pull/19),
23+
[#20](https://github.com/tarides/ocaml-eglot/pull/20),
24+
[#21](https://github.com/tarides/ocaml-eglot/pull/21),
25+
[#22](https://github.com/tarides/ocaml-eglot/pull/22))

README.md

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
> [!WARNING]
2-
> `ocaml-eglot` is **highly experimental** and at a very early stage
3-
> of development. While we're very happy to collect user feedback,
4-
> **don't overwhelm your OCaml development** environment just yet.
5-
61
# ocaml-eglot
72

83
**`ocaml-eglot`** is a lightweight
@@ -15,13 +10,58 @@ client. This tool specifically caters to the OCaml ecosystem by
1510
implementing canonical custom requests and commands exposed by the
1611
[**`ocaml-lsp-server`**](https://github.com/ocaml/ocaml-lsp).
1712

13+
> [!WARNING]
14+
> `ocaml-eglot` is **experimental** and at an early stage
15+
> of development. While we're very happy to collect user feedback.
16+
1817
`ocaml-eglot` bridges the gap between generic LSP support and the
1918
**specific needs of OCaml developers**. Its tight coupling with Eglot
2019
ensures a lightweight experience without sacrificing the advanced
2120
features made available by `ocaml-lsp-server`. Its aim is to offer a
2221
user experience as close as possible to that offered by the Emacs mode
2322
[Merlin](https://ocaml.github.io/merlin/editor/emacs/).
2423

24+
## Installation
25+
26+
`ocaml-eglot` is distributed as a [MELPA
27+
package](https://melpa.org/#/ocaml-eglot). `ocaml-eglot` is only an
28+
interface between `eglot` (available _out of the box_ since `emacs >=
29+
29.1`) and Emacs, a major mode dedicated to OCaml editing must be
30+
installed (e.g. [caml-mode](https://melpa.org/#/caml) or
31+
[tuareg](https://melpa.org/#/tuareg)). Then, for example, you can use
32+
[`use-package`](https://www.gnu.org/software/emacs/manual/html_node/use-package/Lisp-Configuration.html)
33+
to install OCaml-eglot. You will also need
34+
`https://ocaml.org/p/ocaml-lsp-server/latest` in the [current
35+
switch](https://ocaml.org/docs/opam-switch-introduction).
36+
37+
38+
Here's an example with Tuareg already installed:
39+
40+
```scheme
41+
(use-package ocaml-eglot
42+
:ensure t
43+
:after tuareg
44+
:hook
45+
(tuareg-mode . ocaml-eglot)
46+
(ocaml-eglot . eglot-ensure))
47+
```
48+
49+
### Activating `format-on-save`
50+
51+
Eglot provides a hook to format the buffer on saving:
52+
53+
```diff
54+
(use-package ocaml-eglot
55+
:ensure t
56+
:after tuareg
57+
:hook
58+
(tuareg-mode . ocaml-eglot)
59+
- (ocaml-eglot . eglot-ensure))
60+
+ (ocaml-eglot . eglot-ensure)
61+
+ :config
62+
+ (add-hook #'after-save-hook #'eglot-format))
63+
```
64+
2565
## Features
2666

2767
### Browsing errors
@@ -122,6 +162,12 @@ project, it requires an index. This index can be created by running
122162

123163
![Occurences example](media/occurences.gif)
124164

165+
### Renaming
166+
167+
Use `ocaml-eglot-rename` to rename the symbol under the cursor. Starting with OCaml 5.3 it is possible to rename a symbol across multiple files after building an up-to-date index with `dune build @ocaml-index`.
168+
169+
![Rename example](media/rename.gif)
170+
125171
### Infer Interface
126172

127173
Used to infer the type of an interface file. If the buffer is not
@@ -218,3 +264,32 @@ option`:
218264
(the search type is defined by the input query)
219265

220266
![Search Example](media/search.gif)
267+
268+
## Comparison of Merlin and OCaml-eglot commands
269+
270+
| `merlin` | `ocaml-eglot` | Note |
271+
|-----------------------------|------------------------------------|--------------------------------------------------------------------------------------------------------------|
272+
| `merlin-error-check` || The functionality is supported by `eglot` diagnostics (via LSP). |
273+
| `merlin-error-next` | `ocaml-eglot-error-next` | |
274+
| `merlin-error-prev` | `ocaml-eglot-error-prev` | |
275+
| `merlin-type-enclosing` | `ocaml-eglot-type-enclosing` | |
276+
| `merlin-type-expr` | `ocaml-eglot-type-expression` | |
277+
| `merlin-locate` | `ocaml-eglot-find-declaration` | |
278+
|| `ocaml-eglot-find-definition` | Available in Merlin by configuration |
279+
|| `ocaml-eglot-find-type-definition` | |
280+
| `merlin-locate-ident` || |
281+
| `merlin-occurences` | `ocaml-eglot-occurences` | |
282+
| `merlin-project-occurences` || Handle by `ocaml-eglot-occurences` (if `ocaml-version >= 5.2` and need an index, `dune build @ocaml-index`) |
283+
| `merlin-iedit-occurrences` | `ocaml-eglot-rename` | |
284+
| `merlin-document` | `ocaml-eglot-document` | also `ocaml-eglot-document-identifier` |
285+
| `merlin-phrase-next` | `ocaml-eglot-phrase-next` | |
286+
| `merlin-phrase-prev` | `ocaml-eglot-phrase-prev` | |
287+
| `merlin-switch-to-ml` | `ocaml-eglot-alternate-file` | |
288+
| `merlin-switch-to-mli` | `ocaml-eglot-alternate-file` | |
289+
|| `ocaml-eglot-infer-interface` | It was supported by `Tuareg` (and a bit ad-hoc) |
290+
| `merlin-jump` | `ocaml-eglot-jump` | |
291+
| `merlin-destruct` | `ocaml-eglot-destruct` | |
292+
| `merlin-construct` | `ocaml-eglot-construct` | |
293+
| `merlin-next-hole` | `ocaml-eglot-hole-next` | |
294+
| `merlin-previous-hole` | `ocaml-eglot-hole-prev` | |
295+
| `merlin-toggle-view-errors` || An `eglot` configuration |

media/rename.gif

65.7 KB
Loading

ocaml-eglot-type-enclosing.el

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,22 +62,27 @@
6262
ocaml-eglot-type-enclosing-current-type)
6363
(kill-new ocaml-eglot-type-enclosing-current-type)))
6464

65-
(defun ocaml-eglot-type-enclosing--with-fixed-offset ()
66-
"Compute the type enclosing for a dedicated offset."
65+
(defun ocaml-eglot-type-enclosing--with-fixed-offset (&optional prev-verb)
66+
"Compute the type enclosing for a dedicated offset.
67+
If PREV-VERB is given, the verbosity change ensure that the type is different."
6768
(let* ((verbosity ocaml-eglot-type-enclosing-verbosity)
6869
(index ocaml-eglot-type-enclosing-offset)
6970
(at (ocaml-eglot-util--current-position-or-range))
7071
(result (ocaml-eglot-req--type-enclosings at index verbosity))
7172
(type (cl-getf result :type)))
73+
(when (and prev-verb
74+
(string= type ocaml-eglot-type-enclosing-current-type))
75+
(setq ocaml-eglot-type-enclosing-verbosity prev-verb))
7276
(setq ocaml-eglot-type-enclosing-current-type type)
7377
(ocaml-eglot-type-enclosing--display type t)))
7478

7579
(defun ocaml-eglot-type-enclosing-increase-verbosity ()
7680
"Increase the verbosity of the current request."
7781
(interactive)
78-
(setq ocaml-eglot-type-enclosing-verbosity
79-
(1+ ocaml-eglot-type-enclosing-verbosity))
80-
(ocaml-eglot-type-enclosing--with-fixed-offset))
82+
(let ((prev-verbosity ocaml-eglot-type-enclosing-verbosity))
83+
(setq ocaml-eglot-type-enclosing-verbosity
84+
(1+ ocaml-eglot-type-enclosing-verbosity))
85+
(ocaml-eglot-type-enclosing--with-fixed-offset prev-verbosity)))
8186

8287
(defun ocaml-eglot-type-enclosing-decrease-verbosity ()
8388
"Decrease the verbosity of the current request."

ocaml-eglot.el

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -362,13 +362,14 @@ If there is no available holes, it returns the first one of HOLES."
362362
(line (split-string doc "[\r\n]+")))
363363
(car line)))
364364

365-
(defun ocaml-eglot--search-to-completion (entries)
366-
"Transforms a list of ENTRIES into a search candidate (autocomplete)."
365+
(defun ocaml-eglot--search-to-completion (entries key-completable)
366+
"Transforms a list of ENTRIES into a search candidate (autocomplete).
367+
KEY-COMPLETABLE define the current value to be selected."
367368
(mapcar
368369
(lambda (entry)
369370
(let* ((value-name (cl-getf entry :name))
370371
(value-type (cl-getf entry :typ))
371-
(value-hole (cl-getf entry :constructible))
372+
(value-hole (cl-getf entry key-completable))
372373
(value-doc (ocaml-eglot--search-as-doc (cl-getf entry :doc)))
373374
(key (ocaml-eglot--search-as-key value-name value-type value-doc)))
374375
(cons key value-hole)))
@@ -386,26 +387,34 @@ If there is no available holes, it returns the first one of HOLES."
386387
(cycle-sort-function . identity))
387388
(complete-with-action action choices string pred))))
388389

389-
(defun ocaml-eglot-search (query &optional limit)
390+
(defun ocaml-eglot--search (query limit key)
390391
"Search a value using his type (or polarity) by a QUERY.
391-
the universal prefix argument can be used to change the maximim number
392-
of result (LIMIT)."
393-
(interactive "sSearch query: \np")
392+
the universal prefix argument can be used to change the maximum number
393+
of result (LIMIT). KEY define the current value to be selected."
394394
(eglot--server-capable-or-lose :experimental :ocamllsp :handleTypeSearch)
395-
(let* ((start (eglot--pos-to-lsp-position))
396-
(limit (or(if (> limit 1) limit nil)
395+
(let* ((limit (or(if (> limit 1) limit nil)
397396
ocaml-eglot-type-search-limit 25))
398397
(with-doc (or ocaml-eglot-type-search-include-doc :json-false))
399398
;; We use plaintext because the result of the documentation may
400399
;; be truncated
401400
(entries (ocaml-eglot-req--search query limit with-doc "plaintext"))
402-
(choices (ocaml-eglot--search-to-completion entries))
401+
(choices (ocaml-eglot--search-to-completion entries key))
403402
(chosen (ocaml-eglot--search-completion
404403
choices
405404
(completing-read
406405
"Candidates: "
407406
(ocaml-eglot--search-complete-sort choices)
408-
nil nil nil t)))
407+
nil nil nil t))))
408+
chosen))
409+
410+
(defun ocaml-eglot-search (query &optional limit)
411+
"Search a value using his type (or polarity) by a QUERY.
412+
the universal prefix argument can be used to change the maximim number
413+
of result (LIMIT)."
414+
(interactive "sSearch query: \np")
415+
(eglot--server-capable-or-lose :experimental :ocamllsp :handleTypeSearch)
416+
(let* ((start (eglot--pos-to-lsp-position))
417+
(chosen (ocaml-eglot--search query limit :constructible))
409418
(result (concat "(" chosen ")"))
410419
(end (ocaml-eglot-util--position-increase-char start result)))
411420
(when (region-active-p)
@@ -505,6 +514,11 @@ and print its type."
505514
(interactive)
506515
(call-interactively #'xref-find-references))
507516

517+
(defun ocaml-eglot-rename ()
518+
"Rename the symbol at point."
519+
(interactive)
520+
(call-interactively #'eglot-rename))
521+
508522
;;; Mode
509523

510524
(defvar ocaml-eglot-map

0 commit comments

Comments
 (0)