module analysis::typepal::refactor::Rename
Usage
import analysis::typepal::refactor::Rename;
Dependencies
import analysis::diff::edits::TextEdits;
import analysis::typepal::FailMessage;
import analysis::typepal::Messenger;
import analysis::typepal::TModel;
import util::Monitor;
import Exception;
import IO;
import List;
import Map;
import Message;
import Node;
import ParseTree;
import Relation;
import Set;
alias RenameResult
tuple[list[FileSystemChange], set[Message]]
alias Focus
list[Tree]
data Renamer
Tracks state of renaming and provides helper functions.
data Renamer
= renamer(
void(FailMessage) msg
, void(FileSystemChange) documentEdit
, void(TextEdit) textEdit
, RenameConfig() getConfig
)
;
Tracks the state of the renaming, as an argument to every function of the rename framework.
msgregisters a Fail Message. Registration of an FailMessage-error triggers premature termination of the renaming at the soonest possibility (typically before the next rename phase).documentEditregisters a FileSystemChange, which represents a change required for the renaming.textEditregisters a Text Edit, which represents a change required for the renaming. It is a convenience function that converts to a FileSystemChange internally, grouping Text Edits to the same file where possible.getConfigretrieves the RenameConfig.
data RenameConfig
Language-specific configuration of identifier renaming.
data RenameConfig
= rconfig(
Tree(loc) parseLoc
, TModel(Tree) tmodelForTree
, TModel(loc) tmodelForLoc = TModel(loc l) { return tmodelForTree(parseLoc(l)); }
, bool debug = false
, str jobLabel = "Renaming"
)
;
Configures the rename for a specific language, by giving the following mandatory arguments:
Tree parseLoc(loc l), which parses a file and returns a Tree. Typically this would beparse(#StartSymbol, l).TModel tmodelForTree(Tree t), which type-checks a Tree and returns a TModel. Typically, this would be Collect And Solve. The configuration also takes some optional arguments:TModel tmodelForLoc(loc l), which type-checks a file given itslocinstead of its parse tree. This can be useful when the file does not have to be parsed to produce a TModel, for example when it had already been type-checked just before starting the renaming. Defaults totmodelForTree(parseLoc(l)).bool debug, which indicates whether debug output should be printed during the renaming. Defaults tofalse. *str jobLabel, which can be used to change the base label of the rename progress bar. Defaults to"Renaming".
function sortDocEdits
Sorts ((analysis::diff::edits::TextEdits::FileSystemChange))s.
list[FileSystemChange] sortDocEdits(list[FileSystemChange] edits)
Applying edits through ExecuteTextEdits should happen in a specific order. Specifically, files should be created before they can be modified, and after renaming them, modifications/deletions should refer to the new name. This functions sorts edits in the following order.
function rename
Renames the identifier under the cursor to newName and returns the required ((analysis::diff::edits::TextEdits::FileSystemChange))s and ((Message::Message))s.
RenameResult rename(
Focus cursor
, str newName
, RenameConfig config)
Renames the identifier under the cursor (represented as a Focus) to newName, given a specific RenameConfig.
This renaming uses TModels produced by the type-checker.
The rename framework provides a default implementation, which can be selectively extended for languages that require different rename behaviour than the default. These functions constitute the implementation of renaming:
- getCursorDefinitions
- findOccurrenceFiles
- findAdditionalDefinitions
- validateNewNameOccurrences
- renameDefinition
- renameUses
- nameLocation
Pitfalls
- Since the RenameConfig that this function passes to the various hooks contains state and caches, it should never escape the scope of a single invoation of rename. Instead, it should always be accessed via Renamer's
getConfig. - The renaming highly depends on a type-check function
TModel(Tree). If such a function does not exist, this framework cannot function.
function _rename
RenameResult _rename(
Focus cursor
, str newName
, RenameConfig config)
function defNameLocations
Compute locations of names of defs in tr.
map[Define, loc] defNameLocations(Tree tr, set[Define] defs, Renamer _r)
function getCursorDefinitions
Computes ((Define))(s) for the name under cursor.
default set[Define] getCursorDefinitions(Focus cursor, Tree(loc) _getTree, TModel(Tree) getModel, Renamer r)
function findOccurrenceFiles
Computes in which files occurrences of cursorDefs and newName might occur (over-approximation). This is not supposed to call the type-checker on any file for performance reasons.
default tuple[set[loc] defFiles, set[loc] useFiles, set[loc] newNameFiles] findOccurrenceFiles(set[Define] cursorDefs, Focus cursor, str newName, Tree(loc) _getTree, Renamer r)
Pitfalls
For any file in defFiles + useFiles, the framework calls RenameConfig::tmodelForLoc. If type-cehcking is expensive and this function over-approximates by a large margin, the performance of the renaming might degrade.
function findAdditionalDefinitions
Computes additional definitions (e.g. overloads of cursorDefs) in a single file.
default set[Define] findAdditionalDefinitions(set[Define] _cursorDefs, Tree _tr, TModel _tm, Renamer _r)
function validateNewNameOccurrences
Validates for a single file with occurrences of newName that, when renaming all occurrences of cursorDefs to newName, no problems will be introduced.
default void validateNewNameOccurrences(set[Define] cursorDefs, str newName, Tree tr, Renamer r)
Examples
This could be used to detect many kinds of problems, e.g. static errors and semantic changes due to shadowing or overloading.
function renameDefinition
Renames a single ((Define)) _d with its name at nameLoc, defined in ((TModel)) _tm, to newName, by producing corresponding ((FileSystemChange))s.
default void renameDefinition(Define _d, loc nameLoc, str newName, TModel _tm, Renamer r)
function renameUses
{Renames all uses of defs in a single file/((TModel)) tm, by producing corresponding ((FileSystemChange))s.}
default void renameUses(set[Define] defs, str newName, TModel tm, Renamer r)
function nameLocation
Finds the location of the identifier within definition ((ParseTree::Tree)) t corresponding to ((Define)) d, where t.src == d.defined.
default loc nameLocation(Tree t, Define d)