-
Notifications
You must be signed in to change notification settings - Fork 15
merlin document support for PPXs #167
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
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
6497788
initial working with tests
maxmwang 956239e
temp update
maxmwang a2c9974
temp
maxmwang d488785
major refactors, add support for .mli files
maxmwang f7ae213
refactoring and cleanup
maxmwang 15a298f
move all logic into override_document.ml; cleanup code; add logging
maxmwang 5df754f
add many more tests; handle edge cases
maxmwang 18df9f9
revert std.ml changes
maxmwang 0f721ca
improve comments
maxmwang 2a00fec
update more comments
maxmwang c72e157
suggested changes
maxmwang 9328c1b
test nested ppx
maxmwang 0f67e7f
document payload test
maxmwang a372bea
some more tests
maxmwang 182e683
resolve final comments
maxmwang 54cb166
add test on floating attribute
maxmwang ba20e49
add test for attribute in an extension's payload
maxmwang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| open Std | ||
|
|
||
| let { Logger.log } = Logger.for_section "override_document" | ||
|
|
||
| let error_failed_to_parse_position_field_values = | ||
| Error "failed to parse position field values" | ||
|
|
||
| let error_unexpected_position_expression_structure = | ||
| Error "unexpected position expression structure" | ||
|
|
||
| let error_unexpected_merlin_document_attribute_structure = | ||
| Error "unexpected merlin.document attribute structure" | ||
|
|
||
| module Override = struct | ||
| type t = { loc : Location.t; doc : string } | ||
|
|
||
| let expr_to_pos ({ pexp_desc; _ } : Parsetree.expression) = | ||
| match pexp_desc with | ||
| | Pexp_record | ||
| ( [ ( { txt = Lident "pos_fname"; _ }, | ||
| { pexp_desc = Pexp_constant (Pconst_string (pos_fname, _, _)); _ } | ||
| ); | ||
| ( { txt = Lident "pos_lnum"; _ }, | ||
| { pexp_desc = Pexp_constant (Pconst_integer (lnum, None)); _ } ); | ||
| ( { txt = Lident "pos_bol"; _ }, | ||
| { pexp_desc = Pexp_constant (Pconst_integer (bol, None)); _ } ); | ||
| ( { txt = Lident "pos_cnum"; _ }, | ||
| { pexp_desc = Pexp_constant (Pconst_integer (cnum, None)); _ } ) | ||
| ], | ||
| None ) -> ( | ||
| match | ||
| (int_of_string_opt lnum, int_of_string_opt bol, int_of_string_opt cnum) | ||
| with | ||
| | Some pos_lnum, Some pos_bol, Some pos_cnum -> | ||
| Ok { Lexing.pos_fname; pos_lnum; pos_bol; pos_cnum } | ||
| | _ -> error_failed_to_parse_position_field_values) | ||
| | _ -> error_unexpected_position_expression_structure | ||
|
|
||
| let of_expression ({ pexp_desc; _ } : Parsetree.expression) = | ||
| match pexp_desc with | ||
| | Pexp_tuple | ||
| [ ( None, | ||
| { pexp_desc = | ||
| Pexp_record | ||
| ( [ ({ txt = Lident "loc_start"; _ }, loc_start_expr); | ||
| ({ txt = Lident "loc_end"; _ }, loc_end_expr); | ||
| ({ txt = Lident "loc_ghost"; _ }, loc_ghost_expr) | ||
| ], | ||
| None ); | ||
| _ | ||
| } ); | ||
| ( None, | ||
| { pexp_desc = Pexp_constant (Pconst_string (documentation, _, _)); | ||
| _ | ||
| } ) | ||
| ] -> | ||
| let open Misc_stdlib.Monad.Result.Syntax in | ||
| let* loc_start = expr_to_pos loc_start_expr in | ||
| let* loc_end = expr_to_pos loc_end_expr in | ||
| let* loc_ghost = | ||
| match loc_ghost_expr.pexp_desc with | ||
| | Pexp_construct ({ txt = Lident "false"; _ }, None) -> Ok false | ||
| | Pexp_construct ({ txt = Lident "true"; _ }, None) -> Ok true | ||
| | _ -> error_failed_to_parse_position_field_values | ||
| in | ||
| Ok | ||
| { loc = { Location.loc_start; loc_end; loc_ghost }; | ||
| doc = documentation | ||
| } | ||
| | _ -> error_unexpected_merlin_document_attribute_structure | ||
|
|
||
| let is_target_override t ~cursor = | ||
| Lexing.compare_pos cursor t.loc.loc_start >= 0 | ||
| && Lexing.compare_pos cursor t.loc.loc_end <= 0 | ||
|
|
||
| let doc t = t.doc | ||
| end | ||
|
|
||
| type t = Override.t list | ||
|
|
||
| let rec of_payload ({ pexp_desc; _ } : Parsetree.expression) = | ||
| match pexp_desc with | ||
| | Pexp_construct | ||
| ( { txt = Lident "::"; _ }, | ||
| Some { pexp_desc = Pexp_tuple [ (None, override); (None, rest) ]; _ } ) | ||
| -> ( | ||
| match Override.of_expression override with | ||
| | Ok override -> override :: of_payload rest | ||
| | Error err -> | ||
| log ~title:"of_payload" "%s" err; | ||
| of_payload rest) | ||
| | _ -> [] | ||
|
|
||
| let of_attribute (attribute : Parsetree.attribute) = | ||
| match attribute with | ||
| | { attr_payload = PStr [ { pstr_desc = Pstr_eval (expr, _); _ } ]; _ } -> | ||
| Ok (of_payload expr) | ||
| | _ -> error_unexpected_merlin_document_attribute_structure | ||
|
|
||
| let get_overrides pipeline = | ||
| let attributes = | ||
| match Mpipeline.ppx_parsetree pipeline with | ||
| | `Interface signature -> | ||
| List.filter_map signature.psg_items | ||
| ~f:(fun (signature_item : Parsetree.signature_item) -> | ||
| match signature_item.psig_desc with | ||
| | Psig_attribute | ||
| ({ attr_name = { txt = "merlin.document"; _ }; _ } as attr) -> | ||
| Some attr | ||
| | _ -> None) | ||
| | `Implementation structure -> | ||
| List.filter_map structure | ||
| ~f:(fun (structure_item : Parsetree.structure_item) -> | ||
| match structure_item.pstr_desc with | ||
| | Pstr_attribute | ||
| ({ attr_name = { txt = "merlin.document"; _ }; _ } as attr) -> | ||
| Some attr | ||
| | _ -> None) | ||
| in | ||
| List.concat_map attributes ~f:(fun attribute -> | ||
| match of_attribute attribute with | ||
| | Ok overrides -> overrides | ||
| | Error err -> | ||
| log ~title:"get_overrides" "%s" err; | ||
| []) | ||
|
|
||
| let find t ~cursor = | ||
| match List.find_all ~f:(Override.is_target_override ~cursor) t with | ||
| | [] -> None | ||
| | override :: [] -> Some override | ||
| | override :: _ :: _ -> | ||
| log ~title:"find" "found multiple target overrides, using first target"; | ||
liam923 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Some override | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| (** Decodes the [@@@merlin.document] attribute into a list and provides [find] to iterate | ||
| through. | ||
|
|
||
| The [@@@merlin.document] attribute is a list of tuples pairing a [Location.t] with | ||
| a documentation string. This attribute can be used to override merlin's [Document] | ||
| behavior. | ||
|
|
||
| The expected structure of [@@@merlin.document]'s payload is as follows: | ||
| {| | ||
| [ | ||
| ( | ||
| { | ||
| "loc_start" = { pos_fname = "filename.ml"; pos_lnum = 1; pos_bol = 0; pos_cnum = 0} | ||
| "loc_end" = { pos_fname = "filename.ml"; pos_lnum = 1; pos_bol = 0; pos_cnum = 0} | ||
| "loc_ghost" = false | ||
| }, | ||
| "<docstring>" | ||
| ); | ||
| ... | ||
| ] | ||
| |} | ||
| Each individual element of the list is stored as an [Override.t], and the full list | ||
| is stored as a [t]. | ||
| *) | ||
|
|
||
| module Override : sig | ||
| type t | ||
|
|
||
| val doc : t -> string | ||
| end | ||
|
|
||
| type t | ||
|
|
||
| (** Constructs a [t] from a [Mpipeline.t]. An error is returned on an unexpected | ||
| AST node structures and parsing errors. | ||
|
|
||
| If there are multiple [@@@merlin.document] attributes, they will be merged. *) | ||
| val get_overrides : Mpipeline.t -> t | ||
|
|
||
| (** Finds the first [Override.t] that [cursor] is enclosed in. *) | ||
| val find : t -> cursor:Lexing.position -> Override.t option |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.