We have a RenameProvider
implementation to help with renaming fragments easily
throughout a project.
export class LiterateRenameProvider implements vscode.RenameProvider
{
constructor(public readonly repository : FragmentRepository) {}
<<fragment rename provider>>
}
The provider needs to implement provideRenameEdits
, which takes a document, a
position and the new string to use for the fragment name. There is also a
cancellation token, but we haven't used these yet. TBD: figure out how to use
the cancellation tokens.
We start by finding the token that is at the current position given. When the
fragment has been confirmed we find all occurrences throughout the project in
the workspace folder and create workspace edits to replace with the given
newName
.
public async provideRenameEdits(
document : vscode.TextDocument,
position : vscode.Position,
newName : string,
_ : vscode.CancellationToken
)
{
let workspaceEdit = new vscode.WorkspaceEdit();
<<get workspace for renames>>
<<find fragment to rename at position>>
<<create workspace edits for fragment in project>>
}
We start by getting the workspace folder for the rename attempt in the given
text document. If the workspace folder is not a literate project just return
the workspaceEdit
without creating any edits to it.
const workspaceFolder : vscode.WorkspaceFolder | undefined = determineWorkspaceFolder(document);
if(!workspaceFolder) { return workspaceEdit; }
Now that we have a workspace folder with a project in it we get the fragment location for the given position in the text document.
let fragmentLocation = this.repository.getFragmentTagLocation(
document,
document.lineAt(position.line),
position
);
We can actually create good rename edits only when we have a proper fragment location. There is no use in trying when we're not on a fragment. If we do have a valid fragment location we get the uris for all literate files in the workspace folder.
if(fragmentLocation.valid)
{
const foundLiterateFiles = await getLiterateFileUris(workspaceFolder);
For each uri in the list we get the correct content as a string and plit that into lines if possible. We'll iterate over the lines, keeping a counter of the line number so that we can create the correct range when a hit has been found.
try {
for (let fl of foundLiterateFiles) {
<<get text and split into lines>>
let lineNumber = 0;
for(const line of lines)
{
let fromIdx = 0;
while(true)
{
<<find fragment on line>>
<<create replace on workspace edit>>
<<ensure correct start index for further searching>>
}
lineNumber++;
}
}
} catch(error)
{
console.log(error);
}
}
return workspaceEdit;
Get the file contet as string using getFileContent
. The string is split on
\n
, so that we get the document lines.
const text = await getFileContent(fl);
const lines = text.split("\n");
Since a line could contain multiple mentions of the fragment we do this in a loop until no more hits of the fragment name are found. When that happens we break the while loop in which we check for occurrences.
let foundIdx = line.indexOf(fragmentLocation.name, fromIdx);
if(foundIdx===-1)
{
break;
}
When a good hit was found we create a replace
on the workspaceEdit
with the
file uri, the new range and the text to use for the replacement.
let foundRange = new vscode.Range(lineNumber, foundIdx, lineNumber, foundIdx + fragmentLocation.name.length);
workspaceEdit.replace(
fl,
foundRange,
newName
);
While we're still looping we need to bump the fromIdx
to ensure we skip past
the fragment usage or mention we just found on the line.
fromIdx += foundIdx + fragmentLocation.name.length;