Skip to content

Commit

Permalink
Merge pull request #151 from mjbvz/fix-150
Browse files Browse the repository at this point in the history
Fix angle bracket link updates with fragments
  • Loading branch information
mjbvz authored Sep 25, 2023
2 parents 8c3c048 + f26994e commit 7afef70
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 39 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 0.4.0-alpha.7 — September 25, 2023
- Fix path updates for angle bracket links with fragments.

## 0.4.0-alpha.6 — September 5, 2023
- Fix path completions when file name contains a literal `%`. In these cases the `%` needs to be encoded to prevent it from being incorrectly decoded on link click
- Fix more cases for link detection for links containing escaped characters.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vscode-markdown-languageservice",
"description": "Markdown language service",
"version": "0.4.0-alpha.6",
"version": "0.4.0-alpha.7",
"author": "Microsoft Corporation",
"license": "MIT",
"engines": {
Expand Down
32 changes: 21 additions & 11 deletions src/languageFeatures/rename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,28 +288,38 @@ export function getFilePathRange(link: MdLink): lsp.Range {
return link.source.hrefRange;
}

export function getLinkRenameEdit(link: MdLink, newName: string): lsp.TextEdit {
const pathRange = getFilePathRange(link);
function newPathWithFragmentIfNeeded(newPath: string, link: MdLink): string {
if (link.href.kind === HrefKind.Internal && link.href.fragment) {
return newPath + '#' + link.href.fragment;
}
return newPath;
}

export function getLinkRenameEdit(link: MdLink, newPathText: string): lsp.TextEdit {
const linkRange = link.source.hrefRange;

// TODO: this won't be correct if the file name contains `\`
const newLinkText = newName.replace(/\\/g, '/');
newPathText = newPathWithFragmentIfNeeded(newPathText.replace(/\\/g, '/'), link);

if (link.source.isAngleBracketLink) {
if (!needsAngleBracketLink(newLinkText)) {
if (!needsAngleBracketLink(newPathText)) {
// Remove the angle brackets
const range = makeRange(translatePosition(pathRange.start, { characterDelta: -1 }), translatePosition(pathRange.end, { characterDelta: 1 }));
return { range, newText: newLinkText };
const range = makeRange(
translatePosition(linkRange.start, { characterDelta: -1 }),
translatePosition(linkRange.end, { characterDelta: 1 }));

return { range, newText: newPathText };
} else {
return { range: pathRange, newText: escapeForAngleBracketLink(newLinkText) };
return { range: linkRange, newText: escapeForAngleBracketLink(newPathText) };
}
}

// We might need to use angle brackets for the link
if (needsAngleBracketLink(newLinkText)) {
return { range: pathRange, newText: `<${escapeForAngleBracketLink(newLinkText)}>` };
if (needsAngleBracketLink(newPathText)) {
return { range: linkRange, newText: `<${escapeForAngleBracketLink(newPathText)}>` };
}

return { range: pathRange, newText: newLinkText };
return { range: linkRange, newText: newPathText };
}


98 changes: 75 additions & 23 deletions src/test/fileRename.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ suite('File Rename', () => {
});
}));

test('Rename file should ignore fragments', withStore(async (store) => {
test('Rename file should preserve fragments', withStore(async (store) => {
const uri = workspacePath('doc.md');
const doc = new InMemoryDocument(uri, joinLines(
`[abc](/old.md#frag)`,
Expand All @@ -106,10 +106,10 @@ suite('File Rename', () => {
const response = await getFileRenameEdits(store, [{ oldUri, newUri }], workspace);
assertEditsEqual(response!.edit, {
uri, edits: [
lsp.TextEdit.replace(makeRange(0, 6, 0, 13), '/new.md'),
lsp.TextEdit.replace(makeRange(1, 6, 1, 12), 'new.md'),
lsp.TextEdit.replace(makeRange(2, 6, 2, 14), './new.md'),
lsp.TextEdit.replace(makeRange(3, 7, 3, 15), './new.md'),
lsp.TextEdit.replace(makeRange(0, 6, 0, 18), '/new.md#frag'),
lsp.TextEdit.replace(makeRange(1, 6, 1, 17), 'new.md#frag'),
lsp.TextEdit.replace(makeRange(2, 6, 2, 19), './new.md#frag'),
lsp.TextEdit.replace(makeRange(3, 7, 3, 20), './new.md#frag'),
]
});
}));
Expand All @@ -130,10 +130,10 @@ suite('File Rename', () => {
const response = await getFileRenameEdits(store, [{ oldUri, newUri }], workspace, { preferredMdPathExtensionStyle: PreferredMdPathExtensionStyle.removeExtension });
assertEditsEqual(response!.edit, {
uri: docUri, edits: [
lsp.TextEdit.replace(makeRange(0, 6, 0, 13), '/new'),
lsp.TextEdit.replace(makeRange(1, 6, 1, 12), 'new'),
lsp.TextEdit.replace(makeRange(2, 6, 2, 14), './new'),
lsp.TextEdit.replace(makeRange(3, 7, 3, 15), './new'),
lsp.TextEdit.replace(makeRange(0, 6, 0, 18), '/new#frag'),
lsp.TextEdit.replace(makeRange(1, 6, 1, 17), 'new#frag'),
lsp.TextEdit.replace(makeRange(2, 6, 2, 19), './new#frag'),
lsp.TextEdit.replace(makeRange(3, 7, 3, 20), './new#frag'),
]
});
}));
Expand All @@ -154,10 +154,10 @@ suite('File Rename', () => {
const response = await getFileRenameEdits(store, [{ oldUri, newUri }], workspace);
assertEditsEqual(response!.edit, {
uri: docUri, edits: [
lsp.TextEdit.replace(makeRange(0, 6, 0, 10), '/new'),
lsp.TextEdit.replace(makeRange(1, 6, 1, 9), 'new'),
lsp.TextEdit.replace(makeRange(2, 6, 2, 11), './new'),
lsp.TextEdit.replace(makeRange(3, 7, 3, 12), './new'),
lsp.TextEdit.replace(makeRange(0, 6, 0, 15), '/new#frag'),
lsp.TextEdit.replace(makeRange(1, 6, 1, 14), 'new#frag'),
lsp.TextEdit.replace(makeRange(2, 6, 2, 16), './new#frag'),
lsp.TextEdit.replace(makeRange(3, 7, 3, 17), './new#frag'),
]
});
}));
Expand Down Expand Up @@ -241,9 +241,9 @@ suite('File Rename', () => {
const response = await getFileRenameEdits(store, [{ oldUri: oldUri, newUri }], workspace);
assertEditsEqual(response!.edit, {
uri: newUri, edits: [
lsp.TextEdit.replace(makeRange(1, 6, 1, 14), '../other.md'),
lsp.TextEdit.replace(makeRange(2, 6, 2, 16), '../other.md'),
lsp.TextEdit.replace(makeRange(3, 7, 3, 17), '../other.md'),
lsp.TextEdit.replace(makeRange(1, 6, 1, 19), '../other.md#frag'),
lsp.TextEdit.replace(makeRange(2, 6, 2, 21), '../other.md#frag'),
lsp.TextEdit.replace(makeRange(3, 7, 3, 22), '../other.md#frag'),
]
});
}));
Expand All @@ -264,9 +264,9 @@ suite('File Rename', () => {
const response = await getFileRenameEdits(store, [{ oldUri: oldUri, newUri }], workspace);
assertEditsEqual(response!.edit, {
uri: newUri, edits: [
lsp.TextEdit.replace(makeRange(1, 6, 1, 11), '../other'),
lsp.TextEdit.replace(makeRange(2, 6, 2, 13), '../other'),
lsp.TextEdit.replace(makeRange(3, 7, 3, 14), '../other'),
lsp.TextEdit.replace(makeRange(1, 6, 1, 16), '../other#frag'),
lsp.TextEdit.replace(makeRange(2, 6, 2, 18), '../other#frag'),
lsp.TextEdit.replace(makeRange(3, 7, 3, 19), '../other#frag'),
]
});
}));
Expand Down Expand Up @@ -569,11 +569,11 @@ suite('File Rename', () => {
assertEditsEqual(response!.edit, {
// Here we need to be using the new path to 'doc'
uri: newDocUri, edits: [
lsp.TextEdit.replace(makeRange(2, 6, 2, 18), 'newReadme.md'),
lsp.TextEdit.replace(makeRange(3, 6, 3, 20), './newReadme.md'),
lsp.TextEdit.replace(makeRange(2, 6, 2, 25), 'newReadme.md#header'),
lsp.TextEdit.replace(makeRange(3, 6, 3, 27), './newReadme.md#header'),

lsp.TextEdit.replace(makeRange(6, 8, 6, 20), 'newReadme.md'),
lsp.TextEdit.replace(makeRange(7, 8, 7, 22), './newReadme.md'),
lsp.TextEdit.replace(makeRange(6, 8, 6, 27), 'newReadme.md#header'),
lsp.TextEdit.replace(makeRange(7, 8, 7, 29), './newReadme.md#header'),
]
});
}));
Expand Down Expand Up @@ -689,4 +689,56 @@ suite('File Rename', () => {
]
});
}));

test('Should handle renames for angle bracket link with fragment when angle brackets are removed (#150)', withStore(async (store) => {
const uri = workspacePath('doc.md');
const doc = new InMemoryDocument(uri, joinLines(
`[text](foo.md#abc 'text')`,
`[text](<foo.md#abc>)`,
``,
`[def1]: foo.md#abc`,
`[def2]: <foo.md#abc> 'text'`,
));

const workspace = store.add(new InMemoryWorkspace([doc]));

const oldUri = workspacePath('foo.md');
const newUri = workspacePath('bar.md');

const response = await getFileRenameEdits(store, [{ oldUri, newUri }], workspace);
assertEditsEqual(response!.edit, {
uri, edits: [
lsp.TextEdit.replace(makeRange(0, 7, 0, 17), 'bar.md#abc'),
lsp.TextEdit.replace(makeRange(1, 7, 1, 19), 'bar.md#abc'),
lsp.TextEdit.replace(makeRange(3, 8, 3, 18), 'bar.md#abc'),
lsp.TextEdit.replace(makeRange(4, 8, 4, 20), 'bar.md#abc'),
]
});
}));

test('Should handle renames for angle bracket link with fragment when angle brackets are added (#150)', withStore(async (store) => {
const uri = workspacePath('doc.md');
const doc = new InMemoryDocument(uri, joinLines(
`[text](foo.md#abc 'text')`,
`[text](<foo.md#abc>)`,
``,
`[def1]: foo.md#abc 'text'`,
`[def2]: <foo.md#abc>`,
));

const workspace = store.add(new InMemoryWorkspace([doc]));

const oldUri = workspacePath('foo.md');
const newUri = workspacePath('foo space.md');

const response = await getFileRenameEdits(store, [{ oldUri, newUri }], workspace);
assertEditsEqual(response!.edit, {
uri, edits: [
lsp.TextEdit.replace(makeRange(0, 7, 0, 17), '<foo space.md#abc>'),
lsp.TextEdit.replace(makeRange(1, 8, 1, 18), 'foo space.md#abc'),
lsp.TextEdit.replace(makeRange(3, 8, 3, 18), '<foo space.md#abc>'),
lsp.TextEdit.replace(makeRange(4, 9, 4, 19), 'foo space.md#abc'),
]
});
}));
});
4 changes: 2 additions & 2 deletions src/test/rename.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,8 @@ suite('Rename', () => {
newUri: workspacePath('new File.md'), // Rename on disk should use file extension
}, {
uri: uri, edits: [
lsp.TextEdit.replace(makeRange(0, 7, 0, 11), '</new File>'), // Links should continue to use extension-less paths
lsp.TextEdit.replace(makeRange(1, 7, 1, 11), '</new File>'),
lsp.TextEdit.replace(makeRange(0, 7, 0, 18), '</new File#header>'), // Links should continue to use extension-less paths
lsp.TextEdit.replace(makeRange(1, 7, 1, 17), '</new File#other>'),
]
});
}));
Expand Down

0 comments on commit 7afef70

Please sign in to comment.