From b6d152372c682f441925042ea8f4acc6430ac2d8 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 18 Dec 2023 21:59:33 +0100 Subject: [PATCH 1/2] heuristic for JSX completion happening at the very end of a component with children --- analysis/src/CompletionFrontEnd.ml | 8 ++--- analysis/src/CompletionJsx.ml | 32 +++++++++++++++---- analysis/tests/src/CompletionJsx.res | 3 ++ .../tests/src/expected/CompletionJsx.res.txt | 19 +++++++++++ 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 70ce8eda5..d8cfb8f9e 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -249,14 +249,14 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor Some text.[offsetNoWhite] else None in + let charAtCursor = + if offset < String.length text then text.[offset] else '\n' + in let posBeforeCursor = Pos.posBeforeCursor posCursor in let charBeforeCursor, blankAfterCursor = match Pos.positionToOffset text posCursor with | Some offset when offset > 0 -> ( let charBeforeCursor = text.[offset - 1] in - let charAtCursor = - if offset < String.length text then text.[offset] else '\n' - in match charAtCursor with | ' ' | '\t' | '\r' | '\n' -> (Some charBeforeCursor, Some charBeforeCursor) @@ -918,7 +918,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor CompletionJsx.findJsxPropsCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor ~posAfterCompName:(Loc.end_ compName.loc) - ~firstCharBeforeCursorNoWhite + ~firstCharBeforeCursorNoWhite ~charAtCursor in if jsxCompletable <> None then setResultOpt jsxCompletable else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then diff --git a/analysis/src/CompletionJsx.ml b/analysis/src/CompletionJsx.ml index bf79e0401..4763cf5e1 100644 --- a/analysis/src/CompletionJsx.ml +++ b/analysis/src/CompletionJsx.ml @@ -802,12 +802,17 @@ type jsxProps = { } let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor - ~firstCharBeforeCursorNoWhite ~posAfterCompName = + ~firstCharBeforeCursorNoWhite ~charAtCursor ~posAfterCompName = let allLabels = List.fold_right (fun prop allLabels -> prop.name :: allLabels) jsxProps.props [] in + let beforeChildrenStart = + match jsxProps.childrenStart with + | Some childrenPos -> posBeforeCursor < childrenPos + | None -> posBeforeCursor <= endPos + in let rec loop props = match props with | prop :: rest -> @@ -860,13 +865,28 @@ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor nested = []; }) else None + else if rest = [] && beforeChildrenStart && charAtCursor = '>' then + (* This is a special case for: (completing directly after the '='). + The completion comes at the end of the component, after the equals sign, but before any + children starts, and '>' marks that it's at the end of the component JSX. + + The parser parses this differently to when there's a space after the '=' sign. + This little heuristic makes sure we pick up this special case. *) + Some + (Cexpression + { + contextPath = + CJsxPropValue + { + pathToComponent = + Utils.flattenLongIdent ~jsx:true jsxProps.compName.txt; + propName = prop.name; + }; + prefix = ""; + nested = []; + }) else loop rest | [] -> - let beforeChildrenStart = - match jsxProps.childrenStart with - | Some childrenPos -> posBeforeCursor < childrenPos - | None -> posBeforeCursor <= endPos - in let afterCompName = posBeforeCursor >= posAfterCompName in if afterCompName && beforeChildrenStart then Some diff --git a/analysis/tests/src/CompletionJsx.res b/analysis/tests/src/CompletionJsx.res index ad1263337..5e966180f 100644 --- a/analysis/tests/src/CompletionJsx.res +++ b/analysis/tests/src/CompletionJsx.res @@ -45,3 +45,6 @@ module CompWithoutJsxPpx = { // +// ^com diff --git a/analysis/tests/src/expected/CompletionJsx.res.txt b/analysis/tests/src/expected/CompletionJsx.res.txt index fba67fac1..31fda79cb 100644 --- a/analysis/tests/src/expected/CompletionJsx.res.txt +++ b/analysis/tests/src/expected/CompletionJsx.res.txt @@ -473,3 +473,22 @@ Path CompWithoutJsxPpx.make "documentation": null }] +Complete src/CompletionJsx.res 48:27 +posCursor:[48:27] posNoWhite:[48:26] Found expr:[48:4->48:28] +JSX 48:17] someProp[48:18->48:26]=...[48:18->48:26]> _children:None +Completable: Cexpression CJsxPropValue [SomeComponent] someProp +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath CJsxPropValue [SomeComponent] someProp +Path SomeComponent.make +[{ + "label": "\"\"", + "kind": 12, + "tags": [], + "detail": "string", + "documentation": null, + "sortText": "A", + "insertText": "{\"$0\"}", + "insertTextFormat": 2 + }] + From 81018f5774d8a8fa562f0f4ab60b723727510d00 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 19 Dec 2023 10:46:13 +0100 Subject: [PATCH 2/2] fix comment --- analysis/src/CompletionJsx.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/analysis/src/CompletionJsx.ml b/analysis/src/CompletionJsx.ml index 4763cf5e1..7feaa122f 100644 --- a/analysis/src/CompletionJsx.ml +++ b/analysis/src/CompletionJsx.ml @@ -869,8 +869,6 @@ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor (* This is a special case for: (completing directly after the '='). The completion comes at the end of the component, after the equals sign, but before any children starts, and '>' marks that it's at the end of the component JSX. - - The parser parses this differently to when there's a space after the '=' sign. This little heuristic makes sure we pick up this special case. *) Some (Cexpression