From 6ddf611fcfba74213329cdcb4d7a3144f8c1b069 Mon Sep 17 00:00:00 2001 From: Amirali Esmaeili Date: Wed, 26 May 2021 05:33:43 +0430 Subject: [PATCH 01/11] Fix references to file modules --- analysis/src/References.ml | 29 +++++++++++++++++------ analysis/tests/src/expected/Cross.res.txt | 3 ++- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/analysis/src/References.ml b/analysis/src/References.ml index 3da82433e..b313b9044 100644 --- a/analysis/src/References.ml +++ b/analysis/src/References.ml @@ -459,15 +459,30 @@ let forLocalStamp ~full:{file; extra; package} stamp tip = let allReferencesForLocItem ~full:({file; package} as full) locItem = match locItem.locType with | TopLevelModule moduleName -> - let locs = - match Hashtbl.find_opt full.extra.fileReferences moduleName with + let otherModulesReferences = + package.localModules + |> Utils.filterMap (fun name -> + match ProcessCmt.fileForModule ~package name with + | None -> None + | Some file -> ProcessCmt.getFullFromCmt ~uri:file.uri) + |> List.map (fun full -> + match Hashtbl.find_opt full.extra.fileReferences moduleName with + | None -> [] + | Some locs -> + locs + |> List.map (fun loc -> + (Uri2.fromPath loc.Location.loc_start.pos_fname, [loc]))) + |> List.flatten + in + let targetModuleReference = + match Hashtbl.find_opt package.pathsForModule moduleName with | None -> [] - | Some locs -> - locs - |> List.map (fun loc -> - (Uri2.fromPath loc.Location.loc_start.pos_fname, [loc])) + | Some paths -> ( + match SharedTypes.getSrc paths with + | None -> [] + | Some src -> [(Uri2.fromPath src, [Utils.topLoc src])]) in - locs + List.append targetModuleReference otherModulesReferences | Typed (_, _, NotFound) | LModule NotFound | Constant _ -> [] | TypeDefinition (_, _, stamp) -> forLocalStamp ~full stamp Type | Typed (_, _, (LocalReference (stamp, tip) | Definition (stamp, tip))) diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index 63f400742..fbce959f9 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -3,6 +3,7 @@ References tests/src/Cross.res 0:17 {"uri": "Cross.res", "range": {"start": {"line": 0, "character": 15}, "end": {"line": 0, "character": 25}}}, {"uri": "Cross.res", "range": {"start": {"line": 3, "character": 16}, "end": {"line": 3, "character": 26}}}, {"uri": "Cross.res", "range": {"start": {"line": 6, "character": 13}, "end": {"line": 6, "character": 23}}}, -{"uri": "Cross.res", "range": {"start": {"line": 8, "character": 16}, "end": {"line": 8, "character": 26}}} +{"uri": "Cross.res", "range": {"start": {"line": 8, "character": 16}, "end": {"line": 8, "character": 26}}}, +{"uri": "References.res", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}} ] From a328945474fb13786ff7d979dc375a4f98a68cb2 Mon Sep 17 00:00:00 2001 From: Amirali Esmaeili Date: Wed, 26 May 2021 17:26:53 +0430 Subject: [PATCH 02/11] Fix reference to interfaces --- analysis/src/References.ml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/analysis/src/References.ml b/analysis/src/References.ml index b313b9044..45ee775ff 100644 --- a/analysis/src/References.ml +++ b/analysis/src/References.ml @@ -474,15 +474,19 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = (Uri2.fromPath loc.Location.loc_start.pos_fname, [loc]))) |> List.flatten in - let targetModuleReference = + let targetModuleReferences = match Hashtbl.find_opt package.pathsForModule moduleName with | None -> [] | Some paths -> ( - match SharedTypes.getSrc paths with - | None -> [] - | Some src -> [(Uri2.fromPath src, [Utils.topLoc src])]) + let moduleSrcToRef src = (Uri2.fromPath src, [Utils.topLoc src]) in + match paths with + | Impl (_, None) -> [] + | Impl (_, Some src) -> [moduleSrcToRef src] + | Intf (_, srci) -> [moduleSrcToRef srci] + | IntfAndImpl (_, srci, _, src) -> + [moduleSrcToRef srci; moduleSrcToRef src]) in - List.append targetModuleReference otherModulesReferences + List.append targetModuleReferences otherModulesReferences | Typed (_, _, NotFound) | LModule NotFound | Constant _ -> [] | TypeDefinition (_, _, stamp) -> forLocalStamp ~full stamp Type | Typed (_, _, (LocalReference (stamp, tip) | Definition (stamp, tip))) From f15a8380e807df8e2efd7e11255ab2ecbf264ae3 Mon Sep 17 00:00:00 2001 From: Amirali Esmaeili Date: Wed, 26 May 2021 23:57:53 +0430 Subject: [PATCH 03/11] Add support for file renames --- server/src/server.ts | 32 +++++++++++++++++++++++++------- server/src/utils.ts | 8 ++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index d6e72584b..81330412f 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -15,7 +15,7 @@ import * as utils from "./utils"; import * as c from "./constants"; import * as chokidar from "chokidar"; import { assert } from "console"; -import { fileURLToPath } from "url"; +import { fileURLToPath, pathToFileURL } from "url"; import { ChildProcess } from "child_process"; import { WorkspaceEdit } from "vscode-languageserver"; import { TextEdit } from "vscode-languageserver-types"; @@ -294,16 +294,34 @@ function rename(msg: p.RequestMessage) { if (locations === null) { result = null; } else { - let changes: { [uri: string]: TextEdit[] } = {}; + let textEdits: { [uri: string]: TextEdit[] } = {}; + let documentChanges: (p.RenameFile | p.TextDocumentEdit)[] = []; + locations.forEach(({ uri, range }) => { - let textEdit: TextEdit = { range, newText: params.newName }; - if (uri in changes) { - changes[uri].push(textEdit); + if (utils.isRangeTopOfFile(range)) { + let filePath = fileURLToPath(uri); + let newFilePath = `${path.dirname(filePath)}/${params.newName}${path.extname(filePath)}`; + let newUri = pathToFileURL(newFilePath).href; + let rename: p.RenameFile = { kind: "rename", oldUri: uri, newUri }; + documentChanges.push(rename); } else { - changes[uri] = [textEdit]; + let textEdit: TextEdit = { range, newText: params.newName }; + if (uri in textEdits) { + textEdits[uri].push(textEdit); + } else { + textEdits[uri] = [textEdit]; + } } }); - result = { changes }; + + Object.entries(textEdits) + .forEach(([uri, edits]) => { + let textDocumentEdit = { textDocument: { uri, version: null }, edits }; + documentChanges.push(textDocumentEdit); + }); + + + result = { documentChanges }; } let response: m.ResponseMessage = { jsonrpc: c.jsonrpcVersion, diff --git a/server/src/utils.ts b/server/src/utils.ts index 2c6d236f9..b32fd80a6 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -476,3 +476,11 @@ export let parseCompilerLogOutput = ( return { done, result }; }; + +export let isRangeTopOfFile = (range: p.Range) => + [ + range.start.character, + range.start.line, + range.end.character, + range.end.line + ].every(n => n === 0); From 13a09b39ba3f6b4b0d4eb8a0c011599caac97903 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 30 May 2021 04:54:34 +0200 Subject: [PATCH 04/11] Fix build after rebase. --- analysis/src/References.ml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/analysis/src/References.ml b/analysis/src/References.ml index 45ee775ff..348f788eb 100644 --- a/analysis/src/References.ml +++ b/analysis/src/References.ml @@ -460,7 +460,7 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = match locItem.locType with | TopLevelModule moduleName -> let otherModulesReferences = - package.localModules + package.projectFiles |> Utils.filterMap (fun name -> match ProcessCmt.fileForModule ~package name with | None -> None @@ -480,11 +480,9 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = | Some paths -> ( let moduleSrcToRef src = (Uri2.fromPath src, [Utils.topLoc src]) in match paths with - | Impl (_, None) -> [] - | Impl (_, Some src) -> [moduleSrcToRef src] - | Intf (_, srci) -> [moduleSrcToRef srci] - | IntfAndImpl (_, srci, _, src) -> - [moduleSrcToRef srci; moduleSrcToRef src]) + | Impl {res} -> [moduleSrcToRef res] + | IntfAndImpl {resi; res} -> [moduleSrcToRef resi; moduleSrcToRef res] + | Namespace _ -> []) in List.append targetModuleReferences otherModulesReferences | Typed (_, _, NotFound) | LModule NotFound | Constant _ -> [] From f8802ba91d8071eb7e31301cbe0a0e39b536edd6 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Sun, 30 May 2021 04:59:23 +0200 Subject: [PATCH 05/11] Refactor getSrc --- analysis/src/References.ml | 7 ++----- analysis/src/SharedTypes.ml | 6 ++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/analysis/src/References.ml b/analysis/src/References.ml index 348f788eb..43095c8a0 100644 --- a/analysis/src/References.ml +++ b/analysis/src/References.ml @@ -477,12 +477,9 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = let targetModuleReferences = match Hashtbl.find_opt package.pathsForModule moduleName with | None -> [] - | Some paths -> ( + | Some paths -> let moduleSrcToRef src = (Uri2.fromPath src, [Utils.topLoc src]) in - match paths with - | Impl {res} -> [moduleSrcToRef res] - | IntfAndImpl {resi; res} -> [moduleSrcToRef resi; moduleSrcToRef res] - | Namespace _ -> []) + getSrc paths |> List.map moduleSrcToRef in List.append targetModuleReferences otherModulesReferences | Typed (_, _, NotFound) | LModule NotFound | Constant _ -> [] diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index ca5ebeddf..948996527 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -130,6 +130,12 @@ let showPaths paths = | IntfAndImpl {cmti; resi; cmt; res} -> Printf.sprintf "IntfAndImpl(%s, %s, %s, %s)" cmti resi cmt res +let getSrc p = + match p with + | Impl {res} -> [res] + | Namespace _ -> [] + | IntfAndImpl {resi; res} -> [resi; res] + let getUri p = match p with | Impl {res} -> Uri2.fromPath res From 96ef240e00cf1bcf2a5772ae09aecc19426701bc Mon Sep 17 00:00:00 2001 From: Amirali Esmaeili Date: Sat, 5 Jun 2021 03:25:54 +0430 Subject: [PATCH 06/11] Add rename command --- analysis/src/Cli.ml | 12 +++++-- analysis/src/Commands.ml | 72 ++++++++++++++++++++++++++++++++++++++++ analysis/src/Protocol.ml | 47 +++++++++++++++++++++++++- analysis/src/Utils.ml | 6 ++++ 4 files changed, 134 insertions(+), 3 deletions(-) diff --git a/analysis/src/Cli.ml b/analysis/src/Cli.ml index 6b9edc32b..ce982dafc 100644 --- a/analysis/src/Cli.ml +++ b/analysis/src/Cli.ml @@ -8,6 +8,7 @@ API examples: ./rescript-editor-analysis.exe documentSymbol src/Foo.res ./rescript-editor-analysis.exe hover src/MyFile.res 10 2 ./rescript-editor-analysis.exe references src/MyFile.res 10 2 + ./rescript-editor-analysis.exe rename src/MyFile.res 10 2 foo Dev-time examples: ./rescript-editor-analysis.exe dump src/MyFile.res src/MyFile2.res @@ -38,6 +39,10 @@ Options: ./rescript-editor-analysis.exe references src/MyFile.res 10 2 + rename: rename all appearances of item in MyFile.res at line 10 column 2 with foo: + + ./rescript-editor-analysis.exe rename src/MyFile.res 10 2 foo + dump: for debugging, show all definitions and hovers for MyFile.res and MyFile.res: ./rescript-editor-analysis.exe dump src/Foo.res src/MyFile.res @@ -50,8 +55,8 @@ Options: let main () = match Array.to_list Sys.argv with | [_; "completion"; path; line; col; currentFile] -> - Commands.completion ~path ~line:(int_of_string line) ~col:(int_of_string col) - ~currentFile + Commands.completion ~path ~line:(int_of_string line) + ~col:(int_of_string col) ~currentFile | [_; "definition"; path; line; col] -> Commands.definition ~path ~line:(int_of_string line) ~col:(int_of_string col) @@ -62,6 +67,9 @@ let main () = | [_; "references"; path; line; col] -> Commands.references ~path ~line:(int_of_string line) ~col:(int_of_string col) + | [_; "rename"; path; line; col; newName] -> + Commands.rename ~path ~line:(int_of_string line) ~col:(int_of_string col) + ~newName | [_; "test"; path] -> Commands.test ~path | args when List.mem "-h" args || List.mem "--help" args -> prerr_endline help | _ -> diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index d2e0dd544..667f98bef 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -176,6 +176,78 @@ let documentSymbol ~path = in print_endline ("[\n" ^ (allSymbols |> String.concat ",\n") ^ "\n]") +let rename ~path ~line ~col ~newName = + let uri = Uri2.fromPath path in + let result = + match ProcessCmt.getFullFromCmt ~uri with + | None -> Protocol.null + | Some full -> ( + let pos = Utils.protocolLineColToCmtLoc ~line ~col in + match References.locItemForPos ~full pos with + | None -> Protocol.null + | Some locItem -> + let allReferences = References.allReferencesForLocItem ~full locItem in + let referencesToToplevelModules, referencesToItems = + allReferences + |> List.fold_left + (fun acc (uri2, references) -> + (references |> List.map (fun loc -> (uri2, loc))) @ acc) + [] + |> List.partition (fun (_, loc) -> Utils.isTopLoc loc) + in + let fileRenames = + referencesToToplevelModules + |> List.map (fun (uri, _) -> + let path = Uri2.toPath uri in + let dir = Filename.dirname path in + let ext = Filename.extension path in + let sep = Filename.dir_sep in + let newPath = dir ^ sep ^ newName ^ ext in + let newUri = Uri2.fromPath newPath in + Protocol. + { + kind = `rename; + oldUri = uri |> Uri2.toString; + newUri = newUri |> Uri2.toString; + }) + in + let textDocumentEdits = + let module StringMap = Misc.StringMap in + let textEditsByUri = + referencesToItems + |> List.map (fun (uri, loc) -> (Uri2.toString uri, loc)) + |> List.fold_left + (fun acc (uri, loc) -> + let textEdit = + Protocol. + {range = Utils.cmtLocToRange loc; newText = newName} + in + match StringMap.find_opt uri acc with + | None -> StringMap.add uri [textEdit] acc + | Some prevEdits -> + StringMap.add uri (textEdit :: prevEdits) acc) + StringMap.empty + in + StringMap.fold + (fun uri edits acc -> + let textDocumentEdit = + Protocol.{textDocument = {uri; version = None}; edits} + in + textDocumentEdit :: acc) + textEditsByUri [] + in + let fileRenamesString = + fileRenames |> List.map Protocol.stringifyRenameFile + in + let textDocumentEditsString = + textDocumentEdits |> List.map Protocol.stringifyTextDocumentEdit + in + "[\n" + ^ (fileRenamesString @ textDocumentEditsString |> String.concat ",\n") + ^ "\n]") + in + print_endline result + let test ~path = Uri2.stripPath := true; match Files.readFile path with diff --git a/analysis/src/Protocol.ml b/analysis/src/Protocol.ml index 021ca46b7..8289d02f2 100644 --- a/analysis/src/Protocol.ml +++ b/analysis/src/Protocol.ml @@ -18,6 +18,20 @@ type location = {uri : string; range : range} type documentSymbolItem = {name : string; kind : int; location : location} +type renameFile = {kind : [`rename]; oldUri : string; newUri : string} + +type textEdit = {range : range; newText : string} + +type optionalVersionedTextDocumentIdentifier = { + version : int option; + uri : string; +} + +type textDocumentEdit = { + textDocument : optionalVersionedTextDocumentIdentifier; + edits : textEdit list; +} + let null = "null" let array l = "[" ^ String.concat ", " l ^ "]" @@ -52,7 +66,7 @@ let stringifyCompletionItem c = let stringifyHover h = Printf.sprintf {|{"contents": "%s"}|} (Json.escape h.contents) -let stringifyLocation h = +let stringifyLocation (h : location) = Printf.sprintf {|{"uri": "%s", "range": %s}|} (Json.escape h.uri) (stringifyRange h.range) @@ -65,3 +79,34 @@ let stringifyDocumentSymbolItem i = }|} (Json.escape i.name) i.kind (stringifyLocation i.location) + +let stringifyRenameFile rf = + Printf.sprintf {|{ + "kind": "rename", + "oldUri": "%s", + "newUri": "%s" +}|} + (Json.escape rf.oldUri) (Json.escape rf.newUri) + +let stringifyTextEdit te = + Printf.sprintf {|{ + "range": %s, + "newText": "%s" + }|} + (stringifyRange te.range) (Json.escape te.newText) + +let stringifyoptionalVersionedTextDocumentIdentifier td = + Printf.sprintf {|{ + "version": %s, + "uri": "%s" + }|} + (match td.version with None -> null | Some v -> string_of_int v) + (Json.escape td.uri) + +let stringifyTextDocumentEdit tde = + Printf.sprintf {|{ + "textDocument": %s, + "edits": %s + }|} + (stringifyoptionalVersionedTextDocumentIdentifier tde.textDocument) + (tde.edits |> List.map stringifyTextEdit |> array) diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml index d56632619..767d8714d 100644 --- a/analysis/src/Utils.ml +++ b/analysis/src/Utils.ml @@ -7,6 +7,12 @@ let topLoc fname = loc_ghost = false; } +let isTopLoc (loc : Warnings.loc) = + let isTopPos (pos : Lexing.position) = + pos.pos_lnum = 1 && pos.pos_bol = 0 && pos.pos_cnum = 0 + in + isTopPos loc.loc_start && isTopPos loc.loc_end && loc.loc_ghost = false + (** * `startsWith(string, prefix)` * true if the string starts with the prefix From e31cf55e5ae3d55962e86385fd23ea7d679997d5 Mon Sep 17 00:00:00 2001 From: Amirali Esmaeili Date: Sat, 5 Jun 2021 03:29:18 +0430 Subject: [PATCH 07/11] Make dce analyzer happy --- analysis/src/Protocol.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/analysis/src/Protocol.ml b/analysis/src/Protocol.ml index 8289d02f2..f93d9e090 100644 --- a/analysis/src/Protocol.ml +++ b/analysis/src/Protocol.ml @@ -82,10 +82,11 @@ let stringifyDocumentSymbolItem i = let stringifyRenameFile rf = Printf.sprintf {|{ - "kind": "rename", + "kind": "%s", "oldUri": "%s", "newUri": "%s" }|} + (match rf.kind with `rename -> "rename") (Json.escape rf.oldUri) (Json.escape rf.newUri) let stringifyTextEdit te = From 53344ce0d182a684fb4ad1b26dc63b5496a6ac00 Mon Sep 17 00:00:00 2001 From: Amirali Esmaeili Date: Sat, 5 Jun 2021 03:39:47 +0430 Subject: [PATCH 08/11] Add tests --- analysis/tests/src/Cross.res | 10 ++++++++++ analysis/tests/src/ReferencesWithInterface.res | 2 ++ analysis/tests/src/ReferencesWithInterface.resi | 2 ++ analysis/tests/src/expected/Cross.res.txt | 10 ++++++++++ .../tests/src/expected/ReferencesWithInterface.res.txt | 9 +++++++++ .../src/expected/ReferencesWithInterface.resi.txt | 9 +++++++++ 6 files changed, 42 insertions(+) create mode 100644 analysis/tests/src/ReferencesWithInterface.res create mode 100644 analysis/tests/src/ReferencesWithInterface.resi create mode 100644 analysis/tests/src/expected/ReferencesWithInterface.res.txt create mode 100644 analysis/tests/src/expected/ReferencesWithInterface.resi.txt diff --git a/analysis/tests/src/Cross.res b/analysis/tests/src/Cross.res index 489f5ae0b..d427b7d12 100644 --- a/analysis/tests/src/Cross.res +++ b/analysis/tests/src/Cross.res @@ -7,3 +7,13 @@ let crossRef2 = References.x module Ref = References let crossRef3 = References.x + + +let crossRefWithInterface = ReferencesWithInterface.x +// ^ref + +let crossRefWithInterface2 = ReferencesWithInterface.x + +module RefWithInterface = ReferencesWithInterface + +let crossRefWithInterface3 = ReferencesWithInterface.x diff --git a/analysis/tests/src/ReferencesWithInterface.res b/analysis/tests/src/ReferencesWithInterface.res new file mode 100644 index 000000000..54b4b8b16 --- /dev/null +++ b/analysis/tests/src/ReferencesWithInterface.res @@ -0,0 +1,2 @@ +let x = 2 +// ^ref diff --git a/analysis/tests/src/ReferencesWithInterface.resi b/analysis/tests/src/ReferencesWithInterface.resi new file mode 100644 index 000000000..765cf3c6d --- /dev/null +++ b/analysis/tests/src/ReferencesWithInterface.resi @@ -0,0 +1,2 @@ +let x: int +// ^ref diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index fbce959f9..23690b5d2 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -7,3 +7,13 @@ References tests/src/Cross.res 0:17 {"uri": "References.res", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}} ] +References tests/src/Cross.res 11:31 +[ +{"uri": "Cross.res", "range": {"start": {"line": 11, "character": 28}, "end": {"line": 11, "character": 51}}}, +{"uri": "Cross.res", "range": {"start": {"line": 14, "character": 29}, "end": {"line": 14, "character": 52}}}, +{"uri": "Cross.res", "range": {"start": {"line": 16, "character": 26}, "end": {"line": 16, "character": 49}}}, +{"uri": "Cross.res", "range": {"start": {"line": 18, "character": 29}, "end": {"line": 18, "character": 52}}}, +{"uri": "ReferencesWithInterface.res", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}}, +{"uri": "ReferencesWithInterface.resi", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}} +] + diff --git a/analysis/tests/src/expected/ReferencesWithInterface.res.txt b/analysis/tests/src/expected/ReferencesWithInterface.res.txt new file mode 100644 index 000000000..2a077a83f --- /dev/null +++ b/analysis/tests/src/expected/ReferencesWithInterface.res.txt @@ -0,0 +1,9 @@ +References tests/src/ReferencesWithInterface.res 0:4 +[ +{"uri": "Cross.res", "range": {"start": {"line": 18, "character": 53}, "end": {"line": 18, "character": 54}}}, +{"uri": "Cross.res", "range": {"start": {"line": 14, "character": 53}, "end": {"line": 14, "character": 54}}}, +{"uri": "Cross.res", "range": {"start": {"line": 11, "character": 52}, "end": {"line": 11, "character": 53}}}, +{"uri": "ReferencesWithInterface.resi", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}}, +{"uri": "ReferencesWithInterface.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}} +] + diff --git a/analysis/tests/src/expected/ReferencesWithInterface.resi.txt b/analysis/tests/src/expected/ReferencesWithInterface.resi.txt new file mode 100644 index 000000000..e7f7db246 --- /dev/null +++ b/analysis/tests/src/expected/ReferencesWithInterface.resi.txt @@ -0,0 +1,9 @@ +References tests/src/ReferencesWithInterface.resi 0:4 +[ +{"uri": "Cross.res", "range": {"start": {"line": 18, "character": 53}, "end": {"line": 18, "character": 54}}}, +{"uri": "Cross.res", "range": {"start": {"line": 14, "character": 53}, "end": {"line": 14, "character": 54}}}, +{"uri": "Cross.res", "range": {"start": {"line": 11, "character": 52}, "end": {"line": 11, "character": 53}}}, +{"uri": "ReferencesWithInterface.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}}, +{"uri": "ReferencesWithInterface.resi", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}} +] + From db7f838d697c79537bb69f612eb02a96f5479cc0 Mon Sep 17 00:00:00 2001 From: Amirali Esmaeili Date: Sat, 5 Jun 2021 08:26:18 +0430 Subject: [PATCH 09/11] Fix checking for interface file on windows --- analysis/src/References.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analysis/src/References.ml b/analysis/src/References.ml index 43095c8a0..9dcc16aa7 100644 --- a/analysis/src/References.ml +++ b/analysis/src/References.ml @@ -192,7 +192,7 @@ let alternateDeclared ~(file : File.t) ~package declared tip = maybeLog "Have both!!"; let resiUri = Uri2.fromPath resi in let resUri = Uri2.fromPath res in - if resiUri = file.uri then + if Uri2.isInterface file.uri then match ProcessCmt.getFullFromCmt ~uri:resUri with | None -> None | Some {file; extra} -> ( From c025498f0444e199dc724015f561313324f3a65e Mon Sep 17 00:00:00 2001 From: Amirali Esmaeili Date: Sat, 5 Jun 2021 09:01:05 +0430 Subject: [PATCH 10/11] Add rename tests --- analysis/src/Commands.ml | 9 +++ analysis/tests/src/Cross.res | 7 ++ analysis/tests/src/Rename.res | 11 ++++ analysis/tests/src/RenameWithInterface.res | 2 + analysis/tests/src/RenameWithInterface.resi | 2 + analysis/tests/src/expected/Cross.res.txt | 64 +++++++++++++++++++ analysis/tests/src/expected/Rename.res.txt | 37 +++++++++++ .../src/expected/RenameWithInterface.res.txt | 37 +++++++++++ .../src/expected/RenameWithInterface.resi.txt | 37 +++++++++++ 9 files changed, 206 insertions(+) create mode 100644 analysis/tests/src/Rename.res create mode 100644 analysis/tests/src/RenameWithInterface.res create mode 100644 analysis/tests/src/RenameWithInterface.resi create mode 100644 analysis/tests/src/expected/Rename.res.txt create mode 100644 analysis/tests/src/expected/RenameWithInterface.res.txt create mode 100644 analysis/tests/src/expected/RenameWithInterface.resi.txt diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 667f98bef..417fd397e 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -283,6 +283,15 @@ let test ~path = | "doc" -> print_endline ("DocumentSymbol " ^ path); documentSymbol ~path + | "ren" -> + let newName = String.sub rest 4 (len - mlen - 4) in + let () = + print_endline + ("Rename " ^ path ^ " " ^ string_of_int line ^ ":" + ^ string_of_int col ^ " " ^ newName) + in + + rename ~path ~line ~col ~newName | "com" -> print_endline ("Complete " ^ path ^ " " ^ string_of_int line ^ ":" diff --git a/analysis/tests/src/Cross.res b/analysis/tests/src/Cross.res index d427b7d12..c4fc51baf 100644 --- a/analysis/tests/src/Cross.res +++ b/analysis/tests/src/Cross.res @@ -17,3 +17,10 @@ let crossRefWithInterface2 = ReferencesWithInterface.x module RefWithInterface = ReferencesWithInterface let crossRefWithInterface3 = ReferencesWithInterface.x + + +let _ = RenameWithInterface.x +// ^ren RenameWithInterfacePrime + +let _ = RenameWithInterface.x +// ^ren xPrime diff --git a/analysis/tests/src/Rename.res b/analysis/tests/src/Rename.res new file mode 100644 index 000000000..b9e3270de --- /dev/null +++ b/analysis/tests/src/Rename.res @@ -0,0 +1,11 @@ +let x = 12 +// ^ren y + +let a = x + +let b = a + +let c = x + +let foo = (~xx) => xx + 1 +// ^ren yy diff --git a/analysis/tests/src/RenameWithInterface.res b/analysis/tests/src/RenameWithInterface.res new file mode 100644 index 000000000..5c68741c7 --- /dev/null +++ b/analysis/tests/src/RenameWithInterface.res @@ -0,0 +1,2 @@ +let x = 2 +// ^ren y diff --git a/analysis/tests/src/RenameWithInterface.resi b/analysis/tests/src/RenameWithInterface.resi new file mode 100644 index 000000000..8657edc21 --- /dev/null +++ b/analysis/tests/src/RenameWithInterface.resi @@ -0,0 +1,2 @@ +let x: int +// ^ren y diff --git a/analysis/tests/src/expected/Cross.res.txt b/analysis/tests/src/expected/Cross.res.txt index 23690b5d2..2eedb9c62 100644 --- a/analysis/tests/src/expected/Cross.res.txt +++ b/analysis/tests/src/expected/Cross.res.txt @@ -17,3 +17,67 @@ References tests/src/Cross.res 11:31 {"uri": "ReferencesWithInterface.resi", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}} ] +Rename tests/src/Cross.res 21:13 RenameWithInterfacePrime +[ +{ + "kind": "rename", + "oldUri": "RenameWithInterface.res", + "newUri": "RenameWithInterfacePrime.res" +}, +{ + "kind": "rename", + "oldUri": "RenameWithInterface.resi", + "newUri": "RenameWithInterfacePrime.resi" +}, +{ + "textDocument": { + "version": null, + "uri": "Cross.res" + }, + "edits": [{ + "range": {"start": {"line": 24, "character": 8}, "end": {"line": 24, "character": 27}}, + "newText": "RenameWithInterfacePrime" + }, { + "range": {"start": {"line": 21, "character": 8}, "end": {"line": 21, "character": 27}}, + "newText": "RenameWithInterfacePrime" + }] + } +] + +Rename tests/src/Cross.res 24:28 xPrime +[ +{ + "textDocument": { + "version": null, + "uri": "RenameWithInterface.resi" + }, + "edits": [{ + "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, + "newText": "xPrime" + }] + }, +{ + "textDocument": { + "version": null, + "uri": "RenameWithInterface.res" + }, + "edits": [{ + "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, + "newText": "xPrime" + }] + }, +{ + "textDocument": { + "version": null, + "uri": "Cross.res" + }, + "edits": [{ + "range": {"start": {"line": 21, "character": 28}, "end": {"line": 21, "character": 29}}, + "newText": "xPrime" + }, { + "range": {"start": {"line": 24, "character": 28}, "end": {"line": 24, "character": 29}}, + "newText": "xPrime" + }] + } +] + diff --git a/analysis/tests/src/expected/Rename.res.txt b/analysis/tests/src/expected/Rename.res.txt new file mode 100644 index 000000000..49cb75877 --- /dev/null +++ b/analysis/tests/src/expected/Rename.res.txt @@ -0,0 +1,37 @@ +Rename tests/src/Rename.res 0:4 y +[ +{ + "textDocument": { + "version": null, + "uri": "Rename.res" + }, + "edits": [{ + "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, + "newText": "y" + }, { + "range": {"start": {"line": 3, "character": 8}, "end": {"line": 3, "character": 9}}, + "newText": "y" + }, { + "range": {"start": {"line": 7, "character": 8}, "end": {"line": 7, "character": 9}}, + "newText": "y" + }] + } +] + +Rename tests/src/Rename.res 9:19 yy +[ +{ + "textDocument": { + "version": null, + "uri": "Rename.res" + }, + "edits": [{ + "range": {"start": {"line": 9, "character": 12}, "end": {"line": 9, "character": 14}}, + "newText": "yy" + }, { + "range": {"start": {"line": 9, "character": 19}, "end": {"line": 9, "character": 21}}, + "newText": "yy" + }] + } +] + diff --git a/analysis/tests/src/expected/RenameWithInterface.res.txt b/analysis/tests/src/expected/RenameWithInterface.res.txt new file mode 100644 index 000000000..8730f2535 --- /dev/null +++ b/analysis/tests/src/expected/RenameWithInterface.res.txt @@ -0,0 +1,37 @@ +Rename tests/src/RenameWithInterface.res 0:4 y +[ +{ + "textDocument": { + "version": null, + "uri": "RenameWithInterface.resi" + }, + "edits": [{ + "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, + "newText": "y" + }] + }, +{ + "textDocument": { + "version": null, + "uri": "RenameWithInterface.res" + }, + "edits": [{ + "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, + "newText": "y" + }] + }, +{ + "textDocument": { + "version": null, + "uri": "Cross.res" + }, + "edits": [{ + "range": {"start": {"line": 21, "character": 28}, "end": {"line": 21, "character": 29}}, + "newText": "y" + }, { + "range": {"start": {"line": 24, "character": 28}, "end": {"line": 24, "character": 29}}, + "newText": "y" + }] + } +] + diff --git a/analysis/tests/src/expected/RenameWithInterface.resi.txt b/analysis/tests/src/expected/RenameWithInterface.resi.txt new file mode 100644 index 000000000..2524b6a43 --- /dev/null +++ b/analysis/tests/src/expected/RenameWithInterface.resi.txt @@ -0,0 +1,37 @@ +Rename tests/src/RenameWithInterface.resi 0:4 y +[ +{ + "textDocument": { + "version": null, + "uri": "RenameWithInterface.resi" + }, + "edits": [{ + "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, + "newText": "y" + }] + }, +{ + "textDocument": { + "version": null, + "uri": "RenameWithInterface.res" + }, + "edits": [{ + "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}, + "newText": "y" + }] + }, +{ + "textDocument": { + "version": null, + "uri": "Cross.res" + }, + "edits": [{ + "range": {"start": {"line": 21, "character": 28}, "end": {"line": 21, "character": 29}}, + "newText": "y" + }, { + "range": {"start": {"line": 24, "character": 28}, "end": {"line": 24, "character": 29}}, + "newText": "y" + }] + } +] + From b1dc77df5beefa36181d3ef7cdfd61d9b4115666 Mon Sep 17 00:00:00 2001 From: Amirali Esmaeili Date: Sat, 5 Jun 2021 09:07:43 +0430 Subject: [PATCH 11/11] Update server to use rename command --- server/src/server.ts | 46 +++++++++++++------------------------------- server/src/utils.ts | 8 -------- 2 files changed, 13 insertions(+), 41 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index 81330412f..b0e8e930e 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -286,48 +286,28 @@ function rename(msg: p.RequestMessage) { // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename let params = msg.params as p.RenameParams; let filePath = fileURLToPath(params.textDocument.uri); - let locations: p.Location[] | null = utils.getReferencesForPosition( + let documentChanges: + | (p.RenameFile | p.TextDocumentEdit)[] + | null = utils.runAnalysisAfterSanityCheck(filePath, [ + "rename", filePath, - params.position - ); - let result: WorkspaceEdit | null; - if (locations === null) { - result = null; - } else { - let textEdits: { [uri: string]: TextEdit[] } = {}; - let documentChanges: (p.RenameFile | p.TextDocumentEdit)[] = []; - - locations.forEach(({ uri, range }) => { - if (utils.isRangeTopOfFile(range)) { - let filePath = fileURLToPath(uri); - let newFilePath = `${path.dirname(filePath)}/${params.newName}${path.extname(filePath)}`; - let newUri = pathToFileURL(newFilePath).href; - let rename: p.RenameFile = { kind: "rename", oldUri: uri, newUri }; - documentChanges.push(rename); - } else { - let textEdit: TextEdit = { range, newText: params.newName }; - if (uri in textEdits) { - textEdits[uri].push(textEdit); - } else { - textEdits[uri] = [textEdit]; - } - } - }); - - Object.entries(textEdits) - .forEach(([uri, edits]) => { - let textDocumentEdit = { textDocument: { uri, version: null }, edits }; - documentChanges.push(textDocumentEdit); - }); + params.position.line, + params.position.character, + params.newName + ]); + let result: WorkspaceEdit | null = null; + if (documentChanges !== null) { result = { documentChanges }; } + let response: m.ResponseMessage = { jsonrpc: c.jsonrpcVersion, id: msg.id, - result, + result }; + return response; } diff --git a/server/src/utils.ts b/server/src/utils.ts index b32fd80a6..2c6d236f9 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -476,11 +476,3 @@ export let parseCompilerLogOutput = ( return { done, result }; }; - -export let isRangeTopOfFile = (range: p.Range) => - [ - range.start.character, - range.start.line, - range.end.character, - range.end.line - ].every(n => n === 0);