Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Command to provide information for OCaml syntax #1706

Merged
merged 75 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
d94449b
add new command boilerplate
PizieDust Nov 15, 2023
bd358ad
add identifier to syntax_doc definition
PizieDust Nov 21, 2023
064be5d
update query json to use new identifier
PizieDust Nov 21, 2023
e839a74
include new return types for query protocol
PizieDust Nov 21, 2023
fffcb72
update test to check type variant declarations
PizieDust Nov 21, 2023
854dbc7
syntax documentation boiler plate
PizieDust Nov 21, 2023
395908a
poc implementation for syntax_doc command
PizieDust Nov 21, 2023
7d260d7
update the test description
PizieDust Nov 21, 2023
b7a58c3
remove optional identifier and obsolete code
PizieDust Nov 21, 2023
ee2e77a
remove optional identifier
PizieDust Nov 21, 2023
a1b83cf
add new variant examples
PizieDust Nov 21, 2023
7637d07
handle type declarations
PizieDust Nov 21, 2023
4465115
remove redundant parent_node matching
PizieDust Nov 21, 2023
7ad7e84
remove comments
PizieDust Nov 21, 2023
16ba225
Bump version for release 4.13
voodoos Dec 1, 2023
8ccd057
delete intial testing file
PizieDust Dec 4, 2023
ce86e2c
add custom documentation
PizieDust Dec 4, 2023
e5cfd1d
add language extension tests
PizieDust Dec 4, 2023
47fee36
refined node matching for better docs
PizieDust Dec 11, 2023
2ce3cb1
add more tests
PizieDust Dec 11, 2023
ea3916c
remove invalid_identifier ouput
PizieDust Dec 12, 2023
8317508
ocamlformat, limit lines to 80 chars
PizieDust Dec 12, 2023
3fad091
add mli for syntax_doc
PizieDust Dec 12, 2023
cf21041
proper naming
PizieDust Dec 12, 2023
79cd3b1
update docs, move type to query_protocol, leave as record
PizieDust Dec 14, 2023
9d9d66e
add info type for syntax_docment command json
PizieDust Dec 14, 2023
0a43841
update to record for json output
PizieDust Dec 14, 2023
48d50c5
change from string output to json
PizieDust Dec 14, 2023
f7a4f70
delete redundant test files
PizieDust Dec 14, 2023
72f5038
delete tests files
PizieDust Dec 14, 2023
31e88cb
move tests code here and promote
PizieDust Dec 14, 2023
02fcf2f
remove typedtree nodes
PizieDust Dec 14, 2023
f2a9582
change variable name to a more informative name
PizieDust Dec 18, 2023
5f26e57
Make syntax_doc_result optional
PizieDust Dec 18, 2023
cabd4a9
remove redundant cases
PizieDust Dec 18, 2023
d111bcc
use singular and more shorter names
PizieDust Dec 18, 2023
ef59e92
dune promote name changes
PizieDust Dec 18, 2023
db812c9
lint
PizieDust Dec 18, 2023
67af843
url builder function for syntax documentation url
PizieDust Dec 18, 2023
0f3241e
lint
PizieDust Dec 18, 2023
2960fa8
dune promote correct urls
PizieDust Dec 18, 2023
bf338a1
concat urls before returning to query_json
PizieDust Dec 18, 2023
f19b61e
make command return record option
PizieDust Dec 19, 2023
87010ce
delete test file
PizieDust Dec 20, 2023
dc81644
correct formatting to original
PizieDust Dec 20, 2023
ce46ef5
refactor to be more meaningful
PizieDust Dec 20, 2023
7c945ad
use versbose names
PizieDust Dec 20, 2023
fed6dff
test: start making more precise tests
voodoos Jan 9, 2024
5a9c932
Apply suggestions from code review
PizieDust Jan 9, 2024
6dfd27f
Edit descriptions to be less verbose
PizieDust Jan 11, 2024
d2cf998
dune promote description changes
PizieDust Jan 11, 2024
c4f6af5
use syn_doc alias
PizieDust Jan 12, 2024
86d40e9
merge duplicate case results
PizieDust Jan 12, 2024
1b85383
lint
PizieDust Jan 12, 2024
623cf25
reduce verboseness in test
PizieDust Jan 12, 2024
9283ee5
add eof
PizieDust Jan 15, 2024
36776e2
Update src/frontend/ocamlmerlin/new/new_commands.ml
PizieDust Jan 15, 2024
375b298
cover more test
PizieDust Jan 20, 2024
2a2cdf8
update some match cases
PizieDust Jan 20, 2024
4be2179
remove trailing whitespaces
PizieDust Jan 29, 2024
20ddb24
use plural form
PizieDust Jan 30, 2024
5872565
refactor private and public types for same nodes into one match case
PizieDust Jan 30, 2024
5221a23
lint and seperate abstract types for public and private
PizieDust Jan 30, 2024
9f104b6
better targeting first class modules
PizieDust Jan 31, 2024
fb2f278
more test for first class modules and capitalization corrections
PizieDust Jan 31, 2024
adf294d
pass location position to syntaxdoc logic
PizieDust Jan 31, 2024
d15324c
use cursor position to better target locally abstract datatypes
PizieDust Jan 31, 2024
82997fd
test case where locally abstract dt shouldnt be triggered
PizieDust Jan 31, 2024
29d1c26
test for first class module where it shouldnt be triggered
PizieDust Jan 31, 2024
c726db5
use 3rd person singular
PizieDust Jan 31, 2024
b92e9bb
add changelog
PizieDust Feb 1, 2024
964b183
Merge branch 'master' into syntax_documentation
PizieDust Feb 1, 2024
f3cd471
fix indentation
PizieDust Feb 2, 2024
b5e9189
remove excess whitespace
PizieDust Feb 2, 2024
cece67d
proper heading
PizieDust Feb 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ merlin NEXT_VERSION
+ merlin binary
- Add a "heap_mbytes" field to Merlin server responses to report heap usage (#1717)
- Add cache stats to telemetry (#1711)
- Add new SyntaxDocument command to find information about the node under the cursor (#1706)
- Fix `FLG -pp ppx.exe -as-pp/-dump-ast` use of invalid shell redirection when
direct process launch on Windows. (#1723, fixes #1722)
- Add a query_num field to the `ocamlmerlin` responses to detect server crashes (#1716)
Expand Down
230 changes: 230 additions & 0 deletions src/analysis/syntax_doc.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
open Browse_raw

type syntax_info = Query_protocol.syntax_doc_result option

let syntax_doc_url endpoint =
let base_url = "https://v2.ocaml.org/releases/4.14/htmlman/" in
base_url ^ endpoint

let get_syntax_doc cursor_loc node : syntax_info =
match node with
| (_, Type_kind _)
:: (_, Type_declaration _)
:: (_, With_constraint (Twith_typesubst _))
:: _ ->
Some
{
name = "Destructive substitution";
description =
"Behaves like normal signature constraints but removes the \
redefined type or module from the signature.";
documentation =
syntax_doc_url
"signaturesubstitution.html#ss:destructive-substitution";
}
| (_, Type_kind _)
:: (_, Type_declaration _)
:: (_, Signature_item ({ sig_desc = Tsig_typesubst _; _ }, _))
:: _ ->
Some
{
name = "Local substitution";
description =
"Behaves like destructive substitution but is introduced during \
the specification of the signature, and will apply to all the \
items that follow.";
documentation =
syntax_doc_url "signaturesubstitution.html#ss:local-substitution";
}
| (_, Module_type _)
:: (_, Module_type _)
:: ( _,
Module_type_constraint
(Tmodtype_explicit
{ mty_desc = Tmty_with (_, [ (_, _, Twith_modtype _) ]); _ }) )
:: _ ->
Some
{
name = "Module substitution";
PizieDust marked this conversation as resolved.
Show resolved Hide resolved
description =
"Behaves like type substitutions but are useful to refine an \
abstract module type in a signature into a concrete module type,";
documentation =
syntax_doc_url
"signaturesubstitution.html#ss:module-type-substitution";
}
| (_, Type_kind Ttype_open) :: (_, Type_declaration { typ_private; _ }) :: _
->
let e_name = "Extensible Variant Type" in
let e_description =
"Can be extended with new variant constructors using `+=`."
in
let e_url = "extensiblevariants.html" in
let name, description, url =
match typ_private with
| Public -> (e_name, e_description, e_url)
| Private ->
( Format.sprintf "Private %s" e_name,
Format.sprintf
"%s. Prevents new constructors from being declared directly, \
but allows extension constructors to be referred to in \
interfaces."
e_description,
"extensiblevariants.html#ss:private-extensible" )
in
Some { name; description; documentation = syntax_doc_url url }
| (_, Constructor_declaration _)
:: (_, Type_kind (Ttype_variant _))
:: (_, Type_declaration { typ_private; _ })
:: _
| _
:: (_, Constructor_declaration _)
:: (_, Type_kind (Ttype_variant _))
:: (_, Type_declaration { typ_private; _ })
:: _ ->
let v_name = "Variant Type" in
let v_description =
"Represent's data that may take on multiple different forms."
in
let v_url = "typedecl.html#ss:typedefs" in
let name, description, url =
match typ_private with
| Public -> (v_name, v_description, v_url)
| Private ->
( Format.sprintf "Private %s" v_name,
Format.sprintf
"%s This type is private, values cannot be constructed \
directly but can be de-structured as usual."
v_description,
"privatetypes.html#ss:private-types-variant" )
in
Some { name; description; documentation = syntax_doc_url url }
| (_, Core_type _)
:: (_, Core_type _)
:: (_, Label_declaration _)
:: (_, Type_kind (Ttype_record _))
:: (_, Type_declaration { typ_private; _ })
:: _ ->
let r_name = "Record Type" in
let r_description = "Defines variants with a fixed set of fields" in
let r_url = "typedecl.html#ss:typedefs" in
let name, description, url =
match typ_private with
| Public -> (r_name, r_description, r_url)
| Private ->
( Format.sprintf "Private %s" r_name,
Format.sprintf
"%s This type is private, values cannot be constructed \
directly but can be de-structured as usual."
r_description,
"privatetypes.html#ss:private-types-variant" )
in
Some { name; description; documentation = syntax_doc_url url }
| (_, Type_kind (Ttype_variant _))
:: (_, Type_declaration { typ_private = Public; _ })
:: _ ->
Some
{
name = "Empty Variant Type";
description = "An empty variant type.";
documentation = syntax_doc_url "emptyvariants.html";
}
| (_, Type_kind Ttype_abstract)
:: (_, Type_declaration { typ_private = Public; typ_manifest = None; _ })
:: _ ->
Some
{
name = "Abstract Type";
description =
"Define variants with arbitrary data structures, including other \
variants, records, and functions";
documentation = syntax_doc_url "typedecl.html#ss:typedefs";
}
| (_, Type_kind Ttype_abstract)
:: (_, Type_declaration { typ_private = Private; _ })
:: _ ->
Some
{
name = "Private Type Abbreviation";
description =
"Declares a type that is distinct from its implementation type \
`typexpr`.";
documentation =
syntax_doc_url "privatetypes.html#ss:private-types-abbrev";
}
| (_, Expression _)
:: (_, Expression _)
:: (_, Value_binding _)
:: (_, Structure_item ({ str_desc = Tstr_value (Recursive, _); _ }, _))
:: _ ->
Some
{
name = "Recursive value definition";
description =
"Supports a certain class of recursive definitions of \
non-functional values.";
documentation = syntax_doc_url "letrecvalues.html";
}
| (_, Module_expr _) :: (_, Module_type { mty_desc = Tmty_typeof _; _ }) :: _
->
Some
{
name = "Recovering module type";
description =
"Expands to the module type (signature or functor type) inferred \
for the module expression `module-expr`. ";
documentation = syntax_doc_url "moduletypeof.html";
}
| (_, Module_expr _)
:: (_, Module_expr _)
:: (_, Module_binding _)
:: (_, Structure_item ({ str_desc = Tstr_recmodule _; _ }, _))
:: _ ->
Some
{
name = "Recursive module";
description =
"A simultaneous definition of modules that can refer recursively \
to each others.";
documentation = syntax_doc_url "recursivemodules.html";
}
| (_, Expression _)
:: (_, Expression _)
:: (_, Case _)
:: (_, Expression _)
:: ( _,
Value_binding
{
vb_expr =
{ exp_extra = [ (Texp_newtype' (_, loc), _, _) ]; exp_loc; _ };
_;
} )
:: _ -> (
let in_range =
cursor_loc.Lexing.pos_cnum - 1 > exp_loc.loc_start.pos_cnum
&& cursor_loc.Lexing.pos_cnum <= loc.loc.loc_end.pos_cnum + 1
in
match in_range with
| true ->
Some
{
name = "Locally Abstract Type";
description =
"Type constructor which is considered abstract in the scope of \
the sub-expression and replaced by a fresh type variable.";
documentation = syntax_doc_url "locallyabstract.html";
}
| false -> None)
| (_, Module_expr _)
:: (_, Module_expr _)
:: (_, Expression { exp_desc = Texp_pack _; _ })
:: _ ->
Some
{
name = "First class module";
description =
"Converts a module (structure or functor) to a value of the core \
language that encapsulates the module.";
documentation = syntax_doc_url "firstclassmodules.html";
}
| _ -> None
1 change: 1 addition & 0 deletions src/analysis/syntax_doc.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
val get_syntax_doc: Lexing.position -> (Env.t * Browse_raw.node) list -> Query_protocol.syntax_doc_result option
15 changes: 15 additions & 0 deletions src/frontend/ocamlmerlin/new/new_commands.ml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,21 @@ Otherwise, Merlin looks for the documentation for the entity under the cursor (a
end
;

command "syntax-document"
~doc: "Returns documentation for OCaml syntax for the entity under the cursor"
~spec: [
arg "-position" "<position> Position to complete"
(marg_position (fun pos _pos -> pos));
]
~default: `None
begin fun buffer pos ->
match pos with
| `None -> failwith "-position <pos> is mandatory"
| #Msource.position as pos ->
run buffer (Query_protocol.Syntax_document pos)
end
;

command "enclosing"
~spec: [
arg "-position" "<position> Position to complete"
Expand Down
12 changes: 12 additions & 0 deletions src/frontend/ocamlmerlin/query_json.ml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ let dump (type a) : a t -> json =
);
"position", mk_position pos;
]
| Syntax_document pos ->
mk "syntax-document" [ ("position", mk_position pos) ]
| Locate (prefix, look_for, pos) ->
mk "locate" [
"prefix", (match prefix with
Expand Down Expand Up @@ -380,6 +382,16 @@ let json_of_response (type a) (query : a t) (response : a) : json =
| `Found doc ->
`String doc
end
| Syntax_document _, resp ->
(match resp with
| `Found info ->
`Assoc
[
("name", `String info.name);
("description", `String info.description);
("url", `String info.documentation);
]
| `No_documentation -> `String "No documentation found")
| Locate_type _, resp -> json_of_locate resp
| Locate _, resp -> json_of_locate resp
| Jump _, resp ->
Expand Down
9 changes: 9 additions & 0 deletions src/frontend/query_commands.ml
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,15 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a =
Locate.get_doc ~config
~env ~local_defs ~comments ~pos (`User_input path)

| Syntax_document pos ->
let typer = Mpipeline.typer_result pipeline in
let pos = Mpipeline.get_lexing_pos pipeline pos in
let node = Mtyper.node_at typer pos in
let res = Syntax_doc.get_syntax_doc pos node in
(match res with
| Some res -> `Found res
| None -> `No_documentation)

PizieDust marked this conversation as resolved.
Show resolved Hide resolved
| Locate (patho, ml_or_mli, pos) ->
let typer = Mpipeline.typer_result pipeline in
let local_defs = Mtyper.get_typedtree typer in
Expand Down
12 changes: 12 additions & 0 deletions src/frontend/query_protocol.ml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ type error_filter = {
typing : bool;
}

type syntax_doc_result =
{
name : string;
description : string;
documentation : string
}

type is_tail_position = [`No | `Tail_position | `Tail_call]

type _ _bool = bool
Expand Down Expand Up @@ -133,6 +140,11 @@ type _ t =
| `Not_found of string * string option
| `No_documentation
] t
| Syntax_document
: Msource.position
-> [ `Found of syntax_doc_result
| `No_documentation
] t
| Locate_type
: Msource.position
-> [ `Found of string option * Lexing.position
Expand Down
Loading