Skip to content

Set #263

New issue

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

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

Already on GitHub? Sign in to your account

Closed
wants to merge 14 commits into from
Closed

Set #263

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions analysis/src/Cli.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
| _ ->
Expand Down
72 changes: 72 additions & 0 deletions analysis/src/Commands.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions analysis/src/NewCompletions.ml
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ let getItems ~full ~package ~rawOpens ~allFiles ~pos ~parts =
in
(* TODO complete the namespaced name too *)
let localModuleNames =
allFiles
allFiles |> FileSet.elements
|> Utils.filterMap (fun name ->
if Utils.startsWith name suffix && not (String.contains name '-')
then Some {(emptyDeclared name) with item = FileModule name}
Expand Down Expand Up @@ -1148,7 +1148,9 @@ let computeCompletions ~uri ~textOpt ~pos =
| Some full ->
let rawOpens = PartialParser.findOpens text offset in
let package = full.package in
let allFiles = package.projectFiles @ package.dependenciesFiles in
let allFiles =
FileSet.union package.projectFiles package.dependenciesFiles
in
let findItems ~exact parts =
let items =
getItems ~full ~package ~rawOpens ~allFiles ~pos ~parts
Expand Down
8 changes: 6 additions & 2 deletions analysis/src/Packages.ml
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,12 @@ let newBsPackage rootPath =
Log.log ("Opens from bsconfig: " ^ (opens |> String.concat " "));
{
SharedTypes.rootPath;
projectFiles = projectFilesAndPaths |> List.map fst;
dependenciesFiles = dependenciesFilesAndPaths |> List.map fst;
projectFiles =
projectFilesAndPaths |> List.map fst
|> SharedTypes.FileSet.of_list;
dependenciesFiles =
dependenciesFilesAndPaths |> List.map fst
|> SharedTypes.FileSet.of_list;
pathsForModule;
opens;
namespace;
Expand Down
12 changes: 6 additions & 6 deletions analysis/src/ProcessCmt.ml
Original file line number Diff line number Diff line change
Expand Up @@ -734,12 +734,12 @@ struct
else []))

let addFileReference moduleName loc =
Hashtbl.replace extra.fileReferences moduleName
(loc
::
(if Hashtbl.mem extra.fileReferences moduleName then
Hashtbl.find extra.fileReferences moduleName
else []))
let newLocs =
match Hashtbl.find_opt extra.fileReferences moduleName with
| Some oldLocs -> LocationSet.add loc oldLocs
| None -> LocationSet.singleton loc
in
Hashtbl.replace extra.fileReferences moduleName newLocs

let env = QueryEnv.fromFile Collector.file

Expand Down
48 changes: 47 additions & 1 deletion analysis/src/Protocol.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 ^ "]"
Expand Down Expand Up @@ -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)

Expand All @@ -65,3 +79,35 @@ let stringifyDocumentSymbolItem i =
}|}
(Json.escape i.name) i.kind
(stringifyLocation i.location)

let stringifyRenameFile rf =
Printf.sprintf {|{
"kind": "%s",
"oldUri": "%s",
"newUri": "%s"
}|}
(match rf.kind with `rename -> "rename")
(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)
31 changes: 22 additions & 9 deletions analysis/src/References.ml
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,6 @@ let forLocalStamp ~full:{file; extra; package} stamp tip =
match Hashtbl.find_opt extra.internalReferences localStamp with
| None -> []
| Some local ->
maybeLog ("Checking externals: " ^ string_of_int stamp);
let externals =
match declaredForTip ~stamps:env.file.stamps stamp tip with
| None -> []
Expand Down Expand Up @@ -426,7 +425,7 @@ let forLocalStamp ~full:{file; extra; package} stamp tip =
maybeLog ("Now checking path " ^ pathToString path);
let thisModuleName = file.moduleName in
let externals =
package.projectFiles
package.projectFiles |> FileSet.elements
|> List.filter (fun name -> name <> file.moduleName)
|> Utils.filterMap (fun name ->
match ProcessCmt.fileForModule ~package name with
Expand Down Expand Up @@ -459,15 +458,29 @@ 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.projectFiles |> FileSet.elements
|> 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 |> LocationSet.elements
|> List.map (fun loc ->
(Uri2.fromPath loc.Location.loc_start.pos_fname, [loc])))
|> List.flatten
in
let targetModuleReferences =
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 ->
let moduleSrcToRef src = (Uri2.fromPath src, [Utils.topLoc src]) in
getSrc paths |> List.map moduleSrcToRef
in
locs
List.append targetModuleReferences otherModulesReferences
| Typed (_, _, NotFound) | LModule NotFound | Constant _ -> []
| TypeDefinition (_, _, stamp) -> forLocalStamp ~full stamp Type
| Typed (_, _, (LocalReference (stamp, tip) | Definition (stamp, tip)))
Expand Down
20 changes: 17 additions & 3 deletions analysis/src/SharedTypes.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -194,10 +200,16 @@ type openTracker = {
mutable used : (path * tip * Location.t) list;
}

module LocationSet = Set.Make (struct
include Location

let compare = compare (* polymorphic compare should be OK *)
end)

type extra = {
internalReferences : (int, Location.t list) Hashtbl.t;
externalReferences : (string, (path * tip * Location.t) list) Hashtbl.t;
fileReferences : (string, Location.t list) Hashtbl.t;
fileReferences : (string, LocationSet.t) Hashtbl.t;
mutable locItems : locItem list;
(* This is the "open location", like the location...
or maybe the >> location of the open ident maybe *)
Expand All @@ -207,10 +219,12 @@ type extra = {

type file = string

module FileSet = Set.Make (String)

type package = {
rootPath : filePath;
projectFiles : file list;
dependenciesFiles : file list;
projectFiles : FileSet.t;
dependenciesFiles : FileSet.t;
pathsForModule : (file, paths) Hashtbl.t;
namespace : string option;
opens : string list;
Expand Down
6 changes: 6 additions & 0 deletions analysis/src/Utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions analysis/tests/src/Cross.res
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions analysis/tests/src/ReferencesWithInterface.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
let x = 2
// ^ref
5 changes: 5 additions & 0 deletions analysis/tests/src/ReferencesWithInterface.resi
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@



let x: int
// ^ref
17 changes: 14 additions & 3 deletions analysis/tests/src/expected/Cross.res.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
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": 8, "character": 16}, "end": {"line": 8, "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": 3, "character": 16}, "end": {"line": 3, "character": 26}}},
{"uri": "Cross.res", "range": {"start": {"line": 0, "character": 15}, "end": {"line": 0, "character": 25}}},
{"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": 18, "character": 29}, "end": {"line": 18, "character": 52}}},
{"uri": "Cross.res", "range": {"start": {"line": 16, "character": 26}, "end": {"line": 16, "character": 49}}},
{"uri": "Cross.res", "range": {"start": {"line": 14, "character": 29}, "end": {"line": 14, "character": 52}}},
{"uri": "Cross.res", "range": {"start": {"line": 11, "character": 28}, "end": {"line": 11, "character": 51}}},
{"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}}}
]

Loading