Fix md rename when file does not exist

Fixes #148147

- Don't try renaming non-existent files
- Also fixes a bug where renaming in untitled files would insert `untitled:`
This commit is contained in:
Matt Bierner 2022-04-30 19:52:44 -04:00
parent 3e182633c7
commit 8449a9f5cb
No known key found for this signature in database
GPG key ID: 099C331567E11888
4 changed files with 53 additions and 26 deletions

View file

@ -169,8 +169,10 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide
}
// First rename the file
fileRenames.push({ from: targetUri, to: resolvedNewFilePath });
edit.renameFile(targetUri, resolvedNewFilePath);
if (await this.workspaceContents.fileExists(targetUri)) {
fileRenames.push({ from: targetUri, to: resolvedNewFilePath });
edit.renameFile(targetUri, resolvedNewFilePath);
}
// Then update all refs to it
for (const ref of allRefsInfo.references) {
@ -181,9 +183,14 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide
const root = resolveDocumentLink('/', ref.link.source.resource);
newPath = '/' + path.relative(root.toString(true), rawNewFilePath.toString(true));
} else {
newPath = path.relative(URI.Utils.dirname(ref.link.source.resource).toString(true), rawNewFilePath.toString(true));
if (newName.startsWith('./') && !newPath.startsWith('../') || newName.startsWith('.\\') && !newPath.startsWith('..\\')) {
newPath = './' + newPath;
const rootDir = URI.Utils.dirname(ref.link.source.resource);
if (rootDir.scheme === rawNewFilePath.scheme && rootDir.scheme !== 'untitled') {
newPath = path.relative(rootDir.toString(true), rawNewFilePath.toString(true));
if (newName.startsWith('./') && !newPath.startsWith('../') || newName.startsWith('.\\') && !newPath.startsWith('..\\')) {
newPath = './' + newPath;
}
} else {
newPath = newName;
}
}
edit.replace(ref.link.source.resource, this.getFilePathRange(ref), encodeURI(newPath.replace(/\\/g, '/')));
@ -262,3 +269,4 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide
return this.cachedRefs;
}
}

View file

@ -25,6 +25,10 @@ export class InMemoryWorkspaceMarkdownDocuments implements MdWorkspaceContents {
return this._documents.get(this.getKey(resource));
}
public async fileExists(resource: vscode.Uri): Promise<boolean> {
return this._documents.has(this.getKey(resource));
}
private readonly _onDidChangeMarkdownDocumentEmitter = new vscode.EventEmitter<SkinnyTextDocument>();
public onDidChangeMarkdownDocument = this._onDidChangeMarkdownDocumentEmitter.event;

View file

@ -458,19 +458,19 @@ suite('markdown: rename', () => {
doc1,
doc2
]));
assertEditsEqual(edit!, {
originalUri: workspacePath('images', 'more', 'image.png'),
newUri: workspacePath('img', 'test', 'new.png'),
}, {
uri: uri1, edits: [
new vscode.TextEdit(new vscode.Range(0, 7, 0, 29), '/img/test/new.png'),
new vscode.TextEdit(new vscode.Range(2, 7, 2, 29), '/img/test/new.png'),
]
}, {
uri: uri2, edits: [
new vscode.TextEdit(new vscode.Range(0, 7, 0, 29), '/img/test/new.png'),
]
});
assertEditsEqual(edit!,
// Should not have file edits since the files don't exist here
{
uri: uri1, edits: [
new vscode.TextEdit(new vscode.Range(0, 7, 0, 29), '/img/test/new.png'),
new vscode.TextEdit(new vscode.Range(2, 7, 2, 29), '/img/test/new.png'),
]
},
{
uri: uri2, edits: [
new vscode.TextEdit(new vscode.Range(0, 7, 0, 29), '/img/test/new.png'),
]
});
});
test('Path rename should use .md extension on extension-less link', async () => {
@ -550,24 +550,27 @@ suite('markdown: rename', () => {
test('Path rename should resolve on links without prefix', async () => {
const uri1 = workspacePath('sub', 'doc.md');
const doc1 = new InMemoryDocument(uri1, joinLines(
`![text](images/cat.gif)`,
`![text](sub2/doc3.md)`,
));
const uri2 = workspacePath('doc2.md');
const doc2 = new InMemoryDocument(uri2, joinLines(
`![text](sub/images/cat.gif)`,
`![text](sub/sub2/doc3.md)`,
));
const edit = await getRenameEdits(doc1, new vscode.Position(0, 10), 'img/cat.gif', new InMemoryWorkspaceMarkdownDocuments([
doc1, doc2,
const uri3 = workspacePath('sub', 'sub2', 'doc3.md');
const doc3 = new InMemoryDocument(uri3, joinLines());
const edit = await getRenameEdits(doc1, new vscode.Position(0, 10), 'sub2/cat.md', new InMemoryWorkspaceMarkdownDocuments([
doc1, doc2, doc3
]));
assertEditsEqual(edit!, {
originalUri: workspacePath('sub', 'images', 'cat.gif'),
newUri: workspacePath('sub', 'img', 'cat.gif'),
originalUri: workspacePath('sub', 'sub2', 'doc3.md'),
newUri: workspacePath('sub', 'sub2', 'cat.md'),
}, {
uri: uri1, edits: [new vscode.TextEdit(new vscode.Range(0, 8, 0, 22), 'img/cat.gif')]
uri: uri1, edits: [new vscode.TextEdit(new vscode.Range(0, 8, 0, 20), 'sub2/cat.md')]
}, {
uri: uri2, edits: [new vscode.TextEdit(new vscode.Range(0, 8, 0, 26), 'sub/img/cat.gif')]
uri: uri2, edits: [new vscode.TextEdit(new vscode.Range(0, 8, 0, 24), 'sub/sub2/cat.md')]
});
});

View file

@ -42,6 +42,8 @@ export interface MdWorkspaceContents {
getMarkdownDocument(resource: vscode.Uri): Promise<SkinnyTextDocument | undefined>;
fileExists(resource: vscode.Uri): Promise<boolean>;
readonly onDidChangeMarkdownDocument: vscode.Event<SkinnyTextDocument>;
readonly onDidCreateMarkdownDocument: vscode.Event<SkinnyTextDocument>;
readonly onDidDeleteMarkdownDocument: vscode.Event<vscode.Uri>;
@ -143,4 +145,14 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace
return undefined;
}
}
public async fileExists(target: vscode.Uri): Promise<boolean> {
let targetResourceStat: vscode.FileStat | undefined;
try {
targetResourceStat = await vscode.workspace.fs.stat(target);
} catch {
return false;
}
return targetResourceStat.type === vscode.FileType.File;
}
}