From ef2f7bfef9c8067338afb44a507cc822ff6c4de1 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sat, 9 Apr 2022 19:32:42 +0200 Subject: [PATCH 001/135] Experiment with using the parser for autocomplete. First step: find the parsetree expression for the item to be completed. --- analysis/src/Cli.ml | 2 +- analysis/src/Commands.ml | 46 ++++++++++++++++--- .../vendor/res_outcome_printer/res_core.ml | 5 +- .../src/expected/CompletePrioritize1.res.txt | 1 + .../src/expected/CompletePrioritize2.res.txt | 1 + .../tests/src/expected/Completion.res.txt | 36 +++++++++++++++ analysis/tests/src/expected/Cross.res.txt | 1 + analysis/tests/src/expected/Div.res.txt | 2 + analysis/tests/src/expected/Jsx.res.txt | 38 +++++++++++++++ .../src/expected/RecordCompletion.res.txt | 4 ++ server/src/utils.ts | 31 +++++++------ 11 files changed, 144 insertions(+), 23 deletions(-) diff --git a/analysis/src/Cli.ml b/analysis/src/Cli.ml index 84ef96d0e..9aabb9ccb 100644 --- a/analysis/src/Cli.ml +++ b/analysis/src/Cli.ml @@ -68,7 +68,7 @@ Options: let main () = match Array.to_list Sys.argv with | [_; "completion"; path; line; col; currentFile] -> - Commands.completion ~path ~line:(int_of_string line) + Commands.completion ~debug:false ~path ~line:(int_of_string line) ~col:(int_of_string col) ~currentFile | [_; "definition"; path; line; col] -> Commands.definition ~path ~line:(int_of_string line) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index e53a2bc5f..848d11824 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -1,9 +1,44 @@ open SharedTypes -let completion ~path ~line ~col ~currentFile = +let posInLoc ~pos ~loc = + Utils.tupleOfLexing loc.Location.loc_start <= pos + && pos < Utils.tupleOfLexing loc.loc_end + +let completionWithParser ~debug ~path ~pos ~currentFile ~textOpt = + let text = match textOpt with Some text -> text | None -> assert false in + let offset = + match PartialParser.positionToOffset text pos with + | Some offset -> offset + | None -> assert false + in + let line, col = pos in + let offsetNoWhite = PartialParser.skipWhite text offset in + let posNoWhite = (line, max 0 col - offset + offsetNoWhite) in + + if Filename.check_suffix path ".res" then ( + let parser = + Res_driver.parsingEngine.parseImplementation ~forPrinter:false + in + let found = ref false in + let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = + if posInLoc ~pos:posNoWhite ~loc:expr.pexp_loc then ( + found := true; + if debug then + Printf.printf "Found expr:%s\n" + (SemanticTokens.locToString expr.pexp_loc)); + Ast_iterator.default_iterator.expr iterator expr + in + let {Res_driver.parsetree = structure} = parser ~filename:currentFile in + let iterator = {Ast_iterator.default_iterator with expr} in + iterator.structure iterator structure |> ignore; + if not !found then if debug then Printf.printf "XXX Not found!\n") + +let completion ~debug ~path ~line ~col ~currentFile = let pos = (line, col) in + let result = let textOpt = Files.readFile currentFile in + completionWithParser ~debug ~path ~pos ~currentFile ~textOpt; let completionItems = match NewCompletions.getCompletable ~textOpt ~pos with | None -> [] @@ -216,10 +251,9 @@ let format ~path = Res_driver.parsingEngine.parseImplementation ~forPrinter:true ~filename:path in - if List.length diagnostics > 0 then "" - else - Res_printer.printImplementation !Res_cli.ResClflags.width structure - comments + (* if List.length diagnostics > 0 then "" + else *) + Res_printer.printImplementation !Res_cli.ResClflags.width structure comments else if Filename.check_suffix path ".resi" then let {Res_driver.parsetree = signature; comments; diagnostics} = Res_driver.parsingEngine.parseInterface ~forPrinter:true ~filename:path @@ -291,7 +325,7 @@ let test ~path = let line = line + 1 in let col = len - mlen - 3 in close_out cout; - completion ~path ~line ~col ~currentFile; + completion ~debug:true ~path ~line ~col ~currentFile; Sys.remove currentFile | "hig" -> print_endline ("Highlight " ^ path); diff --git a/analysis/src/vendor/res_outcome_printer/res_core.ml b/analysis/src/vendor/res_outcome_printer/res_core.ml index 940223486..e5015b78f 100644 --- a/analysis/src/vendor/res_outcome_printer/res_core.ml +++ b/analysis/src/vendor/res_outcome_printer/res_core.ml @@ -3527,8 +3527,11 @@ and parseValueOrConstructor p = Ast_helper.Exp.ident ~loc (Location.mkloc lident loc) | token -> Parser.next p; + let loc = mkLoc startPos p.prevEndPos in Parser.err p (Diagnostics.unexpected token p.breadcrumbs); - Recover.defaultExpr() + let lident = buildLongident ("$"::acc) in + Ast_helper.Exp.ident ~loc (Location.mkloc lident loc) + (* Recover.defaultExpr() *) in aux p [] diff --git a/analysis/tests/src/expected/CompletePrioritize1.res.txt b/analysis/tests/src/expected/CompletePrioritize1.res.txt index 7642c950e..b9ac0363d 100644 --- a/analysis/tests/src/expected/CompletePrioritize1.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize1.res.txt @@ -1,4 +1,5 @@ Complete tests/src/CompletePrioritize1.res 4:2 +Found expr:(5,2)->(5,4) [{ "label": "Test.add", "kind": 12, diff --git a/analysis/tests/src/expected/CompletePrioritize2.res.txt b/analysis/tests/src/expected/CompletePrioritize2.res.txt index e024878a3..89fb31014 100644 --- a/analysis/tests/src/expected/CompletePrioritize2.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize2.res.txt @@ -1,4 +1,5 @@ Complete tests/src/CompletePrioritize2.res 8:2 +Found expr:(9,2)->(9,4) [{ "label": "Test.add", "kind": 12, diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 946922dd0..89f92c9f7 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1,4 +1,5 @@ Complete tests/src/Completion.res 0:2 +Found expr:(1,1)->(1,9) [{ "label": "mapReverse", "kind": 12, @@ -68,6 +69,7 @@ Complete tests/src/Completion.res 0:2 }] Complete tests/src/Completion.res 1:2 +Found expr:(2,1)->(6,6) [{ "label": "Floatarray", "kind": 9, @@ -287,6 +289,7 @@ Complete tests/src/Completion.res 1:2 }] Complete tests/src/Completion.res 2:2 +Found expr:(3,1)->(3,8) [{ "label": "mapi", "kind": 12, @@ -338,6 +341,7 @@ Complete tests/src/Completion.res 2:2 }] Complete tests/src/Completion.res 12:2 +Found expr:(13,10)->(13,15) [{ "label": "customDouble", "kind": 12, @@ -347,6 +351,7 @@ Complete tests/src/Completion.res 12:2 }] Complete tests/src/Completion.res 19:2 +Found expr:(20,9)->(20,18) [{ "label": "age", "kind": 4, @@ -362,6 +367,8 @@ Complete tests/src/Completion.res 19:2 }] Complete tests/src/Completion.res 21:2 +Found expr:(22,1)->(22,11) +Found expr:(22,10)->(22,11) [{ "label": "Js.Array2.mapi", "kind": 12, @@ -377,6 +384,8 @@ Complete tests/src/Completion.res 21:2 }] Complete tests/src/Completion.res 23:2 +Found expr:(24,1)->(24,11) +Found expr:(24,8)->(24,11) [{ "label": "Js.String2.toUpperCase", "kind": 12, @@ -386,6 +395,8 @@ Complete tests/src/Completion.res 23:2 }] Complete tests/src/Completion.res 27:2 +Found expr:(28,1)->(28,6) +Found expr:(28,5)->(28,6) [{ "label": "Belt.Option.eqU", "kind": 12, @@ -401,6 +412,9 @@ Complete tests/src/Completion.res 27:2 }] Complete tests/src/Completion.res 36:2 +Found expr:(37,1)->(46,3) +Found expr:(37,1)->(41,8) +Found expr:(37,3)->(37,5) [{ "label": "ForAuto.abc", "kind": 12, @@ -416,6 +430,8 @@ Complete tests/src/Completion.res 36:2 }] Complete tests/src/Completion.res 38:2 +Found expr:(39,1)->(39,19) +Found expr:(39,10)->(39,19) [{ "label": "unsafeGet", "kind": 12, @@ -431,6 +447,8 @@ Complete tests/src/Completion.res 38:2 }] Complete tests/src/Completion.res 50:2 +Found expr:(51,13)->(51,28) +Found expr:(51,27)->(51,28) [{ "label": "zzz", "kind": 12, @@ -440,6 +458,8 @@ Complete tests/src/Completion.res 50:2 }] Complete tests/src/Completion.res 52:2 +Found expr:(53,13)->(53,21) +Found expr:(53,20)->(53,21) [{ "label": "zoo", "kind": 4, @@ -643,6 +663,7 @@ DocumentSymbol tests/src/Completion.res ] Complete tests/src/Completion.res 56:2 +XXX Not found! [{ "label": "react.component", "kind": 4, @@ -652,6 +673,7 @@ Complete tests/src/Completion.res 56:2 }] Complete tests/src/Completion.res 58:2 +Found expr:(0,-1)->(69,17) [{ "label": "component", "kind": 4, @@ -661,6 +683,7 @@ Complete tests/src/Completion.res 58:2 }] Complete tests/src/Completion.res 60:2 +Found expr:(61,9)->(61,25) [{ "label": "age", "kind": 4, @@ -670,6 +693,7 @@ Complete tests/src/Completion.res 60:2 }] Complete tests/src/Completion.res 62:2 +Found expr:(63,9)->(63,24) [{ "label": "name", "kind": 4, @@ -679,6 +703,7 @@ Complete tests/src/Completion.res 62:2 }] Complete tests/src/Completion.res 64:2 +Found expr:(65,9)->(65,30) [{ "label": "name", "kind": 4, @@ -688,6 +713,7 @@ Complete tests/src/Completion.res 64:2 }] Complete tests/src/Completion.res 67:2 +Found expr:(67,8)->(69,17) [{ "label": "age", "kind": 4, @@ -703,6 +729,7 @@ Complete tests/src/Completion.res 67:2 }] Complete tests/src/Completion.res 72:2 +Found expr:(73,8)->(75,18) [{ "label": "age", "kind": 4, @@ -712,6 +739,7 @@ Complete tests/src/Completion.res 72:2 }] Complete tests/src/Completion.res 76:2 +Found expr:(77,20)->(80,10) [{ "label": "age", "kind": 4, @@ -727,6 +755,7 @@ Complete tests/src/Completion.res 76:2 }] Complete tests/src/Completion.res 79:2 +Found expr:(80,2)->(82,20) [{ "label": "age", "kind": 4, @@ -736,6 +765,7 @@ Complete tests/src/Completion.res 79:2 }] Complete tests/src/Completion.res 83:2 +Found expr:(84,13)->(101,20) [{ "label": "name", "kind": 4, @@ -751,6 +781,7 @@ Complete tests/src/Completion.res 83:2 }] Complete tests/src/Completion.res 88:3 +Found expr:(89,1)->(93,3) [{ "label": "x", "kind": 5, @@ -766,6 +797,7 @@ Complete tests/src/Completion.res 88:3 }] Complete tests/src/Completion.res 90:3 +Found expr:(91,1)->(93,3) [{ "label": "xx", "kind": 5, @@ -781,6 +813,9 @@ Complete tests/src/Completion.res 90:3 }] Complete tests/src/Completion.res 96:3 +Found expr:(96,11)->(99,1) +Found expr:(97,1)->(98,5) +Found expr:(97,1)->(97,3) [{ "label": "myAmazingFunction", "kind": 12, @@ -790,6 +825,7 @@ Complete tests/src/Completion.res 96:3 }] Complete tests/src/Completion.res 100:3 +Found expr:(101,11)->(120,32) [{ "label": "name", "kind": 4, diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index aebbfa5a2..85e2ff73b 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -94,5 +94,6 @@ TypeDefinition tests/src/Cross.res 37:37 {"uri": "DefinitionWithInterface.resi", "range": {"start": {"line": 3, "character": 0}, "end": {"line": 3, "character": 6}}} Complete tests/src/Cross.res 39:2 +Found expr:(40,1)->(40,26) [] diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 2d9a106ab..6e065f5e7 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -2,6 +2,8 @@ Hover tests/src/Div.res 1:10 {"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array,\n) => React.element\n```"} Complete tests/src/Div.res 3:3 +Found expr:(4,2)->(4,15) +Found expr:(4,6)->(4,15) [{ "label": "dangerouslySetInnerHTML", "kind": 4, diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 6c6ff43d3..c6539888c 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -2,6 +2,8 @@ Definition tests/src/Jsx.res 5:9 {"uri": "Jsx.res", "range": {"start": {"line": 2, "character": 6}, "end": {"line": 2, "character": 10}}} Complete tests/src/Jsx.res 7:2 +Found expr:(8,2)->(8,13) +Found expr:(8,11)->(8,13) [{ "label": "first", "kind": 4, @@ -23,6 +25,8 @@ Complete tests/src/Jsx.res 7:2 }] Complete tests/src/Jsx.res 9:2 +Found expr:(10,2)->(10,18) +Found expr:(10,17)->(10,18) [{ "label": "first", "kind": 4, @@ -38,6 +42,8 @@ Complete tests/src/Jsx.res 9:2 }] Complete tests/src/Jsx.res 11:2 +Found expr:(12,10)->(12,11) +Found expr:(12,10)->(12,11) [{ "label": "second", "kind": 4, @@ -65,6 +71,8 @@ Complete tests/src/Jsx.res 11:2 }] Complete tests/src/Jsx.res 18:2 +Found expr:(19,2)->(19,17) +Found expr:(19,16)->(19,17) [{ "label": "key", "kind": 4, @@ -74,6 +82,8 @@ Complete tests/src/Jsx.res 18:2 }] Complete tests/src/Jsx.res 20:2 +Found expr:(21,2)->(21,15) +Found expr:(21,14)->(21,15) [{ "label": "key", "kind": 4, @@ -83,6 +93,8 @@ Complete tests/src/Jsx.res 20:2 }] Complete tests/src/Jsx.res 22:2 +Found expr:(23,2)->(23,19) +Found expr:(23,18)->(23,19) [{ "label": "key", "kind": 4, @@ -92,6 +104,8 @@ Complete tests/src/Jsx.res 22:2 }] Complete tests/src/Jsx.res 24:2 +Found expr:(25,2)->(25,22) +Found expr:(25,21)->(25,22) [{ "label": "key", "kind": 4, @@ -101,6 +115,8 @@ Complete tests/src/Jsx.res 24:2 }] Complete tests/src/Jsx.res 26:2 +Found expr:(27,2)->(27,16) +Found expr:(27,15)->(27,16) [{ "label": "key", "kind": 4, @@ -110,6 +126,8 @@ Complete tests/src/Jsx.res 26:2 }] Complete tests/src/Jsx.res 28:2 +Found expr:(29,2)->(29,14) +Found expr:(29,13)->(29,14) [{ "label": "key", "kind": 4, @@ -119,6 +137,8 @@ Complete tests/src/Jsx.res 28:2 }] Complete tests/src/Jsx.res 30:2 +Found expr:(31,2)->(31,15) +Found expr:(31,14)->(31,15) [{ "label": "key", "kind": 4, @@ -128,9 +148,13 @@ Complete tests/src/Jsx.res 30:2 }] Complete tests/src/Jsx.res 32:2 +Found expr:(33,2)->(33,16) +Found expr:(33,15)->(33,16) [] Complete tests/src/Jsx.res 34:2 +Found expr:(35,2)->(35,14) +Found expr:(35,13)->(35,14) [{ "label": "key", "kind": 4, @@ -140,6 +164,8 @@ Complete tests/src/Jsx.res 34:2 }] Complete tests/src/Jsx.res 36:2 +Found expr:(37,2)->(37,25) +Found expr:(37,24)->(37,25) [{ "label": "key", "kind": 4, @@ -149,6 +175,8 @@ Complete tests/src/Jsx.res 36:2 }] Complete tests/src/Jsx.res 38:2 +Found expr:(39,2)->(39,36) +Found expr:(39,35)->(39,36) [{ "label": "key", "kind": 4, @@ -158,6 +186,8 @@ Complete tests/src/Jsx.res 38:2 }] Complete tests/src/Jsx.res 40:2 +Found expr:(41,2)->(41,23) +Found expr:(41,22)->(41,23) [{ "label": "key", "kind": 4, @@ -170,6 +200,8 @@ Definition tests/src/Jsx.res 43:11 {"uri": "Component.res", "range": {"start": {"line": 1, "character": 4}, "end": {"line": 1, "character": 8}}} Complete tests/src/Jsx.res 52:2 +Found expr:(53,2)->(53,8) +Found expr:(53,6)->(53,8) [{ "label": "align", "kind": 4, @@ -179,6 +211,8 @@ Complete tests/src/Jsx.res 52:2 }] Complete tests/src/Jsx.res 54:2 +Found expr:(55,2)->(55,9) +Found expr:(55,4)->(55,9) [{ "label": "second", "kind": 4, @@ -200,6 +234,8 @@ Complete tests/src/Jsx.res 54:2 }] Complete tests/src/Jsx.res 56:2 +Found expr:(57,2)->(57,14) +Found expr:(57,13)->(57,14) [{ "label": "key", "kind": 4, @@ -209,6 +245,8 @@ Complete tests/src/Jsx.res 56:2 }] Complete tests/src/Jsx.res 58:2 +Found expr:(59,2)->(59,21) +Found expr:(59,20)->(59,21) [{ "label": "key", "kind": 4, diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index eca2e8d1c..6fb9e5c07 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -1,4 +1,6 @@ Complete tests/src/RecordCompletion.res 7:3 +Found expr:(8,1)->(8,7) +Found expr:(8,6)->(8,7) [{ "label": "Js.Array2.mapi", "kind": 12, @@ -14,6 +16,8 @@ Complete tests/src/RecordCompletion.res 7:3 }] Complete tests/src/RecordCompletion.res 9:3 +Found expr:(10,1)->(10,11) +Found expr:(10,10)->(10,11) [{ "label": "Js.Array2.mapi", "kind": 12, diff --git a/server/src/utils.ts b/server/src/utils.ts index 61af68264..32454ac47 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -106,18 +106,19 @@ export let formatCode = (filePath: string, code: string): execResult => { // Default to using the project formatter. If not, use the one we ship with // the analysis binary in the extension itself. - if (bscNativePath != null) { - let result = childProcess.execFileSync(bscNativePath, [ - "-color", - "never", - "-format", - formatTempFileFullPath, - ]); - return { - kind: "success", - result: result.toString(), - }; - } else { + // if (bscNativePath != null) { + // let result = childProcess.execFileSync(bscNativePath, [ + // "-color", + // "never", + // "-format", + // formatTempFileFullPath, + // ]); + // return { + // kind: "success", + // result: result.toString(), + // }; + // } else + { let result = runAnalysisAfterSanityCheck( formatTempFileFullPath, ["format", formatTempFileFullPath], @@ -127,9 +128,9 @@ export let formatCode = (filePath: string, code: string): execResult => { // The formatter returning an empty string means it couldn't format the // sources, probably because of errors. In that case, we bail from // formatting by returning the unformatted content. - if (result === "") { - result = code; - } + // if (result === "") { + // result = code; + // } return { kind: "success", From b04e01394fc4de0c07eb364d92dfc94fe68b03fa Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 11 Apr 2022 17:43:22 +0200 Subject: [PATCH 002/135] Find the location of prop labels in JSX. --- analysis/src/Commands.ml | 70 ++++++++++++++++- .../src/expected/CompletePrioritize1.res.txt | 2 +- .../src/expected/CompletePrioritize2.res.txt | 2 +- .../tests/src/expected/Completion.res.txt | 70 ++++++++--------- analysis/tests/src/expected/Cross.res.txt | 2 +- analysis/tests/src/expected/Div.res.txt | 4 +- analysis/tests/src/expected/Jsx.res.txt | 76 +++++++++---------- .../src/expected/RecordCompletion.res.txt | 8 +- 8 files changed, 150 insertions(+), 84 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 848d11824..7c6c5cb99 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -4,6 +4,57 @@ let posInLoc ~pos ~loc = Utils.tupleOfLexing loc.Location.loc_start <= pos && pos < Utils.tupleOfLexing loc.loc_end +let extractJsxProps ~text (expr : Parsetree.expression) = + let rec extractLabelPos ~pos ~i str = + if i < String.length str then + match str.[i] with + | ' ' | '\r' | '\t' -> + extractLabelPos ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str + | '\n' -> extractLabelPos ~pos:(fst pos + 1, 0) ~i:(i + 1) str + | 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' -> Some pos + | _ -> None + else None + in + match expr.pexp_desc with + | Pexp_apply (eComp, args) -> + (* To check if tha prop is punned, take the string between the last prop + (or the end of e) and the expr in the arg. + If it's whitespace only, then it's a punned . + If it's "?" then it's a punned optional . + Otherwise it should be "id =" perhaps followed by "?". + *) + let rec processProps ~lastOffset ~lastPos args = + match args with + | (Asttypes.Labelled "children", _) :: _ -> [] + | ((Labelled s | Optional s), (eProp : Parsetree.expression)) :: rest -> ( + let ePosStart = Utils.tupleOfLexing eProp.pexp_loc.loc_start in + let ePosEnd = Utils.tupleOfLexing eProp.pexp_loc.loc_end in + match + ( PartialParser.positionToOffset text ePosStart, + PartialParser.positionToOffset text ePosEnd ) + with + | Some offsetStart, Some offsetEnd -> + let label = String.sub text lastOffset (offsetStart - lastOffset) in + let labelPos = + match extractLabelPos ~pos:lastPos ~i:0 label with + | Some pos -> pos + | None -> (* Must be punned *) ePosStart + in + (s, labelPos, eProp) + :: processProps ~lastOffset:offsetEnd ~lastPos:ePosEnd rest + | _ -> assert false) + | _ -> [] + in + let posAfterCompName = Utils.tupleOfLexing eComp.pexp_loc.loc_end in + let offsetAfterCompName = + match PartialParser.positionToOffset text posAfterCompName with + | None -> assert false + | Some offset -> offset + in + args + |> processProps ~lastOffset:offsetAfterCompName ~lastPos:posAfterCompName + | _ -> [] + let completionWithParser ~debug ~path ~pos ~currentFile ~textOpt = let text = match textOpt with Some text -> text | None -> assert false in let offset = @@ -23,9 +74,24 @@ let completionWithParser ~debug ~path ~pos ~currentFile ~textOpt = let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = if posInLoc ~pos:posNoWhite ~loc:expr.pexp_loc then ( found := true; + let exprKind = + match expr.pexp_desc with + | Pexp_apply _ when Res_parsetree_viewer.isJsxExpression expr -> + let props = extractJsxProps ~text expr in + " JSX " + ^ (props + |> List.map (fun (lbl, lblPos, (eProp : Parsetree.expression)) -> + Printf.sprintf "(%s:%s e:%s)" lbl + (SemanticTokens.posToString lblPos) + (SemanticTokens.locToString eProp.pexp_loc)) + |> String.concat ", ") + | _ -> "" + in if debug then - Printf.printf "Found expr:%s\n" - (SemanticTokens.locToString expr.pexp_loc)); + Printf.printf "posNoWhite:%s Found expr:%s%s\n" + (SemanticTokens.posToString posNoWhite ^ " ") + (SemanticTokens.locToString expr.pexp_loc) + exprKind); Ast_iterator.default_iterator.expr iterator expr in let {Res_driver.parsetree = structure} = parser ~filename:currentFile in diff --git a/analysis/tests/src/expected/CompletePrioritize1.res.txt b/analysis/tests/src/expected/CompletePrioritize1.res.txt index b9ac0363d..a4d4971fd 100644 --- a/analysis/tests/src/expected/CompletePrioritize1.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize1.res.txt @@ -1,5 +1,5 @@ Complete tests/src/CompletePrioritize1.res 4:2 -Found expr:(5,2)->(5,4) +posNoWhite:(5,3) Found expr:(5,2)->(5,4) [{ "label": "Test.add", "kind": 12, diff --git a/analysis/tests/src/expected/CompletePrioritize2.res.txt b/analysis/tests/src/expected/CompletePrioritize2.res.txt index 89fb31014..6325b7f95 100644 --- a/analysis/tests/src/expected/CompletePrioritize2.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize2.res.txt @@ -1,5 +1,5 @@ Complete tests/src/CompletePrioritize2.res 8:2 -Found expr:(9,2)->(9,4) +posNoWhite:(9,3) Found expr:(9,2)->(9,4) [{ "label": "Test.add", "kind": 12, diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 89f92c9f7..2523f082e 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1,5 +1,5 @@ Complete tests/src/Completion.res 0:2 -Found expr:(1,1)->(1,9) +posNoWhite:(1,8) Found expr:(1,1)->(1,9) [{ "label": "mapReverse", "kind": 12, @@ -69,7 +69,7 @@ Found expr:(1,1)->(1,9) }] Complete tests/src/Completion.res 1:2 -Found expr:(2,1)->(6,6) +posNoWhite:(2,6) Found expr:(2,1)->(6,6) [{ "label": "Floatarray", "kind": 9, @@ -289,7 +289,7 @@ Found expr:(2,1)->(6,6) }] Complete tests/src/Completion.res 2:2 -Found expr:(3,1)->(3,8) +posNoWhite:(3,7) Found expr:(3,1)->(3,8) [{ "label": "mapi", "kind": 12, @@ -341,7 +341,7 @@ Found expr:(3,1)->(3,8) }] Complete tests/src/Completion.res 12:2 -Found expr:(13,10)->(13,15) +posNoWhite:(13,14) Found expr:(13,10)->(13,15) [{ "label": "customDouble", "kind": 12, @@ -351,7 +351,7 @@ Found expr:(13,10)->(13,15) }] Complete tests/src/Completion.res 19:2 -Found expr:(20,9)->(20,18) +posNoWhite:(20,17) Found expr:(20,9)->(20,18) [{ "label": "age", "kind": 4, @@ -367,8 +367,8 @@ Found expr:(20,9)->(20,18) }] Complete tests/src/Completion.res 21:2 -Found expr:(22,1)->(22,11) -Found expr:(22,10)->(22,11) +posNoWhite:(22,10) Found expr:(22,1)->(22,11) +posNoWhite:(22,10) Found expr:(22,10)->(22,11) [{ "label": "Js.Array2.mapi", "kind": 12, @@ -384,8 +384,8 @@ Found expr:(22,10)->(22,11) }] Complete tests/src/Completion.res 23:2 -Found expr:(24,1)->(24,11) -Found expr:(24,8)->(24,11) +posNoWhite:(24,10) Found expr:(24,1)->(24,11) +posNoWhite:(24,10) Found expr:(24,8)->(24,11) [{ "label": "Js.String2.toUpperCase", "kind": 12, @@ -395,8 +395,8 @@ Found expr:(24,8)->(24,11) }] Complete tests/src/Completion.res 27:2 -Found expr:(28,1)->(28,6) -Found expr:(28,5)->(28,6) +posNoWhite:(28,5) Found expr:(28,1)->(28,6) +posNoWhite:(28,5) Found expr:(28,5)->(28,6) [{ "label": "Belt.Option.eqU", "kind": 12, @@ -412,9 +412,9 @@ Found expr:(28,5)->(28,6) }] Complete tests/src/Completion.res 36:2 -Found expr:(37,1)->(46,3) -Found expr:(37,1)->(41,8) -Found expr:(37,3)->(37,5) +posNoWhite:(37,4) Found expr:(37,1)->(46,3) +posNoWhite:(37,4) Found expr:(37,1)->(41,8) +posNoWhite:(37,4) Found expr:(37,3)->(37,5) [{ "label": "ForAuto.abc", "kind": 12, @@ -430,8 +430,8 @@ Found expr:(37,3)->(37,5) }] Complete tests/src/Completion.res 38:2 -Found expr:(39,1)->(39,19) -Found expr:(39,10)->(39,19) +posNoWhite:(39,18) Found expr:(39,1)->(39,19) +posNoWhite:(39,18) Found expr:(39,10)->(39,19) [{ "label": "unsafeGet", "kind": 12, @@ -447,8 +447,8 @@ Found expr:(39,10)->(39,19) }] Complete tests/src/Completion.res 50:2 -Found expr:(51,13)->(51,28) -Found expr:(51,27)->(51,28) +posNoWhite:(51,27) Found expr:(51,13)->(51,28) JSX (second:(51,20) e:(51,27)->(51,28)) +posNoWhite:(51,27) Found expr:(51,27)->(51,28) [{ "label": "zzz", "kind": 12, @@ -458,8 +458,8 @@ Found expr:(51,27)->(51,28) }] Complete tests/src/Completion.res 52:2 -Found expr:(53,13)->(53,21) -Found expr:(53,20)->(53,21) +posNoWhite:(53,20) Found expr:(53,13)->(53,21) JSX (z:(53,20) e:(53,20)->(53,21)) +posNoWhite:(53,20) Found expr:(53,20)->(53,21) [{ "label": "zoo", "kind": 4, @@ -673,7 +673,7 @@ XXX Not found! }] Complete tests/src/Completion.res 58:2 -Found expr:(0,-1)->(69,17) +posNoWhite:(59,7) Found expr:(0,-1)->(69,17) [{ "label": "component", "kind": 4, @@ -683,7 +683,7 @@ Found expr:(0,-1)->(69,17) }] Complete tests/src/Completion.res 60:2 -Found expr:(61,9)->(61,25) +posNoWhite:(61,24) Found expr:(61,9)->(61,25) [{ "label": "age", "kind": 4, @@ -693,7 +693,7 @@ Found expr:(61,9)->(61,25) }] Complete tests/src/Completion.res 62:2 -Found expr:(63,9)->(63,24) +posNoWhite:(63,23) Found expr:(63,9)->(63,24) [{ "label": "name", "kind": 4, @@ -703,7 +703,7 @@ Found expr:(63,9)->(63,24) }] Complete tests/src/Completion.res 64:2 -Found expr:(65,9)->(65,30) +posNoWhite:(65,29) Found expr:(65,9)->(65,30) [{ "label": "name", "kind": 4, @@ -713,7 +713,7 @@ Found expr:(65,9)->(65,30) }] Complete tests/src/Completion.res 67:2 -Found expr:(67,8)->(69,17) +posNoWhite:(68,1) Found expr:(67,8)->(69,17) [{ "label": "age", "kind": 4, @@ -729,7 +729,7 @@ Found expr:(67,8)->(69,17) }] Complete tests/src/Completion.res 72:2 -Found expr:(73,8)->(75,18) +posNoWhite:(73,10) Found expr:(73,8)->(75,18) [{ "label": "age", "kind": 4, @@ -739,7 +739,7 @@ Found expr:(73,8)->(75,18) }] Complete tests/src/Completion.res 76:2 -Found expr:(77,20)->(80,10) +posNoWhite:(77,21) Found expr:(77,20)->(80,10) [{ "label": "age", "kind": 4, @@ -755,7 +755,7 @@ Found expr:(77,20)->(80,10) }] Complete tests/src/Completion.res 79:2 -Found expr:(80,2)->(82,20) +posNoWhite:(80,4) Found expr:(80,2)->(82,20) [{ "label": "age", "kind": 4, @@ -765,7 +765,7 @@ Found expr:(80,2)->(82,20) }] Complete tests/src/Completion.res 83:2 -Found expr:(84,13)->(101,20) +posNoWhite:(84,14) Found expr:(84,13)->(101,20) [{ "label": "name", "kind": 4, @@ -781,7 +781,7 @@ Found expr:(84,13)->(101,20) }] Complete tests/src/Completion.res 88:3 -Found expr:(89,1)->(93,3) +posNoWhite:(89,2) Found expr:(89,1)->(93,3) [{ "label": "x", "kind": 5, @@ -797,7 +797,7 @@ Found expr:(89,1)->(93,3) }] Complete tests/src/Completion.res 90:3 -Found expr:(91,1)->(93,3) +posNoWhite:(91,18) Found expr:(91,1)->(93,3) [{ "label": "xx", "kind": 5, @@ -813,9 +813,9 @@ Found expr:(91,1)->(93,3) }] Complete tests/src/Completion.res 96:3 -Found expr:(96,11)->(99,1) -Found expr:(97,1)->(98,5) -Found expr:(97,1)->(97,3) +posNoWhite:(97,2) Found expr:(96,11)->(99,1) +posNoWhite:(97,2) Found expr:(97,1)->(98,5) +posNoWhite:(97,2) Found expr:(97,1)->(97,3) [{ "label": "myAmazingFunction", "kind": 12, @@ -825,7 +825,7 @@ Found expr:(97,1)->(97,3) }] Complete tests/src/Completion.res 100:3 -Found expr:(101,11)->(120,32) +posNoWhite:(101,12) Found expr:(101,11)->(120,32) [{ "label": "name", "kind": 4, diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index 85e2ff73b..f7a3602e0 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -94,6 +94,6 @@ TypeDefinition tests/src/Cross.res 37:37 {"uri": "DefinitionWithInterface.resi", "range": {"start": {"line": 3, "character": 0}, "end": {"line": 3, "character": 6}}} Complete tests/src/Cross.res 39:2 -Found expr:(40,1)->(40,26) +posNoWhite:(40,25) Found expr:(40,1)->(40,26) [] diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 6e065f5e7..675701430 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -2,8 +2,8 @@ Hover tests/src/Div.res 1:10 {"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array,\n) => React.element\n```"} Complete tests/src/Div.res 3:3 -Found expr:(4,2)->(4,15) -Found expr:(4,6)->(4,15) +posNoWhite:(4,14) Found expr:(4,2)->(4,15) JSX (dangerous:(4,6) e:(4,6)->(4,15)) +posNoWhite:(4,14) Found expr:(4,6)->(4,15) [{ "label": "dangerouslySetInnerHTML", "kind": 4, diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index c6539888c..81d3b23d7 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -2,8 +2,8 @@ Definition tests/src/Jsx.res 5:9 {"uri": "Jsx.res", "range": {"start": {"line": 2, "character": 6}, "end": {"line": 2, "character": 10}}} Complete tests/src/Jsx.res 7:2 -Found expr:(8,2)->(8,13) -Found expr:(8,11)->(8,13) +posNoWhite:(8,12) Found expr:(8,2)->(8,13) JSX (second:(8,4) e:(8,11)->(8,13)) +posNoWhite:(8,12) Found expr:(8,11)->(8,13) [{ "label": "first", "kind": 4, @@ -25,8 +25,8 @@ Found expr:(8,11)->(8,13) }] Complete tests/src/Jsx.res 9:2 -Found expr:(10,2)->(10,18) -Found expr:(10,17)->(10,18) +posNoWhite:(10,17) Found expr:(10,2)->(10,18) JSX (second:(10,4) e:(10,11)->(10,16)), (f:(10,17) e:(10,17)->(10,18)) +posNoWhite:(10,17) Found expr:(10,17)->(10,18) [{ "label": "first", "kind": 4, @@ -42,8 +42,8 @@ Found expr:(10,17)->(10,18) }] Complete tests/src/Jsx.res 11:2 -Found expr:(12,10)->(12,11) -Found expr:(12,10)->(12,11) +posNoWhite:(12,10) Found expr:(12,10)->(12,11) JSX +posNoWhite:(12,10) Found expr:(12,10)->(12,11) [{ "label": "second", "kind": 4, @@ -71,8 +71,8 @@ Found expr:(12,10)->(12,11) }] Complete tests/src/Jsx.res 18:2 -Found expr:(19,2)->(19,17) -Found expr:(19,16)->(19,17) +posNoWhite:(19,16) Found expr:(19,2)->(19,17) JSX (prop:(19,4) e:(19,10)->(19,14)), (k:(19,16) e:(19,16)->(19,17)) +posNoWhite:(19,16) Found expr:(19,16)->(19,17) [{ "label": "key", "kind": 4, @@ -82,8 +82,8 @@ Found expr:(19,16)->(19,17) }] Complete tests/src/Jsx.res 20:2 -Found expr:(21,2)->(21,15) -Found expr:(21,14)->(21,15) +posNoWhite:(21,14) Found expr:(21,2)->(21,15) JSX (prop:(21,4) e:(21,9)->(21,13)), (k:(21,14) e:(21,14)->(21,15)) +posNoWhite:(21,14) Found expr:(21,14)->(21,15) [{ "label": "key", "kind": 4, @@ -93,8 +93,8 @@ Found expr:(21,14)->(21,15) }] Complete tests/src/Jsx.res 22:2 -Found expr:(23,2)->(23,19) -Found expr:(23,18)->(23,19) +posNoWhite:(23,18) Found expr:(23,2)->(23,19) JSX (prop:(23,4) e:(23,9)->(23,17)), (k:(23,18) e:(23,18)->(23,19)) +posNoWhite:(23,18) Found expr:(23,18)->(23,19) [{ "label": "key", "kind": 4, @@ -104,8 +104,8 @@ Found expr:(23,18)->(23,19) }] Complete tests/src/Jsx.res 24:2 -Found expr:(25,2)->(25,22) -Found expr:(25,21)->(25,22) +posNoWhite:(25,21) Found expr:(25,2)->(25,22) JSX (prop:(25,4) e:(25,9)->(25,20)), (k:(25,21) e:(25,21)->(25,22)) +posNoWhite:(25,21) Found expr:(25,21)->(25,22) [{ "label": "key", "kind": 4, @@ -115,8 +115,8 @@ Found expr:(25,21)->(25,22) }] Complete tests/src/Jsx.res 26:2 -Found expr:(27,2)->(27,16) -Found expr:(27,15)->(27,16) +posNoWhite:(27,15) Found expr:(27,2)->(27,16) JSX (prop:(27,4) e:(27,10)->(27,14)), (k:(27,15) e:(27,15)->(27,16)) +posNoWhite:(27,15) Found expr:(27,15)->(27,16) [{ "label": "key", "kind": 4, @@ -126,8 +126,8 @@ Found expr:(27,15)->(27,16) }] Complete tests/src/Jsx.res 28:2 -Found expr:(29,2)->(29,14) -Found expr:(29,13)->(29,14) +posNoWhite:(29,13) Found expr:(29,2)->(29,14) JSX (prop:(29,4) e:(29,9)->(29,12)), (k:(29,13) e:(29,13)->(29,14)) +posNoWhite:(29,13) Found expr:(29,13)->(29,14) [{ "label": "key", "kind": 4, @@ -137,8 +137,8 @@ Found expr:(29,13)->(29,14) }] Complete tests/src/Jsx.res 30:2 -Found expr:(31,2)->(31,15) -Found expr:(31,14)->(31,15) +posNoWhite:(31,14) Found expr:(31,2)->(31,15) JSX (prop:(31,4) e:(31,9)->(31,13)), (k:(31,14) e:(31,14)->(31,15)) +posNoWhite:(31,14) Found expr:(31,14)->(31,15) [{ "label": "key", "kind": 4, @@ -148,13 +148,13 @@ Found expr:(31,14)->(31,15) }] Complete tests/src/Jsx.res 32:2 -Found expr:(33,2)->(33,16) -Found expr:(33,15)->(33,16) +posNoWhite:(33,15) Found expr:(33,2)->(33,16) JSX (prop:(33,4) e:(33,9)->(33,14)), (k:(33,15) e:(33,15)->(33,16)) +posNoWhite:(33,15) Found expr:(33,15)->(33,16) [] Complete tests/src/Jsx.res 34:2 -Found expr:(35,2)->(35,14) -Found expr:(35,13)->(35,14) +posNoWhite:(35,13) Found expr:(35,2)->(35,14) JSX (prop:(35,4) e:(35,9)->(35,12)), (k:(35,13) e:(35,13)->(35,14)) +posNoWhite:(35,13) Found expr:(35,13)->(35,14) [{ "label": "key", "kind": 4, @@ -164,8 +164,8 @@ Found expr:(35,13)->(35,14) }] Complete tests/src/Jsx.res 36:2 -Found expr:(37,2)->(37,25) -Found expr:(37,24)->(37,25) +posNoWhite:(37,24) Found expr:(37,2)->(37,25) JSX (prop:(37,4) e:(37,9)->(37,23)), (k:(37,24) e:(37,24)->(37,25)) +posNoWhite:(37,24) Found expr:(37,24)->(37,25) [{ "label": "key", "kind": 4, @@ -175,8 +175,8 @@ Found expr:(37,24)->(37,25) }] Complete tests/src/Jsx.res 38:2 -Found expr:(39,2)->(39,36) -Found expr:(39,35)->(39,36) +posNoWhite:(39,35) Found expr:(39,2)->(39,36) JSX (prop:(39,4) e:(39,9)->(39,34)), (k:(39,35) e:(39,35)->(39,36)) +posNoWhite:(39,35) Found expr:(39,35)->(39,36) [{ "label": "key", "kind": 4, @@ -186,8 +186,8 @@ Found expr:(39,35)->(39,36) }] Complete tests/src/Jsx.res 40:2 -Found expr:(41,2)->(41,23) -Found expr:(41,22)->(41,23) +posNoWhite:(41,22) Found expr:(41,2)->(41,23) JSX (prop:(41,4) e:(41,9)->(41,21)), (k:(41,22) e:(41,22)->(41,23)) +posNoWhite:(41,22) Found expr:(41,22)->(41,23) [{ "label": "key", "kind": 4, @@ -200,8 +200,8 @@ Definition tests/src/Jsx.res 43:11 {"uri": "Component.res", "range": {"start": {"line": 1, "character": 4}, "end": {"line": 1, "character": 8}}} Complete tests/src/Jsx.res 52:2 -Found expr:(53,2)->(53,8) -Found expr:(53,6)->(53,8) +posNoWhite:(53,7) Found expr:(53,2)->(53,8) JSX (al:(53,6) e:(53,6)->(53,8)) +posNoWhite:(53,7) Found expr:(53,6)->(53,8) [{ "label": "align", "kind": 4, @@ -211,8 +211,8 @@ Found expr:(53,6)->(53,8) }] Complete tests/src/Jsx.res 54:2 -Found expr:(55,2)->(55,9) -Found expr:(55,4)->(55,9) +posNoWhite:(55,8) Found expr:(55,2)->(55,9) JSX (first:(55,4) e:(55,4)->(55,9)) +posNoWhite:(55,8) Found expr:(55,4)->(55,9) [{ "label": "second", "kind": 4, @@ -234,8 +234,8 @@ Found expr:(55,4)->(55,9) }] Complete tests/src/Jsx.res 56:2 -Found expr:(57,2)->(57,14) -Found expr:(57,13)->(57,14) +posNoWhite:(57,13) Found expr:(57,2)->(57,14) JSX (first:(57,4) e:(57,10)->(57,12)), (k:(57,13) e:(57,13)->(57,14)) +posNoWhite:(57,13) Found expr:(57,13)->(57,14) [{ "label": "key", "kind": 4, @@ -245,8 +245,8 @@ Found expr:(57,13)->(57,14) }] Complete tests/src/Jsx.res 58:2 -Found expr:(59,2)->(59,21) -Found expr:(59,20)->(59,21) +posNoWhite:(59,20) Found expr:(59,2)->(59,21) JSX (first:(59,4) e:(59,17)->(59,19)), (k:(59,20) e:(59,20)->(59,21)) +posNoWhite:(59,20) Found expr:(59,20)->(59,21) [{ "label": "key", "kind": 4, diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index 6fb9e5c07..41a63ff5b 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -1,6 +1,6 @@ Complete tests/src/RecordCompletion.res 7:3 -Found expr:(8,1)->(8,7) -Found expr:(8,6)->(8,7) +posNoWhite:(8,6) Found expr:(8,1)->(8,7) +posNoWhite:(8,6) Found expr:(8,6)->(8,7) [{ "label": "Js.Array2.mapi", "kind": 12, @@ -16,8 +16,8 @@ Found expr:(8,6)->(8,7) }] Complete tests/src/RecordCompletion.res 9:3 -Found expr:(10,1)->(10,11) -Found expr:(10,10)->(10,11) +posNoWhite:(10,10) Found expr:(10,1)->(10,11) +posNoWhite:(10,10) Found expr:(10,10)->(10,11) [{ "label": "Js.Array2.mapi", "kind": 12, From def4820f99b05ca7f5c12f9f3089a6a53be05cf0 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 12 Apr 2022 08:50:22 +0200 Subject: [PATCH 003/135] Show completable in tests. --- analysis/src/Commands.ml | 3 +++ analysis/src/PartialParser.ml | 20 ++++++++++++++ .../src/expected/CompletePrioritize1.res.txt | 1 + .../src/expected/CompletePrioritize2.res.txt | 1 + .../tests/src/expected/Completion.res.txt | 26 +++++++++++++++++++ analysis/tests/src/expected/Cross.res.txt | 1 + analysis/tests/src/expected/Div.res.txt | 1 + analysis/tests/src/expected/Jsx.res.txt | 19 ++++++++++++++ .../src/expected/RecordCompletion.res.txt | 2 ++ 9 files changed, 74 insertions(+) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 7c6c5cb99..0296b043d 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -109,6 +109,9 @@ let completion ~debug ~path ~line ~col ~currentFile = match NewCompletions.getCompletable ~textOpt ~pos with | None -> [] | Some (completable, rawOpens) -> ( + if debug then + Printf.printf "Completable: %s\n" + (PartialParser.completableToString completable); (* Only perform expensive ast operations if there are completables *) match Cmt.fromPath ~path with | None -> [] diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 446dcbd7f..5212fd767 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -196,6 +196,26 @@ type completable = (** e.g. (["M", "foo"], ["a", "b"], "bar") for M.foo["a"]["b"]["bar" *) | Cpipe of pipe * string (** E.g. ("x", "foo") for "x->foo" *) +let completableToString = + let str s = if s = "" then "\"\"" else s in + let list l = "[" ^ (l |> List.map str |> String.concat ", ") ^ "]" in + function + | Cdecorator s -> "Cdecorator(" ^ str s ^ ")" + | Clabel (sl1, s, sl2) -> + "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" + | Cdotpath sl -> "Cdotpath(" ^ (sl |> list) ^ ")" + | Cjsx (sl1, s, sl2) -> + "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" + | Cobj (sl1, sl2, s) -> + "Cobj(" ^ (sl1 |> list) ^ ", " ^ (sl2 |> list) ^ ", " ^ str s ^ ")" + | Cpipe (pipe, s) -> + "Cpipe(" + ^ (match pipe with + | PipeId s -> str s + | PipeArray -> "PipeArray" + | PipeString -> "PipeString") + ^ ", " ^ str s ^ ")" + let isLowercaseIdent id = let rec loop i = if i < 0 then true diff --git a/analysis/tests/src/expected/CompletePrioritize1.res.txt b/analysis/tests/src/expected/CompletePrioritize1.res.txt index a4d4971fd..f0bb99e7b 100644 --- a/analysis/tests/src/expected/CompletePrioritize1.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize1.res.txt @@ -1,5 +1,6 @@ Complete tests/src/CompletePrioritize1.res 4:2 posNoWhite:(5,3) Found expr:(5,2)->(5,4) +Completable: Cpipe(a, "") [{ "label": "Test.add", "kind": 12, diff --git a/analysis/tests/src/expected/CompletePrioritize2.res.txt b/analysis/tests/src/expected/CompletePrioritize2.res.txt index 6325b7f95..b248c50c4 100644 --- a/analysis/tests/src/expected/CompletePrioritize2.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize2.res.txt @@ -1,5 +1,6 @@ Complete tests/src/CompletePrioritize2.res 8:2 posNoWhite:(9,3) Found expr:(9,2)->(9,4) +Completable: Cpipe(a, "") [{ "label": "Test.add", "kind": 12, diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 2523f082e..da535db53 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1,5 +1,6 @@ Complete tests/src/Completion.res 0:2 posNoWhite:(1,8) Found expr:(1,1)->(1,9) +Completable: Cdotpath([MyList, m]) [{ "label": "mapReverse", "kind": 12, @@ -70,6 +71,7 @@ posNoWhite:(1,8) Found expr:(1,1)->(1,9) Complete tests/src/Completion.res 1:2 posNoWhite:(2,6) Found expr:(2,1)->(6,6) +Completable: Cdotpath([Array, ""]) [{ "label": "Floatarray", "kind": 9, @@ -290,6 +292,7 @@ posNoWhite:(2,6) Found expr:(2,1)->(6,6) Complete tests/src/Completion.res 2:2 posNoWhite:(3,7) Found expr:(3,1)->(3,8) +Completable: Cdotpath([Array, m]) [{ "label": "mapi", "kind": 12, @@ -342,6 +345,7 @@ posNoWhite:(3,7) Found expr:(3,1)->(3,8) Complete tests/src/Completion.res 12:2 posNoWhite:(13,14) Found expr:(13,10)->(13,15) +Completable: Cdotpath([Dep, c]) [{ "label": "customDouble", "kind": 12, @@ -352,6 +356,7 @@ posNoWhite:(13,14) Found expr:(13,10)->(13,15) Complete tests/src/Completion.res 19:2 posNoWhite:(20,17) Found expr:(20,9)->(20,18) +Completable: Clabel([Lib, foo], "", []) [{ "label": "age", "kind": 4, @@ -369,6 +374,7 @@ posNoWhite:(20,17) Found expr:(20,9)->(20,18) Complete tests/src/Completion.res 21:2 posNoWhite:(22,10) Found expr:(22,1)->(22,11) posNoWhite:(22,10) Found expr:(22,10)->(22,11) +Completable: Cpipe(PipeArray, m) [{ "label": "Js.Array2.mapi", "kind": 12, @@ -386,6 +392,7 @@ posNoWhite:(22,10) Found expr:(22,10)->(22,11) Complete tests/src/Completion.res 23:2 posNoWhite:(24,10) Found expr:(24,1)->(24,11) posNoWhite:(24,10) Found expr:(24,8)->(24,11) +Completable: Cpipe(PipeString, toU) [{ "label": "Js.String2.toUpperCase", "kind": 12, @@ -397,6 +404,7 @@ posNoWhite:(24,10) Found expr:(24,8)->(24,11) Complete tests/src/Completion.res 27:2 posNoWhite:(28,5) Found expr:(28,1)->(28,6) posNoWhite:(28,5) Found expr:(28,5)->(28,6) +Completable: Cpipe(op, e) [{ "label": "Belt.Option.eqU", "kind": 12, @@ -415,6 +423,7 @@ Complete tests/src/Completion.res 36:2 posNoWhite:(37,4) Found expr:(37,1)->(46,3) posNoWhite:(37,4) Found expr:(37,1)->(41,8) posNoWhite:(37,4) Found expr:(37,3)->(37,5) +Completable: Cpipe(fa, "") [{ "label": "ForAuto.abc", "kind": 12, @@ -432,6 +441,7 @@ posNoWhite:(37,4) Found expr:(37,3)->(37,5) Complete tests/src/Completion.res 38:2 posNoWhite:(39,18) Found expr:(39,1)->(39,19) posNoWhite:(39,18) Found expr:(39,10)->(39,19) +Completable: Cdotpath([Js, Dict, u]) [{ "label": "unsafeGet", "kind": 12, @@ -449,6 +459,7 @@ posNoWhite:(39,18) Found expr:(39,10)->(39,19) Complete tests/src/Completion.res 50:2 posNoWhite:(51,27) Found expr:(51,13)->(51,28) JSX (second:(51,20) e:(51,27)->(51,28)) posNoWhite:(51,27) Found expr:(51,27)->(51,28) +Completable: Cdotpath([z]) [{ "label": "zzz", "kind": 12, @@ -460,6 +471,7 @@ posNoWhite:(51,27) Found expr:(51,27)->(51,28) Complete tests/src/Completion.res 52:2 posNoWhite:(53,20) Found expr:(53,13)->(53,21) JSX (z:(53,20) e:(53,20)->(53,21)) posNoWhite:(53,20) Found expr:(53,20)->(53,21) +Completable: Cjsx([O, Comp], z, []) [{ "label": "zoo", "kind": 4, @@ -664,6 +676,7 @@ DocumentSymbol tests/src/Completion.res Complete tests/src/Completion.res 56:2 XXX Not found! +Completable: Cdecorator(reac) [{ "label": "react.component", "kind": 4, @@ -674,6 +687,7 @@ XXX Not found! Complete tests/src/Completion.res 58:2 posNoWhite:(59,7) Found expr:(0,-1)->(69,17) +Completable: Cdecorator(react.) [{ "label": "component", "kind": 4, @@ -684,6 +698,7 @@ posNoWhite:(59,7) Found expr:(0,-1)->(69,17) Complete tests/src/Completion.res 60:2 posNoWhite:(61,24) Found expr:(61,9)->(61,25) +Completable: Clabel([Lib, foo], "", [name]) [{ "label": "age", "kind": 4, @@ -694,6 +709,7 @@ posNoWhite:(61,24) Found expr:(61,9)->(61,25) Complete tests/src/Completion.res 62:2 posNoWhite:(63,23) Found expr:(63,9)->(63,24) +Completable: Clabel([Lib, foo], "", [age]) [{ "label": "name", "kind": 4, @@ -704,6 +720,7 @@ posNoWhite:(63,23) Found expr:(63,9)->(63,24) Complete tests/src/Completion.res 64:2 posNoWhite:(65,29) Found expr:(65,9)->(65,30) +Completable: Clabel([Lib, foo], "", [age]) [{ "label": "name", "kind": 4, @@ -714,6 +731,7 @@ posNoWhite:(65,29) Found expr:(65,9)->(65,30) Complete tests/src/Completion.res 67:2 posNoWhite:(68,1) Found expr:(67,8)->(69,17) +Completable: Clabel([Lib, foo], "", []) [{ "label": "age", "kind": 4, @@ -730,6 +748,7 @@ posNoWhite:(68,1) Found expr:(67,8)->(69,17) Complete tests/src/Completion.res 72:2 posNoWhite:(73,10) Found expr:(73,8)->(75,18) +Completable: Cobj([someObj], [], a) [{ "label": "age", "kind": 4, @@ -740,6 +759,7 @@ posNoWhite:(73,10) Found expr:(73,8)->(75,18) Complete tests/src/Completion.res 76:2 posNoWhite:(77,21) Found expr:(77,20)->(80,10) +Completable: Cobj([nestedObj], [x, y], "") [{ "label": "age", "kind": 4, @@ -756,6 +776,7 @@ posNoWhite:(77,21) Found expr:(77,20)->(80,10) Complete tests/src/Completion.res 79:2 posNoWhite:(80,4) Found expr:(80,2)->(82,20) +Completable: Cobj([o], [], a) [{ "label": "age", "kind": 4, @@ -766,6 +787,7 @@ posNoWhite:(80,4) Found expr:(80,2)->(82,20) Complete tests/src/Completion.res 83:2 posNoWhite:(84,14) Found expr:(84,13)->(101,20) +Completable: Cobj([no], [x, y], "") [{ "label": "name", "kind": 4, @@ -782,6 +804,7 @@ posNoWhite:(84,14) Found expr:(84,13)->(101,20) Complete tests/src/Completion.res 88:3 posNoWhite:(89,2) Found expr:(89,1)->(93,3) +Completable: Cdotpath([r, ""]) [{ "label": "x", "kind": 5, @@ -798,6 +821,7 @@ posNoWhite:(89,2) Found expr:(89,1)->(93,3) Complete tests/src/Completion.res 90:3 posNoWhite:(91,18) Found expr:(91,1)->(93,3) +Completable: Cdotpath([Obj, Rec, recordVal, ""]) [{ "label": "xx", "kind": 5, @@ -816,6 +840,7 @@ Complete tests/src/Completion.res 96:3 posNoWhite:(97,2) Found expr:(96,11)->(99,1) posNoWhite:(97,2) Found expr:(97,1)->(98,5) posNoWhite:(97,2) Found expr:(97,1)->(97,3) +Completable: Cdotpath([my]) [{ "label": "myAmazingFunction", "kind": 12, @@ -826,6 +851,7 @@ posNoWhite:(97,2) Found expr:(97,1)->(97,3) Complete tests/src/Completion.res 100:3 posNoWhite:(101,12) Found expr:(101,11)->(120,32) +Completable: Cobj([Obj, object], [], "") [{ "label": "name", "kind": 4, diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index f7a3602e0..154a3435b 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -95,5 +95,6 @@ TypeDefinition tests/src/Cross.res 37:37 Complete tests/src/Cross.res 39:2 posNoWhite:(40,25) Found expr:(40,1)->(40,26) +Completable: Cdotpath([DefinitionWithInterface, a]) [] diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 675701430..7cd9875f6 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -4,6 +4,7 @@ Hover tests/src/Div.res 1:10 Complete tests/src/Div.res 3:3 posNoWhite:(4,14) Found expr:(4,2)->(4,15) JSX (dangerous:(4,6) e:(4,6)->(4,15)) posNoWhite:(4,14) Found expr:(4,6)->(4,15) +Completable: Cjsx([div], dangerous, []) [{ "label": "dangerouslySetInnerHTML", "kind": 4, diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 81d3b23d7..c0ac2af20 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -4,6 +4,7 @@ Definition tests/src/Jsx.res 5:9 Complete tests/src/Jsx.res 7:2 posNoWhite:(8,12) Found expr:(8,2)->(8,13) JSX (second:(8,4) e:(8,11)->(8,13)) posNoWhite:(8,12) Found expr:(8,11)->(8,13) +Completable: Cjsx([M], "", [second]) [{ "label": "first", "kind": 4, @@ -27,6 +28,7 @@ posNoWhite:(8,12) Found expr:(8,11)->(8,13) Complete tests/src/Jsx.res 9:2 posNoWhite:(10,17) Found expr:(10,2)->(10,18) JSX (second:(10,4) e:(10,11)->(10,16)), (f:(10,17) e:(10,17)->(10,18)) posNoWhite:(10,17) Found expr:(10,17)->(10,18) +Completable: Cjsx([M], f, [second]) [{ "label": "first", "kind": 4, @@ -44,6 +46,7 @@ posNoWhite:(10,17) Found expr:(10,17)->(10,18) Complete tests/src/Jsx.res 11:2 posNoWhite:(12,10) Found expr:(12,10)->(12,11) JSX posNoWhite:(12,10) Found expr:(12,10)->(12,11) +Completable: Cjsx([M], "", []) [{ "label": "second", "kind": 4, @@ -73,6 +76,7 @@ posNoWhite:(12,10) Found expr:(12,10)->(12,11) Complete tests/src/Jsx.res 18:2 posNoWhite:(19,16) Found expr:(19,2)->(19,17) JSX (prop:(19,4) e:(19,10)->(19,14)), (k:(19,16) e:(19,16)->(19,17)) posNoWhite:(19,16) Found expr:(19,16)->(19,17) +Completable: Cjsx([M], k, [prop]) [{ "label": "key", "kind": 4, @@ -84,6 +88,7 @@ posNoWhite:(19,16) Found expr:(19,16)->(19,17) Complete tests/src/Jsx.res 20:2 posNoWhite:(21,14) Found expr:(21,2)->(21,15) JSX (prop:(21,4) e:(21,9)->(21,13)), (k:(21,14) e:(21,14)->(21,15)) posNoWhite:(21,14) Found expr:(21,14)->(21,15) +Completable: Cjsx([M], k, [prop]) [{ "label": "key", "kind": 4, @@ -95,6 +100,7 @@ posNoWhite:(21,14) Found expr:(21,14)->(21,15) Complete tests/src/Jsx.res 22:2 posNoWhite:(23,18) Found expr:(23,2)->(23,19) JSX (prop:(23,4) e:(23,9)->(23,17)), (k:(23,18) e:(23,18)->(23,19)) posNoWhite:(23,18) Found expr:(23,18)->(23,19) +Completable: Cjsx([M], k, [prop]) [{ "label": "key", "kind": 4, @@ -106,6 +112,7 @@ posNoWhite:(23,18) Found expr:(23,18)->(23,19) Complete tests/src/Jsx.res 24:2 posNoWhite:(25,21) Found expr:(25,2)->(25,22) JSX (prop:(25,4) e:(25,9)->(25,20)), (k:(25,21) e:(25,21)->(25,22)) posNoWhite:(25,21) Found expr:(25,21)->(25,22) +Completable: Cjsx([M], k, [prop]) [{ "label": "key", "kind": 4, @@ -117,6 +124,7 @@ posNoWhite:(25,21) Found expr:(25,21)->(25,22) Complete tests/src/Jsx.res 26:2 posNoWhite:(27,15) Found expr:(27,2)->(27,16) JSX (prop:(27,4) e:(27,10)->(27,14)), (k:(27,15) e:(27,15)->(27,16)) posNoWhite:(27,15) Found expr:(27,15)->(27,16) +Completable: Cjsx([M], k, [prop]) [{ "label": "key", "kind": 4, @@ -128,6 +136,7 @@ posNoWhite:(27,15) Found expr:(27,15)->(27,16) Complete tests/src/Jsx.res 28:2 posNoWhite:(29,13) Found expr:(29,2)->(29,14) JSX (prop:(29,4) e:(29,9)->(29,12)), (k:(29,13) e:(29,13)->(29,14)) posNoWhite:(29,13) Found expr:(29,13)->(29,14) +Completable: Cjsx([M], k, [prop]) [{ "label": "key", "kind": 4, @@ -139,6 +148,7 @@ posNoWhite:(29,13) Found expr:(29,13)->(29,14) Complete tests/src/Jsx.res 30:2 posNoWhite:(31,14) Found expr:(31,2)->(31,15) JSX (prop:(31,4) e:(31,9)->(31,13)), (k:(31,14) e:(31,14)->(31,15)) posNoWhite:(31,14) Found expr:(31,14)->(31,15) +Completable: Cjsx([M], k, [prop]) [{ "label": "key", "kind": 4, @@ -150,11 +160,13 @@ posNoWhite:(31,14) Found expr:(31,14)->(31,15) Complete tests/src/Jsx.res 32:2 posNoWhite:(33,15) Found expr:(33,2)->(33,16) JSX (prop:(33,4) e:(33,9)->(33,14)), (k:(33,15) e:(33,15)->(33,16)) posNoWhite:(33,15) Found expr:(33,15)->(33,16) +Completable: Cdotpath([k]) [] Complete tests/src/Jsx.res 34:2 posNoWhite:(35,13) Found expr:(35,2)->(35,14) JSX (prop:(35,4) e:(35,9)->(35,12)), (k:(35,13) e:(35,13)->(35,14)) posNoWhite:(35,13) Found expr:(35,13)->(35,14) +Completable: Cjsx([M], k, [prop]) [{ "label": "key", "kind": 4, @@ -166,6 +178,7 @@ posNoWhite:(35,13) Found expr:(35,13)->(35,14) Complete tests/src/Jsx.res 36:2 posNoWhite:(37,24) Found expr:(37,2)->(37,25) JSX (prop:(37,4) e:(37,9)->(37,23)), (k:(37,24) e:(37,24)->(37,25)) posNoWhite:(37,24) Found expr:(37,24)->(37,25) +Completable: Cjsx([M], k, [prop]) [{ "label": "key", "kind": 4, @@ -177,6 +190,7 @@ posNoWhite:(37,24) Found expr:(37,24)->(37,25) Complete tests/src/Jsx.res 38:2 posNoWhite:(39,35) Found expr:(39,2)->(39,36) JSX (prop:(39,4) e:(39,9)->(39,34)), (k:(39,35) e:(39,35)->(39,36)) posNoWhite:(39,35) Found expr:(39,35)->(39,36) +Completable: Cjsx([M], k, [prop]) [{ "label": "key", "kind": 4, @@ -188,6 +202,7 @@ posNoWhite:(39,35) Found expr:(39,35)->(39,36) Complete tests/src/Jsx.res 40:2 posNoWhite:(41,22) Found expr:(41,2)->(41,23) JSX (prop:(41,4) e:(41,9)->(41,21)), (k:(41,22) e:(41,22)->(41,23)) posNoWhite:(41,22) Found expr:(41,22)->(41,23) +Completable: Cjsx([M], k, [prop]) [{ "label": "key", "kind": 4, @@ -202,6 +217,7 @@ Definition tests/src/Jsx.res 43:11 Complete tests/src/Jsx.res 52:2 posNoWhite:(53,7) Found expr:(53,2)->(53,8) JSX (al:(53,6) e:(53,6)->(53,8)) posNoWhite:(53,7) Found expr:(53,6)->(53,8) +Completable: Cjsx([Ext], al, []) [{ "label": "align", "kind": 4, @@ -213,6 +229,7 @@ posNoWhite:(53,7) Found expr:(53,6)->(53,8) Complete tests/src/Jsx.res 54:2 posNoWhite:(55,8) Found expr:(55,2)->(55,9) JSX (first:(55,4) e:(55,4)->(55,9)) posNoWhite:(55,8) Found expr:(55,4)->(55,9) +Completable: Cjsx([M], "", [first]) [{ "label": "second", "kind": 4, @@ -236,6 +253,7 @@ posNoWhite:(55,8) Found expr:(55,4)->(55,9) Complete tests/src/Jsx.res 56:2 posNoWhite:(57,13) Found expr:(57,2)->(57,14) JSX (first:(57,4) e:(57,10)->(57,12)), (k:(57,13) e:(57,13)->(57,14)) posNoWhite:(57,13) Found expr:(57,13)->(57,14) +Completable: Cjsx([M], k, [first]) [{ "label": "key", "kind": 4, @@ -247,6 +265,7 @@ posNoWhite:(57,13) Found expr:(57,13)->(57,14) Complete tests/src/Jsx.res 58:2 posNoWhite:(59,20) Found expr:(59,2)->(59,21) JSX (first:(59,4) e:(59,17)->(59,19)), (k:(59,20) e:(59,20)->(59,21)) posNoWhite:(59,20) Found expr:(59,20)->(59,21) +Completable: Cjsx([M], k, [first]) [{ "label": "key", "kind": 4, diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index 41a63ff5b..4b783a5c0 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -1,6 +1,7 @@ Complete tests/src/RecordCompletion.res 7:3 posNoWhite:(8,6) Found expr:(8,1)->(8,7) posNoWhite:(8,6) Found expr:(8,6)->(8,7) +Completable: Cpipe(t.n, m) [{ "label": "Js.Array2.mapi", "kind": 12, @@ -18,6 +19,7 @@ posNoWhite:(8,6) Found expr:(8,6)->(8,7) Complete tests/src/RecordCompletion.res 9:3 posNoWhite:(10,10) Found expr:(10,1)->(10,11) posNoWhite:(10,10) Found expr:(10,10)->(10,11) +Completable: Cpipe(t2.n2.n, m) [{ "label": "Js.Array2.mapi", "kind": 12, From 74e67d32be61d295becc67b8d23004fa523680ad Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 12 Apr 2022 11:11:22 +0200 Subject: [PATCH 004/135] Print the component name. --- analysis/src/Commands.ml | 17 ++++++++- .../tests/src/expected/Completion.res.txt | 4 +- analysis/tests/src/expected/Div.res.txt | 2 +- analysis/tests/src/expected/Jsx.res.txt | 38 +++++++++---------- 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 0296b043d..a3ddd08d0 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -66,6 +66,18 @@ let completionWithParser ~debug ~path ~pos ~currentFile ~textOpt = let offsetNoWhite = PartialParser.skipWhite text offset in let posNoWhite = (line, max 0 col - offset + offsetNoWhite) in + let flattenComponentName lid = + let rec loop acc lid = + match lid with + | Longident.Lident txt -> txt :: acc + | Ldot (lid, txt) -> + let acc = if txt = "createElement" then acc else txt :: acc in + loop acc lid + | _ -> acc + in + loop [] lid + in + if Filename.check_suffix path ".res" then ( let parser = Res_driver.parsingEngine.parseImplementation ~forPrinter:false @@ -76,9 +88,12 @@ let completionWithParser ~debug ~path ~pos ~currentFile ~textOpt = found := true; let exprKind = match expr.pexp_desc with - | Pexp_apply _ when Res_parsetree_viewer.isJsxExpression expr -> + | Pexp_apply ({pexp_desc = Pexp_ident lident}, _) + when Res_parsetree_viewer.isJsxExpression expr -> let props = extractJsxProps ~text expr in " JSX " + ^ (lident.txt |> flattenComponentName |> String.concat ",") + ^ " " ^ (props |> List.map (fun (lbl, lblPos, (eProp : Parsetree.expression)) -> Printf.sprintf "(%s:%s e:%s)" lbl diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index da535db53..afb5098ca 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -457,7 +457,7 @@ Completable: Cdotpath([Js, Dict, u]) }] Complete tests/src/Completion.res 50:2 -posNoWhite:(51,27) Found expr:(51,13)->(51,28) JSX (second:(51,20) e:(51,27)->(51,28)) +posNoWhite:(51,27) Found expr:(51,13)->(51,28) JSX O,Comp (second:(51,20) e:(51,27)->(51,28)) posNoWhite:(51,27) Found expr:(51,27)->(51,28) Completable: Cdotpath([z]) [{ @@ -469,7 +469,7 @@ Completable: Cdotpath([z]) }] Complete tests/src/Completion.res 52:2 -posNoWhite:(53,20) Found expr:(53,13)->(53,21) JSX (z:(53,20) e:(53,20)->(53,21)) +posNoWhite:(53,20) Found expr:(53,13)->(53,21) JSX O,Comp (z:(53,20) e:(53,20)->(53,21)) posNoWhite:(53,20) Found expr:(53,20)->(53,21) Completable: Cjsx([O, Comp], z, []) [{ diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 7cd9875f6..459c95ad0 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -2,7 +2,7 @@ Hover tests/src/Div.res 1:10 {"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array,\n) => React.element\n```"} Complete tests/src/Div.res 3:3 -posNoWhite:(4,14) Found expr:(4,2)->(4,15) JSX (dangerous:(4,6) e:(4,6)->(4,15)) +posNoWhite:(4,14) Found expr:(4,2)->(4,15) JSX div (dangerous:(4,6) e:(4,6)->(4,15)) posNoWhite:(4,14) Found expr:(4,6)->(4,15) Completable: Cjsx([div], dangerous, []) [{ diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index c0ac2af20..86d90c9ac 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -2,7 +2,7 @@ Definition tests/src/Jsx.res 5:9 {"uri": "Jsx.res", "range": {"start": {"line": 2, "character": 6}, "end": {"line": 2, "character": 10}}} Complete tests/src/Jsx.res 7:2 -posNoWhite:(8,12) Found expr:(8,2)->(8,13) JSX (second:(8,4) e:(8,11)->(8,13)) +posNoWhite:(8,12) Found expr:(8,2)->(8,13) JSX M (second:(8,4) e:(8,11)->(8,13)) posNoWhite:(8,12) Found expr:(8,11)->(8,13) Completable: Cjsx([M], "", [second]) [{ @@ -26,7 +26,7 @@ Completable: Cjsx([M], "", [second]) }] Complete tests/src/Jsx.res 9:2 -posNoWhite:(10,17) Found expr:(10,2)->(10,18) JSX (second:(10,4) e:(10,11)->(10,16)), (f:(10,17) e:(10,17)->(10,18)) +posNoWhite:(10,17) Found expr:(10,2)->(10,18) JSX M (second:(10,4) e:(10,11)->(10,16)), (f:(10,17) e:(10,17)->(10,18)) posNoWhite:(10,17) Found expr:(10,17)->(10,18) Completable: Cjsx([M], f, [second]) [{ @@ -44,7 +44,7 @@ Completable: Cjsx([M], f, [second]) }] Complete tests/src/Jsx.res 11:2 -posNoWhite:(12,10) Found expr:(12,10)->(12,11) JSX +posNoWhite:(12,10) Found expr:(12,10)->(12,11) JSX M posNoWhite:(12,10) Found expr:(12,10)->(12,11) Completable: Cjsx([M], "", []) [{ @@ -74,7 +74,7 @@ Completable: Cjsx([M], "", []) }] Complete tests/src/Jsx.res 18:2 -posNoWhite:(19,16) Found expr:(19,2)->(19,17) JSX (prop:(19,4) e:(19,10)->(19,14)), (k:(19,16) e:(19,16)->(19,17)) +posNoWhite:(19,16) Found expr:(19,2)->(19,17) JSX M (prop:(19,4) e:(19,10)->(19,14)), (k:(19,16) e:(19,16)->(19,17)) posNoWhite:(19,16) Found expr:(19,16)->(19,17) Completable: Cjsx([M], k, [prop]) [{ @@ -86,7 +86,7 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 20:2 -posNoWhite:(21,14) Found expr:(21,2)->(21,15) JSX (prop:(21,4) e:(21,9)->(21,13)), (k:(21,14) e:(21,14)->(21,15)) +posNoWhite:(21,14) Found expr:(21,2)->(21,15) JSX M (prop:(21,4) e:(21,9)->(21,13)), (k:(21,14) e:(21,14)->(21,15)) posNoWhite:(21,14) Found expr:(21,14)->(21,15) Completable: Cjsx([M], k, [prop]) [{ @@ -98,7 +98,7 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 22:2 -posNoWhite:(23,18) Found expr:(23,2)->(23,19) JSX (prop:(23,4) e:(23,9)->(23,17)), (k:(23,18) e:(23,18)->(23,19)) +posNoWhite:(23,18) Found expr:(23,2)->(23,19) JSX M (prop:(23,4) e:(23,9)->(23,17)), (k:(23,18) e:(23,18)->(23,19)) posNoWhite:(23,18) Found expr:(23,18)->(23,19) Completable: Cjsx([M], k, [prop]) [{ @@ -110,7 +110,7 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 24:2 -posNoWhite:(25,21) Found expr:(25,2)->(25,22) JSX (prop:(25,4) e:(25,9)->(25,20)), (k:(25,21) e:(25,21)->(25,22)) +posNoWhite:(25,21) Found expr:(25,2)->(25,22) JSX M (prop:(25,4) e:(25,9)->(25,20)), (k:(25,21) e:(25,21)->(25,22)) posNoWhite:(25,21) Found expr:(25,21)->(25,22) Completable: Cjsx([M], k, [prop]) [{ @@ -122,7 +122,7 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 26:2 -posNoWhite:(27,15) Found expr:(27,2)->(27,16) JSX (prop:(27,4) e:(27,10)->(27,14)), (k:(27,15) e:(27,15)->(27,16)) +posNoWhite:(27,15) Found expr:(27,2)->(27,16) JSX M (prop:(27,4) e:(27,10)->(27,14)), (k:(27,15) e:(27,15)->(27,16)) posNoWhite:(27,15) Found expr:(27,15)->(27,16) Completable: Cjsx([M], k, [prop]) [{ @@ -134,7 +134,7 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 28:2 -posNoWhite:(29,13) Found expr:(29,2)->(29,14) JSX (prop:(29,4) e:(29,9)->(29,12)), (k:(29,13) e:(29,13)->(29,14)) +posNoWhite:(29,13) Found expr:(29,2)->(29,14) JSX M (prop:(29,4) e:(29,9)->(29,12)), (k:(29,13) e:(29,13)->(29,14)) posNoWhite:(29,13) Found expr:(29,13)->(29,14) Completable: Cjsx([M], k, [prop]) [{ @@ -146,7 +146,7 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 30:2 -posNoWhite:(31,14) Found expr:(31,2)->(31,15) JSX (prop:(31,4) e:(31,9)->(31,13)), (k:(31,14) e:(31,14)->(31,15)) +posNoWhite:(31,14) Found expr:(31,2)->(31,15) JSX M (prop:(31,4) e:(31,9)->(31,13)), (k:(31,14) e:(31,14)->(31,15)) posNoWhite:(31,14) Found expr:(31,14)->(31,15) Completable: Cjsx([M], k, [prop]) [{ @@ -158,13 +158,13 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 32:2 -posNoWhite:(33,15) Found expr:(33,2)->(33,16) JSX (prop:(33,4) e:(33,9)->(33,14)), (k:(33,15) e:(33,15)->(33,16)) +posNoWhite:(33,15) Found expr:(33,2)->(33,16) JSX M (prop:(33,4) e:(33,9)->(33,14)), (k:(33,15) e:(33,15)->(33,16)) posNoWhite:(33,15) Found expr:(33,15)->(33,16) Completable: Cdotpath([k]) [] Complete tests/src/Jsx.res 34:2 -posNoWhite:(35,13) Found expr:(35,2)->(35,14) JSX (prop:(35,4) e:(35,9)->(35,12)), (k:(35,13) e:(35,13)->(35,14)) +posNoWhite:(35,13) Found expr:(35,2)->(35,14) JSX M (prop:(35,4) e:(35,9)->(35,12)), (k:(35,13) e:(35,13)->(35,14)) posNoWhite:(35,13) Found expr:(35,13)->(35,14) Completable: Cjsx([M], k, [prop]) [{ @@ -176,7 +176,7 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 36:2 -posNoWhite:(37,24) Found expr:(37,2)->(37,25) JSX (prop:(37,4) e:(37,9)->(37,23)), (k:(37,24) e:(37,24)->(37,25)) +posNoWhite:(37,24) Found expr:(37,2)->(37,25) JSX M (prop:(37,4) e:(37,9)->(37,23)), (k:(37,24) e:(37,24)->(37,25)) posNoWhite:(37,24) Found expr:(37,24)->(37,25) Completable: Cjsx([M], k, [prop]) [{ @@ -188,7 +188,7 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 38:2 -posNoWhite:(39,35) Found expr:(39,2)->(39,36) JSX (prop:(39,4) e:(39,9)->(39,34)), (k:(39,35) e:(39,35)->(39,36)) +posNoWhite:(39,35) Found expr:(39,2)->(39,36) JSX M (prop:(39,4) e:(39,9)->(39,34)), (k:(39,35) e:(39,35)->(39,36)) posNoWhite:(39,35) Found expr:(39,35)->(39,36) Completable: Cjsx([M], k, [prop]) [{ @@ -200,7 +200,7 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 40:2 -posNoWhite:(41,22) Found expr:(41,2)->(41,23) JSX (prop:(41,4) e:(41,9)->(41,21)), (k:(41,22) e:(41,22)->(41,23)) +posNoWhite:(41,22) Found expr:(41,2)->(41,23) JSX M (prop:(41,4) e:(41,9)->(41,21)), (k:(41,22) e:(41,22)->(41,23)) posNoWhite:(41,22) Found expr:(41,22)->(41,23) Completable: Cjsx([M], k, [prop]) [{ @@ -215,7 +215,7 @@ Definition tests/src/Jsx.res 43:11 {"uri": "Component.res", "range": {"start": {"line": 1, "character": 4}, "end": {"line": 1, "character": 8}}} Complete tests/src/Jsx.res 52:2 -posNoWhite:(53,7) Found expr:(53,2)->(53,8) JSX (al:(53,6) e:(53,6)->(53,8)) +posNoWhite:(53,7) Found expr:(53,2)->(53,8) JSX Ext (al:(53,6) e:(53,6)->(53,8)) posNoWhite:(53,7) Found expr:(53,6)->(53,8) Completable: Cjsx([Ext], al, []) [{ @@ -227,7 +227,7 @@ Completable: Cjsx([Ext], al, []) }] Complete tests/src/Jsx.res 54:2 -posNoWhite:(55,8) Found expr:(55,2)->(55,9) JSX (first:(55,4) e:(55,4)->(55,9)) +posNoWhite:(55,8) Found expr:(55,2)->(55,9) JSX M (first:(55,4) e:(55,4)->(55,9)) posNoWhite:(55,8) Found expr:(55,4)->(55,9) Completable: Cjsx([M], "", [first]) [{ @@ -251,7 +251,7 @@ Completable: Cjsx([M], "", [first]) }] Complete tests/src/Jsx.res 56:2 -posNoWhite:(57,13) Found expr:(57,2)->(57,14) JSX (first:(57,4) e:(57,10)->(57,12)), (k:(57,13) e:(57,13)->(57,14)) +posNoWhite:(57,13) Found expr:(57,2)->(57,14) JSX M (first:(57,4) e:(57,10)->(57,12)), (k:(57,13) e:(57,13)->(57,14)) posNoWhite:(57,13) Found expr:(57,13)->(57,14) Completable: Cjsx([M], k, [first]) [{ @@ -263,7 +263,7 @@ Completable: Cjsx([M], k, [first]) }] Complete tests/src/Jsx.res 58:2 -posNoWhite:(59,20) Found expr:(59,2)->(59,21) JSX (first:(59,4) e:(59,17)->(59,19)), (k:(59,20) e:(59,20)->(59,21)) +posNoWhite:(59,20) Found expr:(59,2)->(59,21) JSX M (first:(59,4) e:(59,17)->(59,19)), (k:(59,20) e:(59,20)->(59,21)) posNoWhite:(59,20) Found expr:(59,20)->(59,21) Completable: Cjsx([M], k, [first]) [{ From 5e7d735f1812b48b7184afdcb4381551bdbe9325 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 12 Apr 2022 11:48:26 +0200 Subject: [PATCH 005/135] Print loc of component name and children. --- analysis/src/Commands.ml | 68 +++++++------ .../src/expected/CompletePrioritize1.res.txt | 2 +- .../src/expected/CompletePrioritize2.res.txt | 2 +- .../tests/src/expected/Completion.res.txt | 72 +++++++------- analysis/tests/src/expected/Cross.res.txt | 2 +- analysis/tests/src/expected/Div.res.txt | 5 +- analysis/tests/src/expected/Jsx.res.txt | 95 +++++++++++-------- .../src/expected/RecordCompletion.res.txt | 8 +- 8 files changed, 141 insertions(+), 113 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index a3ddd08d0..bfdd61e4a 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -23,9 +23,10 @@ let extractJsxProps ~text (expr : Parsetree.expression) = If it's "?" then it's a punned optional . Otherwise it should be "id =" perhaps followed by "?". *) - let rec processProps ~lastOffset ~lastPos args = + let rec processProps ~lastOffset ~lastPos ~acc args = match args with - | (Asttypes.Labelled "children", _) :: _ -> [] + | (Asttypes.Labelled "children", {Parsetree.pexp_loc}) :: _ -> + (List.rev acc, pexp_loc) | ((Labelled s | Optional s), (eProp : Parsetree.expression)) :: rest -> ( let ePosStart = Utils.tupleOfLexing eProp.pexp_loc.loc_start in let ePosEnd = Utils.tupleOfLexing eProp.pexp_loc.loc_end in @@ -40,10 +41,11 @@ let extractJsxProps ~text (expr : Parsetree.expression) = | Some pos -> pos | None -> (* Must be punned *) ePosStart in - (s, labelPos, eProp) - :: processProps ~lastOffset:offsetEnd ~lastPos:ePosEnd rest + processProps + ~acc:((s, labelPos, eProp) :: acc) + ~lastOffset:offsetEnd ~lastPos:ePosEnd rest | _ -> assert false) - | _ -> [] + | _ -> (* should not happen *) ([], Location.none) in let posAfterCompName = Utils.tupleOfLexing eComp.pexp_loc.loc_end in let offsetAfterCompName = @@ -53,18 +55,21 @@ let extractJsxProps ~text (expr : Parsetree.expression) = in args |> processProps ~lastOffset:offsetAfterCompName ~lastPos:posAfterCompName - | _ -> [] + ~acc:[] + | _ -> (* should not happen *) ([], Location.none) -let completionWithParser ~debug ~path ~pos ~currentFile ~textOpt = +let completionWithParser ~debug ~path ~posCursor ~currentFile ~textOpt = let text = match textOpt with Some text -> text | None -> assert false in let offset = - match PartialParser.positionToOffset text pos with + match PartialParser.positionToOffset text posCursor with | Some offset -> offset | None -> assert false in - let line, col = pos in let offsetNoWhite = PartialParser.skipWhite text offset in - let posNoWhite = (line, max 0 col - offset + offsetNoWhite) in + let posNoWhite = + let line, col = posCursor in + (line, max 0 col - offset + offsetNoWhite) + in let flattenComponentName lid = let rec loop acc lid = @@ -86,27 +91,28 @@ let completionWithParser ~debug ~path ~pos ~currentFile ~textOpt = let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = if posInLoc ~pos:posNoWhite ~loc:expr.pexp_loc then ( found := true; - let exprKind = - match expr.pexp_desc with - | Pexp_apply ({pexp_desc = Pexp_ident lident}, _) - when Res_parsetree_viewer.isJsxExpression expr -> - let props = extractJsxProps ~text expr in - " JSX " - ^ (lident.txt |> flattenComponentName |> String.concat ",") - ^ " " - ^ (props - |> List.map (fun (lbl, lblPos, (eProp : Parsetree.expression)) -> - Printf.sprintf "(%s:%s e:%s)" lbl - (SemanticTokens.posToString lblPos) - (SemanticTokens.locToString eProp.pexp_loc)) - |> String.concat ", ") - | _ -> "" - in if debug then - Printf.printf "posNoWhite:%s Found expr:%s%s\n" - (SemanticTokens.posToString posNoWhite ^ " ") - (SemanticTokens.locToString expr.pexp_loc) - exprKind); + Printf.printf "posCursor:%s posNoWhite:%s Found expr:%s\n" + (SemanticTokens.posToString posCursor) + (SemanticTokens.posToString posNoWhite) + (SemanticTokens.locToString expr.pexp_loc); + + match expr.pexp_desc with + | Pexp_apply + ({pexp_desc = Pexp_ident {txt = compName; loc = compNameLoc}}, _) + when Res_parsetree_viewer.isJsxExpression expr -> + let props, childrenLoc = extractJsxProps ~text expr in + Printf.printf "JSX %s:%s children:%s %s\n" + (compName |> flattenComponentName |> String.concat ",") + (SemanticTokens.locToString compNameLoc) + (SemanticTokens.locToString childrenLoc) + (props + |> List.map (fun (lbl, lblPos, (eProp : Parsetree.expression)) -> + Printf.sprintf "(%s:%s e:%s)" lbl + (SemanticTokens.posToString lblPos) + (SemanticTokens.locToString eProp.pexp_loc)) + |> String.concat ", ") + | _ -> ()); Ast_iterator.default_iterator.expr iterator expr in let {Res_driver.parsetree = structure} = parser ~filename:currentFile in @@ -119,7 +125,7 @@ let completion ~debug ~path ~line ~col ~currentFile = let result = let textOpt = Files.readFile currentFile in - completionWithParser ~debug ~path ~pos ~currentFile ~textOpt; + completionWithParser ~debug ~path ~posCursor:pos ~currentFile ~textOpt; let completionItems = match NewCompletions.getCompletable ~textOpt ~pos with | None -> [] diff --git a/analysis/tests/src/expected/CompletePrioritize1.res.txt b/analysis/tests/src/expected/CompletePrioritize1.res.txt index f0bb99e7b..36388bbd3 100644 --- a/analysis/tests/src/expected/CompletePrioritize1.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize1.res.txt @@ -1,5 +1,5 @@ Complete tests/src/CompletePrioritize1.res 4:2 -posNoWhite:(5,3) Found expr:(5,2)->(5,4) +posCursor:(5,4) posNoWhite:(5,3) Found expr:(5,2)->(5,4) Completable: Cpipe(a, "") [{ "label": "Test.add", diff --git a/analysis/tests/src/expected/CompletePrioritize2.res.txt b/analysis/tests/src/expected/CompletePrioritize2.res.txt index b248c50c4..c4d9572ec 100644 --- a/analysis/tests/src/expected/CompletePrioritize2.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize2.res.txt @@ -1,5 +1,5 @@ Complete tests/src/CompletePrioritize2.res 8:2 -posNoWhite:(9,3) Found expr:(9,2)->(9,4) +posCursor:(9,4) posNoWhite:(9,3) Found expr:(9,2)->(9,4) Completable: Cpipe(a, "") [{ "label": "Test.add", diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index afb5098ca..f1f7b3e79 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1,5 +1,5 @@ Complete tests/src/Completion.res 0:2 -posNoWhite:(1,8) Found expr:(1,1)->(1,9) +posCursor:(1,9) posNoWhite:(1,8) Found expr:(1,1)->(1,9) Completable: Cdotpath([MyList, m]) [{ "label": "mapReverse", @@ -70,7 +70,7 @@ Completable: Cdotpath([MyList, m]) }] Complete tests/src/Completion.res 1:2 -posNoWhite:(2,6) Found expr:(2,1)->(6,6) +posCursor:(2,7) posNoWhite:(2,6) Found expr:(2,1)->(6,6) Completable: Cdotpath([Array, ""]) [{ "label": "Floatarray", @@ -291,7 +291,7 @@ Completable: Cdotpath([Array, ""]) }] Complete tests/src/Completion.res 2:2 -posNoWhite:(3,7) Found expr:(3,1)->(3,8) +posCursor:(3,8) posNoWhite:(3,7) Found expr:(3,1)->(3,8) Completable: Cdotpath([Array, m]) [{ "label": "mapi", @@ -344,7 +344,7 @@ Completable: Cdotpath([Array, m]) }] Complete tests/src/Completion.res 12:2 -posNoWhite:(13,14) Found expr:(13,10)->(13,15) +posCursor:(13,15) posNoWhite:(13,14) Found expr:(13,10)->(13,15) Completable: Cdotpath([Dep, c]) [{ "label": "customDouble", @@ -355,7 +355,7 @@ Completable: Cdotpath([Dep, c]) }] Complete tests/src/Completion.res 19:2 -posNoWhite:(20,17) Found expr:(20,9)->(20,18) +posCursor:(20,18) posNoWhite:(20,17) Found expr:(20,9)->(20,18) Completable: Clabel([Lib, foo], "", []) [{ "label": "age", @@ -372,8 +372,8 @@ Completable: Clabel([Lib, foo], "", []) }] Complete tests/src/Completion.res 21:2 -posNoWhite:(22,10) Found expr:(22,1)->(22,11) -posNoWhite:(22,10) Found expr:(22,10)->(22,11) +posCursor:(22,11) posNoWhite:(22,10) Found expr:(22,1)->(22,11) +posCursor:(22,11) posNoWhite:(22,10) Found expr:(22,10)->(22,11) Completable: Cpipe(PipeArray, m) [{ "label": "Js.Array2.mapi", @@ -390,8 +390,8 @@ Completable: Cpipe(PipeArray, m) }] Complete tests/src/Completion.res 23:2 -posNoWhite:(24,10) Found expr:(24,1)->(24,11) -posNoWhite:(24,10) Found expr:(24,8)->(24,11) +posCursor:(24,11) posNoWhite:(24,10) Found expr:(24,1)->(24,11) +posCursor:(24,11) posNoWhite:(24,10) Found expr:(24,8)->(24,11) Completable: Cpipe(PipeString, toU) [{ "label": "Js.String2.toUpperCase", @@ -402,8 +402,8 @@ Completable: Cpipe(PipeString, toU) }] Complete tests/src/Completion.res 27:2 -posNoWhite:(28,5) Found expr:(28,1)->(28,6) -posNoWhite:(28,5) Found expr:(28,5)->(28,6) +posCursor:(28,6) posNoWhite:(28,5) Found expr:(28,1)->(28,6) +posCursor:(28,6) posNoWhite:(28,5) Found expr:(28,5)->(28,6) Completable: Cpipe(op, e) [{ "label": "Belt.Option.eqU", @@ -420,9 +420,9 @@ Completable: Cpipe(op, e) }] Complete tests/src/Completion.res 36:2 -posNoWhite:(37,4) Found expr:(37,1)->(46,3) -posNoWhite:(37,4) Found expr:(37,1)->(41,8) -posNoWhite:(37,4) Found expr:(37,3)->(37,5) +posCursor:(37,5) posNoWhite:(37,4) Found expr:(37,1)->(46,3) +posCursor:(37,5) posNoWhite:(37,4) Found expr:(37,1)->(41,8) +posCursor:(37,5) posNoWhite:(37,4) Found expr:(37,3)->(37,5) Completable: Cpipe(fa, "") [{ "label": "ForAuto.abc", @@ -439,8 +439,8 @@ Completable: Cpipe(fa, "") }] Complete tests/src/Completion.res 38:2 -posNoWhite:(39,18) Found expr:(39,1)->(39,19) -posNoWhite:(39,18) Found expr:(39,10)->(39,19) +posCursor:(39,19) posNoWhite:(39,18) Found expr:(39,1)->(39,19) +posCursor:(39,19) posNoWhite:(39,18) Found expr:(39,10)->(39,19) Completable: Cdotpath([Js, Dict, u]) [{ "label": "unsafeGet", @@ -457,8 +457,9 @@ Completable: Cdotpath([Js, Dict, u]) }] Complete tests/src/Completion.res 50:2 -posNoWhite:(51,27) Found expr:(51,13)->(51,28) JSX O,Comp (second:(51,20) e:(51,27)->(51,28)) -posNoWhite:(51,27) Found expr:(51,27)->(51,28) +posCursor:(51,28) posNoWhite:(51,27) Found expr:(51,13)->(51,28) +JSX O,Comp:(51,13)->(51,19) children:(0,-1)->(0,-1) (second:(51,20) e:(51,27)->(51,28)) +posCursor:(51,28) posNoWhite:(51,27) Found expr:(51,27)->(51,28) Completable: Cdotpath([z]) [{ "label": "zzz", @@ -469,8 +470,9 @@ Completable: Cdotpath([z]) }] Complete tests/src/Completion.res 52:2 -posNoWhite:(53,20) Found expr:(53,13)->(53,21) JSX O,Comp (z:(53,20) e:(53,20)->(53,21)) -posNoWhite:(53,20) Found expr:(53,20)->(53,21) +posCursor:(53,21) posNoWhite:(53,20) Found expr:(53,13)->(53,21) +JSX O,Comp:(53,13)->(53,19) children:(0,-1)->(0,-1) (z:(53,20) e:(53,20)->(53,21)) +posCursor:(53,21) posNoWhite:(53,20) Found expr:(53,20)->(53,21) Completable: Cjsx([O, Comp], z, []) [{ "label": "zoo", @@ -686,7 +688,7 @@ Completable: Cdecorator(reac) }] Complete tests/src/Completion.res 58:2 -posNoWhite:(59,7) Found expr:(0,-1)->(69,17) +posCursor:(59,8) posNoWhite:(59,7) Found expr:(0,-1)->(69,17) Completable: Cdecorator(react.) [{ "label": "component", @@ -697,7 +699,7 @@ Completable: Cdecorator(react.) }] Complete tests/src/Completion.res 60:2 -posNoWhite:(61,24) Found expr:(61,9)->(61,25) +posCursor:(61,25) posNoWhite:(61,24) Found expr:(61,9)->(61,25) Completable: Clabel([Lib, foo], "", [name]) [{ "label": "age", @@ -708,7 +710,7 @@ Completable: Clabel([Lib, foo], "", [name]) }] Complete tests/src/Completion.res 62:2 -posNoWhite:(63,23) Found expr:(63,9)->(63,24) +posCursor:(63,24) posNoWhite:(63,23) Found expr:(63,9)->(63,24) Completable: Clabel([Lib, foo], "", [age]) [{ "label": "name", @@ -719,7 +721,7 @@ Completable: Clabel([Lib, foo], "", [age]) }] Complete tests/src/Completion.res 64:2 -posNoWhite:(65,29) Found expr:(65,9)->(65,30) +posCursor:(65,30) posNoWhite:(65,29) Found expr:(65,9)->(65,30) Completable: Clabel([Lib, foo], "", [age]) [{ "label": "name", @@ -730,7 +732,7 @@ Completable: Clabel([Lib, foo], "", [age]) }] Complete tests/src/Completion.res 67:2 -posNoWhite:(68,1) Found expr:(67,8)->(69,17) +posCursor:(68,2) posNoWhite:(68,1) Found expr:(67,8)->(69,17) Completable: Clabel([Lib, foo], "", []) [{ "label": "age", @@ -747,7 +749,7 @@ Completable: Clabel([Lib, foo], "", []) }] Complete tests/src/Completion.res 72:2 -posNoWhite:(73,10) Found expr:(73,8)->(75,18) +posCursor:(73,11) posNoWhite:(73,10) Found expr:(73,8)->(75,18) Completable: Cobj([someObj], [], a) [{ "label": "age", @@ -758,7 +760,7 @@ Completable: Cobj([someObj], [], a) }] Complete tests/src/Completion.res 76:2 -posNoWhite:(77,21) Found expr:(77,20)->(80,10) +posCursor:(77,22) posNoWhite:(77,21) Found expr:(77,20)->(80,10) Completable: Cobj([nestedObj], [x, y], "") [{ "label": "age", @@ -775,7 +777,7 @@ Completable: Cobj([nestedObj], [x, y], "") }] Complete tests/src/Completion.res 79:2 -posNoWhite:(80,4) Found expr:(80,2)->(82,20) +posCursor:(80,5) posNoWhite:(80,4) Found expr:(80,2)->(82,20) Completable: Cobj([o], [], a) [{ "label": "age", @@ -786,7 +788,7 @@ Completable: Cobj([o], [], a) }] Complete tests/src/Completion.res 83:2 -posNoWhite:(84,14) Found expr:(84,13)->(101,20) +posCursor:(84,15) posNoWhite:(84,14) Found expr:(84,13)->(101,20) Completable: Cobj([no], [x, y], "") [{ "label": "name", @@ -803,7 +805,7 @@ Completable: Cobj([no], [x, y], "") }] Complete tests/src/Completion.res 88:3 -posNoWhite:(89,2) Found expr:(89,1)->(93,3) +posCursor:(89,3) posNoWhite:(89,2) Found expr:(89,1)->(93,3) Completable: Cdotpath([r, ""]) [{ "label": "x", @@ -820,7 +822,7 @@ Completable: Cdotpath([r, ""]) }] Complete tests/src/Completion.res 90:3 -posNoWhite:(91,18) Found expr:(91,1)->(93,3) +posCursor:(91,19) posNoWhite:(91,18) Found expr:(91,1)->(93,3) Completable: Cdotpath([Obj, Rec, recordVal, ""]) [{ "label": "xx", @@ -837,9 +839,9 @@ Completable: Cdotpath([Obj, Rec, recordVal, ""]) }] Complete tests/src/Completion.res 96:3 -posNoWhite:(97,2) Found expr:(96,11)->(99,1) -posNoWhite:(97,2) Found expr:(97,1)->(98,5) -posNoWhite:(97,2) Found expr:(97,1)->(97,3) +posCursor:(97,3) posNoWhite:(97,2) Found expr:(96,11)->(99,1) +posCursor:(97,3) posNoWhite:(97,2) Found expr:(97,1)->(98,5) +posCursor:(97,3) posNoWhite:(97,2) Found expr:(97,1)->(97,3) Completable: Cdotpath([my]) [{ "label": "myAmazingFunction", @@ -850,7 +852,7 @@ Completable: Cdotpath([my]) }] Complete tests/src/Completion.res 100:3 -posNoWhite:(101,12) Found expr:(101,11)->(120,32) +posCursor:(101,13) posNoWhite:(101,12) Found expr:(101,11)->(120,32) Completable: Cobj([Obj, object], [], "") [{ "label": "name", diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index 154a3435b..3ab9b3928 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -94,7 +94,7 @@ TypeDefinition tests/src/Cross.res 37:37 {"uri": "DefinitionWithInterface.resi", "range": {"start": {"line": 3, "character": 0}, "end": {"line": 3, "character": 6}}} Complete tests/src/Cross.res 39:2 -posNoWhite:(40,25) Found expr:(40,1)->(40,26) +posCursor:(40,26) posNoWhite:(40,25) Found expr:(40,1)->(40,26) Completable: Cdotpath([DefinitionWithInterface, a]) [] diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 459c95ad0..9f4e2a519 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -2,8 +2,9 @@ Hover tests/src/Div.res 1:10 {"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array,\n) => React.element\n```"} Complete tests/src/Div.res 3:3 -posNoWhite:(4,14) Found expr:(4,2)->(4,15) JSX div (dangerous:(4,6) e:(4,6)->(4,15)) -posNoWhite:(4,14) Found expr:(4,6)->(4,15) +posCursor:(4,15) posNoWhite:(4,14) Found expr:(4,2)->(4,15) +JSX div:(4,2)->(4,5) children:(0,-1)->(0,-1) (dangerous:(4,6) e:(4,6)->(4,15)) +posCursor:(4,15) posNoWhite:(4,14) Found expr:(4,6)->(4,15) Completable: Cjsx([div], dangerous, []) [{ "label": "dangerouslySetInnerHTML", diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 86d90c9ac..982ee2ea2 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -2,8 +2,9 @@ Definition tests/src/Jsx.res 5:9 {"uri": "Jsx.res", "range": {"start": {"line": 2, "character": 6}, "end": {"line": 2, "character": 10}}} Complete tests/src/Jsx.res 7:2 -posNoWhite:(8,12) Found expr:(8,2)->(8,13) JSX M (second:(8,4) e:(8,11)->(8,13)) -posNoWhite:(8,12) Found expr:(8,11)->(8,13) +posCursor:(8,14) posNoWhite:(8,12) Found expr:(8,2)->(8,13) +JSX M:(8,2)->(8,3) children:(0,-1)->(0,-1) (second:(8,4) e:(8,11)->(8,13)) +posCursor:(8,14) posNoWhite:(8,12) Found expr:(8,11)->(8,13) Completable: Cjsx([M], "", [second]) [{ "label": "first", @@ -26,8 +27,9 @@ Completable: Cjsx([M], "", [second]) }] Complete tests/src/Jsx.res 9:2 -posNoWhite:(10,17) Found expr:(10,2)->(10,18) JSX M (second:(10,4) e:(10,11)->(10,16)), (f:(10,17) e:(10,17)->(10,18)) -posNoWhite:(10,17) Found expr:(10,17)->(10,18) +posCursor:(10,18) posNoWhite:(10,17) Found expr:(10,2)->(10,18) +JSX M:(10,2)->(10,3) children:(0,-1)->(0,-1) (second:(10,4) e:(10,11)->(10,16)), (f:(10,17) e:(10,17)->(10,18)) +posCursor:(10,18) posNoWhite:(10,17) Found expr:(10,17)->(10,18) Completable: Cjsx([M], f, [second]) [{ "label": "first", @@ -44,8 +46,9 @@ Completable: Cjsx([M], f, [second]) }] Complete tests/src/Jsx.res 11:2 -posNoWhite:(12,10) Found expr:(12,10)->(12,11) JSX M -posNoWhite:(12,10) Found expr:(12,10)->(12,11) +posCursor:(12,12) posNoWhite:(12,10) Found expr:(12,10)->(12,11) +JSX M:(12,10)->(12,11) children:(0,-1)->(0,-1) +posCursor:(12,12) posNoWhite:(12,10) Found expr:(12,10)->(12,11) Completable: Cjsx([M], "", []) [{ "label": "second", @@ -74,8 +77,9 @@ Completable: Cjsx([M], "", []) }] Complete tests/src/Jsx.res 18:2 -posNoWhite:(19,16) Found expr:(19,2)->(19,17) JSX M (prop:(19,4) e:(19,10)->(19,14)), (k:(19,16) e:(19,16)->(19,17)) -posNoWhite:(19,16) Found expr:(19,16)->(19,17) +posCursor:(19,17) posNoWhite:(19,16) Found expr:(19,2)->(19,17) +JSX M:(19,2)->(19,3) children:(0,-1)->(0,-1) (prop:(19,4) e:(19,10)->(19,14)), (k:(19,16) e:(19,16)->(19,17)) +posCursor:(19,17) posNoWhite:(19,16) Found expr:(19,16)->(19,17) Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -86,8 +90,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 20:2 -posNoWhite:(21,14) Found expr:(21,2)->(21,15) JSX M (prop:(21,4) e:(21,9)->(21,13)), (k:(21,14) e:(21,14)->(21,15)) -posNoWhite:(21,14) Found expr:(21,14)->(21,15) +posCursor:(21,15) posNoWhite:(21,14) Found expr:(21,2)->(21,15) +JSX M:(21,2)->(21,3) children:(0,-1)->(0,-1) (prop:(21,4) e:(21,9)->(21,13)), (k:(21,14) e:(21,14)->(21,15)) +posCursor:(21,15) posNoWhite:(21,14) Found expr:(21,14)->(21,15) Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -98,8 +103,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 22:2 -posNoWhite:(23,18) Found expr:(23,2)->(23,19) JSX M (prop:(23,4) e:(23,9)->(23,17)), (k:(23,18) e:(23,18)->(23,19)) -posNoWhite:(23,18) Found expr:(23,18)->(23,19) +posCursor:(23,19) posNoWhite:(23,18) Found expr:(23,2)->(23,19) +JSX M:(23,2)->(23,3) children:(0,-1)->(0,-1) (prop:(23,4) e:(23,9)->(23,17)), (k:(23,18) e:(23,18)->(23,19)) +posCursor:(23,19) posNoWhite:(23,18) Found expr:(23,18)->(23,19) Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -110,8 +116,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 24:2 -posNoWhite:(25,21) Found expr:(25,2)->(25,22) JSX M (prop:(25,4) e:(25,9)->(25,20)), (k:(25,21) e:(25,21)->(25,22)) -posNoWhite:(25,21) Found expr:(25,21)->(25,22) +posCursor:(25,22) posNoWhite:(25,21) Found expr:(25,2)->(25,22) +JSX M:(25,2)->(25,3) children:(0,-1)->(0,-1) (prop:(25,4) e:(25,9)->(25,20)), (k:(25,21) e:(25,21)->(25,22)) +posCursor:(25,22) posNoWhite:(25,21) Found expr:(25,21)->(25,22) Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -122,8 +129,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 26:2 -posNoWhite:(27,15) Found expr:(27,2)->(27,16) JSX M (prop:(27,4) e:(27,10)->(27,14)), (k:(27,15) e:(27,15)->(27,16)) -posNoWhite:(27,15) Found expr:(27,15)->(27,16) +posCursor:(27,16) posNoWhite:(27,15) Found expr:(27,2)->(27,16) +JSX M:(27,2)->(27,3) children:(0,-1)->(0,-1) (prop:(27,4) e:(27,10)->(27,14)), (k:(27,15) e:(27,15)->(27,16)) +posCursor:(27,16) posNoWhite:(27,15) Found expr:(27,15)->(27,16) Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -134,8 +142,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 28:2 -posNoWhite:(29,13) Found expr:(29,2)->(29,14) JSX M (prop:(29,4) e:(29,9)->(29,12)), (k:(29,13) e:(29,13)->(29,14)) -posNoWhite:(29,13) Found expr:(29,13)->(29,14) +posCursor:(29,14) posNoWhite:(29,13) Found expr:(29,2)->(29,14) +JSX M:(29,2)->(29,3) children:(0,-1)->(0,-1) (prop:(29,4) e:(29,9)->(29,12)), (k:(29,13) e:(29,13)->(29,14)) +posCursor:(29,14) posNoWhite:(29,13) Found expr:(29,13)->(29,14) Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -146,8 +155,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 30:2 -posNoWhite:(31,14) Found expr:(31,2)->(31,15) JSX M (prop:(31,4) e:(31,9)->(31,13)), (k:(31,14) e:(31,14)->(31,15)) -posNoWhite:(31,14) Found expr:(31,14)->(31,15) +posCursor:(31,15) posNoWhite:(31,14) Found expr:(31,2)->(31,15) +JSX M:(31,2)->(31,3) children:(0,-1)->(0,-1) (prop:(31,4) e:(31,9)->(31,13)), (k:(31,14) e:(31,14)->(31,15)) +posCursor:(31,15) posNoWhite:(31,14) Found expr:(31,14)->(31,15) Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -158,14 +168,16 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 32:2 -posNoWhite:(33,15) Found expr:(33,2)->(33,16) JSX M (prop:(33,4) e:(33,9)->(33,14)), (k:(33,15) e:(33,15)->(33,16)) -posNoWhite:(33,15) Found expr:(33,15)->(33,16) +posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,2)->(33,16) +JSX M:(33,2)->(33,3) children:(0,-1)->(0,-1) (prop:(33,4) e:(33,9)->(33,14)), (k:(33,15) e:(33,15)->(33,16)) +posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,15)->(33,16) Completable: Cdotpath([k]) [] Complete tests/src/Jsx.res 34:2 -posNoWhite:(35,13) Found expr:(35,2)->(35,14) JSX M (prop:(35,4) e:(35,9)->(35,12)), (k:(35,13) e:(35,13)->(35,14)) -posNoWhite:(35,13) Found expr:(35,13)->(35,14) +posCursor:(35,14) posNoWhite:(35,13) Found expr:(35,2)->(35,14) +JSX M:(35,2)->(35,3) children:(0,-1)->(0,-1) (prop:(35,4) e:(35,9)->(35,12)), (k:(35,13) e:(35,13)->(35,14)) +posCursor:(35,14) posNoWhite:(35,13) Found expr:(35,13)->(35,14) Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -176,8 +188,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 36:2 -posNoWhite:(37,24) Found expr:(37,2)->(37,25) JSX M (prop:(37,4) e:(37,9)->(37,23)), (k:(37,24) e:(37,24)->(37,25)) -posNoWhite:(37,24) Found expr:(37,24)->(37,25) +posCursor:(37,25) posNoWhite:(37,24) Found expr:(37,2)->(37,25) +JSX M:(37,2)->(37,3) children:(0,-1)->(0,-1) (prop:(37,4) e:(37,9)->(37,23)), (k:(37,24) e:(37,24)->(37,25)) +posCursor:(37,25) posNoWhite:(37,24) Found expr:(37,24)->(37,25) Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -188,8 +201,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 38:2 -posNoWhite:(39,35) Found expr:(39,2)->(39,36) JSX M (prop:(39,4) e:(39,9)->(39,34)), (k:(39,35) e:(39,35)->(39,36)) -posNoWhite:(39,35) Found expr:(39,35)->(39,36) +posCursor:(39,36) posNoWhite:(39,35) Found expr:(39,2)->(39,36) +JSX M:(39,2)->(39,3) children:(0,-1)->(0,-1) (prop:(39,4) e:(39,9)->(39,34)), (k:(39,35) e:(39,35)->(39,36)) +posCursor:(39,36) posNoWhite:(39,35) Found expr:(39,35)->(39,36) Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -200,8 +214,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 40:2 -posNoWhite:(41,22) Found expr:(41,2)->(41,23) JSX M (prop:(41,4) e:(41,9)->(41,21)), (k:(41,22) e:(41,22)->(41,23)) -posNoWhite:(41,22) Found expr:(41,22)->(41,23) +posCursor:(41,23) posNoWhite:(41,22) Found expr:(41,2)->(41,23) +JSX M:(41,2)->(41,3) children:(0,-1)->(0,-1) (prop:(41,4) e:(41,9)->(41,21)), (k:(41,22) e:(41,22)->(41,23)) +posCursor:(41,23) posNoWhite:(41,22) Found expr:(41,22)->(41,23) Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -215,8 +230,9 @@ Definition tests/src/Jsx.res 43:11 {"uri": "Component.res", "range": {"start": {"line": 1, "character": 4}, "end": {"line": 1, "character": 8}}} Complete tests/src/Jsx.res 52:2 -posNoWhite:(53,7) Found expr:(53,2)->(53,8) JSX Ext (al:(53,6) e:(53,6)->(53,8)) -posNoWhite:(53,7) Found expr:(53,6)->(53,8) +posCursor:(53,8) posNoWhite:(53,7) Found expr:(53,2)->(53,8) +JSX Ext:(53,2)->(53,5) children:(0,-1)->(0,-1) (al:(53,6) e:(53,6)->(53,8)) +posCursor:(53,8) posNoWhite:(53,7) Found expr:(53,6)->(53,8) Completable: Cjsx([Ext], al, []) [{ "label": "align", @@ -227,8 +243,9 @@ Completable: Cjsx([Ext], al, []) }] Complete tests/src/Jsx.res 54:2 -posNoWhite:(55,8) Found expr:(55,2)->(55,9) JSX M (first:(55,4) e:(55,4)->(55,9)) -posNoWhite:(55,8) Found expr:(55,4)->(55,9) +posCursor:(55,10) posNoWhite:(55,8) Found expr:(55,2)->(55,9) +JSX M:(55,2)->(55,3) children:(0,-1)->(0,-1) (first:(55,4) e:(55,4)->(55,9)) +posCursor:(55,10) posNoWhite:(55,8) Found expr:(55,4)->(55,9) Completable: Cjsx([M], "", [first]) [{ "label": "second", @@ -251,8 +268,9 @@ Completable: Cjsx([M], "", [first]) }] Complete tests/src/Jsx.res 56:2 -posNoWhite:(57,13) Found expr:(57,2)->(57,14) JSX M (first:(57,4) e:(57,10)->(57,12)), (k:(57,13) e:(57,13)->(57,14)) -posNoWhite:(57,13) Found expr:(57,13)->(57,14) +posCursor:(57,14) posNoWhite:(57,13) Found expr:(57,2)->(57,14) +JSX M:(57,2)->(57,3) children:(0,-1)->(0,-1) (first:(57,4) e:(57,10)->(57,12)), (k:(57,13) e:(57,13)->(57,14)) +posCursor:(57,14) posNoWhite:(57,13) Found expr:(57,13)->(57,14) Completable: Cjsx([M], k, [first]) [{ "label": "key", @@ -263,8 +281,9 @@ Completable: Cjsx([M], k, [first]) }] Complete tests/src/Jsx.res 58:2 -posNoWhite:(59,20) Found expr:(59,2)->(59,21) JSX M (first:(59,4) e:(59,17)->(59,19)), (k:(59,20) e:(59,20)->(59,21)) -posNoWhite:(59,20) Found expr:(59,20)->(59,21) +posCursor:(59,21) posNoWhite:(59,20) Found expr:(59,2)->(59,21) +JSX M:(59,2)->(59,3) children:(0,-1)->(0,-1) (first:(59,4) e:(59,17)->(59,19)), (k:(59,20) e:(59,20)->(59,21)) +posCursor:(59,21) posNoWhite:(59,20) Found expr:(59,20)->(59,21) Completable: Cjsx([M], k, [first]) [{ "label": "key", diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index 4b783a5c0..68e5e0f57 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -1,6 +1,6 @@ Complete tests/src/RecordCompletion.res 7:3 -posNoWhite:(8,6) Found expr:(8,1)->(8,7) -posNoWhite:(8,6) Found expr:(8,6)->(8,7) +posCursor:(8,7) posNoWhite:(8,6) Found expr:(8,1)->(8,7) +posCursor:(8,7) posNoWhite:(8,6) Found expr:(8,6)->(8,7) Completable: Cpipe(t.n, m) [{ "label": "Js.Array2.mapi", @@ -17,8 +17,8 @@ Completable: Cpipe(t.n, m) }] Complete tests/src/RecordCompletion.res 9:3 -posNoWhite:(10,10) Found expr:(10,1)->(10,11) -posNoWhite:(10,10) Found expr:(10,10)->(10,11) +posCursor:(10,11) posNoWhite:(10,10) Found expr:(10,1)->(10,11) +posCursor:(10,11) posNoWhite:(10,10) Found expr:(10,10)->(10,11) Completable: Cpipe(t2.n2.n, m) [{ "label": "Js.Array2.mapi", From e854c4625a87c5a2bc3dc55084fdc5ec4557cf5f Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 12 Apr 2022 14:05:22 +0200 Subject: [PATCH 006/135] Check if children exist. --- analysis/src/Commands.ml | 92 +++++++++---------- .../tests/src/expected/Completion.res.txt | 4 +- analysis/tests/src/expected/Div.res.txt | 2 +- analysis/tests/src/expected/Jsx.res.txt | 38 ++++---- 4 files changed, 67 insertions(+), 69 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index bfdd61e4a..ffbdd885c 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -4,7 +4,7 @@ let posInLoc ~pos ~loc = Utils.tupleOfLexing loc.Location.loc_start <= pos && pos < Utils.tupleOfLexing loc.loc_end -let extractJsxProps ~text (expr : Parsetree.expression) = +let extractJsxProps ~text ~(eComp : Parsetree.expression) ~args = let rec extractLabelPos ~pos ~i str = if i < String.length str then match str.[i] with @@ -15,48 +15,41 @@ let extractJsxProps ~text (expr : Parsetree.expression) = | _ -> None else None in - match expr.pexp_desc with - | Pexp_apply (eComp, args) -> - (* To check if tha prop is punned, take the string between the last prop - (or the end of e) and the expr in the arg. - If it's whitespace only, then it's a punned . - If it's "?" then it's a punned optional . - Otherwise it should be "id =" perhaps followed by "?". - *) - let rec processProps ~lastOffset ~lastPos ~acc args = - match args with - | (Asttypes.Labelled "children", {Parsetree.pexp_loc}) :: _ -> - (List.rev acc, pexp_loc) - | ((Labelled s | Optional s), (eProp : Parsetree.expression)) :: rest -> ( - let ePosStart = Utils.tupleOfLexing eProp.pexp_loc.loc_start in - let ePosEnd = Utils.tupleOfLexing eProp.pexp_loc.loc_end in - match - ( PartialParser.positionToOffset text ePosStart, - PartialParser.positionToOffset text ePosEnd ) - with - | Some offsetStart, Some offsetEnd -> - let label = String.sub text lastOffset (offsetStart - lastOffset) in - let labelPos = - match extractLabelPos ~pos:lastPos ~i:0 label with - | Some pos -> pos - | None -> (* Must be punned *) ePosStart - in - processProps - ~acc:((s, labelPos, eProp) :: acc) - ~lastOffset:offsetEnd ~lastPos:ePosEnd rest - | _ -> assert false) - | _ -> (* should not happen *) ([], Location.none) - in - let posAfterCompName = Utils.tupleOfLexing eComp.pexp_loc.loc_end in - let offsetAfterCompName = - match PartialParser.positionToOffset text posAfterCompName with - | None -> assert false - | Some offset -> offset - in - args - |> processProps ~lastOffset:offsetAfterCompName ~lastPos:posAfterCompName - ~acc:[] - | _ -> (* should not happen *) ([], Location.none) + let rec processProps ~lastOffset ~lastPos ~acc args = + match args with + | (Asttypes.Labelled "children", {Parsetree.pexp_loc}) :: _ -> + ( List.rev acc, + if pexp_loc.loc_ghost then None + else Some (Utils.tupleOfLexing pexp_loc.loc_start) ) + | ((Labelled s | Optional s), (eProp : Parsetree.expression)) :: rest -> ( + let ePosStart = Utils.tupleOfLexing eProp.pexp_loc.loc_start in + let ePosEnd = Utils.tupleOfLexing eProp.pexp_loc.loc_end in + match + ( PartialParser.positionToOffset text ePosStart, + PartialParser.positionToOffset text ePosEnd ) + with + | Some offsetStart, Some offsetEnd -> + let label = String.sub text lastOffset (offsetStart - lastOffset) in + let labelPos = + match extractLabelPos ~pos:lastPos ~i:0 label with + | Some pos -> pos + | None -> (* Must be punned *) ePosStart + in + processProps + ~acc:((s, labelPos, eProp) :: acc) + ~lastOffset:offsetEnd ~lastPos:ePosEnd rest + | _ -> assert false) + | _ -> (* should not happen *) ([], None) + in + let posAfterCompName = Utils.tupleOfLexing eComp.pexp_loc.loc_end in + let offsetAfterCompName = + match PartialParser.positionToOffset text posAfterCompName with + | None -> assert false + | Some offset -> offset + in + args + |> processProps ~lastOffset:offsetAfterCompName ~lastPos:posAfterCompName + ~acc:[] let completionWithParser ~debug ~path ~posCursor ~currentFile ~textOpt = let text = match textOpt with Some text -> text | None -> assert false in @@ -99,13 +92,18 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~textOpt = match expr.pexp_desc with | Pexp_apply - ({pexp_desc = Pexp_ident {txt = compName; loc = compNameLoc}}, _) + ( ({pexp_desc = Pexp_ident {txt = compName; loc = compNameLoc}} as + eComp), + args ) when Res_parsetree_viewer.isJsxExpression expr -> - let props, childrenLoc = extractJsxProps ~text expr in - Printf.printf "JSX %s:%s children:%s %s\n" + let props, childrenPosStartOpt = extractJsxProps ~text ~eComp ~args in + Printf.printf "JSX %s:%s childrenStart:%s %s\n" (compName |> flattenComponentName |> String.concat ",") (SemanticTokens.locToString compNameLoc) - (SemanticTokens.locToString childrenLoc) + (match childrenPosStartOpt with + | None -> "None" + | Some childrenPosStart -> + SemanticTokens.posToString childrenPosStart) (props |> List.map (fun (lbl, lblPos, (eProp : Parsetree.expression)) -> Printf.sprintf "(%s:%s e:%s)" lbl diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index f1f7b3e79..a7bd23f6a 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -458,7 +458,7 @@ Completable: Cdotpath([Js, Dict, u]) Complete tests/src/Completion.res 50:2 posCursor:(51,28) posNoWhite:(51,27) Found expr:(51,13)->(51,28) -JSX O,Comp:(51,13)->(51,19) children:(0,-1)->(0,-1) (second:(51,20) e:(51,27)->(51,28)) +JSX O,Comp:(51,13)->(51,19) childrenStart:None (second:(51,20) e:(51,27)->(51,28)) posCursor:(51,28) posNoWhite:(51,27) Found expr:(51,27)->(51,28) Completable: Cdotpath([z]) [{ @@ -471,7 +471,7 @@ Completable: Cdotpath([z]) Complete tests/src/Completion.res 52:2 posCursor:(53,21) posNoWhite:(53,20) Found expr:(53,13)->(53,21) -JSX O,Comp:(53,13)->(53,19) children:(0,-1)->(0,-1) (z:(53,20) e:(53,20)->(53,21)) +JSX O,Comp:(53,13)->(53,19) childrenStart:None (z:(53,20) e:(53,20)->(53,21)) posCursor:(53,21) posNoWhite:(53,20) Found expr:(53,20)->(53,21) Completable: Cjsx([O, Comp], z, []) [{ diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 9f4e2a519..6d1f3c6d2 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -3,7 +3,7 @@ Hover tests/src/Div.res 1:10 Complete tests/src/Div.res 3:3 posCursor:(4,15) posNoWhite:(4,14) Found expr:(4,2)->(4,15) -JSX div:(4,2)->(4,5) children:(0,-1)->(0,-1) (dangerous:(4,6) e:(4,6)->(4,15)) +JSX div:(4,2)->(4,5) childrenStart:None (dangerous:(4,6) e:(4,6)->(4,15)) posCursor:(4,15) posNoWhite:(4,14) Found expr:(4,6)->(4,15) Completable: Cjsx([div], dangerous, []) [{ diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 982ee2ea2..897b8f43d 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -3,7 +3,7 @@ Definition tests/src/Jsx.res 5:9 Complete tests/src/Jsx.res 7:2 posCursor:(8,14) posNoWhite:(8,12) Found expr:(8,2)->(8,13) -JSX M:(8,2)->(8,3) children:(0,-1)->(0,-1) (second:(8,4) e:(8,11)->(8,13)) +JSX M:(8,2)->(8,3) childrenStart:None (second:(8,4) e:(8,11)->(8,13)) posCursor:(8,14) posNoWhite:(8,12) Found expr:(8,11)->(8,13) Completable: Cjsx([M], "", [second]) [{ @@ -28,7 +28,7 @@ Completable: Cjsx([M], "", [second]) Complete tests/src/Jsx.res 9:2 posCursor:(10,18) posNoWhite:(10,17) Found expr:(10,2)->(10,18) -JSX M:(10,2)->(10,3) children:(0,-1)->(0,-1) (second:(10,4) e:(10,11)->(10,16)), (f:(10,17) e:(10,17)->(10,18)) +JSX M:(10,2)->(10,3) childrenStart:None (second:(10,4) e:(10,11)->(10,16)), (f:(10,17) e:(10,17)->(10,18)) posCursor:(10,18) posNoWhite:(10,17) Found expr:(10,17)->(10,18) Completable: Cjsx([M], f, [second]) [{ @@ -47,7 +47,7 @@ Completable: Cjsx([M], f, [second]) Complete tests/src/Jsx.res 11:2 posCursor:(12,12) posNoWhite:(12,10) Found expr:(12,10)->(12,11) -JSX M:(12,10)->(12,11) children:(0,-1)->(0,-1) +JSX M:(12,10)->(12,11) childrenStart:None posCursor:(12,12) posNoWhite:(12,10) Found expr:(12,10)->(12,11) Completable: Cjsx([M], "", []) [{ @@ -78,7 +78,7 @@ Completable: Cjsx([M], "", []) Complete tests/src/Jsx.res 18:2 posCursor:(19,17) posNoWhite:(19,16) Found expr:(19,2)->(19,17) -JSX M:(19,2)->(19,3) children:(0,-1)->(0,-1) (prop:(19,4) e:(19,10)->(19,14)), (k:(19,16) e:(19,16)->(19,17)) +JSX M:(19,2)->(19,3) childrenStart:None (prop:(19,4) e:(19,10)->(19,14)), (k:(19,16) e:(19,16)->(19,17)) posCursor:(19,17) posNoWhite:(19,16) Found expr:(19,16)->(19,17) Completable: Cjsx([M], k, [prop]) [{ @@ -91,7 +91,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 20:2 posCursor:(21,15) posNoWhite:(21,14) Found expr:(21,2)->(21,15) -JSX M:(21,2)->(21,3) children:(0,-1)->(0,-1) (prop:(21,4) e:(21,9)->(21,13)), (k:(21,14) e:(21,14)->(21,15)) +JSX M:(21,2)->(21,3) childrenStart:None (prop:(21,4) e:(21,9)->(21,13)), (k:(21,14) e:(21,14)->(21,15)) posCursor:(21,15) posNoWhite:(21,14) Found expr:(21,14)->(21,15) Completable: Cjsx([M], k, [prop]) [{ @@ -104,7 +104,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 22:2 posCursor:(23,19) posNoWhite:(23,18) Found expr:(23,2)->(23,19) -JSX M:(23,2)->(23,3) children:(0,-1)->(0,-1) (prop:(23,4) e:(23,9)->(23,17)), (k:(23,18) e:(23,18)->(23,19)) +JSX M:(23,2)->(23,3) childrenStart:None (prop:(23,4) e:(23,9)->(23,17)), (k:(23,18) e:(23,18)->(23,19)) posCursor:(23,19) posNoWhite:(23,18) Found expr:(23,18)->(23,19) Completable: Cjsx([M], k, [prop]) [{ @@ -117,7 +117,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 24:2 posCursor:(25,22) posNoWhite:(25,21) Found expr:(25,2)->(25,22) -JSX M:(25,2)->(25,3) children:(0,-1)->(0,-1) (prop:(25,4) e:(25,9)->(25,20)), (k:(25,21) e:(25,21)->(25,22)) +JSX M:(25,2)->(25,3) childrenStart:None (prop:(25,4) e:(25,9)->(25,20)), (k:(25,21) e:(25,21)->(25,22)) posCursor:(25,22) posNoWhite:(25,21) Found expr:(25,21)->(25,22) Completable: Cjsx([M], k, [prop]) [{ @@ -130,7 +130,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 26:2 posCursor:(27,16) posNoWhite:(27,15) Found expr:(27,2)->(27,16) -JSX M:(27,2)->(27,3) children:(0,-1)->(0,-1) (prop:(27,4) e:(27,10)->(27,14)), (k:(27,15) e:(27,15)->(27,16)) +JSX M:(27,2)->(27,3) childrenStart:None (prop:(27,4) e:(27,10)->(27,14)), (k:(27,15) e:(27,15)->(27,16)) posCursor:(27,16) posNoWhite:(27,15) Found expr:(27,15)->(27,16) Completable: Cjsx([M], k, [prop]) [{ @@ -143,7 +143,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 28:2 posCursor:(29,14) posNoWhite:(29,13) Found expr:(29,2)->(29,14) -JSX M:(29,2)->(29,3) children:(0,-1)->(0,-1) (prop:(29,4) e:(29,9)->(29,12)), (k:(29,13) e:(29,13)->(29,14)) +JSX M:(29,2)->(29,3) childrenStart:None (prop:(29,4) e:(29,9)->(29,12)), (k:(29,13) e:(29,13)->(29,14)) posCursor:(29,14) posNoWhite:(29,13) Found expr:(29,13)->(29,14) Completable: Cjsx([M], k, [prop]) [{ @@ -156,7 +156,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 30:2 posCursor:(31,15) posNoWhite:(31,14) Found expr:(31,2)->(31,15) -JSX M:(31,2)->(31,3) children:(0,-1)->(0,-1) (prop:(31,4) e:(31,9)->(31,13)), (k:(31,14) e:(31,14)->(31,15)) +JSX M:(31,2)->(31,3) childrenStart:None (prop:(31,4) e:(31,9)->(31,13)), (k:(31,14) e:(31,14)->(31,15)) posCursor:(31,15) posNoWhite:(31,14) Found expr:(31,14)->(31,15) Completable: Cjsx([M], k, [prop]) [{ @@ -169,14 +169,14 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 32:2 posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,2)->(33,16) -JSX M:(33,2)->(33,3) children:(0,-1)->(0,-1) (prop:(33,4) e:(33,9)->(33,14)), (k:(33,15) e:(33,15)->(33,16)) +JSX M:(33,2)->(33,3) childrenStart:None (prop:(33,4) e:(33,9)->(33,14)), (k:(33,15) e:(33,15)->(33,16)) posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,15)->(33,16) Completable: Cdotpath([k]) [] Complete tests/src/Jsx.res 34:2 posCursor:(35,14) posNoWhite:(35,13) Found expr:(35,2)->(35,14) -JSX M:(35,2)->(35,3) children:(0,-1)->(0,-1) (prop:(35,4) e:(35,9)->(35,12)), (k:(35,13) e:(35,13)->(35,14)) +JSX M:(35,2)->(35,3) childrenStart:None (prop:(35,4) e:(35,9)->(35,12)), (k:(35,13) e:(35,13)->(35,14)) posCursor:(35,14) posNoWhite:(35,13) Found expr:(35,13)->(35,14) Completable: Cjsx([M], k, [prop]) [{ @@ -189,7 +189,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 36:2 posCursor:(37,25) posNoWhite:(37,24) Found expr:(37,2)->(37,25) -JSX M:(37,2)->(37,3) children:(0,-1)->(0,-1) (prop:(37,4) e:(37,9)->(37,23)), (k:(37,24) e:(37,24)->(37,25)) +JSX M:(37,2)->(37,3) childrenStart:None (prop:(37,4) e:(37,9)->(37,23)), (k:(37,24) e:(37,24)->(37,25)) posCursor:(37,25) posNoWhite:(37,24) Found expr:(37,24)->(37,25) Completable: Cjsx([M], k, [prop]) [{ @@ -202,7 +202,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 38:2 posCursor:(39,36) posNoWhite:(39,35) Found expr:(39,2)->(39,36) -JSX M:(39,2)->(39,3) children:(0,-1)->(0,-1) (prop:(39,4) e:(39,9)->(39,34)), (k:(39,35) e:(39,35)->(39,36)) +JSX M:(39,2)->(39,3) childrenStart:None (prop:(39,4) e:(39,9)->(39,34)), (k:(39,35) e:(39,35)->(39,36)) posCursor:(39,36) posNoWhite:(39,35) Found expr:(39,35)->(39,36) Completable: Cjsx([M], k, [prop]) [{ @@ -215,7 +215,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 40:2 posCursor:(41,23) posNoWhite:(41,22) Found expr:(41,2)->(41,23) -JSX M:(41,2)->(41,3) children:(0,-1)->(0,-1) (prop:(41,4) e:(41,9)->(41,21)), (k:(41,22) e:(41,22)->(41,23)) +JSX M:(41,2)->(41,3) childrenStart:None (prop:(41,4) e:(41,9)->(41,21)), (k:(41,22) e:(41,22)->(41,23)) posCursor:(41,23) posNoWhite:(41,22) Found expr:(41,22)->(41,23) Completable: Cjsx([M], k, [prop]) [{ @@ -231,7 +231,7 @@ Definition tests/src/Jsx.res 43:11 Complete tests/src/Jsx.res 52:2 posCursor:(53,8) posNoWhite:(53,7) Found expr:(53,2)->(53,8) -JSX Ext:(53,2)->(53,5) children:(0,-1)->(0,-1) (al:(53,6) e:(53,6)->(53,8)) +JSX Ext:(53,2)->(53,5) childrenStart:None (al:(53,6) e:(53,6)->(53,8)) posCursor:(53,8) posNoWhite:(53,7) Found expr:(53,6)->(53,8) Completable: Cjsx([Ext], al, []) [{ @@ -244,7 +244,7 @@ Completable: Cjsx([Ext], al, []) Complete tests/src/Jsx.res 54:2 posCursor:(55,10) posNoWhite:(55,8) Found expr:(55,2)->(55,9) -JSX M:(55,2)->(55,3) children:(0,-1)->(0,-1) (first:(55,4) e:(55,4)->(55,9)) +JSX M:(55,2)->(55,3) childrenStart:None (first:(55,4) e:(55,4)->(55,9)) posCursor:(55,10) posNoWhite:(55,8) Found expr:(55,4)->(55,9) Completable: Cjsx([M], "", [first]) [{ @@ -269,7 +269,7 @@ Completable: Cjsx([M], "", [first]) Complete tests/src/Jsx.res 56:2 posCursor:(57,14) posNoWhite:(57,13) Found expr:(57,2)->(57,14) -JSX M:(57,2)->(57,3) children:(0,-1)->(0,-1) (first:(57,4) e:(57,10)->(57,12)), (k:(57,13) e:(57,13)->(57,14)) +JSX M:(57,2)->(57,3) childrenStart:None (first:(57,4) e:(57,10)->(57,12)), (k:(57,13) e:(57,13)->(57,14)) posCursor:(57,14) posNoWhite:(57,13) Found expr:(57,13)->(57,14) Completable: Cjsx([M], k, [first]) [{ @@ -282,7 +282,7 @@ Completable: Cjsx([M], k, [first]) Complete tests/src/Jsx.res 58:2 posCursor:(59,21) posNoWhite:(59,20) Found expr:(59,2)->(59,21) -JSX M:(59,2)->(59,3) children:(0,-1)->(0,-1) (first:(59,4) e:(59,17)->(59,19)), (k:(59,20) e:(59,20)->(59,21)) +JSX M:(59,2)->(59,3) childrenStart:None (first:(59,4) e:(59,17)->(59,19)), (k:(59,20) e:(59,20)->(59,21)) posCursor:(59,21) posNoWhite:(59,20) Found expr:(59,20)->(59,21) Completable: Cjsx([M], k, [first]) [{ From c9e5bda9126f4a5e10f7655b912d2c112969a360 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 12 Apr 2022 14:28:31 +0200 Subject: [PATCH 007/135] refactor --- analysis/src/Cli.ml | 5 +- analysis/src/Commands.ml | 121 ++++++++++++++++++--------------- analysis/src/NewCompletions.ml | 21 +++--- 3 files changed, 78 insertions(+), 69 deletions(-) diff --git a/analysis/src/Cli.ml b/analysis/src/Cli.ml index 9aabb9ccb..3a9a5fbb7 100644 --- a/analysis/src/Cli.ml +++ b/analysis/src/Cli.ml @@ -68,8 +68,9 @@ Options: let main () = match Array.to_list Sys.argv with | [_; "completion"; path; line; col; currentFile] -> - Commands.completion ~debug:false ~path ~line:(int_of_string line) - ~col:(int_of_string col) ~currentFile + Commands.completion ~debug:false ~path + ~pos:(int_of_string line, int_of_string col) + ~currentFile | [_; "definition"; path; line; col] -> Commands.definition ~path ~line:(int_of_string line) ~col:(int_of_string col) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index ffbdd885c..0c86add4f 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -4,7 +4,15 @@ let posInLoc ~pos ~loc = Utils.tupleOfLexing loc.Location.loc_start <= pos && pos < Utils.tupleOfLexing loc.loc_end -let extractJsxProps ~text ~(eComp : Parsetree.expression) ~args = +type prop = {name : string; pos : int * int; exp : Parsetree.expression} + +type jsxProps = { + componentPath : string list; + props : prop list; + childrenStart : (int * int) option; +} + +let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = let rec extractLabelPos ~pos ~i str = if i < String.length str then match str.[i] with @@ -15,12 +23,27 @@ let extractJsxProps ~text ~(eComp : Parsetree.expression) ~args = | _ -> None else None in + let flattenComponentName lid = + let rec loop acc lid = + match lid with + | Longident.Lident txt -> txt :: acc + | Ldot (lid, txt) -> + let acc = if txt = "createElement" then acc else txt :: acc in + loop acc lid + | _ -> acc + in + loop [] lid + in let rec processProps ~lastOffset ~lastPos ~acc args = match args with | (Asttypes.Labelled "children", {Parsetree.pexp_loc}) :: _ -> - ( List.rev acc, - if pexp_loc.loc_ghost then None - else Some (Utils.tupleOfLexing pexp_loc.loc_start) ) + { + componentPath = flattenComponentName compName.txt; + props = List.rev acc; + childrenStart = + (if pexp_loc.loc_ghost then None + else Some (Utils.tupleOfLexing pexp_loc.loc_start)); + } | ((Labelled s | Optional s), (eProp : Parsetree.expression)) :: rest -> ( let ePosStart = Utils.tupleOfLexing eProp.pexp_loc.loc_start in let ePosEnd = Utils.tupleOfLexing eProp.pexp_loc.loc_end in @@ -36,12 +59,14 @@ let extractJsxProps ~text ~(eComp : Parsetree.expression) ~args = | None -> (* Must be punned *) ePosStart in processProps - ~acc:((s, labelPos, eProp) :: acc) + ~acc:({name = s; pos = labelPos; exp = eProp} :: acc) ~lastOffset:offsetEnd ~lastPos:ePosEnd rest | _ -> assert false) - | _ -> (* should not happen *) ([], None) + | _ -> + (* should not happen *) + {componentPath = []; props = []; childrenStart = None} in - let posAfterCompName = Utils.tupleOfLexing eComp.pexp_loc.loc_end in + let posAfterCompName = Utils.tupleOfLexing compName.loc.loc_end in let offsetAfterCompName = match PartialParser.positionToOffset text posAfterCompName with | None -> assert false @@ -51,8 +76,7 @@ let extractJsxProps ~text ~(eComp : Parsetree.expression) ~args = |> processProps ~lastOffset:offsetAfterCompName ~lastPos:posAfterCompName ~acc:[] -let completionWithParser ~debug ~path ~posCursor ~currentFile ~textOpt = - let text = match textOpt with Some text -> text | None -> assert false in +let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let offset = match PartialParser.positionToOffset text posCursor with | Some offset -> offset @@ -64,18 +88,6 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~textOpt = (line, max 0 col - offset + offsetNoWhite) in - let flattenComponentName lid = - let rec loop acc lid = - match lid with - | Longident.Lident txt -> txt :: acc - | Ldot (lid, txt) -> - let acc = if txt = "createElement" then acc else txt :: acc in - loop acc lid - | _ -> acc - in - loop [] lid - in - if Filename.check_suffix path ".res" then ( let parser = Res_driver.parsingEngine.parseImplementation ~forPrinter:false @@ -91,24 +103,23 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~textOpt = (SemanticTokens.locToString expr.pexp_loc); match expr.pexp_desc with - | Pexp_apply - ( ({pexp_desc = Pexp_ident {txt = compName; loc = compNameLoc}} as - eComp), - args ) + | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) when Res_parsetree_viewer.isJsxExpression expr -> - let props, childrenPosStartOpt = extractJsxProps ~text ~eComp ~args in + let {componentPath; props; childrenStart} = + extractJsxProps ~text ~compName ~args + in Printf.printf "JSX %s:%s childrenStart:%s %s\n" - (compName |> flattenComponentName |> String.concat ",") - (SemanticTokens.locToString compNameLoc) - (match childrenPosStartOpt with + (componentPath |> String.concat ",") + (SemanticTokens.locToString compName.loc) + (match childrenStart with | None -> "None" | Some childrenPosStart -> SemanticTokens.posToString childrenPosStart) (props - |> List.map (fun (lbl, lblPos, (eProp : Parsetree.expression)) -> - Printf.sprintf "(%s:%s e:%s)" lbl - (SemanticTokens.posToString lblPos) - (SemanticTokens.locToString eProp.pexp_loc)) + |> List.map (fun {name; pos; exp} -> + Printf.sprintf "(%s:%s e:%s)" name + (SemanticTokens.posToString pos) + (SemanticTokens.locToString exp.pexp_loc)) |> String.concat ", ") | _ -> ()); Ast_iterator.default_iterator.expr iterator expr @@ -118,30 +129,30 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~textOpt = iterator.structure iterator structure |> ignore; if not !found then if debug then Printf.printf "XXX Not found!\n") -let completion ~debug ~path ~line ~col ~currentFile = - let pos = (line, col) in - +let completion ~debug ~path ~pos ~currentFile = let result = let textOpt = Files.readFile currentFile in - completionWithParser ~debug ~path ~posCursor:pos ~currentFile ~textOpt; - let completionItems = - match NewCompletions.getCompletable ~textOpt ~pos with - | None -> [] - | Some (completable, rawOpens) -> ( - if debug then - Printf.printf "Completable: %s\n" - (PartialParser.completableToString completable); - (* Only perform expensive ast operations if there are completables *) - match Cmt.fromPath ~path with + match textOpt with + | None -> [] + | Some text -> + completionWithParser ~debug ~path ~posCursor:pos ~currentFile ~text; + let completionItems = + match NewCompletions.getCompletable ~text ~pos with | None -> [] - | Some full -> - NewCompletions.computeCompletions ~completable ~full ~pos ~rawOpens) - in - completionItems - |> List.map Protocol.stringifyCompletionItem - |> Protocol.array + | Some (completable, rawOpens) -> ( + if debug then + Printf.printf "Completable: %s\n" + (PartialParser.completableToString completable); + (* Only perform expensive ast operations if there are completables *) + match Cmt.fromPath ~path with + | None -> [] + | Some full -> + NewCompletions.computeCompletions ~completable ~full ~pos ~rawOpens) + in + completionItems in - print_endline result + print_endline + (result |> List.map Protocol.stringifyCompletionItem |> Protocol.array) let hover ~path ~line ~col = let result = @@ -335,7 +346,7 @@ let rename ~path ~line ~col ~newName = let format ~path = if Filename.check_suffix path ".res" then - let {Res_driver.parsetree = structure; comments; diagnostics} = + let {Res_driver.parsetree = structure; comments} = Res_driver.parsingEngine.parseImplementation ~forPrinter:true ~filename:path in @@ -413,7 +424,7 @@ let test ~path = let line = line + 1 in let col = len - mlen - 3 in close_out cout; - completion ~debug:true ~path ~line ~col ~currentFile; + completion ~debug:true ~path ~pos:(line, col) ~currentFile; Sys.remove currentFile | "hig" -> print_endline ("Highlight " ^ path); diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 67e64f853..f9bbf97d0 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -1226,20 +1226,17 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens |> List.filter (fun (name, _t) -> Utils.startsWith name prefix) |> List.map mkLabel -let getCompletable ~textOpt ~pos = - match textOpt with +let getCompletable ~text ~pos = + match PartialParser.positionToOffset text pos with | None -> None - | Some text -> ( - match PartialParser.positionToOffset text pos with + | Some offset -> ( + match PartialParser.findCompletable text offset with | None -> None - | Some offset -> ( - match PartialParser.findCompletable text offset with - | None -> None - | Some completable -> - let offsetFromLineStart = offset - snd pos in - (* try to avoid confusion e.g. unclosed quotes at current position *) - let rawOpens = PartialParser.findOpens text offsetFromLineStart in - Some (completable, rawOpens))) + | Some completable -> + let offsetFromLineStart = offset - snd pos in + (* try to avoid confusion e.g. unclosed quotes at current position *) + let rawOpens = PartialParser.findOpens text offsetFromLineStart in + Some (completable, rawOpens)) let computeCompletions ~completable ~full ~pos ~rawOpens = let package = full.package in From d1ea3ae8ccd27637c63c1359c4b8ac7e4fff39d4 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 12 Apr 2022 14:53:44 +0200 Subject: [PATCH 008/135] Debug. --- analysis/src/Commands.ml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 0c86add4f..37ab7a6be 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -108,19 +108,20 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let {componentPath; props; childrenStart} = extractJsxProps ~text ~compName ~args in - Printf.printf "JSX %s:%s childrenStart:%s %s\n" - (componentPath |> String.concat ",") - (SemanticTokens.locToString compName.loc) - (match childrenStart with - | None -> "None" - | Some childrenPosStart -> - SemanticTokens.posToString childrenPosStart) - (props - |> List.map (fun {name; pos; exp} -> - Printf.sprintf "(%s:%s e:%s)" name - (SemanticTokens.posToString pos) - (SemanticTokens.locToString exp.pexp_loc)) - |> String.concat ", ") + if debug then + Printf.printf "JSX %s:%s childrenStart:%s %s\n" + (componentPath |> String.concat ",") + (SemanticTokens.locToString compName.loc) + (match childrenStart with + | None -> "None" + | Some childrenPosStart -> + SemanticTokens.posToString childrenPosStart) + (props + |> List.map (fun {name; pos; exp} -> + Printf.sprintf "(%s:%s e:%s)" name + (SemanticTokens.posToString pos) + (SemanticTokens.locToString exp.pexp_loc)) + |> String.concat ", ") | _ -> ()); Ast_iterator.default_iterator.expr iterator expr in From c86f7d1e22a62a59853dfffacb313d0304425eac Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 12 Apr 2022 15:24:47 +0200 Subject: [PATCH 009/135] Print prop range. --- analysis/src/Commands.ml | 73 +++++++++++++++---- .../tests/src/expected/Completion.res.txt | 4 +- analysis/tests/src/expected/Div.res.txt | 2 +- analysis/tests/src/expected/Jsx.res.txt | 36 ++++----- 4 files changed, 80 insertions(+), 35 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 37ab7a6be..e89ff2824 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -4,7 +4,12 @@ let posInLoc ~pos ~loc = Utils.tupleOfLexing loc.Location.loc_start <= pos && pos < Utils.tupleOfLexing loc.loc_end -type prop = {name : string; pos : int * int; exp : Parsetree.expression} +type prop = { + name : string; + posStart : int * int; + posEnd : int * int; + exp : Parsetree.expression; +} type jsxProps = { componentPath : string list; @@ -12,6 +17,26 @@ type jsxProps = { childrenStart : (int * int) option; } +let findJsxPropCompletable ~jsxProps ~endPos ~pos = + let rec loop ~seen props = + match props with + | prop :: rest -> + if prop.posEnd = pos then + Some (PartialParser.Cjsx (jsxProps.componentPath, prop.name, seen)) + else if posInLoc ~pos ~loc:prop.exp.pexp_loc then None + else loop ~seen:(seen @ [prop.name]) rest + | [] -> + let posAfterProps = + match jsxProps.childrenStart with + | Some childrenPos -> childrenPos + | None -> endPos + in + if pos <= posAfterProps then + Some (PartialParser.Cjsx (jsxProps.componentPath, "", seen)) + else None + in + loop ~seen:[] jsxProps.props + let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = let rec extractLabelPos ~pos ~i str = if i < String.length str then @@ -59,7 +84,14 @@ let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = | None -> (* Must be punned *) ePosStart in processProps - ~acc:({name = s; pos = labelPos; exp = eProp} :: acc) + ~acc: + ({ + name = s; + posStart = labelPos; + posEnd = (fst labelPos, snd labelPos + String.length s); + exp = eProp; + } + :: acc) ~lastOffset:offsetEnd ~lastPos:ePosEnd rest | _ -> assert false) | _ -> @@ -82,7 +114,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | Some offset -> offset | None -> assert false in - let offsetNoWhite = PartialParser.skipWhite text offset in + let offsetNoWhite = PartialParser.skipWhite text (offset - 1) in let posNoWhite = let line, col = posCursor in (line, max 0 col - offset + offsetNoWhite) @@ -105,23 +137,36 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = match expr.pexp_desc with | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) when Res_parsetree_viewer.isJsxExpression expr -> - let {componentPath; props; childrenStart} = - extractJsxProps ~text ~compName ~args - in + let jsxProps = extractJsxProps ~text ~compName ~args in if debug then Printf.printf "JSX %s:%s childrenStart:%s %s\n" - (componentPath |> String.concat ",") + (jsxProps.componentPath |> String.concat ",") (SemanticTokens.locToString compName.loc) - (match childrenStart with + (match jsxProps.childrenStart with | None -> "None" | Some childrenPosStart -> SemanticTokens.posToString childrenPosStart) - (props - |> List.map (fun {name; pos; exp} -> - Printf.sprintf "(%s:%s e:%s)" name - (SemanticTokens.posToString pos) + (jsxProps.props + |> List.map (fun {name; posStart; posEnd; exp} -> + Printf.sprintf "(%s:%s->%s e:%s)" name + (SemanticTokens.posToString posStart) + (SemanticTokens.posToString posEnd) (SemanticTokens.locToString exp.pexp_loc)) - |> String.concat ", ") + |> String.concat ", "); + let () = + match + findJsxPropCompletable ~jsxProps + ~endPos:(Utils.tupleOfLexing expr.pexp_loc.loc_end) + ~pos:posCursor + with + | Some completable -> + (); + (* if debug then + Printf.printf "Found JSX completable %s\n" + (PartialParser.completableToString completable) *) + | None -> () + in + () | _ -> ()); Ast_iterator.default_iterator.expr iterator expr in @@ -134,7 +179,7 @@ let completion ~debug ~path ~pos ~currentFile = let result = let textOpt = Files.readFile currentFile in match textOpt with - | None -> [] + | None | Some "" -> [] | Some text -> completionWithParser ~debug ~path ~posCursor:pos ~currentFile ~text; let completionItems = diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index a7bd23f6a..e94625fe8 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -458,7 +458,7 @@ Completable: Cdotpath([Js, Dict, u]) Complete tests/src/Completion.res 50:2 posCursor:(51,28) posNoWhite:(51,27) Found expr:(51,13)->(51,28) -JSX O,Comp:(51,13)->(51,19) childrenStart:None (second:(51,20) e:(51,27)->(51,28)) +JSX O,Comp:(51,13)->(51,19) childrenStart:None (second:(51,20)->(51,26) e:(51,27)->(51,28)) posCursor:(51,28) posNoWhite:(51,27) Found expr:(51,27)->(51,28) Completable: Cdotpath([z]) [{ @@ -471,7 +471,7 @@ Completable: Cdotpath([z]) Complete tests/src/Completion.res 52:2 posCursor:(53,21) posNoWhite:(53,20) Found expr:(53,13)->(53,21) -JSX O,Comp:(53,13)->(53,19) childrenStart:None (z:(53,20) e:(53,20)->(53,21)) +JSX O,Comp:(53,13)->(53,19) childrenStart:None (z:(53,20)->(53,21) e:(53,20)->(53,21)) posCursor:(53,21) posNoWhite:(53,20) Found expr:(53,20)->(53,21) Completable: Cjsx([O, Comp], z, []) [{ diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 6d1f3c6d2..0961b86e8 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -3,7 +3,7 @@ Hover tests/src/Div.res 1:10 Complete tests/src/Div.res 3:3 posCursor:(4,15) posNoWhite:(4,14) Found expr:(4,2)->(4,15) -JSX div:(4,2)->(4,5) childrenStart:None (dangerous:(4,6) e:(4,6)->(4,15)) +JSX div:(4,2)->(4,5) childrenStart:None (dangerous:(4,6)->(4,15) e:(4,6)->(4,15)) posCursor:(4,15) posNoWhite:(4,14) Found expr:(4,6)->(4,15) Completable: Cjsx([div], dangerous, []) [{ diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 897b8f43d..ed7b28455 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -3,7 +3,7 @@ Definition tests/src/Jsx.res 5:9 Complete tests/src/Jsx.res 7:2 posCursor:(8,14) posNoWhite:(8,12) Found expr:(8,2)->(8,13) -JSX M:(8,2)->(8,3) childrenStart:None (second:(8,4) e:(8,11)->(8,13)) +JSX M:(8,2)->(8,3) childrenStart:None (second:(8,4)->(8,10) e:(8,11)->(8,13)) posCursor:(8,14) posNoWhite:(8,12) Found expr:(8,11)->(8,13) Completable: Cjsx([M], "", [second]) [{ @@ -28,7 +28,7 @@ Completable: Cjsx([M], "", [second]) Complete tests/src/Jsx.res 9:2 posCursor:(10,18) posNoWhite:(10,17) Found expr:(10,2)->(10,18) -JSX M:(10,2)->(10,3) childrenStart:None (second:(10,4) e:(10,11)->(10,16)), (f:(10,17) e:(10,17)->(10,18)) +JSX M:(10,2)->(10,3) childrenStart:None (second:(10,4)->(10,10) e:(10,11)->(10,16)), (f:(10,17)->(10,18) e:(10,17)->(10,18)) posCursor:(10,18) posNoWhite:(10,17) Found expr:(10,17)->(10,18) Completable: Cjsx([M], f, [second]) [{ @@ -78,7 +78,7 @@ Completable: Cjsx([M], "", []) Complete tests/src/Jsx.res 18:2 posCursor:(19,17) posNoWhite:(19,16) Found expr:(19,2)->(19,17) -JSX M:(19,2)->(19,3) childrenStart:None (prop:(19,4) e:(19,10)->(19,14)), (k:(19,16) e:(19,16)->(19,17)) +JSX M:(19,2)->(19,3) childrenStart:None (prop:(19,4)->(19,8) e:(19,10)->(19,14)), (k:(19,16)->(19,17) e:(19,16)->(19,17)) posCursor:(19,17) posNoWhite:(19,16) Found expr:(19,16)->(19,17) Completable: Cjsx([M], k, [prop]) [{ @@ -91,7 +91,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 20:2 posCursor:(21,15) posNoWhite:(21,14) Found expr:(21,2)->(21,15) -JSX M:(21,2)->(21,3) childrenStart:None (prop:(21,4) e:(21,9)->(21,13)), (k:(21,14) e:(21,14)->(21,15)) +JSX M:(21,2)->(21,3) childrenStart:None (prop:(21,4)->(21,8) e:(21,9)->(21,13)), (k:(21,14)->(21,15) e:(21,14)->(21,15)) posCursor:(21,15) posNoWhite:(21,14) Found expr:(21,14)->(21,15) Completable: Cjsx([M], k, [prop]) [{ @@ -104,7 +104,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 22:2 posCursor:(23,19) posNoWhite:(23,18) Found expr:(23,2)->(23,19) -JSX M:(23,2)->(23,3) childrenStart:None (prop:(23,4) e:(23,9)->(23,17)), (k:(23,18) e:(23,18)->(23,19)) +JSX M:(23,2)->(23,3) childrenStart:None (prop:(23,4)->(23,8) e:(23,9)->(23,17)), (k:(23,18)->(23,19) e:(23,18)->(23,19)) posCursor:(23,19) posNoWhite:(23,18) Found expr:(23,18)->(23,19) Completable: Cjsx([M], k, [prop]) [{ @@ -117,7 +117,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 24:2 posCursor:(25,22) posNoWhite:(25,21) Found expr:(25,2)->(25,22) -JSX M:(25,2)->(25,3) childrenStart:None (prop:(25,4) e:(25,9)->(25,20)), (k:(25,21) e:(25,21)->(25,22)) +JSX M:(25,2)->(25,3) childrenStart:None (prop:(25,4)->(25,8) e:(25,9)->(25,20)), (k:(25,21)->(25,22) e:(25,21)->(25,22)) posCursor:(25,22) posNoWhite:(25,21) Found expr:(25,21)->(25,22) Completable: Cjsx([M], k, [prop]) [{ @@ -130,7 +130,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 26:2 posCursor:(27,16) posNoWhite:(27,15) Found expr:(27,2)->(27,16) -JSX M:(27,2)->(27,3) childrenStart:None (prop:(27,4) e:(27,10)->(27,14)), (k:(27,15) e:(27,15)->(27,16)) +JSX M:(27,2)->(27,3) childrenStart:None (prop:(27,4)->(27,8) e:(27,10)->(27,14)), (k:(27,15)->(27,16) e:(27,15)->(27,16)) posCursor:(27,16) posNoWhite:(27,15) Found expr:(27,15)->(27,16) Completable: Cjsx([M], k, [prop]) [{ @@ -143,7 +143,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 28:2 posCursor:(29,14) posNoWhite:(29,13) Found expr:(29,2)->(29,14) -JSX M:(29,2)->(29,3) childrenStart:None (prop:(29,4) e:(29,9)->(29,12)), (k:(29,13) e:(29,13)->(29,14)) +JSX M:(29,2)->(29,3) childrenStart:None (prop:(29,4)->(29,8) e:(29,9)->(29,12)), (k:(29,13)->(29,14) e:(29,13)->(29,14)) posCursor:(29,14) posNoWhite:(29,13) Found expr:(29,13)->(29,14) Completable: Cjsx([M], k, [prop]) [{ @@ -156,7 +156,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 30:2 posCursor:(31,15) posNoWhite:(31,14) Found expr:(31,2)->(31,15) -JSX M:(31,2)->(31,3) childrenStart:None (prop:(31,4) e:(31,9)->(31,13)), (k:(31,14) e:(31,14)->(31,15)) +JSX M:(31,2)->(31,3) childrenStart:None (prop:(31,4)->(31,8) e:(31,9)->(31,13)), (k:(31,14)->(31,15) e:(31,14)->(31,15)) posCursor:(31,15) posNoWhite:(31,14) Found expr:(31,14)->(31,15) Completable: Cjsx([M], k, [prop]) [{ @@ -169,14 +169,14 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 32:2 posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,2)->(33,16) -JSX M:(33,2)->(33,3) childrenStart:None (prop:(33,4) e:(33,9)->(33,14)), (k:(33,15) e:(33,15)->(33,16)) +JSX M:(33,2)->(33,3) childrenStart:None (prop:(33,4)->(33,8) e:(33,9)->(33,14)), (k:(33,15)->(33,16) e:(33,15)->(33,16)) posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,15)->(33,16) Completable: Cdotpath([k]) [] Complete tests/src/Jsx.res 34:2 posCursor:(35,14) posNoWhite:(35,13) Found expr:(35,2)->(35,14) -JSX M:(35,2)->(35,3) childrenStart:None (prop:(35,4) e:(35,9)->(35,12)), (k:(35,13) e:(35,13)->(35,14)) +JSX M:(35,2)->(35,3) childrenStart:None (prop:(35,4)->(35,8) e:(35,9)->(35,12)), (k:(35,13)->(35,14) e:(35,13)->(35,14)) posCursor:(35,14) posNoWhite:(35,13) Found expr:(35,13)->(35,14) Completable: Cjsx([M], k, [prop]) [{ @@ -189,7 +189,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 36:2 posCursor:(37,25) posNoWhite:(37,24) Found expr:(37,2)->(37,25) -JSX M:(37,2)->(37,3) childrenStart:None (prop:(37,4) e:(37,9)->(37,23)), (k:(37,24) e:(37,24)->(37,25)) +JSX M:(37,2)->(37,3) childrenStart:None (prop:(37,4)->(37,8) e:(37,9)->(37,23)), (k:(37,24)->(37,25) e:(37,24)->(37,25)) posCursor:(37,25) posNoWhite:(37,24) Found expr:(37,24)->(37,25) Completable: Cjsx([M], k, [prop]) [{ @@ -202,7 +202,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 38:2 posCursor:(39,36) posNoWhite:(39,35) Found expr:(39,2)->(39,36) -JSX M:(39,2)->(39,3) childrenStart:None (prop:(39,4) e:(39,9)->(39,34)), (k:(39,35) e:(39,35)->(39,36)) +JSX M:(39,2)->(39,3) childrenStart:None (prop:(39,4)->(39,8) e:(39,9)->(39,34)), (k:(39,35)->(39,36) e:(39,35)->(39,36)) posCursor:(39,36) posNoWhite:(39,35) Found expr:(39,35)->(39,36) Completable: Cjsx([M], k, [prop]) [{ @@ -215,7 +215,7 @@ Completable: Cjsx([M], k, [prop]) Complete tests/src/Jsx.res 40:2 posCursor:(41,23) posNoWhite:(41,22) Found expr:(41,2)->(41,23) -JSX M:(41,2)->(41,3) childrenStart:None (prop:(41,4) e:(41,9)->(41,21)), (k:(41,22) e:(41,22)->(41,23)) +JSX M:(41,2)->(41,3) childrenStart:None (prop:(41,4)->(41,8) e:(41,9)->(41,21)), (k:(41,22)->(41,23) e:(41,22)->(41,23)) posCursor:(41,23) posNoWhite:(41,22) Found expr:(41,22)->(41,23) Completable: Cjsx([M], k, [prop]) [{ @@ -231,7 +231,7 @@ Definition tests/src/Jsx.res 43:11 Complete tests/src/Jsx.res 52:2 posCursor:(53,8) posNoWhite:(53,7) Found expr:(53,2)->(53,8) -JSX Ext:(53,2)->(53,5) childrenStart:None (al:(53,6) e:(53,6)->(53,8)) +JSX Ext:(53,2)->(53,5) childrenStart:None (al:(53,6)->(53,8) e:(53,6)->(53,8)) posCursor:(53,8) posNoWhite:(53,7) Found expr:(53,6)->(53,8) Completable: Cjsx([Ext], al, []) [{ @@ -244,7 +244,7 @@ Completable: Cjsx([Ext], al, []) Complete tests/src/Jsx.res 54:2 posCursor:(55,10) posNoWhite:(55,8) Found expr:(55,2)->(55,9) -JSX M:(55,2)->(55,3) childrenStart:None (first:(55,4) e:(55,4)->(55,9)) +JSX M:(55,2)->(55,3) childrenStart:None (first:(55,4)->(55,9) e:(55,4)->(55,9)) posCursor:(55,10) posNoWhite:(55,8) Found expr:(55,4)->(55,9) Completable: Cjsx([M], "", [first]) [{ @@ -269,7 +269,7 @@ Completable: Cjsx([M], "", [first]) Complete tests/src/Jsx.res 56:2 posCursor:(57,14) posNoWhite:(57,13) Found expr:(57,2)->(57,14) -JSX M:(57,2)->(57,3) childrenStart:None (first:(57,4) e:(57,10)->(57,12)), (k:(57,13) e:(57,13)->(57,14)) +JSX M:(57,2)->(57,3) childrenStart:None (first:(57,4)->(57,9) e:(57,10)->(57,12)), (k:(57,13)->(57,14) e:(57,13)->(57,14)) posCursor:(57,14) posNoWhite:(57,13) Found expr:(57,13)->(57,14) Completable: Cjsx([M], k, [first]) [{ @@ -282,7 +282,7 @@ Completable: Cjsx([M], k, [first]) Complete tests/src/Jsx.res 58:2 posCursor:(59,21) posNoWhite:(59,20) Found expr:(59,2)->(59,21) -JSX M:(59,2)->(59,3) childrenStart:None (first:(59,4) e:(59,17)->(59,19)), (k:(59,20) e:(59,20)->(59,21)) +JSX M:(59,2)->(59,3) childrenStart:None (first:(59,4)->(59,9) e:(59,17)->(59,19)), (k:(59,20)->(59,21) e:(59,20)->(59,21)) posCursor:(59,21) posNoWhite:(59,20) Found expr:(59,20)->(59,21) Completable: Cjsx([M], k, [first]) [{ From 1a0ebf28b0d50522b16be85dd615ee7f24f9f4cb Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 12 Apr 2022 15:50:04 +0200 Subject: [PATCH 010/135] Check differences on JSX behaviour. --- analysis/src/Commands.ml | 36 ++++++++++++++----------- analysis/tests/src/expected/Jsx.res.txt | 1 + 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index e89ff2824..f9d3e6e82 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -21,7 +21,7 @@ let findJsxPropCompletable ~jsxProps ~endPos ~pos = let rec loop ~seen props = match props with | prop :: rest -> - if prop.posEnd = pos then + if prop.posStart <= pos && pos < prop.posEnd then Some (PartialParser.Cjsx (jsxProps.componentPath, prop.name, seen)) else if posInLoc ~pos ~loc:prop.exp.pexp_loc then None else loop ~seen:(seen @ [prop.name]) rest @@ -125,6 +125,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = Res_driver.parsingEngine.parseImplementation ~forPrinter:false in let found = ref false in + let result = ref None in let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = if posInLoc ~pos:posNoWhite ~loc:expr.pexp_loc then ( found := true; @@ -153,27 +154,21 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (SemanticTokens.posToString posEnd) (SemanticTokens.locToString exp.pexp_loc)) |> String.concat ", "); - let () = - match - findJsxPropCompletable ~jsxProps - ~endPos:(Utils.tupleOfLexing expr.pexp_loc.loc_end) - ~pos:posCursor - with - | Some completable -> - (); - (* if debug then - Printf.printf "Found JSX completable %s\n" - (PartialParser.completableToString completable) *) - | None -> () + let jsxCompletable = + findJsxPropCompletable ~jsxProps + ~endPos:(Utils.tupleOfLexing expr.pexp_loc.loc_end) + ~pos:(fst posCursor, max 0 (snd posCursor - 1)) in - () + result := jsxCompletable | _ -> ()); Ast_iterator.default_iterator.expr iterator expr in let {Res_driver.parsetree = structure} = parser ~filename:currentFile in let iterator = {Ast_iterator.default_iterator with expr} in iterator.structure iterator structure |> ignore; - if not !found then if debug then Printf.printf "XXX Not found!\n") + if !found = false then if debug then Printf.printf "XXX Not found!\n"; + !result) + else None let completion ~debug ~path ~pos ~currentFile = let result = @@ -181,7 +176,9 @@ let completion ~debug ~path ~pos ~currentFile = match textOpt with | None | Some "" -> [] | Some text -> - completionWithParser ~debug ~path ~posCursor:pos ~currentFile ~text; + let jsxCompletable = + completionWithParser ~debug ~path ~posCursor:pos ~currentFile ~text + in let completionItems = match NewCompletions.getCompletable ~text ~pos with | None -> [] @@ -189,6 +186,13 @@ let completion ~debug ~path ~pos ~currentFile = if debug then Printf.printf "Completable: %s\n" (PartialParser.completableToString completable); + let () = + match completable with + | Cjsx _ -> assert (jsxCompletable = Some completable) + | _ -> + if jsxCompletable <> None && debug then + Printf.printf "XXX inconsistency\n" + in (* Only perform expensive ast operations if there are completables *) match Cmt.fromPath ~path with | None -> [] diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index ed7b28455..b257e072c 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -172,6 +172,7 @@ posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,2)->(33,16) JSX M:(33,2)->(33,3) childrenStart:None (prop:(33,4)->(33,8) e:(33,9)->(33,14)), (k:(33,15)->(33,16) e:(33,15)->(33,16)) posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,15)->(33,16) Completable: Cdotpath([k]) +XXX inconsistency [] Complete tests/src/Jsx.res 34:2 From 334b8c9d8f80f87e23736ecb464875d2b79600a5 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 12 Apr 2022 16:03:27 +0200 Subject: [PATCH 011/135] Take out code for backwards parsing jsx. --- analysis/src/Commands.ml | 36 ++++---- analysis/src/NewCompletions.ml | 12 --- analysis/src/PartialParser.ml | 109 +----------------------- analysis/tests/src/expected/Jsx.res.txt | 11 ++- 4 files changed, 31 insertions(+), 137 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index f9d3e6e82..7667ee5f7 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -180,24 +180,26 @@ let completion ~debug ~path ~pos ~currentFile = completionWithParser ~debug ~path ~posCursor:pos ~currentFile ~text in let completionItems = - match NewCompletions.getCompletable ~text ~pos with + match PartialParser.positionToOffset text pos with | None -> [] - | Some (completable, rawOpens) -> ( - if debug then - Printf.printf "Completable: %s\n" - (PartialParser.completableToString completable); - let () = - match completable with - | Cjsx _ -> assert (jsxCompletable = Some completable) - | _ -> - if jsxCompletable <> None && debug then - Printf.printf "XXX inconsistency\n" - in - (* Only perform expensive ast operations if there are completables *) - match Cmt.fromPath ~path with - | None -> [] - | Some full -> - NewCompletions.computeCompletions ~completable ~full ~pos ~rawOpens) + | Some offset -> ( + match (jsxCompletable, PartialParser.findCompletable text offset) with + | None, None -> [] + | Some completable, _ | _, Some completable -> ( + if debug then + Printf.printf "Completable: %s\n" + (PartialParser.completableToString completable); + let rawOpens = + let offsetFromLineStart = offset - snd pos in + (* try to avoid confusion e.g. unclosed quotes at current position *) + PartialParser.findOpens text offsetFromLineStart + in + (* Only perform expensive ast operations if there are completables *) + match Cmt.fromPath ~path with + | None -> [] + | Some full -> + NewCompletions.computeCompletions ~completable ~full ~pos + ~rawOpens)) in completionItems in diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index f9bbf97d0..4a4294415 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -1226,18 +1226,6 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens |> List.filter (fun (name, _t) -> Utils.startsWith name prefix) |> List.map mkLabel -let getCompletable ~text ~pos = - match PartialParser.positionToOffset text pos with - | None -> None - | Some offset -> ( - match PartialParser.findCompletable text offset with - | None -> None - | Some completable -> - let offsetFromLineStart = offset - snd pos in - (* try to avoid confusion e.g. unclosed quotes at current position *) - let rawOpens = PartialParser.findOpens text offsetFromLineStart in - Some (completable, rawOpens)) - let computeCompletions ~completable ~full ~pos ~rawOpens = let package = full.package in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 5212fd767..1707ce46b 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -92,97 +92,6 @@ let findCallFromArgument text offset = in loop [] offset -(* skip A or #A or %A if present *) -let skipOptVariantExtension text i = - if i > 0 then - match text.[i] with - | 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' -> - let i = startOfLident text i - 1 in - let i = - if i > 0 then match text.[i] with '#' | '%' -> i - 1 | _ -> i else i - in - i - | _ -> i - else i - -(* Figure out whether id should be autocompleted as component prop. - Find JSX context ctx for component M to autocomplete id (already parsed) as a prop. - ctx ::= | [...] - optVariant ::= id | #id | %id | _nothing_ -*) -let findJsxContext text offset = - let rec loop identsSeen i = - let i = skipWhite text i in - if i > 0 then - match text.[i] with - | '}' -> - let i1 = findBackSkippingCommentsAndStrings text '{' '}' (i - 1) 0 in - if i1 > 0 then beforeParen identsSeen i1 else None - | ')' -> - let i1 = findBackSkippingCommentsAndStrings text '(' ')' (i - 1) 0 in - if i1 > 0 then beforeParen identsSeen i1 else None - | '>' -> - let i1 = findBackSkippingCommentsAndStrings text '<' '>' (i - 1) 0 in - if i1 > 0 then beforeValue identsSeen i1 else None - | ']' -> - let i1 = findBackSkippingCommentsAndStrings text '[' ']' (i - 1) 0 in - if i1 > 0 then beforeValue identsSeen i1 else None - | '"' -> - let i1 = findBack text '"' (i - 1) in - if i1 > 0 then beforeValue identsSeen i1 else None - | '\'' -> - let i1 = findBack text '\'' (i - 1) in - if i1 > 0 then beforeValue identsSeen i1 else None - | '`' -> - let i1 = findBack text '`' (i - 1) in - if i1 > 0 then beforeValue identsSeen i1 else None - | _ -> - let i1 = startOfLident text i in - let ident = String.sub text i1 (i - i1 + 1) in - if i1 >= 1 && ident <> "" then - match ident.[0] with - | ('a' .. 'z' | 'A' .. 'Z') when i1 >= 1 && text.[i1 - 1] = '<' -> - Some (ident, identsSeen) - | _ -> - if i1 >= 1 && text.[i1 - 1] = '#' then - beforeValue identsSeen (i1 - 2) - else beforeIdent ~ident identsSeen (i1 - 1) - else None - else None - and beforeIdent ~ident identsSeen i = - let i = skipWhite text i in - if i > 0 then - match text.[i] with - | '?' -> fromEquals identsSeen (i - 1) - | '=' -> fromEquals identsSeen i - | _ -> (* punning *) loop (ident :: identsSeen) i - else None - and beforeParen identsSeen i = - let i = skipWhite text i in - beforeValue identsSeen (skipOptVariantExtension text i) - and beforeValue identsSeen i = - let i = skipWhite text i in - if i > 0 then - match text.[i] with - | '?' -> fromEquals identsSeen (i - 1) - | _ -> fromEquals identsSeen i - else None - and fromEquals identsSeen i = - let i = skipWhite text i in - if i > 0 then - match text.[i] with - | '=' -> ( - let i = skipWhite text (i - 1) in - let i1 = startOfLident text i in - let ident = String.sub text i1 (i - i1 + 1) in - match ident with "" -> None | _ -> loop (ident :: identsSeen) (i1 - 1)) - | _ -> None - else None - in - loop [] offset - type pipe = PipeId of string | PipeArray | PipeString type completable = @@ -235,11 +144,7 @@ let findCompletable text offset = match s.[len - 1] with '.' -> dotpath @ [""] | _ -> dotpath in match dotpath with - | [id] when String.lowercase_ascii id = id -> ( - match findJsxContext text (offset - len - 1) with - | None -> Cdotpath dotpath - | Some (componentName, identsSeen) -> - Cjsx (Str.split (Str.regexp_string ".") componentName, id, identsSeen)) + | [id] when String.lowercase_ascii id = id -> Cdotpath dotpath | _ -> Cdotpath dotpath in let mkPipe off partialName = @@ -301,15 +206,9 @@ let findCompletable text offset = let partialName = suffix i in mkObj ~off:(i - 2) ~partialName | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> loop (i - 1) - | ' ' when i = offset - 1 -> ( - (* autocomplete with no id: check if inside JSX *) - match findJsxContext text (offset - 1) with - | None -> None - | Some (componentName, identsSeen) -> - Some - (Cjsx - (Str.split (Str.regexp_string ".") componentName, "", identsSeen)) - ) + | ' ' when i = offset - 1 -> + (* autocomplete with no id *) + None | _ -> if i = offset - 1 then None else Some (mkPath (suffix i)) in if offset > String.length text || offset = 0 then None else loop (offset - 1) diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index b257e072c..dc39f741d 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -171,9 +171,14 @@ Complete tests/src/Jsx.res 32:2 posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,2)->(33,16) JSX M:(33,2)->(33,3) childrenStart:None (prop:(33,4)->(33,8) e:(33,9)->(33,14)), (k:(33,15)->(33,16) e:(33,15)->(33,16)) posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,15)->(33,16) -Completable: Cdotpath([k]) -XXX inconsistency -[] +Completable: Cjsx([M], k, [prop]) +[{ + "label": "key", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": null + }] Complete tests/src/Jsx.res 34:2 posCursor:(35,14) posNoWhite:(35,13) Found expr:(35,2)->(35,14) From 80ea375b0003b9413026ceb4e5e858111e216aef Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 12 Apr 2022 16:18:57 +0200 Subject: [PATCH 012/135] Tweak case with children. --- analysis/src/Commands.ml | 8 ++++---- analysis/tests/src/Jsx.res | 2 ++ analysis/tests/src/expected/Jsx.res.txt | 6 ++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 7667ee5f7..ae26fc24c 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -26,12 +26,12 @@ let findJsxPropCompletable ~jsxProps ~endPos ~pos = else if posInLoc ~pos ~loc:prop.exp.pexp_loc then None else loop ~seen:(seen @ [prop.name]) rest | [] -> - let posAfterProps = + let beforeChildrenStart = match jsxProps.childrenStart with - | Some childrenPos -> childrenPos - | None -> endPos + | Some childrenPos -> pos < childrenPos + | None -> pos <= endPos in - if pos <= posAfterProps then + if beforeChildrenStart then Some (PartialParser.Cjsx (jsxProps.componentPath, "", seen)) else None in diff --git a/analysis/tests/src/Jsx.res b/analysis/tests/src/Jsx.res index da2a1293b..a33b0a19f 100644 --- a/analysis/tests/src/Jsx.res +++ b/analysis/tests/src/Jsx.res @@ -58,3 +58,5 @@ let _ = (Ext.make, Ext.makeProps) //^com \ No newline at end of file diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index dc39f741d..7936aba3e 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -299,3 +299,9 @@ Completable: Cjsx([M], k, [first]) "documentation": null }] +Complete tests/src/Jsx.res 60:2 +posCursor:(61,4) posNoWhite:(61,3) Found expr:(61,2)->(61,4) +JSX M:(61,2)->(61,3) childrenStart:(61,3) +posCursor:(61,4) posNoWhite:(61,3) Found expr:(61,3)->(62,0) +[] + From d973ad67dc2fae6012c3bb360d0f8a2b797b7604 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 12 Apr 2022 16:39:38 +0200 Subject: [PATCH 013/135] Don't fire unless after component name. --- analysis/src/Commands.ml | 17 ++++++++++------- analysis/tests/src/Completion.res | 4 +++- analysis/tests/src/expected/Completion.res.txt | 13 +++++++++++++ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index ae26fc24c..6b58c3e9d 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -17,21 +17,23 @@ type jsxProps = { childrenStart : (int * int) option; } -let findJsxPropCompletable ~jsxProps ~endPos ~pos = +let findJsxPropCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName + = let rec loop ~seen props = match props with | prop :: rest -> - if prop.posStart <= pos && pos < prop.posEnd then + if prop.posStart <= posBeforeCursor && posBeforeCursor < prop.posEnd then Some (PartialParser.Cjsx (jsxProps.componentPath, prop.name, seen)) - else if posInLoc ~pos ~loc:prop.exp.pexp_loc then None + else if posInLoc ~pos:posBeforeCursor ~loc:prop.exp.pexp_loc then None else loop ~seen:(seen @ [prop.name]) rest | [] -> let beforeChildrenStart = match jsxProps.childrenStart with - | Some childrenPos -> pos < childrenPos - | None -> pos <= endPos + | Some childrenPos -> posBeforeCursor < childrenPos + | None -> posBeforeCursor <= endPos in - if beforeChildrenStart then + let afterCompName = posBeforeCursor >= posAfterCompName in + if afterCompName && beforeChildrenStart then Some (PartialParser.Cjsx (jsxProps.componentPath, "", seen)) else None in @@ -157,7 +159,8 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let jsxCompletable = findJsxPropCompletable ~jsxProps ~endPos:(Utils.tupleOfLexing expr.pexp_loc.loc_end) - ~pos:(fst posCursor, max 0 (snd posCursor - 1)) + ~posBeforeCursor:(fst posCursor, max 0 (snd posCursor - 1)) + ~posAfterCompName:(Utils.tupleOfLexing compName.loc.loc_end) in result := jsxCompletable | _ -> ()); diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index db879a365..b9316c31a 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -122,4 +122,6 @@ let foo = { add((x: Inner.z), Inner.v + y) } -exception MyOtherException \ No newline at end of file +exception MyOtherException + +// ^com (126,4) +JSX O:(126,2)->(126,4) childrenStart:None +posCursor:(126,4) posNoWhite:(126,3) Found expr:(126,2)->(126,4) +Completable: Cdotpath([O, ""]) +[{ + "label": "Comp", + "kind": 9, + "tags": [], + "detail": "module", + "documentation": null + }] + From 9ec656b3f72fb7b333d77c4bd50ed58cfb252ca2 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 13 Apr 2022 09:01:53 +0200 Subject: [PATCH 014/135] Extract locations of labels in function application. --- analysis/src/Commands.ml | 132 ++++++++++++++++++ .../tests/src/expected/Completion.res.txt | 7 + 2 files changed, 139 insertions(+) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 6b58c3e9d..015430fb8 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -39,10 +39,40 @@ let findJsxPropCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName in loop ~seen:[] jsxProps.props +let rec skipLineComment ~pos ~i str = + if i < String.length str then + match str.[i] with + | '\n' -> Some ((fst pos + 1, 0), i + 1) + | _ -> skipLineComment ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str + else None + +let rec skipComment ~pos ~i ~depth str = + if i < String.length str then + match str.[i] with + | '\n' -> skipComment ~depth ~pos:(fst pos + 1, 0) ~i:(i + 1) str + | '/' when i + 1 < String.length str && str.[i + 1] = '*' -> + skipComment ~depth:(depth + 1) ~pos:(fst pos, snd pos + 2) ~i:(i + 2) str + | '*' when i + 1 < String.length str && str.[i + 1] = '/' -> + if depth > 1 then + skipComment ~depth:(depth - 1) + ~pos:(fst pos, snd pos + 2) + ~i:(i + 2) str + else Some ((fst pos, snd pos + 2), i + 2) + | _ -> skipComment ~depth ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str + else None + let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = let rec extractLabelPos ~pos ~i str = if i < String.length str then match str.[i] with + | '/' when i + 1 < String.length str && str.[i + 1] = '/' -> ( + match skipLineComment ~pos ~i str with + | Some (pos, i) -> extractLabelPos ~pos ~i str + | None -> None) + | '/' when i + 1 < String.length str && str.[i + 1] = '*' -> ( + match skipComment ~depth:0 ~pos ~i str with + | Some (pos, i) -> extractLabelPos ~pos ~i str + | None -> None) | ' ' | '\r' | '\t' -> extractLabelPos ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str | '\n' -> extractLabelPos ~pos:(fst pos + 1, 0) ~i:(i + 1) str @@ -110,6 +140,86 @@ let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = |> processProps ~lastOffset:offsetAfterCompName ~lastPos:posAfterCompName ~acc:[] +type labelled = { + name : string; + opt : bool; + posStart : int * int; + posEnd : int * int; +} + +type label = labelled option +type arg = {label : label; exp : Parsetree.expression} + +let extractExpApplyArgs ~text ~eFun ~args = + let rec extractLabelPos ~pos ~i str = + if i < String.length str then + match str.[i] with + | '/' when i + 1 < String.length str && str.[i + 1] = '/' -> ( + match skipLineComment ~pos ~i str with + | Some (pos, i) -> extractLabelPos ~pos ~i str + | None -> None) + | '/' when i + 1 < String.length str && str.[i + 1] = '*' -> ( + match skipComment ~depth:0 ~pos ~i str with + | Some (pos, i) -> extractLabelPos ~pos ~i str + | None -> None) + | ' ' | '\r' | '\t' | ',' | '(' | '~' -> + extractLabelPos ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str + | '\n' -> extractLabelPos ~pos:(fst pos + 1, 0) ~i:(i + 1) str + | 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' -> Some pos + | _ -> None + else None + in + let rec processArgs ~lastOffset ~lastPos ~acc args = + match args with + | (((Asttypes.Labelled s | Optional s) as label), (e : Parsetree.expression)) + :: rest -> ( + let ePosStart = Utils.tupleOfLexing e.pexp_loc.loc_start in + let ePosEnd = Utils.tupleOfLexing e.pexp_loc.loc_end in + match + ( PartialParser.positionToOffset text ePosStart, + PartialParser.positionToOffset text ePosEnd ) + with + | Some offsetStart, Some offsetEnd -> + let labelText = String.sub text lastOffset (offsetStart - lastOffset) in + let labelPos = + match extractLabelPos ~pos:lastPos ~i:0 labelText with + | Some pos -> pos + | None -> (* Must be punned *) ePosStart + in + let labelled = + { + name = s; + opt = (match label with Optional _ -> true | _ -> false); + posStart = labelPos; + posEnd = (fst labelPos, snd labelPos + String.length s); + } + in + processArgs + ~acc:({label = Some labelled; exp = e} :: acc) + ~lastOffset:offsetEnd ~lastPos:ePosEnd rest + | _ -> assert false) + | (Asttypes.Nolabel, (e : Parsetree.expression)) :: rest -> ( + if e.pexp_loc.loc_ghost then processArgs ~acc ~lastOffset ~lastPos rest + else + let ePosEnd = Utils.tupleOfLexing e.pexp_loc.loc_end in + match PartialParser.positionToOffset text ePosEnd with + | Some offsetEnd -> + processArgs + ~acc:({label = None; exp = e} :: acc) + ~lastOffset:offsetEnd ~lastPos:ePosEnd rest + | _ -> + (* should not happen *) + assert false) + | [] -> List.rev acc + in + let lastPos = Utils.tupleOfLexing eFun.Parsetree.pexp_loc.loc_end in + let lastOffset = + match PartialParser.positionToOffset text lastPos with + | Some offset -> offset + | None -> assert false + in + args |> processArgs ~lastOffset ~lastPos ~acc:[] + let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let offset = match PartialParser.positionToOffset text posCursor with @@ -163,6 +273,28 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = ~posAfterCompName:(Utils.tupleOfLexing compName.loc.loc_end) in result := jsxCompletable + | Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "|."}}, [_; _]) -> + (* Pipe *) + () + | Pexp_apply (eFun, args) -> + let args = extractExpApplyArgs ~text ~eFun ~args in + if debug then + Printf.printf "Pexp_apply ...%s (%s)\n" + (SemanticTokens.locToString eFun.pexp_loc) + (args + |> List.map (fun {label; exp} -> + Printf.sprintf "%s...%s" + (match label with + | None -> "" + | Some {name; opt; posStart; posEnd} -> + "~" ^ name + ^ SemanticTokens.posToString posStart + ^ "->" + ^ SemanticTokens.posToString posEnd + ^ "=" + ^ if opt then "?" else "") + (SemanticTokens.locToString exp.pexp_loc)) + |> String.concat ", ") | _ -> ()); Ast_iterator.default_iterator.expr iterator expr in diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 13e29210e..667565ccd 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -356,6 +356,7 @@ Completable: Cdotpath([Dep, c]) Complete tests/src/Completion.res 19:2 posCursor:(20,18) posNoWhite:(20,17) Found expr:(20,9)->(20,18) +Pexp_apply ...(20,9)->(20,16) () Completable: Clabel([Lib, foo], "", []) [{ "label": "age", @@ -421,6 +422,7 @@ Completable: Cpipe(op, e) Complete tests/src/Completion.res 36:2 posCursor:(37,5) posNoWhite:(37,4) Found expr:(37,1)->(46,3) +Pexp_apply ...(41,9)->(41,10) (...(37,1)->(41,8), ...(42,2)->(46,3)) posCursor:(37,5) posNoWhite:(37,4) Found expr:(37,1)->(41,8) posCursor:(37,5) posNoWhite:(37,4) Found expr:(37,3)->(37,5) Completable: Cpipe(fa, "") @@ -689,6 +691,7 @@ Completable: Cdecorator(reac) Complete tests/src/Completion.res 58:2 posCursor:(59,8) posNoWhite:(59,7) Found expr:(0,-1)->(69,17) +Pexp_apply ...(67,6)->(67,7) (...(67,8)->(69,17)) Completable: Cdecorator(react.) [{ "label": "component", @@ -700,6 +703,7 @@ Completable: Cdecorator(react.) Complete tests/src/Completion.res 60:2 posCursor:(61,25) posNoWhite:(61,24) Found expr:(61,9)->(61,25) +Pexp_apply ...(61,9)->(61,16) (~name(61,18)->(61,22)=...(61,18)->(61,22)) Completable: Clabel([Lib, foo], "", [name]) [{ "label": "age", @@ -711,6 +715,7 @@ Completable: Clabel([Lib, foo], "", [name]) Complete tests/src/Completion.res 62:2 posCursor:(63,24) posNoWhite:(63,23) Found expr:(63,9)->(63,24) +Pexp_apply ...(63,9)->(63,16) (~age(63,18)->(63,21)=...(63,18)->(63,21)) Completable: Clabel([Lib, foo], "", [age]) [{ "label": "name", @@ -722,6 +727,7 @@ Completable: Clabel([Lib, foo], "", [age]) Complete tests/src/Completion.res 64:2 posCursor:(65,30) posNoWhite:(65,29) Found expr:(65,9)->(65,30) +Pexp_apply ...(65,9)->(65,16) (~age(65,18)->(65,21)=...(65,23)->(65,26)) Completable: Clabel([Lib, foo], "", [age]) [{ "label": "name", @@ -733,6 +739,7 @@ Completable: Clabel([Lib, foo], "", [age]) Complete tests/src/Completion.res 67:2 posCursor:(68,2) posNoWhite:(68,1) Found expr:(67,8)->(69,17) +Pexp_apply ...(67,8)->(67,15) (~age(69,1)->(69,4)=...(69,5)->(69,6), ~name(69,9)->(69,13)=...(69,14)->(69,16)) Completable: Clabel([Lib, foo], "", []) [{ "label": "age", From 2a19fc3f0fe422eee6ea2b7ea3b3545d7a6f52f4 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 13 Apr 2022 09:13:39 +0200 Subject: [PATCH 015/135] Tweak debug printing of positions and locations. --- analysis/src/Commands.ml | 16 +- analysis/src/SemanticTokens.ml | 4 +- .../src/expected/CompletePrioritize1.res.txt | 2 +- .../src/expected/CompletePrioritize2.res.txt | 2 +- .../tests/src/expected/Completion.res.txt | 94 +++--- analysis/tests/src/expected/Cross.res.txt | 2 +- analysis/tests/src/expected/Div.res.txt | 6 +- analysis/tests/src/expected/Highlight.res.txt | 280 +++++++++--------- analysis/tests/src/expected/Jsx.res.txt | 120 ++++---- .../src/expected/RecordCompletion.res.txt | 8 +- 10 files changed, 267 insertions(+), 267 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 015430fb8..07ecb3167 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -242,7 +242,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = if posInLoc ~pos:posNoWhite ~loc:expr.pexp_loc then ( found := true; if debug then - Printf.printf "posCursor:%s posNoWhite:%s Found expr:%s\n" + Printf.printf "posCursor:[%s] posNoWhite:[%s] Found expr:%s\n" (SemanticTokens.posToString posCursor) (SemanticTokens.posToString posNoWhite) (SemanticTokens.locToString expr.pexp_loc); @@ -252,20 +252,20 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = when Res_parsetree_viewer.isJsxExpression expr -> let jsxProps = extractJsxProps ~text ~compName ~args in if debug then - Printf.printf "JSX %s:%s childrenStart:%s %s\n" + Printf.printf "JSX <%s:%s %s> _children:%s\n" (jsxProps.componentPath |> String.concat ",") (SemanticTokens.locToString compName.loc) - (match jsxProps.childrenStart with - | None -> "None" - | Some childrenPosStart -> - SemanticTokens.posToString childrenPosStart) (jsxProps.props |> List.map (fun {name; posStart; posEnd; exp} -> - Printf.sprintf "(%s:%s->%s e:%s)" name + Printf.sprintf "%s[%s->%s]=...%s" name (SemanticTokens.posToString posStart) (SemanticTokens.posToString posEnd) (SemanticTokens.locToString exp.pexp_loc)) - |> String.concat ", "); + |> String.concat " ") + (match jsxProps.childrenStart with + | None -> "None" + | Some childrenPosStart -> + SemanticTokens.posToString childrenPosStart); let jsxCompletable = findJsxPropCompletable ~jsxProps ~endPos:(Utils.tupleOfLexing expr.pexp_loc.loc_end) diff --git a/analysis/src/SemanticTokens.ml b/analysis/src/SemanticTokens.ml index 12fc2e583..e3b6feac2 100644 --- a/analysis/src/SemanticTokens.ml +++ b/analysis/src/SemanticTokens.ml @@ -92,11 +92,11 @@ end let locToPositions (loc : Location.t) = (Utils.tupleOfLexing loc.loc_start, Utils.tupleOfLexing loc.loc_end) -let posToString (loc, col) = Printf.sprintf "(%d,%d)" loc col +let posToString (loc, col) = Printf.sprintf "%d:%d" loc col let locToString (loc : Location.t) = let posStart, posEnd = locToPositions loc in - Printf.sprintf "%s->%s" (posToString posStart) (posToString posEnd) + Printf.sprintf "[%s->%s]" (posToString posStart) (posToString posEnd) let isLowercaseId id = id <> "" diff --git a/analysis/tests/src/expected/CompletePrioritize1.res.txt b/analysis/tests/src/expected/CompletePrioritize1.res.txt index 36388bbd3..743107aa6 100644 --- a/analysis/tests/src/expected/CompletePrioritize1.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize1.res.txt @@ -1,5 +1,5 @@ Complete tests/src/CompletePrioritize1.res 4:2 -posCursor:(5,4) posNoWhite:(5,3) Found expr:(5,2)->(5,4) +posCursor:[5:4] posNoWhite:[5:3] Found expr:[5:2->5:4] Completable: Cpipe(a, "") [{ "label": "Test.add", diff --git a/analysis/tests/src/expected/CompletePrioritize2.res.txt b/analysis/tests/src/expected/CompletePrioritize2.res.txt index c4d9572ec..3dbc17d53 100644 --- a/analysis/tests/src/expected/CompletePrioritize2.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize2.res.txt @@ -1,5 +1,5 @@ Complete tests/src/CompletePrioritize2.res 8:2 -posCursor:(9,4) posNoWhite:(9,3) Found expr:(9,2)->(9,4) +posCursor:[9:4] posNoWhite:[9:3] Found expr:[9:2->9:4] Completable: Cpipe(a, "") [{ "label": "Test.add", diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 667565ccd..ce84d41a5 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1,5 +1,5 @@ Complete tests/src/Completion.res 0:2 -posCursor:(1,9) posNoWhite:(1,8) Found expr:(1,1)->(1,9) +posCursor:[1:9] posNoWhite:[1:8] Found expr:[1:1->1:9] Completable: Cdotpath([MyList, m]) [{ "label": "mapReverse", @@ -70,7 +70,7 @@ Completable: Cdotpath([MyList, m]) }] Complete tests/src/Completion.res 1:2 -posCursor:(2,7) posNoWhite:(2,6) Found expr:(2,1)->(6,6) +posCursor:[2:7] posNoWhite:[2:6] Found expr:[2:1->6:6] Completable: Cdotpath([Array, ""]) [{ "label": "Floatarray", @@ -291,7 +291,7 @@ Completable: Cdotpath([Array, ""]) }] Complete tests/src/Completion.res 2:2 -posCursor:(3,8) posNoWhite:(3,7) Found expr:(3,1)->(3,8) +posCursor:[3:8] posNoWhite:[3:7] Found expr:[3:1->3:8] Completable: Cdotpath([Array, m]) [{ "label": "mapi", @@ -344,7 +344,7 @@ Completable: Cdotpath([Array, m]) }] Complete tests/src/Completion.res 12:2 -posCursor:(13,15) posNoWhite:(13,14) Found expr:(13,10)->(13,15) +posCursor:[13:15] posNoWhite:[13:14] Found expr:[13:10->13:15] Completable: Cdotpath([Dep, c]) [{ "label": "customDouble", @@ -355,8 +355,8 @@ Completable: Cdotpath([Dep, c]) }] Complete tests/src/Completion.res 19:2 -posCursor:(20,18) posNoWhite:(20,17) Found expr:(20,9)->(20,18) -Pexp_apply ...(20,9)->(20,16) () +posCursor:[20:18] posNoWhite:[20:17] Found expr:[20:9->20:18] +Pexp_apply ...[20:9->20:16] () Completable: Clabel([Lib, foo], "", []) [{ "label": "age", @@ -373,8 +373,8 @@ Completable: Clabel([Lib, foo], "", []) }] Complete tests/src/Completion.res 21:2 -posCursor:(22,11) posNoWhite:(22,10) Found expr:(22,1)->(22,11) -posCursor:(22,11) posNoWhite:(22,10) Found expr:(22,10)->(22,11) +posCursor:[22:11] posNoWhite:[22:10] Found expr:[22:1->22:11] +posCursor:[22:11] posNoWhite:[22:10] Found expr:[22:10->22:11] Completable: Cpipe(PipeArray, m) [{ "label": "Js.Array2.mapi", @@ -391,8 +391,8 @@ Completable: Cpipe(PipeArray, m) }] Complete tests/src/Completion.res 23:2 -posCursor:(24,11) posNoWhite:(24,10) Found expr:(24,1)->(24,11) -posCursor:(24,11) posNoWhite:(24,10) Found expr:(24,8)->(24,11) +posCursor:[24:11] posNoWhite:[24:10] Found expr:[24:1->24:11] +posCursor:[24:11] posNoWhite:[24:10] Found expr:[24:8->24:11] Completable: Cpipe(PipeString, toU) [{ "label": "Js.String2.toUpperCase", @@ -403,8 +403,8 @@ Completable: Cpipe(PipeString, toU) }] Complete tests/src/Completion.res 27:2 -posCursor:(28,6) posNoWhite:(28,5) Found expr:(28,1)->(28,6) -posCursor:(28,6) posNoWhite:(28,5) Found expr:(28,5)->(28,6) +posCursor:[28:6] posNoWhite:[28:5] Found expr:[28:1->28:6] +posCursor:[28:6] posNoWhite:[28:5] Found expr:[28:5->28:6] Completable: Cpipe(op, e) [{ "label": "Belt.Option.eqU", @@ -421,10 +421,10 @@ Completable: Cpipe(op, e) }] Complete tests/src/Completion.res 36:2 -posCursor:(37,5) posNoWhite:(37,4) Found expr:(37,1)->(46,3) -Pexp_apply ...(41,9)->(41,10) (...(37,1)->(41,8), ...(42,2)->(46,3)) -posCursor:(37,5) posNoWhite:(37,4) Found expr:(37,1)->(41,8) -posCursor:(37,5) posNoWhite:(37,4) Found expr:(37,3)->(37,5) +posCursor:[37:5] posNoWhite:[37:4] Found expr:[37:1->46:3] +Pexp_apply ...[41:9->41:10] (...[37:1->41:8], ...[42:2->46:3]) +posCursor:[37:5] posNoWhite:[37:4] Found expr:[37:1->41:8] +posCursor:[37:5] posNoWhite:[37:4] Found expr:[37:3->37:5] Completable: Cpipe(fa, "") [{ "label": "ForAuto.abc", @@ -441,8 +441,8 @@ Completable: Cpipe(fa, "") }] Complete tests/src/Completion.res 38:2 -posCursor:(39,19) posNoWhite:(39,18) Found expr:(39,1)->(39,19) -posCursor:(39,19) posNoWhite:(39,18) Found expr:(39,10)->(39,19) +posCursor:[39:19] posNoWhite:[39:18] Found expr:[39:1->39:19] +posCursor:[39:19] posNoWhite:[39:18] Found expr:[39:10->39:19] Completable: Cdotpath([Js, Dict, u]) [{ "label": "unsafeGet", @@ -459,9 +459,9 @@ Completable: Cdotpath([Js, Dict, u]) }] Complete tests/src/Completion.res 50:2 -posCursor:(51,28) posNoWhite:(51,27) Found expr:(51,13)->(51,28) -JSX O,Comp:(51,13)->(51,19) childrenStart:None (second:(51,20)->(51,26) e:(51,27)->(51,28)) -posCursor:(51,28) posNoWhite:(51,27) Found expr:(51,27)->(51,28) +posCursor:[51:28] posNoWhite:[51:27] Found expr:[51:13->51:28] +JSX 51:19] second[51:20->51:26]=...[51:27->51:28]> _children:None +posCursor:[51:28] posNoWhite:[51:27] Found expr:[51:27->51:28] Completable: Cdotpath([z]) [{ "label": "zzz", @@ -472,9 +472,9 @@ Completable: Cdotpath([z]) }] Complete tests/src/Completion.res 52:2 -posCursor:(53,21) posNoWhite:(53,20) Found expr:(53,13)->(53,21) -JSX O,Comp:(53,13)->(53,19) childrenStart:None (z:(53,20)->(53,21) e:(53,20)->(53,21)) -posCursor:(53,21) posNoWhite:(53,20) Found expr:(53,20)->(53,21) +posCursor:[53:21] posNoWhite:[53:20] Found expr:[53:13->53:21] +JSX 53:19] z[53:20->53:21]=...[53:20->53:21]> _children:None +posCursor:[53:21] posNoWhite:[53:20] Found expr:[53:20->53:21] Completable: Cjsx([O, Comp], z, []) [{ "label": "zoo", @@ -690,8 +690,8 @@ Completable: Cdecorator(reac) }] Complete tests/src/Completion.res 58:2 -posCursor:(59,8) posNoWhite:(59,7) Found expr:(0,-1)->(69,17) -Pexp_apply ...(67,6)->(67,7) (...(67,8)->(69,17)) +posCursor:[59:8] posNoWhite:[59:7] Found expr:[0:-1->69:17] +Pexp_apply ...[67:6->67:7] (...[67:8->69:17]) Completable: Cdecorator(react.) [{ "label": "component", @@ -702,8 +702,8 @@ Completable: Cdecorator(react.) }] Complete tests/src/Completion.res 60:2 -posCursor:(61,25) posNoWhite:(61,24) Found expr:(61,9)->(61,25) -Pexp_apply ...(61,9)->(61,16) (~name(61,18)->(61,22)=...(61,18)->(61,22)) +posCursor:[61:25] posNoWhite:[61:24] Found expr:[61:9->61:25] +Pexp_apply ...[61:9->61:16] (~name61:18->61:22=...[61:18->61:22]) Completable: Clabel([Lib, foo], "", [name]) [{ "label": "age", @@ -714,8 +714,8 @@ Completable: Clabel([Lib, foo], "", [name]) }] Complete tests/src/Completion.res 62:2 -posCursor:(63,24) posNoWhite:(63,23) Found expr:(63,9)->(63,24) -Pexp_apply ...(63,9)->(63,16) (~age(63,18)->(63,21)=...(63,18)->(63,21)) +posCursor:[63:24] posNoWhite:[63:23] Found expr:[63:9->63:24] +Pexp_apply ...[63:9->63:16] (~age63:18->63:21=...[63:18->63:21]) Completable: Clabel([Lib, foo], "", [age]) [{ "label": "name", @@ -726,8 +726,8 @@ Completable: Clabel([Lib, foo], "", [age]) }] Complete tests/src/Completion.res 64:2 -posCursor:(65,30) posNoWhite:(65,29) Found expr:(65,9)->(65,30) -Pexp_apply ...(65,9)->(65,16) (~age(65,18)->(65,21)=...(65,23)->(65,26)) +posCursor:[65:30] posNoWhite:[65:29] Found expr:[65:9->65:30] +Pexp_apply ...[65:9->65:16] (~age65:18->65:21=...[65:23->65:26]) Completable: Clabel([Lib, foo], "", [age]) [{ "label": "name", @@ -738,8 +738,8 @@ Completable: Clabel([Lib, foo], "", [age]) }] Complete tests/src/Completion.res 67:2 -posCursor:(68,2) posNoWhite:(68,1) Found expr:(67,8)->(69,17) -Pexp_apply ...(67,8)->(67,15) (~age(69,1)->(69,4)=...(69,5)->(69,6), ~name(69,9)->(69,13)=...(69,14)->(69,16)) +posCursor:[68:2] posNoWhite:[68:1] Found expr:[67:8->69:17] +Pexp_apply ...[67:8->67:15] (~age69:1->69:4=...[69:5->69:6], ~name69:9->69:13=...[69:14->69:16]) Completable: Clabel([Lib, foo], "", []) [{ "label": "age", @@ -756,7 +756,7 @@ Completable: Clabel([Lib, foo], "", []) }] Complete tests/src/Completion.res 72:2 -posCursor:(73,11) posNoWhite:(73,10) Found expr:(73,8)->(75,18) +posCursor:[73:11] posNoWhite:[73:10] Found expr:[73:8->75:18] Completable: Cobj([someObj], [], a) [{ "label": "age", @@ -767,7 +767,7 @@ Completable: Cobj([someObj], [], a) }] Complete tests/src/Completion.res 76:2 -posCursor:(77,22) posNoWhite:(77,21) Found expr:(77,20)->(80,10) +posCursor:[77:22] posNoWhite:[77:21] Found expr:[77:20->80:10] Completable: Cobj([nestedObj], [x, y], "") [{ "label": "age", @@ -784,7 +784,7 @@ Completable: Cobj([nestedObj], [x, y], "") }] Complete tests/src/Completion.res 79:2 -posCursor:(80,5) posNoWhite:(80,4) Found expr:(80,2)->(82,20) +posCursor:[80:5] posNoWhite:[80:4] Found expr:[80:2->82:20] Completable: Cobj([o], [], a) [{ "label": "age", @@ -795,7 +795,7 @@ Completable: Cobj([o], [], a) }] Complete tests/src/Completion.res 83:2 -posCursor:(84,15) posNoWhite:(84,14) Found expr:(84,13)->(101,20) +posCursor:[84:15] posNoWhite:[84:14] Found expr:[84:13->101:20] Completable: Cobj([no], [x, y], "") [{ "label": "name", @@ -812,7 +812,7 @@ Completable: Cobj([no], [x, y], "") }] Complete tests/src/Completion.res 88:3 -posCursor:(89,3) posNoWhite:(89,2) Found expr:(89,1)->(93,3) +posCursor:[89:3] posNoWhite:[89:2] Found expr:[89:1->93:3] Completable: Cdotpath([r, ""]) [{ "label": "x", @@ -829,7 +829,7 @@ Completable: Cdotpath([r, ""]) }] Complete tests/src/Completion.res 90:3 -posCursor:(91,19) posNoWhite:(91,18) Found expr:(91,1)->(93,3) +posCursor:[91:19] posNoWhite:[91:18] Found expr:[91:1->93:3] Completable: Cdotpath([Obj, Rec, recordVal, ""]) [{ "label": "xx", @@ -846,9 +846,9 @@ Completable: Cdotpath([Obj, Rec, recordVal, ""]) }] Complete tests/src/Completion.res 96:3 -posCursor:(97,3) posNoWhite:(97,2) Found expr:(96,11)->(99,1) -posCursor:(97,3) posNoWhite:(97,2) Found expr:(97,1)->(98,5) -posCursor:(97,3) posNoWhite:(97,2) Found expr:(97,1)->(97,3) +posCursor:[97:3] posNoWhite:[97:2] Found expr:[96:11->99:1] +posCursor:[97:3] posNoWhite:[97:2] Found expr:[97:1->98:5] +posCursor:[97:3] posNoWhite:[97:2] Found expr:[97:1->97:3] Completable: Cdotpath([my]) [{ "label": "myAmazingFunction", @@ -859,7 +859,7 @@ Completable: Cdotpath([my]) }] Complete tests/src/Completion.res 100:3 -posCursor:(101,13) posNoWhite:(101,12) Found expr:(101,11)->(120,32) +posCursor:[101:13] posNoWhite:[101:12] Found expr:[101:11->120:32] Completable: Cobj([Obj, object], [], "") [{ "label": "name", @@ -876,9 +876,9 @@ Completable: Cobj([Obj, object], [], "") }] Complete tests/src/Completion.res 125:3 -posCursor:(126,4) posNoWhite:(126,3) Found expr:(126,2)->(126,4) -JSX O:(126,2)->(126,4) childrenStart:None -posCursor:(126,4) posNoWhite:(126,3) Found expr:(126,2)->(126,4) +posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] +JSX 126:4] > _children:None +posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] Completable: Cdotpath([O, ""]) [{ "label": "Comp", diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index 3ab9b3928..056ffc03f 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -94,7 +94,7 @@ TypeDefinition tests/src/Cross.res 37:37 {"uri": "DefinitionWithInterface.resi", "range": {"start": {"line": 3, "character": 0}, "end": {"line": 3, "character": 6}}} Complete tests/src/Cross.res 39:2 -posCursor:(40,26) posNoWhite:(40,25) Found expr:(40,1)->(40,26) +posCursor:[40:26] posNoWhite:[40:25] Found expr:[40:1->40:26] Completable: Cdotpath([DefinitionWithInterface, a]) [] diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 0961b86e8..ce62f4a52 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -2,9 +2,9 @@ Hover tests/src/Div.res 1:10 {"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array,\n) => React.element\n```"} Complete tests/src/Div.res 3:3 -posCursor:(4,15) posNoWhite:(4,14) Found expr:(4,2)->(4,15) -JSX div:(4,2)->(4,5) childrenStart:None (dangerous:(4,6)->(4,15) e:(4,6)->(4,15)) -posCursor:(4,15) posNoWhite:(4,14) Found expr:(4,6)->(4,15) +posCursor:[4:15] posNoWhite:[4:14] Found expr:[4:2->4:15] +JSX 4:5] dangerous[4:6->4:15]=...[4:6->4:15]> _children:None +posCursor:[4:15] posNoWhite:[4:14] Found expr:[4:6->4:15] Completable: Cjsx([div], dangerous, []) [{ "label": "dangerouslySetInnerHTML", diff --git a/analysis/tests/src/expected/Highlight.res.txt b/analysis/tests/src/expected/Highlight.res.txt index b14f2499e..ed6a39136 100644 --- a/analysis/tests/src/expected/Highlight.res.txt +++ b/analysis/tests/src/expected/Highlight.res.txt @@ -1,143 +1,143 @@ Highlight tests/src/Highlight.res structure items:38 diagnostics:0 -Lident: M (0,7) Class -Lident: C (1,9) Class -Lident: Component (1,13) Class -Variable: _c (4,4)->(4,6) -JsxTag <: (4,9) -Lident: Component (4,10) Class -Variable: _mc (6,4)->(6,7) -JsxTag <: (6,10) -Ldot: M (6,11) Class -Lident: C (6,13) Class -Variable: _d (8,4)->(8,6) -JsxTag <: (8,9) -Lident: div (8,10) JsxLowercase -Variable: _d2 (10,4)->(10,7) -JsxTag <: (11,2) -Lident: div (11,3) JsxLowercase -Lident: div (16,4) JsxLowercase -JsxTag >: (11,6) -JsxTag >: (16,7) -Ldot: React (12,5) Class -Lident: string (12,11) Variable -JsxTag <: (13,4) -Lident: div (13,5) JsxLowercase -Lident: div (13,34) JsxLowercase -JsxTag >: (13,8) -JsxTag >: (13,37) -Ldot: React (13,11) Class -Lident: string (13,17) Variable -Ldot: React (14,5) Class -Lident: string (14,11) Variable -Ldot: React (15,5) Class -Lident: string (15,11) Variable -Lident: pair (18,5) Type -Lident: looooooooooooooooooooooooooooooooooooooong_int (20,5) Type -Lident: int (20,54) Type -Lident: looooooooooooooooooooooooooooooooooooooong_string (22,5) Type -Lident: string (22,57) Type -Lident: pairIntString (24,5) Type -Lident: list (24,21) Type -TypeArg: (25,2)->(28,3) -Lident: pair (25,2) Type -TypeArg: (26,4)->(26,50) -TypeArg: (27,4)->(27,53) -Lident: looooooooooooooooooooooooooooooooooooooong_int (26,4) Type -Lident: looooooooooooooooooooooooooooooooooooooong_string (27,4) Type -Binary operator < (31,12)->(31,13) -Binary operator > (31,22)->(31,23) -Lident: MT (33,12) Type -Lident: DDF (34,9) Class -Lident: DDF (39,7) Class -Lident: DDF (40,9) Class -Lident: MT (39,12) Type -Lident: XX (45,7) Class -Lident: YY (46,9) Class -Lident: t (47,9) Type -Lident: int (47,13) Type -Ldot: XX (51,5) Class -Lident: YY (51,8) Class -Lident: tt (53,5) Type -Lident: t (53,10) Type -Lident: T (57,7) Class -Lident: someRecord (58,7) Type -Lident: someField (59,4) Property -Lident: int (59,15) Type -Lident: someOtherField (60,4) Property -Lident: string (60,20) Type -Lident: theParam (61,4) Property -Lident: someEnum (64,7) Type -Lident: A (64,18) EnumMember -Lident: B (64,22) EnumMember -Lident: C (64,26) EnumMember -Variable: foo (67,4)->(67,7) -Variable: x (67,10)->(67,11) -Ldot: T (67,17) Class -Lident: someField (67,19) Property -Lident: x (67,15) Variable -Variable: add (69,4)->(69,7) -Variable: x (69,21)->(69,22) -Variable: world (69,24)->(69,30) -Lident: x (69,35) Variable -Lident: world (69,39) Variable -Lident: add (71,8) Variable -JsxTag <: (73,8) -Lident: div (73,9) JsxLowercase -Lident: div (73,36) JsxLowercase -JsxTag >: (73,24) -JsxTag >: (73,39) -JsxTag <: (73,26) -Lident: div (73,27) JsxLowercase -Lident: SomeComponent (75,7) Class -Lident: Nested (76,9) Class -Variable: make (78,8)->(78,12) -Variable: children (78,16)->(78,25) -Lident: children (79,10) Variable -JsxTag <: (84,8) -Ldot: SomeComponent (84,9) Class -Lident: Nested (84,23) Class -Ldot: SomeComponent (84,41) Class -Lident: Nested (84,55) Class -JsxTag >: (84,29) -JsxTag >: (84,61) -JsxTag <: (84,31) -Lident: div (84,32) JsxLowercase -Variable: toAs (90,4)->(90,8) -Variable: x (90,19)->(90,20) -Lident: x (90,25) Variable -Variable: _toEquals (91,4)->(91,13) -Lident: toAs (91,16) Variable -Variable: to (93,4)->(93,6) -Lident: to (94,9) Variable -Lident: to (94,14) Variable -Lident: to (94,20) Variable -Lident: to (94,25) Variable -Lident: ToAsProp (98,7) Class -Variable: make (100,6)->(100,10) -Variable: to (100,14)->(100,17) -Ldot: React (101,8) Class -Lident: int (101,14) Variable -Lident: to (101,18) Variable -JsxTag <: (104,8) -Lident: ToAsProp (104,9) Class -Variable: true (107,4)->(107,11) -Lident: true (108,8)->(108,15) Variable -Variable: enumInModule (110,4)->(110,16) -Ldot: T (110,19) Class -Lident: A (110,21) EnumMember -Lident: typeInModule (112,5) Type -Ldot: XX (112,20) Class -Ldot: YY (112,23) Class -Lident: t (112,26) Type -Lident: QQ (114,7) Class -Lident: somePolyEnumType (115,7) Type -Lident: list (118,29) Type -TypeArg: (118,34)->(118,37) -Lident: int (118,34) Type -Variable: x (123,8)->(123,9) -Lident: x (124,9) Variable -Ldot: QQ (126,8) Class -Lident: somePolyEnumType (126,11) Type -Lident: abc (133,9)->(133,14) Property +Lident: M 0:7 Class +Lident: C 1:9 Class +Lident: Component 1:13 Class +Variable: _c [4:4->4:6] +JsxTag <: 4:9 +Lident: Component 4:10 Class +Variable: _mc [6:4->6:7] +JsxTag <: 6:10 +Ldot: M 6:11 Class +Lident: C 6:13 Class +Variable: _d [8:4->8:6] +JsxTag <: 8:9 +Lident: div 8:10 JsxLowercase +Variable: _d2 [10:4->10:7] +JsxTag <: 11:2 +Lident: div 11:3 JsxLowercase +Lident: div 16:4 JsxLowercase +JsxTag >: 11:6 +JsxTag >: 16:7 +Ldot: React 12:5 Class +Lident: string 12:11 Variable +JsxTag <: 13:4 +Lident: div 13:5 JsxLowercase +Lident: div 13:34 JsxLowercase +JsxTag >: 13:8 +JsxTag >: 13:37 +Ldot: React 13:11 Class +Lident: string 13:17 Variable +Ldot: React 14:5 Class +Lident: string 14:11 Variable +Ldot: React 15:5 Class +Lident: string 15:11 Variable +Lident: pair 18:5 Type +Lident: looooooooooooooooooooooooooooooooooooooong_int 20:5 Type +Lident: int 20:54 Type +Lident: looooooooooooooooooooooooooooooooooooooong_string 22:5 Type +Lident: string 22:57 Type +Lident: pairIntString 24:5 Type +Lident: list 24:21 Type +TypeArg: [25:2->28:3] +Lident: pair 25:2 Type +TypeArg: [26:4->26:50] +TypeArg: [27:4->27:53] +Lident: looooooooooooooooooooooooooooooooooooooong_int 26:4 Type +Lident: looooooooooooooooooooooooooooooooooooooong_string 27:4 Type +Binary operator < [31:12->31:13] +Binary operator > [31:22->31:23] +Lident: MT 33:12 Type +Lident: DDF 34:9 Class +Lident: DDF 39:7 Class +Lident: DDF 40:9 Class +Lident: MT 39:12 Type +Lident: XX 45:7 Class +Lident: YY 46:9 Class +Lident: t 47:9 Type +Lident: int 47:13 Type +Ldot: XX 51:5 Class +Lident: YY 51:8 Class +Lident: tt 53:5 Type +Lident: t 53:10 Type +Lident: T 57:7 Class +Lident: someRecord 58:7 Type +Lident: someField 59:4 Property +Lident: int 59:15 Type +Lident: someOtherField 60:4 Property +Lident: string 60:20 Type +Lident: theParam 61:4 Property +Lident: someEnum 64:7 Type +Lident: A 64:18 EnumMember +Lident: B 64:22 EnumMember +Lident: C 64:26 EnumMember +Variable: foo [67:4->67:7] +Variable: x [67:10->67:11] +Ldot: T 67:17 Class +Lident: someField 67:19 Property +Lident: x 67:15 Variable +Variable: add [69:4->69:7] +Variable: x [69:21->69:22] +Variable: world [69:24->69:30] +Lident: x 69:35 Variable +Lident: world 69:39 Variable +Lident: add 71:8 Variable +JsxTag <: 73:8 +Lident: div 73:9 JsxLowercase +Lident: div 73:36 JsxLowercase +JsxTag >: 73:24 +JsxTag >: 73:39 +JsxTag <: 73:26 +Lident: div 73:27 JsxLowercase +Lident: SomeComponent 75:7 Class +Lident: Nested 76:9 Class +Variable: make [78:8->78:12] +Variable: children [78:16->78:25] +Lident: children 79:10 Variable +JsxTag <: 84:8 +Ldot: SomeComponent 84:9 Class +Lident: Nested 84:23 Class +Ldot: SomeComponent 84:41 Class +Lident: Nested 84:55 Class +JsxTag >: 84:29 +JsxTag >: 84:61 +JsxTag <: 84:31 +Lident: div 84:32 JsxLowercase +Variable: toAs [90:4->90:8] +Variable: x [90:19->90:20] +Lident: x 90:25 Variable +Variable: _toEquals [91:4->91:13] +Lident: toAs 91:16 Variable +Variable: to [93:4->93:6] +Lident: to 94:9 Variable +Lident: to 94:14 Variable +Lident: to 94:20 Variable +Lident: to 94:25 Variable +Lident: ToAsProp 98:7 Class +Variable: make [100:6->100:10] +Variable: to [100:14->100:17] +Ldot: React 101:8 Class +Lident: int 101:14 Variable +Lident: to 101:18 Variable +JsxTag <: 104:8 +Lident: ToAsProp 104:9 Class +Variable: true [107:4->107:11] +Lident: true 108:8->108:15 Variable +Variable: enumInModule [110:4->110:16] +Ldot: T 110:19 Class +Lident: A 110:21 EnumMember +Lident: typeInModule 112:5 Type +Ldot: XX 112:20 Class +Ldot: YY 112:23 Class +Lident: t 112:26 Type +Lident: QQ 114:7 Class +Lident: somePolyEnumType 115:7 Type +Lident: list 118:29 Type +TypeArg: [118:34->118:37] +Lident: int 118:34 Type +Variable: x [123:8->123:9] +Lident: x 124:9 Variable +Ldot: QQ 126:8 Class +Lident: somePolyEnumType 126:11 Type +Lident: abc 133:9->133:14 Property diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 7936aba3e..68fa2d8a4 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -2,9 +2,9 @@ Definition tests/src/Jsx.res 5:9 {"uri": "Jsx.res", "range": {"start": {"line": 2, "character": 6}, "end": {"line": 2, "character": 10}}} Complete tests/src/Jsx.res 7:2 -posCursor:(8,14) posNoWhite:(8,12) Found expr:(8,2)->(8,13) -JSX M:(8,2)->(8,3) childrenStart:None (second:(8,4)->(8,10) e:(8,11)->(8,13)) -posCursor:(8,14) posNoWhite:(8,12) Found expr:(8,11)->(8,13) +posCursor:[8:14] posNoWhite:[8:12] Found expr:[8:2->8:13] +JSX 8:3] second[8:4->8:10]=...[8:11->8:13]> _children:None +posCursor:[8:14] posNoWhite:[8:12] Found expr:[8:11->8:13] Completable: Cjsx([M], "", [second]) [{ "label": "first", @@ -27,9 +27,9 @@ Completable: Cjsx([M], "", [second]) }] Complete tests/src/Jsx.res 9:2 -posCursor:(10,18) posNoWhite:(10,17) Found expr:(10,2)->(10,18) -JSX M:(10,2)->(10,3) childrenStart:None (second:(10,4)->(10,10) e:(10,11)->(10,16)), (f:(10,17)->(10,18) e:(10,17)->(10,18)) -posCursor:(10,18) posNoWhite:(10,17) Found expr:(10,17)->(10,18) +posCursor:[10:18] posNoWhite:[10:17] Found expr:[10:2->10:18] +JSX 10:3] second[10:4->10:10]=...[10:11->10:16] f[10:17->10:18]=...[10:17->10:18]> _children:None +posCursor:[10:18] posNoWhite:[10:17] Found expr:[10:17->10:18] Completable: Cjsx([M], f, [second]) [{ "label": "first", @@ -46,9 +46,9 @@ Completable: Cjsx([M], f, [second]) }] Complete tests/src/Jsx.res 11:2 -posCursor:(12,12) posNoWhite:(12,10) Found expr:(12,10)->(12,11) -JSX M:(12,10)->(12,11) childrenStart:None -posCursor:(12,12) posNoWhite:(12,10) Found expr:(12,10)->(12,11) +posCursor:[12:12] posNoWhite:[12:10] Found expr:[12:10->12:11] +JSX 12:11] > _children:None +posCursor:[12:12] posNoWhite:[12:10] Found expr:[12:10->12:11] Completable: Cjsx([M], "", []) [{ "label": "second", @@ -77,9 +77,9 @@ Completable: Cjsx([M], "", []) }] Complete tests/src/Jsx.res 18:2 -posCursor:(19,17) posNoWhite:(19,16) Found expr:(19,2)->(19,17) -JSX M:(19,2)->(19,3) childrenStart:None (prop:(19,4)->(19,8) e:(19,10)->(19,14)), (k:(19,16)->(19,17) e:(19,16)->(19,17)) -posCursor:(19,17) posNoWhite:(19,16) Found expr:(19,16)->(19,17) +posCursor:[19:17] posNoWhite:[19:16] Found expr:[19:2->19:17] +JSX 19:3] prop[19:4->19:8]=...[19:10->19:14] k[19:16->19:17]=...[19:16->19:17]> _children:None +posCursor:[19:17] posNoWhite:[19:16] Found expr:[19:16->19:17] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -90,9 +90,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 20:2 -posCursor:(21,15) posNoWhite:(21,14) Found expr:(21,2)->(21,15) -JSX M:(21,2)->(21,3) childrenStart:None (prop:(21,4)->(21,8) e:(21,9)->(21,13)), (k:(21,14)->(21,15) e:(21,14)->(21,15)) -posCursor:(21,15) posNoWhite:(21,14) Found expr:(21,14)->(21,15) +posCursor:[21:15] posNoWhite:[21:14] Found expr:[21:2->21:15] +JSX 21:3] prop[21:4->21:8]=...[21:9->21:13] k[21:14->21:15]=...[21:14->21:15]> _children:None +posCursor:[21:15] posNoWhite:[21:14] Found expr:[21:14->21:15] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -103,9 +103,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 22:2 -posCursor:(23,19) posNoWhite:(23,18) Found expr:(23,2)->(23,19) -JSX M:(23,2)->(23,3) childrenStart:None (prop:(23,4)->(23,8) e:(23,9)->(23,17)), (k:(23,18)->(23,19) e:(23,18)->(23,19)) -posCursor:(23,19) posNoWhite:(23,18) Found expr:(23,18)->(23,19) +posCursor:[23:19] posNoWhite:[23:18] Found expr:[23:2->23:19] +JSX 23:3] prop[23:4->23:8]=...[23:9->23:17] k[23:18->23:19]=...[23:18->23:19]> _children:None +posCursor:[23:19] posNoWhite:[23:18] Found expr:[23:18->23:19] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -116,9 +116,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 24:2 -posCursor:(25,22) posNoWhite:(25,21) Found expr:(25,2)->(25,22) -JSX M:(25,2)->(25,3) childrenStart:None (prop:(25,4)->(25,8) e:(25,9)->(25,20)), (k:(25,21)->(25,22) e:(25,21)->(25,22)) -posCursor:(25,22) posNoWhite:(25,21) Found expr:(25,21)->(25,22) +posCursor:[25:22] posNoWhite:[25:21] Found expr:[25:2->25:22] +JSX 25:3] prop[25:4->25:8]=...[25:9->25:20] k[25:21->25:22]=...[25:21->25:22]> _children:None +posCursor:[25:22] posNoWhite:[25:21] Found expr:[25:21->25:22] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -129,9 +129,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 26:2 -posCursor:(27,16) posNoWhite:(27,15) Found expr:(27,2)->(27,16) -JSX M:(27,2)->(27,3) childrenStart:None (prop:(27,4)->(27,8) e:(27,10)->(27,14)), (k:(27,15)->(27,16) e:(27,15)->(27,16)) -posCursor:(27,16) posNoWhite:(27,15) Found expr:(27,15)->(27,16) +posCursor:[27:16] posNoWhite:[27:15] Found expr:[27:2->27:16] +JSX 27:3] prop[27:4->27:8]=...[27:10->27:14] k[27:15->27:16]=...[27:15->27:16]> _children:None +posCursor:[27:16] posNoWhite:[27:15] Found expr:[27:15->27:16] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -142,9 +142,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 28:2 -posCursor:(29,14) posNoWhite:(29,13) Found expr:(29,2)->(29,14) -JSX M:(29,2)->(29,3) childrenStart:None (prop:(29,4)->(29,8) e:(29,9)->(29,12)), (k:(29,13)->(29,14) e:(29,13)->(29,14)) -posCursor:(29,14) posNoWhite:(29,13) Found expr:(29,13)->(29,14) +posCursor:[29:14] posNoWhite:[29:13] Found expr:[29:2->29:14] +JSX 29:3] prop[29:4->29:8]=...[29:9->29:12] k[29:13->29:14]=...[29:13->29:14]> _children:None +posCursor:[29:14] posNoWhite:[29:13] Found expr:[29:13->29:14] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -155,9 +155,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 30:2 -posCursor:(31,15) posNoWhite:(31,14) Found expr:(31,2)->(31,15) -JSX M:(31,2)->(31,3) childrenStart:None (prop:(31,4)->(31,8) e:(31,9)->(31,13)), (k:(31,14)->(31,15) e:(31,14)->(31,15)) -posCursor:(31,15) posNoWhite:(31,14) Found expr:(31,14)->(31,15) +posCursor:[31:15] posNoWhite:[31:14] Found expr:[31:2->31:15] +JSX 31:3] prop[31:4->31:8]=...[31:9->31:13] k[31:14->31:15]=...[31:14->31:15]> _children:None +posCursor:[31:15] posNoWhite:[31:14] Found expr:[31:14->31:15] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -168,9 +168,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 32:2 -posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,2)->(33,16) -JSX M:(33,2)->(33,3) childrenStart:None (prop:(33,4)->(33,8) e:(33,9)->(33,14)), (k:(33,15)->(33,16) e:(33,15)->(33,16)) -posCursor:(33,16) posNoWhite:(33,15) Found expr:(33,15)->(33,16) +posCursor:[33:16] posNoWhite:[33:15] Found expr:[33:2->33:16] +JSX 33:3] prop[33:4->33:8]=...[33:9->33:14] k[33:15->33:16]=...[33:15->33:16]> _children:None +posCursor:[33:16] posNoWhite:[33:15] Found expr:[33:15->33:16] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -181,9 +181,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 34:2 -posCursor:(35,14) posNoWhite:(35,13) Found expr:(35,2)->(35,14) -JSX M:(35,2)->(35,3) childrenStart:None (prop:(35,4)->(35,8) e:(35,9)->(35,12)), (k:(35,13)->(35,14) e:(35,13)->(35,14)) -posCursor:(35,14) posNoWhite:(35,13) Found expr:(35,13)->(35,14) +posCursor:[35:14] posNoWhite:[35:13] Found expr:[35:2->35:14] +JSX 35:3] prop[35:4->35:8]=...[35:9->35:12] k[35:13->35:14]=...[35:13->35:14]> _children:None +posCursor:[35:14] posNoWhite:[35:13] Found expr:[35:13->35:14] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -194,9 +194,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 36:2 -posCursor:(37,25) posNoWhite:(37,24) Found expr:(37,2)->(37,25) -JSX M:(37,2)->(37,3) childrenStart:None (prop:(37,4)->(37,8) e:(37,9)->(37,23)), (k:(37,24)->(37,25) e:(37,24)->(37,25)) -posCursor:(37,25) posNoWhite:(37,24) Found expr:(37,24)->(37,25) +posCursor:[37:25] posNoWhite:[37:24] Found expr:[37:2->37:25] +JSX 37:3] prop[37:4->37:8]=...[37:9->37:23] k[37:24->37:25]=...[37:24->37:25]> _children:None +posCursor:[37:25] posNoWhite:[37:24] Found expr:[37:24->37:25] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -207,9 +207,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 38:2 -posCursor:(39,36) posNoWhite:(39,35) Found expr:(39,2)->(39,36) -JSX M:(39,2)->(39,3) childrenStart:None (prop:(39,4)->(39,8) e:(39,9)->(39,34)), (k:(39,35)->(39,36) e:(39,35)->(39,36)) -posCursor:(39,36) posNoWhite:(39,35) Found expr:(39,35)->(39,36) +posCursor:[39:36] posNoWhite:[39:35] Found expr:[39:2->39:36] +JSX 39:3] prop[39:4->39:8]=...[39:9->39:34] k[39:35->39:36]=...[39:35->39:36]> _children:None +posCursor:[39:36] posNoWhite:[39:35] Found expr:[39:35->39:36] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -220,9 +220,9 @@ Completable: Cjsx([M], k, [prop]) }] Complete tests/src/Jsx.res 40:2 -posCursor:(41,23) posNoWhite:(41,22) Found expr:(41,2)->(41,23) -JSX M:(41,2)->(41,3) childrenStart:None (prop:(41,4)->(41,8) e:(41,9)->(41,21)), (k:(41,22)->(41,23) e:(41,22)->(41,23)) -posCursor:(41,23) posNoWhite:(41,22) Found expr:(41,22)->(41,23) +posCursor:[41:23] posNoWhite:[41:22] Found expr:[41:2->41:23] +JSX 41:3] prop[41:4->41:8]=...[41:9->41:21] k[41:22->41:23]=...[41:22->41:23]> _children:None +posCursor:[41:23] posNoWhite:[41:22] Found expr:[41:22->41:23] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -236,9 +236,9 @@ Definition tests/src/Jsx.res 43:11 {"uri": "Component.res", "range": {"start": {"line": 1, "character": 4}, "end": {"line": 1, "character": 8}}} Complete tests/src/Jsx.res 52:2 -posCursor:(53,8) posNoWhite:(53,7) Found expr:(53,2)->(53,8) -JSX Ext:(53,2)->(53,5) childrenStart:None (al:(53,6)->(53,8) e:(53,6)->(53,8)) -posCursor:(53,8) posNoWhite:(53,7) Found expr:(53,6)->(53,8) +posCursor:[53:8] posNoWhite:[53:7] Found expr:[53:2->53:8] +JSX 53:5] al[53:6->53:8]=...[53:6->53:8]> _children:None +posCursor:[53:8] posNoWhite:[53:7] Found expr:[53:6->53:8] Completable: Cjsx([Ext], al, []) [{ "label": "align", @@ -249,9 +249,9 @@ Completable: Cjsx([Ext], al, []) }] Complete tests/src/Jsx.res 54:2 -posCursor:(55,10) posNoWhite:(55,8) Found expr:(55,2)->(55,9) -JSX M:(55,2)->(55,3) childrenStart:None (first:(55,4)->(55,9) e:(55,4)->(55,9)) -posCursor:(55,10) posNoWhite:(55,8) Found expr:(55,4)->(55,9) +posCursor:[55:10] posNoWhite:[55:8] Found expr:[55:2->55:9] +JSX 55:3] first[55:4->55:9]=...[55:4->55:9]> _children:None +posCursor:[55:10] posNoWhite:[55:8] Found expr:[55:4->55:9] Completable: Cjsx([M], "", [first]) [{ "label": "second", @@ -274,9 +274,9 @@ Completable: Cjsx([M], "", [first]) }] Complete tests/src/Jsx.res 56:2 -posCursor:(57,14) posNoWhite:(57,13) Found expr:(57,2)->(57,14) -JSX M:(57,2)->(57,3) childrenStart:None (first:(57,4)->(57,9) e:(57,10)->(57,12)), (k:(57,13)->(57,14) e:(57,13)->(57,14)) -posCursor:(57,14) posNoWhite:(57,13) Found expr:(57,13)->(57,14) +posCursor:[57:14] posNoWhite:[57:13] Found expr:[57:2->57:14] +JSX 57:3] first[57:4->57:9]=...[57:10->57:12] k[57:13->57:14]=...[57:13->57:14]> _children:None +posCursor:[57:14] posNoWhite:[57:13] Found expr:[57:13->57:14] Completable: Cjsx([M], k, [first]) [{ "label": "key", @@ -287,9 +287,9 @@ Completable: Cjsx([M], k, [first]) }] Complete tests/src/Jsx.res 58:2 -posCursor:(59,21) posNoWhite:(59,20) Found expr:(59,2)->(59,21) -JSX M:(59,2)->(59,3) childrenStart:None (first:(59,4)->(59,9) e:(59,17)->(59,19)), (k:(59,20)->(59,21) e:(59,20)->(59,21)) -posCursor:(59,21) posNoWhite:(59,20) Found expr:(59,20)->(59,21) +posCursor:[59:21] posNoWhite:[59:20] Found expr:[59:2->59:21] +JSX 59:3] first[59:4->59:9]=...[59:17->59:19] k[59:20->59:21]=...[59:20->59:21]> _children:None +posCursor:[59:21] posNoWhite:[59:20] Found expr:[59:20->59:21] Completable: Cjsx([M], k, [first]) [{ "label": "key", @@ -300,8 +300,8 @@ Completable: Cjsx([M], k, [first]) }] Complete tests/src/Jsx.res 60:2 -posCursor:(61,4) posNoWhite:(61,3) Found expr:(61,2)->(61,4) -JSX M:(61,2)->(61,3) childrenStart:(61,3) -posCursor:(61,4) posNoWhite:(61,3) Found expr:(61,3)->(62,0) +posCursor:[61:4] posNoWhite:[61:3] Found expr:[61:2->61:4] +JSX 61:3] > _children:61:3 +posCursor:[61:4] posNoWhite:[61:3] Found expr:[61:3->62:0] [] diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index 68e5e0f57..552df3356 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -1,6 +1,6 @@ Complete tests/src/RecordCompletion.res 7:3 -posCursor:(8,7) posNoWhite:(8,6) Found expr:(8,1)->(8,7) -posCursor:(8,7) posNoWhite:(8,6) Found expr:(8,6)->(8,7) +posCursor:[8:7] posNoWhite:[8:6] Found expr:[8:1->8:7] +posCursor:[8:7] posNoWhite:[8:6] Found expr:[8:6->8:7] Completable: Cpipe(t.n, m) [{ "label": "Js.Array2.mapi", @@ -17,8 +17,8 @@ Completable: Cpipe(t.n, m) }] Complete tests/src/RecordCompletion.res 9:3 -posCursor:(10,11) posNoWhite:(10,10) Found expr:(10,1)->(10,11) -posCursor:(10,11) posNoWhite:(10,10) Found expr:(10,10)->(10,11) +posCursor:[10:11] posNoWhite:[10:10] Found expr:[10:1->10:11] +posCursor:[10:11] posNoWhite:[10:10] Found expr:[10:10->10:11] Completable: Cpipe(t2.n2.n, m) [{ "label": "Js.Array2.mapi", From 4bb1c4b3e87f58dc477af5118a862e73ca925a05 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 13 Apr 2022 09:26:13 +0200 Subject: [PATCH 016/135] refactor --- analysis/src/Commands.ml | 26 ++++++++++--------------- analysis/src/Loc.ml | 3 +++ analysis/src/Pos.ml | 4 ++++ analysis/src/Range.ml | 7 +++++++ analysis/src/SemanticTokens.ml | 35 +++++++++++++--------------------- 5 files changed, 37 insertions(+), 38 deletions(-) create mode 100644 analysis/src/Loc.ml create mode 100644 analysis/src/Pos.ml create mode 100644 analysis/src/Range.ml diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 07ecb3167..b4eb8ee51 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -243,9 +243,8 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = found := true; if debug then Printf.printf "posCursor:[%s] posNoWhite:[%s] Found expr:%s\n" - (SemanticTokens.posToString posCursor) - (SemanticTokens.posToString posNoWhite) - (SemanticTokens.locToString expr.pexp_loc); + (Pos.toString posCursor) (Pos.toString posNoWhite) + (Loc.toString expr.pexp_loc); match expr.pexp_desc with | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) @@ -254,18 +253,16 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = if debug then Printf.printf "JSX <%s:%s %s> _children:%s\n" (jsxProps.componentPath |> String.concat ",") - (SemanticTokens.locToString compName.loc) + (Loc.toString compName.loc) (jsxProps.props |> List.map (fun {name; posStart; posEnd; exp} -> Printf.sprintf "%s[%s->%s]=...%s" name - (SemanticTokens.posToString posStart) - (SemanticTokens.posToString posEnd) - (SemanticTokens.locToString exp.pexp_loc)) + (Pos.toString posStart) (Pos.toString posEnd) + (Loc.toString exp.pexp_loc)) |> String.concat " ") (match jsxProps.childrenStart with | None -> "None" - | Some childrenPosStart -> - SemanticTokens.posToString childrenPosStart); + | Some childrenPosStart -> Pos.toString childrenPosStart); let jsxCompletable = findJsxPropCompletable ~jsxProps ~endPos:(Utils.tupleOfLexing expr.pexp_loc.loc_end) @@ -280,20 +277,17 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let args = extractExpApplyArgs ~text ~eFun ~args in if debug then Printf.printf "Pexp_apply ...%s (%s)\n" - (SemanticTokens.locToString eFun.pexp_loc) + (Loc.toString eFun.pexp_loc) (args |> List.map (fun {label; exp} -> Printf.sprintf "%s...%s" (match label with | None -> "" | Some {name; opt; posStart; posEnd} -> - "~" ^ name - ^ SemanticTokens.posToString posStart - ^ "->" - ^ SemanticTokens.posToString posEnd - ^ "=" + "~" ^ name ^ Pos.toString posStart ^ "->" + ^ Pos.toString posEnd ^ "=" ^ if opt then "?" else "") - (SemanticTokens.locToString exp.pexp_loc)) + (Loc.toString exp.pexp_loc)) |> String.concat ", ") | _ -> ()); Ast_iterator.default_iterator.expr iterator expr diff --git a/analysis/src/Loc.ml b/analysis/src/Loc.ml new file mode 100644 index 000000000..cad84cae8 --- /dev/null +++ b/analysis/src/Loc.ml @@ -0,0 +1,3 @@ +type t = Location.t + +let toString (loc : t) = loc |> Range.ofLoc |> Range.toString diff --git a/analysis/src/Pos.ml b/analysis/src/Pos.ml new file mode 100644 index 000000000..78bc233d9 --- /dev/null +++ b/analysis/src/Pos.ml @@ -0,0 +1,4 @@ +type t = int * int + +let ofLexing = Utils.tupleOfLexing +let toString (loc, col) = Printf.sprintf "%d:%d" loc col diff --git a/analysis/src/Range.ml b/analysis/src/Range.ml new file mode 100644 index 000000000..6fa700677 --- /dev/null +++ b/analysis/src/Range.ml @@ -0,0 +1,7 @@ +type t = Pos.t * Pos.t + +let ofLoc (loc : Location.t) = + (Pos.ofLexing loc.loc_start, Pos.ofLexing loc.loc_end) + +let toString ((posStart, posEnd) : t) = + Printf.sprintf "[%s->%s]" (Pos.toString posStart) (Pos.toString posEnd) diff --git a/analysis/src/SemanticTokens.ml b/analysis/src/SemanticTokens.ml index e3b6feac2..a1c2b7d33 100644 --- a/analysis/src/SemanticTokens.ml +++ b/analysis/src/SemanticTokens.ml @@ -89,15 +89,6 @@ module Token = struct Buffer.contents buf end -let locToPositions (loc : Location.t) = - (Utils.tupleOfLexing loc.loc_start, Utils.tupleOfLexing loc.loc_end) - -let posToString (loc, col) = Printf.sprintf "%d:%d" loc col - -let locToString (loc : Location.t) = - let posStart, posEnd = locToPositions loc in - Printf.sprintf "[%s->%s]" (posToString posStart) (posToString posEnd) - let isLowercaseId id = id <> "" && @@ -110,7 +101,7 @@ let isUppercaseId id = let c = id.[0] in c >= 'A' && c <= 'Z' -let emitFromPos posStart posEnd ~type_ emitter = +let emitFromRange (posStart, posEnd) ~type_ emitter = let length = if fst posStart = fst posEnd then snd posEnd - snd posStart else 0 in @@ -119,8 +110,7 @@ let emitFromPos posStart posEnd ~type_ emitter = |> Token.add ~line:(fst posStart) ~char:(snd posStart) ~length ~type_ let emitFromLoc ~loc ~type_ emitter = - let posStart, posEnd = locToPositions loc in - emitter |> emitFromPos posStart posEnd ~type_ + emitter |> emitFromRange (Range.ofLoc loc) ~type_ let emitLongident ?(backwards = false) ?(jsx = false) ?(lowerCaseToken = if jsx then Token.JsxLowercase else Token.Variable) @@ -151,17 +141,17 @@ let emitLongident ?(backwards = false) ?(jsx = false) | None -> (posAfter, false) in if debug then - Printf.printf "Lident: %s %s%s %s\n" id (posToString pos) - (if lenMismatch then "->" ^ posToString posEnd else "") + Printf.printf "Lident: %s %s%s %s\n" id (Pos.toString pos) + (if lenMismatch then "->" ^ Pos.toString posEnd else "") (Token.tokenTypeDebug type_); - emitter |> emitFromPos pos posEnd ~type_ + emitter |> emitFromRange (pos, posEnd) ~type_ | id :: segments when isUppercaseId id || isLowercaseId id -> let type_ = if isUppercaseId id then upperCaseToken else lowerCaseToken in if debug then - Printf.printf "Ldot: %s %s %s\n" id (posToString pos) + Printf.printf "Ldot: %s %s %s\n" id (Pos.toString pos) (Token.tokenTypeDebug type_); let length = String.length id in - emitter |> emitFromPos pos (fst pos, snd pos + length) ~type_; + emitter |> emitFromRange (pos, (fst pos, snd pos + length)) ~type_; loop (fst pos, snd pos + length + 1) segments | _ -> () in @@ -173,7 +163,7 @@ let emitLongident ?(backwards = false) ?(jsx = false) else loop pos segments let emitVariable ~id ~debug ~loc emitter = - if debug then Printf.printf "Variable: %s %s\n" id (locToString loc); + if debug then Printf.printf "Variable: %s %s\n" id (Loc.toString loc); emitter |> emitFromLoc ~loc ~type_:Variable let emitJsxOpen ~lid ~debug ~loc emitter = @@ -186,8 +176,8 @@ let emitJsxClose ~lid ~debug ~pos emitter = emitter |> emitLongident ~backwards:true ~pos ~lid ~jsx:true ~debug let emitJsxTag ~debug ~name ~pos emitter = - if debug then Printf.printf "JsxTag %s: %s\n" name (posToString pos); - emitter |> emitFromPos pos (fst pos, snd pos + 1) ~type_:Token.JsxTag + if debug then Printf.printf "JsxTag %s: %s\n" name (Pos.toString pos); + emitter |> emitFromRange (pos, (fst pos, snd pos + 1)) ~type_:Token.JsxTag let emitType ~lid ~debug ~loc emitter = emitter @@ -210,7 +200,7 @@ let emitVariant ~(name : Longident.t Location.loc) ~debug emitter = let command ~debug ~emitter ~path = let processTypeArg (coreType : Parsetree.core_type) = - if debug then Printf.printf "TypeArg: %s\n" (locToString coreType.ptyp_loc) + if debug then Printf.printf "TypeArg: %s\n" (Loc.toString coreType.ptyp_loc) in let typ (iterator : Ast_iterator.iterator) (coreType : Parsetree.core_type) = match coreType.ptyp_desc with @@ -316,7 +306,8 @@ let command ~debug ~emitter ~path = Pexp_ident {txt = Longident.Lident (("<" | ">") as op); loc}; }, [_; _] ) -> - if debug then Printf.printf "Binary operator %s %s\n" op (locToString loc); + if debug then + Printf.printf "Binary operator %s %s\n" op (Loc.toString loc); emitter |> emitFromLoc ~loc ~type_:Operator; Ast_iterator.default_iterator.expr iterator e | Pexp_record (cases, _) -> From dd4cf1fd550496f7788017260a9dc683e57d9986 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 13 Apr 2022 09:42:08 +0200 Subject: [PATCH 017/135] Refactor pos out of Utils. --- analysis/src/Commands.ml | 28 ++++++---------- analysis/src/CreateInterface.ml | 5 ++- analysis/src/Loc.ml | 6 +++- analysis/src/NewCompletions.ml | 4 +-- analysis/src/Pos.ml | 4 ++- analysis/src/ProcessCmt.ml | 59 +++++---------------------------- analysis/src/Range.ml | 3 -- analysis/src/SemanticTokens.ml | 41 +++++++++++------------ analysis/src/Utils.ml | 8 ++--- 9 files changed, 53 insertions(+), 105 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index b4eb8ee51..bdc6826a0 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -1,9 +1,5 @@ open SharedTypes -let posInLoc ~pos ~loc = - Utils.tupleOfLexing loc.Location.loc_start <= pos - && pos < Utils.tupleOfLexing loc.loc_end - type prop = { name : string; posStart : int * int; @@ -24,7 +20,7 @@ let findJsxPropCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName | prop :: rest -> if prop.posStart <= posBeforeCursor && posBeforeCursor < prop.posEnd then Some (PartialParser.Cjsx (jsxProps.componentPath, prop.name, seen)) - else if posInLoc ~pos:posBeforeCursor ~loc:prop.exp.pexp_loc then None + else if prop.exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None else loop ~seen:(seen @ [prop.name]) rest | [] -> let beforeChildrenStart = @@ -98,12 +94,10 @@ let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = componentPath = flattenComponentName compName.txt; props = List.rev acc; childrenStart = - (if pexp_loc.loc_ghost then None - else Some (Utils.tupleOfLexing pexp_loc.loc_start)); + (if pexp_loc.loc_ghost then None else Some (Loc.start pexp_loc)); } | ((Labelled s | Optional s), (eProp : Parsetree.expression)) :: rest -> ( - let ePosStart = Utils.tupleOfLexing eProp.pexp_loc.loc_start in - let ePosEnd = Utils.tupleOfLexing eProp.pexp_loc.loc_end in + let ePosStart, ePosEnd = Loc.range eProp.pexp_loc in match ( PartialParser.positionToOffset text ePosStart, PartialParser.positionToOffset text ePosEnd ) @@ -130,7 +124,7 @@ let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = (* should not happen *) {componentPath = []; props = []; childrenStart = None} in - let posAfterCompName = Utils.tupleOfLexing compName.loc.loc_end in + let posAfterCompName = Loc.end_ compName.loc in let offsetAfterCompName = match PartialParser.positionToOffset text posAfterCompName with | None -> assert false @@ -173,8 +167,7 @@ let extractExpApplyArgs ~text ~eFun ~args = match args with | (((Asttypes.Labelled s | Optional s) as label), (e : Parsetree.expression)) :: rest -> ( - let ePosStart = Utils.tupleOfLexing e.pexp_loc.loc_start in - let ePosEnd = Utils.tupleOfLexing e.pexp_loc.loc_end in + let ePosStart, ePosEnd = Loc.range e.pexp_loc in match ( PartialParser.positionToOffset text ePosStart, PartialParser.positionToOffset text ePosEnd ) @@ -201,7 +194,7 @@ let extractExpApplyArgs ~text ~eFun ~args = | (Asttypes.Nolabel, (e : Parsetree.expression)) :: rest -> ( if e.pexp_loc.loc_ghost then processArgs ~acc ~lastOffset ~lastPos rest else - let ePosEnd = Utils.tupleOfLexing e.pexp_loc.loc_end in + let ePosEnd = Loc.end_ e.pexp_loc in match PartialParser.positionToOffset text ePosEnd with | Some offsetEnd -> processArgs @@ -212,7 +205,7 @@ let extractExpApplyArgs ~text ~eFun ~args = assert false) | [] -> List.rev acc in - let lastPos = Utils.tupleOfLexing eFun.Parsetree.pexp_loc.loc_end in + let lastPos = Loc.end_ eFun.Parsetree.pexp_loc in let lastOffset = match PartialParser.positionToOffset text lastPos with | Some offset -> offset @@ -239,7 +232,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let found = ref false in let result = ref None in let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = - if posInLoc ~pos:posNoWhite ~loc:expr.pexp_loc then ( + if expr.pexp_loc |> Loc.hasPos ~pos:posNoWhite then ( found := true; if debug then Printf.printf "posCursor:[%s] posNoWhite:[%s] Found expr:%s\n" @@ -264,10 +257,9 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | None -> "None" | Some childrenPosStart -> Pos.toString childrenPosStart); let jsxCompletable = - findJsxPropCompletable ~jsxProps - ~endPos:(Utils.tupleOfLexing expr.pexp_loc.loc_end) + findJsxPropCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor:(fst posCursor, max 0 (snd posCursor - 1)) - ~posAfterCompName:(Utils.tupleOfLexing compName.loc.loc_end) + ~posAfterCompName:(Loc.end_ compName.loc) in result := jsxCompletable | Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "|."}}, [_; _]) -> diff --git a/analysis/src/CreateInterface.ml b/analysis/src/CreateInterface.ml index 04fc1b358..fd168bcbc 100644 --- a/analysis/src/CreateInterface.ml +++ b/analysis/src/CreateInterface.ml @@ -113,7 +113,7 @@ let printSignature ~extractor ~signature = :: Sig_value (makeId (* make *), makeValueDesc) :: rest when Ident.name makePropsId = Ident.name makeId ^ "Props" && ((* from implementation *) makePropsLoc.loc_ghost - || (* from interface *) makePropsLoc = makeValueDesc.val_loc) + || (* from interface *) makePropsLoc = makeValueDesc.val_loc) && getComponentType makeValueDesc.val_type <> None -> (* {"name": string} => retType ~~> (~name:string) => retType @@ -164,8 +164,7 @@ let printSignature ~extractor ~signature = (* Rescript primitive name, e.g. @val external ... Copy the external declaration verbatim from the implementation file *) let lines = - let posStart = val_loc.loc_start |> Utils.tupleOfLexing in - let posEnd = val_loc.loc_end |> Utils.tupleOfLexing in + let posStart, posEnd = Loc.range val_loc in extractor |> SourceFileExtractor.extract ~posStart ~posEnd in Buffer.add_string buf ((lines |> String.concat "\n") ^ "\n"); diff --git a/analysis/src/Loc.ml b/analysis/src/Loc.ml index cad84cae8..7c3db9451 100644 --- a/analysis/src/Loc.ml +++ b/analysis/src/Loc.ml @@ -1,3 +1,7 @@ type t = Location.t -let toString (loc : t) = loc |> Range.ofLoc |> Range.toString +let start (loc : t) = Pos.ofLexing loc.loc_start +let end_ (loc : t) = Pos.ofLexing loc.loc_end +let range loc : Range.t = (start loc, end_ loc) +let toString (loc : t) = loc |> range |> Range.toString +let hasPos ~pos loc = start loc <= pos && pos < end_ loc diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 4a4294415..f7fd2b678 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -1238,10 +1238,10 @@ let computeCompletions ~completable ~full ~pos ~rawOpens = let rec prioritize decls = match decls with | (d1, e1) :: (d2, e2) :: rest -> - let pos2 = d2.Completion.extentLoc.loc_start |> Utils.tupleOfLexing in + let pos2 = d2.Completion.extentLoc.loc_start |> Pos.ofLexing in if pos2 >= pos then prioritize ((d1, e1) :: rest) else - let pos1 = d1.extentLoc.loc_start |> Utils.tupleOfLexing in + let pos1 = d1.extentLoc.loc_start |> Pos.ofLexing in if pos1 <= pos2 then prioritize ((d2, e2) :: rest) else prioritize ((d1, e1) :: rest) | [] | [_] -> decls diff --git a/analysis/src/Pos.ml b/analysis/src/Pos.ml index 78bc233d9..cfcf6b7b6 100644 --- a/analysis/src/Pos.ml +++ b/analysis/src/Pos.ml @@ -1,4 +1,6 @@ type t = int * int -let ofLexing = Utils.tupleOfLexing +let ofLexing {Lexing.pos_lnum; pos_cnum; pos_bol} = + (pos_lnum - 1, pos_cnum - pos_bol) + let toString (loc, col) = Printf.sprintf "%d:%d" loc col diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index a01c15765..7fbb1def3 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -49,12 +49,7 @@ let rec forTypeSignatureItem ~env ~(exported : Exported.t) (Exported.add exported Exported.Value) Stamps.addValue in - [ - { - Module.kind = Module.Value declared.item; - name = declared.name.txt; - }; - ] + [{Module.kind = Module.Value declared.item; name = declared.name.txt}] | Sig_type ( ident, ({type_loc; type_kind; type_manifest; type_attributes} as decl), @@ -127,12 +122,7 @@ let rec forTypeSignatureItem ~env ~(exported : Exported.t) (Exported.add exported Exported.Type) Stamps.addType in - [ - { - Module.kind = Type (declared.item, recStatus); - name = declared.name.txt; - }; - ] + [{Module.kind = Type (declared.item, recStatus); name = declared.name.txt}] | Sig_module (ident, {md_type; md_attributes; md_loc}, _) -> let declared = addItem ~extent:md_loc @@ -142,12 +132,7 @@ let rec forTypeSignatureItem ~env ~(exported : Exported.t) (Exported.add exported Exported.Module) Stamps.addModule in - [ - { - Module.kind = Module declared.item; - name = declared.name.txt; - }; - ] + [{Module.kind = Module declared.item; name = declared.name.txt}] | _ -> [] and forTypeSignature env signature = @@ -247,12 +232,7 @@ let rec forSignatureItem ~env ~(exported : Exported.t) (Exported.add exported Exported.Value) Stamps.addValue in - [ - { - Module.kind = Module.Value declared.item; - name = declared.name.txt; - }; - ] + [{Module.kind = Module.Value declared.item; name = declared.name.txt}] | Tsig_type (recFlag, decls) -> decls |> List.mapi (fun i decl -> @@ -272,12 +252,7 @@ let rec forSignatureItem ~env ~(exported : Exported.t) (Exported.add exported Exported.Module) Stamps.addModule in - [ - { - Module.kind = Module declared.item; - name = declared.name.txt; - }; - ] + [{Module.kind = Module declared.item; name = declared.name.txt}] | Tsig_recmodule modDecls -> modDecls |> List.map (fun modDecl -> @@ -375,12 +350,7 @@ let rec forStructureItem ~env ~(exported : Exported.t) item = (Exported.add exported Exported.Module) Stamps.addModule in - [ - { - Module.kind = Module declared.item; - name = declared.name.txt; - }; - ] + [{Module.kind = Module declared.item; name = declared.name.txt}] | Tstr_recmodule modDecls -> modDecls |> List.map (fun modDecl -> @@ -406,12 +376,7 @@ let rec forStructureItem ~env ~(exported : Exported.t) item = (Exported.add exported Exported.Module) Stamps.addModule in - [ - { - Module.kind = Module modTypeItem; - name = declared.name.txt; - }; - ] + [{Module.kind = Module modTypeItem; name = declared.name.txt}] | Tstr_include {incl_mod; incl_type} -> let env = match getModulePath incl_mod.mod_desc with @@ -435,12 +400,7 @@ let rec forStructureItem ~env ~(exported : Exported.t) item = (Exported.add exported Exported.Value) Stamps.addValue in - [ - { - Module.kind = Value declared.item; - name = declared.name.txt; - }; - ] + [{Module.kind = Value declared.item; name = declared.name.txt}] | Tstr_type (recFlag, decls) -> decls |> List.mapi (fun i decl -> @@ -1248,8 +1208,7 @@ let rec resolvePath ~env ~path ~package = | Some file -> resolvePath ~env:(QueryEnv.fromFile file) ~path:fullPath ~package)) -let locationIsBefore {Location.loc_start} pos = - Utils.tupleOfLexing loc_start <= pos +let locationIsBefore {Location.loc_start} pos = Pos.ofLexing loc_start <= pos let findInScope pos name iter stamps = (* Log.log("Find " ++ name ++ " with " ++ string_of_int(Hashtbl.length(stamps)) ++ " stamps"); *) diff --git a/analysis/src/Range.ml b/analysis/src/Range.ml index 6fa700677..cdc2e0a41 100644 --- a/analysis/src/Range.ml +++ b/analysis/src/Range.ml @@ -1,7 +1,4 @@ type t = Pos.t * Pos.t -let ofLoc (loc : Location.t) = - (Pos.ofLexing loc.loc_start, Pos.ofLexing loc.loc_end) - let toString ((posStart, posEnd) : t) = Printf.sprintf "[%s->%s]" (Pos.toString posStart) (Pos.toString posEnd) diff --git a/analysis/src/SemanticTokens.ml b/analysis/src/SemanticTokens.ml index a1c2b7d33..3ee86c7ef 100644 --- a/analysis/src/SemanticTokens.ml +++ b/analysis/src/SemanticTokens.ml @@ -110,7 +110,7 @@ let emitFromRange (posStart, posEnd) ~type_ emitter = |> Token.add ~line:(fst posStart) ~char:(snd posStart) ~length ~type_ let emitFromLoc ~loc ~type_ emitter = - emitter |> emitFromRange (Range.ofLoc loc) ~type_ + emitter |> emitFromRange (Loc.range loc) ~type_ let emitLongident ?(backwards = false) ?(jsx = false) ?(lowerCaseToken = if jsx then Token.JsxLowercase else Token.Variable) @@ -169,7 +169,7 @@ let emitVariable ~id ~debug ~loc emitter = let emitJsxOpen ~lid ~debug ~loc emitter = emitter |> emitLongident - ~pos:(Utils.tupleOfLexing loc.Location.loc_start) + ~pos:(Pos.ofLexing loc.Location.loc_start) ~lid ~jsx:true ~debug let emitJsxClose ~lid ~debug ~pos emitter = @@ -182,20 +182,20 @@ let emitJsxTag ~debug ~name ~pos emitter = let emitType ~lid ~debug ~loc emitter = emitter |> emitLongident ~lowerCaseToken:Token.Type - ~pos:(Utils.tupleOfLexing loc.Location.loc_start) + ~pos:(Pos.ofLexing loc.Location.loc_start) ~lid ~debug let emitRecordLabel ~(label : Longident.t Location.loc) ~debug emitter = emitter |> emitLongident ~lowerCaseToken:Token.Property - ~pos:(Utils.tupleOfLexing label.loc.loc_start) - ~posEnd:(Some (Utils.tupleOfLexing label.loc.loc_end)) + ~pos:(Pos.ofLexing label.loc.loc_start) + ~posEnd:(Some (Pos.ofLexing label.loc.loc_end)) ~lid:label.txt ~debug let emitVariant ~(name : Longident.t Location.loc) ~debug emitter = emitter |> emitLongident ~lastToken:(Some Token.EnumMember) - ~pos:(Utils.tupleOfLexing name.loc.loc_start) + ~pos:(Pos.ofLexing name.loc.loc_start) ~lid:name.txt ~debug let command ~debug ~emitter ~path = @@ -244,8 +244,8 @@ let command ~debug ~emitter ~path = if lid <> Lident "not" then emitter |> emitLongident - ~pos:(Utils.tupleOfLexing loc.loc_start) - ~posEnd:(Some (Utils.tupleOfLexing loc.loc_end)) + ~pos:(Pos.ofLexing loc.loc_start) + ~posEnd:(Some (Pos.ofLexing loc.loc_end)) ~lid ~debug; Ast_iterator.default_iterator.expr iterator e | Pexp_apply ({pexp_desc = Pexp_ident lident; pexp_loc}, args) @@ -261,7 +261,7 @@ let command ~debug ~emitter ~path = emitter (* --> emitJsxTag ~debug ~name:"<" ~pos: - (let pos = Utils.tupleOfLexing e.pexp_loc.loc_start in + (let pos = Pos.ofLexing e.pexp_loc.loc_start in (fst pos, snd pos - 1 (* the AST skips the loc of < somehow *))); emitter |> emitJsxOpen ~lid:lident.txt ~debug ~loc:pexp_loc; @@ -269,7 +269,7 @@ let command ~debug ~emitter ~path = let rec loop = function | (Asttypes.Labelled "children", {Parsetree.pexp_loc = {loc_start}}) :: _ -> - Utils.tupleOfLexing loc_start + Pos.ofLexing loc_start | _ :: args -> loop args | [] -> (* should not happen *) (-1, -1) in @@ -277,7 +277,7 @@ let command ~debug ~emitter ~path = loop args in let posOfFinalGreatherthan = - let pos = Utils.tupleOfLexing e.pexp_loc.loc_end in + let pos = Pos.ofLexing e.pexp_loc.loc_end in (fst pos, snd pos - 1) in let selfClosing = @@ -286,10 +286,10 @@ let command ~debug ~emitter ~path = (* there's an off-by one somehow in the AST *) in (if not selfClosing then - let lineStart, colStart = Utils.tupleOfLexing pexp_loc.loc_start in - let lineEnd, colEnd = Utils.tupleOfLexing pexp_loc.loc_end in + let lineStart, colStart = Pos.ofLexing pexp_loc.loc_start in + let lineEnd, colEnd = Pos.ofLexing pexp_loc.loc_end in let length = if lineStart = lineEnd then colEnd - colStart else 0 in - let lineEndWhole, colEndWhole = Utils.tupleOfLexing e.pexp_loc.loc_end in + let lineEndWhole, colEndWhole = Pos.ofLexing e.pexp_loc.loc_end in if length > 0 && colEndWhole > length then ( emitter |> emitJsxClose ~debug ~lid:lident.txt @@ -329,8 +329,7 @@ let command ~debug ~emitter ~path = (me : Parsetree.module_expr) = match me.pmod_desc with | Pmod_ident {txt = lid; loc} -> - emitter - |> emitLongident ~pos:(Utils.tupleOfLexing loc.loc_start) ~lid ~debug; + emitter |> emitLongident ~pos:(Pos.ofLexing loc.loc_start) ~lid ~debug; Ast_iterator.default_iterator.module_expr iterator me | _ -> Ast_iterator.default_iterator.module_expr iterator me in @@ -338,7 +337,7 @@ let command ~debug ~emitter ~path = (mb : Parsetree.module_binding) = emitter |> emitLongident - ~pos:(Utils.tupleOfLexing mb.pmb_name.loc.loc_start) + ~pos:(Pos.ofLexing mb.pmb_name.loc.loc_start) ~lid:(Longident.Lident mb.pmb_name.txt) ~debug; Ast_iterator.default_iterator.module_binding iterator mb in @@ -346,7 +345,7 @@ let command ~debug ~emitter ~path = (md : Parsetree.module_declaration) = emitter |> emitLongident - ~pos:(Utils.tupleOfLexing md.pmd_name.loc.loc_start) + ~pos:(Pos.ofLexing md.pmd_name.loc.loc_start) ~lid:(Longident.Lident md.pmd_name.txt) ~debug; Ast_iterator.default_iterator.module_declaration iterator md in @@ -356,7 +355,7 @@ let command ~debug ~emitter ~path = | Pmty_ident {txt = lid; loc} -> emitter |> emitLongident ~upperCaseToken:Token.Type - ~pos:(Utils.tupleOfLexing loc.loc_start) + ~pos:(Pos.ofLexing loc.loc_start) ~lid ~debug; Ast_iterator.default_iterator.module_type iterator mt | _ -> Ast_iterator.default_iterator.module_type iterator mt @@ -365,7 +364,7 @@ let command ~debug ~emitter ~path = (mtd : Parsetree.module_type_declaration) = emitter |> emitLongident ~upperCaseToken:Token.Type - ~pos:(Utils.tupleOfLexing mtd.pmtd_name.loc.loc_start) + ~pos:(Pos.ofLexing mtd.pmtd_name.loc.loc_start) ~lid:(Longident.Lident mtd.pmtd_name.txt) ~debug; Ast_iterator.default_iterator.module_type_declaration iterator mtd in @@ -373,7 +372,7 @@ let command ~debug ~emitter ~path = (od : Parsetree.open_description) = emitter |> emitLongident - ~pos:(Utils.tupleOfLexing od.popen_lid.loc.loc_start) + ~pos:(Pos.ofLexing od.popen_lid.loc.loc_start) ~lid:od.popen_lid.txt ~debug; Ast_iterator.default_iterator.open_description iterator od in diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml index 5434ae43d..2febe2426 100644 --- a/analysis/src/Utils.ml +++ b/analysis/src/Utils.ml @@ -51,15 +51,12 @@ let dedup items = Hashtbl.add m a (); true)) -let tupleOfLexing {Lexing.pos_lnum; pos_cnum; pos_bol} = - (pos_lnum - 1, pos_cnum - pos_bol) - (** Check if pos is within the location, but be fuzzy about when the location ends. If it's within 5 lines, go with it. *) -let locationContainsFuzzy {Location.loc_start; loc_end} (l, c) = - tupleOfLexing loc_start <= (l, c) && tupleOfLexing loc_end >= (l - 5, c) +let locationContainsFuzzy loc (l, c) = + Loc.start loc <= (l, c) && Loc.end_ loc >= (l - 5, c) let filterMap f = let rec aux accu = function @@ -70,5 +67,4 @@ let filterMap f = aux [] let dumpPath path = Str.global_replace (Str.regexp_string "\\") "/" path - let isUncurriedInternal path = startsWith (Path.name path) "Js.Fn.arity" From acf53445d34d70e7dfd9cb5a79e1d1510dd33b2d Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 13 Apr 2022 12:50:49 +0200 Subject: [PATCH 018/135] Take label completion from the parser. --- analysis/src/Commands.ml | 81 +++++++++++++------ .../tests/src/expected/Completion.res.txt | 16 +--- 2 files changed, 60 insertions(+), 37 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index bdc6826a0..835d71ca2 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -13,7 +13,7 @@ type jsxProps = { childrenStart : (int * int) option; } -let findJsxPropCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName +let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName = let rec loop ~seen props = match props with @@ -57,6 +57,17 @@ let rec skipComment ~pos ~i ~depth str = | _ -> skipComment ~depth ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str else None +let flattenLongIdent ~jsx lid = + let rec loop acc lid = + match lid with + | Longident.Lident txt -> txt :: acc + | Ldot (lid, txt) -> + let acc = if jsx && txt = "createElement" then acc else txt :: acc in + loop acc lid + | _ -> acc + in + loop [] lid + let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = let rec extractLabelPos ~pos ~i str = if i < String.length str then @@ -76,22 +87,11 @@ let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = | _ -> None else None in - let flattenComponentName lid = - let rec loop acc lid = - match lid with - | Longident.Lident txt -> txt :: acc - | Ldot (lid, txt) -> - let acc = if txt = "createElement" then acc else txt :: acc in - loop acc lid - | _ -> acc - in - loop [] lid - in let rec processProps ~lastOffset ~lastPos ~acc args = match args with | (Asttypes.Labelled "children", {Parsetree.pexp_loc}) :: _ -> { - componentPath = flattenComponentName compName.txt; + componentPath = flattenLongIdent ~jsx:true compName.txt; props = List.rev acc; childrenStart = (if pexp_loc.loc_ghost then None else Some (Loc.start pexp_loc)); @@ -144,7 +144,38 @@ type labelled = { type label = labelled option type arg = {label : label; exp : Parsetree.expression} -let extractExpApplyArgs ~text ~eFun ~args = +let findExpApplyCompletable ~(args : arg list) ~endPos ~posBeforeCursor + ~(funName : Longident.t Location.loc) = + let funPath = flattenLongIdent ~jsx:false funName.txt in + let posAfterFunName = Loc.end_ funName.loc in + let allNames = + List.fold_right + (fun arg allLabels -> + match arg with + | {label = Some labelled} -> labelled.name :: allLabels + | {label = None} -> allLabels) + args [] + in + let rec loop args = + match args with + | {label = Some labelled; exp} :: rest -> + if + labelled.posStart <= posBeforeCursor + && posBeforeCursor < labelled.posEnd + then Some (PartialParser.Clabel (funPath, labelled.name, allNames)) + else if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None + else loop rest + | {label = None; exp} :: rest -> + if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None + else loop rest + | [] -> + if posAfterFunName <= posBeforeCursor && posBeforeCursor < endPos then + Some (PartialParser.Clabel (funPath, "", allNames)) + else None + in + loop args + +let extractExpApplyArgs ~text ~(funName : Longident.t Location.loc) ~args = let rec extractLabelPos ~pos ~i str = if i < String.length str then match str.[i] with @@ -205,7 +236,7 @@ let extractExpApplyArgs ~text ~eFun ~args = assert false) | [] -> List.rev acc in - let lastPos = Loc.end_ eFun.Parsetree.pexp_loc in + let lastPos = Loc.end_ funName.loc in let lastOffset = match PartialParser.positionToOffset text lastPos with | Some offset -> offset @@ -224,6 +255,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let line, col = posCursor in (line, max 0 col - offset + offsetNoWhite) in + let posBeforeCursor = (fst posCursor, max 0 (snd posCursor - 1)) in if Filename.check_suffix path ".res" then ( let parser = @@ -257,19 +289,17 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | None -> "None" | Some childrenPosStart -> Pos.toString childrenPosStart); let jsxCompletable = - findJsxPropCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) - ~posBeforeCursor:(fst posCursor, max 0 (snd posCursor - 1)) - ~posAfterCompName:(Loc.end_ compName.loc) + findJsxPropsCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) + ~posBeforeCursor ~posAfterCompName:(Loc.end_ compName.loc) in result := jsxCompletable | Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "|."}}, [_; _]) -> (* Pipe *) () - | Pexp_apply (eFun, args) -> - let args = extractExpApplyArgs ~text ~eFun ~args in + | Pexp_apply ({pexp_desc = Pexp_ident funName}, args) -> + let args = extractExpApplyArgs ~text ~funName ~args in if debug then - Printf.printf "Pexp_apply ...%s (%s)\n" - (Loc.toString eFun.pexp_loc) + Printf.printf "Pexp_apply ...%s (%s)\n" (Loc.toString funName.loc) (args |> List.map (fun {label; exp} -> Printf.sprintf "%s...%s" @@ -280,7 +310,12 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = ^ Pos.toString posEnd ^ "=" ^ if opt then "?" else "") (Loc.toString exp.pexp_loc)) - |> String.concat ", ") + |> String.concat ", "); + let expApplyCompletable = + findExpApplyCompletable ~funName ~args + ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor + in + result := expApplyCompletable | _ -> ()); Ast_iterator.default_iterator.expr iterator expr in diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index ce84d41a5..bf4a62322 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -740,20 +740,8 @@ Completable: Clabel([Lib, foo], "", [age]) Complete tests/src/Completion.res 67:2 posCursor:[68:2] posNoWhite:[68:1] Found expr:[67:8->69:17] Pexp_apply ...[67:8->67:15] (~age69:1->69:4=...[69:5->69:6], ~name69:9->69:13=...[69:14->69:16]) -Completable: Clabel([Lib, foo], "", []) -[{ - "label": "age", - "kind": 4, - "tags": [], - "detail": "int", - "documentation": null - }, { - "label": "name", - "kind": 4, - "tags": [], - "detail": "string", - "documentation": null - }] +Completable: Clabel([Lib, foo], "", [age, name]) +[] Complete tests/src/Completion.res 72:2 posCursor:[73:11] posNoWhite:[73:10] Found expr:[73:8->75:18] From f8b5b4850de40e65f9cb0c337c5b1f4f42636bff Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 13 Apr 2022 12:53:40 +0200 Subject: [PATCH 019/135] Remove backward parser for function calls. --- analysis/src/PartialParser.ml | 52 +---------------------------------- 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 1707ce46b..bb6503b1d 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -30,9 +30,6 @@ let rec findLineComment text offset = (* Check if the position is inside a `//` comment *) let insideLineComment text offset = findLineComment text offset <> None -let skipLineComment text offset = - match findLineComment text offset with None -> offset | Some n -> n - 1 - let rec skipWhite text i = if i < 0 then 0 else @@ -48,50 +45,6 @@ let rec startOfLident text i = startOfLident text (i - 1) | _ -> i + 1 -(* foo(... ~arg) from ~arg find foo *) -let findCallFromArgument text offset = - let none = ([], []) in - let rec loop identsSeen i = - let i = skipLineComment text i in - let i = skipWhite text i in - if i > 0 then - match text.[i] with - | '}' -> - let i1 = findBackSkippingCommentsAndStrings text '{' '}' (i - 1) 0 in - if i1 > 0 then loop identsSeen i1 else none - | ')' -> - let i1 = findBackSkippingCommentsAndStrings text '(' ')' (i - 1) 0 in - if i1 > 0 then loop identsSeen i1 else none - | ']' -> - let i1 = findBackSkippingCommentsAndStrings text '[' ']' (i - 1) 0 in - if i1 > 0 then loop identsSeen i1 else none - | '"' -> - let i1 = findBack text '"' (i - 1) in - if i1 > 0 then loop identsSeen i1 else none - | '\'' -> - let i1 = findBack text '\'' (i - 1) in - if i1 > 0 then loop identsSeen i1 else none - | '`' -> - let i1 = findBack text '`' (i - 1) in - if i1 > 0 then loop identsSeen i1 else none - | '(' -> - let i1 = skipWhite text (i - 1) in - let i0 = startOfLident text i1 in - let funLident = String.sub text i0 (i1 - i0 + 1) in - (Str.split (Str.regexp_string ".") funLident, identsSeen) - | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> - let i1 = startOfLident text i in - let ident = String.sub text i1 (i - i1 + 1) in - if i1 - 1 > 0 then - match text.[i1 - 1] with - | '~' -> loop (ident :: identsSeen) (i1 - 2) - | _ -> loop identsSeen (i1 - 1) - else none - | _ -> loop identsSeen (i - 1) - else none - in - loop [] offset - type pipe = PipeId of string | PipeArray | PipeString type completable = @@ -197,10 +150,7 @@ let findCompletable text offset = let rest = suffix i in if isLowercaseIdent rest then mkPipe (i - 2) rest else Some (mkPath rest) - | '~' -> - let labelPrefix = suffix i in - let funPath, identsSeen = findCallFromArgument text (i - 1) in - Some (Clabel (funPath, labelPrefix, identsSeen)) + | '~' -> None | '@' -> Some (Cdecorator (suffix i)) | '"' when i > 0 && text.[i - 1] = '[' -> let partialName = suffix i in From e423bbe3cdad217f1248e398e31cc57552d07c7b Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 13 Apr 2022 14:23:13 +0200 Subject: [PATCH 020/135] Add parser-base autocompletion for pipe. --- analysis/src/Commands.ml | 150 ++++++++++++------ analysis/src/NewCompletions.ml | 6 +- analysis/src/PartialParser.ml | 12 +- .../src/expected/CompletePrioritize1.res.txt | 4 +- .../src/expected/CompletePrioritize2.res.txt | 4 +- .../tests/src/expected/Completion.res.txt | 4 +- .../src/expected/RecordCompletion.res.txt | 4 +- 7 files changed, 118 insertions(+), 66 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 835d71ca2..36de8f8ac 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -57,7 +57,7 @@ let rec skipComment ~pos ~i ~depth str = | _ -> skipComment ~depth ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str else None -let flattenLongIdent ~jsx lid = +let flattenLongIdent ?(jsx = false) lid = let rec loop acc lid = match lid with | Longident.Lident txt -> txt :: acc @@ -146,7 +146,7 @@ type arg = {label : label; exp : Parsetree.expression} let findExpApplyCompletable ~(args : arg list) ~endPos ~posBeforeCursor ~(funName : Longident.t Location.loc) = - let funPath = flattenLongIdent ~jsx:false funName.txt in + let funPath = flattenLongIdent funName.txt in let posAfterFunName = Loc.end_ funName.loc in let allNames = List.fold_right @@ -264,60 +264,108 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let found = ref false in let result = ref None in let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = - if expr.pexp_loc |> Loc.hasPos ~pos:posNoWhite then ( + let setFound () = found := true; if debug then Printf.printf "posCursor:[%s] posNoWhite:[%s] Found expr:%s\n" (Pos.toString posCursor) (Pos.toString posNoWhite) - (Loc.toString expr.pexp_loc); + (Loc.toString expr.pexp_loc) + in + let setPipeResult ~(lhs : Parsetree.expression) ~id = + let rec findPipe (e : Parsetree.expression) = + match e.pexp_desc with + | Pexp_constant (Pconst_string _) -> Some PartialParser.PipeString + | Pexp_array _ -> Some PartialParser.PipeArray + | Pexp_ident {txt} -> + Some (PartialParser.PipeId (flattenLongIdent txt)) + | Pexp_field (e1, {txt}) -> ( + match findPipe e1 with + | Some (PipeId path) -> Some (PipeId (path @ flattenLongIdent txt)) + | _ -> None) + | _ -> None + in + match findPipe lhs with + | Some pipe -> + result := Some (PartialParser.Cpipe (pipe, id)); + true + | None -> false + in - match expr.pexp_desc with - | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) - when Res_parsetree_viewer.isJsxExpression expr -> - let jsxProps = extractJsxProps ~text ~compName ~args in - if debug then - Printf.printf "JSX <%s:%s %s> _children:%s\n" - (jsxProps.componentPath |> String.concat ",") - (Loc.toString compName.loc) - (jsxProps.props - |> List.map (fun {name; posStart; posEnd; exp} -> - Printf.sprintf "%s[%s->%s]=...%s" name - (Pos.toString posStart) (Pos.toString posEnd) - (Loc.toString exp.pexp_loc)) - |> String.concat " ") - (match jsxProps.childrenStart with - | None -> "None" - | Some childrenPosStart -> Pos.toString childrenPosStart); - let jsxCompletable = - findJsxPropsCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) - ~posBeforeCursor ~posAfterCompName:(Loc.end_ compName.loc) - in - result := jsxCompletable - | Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "|."}}, [_; _]) -> - (* Pipe *) - () - | Pexp_apply ({pexp_desc = Pexp_ident funName}, args) -> - let args = extractExpApplyArgs ~text ~funName ~args in - if debug then - Printf.printf "Pexp_apply ...%s (%s)\n" (Loc.toString funName.loc) - (args - |> List.map (fun {label; exp} -> - Printf.sprintf "%s...%s" - (match label with - | None -> "" - | Some {name; opt; posStart; posEnd} -> - "~" ^ name ^ Pos.toString posStart ^ "->" - ^ Pos.toString posEnd ^ "=" - ^ if opt then "?" else "") - (Loc.toString exp.pexp_loc)) - |> String.concat ", "); - let expApplyCompletable = - findExpApplyCompletable ~funName ~args - ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor - in - result := expApplyCompletable - | _ -> ()); - Ast_iterator.default_iterator.expr iterator expr + match expr.pexp_desc with + | Pexp_apply + ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, + [ + (_, lhs); + (_, {pexp_desc = Pexp_extension _; pexp_loc = {loc_ghost = true}}); + ] ) + when opLoc |> Loc.hasPos ~pos:posBeforeCursor -> + (* Case foo-> when the parser adds a ghost expression to the rhs + so the apply expression does not include the cursor *) + if setPipeResult ~lhs ~id:"" then setFound () + | _ -> + if expr.pexp_loc |> Loc.hasPos ~pos:posNoWhite then ( + setFound (); + match expr.pexp_desc with + | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) + when Res_parsetree_viewer.isJsxExpression expr -> + let jsxProps = extractJsxProps ~text ~compName ~args in + if debug then + Printf.printf "JSX <%s:%s %s> _children:%s\n" + (jsxProps.componentPath |> String.concat ",") + (Loc.toString compName.loc) + (jsxProps.props + |> List.map (fun {name; posStart; posEnd; exp} -> + Printf.sprintf "%s[%s->%s]=...%s" name + (Pos.toString posStart) (Pos.toString posEnd) + (Loc.toString exp.pexp_loc)) + |> String.concat " ") + (match jsxProps.childrenStart with + | None -> "None" + | Some childrenPosStart -> Pos.toString childrenPosStart); + let jsxCompletable = + findJsxPropsCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) + ~posBeforeCursor ~posAfterCompName:(Loc.end_ compName.loc) + in + result := jsxCompletable + | Pexp_apply + ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, + [ + (_, lhs); + (_, {pexp_desc = Pexp_ident {txt = Longident.Lident id; loc}}); + ] ) + when loc |> Loc.hasPos ~pos:posBeforeCursor -> + (* Case foo->id *) + setPipeResult ~lhs ~id |> ignore + | Pexp_apply + ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, + [(_, lhs); _] ) + when Loc.end_ opLoc = posCursor -> + (* Case foo-> *) + setPipeResult ~lhs ~id:"" |> ignore + | Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "|."}}, [_; _]) -> + () + | Pexp_apply ({pexp_desc = Pexp_ident funName}, args) -> + let args = extractExpApplyArgs ~text ~funName ~args in + if debug then + Printf.printf "Pexp_apply ...%s (%s)\n" (Loc.toString funName.loc) + (args + |> List.map (fun {label; exp} -> + Printf.sprintf "%s...%s" + (match label with + | None -> "" + | Some {name; opt; posStart; posEnd} -> + "~" ^ name ^ Pos.toString posStart ^ "->" + ^ Pos.toString posEnd ^ "=" + ^ if opt then "?" else "") + (Loc.toString exp.pexp_loc)) + |> String.concat ", "); + let expApplyCompletable = + findExpApplyCompletable ~funName ~args + ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor + in + result := expApplyCompletable + | _ -> ()); + Ast_iterator.default_iterator.expr iterator expr in let {Res_driver.parsetree = structure} = parser ~filename:currentFile in let iterator = {Ast_iterator.default_iterator with expr} in diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index f7fd2b678..2c586deab 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -1020,7 +1020,7 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens | Path.Pident id when Ident.name id = "string" -> stringModulePath | _ -> ( match loop path with _ :: rest -> List.rev rest | [] -> []) in - let getLhsPath ~pipeId ~partialName = + let getLhsPath ~pipeIdPath ~partialName = let getConstr typ = match typ.Types.desc with | Tconstr (path, _, _) @@ -1053,7 +1053,7 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens | None -> None | Some (typ1, env1) -> getFields ~env:env1 ~typ:typ1 rest) in - match String.split_on_char '.' pipeId with + match pipeIdPath with | x :: fieldNames -> ( match [x] |> processDotPath ~exact:true with | ({Completion.kind = Value typ}, env) :: _ -> ( @@ -1065,7 +1065,7 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens in let lhsPath = match pipe with - | PipeId pipeId -> getLhsPath ~pipeId ~partialName + | PipeId pipeIdPath -> getLhsPath ~pipeIdPath ~partialName | PipeString -> Some (stringModulePath, partialName) | PipeArray -> Some (arrayModulePath, partialName) in diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index bb6503b1d..5af649a8c 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -45,7 +45,7 @@ let rec startOfLident text i = startOfLident text (i - 1) | _ -> i + 1 -type pipe = PipeId of string | PipeArray | PipeString +type pipe = PipeId of string list | PipeArray | PipeString type completable = | Cdecorator of string (** e.g. @module *) @@ -73,7 +73,7 @@ let completableToString = | Cpipe (pipe, s) -> "Cpipe(" ^ (match pipe with - | PipeId s -> str s + | PipeId sl -> sl |> list | PipeArray -> "PipeArray" | PipeString -> "PipeString") ^ ", " ^ str s ^ ")" @@ -103,13 +103,17 @@ let findCompletable text offset = let mkPipe off partialName = let off = skipWhite text off in let rec loop i = - if i < 0 then Some (PipeId (String.sub text 0 (i - 1))) + if i < 0 then + Some (PipeId (String.split_on_char '\n' (String.sub text 0 (i - 1)))) else match text.[i] with | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> loop (i - 1) | '"' when i == off -> Some PipeString | ']' when i == off -> Some PipeArray - | _ -> Some (PipeId (String.sub text (i + 1) (off - i))) + | _ -> + Some + (PipeId + (String.split_on_char '\n' (String.sub text (i + 1) (off - i)))) in match loop off with | None -> None diff --git a/analysis/tests/src/expected/CompletePrioritize1.res.txt b/analysis/tests/src/expected/CompletePrioritize1.res.txt index 743107aa6..bc06e0b38 100644 --- a/analysis/tests/src/expected/CompletePrioritize1.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize1.res.txt @@ -1,6 +1,6 @@ Complete tests/src/CompletePrioritize1.res 4:2 -posCursor:[5:4] posNoWhite:[5:3] Found expr:[5:2->5:4] -Completable: Cpipe(a, "") +posCursor:[5:4] posNoWhite:[5:3] Found expr:[5:1->0:-1] +Completable: Cpipe([a], "") [{ "label": "Test.add", "kind": 12, diff --git a/analysis/tests/src/expected/CompletePrioritize2.res.txt b/analysis/tests/src/expected/CompletePrioritize2.res.txt index 3dbc17d53..b2e2f71eb 100644 --- a/analysis/tests/src/expected/CompletePrioritize2.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize2.res.txt @@ -1,6 +1,6 @@ Complete tests/src/CompletePrioritize2.res 8:2 -posCursor:[9:4] posNoWhite:[9:3] Found expr:[9:2->9:4] -Completable: Cpipe(a, "") +posCursor:[9:4] posNoWhite:[9:3] Found expr:[9:1->0:-1] +Completable: Cpipe([a], "") [{ "label": "Test.add", "kind": 12, diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index bf4a62322..40ac1a0b9 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -405,7 +405,7 @@ Completable: Cpipe(PipeString, toU) Complete tests/src/Completion.res 27:2 posCursor:[28:6] posNoWhite:[28:5] Found expr:[28:1->28:6] posCursor:[28:6] posNoWhite:[28:5] Found expr:[28:5->28:6] -Completable: Cpipe(op, e) +Completable: Cpipe([op], e) [{ "label": "Belt.Option.eqU", "kind": 12, @@ -425,7 +425,7 @@ posCursor:[37:5] posNoWhite:[37:4] Found expr:[37:1->46:3] Pexp_apply ...[41:9->41:10] (...[37:1->41:8], ...[42:2->46:3]) posCursor:[37:5] posNoWhite:[37:4] Found expr:[37:1->41:8] posCursor:[37:5] posNoWhite:[37:4] Found expr:[37:3->37:5] -Completable: Cpipe(fa, "") +Completable: Cpipe([fa], "") [{ "label": "ForAuto.abc", "kind": 12, diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index 552df3356..a5ce0283b 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -1,7 +1,7 @@ Complete tests/src/RecordCompletion.res 7:3 posCursor:[8:7] posNoWhite:[8:6] Found expr:[8:1->8:7] posCursor:[8:7] posNoWhite:[8:6] Found expr:[8:6->8:7] -Completable: Cpipe(t.n, m) +Completable: Cpipe([t, n], m) [{ "label": "Js.Array2.mapi", "kind": 12, @@ -19,7 +19,7 @@ Completable: Cpipe(t.n, m) Complete tests/src/RecordCompletion.res 9:3 posCursor:[10:11] posNoWhite:[10:10] Found expr:[10:1->10:11] posCursor:[10:11] posNoWhite:[10:10] Found expr:[10:10->10:11] -Completable: Cpipe(t2.n2.n, m) +Completable: Cpipe([t2, n2, n], m) [{ "label": "Js.Array2.mapi", "kind": 12, From d8c45ea4df21df641e15d04eb69f738db7e149bd Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 13 Apr 2022 14:25:52 +0200 Subject: [PATCH 021/135] Remove backwards parser for pipe. --- analysis/src/PartialParser.ml | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 5af649a8c..f128beb2f 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -78,17 +78,6 @@ let completableToString = | PipeString -> "PipeString") ^ ", " ^ str s ^ ")" -let isLowercaseIdent id = - let rec loop i = - if i < 0 then true - else - match id.[i] with - | ('a' .. 'z' | '_') when i = 0 -> true - | ('a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_') when i > 0 -> loop (i - 1) - | _ -> false - in - loop (String.length id - 1) - let findCompletable text offset = let mkPath s = let len = String.length s in @@ -100,25 +89,6 @@ let findCompletable text offset = | [id] when String.lowercase_ascii id = id -> Cdotpath dotpath | _ -> Cdotpath dotpath in - let mkPipe off partialName = - let off = skipWhite text off in - let rec loop i = - if i < 0 then - Some (PipeId (String.split_on_char '\n' (String.sub text 0 (i - 1)))) - else - match text.[i] with - | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> loop (i - 1) - | '"' when i == off -> Some PipeString - | ']' when i == off -> Some PipeArray - | _ -> - Some - (PipeId - (String.split_on_char '\n' (String.sub text (i + 1) (off - i)))) - in - match loop off with - | None -> None - | Some lhs -> Some (Cpipe (lhs, partialName)) - in let mkObj ~off ~partialName = let off = skipWhite text off in let rec loop off path i = @@ -150,10 +120,6 @@ let findCompletable text offset = if i < 0 then Some (mkPath (suffix i)) else match text.[i] with - | '>' when i > 0 && text.[i - 1] = '-' -> - let rest = suffix i in - if isLowercaseIdent rest then mkPipe (i - 2) rest - else Some (mkPath rest) | '~' -> None | '@' -> Some (Cdecorator (suffix i)) | '"' when i > 0 && text.[i - 1] = '[' -> From 7229e8514b8c8b184217f8c829782d65f5c2d69e Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 13 Apr 2022 20:58:31 +0200 Subject: [PATCH 022/135] Parser: fix location of foo["abc"] --- analysis/src/vendor/res_outcome_printer/res_core.ml | 2 +- analysis/tests/src/expected/Completion.res.txt | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/analysis/src/vendor/res_outcome_printer/res_core.ml b/analysis/src/vendor/res_outcome_printer/res_core.ml index e5015b78f..4617da43d 100644 --- a/analysis/src/vendor/res_outcome_printer/res_core.ml +++ b/analysis/src/vendor/res_outcome_printer/res_core.ml @@ -1921,7 +1921,7 @@ and parseBracketAccess p expr startPos = let rbracket = p.prevEndPos in let e = let identLoc = mkLoc stringStart stringEnd in - let loc = mkLoc lbracket rbracket in + let loc = mkLoc startPos rbracket in Ast_helper.Exp.send ~loc expr (Location.mkloc s identLoc) in let e = parsePrimaryExpr ~operand:e p in diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 40ac1a0b9..857013ab5 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -744,7 +744,7 @@ Completable: Clabel([Lib, foo], "", [age, name]) [] Complete tests/src/Completion.res 72:2 -posCursor:[73:11] posNoWhite:[73:10] Found expr:[73:8->75:18] +posCursor:[73:11] posNoWhite:[73:10] Found expr:[73:1->75:18] Completable: Cobj([someObj], [], a) [{ "label": "age", @@ -755,7 +755,7 @@ Completable: Cobj([someObj], [], a) }] Complete tests/src/Completion.res 76:2 -posCursor:[77:22] posNoWhite:[77:21] Found expr:[77:20->80:10] +posCursor:[77:22] posNoWhite:[77:21] Found expr:[77:1->80:10] Completable: Cobj([nestedObj], [x, y], "") [{ "label": "age", @@ -772,7 +772,7 @@ Completable: Cobj([nestedObj], [x, y], "") }] Complete tests/src/Completion.res 79:2 -posCursor:[80:5] posNoWhite:[80:4] Found expr:[80:2->82:20] +posCursor:[80:5] posNoWhite:[80:4] Found expr:[80:1->82:20] Completable: Cobj([o], [], a) [{ "label": "age", @@ -783,7 +783,7 @@ Completable: Cobj([o], [], a) }] Complete tests/src/Completion.res 83:2 -posCursor:[84:15] posNoWhite:[84:14] Found expr:[84:13->101:20] +posCursor:[84:15] posNoWhite:[84:14] Found expr:[84:1->101:20] Completable: Cobj([no], [x, y], "") [{ "label": "name", @@ -847,7 +847,7 @@ Completable: Cdotpath([my]) }] Complete tests/src/Completion.res 100:3 -posCursor:[101:13] posNoWhite:[101:12] Found expr:[101:11->120:32] +posCursor:[101:13] posNoWhite:[101:12] Found expr:[101:1->120:32] Completable: Cobj([Obj, object], [], "") [{ "label": "name", From 12994399789e4dd5b8414532993cdd1a15f94410 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 13 Apr 2022 21:04:41 +0200 Subject: [PATCH 023/135] Use the parser for object completion. --- analysis/src/Commands.ml | 34 +++++++++++++++++++ analysis/src/Range.ml | 2 ++ .../tests/src/expected/Completion.res.txt | 5 +++ 3 files changed, 41 insertions(+) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 36de8f8ac..bf8b4b37f 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -364,6 +364,40 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor in result := expApplyCompletable + | Pexp_send (lhs, {txt; loc}) -> ( + (* e["txt"] + If the string for txt is not closed, it could go over several lines. + Only take the first like to represent the label *) + let txtLines = txt |> String.split_on_char '\n' in + let label = List.hd txtLines in + let labelRange = + let l, c = Loc.start loc in + ((l, c + 1), (l, c + 1 + String.length label)) + in + + let rec processLhs (lhs : Parsetree.expression) = + match lhs.pexp_desc with + | Pexp_ident id -> Some (flattenLongIdent id.txt, []) + | Pexp_send (e1, {txt}) -> ( + match processLhs e1 with + | None -> None + | Some (idPath, nestedPath) -> Some (idPath, nestedPath @ [txt]) + ) + | _ -> None + in + + if debug then + Printf.printf "XXX Pexp_send %s%s e:%s\n" label + (Range.toString labelRange) + (Loc.toString lhs.pexp_loc); + if + labelRange |> Range.hasPos ~pos:posBeforeCursor + || (label = "" && posCursor = fst labelRange) + then + match processLhs lhs with + | Some (idPath, nestedPath) -> + result := Some (PartialParser.Cobj (idPath, nestedPath, label)) + | None -> ()) | _ -> ()); Ast_iterator.default_iterator.expr iterator expr in diff --git a/analysis/src/Range.ml b/analysis/src/Range.ml index cdc2e0a41..a74349075 100644 --- a/analysis/src/Range.ml +++ b/analysis/src/Range.ml @@ -2,3 +2,5 @@ type t = Pos.t * Pos.t let toString ((posStart, posEnd) : t) = Printf.sprintf "[%s->%s]" (Pos.toString posStart) (Pos.toString posEnd) + +let hasPos ~pos ((posStart, posEnd) : t) = posStart <= pos && pos < posEnd diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 857013ab5..20875212b 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -745,6 +745,7 @@ Completable: Clabel([Lib, foo], "", [age, name]) Complete tests/src/Completion.res 72:2 posCursor:[73:11] posNoWhite:[73:10] Found expr:[73:1->75:18] +XXX Pexp_send a[73:10->73:11] e:[73:1->73:8] Completable: Cobj([someObj], [], a) [{ "label": "age", @@ -756,6 +757,7 @@ Completable: Cobj([someObj], [], a) Complete tests/src/Completion.res 76:2 posCursor:[77:22] posNoWhite:[77:21] Found expr:[77:1->80:10] +XXX Pexp_send [77:22->77:22] e:[77:1->77:20] Completable: Cobj([nestedObj], [x, y], "") [{ "label": "age", @@ -773,6 +775,7 @@ Completable: Cobj([nestedObj], [x, y], "") Complete tests/src/Completion.res 79:2 posCursor:[80:5] posNoWhite:[80:4] Found expr:[80:1->82:20] +XXX Pexp_send a[80:4->80:5] e:[80:1->80:2] Completable: Cobj([o], [], a) [{ "label": "age", @@ -784,6 +787,7 @@ Completable: Cobj([o], [], a) Complete tests/src/Completion.res 83:2 posCursor:[84:15] posNoWhite:[84:14] Found expr:[84:1->101:20] +XXX Pexp_send [84:15->84:15] e:[84:1->84:13] Completable: Cobj([no], [x, y], "") [{ "label": "name", @@ -848,6 +852,7 @@ Completable: Cdotpath([my]) Complete tests/src/Completion.res 100:3 posCursor:[101:13] posNoWhite:[101:12] Found expr:[101:1->120:32] +XXX Pexp_send [101:13->101:13] e:[101:1->101:11] Completable: Cobj([Obj, object], [], "") [{ "label": "name", From 76269e57bceb38c61e99d2e4094852b140c74772 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 13 Apr 2022 21:05:56 +0200 Subject: [PATCH 024/135] Remove backwards parser for objects. --- analysis/src/PartialParser.ml | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index f128beb2f..5c03549a7 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -89,31 +89,6 @@ let findCompletable text offset = | [id] when String.lowercase_ascii id = id -> Cdotpath dotpath | _ -> Cdotpath dotpath in - let mkObj ~off ~partialName = - let off = skipWhite text off in - let rec loop off path i = - if i < 0 then - let id = String.sub text 0 (i - 1) in - Some ([], [id]) - else - match text.[i] with - | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' | '.' -> - loop off path (i - 1) - | ']' when i > 1 && text.[i - 1] = '"' -> - let i0 = i - 2 in - let i1 = startOfLident text i0 in - let ident = String.sub text i1 (i0 - i1 + 1) in - if ident <> "" && i1 > 1 && text.[i1 - 1] = '"' && text.[i1 - 2] = '[' - then loop (off - i + i1 - 3) (ident :: path) (i1 - 3) - else None - | _ -> - let id = String.sub text (i + 1) (off - i) in - Some (path, Str.split (Str.regexp_string ".") id) - in - match loop off [] off with - | None -> None - | Some (path, lhs) -> Some (Cobj (lhs, path, partialName)) - in let suffix i = String.sub text (i + 1) (offset - (i + 1)) in let rec loop i = @@ -122,9 +97,6 @@ let findCompletable text offset = match text.[i] with | '~' -> None | '@' -> Some (Cdecorator (suffix i)) - | '"' when i > 0 && text.[i - 1] = '[' -> - let partialName = suffix i in - mkObj ~off:(i - 2) ~partialName | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> loop (i - 1) | ' ' when i = offset - 1 -> (* autocomplete with no id *) From 27cb4a23045243a48136bd45f44154c96808534c Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 08:32:09 +0200 Subject: [PATCH 025/135] Use parser for common paths. --- analysis/src/Commands.ml | 54 +++++++++++- .../vendor/res_outcome_printer/res_core.ml | 2 +- analysis/tests/src/Completion.res | 6 ++ .../tests/src/expected/Completion.res.txt | 82 ++++++++++++++++++- analysis/tests/src/expected/Cross.res.txt | 1 + analysis/tests/src/expected/Div.res.txt | 1 + analysis/tests/src/expected/Jsx.res.txt | 19 +++++ .../src/expected/RecordCompletion.res.txt | 2 + 8 files changed, 161 insertions(+), 6 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index bf8b4b37f..334e91c66 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -62,9 +62,13 @@ let flattenLongIdent ?(jsx = false) lid = match lid with | Longident.Lident txt -> txt :: acc | Ldot (lid, txt) -> - let acc = if jsx && txt = "createElement" then acc else txt :: acc in + let acc = + if jsx && txt = "createElement" then acc + else if txt = "$" then "" :: acc + else txt :: acc + in loop acc lid - | _ -> acc + | Lapply _ -> acc in loop [] lid @@ -306,6 +310,41 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = if expr.pexp_loc |> Loc.hasPos ~pos:posNoWhite then ( setFound (); match expr.pexp_desc with + | Pexp_ident id -> + if debug then + Printf.printf "Pexp_ident %s:%s\n" + (flattenLongIdent id.txt |> String.concat ".") + (Loc.toString id.loc); + if id.loc |> Loc.hasPos ~pos:posBeforeCursor then + if !result = None then + result := + Some (PartialParser.Cdotpath (flattenLongIdent id.txt)) + | Pexp_field (e, fieldName) -> ( + if debug then + Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) + (flattenLongIdent fieldName.txt |> String.concat ".") + (Loc.toString fieldName.loc); + let rec digLhs (lhs : Parsetree.expression) = + match lhs.pexp_desc with + | Pexp_ident id -> Some (flattenLongIdent id.txt) + | Pexp_field (lhs, id) -> ( + match digLhs lhs with + | Some path -> Some (path @ flattenLongIdent id.txt) + | None -> None) + | _ -> None + in + if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then + match digLhs e with + | Some path -> + if !result = None then + result := Some (PartialParser.Cdotpath path) + | None -> () + else if Loc.end_ e.pexp_loc = posBeforeCursor then + match digLhs e with + | Some path -> + if !result = None then + result := Some (PartialParser.Cdotpath (path @ [""])) + | None -> ()) | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) when Res_parsetree_viewer.isJsxExpression expr -> let jsxProps = extractJsxProps ~text ~compName ~args in @@ -326,7 +365,14 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = findJsxPropsCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor ~posAfterCompName:(Loc.end_ compName.loc) in - result := jsxCompletable + if jsxCompletable <> None then result := jsxCompletable + else if + compName.loc |> Loc.hasPos ~pos:posBeforeCursor && !result = None + then + result := + Some + (PartialParser.Cdotpath + (flattenLongIdent ~jsx:true compName.txt)) | Pexp_apply ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, [ @@ -363,7 +409,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = findExpApplyCompletable ~funName ~args ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor in - result := expApplyCompletable + if expApplyCompletable <> None then result := expApplyCompletable | Pexp_send (lhs, {txt; loc}) -> ( (* e["txt"] If the string for txt is not closed, it could go over several lines. diff --git a/analysis/src/vendor/res_outcome_printer/res_core.ml b/analysis/src/vendor/res_outcome_printer/res_core.ml index 4617da43d..0750c6a00 100644 --- a/analysis/src/vendor/res_outcome_printer/res_core.ml +++ b/analysis/src/vendor/res_outcome_printer/res_core.ml @@ -705,7 +705,7 @@ let parseModuleLongIdentTail ~lowercase p startPos ident = end | t -> Parser.err p (Diagnostics.uident t); - Location.mkloc acc (mkLoc startPos p.prevEndPos) + Location.mkloc (Longident.Ldot (acc, "$")) (mkLoc startPos p.prevEndPos) in loop p ident diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index b9316c31a..2f58d7cb5 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -125,3 +125,9 @@ let foo = { exception MyOtherException // ^com 1:9] +Pexp_ident MyList.m:[1:1->1:9] Completable: Cdotpath([MyList, m]) [{ "label": "mapReverse", @@ -71,6 +72,7 @@ Completable: Cdotpath([MyList, m]) Complete tests/src/Completion.res 1:2 posCursor:[2:7] posNoWhite:[2:6] Found expr:[2:1->6:6] +Pexp_ident Array.:[2:1->6:6] Completable: Cdotpath([Array, ""]) [{ "label": "Floatarray", @@ -292,6 +294,7 @@ Completable: Cdotpath([Array, ""]) Complete tests/src/Completion.res 2:2 posCursor:[3:8] posNoWhite:[3:7] Found expr:[3:1->3:8] +Pexp_ident Array.m:[3:1->3:8] Completable: Cdotpath([Array, m]) [{ "label": "mapi", @@ -345,6 +348,7 @@ Completable: Cdotpath([Array, m]) Complete tests/src/Completion.res 12:2 posCursor:[13:15] posNoWhite:[13:14] Found expr:[13:10->13:15] +Pexp_ident Dep.c:[13:10->13:15] Completable: Cdotpath([Dep, c]) [{ "label": "customDouble", @@ -375,6 +379,7 @@ Completable: Clabel([Lib, foo], "", []) Complete tests/src/Completion.res 21:2 posCursor:[22:11] posNoWhite:[22:10] Found expr:[22:1->22:11] posCursor:[22:11] posNoWhite:[22:10] Found expr:[22:10->22:11] +Pexp_ident m:[22:10->22:11] Completable: Cpipe(PipeArray, m) [{ "label": "Js.Array2.mapi", @@ -393,6 +398,7 @@ Completable: Cpipe(PipeArray, m) Complete tests/src/Completion.res 23:2 posCursor:[24:11] posNoWhite:[24:10] Found expr:[24:1->24:11] posCursor:[24:11] posNoWhite:[24:10] Found expr:[24:8->24:11] +Pexp_ident toU:[24:8->24:11] Completable: Cpipe(PipeString, toU) [{ "label": "Js.String2.toUpperCase", @@ -405,6 +411,7 @@ Completable: Cpipe(PipeString, toU) Complete tests/src/Completion.res 27:2 posCursor:[28:6] posNoWhite:[28:5] Found expr:[28:1->28:6] posCursor:[28:6] posNoWhite:[28:5] Found expr:[28:5->28:6] +Pexp_ident e:[28:5->28:6] Completable: Cpipe([op], e) [{ "label": "Belt.Option.eqU", @@ -425,6 +432,7 @@ posCursor:[37:5] posNoWhite:[37:4] Found expr:[37:1->46:3] Pexp_apply ...[41:9->41:10] (...[37:1->41:8], ...[42:2->46:3]) posCursor:[37:5] posNoWhite:[37:4] Found expr:[37:1->41:8] posCursor:[37:5] posNoWhite:[37:4] Found expr:[37:3->37:5] +Pexp_ident |.:[37:3->37:5] Completable: Cpipe([fa], "") [{ "label": "ForAuto.abc", @@ -443,6 +451,7 @@ Completable: Cpipe([fa], "") Complete tests/src/Completion.res 38:2 posCursor:[39:19] posNoWhite:[39:18] Found expr:[39:1->39:19] posCursor:[39:19] posNoWhite:[39:18] Found expr:[39:10->39:19] +Pexp_ident Js.Dict.u:[39:10->39:19] Completable: Cdotpath([Js, Dict, u]) [{ "label": "unsafeGet", @@ -462,6 +471,7 @@ Complete tests/src/Completion.res 50:2 posCursor:[51:28] posNoWhite:[51:27] Found expr:[51:13->51:28] JSX 51:19] second[51:20->51:26]=...[51:27->51:28]> _children:None posCursor:[51:28] posNoWhite:[51:27] Found expr:[51:27->51:28] +Pexp_ident z:[51:27->51:28] Completable: Cdotpath([z]) [{ "label": "zzz", @@ -475,6 +485,7 @@ Complete tests/src/Completion.res 52:2 posCursor:[53:21] posNoWhite:[53:20] Found expr:[53:13->53:21] JSX 53:19] z[53:20->53:21]=...[53:20->53:21]> _children:None posCursor:[53:21] posNoWhite:[53:20] Found expr:[53:20->53:21] +Pexp_ident z:[53:20->53:21] Completable: Cjsx([O, Comp], z, []) [{ "label": "zoo", @@ -675,6 +686,41 @@ DocumentSymbol tests/src/Completion.res "name": "MyOtherException", "kind": 9, "location": {"uri": "Completion.res", "range": {"start": {"line": 124, "character": 0}, "end": {"line": 124, "character": 26}}} +}, +{ + "name": "aa", + "kind": 26, + "location": {"uri": "Completion.res", "range": {"start": {"line": 128, "character": 0}, "end": {"line": 128, "character": 29}}} +}, +{ + "name": "x", + "kind": 7, + "location": {"uri": "Completion.res", "range": {"start": {"line": 128, "character": 10}, "end": {"line": 128, "character": 15}}} +}, +{ + "name": "name", + "kind": 7, + "location": {"uri": "Completion.res", "range": {"start": {"line": 128, "character": 17}, "end": {"line": 128, "character": 28}}} +}, +{ + "name": "bb", + "kind": 26, + "location": {"uri": "Completion.res", "range": {"start": {"line": 129, "character": 0}, "end": {"line": 129, "character": 24}}} +}, +{ + "name": "aa", + "kind": 7, + "location": {"uri": "Completion.res", "range": {"start": {"line": 129, "character": 11}, "end": {"line": 129, "character": 16}}} +}, +{ + "name": "w", + "kind": 7, + "location": {"uri": "Completion.res", "range": {"start": {"line": 129, "character": 18}, "end": {"line": 129, "character": 23}}} +}, +{ + "name": "q", + "kind": 13, + "location": {"uri": "Completion.res", "range": {"start": {"line": 130, "character": 0}, "end": {"line": 130, "character": 23}}} } ] @@ -805,6 +851,7 @@ Completable: Cobj([no], [x, y], "") Complete tests/src/Completion.res 88:3 posCursor:[89:3] posNoWhite:[89:2] Found expr:[89:1->93:3] +Pexp_field [89:1->89:2] _:[93:0->93:3] Completable: Cdotpath([r, ""]) [{ "label": "x", @@ -822,6 +869,7 @@ Completable: Cdotpath([r, ""]) Complete tests/src/Completion.res 90:3 posCursor:[91:19] posNoWhite:[91:18] Found expr:[91:1->93:3] +Pexp_field [91:1->91:18] _:[93:0->93:3] Completable: Cdotpath([Obj, Rec, recordVal, ""]) [{ "label": "xx", @@ -841,6 +889,7 @@ Complete tests/src/Completion.res 96:3 posCursor:[97:3] posNoWhite:[97:2] Found expr:[96:11->99:1] posCursor:[97:3] posNoWhite:[97:2] Found expr:[97:1->98:5] posCursor:[97:3] posNoWhite:[97:2] Found expr:[97:1->97:3] +Pexp_ident my:[97:1->97:3] Completable: Cdotpath([my]) [{ "label": "myAmazingFunction", @@ -870,8 +919,9 @@ Completable: Cobj([Obj, object], [], "") Complete tests/src/Completion.res 125:3 posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] -JSX 126:4] > _children:None +JSX 126:4] > _children:None posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] +Pexp_ident O..createElement:[126:2->126:4] Completable: Cdotpath([O, ""]) [{ "label": "Comp", @@ -881,3 +931,33 @@ Completable: Cdotpath([O, ""]) "documentation": null }] +Complete tests/src/Completion.res 130:3 +posCursor:[131:6] posNoWhite:[131:5] Found expr:[131:1->133:0] +Pexp_field [131:1->131:5] _:[133:0->133:0] +Completable: Cdotpath([q, aa, ""]) +[{ + "label": "x", + "kind": 5, + "tags": [], + "detail": "x: int\n\ntype aa = {x: int, name: string}", + "documentation": null + }, { + "label": "name", + "kind": 5, + "tags": [], + "detail": "name: string\n\ntype aa = {x: int, name: string}", + "documentation": null + }] + +Complete tests/src/Completion.res 131:3 +posCursor:[132:7] posNoWhite:[132:6] Found expr:[132:1->132:7] +Pexp_field [132:1->132:5] n:[132:6->132:7] +Completable: Cdotpath([q, aa]) +[{ + "label": "aa", + "kind": 5, + "tags": [], + "detail": "aa: aa\n\ntype bb = {aa: aa, w: int}", + "documentation": null + }] + diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index 056ffc03f..56d28facd 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -95,6 +95,7 @@ TypeDefinition tests/src/Cross.res 37:37 Complete tests/src/Cross.res 39:2 posCursor:[40:26] posNoWhite:[40:25] Found expr:[40:1->40:26] +Pexp_ident DefinitionWithInterface.a:[40:1->40:26] Completable: Cdotpath([DefinitionWithInterface, a]) [] diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index ce62f4a52..ed84c241b 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -5,6 +5,7 @@ Complete tests/src/Div.res 3:3 posCursor:[4:15] posNoWhite:[4:14] Found expr:[4:2->4:15] JSX 4:5] dangerous[4:6->4:15]=...[4:6->4:15]> _children:None posCursor:[4:15] posNoWhite:[4:14] Found expr:[4:6->4:15] +Pexp_ident dangerous:[4:6->4:15] Completable: Cjsx([div], dangerous, []) [{ "label": "dangerouslySetInnerHTML", diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 68fa2d8a4..cd6f90b16 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -5,6 +5,7 @@ Complete tests/src/Jsx.res 7:2 posCursor:[8:14] posNoWhite:[8:12] Found expr:[8:2->8:13] JSX 8:3] second[8:4->8:10]=...[8:11->8:13]> _children:None posCursor:[8:14] posNoWhite:[8:12] Found expr:[8:11->8:13] +Pexp_ident fi:[8:11->8:13] Completable: Cjsx([M], "", [second]) [{ "label": "first", @@ -30,6 +31,7 @@ Complete tests/src/Jsx.res 9:2 posCursor:[10:18] posNoWhite:[10:17] Found expr:[10:2->10:18] JSX 10:3] second[10:4->10:10]=...[10:11->10:16] f[10:17->10:18]=...[10:17->10:18]> _children:None posCursor:[10:18] posNoWhite:[10:17] Found expr:[10:17->10:18] +Pexp_ident f:[10:17->10:18] Completable: Cjsx([M], f, [second]) [{ "label": "first", @@ -49,6 +51,7 @@ Complete tests/src/Jsx.res 11:2 posCursor:[12:12] posNoWhite:[12:10] Found expr:[12:10->12:11] JSX 12:11] > _children:None posCursor:[12:12] posNoWhite:[12:10] Found expr:[12:10->12:11] +Pexp_ident M.createElement:[12:10->12:11] Completable: Cjsx([M], "", []) [{ "label": "second", @@ -80,6 +83,7 @@ Complete tests/src/Jsx.res 18:2 posCursor:[19:17] posNoWhite:[19:16] Found expr:[19:2->19:17] JSX 19:3] prop[19:4->19:8]=...[19:10->19:14] k[19:16->19:17]=...[19:16->19:17]> _children:None posCursor:[19:17] posNoWhite:[19:16] Found expr:[19:16->19:17] +Pexp_ident k:[19:16->19:17] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -93,6 +97,7 @@ Complete tests/src/Jsx.res 20:2 posCursor:[21:15] posNoWhite:[21:14] Found expr:[21:2->21:15] JSX 21:3] prop[21:4->21:8]=...[21:9->21:13] k[21:14->21:15]=...[21:14->21:15]> _children:None posCursor:[21:15] posNoWhite:[21:14] Found expr:[21:14->21:15] +Pexp_ident k:[21:14->21:15] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -106,6 +111,7 @@ Complete tests/src/Jsx.res 22:2 posCursor:[23:19] posNoWhite:[23:18] Found expr:[23:2->23:19] JSX 23:3] prop[23:4->23:8]=...[23:9->23:17] k[23:18->23:19]=...[23:18->23:19]> _children:None posCursor:[23:19] posNoWhite:[23:18] Found expr:[23:18->23:19] +Pexp_ident k:[23:18->23:19] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -119,6 +125,7 @@ Complete tests/src/Jsx.res 24:2 posCursor:[25:22] posNoWhite:[25:21] Found expr:[25:2->25:22] JSX 25:3] prop[25:4->25:8]=...[25:9->25:20] k[25:21->25:22]=...[25:21->25:22]> _children:None posCursor:[25:22] posNoWhite:[25:21] Found expr:[25:21->25:22] +Pexp_ident k:[25:21->25:22] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -132,6 +139,7 @@ Complete tests/src/Jsx.res 26:2 posCursor:[27:16] posNoWhite:[27:15] Found expr:[27:2->27:16] JSX 27:3] prop[27:4->27:8]=...[27:10->27:14] k[27:15->27:16]=...[27:15->27:16]> _children:None posCursor:[27:16] posNoWhite:[27:15] Found expr:[27:15->27:16] +Pexp_ident k:[27:15->27:16] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -145,6 +153,7 @@ Complete tests/src/Jsx.res 28:2 posCursor:[29:14] posNoWhite:[29:13] Found expr:[29:2->29:14] JSX 29:3] prop[29:4->29:8]=...[29:9->29:12] k[29:13->29:14]=...[29:13->29:14]> _children:None posCursor:[29:14] posNoWhite:[29:13] Found expr:[29:13->29:14] +Pexp_ident k:[29:13->29:14] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -158,6 +167,7 @@ Complete tests/src/Jsx.res 30:2 posCursor:[31:15] posNoWhite:[31:14] Found expr:[31:2->31:15] JSX 31:3] prop[31:4->31:8]=...[31:9->31:13] k[31:14->31:15]=...[31:14->31:15]> _children:None posCursor:[31:15] posNoWhite:[31:14] Found expr:[31:14->31:15] +Pexp_ident k:[31:14->31:15] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -171,6 +181,7 @@ Complete tests/src/Jsx.res 32:2 posCursor:[33:16] posNoWhite:[33:15] Found expr:[33:2->33:16] JSX 33:3] prop[33:4->33:8]=...[33:9->33:14] k[33:15->33:16]=...[33:15->33:16]> _children:None posCursor:[33:16] posNoWhite:[33:15] Found expr:[33:15->33:16] +Pexp_ident k:[33:15->33:16] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -184,6 +195,7 @@ Complete tests/src/Jsx.res 34:2 posCursor:[35:14] posNoWhite:[35:13] Found expr:[35:2->35:14] JSX 35:3] prop[35:4->35:8]=...[35:9->35:12] k[35:13->35:14]=...[35:13->35:14]> _children:None posCursor:[35:14] posNoWhite:[35:13] Found expr:[35:13->35:14] +Pexp_ident k:[35:13->35:14] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -197,6 +209,7 @@ Complete tests/src/Jsx.res 36:2 posCursor:[37:25] posNoWhite:[37:24] Found expr:[37:2->37:25] JSX 37:3] prop[37:4->37:8]=...[37:9->37:23] k[37:24->37:25]=...[37:24->37:25]> _children:None posCursor:[37:25] posNoWhite:[37:24] Found expr:[37:24->37:25] +Pexp_ident k:[37:24->37:25] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -210,6 +223,7 @@ Complete tests/src/Jsx.res 38:2 posCursor:[39:36] posNoWhite:[39:35] Found expr:[39:2->39:36] JSX 39:3] prop[39:4->39:8]=...[39:9->39:34] k[39:35->39:36]=...[39:35->39:36]> _children:None posCursor:[39:36] posNoWhite:[39:35] Found expr:[39:35->39:36] +Pexp_ident k:[39:35->39:36] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -223,6 +237,7 @@ Complete tests/src/Jsx.res 40:2 posCursor:[41:23] posNoWhite:[41:22] Found expr:[41:2->41:23] JSX 41:3] prop[41:4->41:8]=...[41:9->41:21] k[41:22->41:23]=...[41:22->41:23]> _children:None posCursor:[41:23] posNoWhite:[41:22] Found expr:[41:22->41:23] +Pexp_ident k:[41:22->41:23] Completable: Cjsx([M], k, [prop]) [{ "label": "key", @@ -239,6 +254,7 @@ Complete tests/src/Jsx.res 52:2 posCursor:[53:8] posNoWhite:[53:7] Found expr:[53:2->53:8] JSX 53:5] al[53:6->53:8]=...[53:6->53:8]> _children:None posCursor:[53:8] posNoWhite:[53:7] Found expr:[53:6->53:8] +Pexp_ident al:[53:6->53:8] Completable: Cjsx([Ext], al, []) [{ "label": "align", @@ -252,6 +268,7 @@ Complete tests/src/Jsx.res 54:2 posCursor:[55:10] posNoWhite:[55:8] Found expr:[55:2->55:9] JSX 55:3] first[55:4->55:9]=...[55:4->55:9]> _children:None posCursor:[55:10] posNoWhite:[55:8] Found expr:[55:4->55:9] +Pexp_ident first:[55:4->55:9] Completable: Cjsx([M], "", [first]) [{ "label": "second", @@ -277,6 +294,7 @@ Complete tests/src/Jsx.res 56:2 posCursor:[57:14] posNoWhite:[57:13] Found expr:[57:2->57:14] JSX 57:3] first[57:4->57:9]=...[57:10->57:12] k[57:13->57:14]=...[57:13->57:14]> _children:None posCursor:[57:14] posNoWhite:[57:13] Found expr:[57:13->57:14] +Pexp_ident k:[57:13->57:14] Completable: Cjsx([M], k, [first]) [{ "label": "key", @@ -290,6 +308,7 @@ Complete tests/src/Jsx.res 58:2 posCursor:[59:21] posNoWhite:[59:20] Found expr:[59:2->59:21] JSX 59:3] first[59:4->59:9]=...[59:17->59:19] k[59:20->59:21]=...[59:20->59:21]> _children:None posCursor:[59:21] posNoWhite:[59:20] Found expr:[59:20->59:21] +Pexp_ident k:[59:20->59:21] Completable: Cjsx([M], k, [first]) [{ "label": "key", diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index a5ce0283b..fbfb94295 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -1,6 +1,7 @@ Complete tests/src/RecordCompletion.res 7:3 posCursor:[8:7] posNoWhite:[8:6] Found expr:[8:1->8:7] posCursor:[8:7] posNoWhite:[8:6] Found expr:[8:6->8:7] +Pexp_ident m:[8:6->8:7] Completable: Cpipe([t, n], m) [{ "label": "Js.Array2.mapi", @@ -19,6 +20,7 @@ Completable: Cpipe([t, n], m) Complete tests/src/RecordCompletion.res 9:3 posCursor:[10:11] posNoWhite:[10:10] Found expr:[10:1->10:11] posCursor:[10:11] posNoWhite:[10:10] Found expr:[10:10->10:11] +Pexp_ident m:[10:10->10:11] Completable: Cpipe([t2, n2, n], m) [{ "label": "Js.Array2.mapi", From 49b85aed9c3576ed88818ef53b89435fc735cdd8 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 08:35:07 +0200 Subject: [PATCH 026/135] Remove backwards parsing of common paths. --- analysis/src/PartialParser.ml | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 5c03549a7..330c56f25 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -79,29 +79,17 @@ let completableToString = ^ ", " ^ str s ^ ")" let findCompletable text offset = - let mkPath s = - let len = String.length s in - let dotpath = Str.split (Str.regexp_string ".") s in - let dotpath = - match s.[len - 1] with '.' -> dotpath @ [""] | _ -> dotpath - in - match dotpath with - | [id] when String.lowercase_ascii id = id -> Cdotpath dotpath - | _ -> Cdotpath dotpath - in - let suffix i = String.sub text (i + 1) (offset - (i + 1)) in let rec loop i = - if i < 0 then Some (mkPath (suffix i)) + if i < 0 then None else match text.[i] with - | '~' -> None | '@' -> Some (Cdecorator (suffix i)) | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> loop (i - 1) | ' ' when i = offset - 1 -> (* autocomplete with no id *) None - | _ -> if i = offset - 1 then None else Some (mkPath (suffix i)) + | _ -> if i = offset - 1 then None else None in if offset > String.length text || offset = 0 then None else loop (offset - 1) From 88bf40b63e544518bc9d4016b70cb27f4f351fda Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 09:13:58 +0200 Subject: [PATCH 027/135] Don't suggest a prop name if it's already used after the cursor. --- analysis/src/Commands.ml | 15 ++++++--- .../tests/src/expected/Completion.res.txt | 2 +- analysis/tests/src/expected/Div.res.txt | 2 +- analysis/tests/src/expected/Jsx.res.txt | 32 +++++++++---------- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 334e91c66..e9c98c6b0 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -15,13 +15,18 @@ type jsxProps = { let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName = - let rec loop ~seen props = + let allLabels = + List.fold_right + (fun prop allLabels -> prop.name :: allLabels) + jsxProps.props [] + in + let rec loop props = match props with | prop :: rest -> if prop.posStart <= posBeforeCursor && posBeforeCursor < prop.posEnd then - Some (PartialParser.Cjsx (jsxProps.componentPath, prop.name, seen)) + Some (PartialParser.Cjsx (jsxProps.componentPath, prop.name, allLabels)) else if prop.exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None - else loop ~seen:(seen @ [prop.name]) rest + else loop rest | [] -> let beforeChildrenStart = match jsxProps.childrenStart with @@ -30,10 +35,10 @@ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName in let afterCompName = posBeforeCursor >= posAfterCompName in if afterCompName && beforeChildrenStart then - Some (PartialParser.Cjsx (jsxProps.componentPath, "", seen)) + Some (PartialParser.Cjsx (jsxProps.componentPath, "", allLabels)) else None in - loop ~seen:[] jsxProps.props + loop jsxProps.props let rec skipLineComment ~pos ~i str = if i < String.length str then diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 57e717620..8e2dcc41b 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -486,7 +486,7 @@ posCursor:[53:21] posNoWhite:[53:20] Found expr:[53:13->53:21] JSX 53:19] z[53:20->53:21]=...[53:20->53:21]> _children:None posCursor:[53:21] posNoWhite:[53:20] Found expr:[53:20->53:21] Pexp_ident z:[53:20->53:21] -Completable: Cjsx([O, Comp], z, []) +Completable: Cjsx([O, Comp], z, [z]) [{ "label": "zoo", "kind": 4, diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index ed84c241b..cde6ff78f 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -6,7 +6,7 @@ posCursor:[4:15] posNoWhite:[4:14] Found expr:[4:2->4:15] JSX 4:5] dangerous[4:6->4:15]=...[4:6->4:15]> _children:None posCursor:[4:15] posNoWhite:[4:14] Found expr:[4:6->4:15] Pexp_ident dangerous:[4:6->4:15] -Completable: Cjsx([div], dangerous, []) +Completable: Cjsx([div], dangerous, [dangerous]) [{ "label": "dangerouslySetInnerHTML", "kind": 4, diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index cd6f90b16..b5a2a9d51 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -32,7 +32,7 @@ posCursor:[10:18] posNoWhite:[10:17] Found expr:[10:2->10:18] JSX 10:3] second[10:4->10:10]=...[10:11->10:16] f[10:17->10:18]=...[10:17->10:18]> _children:None posCursor:[10:18] posNoWhite:[10:17] Found expr:[10:17->10:18] Pexp_ident f:[10:17->10:18] -Completable: Cjsx([M], f, [second]) +Completable: Cjsx([M], f, [second, f]) [{ "label": "first", "kind": 4, @@ -84,7 +84,7 @@ posCursor:[19:17] posNoWhite:[19:16] Found expr:[19:2->19:17] JSX 19:3] prop[19:4->19:8]=...[19:10->19:14] k[19:16->19:17]=...[19:16->19:17]> _children:None posCursor:[19:17] posNoWhite:[19:16] Found expr:[19:16->19:17] Pexp_ident k:[19:16->19:17] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -98,7 +98,7 @@ posCursor:[21:15] posNoWhite:[21:14] Found expr:[21:2->21:15] JSX 21:3] prop[21:4->21:8]=...[21:9->21:13] k[21:14->21:15]=...[21:14->21:15]> _children:None posCursor:[21:15] posNoWhite:[21:14] Found expr:[21:14->21:15] Pexp_ident k:[21:14->21:15] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -112,7 +112,7 @@ posCursor:[23:19] posNoWhite:[23:18] Found expr:[23:2->23:19] JSX 23:3] prop[23:4->23:8]=...[23:9->23:17] k[23:18->23:19]=...[23:18->23:19]> _children:None posCursor:[23:19] posNoWhite:[23:18] Found expr:[23:18->23:19] Pexp_ident k:[23:18->23:19] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -126,7 +126,7 @@ posCursor:[25:22] posNoWhite:[25:21] Found expr:[25:2->25:22] JSX 25:3] prop[25:4->25:8]=...[25:9->25:20] k[25:21->25:22]=...[25:21->25:22]> _children:None posCursor:[25:22] posNoWhite:[25:21] Found expr:[25:21->25:22] Pexp_ident k:[25:21->25:22] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -140,7 +140,7 @@ posCursor:[27:16] posNoWhite:[27:15] Found expr:[27:2->27:16] JSX 27:3] prop[27:4->27:8]=...[27:10->27:14] k[27:15->27:16]=...[27:15->27:16]> _children:None posCursor:[27:16] posNoWhite:[27:15] Found expr:[27:15->27:16] Pexp_ident k:[27:15->27:16] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -154,7 +154,7 @@ posCursor:[29:14] posNoWhite:[29:13] Found expr:[29:2->29:14] JSX 29:3] prop[29:4->29:8]=...[29:9->29:12] k[29:13->29:14]=...[29:13->29:14]> _children:None posCursor:[29:14] posNoWhite:[29:13] Found expr:[29:13->29:14] Pexp_ident k:[29:13->29:14] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -168,7 +168,7 @@ posCursor:[31:15] posNoWhite:[31:14] Found expr:[31:2->31:15] JSX 31:3] prop[31:4->31:8]=...[31:9->31:13] k[31:14->31:15]=...[31:14->31:15]> _children:None posCursor:[31:15] posNoWhite:[31:14] Found expr:[31:14->31:15] Pexp_ident k:[31:14->31:15] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -182,7 +182,7 @@ posCursor:[33:16] posNoWhite:[33:15] Found expr:[33:2->33:16] JSX 33:3] prop[33:4->33:8]=...[33:9->33:14] k[33:15->33:16]=...[33:15->33:16]> _children:None posCursor:[33:16] posNoWhite:[33:15] Found expr:[33:15->33:16] Pexp_ident k:[33:15->33:16] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -196,7 +196,7 @@ posCursor:[35:14] posNoWhite:[35:13] Found expr:[35:2->35:14] JSX 35:3] prop[35:4->35:8]=...[35:9->35:12] k[35:13->35:14]=...[35:13->35:14]> _children:None posCursor:[35:14] posNoWhite:[35:13] Found expr:[35:13->35:14] Pexp_ident k:[35:13->35:14] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -210,7 +210,7 @@ posCursor:[37:25] posNoWhite:[37:24] Found expr:[37:2->37:25] JSX 37:3] prop[37:4->37:8]=...[37:9->37:23] k[37:24->37:25]=...[37:24->37:25]> _children:None posCursor:[37:25] posNoWhite:[37:24] Found expr:[37:24->37:25] Pexp_ident k:[37:24->37:25] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -224,7 +224,7 @@ posCursor:[39:36] posNoWhite:[39:35] Found expr:[39:2->39:36] JSX 39:3] prop[39:4->39:8]=...[39:9->39:34] k[39:35->39:36]=...[39:35->39:36]> _children:None posCursor:[39:36] posNoWhite:[39:35] Found expr:[39:35->39:36] Pexp_ident k:[39:35->39:36] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -238,7 +238,7 @@ posCursor:[41:23] posNoWhite:[41:22] Found expr:[41:2->41:23] JSX 41:3] prop[41:4->41:8]=...[41:9->41:21] k[41:22->41:23]=...[41:22->41:23]> _children:None posCursor:[41:23] posNoWhite:[41:22] Found expr:[41:22->41:23] Pexp_ident k:[41:22->41:23] -Completable: Cjsx([M], k, [prop]) +Completable: Cjsx([M], k, [prop, k]) [{ "label": "key", "kind": 4, @@ -255,7 +255,7 @@ posCursor:[53:8] posNoWhite:[53:7] Found expr:[53:2->53:8] JSX 53:5] al[53:6->53:8]=...[53:6->53:8]> _children:None posCursor:[53:8] posNoWhite:[53:7] Found expr:[53:6->53:8] Pexp_ident al:[53:6->53:8] -Completable: Cjsx([Ext], al, []) +Completable: Cjsx([Ext], al, [al]) [{ "label": "align", "kind": 4, @@ -295,7 +295,7 @@ posCursor:[57:14] posNoWhite:[57:13] Found expr:[57:2->57:14] JSX 57:3] first[57:4->57:9]=...[57:10->57:12] k[57:13->57:14]=...[57:13->57:14]> _children:None posCursor:[57:14] posNoWhite:[57:13] Found expr:[57:13->57:14] Pexp_ident k:[57:13->57:14] -Completable: Cjsx([M], k, [first]) +Completable: Cjsx([M], k, [first, k]) [{ "label": "key", "kind": 4, @@ -309,7 +309,7 @@ posCursor:[59:21] posNoWhite:[59:20] Found expr:[59:2->59:21] JSX 59:3] first[59:4->59:9]=...[59:17->59:19] k[59:20->59:21]=...[59:20->59:21]> _children:None posCursor:[59:21] posNoWhite:[59:20] Found expr:[59:20->59:21] Pexp_ident k:[59:20->59:21] -Completable: Cjsx([M], k, [first]) +Completable: Cjsx([M], k, [first, k]) [{ "label": "key", "kind": 4, From 214ba80b828b945acd2ef037ba5721b8e7924ab9 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 10:37:32 +0200 Subject: [PATCH 028/135] Add example of toplevel module completion. --- analysis/tests/src/Completion.res | 4 +++- analysis/tests/src/expected/Completion.res.txt | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index 2f58d7cb5..e472b8a66 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -130,4 +130,6 @@ type aa= {x:int, name:string} type bb = {aa:aa, w:int} let q:bb = assert false // ^com q.aa. -// ^com q.aa.n \ No newline at end of file +// ^com q.aa.n + +// ^com Lis diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 8e2dcc41b..c5b2548f7 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -932,8 +932,8 @@ Completable: Cdotpath([O, ""]) }] Complete tests/src/Completion.res 130:3 -posCursor:[131:6] posNoWhite:[131:5] Found expr:[131:1->133:0] -Pexp_field [131:1->131:5] _:[133:0->133:0] +posCursor:[131:6] posNoWhite:[131:5] Found expr:[131:1->136:0] +Pexp_field [131:1->131:5] _:[136:0->136:0] Completable: Cdotpath([q, aa, ""]) [{ "label": "x", @@ -961,3 +961,7 @@ Completable: Cdotpath([q, aa]) "documentation": null }] +Complete tests/src/Completion.res 133:3 +posCursor:[134:4] posNoWhite:[134:3] Found expr:[134:1->134:4] +[] + From 4fefc26b15d0d7fdeae031650570dd0f659439b4 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 10:38:06 +0200 Subject: [PATCH 029/135] Handle toplevel module completion. --- analysis/src/Commands.ml | 15 +++++++++++++++ analysis/src/Loc.ml | 5 ++++- analysis/tests/src/expected/Completion.res.txt | 16 +++++++++++++++- analysis/tests/src/expected/Jsx.res.txt | 1 + 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index e9c98c6b0..ee2d2d9d8 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -324,6 +324,21 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = if !result = None then result := Some (PartialParser.Cdotpath (flattenLongIdent id.txt)) + | Pexp_construct (id, eOpt) -> + if debug then + Printf.printf "Pexp_construct %s:%s %s\n" + (flattenLongIdent id.txt |> String.concat "\n") + (Loc.toString id.loc) + (match eOpt with + | None -> "None" + | Some e -> Loc.toString e.pexp_loc); + if + eOpt = None && (not id.loc.loc_ghost) + && id.loc |> Loc.hasPos ~pos:posBeforeCursor + then + if !result = None then + result := + Some (PartialParser.Cdotpath (flattenLongIdent id.txt)) | Pexp_field (e, fieldName) -> ( if debug then Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) diff --git a/analysis/src/Loc.ml b/analysis/src/Loc.ml index 7c3db9451..8e0c72bd6 100644 --- a/analysis/src/Loc.ml +++ b/analysis/src/Loc.ml @@ -3,5 +3,8 @@ type t = Location.t let start (loc : t) = Pos.ofLexing loc.loc_start let end_ (loc : t) = Pos.ofLexing loc.loc_end let range loc : Range.t = (start loc, end_ loc) -let toString (loc : t) = loc |> range |> Range.toString + +let toString (loc : t) = + (if loc.loc_ghost then "__ghost__" else "") ^ (loc |> range |> Range.toString) + let hasPos ~pos loc = start loc <= pos && pos < end_ loc diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index c5b2548f7..911a6f5b6 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -963,5 +963,19 @@ Completable: Cdotpath([q, aa]) Complete tests/src/Completion.res 133:3 posCursor:[134:4] posNoWhite:[134:3] Found expr:[134:1->134:4] -[] +Pexp_construct Lis:[134:1->134:4] None +Completable: Cdotpath([Lis]) +[{ + "label": "List", + "kind": 9, + "tags": [], + "detail": "file module", + "documentation": null + }, { + "label": "ListLabels", + "kind": 9, + "tags": [], + "detail": "file module", + "documentation": null + }] diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index b5a2a9d51..7d9bfa0ee 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -322,5 +322,6 @@ Complete tests/src/Jsx.res 60:2 posCursor:[61:4] posNoWhite:[61:3] Found expr:[61:2->61:4] JSX 61:3] > _children:61:3 posCursor:[61:4] posNoWhite:[61:3] Found expr:[61:3->62:0] +Pexp_construct []:__ghost__[61:3->62:0] None [] From 273334e5f5124b2aa6c8f0d14a71faa7e014806a Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 10:48:08 +0200 Subject: [PATCH 030/135] Add example of component with children. --- analysis/tests/src/Jsx.res | 10 ++++++- analysis/tests/src/expected/Jsx.res.txt | 36 ++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/analysis/tests/src/Jsx.res b/analysis/tests/src/Jsx.res index a33b0a19f..ffed92cb2 100644 --- a/analysis/tests/src/Jsx.res +++ b/analysis/tests/src/Jsx.res @@ -59,4 +59,12 @@ let _ = (Ext.make, Ext.makeProps) //^com \ No newline at end of file +//^com + +module WithChildren = { + @react.component + let make = (~name as _: string, ~children) => children +} + +let _ =
+//^com 61:4] +posCursor:[61:4] posNoWhite:[61:3] Found expr:[61:2->65:69] +Pexp_apply ...[63:20->63:21] (...[61:2->63:19], ...[64:2->65:69]) +posCursor:[61:4] posNoWhite:[61:3] Found expr:[61:2->63:19] JSX 61:3] > _children:61:3 -posCursor:[61:4] posNoWhite:[61:3] Found expr:[61:3->62:0] -Pexp_construct []:__ghost__[61:3->62:0] None +posCursor:[61:4] posNoWhite:[61:3] Found expr:[61:3->63:20] +Pexp_construct :::[63:0->63:20] [63:0->63:20] +posCursor:[61:4] posNoWhite:[61:3] Found expr:__ghost__[61:3->63:20] +Pexp_construct []:__ghost__[61:3->63:20] None [] +Complete tests/src/Jsx.res 68:2 +posCursor:[69:15] posNoWhite:[69:13] Found expr:[69:2->69:14] +JSX 69:14] > _children:None +posCursor:[69:15] posNoWhite:[69:13] Found expr:[69:2->69:14] +Pexp_ident WithChildren.createElement:[69:2->69:14] +Completable: Cjsx([WithChildren], "", []) +[{ + "label": "name", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": null + }, { + "label": "children", + "kind": 4, + "tags": [], + "detail": "React.element", + "documentation": null + }, { + "label": "key", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": null + }] + From bef6e0ee8959e0d2116263e8b4310d794bb3a37f Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 10:48:36 +0200 Subject: [PATCH 031/135] Don't suggest. "children" as prop autocompletion. --- analysis/src/NewCompletions.ml | 2 +- analysis/tests/src/expected/Jsx.res.txt | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 2c586deab..e3c7afcf0 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -941,7 +941,7 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens | ({Completion.kind = Completion.Value typ}, _env) :: _ -> let rec getFields (texp : Types.type_expr) = match texp.desc with - | Tfield (name, _, t1, t2) -> + | Tfield (name, _, t1, t2) when name <> "children" -> let fields = t2 |> getFields in (name, t1) :: fields | Tlink te -> te |> getFields diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index d8e6d9c46..2a4445214 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -341,12 +341,6 @@ Completable: Cjsx([WithChildren], "", []) "tags": [], "detail": "string", "documentation": null - }, { - "label": "children", - "kind": 4, - "tags": [], - "detail": "React.element", - "documentation": null }, { "label": "key", "kind": 4, From 46efb43b57b60c0b405e3a9b04a5852ae193b0e9 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 11:13:04 +0200 Subject: [PATCH 032/135] Example of prop completion in the presence of children. --- analysis/tests/src/Jsx.res | 3 ++- analysis/tests/src/expected/Jsx.res.txt | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/analysis/tests/src/Jsx.res b/analysis/tests/src/Jsx.res index ffed92cb2..a0df80061 100644 --- a/analysis/tests/src/Jsx.res +++ b/analysis/tests/src/Jsx.res @@ -67,4 +67,5 @@ module WithChildren = { } let _ =
-//^com 70:16] +JSX 70:14] n[70:15->70:16]=...[70:15->70:16]> _children:None +posCursor:[70:16] posNoWhite:[70:15] Found expr:[70:15->70:16] +Pexp_ident n:[70:15->70:16] +Completable: Cjsx([WithChildren], n, [n]) +[{ + "label": "name", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": null + }] + From dc337bf4267a6831957b2a5720c1076aaccb931b Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 11:29:14 +0200 Subject: [PATCH 033/135] Fix case where children is not the last prop. --- analysis/src/NewCompletions.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index e3c7afcf0..36d2b2e30 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -941,9 +941,9 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens | ({Completion.kind = Completion.Value typ}, _env) :: _ -> let rec getFields (texp : Types.type_expr) = match texp.desc with - | Tfield (name, _, t1, t2) when name <> "children" -> + | Tfield (name, _, t1, t2) -> let fields = t2 |> getFields in - (name, t1) :: fields + if name = "children" then fields else (name, t1) :: fields | Tlink te -> te |> getFields | Tvar None -> [] | _ -> [] From 6a81b385f2ba76fb5ef462c59abebef2b95ea770 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 12:06:22 +0200 Subject: [PATCH 034/135] Add another test that triggers the last children fix. --- analysis/tests/src/Completion.res | 6 ++++ .../tests/src/expected/Completion.res.txt | 34 +++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index e472b8a66..6fc56186f 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -133,3 +133,9 @@ let q:bb = assert false // ^com q.aa.n // ^com Lis + +module WithChildren = { + @react.component + let make = (~children, ~name as _: string) => children +} +// ^com 136:0] -Pexp_field [131:1->131:5] _:[136:0->136:0] +posCursor:[131:6] posNoWhite:[131:5] Found expr:[131:1->136:6] +Pexp_field [131:1->131:5] _:[136:0->136:6] Completable: Cdotpath([q, aa, ""]) [{ "label": "x", @@ -979,3 +989,23 @@ Completable: Cdotpath([Lis]) "documentation": null }] +Complete tests/src/Completion.res 139:3 +posCursor:[140:15] posNoWhite:[140:13] Found expr:[140:2->140:14] +JSX 140:14] > _children:None +posCursor:[140:15] posNoWhite:[140:13] Found expr:[140:2->140:14] +Pexp_ident WithChildren.createElement:[140:2->140:14] +Completable: Cjsx([WithChildren], "", []) +[{ + "label": "name", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": null + }, { + "label": "key", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": null + }] + From 2926430ec859fb1bc900ea356253b2964935bf90 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 17:47:58 +0200 Subject: [PATCH 035/135] Add example that tests local opens. --- analysis/tests/src/Debug.res | 7 ++++- analysis/tests/src/expected/Debug.res.txt | 34 +++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/analysis/tests/src/Debug.res b/analysis/tests/src/Debug.res index 22c432032..45f681528 100644 --- a/analysis/tests/src/Debug.res +++ b/analysis/tests/src/Debug.res @@ -4,4 +4,9 @@ let _ = Belt.List.map let _ = List.map // ^def -// ^db- \ No newline at end of file + +open Js +// ^com eqN +// ^db- +open List +let _ = map \ No newline at end of file diff --git a/analysis/tests/src/expected/Debug.res.txt b/analysis/tests/src/expected/Debug.res.txt index fecfa8798..751e9364f 100644 --- a/analysis/tests/src/expected/Debug.res.txt +++ b/analysis/tests/src/expected/Debug.res.txt @@ -59,4 +59,38 @@ resolvePath path:map [ref] Inner uri list.mli {"uri": "list.mli", "range": {"start": {"line": 116, "character": 4}, "end": {"line": 116, "character": 7}}} +Complete tests/src/Debug.res 7:3 +posCursor:[8:4] posNoWhite:[8:3] Found expr:[8:1->8:4] +Pexp_ident eqN:[8:1->8:4] +Completable: Cdotpath([eqN]) +Opens folkz > 1 Js.place holder +Package opens Pervasives +Package opens Pervasives +fileForModule Impl cmt:tests/node_modules/rescript/lib/ocaml/pervasives.cmti res:tests/node_modules/rescript/lib/ocaml/pervasives.mli +resolving open Js.place holder +resolvePath path:Js.place holder +Not local +resolvePath path:Js.place holder +fileForModule Impl cmt:tests/node_modules/rescript/lib/ocaml/js.cmt res:tests/node_modules/rescript/lib/ocaml/js.ml +resolvePath path:place holder +Opens nows 2 pervasives.mli js.ml +---------------- LOCAL VAL + - Completing in js.ml + -- not capitalized + - Completing in pervasives.mli + -- not capitalized +[{ + "label": "eqNullable", + "kind": 12, + "tags": [], + "detail": "('a, nullable<'a>) => bool", + "documentation": null + }, { + "label": "eqNull", + "kind": 12, + "tags": [], + "detail": "('a, null<'a>) => bool", + "documentation": null + }] + From dfb0ca3861c105fee8e10db956d17e79baa30dcd Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 20:01:25 +0200 Subject: [PATCH 036/135] Example with nested opens. --- analysis/tests/src/Debug.res | 12 +++++++++--- analysis/tests/src/expected/Debug.res.txt | 6 +++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/analysis/tests/src/Debug.res b/analysis/tests/src/Debug.res index 45f681528..00159acdc 100644 --- a/analysis/tests/src/Debug.res +++ b/analysis/tests/src/Debug.res @@ -6,7 +6,13 @@ let _ = List.map // ^def open Js -// ^com eqN +module Before = { + open Belt + let _ = Id.getCmpInternal +} +module Inner = { + // ^com eqN + open List + let _ = map +} // ^db- -open List -let _ = map \ No newline at end of file diff --git a/analysis/tests/src/expected/Debug.res.txt b/analysis/tests/src/expected/Debug.res.txt index 751e9364f..b4c9dec4f 100644 --- a/analysis/tests/src/expected/Debug.res.txt +++ b/analysis/tests/src/expected/Debug.res.txt @@ -59,9 +59,9 @@ resolvePath path:map [ref] Inner uri list.mli {"uri": "list.mli", "range": {"start": {"line": 116, "character": 4}, "end": {"line": 116, "character": 7}}} -Complete tests/src/Debug.res 7:3 -posCursor:[8:4] posNoWhite:[8:3] Found expr:[8:1->8:4] -Pexp_ident eqN:[8:1->8:4] +Complete tests/src/Debug.res 12:5 +posCursor:[13:4] posNoWhite:[13:3] Found expr:[13:1->13:4] +Pexp_ident eqN:[13:1->13:4] Completable: Cdotpath([eqN]) Opens folkz > 1 Js.place holder Package opens Pervasives From b5660528f3ecdd5cc4f3d04fc775a5acbd567879 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 20:02:27 +0200 Subject: [PATCH 037/135] Compute the open's in scope using the AST iterator. --- analysis/src/Commands.ml | 97 ++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 33 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index ee2d2d9d8..a73be1607 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -272,6 +272,38 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = in let found = ref false in let result = ref None in + let opensInScope = ref [] in + let setResultOpt x = + if !result = None then + match x with None -> () | Some x -> result := Some (x, !opensInScope) + in + let setResult x = setResultOpt (Some x) in + let structure (iterator : Ast_iterator.iterator) + (structure : Parsetree.structure) = + let oldOpens = !opensInScope in + Ast_iterator.default_iterator.structure iterator structure; + opensInScope := oldOpens + in + let structure_item (iterator : Ast_iterator.iterator) + (item : Parsetree.structure_item) = + (match item.pstr_desc with + | Pstr_open {popen_lid} -> opensInScope := popen_lid.txt :: !opensInScope + | _ -> ()); + Ast_iterator.default_iterator.structure_item iterator item + in + let signature (iterator : Ast_iterator.iterator) + (signature : Parsetree.signature) = + let oldOpens = !opensInScope in + Ast_iterator.default_iterator.signature iterator signature; + opensInScope := oldOpens + in + let signature_item (iterator : Ast_iterator.iterator) + (item : Parsetree.signature_item) = + (match item.psig_desc with + | Psig_open {popen_lid} -> opensInScope := popen_lid.txt :: !opensInScope + | _ -> ()); + Ast_iterator.default_iterator.signature_item iterator item + in let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = let setFound () = found := true; @@ -295,7 +327,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = in match findPipe lhs with | Some pipe -> - result := Some (PartialParser.Cpipe (pipe, id)); + setResult (PartialParser.Cpipe (pipe, id)); true | None -> false in @@ -321,9 +353,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - if !result = None then - result := - Some (PartialParser.Cdotpath (flattenLongIdent id.txt)) + setResult (PartialParser.Cdotpath (flattenLongIdent id.txt)) | Pexp_construct (id, eOpt) -> if debug then Printf.printf "Pexp_construct %s:%s %s\n" @@ -335,10 +365,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = if eOpt = None && (not id.loc.loc_ghost) && id.loc |> Loc.hasPos ~pos:posBeforeCursor - then - if !result = None then - result := - Some (PartialParser.Cdotpath (flattenLongIdent id.txt)) + then setResult (PartialParser.Cdotpath (flattenLongIdent id.txt)) | Pexp_field (e, fieldName) -> ( if debug then Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) @@ -355,15 +382,11 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = in if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then match digLhs e with - | Some path -> - if !result = None then - result := Some (PartialParser.Cdotpath path) + | Some path -> setResult (PartialParser.Cdotpath path) | None -> () else if Loc.end_ e.pexp_loc = posBeforeCursor then match digLhs e with - | Some path -> - if !result = None then - result := Some (PartialParser.Cdotpath (path @ [""])) + | Some path -> setResult (PartialParser.Cdotpath (path @ [""])) | None -> ()) | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) when Res_parsetree_viewer.isJsxExpression expr -> @@ -385,14 +408,11 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = findJsxPropsCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor ~posAfterCompName:(Loc.end_ compName.loc) in - if jsxCompletable <> None then result := jsxCompletable - else if - compName.loc |> Loc.hasPos ~pos:posBeforeCursor && !result = None - then - result := - Some - (PartialParser.Cdotpath - (flattenLongIdent ~jsx:true compName.txt)) + if jsxCompletable <> None then setResultOpt jsxCompletable + else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then + setResult + (PartialParser.Cdotpath + (flattenLongIdent ~jsx:true compName.txt)) | Pexp_apply ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, [ @@ -429,7 +449,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = findExpApplyCompletable ~funName ~args ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor in - if expApplyCompletable <> None then result := expApplyCompletable + setResultOpt expApplyCompletable | Pexp_send (lhs, {txt; loc}) -> ( (* e["txt"] If the string for txt is not closed, it could go over several lines. @@ -462,14 +482,23 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = then match processLhs lhs with | Some (idPath, nestedPath) -> - result := Some (PartialParser.Cobj (idPath, nestedPath, label)) + setResult (PartialParser.Cobj (idPath, nestedPath, label)) | None -> ()) | _ -> ()); Ast_iterator.default_iterator.expr iterator expr in - let {Res_driver.parsetree = structure} = parser ~filename:currentFile in - let iterator = {Ast_iterator.default_iterator with expr} in - iterator.structure iterator structure |> ignore; + let {Res_driver.parsetree = str} = parser ~filename:currentFile in + let iterator = + { + Ast_iterator.default_iterator with + expr; + signature; + signature_item; + structure; + structure_item; + } + in + iterator.structure iterator str |> ignore; if !found = false then if debug then Printf.printf "XXX Not found!\n"; !result) else None @@ -487,16 +516,18 @@ let completion ~debug ~path ~pos ~currentFile = match PartialParser.positionToOffset text pos with | None -> [] | Some offset -> ( - match (jsxCompletable, PartialParser.findCompletable text offset) with - | None, None -> [] - | Some completable, _ | _, Some completable -> ( + match + (jsxCompletable, (PartialParser.findCompletable text offset, [])) + with + | None, (None, _) -> [] + | Some (completable, opensInScope), _ + | _, (Some completable, opensInScope) -> ( if debug then Printf.printf "Completable: %s\n" (PartialParser.completableToString completable); let rawOpens = - let offsetFromLineStart = offset - snd pos in - (* try to avoid confusion e.g. unclosed quotes at current position *) - PartialParser.findOpens text offsetFromLineStart + opensInScope + |> List.map (fun id -> flattenLongIdent id @ ["place holder"]) in (* Only perform expensive ast operations if there are completables *) match Cmt.fromPath ~path with From 314e77083245ed03a7a4f673f9d93adb1c26cd98 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 20:04:48 +0200 Subject: [PATCH 038/135] Remove code that parses backwards to find open's. --- analysis/src/PartialParser.ml | 102 ---------------------------------- 1 file changed, 102 deletions(-) diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 330c56f25..cc85fadab 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -1,35 +1,3 @@ -let rec findBack text char i = - if i < 0 then i - else if text.[i] = char && (i = 0 || text.[i - 1] <> '/') then i - 1 - else findBack text char (i - 1) - -let rec findOpenComment text i = - if i < 1 then 0 - else if text.[i] = '*' && text.[i - 1] = '/' then i - 2 - else findOpenComment text (i - 1) - -let rec findBackSkippingCommentsAndStrings text char pair i level = - let loop = findBackSkippingCommentsAndStrings text char pair in - if i < 0 then 0 - else if text.[i] = char then - if level = 0 then i - 1 else loop (i - 1) (level - 1) - else if text.[i] = pair then loop (i - 1) (level + 1) - else - match text.[i] with - | '"' -> loop (findBack text '"' (i - 1)) level - | '/' when i >= 1 && text.[i - 1] = '*' -> - loop (findOpenComment text (i - 2)) level - | _ -> loop (i - 1) level - -let rec findLineComment text offset = - if offset <= 0 || text.[offset] = '\n' then None - else if offset > 0 && text.[offset] = '/' && text.[offset - 1] = '/' then - Some (offset - 1) - else findLineComment text (offset - 1) - -(* Check if the position is inside a `//` comment *) -let insideLineComment text offset = findLineComment text offset <> None - let rec skipWhite text i = if i < 0 then 0 else @@ -37,14 +5,6 @@ let rec skipWhite text i = | ' ' | '\n' | '\r' | '\t' -> skipWhite text (i - 1) | _ -> i -let rec startOfLident text i = - if i < 0 then 0 - else - match text.[i] with - | 'a' .. 'z' | 'A' .. 'Z' | '.' | '_' | '0' .. '9' -> - startOfLident text (i - 1) - | _ -> i + 1 - type pipe = PipeId of string list | PipeArray | PipeString type completable = @@ -93,68 +53,6 @@ let findCompletable text offset = in if offset > String.length text || offset = 0 then None else loop (offset - 1) -let findOpens text offset = - let opens = ref [] in - let pathOfModuleOpen o = - let rec loop items = - match items with - | [] -> ["place holder"] - | one :: rest -> one :: loop rest - in - loop (o |> Str.split (Str.regexp_string ".")) - in - let add o = opens := (o |> pathOfModuleOpen) :: !opens in - let maybeOpen i0 = - let rec loop i = - if i < 4 then 0 - else - match text.[i] with - | 'a' .. 'z' | 'A' .. 'Z' | '.' | '_' | '0' .. '9' -> loop (i - 1) - | ' ' | '!' -> - let at = skipWhite text (i - 1) in - let at = - if at >= 0 && text.[at] = '!' then - (* handle open! *) - skipWhite text (at - 1) - else at - in - if - at >= 3 - && text.[at - 3] = 'o' - && text.[at - 2] = 'p' - && text.[at - 1] = 'e' - && text.[at] = 'n' - && not (insideLineComment text (at - 4)) - then ( - add (String.sub text (i + 1) (i0 + 1 - (i + 1))); - at - 4) - else at - | _ -> i - in - loop (i0 - 1) - in - let rec loop i = - if i > 1 then - match text.[i] with - | '}' -> loop (findBackSkippingCommentsAndStrings text '{' '}' (i - 1) 0) - | ']' -> loop (findBackSkippingCommentsAndStrings text '[' ']' (i - 1) 0) - | ')' -> loop (findBackSkippingCommentsAndStrings text '(' ')' (i - 1) 0) - | '"' -> loop (findBack text '"' (i - 1)) - | 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' -> loop (maybeOpen i) - | '(' when text.[i - 1] = '.' -> ( - match text.[i - 2] with - | 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' -> - let i0 = startOfLident text (i - 3) in - add (String.sub text i0 (i - i0 - 1)) - | _ -> loop (i - 1)) - | _ -> - if i > 1 && text.[i] = '/' && text.[i - 1] = '*' then - loop (findOpenComment text (i - 2)) - else loop (i - 1) - in - loop (offset - 1) |> ignore; - !opens - let offsetOfLine text line = let ln = String.length text in let rec loop i lno = From 27cef12a3dcc92a6cd02d54fba21b27ab7967cde Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 20:07:22 +0200 Subject: [PATCH 039/135] cleanup --- analysis/src/Commands.ml | 9 +++++---- analysis/src/PartialParser.ml | 14 +++++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index a73be1607..b174b219e 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -96,6 +96,9 @@ let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = | _ -> None else None in + let thisCaseShouldNotHappen = + {componentPath = []; props = []; childrenStart = None} + in let rec processProps ~lastOffset ~lastPos ~acc args = match args with | (Asttypes.Labelled "children", {Parsetree.pexp_loc}) :: _ -> @@ -128,10 +131,8 @@ let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = } :: acc) ~lastOffset:offsetEnd ~lastPos:ePosEnd rest - | _ -> assert false) - | _ -> - (* should not happen *) - {componentPath = []; props = []; childrenStart = None} + | _ -> thisCaseShouldNotHappen) + | _ -> thisCaseShouldNotHappen in let posAfterCompName = Loc.end_ compName.loc in let offsetAfterCompName = diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index cc85fadab..3afbb3d81 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -1,10 +1,3 @@ -let rec skipWhite text i = - if i < 0 then 0 - else - match text.[i] with - | ' ' | '\n' | '\r' | '\t' -> skipWhite text (i - 1) - | _ -> i - type pipe = PipeId of string list | PipeArray | PipeString type completable = @@ -38,6 +31,13 @@ let completableToString = | PipeString -> "PipeString") ^ ", " ^ str s ^ ")" +let rec skipWhite text i = + if i < 0 then 0 + else + match text.[i] with + | ' ' | '\n' | '\r' | '\t' -> skipWhite text (i - 1) + | _ -> i + let findCompletable text offset = let suffix i = String.sub text (i + 1) (offset - (i + 1)) in let rec loop i = From a1c5bf6f012e2c48126a24a7431ff2575266fa19 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 21:33:41 +0200 Subject: [PATCH 040/135] Get attributes from the parser. --- analysis/src/Commands.ml | 47 +++++++++++++++++++ .../tests/src/expected/Completion.res.txt | 3 +- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index b174b219e..2a0448481 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -305,6 +305,52 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | _ -> ()); Ast_iterator.default_iterator.signature_item iterator item in + let attribute (iterator : Ast_iterator.iterator) + ((id, payload) : Parsetree.attribute) = + (if String.length id.txt >= 3 && String.sub id.txt 0 3 = "ns." then + (* skip: internal parser attribute *) () + else if id.loc.loc_ghost then () + else if id.loc |> Loc.hasPos ~pos:posBeforeCursor then + let posStart, posEnd = Loc.range id.loc in + match + ( PartialParser.positionToOffset text posStart, + PartialParser.positionToOffset text posEnd ) + with + | Some offsetStart, Some offsetEnd -> + (* Can't trust the parser's location + E.g. @foo. let x... gives as label @foo.let *) + let label = + let rawLabel = + String.sub text offsetStart (offsetEnd - offsetStart) + in + let ( ++ ) x y = + match (x, y) with + | Some i1, Some i2 -> Some (min i1 i2) + | Some _, None -> x + | None, _ -> y + in + let label = + match + String.index_opt rawLabel ' ' + ++ String.index_opt rawLabel '\t' + ++ String.index_opt rawLabel '\r' + ++ String.index_opt rawLabel '\n' + with + | None -> rawLabel + | Some i -> String.sub rawLabel 0 i + in + if label <> "" && label.[0] = '@' then + String.sub label 1 (String.length label - 1) + else label + in + found := true; + if debug then + Printf.printf "Attribute id:%s:%s label:%s\n" id.txt + (Loc.toString id.loc) label; + setResult (PartialParser.Cdecorator label) + | _ -> ()); + Ast_iterator.default_iterator.attribute iterator (id, payload) + in let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = let setFound () = found := true; @@ -492,6 +538,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let iterator = { Ast_iterator.default_iterator with + attribute; expr; signature; signature_item; diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 260dd1312..4c1f1d6c3 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -735,7 +735,7 @@ DocumentSymbol tests/src/Completion.res ] Complete tests/src/Completion.res 56:2 -XXX Not found! +Attribute id:reac:[57:1->57:6] label:reac Completable: Cdecorator(reac) [{ "label": "react.component", @@ -748,6 +748,7 @@ Completable: Cdecorator(reac) Complete tests/src/Completion.res 58:2 posCursor:[59:8] posNoWhite:[59:7] Found expr:[0:-1->69:17] Pexp_apply ...[67:6->67:7] (...[67:8->69:17]) +Attribute id:react.let:[59:1->67:3] label:react. Completable: Cdecorator(react.) [{ "label": "component", From 21c5984bb6a1a1ae561bc0523fd5675affd1340d Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 14 Apr 2022 21:39:16 +0200 Subject: [PATCH 041/135] Remove backwards parser for attributes. --- analysis/src/Commands.ml | 40 ++++++++++++++--------------------- analysis/src/PartialParser.ml | 15 ------------- 2 files changed, 16 insertions(+), 39 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 2a0448481..4cec36fec 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -557,32 +557,24 @@ let completion ~debug ~path ~pos ~currentFile = match textOpt with | None | Some "" -> [] | Some text -> - let jsxCompletable = - completionWithParser ~debug ~path ~posCursor:pos ~currentFile ~text - in let completionItems = - match PartialParser.positionToOffset text pos with + match + completionWithParser ~debug ~path ~posCursor:pos ~currentFile ~text + with | None -> [] - | Some offset -> ( - match - (jsxCompletable, (PartialParser.findCompletable text offset, [])) - with - | None, (None, _) -> [] - | Some (completable, opensInScope), _ - | _, (Some completable, opensInScope) -> ( - if debug then - Printf.printf "Completable: %s\n" - (PartialParser.completableToString completable); - let rawOpens = - opensInScope - |> List.map (fun id -> flattenLongIdent id @ ["place holder"]) - in - (* Only perform expensive ast operations if there are completables *) - match Cmt.fromPath ~path with - | None -> [] - | Some full -> - NewCompletions.computeCompletions ~completable ~full ~pos - ~rawOpens)) + | Some (completable, opensInScope) -> ( + if debug then + Printf.printf "Completable: %s\n" + (PartialParser.completableToString completable); + let rawOpens = + opensInScope + |> List.map (fun id -> flattenLongIdent id @ ["place holder"]) + in + (* Only perform expensive ast operations if there are completables *) + match Cmt.fromPath ~path with + | None -> [] + | Some full -> + NewCompletions.computeCompletions ~completable ~full ~pos ~rawOpens) in completionItems in diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 3afbb3d81..19d4be6d7 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -38,21 +38,6 @@ let rec skipWhite text i = | ' ' | '\n' | '\r' | '\t' -> skipWhite text (i - 1) | _ -> i -let findCompletable text offset = - let suffix i = String.sub text (i + 1) (offset - (i + 1)) in - let rec loop i = - if i < 0 then None - else - match text.[i] with - | '@' -> Some (Cdecorator (suffix i)) - | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> loop (i - 1) - | ' ' when i = offset - 1 -> - (* autocomplete with no id *) - None - | _ -> if i = offset - 1 then None else None - in - if offset > String.length text || offset = 0 then None else loop (offset - 1) - let offsetOfLine text line = let ln = String.length text in let rec loop i lno = From bfb78083e3cc321655172827ec153db550b9e867 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 15 Apr 2022 08:27:54 +0200 Subject: [PATCH 042/135] Autocomplete for types. --- analysis/src/Commands.ml | 38 ++++++-- analysis/src/NewCompletions.ml | 2 +- analysis/src/PartialParser.ml | 8 +- .../vendor/res_outcome_printer/res_core.ml | 2 +- analysis/tests/src/Completion.res | 3 + .../tests/src/expected/Completion.res.txt | 86 ++++++++++++++++--- analysis/tests/src/expected/Cross.res.txt | 2 +- analysis/tests/src/expected/Debug.res.txt | 2 +- 8 files changed, 118 insertions(+), 25 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 4cec36fec..fcb75a3ec 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -400,7 +400,8 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - setResult (PartialParser.Cdotpath (flattenLongIdent id.txt)) + setResult + (PartialParser.Cdotpath (flattenLongIdent id.txt, Value)) | Pexp_construct (id, eOpt) -> if debug then Printf.printf "Pexp_construct %s:%s %s\n" @@ -412,7 +413,9 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = if eOpt = None && (not id.loc.loc_ghost) && id.loc |> Loc.hasPos ~pos:posBeforeCursor - then setResult (PartialParser.Cdotpath (flattenLongIdent id.txt)) + then + setResult + (PartialParser.Cdotpath (flattenLongIdent id.txt, Value)) | Pexp_field (e, fieldName) -> ( if debug then Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) @@ -429,11 +432,12 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = in if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then match digLhs e with - | Some path -> setResult (PartialParser.Cdotpath path) + | Some path -> setResult (PartialParser.Cdotpath (path, Value)) | None -> () else if Loc.end_ e.pexp_loc = posBeforeCursor then match digLhs e with - | Some path -> setResult (PartialParser.Cdotpath (path @ [""])) + | Some path -> + setResult (PartialParser.Cdotpath (path @ [""], Value)) | None -> ()) | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) when Res_parsetree_viewer.isJsxExpression expr -> @@ -459,7 +463,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then setResult (PartialParser.Cdotpath - (flattenLongIdent ~jsx:true compName.txt)) + (flattenLongIdent ~jsx:true compName.txt, Value)) | Pexp_apply ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, [ @@ -534,7 +538,26 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | _ -> ()); Ast_iterator.default_iterator.expr iterator expr in - let {Res_driver.parsetree = str} = parser ~filename:currentFile in + let typ (iterator : Ast_iterator.iterator) (core_type : Parsetree.core_type) + = + if core_type.ptyp_loc |> Loc.hasPos ~pos:posNoWhite then ( + found := true; + if debug then + Printf.printf "posCursor:[%s] posNoWhite:[%s] Found type:%s\n" + (Pos.toString posCursor) (Pos.toString posNoWhite) + (Loc.toString core_type.ptyp_loc); + match core_type.ptyp_desc with + | Ptyp_constr (id, _args) -> + if debug then + Printf.printf "Ptyp_constr %s:%s\n" + (flattenLongIdent id.txt |> String.concat ".") + (Loc.toString id.loc); + if id.loc |> Loc.hasPos ~pos:posBeforeCursor then + (* XXX should do types separately: only show type completions, not value ones *) + setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Type)) + | _ -> ()); + Ast_iterator.default_iterator.typ iterator core_type + in let iterator = { Ast_iterator.default_iterator with @@ -544,8 +567,11 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = signature_item; structure; structure_item; + typ; } in + + let {Res_driver.parsetree = str} = parser ~filename:currentFile in iterator.structure iterator str |> ignore; if !found = false then if debug then Printf.printf "XXX Not found!\n"; !result) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 36d2b2e30..9a0578762 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -993,7 +993,7 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel) @ keyLabels - | Cdotpath dotpath -> + | Cdotpath (dotpath, pathKind) -> let completions = dotpath |> processDotPath ~exact:false in (* TODO(#107): figure out why we're getting duplicates. *) completions |> Utils.dedup diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 19d4be6d7..1787cb440 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -1,10 +1,11 @@ type pipe = PipeId of string list | PipeArray | PipeString +type pathKind = Type | Value type completable = | Cdecorator of string (** e.g. @module *) | Clabel of string list * string * string list (** e.g. (["M", "foo"], "label", ["l1", "l2"]) for M.foo(...~l1...~l2...~label...) *) - | Cdotpath of string list (** e.g. ["M", "foo"] for M.foo *) + | Cdotpath of string list * pathKind (** e.g. ["M", "foo"] for M.foo *) | Cjsx of string list * string * string list (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for "Cdecorator(" ^ str s ^ ")" | Clabel (sl1, s, sl2) -> "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" - | Cdotpath sl -> "Cdotpath(" ^ (sl |> list) ^ ")" + | Cdotpath (sl, k) -> + "Cdotpath(" ^ (sl |> list) ^ "," + ^ (match k with Value -> "Value" | Type -> "Type") + ^ ")" | Cjsx (sl1, s, sl2) -> "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" | Cobj (sl1, sl2, s) -> diff --git a/analysis/src/vendor/res_outcome_printer/res_core.ml b/analysis/src/vendor/res_outcome_printer/res_core.ml index 0750c6a00..d722fa391 100644 --- a/analysis/src/vendor/res_outcome_printer/res_core.ml +++ b/analysis/src/vendor/res_outcome_printer/res_core.ml @@ -682,7 +682,7 @@ let parseValuePathTail p startPos ident = loop p (Longident.Ldot (path, ident)) | token -> Parser.err p (Diagnostics.unexpected token p.breadcrumbs); - Location.mknoloc path + Location.mkloc (Longident.Ldot (path, "$")) (mkLoc startPos p.prevEndPos) in loop p ident diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index 6fc56186f..51af71429 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -139,3 +139,6 @@ module WithChildren = { let make = (~children, ~name as _: string) => children } // ^com 1:9] Pexp_ident MyList.m:[1:1->1:9] -Completable: Cdotpath([MyList, m]) +Completable: Cdotpath([MyList, m],Value) [{ "label": "mapReverse", "kind": 12, @@ -73,7 +73,7 @@ Completable: Cdotpath([MyList, m]) Complete tests/src/Completion.res 1:2 posCursor:[2:7] posNoWhite:[2:6] Found expr:[2:1->6:6] Pexp_ident Array.:[2:1->6:6] -Completable: Cdotpath([Array, ""]) +Completable: Cdotpath([Array, ""],Value) [{ "label": "Floatarray", "kind": 9, @@ -295,7 +295,7 @@ Completable: Cdotpath([Array, ""]) Complete tests/src/Completion.res 2:2 posCursor:[3:8] posNoWhite:[3:7] Found expr:[3:1->3:8] Pexp_ident Array.m:[3:1->3:8] -Completable: Cdotpath([Array, m]) +Completable: Cdotpath([Array, m],Value) [{ "label": "mapi", "kind": 12, @@ -349,7 +349,7 @@ Completable: Cdotpath([Array, m]) Complete tests/src/Completion.res 12:2 posCursor:[13:15] posNoWhite:[13:14] Found expr:[13:10->13:15] Pexp_ident Dep.c:[13:10->13:15] -Completable: Cdotpath([Dep, c]) +Completable: Cdotpath([Dep, c],Value) [{ "label": "customDouble", "kind": 12, @@ -452,7 +452,7 @@ Complete tests/src/Completion.res 38:2 posCursor:[39:19] posNoWhite:[39:18] Found expr:[39:1->39:19] posCursor:[39:19] posNoWhite:[39:18] Found expr:[39:10->39:19] Pexp_ident Js.Dict.u:[39:10->39:19] -Completable: Cdotpath([Js, Dict, u]) +Completable: Cdotpath([Js, Dict, u],Value) [{ "label": "unsafeGet", "kind": 12, @@ -472,7 +472,7 @@ posCursor:[51:28] posNoWhite:[51:27] Found expr:[51:13->51:28] JSX 51:19] second[51:20->51:26]=...[51:27->51:28]> _children:None posCursor:[51:28] posNoWhite:[51:27] Found expr:[51:27->51:28] Pexp_ident z:[51:27->51:28] -Completable: Cdotpath([z]) +Completable: Cdotpath([z],Value) [{ "label": "zzz", "kind": 12, @@ -863,7 +863,7 @@ Completable: Cobj([no], [x, y], "") Complete tests/src/Completion.res 88:3 posCursor:[89:3] posNoWhite:[89:2] Found expr:[89:1->93:3] Pexp_field [89:1->89:2] _:[93:0->93:3] -Completable: Cdotpath([r, ""]) +Completable: Cdotpath([r, ""],Value) [{ "label": "x", "kind": 5, @@ -881,7 +881,7 @@ Completable: Cdotpath([r, ""]) Complete tests/src/Completion.res 90:3 posCursor:[91:19] posNoWhite:[91:18] Found expr:[91:1->93:3] Pexp_field [91:1->91:18] _:[93:0->93:3] -Completable: Cdotpath([Obj, Rec, recordVal, ""]) +Completable: Cdotpath([Obj, Rec, recordVal, ""],Value) [{ "label": "xx", "kind": 5, @@ -901,7 +901,7 @@ posCursor:[97:3] posNoWhite:[97:2] Found expr:[96:11->99:1] posCursor:[97:3] posNoWhite:[97:2] Found expr:[97:1->98:5] posCursor:[97:3] posNoWhite:[97:2] Found expr:[97:1->97:3] Pexp_ident my:[97:1->97:3] -Completable: Cdotpath([my]) +Completable: Cdotpath([my],Value) [{ "label": "myAmazingFunction", "kind": 12, @@ -933,7 +933,7 @@ posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] JSX 126:4] > _children:None posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] Pexp_ident O..createElement:[126:2->126:4] -Completable: Cdotpath([O, ""]) +Completable: Cdotpath([O, ""],Value) [{ "label": "Comp", "kind": 9, @@ -945,7 +945,7 @@ Completable: Cdotpath([O, ""]) Complete tests/src/Completion.res 130:3 posCursor:[131:6] posNoWhite:[131:5] Found expr:[131:1->136:6] Pexp_field [131:1->131:5] _:[136:0->136:6] -Completable: Cdotpath([q, aa, ""]) +Completable: Cdotpath([q, aa, ""],Value) [{ "label": "x", "kind": 5, @@ -963,7 +963,7 @@ Completable: Cdotpath([q, aa, ""]) Complete tests/src/Completion.res 131:3 posCursor:[132:7] posNoWhite:[132:6] Found expr:[132:1->132:7] Pexp_field [132:1->132:5] n:[132:6->132:7] -Completable: Cdotpath([q, aa]) +Completable: Cdotpath([q, aa],Value) [{ "label": "aa", "kind": 5, @@ -975,7 +975,7 @@ Completable: Cdotpath([q, aa]) Complete tests/src/Completion.res 133:3 posCursor:[134:4] posNoWhite:[134:3] Found expr:[134:1->134:4] Pexp_construct Lis:[134:1->134:4] None -Completable: Cdotpath([Lis]) +Completable: Cdotpath([Lis],Value) [{ "label": "List", "kind": 9, @@ -1010,3 +1010,63 @@ Completable: Cjsx([WithChildren], "", []) "documentation": null }] +Complete tests/src/Completion.res 141:3 +posCursor:[142:14] posNoWhite:[142:13] Found type:[142:10->142:14] +Ptyp_constr Js.n:[142:10->142:14] +Completable: Cdotpath([Js, n],Type) +[{ + "label": "null", + "kind": 12, + "tags": [], + "detail": "null<'a>", + "documentation": {"kind": "markdown", "value": " The same as [empty] in {!Js.Null} will be compiled as [null]"} + }, { + "label": "nullToOption", + "kind": 12, + "tags": [], + "detail": "null<'a> => option<'a>", + "documentation": null + }, { + "label": "null_undefined", + "kind": 22, + "tags": [], + "detail": "type null_undefined<'a> = nullable<'a>", + "documentation": null + }, { + "label": "nullable", + "kind": 22, + "tags": [], + "detail": "type nullable<+'a>", + "documentation": {"kind": "markdown", "value": " value of this type can be [undefined], [null] or ['a]\n this type is the same as type [t] n {!Null_undefined} "} + }, { + "label": "null", + "kind": 22, + "tags": [], + "detail": "type null<+'a>", + "documentation": {"kind": "markdown", "value": " nullable, value of this type can be either [null] or ['a]\n this type is the same as type [t] in {!Null}\n"} + }] + +Complete tests/src/Completion.res 142:3 +posCursor:[143:18] posNoWhite:[143:17] Found type:[143:10->143:18] +Ptyp_constr ForAuto.:[143:10->143:18] +Completable: Cdotpath([ForAuto, ""],Type) +[{ + "label": "abc", + "kind": 12, + "tags": [], + "detail": "(t, int) => t", + "documentation": null + }, { + "label": "abd", + "kind": 12, + "tags": [], + "detail": "(t, int) => t", + "documentation": null + }, { + "label": "t", + "kind": 22, + "tags": [], + "detail": "type t = int", + "documentation": null + }] + diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index 56d28facd..05321e242 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -96,6 +96,6 @@ TypeDefinition tests/src/Cross.res 37:37 Complete tests/src/Cross.res 39:2 posCursor:[40:26] posNoWhite:[40:25] Found expr:[40:1->40:26] Pexp_ident DefinitionWithInterface.a:[40:1->40:26] -Completable: Cdotpath([DefinitionWithInterface, a]) +Completable: Cdotpath([DefinitionWithInterface, a],Value) [] diff --git a/analysis/tests/src/expected/Debug.res.txt b/analysis/tests/src/expected/Debug.res.txt index b4c9dec4f..1b987e433 100644 --- a/analysis/tests/src/expected/Debug.res.txt +++ b/analysis/tests/src/expected/Debug.res.txt @@ -62,7 +62,7 @@ resolvePath path:map Complete tests/src/Debug.res 12:5 posCursor:[13:4] posNoWhite:[13:3] Found expr:[13:1->13:4] Pexp_ident eqN:[13:1->13:4] -Completable: Cdotpath([eqN]) +Completable: Cdotpath([eqN],Value) Opens folkz > 1 Js.place holder Package opens Pervasives Package opens Pervasives From c1e2be5d66125313e9c4ee6ca28da762a71463ff Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 15 Apr 2022 08:31:18 +0200 Subject: [PATCH 043/135] refactor --- analysis/src/Commands.ml | 578 +++++++++++++++++++-------------------- 1 file changed, 286 insertions(+), 292 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index fcb75a3ec..fc56a0abe 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -267,310 +267,304 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = in let posBeforeCursor = (fst posCursor, max 0 (snd posCursor - 1)) in - if Filename.check_suffix path ".res" then ( - let parser = - Res_driver.parsingEngine.parseImplementation ~forPrinter:false - in - let found = ref false in - let result = ref None in - let opensInScope = ref [] in - let setResultOpt x = - if !result = None then - match x with None -> () | Some x -> result := Some (x, !opensInScope) - in - let setResult x = setResultOpt (Some x) in - let structure (iterator : Ast_iterator.iterator) - (structure : Parsetree.structure) = - let oldOpens = !opensInScope in - Ast_iterator.default_iterator.structure iterator structure; - opensInScope := oldOpens - in - let structure_item (iterator : Ast_iterator.iterator) - (item : Parsetree.structure_item) = - (match item.pstr_desc with - | Pstr_open {popen_lid} -> opensInScope := popen_lid.txt :: !opensInScope - | _ -> ()); - Ast_iterator.default_iterator.structure_item iterator item - in - let signature (iterator : Ast_iterator.iterator) - (signature : Parsetree.signature) = - let oldOpens = !opensInScope in - Ast_iterator.default_iterator.signature iterator signature; - opensInScope := oldOpens - in - let signature_item (iterator : Ast_iterator.iterator) - (item : Parsetree.signature_item) = - (match item.psig_desc with - | Psig_open {popen_lid} -> opensInScope := popen_lid.txt :: !opensInScope - | _ -> ()); - Ast_iterator.default_iterator.signature_item iterator item - in - let attribute (iterator : Ast_iterator.iterator) - ((id, payload) : Parsetree.attribute) = - (if String.length id.txt >= 3 && String.sub id.txt 0 3 = "ns." then - (* skip: internal parser attribute *) () - else if id.loc.loc_ghost then () - else if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - let posStart, posEnd = Loc.range id.loc in - match - ( PartialParser.positionToOffset text posStart, - PartialParser.positionToOffset text posEnd ) - with - | Some offsetStart, Some offsetEnd -> - (* Can't trust the parser's location - E.g. @foo. let x... gives as label @foo.let *) + let found = ref false in + let result = ref None in + let opensInScope = ref [] in + let setResultOpt x = + if !result = None then + match x with None -> () | Some x -> result := Some (x, !opensInScope) + in + let setResult x = setResultOpt (Some x) in + let structure (iterator : Ast_iterator.iterator) + (structure : Parsetree.structure) = + let oldOpens = !opensInScope in + Ast_iterator.default_iterator.structure iterator structure; + opensInScope := oldOpens + in + let structure_item (iterator : Ast_iterator.iterator) + (item : Parsetree.structure_item) = + (match item.pstr_desc with + | Pstr_open {popen_lid} -> opensInScope := popen_lid.txt :: !opensInScope + | _ -> ()); + Ast_iterator.default_iterator.structure_item iterator item + in + let signature (iterator : Ast_iterator.iterator) + (signature : Parsetree.signature) = + let oldOpens = !opensInScope in + Ast_iterator.default_iterator.signature iterator signature; + opensInScope := oldOpens + in + let signature_item (iterator : Ast_iterator.iterator) + (item : Parsetree.signature_item) = + (match item.psig_desc with + | Psig_open {popen_lid} -> opensInScope := popen_lid.txt :: !opensInScope + | _ -> ()); + Ast_iterator.default_iterator.signature_item iterator item + in + let attribute (iterator : Ast_iterator.iterator) + ((id, payload) : Parsetree.attribute) = + (if String.length id.txt >= 3 && String.sub id.txt 0 3 = "ns." then + (* skip: internal parser attribute *) () + else if id.loc.loc_ghost then () + else if id.loc |> Loc.hasPos ~pos:posBeforeCursor then + let posStart, posEnd = Loc.range id.loc in + match + ( PartialParser.positionToOffset text posStart, + PartialParser.positionToOffset text posEnd ) + with + | Some offsetStart, Some offsetEnd -> + (* Can't trust the parser's location + E.g. @foo. let x... gives as label @foo.let *) + let label = + let rawLabel = + String.sub text offsetStart (offsetEnd - offsetStart) + in + let ( ++ ) x y = + match (x, y) with + | Some i1, Some i2 -> Some (min i1 i2) + | Some _, None -> x + | None, _ -> y + in let label = - let rawLabel = - String.sub text offsetStart (offsetEnd - offsetStart) - in - let ( ++ ) x y = - match (x, y) with - | Some i1, Some i2 -> Some (min i1 i2) - | Some _, None -> x - | None, _ -> y - in - let label = - match - String.index_opt rawLabel ' ' - ++ String.index_opt rawLabel '\t' - ++ String.index_opt rawLabel '\r' - ++ String.index_opt rawLabel '\n' - with - | None -> rawLabel - | Some i -> String.sub rawLabel 0 i - in - if label <> "" && label.[0] = '@' then - String.sub label 1 (String.length label - 1) - else label + match + String.index_opt rawLabel ' ' + ++ String.index_opt rawLabel '\t' + ++ String.index_opt rawLabel '\r' + ++ String.index_opt rawLabel '\n' + with + | None -> rawLabel + | Some i -> String.sub rawLabel 0 i in - found := true; - if debug then - Printf.printf "Attribute id:%s:%s label:%s\n" id.txt - (Loc.toString id.loc) label; - setResult (PartialParser.Cdecorator label) - | _ -> ()); - Ast_iterator.default_iterator.attribute iterator (id, payload) - in - let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = - let setFound () = + if label <> "" && label.[0] = '@' then + String.sub label 1 (String.length label - 1) + else label + in found := true; if debug then - Printf.printf "posCursor:[%s] posNoWhite:[%s] Found expr:%s\n" - (Pos.toString posCursor) (Pos.toString posNoWhite) - (Loc.toString expr.pexp_loc) - in - let setPipeResult ~(lhs : Parsetree.expression) ~id = - let rec findPipe (e : Parsetree.expression) = - match e.pexp_desc with - | Pexp_constant (Pconst_string _) -> Some PartialParser.PipeString - | Pexp_array _ -> Some PartialParser.PipeArray - | Pexp_ident {txt} -> - Some (PartialParser.PipeId (flattenLongIdent txt)) - | Pexp_field (e1, {txt}) -> ( - match findPipe e1 with - | Some (PipeId path) -> Some (PipeId (path @ flattenLongIdent txt)) - | _ -> None) - | _ -> None - in - match findPipe lhs with - | Some pipe -> - setResult (PartialParser.Cpipe (pipe, id)); - true - | None -> false + Printf.printf "Attribute id:%s:%s label:%s\n" id.txt + (Loc.toString id.loc) label; + setResult (PartialParser.Cdecorator label) + | _ -> ()); + Ast_iterator.default_iterator.attribute iterator (id, payload) + in + let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = + let setFound () = + found := true; + if debug then + Printf.printf "posCursor:[%s] posNoWhite:[%s] Found expr:%s\n" + (Pos.toString posCursor) (Pos.toString posNoWhite) + (Loc.toString expr.pexp_loc) + in + let setPipeResult ~(lhs : Parsetree.expression) ~id = + let rec findPipe (e : Parsetree.expression) = + match e.pexp_desc with + | Pexp_constant (Pconst_string _) -> Some PartialParser.PipeString + | Pexp_array _ -> Some PartialParser.PipeArray + | Pexp_ident {txt} -> Some (PartialParser.PipeId (flattenLongIdent txt)) + | Pexp_field (e1, {txt}) -> ( + match findPipe e1 with + | Some (PipeId path) -> Some (PipeId (path @ flattenLongIdent txt)) + | _ -> None) + | _ -> None in - - match expr.pexp_desc with - | Pexp_apply - ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, - [ - (_, lhs); - (_, {pexp_desc = Pexp_extension _; pexp_loc = {loc_ghost = true}}); - ] ) - when opLoc |> Loc.hasPos ~pos:posBeforeCursor -> - (* Case foo-> when the parser adds a ghost expression to the rhs - so the apply expression does not include the cursor *) - if setPipeResult ~lhs ~id:"" then setFound () - | _ -> - if expr.pexp_loc |> Loc.hasPos ~pos:posNoWhite then ( - setFound (); - match expr.pexp_desc with - | Pexp_ident id -> - if debug then - Printf.printf "Pexp_ident %s:%s\n" - (flattenLongIdent id.txt |> String.concat ".") - (Loc.toString id.loc); - if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - setResult - (PartialParser.Cdotpath (flattenLongIdent id.txt, Value)) - | Pexp_construct (id, eOpt) -> - if debug then - Printf.printf "Pexp_construct %s:%s %s\n" - (flattenLongIdent id.txt |> String.concat "\n") - (Loc.toString id.loc) - (match eOpt with - | None -> "None" - | Some e -> Loc.toString e.pexp_loc); - if - eOpt = None && (not id.loc.loc_ghost) - && id.loc |> Loc.hasPos ~pos:posBeforeCursor - then - setResult - (PartialParser.Cdotpath (flattenLongIdent id.txt, Value)) - | Pexp_field (e, fieldName) -> ( - if debug then - Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) - (flattenLongIdent fieldName.txt |> String.concat ".") - (Loc.toString fieldName.loc); - let rec digLhs (lhs : Parsetree.expression) = - match lhs.pexp_desc with - | Pexp_ident id -> Some (flattenLongIdent id.txt) - | Pexp_field (lhs, id) -> ( - match digLhs lhs with - | Some path -> Some (path @ flattenLongIdent id.txt) - | None -> None) - | _ -> None - in - if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then - match digLhs e with - | Some path -> setResult (PartialParser.Cdotpath (path, Value)) - | None -> () - else if Loc.end_ e.pexp_loc = posBeforeCursor then - match digLhs e with - | Some path -> - setResult (PartialParser.Cdotpath (path @ [""], Value)) - | None -> ()) - | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) - when Res_parsetree_viewer.isJsxExpression expr -> - let jsxProps = extractJsxProps ~text ~compName ~args in - if debug then - Printf.printf "JSX <%s:%s %s> _children:%s\n" - (jsxProps.componentPath |> String.concat ",") - (Loc.toString compName.loc) - (jsxProps.props - |> List.map (fun {name; posStart; posEnd; exp} -> - Printf.sprintf "%s[%s->%s]=...%s" name - (Pos.toString posStart) (Pos.toString posEnd) - (Loc.toString exp.pexp_loc)) - |> String.concat " ") - (match jsxProps.childrenStart with - | None -> "None" - | Some childrenPosStart -> Pos.toString childrenPosStart); - let jsxCompletable = - findJsxPropsCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) - ~posBeforeCursor ~posAfterCompName:(Loc.end_ compName.loc) - in - if jsxCompletable <> None then setResultOpt jsxCompletable - else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then - setResult - (PartialParser.Cdotpath - (flattenLongIdent ~jsx:true compName.txt, Value)) - | Pexp_apply - ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, - [ - (_, lhs); - (_, {pexp_desc = Pexp_ident {txt = Longident.Lident id; loc}}); - ] ) - when loc |> Loc.hasPos ~pos:posBeforeCursor -> - (* Case foo->id *) - setPipeResult ~lhs ~id |> ignore - | Pexp_apply - ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, - [(_, lhs); _] ) - when Loc.end_ opLoc = posCursor -> - (* Case foo-> *) - setPipeResult ~lhs ~id:"" |> ignore - | Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "|."}}, [_; _]) -> - () - | Pexp_apply ({pexp_desc = Pexp_ident funName}, args) -> - let args = extractExpApplyArgs ~text ~funName ~args in - if debug then - Printf.printf "Pexp_apply ...%s (%s)\n" (Loc.toString funName.loc) - (args - |> List.map (fun {label; exp} -> - Printf.sprintf "%s...%s" - (match label with - | None -> "" - | Some {name; opt; posStart; posEnd} -> - "~" ^ name ^ Pos.toString posStart ^ "->" - ^ Pos.toString posEnd ^ "=" - ^ if opt then "?" else "") - (Loc.toString exp.pexp_loc)) - |> String.concat ", "); - let expApplyCompletable = - findExpApplyCompletable ~funName ~args - ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor - in - setResultOpt expApplyCompletable - | Pexp_send (lhs, {txt; loc}) -> ( - (* e["txt"] - If the string for txt is not closed, it could go over several lines. - Only take the first like to represent the label *) - let txtLines = txt |> String.split_on_char '\n' in - let label = List.hd txtLines in - let labelRange = - let l, c = Loc.start loc in - ((l, c + 1), (l, c + 1 + String.length label)) - in - - let rec processLhs (lhs : Parsetree.expression) = - match lhs.pexp_desc with - | Pexp_ident id -> Some (flattenLongIdent id.txt, []) - | Pexp_send (e1, {txt}) -> ( - match processLhs e1 with - | None -> None - | Some (idPath, nestedPath) -> Some (idPath, nestedPath @ [txt]) - ) - | _ -> None - in - - if debug then - Printf.printf "XXX Pexp_send %s%s e:%s\n" label - (Range.toString labelRange) - (Loc.toString lhs.pexp_loc); - if - labelRange |> Range.hasPos ~pos:posBeforeCursor - || (label = "" && posCursor = fst labelRange) - then - match processLhs lhs with - | Some (idPath, nestedPath) -> - setResult (PartialParser.Cobj (idPath, nestedPath, label)) - | None -> ()) - | _ -> ()); - Ast_iterator.default_iterator.expr iterator expr + match findPipe lhs with + | Some pipe -> + setResult (PartialParser.Cpipe (pipe, id)); + true + | None -> false in - let typ (iterator : Ast_iterator.iterator) (core_type : Parsetree.core_type) - = - if core_type.ptyp_loc |> Loc.hasPos ~pos:posNoWhite then ( - found := true; - if debug then - Printf.printf "posCursor:[%s] posNoWhite:[%s] Found type:%s\n" - (Pos.toString posCursor) (Pos.toString posNoWhite) - (Loc.toString core_type.ptyp_loc); - match core_type.ptyp_desc with - | Ptyp_constr (id, _args) -> + + match expr.pexp_desc with + | Pexp_apply + ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, + [ + (_, lhs); + (_, {pexp_desc = Pexp_extension _; pexp_loc = {loc_ghost = true}}); + ] ) + when opLoc |> Loc.hasPos ~pos:posBeforeCursor -> + (* Case foo-> when the parser adds a ghost expression to the rhs + so the apply expression does not include the cursor *) + if setPipeResult ~lhs ~id:"" then setFound () + | _ -> + if expr.pexp_loc |> Loc.hasPos ~pos:posNoWhite then ( + setFound (); + match expr.pexp_desc with + | Pexp_ident id -> if debug then - Printf.printf "Ptyp_constr %s:%s\n" + Printf.printf "Pexp_ident %s:%s\n" (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - (* XXX should do types separately: only show type completions, not value ones *) - setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Type)) + setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Value)) + | Pexp_construct (id, eOpt) -> + if debug then + Printf.printf "Pexp_construct %s:%s %s\n" + (flattenLongIdent id.txt |> String.concat "\n") + (Loc.toString id.loc) + (match eOpt with + | None -> "None" + | Some e -> Loc.toString e.pexp_loc); + if + eOpt = None && (not id.loc.loc_ghost) + && id.loc |> Loc.hasPos ~pos:posBeforeCursor + then + setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Value)) + | Pexp_field (e, fieldName) -> ( + if debug then + Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) + (flattenLongIdent fieldName.txt |> String.concat ".") + (Loc.toString fieldName.loc); + let rec digLhs (lhs : Parsetree.expression) = + match lhs.pexp_desc with + | Pexp_ident id -> Some (flattenLongIdent id.txt) + | Pexp_field (lhs, id) -> ( + match digLhs lhs with + | Some path -> Some (path @ flattenLongIdent id.txt) + | None -> None) + | _ -> None + in + if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then + match digLhs e with + | Some path -> setResult (PartialParser.Cdotpath (path, Value)) + | None -> () + else if Loc.end_ e.pexp_loc = posBeforeCursor then + match digLhs e with + | Some path -> + setResult (PartialParser.Cdotpath (path @ [""], Value)) + | None -> ()) + | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) + when Res_parsetree_viewer.isJsxExpression expr -> + let jsxProps = extractJsxProps ~text ~compName ~args in + if debug then + Printf.printf "JSX <%s:%s %s> _children:%s\n" + (jsxProps.componentPath |> String.concat ",") + (Loc.toString compName.loc) + (jsxProps.props + |> List.map (fun {name; posStart; posEnd; exp} -> + Printf.sprintf "%s[%s->%s]=...%s" name + (Pos.toString posStart) (Pos.toString posEnd) + (Loc.toString exp.pexp_loc)) + |> String.concat " ") + (match jsxProps.childrenStart with + | None -> "None" + | Some childrenPosStart -> Pos.toString childrenPosStart); + let jsxCompletable = + findJsxPropsCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) + ~posBeforeCursor ~posAfterCompName:(Loc.end_ compName.loc) + in + if jsxCompletable <> None then setResultOpt jsxCompletable + else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then + setResult + (PartialParser.Cdotpath + (flattenLongIdent ~jsx:true compName.txt, Value)) + | Pexp_apply + ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, + [ + (_, lhs); + (_, {pexp_desc = Pexp_ident {txt = Longident.Lident id; loc}}); + ] ) + when loc |> Loc.hasPos ~pos:posBeforeCursor -> + (* Case foo->id *) + setPipeResult ~lhs ~id |> ignore + | Pexp_apply + ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, + [(_, lhs); _] ) + when Loc.end_ opLoc = posCursor -> + (* Case foo-> *) + setPipeResult ~lhs ~id:"" |> ignore + | Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "|."}}, [_; _]) -> + () + | Pexp_apply ({pexp_desc = Pexp_ident funName}, args) -> + let args = extractExpApplyArgs ~text ~funName ~args in + if debug then + Printf.printf "Pexp_apply ...%s (%s)\n" (Loc.toString funName.loc) + (args + |> List.map (fun {label; exp} -> + Printf.sprintf "%s...%s" + (match label with + | None -> "" + | Some {name; opt; posStart; posEnd} -> + "~" ^ name ^ Pos.toString posStart ^ "->" + ^ Pos.toString posEnd ^ "=" + ^ if opt then "?" else "") + (Loc.toString exp.pexp_loc)) + |> String.concat ", "); + let expApplyCompletable = + findExpApplyCompletable ~funName ~args + ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor + in + setResultOpt expApplyCompletable + | Pexp_send (lhs, {txt; loc}) -> ( + (* e["txt"] + If the string for txt is not closed, it could go over several lines. + Only take the first like to represent the label *) + let txtLines = txt |> String.split_on_char '\n' in + let label = List.hd txtLines in + let labelRange = + let l, c = Loc.start loc in + ((l, c + 1), (l, c + 1 + String.length label)) + in + + let rec processLhs (lhs : Parsetree.expression) = + match lhs.pexp_desc with + | Pexp_ident id -> Some (flattenLongIdent id.txt, []) + | Pexp_send (e1, {txt}) -> ( + match processLhs e1 with + | None -> None + | Some (idPath, nestedPath) -> Some (idPath, nestedPath @ [txt])) + | _ -> None + in + + if debug then + Printf.printf "XXX Pexp_send %s%s e:%s\n" label + (Range.toString labelRange) + (Loc.toString lhs.pexp_loc); + if + labelRange |> Range.hasPos ~pos:posBeforeCursor + || (label = "" && posCursor = fst labelRange) + then + match processLhs lhs with + | Some (idPath, nestedPath) -> + setResult (PartialParser.Cobj (idPath, nestedPath, label)) + | None -> ()) | _ -> ()); - Ast_iterator.default_iterator.typ iterator core_type - in - let iterator = - { - Ast_iterator.default_iterator with - attribute; - expr; - signature; - signature_item; - structure; - structure_item; - typ; - } - in + Ast_iterator.default_iterator.expr iterator expr + in + let typ (iterator : Ast_iterator.iterator) (core_type : Parsetree.core_type) = + if core_type.ptyp_loc |> Loc.hasPos ~pos:posNoWhite then ( + found := true; + if debug then + Printf.printf "posCursor:[%s] posNoWhite:[%s] Found type:%s\n" + (Pos.toString posCursor) (Pos.toString posNoWhite) + (Loc.toString core_type.ptyp_loc); + match core_type.ptyp_desc with + | Ptyp_constr (id, _args) -> + if debug then + Printf.printf "Ptyp_constr %s:%s\n" + (flattenLongIdent id.txt |> String.concat ".") + (Loc.toString id.loc); + if id.loc |> Loc.hasPos ~pos:posBeforeCursor then + setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Type)) + | _ -> ()); + Ast_iterator.default_iterator.typ iterator core_type + in + let iterator = + { + Ast_iterator.default_iterator with + attribute; + expr; + signature; + signature_item; + structure; + structure_item; + typ; + } + in + if Filename.check_suffix path ".res" then ( + let parser = + Res_driver.parsingEngine.parseImplementation ~forPrinter:false + in let {Res_driver.parsetree = str} = parser ~filename:currentFile in iterator.structure iterator str |> ignore; if !found = false then if debug then Printf.printf "XXX Not found!\n"; From 4a012602fc48c62428f25e4ae601947228d5b0db Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 15 Apr 2022 08:42:56 +0200 Subject: [PATCH 044/135] Support interface files. --- analysis/src/Commands.ml | 6 ++++++ analysis/tests/src/Jsx.resi | 6 ++++-- analysis/tests/src/expected/Jsx.resi.txt | 12 ++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index fc56a0abe..a7316aa44 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -569,6 +569,12 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = iterator.structure iterator str |> ignore; if !found = false then if debug then Printf.printf "XXX Not found!\n"; !result) + else if Filename.check_suffix path ".resi" then ( + let parser = Res_driver.parsingEngine.parseInterface ~forPrinter:false in + let {Res_driver.parsetree = signature} = parser ~filename:currentFile in + iterator.signature iterator signature |> ignore; + if !found = false then if debug then Printf.printf "XXX Not found!\n"; + !result) else None let completion ~debug ~path ~pos ~currentFile = diff --git a/analysis/tests/src/Jsx.resi b/analysis/tests/src/Jsx.resi index 4c00a5530..7a42c136c 100644 --- a/analysis/tests/src/Jsx.resi +++ b/analysis/tests/src/Jsx.resi @@ -2,5 +2,7 @@ let make: (~first: string) => React.element // ^hov -let y : int -// ^hov \ No newline at end of file +let y: int +// ^hov + +// ^com type t = React.e diff --git a/analysis/tests/src/expected/Jsx.resi.txt b/analysis/tests/src/expected/Jsx.resi.txt index f82257ca3..3d44338b2 100644 --- a/analysis/tests/src/expected/Jsx.resi.txt +++ b/analysis/tests/src/expected/Jsx.resi.txt @@ -4,3 +4,15 @@ Hover tests/src/Jsx.resi 1:4 Hover tests/src/Jsx.resi 4:4 {"contents": "```rescript\nint\n```"} +Complete tests/src/Jsx.resi 6:3 +posCursor:[7:17] posNoWhite:[7:16] Found type:[7:10->7:17] +Ptyp_constr React.e:[7:10->7:17] +Completable: Cdotpath([React, e],Type) +[{ + "label": "element", + "kind": 22, + "tags": [], + "detail": "type element", + "documentation": null + }] + From e4d93c7334b9a3b8946d72f56ac84cde0cc5e78d Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 15 Apr 2022 10:09:16 +0200 Subject: [PATCH 045/135] Types in value declarations. --- analysis/src/vendor/res_outcome_printer/res_core.ml | 2 +- analysis/tests/src/Jsx.res | 2 ++ analysis/tests/src/Jsx.resi | 2 ++ analysis/tests/src/expected/Jsx.res.txt | 12 ++++++++++++ analysis/tests/src/expected/Jsx.resi.txt | 12 ++++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/analysis/src/vendor/res_outcome_printer/res_core.ml b/analysis/src/vendor/res_outcome_printer/res_core.ml index d722fa391..2085ddf38 100644 --- a/analysis/src/vendor/res_outcome_printer/res_core.ml +++ b/analysis/src/vendor/res_outcome_printer/res_core.ml @@ -655,7 +655,7 @@ let parseValuePath p = aux p (Ldot (path, uident)) | token -> Parser.err p (Diagnostics.unexpected token p.breadcrumbs); - Longident.Lident "_" + Longident.Ldot (path, "$") in let ident = match p.Parser.token with | Lident ident -> Longident.Lident ident diff --git a/analysis/tests/src/Jsx.res b/analysis/tests/src/Jsx.res index a0df80061..508d82e9a 100644 --- a/analysis/tests/src/Jsx.res +++ b/analysis/tests/src/Jsx.res @@ -69,3 +69,5 @@ module WithChildren = { let _ =
//^com 72:16] +Ptyp_constr React.e:[72:9->72:16] +Completable: Cdotpath([React, e],Type) +[{ + "label": "element", + "kind": 22, + "tags": [], + "detail": "type element", + "documentation": null + }] + diff --git a/analysis/tests/src/expected/Jsx.resi.txt b/analysis/tests/src/expected/Jsx.resi.txt index 3d44338b2..cc3ee0b07 100644 --- a/analysis/tests/src/expected/Jsx.resi.txt +++ b/analysis/tests/src/expected/Jsx.resi.txt @@ -16,3 +16,15 @@ Completable: Cdotpath([React, e],Type) "documentation": null }] +Complete tests/src/Jsx.resi 8:3 +posCursor:[9:16] posNoWhite:[9:15] Found type:[9:9->9:16] +Ptyp_constr React.e:[9:9->9:16] +Completable: Cdotpath([React, e],Type) +[{ + "label": "element", + "kind": 22, + "tags": [], + "detail": "type element", + "documentation": null + }] + From 3665736208cd74c925811e071bbfe50e2e9d2042 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 15 Apr 2022 10:28:49 +0200 Subject: [PATCH 046/135] Complete module prefix of type path. --- .../src/vendor/res_outcome_printer/res_core.ml | 18 ++++++++++++++---- analysis/tests/src/Jsx.res | 1 + analysis/tests/src/expected/Jsx.res.txt | 12 ++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/analysis/src/vendor/res_outcome_printer/res_core.ml b/analysis/src/vendor/res_outcome_printer/res_core.ml index 2085ddf38..fc018ba65 100644 --- a/analysis/src/vendor/res_outcome_printer/res_core.ml +++ b/analysis/src/vendor/res_outcome_printer/res_core.ml @@ -651,8 +651,13 @@ let parseValuePath p = | Lident ident -> Longident.Ldot(path, ident) | Uident uident -> Parser.next p; - Parser.expect Dot p; - aux p (Ldot (path, uident)) + if p.Parser.token = Dot then ( + Parser.expect Dot p; + aux p (Ldot (path, uident)) + ) else ( + Parser.err p (Diagnostics.unexpected p.Parser.token p.breadcrumbs); + path + ) | token -> Parser.err p (Diagnostics.unexpected token p.breadcrumbs); Longident.Ldot (path, "$") @@ -661,8 +666,13 @@ let parseValuePath p = | Lident ident -> Longident.Lident ident | Uident ident -> Parser.next p; - Parser.expect Dot p; - aux p (Lident ident) + if p.Parser.token = Dot then ( + Parser.expect Dot p; + aux p (Lident ident) + ) else ( + Parser.err p (Diagnostics.unexpected p.Parser.token p.breadcrumbs); + Longident.Lident ident + ) | token -> Parser.err p (Diagnostics.unexpected token p.breadcrumbs); Longident.Lident "_" diff --git a/analysis/tests/src/Jsx.res b/analysis/tests/src/Jsx.res index 508d82e9a..0101c68bc 100644 --- a/analysis/tests/src/Jsx.res +++ b/analysis/tests/src/Jsx.res @@ -71,3 +71,4 @@ let _ =
//^com 75:0] +Ptyp_constr ReactDOMR:[73:9->75:0] +Completable: Cdotpath([ReactDOMR],Type) +[{ + "label": "ReactDOMRe", + "kind": 9, + "tags": [], + "detail": "file module", + "documentation": null + }] + From b2377eccb104b1b24abb14337ea01058891b64e2 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 15 Apr 2022 11:33:13 +0200 Subject: [PATCH 047/135] Filter completion by kind: Type or Value. Modules belong to both, as they in turn contain both types and values. --- analysis/src/NewCompletions.ml | 36 +++++++++--- analysis/tests/src/Completion.res | 4 ++ .../tests/src/expected/Completion.res.txt | 56 +++++++++++-------- 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 9a0578762..9e1f9edc5 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -935,7 +935,10 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens |> List.map mkLabel) @ keyLabels | Cjsx (componentPath, prefix, identsSeen) -> - let completions = processDotPath ~exact:true (componentPath @ ["make"]) in + let completions = + processDotPath ~pathKind:PartialParser.Value ~exact:true + (componentPath @ ["make"]) + in let labels = match completions with | ({Completion.kind = Completion.Value typ}, _env) :: _ -> @@ -994,7 +997,7 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens |> List.map mkLabel) @ keyLabels | Cdotpath (dotpath, pathKind) -> - let completions = dotpath |> processDotPath ~exact:false in + let completions = dotpath |> processDotPath ~pathKind ~exact:false in (* TODO(#107): figure out why we're getting duplicates. *) completions |> Utils.dedup |> List.map (fun ({Completion.name; deprecated; docstring; kind}, _env) -> @@ -1055,7 +1058,9 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens in match pipeIdPath with | x :: fieldNames -> ( - match [x] |> processDotPath ~exact:true with + match + [x] |> processDotPath ~pathKind:PartialParser.Value ~exact:true + with | ({Completion.kind = Value typ}, env) :: _ -> ( match getFields ~env ~typ fieldNames with | None -> None @@ -1105,7 +1110,9 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens else modulePathMinusOpens ^ "." ^ name in let dotpath = modulePath @ [partialName] in - let declareds = dotpath |> processDotPath ~exact:false in + let declareds = + dotpath |> processDotPath ~pathKind:PartialParser.Value ~exact:false + in declareds |> List.filter (fun ({Completion.kind}, _env) -> match kind with Completion.Value _ -> true | _ -> false) @@ -1162,7 +1169,9 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens |> List.map mkDecorator | Clabel (funPath, prefix, identsSeen) -> let labels = - match funPath |> processDotPath ~exact:true with + match + funPath |> processDotPath ~pathKind:PartialParser.Value ~exact:true + with | ({Completion.kind = Value typ}, _env) :: _ -> let rec getLabels (t : Types.type_expr) = match t.desc with @@ -1211,7 +1220,7 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens in let env0 = QueryEnv.fromFile full.file in let env, fields = - match lhs |> processDotPath ~exact:true with + match lhs |> processDotPath ~pathKind:PartialParser.Value ~exact:true with | ({Completion.kind = Value typ}, env) :: _ -> getObjectFields ~env typ | _ -> (env0, []) in @@ -1229,8 +1238,14 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens let computeCompletions ~completable ~full ~pos ~rawOpens = let package = full.package in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in - let processDotPath ~exact dotpath = + let processDotPath ~(pathKind : PartialParser.pathKind) ~exact dotpath = let completions = getCompletions ~full ~rawOpens ~allFiles ~pos ~dotpath in + let filterKind (kind : Completion.kind) = + match kind with + | Value _ | Field _ | Constructor _ -> pathKind = Value + | Type _ -> pathKind = Type + | Module _ | FileModule _ -> true + in match dotpath |> List.rev with | last :: _ when exact -> (* Heuristic to approximate scope. @@ -1247,8 +1262,11 @@ let computeCompletions ~completable ~full ~pos ~rawOpens = | [] | [_] -> decls in completions - |> List.filter (fun ({Completion.name}, _env) -> name = last) + |> List.filter (fun ({Completion.name; kind}, _env) -> + name = last && filterKind kind) |> prioritize - | _ -> completions + | _ -> + completions + |> List.filter (fun ({Completion.kind}, _env) -> filterKind kind) in completable |> processCompletable ~processDotPath ~full ~package ~rawOpens diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index 51af71429..ebb4106ba 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -142,3 +142,7 @@ module WithChildren = { // ^com type t = Js.n // ^com type t = ForAuto. + +type z = | Allo | Asterix | Baba + +// ^com let q = As diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 13618dab4..5beb8487d 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -731,6 +731,26 @@ DocumentSymbol tests/src/Completion.res "name": "make", "kind": 12, "location": {"uri": "Completion.res", "range": {"start": {"line": 138, "character": 2}, "end": {"line": 138, "character": 69}}} +}, +{ + "name": "z", + "kind": 26, + "location": {"uri": "Completion.res", "range": {"start": {"line": 145, "character": 0}, "end": {"line": 145, "character": 32}}} +}, +{ + "name": "Allo", + "kind": 22, + "location": {"uri": "Completion.res", "range": {"start": {"line": 145, "character": 9}, "end": {"line": 145, "character": 15}}} +}, +{ + "name": "Asterix", + "kind": 22, + "location": {"uri": "Completion.res", "range": {"start": {"line": 145, "character": 16}, "end": {"line": 145, "character": 25}}} +}, +{ + "name": "Baba", + "kind": 22, + "location": {"uri": "Completion.res", "range": {"start": {"line": 145, "character": 26}, "end": {"line": 145, "character": 32}}} } ] @@ -1015,18 +1035,6 @@ posCursor:[142:14] posNoWhite:[142:13] Found type:[142:10->142:14] Ptyp_constr Js.n:[142:10->142:14] Completable: Cdotpath([Js, n],Type) [{ - "label": "null", - "kind": 12, - "tags": [], - "detail": "null<'a>", - "documentation": {"kind": "markdown", "value": " The same as [empty] in {!Js.Null} will be compiled as [null]"} - }, { - "label": "nullToOption", - "kind": 12, - "tags": [], - "detail": "null<'a> => option<'a>", - "documentation": null - }, { "label": "null_undefined", "kind": 22, "tags": [], @@ -1051,18 +1059,6 @@ posCursor:[143:18] posNoWhite:[143:17] Found type:[143:10->143:18] Ptyp_constr ForAuto.:[143:10->143:18] Completable: Cdotpath([ForAuto, ""],Type) [{ - "label": "abc", - "kind": 12, - "tags": [], - "detail": "(t, int) => t", - "documentation": null - }, { - "label": "abd", - "kind": 12, - "tags": [], - "detail": "(t, int) => t", - "documentation": null - }, { "label": "t", "kind": 22, "tags": [], @@ -1070,3 +1066,15 @@ Completable: Cdotpath([ForAuto, ""],Type) "documentation": null }] +Complete tests/src/Completion.res 146:3 +posCursor:[147:11] posNoWhite:[147:10] Found expr:[147:9->147:11] +Pexp_construct As:[147:9->147:11] None +Completable: Cdotpath([As],Value) +[{ + "label": "Asterix", + "kind": 4, + "tags": [], + "detail": "Asterix\n\ntype z = Allo | Asterix | Baba", + "documentation": null + }] + From 2d1943790b859e63688d3ad889de5e739b1e71bf Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 15 Apr 2022 11:51:45 +0200 Subject: [PATCH 048/135] In jsx, only complete with modules. In " Loc.hasPos ~pos:posBeforeCursor then setResult (PartialParser.Cdotpath - (flattenLongIdent ~jsx:true compName.txt, Value)) + (flattenLongIdent ~jsx:true compName.txt, Component)) | Pexp_apply ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, [ diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 9e1f9edc5..b446a1fc8 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -1244,7 +1244,7 @@ let computeCompletions ~completable ~full ~pos ~rawOpens = match kind with | Value _ | Field _ | Constructor _ -> pathKind = Value | Type _ -> pathKind = Type - | Module _ | FileModule _ -> true + | Module _ | FileModule _ -> true (* Component only matches this case *) in match dotpath |> List.rev with | last :: _ when exact -> diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 1787cb440..3b2c8d2a6 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -1,5 +1,5 @@ type pipe = PipeId of string list | PipeArray | PipeString -type pathKind = Type | Value +type pathKind = Type | Value | Component type completable = | Cdecorator of string (** e.g. @module *) @@ -21,7 +21,10 @@ let completableToString = "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" | Cdotpath (sl, k) -> "Cdotpath(" ^ (sl |> list) ^ "," - ^ (match k with Value -> "Value" | Type -> "Type") + ^ (match k with + | Value -> "Value" + | Type -> "Type" + | Component -> "Component") ^ ")" | Cjsx (sl1, s, sl2) -> "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 5beb8487d..a58f85dfa 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -953,7 +953,7 @@ posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] JSX 126:4] > _children:None posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] Pexp_ident O..createElement:[126:2->126:4] -Completable: Cdotpath([O, ""],Value) +Completable: Cdotpath([O, ""],Component) [{ "label": "Comp", "kind": 9, From 6fa3bd57254d9d5c64078f176c798a3dbac6316e Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 15 Apr 2022 12:28:15 +0200 Subject: [PATCH 049/135] Contextual completion of fields. --- analysis/src/Commands.ml | 12 ++++- analysis/src/NewCompletions.ml | 7 ++- analysis/src/PartialParser.ml | 5 +- analysis/tests/src/Jsx.res | 12 +++++ .../tests/src/expected/Completion.res.txt | 12 ++--- analysis/tests/src/expected/Jsx.res.txt | 46 ++++++++++++++++++- 6 files changed, 80 insertions(+), 14 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index c7544c3c2..c5341e904 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -425,12 +425,20 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = in if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then match digLhs e with - | Some path -> setResult (PartialParser.Cdotpath (path, Value)) + | Some path -> + let dotPath = + match fieldName.txt with + | Lident name -> path @ [name] + | _ -> + (* Case x.M.field ignore the x part *) + flattenLongIdent fieldName.txt + in + setResult (PartialParser.Cdotpath (dotPath, Field)) | None -> () else if Loc.end_ e.pexp_loc = posBeforeCursor then match digLhs e with | Some path -> - setResult (PartialParser.Cdotpath (path @ [""], Value)) + setResult (PartialParser.Cdotpath (path @ [""], Field)) | None -> ()) | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) when Res_parsetree_viewer.isJsxExpression expr -> diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index b446a1fc8..de4b44243 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -1242,9 +1242,12 @@ let computeCompletions ~completable ~full ~pos ~rawOpens = let completions = getCompletions ~full ~rawOpens ~allFiles ~pos ~dotpath in let filterKind (kind : Completion.kind) = match kind with - | Value _ | Field _ | Constructor _ -> pathKind = Value + | Value _ | Constructor _ -> pathKind = Value + | Field _ -> pathKind = Field | Type _ -> pathKind = Type - | Module _ | FileModule _ -> true (* Component only matches this case *) + | Module _ | FileModule _ -> + (* Component only matches this case *) + true in match dotpath |> List.rev with | last :: _ when exact -> diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 3b2c8d2a6..269c5af6f 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -1,5 +1,5 @@ type pipe = PipeId of string list | PipeArray | PipeString -type pathKind = Type | Value | Component +type pathKind = Type | Value | Component | Field type completable = | Cdecorator of string (** e.g. @module *) @@ -24,7 +24,8 @@ let completableToString = ^ (match k with | Value -> "Value" | Type -> "Type" - | Component -> "Component") + | Component -> "Component" + | Field -> "Field") ^ ")" | Cjsx (sl1, s, sl2) -> "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" diff --git a/analysis/tests/src/Jsx.res b/analysis/tests/src/Jsx.res index 0101c68bc..e0b5a7f4a 100644 --- a/analysis/tests/src/Jsx.res +++ b/analysis/tests/src/Jsx.res @@ -72,3 +72,15 @@ let _ =
//^com let c : React.e //^com let c : ReactDOMR + +module DefineSomeFields = { + type r = {thisField: int, thatField: string} + let thisValue = 10 +// ^com let foo x = x.th +} + +// ^com let q = DefineSomeFields. + +// ^com let foo x = x.DefineSomeFields.th + +let _ = x => x.DefineSomeFields.thisField + DefineSomeFields.thisValue diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index a58f85dfa..753d8ced2 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -883,7 +883,7 @@ Completable: Cobj([no], [x, y], "") Complete tests/src/Completion.res 88:3 posCursor:[89:3] posNoWhite:[89:2] Found expr:[89:1->93:3] Pexp_field [89:1->89:2] _:[93:0->93:3] -Completable: Cdotpath([r, ""],Value) +Completable: Cdotpath([r, ""],Field) [{ "label": "x", "kind": 5, @@ -901,7 +901,7 @@ Completable: Cdotpath([r, ""],Value) Complete tests/src/Completion.res 90:3 posCursor:[91:19] posNoWhite:[91:18] Found expr:[91:1->93:3] Pexp_field [91:1->91:18] _:[93:0->93:3] -Completable: Cdotpath([Obj, Rec, recordVal, ""],Value) +Completable: Cdotpath([Obj, Rec, recordVal, ""],Field) [{ "label": "xx", "kind": 5, @@ -965,7 +965,7 @@ Completable: Cdotpath([O, ""],Component) Complete tests/src/Completion.res 130:3 posCursor:[131:6] posNoWhite:[131:5] Found expr:[131:1->136:6] Pexp_field [131:1->131:5] _:[136:0->136:6] -Completable: Cdotpath([q, aa, ""],Value) +Completable: Cdotpath([q, aa, ""],Field) [{ "label": "x", "kind": 5, @@ -983,12 +983,12 @@ Completable: Cdotpath([q, aa, ""],Value) Complete tests/src/Completion.res 131:3 posCursor:[132:7] posNoWhite:[132:6] Found expr:[132:1->132:7] Pexp_field [132:1->132:5] n:[132:6->132:7] -Completable: Cdotpath([q, aa],Value) +Completable: Cdotpath([q, aa, n],Field) [{ - "label": "aa", + "label": "name", "kind": 5, "tags": [], - "detail": "aa: aa\n\ntype bb = {aa: aa, w: int}", + "detail": "name: string\n\ntype aa = {x: int, name: string}", "documentation": null }] diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 18cab132f..d83aecbea 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -376,8 +376,8 @@ Completable: Cdotpath([React, e],Type) }] Complete tests/src/Jsx.res 72:2 -posCursor:[73:18] posNoWhite:[73:17] Found type:[73:9->75:0] -Ptyp_constr ReactDOMR:[73:9->75:0] +posCursor:[73:18] posNoWhite:[73:17] Found type:[73:9->75:6] +Ptyp_constr ReactDOMR:[73:9->75:6] Completable: Cdotpath([ReactDOMR],Type) [{ "label": "ReactDOMRe", @@ -387,3 +387,45 @@ Completable: Cdotpath([ReactDOMR],Type) "documentation": null }] +Complete tests/src/Jsx.res 77:3 +posCursor:[78:17] posNoWhite:[78:16] Found expr:[78:9->78:17] +Pexp_apply ...[78:11->78:12] (...[78:9->78:10], ...[78:13->78:17]) +posCursor:[78:17] posNoWhite:[78:16] Found expr:[78:13->78:17] +Pexp_field [78:13->78:14] th:[78:15->78:17] +Completable: Cdotpath([x, th],Field) +[] + +Complete tests/src/Jsx.res 80:3 +posCursor:[81:26] posNoWhite:[81:25] Found expr:[81:9->85:3] +Pexp_ident DefineSomeFields.:[81:9->85:3] +posCursor:[81:26] posNoWhite:[81:25] Found expr:[0:-1->85:70] +Pexp_apply ...[85:6->85:7] (...[85:8->85:70]) +Completable: Cdotpath([DefineSomeFields, ""],Value) +[{ + "label": "thisValue", + "kind": 12, + "tags": [], + "detail": "int", + "documentation": null + }] + +Complete tests/src/Jsx.res 82:3 +posCursor:[83:34] posNoWhite:[83:33] Found expr:[83:9->83:34] +Pexp_apply ...[83:11->83:12] (...[83:9->83:10], ...[83:13->83:34]) +posCursor:[83:34] posNoWhite:[83:33] Found expr:[83:13->83:34] +Pexp_field [83:13->83:14] DefineSomeFields.th:[83:15->83:34] +Completable: Cdotpath([DefineSomeFields, th],Field) +[{ + "label": "thisField", + "kind": 5, + "tags": [], + "detail": "thisField: int\n\ntype r = {thisField: int, thatField: string}", + "documentation": null + }, { + "label": "thatField", + "kind": 5, + "tags": [], + "detail": "thatField: string\n\ntype r = {thisField: int, thatField: string}", + "documentation": null + }] + From 78587a70f063a40c78c319c9fce25c559aca7cbf Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 15 Apr 2022 12:34:26 +0200 Subject: [PATCH 050/135] Refactor. --- analysis/src/NewCompletions.ml | 30 +++++++++++++++++++----------- analysis/src/PartialParser.ml | 6 ++++-- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index de4b44243..2af7377bc 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -936,7 +936,7 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens @ keyLabels | Cjsx (componentPath, prefix, identsSeen) -> let completions = - processDotPath ~pathKind:PartialParser.Value ~exact:true + processDotPath ~completionContext:PartialParser.Value ~exact:true (componentPath @ ["make"]) in let labels = @@ -996,8 +996,10 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel) @ keyLabels - | Cdotpath (dotpath, pathKind) -> - let completions = dotpath |> processDotPath ~pathKind ~exact:false in + | Cdotpath (dotpath, completionContext) -> + let completions = + dotpath |> processDotPath ~completionContext ~exact:false + in (* TODO(#107): figure out why we're getting duplicates. *) completions |> Utils.dedup |> List.map (fun ({Completion.name; deprecated; docstring; kind}, _env) -> @@ -1059,7 +1061,8 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens match pipeIdPath with | x :: fieldNames -> ( match - [x] |> processDotPath ~pathKind:PartialParser.Value ~exact:true + [x] + |> processDotPath ~completionContext:PartialParser.Value ~exact:true with | ({Completion.kind = Value typ}, env) :: _ -> ( match getFields ~env ~typ fieldNames with @@ -1111,7 +1114,8 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens in let dotpath = modulePath @ [partialName] in let declareds = - dotpath |> processDotPath ~pathKind:PartialParser.Value ~exact:false + dotpath + |> processDotPath ~completionContext:PartialParser.Value ~exact:false in declareds |> List.filter (fun ({Completion.kind}, _env) -> @@ -1170,7 +1174,8 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens | Clabel (funPath, prefix, identsSeen) -> let labels = match - funPath |> processDotPath ~pathKind:PartialParser.Value ~exact:true + funPath + |> processDotPath ~completionContext:PartialParser.Value ~exact:true with | ({Completion.kind = Value typ}, _env) :: _ -> let rec getLabels (t : Types.type_expr) = @@ -1220,7 +1225,9 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens in let env0 = QueryEnv.fromFile full.file in let env, fields = - match lhs |> processDotPath ~pathKind:PartialParser.Value ~exact:true with + match + lhs |> processDotPath ~completionContext:PartialParser.Value ~exact:true + with | ({Completion.kind = Value typ}, env) :: _ -> getObjectFields ~env typ | _ -> (env0, []) in @@ -1238,13 +1245,14 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens let computeCompletions ~completable ~full ~pos ~rawOpens = let package = full.package in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in - let processDotPath ~(pathKind : PartialParser.pathKind) ~exact dotpath = + let processDotPath ~(completionContext : PartialParser.completionContext) + ~exact dotpath = let completions = getCompletions ~full ~rawOpens ~allFiles ~pos ~dotpath in let filterKind (kind : Completion.kind) = match kind with - | Value _ | Constructor _ -> pathKind = Value - | Field _ -> pathKind = Field - | Type _ -> pathKind = Type + | Value _ | Constructor _ -> completionContext = Value + | Field _ -> completionContext = Field + | Type _ -> completionContext = Type | Module _ | FileModule _ -> (* Component only matches this case *) true diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 269c5af6f..c0b77667a 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -1,11 +1,13 @@ type pipe = PipeId of string list | PipeArray | PipeString -type pathKind = Type | Value | Component | Field + +(* Completion context *) +type completionContext = Type | Value | Component | Field type completable = | Cdecorator of string (** e.g. @module *) | Clabel of string list * string * string list (** e.g. (["M", "foo"], "label", ["l1", "l2"]) for M.foo(...~l1...~l2...~label...) *) - | Cdotpath of string list * pathKind (** e.g. ["M", "foo"] for M.foo *) + | Cdotpath of string list * completionContext (** e.g. ["M", "foo"] for M.foo *) | Cjsx of string list * string * string list (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for Date: Sat, 16 Apr 2022 09:59:21 +0200 Subject: [PATCH 051/135] Add support for module ids. --- analysis/src/Commands.ml | 29 +++++++++++++++++++ analysis/tests/src/Completion.res | 2 ++ .../tests/src/expected/Completion.res.txt | 17 +++++++++++ 3 files changed, 48 insertions(+) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index c5341e904..9ec52b4a3 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -556,11 +556,40 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | _ -> ()); Ast_iterator.default_iterator.typ iterator core_type in + let module_expr (iterator : Ast_iterator.iterator) + (me : Parsetree.module_expr) = + (match me.pmod_desc with + | Pmod_ident id when id.loc |> Loc.hasPos ~pos:posBeforeCursor -> + if debug then + Printf.printf "Pmod_ident %s:%s\n" + (flattenLongIdent id.txt |> String.concat ".") + (Loc.toString id.loc); + found := true; + setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Component)) + | _ -> ()); + Ast_iterator.default_iterator.module_expr iterator me + in + let module_type (iterator : Ast_iterator.iterator) + (mt : Parsetree.module_type) = + (match mt.pmty_desc with + | Pmty_ident id when id.loc |> Loc.hasPos ~pos:posBeforeCursor -> + if debug then + Printf.printf "Pmty_ident %s:%s\n" + (flattenLongIdent id.txt |> String.concat ".") + (Loc.toString id.loc); + found := true; + setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Component)) + | _ -> ()); + Ast_iterator.default_iterator.module_type iterator mt + in + let iterator = { Ast_iterator.default_iterator with attribute; expr; + module_expr; + module_type; signature; signature_item; structure; diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index ebb4106ba..ce3a40489 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -146,3 +146,5 @@ module WithChildren = { type z = | Allo | Asterix | Baba // ^com let q = As + +// ^com module M = For diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 753d8ced2..d11527c14 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1078,3 +1078,20 @@ Completable: Cdotpath([As],Value) "documentation": null }] +Complete tests/src/Completion.res 148:3 +Pmod_ident For:[149:12->149:15] +Completable: Cdotpath([For],Component) +[{ + "label": "ForAuto", + "kind": 9, + "tags": [], + "detail": "module", + "documentation": null + }, { + "label": "Format", + "kind": 9, + "tags": [], + "detail": "file module", + "documentation": null + }] + From 13119da14ee2b09b69c568c55fb7487cdd09a7d2 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sat, 16 Apr 2022 10:13:02 +0200 Subject: [PATCH 052/135] Refactor --- analysis/src/Commands.ml | 6 +++--- analysis/src/NewCompletions.ml | 30 +++++++++++++++++++----------- analysis/src/PartialParser.ml | 4 ++-- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 9ec52b4a3..c539aa15b 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -464,7 +464,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then setResult (PartialParser.Cdotpath - (flattenLongIdent ~jsx:true compName.txt, Component)) + (flattenLongIdent ~jsx:true compName.txt, Module)) | Pexp_apply ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, [ @@ -565,7 +565,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); found := true; - setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Component)) + setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Module)) | _ -> ()); Ast_iterator.default_iterator.module_expr iterator me in @@ -578,7 +578,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); found := true; - setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Component)) + setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Module)) | _ -> ()); Ast_iterator.default_iterator.module_type iterator mt in diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 2af7377bc..7dbc28dae 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -1245,17 +1245,24 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens let computeCompletions ~completable ~full ~pos ~rawOpens = let package = full.package in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in - let processDotPath ~(completionContext : PartialParser.completionContext) - ~exact dotpath = + let processDotPath ~completionContext ~exact dotpath = let completions = getCompletions ~full ~rawOpens ~allFiles ~pos ~dotpath in - let filterKind (kind : Completion.kind) = - match kind with - | Value _ | Constructor _ -> completionContext = Value - | Field _ -> completionContext = Field - | Type _ -> completionContext = Type - | Module _ | FileModule _ -> - (* Component only matches this case *) + let filterKind ~(completionContext : PartialParser.completionContext) + ~(kind : Completion.kind) = + match (completionContext, kind) with + | Module, (Module _ | FileModule _) -> true + | Module, _ -> false + | (Field | Type | Value), (Module _ | FileModule _) -> + (* M.field M.type M.value *) true + | Value, (Value _ | Constructor _) -> + (* x Red *) + true + | Value, _ -> false + | Field, Field _ -> true + | Field, _ -> false + | Type, Type _ -> true + | Type, _ -> false in match dotpath |> List.rev with | last :: _ when exact -> @@ -1274,10 +1281,11 @@ let computeCompletions ~completable ~full ~pos ~rawOpens = in completions |> List.filter (fun ({Completion.name; kind}, _env) -> - name = last && filterKind kind) + name = last && filterKind ~completionContext ~kind) |> prioritize | _ -> completions - |> List.filter (fun ({Completion.kind}, _env) -> filterKind kind) + |> List.filter (fun ({Completion.kind}, _env) -> + filterKind ~completionContext ~kind) in completable |> processCompletable ~processDotPath ~full ~package ~rawOpens diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index c0b77667a..244cec704 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -1,7 +1,7 @@ type pipe = PipeId of string list | PipeArray | PipeString (* Completion context *) -type completionContext = Type | Value | Component | Field +type completionContext = Type | Value | Module | Field type completable = | Cdecorator of string (** e.g. @module *) @@ -26,7 +26,7 @@ let completableToString = ^ (match k with | Value -> "Value" | Type -> "Type" - | Component -> "Component" + | Module -> "Component" | Field -> "Field") ^ ")" | Cjsx (sl1, s, sl2) -> From fc2593dd7901b90379749bbb84918372fd912977 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 07:10:25 +0200 Subject: [PATCH 053/135] Clean up extensions. the %%private extension adds a dummy module called _local... Prevent from adding it in the first place, instead of trying to remove it later during autocomplete --- analysis/src/DocumentSymbol.ml | 2 +- analysis/src/NewCompletions.ml | 8 ++---- analysis/src/ProcessCmt.ml | 4 ++- analysis/src/SharedTypes.ml | 2 -- analysis/tests/src/Completion.res | 7 +++++ .../tests/src/expected/Completion.res.txt | 27 +++++++++++++++++++ 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/analysis/src/DocumentSymbol.ml b/analysis/src/DocumentSymbol.ml index 79cfb271c..65e38b555 100644 --- a/analysis/src/DocumentSymbol.ml +++ b/analysis/src/DocumentSymbol.ml @@ -91,7 +91,7 @@ let command ~path = | Pstr_module mb -> processModuleBinding mb | Pstr_recmodule mbs -> mbs |> List.iter processModuleBinding | Pstr_exception ec -> processExtensionConstructor ec - | _ -> Ast_iterator.default_iterator.structure_item iterator item); + | _ -> ()); Ast_iterator.default_iterator.structure_item iterator item in let signature_item (iterator : Ast_iterator.iterator) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 7dbc28dae..cedbb658d 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -707,14 +707,10 @@ let valueCompletions ~(env : QueryEnv.t) suffix = Log.log (" - Completing in " ^ Uri2.toString env.file.uri); let results = [] in let results = - if suffix = "" || isCapitalized suffix then ( - (* Get rid of lowercase modules (#417) *) - Exported.iter env.exported Exported.Module (fun name _ -> - if not (isCapitalized name) then - Exported.removeModule env.exported name); + if suffix = "" || isCapitalized suffix then results @ completionForExportedModules ~env ~suffix - @ completionForConstructors ~env ~suffix) + @ completionForConstructors ~env ~suffix else results in let results = diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index 7fbb1def3..9d3d4a5de 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -342,7 +342,9 @@ let rec forStructureItem ~env ~(exported : Exported.t) item = bindings; !items | Tstr_module - {mb_id; mb_attributes; mb_loc; mb_name = name; mb_expr = {mod_desc}} -> + {mb_id; mb_attributes; mb_loc; mb_name = name; mb_expr = {mod_desc}} + when not (String.length name.txt >= 6 && String.sub name.txt 0 6 = "local_") + (* %%private generates a dummy module called local_... *) -> let item = forModule env mod_desc name.txt in let declared = addItem ~item ~name ~extent:mb_loc ~stamp:(Ident.binding_time mb_id) ~env diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index be64933ab..6016daee0 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -73,8 +73,6 @@ module Exported = struct | Module -> t.modules_ in Hashtbl.iter f tbl - - let removeModule {modules_} name = Hashtbl.remove modules_ name end module Module = struct diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index ce3a40489..9354b01d0 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -148,3 +148,10 @@ type z = | Allo | Asterix | Baba // ^com let q = As // ^com module M = For + +module Private = { + %%private(let awr = 3) + let b = awr +} + +// ^com Private. diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index d11527c14..1692fba04 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -751,6 +751,21 @@ DocumentSymbol tests/src/Completion.res "name": "Baba", "kind": 22, "location": {"uri": "Completion.res", "range": {"start": {"line": 145, "character": 26}, "end": {"line": 145, "character": 32}}} +}, +{ + "name": "Private", + "kind": 2, + "location": {"uri": "Completion.res", "range": {"start": {"line": 151, "character": 7}, "end": {"line": 154, "character": 1}}} +}, +{ + "name": "awr", + "kind": 16, + "location": {"uri": "Completion.res", "range": {"start": {"line": 152, "character": 12}, "end": {"line": 152, "character": 23}}} +}, +{ + "name": "b", + "kind": 13, + "location": {"uri": "Completion.res", "range": {"start": {"line": 153, "character": 2}, "end": {"line": 153, "character": 13}}} } ] @@ -1095,3 +1110,15 @@ Completable: Cdotpath([For],Component) "documentation": null }] +Complete tests/src/Completion.res 155:3 +posCursor:[156:9] posNoWhite:[156:8] Found expr:[156:1->158:0] +Pexp_ident Private.:[156:1->158:0] +Completable: Cdotpath([Private, ""],Value) +[{ + "label": "b", + "kind": 12, + "tags": [], + "detail": "int", + "documentation": null + }] + From 4473d27cfbb8680b83c8eab111268efaa5e0c952 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 07:22:27 +0200 Subject: [PATCH 054/135] cleanup --- analysis/src/NewCompletions.ml | 57 +++++++---------------- analysis/tests/src/expected/Debug.res.txt | 2 - 2 files changed, 17 insertions(+), 42 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index cedbb658d..437f886fb 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -683,46 +683,23 @@ let detail name (kind : Completion.kind) = | Field ({typ}, s) -> name ^ ": " ^ (typ |> Shared.typeToString) ^ "\n\n" ^ s | Constructor (c, s) -> showConstructor c ^ "\n\n" ^ s -let localValueCompletions ~pos ~(env : QueryEnv.t) suffix = - let results = [] in +let localCompletions ~pos ~(env : QueryEnv.t) suffix = Log.log "---------------- LOCAL VAL"; - let results = - if suffix = "" || isCapitalized suffix then - results - @ completionForDeclaredModules ~pos ~env ~suffix - @ completionForConstructors ~env ~suffix - else results - in - let results = - if suffix = "" || not (isCapitalized suffix) then - results - @ completionForDeclaredValues ~pos ~env ~suffix - @ completionForDeclaredTypes ~pos ~env ~suffix - @ completionForFields ~env ~suffix - else results - in - results |> List.map (fun r -> (r, env)) + completionForDeclaredModules ~pos ~env ~suffix + @ completionForConstructors ~env ~suffix + @ completionForDeclaredValues ~pos ~env ~suffix + @ completionForDeclaredTypes ~pos ~env ~suffix + @ completionForFields ~env ~suffix + |> List.map (fun r -> (r, env)) -let valueCompletions ~(env : QueryEnv.t) suffix = +let allCompletions ~(env : QueryEnv.t) suffix = Log.log (" - Completing in " ^ Uri2.toString env.file.uri); - let results = [] in - let results = - if suffix = "" || isCapitalized suffix then - results - @ completionForExportedModules ~env ~suffix - @ completionForConstructors ~env ~suffix - else results - in - let results = - if suffix = "" || not (isCapitalized suffix) then ( - Log.log " -- not capitalized"; - results - @ completionForExportedValues ~env ~suffix - @ completionForExportedTypes ~env ~suffix - @ completionForFields ~env ~suffix) - else results - in - results |> List.map (fun r -> (r, env)) + completionForExportedModules ~env ~suffix + @ completionForConstructors ~env ~suffix + @ completionForExportedValues ~env ~suffix + @ completionForExportedTypes ~env ~suffix + @ completionForFields ~env ~suffix + |> List.map (fun r -> (r, env)) let attributeCompletions ~(env : QueryEnv.t) ~suffix = let results = [] in @@ -800,13 +777,13 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos ~dotpath = match dotpath with | [] -> [] | [suffix] -> - let locallyDefinedValues = localValueCompletions ~pos ~env suffix in + let locallyDefinedValues = localCompletions ~pos ~env suffix in let alreadyUsedIdentifiers = Hashtbl.create 10 in let valuesFromOpens = opens |> List.fold_left (fun results env -> - let completionsFromThisOpen = valueCompletions ~env suffix in + let completionsFromThisOpen = allCompletions ~env suffix in List.filter (fun ((declared : Completion.t), _env) -> if Hashtbl.mem alreadyUsedIdentifiers declared.name then false @@ -837,7 +814,7 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos ~dotpath = match getEnvWithOpens ~pos ~env ~package ~opens path with | Some (env, suffix) -> Log.log "Got the env"; - valueCompletions ~env suffix + allCompletions ~env suffix | None -> []) | RecordAccess (valuePath, middleFields, lastField) -> ( Log.log ("lastField :" ^ lastField); diff --git a/analysis/tests/src/expected/Debug.res.txt b/analysis/tests/src/expected/Debug.res.txt index 1b987e433..b42b5c284 100644 --- a/analysis/tests/src/expected/Debug.res.txt +++ b/analysis/tests/src/expected/Debug.res.txt @@ -76,9 +76,7 @@ resolvePath path:place holder Opens nows 2 pervasives.mli js.ml ---------------- LOCAL VAL - Completing in js.ml - -- not capitalized - Completing in pervasives.mli - -- not capitalized [{ "label": "eqNullable", "kind": 12, From 2470fe6f00ff1d0267f969055bef20d635f633e7 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 07:45:40 +0200 Subject: [PATCH 055/135] id shadowing from opens --- analysis/src/Commands.ml | 4 +- analysis/src/NewCompletions.ml | 4 +- analysis/tests/bsconfig.json | 1 + analysis/tests/src/Completion.res | 16 +++++ .../tests/src/expected/Completion.res.txt | 58 ++++++++++++++++++- 5 files changed, 79 insertions(+), 4 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index c539aa15b..420a1bbc4 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -272,7 +272,9 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let opensInScope = ref [] in let setResultOpt x = if !result = None then - match x with None -> () | Some x -> result := Some (x, !opensInScope) + match x with + | None -> () + | Some x -> result := Some (x, List.rev !opensInScope) in let setResult x = setResultOpt (Some x) in let structure (iterator : Ast_iterator.iterator) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 437f886fb..6426612b1 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -786,7 +786,9 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos ~dotpath = let completionsFromThisOpen = allCompletions ~env suffix in List.filter (fun ((declared : Completion.t), _env) -> - if Hashtbl.mem alreadyUsedIdentifiers declared.name then false + if Hashtbl.mem alreadyUsedIdentifiers declared.name then + (* shadowing from opens *) + false else ( Hashtbl.add alreadyUsedIdentifiers declared.name true; true)) diff --git a/analysis/tests/bsconfig.json b/analysis/tests/bsconfig.json index 38fa295dd..812c8f61e 100644 --- a/analysis/tests/bsconfig.json +++ b/analysis/tests/bsconfig.json @@ -6,6 +6,7 @@ "subdirs": true } ], + "bsc-flags": ["-w -33-44"], "bs-dependencies": ["@rescript/react"], "reason": { "react-jsx": 3 } } diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index 9354b01d0..019c6e316 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -155,3 +155,19 @@ module Private = { } // ^com Private. + +module Shadow = { + module A = { + let shadowed = 3 + } + + module B = { + let shadowed = "" +} +} + +open Shadow.A +open Shadow.B +// ^db+ +// ^com sha +let _ = shadowed \ No newline at end of file diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 1692fba04..3e036de9e 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -766,6 +766,31 @@ DocumentSymbol tests/src/Completion.res "name": "b", "kind": 13, "location": {"uri": "Completion.res", "range": {"start": {"line": 153, "character": 2}, "end": {"line": 153, "character": 13}}} +}, +{ + "name": "Shadow", + "kind": 2, + "location": {"uri": "Completion.res", "range": {"start": {"line": 158, "character": 7}, "end": {"line": 166, "character": 1}}} +}, +{ + "name": "A", + "kind": 2, + "location": {"uri": "Completion.res", "range": {"start": {"line": 159, "character": 9}, "end": {"line": 161, "character": 3}}} +}, +{ + "name": "shadowed", + "kind": 16, + "location": {"uri": "Completion.res", "range": {"start": {"line": 160, "character": 4}, "end": {"line": 160, "character": 20}}} +}, +{ + "name": "B", + "kind": 2, + "location": {"uri": "Completion.res", "range": {"start": {"line": 163, "character": 9}, "end": {"line": 165, "character": 1}}} +}, +{ + "name": "shadowed", + "kind": 15, + "location": {"uri": "Completion.res", "range": {"start": {"line": 164, "character": 4}, "end": {"line": 164, "character": 21}}} } ] @@ -1111,8 +1136,8 @@ Completable: Cdotpath([For],Component) }] Complete tests/src/Completion.res 155:3 -posCursor:[156:9] posNoWhite:[156:8] Found expr:[156:1->158:0] -Pexp_ident Private.:[156:1->158:0] +posCursor:[156:9] posNoWhite:[156:8] Found expr:[156:1->158:6] +Pexp_ident Private.:[156:1->158:6] Completable: Cdotpath([Private, ""],Value) [{ "label": "b", @@ -1122,3 +1147,32 @@ Completable: Cdotpath([Private, ""],Value) "documentation": null }] + +Complete tests/src/Completion.res 170:3 +posCursor:[171:4] posNoWhite:[171:3] Found expr:[171:1->171:4] +Pexp_ident sha:[171:1->171:4] +Completable: Cdotpath([sha],Value) +Ident!! Belt.List +Opens folkz > 2 Shadow.A.place holder ... Shadow.B.place holder +Package opens Pervasives +Package opens Pervasives +fileForModule Impl cmt:tests/node_modules/rescript/lib/ocaml/pervasives.cmti res:tests/node_modules/rescript/lib/ocaml/pervasives.mli +resolving open Shadow.A.place holder +resolvePath path:Shadow.A.place holder +Was local +resolving open Shadow.B.place holder +resolvePath path:Shadow.B.place holder +Was local +Opens nows 3 pervasives.mli Completion.res Completion.res +---------------- LOCAL VAL + - Completing in Completion.res + - Completing in Completion.res + - Completing in pervasives.mli +[{ + "label": "shadowed", + "kind": 12, + "tags": [], + "detail": "string", + "documentation": null + }] + From 478f0d12770169157eefa67961d85f7ea620f9cf Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 11:17:28 +0200 Subject: [PATCH 056/135] More examples of shadowing. Issue when completing an id from an inner module. --- analysis/tests/src/Completion.res | 6 +-- .../tests/src/expected/Completion.res.txt | 45 +++++++++++-------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index 019c6e316..289e214c4 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -160,14 +160,14 @@ module Shadow = { module A = { let shadowed = 3 } - module B = { let shadowed = "" -} + } } +// ^com sha open Shadow.A +// ^com sha open Shadow.B -// ^db+ // ^com sha let _ = shadowed \ No newline at end of file diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 3e036de9e..b60eb5a33 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -770,7 +770,7 @@ DocumentSymbol tests/src/Completion.res { "name": "Shadow", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 158, "character": 7}, "end": {"line": 166, "character": 1}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 158, "character": 7}, "end": {"line": 165, "character": 1}}} }, { "name": "A", @@ -785,12 +785,12 @@ DocumentSymbol tests/src/Completion.res { "name": "B", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 163, "character": 9}, "end": {"line": 165, "character": 1}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 162, "character": 9}, "end": {"line": 164, "character": 3}}} }, { "name": "shadowed", "kind": 15, - "location": {"uri": "Completion.res", "range": {"start": {"line": 164, "character": 4}, "end": {"line": 164, "character": 21}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 163, "character": 4}, "end": {"line": 163, "character": 21}}} } ] @@ -1147,27 +1147,34 @@ Completable: Cdotpath([Private, ""],Value) "documentation": null }] +Complete tests/src/Completion.res 166:3 +posCursor:[167:4] posNoWhite:[167:3] Found expr:[167:1->167:4] +Pexp_ident sha:[167:1->167:4] +Completable: Cdotpath([sha],Value) +[{ + "label": "shadowed", + "kind": 12, + "tags": [], + "detail": "string", + "documentation": null + }] + +Complete tests/src/Completion.res 168:3 +posCursor:[169:4] posNoWhite:[169:3] Found expr:[169:1->169:4] +Pexp_ident sha:[169:1->169:4] +Completable: Cdotpath([sha],Value) +[{ + "label": "shadowed", + "kind": 12, + "tags": [], + "detail": "int", + "documentation": null + }] Complete tests/src/Completion.res 170:3 posCursor:[171:4] posNoWhite:[171:3] Found expr:[171:1->171:4] Pexp_ident sha:[171:1->171:4] Completable: Cdotpath([sha],Value) -Ident!! Belt.List -Opens folkz > 2 Shadow.A.place holder ... Shadow.B.place holder -Package opens Pervasives -Package opens Pervasives -fileForModule Impl cmt:tests/node_modules/rescript/lib/ocaml/pervasives.cmti res:tests/node_modules/rescript/lib/ocaml/pervasives.mli -resolving open Shadow.A.place holder -resolvePath path:Shadow.A.place holder -Was local -resolving open Shadow.B.place holder -resolvePath path:Shadow.B.place holder -Was local -Opens nows 3 pervasives.mli Completion.res Completion.res ----------------- LOCAL VAL - - Completing in Completion.res - - Completing in Completion.res - - Completing in pervasives.mli [{ "label": "shadowed", "kind": 12, From 4f96549ac7f70f4131234cfc174e2f51f033dadc Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 11:23:58 +0200 Subject: [PATCH 057/135] Example of qualified record access. ... .M.field --- analysis/tests/src/RecordCompletion.res | 7 +++++++ analysis/tests/src/expected/RecordCompletion.res.txt | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/analysis/tests/src/RecordCompletion.res b/analysis/tests/src/RecordCompletion.res index b9a2b1c76..2dcc7dabf 100644 --- a/analysis/tests/src/RecordCompletion.res +++ b/analysis/tests/src/RecordCompletion.res @@ -9,3 +9,10 @@ let t2 = {n2: t} // ^com t.n->m // ^com t2.n2.n->m + +module R = { + type t = {name: string} +} + +let n = {R.name: ""} +// ^com n.R. diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index fbfb94295..87bad6c14 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -36,3 +36,15 @@ Completable: Cpipe([t2, n2, n], m) "documentation": null }] +Complete tests/src/RecordCompletion.res 16:3 +posCursor:[17:5] posNoWhite:[17:4] Found expr:[17:1->19:0] +Pexp_field [17:1->17:2] R.:[17:3->19:0] +Completable: Cdotpath([R, ""],Field) +[{ + "label": "name", + "kind": 5, + "tags": [], + "detail": "name: string\n\ntype t = {name: string}", + "documentation": null + }] + From cdca5165bab900a5355797510857a28831e63b08 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 13:55:31 +0200 Subject: [PATCH 058/135] Introduce generalized completable Cpath Generate Cpath from the AST, but still convert it back to the old completables in the completion back-end. --- analysis/src/Commands.ml | 52 +++++++++++-------- analysis/src/NewCompletions.ml | 28 +++++++++- analysis/src/PartialParser.ml | 32 +++++++++--- .../tests/src/expected/Completion.res.txt | 52 +++++++++---------- analysis/tests/src/expected/Cross.res.txt | 2 +- analysis/tests/src/expected/Debug.res.txt | 2 +- analysis/tests/src/expected/Jsx.res.txt | 10 ++-- analysis/tests/src/expected/Jsx.resi.txt | 4 +- .../src/expected/RecordCompletion.res.txt | 2 +- 9 files changed, 118 insertions(+), 66 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 420a1bbc4..2cabf3f11 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -397,7 +397,8 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Value)) + setResult + (PartialParser.Cpath (CPId (flattenLongIdent id.txt), Value)) | Pexp_construct (id, eOpt) -> if debug then Printf.printf "Pexp_construct %s:%s %s\n" @@ -410,7 +411,8 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = eOpt = None && (not id.loc.loc_ghost) && id.loc |> Loc.hasPos ~pos:posBeforeCursor then - setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Value)) + setResult + (PartialParser.Cpath (CPId (flattenLongIdent id.txt), Value)) | Pexp_field (e, fieldName) -> ( if debug then Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) @@ -418,29 +420,35 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (Loc.toString fieldName.loc); let rec digLhs (lhs : Parsetree.expression) = match lhs.pexp_desc with - | Pexp_ident id -> Some (flattenLongIdent id.txt) - | Pexp_field (lhs, id) -> ( + | Pexp_ident id -> + Some (PartialParser.CPId (flattenLongIdent id.txt)) + | Pexp_field (lhs, {txt = Lident name}) -> ( match digLhs lhs with - | Some path -> Some (path @ flattenLongIdent id.txt) + | Some contextPath -> + Some (PartialParser.CPField (contextPath, name)) | None -> None) + | Pexp_field (_, {txt = Ldot (id, name)}) -> + Some (PartialParser.CPField (CPId (flattenLongIdent id), name)) | _ -> None in if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then match digLhs e with - | Some path -> - let dotPath = + | Some contextPath -> + let contextPath = match fieldName.txt with - | Lident name -> path @ [name] + | Lident name -> PartialParser.CPField (contextPath, name) | _ -> (* Case x.M.field ignore the x part *) - flattenLongIdent fieldName.txt + PartialParser.CPId (flattenLongIdent fieldName.txt) in - setResult (PartialParser.Cdotpath (dotPath, Field)) + setResult (PartialParser.Cpath (contextPath, Field)) | None -> () else if Loc.end_ e.pexp_loc = posBeforeCursor then match digLhs e with - | Some path -> - setResult (PartialParser.Cdotpath (path @ [""], Field)) + | Some contextPath -> + setResult + (PartialParser.Cpath + (PartialParser.CPField (contextPath, ""), Field)) | None -> ()) | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) when Res_parsetree_viewer.isJsxExpression expr -> @@ -465,8 +473,8 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = if jsxCompletable <> None then setResultOpt jsxCompletable else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then setResult - (PartialParser.Cdotpath - (flattenLongIdent ~jsx:true compName.txt, Module)) + (PartialParser.Cpath + (CPId (flattenLongIdent ~jsx:true compName.txt), Module)) | Pexp_apply ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, [ @@ -517,11 +525,12 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let rec processLhs (lhs : Parsetree.expression) = match lhs.pexp_desc with - | Pexp_ident id -> Some (flattenLongIdent id.txt, []) + | Pexp_ident id -> + Some (PartialParser.CPId (flattenLongIdent id.txt)) | Pexp_send (e1, {txt}) -> ( match processLhs e1 with | None -> None - | Some (idPath, nestedPath) -> Some (idPath, nestedPath @ [txt])) + | Some contexPath -> Some (CPObj (contexPath, txt))) | _ -> None in @@ -534,8 +543,9 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = || (label = "" && posCursor = fst labelRange) then match processLhs lhs with - | Some (idPath, nestedPath) -> - setResult (PartialParser.Cobj (idPath, nestedPath, label)) + | Some contextPath -> + setResult + (PartialParser.Cpath (CPObj (contextPath, label), Value)) | None -> ()) | _ -> ()); Ast_iterator.default_iterator.expr iterator expr @@ -554,7 +564,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Type)) + setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt), Type)) | _ -> ()); Ast_iterator.default_iterator.typ iterator core_type in @@ -567,7 +577,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); found := true; - setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Module)) + setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt), Module)) | _ -> ()); Ast_iterator.default_iterator.module_expr iterator me in @@ -580,7 +590,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); found := true; - setResult (PartialParser.Cdotpath (flattenLongIdent id.txt, Module)) + setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt), Module)) | _ -> ()); Ast_iterator.default_iterator.module_type iterator mt in diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 6426612b1..800865d43 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -971,6 +971,7 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel) @ keyLabels + | Cpath _ -> assert false | Cdotpath (dotpath, completionContext) -> let completions = dotpath |> processDotPath ~completionContext ~exact:false @@ -1217,7 +1218,8 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens |> List.filter (fun (name, _t) -> Utils.startsWith name prefix) |> List.map mkLabel -let computeCompletions ~completable ~full ~pos ~rawOpens = +let computeCompletions ~(completable : PartialParser.completable) ~full ~pos + ~rawOpens = let package = full.package in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in let processDotPath ~completionContext ~exact dotpath = @@ -1263,4 +1265,28 @@ let computeCompletions ~completable ~full ~pos ~rawOpens = |> List.filter (fun ({Completion.kind}, _env) -> filterKind ~completionContext ~kind) in + + let rec processContextPath (cp : PartialParser.contextPath) k : + PartialParser.completable = + match cp with + | CPId path -> PartialParser.Cdotpath (path, k) + | CPField (cp, name) -> ( + match processContextPath cp k with + | Cdotpath (path, k) -> Cdotpath (path @ [name], k) + | _ -> assert false) + | CPObj (cp, objLabel) -> ( + match processContextPath cp k with + | Cdotpath (path, _k) -> Cobj (path, [], objLabel) + | Cobj (path, objPath, label) -> Cobj (path, objPath @ [label], objLabel) + | _ -> assert false) + in + + let completable = + match completable with + | Cobj _ -> assert false + | Cdotpath _ -> assert false + | Cpath (contextPath, k) -> processContextPath contextPath k + | _ -> completable + in + completable |> processCompletable ~processDotPath ~full ~package ~rawOpens diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 244cec704..e472e56b6 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -3,11 +3,18 @@ type pipe = PipeId of string list | PipeArray | PipeString (* Completion context *) type completionContext = Type | Value | Module | Field +type contextPath = + | CPId of string list + | CPField of contextPath * string + | CPObj of contextPath * string + type completable = | Cdecorator of string (** e.g. @module *) | Clabel of string list * string * string list (** e.g. (["M", "foo"], "label", ["l1", "l2"]) for M.foo(...~l1...~l2...~label...) *) - | Cdotpath of string list * completionContext (** e.g. ["M", "foo"] for M.foo *) + | Cpath of contextPath * completionContext + | Cdotpath of string list * completionContext + (** e.g. ["M", "foo"] for M.foo *) | Cjsx of string list * string * string list (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for List.map str |> String.concat ", ") ^ "]" in + let rec contextPathToString = function + | CPId sl -> list sl + | CPField (cp, s) -> contextPathToString cp ^ "." ^ str s + | CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]" + in + let completionContextToString = function + | Value -> "Value" + | Type -> "Type" + | Module -> "Component" + | Field -> "Field" + in function + | Cpath (cp, k) -> + "Cpath (" ^ contextPathToString cp ^ ", " + ^ completionContextToString k + ^ ")" | Cdecorator s -> "Cdecorator(" ^ str s ^ ")" | Clabel (sl1, s, sl2) -> "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" | Cdotpath (sl, k) -> - "Cdotpath(" ^ (sl |> list) ^ "," - ^ (match k with - | Value -> "Value" - | Type -> "Type" - | Module -> "Component" - | Field -> "Field") - ^ ")" + "Cdotpath(" ^ (sl |> list) ^ "," ^ completionContextToString k ^ ")" | Cjsx (sl1, s, sl2) -> "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" | Cobj (sl1, sl2, s) -> diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index b60eb5a33..519ee174f 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1,7 +1,7 @@ Complete tests/src/Completion.res 0:2 posCursor:[1:9] posNoWhite:[1:8] Found expr:[1:1->1:9] Pexp_ident MyList.m:[1:1->1:9] -Completable: Cdotpath([MyList, m],Value) +Completable: Cpath ([MyList, m], Value) [{ "label": "mapReverse", "kind": 12, @@ -73,7 +73,7 @@ Completable: Cdotpath([MyList, m],Value) Complete tests/src/Completion.res 1:2 posCursor:[2:7] posNoWhite:[2:6] Found expr:[2:1->6:6] Pexp_ident Array.:[2:1->6:6] -Completable: Cdotpath([Array, ""],Value) +Completable: Cpath ([Array, ""], Value) [{ "label": "Floatarray", "kind": 9, @@ -295,7 +295,7 @@ Completable: Cdotpath([Array, ""],Value) Complete tests/src/Completion.res 2:2 posCursor:[3:8] posNoWhite:[3:7] Found expr:[3:1->3:8] Pexp_ident Array.m:[3:1->3:8] -Completable: Cdotpath([Array, m],Value) +Completable: Cpath ([Array, m], Value) [{ "label": "mapi", "kind": 12, @@ -349,7 +349,7 @@ Completable: Cdotpath([Array, m],Value) Complete tests/src/Completion.res 12:2 posCursor:[13:15] posNoWhite:[13:14] Found expr:[13:10->13:15] Pexp_ident Dep.c:[13:10->13:15] -Completable: Cdotpath([Dep, c],Value) +Completable: Cpath ([Dep, c], Value) [{ "label": "customDouble", "kind": 12, @@ -452,7 +452,7 @@ Complete tests/src/Completion.res 38:2 posCursor:[39:19] posNoWhite:[39:18] Found expr:[39:1->39:19] posCursor:[39:19] posNoWhite:[39:18] Found expr:[39:10->39:19] Pexp_ident Js.Dict.u:[39:10->39:19] -Completable: Cdotpath([Js, Dict, u],Value) +Completable: Cpath ([Js, Dict, u], Value) [{ "label": "unsafeGet", "kind": 12, @@ -472,7 +472,7 @@ posCursor:[51:28] posNoWhite:[51:27] Found expr:[51:13->51:28] JSX 51:19] second[51:20->51:26]=...[51:27->51:28]> _children:None posCursor:[51:28] posNoWhite:[51:27] Found expr:[51:27->51:28] Pexp_ident z:[51:27->51:28] -Completable: Cdotpath([z],Value) +Completable: Cpath ([z], Value) [{ "label": "zzz", "kind": 12, @@ -863,7 +863,7 @@ Completable: Clabel([Lib, foo], "", [age, name]) Complete tests/src/Completion.res 72:2 posCursor:[73:11] posNoWhite:[73:10] Found expr:[73:1->75:18] XXX Pexp_send a[73:10->73:11] e:[73:1->73:8] -Completable: Cobj([someObj], [], a) +Completable: Cpath ([someObj]["a"], Value) [{ "label": "age", "kind": 4, @@ -875,7 +875,7 @@ Completable: Cobj([someObj], [], a) Complete tests/src/Completion.res 76:2 posCursor:[77:22] posNoWhite:[77:21] Found expr:[77:1->80:10] XXX Pexp_send [77:22->77:22] e:[77:1->77:20] -Completable: Cobj([nestedObj], [x, y], "") +Completable: Cpath ([nestedObj]["x"]["y"][""], Value) [{ "label": "age", "kind": 4, @@ -893,7 +893,7 @@ Completable: Cobj([nestedObj], [x, y], "") Complete tests/src/Completion.res 79:2 posCursor:[80:5] posNoWhite:[80:4] Found expr:[80:1->82:20] XXX Pexp_send a[80:4->80:5] e:[80:1->80:2] -Completable: Cobj([o], [], a) +Completable: Cpath ([o]["a"], Value) [{ "label": "age", "kind": 4, @@ -905,7 +905,7 @@ Completable: Cobj([o], [], a) Complete tests/src/Completion.res 83:2 posCursor:[84:15] posNoWhite:[84:14] Found expr:[84:1->101:20] XXX Pexp_send [84:15->84:15] e:[84:1->84:13] -Completable: Cobj([no], [x, y], "") +Completable: Cpath ([no]["x"]["y"][""], Value) [{ "label": "name", "kind": 4, @@ -923,7 +923,7 @@ Completable: Cobj([no], [x, y], "") Complete tests/src/Completion.res 88:3 posCursor:[89:3] posNoWhite:[89:2] Found expr:[89:1->93:3] Pexp_field [89:1->89:2] _:[93:0->93:3] -Completable: Cdotpath([r, ""],Field) +Completable: Cpath ([r]."", Field) [{ "label": "x", "kind": 5, @@ -941,7 +941,7 @@ Completable: Cdotpath([r, ""],Field) Complete tests/src/Completion.res 90:3 posCursor:[91:19] posNoWhite:[91:18] Found expr:[91:1->93:3] Pexp_field [91:1->91:18] _:[93:0->93:3] -Completable: Cdotpath([Obj, Rec, recordVal, ""],Field) +Completable: Cpath ([Obj, Rec, recordVal]."", Field) [{ "label": "xx", "kind": 5, @@ -961,7 +961,7 @@ posCursor:[97:3] posNoWhite:[97:2] Found expr:[96:11->99:1] posCursor:[97:3] posNoWhite:[97:2] Found expr:[97:1->98:5] posCursor:[97:3] posNoWhite:[97:2] Found expr:[97:1->97:3] Pexp_ident my:[97:1->97:3] -Completable: Cdotpath([my],Value) +Completable: Cpath ([my], Value) [{ "label": "myAmazingFunction", "kind": 12, @@ -973,7 +973,7 @@ Completable: Cdotpath([my],Value) Complete tests/src/Completion.res 100:3 posCursor:[101:13] posNoWhite:[101:12] Found expr:[101:1->120:32] XXX Pexp_send [101:13->101:13] e:[101:1->101:11] -Completable: Cobj([Obj, object], [], "") +Completable: Cpath ([Obj, object][""], Value) [{ "label": "name", "kind": 4, @@ -993,7 +993,7 @@ posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] JSX 126:4] > _children:None posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] Pexp_ident O..createElement:[126:2->126:4] -Completable: Cdotpath([O, ""],Component) +Completable: Cpath ([O, ""], Component) [{ "label": "Comp", "kind": 9, @@ -1005,7 +1005,7 @@ Completable: Cdotpath([O, ""],Component) Complete tests/src/Completion.res 130:3 posCursor:[131:6] posNoWhite:[131:5] Found expr:[131:1->136:6] Pexp_field [131:1->131:5] _:[136:0->136:6] -Completable: Cdotpath([q, aa, ""],Field) +Completable: Cpath ([q].aa."", Field) [{ "label": "x", "kind": 5, @@ -1023,7 +1023,7 @@ Completable: Cdotpath([q, aa, ""],Field) Complete tests/src/Completion.res 131:3 posCursor:[132:7] posNoWhite:[132:6] Found expr:[132:1->132:7] Pexp_field [132:1->132:5] n:[132:6->132:7] -Completable: Cdotpath([q, aa, n],Field) +Completable: Cpath ([q].aa.n, Field) [{ "label": "name", "kind": 5, @@ -1035,7 +1035,7 @@ Completable: Cdotpath([q, aa, n],Field) Complete tests/src/Completion.res 133:3 posCursor:[134:4] posNoWhite:[134:3] Found expr:[134:1->134:4] Pexp_construct Lis:[134:1->134:4] None -Completable: Cdotpath([Lis],Value) +Completable: Cpath ([Lis], Value) [{ "label": "List", "kind": 9, @@ -1073,7 +1073,7 @@ Completable: Cjsx([WithChildren], "", []) Complete tests/src/Completion.res 141:3 posCursor:[142:14] posNoWhite:[142:13] Found type:[142:10->142:14] Ptyp_constr Js.n:[142:10->142:14] -Completable: Cdotpath([Js, n],Type) +Completable: Cpath ([Js, n], Type) [{ "label": "null_undefined", "kind": 22, @@ -1097,7 +1097,7 @@ Completable: Cdotpath([Js, n],Type) Complete tests/src/Completion.res 142:3 posCursor:[143:18] posNoWhite:[143:17] Found type:[143:10->143:18] Ptyp_constr ForAuto.:[143:10->143:18] -Completable: Cdotpath([ForAuto, ""],Type) +Completable: Cpath ([ForAuto, ""], Type) [{ "label": "t", "kind": 22, @@ -1109,7 +1109,7 @@ Completable: Cdotpath([ForAuto, ""],Type) Complete tests/src/Completion.res 146:3 posCursor:[147:11] posNoWhite:[147:10] Found expr:[147:9->147:11] Pexp_construct As:[147:9->147:11] None -Completable: Cdotpath([As],Value) +Completable: Cpath ([As], Value) [{ "label": "Asterix", "kind": 4, @@ -1120,7 +1120,7 @@ Completable: Cdotpath([As],Value) Complete tests/src/Completion.res 148:3 Pmod_ident For:[149:12->149:15] -Completable: Cdotpath([For],Component) +Completable: Cpath ([For], Component) [{ "label": "ForAuto", "kind": 9, @@ -1138,7 +1138,7 @@ Completable: Cdotpath([For],Component) Complete tests/src/Completion.res 155:3 posCursor:[156:9] posNoWhite:[156:8] Found expr:[156:1->158:6] Pexp_ident Private.:[156:1->158:6] -Completable: Cdotpath([Private, ""],Value) +Completable: Cpath ([Private, ""], Value) [{ "label": "b", "kind": 12, @@ -1150,7 +1150,7 @@ Completable: Cdotpath([Private, ""],Value) Complete tests/src/Completion.res 166:3 posCursor:[167:4] posNoWhite:[167:3] Found expr:[167:1->167:4] Pexp_ident sha:[167:1->167:4] -Completable: Cdotpath([sha],Value) +Completable: Cpath ([sha], Value) [{ "label": "shadowed", "kind": 12, @@ -1162,7 +1162,7 @@ Completable: Cdotpath([sha],Value) Complete tests/src/Completion.res 168:3 posCursor:[169:4] posNoWhite:[169:3] Found expr:[169:1->169:4] Pexp_ident sha:[169:1->169:4] -Completable: Cdotpath([sha],Value) +Completable: Cpath ([sha], Value) [{ "label": "shadowed", "kind": 12, @@ -1174,7 +1174,7 @@ Completable: Cdotpath([sha],Value) Complete tests/src/Completion.res 170:3 posCursor:[171:4] posNoWhite:[171:3] Found expr:[171:1->171:4] Pexp_ident sha:[171:1->171:4] -Completable: Cdotpath([sha],Value) +Completable: Cpath ([sha], Value) [{ "label": "shadowed", "kind": 12, diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index 05321e242..ceb89618d 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -96,6 +96,6 @@ TypeDefinition tests/src/Cross.res 37:37 Complete tests/src/Cross.res 39:2 posCursor:[40:26] posNoWhite:[40:25] Found expr:[40:1->40:26] Pexp_ident DefinitionWithInterface.a:[40:1->40:26] -Completable: Cdotpath([DefinitionWithInterface, a],Value) +Completable: Cpath ([DefinitionWithInterface, a], Value) [] diff --git a/analysis/tests/src/expected/Debug.res.txt b/analysis/tests/src/expected/Debug.res.txt index b42b5c284..0aa9ec8c0 100644 --- a/analysis/tests/src/expected/Debug.res.txt +++ b/analysis/tests/src/expected/Debug.res.txt @@ -62,7 +62,7 @@ resolvePath path:map Complete tests/src/Debug.res 12:5 posCursor:[13:4] posNoWhite:[13:3] Found expr:[13:1->13:4] Pexp_ident eqN:[13:1->13:4] -Completable: Cdotpath([eqN],Value) +Completable: Cpath ([eqN], Value) Opens folkz > 1 Js.place holder Package opens Pervasives Package opens Pervasives diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index d83aecbea..a33e1a996 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -366,7 +366,7 @@ Completable: Cjsx([WithChildren], n, [n]) Complete tests/src/Jsx.res 71:2 posCursor:[72:16] posNoWhite:[72:15] Found type:[72:9->72:16] Ptyp_constr React.e:[72:9->72:16] -Completable: Cdotpath([React, e],Type) +Completable: Cpath ([React, e], Type) [{ "label": "element", "kind": 22, @@ -378,7 +378,7 @@ Completable: Cdotpath([React, e],Type) Complete tests/src/Jsx.res 72:2 posCursor:[73:18] posNoWhite:[73:17] Found type:[73:9->75:6] Ptyp_constr ReactDOMR:[73:9->75:6] -Completable: Cdotpath([ReactDOMR],Type) +Completable: Cpath ([ReactDOMR], Type) [{ "label": "ReactDOMRe", "kind": 9, @@ -392,7 +392,7 @@ posCursor:[78:17] posNoWhite:[78:16] Found expr:[78:9->78:17] Pexp_apply ...[78:11->78:12] (...[78:9->78:10], ...[78:13->78:17]) posCursor:[78:17] posNoWhite:[78:16] Found expr:[78:13->78:17] Pexp_field [78:13->78:14] th:[78:15->78:17] -Completable: Cdotpath([x, th],Field) +Completable: Cpath ([x].th, Field) [] Complete tests/src/Jsx.res 80:3 @@ -400,7 +400,7 @@ posCursor:[81:26] posNoWhite:[81:25] Found expr:[81:9->85:3] Pexp_ident DefineSomeFields.:[81:9->85:3] posCursor:[81:26] posNoWhite:[81:25] Found expr:[0:-1->85:70] Pexp_apply ...[85:6->85:7] (...[85:8->85:70]) -Completable: Cdotpath([DefineSomeFields, ""],Value) +Completable: Cpath ([DefineSomeFields, ""], Value) [{ "label": "thisValue", "kind": 12, @@ -414,7 +414,7 @@ posCursor:[83:34] posNoWhite:[83:33] Found expr:[83:9->83:34] Pexp_apply ...[83:11->83:12] (...[83:9->83:10], ...[83:13->83:34]) posCursor:[83:34] posNoWhite:[83:33] Found expr:[83:13->83:34] Pexp_field [83:13->83:14] DefineSomeFields.th:[83:15->83:34] -Completable: Cdotpath([DefineSomeFields, th],Field) +Completable: Cpath ([DefineSomeFields, th], Field) [{ "label": "thisField", "kind": 5, diff --git a/analysis/tests/src/expected/Jsx.resi.txt b/analysis/tests/src/expected/Jsx.resi.txt index cc3ee0b07..e1505bef7 100644 --- a/analysis/tests/src/expected/Jsx.resi.txt +++ b/analysis/tests/src/expected/Jsx.resi.txt @@ -7,7 +7,7 @@ Hover tests/src/Jsx.resi 4:4 Complete tests/src/Jsx.resi 6:3 posCursor:[7:17] posNoWhite:[7:16] Found type:[7:10->7:17] Ptyp_constr React.e:[7:10->7:17] -Completable: Cdotpath([React, e],Type) +Completable: Cpath ([React, e], Type) [{ "label": "element", "kind": 22, @@ -19,7 +19,7 @@ Completable: Cdotpath([React, e],Type) Complete tests/src/Jsx.resi 8:3 posCursor:[9:16] posNoWhite:[9:15] Found type:[9:9->9:16] Ptyp_constr React.e:[9:9->9:16] -Completable: Cdotpath([React, e],Type) +Completable: Cpath ([React, e], Type) [{ "label": "element", "kind": 22, diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index 87bad6c14..022052547 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -39,7 +39,7 @@ Completable: Cpipe([t2, n2, n], m) Complete tests/src/RecordCompletion.res 16:3 posCursor:[17:5] posNoWhite:[17:4] Found expr:[17:1->19:0] Pexp_field [17:1->17:2] R.:[17:3->19:0] -Completable: Cdotpath([R, ""],Field) +Completable: Cpath ([R, ""], Field) [{ "label": "name", "kind": 5, From ed84a7dd5c5c21f66ec2cc37b9ce7741b9fcaaa3 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 14:19:38 +0200 Subject: [PATCH 059/135] Fix qualified field access. --- analysis/src/Commands.ml | 20 +++++++++++-------- analysis/tests/src/expected/Jsx.res.txt | 2 +- .../src/expected/RecordCompletion.res.txt | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 2cabf3f11..a46056626 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -432,17 +432,21 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | _ -> None in if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then - match digLhs e with - | Some contextPath -> + match fieldName.txt with + | Lident name -> ( + match digLhs e with + | Some contextPath -> + let contextPath = PartialParser.CPField (contextPath, name) in + setResult (PartialParser.Cpath (contextPath, Field)) + | None -> ()) + | Ldot (id, name) -> + (* Case x.M.field ignore the x part *) let contextPath = - match fieldName.txt with - | Lident name -> PartialParser.CPField (contextPath, name) - | _ -> - (* Case x.M.field ignore the x part *) - PartialParser.CPId (flattenLongIdent fieldName.txt) + PartialParser.CPField + (CPId (flattenLongIdent id), if name = "$" then "" else name) in setResult (PartialParser.Cpath (contextPath, Field)) - | None -> () + | Lapply _ -> () else if Loc.end_ e.pexp_loc = posBeforeCursor then match digLhs e with | Some contextPath -> diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index a33e1a996..02bf63702 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -414,7 +414,7 @@ posCursor:[83:34] posNoWhite:[83:33] Found expr:[83:9->83:34] Pexp_apply ...[83:11->83:12] (...[83:9->83:10], ...[83:13->83:34]) posCursor:[83:34] posNoWhite:[83:33] Found expr:[83:13->83:34] Pexp_field [83:13->83:14] DefineSomeFields.th:[83:15->83:34] -Completable: Cpath ([DefineSomeFields, th], Field) +Completable: Cpath ([DefineSomeFields].th, Field) [{ "label": "thisField", "kind": 5, diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index 022052547..ef7d1e76d 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -39,7 +39,7 @@ Completable: Cpipe([t2, n2, n], m) Complete tests/src/RecordCompletion.res 16:3 posCursor:[17:5] posNoWhite:[17:4] Found expr:[17:1->19:0] Pexp_field [17:1->17:2] R.:[17:3->19:0] -Completable: Cpath ([R, ""], Field) +Completable: Cpath ([R]."", Field) [{ "label": "name", "kind": 5, From 70a66356229dbcd8618200335e177b4bc7e5a1ca Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 14:36:53 +0200 Subject: [PATCH 060/135] Tweak Cpath: completion context on the id. --- analysis/src/Commands.ml | 33 ++++++------ analysis/src/NewCompletions.ml | 12 ++--- analysis/src/PartialParser.ml | 20 ++++--- .../tests/src/expected/Completion.res.txt | 52 +++++++++---------- analysis/tests/src/expected/Cross.res.txt | 2 +- analysis/tests/src/expected/Debug.res.txt | 2 +- analysis/tests/src/expected/Jsx.res.txt | 10 ++-- analysis/tests/src/expected/Jsx.resi.txt | 4 +- .../src/expected/RecordCompletion.res.txt | 2 +- 9 files changed, 68 insertions(+), 69 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index a46056626..e6163ed83 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -398,7 +398,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (Loc.toString id.loc); if id.loc |> Loc.hasPos ~pos:posBeforeCursor then setResult - (PartialParser.Cpath (CPId (flattenLongIdent id.txt), Value)) + (PartialParser.Cpath (CPId (flattenLongIdent id.txt, Value))) | Pexp_construct (id, eOpt) -> if debug then Printf.printf "Pexp_construct %s:%s %s\n" @@ -412,7 +412,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = && id.loc |> Loc.hasPos ~pos:posBeforeCursor then setResult - (PartialParser.Cpath (CPId (flattenLongIdent id.txt), Value)) + (PartialParser.Cpath (CPId (flattenLongIdent id.txt, Value))) | Pexp_field (e, fieldName) -> ( if debug then Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) @@ -421,14 +421,16 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let rec digLhs (lhs : Parsetree.expression) = match lhs.pexp_desc with | Pexp_ident id -> - Some (PartialParser.CPId (flattenLongIdent id.txt)) + Some (PartialParser.CPId (flattenLongIdent id.txt, Value)) | Pexp_field (lhs, {txt = Lident name}) -> ( match digLhs lhs with | Some contextPath -> Some (PartialParser.CPField (contextPath, name)) | None -> None) | Pexp_field (_, {txt = Ldot (id, name)}) -> - Some (PartialParser.CPField (CPId (flattenLongIdent id), name)) + (* Case x.M.field ignore the x part *) + Some + (PartialParser.CPField (CPId (flattenLongIdent id, Module), name)) | _ -> None in if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then @@ -437,22 +439,22 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = match digLhs e with | Some contextPath -> let contextPath = PartialParser.CPField (contextPath, name) in - setResult (PartialParser.Cpath (contextPath, Field)) + setResult (PartialParser.Cpath contextPath) | None -> ()) | Ldot (id, name) -> (* Case x.M.field ignore the x part *) let contextPath = PartialParser.CPField - (CPId (flattenLongIdent id), if name = "$" then "" else name) + ( CPId (flattenLongIdent id, Module), + if name = "$" then "" else name ) in - setResult (PartialParser.Cpath (contextPath, Field)) + setResult (PartialParser.Cpath contextPath) | Lapply _ -> () else if Loc.end_ e.pexp_loc = posBeforeCursor then match digLhs e with | Some contextPath -> setResult - (PartialParser.Cpath - (PartialParser.CPField (contextPath, ""), Field)) + (PartialParser.Cpath (PartialParser.CPField (contextPath, ""))) | None -> ()) | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) when Res_parsetree_viewer.isJsxExpression expr -> @@ -478,7 +480,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then setResult (PartialParser.Cpath - (CPId (flattenLongIdent ~jsx:true compName.txt), Module)) + (CPId (flattenLongIdent ~jsx:true compName.txt, Module))) | Pexp_apply ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, [ @@ -530,7 +532,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let rec processLhs (lhs : Parsetree.expression) = match lhs.pexp_desc with | Pexp_ident id -> - Some (PartialParser.CPId (flattenLongIdent id.txt)) + Some (PartialParser.CPId (flattenLongIdent id.txt, Value)) | Pexp_send (e1, {txt}) -> ( match processLhs e1 with | None -> None @@ -548,8 +550,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = then match processLhs lhs with | Some contextPath -> - setResult - (PartialParser.Cpath (CPObj (contextPath, label), Value)) + setResult (PartialParser.Cpath (CPObj (contextPath, label))) | None -> ()) | _ -> ()); Ast_iterator.default_iterator.expr iterator expr @@ -568,7 +569,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt), Type)) + setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt, Type))) | _ -> ()); Ast_iterator.default_iterator.typ iterator core_type in @@ -581,7 +582,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); found := true; - setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt), Module)) + setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt, Module))) | _ -> ()); Ast_iterator.default_iterator.module_expr iterator me in @@ -594,7 +595,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); found := true; - setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt), Module)) + setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt, Module))) | _ -> ()); Ast_iterator.default_iterator.module_type iterator mt in diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 800865d43..41322632a 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -1266,16 +1266,16 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos filterKind ~completionContext ~kind) in - let rec processContextPath (cp : PartialParser.contextPath) k : + let rec processContextPath (cp : PartialParser.contextPath) : PartialParser.completable = match cp with - | CPId path -> PartialParser.Cdotpath (path, k) + | CPId (path, k) -> PartialParser.Cdotpath (path, k) | CPField (cp, name) -> ( - match processContextPath cp k with - | Cdotpath (path, k) -> Cdotpath (path @ [name], k) + match processContextPath cp with + | Cdotpath (path, _) -> Cdotpath (path @ [name], Field) | _ -> assert false) | CPObj (cp, objLabel) -> ( - match processContextPath cp k with + match processContextPath cp with | Cdotpath (path, _k) -> Cobj (path, [], objLabel) | Cobj (path, objPath, label) -> Cobj (path, objPath @ [label], objLabel) | _ -> assert false) @@ -1285,7 +1285,7 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos match completable with | Cobj _ -> assert false | Cdotpath _ -> assert false - | Cpath (contextPath, k) -> processContextPath contextPath k + | Cpath contextPath -> processContextPath contextPath | _ -> completable in diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index e472e56b6..a28b8f2a5 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -4,7 +4,7 @@ type pipe = PipeId of string list | PipeArray | PipeString type completionContext = Type | Value | Module | Field type contextPath = - | CPId of string list + | CPId of string list * completionContext | CPField of contextPath * string | CPObj of contextPath * string @@ -12,7 +12,7 @@ type completable = | Cdecorator of string (** e.g. @module *) | Clabel of string list * string * string list (** e.g. (["M", "foo"], "label", ["l1", "l2"]) for M.foo(...~l1...~l2...~label...) *) - | Cpath of contextPath * completionContext + | Cpath of contextPath | Cdotpath of string list * completionContext (** e.g. ["M", "foo"] for M.foo *) | Cjsx of string list * string * string list @@ -24,22 +24,20 @@ type completable = let completableToString = let str s = if s = "" then "\"\"" else s in let list l = "[" ^ (l |> List.map str |> String.concat ", ") ^ "]" in - let rec contextPathToString = function - | CPId sl -> list sl - | CPField (cp, s) -> contextPathToString cp ^ "." ^ str s - | CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]" - in let completionContextToString = function | Value -> "Value" | Type -> "Type" | Module -> "Component" | Field -> "Field" in + let rec contextPathToString = function + | CPId (sl, completionContext) -> + completionContextToString completionContext ^ list sl + | CPField (cp, s) -> contextPathToString cp ^ "." ^ str s + | CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]" + in function - | Cpath (cp, k) -> - "Cpath (" ^ contextPathToString cp ^ ", " - ^ completionContextToString k - ^ ")" + | Cpath cp -> "Cpath " ^ contextPathToString cp | Cdecorator s -> "Cdecorator(" ^ str s ^ ")" | Clabel (sl1, s, sl2) -> "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 519ee174f..e7c503670 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1,7 +1,7 @@ Complete tests/src/Completion.res 0:2 posCursor:[1:9] posNoWhite:[1:8] Found expr:[1:1->1:9] Pexp_ident MyList.m:[1:1->1:9] -Completable: Cpath ([MyList, m], Value) +Completable: Cpath Value[MyList, m] [{ "label": "mapReverse", "kind": 12, @@ -73,7 +73,7 @@ Completable: Cpath ([MyList, m], Value) Complete tests/src/Completion.res 1:2 posCursor:[2:7] posNoWhite:[2:6] Found expr:[2:1->6:6] Pexp_ident Array.:[2:1->6:6] -Completable: Cpath ([Array, ""], Value) +Completable: Cpath Value[Array, ""] [{ "label": "Floatarray", "kind": 9, @@ -295,7 +295,7 @@ Completable: Cpath ([Array, ""], Value) Complete tests/src/Completion.res 2:2 posCursor:[3:8] posNoWhite:[3:7] Found expr:[3:1->3:8] Pexp_ident Array.m:[3:1->3:8] -Completable: Cpath ([Array, m], Value) +Completable: Cpath Value[Array, m] [{ "label": "mapi", "kind": 12, @@ -349,7 +349,7 @@ Completable: Cpath ([Array, m], Value) Complete tests/src/Completion.res 12:2 posCursor:[13:15] posNoWhite:[13:14] Found expr:[13:10->13:15] Pexp_ident Dep.c:[13:10->13:15] -Completable: Cpath ([Dep, c], Value) +Completable: Cpath Value[Dep, c] [{ "label": "customDouble", "kind": 12, @@ -452,7 +452,7 @@ Complete tests/src/Completion.res 38:2 posCursor:[39:19] posNoWhite:[39:18] Found expr:[39:1->39:19] posCursor:[39:19] posNoWhite:[39:18] Found expr:[39:10->39:19] Pexp_ident Js.Dict.u:[39:10->39:19] -Completable: Cpath ([Js, Dict, u], Value) +Completable: Cpath Value[Js, Dict, u] [{ "label": "unsafeGet", "kind": 12, @@ -472,7 +472,7 @@ posCursor:[51:28] posNoWhite:[51:27] Found expr:[51:13->51:28] JSX 51:19] second[51:20->51:26]=...[51:27->51:28]> _children:None posCursor:[51:28] posNoWhite:[51:27] Found expr:[51:27->51:28] Pexp_ident z:[51:27->51:28] -Completable: Cpath ([z], Value) +Completable: Cpath Value[z] [{ "label": "zzz", "kind": 12, @@ -863,7 +863,7 @@ Completable: Clabel([Lib, foo], "", [age, name]) Complete tests/src/Completion.res 72:2 posCursor:[73:11] posNoWhite:[73:10] Found expr:[73:1->75:18] XXX Pexp_send a[73:10->73:11] e:[73:1->73:8] -Completable: Cpath ([someObj]["a"], Value) +Completable: Cpath Value[someObj]["a"] [{ "label": "age", "kind": 4, @@ -875,7 +875,7 @@ Completable: Cpath ([someObj]["a"], Value) Complete tests/src/Completion.res 76:2 posCursor:[77:22] posNoWhite:[77:21] Found expr:[77:1->80:10] XXX Pexp_send [77:22->77:22] e:[77:1->77:20] -Completable: Cpath ([nestedObj]["x"]["y"][""], Value) +Completable: Cpath Value[nestedObj]["x"]["y"][""] [{ "label": "age", "kind": 4, @@ -893,7 +893,7 @@ Completable: Cpath ([nestedObj]["x"]["y"][""], Value) Complete tests/src/Completion.res 79:2 posCursor:[80:5] posNoWhite:[80:4] Found expr:[80:1->82:20] XXX Pexp_send a[80:4->80:5] e:[80:1->80:2] -Completable: Cpath ([o]["a"], Value) +Completable: Cpath Value[o]["a"] [{ "label": "age", "kind": 4, @@ -905,7 +905,7 @@ Completable: Cpath ([o]["a"], Value) Complete tests/src/Completion.res 83:2 posCursor:[84:15] posNoWhite:[84:14] Found expr:[84:1->101:20] XXX Pexp_send [84:15->84:15] e:[84:1->84:13] -Completable: Cpath ([no]["x"]["y"][""], Value) +Completable: Cpath Value[no]["x"]["y"][""] [{ "label": "name", "kind": 4, @@ -923,7 +923,7 @@ Completable: Cpath ([no]["x"]["y"][""], Value) Complete tests/src/Completion.res 88:3 posCursor:[89:3] posNoWhite:[89:2] Found expr:[89:1->93:3] Pexp_field [89:1->89:2] _:[93:0->93:3] -Completable: Cpath ([r]."", Field) +Completable: Cpath Value[r]."" [{ "label": "x", "kind": 5, @@ -941,7 +941,7 @@ Completable: Cpath ([r]."", Field) Complete tests/src/Completion.res 90:3 posCursor:[91:19] posNoWhite:[91:18] Found expr:[91:1->93:3] Pexp_field [91:1->91:18] _:[93:0->93:3] -Completable: Cpath ([Obj, Rec, recordVal]."", Field) +Completable: Cpath Value[Obj, Rec, recordVal]."" [{ "label": "xx", "kind": 5, @@ -961,7 +961,7 @@ posCursor:[97:3] posNoWhite:[97:2] Found expr:[96:11->99:1] posCursor:[97:3] posNoWhite:[97:2] Found expr:[97:1->98:5] posCursor:[97:3] posNoWhite:[97:2] Found expr:[97:1->97:3] Pexp_ident my:[97:1->97:3] -Completable: Cpath ([my], Value) +Completable: Cpath Value[my] [{ "label": "myAmazingFunction", "kind": 12, @@ -973,7 +973,7 @@ Completable: Cpath ([my], Value) Complete tests/src/Completion.res 100:3 posCursor:[101:13] posNoWhite:[101:12] Found expr:[101:1->120:32] XXX Pexp_send [101:13->101:13] e:[101:1->101:11] -Completable: Cpath ([Obj, object][""], Value) +Completable: Cpath Value[Obj, object][""] [{ "label": "name", "kind": 4, @@ -993,7 +993,7 @@ posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] JSX 126:4] > _children:None posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] Pexp_ident O..createElement:[126:2->126:4] -Completable: Cpath ([O, ""], Component) +Completable: Cpath Component[O, ""] [{ "label": "Comp", "kind": 9, @@ -1005,7 +1005,7 @@ Completable: Cpath ([O, ""], Component) Complete tests/src/Completion.res 130:3 posCursor:[131:6] posNoWhite:[131:5] Found expr:[131:1->136:6] Pexp_field [131:1->131:5] _:[136:0->136:6] -Completable: Cpath ([q].aa."", Field) +Completable: Cpath Value[q].aa."" [{ "label": "x", "kind": 5, @@ -1023,7 +1023,7 @@ Completable: Cpath ([q].aa."", Field) Complete tests/src/Completion.res 131:3 posCursor:[132:7] posNoWhite:[132:6] Found expr:[132:1->132:7] Pexp_field [132:1->132:5] n:[132:6->132:7] -Completable: Cpath ([q].aa.n, Field) +Completable: Cpath Value[q].aa.n [{ "label": "name", "kind": 5, @@ -1035,7 +1035,7 @@ Completable: Cpath ([q].aa.n, Field) Complete tests/src/Completion.res 133:3 posCursor:[134:4] posNoWhite:[134:3] Found expr:[134:1->134:4] Pexp_construct Lis:[134:1->134:4] None -Completable: Cpath ([Lis], Value) +Completable: Cpath Value[Lis] [{ "label": "List", "kind": 9, @@ -1073,7 +1073,7 @@ Completable: Cjsx([WithChildren], "", []) Complete tests/src/Completion.res 141:3 posCursor:[142:14] posNoWhite:[142:13] Found type:[142:10->142:14] Ptyp_constr Js.n:[142:10->142:14] -Completable: Cpath ([Js, n], Type) +Completable: Cpath Type[Js, n] [{ "label": "null_undefined", "kind": 22, @@ -1097,7 +1097,7 @@ Completable: Cpath ([Js, n], Type) Complete tests/src/Completion.res 142:3 posCursor:[143:18] posNoWhite:[143:17] Found type:[143:10->143:18] Ptyp_constr ForAuto.:[143:10->143:18] -Completable: Cpath ([ForAuto, ""], Type) +Completable: Cpath Type[ForAuto, ""] [{ "label": "t", "kind": 22, @@ -1109,7 +1109,7 @@ Completable: Cpath ([ForAuto, ""], Type) Complete tests/src/Completion.res 146:3 posCursor:[147:11] posNoWhite:[147:10] Found expr:[147:9->147:11] Pexp_construct As:[147:9->147:11] None -Completable: Cpath ([As], Value) +Completable: Cpath Value[As] [{ "label": "Asterix", "kind": 4, @@ -1120,7 +1120,7 @@ Completable: Cpath ([As], Value) Complete tests/src/Completion.res 148:3 Pmod_ident For:[149:12->149:15] -Completable: Cpath ([For], Component) +Completable: Cpath Component[For] [{ "label": "ForAuto", "kind": 9, @@ -1138,7 +1138,7 @@ Completable: Cpath ([For], Component) Complete tests/src/Completion.res 155:3 posCursor:[156:9] posNoWhite:[156:8] Found expr:[156:1->158:6] Pexp_ident Private.:[156:1->158:6] -Completable: Cpath ([Private, ""], Value) +Completable: Cpath Value[Private, ""] [{ "label": "b", "kind": 12, @@ -1150,7 +1150,7 @@ Completable: Cpath ([Private, ""], Value) Complete tests/src/Completion.res 166:3 posCursor:[167:4] posNoWhite:[167:3] Found expr:[167:1->167:4] Pexp_ident sha:[167:1->167:4] -Completable: Cpath ([sha], Value) +Completable: Cpath Value[sha] [{ "label": "shadowed", "kind": 12, @@ -1162,7 +1162,7 @@ Completable: Cpath ([sha], Value) Complete tests/src/Completion.res 168:3 posCursor:[169:4] posNoWhite:[169:3] Found expr:[169:1->169:4] Pexp_ident sha:[169:1->169:4] -Completable: Cpath ([sha], Value) +Completable: Cpath Value[sha] [{ "label": "shadowed", "kind": 12, @@ -1174,7 +1174,7 @@ Completable: Cpath ([sha], Value) Complete tests/src/Completion.res 170:3 posCursor:[171:4] posNoWhite:[171:3] Found expr:[171:1->171:4] Pexp_ident sha:[171:1->171:4] -Completable: Cpath ([sha], Value) +Completable: Cpath Value[sha] [{ "label": "shadowed", "kind": 12, diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index ceb89618d..5076fff01 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -96,6 +96,6 @@ TypeDefinition tests/src/Cross.res 37:37 Complete tests/src/Cross.res 39:2 posCursor:[40:26] posNoWhite:[40:25] Found expr:[40:1->40:26] Pexp_ident DefinitionWithInterface.a:[40:1->40:26] -Completable: Cpath ([DefinitionWithInterface, a], Value) +Completable: Cpath Value[DefinitionWithInterface, a] [] diff --git a/analysis/tests/src/expected/Debug.res.txt b/analysis/tests/src/expected/Debug.res.txt index 0aa9ec8c0..c2e66b754 100644 --- a/analysis/tests/src/expected/Debug.res.txt +++ b/analysis/tests/src/expected/Debug.res.txt @@ -62,7 +62,7 @@ resolvePath path:map Complete tests/src/Debug.res 12:5 posCursor:[13:4] posNoWhite:[13:3] Found expr:[13:1->13:4] Pexp_ident eqN:[13:1->13:4] -Completable: Cpath ([eqN], Value) +Completable: Cpath Value[eqN] Opens folkz > 1 Js.place holder Package opens Pervasives Package opens Pervasives diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 02bf63702..542fbce21 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -366,7 +366,7 @@ Completable: Cjsx([WithChildren], n, [n]) Complete tests/src/Jsx.res 71:2 posCursor:[72:16] posNoWhite:[72:15] Found type:[72:9->72:16] Ptyp_constr React.e:[72:9->72:16] -Completable: Cpath ([React, e], Type) +Completable: Cpath Type[React, e] [{ "label": "element", "kind": 22, @@ -378,7 +378,7 @@ Completable: Cpath ([React, e], Type) Complete tests/src/Jsx.res 72:2 posCursor:[73:18] posNoWhite:[73:17] Found type:[73:9->75:6] Ptyp_constr ReactDOMR:[73:9->75:6] -Completable: Cpath ([ReactDOMR], Type) +Completable: Cpath Type[ReactDOMR] [{ "label": "ReactDOMRe", "kind": 9, @@ -392,7 +392,7 @@ posCursor:[78:17] posNoWhite:[78:16] Found expr:[78:9->78:17] Pexp_apply ...[78:11->78:12] (...[78:9->78:10], ...[78:13->78:17]) posCursor:[78:17] posNoWhite:[78:16] Found expr:[78:13->78:17] Pexp_field [78:13->78:14] th:[78:15->78:17] -Completable: Cpath ([x].th, Field) +Completable: Cpath Value[x].th [] Complete tests/src/Jsx.res 80:3 @@ -400,7 +400,7 @@ posCursor:[81:26] posNoWhite:[81:25] Found expr:[81:9->85:3] Pexp_ident DefineSomeFields.:[81:9->85:3] posCursor:[81:26] posNoWhite:[81:25] Found expr:[0:-1->85:70] Pexp_apply ...[85:6->85:7] (...[85:8->85:70]) -Completable: Cpath ([DefineSomeFields, ""], Value) +Completable: Cpath Value[DefineSomeFields, ""] [{ "label": "thisValue", "kind": 12, @@ -414,7 +414,7 @@ posCursor:[83:34] posNoWhite:[83:33] Found expr:[83:9->83:34] Pexp_apply ...[83:11->83:12] (...[83:9->83:10], ...[83:13->83:34]) posCursor:[83:34] posNoWhite:[83:33] Found expr:[83:13->83:34] Pexp_field [83:13->83:14] DefineSomeFields.th:[83:15->83:34] -Completable: Cpath ([DefineSomeFields].th, Field) +Completable: Cpath Component[DefineSomeFields].th [{ "label": "thisField", "kind": 5, diff --git a/analysis/tests/src/expected/Jsx.resi.txt b/analysis/tests/src/expected/Jsx.resi.txt index e1505bef7..72bb22f47 100644 --- a/analysis/tests/src/expected/Jsx.resi.txt +++ b/analysis/tests/src/expected/Jsx.resi.txt @@ -7,7 +7,7 @@ Hover tests/src/Jsx.resi 4:4 Complete tests/src/Jsx.resi 6:3 posCursor:[7:17] posNoWhite:[7:16] Found type:[7:10->7:17] Ptyp_constr React.e:[7:10->7:17] -Completable: Cpath ([React, e], Type) +Completable: Cpath Type[React, e] [{ "label": "element", "kind": 22, @@ -19,7 +19,7 @@ Completable: Cpath ([React, e], Type) Complete tests/src/Jsx.resi 8:3 posCursor:[9:16] posNoWhite:[9:15] Found type:[9:9->9:16] Ptyp_constr React.e:[9:9->9:16] -Completable: Cpath ([React, e], Type) +Completable: Cpath Type[React, e] [{ "label": "element", "kind": 22, diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index ef7d1e76d..56ed829f2 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -39,7 +39,7 @@ Completable: Cpipe([t2, n2, n], m) Complete tests/src/RecordCompletion.res 16:3 posCursor:[17:5] posNoWhite:[17:4] Found expr:[17:1->19:0] Pexp_field [17:1->17:2] R.:[17:3->19:0] -Completable: Cpath ([R]."", Field) +Completable: Cpath Component[R]."" [{ "label": "name", "kind": 5, From b9a3d081a6f40060549d0501c7d0212009b46a97 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 14:37:22 +0200 Subject: [PATCH 061/135] rename --- analysis/src/PartialParser.ml | 2 +- analysis/tests/src/expected/Completion.res.txt | 4 ++-- analysis/tests/src/expected/Jsx.res.txt | 2 +- analysis/tests/src/expected/RecordCompletion.res.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index a28b8f2a5..7ad63c644 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -27,7 +27,7 @@ let completableToString = let completionContextToString = function | Value -> "Value" | Type -> "Type" - | Module -> "Component" + | Module -> "Module" | Field -> "Field" in let rec contextPathToString = function diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index e7c503670..8c3c28c2b 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -993,7 +993,7 @@ posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] JSX 126:4] > _children:None posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] Pexp_ident O..createElement:[126:2->126:4] -Completable: Cpath Component[O, ""] +Completable: Cpath Module[O, ""] [{ "label": "Comp", "kind": 9, @@ -1120,7 +1120,7 @@ Completable: Cpath Value[As] Complete tests/src/Completion.res 148:3 Pmod_ident For:[149:12->149:15] -Completable: Cpath Component[For] +Completable: Cpath Module[For] [{ "label": "ForAuto", "kind": 9, diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 542fbce21..c5f0411c2 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -414,7 +414,7 @@ posCursor:[83:34] posNoWhite:[83:33] Found expr:[83:9->83:34] Pexp_apply ...[83:11->83:12] (...[83:9->83:10], ...[83:13->83:34]) posCursor:[83:34] posNoWhite:[83:33] Found expr:[83:13->83:34] Pexp_field [83:13->83:14] DefineSomeFields.th:[83:15->83:34] -Completable: Cpath Component[DefineSomeFields].th +Completable: Cpath Module[DefineSomeFields].th [{ "label": "thisField", "kind": 5, diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index 56ed829f2..ca6ac9783 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -39,7 +39,7 @@ Completable: Cpipe([t2, n2, n], m) Complete tests/src/RecordCompletion.res 16:3 posCursor:[17:5] posNoWhite:[17:4] Found expr:[17:1->19:0] Pexp_field [17:1->17:2] R.:[17:3->19:0] -Completable: Cpath Component[R]."" +Completable: Cpath Module[R]."" [{ "label": "name", "kind": 5, From 3869bca3edfd4752b4d31db1e5afc62be045b026 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 14:49:30 +0200 Subject: [PATCH 062/135] cleanup --- analysis/src/NewCompletions.ml | 128 ++++++++++++++++----------------- 1 file changed, 63 insertions(+), 65 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 41322632a..376bb5c0b 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -754,7 +754,7 @@ let rec extractObjectType ~env ~package (t : Types.type_expr) = | _ -> None) | _ -> None -let getCompletions ~full ~rawOpens ~allFiles ~pos ~dotpath = +let getCompletions ~full ~rawOpens ~allFiles ~pos ~completion = Log.log ("Opens folkz > " ^ string_of_int (List.length rawOpens) @@ -774,9 +774,9 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos ~dotpath = |> List.map (fun (e : QueryEnv.t) -> Uri2.toString e.file.uri))); (* Last open takes priority *) let opens = List.rev resolvedOpens in - match dotpath with - | [] -> [] - | [suffix] -> + match completion with + | Path [] -> [] + | Path [suffix] -> let locallyDefinedValues = localCompletions ~pos ~env suffix in let alreadyUsedIdentifiers = Hashtbl.create 10 in let valuesFromOpens = @@ -808,68 +808,65 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos ~dotpath = else None) in locallyDefinedValues @ valuesFromOpens @ localModuleNames - | _ -> ( - Log.log ("Completing for " ^ String.concat "<.>" dotpath); - match determineCompletion dotpath with - | Path path -> ( - Log.log ("Path " ^ pathToString path); - match getEnvWithOpens ~pos ~env ~package ~opens path with - | Some (env, suffix) -> - Log.log "Got the env"; - allCompletions ~env suffix - | None -> []) - | RecordAccess (valuePath, middleFields, lastField) -> ( - Log.log ("lastField :" ^ lastField); - Log.log ("-------------- Looking for " ^ (valuePath |> pathToString)); - match getEnvWithOpens ~pos ~env ~package ~opens valuePath with - | Some (env, name) -> ( - match - ProcessCmt.findInScope pos name Stamps.iterValues env.file.stamps - with + | Path path -> ( + Log.log ("Path " ^ pathToString path); + match getEnvWithOpens ~pos ~env ~package ~opens path with + | Some (env, suffix) -> + Log.log "Got the env"; + allCompletions ~env suffix + | None -> []) + | RecordAccess (valuePath, middleFields, lastField) -> ( + Log.log ("lastField :" ^ lastField); + Log.log ("-------------- Looking for " ^ (valuePath |> pathToString)); + match getEnvWithOpens ~pos ~env ~package ~opens valuePath with + | Some (env, name) -> ( + match + ProcessCmt.findInScope pos name Stamps.iterValues env.file.stamps + with + | None -> [] + | Some declared -> ( + Log.log ("Found it! " ^ declared.name.txt); + match declared.item |> extractRecordType ~env ~package with | None -> [] - | Some declared -> ( - Log.log ("Found it! " ^ declared.name.txt); - match declared.item |> extractRecordType ~env ~package with - | None -> [] - | Some (env, fields, typDecl) -> ( - match - middleFields - |> List.fold_left - (fun current name -> - match current with + | Some (env, fields, typDecl) -> ( + match + middleFields + |> List.fold_left + (fun current name -> + match current with + | None -> None + | Some (env, fields, _) -> ( + match + fields |> List.find_opt (fun f -> f.fname.txt = name) + with | None -> None - | Some (env, fields, _) -> ( - match - fields |> List.find_opt (fun f -> f.fname.txt = name) - with - | None -> None - | Some attr -> - Log.log ("Found attr " ^ name); - attr.typ |> extractRecordType ~env ~package)) - (Some (env, fields, typDecl)) - with - | None -> [] - | Some (env, fields, typDecl) -> - fields - |> Utils.filterMap (fun field -> - if Utils.startsWith field.fname.txt lastField then - Some - ( Completion.create ~name:field.fname.txt - ~kind: - (Completion.Field - ( field, - typDecl.item.decl - |> Shared.declToString typDecl.name.txt )), - env ) - else None)))) - | None -> []) - | QualifiedRecordAccess path -> ( - match getEnvWithOpens ~pos ~env ~package ~opens path with - | None -> [] - | Some (env, suffix) -> - attributeCompletions ~env ~suffix - @ List.concat - (opens |> List.map (fun env -> attributeCompletions ~env ~suffix)))) + | Some attr -> + Log.log ("Found attr " ^ name); + attr.typ |> extractRecordType ~env ~package)) + (Some (env, fields, typDecl)) + with + | None -> [] + | Some (env, fields, typDecl) -> + fields + |> Utils.filterMap (fun field -> + if Utils.startsWith field.fname.txt lastField then + Some + ( Completion.create ~name:field.fname.txt + ~kind: + (Completion.Field + ( field, + typDecl.item.decl + |> Shared.declToString typDecl.name.txt )), + env ) + else None)))) + | None -> []) + | QualifiedRecordAccess path -> ( + match getEnvWithOpens ~pos ~env ~package ~opens path with + | None -> [] + | Some (env, suffix) -> + attributeCompletions ~env ~suffix + @ List.concat + (opens |> List.map (fun env -> attributeCompletions ~env ~suffix))) let mkItem ~name ~kind ~detail ~deprecated ~docstring = let docContent = @@ -1223,7 +1220,8 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos let package = full.package in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in let processDotPath ~completionContext ~exact dotpath = - let completions = getCompletions ~full ~rawOpens ~allFiles ~pos ~dotpath in + let completion = determineCompletion dotpath in + let completions = getCompletions ~full ~rawOpens ~allFiles ~pos ~completion in let filterKind ~(completionContext : PartialParser.completionContext) ~(kind : Completion.kind) = match (completionContext, kind) with From 1f4ffeb7a3002c8501ee0f62b30f5e2307dbc624 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 14:58:26 +0200 Subject: [PATCH 063/135] refactor --- analysis/src/NewCompletions.ml | 58 +++++----------------------------- analysis/src/PartialParser.ml | 39 +++++++++++++++++++++++ analysis/src/Utils.ml | 6 ++++ 3 files changed, 53 insertions(+), 50 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 376bb5c0b..0d2595149 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -597,51 +597,6 @@ let completionForFields ~(env : QueryEnv.t) ~suffix = | _ -> ()); !res -let isCapitalized name = - if name = "" then false - else - let c = name.[0] in - match c with 'A' .. 'Z' -> true | _ -> false - -type completion = - | QualifiedRecordAccess of path (* e.g. _.A.B.field where _ indicates a path ending in a lowercase id *) - | RecordAccess of path * path * string (* e.g. A.B.var .f1.f2 .f3 *) - | Path of path -(* e.g. A.B.var or A.B *) - -let determineCompletion (dotpath : path) = - let rec loop dotpath = - match dotpath with - | [] -> assert false - | [one] -> Path [one] - | [one; two] -> - if isCapitalized one then Path [one; two] - else RecordAccess ([one], [], two) - | one :: rest -> ( - if isCapitalized one then - match loop rest with - | Path path -> Path (one :: path) - | RecordAccess (valuePath, middleFields, lastField) -> - RecordAccess (one :: valuePath, middleFields, lastField) - | QualifiedRecordAccess _ as completion -> - (* A. _.B.field -> _.B.field *) - completion - else - match loop rest with - | Path path -> - (* x. B.field -> _.B.field *) - QualifiedRecordAccess path - | RecordAccess ([name], middleFields, lastField) -> - RecordAccess ([one], name :: middleFields, lastField) - | RecordAccess (valuePath, middleFields, lastField) -> - (* x.A.B.v.f1.f2.f3 --> .A.B.v.f1.f2.f3 *) - QualifiedRecordAccess (valuePath @ middleFields @ [lastField]) - | QualifiedRecordAccess _ as completion -> - (* x. _.A.f -> _.A.f *) - completion) - in - loop dotpath - (* Note: This is a hack. It will be wrong some times if you have a local thing that overrides an open. @@ -704,12 +659,12 @@ let allCompletions ~(env : QueryEnv.t) suffix = let attributeCompletions ~(env : QueryEnv.t) ~suffix = let results = [] in let results = - if suffix = "" || isCapitalized suffix then + if suffix = "" || Utils.isCapitalized suffix then results @ completionForExportedModules ~env ~suffix else results in let results = - if suffix = "" || not (isCapitalized suffix) then + if suffix = "" || not (Utils.isCapitalized suffix) then results @ completionForExportedValues ~env ~suffix @ completionForFields ~env ~suffix @@ -754,7 +709,8 @@ let rec extractObjectType ~env ~package (t : Types.type_expr) = | _ -> None) | _ -> None -let getCompletions ~full ~rawOpens ~allFiles ~pos ~completion = +let getCompletions ~full ~rawOpens ~allFiles ~pos + ~(completion : PartialParser.completion) = Log.log ("Opens folkz > " ^ string_of_int (List.length rawOpens) @@ -1220,8 +1176,10 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos let package = full.package in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in let processDotPath ~completionContext ~exact dotpath = - let completion = determineCompletion dotpath in - let completions = getCompletions ~full ~rawOpens ~allFiles ~pos ~completion in + let completion = PartialParser.determineCompletion dotpath in + let completions = + getCompletions ~full ~rawOpens ~allFiles ~pos ~completion + in let filterKind ~(completionContext : PartialParser.completionContext) ~(kind : Completion.kind) = match (completionContext, kind) with diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 7ad63c644..8c10d9afa 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -8,6 +8,12 @@ type contextPath = | CPField of contextPath * string | CPObj of contextPath * string +type completion = + | QualifiedRecordAccess of SharedTypes.path (* e.g. _.A.B.field where _ indicates a path ending in a lowercase id *) + | RecordAccess of SharedTypes.path * SharedTypes.path * string (* e.g. A.B.var .f1.f2 .f3 *) + | Path of SharedTypes.path +(* e.g. A.B.var or A.B *) + type completable = | Cdecorator of string (** e.g. @module *) | Clabel of string list * string * string list @@ -55,6 +61,39 @@ let completableToString = | PipeString -> "PipeString") ^ ", " ^ str s ^ ")" +let determineCompletion (dotpath : SharedTypes.path) = + let rec loop dotpath = + match dotpath with + | [] -> assert false + | [one] -> Path [one] + | [one; two] -> + if Utils.isCapitalized one then Path [one; two] + else RecordAccess ([one], [], two) + | one :: rest -> ( + if Utils.isCapitalized one then + match loop rest with + | Path path -> Path (one :: path) + | RecordAccess (valuePath, middleFields, lastField) -> + RecordAccess (one :: valuePath, middleFields, lastField) + | QualifiedRecordAccess _ as completion -> + (* A. _.B.field -> _.B.field *) + completion + else + match loop rest with + | Path path -> + (* x. B.field -> _.B.field *) + QualifiedRecordAccess path + | RecordAccess ([name], middleFields, lastField) -> + RecordAccess ([one], name :: middleFields, lastField) + | RecordAccess (valuePath, middleFields, lastField) -> + (* x.A.B.v.f1.f2.f3 --> .A.B.v.f1.f2.f3 *) + QualifiedRecordAccess (valuePath @ middleFields @ [lastField]) + | QualifiedRecordAccess _ as completion -> + (* x. _.A.f -> _.A.f *) + completion) + in + loop dotpath + let rec skipWhite text i = if i < 0 then 0 else diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml index 2febe2426..acfcb6d13 100644 --- a/analysis/src/Utils.ml +++ b/analysis/src/Utils.ml @@ -68,3 +68,9 @@ let filterMap f = let dumpPath path = Str.global_replace (Str.regexp_string "\\") "/" path let isUncurriedInternal path = startsWith (Path.name path) "Js.Fn.arity" + +let isCapitalized name = + if name = "" then false + else + let c = name.[0] in + match c with 'A' .. 'Z' -> true | _ -> false From b7ec8acc34db6c05562b0f6c83955088e93d30eb Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 15:20:30 +0200 Subject: [PATCH 064/135] No QualifiedRecordAccess required. --- analysis/src/NewCompletions.ml | 23 ----------------------- analysis/src/PartialParser.ml | 15 ++------------- 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 0d2595149..ffd193cd0 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -656,22 +656,6 @@ let allCompletions ~(env : QueryEnv.t) suffix = @ completionForFields ~env ~suffix |> List.map (fun r -> (r, env)) -let attributeCompletions ~(env : QueryEnv.t) ~suffix = - let results = [] in - let results = - if suffix = "" || Utils.isCapitalized suffix then - results @ completionForExportedModules ~env ~suffix - else results - in - let results = - if suffix = "" || not (Utils.isCapitalized suffix) then - results - @ completionForExportedValues ~env ~suffix - @ completionForFields ~env ~suffix - else results - in - results |> List.map (fun r -> (r, env)) - (* TODO filter out things that are defined after the current position *) let resolveRawOpens ~env ~rawOpens ~package = (* TODO Stdlib instead of Pervasives *) @@ -816,13 +800,6 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos env ) else None)))) | None -> []) - | QualifiedRecordAccess path -> ( - match getEnvWithOpens ~pos ~env ~package ~opens path with - | None -> [] - | Some (env, suffix) -> - attributeCompletions ~env ~suffix - @ List.concat - (opens |> List.map (fun env -> attributeCompletions ~env ~suffix))) let mkItem ~name ~kind ~detail ~deprecated ~docstring = let docContent = diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 8c10d9afa..d67da22f6 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -9,7 +9,6 @@ type contextPath = | CPObj of contextPath * string type completion = - | QualifiedRecordAccess of SharedTypes.path (* e.g. _.A.B.field where _ indicates a path ending in a lowercase id *) | RecordAccess of SharedTypes.path * SharedTypes.path * string (* e.g. A.B.var .f1.f2 .f3 *) | Path of SharedTypes.path (* e.g. A.B.var or A.B *) @@ -75,22 +74,12 @@ let determineCompletion (dotpath : SharedTypes.path) = | Path path -> Path (one :: path) | RecordAccess (valuePath, middleFields, lastField) -> RecordAccess (one :: valuePath, middleFields, lastField) - | QualifiedRecordAccess _ as completion -> - (* A. _.B.field -> _.B.field *) - completion else match loop rest with - | Path path -> - (* x. B.field -> _.B.field *) - QualifiedRecordAccess path + | Path path -> Path path | RecordAccess ([name], middleFields, lastField) -> RecordAccess ([one], name :: middleFields, lastField) - | RecordAccess (valuePath, middleFields, lastField) -> - (* x.A.B.v.f1.f2.f3 --> .A.B.v.f1.f2.f3 *) - QualifiedRecordAccess (valuePath @ middleFields @ [lastField]) - | QualifiedRecordAccess _ as completion -> - (* x. _.A.f -> _.A.f *) - completion) + | x -> x) in loop dotpath From 70104420bcf5806741ae92efb1372264425e7e2e Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 15:44:46 +0200 Subject: [PATCH 065/135] refactor --- analysis/src/NewCompletions.ml | 44 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index ffd193cd0..eb0c946a5 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -821,7 +821,7 @@ let mkItem ~name ~kind ~detail ~deprecated ~docstring = else Some {kind = "markdown"; value = docContent}); } -let processCompletable ~processDotPath ~full ~package ~rawOpens +let processCompletable ~processCompletion ~full ~package ~rawOpens (completable : PartialParser.completable) = match completable with | Cjsx ([id], prefix, identsSeen) when String.lowercase_ascii id = id -> @@ -841,8 +841,8 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens @ keyLabels | Cjsx (componentPath, prefix, identsSeen) -> let completions = - processDotPath ~completionContext:PartialParser.Value ~exact:true - (componentPath @ ["make"]) + PartialParser.Path (componentPath @ ["make"]) + |> processCompletion ~completionContext:PartialParser.Value ~exact:true in let labels = match completions with @@ -904,7 +904,8 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens | Cpath _ -> assert false | Cdotpath (dotpath, completionContext) -> let completions = - dotpath |> processDotPath ~completionContext ~exact:false + dotpath |> PartialParser.determineCompletion + |> processCompletion ~completionContext ~exact:false in (* TODO(#107): figure out why we're getting duplicates. *) completions |> Utils.dedup @@ -967,8 +968,9 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens match pipeIdPath with | x :: fieldNames -> ( match - [x] - |> processDotPath ~completionContext:PartialParser.Value ~exact:true + PartialParser.Path [x] + |> processCompletion ~completionContext:PartialParser.Value + ~exact:true with | ({Completion.kind = Value typ}, env) :: _ -> ( match getFields ~env ~typ fieldNames with @@ -1018,10 +1020,10 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens if modulePathMinusOpens = "" then name else modulePathMinusOpens ^ "." ^ name in - let dotpath = modulePath @ [partialName] in let declareds = - dotpath - |> processDotPath ~completionContext:PartialParser.Value ~exact:false + PartialParser.Path (modulePath @ [partialName]) + |> processCompletion ~completionContext:PartialParser.Value + ~exact:false in declareds |> List.filter (fun ({Completion.kind}, _env) -> @@ -1080,8 +1082,8 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens | Clabel (funPath, prefix, identsSeen) -> let labels = match - funPath - |> processDotPath ~completionContext:PartialParser.Value ~exact:true + PartialParser.Path funPath + |> processCompletion ~completionContext:PartialParser.Value ~exact:true with | ({Completion.kind = Value typ}, _env) :: _ -> let rec getLabels (t : Types.type_expr) = @@ -1132,7 +1134,8 @@ let processCompletable ~processDotPath ~full ~package ~rawOpens let env0 = QueryEnv.fromFile full.file in let env, fields = match - lhs |> processDotPath ~completionContext:PartialParser.Value ~exact:true + PartialParser.Path lhs + |> processCompletion ~completionContext:PartialParser.Value ~exact:true with | ({Completion.kind = Value typ}, env) :: _ -> getObjectFields ~env typ | _ -> (env0, []) @@ -1152,8 +1155,7 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos ~rawOpens = let package = full.package in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in - let processDotPath ~completionContext ~exact dotpath = - let completion = PartialParser.determineCompletion dotpath in + let processCompletion ~completionContext ~exact completion = let completions = getCompletions ~full ~rawOpens ~allFiles ~pos ~completion in @@ -1174,8 +1176,14 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos | Type, Type _ -> true | Type, _ -> false in - match dotpath |> List.rev with - | last :: _ when exact -> + let lastName = + match completion with + | Path path -> + if path = [] then None else Some (List.nth path (List.length path - 1)) + | RecordAccess (_, _, name) -> Some name + in + match lastName with + | Some lastName when exact -> (* Heuristic to approximate scope. Take the last position before pos if any, or just return the first element. *) let rec prioritize decls = @@ -1191,7 +1199,7 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos in completions |> List.filter (fun ({Completion.name; kind}, _env) -> - name = last && filterKind ~completionContext ~kind) + name = lastName && filterKind ~completionContext ~kind) |> prioritize | _ -> completions @@ -1222,4 +1230,4 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos | _ -> completable in - completable |> processCompletable ~processDotPath ~full ~package ~rawOpens + completable |> processCompletable ~processCompletion ~full ~package ~rawOpens From 67bcf27f8936cbac75b3c96981c59efdcd0c1bf9 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 17:34:56 +0200 Subject: [PATCH 066/135] rename --- analysis/src/NewCompletions.ml | 68 +++++++++++++++++----------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index eb0c946a5..c6d4f8e3f 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -518,16 +518,16 @@ let completionForDeclareds ~pos iter stamps prefix transformContents = stamps; !res -let completionForDeclaredModules ~pos ~env ~suffix = - completionForDeclareds ~pos Stamps.iterModules env.QueryEnv.file.stamps suffix +let completionForDeclaredModules ~pos ~env ~prefix = + completionForDeclareds ~pos Stamps.iterModules env.QueryEnv.file.stamps prefix (fun m -> Completion.Module m) -let completionForDeclaredValues ~pos ~env ~suffix = - completionForDeclareds ~pos Stamps.iterValues env.QueryEnv.file.stamps suffix +let completionForDeclaredValues ~pos ~env ~prefix = + completionForDeclareds ~pos Stamps.iterValues env.QueryEnv.file.stamps prefix (fun m -> Completion.Value m) -let completionForDeclaredTypes ~pos ~env ~suffix = - completionForDeclareds ~pos Stamps.iterTypes env.QueryEnv.file.stamps suffix +let completionForDeclaredTypes ~pos ~env ~prefix = + completionForDeclareds ~pos Stamps.iterTypes env.QueryEnv.file.stamps prefix (fun m -> Completion.Type m) let completionForExporteds iterExported getDeclared prefix transformContents = @@ -550,19 +550,19 @@ let completionForExporteds iterExported getDeclared prefix transformContents = | None -> ()); !res -let completionForExportedModules ~env ~suffix = +let completionForExportedModules ~env ~prefix = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Module) - (Stamps.findModule env.file.stamps) suffix (fun m -> Completion.Module m) + (Stamps.findModule env.file.stamps) prefix (fun m -> Completion.Module m) -let completionForExportedValues ~env ~suffix = +let completionForExportedValues ~env ~prefix = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Value) - (Stamps.findValue env.file.stamps) suffix (fun v -> Completion.Value v) + (Stamps.findValue env.file.stamps) prefix (fun v -> Completion.Value v) -let completionForExportedTypes ~env ~suffix = +let completionForExportedTypes ~env ~prefix = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Type) - (Stamps.findType env.file.stamps) suffix (fun t -> Completion.Type t) + (Stamps.findType env.file.stamps) prefix (fun t -> Completion.Type t) -let completionForConstructors ~(env : QueryEnv.t) ~suffix = +let completionForConstructors ~(env : QueryEnv.t) ~prefix = let res = ref [] in Exported.iter env.exported Exported.Type (fun _name stamp -> match Stamps.findType env.file.stamps stamp with @@ -570,7 +570,7 @@ let completionForConstructors ~(env : QueryEnv.t) ~suffix = res := (constructors |> List.filter (fun c -> - Utils.startsWith c.Constructor.cname.txt suffix) + Utils.startsWith c.Constructor.cname.txt prefix) |> List.map (fun c -> Completion.create ~name:c.Constructor.cname.txt ~kind: @@ -580,14 +580,14 @@ let completionForConstructors ~(env : QueryEnv.t) ~suffix = | _ -> ()); !res -let completionForFields ~(env : QueryEnv.t) ~suffix = +let completionForFields ~(env : QueryEnv.t) ~prefix = let res = ref [] in Exported.iter env.exported Exported.Type (fun _name stamp -> match Stamps.findType env.file.stamps stamp with | Some ({item = {kind = Record fields}} as t) -> res := (fields - |> List.filter (fun f -> Utils.startsWith f.fname.txt suffix) + |> List.filter (fun f -> Utils.startsWith f.fname.txt prefix) |> List.map (fun f -> Completion.create ~name:f.fname.txt ~kind: @@ -638,22 +638,22 @@ let detail name (kind : Completion.kind) = | Field ({typ}, s) -> name ^ ": " ^ (typ |> Shared.typeToString) ^ "\n\n" ^ s | Constructor (c, s) -> showConstructor c ^ "\n\n" ^ s -let localCompletions ~pos ~(env : QueryEnv.t) suffix = +let localCompletions ~pos ~(env : QueryEnv.t) ~prefix = Log.log "---------------- LOCAL VAL"; - completionForDeclaredModules ~pos ~env ~suffix - @ completionForConstructors ~env ~suffix - @ completionForDeclaredValues ~pos ~env ~suffix - @ completionForDeclaredTypes ~pos ~env ~suffix - @ completionForFields ~env ~suffix + completionForDeclaredModules ~pos ~env ~prefix + @ completionForConstructors ~env ~prefix + @ completionForDeclaredValues ~pos ~env ~prefix + @ completionForDeclaredTypes ~pos ~env ~prefix + @ completionForFields ~env ~prefix |> List.map (fun r -> (r, env)) -let allCompletions ~(env : QueryEnv.t) suffix = +let allCompletions ~(env : QueryEnv.t) ~prefix = Log.log (" - Completing in " ^ Uri2.toString env.file.uri); - completionForExportedModules ~env ~suffix - @ completionForConstructors ~env ~suffix - @ completionForExportedValues ~env ~suffix - @ completionForExportedTypes ~env ~suffix - @ completionForFields ~env ~suffix + completionForExportedModules ~env ~prefix + @ completionForConstructors ~env ~prefix + @ completionForExportedValues ~env ~prefix + @ completionForExportedTypes ~env ~prefix + @ completionForFields ~env ~prefix |> List.map (fun r -> (r, env)) (* TODO filter out things that are defined after the current position *) @@ -716,14 +716,14 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos let opens = List.rev resolvedOpens in match completion with | Path [] -> [] - | Path [suffix] -> - let locallyDefinedValues = localCompletions ~pos ~env suffix in + | Path [prefix] -> + let locallyDefinedValues = localCompletions ~pos ~env ~prefix in let alreadyUsedIdentifiers = Hashtbl.create 10 in let valuesFromOpens = opens |> List.fold_left (fun results env -> - let completionsFromThisOpen = allCompletions ~env suffix in + let completionsFromThisOpen = allCompletions ~env ~prefix in List.filter (fun ((declared : Completion.t), _env) -> if Hashtbl.mem alreadyUsedIdentifiers declared.name then @@ -740,7 +740,7 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos let localModuleNames = allFiles |> FileSet.elements |> Utils.filterMap (fun name -> - if Utils.startsWith name suffix && not (String.contains name '-') + if Utils.startsWith name prefix && not (String.contains name '-') then Some ( Completion.create ~name ~kind:(Completion.FileModule name), @@ -751,9 +751,9 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos | Path path -> ( Log.log ("Path " ^ pathToString path); match getEnvWithOpens ~pos ~env ~package ~opens path with - | Some (env, suffix) -> + | Some (env, prefix) -> Log.log "Got the env"; - allCompletions ~env suffix + allCompletions ~env ~prefix | None -> []) | RecordAccess (valuePath, middleFields, lastField) -> ( Log.log ("lastField :" ^ lastField); From bcc594c2234f2968575484622b3317c72484ae6b Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 18:03:10 +0200 Subject: [PATCH 067/135] refactor --- analysis/src/NewCompletions.ml | 143 ++++++++++++++++----------------- 1 file changed, 70 insertions(+), 73 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index c6d4f8e3f..aac0dc658 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -496,13 +496,16 @@ let resolveOpens ~env ~previous opens ~package = (* loop(previous) *) previous opens -let completionForDeclareds ~pos iter stamps prefix transformContents = +let checkName name ~prefix ~exact = + if exact then name = prefix else Utils.startsWith name prefix + +let completionForDeclareds ~pos ~iter ~stamps ~prefix ~exact transformContents = (* Log.log("completion for declares " ++ prefix); *) let res = ref [] in iter (fun _stamp (declared : _ Declared.t) -> if - Utils.startsWith declared.name.txt prefix + checkName declared.name.txt ~prefix ~exact && Utils.locationContainsFuzzy declared.scopeLoc pos then res := @@ -519,22 +522,23 @@ let completionForDeclareds ~pos iter stamps prefix transformContents = !res let completionForDeclaredModules ~pos ~env ~prefix = - completionForDeclareds ~pos Stamps.iterModules env.QueryEnv.file.stamps prefix - (fun m -> Completion.Module m) + completionForDeclareds ~pos ~iter:Stamps.iterModules + ~stamps:env.QueryEnv.file.stamps ~prefix (fun m -> Completion.Module m) let completionForDeclaredValues ~pos ~env ~prefix = - completionForDeclareds ~pos Stamps.iterValues env.QueryEnv.file.stamps prefix - (fun m -> Completion.Value m) + completionForDeclareds ~pos ~iter:Stamps.iterValues + ~stamps:env.QueryEnv.file.stamps ~prefix (fun m -> Completion.Value m) let completionForDeclaredTypes ~pos ~env ~prefix = - completionForDeclareds ~pos Stamps.iterTypes env.QueryEnv.file.stamps prefix - (fun m -> Completion.Type m) + completionForDeclareds ~pos ~iter:Stamps.iterTypes + ~stamps:env.QueryEnv.file.stamps ~prefix (fun m -> Completion.Type m) -let completionForExporteds iterExported getDeclared prefix transformContents = +let completionForExporteds iterExported getDeclared ~prefix ~exact + transformContents = let res = ref [] in iterExported (fun name stamp -> (* Log.log("checking exported: " ++ name); *) - if Utils.startsWith name prefix then + if checkName name ~prefix ~exact then match getDeclared stamp with | Some (declared : _ Declared.t) -> res := @@ -550,19 +554,22 @@ let completionForExporteds iterExported getDeclared prefix transformContents = | None -> ()); !res -let completionForExportedModules ~env ~prefix = +let completionForExportedModules ~env ~prefix ~exact = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Module) - (Stamps.findModule env.file.stamps) prefix (fun m -> Completion.Module m) + (Stamps.findModule env.file.stamps) ~prefix ~exact (fun m -> + Completion.Module m) -let completionForExportedValues ~env ~prefix = +let completionForExportedValues ~env ~prefix ~exact = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Value) - (Stamps.findValue env.file.stamps) prefix (fun v -> Completion.Value v) + (Stamps.findValue env.file.stamps) ~prefix ~exact (fun v -> + Completion.Value v) -let completionForExportedTypes ~env ~prefix = +let completionForExportedTypes ~env ~prefix ~exact = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Type) - (Stamps.findType env.file.stamps) prefix (fun t -> Completion.Type t) + (Stamps.findType env.file.stamps) ~prefix ~exact (fun t -> + Completion.Type t) -let completionForConstructors ~(env : QueryEnv.t) ~prefix = +let completionForConstructors ~(env : QueryEnv.t) ~prefix ~exact = let res = ref [] in Exported.iter env.exported Exported.Type (fun _name stamp -> match Stamps.findType env.file.stamps stamp with @@ -570,7 +577,7 @@ let completionForConstructors ~(env : QueryEnv.t) ~prefix = res := (constructors |> List.filter (fun c -> - Utils.startsWith c.Constructor.cname.txt prefix) + checkName c.Constructor.cname.txt ~prefix ~exact) |> List.map (fun c -> Completion.create ~name:c.Constructor.cname.txt ~kind: @@ -580,14 +587,14 @@ let completionForConstructors ~(env : QueryEnv.t) ~prefix = | _ -> ()); !res -let completionForFields ~(env : QueryEnv.t) ~prefix = +let completionForFields ~(env : QueryEnv.t) ~prefix ~exact = let res = ref [] in Exported.iter env.exported Exported.Type (fun _name stamp -> match Stamps.findType env.file.stamps stamp with | Some ({item = {kind = Record fields}} as t) -> res := (fields - |> List.filter (fun f -> Utils.startsWith f.fname.txt prefix) + |> List.filter (fun f -> checkName f.fname.txt ~prefix ~exact) |> List.map (fun f -> Completion.create ~name:f.fname.txt ~kind: @@ -638,22 +645,22 @@ let detail name (kind : Completion.kind) = | Field ({typ}, s) -> name ^ ": " ^ (typ |> Shared.typeToString) ^ "\n\n" ^ s | Constructor (c, s) -> showConstructor c ^ "\n\n" ^ s -let localCompletions ~pos ~(env : QueryEnv.t) ~prefix = +let localCompletions ~pos ~(env : QueryEnv.t) ~prefix ~exact = Log.log "---------------- LOCAL VAL"; - completionForDeclaredModules ~pos ~env ~prefix - @ completionForConstructors ~env ~prefix - @ completionForDeclaredValues ~pos ~env ~prefix - @ completionForDeclaredTypes ~pos ~env ~prefix - @ completionForFields ~env ~prefix + completionForDeclaredModules ~pos ~env ~prefix ~exact + @ completionForConstructors ~env ~prefix ~exact + @ completionForDeclaredValues ~pos ~env ~prefix ~exact + @ completionForDeclaredTypes ~pos ~env ~prefix ~exact + @ completionForFields ~env ~prefix ~exact |> List.map (fun r -> (r, env)) -let allCompletions ~(env : QueryEnv.t) ~prefix = +let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = Log.log (" - Completing in " ^ Uri2.toString env.file.uri); - completionForExportedModules ~env ~prefix - @ completionForConstructors ~env ~prefix - @ completionForExportedValues ~env ~prefix - @ completionForExportedTypes ~env ~prefix - @ completionForFields ~env ~prefix + completionForExportedModules ~env ~prefix ~exact + @ completionForConstructors ~env ~prefix ~exact + @ completionForExportedValues ~env ~prefix ~exact + @ completionForExportedTypes ~env ~prefix ~exact + @ completionForFields ~env ~prefix ~exact |> List.map (fun r -> (r, env)) (* TODO filter out things that are defined after the current position *) @@ -693,7 +700,7 @@ let rec extractObjectType ~env ~package (t : Types.type_expr) = | _ -> None) | _ -> None -let getCompletions ~full ~rawOpens ~allFiles ~pos +let getCompletions ~full ~rawOpens ~allFiles ~pos ~exact ~(completion : PartialParser.completion) = Log.log ("Opens folkz > " @@ -717,13 +724,13 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos match completion with | Path [] -> [] | Path [prefix] -> - let locallyDefinedValues = localCompletions ~pos ~env ~prefix in + let locallyDefinedValues = localCompletions ~pos ~env ~prefix ~exact in let alreadyUsedIdentifiers = Hashtbl.create 10 in let valuesFromOpens = opens |> List.fold_left (fun results env -> - let completionsFromThisOpen = allCompletions ~env ~prefix in + let completionsFromThisOpen = allCompletions ~env ~prefix ~exact in List.filter (fun ((declared : Completion.t), _env) -> if Hashtbl.mem alreadyUsedIdentifiers declared.name then @@ -740,7 +747,7 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos let localModuleNames = allFiles |> FileSet.elements |> Utils.filterMap (fun name -> - if Utils.startsWith name prefix && not (String.contains name '-') + if checkName name ~prefix ~exact && not (String.contains name '-') then Some ( Completion.create ~name ~kind:(Completion.FileModule name), @@ -753,7 +760,7 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos match getEnvWithOpens ~pos ~env ~package ~opens path with | Some (env, prefix) -> Log.log "Got the env"; - allCompletions ~env ~prefix + allCompletions ~env ~prefix ~exact | None -> []) | RecordAccess (valuePath, middleFields, lastField) -> ( Log.log ("lastField :" ^ lastField); @@ -789,7 +796,7 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos | Some (env, fields, typDecl) -> fields |> Utils.filterMap (fun field -> - if Utils.startsWith field.fname.txt lastField then + if checkName field.fname.txt ~prefix:lastField ~exact then Some ( Completion.create ~name:field.fname.txt ~kind: @@ -1151,40 +1158,36 @@ let processCompletable ~processCompletion ~full ~package ~rawOpens |> List.filter (fun (name, _t) -> Utils.startsWith name prefix) |> List.map mkLabel +let filterCompletionKind ~(completionContext : PartialParser.completionContext) + ~(kind : Completion.kind) = + match (completionContext, kind) with + | Module, (Module _ | FileModule _) -> true + | Module, _ -> false + | (Field | Type | Value), (Module _ | FileModule _) -> + (* M.field M.type M.value *) + true + | Value, (Value _ | Constructor _) -> + (* x Red *) + true + | Value, _ -> false + | Field, Field _ -> true + | Field, _ -> false + | Type, Type _ -> true + | Type, _ -> false + let computeCompletions ~(completable : PartialParser.completable) ~full ~pos ~rawOpens = let package = full.package in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in let processCompletion ~completionContext ~exact completion = let completions = - getCompletions ~full ~rawOpens ~allFiles ~pos ~completion - in - let filterKind ~(completionContext : PartialParser.completionContext) - ~(kind : Completion.kind) = - match (completionContext, kind) with - | Module, (Module _ | FileModule _) -> true - | Module, _ -> false - | (Field | Type | Value), (Module _ | FileModule _) -> - (* M.field M.type M.value *) - true - | Value, (Value _ | Constructor _) -> - (* x Red *) - true - | Value, _ -> false - | Field, Field _ -> true - | Field, _ -> false - | Type, Type _ -> true - | Type, _ -> false - in - let lastName = - match completion with - | Path path -> - if path = [] then None else Some (List.nth path (List.length path - 1)) - | RecordAccess (_, _, name) -> Some name + getCompletions ~full ~rawOpens ~allFiles ~pos ~completion ~exact + |> List.filter (fun ({Completion.kind}, _env) -> + filterCompletionKind ~completionContext ~kind) in - match lastName with - | Some lastName when exact -> - (* Heuristic to approximate scope. + if exact then + (* Heuristic to approximate scope when an exact name is required and there could + be more than one instance of that name. Take the last position before pos if any, or just return the first element. *) let rec prioritize decls = match decls with @@ -1197,14 +1200,8 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos else prioritize ((d1, e1) :: rest) | [] | [_] -> decls in - completions - |> List.filter (fun ({Completion.name; kind}, _env) -> - name = lastName && filterKind ~completionContext ~kind) - |> prioritize - | _ -> - completions - |> List.filter (fun ({Completion.kind}, _env) -> - filterKind ~completionContext ~kind) + prioritize completions + else completions in let rec processContextPath (cp : PartialParser.contextPath) : From 810c168295161972af4c932f7841c2c559c527eb Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 18:14:49 +0200 Subject: [PATCH 068/135] refactor --- analysis/src/NewCompletions.ml | 111 ++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 52 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index aac0dc658..49340c161 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -521,17 +521,19 @@ let completionForDeclareds ~pos ~iter ~stamps ~prefix ~exact transformContents = stamps; !res -let completionForDeclaredModules ~pos ~env ~prefix = +let completionForDeclaredModules ~pos ~env ~prefix ~exact = completionForDeclareds ~pos ~iter:Stamps.iterModules - ~stamps:env.QueryEnv.file.stamps ~prefix (fun m -> Completion.Module m) + ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> + Completion.Module m) -let completionForDeclaredValues ~pos ~env ~prefix = +let completionForDeclaredValues ~pos ~env ~prefix ~exact = completionForDeclareds ~pos ~iter:Stamps.iterValues - ~stamps:env.QueryEnv.file.stamps ~prefix (fun m -> Completion.Value m) + ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> + Completion.Value m) -let completionForDeclaredTypes ~pos ~env ~prefix = +let completionForDeclaredTypes ~pos ~env ~prefix ~exact = completionForDeclareds ~pos ~iter:Stamps.iterTypes - ~stamps:env.QueryEnv.file.stamps ~prefix (fun m -> Completion.Type m) + ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Type m) let completionForExporteds iterExported getDeclared ~prefix ~exact transformContents = @@ -828,7 +830,49 @@ let mkItem ~name ~kind ~detail ~deprecated ~docstring = else Some {kind = "markdown"; value = docContent}); } -let processCompletable ~processCompletion ~full ~package ~rawOpens +let filterCompletionKind ~(completionContext : PartialParser.completionContext) + ~(kind : Completion.kind) = + match (completionContext, kind) with + | Module, (Module _ | FileModule _) -> true + | Module, _ -> false + | (Field | Type | Value), (Module _ | FileModule _) -> + (* M.field M.type M.value *) + true + | Value, (Value _ | Constructor _) -> + (* x Red *) + true + | Value, _ -> false + | Field, Field _ -> true + | Field, _ -> false + | Type, Type _ -> true + | Type, _ -> false + +let processCompletion ~completionContext ~exact ~full ~rawOpens ~allFiles ~pos + completion = + let completions = + getCompletions ~full ~rawOpens ~allFiles ~pos ~completion ~exact + |> List.filter (fun ({Completion.kind}, _env) -> + filterCompletionKind ~completionContext ~kind) + in + if exact then + (* Heuristic to approximate scope when an exact name is required and there could + be more than one instance of that name. + Take the last position before pos if any, or just return the first element. *) + let rec prioritize decls = + match decls with + | (d1, e1) :: (d2, e2) :: rest -> + let pos2 = d2.Completion.extentLoc.loc_start |> Pos.ofLexing in + if pos2 >= pos then prioritize ((d1, e1) :: rest) + else + let pos1 = d1.extentLoc.loc_start |> Pos.ofLexing in + if pos1 <= pos2 then prioritize ((d2, e2) :: rest) + else prioritize ((d1, e1) :: rest) + | [] | [_] -> decls + in + prioritize completions + else completions + +let processCompletable ~full ~package ~rawOpens ~allFiles ~pos (completable : PartialParser.completable) = match completable with | Cjsx ([id], prefix, identsSeen) when String.lowercase_ascii id = id -> @@ -850,6 +894,7 @@ let processCompletable ~processCompletion ~full ~package ~rawOpens let completions = PartialParser.Path (componentPath @ ["make"]) |> processCompletion ~completionContext:PartialParser.Value ~exact:true + ~full ~rawOpens ~allFiles ~pos in let labels = match completions with @@ -912,7 +957,8 @@ let processCompletable ~processCompletion ~full ~package ~rawOpens | Cdotpath (dotpath, completionContext) -> let completions = dotpath |> PartialParser.determineCompletion - |> processCompletion ~completionContext ~exact:false + |> processCompletion ~completionContext ~exact:false ~full ~rawOpens + ~allFiles ~pos in (* TODO(#107): figure out why we're getting duplicates. *) completions |> Utils.dedup @@ -977,7 +1023,7 @@ let processCompletable ~processCompletion ~full ~package ~rawOpens match PartialParser.Path [x] |> processCompletion ~completionContext:PartialParser.Value - ~exact:true + ~exact:true ~full ~rawOpens ~allFiles ~pos with | ({Completion.kind = Value typ}, env) :: _ -> ( match getFields ~env ~typ fieldNames with @@ -1030,7 +1076,7 @@ let processCompletable ~processCompletion ~full ~package ~rawOpens let declareds = PartialParser.Path (modulePath @ [partialName]) |> processCompletion ~completionContext:PartialParser.Value - ~exact:false + ~exact:false ~full ~rawOpens ~allFiles ~pos in declareds |> List.filter (fun ({Completion.kind}, _env) -> @@ -1091,6 +1137,7 @@ let processCompletable ~processCompletion ~full ~package ~rawOpens match PartialParser.Path funPath |> processCompletion ~completionContext:PartialParser.Value ~exact:true + ~full ~rawOpens ~allFiles ~pos with | ({Completion.kind = Value typ}, _env) :: _ -> let rec getLabels (t : Types.type_expr) = @@ -1143,6 +1190,7 @@ let processCompletable ~processCompletion ~full ~package ~rawOpens match PartialParser.Path lhs |> processCompletion ~completionContext:PartialParser.Value ~exact:true + ~full ~rawOpens ~allFiles ~pos with | ({Completion.kind = Value typ}, env) :: _ -> getObjectFields ~env typ | _ -> (env0, []) @@ -1158,51 +1206,10 @@ let processCompletable ~processCompletion ~full ~package ~rawOpens |> List.filter (fun (name, _t) -> Utils.startsWith name prefix) |> List.map mkLabel -let filterCompletionKind ~(completionContext : PartialParser.completionContext) - ~(kind : Completion.kind) = - match (completionContext, kind) with - | Module, (Module _ | FileModule _) -> true - | Module, _ -> false - | (Field | Type | Value), (Module _ | FileModule _) -> - (* M.field M.type M.value *) - true - | Value, (Value _ | Constructor _) -> - (* x Red *) - true - | Value, _ -> false - | Field, Field _ -> true - | Field, _ -> false - | Type, Type _ -> true - | Type, _ -> false - let computeCompletions ~(completable : PartialParser.completable) ~full ~pos ~rawOpens = let package = full.package in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in - let processCompletion ~completionContext ~exact completion = - let completions = - getCompletions ~full ~rawOpens ~allFiles ~pos ~completion ~exact - |> List.filter (fun ({Completion.kind}, _env) -> - filterCompletionKind ~completionContext ~kind) - in - if exact then - (* Heuristic to approximate scope when an exact name is required and there could - be more than one instance of that name. - Take the last position before pos if any, or just return the first element. *) - let rec prioritize decls = - match decls with - | (d1, e1) :: (d2, e2) :: rest -> - let pos2 = d2.Completion.extentLoc.loc_start |> Pos.ofLexing in - if pos2 >= pos then prioritize ((d1, e1) :: rest) - else - let pos1 = d1.extentLoc.loc_start |> Pos.ofLexing in - if pos1 <= pos2 then prioritize ((d2, e2) :: rest) - else prioritize ((d1, e1) :: rest) - | [] | [_] -> decls - in - prioritize completions - else completions - in let rec processContextPath (cp : PartialParser.contextPath) : PartialParser.completable = @@ -1227,4 +1234,4 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos | _ -> completable in - completable |> processCompletable ~processCompletion ~full ~package ~rawOpens + completable |> processCompletable ~full ~package ~rawOpens ~allFiles ~pos From 7c785817afac1353b6626c8aabc3f0f1948d74fe Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 18:23:17 +0200 Subject: [PATCH 069/135] rename and fix lowecase ascii --- analysis/src/NewCompletions.ml | 35 +++++++++++----------------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 49340c161..b0efe786b 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -874,8 +874,13 @@ let processCompletion ~completionContext ~exact ~full ~rawOpens ~allFiles ~pos let processCompletable ~full ~package ~rawOpens ~allFiles ~pos (completable : PartialParser.completable) = + let processValue ~exact path = + PartialParser.Path path + |> processCompletion ~completionContext:PartialParser.Value ~exact ~full + ~rawOpens ~allFiles ~pos + in match completable with - | Cjsx ([id], prefix, identsSeen) when String.lowercase_ascii id = id -> + | Cjsx ([id], prefix, identsSeen) when String.uncapitalize_ascii id = id -> let mkLabel_ name typString = mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] in @@ -891,11 +896,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos |> List.map mkLabel) @ keyLabels | Cjsx (componentPath, prefix, identsSeen) -> - let completions = - PartialParser.Path (componentPath @ ["make"]) - |> processCompletion ~completionContext:PartialParser.Value ~exact:true - ~full ~rawOpens ~allFiles ~pos - in + let completions = componentPath @ ["make"] |> processValue ~exact:true in let labels = match completions with | ({Completion.kind = Completion.Value typ}, _env) :: _ -> @@ -1020,11 +1021,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos in match pipeIdPath with | x :: fieldNames -> ( - match - PartialParser.Path [x] - |> processCompletion ~completionContext:PartialParser.Value - ~exact:true ~full ~rawOpens ~allFiles ~pos - with + match [x] |> processValue ~exact:true with | ({Completion.kind = Value typ}, env) :: _ -> ( match getFields ~env ~typ fieldNames with | None -> None @@ -1074,9 +1071,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos else modulePathMinusOpens ^ "." ^ name in let declareds = - PartialParser.Path (modulePath @ [partialName]) - |> processCompletion ~completionContext:PartialParser.Value - ~exact:false ~full ~rawOpens ~allFiles ~pos + modulePath @ [partialName] |> processValue ~exact:false in declareds |> List.filter (fun ({Completion.kind}, _env) -> @@ -1134,11 +1129,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos |> List.map mkDecorator | Clabel (funPath, prefix, identsSeen) -> let labels = - match - PartialParser.Path funPath - |> processCompletion ~completionContext:PartialParser.Value ~exact:true - ~full ~rawOpens ~allFiles ~pos - with + match funPath |> processValue ~exact:true with | ({Completion.kind = Value typ}, _env) :: _ -> let rec getLabels (t : Types.type_expr) = match t.desc with @@ -1187,11 +1178,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos in let env0 = QueryEnv.fromFile full.file in let env, fields = - match - PartialParser.Path lhs - |> processCompletion ~completionContext:PartialParser.Value ~exact:true - ~full ~rawOpens ~allFiles ~pos - with + match lhs |> processValue ~exact:true with | ({Completion.kind = Value typ}, env) :: _ -> getObjectFields ~env typ | _ -> (env0, []) in From 8cecc554d32ff2be9c144dfa00cf09ba0f89317e Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 18:25:03 +0200 Subject: [PATCH 070/135] move --- analysis/src/NewCompletions.ml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index b0efe786b..3a39d27d2 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -880,6 +880,19 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos ~rawOpens ~allFiles ~pos in match completable with + | Cpath _ -> assert false + | Cdotpath (dotpath, completionContext) -> + let completions = + dotpath |> PartialParser.determineCompletion + |> processCompletion ~completionContext ~exact:false ~full ~rawOpens + ~allFiles ~pos + in + (* TODO(#107): figure out why we're getting duplicates. *) + completions |> Utils.dedup + |> List.map (fun ({Completion.name; deprecated; docstring; kind}, _env) -> + mkItem ~name + ~kind:(Completion.kindToInt kind) + ~deprecated ~detail:(detail name kind) ~docstring) | Cjsx ([id], prefix, identsSeen) when String.uncapitalize_ascii id = id -> let mkLabel_ name typString = mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] @@ -954,19 +967,6 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel) @ keyLabels - | Cpath _ -> assert false - | Cdotpath (dotpath, completionContext) -> - let completions = - dotpath |> PartialParser.determineCompletion - |> processCompletion ~completionContext ~exact:false ~full ~rawOpens - ~allFiles ~pos - in - (* TODO(#107): figure out why we're getting duplicates. *) - completions |> Utils.dedup - |> List.map (fun ({Completion.name; deprecated; docstring; kind}, _env) -> - mkItem ~name - ~kind:(Completion.kindToInt kind) - ~deprecated ~detail:(detail name kind) ~docstring) | Cpipe (pipe, partialName) -> ( let arrayModulePath = ["Js"; "Array2"] in let listModulePath = ["Belt"; "List"] in From 5d703b168fa900476e77d882b512d9fba87e5e22 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 18:29:16 +0200 Subject: [PATCH 071/135] refactor --- analysis/src/NewCompletions.ml | 105 +++++++++++++++++---------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 3a39d27d2..4a7ee6866 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -872,6 +872,19 @@ let processCompletion ~completionContext ~exact ~full ~rawOpens ~allFiles ~pos prioritize completions else completions +let processDotPath ~full ~rawOpens ~allFiles ~pos dotpath completionContext = + let completions = + dotpath |> PartialParser.determineCompletion + |> processCompletion ~completionContext ~exact:false ~full ~rawOpens + ~allFiles ~pos + in + (* TODO(#107): figure out why we're getting duplicates. *) + completions |> Utils.dedup + |> List.map (fun ({Completion.name; deprecated; docstring; kind}, _env) -> + mkItem ~name + ~kind:(Completion.kindToInt kind) + ~deprecated ~detail:(detail name kind) ~docstring) + let processCompletable ~full ~package ~rawOpens ~allFiles ~pos (completable : PartialParser.completable) = let processValue ~exact path = @@ -882,17 +895,48 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos match completable with | Cpath _ -> assert false | Cdotpath (dotpath, completionContext) -> - let completions = - dotpath |> PartialParser.determineCompletion - |> processCompletion ~completionContext ~exact:false ~full ~rawOpens - ~allFiles ~pos + processDotPath ~full ~rawOpens ~allFiles ~pos dotpath completionContext + | Cobj (lhs, path, prefix) -> + let rec getFields (texp : Types.type_expr) = + match texp.desc with + | Tfield (name, _, t1, t2) -> + let fields = t2 |> getFields in + (name, t1) :: fields + | Tlink te -> te |> getFields + | Tvar None -> [] + | _ -> [] + in + let getObjectFields ~env (t : Types.type_expr) = + match t |> extractObjectType ~env ~package with + | Some (env, tObj) -> (env, getFields tObj) + | None -> (env, []) + in + let rec resolvePath ~env fields path = + match path with + | name :: restPath -> ( + match fields |> List.find_opt (fun (n, _) -> n = name) with + | Some (_, fieldType) -> + let env, innerFields = getObjectFields ~env fieldType in + resolvePath ~env innerFields restPath + | None -> []) + | [] -> fields in - (* TODO(#107): figure out why we're getting duplicates. *) - completions |> Utils.dedup - |> List.map (fun ({Completion.name; deprecated; docstring; kind}, _env) -> - mkItem ~name - ~kind:(Completion.kindToInt kind) - ~deprecated ~detail:(detail name kind) ~docstring) + let env0 = QueryEnv.fromFile full.file in + let env, fields = + match lhs |> processValue ~exact:true with + | ({Completion.kind = Value typ}, env) :: _ -> getObjectFields ~env typ + | _ -> (env0, []) + in + let labels = resolvePath ~env fields path in + let mkLabel_ name typString = + mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] + in + let mkLabel (name, typ) = mkLabel_ name (typ |> Shared.typeToString) in + if labels = [] then [] + else + labels + |> List.filter (fun (name, _t) -> Utils.startsWith name prefix) + |> List.map mkLabel | Cjsx ([id], prefix, identsSeen) when String.uncapitalize_ascii id = id -> let mkLabel_ name typString = mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] @@ -1151,47 +1195,6 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos |> List.filter (fun (name, _t) -> Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel - | Cobj (lhs, path, prefix) -> - let rec getFields (texp : Types.type_expr) = - match texp.desc with - | Tfield (name, _, t1, t2) -> - let fields = t2 |> getFields in - (name, t1) :: fields - | Tlink te -> te |> getFields - | Tvar None -> [] - | _ -> [] - in - let getObjectFields ~env (t : Types.type_expr) = - match t |> extractObjectType ~env ~package with - | Some (env, tObj) -> (env, getFields tObj) - | None -> (env, []) - in - let rec resolvePath ~env fields path = - match path with - | name :: restPath -> ( - match fields |> List.find_opt (fun (n, _) -> n = name) with - | Some (_, fieldType) -> - let env, innerFields = getObjectFields ~env fieldType in - resolvePath ~env innerFields restPath - | None -> []) - | [] -> fields - in - let env0 = QueryEnv.fromFile full.file in - let env, fields = - match lhs |> processValue ~exact:true with - | ({Completion.kind = Value typ}, env) :: _ -> getObjectFields ~env typ - | _ -> (env0, []) - in - let labels = resolvePath ~env fields path in - let mkLabel_ name typString = - mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] - in - let mkLabel (name, typ) = mkLabel_ name (typ |> Shared.typeToString) in - if labels = [] then [] - else - labels - |> List.filter (fun (name, _t) -> Utils.startsWith name prefix) - |> List.map mkLabel let computeCompletions ~(completable : PartialParser.completable) ~full ~pos ~rawOpens = From 32dbfcf9dad5cbb350b82296844018105b60528b Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 18:34:51 +0200 Subject: [PATCH 072/135] cleanup --- analysis/src/NewCompletions.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 4a7ee6866..1290e94b1 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -1118,8 +1118,6 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos modulePath @ [partialName] |> processValue ~exact:false in declareds - |> List.filter (fun ({Completion.kind}, _env) -> - match kind with Completion.Value _ -> true | _ -> false) |> List.map (fun ({Completion.name; deprecated; docstring; kind}, _env) -> mkItem ~name:(completionName name) From 161f6922db45099457abf0ac0ba473617a3a86a9 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 18:37:37 +0200 Subject: [PATCH 073/135] cleanup --- analysis/src/NewCompletions.ml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 1290e94b1..785671771 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -887,10 +887,10 @@ let processDotPath ~full ~rawOpens ~allFiles ~pos dotpath completionContext = let processCompletable ~full ~package ~rawOpens ~allFiles ~pos (completable : PartialParser.completable) = - let processValue ~exact path = + let processValue path = PartialParser.Path path - |> processCompletion ~completionContext:PartialParser.Value ~exact ~full - ~rawOpens ~allFiles ~pos + |> processCompletion ~completionContext:PartialParser.Value ~exact:true + ~full ~rawOpens ~allFiles ~pos in match completable with | Cpath _ -> assert false @@ -923,7 +923,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos in let env0 = QueryEnv.fromFile full.file in let env, fields = - match lhs |> processValue ~exact:true with + match lhs |> processValue with | ({Completion.kind = Value typ}, env) :: _ -> getObjectFields ~env typ | _ -> (env0, []) in @@ -953,7 +953,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos |> List.map mkLabel) @ keyLabels | Cjsx (componentPath, prefix, identsSeen) -> - let completions = componentPath @ ["make"] |> processValue ~exact:true in + let completions = componentPath @ ["make"] |> processValue in let labels = match completions with | ({Completion.kind = Completion.Value typ}, _env) :: _ -> @@ -1065,7 +1065,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos in match pipeIdPath with | x :: fieldNames -> ( - match [x] |> processValue ~exact:true with + match [x] |> processValue with | ({Completion.kind = Value typ}, env) :: _ -> ( match getFields ~env ~typ fieldNames with | None -> None @@ -1115,7 +1115,9 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos else modulePathMinusOpens ^ "." ^ name in let declareds = - modulePath @ [partialName] |> processValue ~exact:false + PartialParser.Path (modulePath @ [partialName]) + |> processCompletion ~completionContext:PartialParser.Value + ~exact:false ~full ~rawOpens ~allFiles ~pos in declareds |> List.map @@ -1171,7 +1173,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos |> List.map mkDecorator | Clabel (funPath, prefix, identsSeen) -> let labels = - match funPath |> processValue ~exact:true with + match funPath |> processValue with | ({Completion.kind = Value typ}, _env) :: _ -> let rec getLabels (t : Types.type_expr) = match t.desc with From c84f36483d4aea451e0fabc02225196549fab5eb Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 19:25:14 +0200 Subject: [PATCH 074/135] refactor --- analysis/src/NewCompletions.ml | 37 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 785671771..55043b06f 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -887,10 +887,14 @@ let processDotPath ~full ~rawOpens ~allFiles ~pos dotpath completionContext = let processCompletable ~full ~package ~rawOpens ~allFiles ~pos (completable : PartialParser.completable) = - let processValue path = - PartialParser.Path path - |> processCompletion ~completionContext:PartialParser.Value ~exact:true - ~full ~rawOpens ~allFiles ~pos + let findValue path = + match + PartialParser.Path path + |> processCompletion ~completionContext:PartialParser.Value ~exact:true + ~full ~rawOpens ~allFiles ~pos + with + | ({Completion.kind = Value typ}, env) :: _ -> Some (typ, env) + | _ -> None in match completable with | Cpath _ -> assert false @@ -923,9 +927,9 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos in let env0 = QueryEnv.fromFile full.file in let env, fields = - match lhs |> processValue with - | ({Completion.kind = Value typ}, env) :: _ -> getObjectFields ~env typ - | _ -> (env0, []) + match lhs |> findValue with + | Some (typ, env) -> getObjectFields ~env typ + | None -> (env0, []) in let labels = resolvePath ~env fields path in let mkLabel_ name typString = @@ -953,10 +957,9 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos |> List.map mkLabel) @ keyLabels | Cjsx (componentPath, prefix, identsSeen) -> - let completions = componentPath @ ["make"] |> processValue in let labels = - match completions with - | ({Completion.kind = Completion.Value typ}, _env) :: _ -> + match componentPath @ ["make"] |> findValue with + | Some (typ, _env) -> let rec getFields (texp : Types.type_expr) = match texp.desc with | Tfield (name, _, t1, t2) -> @@ -995,7 +998,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos | _ -> [] in typ |> getLabels - | _ -> [] + | None -> [] in let mkLabel_ name typString = mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] @@ -1065,12 +1068,12 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos in match pipeIdPath with | x :: fieldNames -> ( - match [x] |> processValue with - | ({Completion.kind = Value typ}, env) :: _ -> ( + match [x] |> findValue with + | Some (typ, env) -> ( match getFields ~env ~typ fieldNames with | None -> None | Some (typ1, _env1) -> fromType typ1) - | _ -> None) + | None -> None) | [] -> None in let lhsPath = @@ -1173,8 +1176,8 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos |> List.map mkDecorator | Clabel (funPath, prefix, identsSeen) -> let labels = - match funPath |> processValue with - | ({Completion.kind = Value typ}, _env) :: _ -> + match funPath |> findValue with + | Some (typ, _env) -> let rec getLabels (t : Types.type_expr) = match t.desc with | Tlink t1 | Tsubst t1 -> getLabels t1 @@ -1184,7 +1187,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos | _ -> [] in typ |> getLabels - | _ -> [] + | None -> [] in let mkLabel (name, typ) = mkItem ~name ~kind:4 ~deprecated:None From e04bbb72c34bf9cc5b91024e9a1621705714f8ff Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 19:40:22 +0200 Subject: [PATCH 075/135] no dedup --- analysis/src/NewCompletions.ml | 10 +++------- analysis/src/Utils.ml | 9 --------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 55043b06f..d22609692 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -873,13 +873,9 @@ let processCompletion ~completionContext ~exact ~full ~rawOpens ~allFiles ~pos else completions let processDotPath ~full ~rawOpens ~allFiles ~pos dotpath completionContext = - let completions = - dotpath |> PartialParser.determineCompletion - |> processCompletion ~completionContext ~exact:false ~full ~rawOpens - ~allFiles ~pos - in - (* TODO(#107): figure out why we're getting duplicates. *) - completions |> Utils.dedup + dotpath |> PartialParser.determineCompletion + |> processCompletion ~completionContext ~exact:false ~full ~rawOpens ~allFiles + ~pos |> List.map (fun ({Completion.name; deprecated; docstring; kind}, _env) -> mkItem ~name ~kind:(Completion.kindToInt kind) diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml index acfcb6d13..81ba932a9 100644 --- a/analysis/src/Utils.ml +++ b/analysis/src/Utils.ml @@ -42,15 +42,6 @@ let rec find fn items = | one :: rest -> ( match fn one with None -> find fn rest | Some x -> Some x) -let dedup items = - let m = Hashtbl.create (List.length items) in - items - |> List.filter (fun a -> - if Hashtbl.mem m a then false - else ( - Hashtbl.add m a (); - true)) - (** Check if pos is within the location, but be fuzzy about when the location ends. If it's within 5 lines, go with it. From d18ca4daf00b161a1750e0156fbf2ba2922ab260 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 19:56:09 +0200 Subject: [PATCH 076/135] refactor --- analysis/src/NewCompletions.ml | 109 +++++++++++++++++---------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index d22609692..66a9e9a6f 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -702,8 +702,50 @@ let rec extractObjectType ~env ~package (t : Types.type_expr) = | _ -> None) | _ -> None -let getCompletions ~full ~rawOpens ~allFiles ~pos ~exact - ~(completion : PartialParser.completion) = +let filterCompletionKind ~(completionContext : PartialParser.completionContext) + ({Completion.kind}, _env) = + match (completionContext, kind) with + | Module, (Module _ | FileModule _) -> true + | Module, _ -> false + | (Field | Type | Value), (Module _ | FileModule _) -> + (* M.field M.type M.value *) + true + | Value, (Value _ | Constructor _) -> + (* x Red *) + true + | Value, _ -> false + | Field, Field _ -> true + | Field, _ -> false + | Type, Type _ -> true + | Type, _ -> false + +let prioritize ~exact ~pos completions = + if exact then + (* Heuristic to approximate scope when an exact name is required and there could + be more than one instance of that name. + Take the last position before pos if any, or just return the first element. *) + let rec loop decls = + match decls with + | (d1, e1) :: (d2, e2) :: rest -> + let pos2 = d2.Completion.extentLoc.loc_start |> Pos.ofLexing in + if pos2 >= pos then loop ((d1, e1) :: rest) + else + let pos1 = d1.extentLoc.loc_start |> Pos.ofLexing in + if pos1 <= pos2 then loop ((d2, e2) :: rest) + else loop ((d1, e1) :: rest) + | [] | [_] -> decls + in + loop completions + else completions + +let getCompletions ~full ~rawOpens ~allFiles ~pos ~exact ~completionContext + (completion : PartialParser.completion) = + let postProcess completions = + completions + |> List.filter (filterCompletionKind ~completionContext) + |> prioritize ~exact ~pos + in + Log.log ("Opens folkz > " ^ string_of_int (List.length rawOpens) @@ -756,13 +798,13 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos ~exact env ) else None) in - locallyDefinedValues @ valuesFromOpens @ localModuleNames + locallyDefinedValues @ valuesFromOpens @ localModuleNames |> postProcess | Path path -> ( Log.log ("Path " ^ pathToString path); match getEnvWithOpens ~pos ~env ~package ~opens path with | Some (env, prefix) -> Log.log "Got the env"; - allCompletions ~env ~prefix ~exact + allCompletions ~env ~prefix ~exact |> postProcess | None -> []) | RecordAccess (valuePath, middleFields, lastField) -> ( Log.log ("lastField :" ^ lastField); @@ -807,7 +849,8 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos ~exact typDecl.item.decl |> Shared.declToString typDecl.name.txt )), env ) - else None)))) + else None) + |> postProcess))) | None -> []) let mkItem ~name ~kind ~detail ~deprecated ~docstring = @@ -830,51 +873,9 @@ let mkItem ~name ~kind ~detail ~deprecated ~docstring = else Some {kind = "markdown"; value = docContent}); } -let filterCompletionKind ~(completionContext : PartialParser.completionContext) - ~(kind : Completion.kind) = - match (completionContext, kind) with - | Module, (Module _ | FileModule _) -> true - | Module, _ -> false - | (Field | Type | Value), (Module _ | FileModule _) -> - (* M.field M.type M.value *) - true - | Value, (Value _ | Constructor _) -> - (* x Red *) - true - | Value, _ -> false - | Field, Field _ -> true - | Field, _ -> false - | Type, Type _ -> true - | Type, _ -> false - -let processCompletion ~completionContext ~exact ~full ~rawOpens ~allFiles ~pos - completion = - let completions = - getCompletions ~full ~rawOpens ~allFiles ~pos ~completion ~exact - |> List.filter (fun ({Completion.kind}, _env) -> - filterCompletionKind ~completionContext ~kind) - in - if exact then - (* Heuristic to approximate scope when an exact name is required and there could - be more than one instance of that name. - Take the last position before pos if any, or just return the first element. *) - let rec prioritize decls = - match decls with - | (d1, e1) :: (d2, e2) :: rest -> - let pos2 = d2.Completion.extentLoc.loc_start |> Pos.ofLexing in - if pos2 >= pos then prioritize ((d1, e1) :: rest) - else - let pos1 = d1.extentLoc.loc_start |> Pos.ofLexing in - if pos1 <= pos2 then prioritize ((d2, e2) :: rest) - else prioritize ((d1, e1) :: rest) - | [] | [_] -> decls - in - prioritize completions - else completions - -let processDotPath ~full ~rawOpens ~allFiles ~pos dotpath completionContext = +let processDotPath ~full ~rawOpens ~allFiles ~pos (dotpath, completionContext) = dotpath |> PartialParser.determineCompletion - |> processCompletion ~completionContext ~exact:false ~full ~rawOpens ~allFiles + |> getCompletions ~completionContext ~exact:false ~full ~rawOpens ~allFiles ~pos |> List.map (fun ({Completion.name; deprecated; docstring; kind}, _env) -> mkItem ~name @@ -886,8 +887,8 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos let findValue path = match PartialParser.Path path - |> processCompletion ~completionContext:PartialParser.Value ~exact:true - ~full ~rawOpens ~allFiles ~pos + |> getCompletions ~completionContext:PartialParser.Value ~exact:true ~full + ~rawOpens ~allFiles ~pos with | ({Completion.kind = Value typ}, env) :: _ -> Some (typ, env) | _ -> None @@ -895,7 +896,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos match completable with | Cpath _ -> assert false | Cdotpath (dotpath, completionContext) -> - processDotPath ~full ~rawOpens ~allFiles ~pos dotpath completionContext + processDotPath ~full ~rawOpens ~allFiles ~pos (dotpath, completionContext) | Cobj (lhs, path, prefix) -> let rec getFields (texp : Types.type_expr) = match texp.desc with @@ -1115,8 +1116,8 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos in let declareds = PartialParser.Path (modulePath @ [partialName]) - |> processCompletion ~completionContext:PartialParser.Value - ~exact:false ~full ~rawOpens ~allFiles ~pos + |> getCompletions ~completionContext:PartialParser.Value ~exact:false + ~full ~rawOpens ~allFiles ~pos in declareds |> List.map From 6c960d74a2f938e826b698715b0e69b25d8fe816 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 20:18:45 +0200 Subject: [PATCH 077/135] refactor --- analysis/src/Commands.ml | 23 ++++++++++++++- analysis/src/NewCompletions.ml | 51 ++++++++++++---------------------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index e6163ed83..d527ccca0 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -631,6 +631,25 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = !result) else None +let getOpens ~rawOpens ~package ~env = + Log.log + ("Opens folkz > " + ^ string_of_int (List.length rawOpens) + ^ " " + ^ String.concat " ... " (rawOpens |> List.map pathToString)); + let packageOpens = "Pervasives" :: package.opens in + Log.log ("Package opens " ^ String.concat " " packageOpens); + let resolvedOpens = NewCompletions.resolveRawOpens ~env ~rawOpens ~package in + Log.log + ("Opens nows " + ^ string_of_int (List.length resolvedOpens) + ^ " " + ^ String.concat " " + (resolvedOpens + |> List.map (fun (e : QueryEnv.t) -> Uri2.toString e.file.uri))); + (* Last open takes priority *) + List.rev resolvedOpens + let completion ~debug ~path ~pos ~currentFile = let result = let textOpt = Files.readFile currentFile in @@ -654,7 +673,9 @@ let completion ~debug ~path ~pos ~currentFile = match Cmt.fromPath ~path with | None -> [] | Some full -> - NewCompletions.computeCompletions ~completable ~full ~pos ~rawOpens) + let env = QueryEnv.fromFile full.file in + let opens = getOpens ~rawOpens ~package:full.package ~env in + NewCompletions.computeCompletions ~completable ~full ~pos ~rawOpens ~opens ~env) in completionItems in diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 66a9e9a6f..a3dfc229e 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -738,7 +738,7 @@ let prioritize ~exact ~pos completions = loop completions else completions -let getCompletions ~full ~rawOpens ~allFiles ~pos ~exact ~completionContext +let getCompletions ~full ~opens ~allFiles ~pos ~exact ~completionContext ~env (completion : PartialParser.completion) = let postProcess completions = completions @@ -746,25 +746,7 @@ let getCompletions ~full ~rawOpens ~allFiles ~pos ~exact ~completionContext |> prioritize ~exact ~pos in - Log.log - ("Opens folkz > " - ^ string_of_int (List.length rawOpens) - ^ " " - ^ String.concat " ... " (rawOpens |> List.map pathToString)); - let env = QueryEnv.fromFile full.file in let package = full.package in - let packageOpens = "Pervasives" :: package.opens in - Log.log ("Package opens " ^ String.concat " " packageOpens); - let resolvedOpens = resolveRawOpens ~env ~rawOpens ~package in - Log.log - ("Opens nows " - ^ string_of_int (List.length resolvedOpens) - ^ " " - ^ String.concat " " - (resolvedOpens - |> List.map (fun (e : QueryEnv.t) -> Uri2.toString e.file.uri))); - (* Last open takes priority *) - let opens = List.rev resolvedOpens in match completion with | Path [] -> [] | Path [prefix] -> @@ -873,22 +855,26 @@ let mkItem ~name ~kind ~detail ~deprecated ~docstring = else Some {kind = "markdown"; value = docContent}); } -let processDotPath ~full ~rawOpens ~allFiles ~pos (dotpath, completionContext) = +let completionToItem ({Completion.name; deprecated; docstring; kind}, _env) = + mkItem ~name + ~kind:(Completion.kindToInt kind) + ~deprecated ~detail:(detail name kind) ~docstring + +let processDotPath ~full ~opens ~allFiles ~pos ~env (dotpath, completionContext) + = dotpath |> PartialParser.determineCompletion - |> getCompletions ~completionContext ~exact:false ~full ~rawOpens ~allFiles - ~pos - |> List.map (fun ({Completion.name; deprecated; docstring; kind}, _env) -> - mkItem ~name - ~kind:(Completion.kindToInt kind) - ~deprecated ~detail:(detail name kind) ~docstring) + |> getCompletions ~completionContext ~exact:false ~full ~opens ~allFiles ~pos + ~env + |> List.map completionToItem -let processCompletable ~full ~package ~rawOpens ~allFiles ~pos +let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos (completable : PartialParser.completable) = + let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in let findValue path = match PartialParser.Path path |> getCompletions ~completionContext:PartialParser.Value ~exact:true ~full - ~rawOpens ~allFiles ~pos + ~opens ~allFiles ~pos ~env with | ({Completion.kind = Value typ}, env) :: _ -> Some (typ, env) | _ -> None @@ -896,7 +882,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos match completable with | Cpath _ -> assert false | Cdotpath (dotpath, completionContext) -> - processDotPath ~full ~rawOpens ~allFiles ~pos (dotpath, completionContext) + processDotPath ~full ~opens ~allFiles ~pos ~env (dotpath, completionContext) | Cobj (lhs, path, prefix) -> let rec getFields (texp : Types.type_expr) = match texp.desc with @@ -1117,7 +1103,7 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos let declareds = PartialParser.Path (modulePath @ [partialName]) |> getCompletions ~completionContext:PartialParser.Value ~exact:false - ~full ~rawOpens ~allFiles ~pos + ~full ~opens ~allFiles ~pos ~env in declareds |> List.map @@ -1197,9 +1183,8 @@ let processCompletable ~full ~package ~rawOpens ~allFiles ~pos |> List.map mkLabel let computeCompletions ~(completable : PartialParser.completable) ~full ~pos - ~rawOpens = + ~rawOpens ~opens ~env = let package = full.package in - let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in let rec processContextPath (cp : PartialParser.contextPath) : PartialParser.completable = @@ -1224,4 +1209,4 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos | _ -> completable in - completable |> processCompletable ~full ~package ~rawOpens ~allFiles ~pos + completable |> processCompletable ~full ~package ~rawOpens ~opens ~env ~pos From 17575072281e1545684ac65ab4cff4d9e2c11c99 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 20:26:47 +0200 Subject: [PATCH 078/135] refactor --- analysis/src/NewCompletions.ml | 39 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index a3dfc229e..965904374 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -738,15 +738,13 @@ let prioritize ~exact ~pos completions = loop completions else completions -let getCompletions ~full ~opens ~allFiles ~pos ~exact ~completionContext ~env - (completion : PartialParser.completion) = - let postProcess completions = - completions - |> List.filter (filterCompletionKind ~completionContext) - |> prioritize ~exact ~pos - in +let postProcess ~pos ~exact ~completionContext completions = + completions + |> List.filter (filterCompletionKind ~completionContext) + |> prioritize ~exact ~pos - let package = full.package in +let getCompletions ~package ~opens ~allFiles ~pos ~exact ~completionContext ~env + (completion : PartialParser.completion) = match completion with | Path [] -> [] | Path [prefix] -> @@ -780,13 +778,15 @@ let getCompletions ~full ~opens ~allFiles ~pos ~exact ~completionContext ~env env ) else None) in - locallyDefinedValues @ valuesFromOpens @ localModuleNames |> postProcess + locallyDefinedValues @ valuesFromOpens @ localModuleNames + |> postProcess ~pos ~exact ~completionContext | Path path -> ( Log.log ("Path " ^ pathToString path); match getEnvWithOpens ~pos ~env ~package ~opens path with | Some (env, prefix) -> Log.log "Got the env"; - allCompletions ~env ~prefix ~exact |> postProcess + allCompletions ~env ~prefix ~exact + |> postProcess ~pos ~exact ~completionContext | None -> []) | RecordAccess (valuePath, middleFields, lastField) -> ( Log.log ("lastField :" ^ lastField); @@ -832,7 +832,7 @@ let getCompletions ~full ~opens ~allFiles ~pos ~exact ~completionContext ~env |> Shared.declToString typDecl.name.txt )), env ) else None) - |> postProcess))) + |> postProcess ~pos ~exact ~completionContext))) | None -> []) let mkItem ~name ~kind ~detail ~deprecated ~docstring = @@ -860,11 +860,11 @@ let completionToItem ({Completion.name; deprecated; docstring; kind}, _env) = ~kind:(Completion.kindToInt kind) ~deprecated ~detail:(detail name kind) ~docstring -let processDotPath ~full ~opens ~allFiles ~pos ~env (dotpath, completionContext) - = +let processDotPath ~package ~opens ~allFiles ~pos ~env + (dotpath, completionContext) = dotpath |> PartialParser.determineCompletion - |> getCompletions ~completionContext ~exact:false ~full ~opens ~allFiles ~pos - ~env + |> getCompletions ~completionContext ~exact:false ~package ~opens ~allFiles + ~pos ~env |> List.map completionToItem let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos @@ -873,8 +873,8 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos let findValue path = match PartialParser.Path path - |> getCompletions ~completionContext:PartialParser.Value ~exact:true ~full - ~opens ~allFiles ~pos ~env + |> getCompletions ~completionContext:PartialParser.Value ~exact:true + ~package ~opens ~allFiles ~pos ~env with | ({Completion.kind = Value typ}, env) :: _ -> Some (typ, env) | _ -> None @@ -882,7 +882,8 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos match completable with | Cpath _ -> assert false | Cdotpath (dotpath, completionContext) -> - processDotPath ~full ~opens ~allFiles ~pos ~env (dotpath, completionContext) + processDotPath ~package ~opens ~allFiles ~pos ~env + (dotpath, completionContext) | Cobj (lhs, path, prefix) -> let rec getFields (texp : Types.type_expr) = match texp.desc with @@ -1103,7 +1104,7 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos let declareds = PartialParser.Path (modulePath @ [partialName]) |> getCompletions ~completionContext:PartialParser.Value ~exact:false - ~full ~opens ~allFiles ~pos ~env + ~package ~opens ~allFiles ~pos ~env in declareds |> List.map From e5aab9f2b5aeb732e63f027dbfda1df053af3aef Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 20:37:11 +0200 Subject: [PATCH 079/135] refactor --- analysis/src/NewCompletions.ml | 127 ++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 59 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 965904374..32192d92d 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -743,11 +743,11 @@ let postProcess ~pos ~exact ~completionContext completions = |> List.filter (filterCompletionKind ~completionContext) |> prioritize ~exact ~pos -let getCompletions ~package ~opens ~allFiles ~pos ~exact ~completionContext ~env - (completion : PartialParser.completion) = - match completion with - | Path [] -> [] - | Path [prefix] -> +let getCompletionsPath ~package ~opens ~allFiles ~pos ~exact ~completionContext + ~env path = + match path with + | [] -> [] + | [prefix] -> let locallyDefinedValues = localCompletions ~pos ~env ~prefix ~exact in let alreadyUsedIdentifiers = Hashtbl.create 10 in let valuesFromOpens = @@ -780,7 +780,7 @@ let getCompletions ~package ~opens ~allFiles ~pos ~exact ~completionContext ~env in locallyDefinedValues @ valuesFromOpens @ localModuleNames |> postProcess ~pos ~exact ~completionContext - | Path path -> ( + | _ -> ( Log.log ("Path " ^ pathToString path); match getEnvWithOpens ~pos ~env ~package ~opens path with | Some (env, prefix) -> @@ -788,52 +788,52 @@ let getCompletions ~package ~opens ~allFiles ~pos ~exact ~completionContext ~env allCompletions ~env ~prefix ~exact |> postProcess ~pos ~exact ~completionContext | None -> []) - | RecordAccess (valuePath, middleFields, lastField) -> ( - Log.log ("lastField :" ^ lastField); - Log.log ("-------------- Looking for " ^ (valuePath |> pathToString)); - match getEnvWithOpens ~pos ~env ~package ~opens valuePath with - | Some (env, name) -> ( - match - ProcessCmt.findInScope pos name Stamps.iterValues env.file.stamps - with + +let getCompletionsRecordAccess ~package ~opens ~pos ~exact ~completionContext + ~env (valuePath, middleFields, lastField) = + Log.log ("lastField :" ^ lastField); + Log.log ("-------------- Looking for " ^ (valuePath |> pathToString)); + match getEnvWithOpens ~pos ~env ~package ~opens valuePath with + | Some (env, name) -> ( + match ProcessCmt.findInScope pos name Stamps.iterValues env.file.stamps with + | None -> [] + | Some declared -> ( + Log.log ("Found it! " ^ declared.name.txt); + match declared.item |> extractRecordType ~env ~package with | None -> [] - | Some declared -> ( - Log.log ("Found it! " ^ declared.name.txt); - match declared.item |> extractRecordType ~env ~package with - | None -> [] - | Some (env, fields, typDecl) -> ( - match - middleFields - |> List.fold_left - (fun current name -> - match current with + | Some (env, fields, typDecl) -> ( + match + middleFields + |> List.fold_left + (fun current name -> + match current with + | None -> None + | Some (env, fields, _) -> ( + match + fields |> List.find_opt (fun f -> f.fname.txt = name) + with | None -> None - | Some (env, fields, _) -> ( - match - fields |> List.find_opt (fun f -> f.fname.txt = name) - with - | None -> None - | Some attr -> - Log.log ("Found attr " ^ name); - attr.typ |> extractRecordType ~env ~package)) - (Some (env, fields, typDecl)) - with - | None -> [] - | Some (env, fields, typDecl) -> - fields - |> Utils.filterMap (fun field -> - if checkName field.fname.txt ~prefix:lastField ~exact then - Some - ( Completion.create ~name:field.fname.txt - ~kind: - (Completion.Field - ( field, - typDecl.item.decl - |> Shared.declToString typDecl.name.txt )), - env ) - else None) - |> postProcess ~pos ~exact ~completionContext))) - | None -> []) + | Some attr -> + Log.log ("Found attr " ^ name); + attr.typ |> extractRecordType ~env ~package)) + (Some (env, fields, typDecl)) + with + | None -> [] + | Some (env, fields, typDecl) -> + fields + |> Utils.filterMap (fun field -> + if checkName field.fname.txt ~prefix:lastField ~exact then + Some + ( Completion.create ~name:field.fname.txt + ~kind: + (Completion.Field + ( field, + typDecl.item.decl + |> Shared.declToString typDecl.name.txt )), + env ) + else None) + |> postProcess ~pos ~exact ~completionContext))) + | None -> [] let mkItem ~name ~kind ~detail ~deprecated ~docstring = let docContent = @@ -862,18 +862,27 @@ let completionToItem ({Completion.name; deprecated; docstring; kind}, _env) = let processDotPath ~package ~opens ~allFiles ~pos ~env (dotpath, completionContext) = - dotpath |> PartialParser.determineCompletion - |> getCompletions ~completionContext ~exact:false ~package ~opens ~allFiles - ~pos ~env - |> List.map completionToItem + let completion = dotpath |> PartialParser.determineCompletion in + let completions = + match completion with + | Path path -> + path + |> getCompletionsPath ~package ~opens ~allFiles ~pos ~exact:false + ~completionContext ~env + | RecordAccess (valuePath, middleFields, lastField) -> + (valuePath, middleFields, lastField) + |> getCompletionsRecordAccess ~package ~opens ~pos ~exact:false + ~completionContext ~env + in + completions |> List.map completionToItem let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos (completable : PartialParser.completable) = let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in let findValue path = match - PartialParser.Path path - |> getCompletions ~completionContext:PartialParser.Value ~exact:true + path + |> getCompletionsPath ~completionContext:PartialParser.Value ~exact:true ~package ~opens ~allFiles ~pos ~env with | ({Completion.kind = Value typ}, env) :: _ -> Some (typ, env) @@ -1102,9 +1111,9 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos else modulePathMinusOpens ^ "." ^ name in let declareds = - PartialParser.Path (modulePath @ [partialName]) - |> getCompletions ~completionContext:PartialParser.Value ~exact:false - ~package ~opens ~allFiles ~pos ~env + modulePath @ [partialName] + |> getCompletionsPath ~completionContext:PartialParser.Value + ~exact:false ~package ~opens ~allFiles ~pos ~env in declareds |> List.map From 0eaca9bafa980cea6646df81072780f7d5a1123f Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 17 Apr 2022 20:48:11 +0200 Subject: [PATCH 080/135] refactor --- analysis/src/NewCompletions.ml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 32192d92d..29d5636d3 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -863,18 +863,15 @@ let completionToItem ({Completion.name; deprecated; docstring; kind}, _env) = let processDotPath ~package ~opens ~allFiles ~pos ~env (dotpath, completionContext) = let completion = dotpath |> PartialParser.determineCompletion in - let completions = - match completion with - | Path path -> - path - |> getCompletionsPath ~package ~opens ~allFiles ~pos ~exact:false - ~completionContext ~env - | RecordAccess (valuePath, middleFields, lastField) -> - (valuePath, middleFields, lastField) - |> getCompletionsRecordAccess ~package ~opens ~pos ~exact:false - ~completionContext ~env - in - completions |> List.map completionToItem + match completion with + | Path path -> + path + |> getCompletionsPath ~package ~opens ~allFiles ~pos ~exact:false + ~completionContext ~env + | RecordAccess (valuePath, middleFields, lastField) -> + (valuePath, middleFields, lastField) + |> getCompletionsRecordAccess ~package ~opens ~pos ~exact:false + ~completionContext ~env let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos (completable : PartialParser.completable) = @@ -893,6 +890,7 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos | Cdotpath (dotpath, completionContext) -> processDotPath ~package ~opens ~allFiles ~pos ~env (dotpath, completionContext) + |> List.map completionToItem | Cobj (lhs, path, prefix) -> let rec getFields (texp : Types.type_expr) = match texp.desc with From 204eda70423ff0f74fddb53b07662affe54f6833 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 06:19:19 +0200 Subject: [PATCH 081/135] refactor --- analysis/src/NewCompletions.ml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 29d5636d3..cad2245f0 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -789,8 +789,8 @@ let getCompletionsPath ~package ~opens ~allFiles ~pos ~exact ~completionContext |> postProcess ~pos ~exact ~completionContext | None -> []) -let getCompletionsRecordAccess ~package ~opens ~pos ~exact ~completionContext - ~env (valuePath, middleFields, lastField) = +let getCompletionsRecordAccess ~package ~opens ~pos ~exact ~env + (valuePath, middleFields, lastField) = Log.log ("lastField :" ^ lastField); Log.log ("-------------- Looking for " ^ (valuePath |> pathToString)); match getEnvWithOpens ~pos ~env ~package ~opens valuePath with @@ -813,9 +813,9 @@ let getCompletionsRecordAccess ~package ~opens ~pos ~exact ~completionContext fields |> List.find_opt (fun f -> f.fname.txt = name) with | None -> None - | Some attr -> - Log.log ("Found attr " ^ name); - attr.typ |> extractRecordType ~env ~package)) + | Some field -> + Log.log ("Found field " ^ name); + field.typ |> extractRecordType ~env ~package)) (Some (env, fields, typDecl)) with | None -> [] @@ -831,8 +831,7 @@ let getCompletionsRecordAccess ~package ~opens ~pos ~exact ~completionContext typDecl.item.decl |> Shared.declToString typDecl.name.txt )), env ) - else None) - |> postProcess ~pos ~exact ~completionContext))) + else None)))) | None -> [] let mkItem ~name ~kind ~detail ~deprecated ~docstring = @@ -870,8 +869,7 @@ let processDotPath ~package ~opens ~allFiles ~pos ~env ~completionContext ~env | RecordAccess (valuePath, middleFields, lastField) -> (valuePath, middleFields, lastField) - |> getCompletionsRecordAccess ~package ~opens ~pos ~exact:false - ~completionContext ~env + |> getCompletionsRecordAccess ~package ~opens ~pos ~exact:false ~env let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos (completable : PartialParser.completable) = @@ -886,6 +884,11 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos | _ -> None in match completable with + | Cpath (CPId (path, completionContext)) -> + path + |> getCompletionsPath ~package ~opens ~allFiles ~pos ~exact:false + ~completionContext ~env + |> List.map completionToItem | Cpath _ -> assert false | Cdotpath (dotpath, completionContext) -> processDotPath ~package ~opens ~allFiles ~pos ~env From a2fdc1cc47868b00e9f40c532c519008a76330ef Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 06:34:16 +0200 Subject: [PATCH 082/135] Add env to completions. --- analysis/src/NewCompletions.ml | 62 +++++++++++++-------------- analysis/src/SharedTypes.ml | 76 +++++++++++++++++++--------------- 2 files changed, 71 insertions(+), 67 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index cad2245f0..b53646eca 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -499,7 +499,8 @@ let resolveOpens ~env ~previous opens ~package = let checkName name ~prefix ~exact = if exact then name = prefix else Utils.startsWith name prefix -let completionForDeclareds ~pos ~iter ~stamps ~prefix ~exact transformContents = +let completionForDeclareds ~pos ~iter ~stamps ~prefix ~exact ~env + transformContents = (* Log.log("completion for declares " ++ prefix); *) let res = ref [] in iter @@ -510,7 +511,7 @@ let completionForDeclareds ~pos ~iter ~stamps ~prefix ~exact transformContents = then res := { - (Completion.create ~name:declared.name.txt + (Completion.create ~name:declared.name.txt ~env ~kind:(transformContents declared.item)) with extentLoc = declared.extentLoc; @@ -522,20 +523,20 @@ let completionForDeclareds ~pos ~iter ~stamps ~prefix ~exact transformContents = !res let completionForDeclaredModules ~pos ~env ~prefix ~exact = - completionForDeclareds ~pos ~iter:Stamps.iterModules + completionForDeclareds ~env ~pos ~iter:Stamps.iterModules ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Module m) let completionForDeclaredValues ~pos ~env ~prefix ~exact = - completionForDeclareds ~pos ~iter:Stamps.iterValues + completionForDeclareds ~env ~pos ~iter:Stamps.iterValues ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Value m) let completionForDeclaredTypes ~pos ~env ~prefix ~exact = - completionForDeclareds ~pos ~iter:Stamps.iterTypes + completionForDeclareds ~env ~pos ~iter:Stamps.iterTypes ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Type m) -let completionForExporteds iterExported getDeclared ~prefix ~exact +let completionForExporteds iterExported getDeclared ~prefix ~exact ~env transformContents = let res = ref [] in iterExported (fun name stamp -> @@ -545,7 +546,7 @@ let completionForExporteds iterExported getDeclared ~prefix ~exact | Some (declared : _ Declared.t) -> res := { - (Completion.create ~name:declared.name.txt + (Completion.create ~name:declared.name.txt ~env ~kind:(transformContents declared.item)) with extentLoc = declared.extentLoc; @@ -558,17 +559,17 @@ let completionForExporteds iterExported getDeclared ~prefix ~exact let completionForExportedModules ~env ~prefix ~exact = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Module) - (Stamps.findModule env.file.stamps) ~prefix ~exact (fun m -> + (Stamps.findModule env.file.stamps) ~prefix ~exact ~env (fun m -> Completion.Module m) let completionForExportedValues ~env ~prefix ~exact = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Value) - (Stamps.findValue env.file.stamps) ~prefix ~exact (fun v -> + (Stamps.findValue env.file.stamps) ~prefix ~exact ~env (fun v -> Completion.Value v) let completionForExportedTypes ~env ~prefix ~exact = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Type) - (Stamps.findType env.file.stamps) ~prefix ~exact (fun t -> + (Stamps.findType env.file.stamps) ~prefix ~exact ~env (fun t -> Completion.Type t) let completionForConstructors ~(env : QueryEnv.t) ~prefix ~exact = @@ -581,7 +582,7 @@ let completionForConstructors ~(env : QueryEnv.t) ~prefix ~exact = |> List.filter (fun c -> checkName c.Constructor.cname.txt ~prefix ~exact) |> List.map (fun c -> - Completion.create ~name:c.Constructor.cname.txt + Completion.create ~name:c.Constructor.cname.txt ~env ~kind: (Completion.Constructor (c, t.item.decl |> Shared.declToString t.name.txt)))) @@ -598,7 +599,7 @@ let completionForFields ~(env : QueryEnv.t) ~prefix ~exact = (fields |> List.filter (fun f -> checkName f.fname.txt ~prefix ~exact) |> List.map (fun f -> - Completion.create ~name:f.fname.txt + Completion.create ~name:f.fname.txt ~env ~kind: (Completion.Field (f, t.item.decl |> Shared.declToString t.name.txt)))) @@ -654,7 +655,6 @@ let localCompletions ~pos ~(env : QueryEnv.t) ~prefix ~exact = @ completionForDeclaredValues ~pos ~env ~prefix ~exact @ completionForDeclaredTypes ~pos ~env ~prefix ~exact @ completionForFields ~env ~prefix ~exact - |> List.map (fun r -> (r, env)) let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = Log.log (" - Completing in " ^ Uri2.toString env.file.uri); @@ -663,7 +663,6 @@ let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = @ completionForExportedValues ~env ~prefix ~exact @ completionForExportedTypes ~env ~prefix ~exact @ completionForFields ~env ~prefix ~exact - |> List.map (fun r -> (r, env)) (* TODO filter out things that are defined after the current position *) let resolveRawOpens ~env ~rawOpens ~package = @@ -703,7 +702,7 @@ let rec extractObjectType ~env ~package (t : Types.type_expr) = | _ -> None let filterCompletionKind ~(completionContext : PartialParser.completionContext) - ({Completion.kind}, _env) = + {Completion.kind} = match (completionContext, kind) with | Module, (Module _ | FileModule _) -> true | Module, _ -> false @@ -726,13 +725,12 @@ let prioritize ~exact ~pos completions = Take the last position before pos if any, or just return the first element. *) let rec loop decls = match decls with - | (d1, e1) :: (d2, e2) :: rest -> + | d1 :: d2 :: rest -> let pos2 = d2.Completion.extentLoc.loc_start |> Pos.ofLexing in - if pos2 >= pos then loop ((d1, e1) :: rest) + if pos2 >= pos then loop (d1 :: rest) else let pos1 = d1.extentLoc.loc_start |> Pos.ofLexing in - if pos1 <= pos2 then loop ((d2, e2) :: rest) - else loop ((d1, e1) :: rest) + if pos1 <= pos2 then loop (d2 :: rest) else loop (d1 :: rest) | [] | [_] -> decls in loop completions @@ -756,7 +754,7 @@ let getCompletionsPath ~package ~opens ~allFiles ~pos ~exact ~completionContext (fun results env -> let completionsFromThisOpen = allCompletions ~env ~prefix ~exact in List.filter - (fun ((declared : Completion.t), _env) -> + (fun (declared : Completion.t) -> if Hashtbl.mem alreadyUsedIdentifiers declared.name then (* shadowing from opens *) false @@ -774,8 +772,8 @@ let getCompletionsPath ~package ~opens ~allFiles ~pos ~exact ~completionContext if checkName name ~prefix ~exact && not (String.contains name '-') then Some - ( Completion.create ~name ~kind:(Completion.FileModule name), - env ) + (Completion.create ~name ~env + ~kind:(Completion.FileModule name)) else None) in locallyDefinedValues @ valuesFromOpens @ localModuleNames @@ -824,13 +822,12 @@ let getCompletionsRecordAccess ~package ~opens ~pos ~exact ~env |> Utils.filterMap (fun field -> if checkName field.fname.txt ~prefix:lastField ~exact then Some - ( Completion.create ~name:field.fname.txt - ~kind: - (Completion.Field - ( field, - typDecl.item.decl - |> Shared.declToString typDecl.name.txt )), - env ) + (Completion.create ~name:field.fname.txt ~env + ~kind: + (Completion.Field + ( field, + typDecl.item.decl + |> Shared.declToString typDecl.name.txt ))) else None)))) | None -> [] @@ -854,7 +851,7 @@ let mkItem ~name ~kind ~detail ~deprecated ~docstring = else Some {kind = "markdown"; value = docContent}); } -let completionToItem ({Completion.name; deprecated; docstring; kind}, _env) = +let completionToItem {Completion.name; deprecated; docstring; kind} = mkItem ~name ~kind:(Completion.kindToInt kind) ~deprecated ~detail:(detail name kind) ~docstring @@ -880,7 +877,7 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos |> getCompletionsPath ~completionContext:PartialParser.Value ~exact:true ~package ~opens ~allFiles ~pos ~env with - | ({Completion.kind = Value typ}, env) :: _ -> Some (typ, env) + | {Completion.kind = Value typ; env} :: _ -> Some (typ, env) | _ -> None in match completable with @@ -1117,8 +1114,7 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos ~exact:false ~package ~opens ~allFiles ~pos ~env in declareds - |> List.map - (fun ({Completion.name; deprecated; docstring; kind}, _env) -> + |> List.map (fun {Completion.name; deprecated; docstring; kind} -> mkItem ~name:(completionName name) ~kind:(Completion.kindToInt kind) ~detail:(detail name kind) ~deprecated ~docstring) diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 6016daee0..62031f9b4 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -92,36 +92,6 @@ module Module = struct and t = Ident of Path.t | Structure of structure | Constraint of t * t end -module Completion = struct - type kind = - | Module of Module.t - | Value of Types.type_expr - | Type of Type.t - | Constructor of Constructor.t * string - | Field of field * string - | FileModule of string - - type t = { - name : string; - extentLoc : Location.t; - deprecated : string option; - docstring : string list; - kind : kind; - } - - let create ~name ~kind = - {name; extentLoc = Location.none; deprecated = None; docstring = []; kind} - - let kindToInt kind = - match kind with - | Module _ -> 9 - | FileModule _ -> 9 - | Constructor (_, _) -> 4 - | Field (_, _) -> 5 - | Type _ -> 22 - | Value _ -> 12 -end - module Declared = struct type 'item t = { name : string Location.loc; @@ -205,10 +175,6 @@ end = struct stamps end -module Env = struct - type t = {stamps : Stamps.t; modulePath : modulePath; scope : Location.t} -end - module File = struct type t = { uri : Uri2.t; @@ -232,6 +198,48 @@ module QueryEnv = struct let fromFile file = {file; exported = file.structure.exported} end +module Completion = struct + type kind = + | Module of Module.t + | Value of Types.type_expr + | Type of Type.t + | Constructor of Constructor.t * string + | Field of field * string + | FileModule of string + + type t = { + name : string; + extentLoc : Location.t; + env : QueryEnv.t; + deprecated : string option; + docstring : string list; + kind : kind; + } + + let create ~name ~kind ~env = + { + name; + extentLoc = Location.none; + env; + deprecated = None; + docstring = []; + kind; + } + + let kindToInt kind = + match kind with + | Module _ -> 9 + | FileModule _ -> 9 + | Constructor (_, _) -> 4 + | Field (_, _) -> 5 + | Type _ -> 22 + | Value _ -> 12 +end + +module Env = struct + type t = {stamps : Stamps.t; modulePath : modulePath; scope : Location.t} +end + type filePath = string type paths = From 5377b917b8a8e55c364248ef0732685d41476d9c Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 06:38:38 +0200 Subject: [PATCH 083/135] refactor --- analysis/src/NewCompletions.ml | 40 ++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index b53646eca..bf442c222 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -741,8 +741,8 @@ let postProcess ~pos ~exact ~completionContext completions = |> List.filter (filterCompletionKind ~completionContext) |> prioritize ~exact ~pos -let getCompletionsPath ~package ~opens ~allFiles ~pos ~exact ~completionContext - ~env path = +let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact + ~completionContext ~env path = match path with | [] -> [] | [prefix] -> @@ -787,7 +787,7 @@ let getCompletionsPath ~package ~opens ~allFiles ~pos ~exact ~completionContext |> postProcess ~pos ~exact ~completionContext | None -> []) -let getCompletionsRecordAccess ~package ~opens ~pos ~exact ~env +let getCompletionsForRecordAccess ~package ~opens ~pos ~exact ~env (valuePath, middleFields, lastField) = Log.log ("lastField :" ^ lastField); Log.log ("-------------- Looking for " ^ (valuePath |> pathToString)); @@ -862,28 +862,30 @@ let processDotPath ~package ~opens ~allFiles ~pos ~env match completion with | Path path -> path - |> getCompletionsPath ~package ~opens ~allFiles ~pos ~exact:false + |> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact:false ~completionContext ~env | RecordAccess (valuePath, middleFields, lastField) -> (valuePath, middleFields, lastField) - |> getCompletionsRecordAccess ~package ~opens ~pos ~exact:false ~env + |> getCompletionsForRecordAccess ~package ~opens ~pos ~exact:false ~env + +let completionsGetTypeEnv = function + | {Completion.kind = Value typ; env} :: _ -> Some (typ, env) + | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (typ, env) + | _ -> None let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos (completable : PartialParser.completable) = let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in - let findValue path = - match - path - |> getCompletionsPath ~completionContext:PartialParser.Value ~exact:true - ~package ~opens ~allFiles ~pos ~env - with - | {Completion.kind = Value typ; env} :: _ -> Some (typ, env) - | _ -> None + let findTypeOfValue path = + path + |> getCompletionsForPath ~completionContext:PartialParser.Value ~exact:true + ~package ~opens ~allFiles ~pos ~env + |> completionsGetTypeEnv in match completable with | Cpath (CPId (path, completionContext)) -> path - |> getCompletionsPath ~package ~opens ~allFiles ~pos ~exact:false + |> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact:false ~completionContext ~env |> List.map completionToItem | Cpath _ -> assert false @@ -918,7 +920,7 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos in let env0 = QueryEnv.fromFile full.file in let env, fields = - match lhs |> findValue with + match lhs |> findTypeOfValue with | Some (typ, env) -> getObjectFields ~env typ | None -> (env0, []) in @@ -949,7 +951,7 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos @ keyLabels | Cjsx (componentPath, prefix, identsSeen) -> let labels = - match componentPath @ ["make"] |> findValue with + match componentPath @ ["make"] |> findTypeOfValue with | Some (typ, _env) -> let rec getFields (texp : Types.type_expr) = match texp.desc with @@ -1059,7 +1061,7 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos in match pipeIdPath with | x :: fieldNames -> ( - match [x] |> findValue with + match [x] |> findTypeOfValue with | Some (typ, env) -> ( match getFields ~env ~typ fieldNames with | None -> None @@ -1110,7 +1112,7 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos in let declareds = modulePath @ [partialName] - |> getCompletionsPath ~completionContext:PartialParser.Value + |> getCompletionsForPath ~completionContext:PartialParser.Value ~exact:false ~package ~opens ~allFiles ~pos ~env in declareds @@ -1166,7 +1168,7 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos |> List.map mkDecorator | Clabel (funPath, prefix, identsSeen) -> let labels = - match funPath |> findValue with + match funPath |> findTypeOfValue with | Some (typ, _env) -> let rec getLabels (t : Types.type_expr) = match t.desc with From faf7a1aa8e2325116483dd5a5d8bfc84733826fe Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 07:08:42 +0200 Subject: [PATCH 084/135] Use new backend for CPId. --- analysis/src/NewCompletions.ml | 61 ++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index bf442c222..e1368b7ff 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -873,6 +873,38 @@ let completionsGetTypeEnv = function | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (typ, env) | _ -> None +let rec getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env ~exact + (contextPath : PartialParser.contextPath) = + match contextPath with + | CPId (path, completionContext) -> + path + |> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact + ~completionContext ~env + | CPField (cp, fieldName) -> ( + match + cp + |> getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env + ~exact:true + |> completionsGetTypeEnv + with + | Some (typ, env) -> ( + match typ |> extractRecordType ~env ~package with + | Some (env, fields, typDecl) -> + fields + |> Utils.filterMap (fun field -> + if checkName field.fname.txt ~prefix:fieldName ~exact then + Some + (Completion.create ~name:field.fname.txt ~env + ~kind: + (Completion.Field + ( field, + typDecl.item.decl + |> Shared.declToString typDecl.name.txt ))) + else None) + | None -> []) + | None -> []) + | _ -> assert false + let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos (completable : PartialParser.completable) = let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in @@ -883,12 +915,11 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos |> completionsGetTypeEnv in match completable with - | Cpath (CPId (path, completionContext)) -> - path - |> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact:false - ~completionContext ~env + | Cpath contextPath -> + contextPath + |> getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env + ~exact:false |> List.map completionToItem - | Cpath _ -> assert false | Cdotpath (dotpath, completionContext) -> processDotPath ~package ~opens ~allFiles ~pos ~env (dotpath, completionContext) @@ -1195,26 +1226,28 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos ~rawOpens ~opens ~env = let package = full.package in - let rec processContextPath (cp : PartialParser.contextPath) : + let rec processContextPath (completable : PartialParser.completable) : PartialParser.completable = - match cp with - | CPId (path, k) -> PartialParser.Cdotpath (path, k) - | CPField (cp, name) -> ( - match processContextPath cp with + match completable with + | Cpath (CPId _) -> completable + | Cpath (CPField (cp, name)) -> ( + match processContextPath (Cpath cp) with | Cdotpath (path, _) -> Cdotpath (path @ [name], Field) + | Cpath (CPId (path, _)) -> Cdotpath (path @ [name], Field) | _ -> assert false) - | CPObj (cp, objLabel) -> ( - match processContextPath cp with - | Cdotpath (path, _k) -> Cobj (path, [], objLabel) + | Cpath (CPObj (cp, objLabel)) -> ( + match processContextPath (Cpath cp) with + | Cdotpath (path, _) | Cpath (CPId (path, _)) -> Cobj (path, [], objLabel) | Cobj (path, objPath, label) -> Cobj (path, objPath @ [label], objLabel) | _ -> assert false) + | _ -> completable in let completable = match completable with | Cobj _ -> assert false | Cdotpath _ -> assert false - | Cpath contextPath -> processContextPath contextPath + | Cpath _ -> processContextPath completable | _ -> completable in From 7e45bcc6b8a9075a60b35c41b4c039d20ed907e5 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 07:57:11 +0200 Subject: [PATCH 085/135] Compile field accesses to the new bakend. --- analysis/src/NewCompletions.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index e1368b7ff..c9d0007c7 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -880,6 +880,11 @@ let rec getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env ~exact path |> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~completionContext ~env + | CPField (CPId (path, Module), fieldName) -> + (* M.field *) + path @ [fieldName] + |> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact + ~completionContext:Field ~env | CPField (cp, fieldName) -> ( match cp @@ -1233,7 +1238,7 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos | Cpath (CPField (cp, name)) -> ( match processContextPath (Cpath cp) with | Cdotpath (path, _) -> Cdotpath (path @ [name], Field) - | Cpath (CPId (path, _)) -> Cdotpath (path @ [name], Field) + | Cpath cp -> Cpath (CPField (cp, name)) | _ -> assert false) | Cpath (CPObj (cp, objLabel)) -> ( match processContextPath (Cpath cp) with From 18283ead559dd93d1c392fc53628268c392fa588 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 08:02:25 +0200 Subject: [PATCH 086/135] Remove Cdotpath and associated dead code. --- analysis/src/NewCompletions.ml | 64 +--------------------------------- analysis/src/PartialParser.ml | 32 ----------------- analysis/src/Utils.ml | 6 ---- 3 files changed, 1 insertion(+), 101 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index c9d0007c7..0e87ef245 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -787,50 +787,6 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact |> postProcess ~pos ~exact ~completionContext | None -> []) -let getCompletionsForRecordAccess ~package ~opens ~pos ~exact ~env - (valuePath, middleFields, lastField) = - Log.log ("lastField :" ^ lastField); - Log.log ("-------------- Looking for " ^ (valuePath |> pathToString)); - match getEnvWithOpens ~pos ~env ~package ~opens valuePath with - | Some (env, name) -> ( - match ProcessCmt.findInScope pos name Stamps.iterValues env.file.stamps with - | None -> [] - | Some declared -> ( - Log.log ("Found it! " ^ declared.name.txt); - match declared.item |> extractRecordType ~env ~package with - | None -> [] - | Some (env, fields, typDecl) -> ( - match - middleFields - |> List.fold_left - (fun current name -> - match current with - | None -> None - | Some (env, fields, _) -> ( - match - fields |> List.find_opt (fun f -> f.fname.txt = name) - with - | None -> None - | Some field -> - Log.log ("Found field " ^ name); - field.typ |> extractRecordType ~env ~package)) - (Some (env, fields, typDecl)) - with - | None -> [] - | Some (env, fields, typDecl) -> - fields - |> Utils.filterMap (fun field -> - if checkName field.fname.txt ~prefix:lastField ~exact then - Some - (Completion.create ~name:field.fname.txt ~env - ~kind: - (Completion.Field - ( field, - typDecl.item.decl - |> Shared.declToString typDecl.name.txt ))) - else None)))) - | None -> [] - let mkItem ~name ~kind ~detail ~deprecated ~docstring = let docContent = (match deprecated with None -> "" | Some s -> "Deprecated: " ^ s ^ "\n\n") @@ -856,18 +812,6 @@ let completionToItem {Completion.name; deprecated; docstring; kind} = ~kind:(Completion.kindToInt kind) ~deprecated ~detail:(detail name kind) ~docstring -let processDotPath ~package ~opens ~allFiles ~pos ~env - (dotpath, completionContext) = - let completion = dotpath |> PartialParser.determineCompletion in - match completion with - | Path path -> - path - |> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact:false - ~completionContext ~env - | RecordAccess (valuePath, middleFields, lastField) -> - (valuePath, middleFields, lastField) - |> getCompletionsForRecordAccess ~package ~opens ~pos ~exact:false ~env - let completionsGetTypeEnv = function | {Completion.kind = Value typ; env} :: _ -> Some (typ, env) | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (typ, env) @@ -925,10 +869,6 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos |> getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env ~exact:false |> List.map completionToItem - | Cdotpath (dotpath, completionContext) -> - processDotPath ~package ~opens ~allFiles ~pos ~env - (dotpath, completionContext) - |> List.map completionToItem | Cobj (lhs, path, prefix) -> let rec getFields (texp : Types.type_expr) = match texp.desc with @@ -1237,12 +1177,11 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos | Cpath (CPId _) -> completable | Cpath (CPField (cp, name)) -> ( match processContextPath (Cpath cp) with - | Cdotpath (path, _) -> Cdotpath (path @ [name], Field) | Cpath cp -> Cpath (CPField (cp, name)) | _ -> assert false) | Cpath (CPObj (cp, objLabel)) -> ( match processContextPath (Cpath cp) with - | Cdotpath (path, _) | Cpath (CPId (path, _)) -> Cobj (path, [], objLabel) + | Cpath (CPId (path, _)) -> Cobj (path, [], objLabel) | Cobj (path, objPath, label) -> Cobj (path, objPath @ [label], objLabel) | _ -> assert false) | _ -> completable @@ -1251,7 +1190,6 @@ let computeCompletions ~(completable : PartialParser.completable) ~full ~pos let completable = match completable with | Cobj _ -> assert false - | Cdotpath _ -> assert false | Cpath _ -> processContextPath completable | _ -> completable in diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index d67da22f6..83cd66cc3 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -8,18 +8,11 @@ type contextPath = | CPField of contextPath * string | CPObj of contextPath * string -type completion = - | RecordAccess of SharedTypes.path * SharedTypes.path * string (* e.g. A.B.var .f1.f2 .f3 *) - | Path of SharedTypes.path -(* e.g. A.B.var or A.B *) - type completable = | Cdecorator of string (** e.g. @module *) | Clabel of string list * string * string list (** e.g. (["M", "foo"], "label", ["l1", "l2"]) for M.foo(...~l1...~l2...~label...) *) | Cpath of contextPath - | Cdotpath of string list * completionContext - (** e.g. ["M", "foo"] for M.foo *) | Cjsx of string list * string * string list (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for "Cdecorator(" ^ str s ^ ")" | Clabel (sl1, s, sl2) -> "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" - | Cdotpath (sl, k) -> - "Cdotpath(" ^ (sl |> list) ^ "," ^ completionContextToString k ^ ")" | Cjsx (sl1, s, sl2) -> "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" | Cobj (sl1, sl2, s) -> @@ -60,29 +51,6 @@ let completableToString = | PipeString -> "PipeString") ^ ", " ^ str s ^ ")" -let determineCompletion (dotpath : SharedTypes.path) = - let rec loop dotpath = - match dotpath with - | [] -> assert false - | [one] -> Path [one] - | [one; two] -> - if Utils.isCapitalized one then Path [one; two] - else RecordAccess ([one], [], two) - | one :: rest -> ( - if Utils.isCapitalized one then - match loop rest with - | Path path -> Path (one :: path) - | RecordAccess (valuePath, middleFields, lastField) -> - RecordAccess (one :: valuePath, middleFields, lastField) - else - match loop rest with - | Path path -> Path path - | RecordAccess ([name], middleFields, lastField) -> - RecordAccess ([one], name :: middleFields, lastField) - | x -> x) - in - loop dotpath - let rec skipWhite text i = if i < 0 then 0 else diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml index 81ba932a9..44cab2362 100644 --- a/analysis/src/Utils.ml +++ b/analysis/src/Utils.ml @@ -59,9 +59,3 @@ let filterMap f = let dumpPath path = Str.global_replace (Str.regexp_string "\\") "/" path let isUncurriedInternal path = startsWith (Path.name path) "Js.Fn.arity" - -let isCapitalized name = - if name = "" then false - else - let c = name.[0] in - match c with 'A' .. 'Z' -> true | _ -> false From 49544faa9bc07bc15617b8d93cdfb61d6ef035c5 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 08:26:37 +0200 Subject: [PATCH 087/135] Handle object access with the new back-end. --- analysis/src/NewCompletions.ml | 55 ++++++++++++++++++---------------- analysis/src/SharedTypes.ml | 2 ++ 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 0e87ef245..bcae79495 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -643,6 +643,7 @@ let detail name (kind : Completion.kind) = match kind with | Type {decl} -> decl |> Shared.declToString name | Value typ -> typ |> Shared.typeToString + | ObjLabel typ -> typ |> Shared.typeToString | Module _ -> "module" | FileModule _ -> "file module" | Field ({typ}, s) -> name ^ ": " ^ (typ |> Shared.typeToString) ^ "\n\n" ^ s @@ -814,6 +815,7 @@ let completionToItem {Completion.name; deprecated; docstring; kind} = let completionsGetTypeEnv = function | {Completion.kind = Value typ; env} :: _ -> Some (typ, env) + | {Completion.kind = ObjLabel typ; env} :: _ -> Some (typ, env) | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (typ, env) | _ -> None @@ -852,7 +854,34 @@ let rec getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env ~exact else None) | None -> []) | None -> []) - | _ -> assert false + | CPObj (cp, label) -> ( + match + cp + |> getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env + ~exact:true + |> completionsGetTypeEnv + with + | Some (typ, env) -> ( + match typ |> extractObjectType ~env ~package with + | Some (env, tObj) -> + let rec getFields (texp : Types.type_expr) = + match texp.desc with + | Tfield (name, _, t1, t2) -> + let fields = t2 |> getFields in + (name, t1) :: fields + | Tlink te -> te |> getFields + | Tvar None -> [] + | _ -> [] + in + tObj |> getFields + |> Utils.filterMap (fun (field, typ) -> + if checkName field ~prefix:label ~exact then + Some + (Completion.create ~name:field ~env + ~kind:(Completion.ObjLabel typ)) + else None) + | None -> []) + | None -> []) let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos (completable : PartialParser.completable) = @@ -1170,28 +1199,4 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos let computeCompletions ~(completable : PartialParser.completable) ~full ~pos ~rawOpens ~opens ~env = let package = full.package in - - let rec processContextPath (completable : PartialParser.completable) : - PartialParser.completable = - match completable with - | Cpath (CPId _) -> completable - | Cpath (CPField (cp, name)) -> ( - match processContextPath (Cpath cp) with - | Cpath cp -> Cpath (CPField (cp, name)) - | _ -> assert false) - | Cpath (CPObj (cp, objLabel)) -> ( - match processContextPath (Cpath cp) with - | Cpath (CPId (path, _)) -> Cobj (path, [], objLabel) - | Cobj (path, objPath, label) -> Cobj (path, objPath @ [label], objLabel) - | _ -> assert false) - | _ -> completable - in - - let completable = - match completable with - | Cobj _ -> assert false - | Cpath _ -> processContextPath completable - | _ -> completable - in - completable |> processCompletable ~full ~package ~rawOpens ~opens ~env ~pos diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 62031f9b4..24cfedf3c 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -202,6 +202,7 @@ module Completion = struct type kind = | Module of Module.t | Value of Types.type_expr + | ObjLabel of Types.type_expr | Type of Type.t | Constructor of Constructor.t * string | Field of field * string @@ -231,6 +232,7 @@ module Completion = struct | Module _ -> 9 | FileModule _ -> 9 | Constructor (_, _) -> 4 + | ObjLabel _ -> 4 | Field (_, _) -> 5 | Type _ -> 22 | Value _ -> 12 From f039fd3aa988249739664f7ea5512677ea307f6b Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 08:29:03 +0200 Subject: [PATCH 088/135] Remove now unused Cobj. --- analysis/src/Commands.ml | 6 +++-- analysis/src/NewCompletions.ml | 48 +++------------------------------- analysis/src/PartialParser.ml | 4 --- 3 files changed, 7 insertions(+), 51 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index d527ccca0..5cda5c12b 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -674,8 +674,10 @@ let completion ~debug ~path ~pos ~currentFile = | None -> [] | Some full -> let env = QueryEnv.fromFile full.file in - let opens = getOpens ~rawOpens ~package:full.package ~env in - NewCompletions.computeCompletions ~completable ~full ~pos ~rawOpens ~opens ~env) + let package = full.package in + let opens = getOpens ~rawOpens ~package ~env in + NewCompletions.computeCompletions ~completable ~package ~pos + ~rawOpens ~opens ~env) in completionItems in diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index bcae79495..580bb313a 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -883,7 +883,7 @@ let rec getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env ~exact | None -> []) | None -> []) -let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos +let processCompletable ~package ~rawOpens ~opens ~env ~pos (completable : PartialParser.completable) = let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in let findTypeOfValue path = @@ -898,47 +898,6 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos |> getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env ~exact:false |> List.map completionToItem - | Cobj (lhs, path, prefix) -> - let rec getFields (texp : Types.type_expr) = - match texp.desc with - | Tfield (name, _, t1, t2) -> - let fields = t2 |> getFields in - (name, t1) :: fields - | Tlink te -> te |> getFields - | Tvar None -> [] - | _ -> [] - in - let getObjectFields ~env (t : Types.type_expr) = - match t |> extractObjectType ~env ~package with - | Some (env, tObj) -> (env, getFields tObj) - | None -> (env, []) - in - let rec resolvePath ~env fields path = - match path with - | name :: restPath -> ( - match fields |> List.find_opt (fun (n, _) -> n = name) with - | Some (_, fieldType) -> - let env, innerFields = getObjectFields ~env fieldType in - resolvePath ~env innerFields restPath - | None -> []) - | [] -> fields - in - let env0 = QueryEnv.fromFile full.file in - let env, fields = - match lhs |> findTypeOfValue with - | Some (typ, env) -> getObjectFields ~env typ - | None -> (env0, []) - in - let labels = resolvePath ~env fields path in - let mkLabel_ name typString = - mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] - in - let mkLabel (name, typ) = mkLabel_ name (typ |> Shared.typeToString) in - if labels = [] then [] - else - labels - |> List.filter (fun (name, _t) -> Utils.startsWith name prefix) - |> List.map mkLabel | Cjsx ([id], prefix, identsSeen) when String.uncapitalize_ascii id = id -> let mkLabel_ name typString = mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] @@ -1196,7 +1155,6 @@ let processCompletable ~full ~package ~rawOpens ~opens ~env ~pos Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel -let computeCompletions ~(completable : PartialParser.completable) ~full ~pos +let computeCompletions ~(completable : PartialParser.completable) ~package ~pos ~rawOpens ~opens ~env = - let package = full.package in - completable |> processCompletable ~full ~package ~rawOpens ~opens ~env ~pos + completable |> processCompletable ~package ~rawOpens ~opens ~env ~pos diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 83cd66cc3..ca68c232e 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -15,8 +15,6 @@ type completable = | Cpath of contextPath | Cjsx of string list * string * string list (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for foo" *) let completableToString = @@ -41,8 +39,6 @@ let completableToString = "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" | Cjsx (sl1, s, sl2) -> "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" - | Cobj (sl1, sl2, s) -> - "Cobj(" ^ (sl1 |> list) ^ ", " ^ (sl2 |> list) ^ ", " ^ str s ^ ")" | Cpipe (pipe, s) -> "Cpipe(" ^ (match pipe with From 6957ac179d48139f1ff3f687deee56c372615fe6 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 10:45:35 +0200 Subject: [PATCH 089/135] Add pipe to the new back-end. --- analysis/src/Commands.ml | 17 ++- analysis/src/NewCompletions.ml | 132 ++++++++++++++++-- analysis/src/PartialParser.ml | 6 + .../src/expected/CompletePrioritize1.res.txt | 2 +- .../src/expected/CompletePrioritize2.res.txt | 2 +- .../tests/src/expected/Completion.res.txt | 8 +- .../src/expected/RecordCompletion.res.txt | 4 +- 7 files changed, 143 insertions(+), 28 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 5cda5c12b..02e0c2aaa 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -360,18 +360,23 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let setPipeResult ~(lhs : Parsetree.expression) ~id = let rec findPipe (e : Parsetree.expression) = match e.pexp_desc with - | Pexp_constant (Pconst_string _) -> Some PartialParser.PipeString - | Pexp_array _ -> Some PartialParser.PipeArray - | Pexp_ident {txt} -> Some (PartialParser.PipeId (flattenLongIdent txt)) - | Pexp_field (e1, {txt}) -> ( + | Pexp_constant (Pconst_string _) -> Some PartialParser.CPString + | Pexp_array _ -> Some PartialParser.CPArray + | Pexp_ident {txt} -> + Some (PartialParser.CPId (flattenLongIdent txt, Value)) + | Pexp_field (e1, {txt = Lident name}) -> ( match findPipe e1 with - | Some (PipeId path) -> Some (PipeId (path @ flattenLongIdent txt)) + | Some contextPath -> Some (CPField (contextPath, name)) | _ -> None) + | Pexp_field (_, {txt = Ldot (lid, name)}) -> + (* Case x.M.field ignore the x part *) + Some + (PartialParser.CPField (CPId (flattenLongIdent lid, Module), name)) | _ -> None in match findPipe lhs with | Some pipe -> - setResult (PartialParser.Cpipe (pipe, id)); + setResult (PartialParser.Cpath (CPPipe (pipe, id))); true | None -> false in diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 580bb313a..04c336e60 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -819,9 +819,23 @@ let completionsGetTypeEnv = function | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (typ, env) | _ -> None -let rec getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env ~exact - (contextPath : PartialParser.contextPath) = +let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos + ~env ~exact (contextPath : PartialParser.contextPath) = match contextPath with + | CPString -> + [ + Completion.create ~name:"string" ~env + ~kind: + (Completion.Value + (Ctype.newconstr (Path.Pident (Ident.create "string")) [])); + ] + | CPArray -> + [ + Completion.create ~name:"array" ~env + ~kind: + (Completion.Value + (Ctype.newconstr (Path.Pident (Ident.create "array")) [])); + ] | CPId (path, completionContext) -> path |> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact @@ -834,8 +848,8 @@ let rec getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env ~exact | CPField (cp, fieldName) -> ( match cp - |> getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env - ~exact:true + |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos + ~env ~exact:true |> completionsGetTypeEnv with | Some (typ, env) -> ( @@ -857,8 +871,8 @@ let rec getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env ~exact | CPObj (cp, label) -> ( match cp - |> getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env - ~exact:true + |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos + ~env ~exact:true |> completionsGetTypeEnv with | Some (typ, env) -> ( @@ -882,6 +896,97 @@ let rec getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env ~exact else None) | None -> []) | None -> []) + | CPPipe (cp, funNamePrefix) -> ( + match + cp + |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos + ~env ~exact:true + |> completionsGetTypeEnv + with + | Some (typ, _envNotUsed) -> ( + let arrayModulePath = ["Js"; "Array2"] in + let listModulePath = ["Belt"; "List"] in + let optionModulePath = ["Belt"; "Option"] in + let stringModulePath = ["Js"; "String2"] in + let getModulePath path = + let rec loop (path : Path.t) = + match path with + | Pident id -> [Ident.name id] + | Pdot (p, s, _) -> s :: loop p + | Papply _ -> [] + in + match path with + | Path.Pident id when Ident.name id = "array" -> arrayModulePath + | Path.Pident id when Ident.name id = "list" -> listModulePath + | Path.Pident id when Ident.name id = "option" -> optionModulePath + | Path.Pident id when Ident.name id = "string" -> stringModulePath + | _ -> ( match loop path with _ :: rest -> List.rev rest | [] -> []) + in + let getConstr typ = + match typ.Types.desc with + | Tconstr (path, _, _) + | Tlink {desc = Tconstr (path, _, _)} + | Tpoly ({desc = Tconstr (path, _, _)}, []) -> + Some path + | _ -> None + in + let fromType typ = + match getConstr typ with + | None -> None + | Some path -> Some (getModulePath path) + in + let lhsPath = fromType typ in + let removePackageOpens modulePath = + match modulePath with + | toplevel :: rest when package.opens |> List.mem toplevel -> rest + | _ -> modulePath + in + let rec removeRawOpen rawOpen modulePath = + match (rawOpen, modulePath) with + | [_], _ -> Some modulePath + | s :: inner, first :: restPath when s = first -> + removeRawOpen inner restPath + | _ -> None + in + let rec removeRawOpens rawOpens modulePath = + match rawOpens with + | rawOpen :: restOpens -> + let newModulePath = + match removeRawOpen rawOpen modulePath with + | None -> modulePath + | Some newModulePath -> newModulePath + in + removeRawOpens restOpens newModulePath + | [] -> modulePath + in + match lhsPath with + | Some modulePath -> ( + match modulePath with + | _ :: _ -> + let modulePathMinusOpens = + modulePath |> removePackageOpens |> removeRawOpens rawOpens + |> String.concat "." + in + let completionName name = + if modulePathMinusOpens = "" then name + else modulePathMinusOpens ^ "." ^ name + in + let completions = + modulePath @ [funNamePrefix] + |> getCompletionsForPath ~completionContext:PartialParser.Value + ~exact:false ~package ~opens ~allFiles ~pos ~env + in + completions + |> List.map (fun (completion : Completion.t) -> + { + completion with + name = completionName completion.name; + env + (* Restore original env for the completion after x->foo()... *); + }) + | [] -> []) + | None -> []) + | None -> []) let processCompletable ~package ~rawOpens ~opens ~env ~pos (completable : PartialParser.completable) = @@ -895,8 +1000,8 @@ let processCompletable ~package ~rawOpens ~opens ~env ~pos match completable with | Cpath contextPath -> contextPath - |> getCompletionsForContextPath ~package ~opens ~allFiles ~pos ~env - ~exact:false + |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos + ~env ~exact:false |> List.map completionToItem | Cjsx ([id], prefix, identsSeen) when String.uncapitalize_ascii id = id -> let mkLabel_ name typString = @@ -1074,16 +1179,15 @@ let processCompletable ~package ~rawOpens ~opens ~env ~pos if modulePathMinusOpens = "" then name else modulePathMinusOpens ^ "." ^ name in - let declareds = + let completions = modulePath @ [partialName] |> getCompletionsForPath ~completionContext:PartialParser.Value ~exact:false ~package ~opens ~allFiles ~pos ~env in - declareds - |> List.map (fun {Completion.name; deprecated; docstring; kind} -> - mkItem ~name:(completionName name) - ~kind:(Completion.kindToInt kind) - ~detail:(detail name kind) ~deprecated ~docstring) + completions + |> List.map (fun (completion : Completion.t) -> + completionToItem + {completion with name = completionName completion.name}) | _ -> []) | None -> []) | Cdecorator prefix -> diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index ca68c232e..f638a5c79 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -4,9 +4,12 @@ type pipe = PipeId of string list | PipeArray | PipeString type completionContext = Type | Value | Module | Field type contextPath = + | CPString + | CPArray | CPId of string list * completionContext | CPField of contextPath * string | CPObj of contextPath * string + | CPPipe of contextPath * string type completable = | Cdecorator of string (** e.g. @module *) @@ -27,10 +30,13 @@ let completableToString = | Field -> "Field" in let rec contextPathToString = function + | CPString -> "string" + | CPArray -> "array" | CPId (sl, completionContext) -> completionContextToString completionContext ^ list sl | CPField (cp, s) -> contextPathToString cp ^ "." ^ str s | CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]" + | CPPipe (cp, s) -> contextPathToString cp ^ "->" ^ s in function | Cpath cp -> "Cpath " ^ contextPathToString cp diff --git a/analysis/tests/src/expected/CompletePrioritize1.res.txt b/analysis/tests/src/expected/CompletePrioritize1.res.txt index bc06e0b38..fc6375ade 100644 --- a/analysis/tests/src/expected/CompletePrioritize1.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize1.res.txt @@ -1,6 +1,6 @@ Complete tests/src/CompletePrioritize1.res 4:2 posCursor:[5:4] posNoWhite:[5:3] Found expr:[5:1->0:-1] -Completable: Cpipe([a], "") +Completable: Cpath Value[a]-> [{ "label": "Test.add", "kind": 12, diff --git a/analysis/tests/src/expected/CompletePrioritize2.res.txt b/analysis/tests/src/expected/CompletePrioritize2.res.txt index b2e2f71eb..e72d14343 100644 --- a/analysis/tests/src/expected/CompletePrioritize2.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize2.res.txt @@ -1,6 +1,6 @@ Complete tests/src/CompletePrioritize2.res 8:2 posCursor:[9:4] posNoWhite:[9:3] Found expr:[9:1->0:-1] -Completable: Cpipe([a], "") +Completable: Cpath Value[a]-> [{ "label": "Test.add", "kind": 12, diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 8c3c28c2b..e142cc8f6 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -380,7 +380,7 @@ Complete tests/src/Completion.res 21:2 posCursor:[22:11] posNoWhite:[22:10] Found expr:[22:1->22:11] posCursor:[22:11] posNoWhite:[22:10] Found expr:[22:10->22:11] Pexp_ident m:[22:10->22:11] -Completable: Cpipe(PipeArray, m) +Completable: Cpath array->m [{ "label": "Js.Array2.mapi", "kind": 12, @@ -399,7 +399,7 @@ Complete tests/src/Completion.res 23:2 posCursor:[24:11] posNoWhite:[24:10] Found expr:[24:1->24:11] posCursor:[24:11] posNoWhite:[24:10] Found expr:[24:8->24:11] Pexp_ident toU:[24:8->24:11] -Completable: Cpipe(PipeString, toU) +Completable: Cpath string->toU [{ "label": "Js.String2.toUpperCase", "kind": 12, @@ -412,7 +412,7 @@ Complete tests/src/Completion.res 27:2 posCursor:[28:6] posNoWhite:[28:5] Found expr:[28:1->28:6] posCursor:[28:6] posNoWhite:[28:5] Found expr:[28:5->28:6] Pexp_ident e:[28:5->28:6] -Completable: Cpipe([op], e) +Completable: Cpath Value[op]->e [{ "label": "Belt.Option.eqU", "kind": 12, @@ -433,7 +433,7 @@ Pexp_apply ...[41:9->41:10] (...[37:1->41:8], ...[42:2->46:3]) posCursor:[37:5] posNoWhite:[37:4] Found expr:[37:1->41:8] posCursor:[37:5] posNoWhite:[37:4] Found expr:[37:3->37:5] Pexp_ident |.:[37:3->37:5] -Completable: Cpipe([fa], "") +Completable: Cpath Value[fa]-> [{ "label": "ForAuto.abc", "kind": 12, diff --git a/analysis/tests/src/expected/RecordCompletion.res.txt b/analysis/tests/src/expected/RecordCompletion.res.txt index ca6ac9783..c88f872eb 100644 --- a/analysis/tests/src/expected/RecordCompletion.res.txt +++ b/analysis/tests/src/expected/RecordCompletion.res.txt @@ -2,7 +2,7 @@ Complete tests/src/RecordCompletion.res 7:3 posCursor:[8:7] posNoWhite:[8:6] Found expr:[8:1->8:7] posCursor:[8:7] posNoWhite:[8:6] Found expr:[8:6->8:7] Pexp_ident m:[8:6->8:7] -Completable: Cpipe([t, n], m) +Completable: Cpath Value[t].n->m [{ "label": "Js.Array2.mapi", "kind": 12, @@ -21,7 +21,7 @@ Complete tests/src/RecordCompletion.res 9:3 posCursor:[10:11] posNoWhite:[10:10] Found expr:[10:1->10:11] posCursor:[10:11] posNoWhite:[10:10] Found expr:[10:10->10:11] Pexp_ident m:[10:10->10:11] -Completable: Cpipe([t2, n2, n], m) +Completable: Cpath Value[t2].n2.n->m [{ "label": "Js.Array2.mapi", "kind": 12, From bdf95266df8027436a9ed8441fe4e98699e1910d Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 10:47:15 +0200 Subject: [PATCH 090/135] Remove pipe from old backend. --- analysis/src/NewCompletions.ml | 114 --------------------------------- analysis/src/PartialParser.ml | 10 --- 2 files changed, 124 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 04c336e60..1965d3588 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -1076,120 +1076,6 @@ let processCompletable ~package ~rawOpens ~opens ~env ~pos Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel) @ keyLabels - | Cpipe (pipe, partialName) -> ( - let arrayModulePath = ["Js"; "Array2"] in - let listModulePath = ["Belt"; "List"] in - let optionModulePath = ["Belt"; "Option"] in - let stringModulePath = ["Js"; "String2"] in - let getModulePath path = - let rec loop (path : Path.t) = - match path with - | Pident id -> [Ident.name id] - | Pdot (p, s, _) -> s :: loop p - | Papply _ -> [] - in - match path with - | Path.Pident id when Ident.name id = "array" -> arrayModulePath - | Path.Pident id when Ident.name id = "list" -> listModulePath - | Path.Pident id when Ident.name id = "option" -> optionModulePath - | Path.Pident id when Ident.name id = "string" -> stringModulePath - | _ -> ( match loop path with _ :: rest -> List.rev rest | [] -> []) - in - let getLhsPath ~pipeIdPath ~partialName = - let getConstr typ = - match typ.Types.desc with - | Tconstr (path, _, _) - | Tlink {desc = Tconstr (path, _, _)} - | Tpoly ({desc = Tconstr (path, _, _)}, []) -> - Some path - | _ -> None - in - let fromType typ = - match getConstr typ with - | None -> None - | Some path -> - let modulePath = getModulePath path in - Some (modulePath, partialName) - in - let getField ~env ~typ fieldName = - match extractRecordType typ ~env ~package with - | Some (env1, fields, _) -> ( - match - fields |> List.find_opt (fun field -> field.fname.txt = fieldName) - with - | None -> None - | Some field -> Some (field.typ, env1)) - | None -> None - in - let rec getFields ~env ~typ = function - | [] -> Some (typ, env) - | fieldName :: rest -> ( - match getField ~env ~typ fieldName with - | None -> None - | Some (typ1, env1) -> getFields ~env:env1 ~typ:typ1 rest) - in - match pipeIdPath with - | x :: fieldNames -> ( - match [x] |> findTypeOfValue with - | Some (typ, env) -> ( - match getFields ~env ~typ fieldNames with - | None -> None - | Some (typ1, _env1) -> fromType typ1) - | None -> None) - | [] -> None - in - let lhsPath = - match pipe with - | PipeId pipeIdPath -> getLhsPath ~pipeIdPath ~partialName - | PipeString -> Some (stringModulePath, partialName) - | PipeArray -> Some (arrayModulePath, partialName) - in - let removePackageOpens modulePath = - match modulePath with - | toplevel :: rest when package.opens |> List.mem toplevel -> rest - | _ -> modulePath - in - let rec removeRawOpen rawOpen modulePath = - match (rawOpen, modulePath) with - | [_], _ -> Some modulePath - | s :: inner, first :: restPath when s = first -> - removeRawOpen inner restPath - | _ -> None - in - let rec removeRawOpens rawOpens modulePath = - match rawOpens with - | rawOpen :: restOpens -> - let newModulePath = - match removeRawOpen rawOpen modulePath with - | None -> modulePath - | Some newModulePath -> newModulePath - in - removeRawOpens restOpens newModulePath - | [] -> modulePath - in - match lhsPath with - | Some (modulePath, partialName) -> ( - match modulePath with - | _ :: _ -> - let modulePathMinusOpens = - modulePath |> removePackageOpens |> removeRawOpens rawOpens - |> String.concat "." - in - let completionName name = - if modulePathMinusOpens = "" then name - else modulePathMinusOpens ^ "." ^ name - in - let completions = - modulePath @ [partialName] - |> getCompletionsForPath ~completionContext:PartialParser.Value - ~exact:false ~package ~opens ~allFiles ~pos ~env - in - completions - |> List.map (fun (completion : Completion.t) -> - completionToItem - {completion with name = completionName completion.name}) - | _ -> []) - | None -> []) | Cdecorator prefix -> let mkDecorator name = mkItem ~name ~kind:4 ~deprecated:None ~detail:"" ~docstring:[] diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index f638a5c79..cd0e98d35 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -1,5 +1,3 @@ -type pipe = PipeId of string list | PipeArray | PipeString - (* Completion context *) type completionContext = Type | Value | Module | Field @@ -18,7 +16,6 @@ type completable = | Cpath of contextPath | Cjsx of string list * string * string list (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for foo" *) let completableToString = let str s = if s = "" then "\"\"" else s in @@ -45,13 +42,6 @@ let completableToString = "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" | Cjsx (sl1, s, sl2) -> "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" - | Cpipe (pipe, s) -> - "Cpipe(" - ^ (match pipe with - | PipeId sl -> sl |> list - | PipeArray -> "PipeArray" - | PipeString -> "PipeString") - ^ ", " ^ str s ^ ")" let rec skipWhite text i = if i < 0 then 0 From 37e2d1b819fd4b0471eab53fd3782d7c5f850aa4 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 10:58:57 +0200 Subject: [PATCH 091/135] Generalize front-end to match new back-end. --- analysis/src/Commands.ml | 70 +++++++++++++--------------------------- 1 file changed, 22 insertions(+), 48 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 02e0c2aaa..919fd267b 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -254,6 +254,24 @@ let extractExpApplyArgs ~text ~(funName : Longident.t Location.loc) ~args = in args |> processArgs ~lastOffset ~lastPos ~acc:[] +let rec exporToContextPath (e : Parsetree.expression) = + match e.pexp_desc with + | Pexp_constant (Pconst_string _) -> Some PartialParser.CPString + | Pexp_array _ -> Some PartialParser.CPArray + | Pexp_ident {txt} -> Some (PartialParser.CPId (flattenLongIdent txt, Value)) + | Pexp_field (e1, {txt = Lident name}) -> ( + match exporToContextPath e1 with + | Some contextPath -> Some (CPField (contextPath, name)) + | _ -> None) + | Pexp_field (_, {txt = Ldot (lid, name)}) -> + (* Case x.M.field ignore the x part *) + Some (PartialParser.CPField (CPId (flattenLongIdent lid, Module), name)) + | Pexp_send (e1, {txt}) -> ( + match exporToContextPath e1 with + | None -> None + | Some contexPath -> Some (CPObj (contexPath, txt))) + | _ -> None + let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let offset = match PartialParser.positionToOffset text posCursor with @@ -358,29 +376,12 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (Loc.toString expr.pexp_loc) in let setPipeResult ~(lhs : Parsetree.expression) ~id = - let rec findPipe (e : Parsetree.expression) = - match e.pexp_desc with - | Pexp_constant (Pconst_string _) -> Some PartialParser.CPString - | Pexp_array _ -> Some PartialParser.CPArray - | Pexp_ident {txt} -> - Some (PartialParser.CPId (flattenLongIdent txt, Value)) - | Pexp_field (e1, {txt = Lident name}) -> ( - match findPipe e1 with - | Some contextPath -> Some (CPField (contextPath, name)) - | _ -> None) - | Pexp_field (_, {txt = Ldot (lid, name)}) -> - (* Case x.M.field ignore the x part *) - Some - (PartialParser.CPField (CPId (flattenLongIdent lid, Module), name)) - | _ -> None - in - match findPipe lhs with + match exporToContextPath lhs with | Some pipe -> setResult (PartialParser.Cpath (CPPipe (pipe, id))); true | None -> false in - match expr.pexp_desc with | Pexp_apply ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, @@ -423,25 +424,10 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) (flattenLongIdent fieldName.txt |> String.concat ".") (Loc.toString fieldName.loc); - let rec digLhs (lhs : Parsetree.expression) = - match lhs.pexp_desc with - | Pexp_ident id -> - Some (PartialParser.CPId (flattenLongIdent id.txt, Value)) - | Pexp_field (lhs, {txt = Lident name}) -> ( - match digLhs lhs with - | Some contextPath -> - Some (PartialParser.CPField (contextPath, name)) - | None -> None) - | Pexp_field (_, {txt = Ldot (id, name)}) -> - (* Case x.M.field ignore the x part *) - Some - (PartialParser.CPField (CPId (flattenLongIdent id, Module), name)) - | _ -> None - in if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then match fieldName.txt with | Lident name -> ( - match digLhs e with + match exporToContextPath e with | Some contextPath -> let contextPath = PartialParser.CPField (contextPath, name) in setResult (PartialParser.Cpath contextPath) @@ -456,7 +442,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = setResult (PartialParser.Cpath contextPath) | Lapply _ -> () else if Loc.end_ e.pexp_loc = posBeforeCursor then - match digLhs e with + match exporToContextPath e with | Some contextPath -> setResult (PartialParser.Cpath (PartialParser.CPField (contextPath, ""))) @@ -533,18 +519,6 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let l, c = Loc.start loc in ((l, c + 1), (l, c + 1 + String.length label)) in - - let rec processLhs (lhs : Parsetree.expression) = - match lhs.pexp_desc with - | Pexp_ident id -> - Some (PartialParser.CPId (flattenLongIdent id.txt, Value)) - | Pexp_send (e1, {txt}) -> ( - match processLhs e1 with - | None -> None - | Some contexPath -> Some (CPObj (contexPath, txt))) - | _ -> None - in - if debug then Printf.printf "XXX Pexp_send %s%s e:%s\n" label (Range.toString labelRange) @@ -553,7 +527,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = labelRange |> Range.hasPos ~pos:posBeforeCursor || (label = "" && posCursor = fst labelRange) then - match processLhs lhs with + match exporToContextPath lhs with | Some contextPath -> setResult (PartialParser.Cpath (CPObj (contextPath, label))) | None -> ()) From a8c23139e7a40a5e19c1ad739313b68b1297e574 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 11:07:15 +0200 Subject: [PATCH 092/135] Add example combining all the forms of completion, nested. --- analysis/tests/src/Completion.res | 16 ++- .../tests/src/expected/Completion.res.txt | 107 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index 289e214c4..d5271ca7e 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -170,4 +170,18 @@ open Shadow.A // ^com sha open Shadow.B // ^com sha -let _ = shadowed \ No newline at end of file +let _ = shadowed + +module FAR = { + type forAutoRecord = {forAuto: ForAuto.t, something: option} + let forAutoRecord: forAutoRecord = assert false +} + +module FAO = { + let forAutoObject = {"forAutoLabel": FAR.forAutoRecord, "age": 32} +} + +// ^com FAO.forAutoObject[" +// ^com FAO.forAutoObject["forAutoLabel"]. +// ^com FAO.forAutoObject["forAutoLabel"].forAuto-> +// ^com FAO.forAutoObject["forAutoLabel"].forAuto->ForAuto.a diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index e142cc8f6..ef9cd4ff8 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -791,6 +791,41 @@ DocumentSymbol tests/src/Completion.res "name": "shadowed", "kind": 15, "location": {"uri": "Completion.res", "range": {"start": {"line": 163, "character": 4}, "end": {"line": 163, "character": 21}}} +}, +{ + "name": "FAR", + "kind": 2, + "location": {"uri": "Completion.res", "range": {"start": {"line": 174, "character": 7}, "end": {"line": 177, "character": 1}}} +}, +{ + "name": "forAutoRecord", + "kind": 26, + "location": {"uri": "Completion.res", "range": {"start": {"line": 175, "character": 2}, "end": {"line": 175, "character": 67}}} +}, +{ + "name": "forAuto", + "kind": 7, + "location": {"uri": "Completion.res", "range": {"start": {"line": 175, "character": 24}, "end": {"line": 175, "character": 42}}} +}, +{ + "name": "something", + "kind": 7, + "location": {"uri": "Completion.res", "range": {"start": {"line": 175, "character": 44}, "end": {"line": 175, "character": 66}}} +}, +{ + "name": "forAutoRecord", + "kind": 13, + "location": {"uri": "Completion.res", "range": {"start": {"line": 176, "character": 2}, "end": {"line": 176, "character": 49}}} +}, +{ + "name": "FAO", + "kind": 2, + "location": {"uri": "Completion.res", "range": {"start": {"line": 179, "character": 7}, "end": {"line": 181, "character": 1}}} +}, +{ + "name": "forAutoObject", + "kind": 13, + "location": {"uri": "Completion.res", "range": {"start": {"line": 180, "character": 2}, "end": {"line": 180, "character": 68}}} } ] @@ -1183,3 +1218,75 @@ Completable: Cpath Value[sha] "documentation": null }] +Complete tests/src/Completion.res 182:3 +posCursor:[183:20] posNoWhite:[183:19] Found expr:[183:1->184:27] +XXX Pexp_send [183:20->183:20] e:[183:1->183:18] +Completable: Cpath Value[FAO, forAutoObject][""] +[{ + "label": "age", + "kind": 4, + "tags": [], + "detail": "int", + "documentation": null + }, { + "label": "forAutoLabel", + "kind": 4, + "tags": [], + "detail": "FAR.forAutoRecord", + "documentation": null + }] + +Complete tests/src/Completion.res 183:3 +posCursor:[184:35] posNoWhite:[184:34] Found expr:[184:1->188:0] +Pexp_field [184:1->184:34] _:[188:0->188:0] +Completable: Cpath Value[FAO, forAutoObject]["forAutoLabel"]."" +[{ + "label": "forAuto", + "kind": 5, + "tags": [], + "detail": "forAuto: ForAuto.t\n\ntype forAutoRecord = {\n forAuto: ForAuto.t,\n something: option,\n}", + "documentation": null + }, { + "label": "something", + "kind": 5, + "tags": [], + "detail": "something: option\n\ntype forAutoRecord = {\n forAuto: ForAuto.t,\n something: option,\n}", + "documentation": null + }] + +Complete tests/src/Completion.res 184:3 +posCursor:[185:44] posNoWhite:[185:43] Found expr:[185:1->0:-1] +Completable: Cpath Value[FAO, forAutoObject]["forAutoLabel"].forAuto-> +[{ + "label": "ForAuto.abc", + "kind": 12, + "tags": [], + "detail": "(t, int) => t", + "documentation": null + }, { + "label": "ForAuto.abd", + "kind": 12, + "tags": [], + "detail": "(t, int) => t", + "documentation": null + }] + +Complete tests/src/Completion.res 185:3 +posCursor:[186:53] posNoWhite:[186:52] Found expr:[186:1->186:53] +posCursor:[186:53] posNoWhite:[186:52] Found expr:[186:44->186:53] +Pexp_ident ForAuto.a:[186:44->186:53] +Completable: Cpath Value[ForAuto, a] +[{ + "label": "abc", + "kind": 12, + "tags": [], + "detail": "(t, int) => t", + "documentation": null + }, { + "label": "abd", + "kind": 12, + "tags": [], + "detail": "(t, int) => t", + "documentation": null + }] + From 3076bb8ea7891732681574c2c7e6ec34473ac702 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 11:10:21 +0200 Subject: [PATCH 093/135] clean up output --- analysis/src/Commands.ml | 2 +- analysis/tests/src/expected/Completion.res.txt | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 919fd267b..0f050a55c 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -520,7 +520,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = ((l, c + 1), (l, c + 1 + String.length label)) in if debug then - Printf.printf "XXX Pexp_send %s%s e:%s\n" label + Printf.printf "Pexp_send %s%s e:%s\n" label (Range.toString labelRange) (Loc.toString lhs.pexp_loc); if diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index ef9cd4ff8..99f298735 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -897,7 +897,7 @@ Completable: Clabel([Lib, foo], "", [age, name]) Complete tests/src/Completion.res 72:2 posCursor:[73:11] posNoWhite:[73:10] Found expr:[73:1->75:18] -XXX Pexp_send a[73:10->73:11] e:[73:1->73:8] +Pexp_send a[73:10->73:11] e:[73:1->73:8] Completable: Cpath Value[someObj]["a"] [{ "label": "age", @@ -909,7 +909,7 @@ Completable: Cpath Value[someObj]["a"] Complete tests/src/Completion.res 76:2 posCursor:[77:22] posNoWhite:[77:21] Found expr:[77:1->80:10] -XXX Pexp_send [77:22->77:22] e:[77:1->77:20] +Pexp_send [77:22->77:22] e:[77:1->77:20] Completable: Cpath Value[nestedObj]["x"]["y"][""] [{ "label": "age", @@ -927,7 +927,7 @@ Completable: Cpath Value[nestedObj]["x"]["y"][""] Complete tests/src/Completion.res 79:2 posCursor:[80:5] posNoWhite:[80:4] Found expr:[80:1->82:20] -XXX Pexp_send a[80:4->80:5] e:[80:1->80:2] +Pexp_send a[80:4->80:5] e:[80:1->80:2] Completable: Cpath Value[o]["a"] [{ "label": "age", @@ -939,7 +939,7 @@ Completable: Cpath Value[o]["a"] Complete tests/src/Completion.res 83:2 posCursor:[84:15] posNoWhite:[84:14] Found expr:[84:1->101:20] -XXX Pexp_send [84:15->84:15] e:[84:1->84:13] +Pexp_send [84:15->84:15] e:[84:1->84:13] Completable: Cpath Value[no]["x"]["y"][""] [{ "label": "name", @@ -1007,7 +1007,7 @@ Completable: Cpath Value[my] Complete tests/src/Completion.res 100:3 posCursor:[101:13] posNoWhite:[101:12] Found expr:[101:1->120:32] -XXX Pexp_send [101:13->101:13] e:[101:1->101:11] +Pexp_send [101:13->101:13] e:[101:1->101:11] Completable: Cpath Value[Obj, object][""] [{ "label": "name", @@ -1220,7 +1220,7 @@ Completable: Cpath Value[sha] Complete tests/src/Completion.res 182:3 posCursor:[183:20] posNoWhite:[183:19] Found expr:[183:1->184:27] -XXX Pexp_send [183:20->183:20] e:[183:1->183:18] +Pexp_send [183:20->183:20] e:[183:1->183:18] Completable: Cpath Value[FAO, forAutoObject][""] [{ "label": "age", From ec52f8cb739c3eec59bd5b7d35457286fdba1e50 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Mon, 18 Apr 2022 19:29:41 +0200 Subject: [PATCH 094/135] labels on Win newlines --- analysis/src/Commands.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 0f050a55c..7a7a8a0f3 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -515,6 +515,11 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = Only take the first like to represent the label *) let txtLines = txt |> String.split_on_char '\n' in let label = List.hd txtLines in + let label = + if label <> "" && label.[String.length label - 1] = '\r' then + String.sub label 0 (String.length label - 1) + else label + in let labelRange = let l, c = Loc.start loc in ((l, c + 1), (l, c + 1 + String.length label)) From 0882ded43a54ec170bc892152326019f350c40d7 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 19 Apr 2022 18:19:03 +0200 Subject: [PATCH 095/135] First step towards scope. --- analysis/src/Commands.ml | 79 ++++++++++++++-------------------- analysis/src/NewCompletions.ml | 14 +++--- analysis/src/Scope.ml | 8 ++++ analysis/src/Utils.ml | 15 +++++++ 4 files changed, 61 insertions(+), 55 deletions(-) create mode 100644 analysis/src/Scope.ml diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 7a7a8a0f3..d8d2bd8df 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -62,21 +62,6 @@ let rec skipComment ~pos ~i ~depth str = | _ -> skipComment ~depth ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str else None -let flattenLongIdent ?(jsx = false) lid = - let rec loop acc lid = - match lid with - | Longident.Lident txt -> txt :: acc - | Ldot (lid, txt) -> - let acc = - if jsx && txt = "createElement" then acc - else if txt = "$" then "" :: acc - else txt :: acc - in - loop acc lid - | Lapply _ -> acc - in - loop [] lid - let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = let rec extractLabelPos ~pos ~i str = if i < String.length str then @@ -103,7 +88,7 @@ let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = match args with | (Asttypes.Labelled "children", {Parsetree.pexp_loc}) :: _ -> { - componentPath = flattenLongIdent ~jsx:true compName.txt; + componentPath = Utils.flattenLongIdent ~jsx:true compName.txt; props = List.rev acc; childrenStart = (if pexp_loc.loc_ghost then None else Some (Loc.start pexp_loc)); @@ -156,7 +141,7 @@ type arg = {label : label; exp : Parsetree.expression} let findExpApplyCompletable ~(args : arg list) ~endPos ~posBeforeCursor ~(funName : Longident.t Location.loc) = - let funPath = flattenLongIdent funName.txt in + let funPath = Utils.flattenLongIdent funName.txt in let posAfterFunName = Loc.end_ funName.loc in let allNames = List.fold_right @@ -258,14 +243,16 @@ let rec exporToContextPath (e : Parsetree.expression) = match e.pexp_desc with | Pexp_constant (Pconst_string _) -> Some PartialParser.CPString | Pexp_array _ -> Some PartialParser.CPArray - | Pexp_ident {txt} -> Some (PartialParser.CPId (flattenLongIdent txt, Value)) + | Pexp_ident {txt} -> + Some (PartialParser.CPId (Utils.flattenLongIdent txt, Value)) | Pexp_field (e1, {txt = Lident name}) -> ( match exporToContextPath e1 with | Some contextPath -> Some (CPField (contextPath, name)) | _ -> None) | Pexp_field (_, {txt = Ldot (lid, name)}) -> (* Case x.M.field ignore the x part *) - Some (PartialParser.CPField (CPId (flattenLongIdent lid, Module), name)) + Some + (PartialParser.CPField (CPId (Utils.flattenLongIdent lid, Module), name)) | Pexp_send (e1, {txt}) -> ( match exporToContextPath e1 with | None -> None @@ -287,37 +274,35 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let found = ref false in let result = ref None in - let opensInScope = ref [] in + let scope = ref (Scope.create ()) in let setResultOpt x = if !result = None then - match x with - | None -> () - | Some x -> result := Some (x, List.rev !opensInScope) + match x with None -> () | Some x -> result := Some (x, !scope) in let setResult x = setResultOpt (Some x) in let structure (iterator : Ast_iterator.iterator) (structure : Parsetree.structure) = - let oldOpens = !opensInScope in + let oldScope = !scope in Ast_iterator.default_iterator.structure iterator structure; - opensInScope := oldOpens + scope := oldScope in let structure_item (iterator : Ast_iterator.iterator) (item : Parsetree.structure_item) = (match item.pstr_desc with - | Pstr_open {popen_lid} -> opensInScope := popen_lid.txt :: !opensInScope + | Pstr_open {popen_lid} -> scope := !scope |> Scope.addModule popen_lid.txt | _ -> ()); Ast_iterator.default_iterator.structure_item iterator item in let signature (iterator : Ast_iterator.iterator) (signature : Parsetree.signature) = - let oldOpens = !opensInScope in + let oldScope = !scope in Ast_iterator.default_iterator.signature iterator signature; - opensInScope := oldOpens + scope := oldScope in let signature_item (iterator : Ast_iterator.iterator) (item : Parsetree.signature_item) = (match item.psig_desc with - | Psig_open {popen_lid} -> opensInScope := popen_lid.txt :: !opensInScope + | Psig_open {popen_lid} -> scope := !scope |> Scope.addModule popen_lid.txt | _ -> ()); Ast_iterator.default_iterator.signature_item iterator item in @@ -400,15 +385,15 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | Pexp_ident id -> if debug then Printf.printf "Pexp_ident %s:%s\n" - (flattenLongIdent id.txt |> String.concat ".") + (Utils.flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); if id.loc |> Loc.hasPos ~pos:posBeforeCursor then setResult - (PartialParser.Cpath (CPId (flattenLongIdent id.txt, Value))) + (PartialParser.Cpath (CPId (Utils.flattenLongIdent id.txt, Value))) | Pexp_construct (id, eOpt) -> if debug then Printf.printf "Pexp_construct %s:%s %s\n" - (flattenLongIdent id.txt |> String.concat "\n") + (Utils.flattenLongIdent id.txt |> String.concat "\n") (Loc.toString id.loc) (match eOpt with | None -> "None" @@ -418,11 +403,11 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = && id.loc |> Loc.hasPos ~pos:posBeforeCursor then setResult - (PartialParser.Cpath (CPId (flattenLongIdent id.txt, Value))) + (PartialParser.Cpath (CPId (Utils.flattenLongIdent id.txt, Value))) | Pexp_field (e, fieldName) -> ( if debug then Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) - (flattenLongIdent fieldName.txt |> String.concat ".") + (Utils.flattenLongIdent fieldName.txt |> String.concat ".") (Loc.toString fieldName.loc); if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then match fieldName.txt with @@ -436,7 +421,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (* Case x.M.field ignore the x part *) let contextPath = PartialParser.CPField - ( CPId (flattenLongIdent id, Module), + ( CPId (Utils.flattenLongIdent id, Module), if name = "$" then "" else name ) in setResult (PartialParser.Cpath contextPath) @@ -471,7 +456,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then setResult (PartialParser.Cpath - (CPId (flattenLongIdent ~jsx:true compName.txt, Module))) + (CPId (Utils.flattenLongIdent ~jsx:true compName.txt, Module))) | Pexp_apply ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, [ @@ -550,10 +535,11 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | Ptyp_constr (id, _args) -> if debug then Printf.printf "Ptyp_constr %s:%s\n" - (flattenLongIdent id.txt |> String.concat ".") + (Utils.flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt, Type))) + setResult + (PartialParser.Cpath (CPId (Utils.flattenLongIdent id.txt, Type))) | _ -> ()); Ast_iterator.default_iterator.typ iterator core_type in @@ -563,10 +549,11 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | Pmod_ident id when id.loc |> Loc.hasPos ~pos:posBeforeCursor -> if debug then Printf.printf "Pmod_ident %s:%s\n" - (flattenLongIdent id.txt |> String.concat ".") + (Utils.flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); found := true; - setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt, Module))) + setResult + (PartialParser.Cpath (CPId (Utils.flattenLongIdent id.txt, Module))) | _ -> ()); Ast_iterator.default_iterator.module_expr iterator me in @@ -576,10 +563,11 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | Pmty_ident id when id.loc |> Loc.hasPos ~pos:posBeforeCursor -> if debug then Printf.printf "Pmty_ident %s:%s\n" - (flattenLongIdent id.txt |> String.concat ".") + (Utils.flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); found := true; - setResult (PartialParser.Cpath (CPId (flattenLongIdent id.txt, Module))) + setResult + (PartialParser.Cpath (CPId (Utils.flattenLongIdent id.txt, Module))) | _ -> ()); Ast_iterator.default_iterator.module_type iterator mt in @@ -645,14 +633,11 @@ let completion ~debug ~path ~pos ~currentFile = completionWithParser ~debug ~path ~posCursor:pos ~currentFile ~text with | None -> [] - | Some (completable, opensInScope) -> ( + | Some (completable, scope) -> ( if debug then Printf.printf "Completable: %s\n" (PartialParser.completableToString completable); - let rawOpens = - opensInScope - |> List.map (fun id -> flattenLongIdent id @ ["place holder"]) - in + let rawOpens = Scope.getRawOpens scope in (* Only perform expensive ast operations if there are completables *) match Cmt.fromPath ~path with | None -> [] diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 1965d3588..e34d74c31 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -675,7 +675,7 @@ let resolveRawOpens ~env ~rawOpens ~package = ~previous: (List.map QueryEnv.fromFile (packageOpens |> Utils.filterMap (ProcessCmt.fileForModule ~package))) - rawOpens ~package + (List.rev rawOpens) ~package in opens @@ -950,13 +950,11 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos in let rec removeRawOpens rawOpens modulePath = match rawOpens with - | rawOpen :: restOpens -> - let newModulePath = - match removeRawOpen rawOpen modulePath with - | None -> modulePath - | Some newModulePath -> newModulePath - in - removeRawOpens restOpens newModulePath + | rawOpen :: restOpens -> ( + let newModulePath = removeRawOpens restOpens modulePath in + match removeRawOpen rawOpen newModulePath with + | None -> newModulePath + | Some mp -> mp) | [] -> modulePath in match lhsPath with diff --git a/analysis/src/Scope.ml b/analysis/src/Scope.ml new file mode 100644 index 000000000..a170639e3 --- /dev/null +++ b/analysis/src/Scope.ml @@ -0,0 +1,8 @@ +type item = Module of string list | Value of unit +type t = item list + +let create () : t = [] +let addModule id t = Module (Utils.flattenLongIdent id @ ["place holder"]) :: t + +let getRawOpens x = + x |> Utils.filterMap (function Module path -> Some path | _ -> None) diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml index 44cab2362..c9ae410b2 100644 --- a/analysis/src/Utils.ml +++ b/analysis/src/Utils.ml @@ -59,3 +59,18 @@ let filterMap f = let dumpPath path = Str.global_replace (Str.regexp_string "\\") "/" path let isUncurriedInternal path = startsWith (Path.name path) "Js.Fn.arity" + +let flattenLongIdent ?(jsx = false) lid = + let rec loop acc lid = + match lid with + | Longident.Lident txt -> txt :: acc + | Ldot (lid, txt) -> + let acc = + if jsx && txt = "createElement" then acc + else if txt = "$" then "" :: acc + else txt :: acc + in + loop acc lid + | Lapply _ -> acc + in + loop [] lid From 69f7e8ada4e0b70bc00d977a57fa0e14e4b94cf9 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 06:33:20 +0200 Subject: [PATCH 096/135] Populate scope with values. --- analysis/src/Commands.ml | 58 +++++++++++--------- analysis/src/NewCompletions.ml | 96 ++++++++++++++++++++++------------ analysis/src/Scope.ml | 12 ++++- 3 files changed, 106 insertions(+), 60 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index d8d2bd8df..9a1f48f0f 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -280,6 +280,17 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = match x with None -> () | Some x -> result := Some (x, !scope) in let setResult x = setResultOpt (Some x) in + let scopeValueDescription (vd : Parsetree.value_description) = + scope := !scope |> Scope.addValue vd.pval_name.txt vd.pval_loc + in + let scopeValueBinding (vb : Parsetree.value_binding) = + match vb.pvb_pat.ppat_desc with + | Ppat_var {txt; loc} + | Ppat_constraint ({ppat_desc = Ppat_var {txt; loc}}, _) -> + scope := !scope |> Scope.addValue txt loc + | _ -> () + in + let structure (iterator : Ast_iterator.iterator) (structure : Parsetree.structure) = let oldScope = !scope in @@ -288,10 +299,18 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = in let structure_item (iterator : Ast_iterator.iterator) (item : Parsetree.structure_item) = + let processed = ref false in (match item.pstr_desc with | Pstr_open {popen_lid} -> scope := !scope |> Scope.addModule popen_lid.txt + | Pstr_primitive vd -> scopeValueDescription vd + | Pstr_value (recFlag, bindings) -> + if recFlag = Recursive then bindings |> List.iter scopeValueBinding; + bindings |> List.iter (fun vb -> iterator.value_binding iterator vb); + if recFlag = Nonrecursive then bindings |> List.iter scopeValueBinding; + processed := true | _ -> ()); - Ast_iterator.default_iterator.structure_item iterator item + if not !processed then + Ast_iterator.default_iterator.structure_item iterator item in let signature (iterator : Ast_iterator.iterator) (signature : Parsetree.signature) = @@ -303,6 +322,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (item : Parsetree.signature_item) = (match item.psig_desc with | Psig_open {popen_lid} -> scope := !scope |> Scope.addModule popen_lid.txt + | Psig_value vd -> scopeValueDescription vd | _ -> ()); Ast_iterator.default_iterator.signature_item iterator item in @@ -353,6 +373,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = Ast_iterator.default_iterator.attribute iterator (id, payload) in let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = + let processed = ref false in let setFound () = found := true; if debug then @@ -521,8 +542,16 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | Some contextPath -> setResult (PartialParser.Cpath (CPObj (contextPath, label))) | None -> ()) + | Pexp_let (recFlag, bindings, e) -> + let oldScope = !scope in + if recFlag = Recursive then bindings |> List.iter scopeValueBinding; + bindings |> List.iter (fun vb -> iterator.value_binding iterator vb); + if recFlag = Nonrecursive then bindings |> List.iter scopeValueBinding; + iterator.expr iterator e; + processed := true; + scope := oldScope | _ -> ()); - Ast_iterator.default_iterator.expr iterator expr + if not !processed then Ast_iterator.default_iterator.expr iterator expr in let typ (iterator : Ast_iterator.iterator) (core_type : Parsetree.core_type) = if core_type.ptyp_loc |> Loc.hasPos ~pos:posNoWhite then ( @@ -603,25 +632,6 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = !result) else None -let getOpens ~rawOpens ~package ~env = - Log.log - ("Opens folkz > " - ^ string_of_int (List.length rawOpens) - ^ " " - ^ String.concat " ... " (rawOpens |> List.map pathToString)); - let packageOpens = "Pervasives" :: package.opens in - Log.log ("Package opens " ^ String.concat " " packageOpens); - let resolvedOpens = NewCompletions.resolveRawOpens ~env ~rawOpens ~package in - Log.log - ("Opens nows " - ^ string_of_int (List.length resolvedOpens) - ^ " " - ^ String.concat " " - (resolvedOpens - |> List.map (fun (e : QueryEnv.t) -> Uri2.toString e.file.uri))); - (* Last open takes priority *) - List.rev resolvedOpens - let completion ~debug ~path ~pos ~currentFile = let result = let textOpt = Files.readFile currentFile in @@ -637,16 +647,14 @@ let completion ~debug ~path ~pos ~currentFile = if debug then Printf.printf "Completable: %s\n" (PartialParser.completableToString completable); - let rawOpens = Scope.getRawOpens scope in (* Only perform expensive ast operations if there are completables *) match Cmt.fromPath ~path with | None -> [] | Some full -> let env = QueryEnv.fromFile full.file in let package = full.package in - let opens = getOpens ~rawOpens ~package ~env in - NewCompletions.computeCompletions ~completable ~package ~pos - ~rawOpens ~opens ~env) + NewCompletions.computeCompletions ~completable ~package ~pos ~scope + ~env) in completionItems in diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index e34d74c31..76a32ab6f 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -649,14 +649,6 @@ let detail name (kind : Completion.kind) = | Field ({typ}, s) -> name ^ ": " ^ (typ |> Shared.typeToString) ^ "\n\n" ^ s | Constructor (c, s) -> showConstructor c ^ "\n\n" ^ s -let localCompletions ~pos ~(env : QueryEnv.t) ~prefix ~exact = - Log.log "---------------- LOCAL VAL"; - completionForDeclaredModules ~pos ~env ~prefix ~exact - @ completionForConstructors ~env ~prefix ~exact - @ completionForDeclaredValues ~pos ~env ~prefix ~exact - @ completionForDeclaredTypes ~pos ~env ~prefix ~exact - @ completionForFields ~env ~prefix ~exact - let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = Log.log (" - Completing in " ^ Uri2.toString env.file.uri); completionForExportedModules ~env ~prefix ~exact @@ -665,6 +657,36 @@ let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = @ completionForExportedTypes ~env ~prefix ~exact @ completionForFields ~env ~prefix ~exact +let findLocalCompletionsPlusOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens + = + Log.log "---------------- LOCAL VAL"; + let completions = + completionForDeclaredModules ~pos ~env ~prefix ~exact + @ completionForConstructors ~env ~prefix ~exact + @ completionForDeclaredValues ~pos ~env ~prefix ~exact + @ completionForDeclaredTypes ~pos ~env ~prefix ~exact + @ completionForFields ~env ~prefix ~exact + in + let namesUsed = Hashtbl.create 10 in + let valuesFromOpens = + opens + |> List.fold_left + (fun results env -> + let completionsFromThisOpen = allCompletions ~env ~prefix ~exact in + List.filter + (fun (declared : Completion.t) -> + if Hashtbl.mem namesUsed declared.name then + (* shadowing from opens *) + false + else ( + Hashtbl.add namesUsed declared.name true; + true)) + completionsFromThisOpen + @ results) + [] + in + completions @ valuesFromOpens + (* TODO filter out things that are defined after the current position *) let resolveRawOpens ~env ~rawOpens ~package = (* TODO Stdlib instead of Pervasives *) @@ -747,37 +769,24 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact match path with | [] -> [] | [prefix] -> - let locallyDefinedValues = localCompletions ~pos ~env ~prefix ~exact in - let alreadyUsedIdentifiers = Hashtbl.create 10 in - let valuesFromOpens = - opens - |> List.fold_left - (fun results env -> - let completionsFromThisOpen = allCompletions ~env ~prefix ~exact in - List.filter - (fun (declared : Completion.t) -> - if Hashtbl.mem alreadyUsedIdentifiers declared.name then - (* shadowing from opens *) - false - else ( - Hashtbl.add alreadyUsedIdentifiers declared.name true; - true)) - completionsFromThisOpen - @ results) - [] + let localCompletionsPlusOpens = + findLocalCompletionsPlusOpens ~pos ~env ~prefix ~exact ~opens in - (* TODO complete the namespaced name too *) - let localModuleNames = + let fileModules = allFiles |> FileSet.elements |> Utils.filterMap (fun name -> - if checkName name ~prefix ~exact && not (String.contains name '-') + if + checkName name ~prefix ~exact + && not + (* TODO complete the namespaced name too *) + (String.contains name '-') then Some (Completion.create ~name ~env ~kind:(Completion.FileModule name)) else None) in - locallyDefinedValues @ valuesFromOpens @ localModuleNames + localCompletionsPlusOpens @ fileModules |> postProcess ~pos ~exact ~completionContext | _ -> ( Log.log ("Path " ^ pathToString path); @@ -986,8 +995,29 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos | None -> []) | None -> []) -let processCompletable ~package ~rawOpens ~opens ~env ~pos +let getOpens ~rawOpens ~package ~env = + Log.log + ("Opens folkz > " + ^ string_of_int (List.length rawOpens) + ^ " " + ^ String.concat " ... " (rawOpens |> List.map pathToString)); + let packageOpens = "Pervasives" :: package.opens in + Log.log ("Package opens " ^ String.concat " " packageOpens); + let resolvedOpens = resolveRawOpens ~env ~rawOpens ~package in + Log.log + ("Opens nows " + ^ string_of_int (List.length resolvedOpens) + ^ " " + ^ String.concat " " + (resolvedOpens + |> List.map (fun (e : QueryEnv.t) -> Uri2.toString e.file.uri))); + (* Last open takes priority *) + List.rev resolvedOpens + +let processCompletable ~package ~scope ~env ~pos (completable : PartialParser.completable) = + let rawOpens = Scope.getRawOpens scope in + let opens = getOpens ~rawOpens ~package ~env in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in let findTypeOfValue path = path @@ -1144,5 +1174,5 @@ let processCompletable ~package ~rawOpens ~opens ~env ~pos |> List.map mkLabel let computeCompletions ~(completable : PartialParser.completable) ~package ~pos - ~rawOpens ~opens ~env = - completable |> processCompletable ~package ~rawOpens ~opens ~env ~pos + ~scope ~env = + completable |> processCompletable ~package ~scope ~env ~pos diff --git a/analysis/src/Scope.ml b/analysis/src/Scope.ml index a170639e3..466d96452 100644 --- a/analysis/src/Scope.ml +++ b/analysis/src/Scope.ml @@ -1,8 +1,16 @@ -type item = Module of string list | Value of unit +type item = Module of string list | Value of string * Location.t type t = item list +let itemToString item = + let str s = if s = "" then "\"\"" else s in + let list l = "[" ^ (l |> List.map str |> String.concat ", ") ^ "]" in + match item with + | Module sl -> "Module " ^ list sl + | Value (s, loc) -> "Value " ^ s ^ " " ^ Loc.toString loc + let create () : t = [] -let addModule id t = Module (Utils.flattenLongIdent id @ ["place holder"]) :: t +let addModule id x = Module (Utils.flattenLongIdent id @ ["place holder"]) :: x +let addValue name loc x = Value (name, loc) :: x let getRawOpens x = x |> Utils.filterMap (function Module path -> Some path | _ -> None) From 03c5f27ae5140d87c61205cf48a7c97eecaf6c7d Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 06:38:52 +0200 Subject: [PATCH 097/135] rename --- analysis/src/NewCompletions.ml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 76a32ab6f..2f7bc47fd 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -522,17 +522,17 @@ let completionForDeclareds ~pos ~iter ~stamps ~prefix ~exact ~env stamps; !res -let completionForDeclaredModules ~pos ~env ~prefix ~exact = +let localCompletionsForModules ~pos ~env ~prefix ~exact = completionForDeclareds ~env ~pos ~iter:Stamps.iterModules ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Module m) -let completionForDeclaredValues ~pos ~env ~prefix ~exact = +let localCompletionsForValues ~pos ~env ~prefix ~exact = completionForDeclareds ~env ~pos ~iter:Stamps.iterValues ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Value m) -let completionForDeclaredTypes ~pos ~env ~prefix ~exact = +let localCompletionsForTypes ~pos ~env ~prefix ~exact = completionForDeclareds ~env ~pos ~iter:Stamps.iterTypes ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Type m) @@ -572,7 +572,7 @@ let completionForExportedTypes ~env ~prefix ~exact = (Stamps.findType env.file.stamps) ~prefix ~exact ~env (fun t -> Completion.Type t) -let completionForConstructors ~(env : QueryEnv.t) ~prefix ~exact = +let localCompletionsForConstructors ~(env : QueryEnv.t) ~prefix ~exact = let res = ref [] in Exported.iter env.exported Exported.Type (fun _name stamp -> match Stamps.findType env.file.stamps stamp with @@ -652,7 +652,7 @@ let detail name (kind : Completion.kind) = let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = Log.log (" - Completing in " ^ Uri2.toString env.file.uri); completionForExportedModules ~env ~prefix ~exact - @ completionForConstructors ~env ~prefix ~exact + @ localCompletionsForConstructors ~env ~prefix ~exact @ completionForExportedValues ~env ~prefix ~exact @ completionForExportedTypes ~env ~prefix ~exact @ completionForFields ~env ~prefix ~exact @@ -661,10 +661,10 @@ let findLocalCompletionsPlusOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens = Log.log "---------------- LOCAL VAL"; let completions = - completionForDeclaredModules ~pos ~env ~prefix ~exact - @ completionForConstructors ~env ~prefix ~exact - @ completionForDeclaredValues ~pos ~env ~prefix ~exact - @ completionForDeclaredTypes ~pos ~env ~prefix ~exact + localCompletionsForModules ~pos ~env ~prefix ~exact + @ localCompletionsForConstructors ~env ~prefix ~exact + @ localCompletionsForValues ~pos ~env ~prefix ~exact + @ localCompletionsForTypes ~pos ~env ~prefix ~exact @ completionForFields ~env ~prefix ~exact in let namesUsed = Hashtbl.create 10 in From 67479f17dfccd2a26f2c667572548f9d83616396 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 06:43:52 +0200 Subject: [PATCH 098/135] prepare insertion point for new local value completion. --- analysis/src/NewCompletions.ml | 37 ++++++++++++----------- analysis/tests/src/expected/Debug.res.txt | 2 +- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 2f7bc47fd..2fe6e768c 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -658,8 +658,8 @@ let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = @ completionForFields ~env ~prefix ~exact let findLocalCompletionsPlusOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens - = - Log.log "---------------- LOCAL VAL"; + ~(completionContext : PartialParser.completionContext) = + Log.log "findLocalCompletionsPlusOpens"; let completions = localCompletionsForModules ~pos ~env ~prefix ~exact @ localCompletionsForConstructors ~env ~prefix ~exact @@ -669,21 +669,23 @@ let findLocalCompletionsPlusOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens in let namesUsed = Hashtbl.create 10 in let valuesFromOpens = - opens - |> List.fold_left - (fun results env -> - let completionsFromThisOpen = allCompletions ~env ~prefix ~exact in - List.filter - (fun (declared : Completion.t) -> - if Hashtbl.mem namesUsed declared.name then - (* shadowing from opens *) - false - else ( - Hashtbl.add namesUsed declared.name true; - true)) - completionsFromThisOpen - @ results) - [] + if completionContext = Value then + opens + |> List.fold_left + (fun results env -> + let completionsFromThisOpen = allCompletions ~env ~prefix ~exact in + List.filter + (fun (declared : Completion.t) -> + if Hashtbl.mem namesUsed declared.name then + (* shadowing from opens *) + false + else ( + Hashtbl.add namesUsed declared.name true; + true)) + completionsFromThisOpen + @ results) + [] + else [] in completions @ valuesFromOpens @@ -771,6 +773,7 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact | [prefix] -> let localCompletionsPlusOpens = findLocalCompletionsPlusOpens ~pos ~env ~prefix ~exact ~opens + ~completionContext in let fileModules = allFiles |> FileSet.elements diff --git a/analysis/tests/src/expected/Debug.res.txt b/analysis/tests/src/expected/Debug.res.txt index c2e66b754..7880d69bf 100644 --- a/analysis/tests/src/expected/Debug.res.txt +++ b/analysis/tests/src/expected/Debug.res.txt @@ -74,7 +74,7 @@ resolvePath path:Js.place holder fileForModule Impl cmt:tests/node_modules/rescript/lib/ocaml/js.cmt res:tests/node_modules/rescript/lib/ocaml/js.ml resolvePath path:place holder Opens nows 2 pervasives.mli js.ml ----------------- LOCAL VAL +findLocalCompletionsPlusOpens - Completing in js.ml - Completing in pervasives.mli [{ From aadd9e11dca8ca971ac3eaa937ae7610c35d994a Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 08:10:25 +0200 Subject: [PATCH 099/135] local constructor infra --- analysis/src/NewCompletions.ml | 37 ++++++++++++-------- analysis/src/ProcessCmt.ml | 63 ++++++++++++++++++++++++---------- analysis/src/SharedTypes.ml | 7 ++++ 3 files changed, 74 insertions(+), 33 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 2fe6e768c..0bd57e0b5 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -536,6 +536,12 @@ let localCompletionsForTypes ~pos ~env ~prefix ~exact = completionForDeclareds ~env ~pos ~iter:Stamps.iterTypes ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Type m) +let localCompletionsForConstructors ~pos ~env ~prefix ~exact = + completionForDeclareds ~env ~pos ~iter:Stamps.iterConstructors + ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun c -> + Completion.Constructor + (c, snd c.typeDecl |> Shared.declToString (fst c.typeDecl))) + let completionForExporteds iterExported getDeclared ~prefix ~exact ~env transformContents = let res = ref [] in @@ -572,7 +578,7 @@ let completionForExportedTypes ~env ~prefix ~exact = (Stamps.findType env.file.stamps) ~prefix ~exact ~env (fun t -> Completion.Type t) -let localCompletionsForConstructors ~(env : QueryEnv.t) ~prefix ~exact = +let completionsForConstructors ~(env : QueryEnv.t) ~prefix ~exact = let res = ref [] in Exported.iter env.exported Exported.Type (fun _name stamp -> match Stamps.findType env.file.stamps stamp with @@ -652,7 +658,7 @@ let detail name (kind : Completion.kind) = let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = Log.log (" - Completing in " ^ Uri2.toString env.file.uri); completionForExportedModules ~env ~prefix ~exact - @ localCompletionsForConstructors ~env ~prefix ~exact + @ completionsForConstructors ~env ~prefix ~exact @ completionForExportedValues ~env ~prefix ~exact @ completionForExportedTypes ~env ~prefix ~exact @ completionForFields ~env ~prefix ~exact @@ -660,16 +666,13 @@ let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = let findLocalCompletionsPlusOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens ~(completionContext : PartialParser.completionContext) = Log.log "findLocalCompletionsPlusOpens"; - let completions = - localCompletionsForModules ~pos ~env ~prefix ~exact - @ localCompletionsForConstructors ~env ~prefix ~exact - @ localCompletionsForValues ~pos ~env ~prefix ~exact - @ localCompletionsForTypes ~pos ~env ~prefix ~exact - @ completionForFields ~env ~prefix ~exact - in - let namesUsed = Hashtbl.create 10 in - let valuesFromOpens = - if completionContext = Value then + if completionContext = Value then + let completions = + localCompletionsForValues ~pos ~env ~prefix ~exact + @ localCompletionsForConstructors ~pos ~env ~prefix ~exact + in + let namesUsed = Hashtbl.create 10 in + let valuesFromOpens = opens |> List.fold_left (fun results env -> @@ -685,9 +688,13 @@ let findLocalCompletionsPlusOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens completionsFromThisOpen @ results) [] - else [] - in - completions @ valuesFromOpens + in + completions @ valuesFromOpens + else + localCompletionsForModules ~pos ~env ~prefix ~exact + @ completionsForConstructors ~env ~prefix ~exact + @ localCompletionsForTypes ~pos ~env ~prefix ~exact + @ completionForFields ~env ~prefix ~exact (* TODO filter out things that are defined after the current position *) let resolveRawOpens ~env ~rawOpens ~package = diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index 9d3d4a5de..05a192bd0 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -55,6 +55,7 @@ let rec forTypeSignatureItem ~env ~(exported : Exported.t) ({type_loc; type_kind; type_manifest; type_attributes} as decl), recStatus ) -> let declared = + let name = Location.mknoloc (Ident.name ident) in addItem ~extent:type_loc ~item: { @@ -89,6 +90,7 @@ let rec forTypeSignatureItem ~env ~(exported : Exported.t) (* TODO(406): constructor record args support *) | Cstr_record _ -> []); res = cd_res; + typeDecl = (name, decl); } in let declared = @@ -117,8 +119,7 @@ let rec forTypeSignatureItem ~env ~(exported : Exported.t) typ = ld_type; }))); } - ~name:(Location.mknoloc (Ident.name ident)) - ~stamp:(Ident.binding_time ident) ~env type_attributes + ~name ~stamp:(Ident.binding_time ident) ~env type_attributes (Exported.add exported Exported.Type) Stamps.addType in @@ -187,23 +188,49 @@ let forTypeDeclaration ~env ~(exported : Exported.t) | Ttype_variant constructors -> Variant (constructors - |> List.map (fun {cd_id; cd_name = cname; cd_args; cd_res} -> - let stamp = Ident.binding_time cd_id in + |> List.map + (fun { - Constructor.stamp; - cname; - args = - (match cd_args with - | Cstr_tuple args -> - args - |> List.map (fun t -> (t.ctyp_type, t.ctyp_loc)) - (* TODO(406) *) - | Cstr_record _ -> []); - res = - (match cd_res with - | None -> None - | Some t -> Some t.ctyp_type); - })) + cd_id; + cd_name = cname; + cd_args; + cd_res; + cd_attributes; + cd_loc; + } + -> + let stamp = Ident.binding_time cd_id in + let item = + { + Constructor.stamp; + cname; + args = + (match cd_args with + | Cstr_tuple args -> + args + |> List.map (fun t -> (t.ctyp_type, t.ctyp_loc)) + (* TODO(406) *) + | Cstr_record _ -> []); + res = + (match cd_res with + | None -> None + | Some t -> Some t.ctyp_type); + typeDecl = (name.txt, typ_type); + } + in + let declared = + ProcessAttributes.newDeclared ~item ~extent:cd_loc + ~scope: + { + Location.loc_start = typ_loc.Location.loc_end; + loc_end = env.scope.loc_end; + loc_ghost = false; + } + ~name:cname ~stamp ~modulePath:env.modulePath true + cd_attributes + in + Stamps.addConstructor env.stamps stamp declared; + item)) | Ttype_record fields -> Record (fields diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 24cfedf3c..ce16276ba 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -12,6 +12,7 @@ module Constructor = struct cname : string Location.loc; args : (Types.type_expr * Location.t) list; res : Types.type_expr option; + typeDecl : string * Types.type_declaration; } end @@ -117,6 +118,7 @@ module Stamps : sig val findType : t -> int -> Type.t Declared.t option val findValue : t -> int -> Types.type_expr Declared.t option val init : unit -> t + val iterConstructors : (int -> Constructor.t Declared.t -> unit) -> t -> unit val iterModules : (int -> Module.t Declared.t -> unit) -> t -> unit val iterTypes : (int -> Type.t Declared.t -> unit) -> t -> unit val iterValues : (int -> Types.type_expr Declared.t -> unit) -> t -> unit @@ -173,6 +175,11 @@ end = struct Hashtbl.iter (fun stamp d -> match d with KValue d -> f stamp d | _ -> ()) stamps + + let iterConstructors f stamps = + Hashtbl.iter + (fun stamp d -> match d with KConstructor d -> f stamp d | _ -> ()) + stamps end module File = struct From dc4c16ec4231ea68d41cbf2751737c318e35d8e6 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 08:24:51 +0200 Subject: [PATCH 100/135] There's no local completion for fields. --- analysis/src/NewCompletions.ml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 0bd57e0b5..c55e971a4 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -613,12 +613,6 @@ let completionForFields ~(env : QueryEnv.t) ~prefix ~exact = | _ -> ()); !res -(* Note: This is a hack. It will be wrong some times if you have a local thing - that overrides an open. - - Maybe the way to fix it is to make note of what things in an open override - locally defined things... -*) let getEnvWithOpens ~pos ~(env : QueryEnv.t) ~package ~(opens : QueryEnv.t list) (path : string list) = match ProcessCmt.resolveFromStamps ~env ~path ~package ~pos with @@ -692,9 +686,7 @@ let findLocalCompletionsPlusOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens completions @ valuesFromOpens else localCompletionsForModules ~pos ~env ~prefix ~exact - @ completionsForConstructors ~env ~prefix ~exact @ localCompletionsForTypes ~pos ~env ~prefix ~exact - @ completionForFields ~env ~prefix ~exact (* TODO filter out things that are defined after the current position *) let resolveRawOpens ~env ~rawOpens ~package = From bb5d6e2d09e0ed574e00c8ada8f6da16ffef3120 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 08:25:30 +0200 Subject: [PATCH 101/135] rename --- analysis/src/NewCompletions.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index c55e971a4..658a804be 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -596,7 +596,7 @@ let completionsForConstructors ~(env : QueryEnv.t) ~prefix ~exact = | _ -> ()); !res -let completionForFields ~(env : QueryEnv.t) ~prefix ~exact = +let completionForExportedFields ~(env : QueryEnv.t) ~prefix ~exact = let res = ref [] in Exported.iter env.exported Exported.Type (fun _name stamp -> match Stamps.findType env.file.stamps stamp with @@ -655,7 +655,7 @@ let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = @ completionsForConstructors ~env ~prefix ~exact @ completionForExportedValues ~env ~prefix ~exact @ completionForExportedTypes ~env ~prefix ~exact - @ completionForFields ~env ~prefix ~exact + @ completionForExportedFields ~env ~prefix ~exact let findLocalCompletionsPlusOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens ~(completionContext : PartialParser.completionContext) = From d7c402ea50c052adc9c0ba0b1df98f8fd8470dc2 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 08:28:42 +0200 Subject: [PATCH 102/135] tweak debug print --- analysis/src/NewCompletions.ml | 14 +++++++++----- analysis/tests/src/expected/Debug.res.txt | 6 +++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 658a804be..8450de838 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -649,8 +649,8 @@ let detail name (kind : Completion.kind) = | Field ({typ}, s) -> name ^ ": " ^ (typ |> Shared.typeToString) ^ "\n\n" ^ s | Constructor (c, s) -> showConstructor c ^ "\n\n" ^ s -let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = - Log.log (" - Completing in " ^ Uri2.toString env.file.uri); +let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact = + Log.log ("findAllCompletions uri:" ^ Uri2.toString env.file.uri); completionForExportedModules ~env ~prefix ~exact @ completionsForConstructors ~env ~prefix ~exact @ completionForExportedValues ~env ~prefix ~exact @@ -659,7 +659,9 @@ let allCompletions ~(env : QueryEnv.t) ~prefix ~exact = let findLocalCompletionsPlusOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens ~(completionContext : PartialParser.completionContext) = - Log.log "findLocalCompletionsPlusOpens"; + Log.log + ("findLocalCompletionsPlusOpens uri:" ^ Uri2.toString env.file.uri ^ " pos:" + ^ Pos.toString pos); if completionContext = Value then let completions = localCompletionsForValues ~pos ~env ~prefix ~exact @@ -670,7 +672,9 @@ let findLocalCompletionsPlusOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens opens |> List.fold_left (fun results env -> - let completionsFromThisOpen = allCompletions ~env ~prefix ~exact in + let completionsFromThisOpen = + findAllCompletions ~env ~prefix ~exact + in List.filter (fun (declared : Completion.t) -> if Hashtbl.mem namesUsed declared.name then @@ -795,7 +799,7 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact match getEnvWithOpens ~pos ~env ~package ~opens path with | Some (env, prefix) -> Log.log "Got the env"; - allCompletions ~env ~prefix ~exact + findAllCompletions ~env ~prefix ~exact |> postProcess ~pos ~exact ~completionContext | None -> []) diff --git a/analysis/tests/src/expected/Debug.res.txt b/analysis/tests/src/expected/Debug.res.txt index 7880d69bf..c61a13d6f 100644 --- a/analysis/tests/src/expected/Debug.res.txt +++ b/analysis/tests/src/expected/Debug.res.txt @@ -74,9 +74,9 @@ resolvePath path:Js.place holder fileForModule Impl cmt:tests/node_modules/rescript/lib/ocaml/js.cmt res:tests/node_modules/rescript/lib/ocaml/js.ml resolvePath path:place holder Opens nows 2 pervasives.mli js.ml -findLocalCompletionsPlusOpens - - Completing in js.ml - - Completing in pervasives.mli +findLocalCompletionsPlusOpens uri:Debug.res pos:13:4 +findAllCompletions uri:js.ml +findAllCompletions uri:pervasives.mli [{ "label": "eqNullable", "kind": 12, From 1d9dab98e122122c7415add1cf35fe172f59de1a Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 08:30:11 +0200 Subject: [PATCH 103/135] rename --- analysis/src/NewCompletions.ml | 6 +++--- analysis/tests/src/expected/Debug.res.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 8450de838..832a3a5fc 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -657,10 +657,10 @@ let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact = @ completionForExportedTypes ~env ~prefix ~exact @ completionForExportedFields ~env ~prefix ~exact -let findLocalCompletionsPlusOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens +let findLocalCompletionsWithOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens ~(completionContext : PartialParser.completionContext) = Log.log - ("findLocalCompletionsPlusOpens uri:" ^ Uri2.toString env.file.uri ^ " pos:" + ("findLocalCompletionsWithOpens uri:" ^ Uri2.toString env.file.uri ^ " pos:" ^ Pos.toString pos); if completionContext = Value then let completions = @@ -775,7 +775,7 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact | [] -> [] | [prefix] -> let localCompletionsPlusOpens = - findLocalCompletionsPlusOpens ~pos ~env ~prefix ~exact ~opens + findLocalCompletionsWithOpens ~pos ~env ~prefix ~exact ~opens ~completionContext in let fileModules = diff --git a/analysis/tests/src/expected/Debug.res.txt b/analysis/tests/src/expected/Debug.res.txt index c61a13d6f..980e47212 100644 --- a/analysis/tests/src/expected/Debug.res.txt +++ b/analysis/tests/src/expected/Debug.res.txt @@ -74,7 +74,7 @@ resolvePath path:Js.place holder fileForModule Impl cmt:tests/node_modules/rescript/lib/ocaml/js.cmt res:tests/node_modules/rescript/lib/ocaml/js.ml resolvePath path:place holder Opens nows 2 pervasives.mli js.ml -findLocalCompletionsPlusOpens uri:Debug.res pos:13:4 +findLocalCompletionsWithOpens uri:Debug.res pos:13:4 findAllCompletions uri:js.ml findAllCompletions uri:pervasives.mli [{ From f1210f3b2a1ec852d7346af0e782b9b74bfc8497 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 08:47:03 +0200 Subject: [PATCH 104/135] Add type declarations and constructors to the scope. --- analysis/src/Commands.ml | 43 +++++++++++++++++++++++++++++++++++----- analysis/src/Scope.ml | 22 +++++++++++++++----- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 9a1f48f0f..4b6d26573 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -281,15 +281,34 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = in let setResult x = setResultOpt (Some x) in let scopeValueDescription (vd : Parsetree.value_description) = - scope := !scope |> Scope.addValue vd.pval_name.txt vd.pval_loc + scope := !scope |> Scope.addValue ~name:vd.pval_name.txt ~loc:vd.pval_loc in let scopeValueBinding (vb : Parsetree.value_binding) = match vb.pvb_pat.ppat_desc with | Ppat_var {txt; loc} | Ppat_constraint ({ppat_desc = Ppat_var {txt; loc}}, _) -> - scope := !scope |> Scope.addValue txt loc + scope := !scope |> Scope.addValue ~name:txt ~loc | _ -> () in + let scopeTypeKind (tk : Parsetree.type_kind) = + match tk with + | Ptype_variant constrDecls -> + constrDecls + |> List.iter (fun (cd : Parsetree.constructor_declaration) -> + scope := + !scope + |> Scope.addConstructor ~name:cd.pcd_name.txt ~loc:cd.pcd_loc) + | Ptype_record labelDecls -> + labelDecls + |> List.iter (fun (ld : Parsetree.label_declaration) -> + scope := + !scope |> Scope.addField ~name:ld.pld_name.txt ~loc:ld.pld_loc) + | _ -> () + in + let scopeTypeDeclaration (td : Parsetree.type_declaration) = + scope := !scope |> Scope.addType ~name:td.ptype_name.txt ~loc:td.ptype_loc; + scopeTypeKind td.ptype_kind + in let structure (iterator : Ast_iterator.iterator) (structure : Parsetree.structure) = @@ -301,13 +320,19 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (item : Parsetree.structure_item) = let processed = ref false in (match item.pstr_desc with - | Pstr_open {popen_lid} -> scope := !scope |> Scope.addModule popen_lid.txt + | Pstr_open {popen_lid} -> + scope := !scope |> Scope.addOpen ~lid:popen_lid.txt | Pstr_primitive vd -> scopeValueDescription vd | Pstr_value (recFlag, bindings) -> if recFlag = Recursive then bindings |> List.iter scopeValueBinding; bindings |> List.iter (fun vb -> iterator.value_binding iterator vb); if recFlag = Nonrecursive then bindings |> List.iter scopeValueBinding; processed := true + | Pstr_type (recFlag, decls) -> + if recFlag = Recursive then decls |> List.iter scopeTypeDeclaration; + decls |> List.iter (fun td -> iterator.type_declaration iterator td); + if recFlag = Nonrecursive then decls |> List.iter scopeTypeDeclaration; + processed := true | _ -> ()); if not !processed then Ast_iterator.default_iterator.structure_item iterator item @@ -320,11 +345,19 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = in let signature_item (iterator : Ast_iterator.iterator) (item : Parsetree.signature_item) = + let processed = ref false in (match item.psig_desc with - | Psig_open {popen_lid} -> scope := !scope |> Scope.addModule popen_lid.txt + | Psig_open {popen_lid} -> + scope := !scope |> Scope.addOpen ~lid:popen_lid.txt | Psig_value vd -> scopeValueDescription vd + | Psig_type (recFlag, decls) -> + if recFlag = Recursive then decls |> List.iter scopeTypeDeclaration; + decls |> List.iter (fun td -> iterator.type_declaration iterator td); + if recFlag = Nonrecursive then decls |> List.iter scopeTypeDeclaration; + processed := true | _ -> ()); - Ast_iterator.default_iterator.signature_item iterator item + if not !processed then + Ast_iterator.default_iterator.signature_item iterator item in let attribute (iterator : Ast_iterator.iterator) ((id, payload) : Parsetree.attribute) = diff --git a/analysis/src/Scope.ml b/analysis/src/Scope.ml index 466d96452..9069fb4b0 100644 --- a/analysis/src/Scope.ml +++ b/analysis/src/Scope.ml @@ -1,16 +1,28 @@ -type item = Module of string list | Value of string * Location.t +type item = + | Constructor of string * Location.t + | Field of string * Location.t + | Open of string list + | Type of string * Location.t + | Value of string * Location.t + type t = item list let itemToString item = let str s = if s = "" then "\"\"" else s in let list l = "[" ^ (l |> List.map str |> String.concat ", ") ^ "]" in match item with - | Module sl -> "Module " ^ list sl + | Constructor (s, loc) -> "Constructor " ^ s ^ " " ^ Loc.toString loc + | Field (s, loc) -> "Field " ^ s ^ " " ^ Loc.toString loc + | Open sl -> "Module " ^ list sl | Value (s, loc) -> "Value " ^ s ^ " " ^ Loc.toString loc + | Type (s, loc) -> "Type " ^ s ^ " " ^ Loc.toString loc let create () : t = [] -let addModule id x = Module (Utils.flattenLongIdent id @ ["place holder"]) :: x -let addValue name loc x = Value (name, loc) :: x +let addConstructor ~name ~loc x = Constructor (name, loc) :: x +let addField ~name ~loc x = Field (name, loc) :: x +let addOpen ~lid x = Open (Utils.flattenLongIdent lid @ ["place holder"]) :: x +let addValue ~name ~loc x = Value (name, loc) :: x +let addType ~name ~loc x = Type (name, loc) :: x let getRawOpens x = - x |> Utils.filterMap (function Module path -> Some path | _ -> None) + x |> Utils.filterMap (function Open path -> Some path | _ -> None) From 02e671bbb5ba2a675cfe2ca78dfc2a7968d9535a Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 08:57:17 +0200 Subject: [PATCH 105/135] refactor --- analysis/src/NewCompletions.ml | 163 +++++++++++++++++---------------- 1 file changed, 83 insertions(+), 80 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 832a3a5fc..8445d9b43 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -499,49 +499,6 @@ let resolveOpens ~env ~previous opens ~package = let checkName name ~prefix ~exact = if exact then name = prefix else Utils.startsWith name prefix -let completionForDeclareds ~pos ~iter ~stamps ~prefix ~exact ~env - transformContents = - (* Log.log("completion for declares " ++ prefix); *) - let res = ref [] in - iter - (fun _stamp (declared : _ Declared.t) -> - if - checkName declared.name.txt ~prefix ~exact - && Utils.locationContainsFuzzy declared.scopeLoc pos - then - res := - { - (Completion.create ~name:declared.name.txt ~env - ~kind:(transformContents declared.item)) - with - extentLoc = declared.extentLoc; - deprecated = declared.deprecated; - docstring = declared.docstring; - } - :: !res) - stamps; - !res - -let localCompletionsForModules ~pos ~env ~prefix ~exact = - completionForDeclareds ~env ~pos ~iter:Stamps.iterModules - ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> - Completion.Module m) - -let localCompletionsForValues ~pos ~env ~prefix ~exact = - completionForDeclareds ~env ~pos ~iter:Stamps.iterValues - ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> - Completion.Value m) - -let localCompletionsForTypes ~pos ~env ~prefix ~exact = - completionForDeclareds ~env ~pos ~iter:Stamps.iterTypes - ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Type m) - -let localCompletionsForConstructors ~pos ~env ~prefix ~exact = - completionForDeclareds ~env ~pos ~iter:Stamps.iterConstructors - ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun c -> - Completion.Constructor - (c, snd c.typeDecl |> Shared.declToString (fst c.typeDecl))) - let completionForExporteds iterExported getDeclared ~prefix ~exact ~env transformContents = let res = ref [] in @@ -657,37 +614,83 @@ let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact = @ completionForExportedTypes ~env ~prefix ~exact @ completionForExportedFields ~env ~prefix ~exact +let completionsForDeclareds ~pos ~iter ~stamps ~prefix ~exact ~env + transformContents = + (* Log.log("completion for declares " ++ prefix); *) + let res = ref [] in + iter + (fun _stamp (declared : _ Declared.t) -> + if + checkName declared.name.txt ~prefix ~exact + && Utils.locationContainsFuzzy declared.scopeLoc pos + then + res := + { + (Completion.create ~name:declared.name.txt ~env + ~kind:(transformContents declared.item)) + with + extentLoc = declared.extentLoc; + deprecated = declared.deprecated; + docstring = declared.docstring; + } + :: !res) + stamps; + !res + +let localCompletionsForModules ~pos ~env ~prefix ~exact = + completionsForDeclareds ~env ~pos ~iter:Stamps.iterModules + ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> + Completion.Module m) + +let localCompletionsForValues ~pos ~env ~prefix ~exact = + completionsForDeclareds ~env ~pos ~iter:Stamps.iterValues + ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> + Completion.Value m) + +let localCompletionsForTypes ~pos ~env ~prefix ~exact = + completionsForDeclareds ~env ~pos ~iter:Stamps.iterTypes + ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Type m) + +let localCompletionsForConstructors ~pos ~env ~prefix ~exact = + completionsForDeclareds ~env ~pos ~iter:Stamps.iterConstructors + ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun c -> + Completion.Constructor + (c, snd c.typeDecl |> Shared.declToString (fst c.typeDecl))) + +let findLocalCompletionsForValues ~pos ~env ~prefix ~exact ~opens ~scope = + let completions = + localCompletionsForValues ~pos ~env ~prefix ~exact + @ localCompletionsForConstructors ~pos ~env ~prefix ~exact + in + let namesUsed = Hashtbl.create 10 in + let valuesFromOpens = + opens + |> List.fold_left + (fun results env -> + let completionsFromThisOpen = + findAllCompletions ~env ~prefix ~exact + in + List.filter + (fun (completion : Completion.t) -> + if Hashtbl.mem namesUsed completion.name then + (* shadowing from opens *) + false + else ( + Hashtbl.add namesUsed completion.name true; + true)) + completionsFromThisOpen + @ results) + [] + in + completions @ valuesFromOpens + let findLocalCompletionsWithOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens - ~(completionContext : PartialParser.completionContext) = + ~scope ~(completionContext : PartialParser.completionContext) = Log.log ("findLocalCompletionsWithOpens uri:" ^ Uri2.toString env.file.uri ^ " pos:" ^ Pos.toString pos); if completionContext = Value then - let completions = - localCompletionsForValues ~pos ~env ~prefix ~exact - @ localCompletionsForConstructors ~pos ~env ~prefix ~exact - in - let namesUsed = Hashtbl.create 10 in - let valuesFromOpens = - opens - |> List.fold_left - (fun results env -> - let completionsFromThisOpen = - findAllCompletions ~env ~prefix ~exact - in - List.filter - (fun (declared : Completion.t) -> - if Hashtbl.mem namesUsed declared.name then - (* shadowing from opens *) - false - else ( - Hashtbl.add namesUsed declared.name true; - true)) - completionsFromThisOpen - @ results) - [] - in - completions @ valuesFromOpens + findLocalCompletionsForValues ~pos ~env ~prefix ~exact ~opens ~scope else localCompletionsForModules ~pos ~env ~prefix ~exact @ localCompletionsForTypes ~pos ~env ~prefix ~exact @@ -769,13 +772,13 @@ let postProcess ~pos ~exact ~completionContext completions = |> List.filter (filterCompletionKind ~completionContext) |> prioritize ~exact ~pos -let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact +let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope ~completionContext ~env path = match path with | [] -> [] | [prefix] -> let localCompletionsPlusOpens = - findLocalCompletionsWithOpens ~pos ~env ~prefix ~exact ~opens + findLocalCompletionsWithOpens ~pos ~env ~prefix ~exact ~opens ~scope ~completionContext in let fileModules = @@ -835,7 +838,7 @@ let completionsGetTypeEnv = function | _ -> None let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact (contextPath : PartialParser.contextPath) = + ~env ~exact ~scope (contextPath : PartialParser.contextPath) = match contextPath with | CPString -> [ @@ -854,17 +857,17 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos | CPId (path, completionContext) -> path |> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact - ~completionContext ~env + ~completionContext ~env ~scope | CPField (CPId (path, Module), fieldName) -> (* M.field *) path @ [fieldName] |> getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact - ~completionContext:Field ~env + ~completionContext:Field ~env ~scope | CPField (cp, fieldName) -> ( match cp |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact:true + ~env ~exact:true ~scope |> completionsGetTypeEnv with | Some (typ, env) -> ( @@ -887,7 +890,7 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos match cp |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact:true + ~env ~exact:true ~scope |> completionsGetTypeEnv with | Some (typ, env) -> ( @@ -915,7 +918,7 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos match cp |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact:true + ~env ~exact:true ~scope |> completionsGetTypeEnv with | Some (typ, _envNotUsed) -> ( @@ -987,7 +990,7 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos let completions = modulePath @ [funNamePrefix] |> getCompletionsForPath ~completionContext:PartialParser.Value - ~exact:false ~package ~opens ~allFiles ~pos ~env + ~exact:false ~package ~opens ~allFiles ~pos ~env ~scope in completions |> List.map (fun (completion : Completion.t) -> @@ -1028,14 +1031,14 @@ let processCompletable ~package ~scope ~env ~pos let findTypeOfValue path = path |> getCompletionsForPath ~completionContext:PartialParser.Value ~exact:true - ~package ~opens ~allFiles ~pos ~env + ~package ~opens ~allFiles ~pos ~env ~scope |> completionsGetTypeEnv in match completable with | Cpath contextPath -> contextPath |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact:false + ~env ~exact:false ~scope |> List.map completionToItem | Cjsx ([id], prefix, identsSeen) when String.uncapitalize_ascii id = id -> let mkLabel_ name typString = From d55a95f72422d7a98a8878c10bddbef91f22466c Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 09:21:30 +0200 Subject: [PATCH 106/135] Start to push duplicate name check everywhere. --- analysis/src/NewCompletions.ml | 82 ++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 8445d9b43..ee9185f88 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -500,13 +500,15 @@ let checkName name ~prefix ~exact = if exact then name = prefix else Utils.startsWith name prefix let completionForExporteds iterExported getDeclared ~prefix ~exact ~env - transformContents = + ~namesUsed transformContents = let res = ref [] in iterExported (fun name stamp -> (* Log.log("checking exported: " ++ name); *) if checkName name ~prefix ~exact then match getDeclared stamp with - | Some (declared : _ Declared.t) -> + | Some (declared : _ Declared.t) + when not (Hashtbl.mem namesUsed declared.name.txt) -> + Hashtbl.add namesUsed declared.name.txt (); res := { (Completion.create ~name:declared.name.txt ~env @@ -517,25 +519,25 @@ let completionForExporteds iterExported getDeclared ~prefix ~exact ~env docstring = declared.docstring; } :: !res - | None -> ()); + | _ -> ()); !res -let completionForExportedModules ~env ~prefix ~exact = +let completionForExportedModules ~env ~prefix ~exact ~namesUsed = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Module) - (Stamps.findModule env.file.stamps) ~prefix ~exact ~env (fun m -> + (Stamps.findModule env.file.stamps) ~prefix ~exact ~env ~namesUsed (fun m -> Completion.Module m) -let completionForExportedValues ~env ~prefix ~exact = +let completionForExportedValues ~env ~prefix ~exact ~namesUsed = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Value) - (Stamps.findValue env.file.stamps) ~prefix ~exact ~env (fun v -> + (Stamps.findValue env.file.stamps) ~prefix ~exact ~env ~namesUsed (fun v -> Completion.Value v) -let completionForExportedTypes ~env ~prefix ~exact = +let completionForExportedTypes ~env ~prefix ~exact ~namesUsed = completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Type) - (Stamps.findType env.file.stamps) ~prefix ~exact ~env (fun t -> + (Stamps.findType env.file.stamps) ~prefix ~exact ~env ~namesUsed (fun t -> Completion.Type t) -let completionsForConstructors ~(env : QueryEnv.t) ~prefix ~exact = +let completionsForConstructors ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed = let res = ref [] in Exported.iter env.exported Exported.Type (fun _name stamp -> match Stamps.findType env.file.stamps stamp with @@ -544,16 +546,21 @@ let completionsForConstructors ~(env : QueryEnv.t) ~prefix ~exact = (constructors |> List.filter (fun c -> checkName c.Constructor.cname.txt ~prefix ~exact) - |> List.map (fun c -> - Completion.create ~name:c.Constructor.cname.txt ~env - ~kind: - (Completion.Constructor - (c, t.item.decl |> Shared.declToString t.name.txt)))) + |> Utils.filterMap (fun c -> + let name = c.Constructor.cname.txt in + if not (Hashtbl.mem namesUsed name) then + let () = Hashtbl.add namesUsed name () in + Some + (Completion.create ~name ~env + ~kind: + (Completion.Constructor + (c, t.item.decl |> Shared.declToString t.name.txt))) + else None)) @ !res | _ -> ()); !res -let completionForExportedFields ~(env : QueryEnv.t) ~prefix ~exact = +let completionForExportedFields ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed = let res = ref [] in Exported.iter env.exported Exported.Type (fun _name stamp -> match Stamps.findType env.file.stamps stamp with @@ -561,11 +568,16 @@ let completionForExportedFields ~(env : QueryEnv.t) ~prefix ~exact = res := (fields |> List.filter (fun f -> checkName f.fname.txt ~prefix ~exact) - |> List.map (fun f -> - Completion.create ~name:f.fname.txt ~env - ~kind: - (Completion.Field - (f, t.item.decl |> Shared.declToString t.name.txt)))) + |> Utils.filterMap (fun f -> + let name = f.fname.txt in + if not (Hashtbl.mem namesUsed name) then + let () = Hashtbl.add namesUsed name () in + Some + (Completion.create ~name ~env + ~kind: + (Completion.Field + (f, t.item.decl |> Shared.declToString t.name.txt))) + else None)) @ !res | _ -> ()); !res @@ -606,13 +618,13 @@ let detail name (kind : Completion.kind) = | Field ({typ}, s) -> name ^ ": " ^ (typ |> Shared.typeToString) ^ "\n\n" ^ s | Constructor (c, s) -> showConstructor c ^ "\n\n" ^ s -let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact = +let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed = Log.log ("findAllCompletions uri:" ^ Uri2.toString env.file.uri); - completionForExportedModules ~env ~prefix ~exact - @ completionsForConstructors ~env ~prefix ~exact - @ completionForExportedValues ~env ~prefix ~exact - @ completionForExportedTypes ~env ~prefix ~exact - @ completionForExportedFields ~env ~prefix ~exact + completionForExportedModules ~env ~prefix ~exact ~namesUsed + @ completionsForConstructors ~env ~prefix ~exact ~namesUsed + @ completionForExportedValues ~env ~prefix ~exact ~namesUsed + @ completionForExportedTypes ~env ~prefix ~exact ~namesUsed + @ completionForExportedFields ~env ~prefix ~exact ~namesUsed let completionsForDeclareds ~pos ~iter ~stamps ~prefix ~exact ~env transformContents = @@ -668,18 +680,9 @@ let findLocalCompletionsForValues ~pos ~env ~prefix ~exact ~opens ~scope = |> List.fold_left (fun results env -> let completionsFromThisOpen = - findAllCompletions ~env ~prefix ~exact + findAllCompletions ~env ~prefix ~exact ~namesUsed in - List.filter - (fun (completion : Completion.t) -> - if Hashtbl.mem namesUsed completion.name then - (* shadowing from opens *) - false - else ( - Hashtbl.add namesUsed completion.name true; - true)) - completionsFromThisOpen - @ results) + completionsFromThisOpen @ results) [] in completions @ valuesFromOpens @@ -802,7 +805,8 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope match getEnvWithOpens ~pos ~env ~package ~opens path with | Some (env, prefix) -> Log.log "Got the env"; - findAllCompletions ~env ~prefix ~exact + let namesUsed = Hashtbl.create 10 in + findAllCompletions ~env ~prefix ~exact ~namesUsed |> postProcess ~pos ~exact ~completionContext | None -> []) From c250cb80e343ec98a6c6e5284b62ff9dbce65dc9 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 09:22:39 +0200 Subject: [PATCH 107/135] Add example that shows repeated results with shadowed ids. --- analysis/tests/src/CompletePrioritize2.res | 16 +++++---- .../src/expected/CompletePrioritize2.res.txt | 34 +++++++++++++++++-- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/analysis/tests/src/CompletePrioritize2.res b/analysis/tests/src/CompletePrioritize2.res index ddeaf3038..d262bb62a 100644 --- a/analysis/tests/src/CompletePrioritize2.res +++ b/analysis/tests/src/CompletePrioritize2.res @@ -1,10 +1,12 @@ -let a = 4 -let _ = a -let a = "" -let _ = a +let ax = 4 +let _ = ax +let ax = "" +let _ = ax module Test = { type t = {name: int} - let add = (a: t) => a.name + 1 + let add = (ax: t) => ax.name + 1 } -let a: Test.t = {name: 4} -//^com a-> \ No newline at end of file +let ax: Test.t = {name: 4} +//^com ax-> + +//^com ax \ No newline at end of file diff --git a/analysis/tests/src/expected/CompletePrioritize2.res.txt b/analysis/tests/src/expected/CompletePrioritize2.res.txt index e72d14343..120f2753d 100644 --- a/analysis/tests/src/expected/CompletePrioritize2.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize2.res.txt @@ -1,6 +1,6 @@ Complete tests/src/CompletePrioritize2.res 8:2 -posCursor:[9:4] posNoWhite:[9:3] Found expr:[9:1->0:-1] -Completable: Cpath Value[a]-> +posCursor:[9:5] posNoWhite:[9:4] Found expr:[9:1->0:-1] +Completable: Cpath Value[ax]-> [{ "label": "Test.add", "kind": 12, @@ -9,3 +9,33 @@ Completable: Cpath Value[a]-> "documentation": null }] +Complete tests/src/CompletePrioritize2.res 10:2 +posCursor:[11:3] posNoWhite:[11:2] Found expr:[11:1->11:3] +Pexp_ident ax:[11:1->11:3] +Completable: Cpath Value[ax] +[{ + "label": "ax", + "kind": 12, + "tags": [], + "detail": "Test.t", + "documentation": null + }, { + "label": "ax", + "kind": 12, + "tags": [], + "detail": "string", + "documentation": null + }, { + "label": "ax", + "kind": 12, + "tags": [], + "detail": "int", + "documentation": null + }, { + "label": "ax", + "kind": 12, + "tags": [], + "detail": "t", + "documentation": null + }] + From 9c6fb259a09a62aded00898f7f04cac8285380cb Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 10:32:45 +0200 Subject: [PATCH 108/135] Use scope-based autocomplete for values and constructors. --- analysis/src/NewCompletions.ml | 80 ++++++++++++++----- analysis/src/Scope.ml | 48 +++++++++++ .../src/expected/CompletePrioritize2.res.txt | 18 ----- .../tests/src/expected/Completion.res.txt | 8 +- 4 files changed, 111 insertions(+), 43 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index ee9185f88..dd79a24e2 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -654,27 +654,68 @@ let localCompletionsForModules ~pos ~env ~prefix ~exact = ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Module m) -let localCompletionsForValues ~pos ~env ~prefix ~exact = - completionsForDeclareds ~env ~pos ~iter:Stamps.iterValues - ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> - Completion.Value m) - let localCompletionsForTypes ~pos ~env ~prefix ~exact = completionsForDeclareds ~env ~pos ~iter:Stamps.iterTypes ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Type m) -let localCompletionsForConstructors ~pos ~env ~prefix ~exact = - completionsForDeclareds ~env ~pos ~iter:Stamps.iterConstructors - ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun c -> - Completion.Constructor - (c, snd c.typeDecl |> Shared.declToString (fst c.typeDecl))) - -let findLocalCompletionsForValues ~pos ~env ~prefix ~exact ~opens ~scope = - let completions = - localCompletionsForValues ~pos ~env ~prefix ~exact - @ localCompletionsForConstructors ~pos ~env ~prefix ~exact - in +let findLocalCompletionsForValuesAndConstructors ~env ~prefix ~exact ~opens + ~scope = + let valueTable = Hashtbl.create 10 in + env.QueryEnv.file.stamps + |> Stamps.iterValues (fun _ declared -> + Hashtbl.replace valueTable + (declared.name.txt, declared.extentLoc |> Loc.start) + declared); + let constructorTable = Hashtbl.create 10 in + env.QueryEnv.file.stamps + |> Stamps.iterConstructors (fun _ declared -> + Hashtbl.replace constructorTable + (declared.name.txt, declared.extentLoc |> Loc.start) + declared); let namesUsed = Hashtbl.create 10 in + let resultRev = ref [] in + let processValue name loc = + if checkName name ~prefix ~exact then + match Hashtbl.find_opt valueTable (name, Loc.start loc) with + | Some declared -> + if not (Hashtbl.mem namesUsed name) then ( + Hashtbl.add namesUsed name (); + resultRev := + { + (Completion.create ~name:declared.name.txt ~env + ~kind:(Value declared.item)) + with + extentLoc = declared.extentLoc; + deprecated = declared.deprecated; + docstring = declared.docstring; + } + :: !resultRev) + | None -> Printf.printf "XXX NotFound %s loc:%s\n" name (Loc.toString loc) + in + let processConstructor name loc = + if checkName name ~prefix ~exact then + match Hashtbl.find_opt constructorTable (name, Loc.start loc) with + | Some declared -> + if not (Hashtbl.mem namesUsed name) then ( + Hashtbl.add namesUsed name (); + resultRev := + { + (Completion.create ~name:declared.name.txt ~env + ~kind: + (Constructor + ( declared.item, + snd declared.item.typeDecl + |> Shared.declToString (fst declared.item.typeDecl) ))) + with + extentLoc = declared.extentLoc; + deprecated = declared.deprecated; + docstring = declared.docstring; + } + :: !resultRev) + | None -> Printf.printf "XXX NotFound %s loc:%s\n" name (Loc.toString loc) + in + scope |> Scope.iterValuesBeforeFirstOpen processValue; + scope |> Scope.iterConstructorsBeforeFirstOpen processConstructor; let valuesFromOpens = opens |> List.fold_left @@ -685,7 +726,9 @@ let findLocalCompletionsForValues ~pos ~env ~prefix ~exact ~opens ~scope = completionsFromThisOpen @ results) [] in - completions @ valuesFromOpens + scope |> Scope.iterValuesAfterFirstOpen processValue; + scope |> Scope.iterConstructorsAfterFirstOpen processConstructor; + List.rev_append !resultRev valuesFromOpens let findLocalCompletionsWithOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens ~scope ~(completionContext : PartialParser.completionContext) = @@ -693,7 +736,8 @@ let findLocalCompletionsWithOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens ("findLocalCompletionsWithOpens uri:" ^ Uri2.toString env.file.uri ^ " pos:" ^ Pos.toString pos); if completionContext = Value then - findLocalCompletionsForValues ~pos ~env ~prefix ~exact ~opens ~scope + findLocalCompletionsForValuesAndConstructors ~env ~prefix ~exact ~opens + ~scope else localCompletionsForModules ~pos ~env ~prefix ~exact @ localCompletionsForTypes ~pos ~env ~prefix ~exact diff --git a/analysis/src/Scope.ml b/analysis/src/Scope.ml index 9069fb4b0..c4d21e444 100644 --- a/analysis/src/Scope.ml +++ b/analysis/src/Scope.ml @@ -24,5 +24,53 @@ let addOpen ~lid x = Open (Utils.flattenLongIdent lid @ ["place holder"]) :: x let addValue ~name ~loc x = Value (name, loc) :: x let addType ~name ~loc x = Type (name, loc) :: x +let iterValuesBeforeFirstOpen f x = + let rec loop items = + match items with + | Value (s, loc) :: rest -> + f s loc; + loop rest + | Open _ :: _ -> () + | _ :: rest -> loop rest + | [] -> () + in + loop x + +let iterConstructorsBeforeFirstOpen f x = + let rec loop items = + match items with + | Constructor (s, loc) :: rest -> + f s loc; + loop rest + | Open _ :: _ -> () + | _ :: rest -> loop rest + | [] -> () + in + loop x + +let iterValuesAfterFirstOpen f x = + let rec loop foundOpen items = + match items with + | Value (s, loc) :: rest -> + if foundOpen then f s loc; + loop foundOpen rest + | Open _ :: rest -> loop true rest + | _ :: rest -> loop foundOpen rest + | [] -> () + in + loop false x + +let iterConstructorsAfterFirstOpen f x = + let rec loop foundOpen items = + match items with + | Constructor (s, loc) :: rest -> + if foundOpen then f s loc; + loop foundOpen rest + | Open _ :: rest -> loop true rest + | _ :: rest -> loop foundOpen rest + | [] -> () + in + loop false x + let getRawOpens x = x |> Utils.filterMap (function Open path -> Some path | _ -> None) diff --git a/analysis/tests/src/expected/CompletePrioritize2.res.txt b/analysis/tests/src/expected/CompletePrioritize2.res.txt index 120f2753d..d8f653e32 100644 --- a/analysis/tests/src/expected/CompletePrioritize2.res.txt +++ b/analysis/tests/src/expected/CompletePrioritize2.res.txt @@ -19,23 +19,5 @@ Completable: Cpath Value[ax] "tags": [], "detail": "Test.t", "documentation": null - }, { - "label": "ax", - "kind": 12, - "tags": [], - "detail": "string", - "documentation": null - }, { - "label": "ax", - "kind": 12, - "tags": [], - "detail": "int", - "documentation": null - }, { - "label": "ax", - "kind": 12, - "tags": [], - "detail": "t", - "documentation": null }] diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 99f298735..b34573156 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1186,13 +1186,7 @@ Complete tests/src/Completion.res 166:3 posCursor:[167:4] posNoWhite:[167:3] Found expr:[167:1->167:4] Pexp_ident sha:[167:1->167:4] Completable: Cpath Value[sha] -[{ - "label": "shadowed", - "kind": 12, - "tags": [], - "detail": "string", - "documentation": null - }] +[] Complete tests/src/Completion.res 168:3 posCursor:[169:4] posNoWhite:[169:3] Found expr:[169:1->169:4] From 39425f6dc99b0f7a40c0f9f3a649d0ce89b257f7 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 10:36:40 +0200 Subject: [PATCH 109/135] log --- analysis/src/NewCompletions.ml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index dd79a24e2..f76c023eb 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -690,7 +690,10 @@ let findLocalCompletionsForValuesAndConstructors ~env ~prefix ~exact ~opens docstring = declared.docstring; } :: !resultRev) - | None -> Printf.printf "XXX NotFound %s loc:%s\n" name (Loc.toString loc) + | None -> + Log.log + (Printf.sprintf "Completion Value Not Found %s loc:%s\n" name + (Loc.toString loc)) in let processConstructor name loc = if checkName name ~prefix ~exact then @@ -712,7 +715,10 @@ let findLocalCompletionsForValuesAndConstructors ~env ~prefix ~exact ~opens docstring = declared.docstring; } :: !resultRev) - | None -> Printf.printf "XXX NotFound %s loc:%s\n" name (Loc.toString loc) + | None -> + Log.log + (Printf.sprintf "Completion Constructor Not Found %s loc:%s\n" name + (Loc.toString loc)) in scope |> Scope.iterValuesBeforeFirstOpen processValue; scope |> Scope.iterConstructorsBeforeFirstOpen processConstructor; From ace661a0895baac47efdc1319d87043011e7bf0b Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 10:47:11 +0200 Subject: [PATCH 110/135] push filtering inside findAllCompletions --- analysis/src/NewCompletions.ml | 23 +++++++++++++------ .../tests/src/expected/Completion.res.txt | 12 +++++----- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index f76c023eb..56251ff2f 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -618,13 +618,21 @@ let detail name (kind : Completion.kind) = | Field ({typ}, s) -> name ^ ": " ^ (typ |> Shared.typeToString) ^ "\n\n" ^ s | Constructor (c, s) -> showConstructor c ^ "\n\n" ^ s -let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed = +let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed + ~(completionContext : PartialParser.completionContext) = Log.log ("findAllCompletions uri:" ^ Uri2.toString env.file.uri); - completionForExportedModules ~env ~prefix ~exact ~namesUsed - @ completionsForConstructors ~env ~prefix ~exact ~namesUsed - @ completionForExportedValues ~env ~prefix ~exact ~namesUsed - @ completionForExportedTypes ~env ~prefix ~exact ~namesUsed - @ completionForExportedFields ~env ~prefix ~exact ~namesUsed + match completionContext with + | Value -> + completionForExportedValues ~env ~prefix ~exact ~namesUsed + @ completionsForConstructors ~env ~prefix ~exact ~namesUsed + @ completionForExportedModules ~env ~prefix ~exact ~namesUsed + | Type -> + completionForExportedTypes ~env ~prefix ~exact ~namesUsed + @ completionForExportedModules ~env ~prefix ~exact ~namesUsed + | Module -> completionForExportedModules ~env ~prefix ~exact ~namesUsed + | Field -> + completionForExportedModules ~env ~prefix ~exact ~namesUsed + @ completionForExportedFields ~env ~prefix ~exact ~namesUsed let completionsForDeclareds ~pos ~iter ~stamps ~prefix ~exact ~env transformContents = @@ -728,6 +736,7 @@ let findLocalCompletionsForValuesAndConstructors ~env ~prefix ~exact ~opens (fun results env -> let completionsFromThisOpen = findAllCompletions ~env ~prefix ~exact ~namesUsed + ~completionContext:Value in completionsFromThisOpen @ results) [] @@ -856,7 +865,7 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope | Some (env, prefix) -> Log.log "Got the env"; let namesUsed = Hashtbl.create 10 in - findAllCompletions ~env ~prefix ~exact ~namesUsed + findAllCompletions ~env ~prefix ~exact ~namesUsed ~completionContext |> postProcess ~pos ~exact ~completionContext | None -> []) diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index b34573156..b5973acc1 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -75,12 +75,6 @@ posCursor:[2:7] posNoWhite:[2:6] Found expr:[2:1->6:6] Pexp_ident Array.:[2:1->6:6] Completable: Cpath Value[Array, ""] [{ - "label": "Floatarray", - "kind": 9, - "tags": [], - "detail": "module", - "documentation": null - }, { "label": "fold_left", "kind": 12, "tags": [], @@ -290,6 +284,12 @@ Completable: Cpath Value[Array, ""] "tags": [], "detail": "(array<'a>, int, array<'a>, int, int) => unit", "documentation": {"kind": "markdown", "value": " [Array.blit v1 o1 v2 o2 len] copies [len] elements\n from array [v1], starting at element number [o1], to array [v2],\n starting at element number [o2]. It works correctly even if\n [v1] and [v2] are the same array, and the source and\n destination chunks overlap.\n\n Raise [Invalid_argument \"Array.blit\"] if [o1] and [len] do not\n designate a valid subarray of [v1], or if [o2] and [len] do not\n designate a valid subarray of [v2]. "} + }, { + "label": "Floatarray", + "kind": 9, + "tags": [], + "detail": "module", + "documentation": null }] Complete tests/src/Completion.res 2:2 From f2c95da4e58fad0b44885ea9e477c5d7ff37c4a9 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 10:48:22 +0200 Subject: [PATCH 111/135] remove dead filtering code --- analysis/src/NewCompletions.ml | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 56251ff2f..2a45019d3 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -794,23 +794,6 @@ let rec extractObjectType ~env ~package (t : Types.type_expr) = | _ -> None) | _ -> None -let filterCompletionKind ~(completionContext : PartialParser.completionContext) - {Completion.kind} = - match (completionContext, kind) with - | Module, (Module _ | FileModule _) -> true - | Module, _ -> false - | (Field | Type | Value), (Module _ | FileModule _) -> - (* M.field M.type M.value *) - true - | Value, (Value _ | Constructor _) -> - (* x Red *) - true - | Value, _ -> false - | Field, Field _ -> true - | Field, _ -> false - | Type, Type _ -> true - | Type, _ -> false - let prioritize ~exact ~pos completions = if exact then (* Heuristic to approximate scope when an exact name is required and there could @@ -829,10 +812,7 @@ let prioritize ~exact ~pos completions = loop completions else completions -let postProcess ~pos ~exact ~completionContext completions = - completions - |> List.filter (filterCompletionKind ~completionContext) - |> prioritize ~exact ~pos +let postProcess ~pos ~exact completions = completions |> prioritize ~exact ~pos let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope ~completionContext ~env path = @@ -857,8 +837,7 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope ~kind:(Completion.FileModule name)) else None) in - localCompletionsPlusOpens @ fileModules - |> postProcess ~pos ~exact ~completionContext + localCompletionsPlusOpens @ fileModules |> postProcess ~pos ~exact | _ -> ( Log.log ("Path " ^ pathToString path); match getEnvWithOpens ~pos ~env ~package ~opens path with @@ -866,7 +845,7 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope Log.log "Got the env"; let namesUsed = Hashtbl.create 10 in findAllCompletions ~env ~prefix ~exact ~namesUsed ~completionContext - |> postProcess ~pos ~exact ~completionContext + |> postProcess ~pos ~exact | None -> []) let mkItem ~name ~kind ~detail ~deprecated ~docstring = From 7307fa8b02afc06240c93f60a0d7c0fe3615e348 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 10:49:48 +0200 Subject: [PATCH 112/135] Not need to prioritize/postprocess anymore. --- analysis/src/NewCompletions.ml | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 2a45019d3..5a1b5f4f7 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -794,26 +794,6 @@ let rec extractObjectType ~env ~package (t : Types.type_expr) = | _ -> None) | _ -> None -let prioritize ~exact ~pos completions = - if exact then - (* Heuristic to approximate scope when an exact name is required and there could - be more than one instance of that name. - Take the last position before pos if any, or just return the first element. *) - let rec loop decls = - match decls with - | d1 :: d2 :: rest -> - let pos2 = d2.Completion.extentLoc.loc_start |> Pos.ofLexing in - if pos2 >= pos then loop (d1 :: rest) - else - let pos1 = d1.extentLoc.loc_start |> Pos.ofLexing in - if pos1 <= pos2 then loop (d2 :: rest) else loop (d1 :: rest) - | [] | [_] -> decls - in - loop completions - else completions - -let postProcess ~pos ~exact completions = completions |> prioritize ~exact ~pos - let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope ~completionContext ~env path = match path with @@ -837,7 +817,7 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope ~kind:(Completion.FileModule name)) else None) in - localCompletionsPlusOpens @ fileModules |> postProcess ~pos ~exact + localCompletionsPlusOpens @ fileModules | _ -> ( Log.log ("Path " ^ pathToString path); match getEnvWithOpens ~pos ~env ~package ~opens path with @@ -845,7 +825,6 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope Log.log "Got the env"; let namesUsed = Hashtbl.create 10 in findAllCompletions ~env ~prefix ~exact ~namesUsed ~completionContext - |> postProcess ~pos ~exact | None -> []) let mkItem ~name ~kind ~detail ~deprecated ~docstring = From 2ca8151c28c1d19898cf795c57b01d759c660046 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 10:51:35 +0200 Subject: [PATCH 113/135] No need to extentLoc in completion. --- analysis/src/NewCompletions.ml | 4 ---- analysis/src/SharedTypes.ml | 10 +--------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 5a1b5f4f7..4a6d86c5d 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -514,7 +514,6 @@ let completionForExporteds iterExported getDeclared ~prefix ~exact ~env (Completion.create ~name:declared.name.txt ~env ~kind:(transformContents declared.item)) with - extentLoc = declared.extentLoc; deprecated = declared.deprecated; docstring = declared.docstring; } @@ -649,7 +648,6 @@ let completionsForDeclareds ~pos ~iter ~stamps ~prefix ~exact ~env (Completion.create ~name:declared.name.txt ~env ~kind:(transformContents declared.item)) with - extentLoc = declared.extentLoc; deprecated = declared.deprecated; docstring = declared.docstring; } @@ -693,7 +691,6 @@ let findLocalCompletionsForValuesAndConstructors ~env ~prefix ~exact ~opens (Completion.create ~name:declared.name.txt ~env ~kind:(Value declared.item)) with - extentLoc = declared.extentLoc; deprecated = declared.deprecated; docstring = declared.docstring; } @@ -718,7 +715,6 @@ let findLocalCompletionsForValuesAndConstructors ~env ~prefix ~exact ~opens snd declared.item.typeDecl |> Shared.declToString (fst declared.item.typeDecl) ))) with - extentLoc = declared.extentLoc; deprecated = declared.deprecated; docstring = declared.docstring; } diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index ce16276ba..2be6bbd4b 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -217,7 +217,6 @@ module Completion = struct type t = { name : string; - extentLoc : Location.t; env : QueryEnv.t; deprecated : string option; docstring : string list; @@ -225,14 +224,7 @@ module Completion = struct } let create ~name ~kind ~env = - { - name; - extentLoc = Location.none; - env; - deprecated = None; - docstring = []; - kind; - } + {name; env; deprecated = None; docstring = []; kind} let kindToInt kind = match kind with From ecf4e1542e93703196c9c58d423d19a0a332a43d Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 11:18:51 +0200 Subject: [PATCH 114/135] Handle scope for modules. --- analysis/src/Commands.ml | 42 +++++++++++++-- analysis/src/NewCompletions.ml | 99 ++++++++++++++++++++++++++++++++-- analysis/src/Scope.ml | 61 +++++++++++++++++++-- 3 files changed, 189 insertions(+), 13 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 4b6d26573..59e07ef36 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -281,7 +281,8 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = in let setResult x = setResultOpt (Some x) in let scopeValueDescription (vd : Parsetree.value_description) = - scope := !scope |> Scope.addValue ~name:vd.pval_name.txt ~loc:vd.pval_loc + scope := + !scope |> Scope.addValue ~name:vd.pval_name.txt ~loc:vd.pval_name.loc in let scopeValueBinding (vb : Parsetree.value_binding) = match vb.pvb_pat.ppat_desc with @@ -306,9 +307,18 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | _ -> () in let scopeTypeDeclaration (td : Parsetree.type_declaration) = - scope := !scope |> Scope.addType ~name:td.ptype_name.txt ~loc:td.ptype_loc; + scope := + !scope |> Scope.addType ~name:td.ptype_name.txt ~loc:td.ptype_name.loc; scopeTypeKind td.ptype_kind in + let scopeModuleBinding (mb : Parsetree.module_binding) = + scope := + !scope |> Scope.addModule ~name:mb.pmb_name.txt ~loc:mb.pmb_name.loc + in + let scopeModuleDeclaration (md : Parsetree.module_declaration) = + scope := + !scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc + in let structure (iterator : Ast_iterator.iterator) (structure : Parsetree.structure) = @@ -333,6 +343,14 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = decls |> List.iter (fun td -> iterator.type_declaration iterator td); if recFlag = Nonrecursive then decls |> List.iter scopeTypeDeclaration; processed := true + | Pstr_module mb -> + iterator.module_binding iterator mb; + scopeModuleBinding mb; + processed := true + | Pstr_recmodule mbs -> + mbs |> List.iter scopeModuleBinding; + mbs |> List.iter (fun b -> iterator.module_binding iterator b); + processed := true | _ -> ()); if not !processed then Ast_iterator.default_iterator.structure_item iterator item @@ -355,6 +373,14 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = decls |> List.iter (fun td -> iterator.type_declaration iterator td); if recFlag = Nonrecursive then decls |> List.iter scopeTypeDeclaration; processed := true + | Psig_module md -> + iterator.module_declaration iterator md; + scopeModuleDeclaration md; + processed := true + | Psig_recmodule mds -> + mds |> List.iter scopeModuleDeclaration; + mds |> List.iter (fun d -> iterator.module_declaration iterator d); + processed := true | _ -> ()); if not !processed then Ast_iterator.default_iterator.signature_item iterator item @@ -581,8 +607,16 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = bindings |> List.iter (fun vb -> iterator.value_binding iterator vb); if recFlag = Nonrecursive then bindings |> List.iter scopeValueBinding; iterator.expr iterator e; - processed := true; - scope := oldScope + scope := oldScope; + processed := true + | Pexp_letmodule (name, modExpr, modBody) -> + let oldScope = !scope in + iterator.location iterator name.loc; + iterator.module_expr iterator modExpr; + scope := !scope |> Scope.addModule ~name:name.txt ~loc:name.loc; + iterator.expr iterator modBody; + scope := oldScope; + processed := true | _ -> ()); if not !processed then Ast_iterator.default_iterator.expr iterator expr in diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 4a6d86c5d..8f5811c99 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -741,17 +741,108 @@ let findLocalCompletionsForValuesAndConstructors ~env ~prefix ~exact ~opens scope |> Scope.iterConstructorsAfterFirstOpen processConstructor; List.rev_append !resultRev valuesFromOpens +let findLocalCompletionsForTypes ~env ~prefix ~exact ~opens ~scope = + let typesTable = Hashtbl.create 10 in + env.QueryEnv.file.stamps + |> Stamps.iterTypes (fun _ declared -> + Hashtbl.replace typesTable + (declared.name.txt, declared.extentLoc |> Loc.start) + declared); + let namesUsed = Hashtbl.create 10 in + let resultRev = ref [] in + let processType name loc = + if checkName name ~prefix ~exact then + match Hashtbl.find_opt typesTable (name, Loc.start loc) with + | Some declared -> + if not (Hashtbl.mem namesUsed name) then ( + Hashtbl.add namesUsed name (); + resultRev := + { + (Completion.create ~name:declared.name.txt ~env + ~kind:(Type declared.item)) + with + deprecated = declared.deprecated; + docstring = declared.docstring; + } + :: !resultRev) + | None -> + Log.log + (Printf.sprintf "Completion Type Not Found %s loc:%s\n" name + (Loc.toString loc)) + in + scope |> Scope.iterTypesBeforeFirstOpen processType; + let valuesFromOpens = + opens + |> List.fold_left + (fun results env -> + let completionsFromThisOpen = + findAllCompletions ~env ~prefix ~exact ~namesUsed + ~completionContext:Value + in + completionsFromThisOpen @ results) + [] + in + scope |> Scope.iterTypesAfterFirstOpen processType; + List.rev_append !resultRev valuesFromOpens + +let findLocalCompletionsForModules ~env ~prefix ~exact ~opens ~scope = + let modulesTable = Hashtbl.create 10 in + env.QueryEnv.file.stamps + |> Stamps.iterModules (fun _ declared -> + Hashtbl.replace modulesTable + (declared.name.txt, declared.extentLoc |> Loc.start) + declared); + let namesUsed = Hashtbl.create 10 in + let resultRev = ref [] in + let processModule name loc = + if checkName name ~prefix ~exact then + match Hashtbl.find_opt modulesTable (name, Loc.start loc) with + | Some declared -> + if not (Hashtbl.mem namesUsed name) then ( + Hashtbl.add namesUsed name (); + resultRev := + { + (Completion.create ~name:declared.name.txt ~env + ~kind:(Module declared.item)) + with + deprecated = declared.deprecated; + docstring = declared.docstring; + } + :: !resultRev) + | None -> + Log.log + (Printf.sprintf "Completion Module Not Found %s loc:%s\n" name + (Loc.toString loc)) + in + scope |> Scope.iterModulesBeforeFirstOpen processModule; + let valuesFromOpens = + opens + |> List.fold_left + (fun results env -> + let completionsFromThisOpen = + findAllCompletions ~env ~prefix ~exact ~namesUsed + ~completionContext:Value + in + completionsFromThisOpen @ results) + [] + in + scope |> Scope.iterModulesAfterFirstOpen processModule; + List.rev_append !resultRev valuesFromOpens + let findLocalCompletionsWithOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens ~scope ~(completionContext : PartialParser.completionContext) = Log.log ("findLocalCompletionsWithOpens uri:" ^ Uri2.toString env.file.uri ^ " pos:" ^ Pos.toString pos); - if completionContext = Value then + match completionContext with + | Value -> findLocalCompletionsForValuesAndConstructors ~env ~prefix ~exact ~opens ~scope - else - localCompletionsForModules ~pos ~env ~prefix ~exact - @ localCompletionsForTypes ~pos ~env ~prefix ~exact + | Type -> findLocalCompletionsForTypes ~env ~prefix ~exact ~opens ~scope + | Module -> findLocalCompletionsForModules ~env ~prefix ~exact ~opens ~scope + | Field -> + (* There's no local completion for fields *) + [] (* TODO filter out things that are defined after the current position *) let resolveRawOpens ~env ~rawOpens ~package = diff --git a/analysis/src/Scope.ml b/analysis/src/Scope.ml index c4d21e444..1f458db3b 100644 --- a/analysis/src/Scope.ml +++ b/analysis/src/Scope.ml @@ -1,6 +1,7 @@ type item = | Constructor of string * Location.t | Field of string * Location.t + | Module of string * Location.t | Open of string list | Type of string * Location.t | Value of string * Location.t @@ -13,13 +14,15 @@ let itemToString item = match item with | Constructor (s, loc) -> "Constructor " ^ s ^ " " ^ Loc.toString loc | Field (s, loc) -> "Field " ^ s ^ " " ^ Loc.toString loc - | Open sl -> "Module " ^ list sl + | Open sl -> "Open " ^ list sl + | Module (s, loc) -> "Module " ^ s ^ " " ^ Loc.toString loc | Value (s, loc) -> "Value " ^ s ^ " " ^ Loc.toString loc | Type (s, loc) -> "Type " ^ s ^ " " ^ Loc.toString loc let create () : t = [] let addConstructor ~name ~loc x = Constructor (name, loc) :: x let addField ~name ~loc x = Field (name, loc) :: x +let addModule ~name ~loc x = Module (name, loc) :: x let addOpen ~lid x = Open (Utils.flattenLongIdent lid @ ["place holder"]) :: x let addValue ~name ~loc x = Value (name, loc) :: x let addType ~name ~loc x = Type (name, loc) :: x @@ -36,6 +39,18 @@ let iterValuesBeforeFirstOpen f x = in loop x +let iterValuesAfterFirstOpen f x = + let rec loop foundOpen items = + match items with + | Value (s, loc) :: rest -> + if foundOpen then f s loc; + loop foundOpen rest + | Open _ :: rest -> loop true rest + | _ :: rest -> loop foundOpen rest + | [] -> () + in + loop false x + let iterConstructorsBeforeFirstOpen f x = let rec loop items = match items with @@ -48,10 +63,10 @@ let iterConstructorsBeforeFirstOpen f x = in loop x -let iterValuesAfterFirstOpen f x = +let iterConstructorsAfterFirstOpen f x = let rec loop foundOpen items = match items with - | Value (s, loc) :: rest -> + | Constructor (s, loc) :: rest -> if foundOpen then f s loc; loop foundOpen rest | Open _ :: rest -> loop true rest @@ -60,10 +75,46 @@ let iterValuesAfterFirstOpen f x = in loop false x -let iterConstructorsAfterFirstOpen f x = +let iterTypesBeforeFirstOpen f x = + let rec loop items = + match items with + | Type (s, loc) :: rest -> + f s loc; + loop rest + | Open _ :: _ -> () + | _ :: rest -> loop rest + | [] -> () + in + loop x + +let iterTypesAfterFirstOpen f x = let rec loop foundOpen items = match items with - | Constructor (s, loc) :: rest -> + | Type (s, loc) :: rest -> + if foundOpen then f s loc; + loop foundOpen rest + | Open _ :: rest -> loop true rest + | _ :: rest -> loop foundOpen rest + | [] -> () + in + loop false x + +let iterModulesBeforeFirstOpen f x = + let rec loop items = + match items with + | Module (s, loc) :: rest -> + f s loc; + loop rest + | Open _ :: _ -> () + | _ :: rest -> loop rest + | [] -> () + in + loop x + +let iterModulesAfterFirstOpen f x = + let rec loop foundOpen items = + match items with + | Module (s, loc) :: rest -> if foundOpen then f s loc; loop foundOpen rest | Open _ :: rest -> loop true rest From c75b2c5c07d69ce822d3892de2be23567dadfa62 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 11:20:41 +0200 Subject: [PATCH 115/135] remove dead code --- analysis/src/NewCompletions.ml | 31 ------------------------------- analysis/src/Utils.ml | 7 ------- 2 files changed, 38 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 8f5811c99..00cd7ba8e 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -633,37 +633,6 @@ let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed completionForExportedModules ~env ~prefix ~exact ~namesUsed @ completionForExportedFields ~env ~prefix ~exact ~namesUsed -let completionsForDeclareds ~pos ~iter ~stamps ~prefix ~exact ~env - transformContents = - (* Log.log("completion for declares " ++ prefix); *) - let res = ref [] in - iter - (fun _stamp (declared : _ Declared.t) -> - if - checkName declared.name.txt ~prefix ~exact - && Utils.locationContainsFuzzy declared.scopeLoc pos - then - res := - { - (Completion.create ~name:declared.name.txt ~env - ~kind:(transformContents declared.item)) - with - deprecated = declared.deprecated; - docstring = declared.docstring; - } - :: !res) - stamps; - !res - -let localCompletionsForModules ~pos ~env ~prefix ~exact = - completionsForDeclareds ~env ~pos ~iter:Stamps.iterModules - ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> - Completion.Module m) - -let localCompletionsForTypes ~pos ~env ~prefix ~exact = - completionsForDeclareds ~env ~pos ~iter:Stamps.iterTypes - ~stamps:env.QueryEnv.file.stamps ~prefix ~exact (fun m -> Completion.Type m) - let findLocalCompletionsForValuesAndConstructors ~env ~prefix ~exact ~opens ~scope = let valueTable = Hashtbl.create 10 in diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml index c9ae410b2..e8c353466 100644 --- a/analysis/src/Utils.ml +++ b/analysis/src/Utils.ml @@ -42,13 +42,6 @@ let rec find fn items = | one :: rest -> ( match fn one with None -> find fn rest | Some x -> Some x) -(** - Check if pos is within the location, but be fuzzy about when the location ends. - If it's within 5 lines, go with it. -*) -let locationContainsFuzzy loc (l, c) = - Loc.start loc <= (l, c) && Loc.end_ loc >= (l - 5, c) - let filterMap f = let rec aux accu = function | [] -> List.rev accu From b3dbab6b7ab140d63baa4a9256d4cf4e3ec36b81 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 13:16:48 +0200 Subject: [PATCH 116/135] rename --- analysis/src/NewCompletions.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 00cd7ba8e..9887f8d19 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -855,7 +855,7 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope match path with | [] -> [] | [prefix] -> - let localCompletionsPlusOpens = + let localCompletionsWithOpens = findLocalCompletionsWithOpens ~pos ~env ~prefix ~exact ~opens ~scope ~completionContext in @@ -873,7 +873,7 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope ~kind:(Completion.FileModule name)) else None) in - localCompletionsPlusOpens @ fileModules + localCompletionsWithOpens @ fileModules | _ -> ( Log.log ("Path " ^ pathToString path); match getEnvWithOpens ~pos ~env ~package ~opens path with From 96c8263b7e7ef08f0a25ea814dbf181a4e80f312 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 13:22:09 +0200 Subject: [PATCH 117/135] rename --- analysis/src/ProcessCmt.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index 05a192bd0..7cdf9a91c 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -672,19 +672,19 @@ let rec resolvePathInner ~(env : QueryEnv.t) ~path = | Some stamp -> ( match Stamps.findModule env.file.stamps stamp with | None -> None - | Some {item = kind} -> findInModule ~env kind subPath)) + | Some {item} -> findInModule ~env item subPath)) -and findInModule ~env kind path = - match kind with +and findInModule ~env module_ path = + match module_ with | Structure {exported} -> resolvePathInner ~env:{env with exported} ~path - | Constraint (_, moduleTypeKind) -> findInModule ~env moduleTypeKind path + | Constraint (_, module1) -> findInModule ~env module1 path | Ident modulePath -> ( let stamp, moduleName, fullPath = joinPaths modulePath path in if stamp = 0 then Some (`Global (moduleName, fullPath)) else match Stamps.findModule env.file.stamps stamp with | None -> None - | Some {item = kind} -> findInModule ~env kind fullPath) + | Some {item} -> findInModule ~env item fullPath) let fromCompilerPath ~(env : QueryEnv.t) path = match makePath path with From 5718ef957bcae9c3841e43bd7d9ccff09b86cf07 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 13:26:21 +0200 Subject: [PATCH 118/135] refactor --- analysis/src/NewCompletions.ml | 44 +++++++++++++++++++++++++++++++++- analysis/src/ProcessCmt.ml | 42 -------------------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 9887f8d19..43c8bcca8 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -581,9 +581,51 @@ let completionForExportedFields ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed = | _ -> ()); !res +let locationIsBefore {Location.loc_start} pos = Pos.ofLexing loc_start <= pos + +let findModuleInScope pos name iter stamps = + (* Log.log("Find " ++ name ++ " with " ++ string_of_int(Hashtbl.length(stamps)) ++ " stamps"); *) + let res = ref None in + iter + (fun _stamp (declared : _ Declared.t) -> + if declared.name.txt = name then + (* Log.log("a stamp " ++ Utils.showLocation(declared.scopeLoc) ++ " " ++ string_of_int(l) ++ "," ++ string_of_int(c)); *) + if locationIsBefore declared.scopeLoc pos then + match !res with + | None -> res := Some declared + | Some current -> + if + current.name.loc.loc_start.pos_cnum + < declared.name.loc.loc_start.pos_cnum + then res := Some declared) + stamps; + !res + +let resolveFromStamps ~(env : QueryEnv.t) ~path ~package ~pos = + match path with + | [] -> None + | [name] -> Some (env, name) + | name :: inner -> ( + (* Log.log("Finding from stamps " ++ name); *) + match findModuleInScope pos name Stamps.iterModules env.file.stamps with + | None -> None + | Some declared -> ( + (* Log.log("found it"); *) + match ProcessCmt.findInModule ~env declared.item inner with + | None -> None + | Some res -> ( + match res with + | `Local (env, name) -> Some (env, name) + | `Global (moduleName, fullPath) -> ( + match ProcessCmt.fileForModule ~package moduleName with + | None -> None + | Some file -> + ProcessCmt.resolvePath ~env:(QueryEnv.fromFile file) ~path:fullPath + ~package)))) + let getEnvWithOpens ~pos ~(env : QueryEnv.t) ~package ~(opens : QueryEnv.t list) (path : string list) = - match ProcessCmt.resolveFromStamps ~env ~path ~package ~pos with + match resolveFromStamps ~env ~path ~package ~pos with | Some x -> Some x | None -> let rec loop opens = diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index 7cdf9a91c..10da582dd 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -1237,48 +1237,6 @@ let rec resolvePath ~env ~path ~package = | Some file -> resolvePath ~env:(QueryEnv.fromFile file) ~path:fullPath ~package)) -let locationIsBefore {Location.loc_start} pos = Pos.ofLexing loc_start <= pos - -let findInScope pos name iter stamps = - (* Log.log("Find " ++ name ++ " with " ++ string_of_int(Hashtbl.length(stamps)) ++ " stamps"); *) - let res = ref None in - iter - (fun _stamp (declared : _ Declared.t) -> - if declared.name.txt = name then - (* Log.log("a stamp " ++ Utils.showLocation(declared.scopeLoc) ++ " " ++ string_of_int(l) ++ "," ++ string_of_int(c)); *) - if locationIsBefore declared.scopeLoc pos then - match !res with - | None -> res := Some declared - | Some current -> - if - current.name.loc.loc_start.pos_cnum - < declared.name.loc.loc_start.pos_cnum - then res := Some declared) - stamps; - !res - -let resolveFromStamps ~(env : QueryEnv.t) ~path ~package ~pos = - match path with - | [] -> None - | [name] -> Some (env, name) - | name :: inner -> ( - (* Log.log("Finding from stamps " ++ name); *) - match findInScope pos name Stamps.iterModules env.file.stamps with - | None -> None - | Some declared -> ( - (* Log.log("found it"); *) - match findInModule ~env declared.item inner with - | None -> None - | Some res -> ( - match res with - | `Local (env, name) -> Some (env, name) - | `Global (moduleName, fullPath) -> ( - match fileForModule ~package moduleName with - | None -> None - | Some file -> - resolvePath ~env:(QueryEnv.fromFile file) ~path:fullPath ~package))) - ) - let resolveModuleFromCompilerPath ~env ~package path = match fromCompilerPath ~env path with | `Global (moduleName, path) -> ( From 26094c09325527654435511864405eba5445983c Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 17:41:07 +0200 Subject: [PATCH 119/135] top-level logic for getEnvWithOpens --- analysis/src/NewCompletions.ml | 101 +++++++++++++++++---------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 43c8bcca8..7903e2f4d 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -583,13 +583,11 @@ let completionForExportedFields ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed = let locationIsBefore {Location.loc_start} pos = Pos.ofLexing loc_start <= pos -let findModuleInScope pos name iter stamps = - (* Log.log("Find " ++ name ++ " with " ++ string_of_int(Hashtbl.length(stamps)) ++ " stamps"); *) +let findModuleInScope ~pos ~moduleName ~stamps = let res = ref None in - iter + Stamps.iterModules (fun _stamp (declared : _ Declared.t) -> - if declared.name.txt = name then - (* Log.log("a stamp " ++ Utils.showLocation(declared.scopeLoc) ++ " " ++ string_of_int(l) ++ "," ++ string_of_int(c)); *) + if declared.name.txt = moduleName then if locationIsBefore declared.scopeLoc pos then match !res with | None -> res := Some declared @@ -601,53 +599,56 @@ let findModuleInScope pos name iter stamps = stamps; !res -let resolveFromStamps ~(env : QueryEnv.t) ~path ~package ~pos = - match path with - | [] -> None - | [name] -> Some (env, name) - | name :: inner -> ( - (* Log.log("Finding from stamps " ++ name); *) - match findModuleInScope pos name Stamps.iterModules env.file.stamps with +let resolvePathFromStamps ~(env : QueryEnv.t) ~package ~pos ~moduleName ~path = + (* Log.log("Finding from stamps " ++ name); *) + match findModuleInScope ~pos ~moduleName ~stamps:env.file.stamps with + | None -> None + | Some declared -> ( + (* Log.log("found it"); *) + match ProcessCmt.findInModule ~env declared.item path with | None -> None - | Some declared -> ( - (* Log.log("found it"); *) - match ProcessCmt.findInModule ~env declared.item inner with - | None -> None - | Some res -> ( - match res with - | `Local (env, name) -> Some (env, name) - | `Global (moduleName, fullPath) -> ( - match ProcessCmt.fileForModule ~package moduleName with - | None -> None - | Some file -> - ProcessCmt.resolvePath ~env:(QueryEnv.fromFile file) ~path:fullPath - ~package)))) + | Some res -> ( + match res with + | `Local (env, name) -> Some (env, name) + | `Global (moduleName, fullPath) -> ( + match ProcessCmt.fileForModule ~package moduleName with + | None -> None + | Some file -> + ProcessCmt.resolvePath ~env:(QueryEnv.fromFile file) ~path:fullPath + ~package))) + +let resolveModuleWithOpens ~opens ~package ~moduleName = + let rec loop opens = + match opens with + | (env : QueryEnv.t) :: rest -> ( + Log.log ("Looking for env in " ^ Uri2.toString env.file.uri); + match ProcessCmt.resolvePath ~env ~package ~path:[moduleName; ""] with + | Some (env, _) -> Some env + | None -> loop rest) + | [] -> None + in + loop opens + +let resolveFileModule ~moduleName ~package = + Log.log ("Getting module " ^ moduleName); + match ProcessCmt.fileForModule ~package moduleName with + | None -> None + | Some file -> + Log.log "got it"; + let env = QueryEnv.fromFile file in + Some env let getEnvWithOpens ~pos ~(env : QueryEnv.t) ~package ~(opens : QueryEnv.t list) - (path : string list) = - match resolveFromStamps ~env ~path ~package ~pos with + ~moduleName (path : string list) = + match resolvePathFromStamps ~env ~pos ~moduleName ~path ~package with | Some x -> Some x - | None -> - let rec loop opens = - match opens with - | (env : QueryEnv.t) :: rest -> ( - Log.log ("Looking for env in " ^ Uri2.toString env.file.uri); - match ProcessCmt.resolvePath ~env ~package ~path with - | Some x -> Some x - | None -> loop rest) - | [] -> ( - match path with - | [] | [_] -> None - | top :: path -> ( - Log.log ("Getting module " ^ top); - match ProcessCmt.fileForModule ~package top with - | None -> None - | Some file -> - Log.log "got it"; - let env = QueryEnv.fromFile file in - ProcessCmt.resolvePath ~env ~package ~path)) - in - loop opens + | None -> ( + match resolveModuleWithOpens ~opens ~package ~moduleName with + | Some env -> ProcessCmt.resolvePath ~env ~package ~path + | None -> ( + match resolveFileModule ~moduleName ~package with + | None -> None + | Some env -> ProcessCmt.resolvePath ~env ~package ~path)) let detail name (kind : Completion.kind) = match kind with @@ -916,9 +917,9 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope else None) in localCompletionsWithOpens @ fileModules - | _ -> ( + | moduleName :: path -> ( Log.log ("Path " ^ pathToString path); - match getEnvWithOpens ~pos ~env ~package ~opens path with + match getEnvWithOpens ~pos ~env ~package ~opens ~moduleName path with | Some (env, prefix) -> Log.log "Got the env"; let namesUsed = Hashtbl.create 10 in From 2c8df45a308a531bb821fb7e96e2283aab4e33a1 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 17:53:40 +0200 Subject: [PATCH 120/135] Use scope to find local module in path resolution. --- analysis/src/NewCompletions.ml | 49 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 7903e2f4d..c6fd4b63d 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -583,25 +583,30 @@ let completionForExportedFields ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed = let locationIsBefore {Location.loc_start} pos = Pos.ofLexing loc_start <= pos -let findModuleInScope ~pos ~moduleName ~stamps = - let res = ref None in - Stamps.iterModules - (fun _stamp (declared : _ Declared.t) -> - if declared.name.txt = moduleName then - if locationIsBefore declared.scopeLoc pos then - match !res with - | None -> res := Some declared - | Some current -> - if - current.name.loc.loc_start.pos_cnum - < declared.name.loc.loc_start.pos_cnum - then res := Some declared) - stamps; - !res +let findModuleInScope ~env ~moduleName ~scope = + let modulesTable = Hashtbl.create 10 in + env.QueryEnv.file.stamps + |> Stamps.iterModules (fun _ declared -> + Hashtbl.replace modulesTable + (declared.name.txt, declared.extentLoc |> Loc.start) + declared); + let result = ref None in + let processModule name loc = + if name = moduleName && !result = None then + match Hashtbl.find_opt modulesTable (name, Loc.start loc) with + | Some declared -> result := Some declared + | None -> + Log.log + (Printf.sprintf "Module Not Found %s loc:%s\n" name (Loc.toString loc)) + in + scope |> Scope.iterModulesBeforeFirstOpen processModule; + scope |> Scope.iterModulesAfterFirstOpen processModule; + !result -let resolvePathFromStamps ~(env : QueryEnv.t) ~package ~pos ~moduleName ~path = +let resolvePathFromStamps ~(env : QueryEnv.t) ~package ~scope ~moduleName ~path + = (* Log.log("Finding from stamps " ++ name); *) - match findModuleInScope ~pos ~moduleName ~stamps:env.file.stamps with + match findModuleInScope ~env ~moduleName ~scope with | None -> None | Some declared -> ( (* Log.log("found it"); *) @@ -638,9 +643,10 @@ let resolveFileModule ~moduleName ~package = let env = QueryEnv.fromFile file in Some env -let getEnvWithOpens ~pos ~(env : QueryEnv.t) ~package ~(opens : QueryEnv.t list) - ~moduleName (path : string list) = - match resolvePathFromStamps ~env ~pos ~moduleName ~path ~package with +let getEnvWithOpens ~scope ~(env : QueryEnv.t) ~package + ~(opens : QueryEnv.t list) ~moduleName (path : string list) = + (* TODO: handle interleaving of opens and local modules correctly *) + match resolvePathFromStamps ~env ~scope ~moduleName ~path ~package with | Some x -> Some x | None -> ( match resolveModuleWithOpens ~opens ~package ~moduleName with @@ -843,6 +849,7 @@ let findLocalCompletionsForModules ~env ~prefix ~exact ~opens ~scope = let findLocalCompletionsWithOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens ~scope ~(completionContext : PartialParser.completionContext) = + (* TODO: handle arbitrary interleaving of opens and local bindings correctly *) Log.log ("findLocalCompletionsWithOpens uri:" ^ Uri2.toString env.file.uri ^ " pos:" ^ Pos.toString pos); @@ -919,7 +926,7 @@ let getCompletionsForPath ~package ~opens ~allFiles ~pos ~exact ~scope localCompletionsWithOpens @ fileModules | moduleName :: path -> ( Log.log ("Path " ^ pathToString path); - match getEnvWithOpens ~pos ~env ~package ~opens ~moduleName path with + match getEnvWithOpens ~scope ~env ~package ~opens ~moduleName path with | Some (env, prefix) -> Log.log "Got the env"; let namesUsed = Hashtbl.create 10 in From cce0d229767e4ed2a7b9908f98581a06ffd775a9 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 17:59:22 +0200 Subject: [PATCH 121/135] Remove old code to approximate scope. --- analysis/src/NewCompletions.ml | 2 - analysis/src/ProcessAttributes.ml | 4 +- analysis/src/ProcessCmt.ml | 91 +++---------------------------- analysis/src/SharedTypes.ml | 3 +- 4 files changed, 11 insertions(+), 89 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index c6fd4b63d..e3851e953 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -581,8 +581,6 @@ let completionForExportedFields ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed = | _ -> ()); !res -let locationIsBefore {Location.loc_start} pos = Pos.ofLexing loc_start <= pos - let findModuleInScope ~env ~moduleName ~scope = let modulesTable = Hashtbl.create 10 in env.QueryEnv.file.stamps diff --git a/analysis/src/ProcessAttributes.ml b/analysis/src/ProcessAttributes.ml index 0836b7596..ac0835c87 100644 --- a/analysis/src/ProcessAttributes.ml +++ b/analysis/src/ProcessAttributes.ml @@ -34,13 +34,11 @@ let rec findDeprecatedAttribute attributes = | ({Asttypes.txt = "deprecated"}, _) :: _ -> Some "" | _ :: rest -> findDeprecatedAttribute rest -let newDeclared ~item ~scope ~extent ~name ~stamp ~modulePath isExported - attributes = +let newDeclared ~item ~extent ~name ~stamp ~modulePath isExported attributes = { Declared.name; stamp; extentLoc = extent; - scopeLoc = scope; isExported; modulePath; deprecated = findDeprecatedAttribute attributes; diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index 10da582dd..e0294aee2 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -25,14 +25,8 @@ let addItem ~(name : string Location.loc) ~extent ~stamp ~(env : Env.t) ~item attributes addExported addStamp = let isExported = addExported name.txt stamp in let declared = - ProcessAttributes.newDeclared ~item - ~scope: - { - Location.loc_start = extent.Location.loc_end; - loc_end = env.scope.loc_end; - loc_ghost = false; - } - ~extent ~name ~stamp ~modulePath:env.modulePath isExported attributes + ProcessAttributes.newDeclared ~item ~extent ~name ~stamp + ~modulePath:env.modulePath isExported attributes in addStamp env.stamps stamp declared; declared @@ -95,12 +89,6 @@ let rec forTypeSignatureItem ~env ~(exported : Exported.t) in let declared = ProcessAttributes.newDeclared ~item ~extent:cd_loc - ~scope: - { - Location.loc_start = type_loc.Location.loc_end; - loc_end = env.scope.loc_end; - loc_ghost = false; - } ~name:(Location.mknoloc name) ~stamp (* TODO maybe this needs another child *) ~modulePath:env.modulePath true cd_attributes @@ -220,12 +208,6 @@ let forTypeDeclaration ~env ~(exported : Exported.t) in let declared = ProcessAttributes.newDeclared ~item ~extent:cd_loc - ~scope: - { - Location.loc_start = typ_loc.Location.loc_end; - loc_end = env.scope.loc_end; - loc_ghost = false; - } ~name:cname ~stamp ~modulePath:env.modulePath true cd_attributes in @@ -447,11 +429,7 @@ and forModule env mod_desc moduleName = | Tmod_ident (path, _lident) -> Ident path | Tmod_structure structure -> let env = - { - env with - scope = impItemsExtent structure.str_items; - modulePath = ExportedModule (moduleName, env.modulePath); - } + {env with modulePath = ExportedModule (moduleName, env.modulePath)} in let contents = forStructure ~env structure.str_items in Structure contents @@ -465,12 +443,6 @@ and forModule env mod_desc moduleName = let stamp = Ident.binding_time ident in let declared = ProcessAttributes.newDeclared ~item:kind ~name:argName - ~scope: - { - Location.loc_start = t.mty_loc.loc_end; - loc_end = env.scope.loc_end; - loc_ghost = false; - } ~extent:t.Typedtree.mty_loc ~stamp ~modulePath:NotVisible false [] in Stamps.addModule env.stamps stamp declared)); @@ -522,24 +494,8 @@ let forCmt ~moduleName ~uri ({cmt_modname; cmt_annots} : Cmt_format.cmt_infos) = | _ -> None) |> List.concat in - let extent = impItemsExtent items in - let extent = - { - extent with - loc_end = - { - extent.loc_end with - pos_lnum = extent.loc_end.pos_lnum + 1000000; - pos_cnum = extent.loc_end.pos_cnum + 100000000; - }; - } - in let env = - { - Env.scope = extent; - stamps = Stamps.init (); - modulePath = File (uri, moduleName); - } + {Env.stamps = Stamps.init (); modulePath = File (uri, moduleName)} in let structure = forStructure ~env items in {File.uri; moduleName = cmt_modname; stamps = env.stamps; structure} @@ -554,31 +510,19 @@ let forCmt ~moduleName ~uri ({cmt_modname; cmt_annots} : Cmt_format.cmt_infos) = |> List.concat in let env = - { - Env.scope = sigItemsExtent items; - stamps = Stamps.init (); - modulePath = File (uri, moduleName); - } + {Env.stamps = Stamps.init (); modulePath = File (uri, moduleName)} in let structure = forSignature ~env items in {uri; moduleName = cmt_modname; stamps = env.stamps; structure} | Implementation structure -> let env = - { - Env.scope = impItemsExtent structure.str_items; - stamps = Stamps.init (); - modulePath = File (uri, moduleName); - } + {Env.stamps = Stamps.init (); modulePath = File (uri, moduleName)} in let structure = forStructure ~env structure.str_items in {uri; moduleName = cmt_modname; stamps = env.stamps; structure} | Interface signature -> let env = - { - Env.scope = sigItemsExtent signature.sig_items; - stamps = Stamps.init (); - modulePath = File (uri, moduleName); - } + {Env.stamps = Stamps.init (); modulePath = File (uri, moduleName)} in let structure = forSignature ~env signature.sig_items in {uri; moduleName = cmt_modname; stamps = env.stamps; structure} @@ -884,10 +828,6 @@ struct addLocItem extra nameLoc (Typed (name, constructorType, locType)) | _ -> () - let currentScopeExtent () = - if !Collector.scopeExtent = [] then Location.none - else List.hd !Collector.scopeExtent - let addScopeExtent loc = Collector.scopeExtent := loc :: !Collector.scopeExtent @@ -972,12 +912,6 @@ struct if Stamps.findValue Collector.file.stamps stamp = None then ( let declared = ProcessAttributes.newDeclared ~name ~stamp ~extent:val_loc - ~scope: - { - loc_ghost = true; - loc_start = val_loc.loc_end; - loc_end = (currentScopeExtent ()).loc_end; - } ~modulePath:NotVisible ~item:val_desc.ctyp_type false val_attributes in Stamps.addValue Collector.file.stamps stamp declared; @@ -996,15 +930,8 @@ struct let addForPattern stamp name = if Stamps.findValue Collector.file.stamps stamp = None then ( let declared = - ProcessAttributes.newDeclared ~name ~stamp - ~scope: - { - loc_ghost = true; - loc_start = pat_loc.loc_end; - loc_end = (currentScopeExtent ()).loc_end; - } - ~modulePath:NotVisible ~extent:pat_loc ~item:pat_type false - pat_attributes + ProcessAttributes.newDeclared ~name ~stamp ~modulePath:NotVisible + ~extent:pat_loc ~item:pat_type false pat_attributes in Stamps.addValue Collector.file.stamps stamp declared; addReference stamp name.loc; diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 2be6bbd4b..310c2920c 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -97,7 +97,6 @@ module Declared = struct type 'item t = { name : string Location.loc; extentLoc : Location.t; - scopeLoc : Location.t; stamp : int; modulePath : modulePath; isExported : bool; @@ -238,7 +237,7 @@ module Completion = struct end module Env = struct - type t = {stamps : Stamps.t; modulePath : modulePath; scope : Location.t} + type t = {stamps : Stamps.t; modulePath : modulePath} end type filePath = string From a457188338193950daed427dbefca8df8de4d485 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 18:04:22 +0200 Subject: [PATCH 122/135] Remove code to process extent. --- analysis/src/ProcessCmt.ml | 84 -------------------------------------- 1 file changed, 84 deletions(-) diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index e0294aee2..e9ba99a04 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -1,26 +1,6 @@ open Typedtree open SharedTypes -let locsExtent locs = - let locs = locs |> List.filter (fun loc -> not loc.Location.loc_ghost) in - (* This filters out ghost locs, but still assumes positions are ordered. - Perhaps compute min/max. *) - match locs with - | [] -> Location.none - | first :: _ -> - let last = List.nth locs (List.length locs - 1) in - let first, last = - if first.loc_start.pos_cnum < last.loc_start.pos_cnum then (first, last) - else (last, first) - in - {loc_ghost = true; loc_start = first.loc_start; loc_end = last.loc_end} - -let impItemsExtent items = - items |> List.map (fun item -> item.Typedtree.str_loc) |> locsExtent - -let sigItemsExtent items = - items |> List.map (fun item -> item.Typedtree.sig_loc) |> locsExtent - let addItem ~(name : string Location.loc) ~extent ~stamp ~(env : Env.t) ~item attributes addExported addStamp = let isExported = addExported name.txt stamp in @@ -649,7 +629,6 @@ let fromCompilerPath ~(env : QueryEnv.t) path = module F (Collector : sig val extra : extra val file : File.t - val scopeExtent : Location.t list ref end) = struct let extra = Collector.extra @@ -828,13 +807,6 @@ struct addLocItem extra nameLoc (Typed (name, constructorType, locType)) | _ -> () - let addScopeExtent loc = - Collector.scopeExtent := loc :: !Collector.scopeExtent - - let popScopeExtent () = - if List.length !Collector.scopeExtent > 1 then - Collector.scopeExtent := List.tl !Collector.scopeExtent - let rec lidIsComplex (lid : Longident.t) = match lid with | Lapply _ -> true @@ -890,21 +862,6 @@ struct Hashtbl.replace Collector.extra.opens loc () | _ -> () - let enter_structure {str_items} = - if str_items <> [] then - let first = List.hd str_items in - let last = List.nth str_items (List.length str_items - 1) in - let extent = - { - Location.loc_ghost = true; - loc_start = first.str_loc.loc_start; - loc_end = last.str_loc.loc_end; - } - in - addScopeExtent extent - - let leave_structure str = if str.str_items <> [] then popScopeExtent () - let enter_signature_item item = match item.sig_desc with | Tsig_value {val_id; val_loc; val_name = name; val_desc; val_attributes} -> @@ -978,42 +935,14 @@ struct addForConstructor expression.exp_type lident constructor | Texp_field (inner, lident, _label_description) -> addForField inner.exp_type expression.exp_type lident - | Texp_let (_, _, _) -> - (* TODO this scope tracking won't work for recursive *) - addScopeExtent expression.exp_loc - | Texp_function {cases} -> ( - match cases with - | [{c_lhs = {pat_desc = Tpat_var _}; c_rhs}] -> - addScopeExtent c_rhs.exp_loc - | _ -> ()) - | _ -> () - - let leave_expression expression = - match expression.exp_desc with - | Texp_let (_isrec, _bindings, _expr) -> popScopeExtent () - | Texp_function {cases} -> ( - match cases with [_] -> popScopeExtent () | _ -> ()) | _ -> () end let extraForStructureItems ~(file : File.t) (items : Typedtree.structure_item list) parts = let extra = extraForFile ~file in - let extent = impItemsExtent items in - let extent = - { - extent with - loc_end = - { - extent.loc_end with - pos_lnum = extent.loc_end.pos_lnum + 1000000; - pos_cnum = extent.loc_end.pos_cnum + 100000000; - }; - } - in (* TODO look through parts and extend the extent *) let module Iter = TypedtreeIter.MakeIterator (F (struct - let scopeExtent = ref [extent] let extra = extra let file = file end)) in @@ -1034,21 +963,8 @@ let extraForStructureItems ~(file : File.t) let extraForSignatureItems ~(file : File.t) (items : Typedtree.signature_item list) parts = let extra = extraForFile ~file in - let extent = sigItemsExtent items in - let extent = - { - extent with - loc_end = - { - extent.loc_end with - pos_lnum = extent.loc_end.pos_lnum + 1000000; - pos_cnum = extent.loc_end.pos_cnum + 100000000; - }; - } - in (* TODO look through parts and extend the extent *) let module Iter = TypedtreeIter.MakeIterator (F (struct - let scopeExtent = ref [extent] let extra = extra let file = file end)) in From 6e856a8e458a747bdb77f61ba5b73180ae5a8d5b Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 18:16:42 +0200 Subject: [PATCH 123/135] Add example showing how parser recovery could build id too eagerly.
parses as x=Outer.Inner.name --- analysis/tests/src/Jsx.res | 34 +++++-- analysis/tests/src/expected/Jsx.res.txt | 124 ++++++++++-------------- 2 files changed, 76 insertions(+), 82 deletions(-) diff --git a/analysis/tests/src/Jsx.res b/analysis/tests/src/Jsx.res index e0b5a7f4a..0eb570080 100644 --- a/analysis/tests/src/Jsx.res +++ b/analysis/tests/src/Jsx.res @@ -6,11 +6,11 @@ module M = { let _ = // ^def -//^com React.string(first) @@ -42,7 +42,7 @@ let y = 44 //^com -// ^def +// ^def module Ext = { @react.component @module("@material-ui/core") @@ -53,7 +53,7 @@ let _ = (Ext.make, Ext.makeProps) //^com children } -let _ =
-//^com
+//^com
module DefineSomeFields = { type r = {thisField: int, thatField: string} let thisValue = 10 -// ^com let foo x = x.th + // ^com let foo x = x.th } // ^com let q = DefineSomeFields. @@ -84,3 +84,23 @@ module DefineSomeFields = { // ^com let foo x = x.DefineSomeFields.th let _ = x => x.DefineSomeFields.thisField + DefineSomeFields.thisValue + +module Outer = { + module Inner = { + let hello = 3 + } +} +let _ = Outer.Inner.hello + +let _ = +
+ +let _ = +
+ diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index c5f0411c2..c008bbbd2 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -2,30 +2,12 @@ Definition tests/src/Jsx.res 5:9 {"uri": "Jsx.res", "range": {"start": {"line": 2, "character": 6}, "end": {"line": 2, "character": 10}}} Complete tests/src/Jsx.res 7:2 -posCursor:[8:14] posNoWhite:[8:12] Found expr:[8:2->8:13] +posCursor:[8:13] posNoWhite:[8:12] Found expr:[8:2->8:13] JSX 8:3] second[8:4->8:10]=...[8:11->8:13]> _children:None -posCursor:[8:14] posNoWhite:[8:12] Found expr:[8:11->8:13] +posCursor:[8:13] posNoWhite:[8:12] Found expr:[8:11->8:13] Pexp_ident fi:[8:11->8:13] -Completable: Cjsx([M], "", [second]) -[{ - "label": "first", - "kind": 4, - "tags": [], - "detail": "string", - "documentation": null - }, { - "label": "fun", - "kind": 4, - "tags": [], - "detail": "option", - "documentation": null - }, { - "label": "key", - "kind": 4, - "tags": [], - "detail": "string", - "documentation": null - }] +Completable: Cpath Value[fi] +[] Complete tests/src/Jsx.res 9:2 posCursor:[10:18] posNoWhite:[10:17] Found expr:[10:2->10:18] @@ -48,34 +30,28 @@ Completable: Cjsx([M], f, [second, f]) }] Complete tests/src/Jsx.res 11:2 -posCursor:[12:12] posNoWhite:[12:10] Found expr:[12:10->12:11] +posCursor:[12:11] posNoWhite:[12:10] Found expr:[12:10->12:11] JSX 12:11] > _children:None -posCursor:[12:12] posNoWhite:[12:10] Found expr:[12:10->12:11] +posCursor:[12:11] posNoWhite:[12:10] Found expr:[12:10->12:11] Pexp_ident M.createElement:[12:10->12:11] -Completable: Cjsx([M], "", []) +Completable: Cpath Module[M] [{ - "label": "second", - "kind": 4, - "tags": [], - "detail": "option", - "documentation": null - }, { - "label": "first", - "kind": 4, + "label": "M", + "kind": 9, "tags": [], - "detail": "string", + "detail": "module", "documentation": null }, { - "label": "fun", - "kind": 4, + "label": "Map", + "kind": 9, "tags": [], - "detail": "option", + "detail": "file module", "documentation": null }, { - "label": "key", - "kind": 4, + "label": "MoreLabels", + "kind": 9, "tags": [], - "detail": "string", + "detail": "file module", "documentation": null }] @@ -265,30 +241,12 @@ Completable: Cjsx([Ext], al, [al]) }] Complete tests/src/Jsx.res 54:2 -posCursor:[55:10] posNoWhite:[55:8] Found expr:[55:2->55:9] +posCursor:[55:9] posNoWhite:[55:8] Found expr:[55:2->55:9] JSX 55:3] first[55:4->55:9]=...[55:4->55:9]> _children:None -posCursor:[55:10] posNoWhite:[55:8] Found expr:[55:4->55:9] +posCursor:[55:9] posNoWhite:[55:8] Found expr:[55:4->55:9] Pexp_ident first:[55:4->55:9] -Completable: Cjsx([M], "", [first]) -[{ - "label": "second", - "kind": 4, - "tags": [], - "detail": "option", - "documentation": null - }, { - "label": "fun", - "kind": 4, - "tags": [], - "detail": "option", - "documentation": null - }, { - "label": "key", - "kind": 4, - "tags": [], - "detail": "string", - "documentation": null - }] +Completable: Cjsx([M], first, [first]) +[] Complete tests/src/Jsx.res 56:2 posCursor:[57:14] posNoWhite:[57:13] Found expr:[57:2->57:14] @@ -330,22 +288,16 @@ Pexp_construct []:__ghost__[61:3->63:20] None [] Complete tests/src/Jsx.res 68:2 -posCursor:[69:15] posNoWhite:[69:13] Found expr:[69:2->69:14] +posCursor:[69:14] posNoWhite:[69:13] Found expr:[69:2->69:14] JSX 69:14] > _children:None -posCursor:[69:15] posNoWhite:[69:13] Found expr:[69:2->69:14] +posCursor:[69:14] posNoWhite:[69:13] Found expr:[69:2->69:14] Pexp_ident WithChildren.createElement:[69:2->69:14] -Completable: Cjsx([WithChildren], "", []) +Completable: Cpath Module[WithChildren] [{ - "label": "name", - "kind": 4, - "tags": [], - "detail": "string", - "documentation": null - }, { - "label": "key", - "kind": 4, + "label": "WithChildren", + "kind": 9, "tags": [], - "detail": "string", + "detail": "module", "documentation": null }] @@ -387,7 +339,7 @@ Completable: Cpath Type[ReactDOMR] "documentation": null }] -Complete tests/src/Jsx.res 77:3 +Complete tests/src/Jsx.res 77:5 posCursor:[78:17] posNoWhite:[78:16] Found expr:[78:9->78:17] Pexp_apply ...[78:11->78:12] (...[78:9->78:10], ...[78:13->78:17]) posCursor:[78:17] posNoWhite:[78:16] Found expr:[78:13->78:17] @@ -429,3 +381,25 @@ Completable: Cpath Module[DefineSomeFields].th "documentation": null }] +Complete tests/src/Jsx.res 95:5 +posCursor:[96:16] posNoWhite:[96:15] Found expr:[95:3->98:4] +JSX 95:6] x[96:1->96:2]=...[96:3->96:16] name[97:4->97:8]=...[97:9->97:11]> _children:98:2 +posCursor:[96:16] posNoWhite:[96:15] Found expr:[96:3->96:16] +Pexp_ident Outer.Inner.h:[96:3->96:16] +Completable: Cpath Value[Outer, Inner, h] +[{ + "label": "hello", + "kind": 12, + "tags": [], + "detail": "int", + "documentation": null + }] + +Complete tests/src/Jsx.res 101:5 +posCursor:[102:15] posNoWhite:[102:14] Found expr:[101:3->103:9] +JSX 101:6] x[102:1->102:2]=...[102:3->103:8]> _children:None +posCursor:[102:15] posNoWhite:[102:14] Found expr:[102:3->103:8] +Pexp_ident Outer.Inner.name:[102:3->103:8] +Completable: Cpath Value[Outer, Inner, name] +[] + From 5cbdcfa372e4ab151492e3a742e8edde5cbad845 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 18:41:16 +0200 Subject: [PATCH 124/135] Fix issue with "." and parser recovery. Sometimes "Foo. " is followed by "bar" and the parser's behaviour is to parse as "Foo.bar". This gets back the intended path "Foo." --- analysis/src/Commands.ml | 32 +++++++++++++++++-- analysis/tests/src/Jsx.res | 1 + .../tests/src/expected/Completion.res.txt | 3 ++ analysis/tests/src/expected/Jsx.res.txt | 22 +++++++++---- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 59e07ef36..483b96124 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -271,6 +271,11 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (line, max 0 col - offset + offsetNoWhite) in let posBeforeCursor = (fst posCursor, max 0 (snd posCursor - 1)) in + let charBeforeCursor, charAtCursor = + match PartialParser.positionToOffset text posCursor with + | Some offset when offset > 0 -> (Some text.[offset - 1], Some text.[offset]) + | _ -> (None, None) + in let found = ref false in let result = ref None in @@ -467,9 +472,32 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = Printf.printf "Pexp_ident %s:%s\n" (Utils.flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); + let idBreaksUp = + charBeforeCursor = Some '.' + && + match charAtCursor with + | Some (' ' | '\t' | '\n' | '\r') -> true + | _ -> false + in if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - setResult - (PartialParser.Cpath (CPId (Utils.flattenLongIdent id.txt, Value))) + let path_ = id.txt |> Utils.flattenLongIdent in + let path = + if idBreaksUp then ( + (* Sometimes "Foo. " is followed by "bar" and the parser's + behaviour is to parse as "Foo.bar". + This gets back the intended path "Foo." *) + let path = + match path_ |> List.rev with + | _last :: pathRev -> List.rev ("" :: pathRev) + | path -> path + in + if debug then + Printf.printf "Id breaks up. New path:%s\n" + (path |> String.concat "."); + path) + else path_ + in + setResult (PartialParser.Cpath (CPId (path, Value))) | Pexp_construct (id, eOpt) -> if debug then Printf.printf "Pexp_construct %s:%s %s\n" diff --git a/analysis/tests/src/Jsx.res b/analysis/tests/src/Jsx.res index 0eb570080..c87938a29 100644 --- a/analysis/tests/src/Jsx.res +++ b/analysis/tests/src/Jsx.res @@ -98,6 +98,7 @@ let _ = name="" /> + let _ =
6:6] Pexp_ident Array.:[2:1->6:6] +Id breaks up. New path:Array. Completable: Cpath Value[Array, ""] [{ "label": "fold_left", @@ -1028,6 +1029,7 @@ posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] JSX 126:4] > _children:None posCursor:[126:4] posNoWhite:[126:3] Found expr:[126:2->126:4] Pexp_ident O..createElement:[126:2->126:4] +Id breaks up. New path:O.. Completable: Cpath Module[O, ""] [{ "label": "Comp", @@ -1173,6 +1175,7 @@ Completable: Cpath Module[For] Complete tests/src/Completion.res 155:3 posCursor:[156:9] posNoWhite:[156:8] Found expr:[156:1->158:6] Pexp_ident Private.:[156:1->158:6] +Id breaks up. New path:Private. Completable: Cpath Value[Private, ""] [{ "label": "b", diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index c008bbbd2..405880897 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -350,6 +350,7 @@ Completable: Cpath Value[x].th Complete tests/src/Jsx.res 80:3 posCursor:[81:26] posNoWhite:[81:25] Found expr:[81:9->85:3] Pexp_ident DefineSomeFields.:[81:9->85:3] +Id breaks up. New path:DefineSomeFields. posCursor:[81:26] posNoWhite:[81:25] Found expr:[0:-1->85:70] Pexp_apply ...[85:6->85:7] (...[85:8->85:70]) Completable: Cpath Value[DefineSomeFields, ""] @@ -395,11 +396,18 @@ Completable: Cpath Value[Outer, Inner, h] "documentation": null }] -Complete tests/src/Jsx.res 101:5 -posCursor:[102:15] posNoWhite:[102:14] Found expr:[101:3->103:9] -JSX 101:6] x[102:1->102:2]=...[102:3->103:8]> _children:None -posCursor:[102:15] posNoWhite:[102:14] Found expr:[102:3->103:8] -Pexp_ident Outer.Inner.name:[102:3->103:8] -Completable: Cpath Value[Outer, Inner, name] -[] +Complete tests/src/Jsx.res 102:5 +posCursor:[103:15] posNoWhite:[103:14] Found expr:[102:3->104:9] +JSX 102:6] x[103:1->103:2]=...[103:3->104:8]> _children:None +posCursor:[103:15] posNoWhite:[103:14] Found expr:[103:3->104:8] +Pexp_ident Outer.Inner.name:[103:3->104:8] +Id breaks up. New path:Outer.Inner. +Completable: Cpath Value[Outer, Inner, ""] +[{ + "label": "hello", + "kind": 12, + "tags": [], + "detail": "int", + "documentation": null + }] From 69927e4b3d5a694ecc0c199aadf247e71853dc4e Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 20:30:16 +0200 Subject: [PATCH 125/135] Allow autocomplete on blank. Complete with anything in scope including open modules and pervasives. --- analysis/src/Commands.ml | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 483b96124..9a38f7b72 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -271,10 +271,17 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (line, max 0 col - offset + offsetNoWhite) in let posBeforeCursor = (fst posCursor, max 0 (snd posCursor - 1)) in - let charBeforeCursor, charAtCursor = + let blankAfterCursor = match PartialParser.positionToOffset text posCursor with - | Some offset when offset > 0 -> (Some text.[offset - 1], Some text.[offset]) - | _ -> (None, None) + | 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 + | _ -> None) + | _ -> None in let found = ref false in @@ -472,17 +479,10 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = Printf.printf "Pexp_ident %s:%s\n" (Utils.flattenLongIdent id.txt |> String.concat ".") (Loc.toString id.loc); - let idBreaksUp = - charBeforeCursor = Some '.' - && - match charAtCursor with - | Some (' ' | '\t' | '\n' | '\r') -> true - | _ -> false - in if id.loc |> Loc.hasPos ~pos:posBeforeCursor then let path_ = id.txt |> Utils.flattenLongIdent in let path = - if idBreaksUp then ( + if blankAfterCursor = Some '.' then ( (* Sometimes "Foo. " is followed by "bar" and the parser's behaviour is to parse as "Foo.bar". This gets back the intended path "Foo." *) @@ -717,12 +717,16 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = in let {Res_driver.parsetree = str} = parser ~filename:currentFile in iterator.structure iterator str |> ignore; + if blankAfterCursor = Some ' ' || blankAfterCursor = Some '\n' then + setResult (PartialParser.Cpath (CPId ([""], Value))); if !found = false then if debug then Printf.printf "XXX Not found!\n"; !result) else if Filename.check_suffix path ".resi" then ( let parser = Res_driver.parsingEngine.parseInterface ~forPrinter:false in let {Res_driver.parsetree = signature} = parser ~filename:currentFile in iterator.signature iterator signature |> ignore; + if blankAfterCursor = Some ' ' || blankAfterCursor = Some '\n' then + setResult (PartialParser.Cpath (CPId ([""], Type))); if !found = false then if debug then Printf.printf "XXX Not found!\n"; !result) else None From 224f4b702c2db9b4b10d76ef7adb38c2d3c6dec5 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Wed, 20 Apr 2022 20:47:15 +0200 Subject: [PATCH 126/135] Fix local completions for types and modules. --- analysis/src/NewCompletions.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index e3851e953..f60616aad 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -793,7 +793,7 @@ let findLocalCompletionsForTypes ~env ~prefix ~exact ~opens ~scope = (fun results env -> let completionsFromThisOpen = findAllCompletions ~env ~prefix ~exact ~namesUsed - ~completionContext:Value + ~completionContext:Type in completionsFromThisOpen @ results) [] @@ -837,7 +837,7 @@ let findLocalCompletionsForModules ~env ~prefix ~exact ~opens ~scope = (fun results env -> let completionsFromThisOpen = findAllCompletions ~env ~prefix ~exact ~namesUsed - ~completionContext:Value + ~completionContext:Module in completionsFromThisOpen @ results) [] From 511faa87f4e92e4e3cf9afe09fdfd5c20bc3801b Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 21 Apr 2022 21:32:19 +0200 Subject: [PATCH 127/135] Fix issue whith completing x= in JSX. When completing `x=`, if the following contains `name=...`, then the parser interprets it as `x=name`. Now detect that the cursor is after the label `x` but before `name`, and don;t interpret the situation as label completion. --- analysis/src/Commands.ml | 6 +++++- analysis/tests/src/Jsx.res | 7 ++++++- analysis/tests/src/expected/Jsx.res.txt | 15 ++++++++++----- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 9a38f7b72..c647faf49 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -25,6 +25,10 @@ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName | prop :: rest -> if prop.posStart <= posBeforeCursor && posBeforeCursor < prop.posEnd then Some (PartialParser.Cjsx (jsxProps.componentPath, prop.name, allLabels)) + else if + prop.posEnd <= posBeforeCursor + && posBeforeCursor < Loc.start prop.exp.pexp_loc + then None else if prop.exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None else loop rest | [] -> @@ -99,7 +103,7 @@ let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = ( PartialParser.positionToOffset text ePosStart, PartialParser.positionToOffset text ePosEnd ) with - | Some offsetStart, Some offsetEnd -> + | Some offsetStart, Some offsetEnd when not eProp.pexp_loc.loc_ghost -> let label = String.sub text lastOffset (offsetStart - lastOffset) in let labelPos = match extractLabelPos ~pos:lastPos ~i:0 label with diff --git a/analysis/tests/src/Jsx.res b/analysis/tests/src/Jsx.res index c87938a29..26a71ed1c 100644 --- a/analysis/tests/src/Jsx.res +++ b/analysis/tests/src/Jsx.res @@ -98,10 +98,15 @@ let _ = name="" /> - let _ =
+ + +let _ = +
diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 405880897..b14c9ac4b 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -396,11 +396,11 @@ Completable: Cpath Value[Outer, Inner, h] "documentation": null }] -Complete tests/src/Jsx.res 102:5 -posCursor:[103:15] posNoWhite:[103:14] Found expr:[102:3->104:9] -JSX 102:6] x[103:1->103:2]=...[103:3->104:8]> _children:None -posCursor:[103:15] posNoWhite:[103:14] Found expr:[103:3->104:8] -Pexp_ident Outer.Inner.name:[103:3->104:8] +Complete tests/src/Jsx.res 101:5 +posCursor:[102:15] posNoWhite:[102:14] Found expr:[101:3->103:9] +JSX 101:6] x[102:1->102:2]=...[102:3->103:8]> _children:None +posCursor:[102:15] posNoWhite:[102:14] Found expr:[102:3->103:8] +Pexp_ident Outer.Inner.name:[102:3->103:8] Id breaks up. New path:Outer.Inner. Completable: Cpath Value[Outer, Inner, ""] [{ @@ -411,3 +411,8 @@ Completable: Cpath Value[Outer, Inner, ""] "documentation": null }] +Complete tests/src/Jsx.res 107:5 +posCursor:[108:3] posNoWhite:[108:2] Found expr:[107:3->109:9] +JSX 107:6] x[108:1->108:2]=...[109:4->109:8]> _children:None +[] + From 39c3cef8592852e3d45e99bac3ee197e569c9985 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 22 Apr 2022 07:47:27 +0200 Subject: [PATCH 128/135] Clean up parser recovery. Use symbol "_" like in other places in the parser. --- analysis/src/Commands.ml | 2 +- analysis/src/Utils.ml | 2 +- analysis/src/vendor/res_outcome_printer/res_core.ml | 9 ++++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index c647faf49..9e94fa427 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -534,7 +534,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = let contextPath = PartialParser.CPField ( CPId (Utils.flattenLongIdent id, Module), - if name = "$" then "" else name ) + if name = "_" then "" else name ) in setResult (PartialParser.Cpath contextPath) | Lapply _ -> () diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml index e8c353466..cb053c523 100644 --- a/analysis/src/Utils.ml +++ b/analysis/src/Utils.ml @@ -60,7 +60,7 @@ let flattenLongIdent ?(jsx = false) lid = | Ldot (lid, txt) -> let acc = if jsx && txt = "createElement" then acc - else if txt = "$" then "" :: acc + else if txt = "_" then "" :: acc else txt :: acc in loop acc lid diff --git a/analysis/src/vendor/res_outcome_printer/res_core.ml b/analysis/src/vendor/res_outcome_printer/res_core.ml index fc018ba65..c400dc9e4 100644 --- a/analysis/src/vendor/res_outcome_printer/res_core.ml +++ b/analysis/src/vendor/res_outcome_printer/res_core.ml @@ -660,7 +660,7 @@ let parseValuePath p = ) | token -> Parser.err p (Diagnostics.unexpected token p.breadcrumbs); - Longident.Ldot (path, "$") + Longident.Ldot (path, "_") in let ident = match p.Parser.token with | Lident ident -> Longident.Lident ident @@ -692,7 +692,7 @@ let parseValuePathTail p startPos ident = loop p (Longident.Ldot (path, ident)) | token -> Parser.err p (Diagnostics.unexpected token p.breadcrumbs); - Location.mkloc (Longident.Ldot (path, "$")) (mkLoc startPos p.prevEndPos) + Location.mkloc (Longident.Ldot (path, "_")) (mkLoc startPos p.prevEndPos) in loop p ident @@ -715,7 +715,7 @@ let parseModuleLongIdentTail ~lowercase p startPos ident = end | t -> Parser.err p (Diagnostics.uident t); - Location.mkloc (Longident.Ldot (acc, "$")) (mkLoc startPos p.prevEndPos) + Location.mkloc (Longident.Ldot (acc, "_")) (mkLoc startPos p.prevEndPos) in loop p ident @@ -3539,9 +3539,8 @@ and parseValueOrConstructor p = Parser.next p; let loc = mkLoc startPos p.prevEndPos in Parser.err p (Diagnostics.unexpected token p.breadcrumbs); - let lident = buildLongident ("$"::acc) in + let lident = buildLongident ("_"::acc) in Ast_helper.Exp.ident ~loc (Location.mkloc lident loc) - (* Recover.defaultExpr() *) in aux p [] From c342b8e845472d2efd5ab51e54dca8124aa57dc7 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 22 Apr 2022 07:52:17 +0200 Subject: [PATCH 129/135] restore normal formatting logic --- server/src/utils.ts | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/server/src/utils.ts b/server/src/utils.ts index 32454ac47..61af68264 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -106,19 +106,18 @@ export let formatCode = (filePath: string, code: string): execResult => { // Default to using the project formatter. If not, use the one we ship with // the analysis binary in the extension itself. - // if (bscNativePath != null) { - // let result = childProcess.execFileSync(bscNativePath, [ - // "-color", - // "never", - // "-format", - // formatTempFileFullPath, - // ]); - // return { - // kind: "success", - // result: result.toString(), - // }; - // } else - { + if (bscNativePath != null) { + let result = childProcess.execFileSync(bscNativePath, [ + "-color", + "never", + "-format", + formatTempFileFullPath, + ]); + return { + kind: "success", + result: result.toString(), + }; + } else { let result = runAnalysisAfterSanityCheck( formatTempFileFullPath, ["format", formatTempFileFullPath], @@ -128,9 +127,9 @@ export let formatCode = (filePath: string, code: string): execResult => { // The formatter returning an empty string means it couldn't format the // sources, probably because of errors. In that case, we bail from // formatting by returning the unformatted content. - // if (result === "") { - // result = code; - // } + if (result === "") { + result = code; + } return { kind: "success", From 20f9c3d5e749177cfeef9fd59b6a55fcfd8fad56 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 22 Apr 2022 08:05:28 +0200 Subject: [PATCH 130/135] Move completion code to separate file. --- analysis/src/Commands.ml | 744 +-------------------------------- analysis/src/Completion.ml | 744 +++++++++++++++++++++++++++++++++ analysis/src/NewCompletions.ml | 20 +- analysis/src/PartialParser.ml | 67 --- analysis/src/SharedTypes.ml | 45 ++ 5 files changed, 802 insertions(+), 818 deletions(-) create mode 100644 analysis/src/Completion.ml diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 9e94fa427..7de4c5f0d 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -1,740 +1,3 @@ -open SharedTypes - -type prop = { - name : string; - posStart : int * int; - posEnd : int * int; - exp : Parsetree.expression; -} - -type jsxProps = { - componentPath : string list; - props : prop list; - childrenStart : (int * int) option; -} - -let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName - = - let allLabels = - List.fold_right - (fun prop allLabels -> prop.name :: allLabels) - jsxProps.props [] - in - let rec loop props = - match props with - | prop :: rest -> - if prop.posStart <= posBeforeCursor && posBeforeCursor < prop.posEnd then - Some (PartialParser.Cjsx (jsxProps.componentPath, prop.name, allLabels)) - else if - prop.posEnd <= posBeforeCursor - && posBeforeCursor < Loc.start prop.exp.pexp_loc - then None - else if prop.exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None - 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 (PartialParser.Cjsx (jsxProps.componentPath, "", allLabels)) - else None - in - loop jsxProps.props - -let rec skipLineComment ~pos ~i str = - if i < String.length str then - match str.[i] with - | '\n' -> Some ((fst pos + 1, 0), i + 1) - | _ -> skipLineComment ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str - else None - -let rec skipComment ~pos ~i ~depth str = - if i < String.length str then - match str.[i] with - | '\n' -> skipComment ~depth ~pos:(fst pos + 1, 0) ~i:(i + 1) str - | '/' when i + 1 < String.length str && str.[i + 1] = '*' -> - skipComment ~depth:(depth + 1) ~pos:(fst pos, snd pos + 2) ~i:(i + 2) str - | '*' when i + 1 < String.length str && str.[i + 1] = '/' -> - if depth > 1 then - skipComment ~depth:(depth - 1) - ~pos:(fst pos, snd pos + 2) - ~i:(i + 2) str - else Some ((fst pos, snd pos + 2), i + 2) - | _ -> skipComment ~depth ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str - else None - -let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = - let rec extractLabelPos ~pos ~i str = - if i < String.length str then - match str.[i] with - | '/' when i + 1 < String.length str && str.[i + 1] = '/' -> ( - match skipLineComment ~pos ~i str with - | Some (pos, i) -> extractLabelPos ~pos ~i str - | None -> None) - | '/' when i + 1 < String.length str && str.[i + 1] = '*' -> ( - match skipComment ~depth:0 ~pos ~i str with - | Some (pos, i) -> extractLabelPos ~pos ~i str - | None -> None) - | ' ' | '\r' | '\t' -> - extractLabelPos ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str - | '\n' -> extractLabelPos ~pos:(fst pos + 1, 0) ~i:(i + 1) str - | 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' -> Some pos - | _ -> None - else None - in - let thisCaseShouldNotHappen = - {componentPath = []; props = []; childrenStart = None} - in - let rec processProps ~lastOffset ~lastPos ~acc args = - match args with - | (Asttypes.Labelled "children", {Parsetree.pexp_loc}) :: _ -> - { - componentPath = Utils.flattenLongIdent ~jsx:true compName.txt; - props = List.rev acc; - childrenStart = - (if pexp_loc.loc_ghost then None else Some (Loc.start pexp_loc)); - } - | ((Labelled s | Optional s), (eProp : Parsetree.expression)) :: rest -> ( - let ePosStart, ePosEnd = Loc.range eProp.pexp_loc in - match - ( PartialParser.positionToOffset text ePosStart, - PartialParser.positionToOffset text ePosEnd ) - with - | Some offsetStart, Some offsetEnd when not eProp.pexp_loc.loc_ghost -> - let label = String.sub text lastOffset (offsetStart - lastOffset) in - let labelPos = - match extractLabelPos ~pos:lastPos ~i:0 label with - | Some pos -> pos - | None -> (* Must be punned *) ePosStart - in - processProps - ~acc: - ({ - name = s; - posStart = labelPos; - posEnd = (fst labelPos, snd labelPos + String.length s); - exp = eProp; - } - :: acc) - ~lastOffset:offsetEnd ~lastPos:ePosEnd rest - | _ -> thisCaseShouldNotHappen) - | _ -> thisCaseShouldNotHappen - in - let posAfterCompName = Loc.end_ compName.loc in - let offsetAfterCompName = - match PartialParser.positionToOffset text posAfterCompName with - | None -> assert false - | Some offset -> offset - in - args - |> processProps ~lastOffset:offsetAfterCompName ~lastPos:posAfterCompName - ~acc:[] - -type labelled = { - name : string; - opt : bool; - posStart : int * int; - posEnd : int * int; -} - -type label = labelled option -type arg = {label : label; exp : Parsetree.expression} - -let findExpApplyCompletable ~(args : arg list) ~endPos ~posBeforeCursor - ~(funName : Longident.t Location.loc) = - let funPath = Utils.flattenLongIdent funName.txt in - let posAfterFunName = Loc.end_ funName.loc in - let allNames = - List.fold_right - (fun arg allLabels -> - match arg with - | {label = Some labelled} -> labelled.name :: allLabels - | {label = None} -> allLabels) - args [] - in - let rec loop args = - match args with - | {label = Some labelled; exp} :: rest -> - if - labelled.posStart <= posBeforeCursor - && posBeforeCursor < labelled.posEnd - then Some (PartialParser.Clabel (funPath, labelled.name, allNames)) - else if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None - else loop rest - | {label = None; exp} :: rest -> - if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None - else loop rest - | [] -> - if posAfterFunName <= posBeforeCursor && posBeforeCursor < endPos then - Some (PartialParser.Clabel (funPath, "", allNames)) - else None - in - loop args - -let extractExpApplyArgs ~text ~(funName : Longident.t Location.loc) ~args = - let rec extractLabelPos ~pos ~i str = - if i < String.length str then - match str.[i] with - | '/' when i + 1 < String.length str && str.[i + 1] = '/' -> ( - match skipLineComment ~pos ~i str with - | Some (pos, i) -> extractLabelPos ~pos ~i str - | None -> None) - | '/' when i + 1 < String.length str && str.[i + 1] = '*' -> ( - match skipComment ~depth:0 ~pos ~i str with - | Some (pos, i) -> extractLabelPos ~pos ~i str - | None -> None) - | ' ' | '\r' | '\t' | ',' | '(' | '~' -> - extractLabelPos ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str - | '\n' -> extractLabelPos ~pos:(fst pos + 1, 0) ~i:(i + 1) str - | 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' -> Some pos - | _ -> None - else None - in - let rec processArgs ~lastOffset ~lastPos ~acc args = - match args with - | (((Asttypes.Labelled s | Optional s) as label), (e : Parsetree.expression)) - :: rest -> ( - let ePosStart, ePosEnd = Loc.range e.pexp_loc in - match - ( PartialParser.positionToOffset text ePosStart, - PartialParser.positionToOffset text ePosEnd ) - with - | Some offsetStart, Some offsetEnd -> - let labelText = String.sub text lastOffset (offsetStart - lastOffset) in - let labelPos = - match extractLabelPos ~pos:lastPos ~i:0 labelText with - | Some pos -> pos - | None -> (* Must be punned *) ePosStart - in - let labelled = - { - name = s; - opt = (match label with Optional _ -> true | _ -> false); - posStart = labelPos; - posEnd = (fst labelPos, snd labelPos + String.length s); - } - in - processArgs - ~acc:({label = Some labelled; exp = e} :: acc) - ~lastOffset:offsetEnd ~lastPos:ePosEnd rest - | _ -> assert false) - | (Asttypes.Nolabel, (e : Parsetree.expression)) :: rest -> ( - if e.pexp_loc.loc_ghost then processArgs ~acc ~lastOffset ~lastPos rest - else - let ePosEnd = Loc.end_ e.pexp_loc in - match PartialParser.positionToOffset text ePosEnd with - | Some offsetEnd -> - processArgs - ~acc:({label = None; exp = e} :: acc) - ~lastOffset:offsetEnd ~lastPos:ePosEnd rest - | _ -> - (* should not happen *) - assert false) - | [] -> List.rev acc - in - let lastPos = Loc.end_ funName.loc in - let lastOffset = - match PartialParser.positionToOffset text lastPos with - | Some offset -> offset - | None -> assert false - in - args |> processArgs ~lastOffset ~lastPos ~acc:[] - -let rec exporToContextPath (e : Parsetree.expression) = - match e.pexp_desc with - | Pexp_constant (Pconst_string _) -> Some PartialParser.CPString - | Pexp_array _ -> Some PartialParser.CPArray - | Pexp_ident {txt} -> - Some (PartialParser.CPId (Utils.flattenLongIdent txt, Value)) - | Pexp_field (e1, {txt = Lident name}) -> ( - match exporToContextPath e1 with - | Some contextPath -> Some (CPField (contextPath, name)) - | _ -> None) - | Pexp_field (_, {txt = Ldot (lid, name)}) -> - (* Case x.M.field ignore the x part *) - Some - (PartialParser.CPField (CPId (Utils.flattenLongIdent lid, Module), name)) - | Pexp_send (e1, {txt}) -> ( - match exporToContextPath e1 with - | None -> None - | Some contexPath -> Some (CPObj (contexPath, txt))) - | _ -> None - -let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = - let offset = - match PartialParser.positionToOffset text posCursor with - | Some offset -> offset - | None -> assert false - in - let offsetNoWhite = PartialParser.skipWhite text (offset - 1) in - let posNoWhite = - let line, col = posCursor in - (line, max 0 col - offset + offsetNoWhite) - in - let posBeforeCursor = (fst posCursor, max 0 (snd posCursor - 1)) in - let blankAfterCursor = - match PartialParser.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 - | _ -> None) - | _ -> None - in - - let found = ref false in - let result = ref None in - let scope = ref (Scope.create ()) in - let setResultOpt x = - if !result = None then - match x with None -> () | Some x -> result := Some (x, !scope) - in - let setResult x = setResultOpt (Some x) in - let scopeValueDescription (vd : Parsetree.value_description) = - scope := - !scope |> Scope.addValue ~name:vd.pval_name.txt ~loc:vd.pval_name.loc - in - let scopeValueBinding (vb : Parsetree.value_binding) = - match vb.pvb_pat.ppat_desc with - | Ppat_var {txt; loc} - | Ppat_constraint ({ppat_desc = Ppat_var {txt; loc}}, _) -> - scope := !scope |> Scope.addValue ~name:txt ~loc - | _ -> () - in - let scopeTypeKind (tk : Parsetree.type_kind) = - match tk with - | Ptype_variant constrDecls -> - constrDecls - |> List.iter (fun (cd : Parsetree.constructor_declaration) -> - scope := - !scope - |> Scope.addConstructor ~name:cd.pcd_name.txt ~loc:cd.pcd_loc) - | Ptype_record labelDecls -> - labelDecls - |> List.iter (fun (ld : Parsetree.label_declaration) -> - scope := - !scope |> Scope.addField ~name:ld.pld_name.txt ~loc:ld.pld_loc) - | _ -> () - in - let scopeTypeDeclaration (td : Parsetree.type_declaration) = - scope := - !scope |> Scope.addType ~name:td.ptype_name.txt ~loc:td.ptype_name.loc; - scopeTypeKind td.ptype_kind - in - let scopeModuleBinding (mb : Parsetree.module_binding) = - scope := - !scope |> Scope.addModule ~name:mb.pmb_name.txt ~loc:mb.pmb_name.loc - in - let scopeModuleDeclaration (md : Parsetree.module_declaration) = - scope := - !scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc - in - - let structure (iterator : Ast_iterator.iterator) - (structure : Parsetree.structure) = - let oldScope = !scope in - Ast_iterator.default_iterator.structure iterator structure; - scope := oldScope - in - let structure_item (iterator : Ast_iterator.iterator) - (item : Parsetree.structure_item) = - let processed = ref false in - (match item.pstr_desc with - | Pstr_open {popen_lid} -> - scope := !scope |> Scope.addOpen ~lid:popen_lid.txt - | Pstr_primitive vd -> scopeValueDescription vd - | Pstr_value (recFlag, bindings) -> - if recFlag = Recursive then bindings |> List.iter scopeValueBinding; - bindings |> List.iter (fun vb -> iterator.value_binding iterator vb); - if recFlag = Nonrecursive then bindings |> List.iter scopeValueBinding; - processed := true - | Pstr_type (recFlag, decls) -> - if recFlag = Recursive then decls |> List.iter scopeTypeDeclaration; - decls |> List.iter (fun td -> iterator.type_declaration iterator td); - if recFlag = Nonrecursive then decls |> List.iter scopeTypeDeclaration; - processed := true - | Pstr_module mb -> - iterator.module_binding iterator mb; - scopeModuleBinding mb; - processed := true - | Pstr_recmodule mbs -> - mbs |> List.iter scopeModuleBinding; - mbs |> List.iter (fun b -> iterator.module_binding iterator b); - processed := true - | _ -> ()); - if not !processed then - Ast_iterator.default_iterator.structure_item iterator item - in - let signature (iterator : Ast_iterator.iterator) - (signature : Parsetree.signature) = - let oldScope = !scope in - Ast_iterator.default_iterator.signature iterator signature; - scope := oldScope - in - let signature_item (iterator : Ast_iterator.iterator) - (item : Parsetree.signature_item) = - let processed = ref false in - (match item.psig_desc with - | Psig_open {popen_lid} -> - scope := !scope |> Scope.addOpen ~lid:popen_lid.txt - | Psig_value vd -> scopeValueDescription vd - | Psig_type (recFlag, decls) -> - if recFlag = Recursive then decls |> List.iter scopeTypeDeclaration; - decls |> List.iter (fun td -> iterator.type_declaration iterator td); - if recFlag = Nonrecursive then decls |> List.iter scopeTypeDeclaration; - processed := true - | Psig_module md -> - iterator.module_declaration iterator md; - scopeModuleDeclaration md; - processed := true - | Psig_recmodule mds -> - mds |> List.iter scopeModuleDeclaration; - mds |> List.iter (fun d -> iterator.module_declaration iterator d); - processed := true - | _ -> ()); - if not !processed then - Ast_iterator.default_iterator.signature_item iterator item - in - let attribute (iterator : Ast_iterator.iterator) - ((id, payload) : Parsetree.attribute) = - (if String.length id.txt >= 3 && String.sub id.txt 0 3 = "ns." then - (* skip: internal parser attribute *) () - else if id.loc.loc_ghost then () - else if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - let posStart, posEnd = Loc.range id.loc in - match - ( PartialParser.positionToOffset text posStart, - PartialParser.positionToOffset text posEnd ) - with - | Some offsetStart, Some offsetEnd -> - (* Can't trust the parser's location - E.g. @foo. let x... gives as label @foo.let *) - let label = - let rawLabel = - String.sub text offsetStart (offsetEnd - offsetStart) - in - let ( ++ ) x y = - match (x, y) with - | Some i1, Some i2 -> Some (min i1 i2) - | Some _, None -> x - | None, _ -> y - in - let label = - match - String.index_opt rawLabel ' ' - ++ String.index_opt rawLabel '\t' - ++ String.index_opt rawLabel '\r' - ++ String.index_opt rawLabel '\n' - with - | None -> rawLabel - | Some i -> String.sub rawLabel 0 i - in - if label <> "" && label.[0] = '@' then - String.sub label 1 (String.length label - 1) - else label - in - found := true; - if debug then - Printf.printf "Attribute id:%s:%s label:%s\n" id.txt - (Loc.toString id.loc) label; - setResult (PartialParser.Cdecorator label) - | _ -> ()); - Ast_iterator.default_iterator.attribute iterator (id, payload) - in - let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = - let processed = ref false in - let setFound () = - found := true; - if debug then - Printf.printf "posCursor:[%s] posNoWhite:[%s] Found expr:%s\n" - (Pos.toString posCursor) (Pos.toString posNoWhite) - (Loc.toString expr.pexp_loc) - in - let setPipeResult ~(lhs : Parsetree.expression) ~id = - match exporToContextPath lhs with - | Some pipe -> - setResult (PartialParser.Cpath (CPPipe (pipe, id))); - true - | None -> false - in - match expr.pexp_desc with - | Pexp_apply - ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, - [ - (_, lhs); - (_, {pexp_desc = Pexp_extension _; pexp_loc = {loc_ghost = true}}); - ] ) - when opLoc |> Loc.hasPos ~pos:posBeforeCursor -> - (* Case foo-> when the parser adds a ghost expression to the rhs - so the apply expression does not include the cursor *) - if setPipeResult ~lhs ~id:"" then setFound () - | _ -> - if expr.pexp_loc |> Loc.hasPos ~pos:posNoWhite then ( - setFound (); - match expr.pexp_desc with - | Pexp_ident id -> - if debug then - Printf.printf "Pexp_ident %s:%s\n" - (Utils.flattenLongIdent id.txt |> String.concat ".") - (Loc.toString id.loc); - if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - let path_ = id.txt |> Utils.flattenLongIdent in - let path = - if blankAfterCursor = Some '.' then ( - (* Sometimes "Foo. " is followed by "bar" and the parser's - behaviour is to parse as "Foo.bar". - This gets back the intended path "Foo." *) - let path = - match path_ |> List.rev with - | _last :: pathRev -> List.rev ("" :: pathRev) - | path -> path - in - if debug then - Printf.printf "Id breaks up. New path:%s\n" - (path |> String.concat "."); - path) - else path_ - in - setResult (PartialParser.Cpath (CPId (path, Value))) - | Pexp_construct (id, eOpt) -> - if debug then - Printf.printf "Pexp_construct %s:%s %s\n" - (Utils.flattenLongIdent id.txt |> String.concat "\n") - (Loc.toString id.loc) - (match eOpt with - | None -> "None" - | Some e -> Loc.toString e.pexp_loc); - if - eOpt = None && (not id.loc.loc_ghost) - && id.loc |> Loc.hasPos ~pos:posBeforeCursor - then - setResult - (PartialParser.Cpath (CPId (Utils.flattenLongIdent id.txt, Value))) - | Pexp_field (e, fieldName) -> ( - if debug then - Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) - (Utils.flattenLongIdent fieldName.txt |> String.concat ".") - (Loc.toString fieldName.loc); - if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then - match fieldName.txt with - | Lident name -> ( - match exporToContextPath e with - | Some contextPath -> - let contextPath = PartialParser.CPField (contextPath, name) in - setResult (PartialParser.Cpath contextPath) - | None -> ()) - | Ldot (id, name) -> - (* Case x.M.field ignore the x part *) - let contextPath = - PartialParser.CPField - ( CPId (Utils.flattenLongIdent id, Module), - if name = "_" then "" else name ) - in - setResult (PartialParser.Cpath contextPath) - | Lapply _ -> () - else if Loc.end_ e.pexp_loc = posBeforeCursor then - match exporToContextPath e with - | Some contextPath -> - setResult - (PartialParser.Cpath (PartialParser.CPField (contextPath, ""))) - | None -> ()) - | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) - when Res_parsetree_viewer.isJsxExpression expr -> - let jsxProps = extractJsxProps ~text ~compName ~args in - if debug then - Printf.printf "JSX <%s:%s %s> _children:%s\n" - (jsxProps.componentPath |> String.concat ",") - (Loc.toString compName.loc) - (jsxProps.props - |> List.map (fun {name; posStart; posEnd; exp} -> - Printf.sprintf "%s[%s->%s]=...%s" name - (Pos.toString posStart) (Pos.toString posEnd) - (Loc.toString exp.pexp_loc)) - |> String.concat " ") - (match jsxProps.childrenStart with - | None -> "None" - | Some childrenPosStart -> Pos.toString childrenPosStart); - let jsxCompletable = - findJsxPropsCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) - ~posBeforeCursor ~posAfterCompName:(Loc.end_ compName.loc) - in - if jsxCompletable <> None then setResultOpt jsxCompletable - else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then - setResult - (PartialParser.Cpath - (CPId (Utils.flattenLongIdent ~jsx:true compName.txt, Module))) - | Pexp_apply - ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, - [ - (_, lhs); - (_, {pexp_desc = Pexp_ident {txt = Longident.Lident id; loc}}); - ] ) - when loc |> Loc.hasPos ~pos:posBeforeCursor -> - (* Case foo->id *) - setPipeResult ~lhs ~id |> ignore - | Pexp_apply - ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, - [(_, lhs); _] ) - when Loc.end_ opLoc = posCursor -> - (* Case foo-> *) - setPipeResult ~lhs ~id:"" |> ignore - | Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "|."}}, [_; _]) -> - () - | Pexp_apply ({pexp_desc = Pexp_ident funName}, args) -> - let args = extractExpApplyArgs ~text ~funName ~args in - if debug then - Printf.printf "Pexp_apply ...%s (%s)\n" (Loc.toString funName.loc) - (args - |> List.map (fun {label; exp} -> - Printf.sprintf "%s...%s" - (match label with - | None -> "" - | Some {name; opt; posStart; posEnd} -> - "~" ^ name ^ Pos.toString posStart ^ "->" - ^ Pos.toString posEnd ^ "=" - ^ if opt then "?" else "") - (Loc.toString exp.pexp_loc)) - |> String.concat ", "); - let expApplyCompletable = - findExpApplyCompletable ~funName ~args - ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor - in - setResultOpt expApplyCompletable - | Pexp_send (lhs, {txt; loc}) -> ( - (* e["txt"] - If the string for txt is not closed, it could go over several lines. - Only take the first like to represent the label *) - let txtLines = txt |> String.split_on_char '\n' in - let label = List.hd txtLines in - let label = - if label <> "" && label.[String.length label - 1] = '\r' then - String.sub label 0 (String.length label - 1) - else label - in - let labelRange = - let l, c = Loc.start loc in - ((l, c + 1), (l, c + 1 + String.length label)) - in - if debug then - Printf.printf "Pexp_send %s%s e:%s\n" label - (Range.toString labelRange) - (Loc.toString lhs.pexp_loc); - if - labelRange |> Range.hasPos ~pos:posBeforeCursor - || (label = "" && posCursor = fst labelRange) - then - match exporToContextPath lhs with - | Some contextPath -> - setResult (PartialParser.Cpath (CPObj (contextPath, label))) - | None -> ()) - | Pexp_let (recFlag, bindings, e) -> - let oldScope = !scope in - if recFlag = Recursive then bindings |> List.iter scopeValueBinding; - bindings |> List.iter (fun vb -> iterator.value_binding iterator vb); - if recFlag = Nonrecursive then bindings |> List.iter scopeValueBinding; - iterator.expr iterator e; - scope := oldScope; - processed := true - | Pexp_letmodule (name, modExpr, modBody) -> - let oldScope = !scope in - iterator.location iterator name.loc; - iterator.module_expr iterator modExpr; - scope := !scope |> Scope.addModule ~name:name.txt ~loc:name.loc; - iterator.expr iterator modBody; - scope := oldScope; - processed := true - | _ -> ()); - if not !processed then Ast_iterator.default_iterator.expr iterator expr - in - let typ (iterator : Ast_iterator.iterator) (core_type : Parsetree.core_type) = - if core_type.ptyp_loc |> Loc.hasPos ~pos:posNoWhite then ( - found := true; - if debug then - Printf.printf "posCursor:[%s] posNoWhite:[%s] Found type:%s\n" - (Pos.toString posCursor) (Pos.toString posNoWhite) - (Loc.toString core_type.ptyp_loc); - match core_type.ptyp_desc with - | Ptyp_constr (id, _args) -> - if debug then - Printf.printf "Ptyp_constr %s:%s\n" - (Utils.flattenLongIdent id.txt |> String.concat ".") - (Loc.toString id.loc); - if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - setResult - (PartialParser.Cpath (CPId (Utils.flattenLongIdent id.txt, Type))) - | _ -> ()); - Ast_iterator.default_iterator.typ iterator core_type - in - let module_expr (iterator : Ast_iterator.iterator) - (me : Parsetree.module_expr) = - (match me.pmod_desc with - | Pmod_ident id when id.loc |> Loc.hasPos ~pos:posBeforeCursor -> - if debug then - Printf.printf "Pmod_ident %s:%s\n" - (Utils.flattenLongIdent id.txt |> String.concat ".") - (Loc.toString id.loc); - found := true; - setResult - (PartialParser.Cpath (CPId (Utils.flattenLongIdent id.txt, Module))) - | _ -> ()); - Ast_iterator.default_iterator.module_expr iterator me - in - let module_type (iterator : Ast_iterator.iterator) - (mt : Parsetree.module_type) = - (match mt.pmty_desc with - | Pmty_ident id when id.loc |> Loc.hasPos ~pos:posBeforeCursor -> - if debug then - Printf.printf "Pmty_ident %s:%s\n" - (Utils.flattenLongIdent id.txt |> String.concat ".") - (Loc.toString id.loc); - found := true; - setResult - (PartialParser.Cpath (CPId (Utils.flattenLongIdent id.txt, Module))) - | _ -> ()); - Ast_iterator.default_iterator.module_type iterator mt - in - - let iterator = - { - Ast_iterator.default_iterator with - attribute; - expr; - module_expr; - module_type; - signature; - signature_item; - structure; - structure_item; - typ; - } - in - - if Filename.check_suffix path ".res" then ( - let parser = - Res_driver.parsingEngine.parseImplementation ~forPrinter:false - in - let {Res_driver.parsetree = str} = parser ~filename:currentFile in - iterator.structure iterator str |> ignore; - if blankAfterCursor = Some ' ' || blankAfterCursor = Some '\n' then - setResult (PartialParser.Cpath (CPId ([""], Value))); - if !found = false then if debug then Printf.printf "XXX Not found!\n"; - !result) - else if Filename.check_suffix path ".resi" then ( - let parser = Res_driver.parsingEngine.parseInterface ~forPrinter:false in - let {Res_driver.parsetree = signature} = parser ~filename:currentFile in - iterator.signature iterator signature |> ignore; - if blankAfterCursor = Some ' ' || blankAfterCursor = Some '\n' then - setResult (PartialParser.Cpath (CPId ([""], Type))); - if !found = false then if debug then Printf.printf "XXX Not found!\n"; - !result) - else None - let completion ~debug ~path ~pos ~currentFile = let result = let textOpt = Files.readFile currentFile in @@ -743,18 +6,19 @@ let completion ~debug ~path ~pos ~currentFile = | Some text -> let completionItems = match - completionWithParser ~debug ~path ~posCursor:pos ~currentFile ~text + Completion.completionWithParser ~debug ~path ~posCursor:pos + ~currentFile ~text with | None -> [] | Some (completable, scope) -> ( if debug then Printf.printf "Completable: %s\n" - (PartialParser.completableToString completable); + (SharedTypes.completableToString completable); (* Only perform expensive ast operations if there are completables *) match Cmt.fromPath ~path with | None -> [] | Some full -> - let env = QueryEnv.fromFile full.file in + let env = SharedTypes.QueryEnv.fromFile full.file in let package = full.package in NewCompletions.computeCompletions ~completable ~package ~pos ~scope ~env) diff --git a/analysis/src/Completion.ml b/analysis/src/Completion.ml new file mode 100644 index 000000000..88b79b67a --- /dev/null +++ b/analysis/src/Completion.ml @@ -0,0 +1,744 @@ +open SharedTypes + +let rec skipWhite text i = + if i < 0 then 0 + else + match text.[i] with + | ' ' | '\n' | '\r' | '\t' -> skipWhite text (i - 1) + | _ -> i + +let offsetOfLine text line = + let ln = String.length text in + let rec loop i lno = + if i >= ln then None + else + match text.[i] with + | '\n' -> if lno = line - 1 then Some (i + 1) else loop (i + 1) (lno + 1) + | _ -> loop (i + 1) lno + in + match line with 0 -> Some 0 | _ -> loop 0 0 + +let positionToOffset text (line, character) = + match offsetOfLine text line with + | None -> None + | Some bol -> Some (bol + character) + +type prop = { + name : string; + posStart : int * int; + posEnd : int * int; + exp : Parsetree.expression; +} + +type jsxProps = { + componentPath : string list; + props : prop list; + childrenStart : (int * int) option; +} + +let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName + = + let allLabels = + List.fold_right + (fun prop allLabels -> prop.name :: allLabels) + jsxProps.props [] + in + let rec loop props = + match props with + | prop :: rest -> + if prop.posStart <= posBeforeCursor && posBeforeCursor < prop.posEnd then + Some (Cjsx (jsxProps.componentPath, prop.name, allLabels)) + else if + prop.posEnd <= posBeforeCursor + && posBeforeCursor < Loc.start prop.exp.pexp_loc + then None + else if prop.exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None + 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 (Cjsx (jsxProps.componentPath, "", allLabels)) + else None + in + loop jsxProps.props + +let rec skipLineComment ~pos ~i str = + if i < String.length str then + match str.[i] with + | '\n' -> Some ((fst pos + 1, 0), i + 1) + | _ -> skipLineComment ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str + else None + +let rec skipComment ~pos ~i ~depth str = + if i < String.length str then + match str.[i] with + | '\n' -> skipComment ~depth ~pos:(fst pos + 1, 0) ~i:(i + 1) str + | '/' when i + 1 < String.length str && str.[i + 1] = '*' -> + skipComment ~depth:(depth + 1) ~pos:(fst pos, snd pos + 2) ~i:(i + 2) str + | '*' when i + 1 < String.length str && str.[i + 1] = '/' -> + if depth > 1 then + skipComment ~depth:(depth - 1) + ~pos:(fst pos, snd pos + 2) + ~i:(i + 2) str + else Some ((fst pos, snd pos + 2), i + 2) + | _ -> skipComment ~depth ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str + else None + +let extractJsxProps ~text ~(compName : Longident.t Location.loc) ~args = + let rec extractLabelPos ~pos ~i str = + if i < String.length str then + match str.[i] with + | '/' when i + 1 < String.length str && str.[i + 1] = '/' -> ( + match skipLineComment ~pos ~i str with + | Some (pos, i) -> extractLabelPos ~pos ~i str + | None -> None) + | '/' when i + 1 < String.length str && str.[i + 1] = '*' -> ( + match skipComment ~depth:0 ~pos ~i str with + | Some (pos, i) -> extractLabelPos ~pos ~i str + | None -> None) + | ' ' | '\r' | '\t' -> + extractLabelPos ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str + | '\n' -> extractLabelPos ~pos:(fst pos + 1, 0) ~i:(i + 1) str + | 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' -> Some pos + | _ -> None + else None + in + let thisCaseShouldNotHappen = + {componentPath = []; props = []; childrenStart = None} + in + let rec processProps ~lastOffset ~lastPos ~acc args = + match args with + | (Asttypes.Labelled "children", {Parsetree.pexp_loc}) :: _ -> + { + componentPath = Utils.flattenLongIdent ~jsx:true compName.txt; + props = List.rev acc; + childrenStart = + (if pexp_loc.loc_ghost then None else Some (Loc.start pexp_loc)); + } + | ((Labelled s | Optional s), (eProp : Parsetree.expression)) :: rest -> ( + let ePosStart, ePosEnd = Loc.range eProp.pexp_loc in + match + (positionToOffset text ePosStart, positionToOffset text ePosEnd) + with + | Some offsetStart, Some offsetEnd when not eProp.pexp_loc.loc_ghost -> + let label = String.sub text lastOffset (offsetStart - lastOffset) in + let labelPos = + match extractLabelPos ~pos:lastPos ~i:0 label with + | Some pos -> pos + | None -> (* Must be punned *) ePosStart + in + processProps + ~acc: + ({ + name = s; + posStart = labelPos; + posEnd = (fst labelPos, snd labelPos + String.length s); + exp = eProp; + } + :: acc) + ~lastOffset:offsetEnd ~lastPos:ePosEnd rest + | _ -> thisCaseShouldNotHappen) + | _ -> thisCaseShouldNotHappen + in + let posAfterCompName = Loc.end_ compName.loc in + let offsetAfterCompName = + match positionToOffset text posAfterCompName with + | None -> assert false + | Some offset -> offset + in + args + |> processProps ~lastOffset:offsetAfterCompName ~lastPos:posAfterCompName + ~acc:[] + +type labelled = { + name : string; + opt : bool; + posStart : int * int; + posEnd : int * int; +} + +type label = labelled option +type arg = {label : label; exp : Parsetree.expression} + +let findExpApplyCompletable ~(args : arg list) ~endPos ~posBeforeCursor + ~(funName : Longident.t Location.loc) = + let funPath = Utils.flattenLongIdent funName.txt in + let posAfterFunName = Loc.end_ funName.loc in + let allNames = + List.fold_right + (fun arg allLabels -> + match arg with + | {label = Some labelled} -> labelled.name :: allLabels + | {label = None} -> allLabels) + args [] + in + let rec loop args = + match args with + | {label = Some labelled; exp} :: rest -> + if + labelled.posStart <= posBeforeCursor + && posBeforeCursor < labelled.posEnd + then Some (Clabel (funPath, labelled.name, allNames)) + else if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None + else loop rest + | {label = None; exp} :: rest -> + if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None + else loop rest + | [] -> + if posAfterFunName <= posBeforeCursor && posBeforeCursor < endPos then + Some (Clabel (funPath, "", allNames)) + else None + in + loop args + +let extractExpApplyArgs ~text ~(funName : Longident.t Location.loc) ~args = + let rec extractLabelPos ~pos ~i str = + if i < String.length str then + match str.[i] with + | '/' when i + 1 < String.length str && str.[i + 1] = '/' -> ( + match skipLineComment ~pos ~i str with + | Some (pos, i) -> extractLabelPos ~pos ~i str + | None -> None) + | '/' when i + 1 < String.length str && str.[i + 1] = '*' -> ( + match skipComment ~depth:0 ~pos ~i str with + | Some (pos, i) -> extractLabelPos ~pos ~i str + | None -> None) + | ' ' | '\r' | '\t' | ',' | '(' | '~' -> + extractLabelPos ~pos:(fst pos, snd pos + 1) ~i:(i + 1) str + | '\n' -> extractLabelPos ~pos:(fst pos + 1, 0) ~i:(i + 1) str + | 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' -> Some pos + | _ -> None + else None + in + let rec processArgs ~lastOffset ~lastPos ~acc args = + match args with + | (((Asttypes.Labelled s | Optional s) as label), (e : Parsetree.expression)) + :: rest -> ( + let ePosStart, ePosEnd = Loc.range e.pexp_loc in + match + (positionToOffset text ePosStart, positionToOffset text ePosEnd) + with + | Some offsetStart, Some offsetEnd -> + let labelText = String.sub text lastOffset (offsetStart - lastOffset) in + let labelPos = + match extractLabelPos ~pos:lastPos ~i:0 labelText with + | Some pos -> pos + | None -> (* Must be punned *) ePosStart + in + let labelled = + { + name = s; + opt = (match label with Optional _ -> true | _ -> false); + posStart = labelPos; + posEnd = (fst labelPos, snd labelPos + String.length s); + } + in + processArgs + ~acc:({label = Some labelled; exp = e} :: acc) + ~lastOffset:offsetEnd ~lastPos:ePosEnd rest + | _ -> assert false) + | (Asttypes.Nolabel, (e : Parsetree.expression)) :: rest -> ( + if e.pexp_loc.loc_ghost then processArgs ~acc ~lastOffset ~lastPos rest + else + let ePosEnd = Loc.end_ e.pexp_loc in + match positionToOffset text ePosEnd with + | Some offsetEnd -> + processArgs + ~acc:({label = None; exp = e} :: acc) + ~lastOffset:offsetEnd ~lastPos:ePosEnd rest + | _ -> + (* should not happen *) + assert false) + | [] -> List.rev acc + in + let lastPos = Loc.end_ funName.loc in + let lastOffset = + match positionToOffset text lastPos with + | Some offset -> offset + | None -> assert false + in + args |> processArgs ~lastOffset ~lastPos ~acc:[] + +let rec exporToContextPath (e : Parsetree.expression) = + match e.pexp_desc with + | Pexp_constant (Pconst_string _) -> Some CPString + | Pexp_array _ -> Some CPArray + | Pexp_ident {txt} -> Some (CPId (Utils.flattenLongIdent txt, Value)) + | Pexp_field (e1, {txt = Lident name}) -> ( + match exporToContextPath e1 with + | Some contextPath -> Some (CPField (contextPath, name)) + | _ -> None) + | Pexp_field (_, {txt = Ldot (lid, name)}) -> + (* Case x.M.field ignore the x part *) + Some (CPField (CPId (Utils.flattenLongIdent lid, Module), name)) + | Pexp_send (e1, {txt}) -> ( + match exporToContextPath e1 with + | None -> None + | Some contexPath -> Some (CPObj (contexPath, txt))) + | _ -> None + +let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = + let offset = + match positionToOffset text posCursor with + | Some offset -> offset + | None -> assert false + in + let offsetNoWhite = skipWhite text (offset - 1) in + let posNoWhite = + let line, col = posCursor in + (line, max 0 col - offset + offsetNoWhite) + in + let posBeforeCursor = (fst posCursor, max 0 (snd posCursor - 1)) in + let blankAfterCursor = + match 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 + | _ -> None) + | _ -> None + in + + let found = ref false in + let result = ref None in + let scope = ref (Scope.create ()) in + let setResultOpt x = + if !result = None then + match x with None -> () | Some x -> result := Some (x, !scope) + in + let setResult x = setResultOpt (Some x) in + let scopeValueDescription (vd : Parsetree.value_description) = + scope := + !scope |> Scope.addValue ~name:vd.pval_name.txt ~loc:vd.pval_name.loc + in + let scopeValueBinding (vb : Parsetree.value_binding) = + match vb.pvb_pat.ppat_desc with + | Ppat_var {txt; loc} + | Ppat_constraint ({ppat_desc = Ppat_var {txt; loc}}, _) -> + scope := !scope |> Scope.addValue ~name:txt ~loc + | _ -> () + in + let scopeTypeKind (tk : Parsetree.type_kind) = + match tk with + | Ptype_variant constrDecls -> + constrDecls + |> List.iter (fun (cd : Parsetree.constructor_declaration) -> + scope := + !scope + |> Scope.addConstructor ~name:cd.pcd_name.txt ~loc:cd.pcd_loc) + | Ptype_record labelDecls -> + labelDecls + |> List.iter (fun (ld : Parsetree.label_declaration) -> + scope := + !scope |> Scope.addField ~name:ld.pld_name.txt ~loc:ld.pld_loc) + | _ -> () + in + let scopeTypeDeclaration (td : Parsetree.type_declaration) = + scope := + !scope |> Scope.addType ~name:td.ptype_name.txt ~loc:td.ptype_name.loc; + scopeTypeKind td.ptype_kind + in + let scopeModuleBinding (mb : Parsetree.module_binding) = + scope := + !scope |> Scope.addModule ~name:mb.pmb_name.txt ~loc:mb.pmb_name.loc + in + let scopeModuleDeclaration (md : Parsetree.module_declaration) = + scope := + !scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc + in + + let structure (iterator : Ast_iterator.iterator) + (structure : Parsetree.structure) = + let oldScope = !scope in + Ast_iterator.default_iterator.structure iterator structure; + scope := oldScope + in + let structure_item (iterator : Ast_iterator.iterator) + (item : Parsetree.structure_item) = + let processed = ref false in + (match item.pstr_desc with + | Pstr_open {popen_lid} -> + scope := !scope |> Scope.addOpen ~lid:popen_lid.txt + | Pstr_primitive vd -> scopeValueDescription vd + | Pstr_value (recFlag, bindings) -> + if recFlag = Recursive then bindings |> List.iter scopeValueBinding; + bindings |> List.iter (fun vb -> iterator.value_binding iterator vb); + if recFlag = Nonrecursive then bindings |> List.iter scopeValueBinding; + processed := true + | Pstr_type (recFlag, decls) -> + if recFlag = Recursive then decls |> List.iter scopeTypeDeclaration; + decls |> List.iter (fun td -> iterator.type_declaration iterator td); + if recFlag = Nonrecursive then decls |> List.iter scopeTypeDeclaration; + processed := true + | Pstr_module mb -> + iterator.module_binding iterator mb; + scopeModuleBinding mb; + processed := true + | Pstr_recmodule mbs -> + mbs |> List.iter scopeModuleBinding; + mbs |> List.iter (fun b -> iterator.module_binding iterator b); + processed := true + | _ -> ()); + if not !processed then + Ast_iterator.default_iterator.structure_item iterator item + in + let signature (iterator : Ast_iterator.iterator) + (signature : Parsetree.signature) = + let oldScope = !scope in + Ast_iterator.default_iterator.signature iterator signature; + scope := oldScope + in + let signature_item (iterator : Ast_iterator.iterator) + (item : Parsetree.signature_item) = + let processed = ref false in + (match item.psig_desc with + | Psig_open {popen_lid} -> + scope := !scope |> Scope.addOpen ~lid:popen_lid.txt + | Psig_value vd -> scopeValueDescription vd + | Psig_type (recFlag, decls) -> + if recFlag = Recursive then decls |> List.iter scopeTypeDeclaration; + decls |> List.iter (fun td -> iterator.type_declaration iterator td); + if recFlag = Nonrecursive then decls |> List.iter scopeTypeDeclaration; + processed := true + | Psig_module md -> + iterator.module_declaration iterator md; + scopeModuleDeclaration md; + processed := true + | Psig_recmodule mds -> + mds |> List.iter scopeModuleDeclaration; + mds |> List.iter (fun d -> iterator.module_declaration iterator d); + processed := true + | _ -> ()); + if not !processed then + Ast_iterator.default_iterator.signature_item iterator item + in + let attribute (iterator : Ast_iterator.iterator) + ((id, payload) : Parsetree.attribute) = + (if String.length id.txt >= 3 && String.sub id.txt 0 3 = "ns." then + (* skip: internal parser attribute *) () + else if id.loc.loc_ghost then () + else if id.loc |> Loc.hasPos ~pos:posBeforeCursor then + let posStart, posEnd = Loc.range id.loc in + match (positionToOffset text posStart, positionToOffset text posEnd) with + | Some offsetStart, Some offsetEnd -> + (* Can't trust the parser's location + E.g. @foo. let x... gives as label @foo.let *) + let label = + let rawLabel = + String.sub text offsetStart (offsetEnd - offsetStart) + in + let ( ++ ) x y = + match (x, y) with + | Some i1, Some i2 -> Some (min i1 i2) + | Some _, None -> x + | None, _ -> y + in + let label = + match + String.index_opt rawLabel ' ' + ++ String.index_opt rawLabel '\t' + ++ String.index_opt rawLabel '\r' + ++ String.index_opt rawLabel '\n' + with + | None -> rawLabel + | Some i -> String.sub rawLabel 0 i + in + if label <> "" && label.[0] = '@' then + String.sub label 1 (String.length label - 1) + else label + in + found := true; + if debug then + Printf.printf "Attribute id:%s:%s label:%s\n" id.txt + (Loc.toString id.loc) label; + setResult (Cdecorator label) + | _ -> ()); + Ast_iterator.default_iterator.attribute iterator (id, payload) + in + let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = + let processed = ref false in + let setFound () = + found := true; + if debug then + Printf.printf "posCursor:[%s] posNoWhite:[%s] Found expr:%s\n" + (Pos.toString posCursor) (Pos.toString posNoWhite) + (Loc.toString expr.pexp_loc) + in + let setPipeResult ~(lhs : Parsetree.expression) ~id = + match exporToContextPath lhs with + | Some pipe -> + setResult (Cpath (CPPipe (pipe, id))); + true + | None -> false + in + match expr.pexp_desc with + | Pexp_apply + ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, + [ + (_, lhs); + (_, {pexp_desc = Pexp_extension _; pexp_loc = {loc_ghost = true}}); + ] ) + when opLoc |> Loc.hasPos ~pos:posBeforeCursor -> + (* Case foo-> when the parser adds a ghost expression to the rhs + so the apply expression does not include the cursor *) + if setPipeResult ~lhs ~id:"" then setFound () + | _ -> + if expr.pexp_loc |> Loc.hasPos ~pos:posNoWhite then ( + setFound (); + match expr.pexp_desc with + | Pexp_ident id -> + if debug then + Printf.printf "Pexp_ident %s:%s\n" + (Utils.flattenLongIdent id.txt |> String.concat ".") + (Loc.toString id.loc); + if id.loc |> Loc.hasPos ~pos:posBeforeCursor then + let path_ = id.txt |> Utils.flattenLongIdent in + let path = + if blankAfterCursor = Some '.' then ( + (* Sometimes "Foo. " is followed by "bar" and the parser's + behaviour is to parse as "Foo.bar". + This gets back the intended path "Foo." *) + let path = + match path_ |> List.rev with + | _last :: pathRev -> List.rev ("" :: pathRev) + | path -> path + in + if debug then + Printf.printf "Id breaks up. New path:%s\n" + (path |> String.concat "."); + path) + else path_ + in + setResult (Cpath (CPId (path, Value))) + | Pexp_construct (id, eOpt) -> + if debug then + Printf.printf "Pexp_construct %s:%s %s\n" + (Utils.flattenLongIdent id.txt |> String.concat "\n") + (Loc.toString id.loc) + (match eOpt with + | None -> "None" + | Some e -> Loc.toString e.pexp_loc); + if + eOpt = None && (not id.loc.loc_ghost) + && id.loc |> Loc.hasPos ~pos:posBeforeCursor + then setResult (Cpath (CPId (Utils.flattenLongIdent id.txt, Value))) + | Pexp_field (e, fieldName) -> ( + if debug then + Printf.printf "Pexp_field %s %s:%s\n" (Loc.toString e.pexp_loc) + (Utils.flattenLongIdent fieldName.txt |> String.concat ".") + (Loc.toString fieldName.loc); + if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then + match fieldName.txt with + | Lident name -> ( + match exporToContextPath e with + | Some contextPath -> + let contextPath = CPField (contextPath, name) in + setResult (Cpath contextPath) + | None -> ()) + | Ldot (id, name) -> + (* Case x.M.field ignore the x part *) + let contextPath = + CPField + ( CPId (Utils.flattenLongIdent id, Module), + if name = "_" then "" else name ) + in + setResult (Cpath contextPath) + | Lapply _ -> () + else if Loc.end_ e.pexp_loc = posBeforeCursor then + match exporToContextPath e with + | Some contextPath -> setResult (Cpath (CPField (contextPath, ""))) + | None -> ()) + | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) + when Res_parsetree_viewer.isJsxExpression expr -> + let jsxProps = extractJsxProps ~text ~compName ~args in + if debug then + Printf.printf "JSX <%s:%s %s> _children:%s\n" + (jsxProps.componentPath |> String.concat ",") + (Loc.toString compName.loc) + (jsxProps.props + |> List.map (fun {name; posStart; posEnd; exp} -> + Printf.sprintf "%s[%s->%s]=...%s" name + (Pos.toString posStart) (Pos.toString posEnd) + (Loc.toString exp.pexp_loc)) + |> String.concat " ") + (match jsxProps.childrenStart with + | None -> "None" + | Some childrenPosStart -> Pos.toString childrenPosStart); + let jsxCompletable = + findJsxPropsCompletable ~jsxProps ~endPos:(Loc.end_ expr.pexp_loc) + ~posBeforeCursor ~posAfterCompName:(Loc.end_ compName.loc) + in + if jsxCompletable <> None then setResultOpt jsxCompletable + else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then + setResult + (Cpath + (CPId (Utils.flattenLongIdent ~jsx:true compName.txt, Module))) + | Pexp_apply + ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, + [ + (_, lhs); + (_, {pexp_desc = Pexp_ident {txt = Longident.Lident id; loc}}); + ] ) + when loc |> Loc.hasPos ~pos:posBeforeCursor -> + (* Case foo->id *) + setPipeResult ~lhs ~id |> ignore + | Pexp_apply + ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, + [(_, lhs); _] ) + when Loc.end_ opLoc = posCursor -> + (* Case foo-> *) + setPipeResult ~lhs ~id:"" |> ignore + | Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "|."}}, [_; _]) -> + () + | Pexp_apply ({pexp_desc = Pexp_ident funName}, args) -> + let args = extractExpApplyArgs ~text ~funName ~args in + if debug then + Printf.printf "Pexp_apply ...%s (%s)\n" (Loc.toString funName.loc) + (args + |> List.map (fun {label; exp} -> + Printf.sprintf "%s...%s" + (match label with + | None -> "" + | Some {name; opt; posStart; posEnd} -> + "~" ^ name ^ Pos.toString posStart ^ "->" + ^ Pos.toString posEnd ^ "=" + ^ if opt then "?" else "") + (Loc.toString exp.pexp_loc)) + |> String.concat ", "); + let expApplyCompletable = + findExpApplyCompletable ~funName ~args + ~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor + in + setResultOpt expApplyCompletable + | Pexp_send (lhs, {txt; loc}) -> ( + (* e["txt"] + If the string for txt is not closed, it could go over several lines. + Only take the first like to represent the label *) + let txtLines = txt |> String.split_on_char '\n' in + let label = List.hd txtLines in + let label = + if label <> "" && label.[String.length label - 1] = '\r' then + String.sub label 0 (String.length label - 1) + else label + in + let labelRange = + let l, c = Loc.start loc in + ((l, c + 1), (l, c + 1 + String.length label)) + in + if debug then + Printf.printf "Pexp_send %s%s e:%s\n" label + (Range.toString labelRange) + (Loc.toString lhs.pexp_loc); + if + labelRange |> Range.hasPos ~pos:posBeforeCursor + || (label = "" && posCursor = fst labelRange) + then + match exporToContextPath lhs with + | Some contextPath -> setResult (Cpath (CPObj (contextPath, label))) + | None -> ()) + | Pexp_let (recFlag, bindings, e) -> + let oldScope = !scope in + if recFlag = Recursive then bindings |> List.iter scopeValueBinding; + bindings |> List.iter (fun vb -> iterator.value_binding iterator vb); + if recFlag = Nonrecursive then bindings |> List.iter scopeValueBinding; + iterator.expr iterator e; + scope := oldScope; + processed := true + | Pexp_letmodule (name, modExpr, modBody) -> + let oldScope = !scope in + iterator.location iterator name.loc; + iterator.module_expr iterator modExpr; + scope := !scope |> Scope.addModule ~name:name.txt ~loc:name.loc; + iterator.expr iterator modBody; + scope := oldScope; + processed := true + | _ -> ()); + if not !processed then Ast_iterator.default_iterator.expr iterator expr + in + let typ (iterator : Ast_iterator.iterator) (core_type : Parsetree.core_type) = + if core_type.ptyp_loc |> Loc.hasPos ~pos:posNoWhite then ( + found := true; + if debug then + Printf.printf "posCursor:[%s] posNoWhite:[%s] Found type:%s\n" + (Pos.toString posCursor) (Pos.toString posNoWhite) + (Loc.toString core_type.ptyp_loc); + match core_type.ptyp_desc with + | Ptyp_constr (id, _args) -> + if debug then + Printf.printf "Ptyp_constr %s:%s\n" + (Utils.flattenLongIdent id.txt |> String.concat ".") + (Loc.toString id.loc); + if id.loc |> Loc.hasPos ~pos:posBeforeCursor then + setResult (Cpath (CPId (Utils.flattenLongIdent id.txt, Type))) + | _ -> ()); + Ast_iterator.default_iterator.typ iterator core_type + in + let module_expr (iterator : Ast_iterator.iterator) + (me : Parsetree.module_expr) = + (match me.pmod_desc with + | Pmod_ident id when id.loc |> Loc.hasPos ~pos:posBeforeCursor -> + if debug then + Printf.printf "Pmod_ident %s:%s\n" + (Utils.flattenLongIdent id.txt |> String.concat ".") + (Loc.toString id.loc); + found := true; + setResult (Cpath (CPId (Utils.flattenLongIdent id.txt, Module))) + | _ -> ()); + Ast_iterator.default_iterator.module_expr iterator me + in + let module_type (iterator : Ast_iterator.iterator) + (mt : Parsetree.module_type) = + (match mt.pmty_desc with + | Pmty_ident id when id.loc |> Loc.hasPos ~pos:posBeforeCursor -> + if debug then + Printf.printf "Pmty_ident %s:%s\n" + (Utils.flattenLongIdent id.txt |> String.concat ".") + (Loc.toString id.loc); + found := true; + setResult (Cpath (CPId (Utils.flattenLongIdent id.txt, Module))) + | _ -> ()); + Ast_iterator.default_iterator.module_type iterator mt + in + + let iterator = + { + Ast_iterator.default_iterator with + attribute; + expr; + module_expr; + module_type; + signature; + signature_item; + structure; + structure_item; + typ; + } + in + + if Filename.check_suffix path ".res" then ( + let parser = + Res_driver.parsingEngine.parseImplementation ~forPrinter:false + in + let {Res_driver.parsetree = str} = parser ~filename:currentFile in + iterator.structure iterator str |> ignore; + if blankAfterCursor = Some ' ' || blankAfterCursor = Some '\n' then + setResult (Cpath (CPId ([""], Value))); + if !found = false then if debug then Printf.printf "XXX Not found!\n"; + !result) + else if Filename.check_suffix path ".resi" then ( + let parser = Res_driver.parsingEngine.parseInterface ~forPrinter:false in + let {Res_driver.parsetree = signature} = parser ~filename:currentFile in + iterator.signature iterator signature |> ignore; + if blankAfterCursor = Some ' ' || blankAfterCursor = Some '\n' then + setResult (Cpath (CPId ([""], Type))); + if !found = false then if debug then Printf.printf "XXX Not found!\n"; + !result) + else None diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index f60616aad..9e66a64da 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -665,7 +665,7 @@ let detail name (kind : Completion.kind) = | Constructor (c, s) -> showConstructor c ^ "\n\n" ^ s let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed - ~(completionContext : PartialParser.completionContext) = + ~(completionContext : completionContext) = Log.log ("findAllCompletions uri:" ^ Uri2.toString env.file.uri); match completionContext with | Value -> @@ -846,7 +846,7 @@ let findLocalCompletionsForModules ~env ~prefix ~exact ~opens ~scope = List.rev_append !resultRev valuesFromOpens let findLocalCompletionsWithOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens - ~scope ~(completionContext : PartialParser.completionContext) = + ~scope ~(completionContext : completionContext) = (* TODO: handle arbitrary interleaving of opens and local bindings correctly *) Log.log ("findLocalCompletionsWithOpens uri:" ^ Uri2.toString env.file.uri ^ " pos:" @@ -963,7 +963,7 @@ let completionsGetTypeEnv = function | _ -> None let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact ~scope (contextPath : PartialParser.contextPath) = + ~env ~exact ~scope (contextPath : contextPath) = match contextPath with | CPString -> [ @@ -1114,8 +1114,8 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos in let completions = modulePath @ [funNamePrefix] - |> getCompletionsForPath ~completionContext:PartialParser.Value - ~exact:false ~package ~opens ~allFiles ~pos ~env ~scope + |> getCompletionsForPath ~completionContext:Value ~exact:false + ~package ~opens ~allFiles ~pos ~env ~scope in completions |> List.map (fun (completion : Completion.t) -> @@ -1148,15 +1148,14 @@ let getOpens ~rawOpens ~package ~env = (* Last open takes priority *) List.rev resolvedOpens -let processCompletable ~package ~scope ~env ~pos - (completable : PartialParser.completable) = +let processCompletable ~package ~scope ~env ~pos (completable : completable) = let rawOpens = Scope.getRawOpens scope in let opens = getOpens ~rawOpens ~package ~env in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in let findTypeOfValue path = path - |> getCompletionsForPath ~completionContext:PartialParser.Value ~exact:true - ~package ~opens ~allFiles ~pos ~env ~scope + |> getCompletionsForPath ~completionContext:Value ~exact:true ~package + ~opens ~allFiles ~pos ~env ~scope |> completionsGetTypeEnv in match completable with @@ -1307,6 +1306,5 @@ let processCompletable ~package ~scope ~env ~pos Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel -let computeCompletions ~(completable : PartialParser.completable) ~package ~pos - ~scope ~env = +let computeCompletions ~(completable : completable) ~package ~pos ~scope ~env = completable |> processCompletable ~package ~scope ~env ~pos diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index cd0e98d35..e69de29bb 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -1,67 +0,0 @@ -(* Completion context *) -type completionContext = Type | Value | Module | Field - -type contextPath = - | CPString - | CPArray - | CPId of string list * completionContext - | CPField of contextPath * string - | CPObj of contextPath * string - | CPPipe of contextPath * string - -type completable = - | Cdecorator of string (** e.g. @module *) - | Clabel of string list * string * string list - (** e.g. (["M", "foo"], "label", ["l1", "l2"]) for M.foo(...~l1...~l2...~label...) *) - | Cpath of contextPath - | Cjsx of string list * string * string list - (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for List.map str |> String.concat ", ") ^ "]" in - let completionContextToString = function - | Value -> "Value" - | Type -> "Type" - | Module -> "Module" - | Field -> "Field" - in - let rec contextPathToString = function - | CPString -> "string" - | CPArray -> "array" - | CPId (sl, completionContext) -> - completionContextToString completionContext ^ list sl - | CPField (cp, s) -> contextPathToString cp ^ "." ^ str s - | CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]" - | CPPipe (cp, s) -> contextPathToString cp ^ "->" ^ s - in - function - | Cpath cp -> "Cpath " ^ contextPathToString cp - | Cdecorator s -> "Cdecorator(" ^ str s ^ ")" - | Clabel (sl1, s, sl2) -> - "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" - | Cjsx (sl1, s, sl2) -> - "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" - -let rec skipWhite text i = - if i < 0 then 0 - else - match text.[i] with - | ' ' | '\n' | '\r' | '\t' -> skipWhite text (i - 1) - | _ -> i - -let offsetOfLine text line = - let ln = String.length text in - let rec loop i lno = - if i >= ln then None - else - match text.[i] with - | '\n' -> if lno = line - 1 then Some (i + 1) else loop (i + 1) (lno + 1) - | _ -> loop (i + 1) lno - in - match line with 0 -> Some 0 | _ -> loop 0 0 - -let positionToOffset text (line, character) = - match offsetOfLine text line with - | None -> None - | Some bol -> Some (bol + character) diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 310c2920c..b7f3f366a 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -395,3 +395,48 @@ let locItemToString {loc = {Location.loc_start; loc_end}; locType} = (* needed for debugging *) let _ = locItemToString + +(* Completion context *) +type completionContext = Type | Value | Module | Field + +type contextPath = + | CPString + | CPArray + | CPId of string list * completionContext + | CPField of contextPath * string + | CPObj of contextPath * string + | CPPipe of contextPath * string + +type completable = + | Cdecorator of string (** e.g. @module *) + | Clabel of string list * string * string list + (** e.g. (["M", "foo"], "label", ["l1", "l2"]) for M.foo(...~l1...~l2...~label...) *) + | Cpath of contextPath + | Cjsx of string list * string * string list + (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for List.map str |> String.concat ", ") ^ "]" in + let completionContextToString = function + | Value -> "Value" + | Type -> "Type" + | Module -> "Module" + | Field -> "Field" + in + let rec contextPathToString = function + | CPString -> "string" + | CPArray -> "array" + | CPId (sl, completionContext) -> + completionContextToString completionContext ^ list sl + | CPField (cp, s) -> contextPathToString cp ^ "." ^ str s + | CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]" + | CPPipe (cp, s) -> contextPathToString cp ^ "->" ^ s + in + function + | Cpath cp -> "Cpath " ^ contextPathToString cp + | Cdecorator s -> "Cdecorator(" ^ str s ^ ")" + | Clabel (sl1, s, sl2) -> + "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" + | Cjsx (sl1, s, sl2) -> + "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" From 64ec49a91fb3b06ac37decf816ed0d6696900957 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 22 Apr 2022 08:08:29 +0200 Subject: [PATCH 131/135] rename --- analysis/src/Completion.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/analysis/src/Completion.ml b/analysis/src/Completion.ml index 88b79b67a..7f2753661 100644 --- a/analysis/src/Completion.ml +++ b/analysis/src/Completion.ml @@ -264,20 +264,20 @@ let extractExpApplyArgs ~text ~(funName : Longident.t Location.loc) ~args = in args |> processArgs ~lastOffset ~lastPos ~acc:[] -let rec exporToContextPath (e : Parsetree.expression) = +let rec exprToContextPath (e : Parsetree.expression) = match e.pexp_desc with | Pexp_constant (Pconst_string _) -> Some CPString | Pexp_array _ -> Some CPArray | Pexp_ident {txt} -> Some (CPId (Utils.flattenLongIdent txt, Value)) | Pexp_field (e1, {txt = Lident name}) -> ( - match exporToContextPath e1 with + match exprToContextPath e1 with | Some contextPath -> Some (CPField (contextPath, name)) | _ -> None) | Pexp_field (_, {txt = Ldot (lid, name)}) -> (* Case x.M.field ignore the x part *) Some (CPField (CPId (Utils.flattenLongIdent lid, Module), name)) | Pexp_send (e1, {txt}) -> ( - match exporToContextPath e1 with + match exprToContextPath e1 with | None -> None | Some contexPath -> Some (CPObj (contexPath, txt))) | _ -> None @@ -473,7 +473,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = (Loc.toString expr.pexp_loc) in let setPipeResult ~(lhs : Parsetree.expression) ~id = - match exporToContextPath lhs with + match exprToContextPath lhs with | Some pipe -> setResult (Cpath (CPPipe (pipe, id))); true @@ -538,7 +538,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = if fieldName.loc |> Loc.hasPos ~pos:posBeforeCursor then match fieldName.txt with | Lident name -> ( - match exporToContextPath e with + match exprToContextPath e with | Some contextPath -> let contextPath = CPField (contextPath, name) in setResult (Cpath contextPath) @@ -553,7 +553,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = setResult (Cpath contextPath) | Lapply _ -> () else if Loc.end_ e.pexp_loc = posBeforeCursor then - match exporToContextPath e with + match exprToContextPath e with | Some contextPath -> setResult (Cpath (CPField (contextPath, ""))) | None -> ()) | Pexp_apply ({pexp_desc = Pexp_ident compName}, args) @@ -641,7 +641,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = labelRange |> Range.hasPos ~pos:posBeforeCursor || (label = "" && posCursor = fst labelRange) then - match exporToContextPath lhs with + match exprToContextPath lhs with | Some contextPath -> setResult (Cpath (CPObj (contextPath, label))) | None -> ()) | Pexp_let (recFlag, bindings, e) -> From 62c2183824882c78b8163b3c0ed343a137b3b097 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 22 Apr 2022 08:11:02 +0200 Subject: [PATCH 132/135] restore check on formatting --- analysis/src/Commands.ml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 7de4c5f0d..889ef5cc0 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -220,13 +220,14 @@ let rename ~path ~line ~col ~newName = let format ~path = if Filename.check_suffix path ".res" then - let {Res_driver.parsetree = structure; comments} = + let {Res_driver.parsetree = structure; comments; diagnostics} = Res_driver.parsingEngine.parseImplementation ~forPrinter:true ~filename:path in - (* if List.length diagnostics > 0 then "" - else *) - Res_printer.printImplementation !Res_cli.ResClflags.width structure comments + if List.length diagnostics > 0 then "" + else + Res_printer.printImplementation !Res_cli.ResClflags.width structure + comments else if Filename.check_suffix path ".resi" then let {Res_driver.parsetree = signature; comments; diagnostics} = Res_driver.parsingEngine.parseInterface ~forPrinter:true ~filename:path From c927bbfd366be26f65b3eb911ea48b14724424cb Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 22 Apr 2022 08:17:32 +0200 Subject: [PATCH 133/135] Move completable to own module. --- analysis/src/Commands.ml | 2 +- analysis/src/Completion.ml | 12 ++--- analysis/src/NewCompletions.ml | 11 +++-- analysis/src/SharedTypes.ml | 90 +++++++++++++++++----------------- 4 files changed, 59 insertions(+), 56 deletions(-) diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 889ef5cc0..dc6de6cb4 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -13,7 +13,7 @@ let completion ~debug ~path ~pos ~currentFile = | Some (completable, scope) -> ( if debug then Printf.printf "Completable: %s\n" - (SharedTypes.completableToString completable); + (SharedTypes.Completable.toString completable); (* Only perform expensive ast operations if there are completables *) match Cmt.fromPath ~path with | None -> [] diff --git a/analysis/src/Completion.ml b/analysis/src/Completion.ml index 7f2753661..a3083fb21 100644 --- a/analysis/src/Completion.ml +++ b/analysis/src/Completion.ml @@ -47,7 +47,7 @@ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName match props with | prop :: rest -> if prop.posStart <= posBeforeCursor && posBeforeCursor < prop.posEnd then - Some (Cjsx (jsxProps.componentPath, prop.name, allLabels)) + Some (Completable.Cjsx (jsxProps.componentPath, prop.name, allLabels)) else if prop.posEnd <= posBeforeCursor && posBeforeCursor < Loc.start prop.exp.pexp_loc @@ -183,7 +183,7 @@ let findExpApplyCompletable ~(args : arg list) ~endPos ~posBeforeCursor if labelled.posStart <= posBeforeCursor && posBeforeCursor < labelled.posEnd - then Some (Clabel (funPath, labelled.name, allNames)) + then Some (Completable.Clabel (funPath, labelled.name, allNames)) else if exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then None else loop rest | {label = None; exp} :: rest -> @@ -266,7 +266,7 @@ let extractExpApplyArgs ~text ~(funName : Longident.t Location.loc) ~args = let rec exprToContextPath (e : Parsetree.expression) = match e.pexp_desc with - | Pexp_constant (Pconst_string _) -> Some CPString + | Pexp_constant (Pconst_string _) -> Some Completable.CPString | Pexp_array _ -> Some CPArray | Pexp_ident {txt} -> Some (CPId (Utils.flattenLongIdent txt, Value)) | Pexp_field (e1, {txt = Lident name}) -> ( @@ -459,7 +459,7 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = if debug then Printf.printf "Attribute id:%s:%s label:%s\n" id.txt (Loc.toString id.loc) label; - setResult (Cdecorator label) + setResult (Completable.Cdecorator label) | _ -> ()); Ast_iterator.default_iterator.attribute iterator (id, payload) in @@ -540,13 +540,13 @@ let completionWithParser ~debug ~path ~posCursor ~currentFile ~text = | Lident name -> ( match exprToContextPath e with | Some contextPath -> - let contextPath = CPField (contextPath, name) in + let contextPath = Completable.CPField (contextPath, name) in setResult (Cpath contextPath) | None -> ()) | Ldot (id, name) -> (* Case x.M.field ignore the x part *) let contextPath = - CPField + Completable.CPField ( CPId (Utils.flattenLongIdent id, Module), if name = "_" then "" else name ) in diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 9e66a64da..8d4112649 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -665,7 +665,7 @@ let detail name (kind : Completion.kind) = | Constructor (c, s) -> showConstructor c ^ "\n\n" ^ s let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed - ~(completionContext : completionContext) = + ~(completionContext : Completable.completionContext) = Log.log ("findAllCompletions uri:" ^ Uri2.toString env.file.uri); match completionContext with | Value -> @@ -846,7 +846,7 @@ let findLocalCompletionsForModules ~env ~prefix ~exact ~opens ~scope = List.rev_append !resultRev valuesFromOpens let findLocalCompletionsWithOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens - ~scope ~(completionContext : completionContext) = + ~scope ~(completionContext : Completable.completionContext) = (* TODO: handle arbitrary interleaving of opens and local bindings correctly *) Log.log ("findLocalCompletionsWithOpens uri:" ^ Uri2.toString env.file.uri ^ " pos:" @@ -963,7 +963,7 @@ let completionsGetTypeEnv = function | _ -> None let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact ~scope (contextPath : contextPath) = + ~env ~exact ~scope (contextPath : Completable.contextPath) = match contextPath with | CPString -> [ @@ -1148,7 +1148,7 @@ let getOpens ~rawOpens ~package ~env = (* Last open takes priority *) List.rev resolvedOpens -let processCompletable ~package ~scope ~env ~pos (completable : completable) = +let processCompletable ~package ~scope ~env ~pos (completable : Completable.t) = let rawOpens = Scope.getRawOpens scope in let opens = getOpens ~rawOpens ~package ~env in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in @@ -1306,5 +1306,6 @@ let processCompletable ~package ~scope ~env ~pos (completable : completable) = Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel -let computeCompletions ~(completable : completable) ~package ~pos ~scope ~env = +let computeCompletions ~(completable : Completable.t) ~package ~pos ~scope ~env + = completable |> processCompletable ~package ~scope ~env ~pos diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index b7f3f366a..aaa2ca282 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -396,47 +396,49 @@ let locItemToString {loc = {Location.loc_start; loc_end}; locType} = (* needed for debugging *) let _ = locItemToString -(* Completion context *) -type completionContext = Type | Value | Module | Field - -type contextPath = - | CPString - | CPArray - | CPId of string list * completionContext - | CPField of contextPath * string - | CPObj of contextPath * string - | CPPipe of contextPath * string - -type completable = - | Cdecorator of string (** e.g. @module *) - | Clabel of string list * string * string list - (** e.g. (["M", "foo"], "label", ["l1", "l2"]) for M.foo(...~l1...~l2...~label...) *) - | Cpath of contextPath - | Cjsx of string list * string * string list - (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for List.map str |> String.concat ", ") ^ "]" in - let completionContextToString = function - | Value -> "Value" - | Type -> "Type" - | Module -> "Module" - | Field -> "Field" - in - let rec contextPathToString = function - | CPString -> "string" - | CPArray -> "array" - | CPId (sl, completionContext) -> - completionContextToString completionContext ^ list sl - | CPField (cp, s) -> contextPathToString cp ^ "." ^ str s - | CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]" - | CPPipe (cp, s) -> contextPathToString cp ^ "->" ^ s - in - function - | Cpath cp -> "Cpath " ^ contextPathToString cp - | Cdecorator s -> "Cdecorator(" ^ str s ^ ")" - | Clabel (sl1, s, sl2) -> - "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" - | Cjsx (sl1, s, sl2) -> - "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" +module Completable = struct + (* Completion context *) + type completionContext = Type | Value | Module | Field + + type contextPath = + | CPString + | CPArray + | CPId of string list * completionContext + | CPField of contextPath * string + | CPObj of contextPath * string + | CPPipe of contextPath * string + + type t = + | Cdecorator of string (** e.g. @module *) + | Clabel of string list * string * string list + (** e.g. (["M", "foo"], "label", ["l1", "l2"]) for M.foo(...~l1...~l2...~label...) *) + | Cpath of contextPath + | Cjsx of string list * string * string list + (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for List.map str |> String.concat ", ") ^ "]" in + let completionContextToString = function + | Value -> "Value" + | Type -> "Type" + | Module -> "Module" + | Field -> "Field" + in + let rec contextPathToString = function + | CPString -> "string" + | CPArray -> "array" + | CPId (sl, completionContext) -> + completionContextToString completionContext ^ list sl + | CPField (cp, s) -> contextPathToString cp ^ "." ^ str s + | CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]" + | CPPipe (cp, s) -> contextPathToString cp ^ "->" ^ s + in + function + | Cpath cp -> "Cpath " ^ contextPathToString cp + | Cdecorator s -> "Cdecorator(" ^ str s ^ ")" + | Clabel (sl1, s, sl2) -> + "Clabel(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" + | Cjsx (sl1, s, sl2) -> + "Cjsx(" ^ (sl1 |> list) ^ ", " ^ str s ^ ", " ^ (sl2 |> list) ^ ")" +end From 882b8621c858af226c03e2469bc09ef750cfd951 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sat, 23 Apr 2022 08:28:12 +0200 Subject: [PATCH 134/135] tweak order of fields --- analysis/src/NewCompletions.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 8d4112649..d546afc3e 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -677,8 +677,8 @@ let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed @ completionForExportedModules ~env ~prefix ~exact ~namesUsed | Module -> completionForExportedModules ~env ~prefix ~exact ~namesUsed | Field -> - completionForExportedModules ~env ~prefix ~exact ~namesUsed - @ completionForExportedFields ~env ~prefix ~exact ~namesUsed + completionForExportedFields ~env ~prefix ~exact ~namesUsed + @ completionForExportedModules ~env ~prefix ~exact ~namesUsed let findLocalCompletionsForValuesAndConstructors ~env ~prefix ~exact ~opens ~scope = From 6526a62c0b6b7f43645357e28e08297c8da79af3 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sat, 23 Apr 2022 08:29:37 +0200 Subject: [PATCH 135/135] rename --- analysis/src/NewCompletions.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index d546afc3e..35e0e75c1 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -536,7 +536,8 @@ let completionForExportedTypes ~env ~prefix ~exact ~namesUsed = (Stamps.findType env.file.stamps) ~prefix ~exact ~env ~namesUsed (fun t -> Completion.Type t) -let completionsForConstructors ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed = +let completionsForExportedConstructors ~(env : QueryEnv.t) ~prefix ~exact + ~namesUsed = let res = ref [] in Exported.iter env.exported Exported.Type (fun _name stamp -> match Stamps.findType env.file.stamps stamp with @@ -670,7 +671,7 @@ let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed match completionContext with | Value -> completionForExportedValues ~env ~prefix ~exact ~namesUsed - @ completionsForConstructors ~env ~prefix ~exact ~namesUsed + @ completionsForExportedConstructors ~env ~prefix ~exact ~namesUsed @ completionForExportedModules ~env ~prefix ~exact ~namesUsed | Type -> completionForExportedTypes ~env ~prefix ~exact ~namesUsed