Skip to content

Commit

Permalink
Merge pull request #545 from usethesource/fix/rename-refactoring/use-…
Browse files Browse the repository at this point in the history
…tmodel

Renaming: Use extended TModel instead of custom ADT.
  • Loading branch information
toinehartman authored Dec 17, 2024
2 parents 6725358 + 24010c8 commit 3c5ff77
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 154 deletions.
142 changes: 72 additions & 70 deletions rascal-lsp/src/main/rascal/lang/rascal/lsp/refactor/Rename.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -85,22 +85,22 @@ private set[IllegalRenameReason] rascalCheckLegalName(str name, set[IdRole] role
if ({fieldId(), *_} := roles) asType = <#NonterminalLabel, "constructor field name">;
if (size(syntaxRoles & roles) > 0) asType = <#Nonterminal, "non-terminal name">;

if (!tryParseAs(asType.as, escName)) return {invalidName(escName, asType.desc)};
if (tryParseAs(asType.as, escName) is nothing) return {invalidName(escName, asType.desc)};
return {};
}

private void rascalCheckLegalName(str name, Symbol sym) {
escName = rascalEscapeName(name);
g = grammar(#start[Module]);
if (!tryParseAs(type(sym, g.rules), escName)) {
if (tryParseAs(type(sym, g.rules), escName) is nothing) {
throw illegalRename("\'<escName>\' is not a valid name at this position", {invalidName(escName, "<sym>")});
}
}

private set[IllegalRenameReason] rascalCheckDefinitionsOutsideWorkspace(WorkspaceInfo ws, set[loc] defs) =
private set[IllegalRenameReason] rascalCheckDefinitionsOutsideWorkspace(TModel ws, set[loc] defs) =
{ definitionsOutsideWorkspace(d) | set[loc] d <- groupRangeByDomain({<f, d> | loc d <- defs, f := d.top, f notin ws.sourceFiles}) };

private set[IllegalRenameReason] rascalCheckCausesDoubleDeclarations(WorkspaceInfo ws, set[loc] currentDefs, set[Define] newDefs, str newName) {
private set[IllegalRenameReason] rascalCheckCausesDoubleDeclarations(TModel ws, set[loc] currentDefs, set[Define] newDefs, str newName) {
// Is newName already resolvable from a scope where <current-name> is currently declared?
rel[loc old, loc new] doubleDeclarations = {<cD, nD.defined> | <loc cD, Define nD> <- (currentDefs * newDefs)
, isContainedIn(cD, nD.scope)
Expand Down Expand Up @@ -128,8 +128,8 @@ private set[IllegalRenameReason] rascalCheckCausesDoubleDeclarations(WorkspaceIn
return {doubleDeclaration(old, doubleDeclarations[old]) | old <- (doubleDeclarations + doubleFieldDeclarations + doubleTypeParamDeclarations).old};
}
private set[IllegalRenameReason] rascalCheckCausesCaptures(WorkspaceInfo ws, start[Module] m, set[loc] currentDefs, set[loc] currentUses, set[Define] newDefs) {
set[Define] rascalFindImplicitDefinitions(WorkspaceInfo ws, start[Module] m, set[Define] newDefs) {
private set[IllegalRenameReason] rascalCheckCausesCaptures(TModel ws, start[Module] m, set[loc] currentDefs, set[loc] currentUses, set[Define] newDefs) {
set[Define] rascalFindImplicitDefinitions(TModel ws, start[Module] m, set[Define] newDefs) {
set[loc] maybeImplicitDefs = {l | /QualifiedName n := m, just(l) := rascalLocationOfName(n)};
return {def | Define def <- newDefs, (def.idRole is variableId && def.defined in ws.useDef<0>)
|| (def.idRole is patternVariableId && def.defined in maybeImplicitDefs)};
Expand Down Expand Up @@ -170,7 +170,7 @@ private set[IllegalRenameReason] rascalCheckCausesCaptures(WorkspaceInfo ws, sta
return allCaptures == {} ? {} : {captureChange(allCaptures)};
}
private set[IllegalRenameReason] rascalCollectIllegalRenames(WorkspaceInfo ws, start[Module] m, set[loc] currentDefs, set[loc] currentUses, str newName) {
private set[IllegalRenameReason] rascalCollectIllegalRenames(TModel ws, start[Module] m, set[loc] currentDefs, set[loc] currentUses, str newName) {
set[Define] newNameDefs = {def | Define def:<_, newName, _, _, _, _> <- ws.defines};
return
Expand Down Expand Up @@ -221,7 +221,7 @@ Maybe[loc] rascalLocationOfName(Nonterminal nt) = just(nt.src);
Maybe[loc] rascalLocationOfName(NonterminalLabel l) = just(l.src);
default Maybe[loc] rascalLocationOfName(Tree t) = nothing();
private tuple[set[IllegalRenameReason] reasons, list[TextEdit] edits] computeTextEdits(WorkspaceInfo ws, start[Module] m, set[RenameLocation] defs, set[RenameLocation] uses, str name, ChangeAnnotationRegister registerChangeAnnotation) {
private tuple[set[IllegalRenameReason] reasons, list[TextEdit] edits] computeTextEdits(TModel ws, start[Module] m, set[RenameLocation] defs, set[RenameLocation] uses, str name, ChangeAnnotationRegister registerChangeAnnotation) {
if (reasons := rascalCollectIllegalRenames(ws, m, defs.l, uses.l, name), reasons != {}) {
return <reasons, []>;
}
Expand All @@ -245,10 +245,10 @@ private tuple[set[IllegalRenameReason] reasons, list[TextEdit] edits] computeTex
, rel[Maybe[ChangeAnnotationId] ann, bool isDef] renameOpts := renames[nameOfUseDef[l]]]>;
}
private tuple[set[IllegalRenameReason] reasons, list[TextEdit] edits] computeTextEdits(WorkspaceInfo ws, loc moduleLoc, set[RenameLocation] defs, set[RenameLocation] uses, str name, ChangeAnnotationRegister registerChangeAnnotation) =
private tuple[set[IllegalRenameReason] reasons, list[TextEdit] edits] computeTextEdits(TModel ws, loc moduleLoc, set[RenameLocation] defs, set[RenameLocation] uses, str name, ChangeAnnotationRegister registerChangeAnnotation) =
computeTextEdits(ws, parseModuleWithSpacesCached(moduleLoc), defs, uses, name, registerChangeAnnotation);
private bool rascalIsFunctionLocalDefs(WorkspaceInfo ws, set[loc] defs) {
private bool rascalIsFunctionLocalDefs(TModel ws, set[loc] defs) {
for (d <- defs) {
if (Define fun: <_, _, _, _, _, defType(afunc(_, _, _))> <- ws.defines
&& isContainedIn(ws.definitions[d].scope, fun.defined)) {
Expand All @@ -259,14 +259,14 @@ private bool rascalIsFunctionLocalDefs(WorkspaceInfo ws, set[loc] defs) {
return true;
}
private bool rascalIsFunctionLocal(WorkspaceInfo ws, cursor(def(), cursorLoc, _)) =
private bool rascalIsFunctionLocal(TModel ws, cursor(def(), cursorLoc, _)) =
rascalIsFunctionLocalDefs(ws, rascalGetOverloadedDefs(ws, {cursorLoc}, rascalMayOverloadSameName));
private bool rascalIsFunctionLocal(WorkspaceInfo ws, cursor(use(), cursorLoc, _)) =
private bool rascalIsFunctionLocal(TModel ws, cursor(use(), cursorLoc, _)) =
rascalIsFunctionLocalDefs(ws, rascalGetOverloadedDefs(ws, getDefs(ws, cursorLoc), rascalMayOverloadSameName));
private bool rascalIsFunctionLocal(WorkspaceInfo _, cursor(typeParam(), _, _)) = true;
private bool rascalIsFunctionLocal(TModel _, cursor(typeParam(), _, _)) = true;
private default bool rascalIsFunctionLocal(_, _) = false;
Maybe[AType] rascalAdtCommonKeywordFieldType(WorkspaceInfo ws, str fieldName, Define _:<_, _, _, _, _, DefInfo defInfo>) {
Maybe[AType] rascalAdtCommonKeywordFieldType(TModel ws, str fieldName, Define _:<_, _, _, _, _, DefInfo defInfo>) {
if (defInfo.commonKeywordFields?
&& kwf:(KeywordFormal) `<Type _> <Name kwName> = <Expression _>` <- defInfo.commonKeywordFields
&& "<kwName>" == fieldName) {
Expand All @@ -286,7 +286,7 @@ Maybe[AType] rascalConsFieldType(str fieldName, Define _:<_, _, _, constructorId
return nothing();
}
private CursorKind rascalGetDataFieldCursorKind(WorkspaceInfo ws, loc container, loc cursorLoc, str cursorName) {
private CursorKind rascalGetDataFieldCursorKind(TModel ws, loc container, loc cursorLoc, str cursorName) {
for (Define dt <- rascalGetADTDefinitions(ws, container)
&& AType adtType := dt.defInfo.atype) {
if (just(fieldType) := rascalAdtCommonKeywordFieldType(ws, cursorName, dt)) {
Expand All @@ -313,7 +313,7 @@ private CursorKind rascalGetDataFieldCursorKind(WorkspaceInfo ws, loc container,
throw illegalRename("Cannot rename \'<cursorName>\'; it is not defined in this workspace", {definitionsOutsideWorkspace(fromDefs)});
}
private CursorKind rascalGetCursorKind(WorkspaceInfo ws, loc cursorLoc, str cursorName, rel[loc l, CursorKind kind] locsContainingCursor, rel[loc field, loc container] fields, rel[loc kw, loc container] keywords) {
private CursorKind rascalGetCursorKind(TModel ws, loc cursorLoc, str cursorName, rel[loc l, CursorKind kind] locsContainingCursor, rel[loc field, loc container] fields, rel[loc kw, loc container] keywords) {
loc c = min(locsContainingCursor.l);
switch (locsContainingCursor[c]) {
case {moduleName(), *_}: {
Expand Down Expand Up @@ -384,7 +384,7 @@ private CursorKind rascalGetCursorKind(WorkspaceInfo ws, loc cursorLoc, str curs
throw unsupportedRename("Could not retrieve information for \'<cursorName>\' at <cursorLoc>.");
}
private Cursor rascalGetCursor(WorkspaceInfo ws, Tree cursorT) {
private Cursor rascalGetCursor(TModel ws, Tree cursorT) {
loc cursorLoc = cursorT.src;
str cursorName = "<cursorT>";
Expand Down Expand Up @@ -436,10 +436,16 @@ private Cursor rascalGetCursor(WorkspaceInfo ws, Tree cursorT) {
return cursor(kind, min(locsContainingCursor.l), cursorName);
}

private set[Name] rascalNameToEquivalentNames(str name) = {
[Name] name,
startsWith(name, "\\") ? [Name] name : [Name] "\\<name>"
};
private set[Tree] rascalNameToEquivalentNames(str name) {
set[Tree] equivs = {t
| T <- {#Name, #Nonterminal, #NonterminalLabel}
, just(Tree t) := tryParseAs(T, name)
};

if (!startsWith(name, "\\") && just(en) := tryParseAs(#Name, "\\<name>")) equivs += en;

return equivs;
}

private bool rascalContainsName(loc l, str name) {
m = parseModuleWithSpacesCached(l);
Expand All @@ -449,6 +455,44 @@ private bool rascalContainsName(loc l, str name) {
return false;
}
private set[TModel] rascalTModels(set[loc] fs, PathConfig pcfg) {
RascalCompilerConfig ccfg = rascalCompilerConfig(pcfg)[verbose = false]
[logPathConfig = false];
ms = rascalTModelForLocs(toList(fs), ccfg, dummy_compile1);
set[TModel] tmodels = {};
for (modName <- ms.moduleLocs) {
<found, tm, ms> = getTModelForModule(modName, ms);
if (!found) throw unexpectedFailure("Cannot read TModel for module \'<modName>\'\n<toString(ms.messages)>");
tmodels += convertTModel2PhysicalLocs(tm);
}
return tmodels;
}
ProjectFiles preloadFiles(set[loc] workspaceFolders, loc cursorLoc) {
return { <
max([f | f <- workspaceFolders, isPrefixOf(f, cursorLoc)]),
true,
cursorLoc.top
> };
}
ProjectFiles allWorkspaceFiles(set[loc] workspaceFolders, str cursorName, bool(loc, str) containsName, PathConfig(loc) getPathConfig) {
return {
// If we do not find any occurrences of the name under the cursor in a module,
// we are not interested in loading the model, but we still want to inform the
// renaming framework about the existence of the file.
<folder, containsName(file, cursorName), file>
| folder <- workspaceFolders
, PathConfig pcfg := getPathConfig(folder)
, srcFolder <- pcfg.srcs
, file <- find(srcFolder, "rsc")
};
}
set[TModel] tmodelsForProjectFiles(ProjectFiles projectFiles, set[TModel](set[loc], PathConfig) tmodelsForFiles, PathConfig(loc) getPathConfig) =
({} | it + tmodelsForFiles(projectFiles[pf, true], pcfg) | pf <- projectFiles.projectFolder, pcfg := getPathConfig(pf));
@synopsis{
Rename the Rascal symbol under the cursor. Renames all related (overloaded) definitions and uses of those definitions.
Renaming is not supported for some symbols.
Expand Down Expand Up @@ -518,66 +562,24 @@ private bool rascalContainsName(loc l, str name) {
}
Edits rascalRenameSymbol(Tree cursorT, set[loc] workspaceFolders, str newName, PathConfig(loc) getPathConfig)
= job("renaming <cursorT> to <newName>", Edits(void(str, int) step) {
step("checking validity of new name", 1);
loc cursorLoc = cursorT.src;
str cursorName = "<cursorT>";
rascalCheckLegalName(newName, typeOf(cursorT));
step("collecting workspace information", 1);
WorkspaceInfo ws = workspaceInfo(
// Get path config
getPathConfig,
// Preload
ProjectFiles() {
return { <
max([f | f <- workspaceFolders, isPrefixOf(f, cursorLoc)]),
cursorLoc.top,
true
> };
},
// Full load
ProjectFiles() {
return {
// If we do not find any occurrences of the name under the cursor in a module,
// we are not interested in loading the model, but we still want to inform the
// renaming framework about the existence of the file.
<folder, file, rascalContainsName(file, cursorName)>
| folder <- workspaceFolders
, PathConfig pcfg := getPathConfig(folder)
, srcFolder <- pcfg.srcs
, file <- find(srcFolder, "rsc")
};
},
// Load TModel for loc
set[TModel](ProjectFiles projectFiles) {
set[TModel] tmodels = {};
for (projectFolder <- projectFiles.projectFolder, \files := projectFiles[projectFolder]) {
PathConfig pcfg = getPathConfig(projectFolder);
RascalCompilerConfig ccfg = rascalCompilerConfig(pcfg)[verbose = false]
[logPathConfig = false];
for (<file, true> <- \files) {
ms = rascalTModelForLocs([file], ccfg, dummy_compile1);
for (modName <- ms.moduleLocs) {
<found, tm, ms> = getTModelForModule(modName, ms);
if (!found) throw unexpectedFailure("Cannot read TModel for module \'<modName>\'\n<toString(ms.messages)>");
tmodels += convertTModel2PhysicalLocs(tm);
}
}
}
return tmodels;
}
);
step("preloading minimal workspace information", 1);
ws = preLoad(ws);
set[TModel] localTmodelsForFiles(ProjectFiles projectFiles) = tmodelsForProjectFiles(projectFiles, rascalTModels, getPathConfig);
TModel ws = loadLocs(tmodel(), preloadFiles(workspaceFolders, cursorLoc), localTmodelsForFiles);
step("analyzing name at cursor", 1);
cur = rascalGetCursor(ws, cursorT);
step("loading required type information", 1);
if (!rascalIsFunctionLocal(ws, cur)) {
ws = loadWorkspace(ws);
ws = loadLocs(ws, allWorkspaceFiles(workspaceFolders, cursorName, rascalContainsName, getPathConfig), localTmodelsForFiles);
}
step("collecting uses of \'<cursorName>\'", 1);
Expand All @@ -597,7 +599,7 @@ Edits rascalRenameSymbol(Tree cursorT, set[loc] workspaceFolders, str newName, P
return id;
};
<defs, uses, getRenames> = rascalGetDefsUses(ws, cur, rascalMayOverloadSameName, registerChangeAnnotation);
<defs, uses, getRenames> = rascalGetDefsUses(ws, cur, rascalMayOverloadSameName, registerChangeAnnotation, getPathConfig);
rel[loc file, RenameLocation defines] defsPerFile = {<d.l.top, d> | d <- defs};
rel[loc file, RenameLocation uses] usesPerFile = {<u.l.top, u> | u <- uses};
Expand Down
7 changes: 3 additions & 4 deletions rascal-lsp/src/main/rascal/lang/rascal/lsp/refactor/Util.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,11 @@ start[Module] parseModuleWithSpacesCached(loc l) {
@synopsis{
Try to parse string `name` as reified type `begin` and return whether this succeeded.
}
bool tryParseAs(type[&T <: Tree] begin, str name, bool allowAmbiguity = false) {
Maybe[&T <: Tree] tryParseAs(type[&T <: Tree] begin, str name, bool allowAmbiguity = false) {
try {
parse(begin, name, allowAmbiguity = allowAmbiguity);
return true;
return just(parse(begin, name, allowAmbiguity = allowAmbiguity));
} catch ParseError(_): {
return false;
return nothing();
}
}

Expand Down
Loading

0 comments on commit 3c5ff77

Please sign in to comment.