mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 05:19:39 +00:00
Dispose in markdown tests (#153345)
Updates the markdown tests to dispose of disposables created during the test
This commit is contained in:
parent
464c3dc728
commit
da0f64881a
|
@ -10,17 +10,19 @@ import { MdVsCodeDefinitionProvider } from '../languageFeatures/definitions';
|
|||
import { MdReferencesProvider } from '../languageFeatures/references';
|
||||
import { MdTableOfContentsProvider } from '../tableOfContents';
|
||||
import { noopToken } from '../util/cancellation';
|
||||
import { DisposableStore } from '../util/dispose';
|
||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
||||
import { IMdWorkspace } from '../workspace';
|
||||
import { createNewMarkdownEngine } from './engine';
|
||||
import { InMemoryMdWorkspace } from './inMemoryWorkspace';
|
||||
import { nulLogger } from './nulLogging';
|
||||
import { joinLines, workspacePath } from './util';
|
||||
import { joinLines, withStore, workspacePath } from './util';
|
||||
|
||||
|
||||
function getDefinition(doc: InMemoryDocument, pos: vscode.Position, workspace: IMdWorkspace) {
|
||||
function getDefinition(store: DisposableStore, doc: InMemoryDocument, pos: vscode.Position, workspace: IMdWorkspace) {
|
||||
const engine = createNewMarkdownEngine();
|
||||
const referencesProvider = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace, nulLogger), nulLogger);
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const referencesProvider = store.add(new MdReferencesProvider(engine, workspace, tocProvider, nulLogger));
|
||||
const provider = new MdVsCodeDefinitionProvider(referencesProvider);
|
||||
return provider.provideDefinition(doc, pos, noopToken);
|
||||
}
|
||||
|
@ -46,31 +48,33 @@ function assertDefinitionsEqual(actualDef: vscode.Definition, ...expectedDefs: {
|
|||
}
|
||||
|
||||
suite('markdown: Go to definition', () => {
|
||||
test('Should not return definition when on link text', async () => {
|
||||
test('Should not return definition when on link text', withStore(async (store) => {
|
||||
const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines(
|
||||
`[ref](#abc)`,
|
||||
`[ref]: http://example.com`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const defs = await getDefinition(doc, new vscode.Position(0, 1), new InMemoryMdWorkspace([doc]));
|
||||
const defs = await getDefinition(store, doc, new vscode.Position(0, 1), workspace);
|
||||
assert.deepStrictEqual(defs, undefined);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find definition links within file from link', async () => {
|
||||
test('Should find definition links within file from link', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(docUri, joinLines(
|
||||
`[link 1][abc]`, // trigger here
|
||||
``,
|
||||
`[abc]: https://example.com`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const defs = await getDefinition(doc, new vscode.Position(0, 12), new InMemoryMdWorkspace([doc]));
|
||||
const defs = await getDefinition(store, doc, new vscode.Position(0, 12), workspace);
|
||||
assertDefinitionsEqual(defs!,
|
||||
{ uri: docUri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find definition links using shorthand', async () => {
|
||||
test('Should find definition links using shorthand', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(docUri, joinLines(
|
||||
`[ref]`, // trigger 1
|
||||
|
@ -79,59 +83,62 @@ suite('markdown: Go to definition', () => {
|
|||
``,
|
||||
`[ref]: /Hello.md` // trigger 3
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
{
|
||||
const defs = await getDefinition(doc, new vscode.Position(0, 2), new InMemoryMdWorkspace([doc]));
|
||||
const defs = await getDefinition(store, doc, new vscode.Position(0, 2), workspace);
|
||||
assertDefinitionsEqual(defs!,
|
||||
{ uri: docUri, line: 4 },
|
||||
);
|
||||
}
|
||||
{
|
||||
const defs = await getDefinition(doc, new vscode.Position(2, 7), new InMemoryMdWorkspace([doc]));
|
||||
const defs = await getDefinition(store, doc, new vscode.Position(2, 7), workspace);
|
||||
assertDefinitionsEqual(defs!,
|
||||
{ uri: docUri, line: 4 },
|
||||
);
|
||||
}
|
||||
{
|
||||
const defs = await getDefinition(doc, new vscode.Position(4, 2), new InMemoryMdWorkspace([doc]));
|
||||
const defs = await getDefinition(store, doc, new vscode.Position(4, 2), workspace);
|
||||
assertDefinitionsEqual(defs!,
|
||||
{ uri: docUri, line: 4 },
|
||||
);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find definition links within file from definition', async () => {
|
||||
test('Should find definition links within file from definition', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(docUri, joinLines(
|
||||
`[link 1][abc]`,
|
||||
``,
|
||||
`[abc]: https://example.com`, // trigger here
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const defs = await getDefinition(doc, new vscode.Position(2, 3), new InMemoryMdWorkspace([doc]));
|
||||
const defs = await getDefinition(store, doc, new vscode.Position(2, 3), workspace);
|
||||
assertDefinitionsEqual(defs!,
|
||||
{ uri: docUri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not find definition links across files', async () => {
|
||||
test('Should not find definition links across files', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(docUri, joinLines(
|
||||
`[link 1][abc]`,
|
||||
``,
|
||||
`[abc]: https://example.com`,
|
||||
));
|
||||
|
||||
const defs = await getDefinition(doc, new vscode.Position(0, 12), new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(workspacePath('other.md'), joinLines(
|
||||
`[link 1][abc]`,
|
||||
``,
|
||||
`[abc]: https://example.com?bad`,
|
||||
`[abc]: https://example.com?bad`
|
||||
))
|
||||
]));
|
||||
|
||||
const defs = await getDefinition(store, doc, new vscode.Position(0, 12), workspace);
|
||||
assertDefinitionsEqual(defs!,
|
||||
{ uri: docUri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -11,14 +11,14 @@ import { MdLinkProvider } from '../languageFeatures/documentLinks';
|
|||
import { MdReferencesProvider } from '../languageFeatures/references';
|
||||
import { MdTableOfContentsProvider } from '../tableOfContents';
|
||||
import { noopToken } from '../util/cancellation';
|
||||
import { disposeAll } from '../util/dispose';
|
||||
import { DisposableStore } from '../util/dispose';
|
||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
||||
import { ResourceMap } from '../util/resourceMap';
|
||||
import { IMdWorkspace } from '../workspace';
|
||||
import { createNewMarkdownEngine } from './engine';
|
||||
import { InMemoryMdWorkspace } from './inMemoryWorkspace';
|
||||
import { nulLogger } from './nulLogging';
|
||||
import { assertRangeEqual, joinLines, workspacePath } from './util';
|
||||
import { assertRangeEqual, joinLines, withStore, workspacePath } from './util';
|
||||
|
||||
const defaultDiagnosticsOptions = Object.freeze<DiagnosticOptions>({
|
||||
enabled: true,
|
||||
|
@ -29,10 +29,10 @@ const defaultDiagnosticsOptions = Object.freeze<DiagnosticOptions>({
|
|||
ignoreLinks: [],
|
||||
});
|
||||
|
||||
async function getComputedDiagnostics(doc: InMemoryDocument, workspace: IMdWorkspace, options: Partial<DiagnosticOptions> = {}): Promise<vscode.Diagnostic[]> {
|
||||
async function getComputedDiagnostics(store: DisposableStore, doc: InMemoryDocument, workspace: IMdWorkspace, options: Partial<DiagnosticOptions> = {}): Promise<vscode.Diagnostic[]> {
|
||||
const engine = createNewMarkdownEngine();
|
||||
const linkProvider = new MdLinkProvider(engine, workspace, nulLogger);
|
||||
const tocProvider = new MdTableOfContentsProvider(engine, workspace, nulLogger);
|
||||
const linkProvider = store.add(new MdLinkProvider(engine, workspace, nulLogger));
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const computer = new DiagnosticComputer(workspace, linkProvider, tocProvider);
|
||||
return (
|
||||
await computer.getDiagnostics(doc, { ...defaultDiagnosticsOptions, ...options, }, noopToken)
|
||||
|
@ -108,31 +108,33 @@ class MemoryDiagnosticReporter extends DiagnosticReporter {
|
|||
|
||||
suite('markdown: Diagnostic Computer', () => {
|
||||
|
||||
test('Should not return any diagnostics for empty document', async () => {
|
||||
test('Should not return any diagnostics for empty document', withStore(async (store) => {
|
||||
const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines(
|
||||
`text`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc, new InMemoryMdWorkspace([doc]));
|
||||
const diagnostics = await getComputedDiagnostics(store, doc, workspace);
|
||||
assert.deepStrictEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should generate diagnostic for link to file that does not exist', async () => {
|
||||
test('Should generate diagnostic for link to file that does not exist', withStore(async (store) => {
|
||||
const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines(
|
||||
`[bad](/no/such/file.md)`,
|
||||
`[good](/doc.md)`,
|
||||
`[good-ref]: /doc.md`,
|
||||
`[bad-ref]: /no/such/file.md`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc, new InMemoryMdWorkspace([doc]));
|
||||
const diagnostics = await getComputedDiagnostics(store, doc, workspace);
|
||||
assertDiagnosticsEqual(diagnostics, [
|
||||
new vscode.Range(0, 6, 0, 22),
|
||||
new vscode.Range(3, 11, 3, 27),
|
||||
]);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should generate diagnostics for links to header that does not exist in current file', async () => {
|
||||
test('Should generate diagnostics for links to header that does not exist in current file', withStore(async (store) => {
|
||||
const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines(
|
||||
`[good](#good-header)`,
|
||||
`# Good Header`,
|
||||
|
@ -141,15 +143,16 @@ suite('markdown: Diagnostic Computer', () => {
|
|||
`[good-ref]: #good-header`,
|
||||
`[bad-ref]: #no-such-header`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc, new InMemoryMdWorkspace([doc]));
|
||||
const diagnostics = await getComputedDiagnostics(store, doc, workspace);
|
||||
assertDiagnosticsEqual(diagnostics, [
|
||||
new vscode.Range(2, 6, 2, 21),
|
||||
new vscode.Range(5, 11, 5, 26),
|
||||
]);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should generate diagnostics for links to non-existent headers in other files', async () => {
|
||||
test('Should generate diagnostics for links to non-existent headers in other files', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`# My header`,
|
||||
`[good](#my-header)`,
|
||||
|
@ -163,13 +166,13 @@ suite('markdown: Diagnostic Computer', () => {
|
|||
`# Other header`,
|
||||
));
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc1, new InMemoryMdWorkspace([doc1, doc2]));
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, new InMemoryMdWorkspace([doc1, doc2]));
|
||||
assertDiagnosticsEqual(diagnostics, [
|
||||
new vscode.Range(5, 14, 5, 35),
|
||||
]);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should support links both with and without .md file extension', async () => {
|
||||
test('Should support links both with and without .md file extension', withStore(async (store) => {
|
||||
const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines(
|
||||
`# My header`,
|
||||
`[good](#my-header)`,
|
||||
|
@ -178,188 +181,182 @@ suite('markdown: Diagnostic Computer', () => {
|
|||
`[good](/doc#my-header)`,
|
||||
`[good](doc#my-header)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc, new InMemoryMdWorkspace([doc]));
|
||||
const diagnostics = await getComputedDiagnostics(store, doc, workspace);
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should generate diagnostics for non-existent link reference', async () => {
|
||||
test('Should generate diagnostics for non-existent link reference', withStore(async (store) => {
|
||||
const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines(
|
||||
`[good link][good]`,
|
||||
`[bad link][no-such]`,
|
||||
``,
|
||||
`[good]: http://example.com`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc, new InMemoryMdWorkspace([doc]));
|
||||
const diagnostics = await getComputedDiagnostics(store, doc, workspace);
|
||||
assertDiagnosticsEqual(diagnostics, [
|
||||
new vscode.Range(1, 11, 1, 18),
|
||||
]);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not generate diagnostics when validate is disabled', async () => {
|
||||
test('Should not generate diagnostics when validate is disabled', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`[text](#no-such-header)`,
|
||||
`[text][no-such-ref]`,
|
||||
));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1]);
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, new MemoryDiagnosticConfiguration({ enabled: false }).getOptions(doc1.uri));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1]));
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, new MemoryDiagnosticConfiguration({ enabled: false }).getOptions(doc1.uri));
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not generate diagnostics for email autolink', async () => {
|
||||
test('Should not generate diagnostics for email autolink', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`a <user@example.com> c`,
|
||||
));
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc1, new InMemoryMdWorkspace([doc1]));
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, new InMemoryMdWorkspace([doc1]));
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not generate diagnostics for html tag that looks like an autolink', async () => {
|
||||
test('Should not generate diagnostics for html tag that looks like an autolink', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`a <tag>b</tag> c`,
|
||||
`a <scope:tag>b</scope:tag> c`,
|
||||
));
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc1, new InMemoryMdWorkspace([doc1]));
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, new InMemoryMdWorkspace([doc1]));
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should allow ignoring invalid file link using glob', async () => {
|
||||
test('Should allow ignoring invalid file link using glob', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`[text](/no-such-file)`,
|
||||
`![img](/no-such-file)`,
|
||||
`[text]: /no-such-file`,
|
||||
));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1]);
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/no-such-file'] });
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1]));
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { ignoreLinks: ['/no-such-file'] });
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should be able to disable fragment validation for external files', async () => {
|
||||
test('Should be able to disable fragment validation for external files', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`![i](/doc2.md#no-such)`,
|
||||
));
|
||||
const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines(''));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1, doc2]);
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { validateMarkdownFileLinkFragments: DiagnosticLevel.ignore });
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { validateMarkdownFileLinkFragments: DiagnosticLevel.ignore });
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Disabling own fragment validation should also disable path fragment validation by default', async () => {
|
||||
test('Disabling own fragment validation should also disable path fragment validation by default', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`[b](#no-head)`,
|
||||
`![i](/doc2.md#no-such)`,
|
||||
));
|
||||
const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines(''));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1, doc2]);
|
||||
|
||||
{
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { validateFragmentLinks: DiagnosticLevel.ignore });
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { validateFragmentLinks: DiagnosticLevel.ignore });
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
}
|
||||
{
|
||||
// But we should be able to override the default
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { validateFragmentLinks: DiagnosticLevel.ignore, validateMarkdownFileLinkFragments: DiagnosticLevel.warning });
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { validateFragmentLinks: DiagnosticLevel.ignore, validateMarkdownFileLinkFragments: DiagnosticLevel.warning });
|
||||
assertDiagnosticsEqual(diagnostics, [
|
||||
new vscode.Range(1, 13, 1, 21),
|
||||
]);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
test('ignoreLinks should allow skipping link to non-existent file', async () => {
|
||||
test('ignoreLinks should allow skipping link to non-existent file', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`[text](/no-such-file#header)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1]));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1]);
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/no-such-file'] });
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { ignoreLinks: ['/no-such-file'] });
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('ignoreLinks should not consider link fragment', async () => {
|
||||
test('ignoreLinks should not consider link fragment', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`[text](/no-such-file#header)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1]));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1]);
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/no-such-file'] });
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { ignoreLinks: ['/no-such-file'] });
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('ignoreLinks should support globs', async () => {
|
||||
test('ignoreLinks should support globs', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`![i](/images/aaa.png)`,
|
||||
`![i](/images/sub/bbb.png)`,
|
||||
`![i](/images/sub/sub2/ccc.png)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1]));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1]);
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/images/**/*.png'] });
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { ignoreLinks: ['/images/**/*.png'] });
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('ignoreLinks should support ignoring header', async () => {
|
||||
test('ignoreLinks should support ignoring header', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`![i](#no-such)`,
|
||||
));
|
||||
const workspace = new InMemoryMdWorkspace([doc1]);
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1]));
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['#no-such'] });
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { ignoreLinks: ['#no-such'] });
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('ignoreLinks should support ignoring header in file', async () => {
|
||||
test('ignoreLinks should support ignoring header in file', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`![i](/doc2.md#no-such)`,
|
||||
));
|
||||
const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines(''));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1, doc2]));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1, doc2]);
|
||||
{
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md#no-such'] });
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { ignoreLinks: ['/doc2.md#no-such'] });
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
}
|
||||
{
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md#*'] });
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { ignoreLinks: ['/doc2.md#*'] });
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
test('ignoreLinks should support ignore header links if file is ignored', async () => {
|
||||
test('ignoreLinks should support ignore header links if file is ignored', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`![i](/doc2.md#no-such)`,
|
||||
));
|
||||
const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines(''));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1, doc2]);
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md'] });
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { ignoreLinks: ['/doc2.md'] });
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not detect checkboxes as invalid links', async () => {
|
||||
test('Should not detect checkboxes as invalid links', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`- [x]`,
|
||||
`- [X]`,
|
||||
`- [ ]`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1]));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1]);
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md'] });
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, { ignoreLinks: ['/doc2.md'] });
|
||||
assertDiagnosticsEqual(diagnostics, []);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should detect invalid links with titles', async () => {
|
||||
test('Should detect invalid links with titles', withStore(async (store) => {
|
||||
const doc = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`[link](<no such.md> "text")`,
|
||||
`[link](<no such.md> 'text')`,
|
||||
|
@ -368,7 +365,9 @@ suite('markdown: Diagnostic Computer', () => {
|
|||
`[link](no-such.md 'text')`,
|
||||
`[link](no-such.md (text))`,
|
||||
));
|
||||
const diagnostics = await getComputedDiagnostics(doc, new InMemoryMdWorkspace([doc]));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(store, doc, workspace);
|
||||
assertDiagnosticsEqual(diagnostics, [
|
||||
new vscode.Range(0, 8, 0, 18),
|
||||
new vscode.Range(1, 8, 1, 18),
|
||||
|
@ -377,36 +376,36 @@ suite('markdown: Diagnostic Computer', () => {
|
|||
new vscode.Range(4, 7, 4, 17),
|
||||
new vscode.Range(5, 7, 5, 17),
|
||||
]);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should generate diagnostics for non-existent header using file link to own file', async () => {
|
||||
test('Should generate diagnostics for non-existent header using file link to own file', withStore(async (store) => {
|
||||
const doc = new InMemoryDocument(workspacePath('sub', 'doc.md'), joinLines(
|
||||
`[bad](doc.md#no-such)`,
|
||||
`[bad](doc#no-such)`,
|
||||
`[bad](/sub/doc.md#no-such)`,
|
||||
`[bad](/sub/doc#no-such)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc, new InMemoryMdWorkspace([doc]));
|
||||
const diagnostics = await getComputedDiagnostics(store, doc, workspace);
|
||||
assertDiagnosticsEqual(orderDiagnosticsByRange(diagnostics), [
|
||||
new vscode.Range(0, 12, 0, 20),
|
||||
new vscode.Range(1, 9, 1, 17),
|
||||
new vscode.Range(2, 17, 2, 25),
|
||||
new vscode.Range(3, 14, 3, 22),
|
||||
]);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Own header link using file path link should be controlled by "validateMarkdownFileLinkFragments" instead of "validateFragmentLinks"', async () => {
|
||||
test('Own header link using file path link should be controlled by "validateMarkdownFileLinkFragments" instead of "validateFragmentLinks"', withStore(async (store) => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('sub', 'doc.md'), joinLines(
|
||||
`[bad](doc.md#no-such)`,
|
||||
`[bad](doc#no-such)`,
|
||||
`[bad](/sub/doc.md#no-such)`,
|
||||
`[bad](/sub/doc#no-such)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1]));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1]);
|
||||
|
||||
const diagnostics = await getComputedDiagnostics(doc1, workspace, {
|
||||
const diagnostics = await getComputedDiagnostics(store, doc1, workspace, {
|
||||
validateFragmentLinks: DiagnosticLevel.ignore,
|
||||
validateMarkdownFileLinkFragments: DiagnosticLevel.warning,
|
||||
});
|
||||
|
@ -416,31 +415,22 @@ suite('markdown: Diagnostic Computer', () => {
|
|||
new vscode.Range(2, 17, 2, 25),
|
||||
new vscode.Range(3, 14, 3, 22),
|
||||
]);
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
suite('Markdown: Diagnostics manager', () => {
|
||||
|
||||
const _disposables: vscode.Disposable[] = [];
|
||||
|
||||
setup(() => {
|
||||
disposeAll(_disposables);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
disposeAll(_disposables);
|
||||
});
|
||||
|
||||
function createDiagnosticsManager(
|
||||
store: DisposableStore,
|
||||
workspace: IMdWorkspace,
|
||||
configuration = new MemoryDiagnosticConfiguration({}),
|
||||
reporter: DiagnosticReporter = new DiagnosticCollectionReporter(),
|
||||
) {
|
||||
const engine = createNewMarkdownEngine();
|
||||
const linkProvider = new MdLinkProvider(engine, workspace, nulLogger);
|
||||
const tocProvider = new MdTableOfContentsProvider(engine, workspace, nulLogger);
|
||||
const referencesProvider = new MdReferencesProvider(engine, workspace, tocProvider, nulLogger);
|
||||
const manager = new DiagnosticManager(
|
||||
const linkProvider = store.add(new MdLinkProvider(engine, workspace, nulLogger));
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const referencesProvider = store.add(new MdReferencesProvider(engine, workspace, tocProvider, nulLogger));
|
||||
const manager = store.add(new DiagnosticManager(
|
||||
workspace,
|
||||
new DiagnosticComputer(workspace, linkProvider, tocProvider),
|
||||
configuration,
|
||||
|
@ -448,27 +438,26 @@ suite('Markdown: Diagnostics manager', () => {
|
|||
referencesProvider,
|
||||
tocProvider,
|
||||
nulLogger,
|
||||
0);
|
||||
_disposables.push(linkProvider, tocProvider, referencesProvider, manager);
|
||||
0));
|
||||
return manager;
|
||||
}
|
||||
|
||||
test('Changing enable/disable should recompute diagnostics', async () => {
|
||||
test('Changing enable/disable should recompute diagnostics', withStore(async (store) => {
|
||||
const doc1Uri = workspacePath('doc1.md');
|
||||
const doc2Uri = workspacePath('doc2.md');
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(doc1Uri, joinLines(
|
||||
`[text](#no-such-1)`,
|
||||
)),
|
||||
new InMemoryDocument(doc2Uri, joinLines(
|
||||
`[text](#no-such-2)`,
|
||||
))
|
||||
]);
|
||||
]));
|
||||
|
||||
const reporter = new MemoryDiagnosticReporter();
|
||||
const reporter = store.add(new MemoryDiagnosticReporter());
|
||||
const config = new MemoryDiagnosticConfiguration({ enabled: true });
|
||||
|
||||
const manager = createDiagnosticsManager(workspace, config, reporter);
|
||||
const manager = createDiagnosticsManager(store, workspace, config, reporter);
|
||||
await manager.ready;
|
||||
|
||||
// Check initial state (Enabled)
|
||||
|
@ -495,9 +484,9 @@ suite('Markdown: Diagnostics manager', () => {
|
|||
assertDiagnosticsEqual(reporter.get(doc2Uri), [
|
||||
new vscode.Range(0, 7, 0, 17),
|
||||
]);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should revalidate linked files when header changes', async () => {
|
||||
test('Should revalidate linked files when header changes', withStore(async (store) => {
|
||||
const doc1Uri = workspacePath('doc1.md');
|
||||
const doc1 = new InMemoryDocument(doc1Uri, joinLines(
|
||||
`[text](#no-such)`,
|
||||
|
@ -509,11 +498,10 @@ suite('Markdown: Diagnostics manager', () => {
|
|||
`[text](#header)`,
|
||||
`[text](#no-such-2)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1, doc2]));
|
||||
const reporter = store.add(new MemoryDiagnosticReporter());
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1, doc2]);
|
||||
const reporter = new MemoryDiagnosticReporter();
|
||||
|
||||
const manager = createDiagnosticsManager(workspace, new MemoryDiagnosticConfiguration({}), reporter);
|
||||
const manager = createDiagnosticsManager(store, workspace, new MemoryDiagnosticConfiguration({}), reporter);
|
||||
await manager.ready;
|
||||
|
||||
// Check initial state
|
||||
|
@ -553,9 +541,9 @@ suite('Markdown: Diagnostics manager', () => {
|
|||
assertDiagnosticsEqual(reporter.get(doc2Uri), [
|
||||
new vscode.Range(2, 7, 2, 17),
|
||||
]);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should revalidate linked files when file is deleted/created', async () => {
|
||||
test('Should revalidate linked files when file is deleted/created', withStore(async (store) => {
|
||||
const doc1Uri = workspacePath('doc1.md');
|
||||
const doc1 = new InMemoryDocument(doc1Uri, joinLines(
|
||||
`[text](/doc2.md)`,
|
||||
|
@ -565,11 +553,10 @@ suite('Markdown: Diagnostics manager', () => {
|
|||
const doc2 = new InMemoryDocument(doc2Uri, joinLines(
|
||||
`# Header`
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1, doc2]));
|
||||
const reporter = store.add(new MemoryDiagnosticReporter());
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1, doc2]);
|
||||
const reporter = new MemoryDiagnosticReporter();
|
||||
|
||||
const manager = createDiagnosticsManager(workspace, new MemoryDiagnosticConfiguration({}), reporter);
|
||||
const manager = createDiagnosticsManager(store, workspace, new MemoryDiagnosticConfiguration({}), reporter);
|
||||
await manager.ready;
|
||||
|
||||
// Check initial state
|
||||
|
@ -589,5 +576,5 @@ suite('Markdown: Diagnostics manager', () => {
|
|||
workspace.createDocument(doc2);
|
||||
await reporter.waitPendingWork();
|
||||
assertDiagnosticsEqual(reporter.get(doc1Uri), []);
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -7,92 +7,115 @@ import * as assert from 'assert';
|
|||
import 'mocha';
|
||||
import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbols';
|
||||
import { MdTableOfContentsProvider } from '../tableOfContents';
|
||||
import { DisposableStore } from '../util/dispose';
|
||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
||||
import { createNewMarkdownEngine } from './engine';
|
||||
import { InMemoryMdWorkspace } from './inMemoryWorkspace';
|
||||
import { nulLogger } from './nulLogging';
|
||||
import { workspacePath } from './util';
|
||||
import { joinLines, withStore, workspacePath } from './util';
|
||||
|
||||
|
||||
function getSymbolsForFile(fileContents: string) {
|
||||
function getSymbolsForFile(store: DisposableStore, fileContents: string) {
|
||||
const doc = new InMemoryDocument(workspacePath('test.md'), fileContents);
|
||||
const workspace = new InMemoryMdWorkspace([doc]);
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
const engine = createNewMarkdownEngine();
|
||||
const provider = new MdDocumentSymbolProvider(new MdTableOfContentsProvider(engine, workspace, nulLogger), nulLogger);
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const provider = new MdDocumentSymbolProvider(tocProvider, nulLogger);
|
||||
return provider.provideDocumentSymbols(doc);
|
||||
}
|
||||
|
||||
suite('markdown.DocumentSymbolProvider', () => {
|
||||
test('Should not return anything for empty document', async () => {
|
||||
const symbols = await getSymbolsForFile('');
|
||||
suite('Markdown: DocumentSymbolProvider', () => {
|
||||
test('Should not return anything for empty document', withStore(async (store) => {
|
||||
const symbols = await getSymbolsForFile(store, '');
|
||||
assert.strictEqual(symbols.length, 0);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not return anything for document with no headers', async () => {
|
||||
const symbols = await getSymbolsForFile('a\na');
|
||||
test('Should not return anything for document with no headers', withStore(async (store) => {
|
||||
const symbols = await getSymbolsForFile(store, joinLines(
|
||||
`a`,
|
||||
`a`,
|
||||
));
|
||||
assert.strictEqual(symbols.length, 0);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not return anything for document with # but no real headers', async () => {
|
||||
const symbols = await getSymbolsForFile('a#a\na#');
|
||||
test('Should not return anything for document with # but no real headers', withStore(async (store) => {
|
||||
const symbols = await getSymbolsForFile(store, joinLines(
|
||||
`a#a`,
|
||||
`a#`,
|
||||
));
|
||||
assert.strictEqual(symbols.length, 0);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should return single symbol for single header', async () => {
|
||||
const symbols = await getSymbolsForFile('# h');
|
||||
test('Should return single symbol for single header', withStore(async (store) => {
|
||||
const symbols = await getSymbolsForFile(store, '# h');
|
||||
assert.strictEqual(symbols.length, 1);
|
||||
assert.strictEqual(symbols[0].name, '# h');
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not care about symbol level for single header', async () => {
|
||||
const symbols = await getSymbolsForFile('### h');
|
||||
test('Should not care about symbol level for single header', withStore(async (store) => {
|
||||
const symbols = await getSymbolsForFile(store, '### h');
|
||||
assert.strictEqual(symbols.length, 1);
|
||||
assert.strictEqual(symbols[0].name, '### h');
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should put symbols of same level in flat list', async () => {
|
||||
const symbols = await getSymbolsForFile('## h\n## h2');
|
||||
test('Should put symbols of same level in flat list', withStore(async (store) => {
|
||||
const symbols = await getSymbolsForFile(store, joinLines(
|
||||
`## h`,
|
||||
`## h2`,
|
||||
));
|
||||
assert.strictEqual(symbols.length, 2);
|
||||
assert.strictEqual(symbols[0].name, '## h');
|
||||
assert.strictEqual(symbols[1].name, '## h2');
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should nest symbol of level - 1 under parent', async () => {
|
||||
|
||||
const symbols = await getSymbolsForFile('# h\n## h2\n## h3');
|
||||
test('Should nest symbol of level - 1 under parent', withStore(async (store) => {
|
||||
const symbols = await getSymbolsForFile(store, joinLines(
|
||||
`# h`,
|
||||
`## h2`,
|
||||
`## h3`,
|
||||
));
|
||||
assert.strictEqual(symbols.length, 1);
|
||||
assert.strictEqual(symbols[0].name, '# h');
|
||||
assert.strictEqual(symbols[0].children.length, 2);
|
||||
assert.strictEqual(symbols[0].children[0].name, '## h2');
|
||||
assert.strictEqual(symbols[0].children[1].name, '## h3');
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should nest symbol of level - n under parent', async () => {
|
||||
const symbols = await getSymbolsForFile('# h\n#### h2');
|
||||
test('Should nest symbol of level - n under parent', withStore(async (store) => {
|
||||
const symbols = await getSymbolsForFile(store, joinLines(
|
||||
`# h`,
|
||||
`#### h2`,
|
||||
));
|
||||
assert.strictEqual(symbols.length, 1);
|
||||
assert.strictEqual(symbols[0].name, '# h');
|
||||
assert.strictEqual(symbols[0].children.length, 1);
|
||||
assert.strictEqual(symbols[0].children[0].name, '#### h2');
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should flatten children where lower level occurs first', async () => {
|
||||
const symbols = await getSymbolsForFile('# h\n### h2\n## h3');
|
||||
test('Should flatten children where lower level occurs first', withStore(async (store) => {
|
||||
const symbols = await getSymbolsForFile(store, joinLines(
|
||||
`# h`,
|
||||
`### h2`,
|
||||
`## h3`,
|
||||
));
|
||||
assert.strictEqual(symbols.length, 1);
|
||||
assert.strictEqual(symbols[0].name, '# h');
|
||||
assert.strictEqual(symbols[0].children.length, 2);
|
||||
assert.strictEqual(symbols[0].children[0].name, '### h2');
|
||||
assert.strictEqual(symbols[0].children[1].name, '## h3');
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should handle line separator in file. Issue #63749', async () => {
|
||||
const symbols = await getSymbolsForFile(`# A
|
||||
- foo
|
||||
|
||||
# B
|
||||
- bar`);
|
||||
test('Should handle line separator in file. Issue #63749', withStore(async (store) => {
|
||||
const symbols = await getSymbolsForFile(store, joinLines(
|
||||
`# A`,
|
||||
`- foo`,
|
||||
``,
|
||||
`# B`,
|
||||
`- bar`,
|
||||
));
|
||||
assert.strictEqual(symbols.length, 2);
|
||||
assert.strictEqual(symbols[0].name, '# A');
|
||||
assert.strictEqual(symbols[1].name, '# B');
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
|
|
|
@ -9,17 +9,19 @@ import * as vscode from 'vscode';
|
|||
import { MdReference, MdReferencesProvider } from '../languageFeatures/references';
|
||||
import { MdTableOfContentsProvider } from '../tableOfContents';
|
||||
import { noopToken } from '../util/cancellation';
|
||||
import { DisposableStore } from '../util/dispose';
|
||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
||||
import { IMdWorkspace } from '../workspace';
|
||||
import { createNewMarkdownEngine } from './engine';
|
||||
import { InMemoryMdWorkspace } from './inMemoryWorkspace';
|
||||
import { nulLogger } from './nulLogging';
|
||||
import { joinLines, workspacePath } from './util';
|
||||
import { joinLines, withStore, workspacePath } from './util';
|
||||
|
||||
|
||||
function getFileReferences(resource: vscode.Uri, workspace: IMdWorkspace) {
|
||||
function getFileReferences(store: DisposableStore, resource: vscode.Uri, workspace: IMdWorkspace) {
|
||||
const engine = createNewMarkdownEngine();
|
||||
const computer = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace, nulLogger), nulLogger);
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const computer = store.add(new MdReferencesProvider(engine, workspace, tocProvider, nulLogger));
|
||||
return computer.getAllReferencesToFile(resource, noopToken);
|
||||
}
|
||||
|
||||
|
@ -37,82 +39,82 @@ function assertReferencesEqual(actualRefs: readonly MdReference[], ...expectedRe
|
|||
|
||||
suite('markdown: find file references', () => {
|
||||
|
||||
test('Should find basic references', async () => {
|
||||
test('Should find basic references', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const otherUri = workspacePath('other.md');
|
||||
|
||||
const refs = await getFileReferences(otherUri, new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(docUri, joinLines(
|
||||
`# header`,
|
||||
`[link 1](./other.md)`,
|
||||
`[link 2](./other.md)`,
|
||||
`[link 2](./other.md)`
|
||||
)),
|
||||
new InMemoryDocument(otherUri, joinLines(
|
||||
`# header`,
|
||||
`pre`,
|
||||
`[link 3](./other.md)`,
|
||||
`post`,
|
||||
`post`
|
||||
)),
|
||||
]));
|
||||
|
||||
assertReferencesEqual(refs!,
|
||||
const refs = await getFileReferences(store, otherUri, workspace);
|
||||
assertReferencesEqual(refs,
|
||||
{ uri: docUri, line: 1 },
|
||||
{ uri: docUri, line: 2 },
|
||||
{ uri: otherUri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find references with and without file extensions', async () => {
|
||||
test('Should find references with and without file extensions', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const otherUri = workspacePath('other.md');
|
||||
|
||||
const refs = await getFileReferences(otherUri, new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(docUri, joinLines(
|
||||
`# header`,
|
||||
`[link 1](./other.md)`,
|
||||
`[link 2](./other)`,
|
||||
`[link 2](./other)`
|
||||
)),
|
||||
new InMemoryDocument(otherUri, joinLines(
|
||||
`# header`,
|
||||
`pre`,
|
||||
`[link 3](./other.md)`,
|
||||
`[link 4](./other)`,
|
||||
`post`,
|
||||
`post`
|
||||
)),
|
||||
]));
|
||||
|
||||
assertReferencesEqual(refs!,
|
||||
const refs = await getFileReferences(store, otherUri, workspace);
|
||||
assertReferencesEqual(refs,
|
||||
{ uri: docUri, line: 1 },
|
||||
{ uri: docUri, line: 2 },
|
||||
{ uri: otherUri, line: 2 },
|
||||
{ uri: otherUri, line: 3 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find references with headers on links', async () => {
|
||||
test('Should find references with headers on links', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const otherUri = workspacePath('other.md');
|
||||
|
||||
const refs = await getFileReferences(otherUri, new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(docUri, joinLines(
|
||||
`# header`,
|
||||
`[link 1](./other.md#sub-bla)`,
|
||||
`[link 2](./other#sub-bla)`,
|
||||
`[link 2](./other#sub-bla)`
|
||||
)),
|
||||
new InMemoryDocument(otherUri, joinLines(
|
||||
`# header`,
|
||||
`pre`,
|
||||
`[link 3](./other.md#sub-bla)`,
|
||||
`[link 4](./other#sub-bla)`,
|
||||
`post`,
|
||||
`post`
|
||||
)),
|
||||
]));
|
||||
|
||||
assertReferencesEqual(refs!,
|
||||
const refs = await getFileReferences(store, otherUri, workspace);
|
||||
assertReferencesEqual(refs,
|
||||
{ uri: docUri, line: 1 },
|
||||
{ uri: docUri, line: 2 },
|
||||
{ uri: otherUri, line: 2 },
|
||||
{ uri: otherUri, line: 3 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -8,32 +8,43 @@ import 'mocha';
|
|||
import * as vscode from 'vscode';
|
||||
import { MdFoldingProvider } from '../languageFeatures/folding';
|
||||
import { MdTableOfContentsProvider } from '../tableOfContents';
|
||||
import { noopToken } from '../util/cancellation';
|
||||
import { DisposableStore } from '../util/dispose';
|
||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
||||
import { createNewMarkdownEngine } from './engine';
|
||||
import { InMemoryMdWorkspace } from './inMemoryWorkspace';
|
||||
import { nulLogger } from './nulLogging';
|
||||
import { joinLines } from './util';
|
||||
import { joinLines, withStore } from './util';
|
||||
|
||||
const testFileName = vscode.Uri.file('test.md');
|
||||
|
||||
suite('markdown.FoldingProvider', () => {
|
||||
test('Should not return anything for empty document', async () => {
|
||||
const folds = await getFoldsForDocument(``);
|
||||
assert.strictEqual(folds.length, 0);
|
||||
});
|
||||
async function getFoldsForDocument(store: DisposableStore, contents: string) {
|
||||
const doc = new InMemoryDocument(testFileName, contents);
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
const engine = createNewMarkdownEngine();
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const provider = new MdFoldingProvider(engine, tocProvider);
|
||||
return provider.provideFoldingRanges(doc, {}, noopToken);
|
||||
}
|
||||
|
||||
test('Should not return anything for document without headers', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
suite('markdown.FoldingProvider', () => {
|
||||
test('Should not return anything for empty document', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, ``);
|
||||
assert.strictEqual(folds.length, 0);
|
||||
}));
|
||||
|
||||
test('Should not return anything for document without headers', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
`a`,
|
||||
`**b** afas`,
|
||||
`a#b`,
|
||||
`a`,
|
||||
));
|
||||
assert.strictEqual(folds.length, 0);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should fold from header to end of document', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('Should fold from header to end of document', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
`a`,
|
||||
`# b`,
|
||||
`c`,
|
||||
|
@ -43,10 +54,10 @@ suite('markdown.FoldingProvider', () => {
|
|||
const firstFold = folds[0];
|
||||
assert.strictEqual(firstFold.start, 1);
|
||||
assert.strictEqual(firstFold.end, 3);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should leave single newline before next header', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('Should leave single newline before next header', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
``,
|
||||
`# a`,
|
||||
`x`,
|
||||
|
@ -58,10 +69,10 @@ suite('markdown.FoldingProvider', () => {
|
|||
const firstFold = folds[0];
|
||||
assert.strictEqual(firstFold.start, 1);
|
||||
assert.strictEqual(firstFold.end, 2);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should collapse multiple newlines to single newline before next header', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('Should collapse multiple newlines to single newline before next header', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
``,
|
||||
`# a`,
|
||||
`x`,
|
||||
|
@ -75,10 +86,10 @@ suite('markdown.FoldingProvider', () => {
|
|||
const firstFold = folds[0];
|
||||
assert.strictEqual(firstFold.start, 1);
|
||||
assert.strictEqual(firstFold.end, 4);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not collapse if there is no newline before next header', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('Should not collapse if there is no newline before next header', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
``,
|
||||
`# a`,
|
||||
`x`,
|
||||
|
@ -89,10 +100,10 @@ suite('markdown.FoldingProvider', () => {
|
|||
const firstFold = folds[0];
|
||||
assert.strictEqual(firstFold.start, 1);
|
||||
assert.strictEqual(firstFold.end, 2);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should fold nested <!-- #region --> markers', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('Should fold nested <!-- #region --> markers', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
`a`,
|
||||
`<!-- #region -->`,
|
||||
`b`,
|
||||
|
@ -116,10 +127,10 @@ suite('markdown.FoldingProvider', () => {
|
|||
assert.strictEqual(first.end, 5);
|
||||
assert.strictEqual(second.start, 7);
|
||||
assert.strictEqual(second.end, 9);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should fold from list to end of document', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('Should fold from list to end of document', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
`a`,
|
||||
`- b`,
|
||||
`c`,
|
||||
|
@ -129,10 +140,10 @@ suite('markdown.FoldingProvider', () => {
|
|||
const firstFold = folds[0];
|
||||
assert.strictEqual(firstFold.start, 1);
|
||||
assert.strictEqual(firstFold.end, 3);
|
||||
});
|
||||
}));
|
||||
|
||||
test('lists folds should span multiple lines of content', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('lists folds should span multiple lines of content', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
`a`,
|
||||
`- This list item\n spans multiple\n lines.`,
|
||||
));
|
||||
|
@ -140,10 +151,10 @@ suite('markdown.FoldingProvider', () => {
|
|||
const firstFold = folds[0];
|
||||
assert.strictEqual(firstFold.start, 1);
|
||||
assert.strictEqual(firstFold.end, 3);
|
||||
});
|
||||
}));
|
||||
|
||||
test('List should leave single blankline before new element', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('List should leave single blankline before new element', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
`- a`,
|
||||
`a`,
|
||||
``,
|
||||
|
@ -154,10 +165,10 @@ suite('markdown.FoldingProvider', () => {
|
|||
const firstFold = folds[0];
|
||||
assert.strictEqual(firstFold.start, 0);
|
||||
assert.strictEqual(firstFold.end, 2);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should fold fenced code blocks', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('Should fold fenced code blocks', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
`~~~ts`,
|
||||
`a`,
|
||||
`~~~`,
|
||||
|
@ -167,10 +178,10 @@ suite('markdown.FoldingProvider', () => {
|
|||
const firstFold = folds[0];
|
||||
assert.strictEqual(firstFold.start, 0);
|
||||
assert.strictEqual(firstFold.end, 2);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should fold fenced code blocks with yaml front matter', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('Should fold fenced code blocks with yaml front matter', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
`---`,
|
||||
`title: bla`,
|
||||
`---`,
|
||||
|
@ -188,10 +199,10 @@ suite('markdown.FoldingProvider', () => {
|
|||
const firstFold = folds[0];
|
||||
assert.strictEqual(firstFold.start, 4);
|
||||
assert.strictEqual(firstFold.end, 6);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should fold html blocks', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('Should fold html blocks', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
`x`,
|
||||
`<div>`,
|
||||
` fa`,
|
||||
|
@ -201,10 +212,10 @@ suite('markdown.FoldingProvider', () => {
|
|||
const firstFold = folds[0];
|
||||
assert.strictEqual(firstFold.start, 1);
|
||||
assert.strictEqual(firstFold.end, 3);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should fold html block comments', async () => {
|
||||
const folds = await getFoldsForDocument(joinLines(
|
||||
test('Should fold html block comments', withStore(async (store) => {
|
||||
const folds = await getFoldsForDocument(store, joinLines(
|
||||
`x`,
|
||||
`<!--`,
|
||||
`fa`,
|
||||
|
@ -215,14 +226,5 @@ suite('markdown.FoldingProvider', () => {
|
|||
assert.strictEqual(firstFold.start, 1);
|
||||
assert.strictEqual(firstFold.end, 3);
|
||||
assert.strictEqual(firstFold.kind, vscode.FoldingRangeKind.Comment);
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
async function getFoldsForDocument(contents: string) {
|
||||
const doc = new InMemoryDocument(testFileName, contents);
|
||||
const workspace = new InMemoryMdWorkspace([doc]);
|
||||
const engine = createNewMarkdownEngine();
|
||||
const provider = new MdFoldingProvider(engine, new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
return await provider.provideFoldingRanges(doc, {}, new vscode.CancellationTokenSource().token);
|
||||
}
|
||||
|
|
|
@ -7,14 +7,16 @@ import * as assert from 'assert';
|
|||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import { ITextDocument } from '../types/textDocument';
|
||||
import { Disposable } from '../util/dispose';
|
||||
import { ResourceMap } from '../util/resourceMap';
|
||||
import { IMdWorkspace } from '../workspace';
|
||||
|
||||
|
||||
export class InMemoryMdWorkspace implements IMdWorkspace {
|
||||
export class InMemoryMdWorkspace extends Disposable implements IMdWorkspace {
|
||||
private readonly _documents = new ResourceMap<ITextDocument>(uri => uri.fsPath);
|
||||
|
||||
constructor(documents: ITextDocument[]) {
|
||||
super();
|
||||
for (const doc of documents) {
|
||||
this._documents.set(doc.uri, doc);
|
||||
}
|
||||
|
@ -49,13 +51,13 @@ export class InMemoryMdWorkspace implements IMdWorkspace {
|
|||
return Array.from(files.entries());
|
||||
}
|
||||
|
||||
private readonly _onDidChangeMarkdownDocumentEmitter = new vscode.EventEmitter<ITextDocument>();
|
||||
private readonly _onDidChangeMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<ITextDocument>());
|
||||
public onDidChangeMarkdownDocument = this._onDidChangeMarkdownDocumentEmitter.event;
|
||||
|
||||
private readonly _onDidCreateMarkdownDocumentEmitter = new vscode.EventEmitter<ITextDocument>();
|
||||
private readonly _onDidCreateMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<ITextDocument>());
|
||||
public onDidCreateMarkdownDocument = this._onDidCreateMarkdownDocumentEmitter.event;
|
||||
|
||||
private readonly _onDidDeleteMarkdownDocumentEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||
private readonly _onDidDeleteMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<vscode.Uri>());
|
||||
public onDidDeleteMarkdownDocument = this._onDidDeleteMarkdownDocumentEmitter.event;
|
||||
|
||||
public updateDocument(document: ITextDocument) {
|
||||
|
|
|
@ -9,17 +9,19 @@ import * as vscode from 'vscode';
|
|||
import { MdReferencesProvider, MdVsCodeReferencesProvider } from '../languageFeatures/references';
|
||||
import { MdTableOfContentsProvider } from '../tableOfContents';
|
||||
import { noopToken } from '../util/cancellation';
|
||||
import { DisposableStore } from '../util/dispose';
|
||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
||||
import { IMdWorkspace } from '../workspace';
|
||||
import { createNewMarkdownEngine } from './engine';
|
||||
import { InMemoryMdWorkspace } from './inMemoryWorkspace';
|
||||
import { nulLogger } from './nulLogging';
|
||||
import { joinLines, workspacePath } from './util';
|
||||
import { joinLines, withStore, workspacePath } from './util';
|
||||
|
||||
|
||||
function getReferences(doc: InMemoryDocument, pos: vscode.Position, workspace: IMdWorkspace) {
|
||||
function getReferences(store: DisposableStore, doc: InMemoryDocument, pos: vscode.Position, workspace: IMdWorkspace) {
|
||||
const engine = createNewMarkdownEngine();
|
||||
const computer = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace, nulLogger), nulLogger);
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const computer = store.add(new MdReferencesProvider(engine, workspace, tocProvider, nulLogger));
|
||||
const provider = new MdVsCodeReferencesProvider(computer);
|
||||
return provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken);
|
||||
}
|
||||
|
@ -42,26 +44,27 @@ function assertReferencesEqual(actualRefs: readonly vscode.Location[], ...expect
|
|||
}
|
||||
}
|
||||
|
||||
suite('markdown: find all references', () => {
|
||||
test('Should not return references when not on header or link', async () => {
|
||||
suite('Markdown: Find all references', () => {
|
||||
test('Should not return references when not on header or link', withStore(async (store) => {
|
||||
const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines(
|
||||
`# abc`,
|
||||
``,
|
||||
`[link 1](#abc)`,
|
||||
`text`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
{
|
||||
const refs = await getReferences(doc, new vscode.Position(1, 0), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(1, 0), workspace);
|
||||
assert.deepStrictEqual(refs, []);
|
||||
}
|
||||
{
|
||||
const refs = await getReferences(doc, new vscode.Position(3, 2), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(3, 2), workspace);
|
||||
assert.deepStrictEqual(refs, []);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find references from header within same file', async () => {
|
||||
test('Should find references from header within same file', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`# abc`,
|
||||
|
@ -70,55 +73,61 @@ suite('markdown: find all references', () => {
|
|||
`[not link](#noabc)`,
|
||||
`[link 2](#abc)`,
|
||||
));
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 3), new InMemoryMdWorkspace([doc]));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 3), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri, line: 0 },
|
||||
{ uri, line: 2 },
|
||||
{ uri, line: 4 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not return references when on link text', async () => {
|
||||
test('Should not return references when on link text', withStore(async (store) => {
|
||||
const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines(
|
||||
`[ref](#abc)`,
|
||||
`[ref]: http://example.com`,
|
||||
));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 1), new InMemoryMdWorkspace([doc]));
|
||||
assert.deepStrictEqual(refs, []);
|
||||
});
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
test('Should find references using normalized slug', async () => {
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 1), workspace);
|
||||
assert.deepStrictEqual(refs, []);
|
||||
}));
|
||||
|
||||
test('Should find references using normalized slug', withStore(async (store) => {
|
||||
const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines(
|
||||
`# a B c`,
|
||||
`[simple](#a-b-c)`,
|
||||
`[start underscore](#_a-b-c)`,
|
||||
`[different case](#a-B-C)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
{
|
||||
// Trigger header
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 0), new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 0), workspace);
|
||||
assert.deepStrictEqual(refs!.length, 4);
|
||||
}
|
||||
{
|
||||
// Trigger on line 1
|
||||
const refs = await getReferences(doc, new vscode.Position(1, 12), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(1, 12), workspace);
|
||||
assert.deepStrictEqual(refs!.length, 4);
|
||||
}
|
||||
{
|
||||
// Trigger on line 2
|
||||
const refs = await getReferences(doc, new vscode.Position(2, 24), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(2, 24), workspace);
|
||||
assert.deepStrictEqual(refs!.length, 4);
|
||||
}
|
||||
{
|
||||
// Trigger on line 3
|
||||
const refs = await getReferences(doc, new vscode.Position(3, 20), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(3, 20), workspace);
|
||||
assert.deepStrictEqual(refs!.length, 4);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find references from header across files', async () => {
|
||||
test('Should find references from header across files', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const other1Uri = workspacePath('sub', 'other.md');
|
||||
const other2Uri = workspacePath('other2.md');
|
||||
|
@ -128,59 +137,64 @@ suite('markdown: find all references', () => {
|
|||
``,
|
||||
`[link 1](#abc)`,
|
||||
));
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 3), new InMemoryMdWorkspace([
|
||||
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(other1Uri, joinLines(
|
||||
`[not link](#abc)`,
|
||||
`[not link](/doc.md#abz)`,
|
||||
`[link](/doc.md#abc)`,
|
||||
`[link](/doc.md#abc)`
|
||||
)),
|
||||
new InMemoryDocument(other2Uri, joinLines(
|
||||
`[not link](#abc)`,
|
||||
`[not link](./doc.md#abz)`,
|
||||
`[link](./doc.md#abc)`,
|
||||
`[link](./doc.md#abc)`
|
||||
))
|
||||
]));
|
||||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 3), workspace);
|
||||
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: docUri, line: 0 }, // Header definition
|
||||
{ uri: docUri, line: 2 },
|
||||
{ uri: other1Uri, line: 2 },
|
||||
{ uri: other2Uri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find references from header to link definitions ', async () => {
|
||||
test('Should find references from header to link definitions ', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`# abc`,
|
||||
``,
|
||||
`[bla]: #abc`
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 3), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 3), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri, line: 0 }, // Header definition
|
||||
{ uri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find header references from link definition', async () => {
|
||||
test('Should find header references from link definition', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`# A b C`,
|
||||
`[text][bla]`,
|
||||
`[bla]: #a-b-c`, // trigger here
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(2, 9), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(2, 9), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri, line: 0 }, // Header definition
|
||||
{ uri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find references from link within same file', async () => {
|
||||
test('Should find references from link within same file', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`# abc`,
|
||||
|
@ -189,16 +203,17 @@ suite('markdown: find all references', () => {
|
|||
`[not link](#noabc)`,
|
||||
`[link 2](#abc)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(2, 10), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(2, 10), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri, line: 0 }, // Header definition
|
||||
{ uri, line: 2 },
|
||||
{ uri, line: 4 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find references from link across files', async () => {
|
||||
test('Should find references from link across files', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const other1Uri = workspacePath('sub', 'other.md');
|
||||
const other2Uri = workspacePath('other2.md');
|
||||
|
@ -208,21 +223,22 @@ suite('markdown: find all references', () => {
|
|||
``,
|
||||
`[link 1](#abc)`,
|
||||
));
|
||||
const refs = await getReferences(doc, new vscode.Position(2, 10), new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(other1Uri, joinLines(
|
||||
`[not link](#abc)`,
|
||||
`[not link](/doc.md#abz)`,
|
||||
`[with ext](/doc.md#abc)`,
|
||||
`[without ext](/doc#abc)`,
|
||||
`[without ext](/doc#abc)`
|
||||
)),
|
||||
new InMemoryDocument(other2Uri, joinLines(
|
||||
`[not link](#abc)`,
|
||||
`[not link](./doc.md#abz)`,
|
||||
`[link](./doc.md#abc)`,
|
||||
`[link](./doc.md#abc)`
|
||||
))
|
||||
]));
|
||||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(2, 10), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: docUri, line: 0 }, // Header definition
|
||||
{ uri: docUri, line: 2 },
|
||||
|
@ -230,9 +246,9 @@ suite('markdown: find all references', () => {
|
|||
{ uri: other1Uri, line: 3 }, // Other without ext
|
||||
{ uri: other2Uri, line: 2 }, // Other2
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find references without requiring file extensions', async () => {
|
||||
test('Should find references without requiring file extensions', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const other1Uri = workspacePath('other.md');
|
||||
|
||||
|
@ -241,7 +257,7 @@ suite('markdown: find all references', () => {
|
|||
``,
|
||||
`[link 1](#a-b-c)`,
|
||||
));
|
||||
const refs = await getReferences(doc, new vscode.Position(2, 10), new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(other1Uri, joinLines(
|
||||
`[not link](#a-b-c)`,
|
||||
|
@ -249,10 +265,11 @@ suite('markdown: find all references', () => {
|
|||
`[with ext](/doc.md#a-b-c)`,
|
||||
`[without ext](/doc#a-b-c)`,
|
||||
`[rel with ext](./doc.md#a-b-c)`,
|
||||
`[rel without ext](./doc#a-b-c)`,
|
||||
`[rel without ext](./doc#a-b-c)`
|
||||
)),
|
||||
]));
|
||||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(2, 10), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: docUri, line: 0 }, // Header definition
|
||||
{ uri: docUri, line: 2 },
|
||||
|
@ -261,9 +278,9 @@ suite('markdown: find all references', () => {
|
|||
{ uri: other1Uri, line: 4 }, // Other relative link with ext
|
||||
{ uri: other1Uri, line: 5 }, // Other relative link without ext
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find references from link across files when triggered on link without file extension', async () => {
|
||||
test('Should find references from link across files when triggered on link without file extension', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const other1Uri = workspacePath('sub', 'other.md');
|
||||
|
||||
|
@ -272,23 +289,24 @@ suite('markdown: find all references', () => {
|
|||
`[without ext](./sub/other.md#header)`,
|
||||
));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 23), new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(other1Uri, joinLines(
|
||||
`pre`,
|
||||
`# header`,
|
||||
`post`,
|
||||
`post`
|
||||
)),
|
||||
]));
|
||||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 23), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: other1Uri, line: 1 }, // Header definition
|
||||
{ uri: docUri, line: 0 },
|
||||
{ uri: docUri, line: 1 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should include header references when triggered on file link', async () => {
|
||||
test('Should include header references when triggered on file link', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const otherUri = workspacePath('sub', 'other.md');
|
||||
|
||||
|
@ -297,24 +315,24 @@ suite('markdown: find all references', () => {
|
|||
`[with ext](./sub/other#header)`,
|
||||
`[without ext](./sub/other.md#no-such-header)`,
|
||||
));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 15), new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(otherUri, joinLines(
|
||||
`pre`,
|
||||
`# header`, // Definition should not be included since we triggered on a file link
|
||||
`post`,
|
||||
`# header`,
|
||||
`post`
|
||||
)),
|
||||
]));
|
||||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 15), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: docUri, line: 0 },
|
||||
{ uri: docUri, line: 1 },
|
||||
{ uri: docUri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not include refs from other file to own header', async () => {
|
||||
test('Should not include refs from other file to own header', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const otherUri = workspacePath('sub', 'other.md');
|
||||
|
||||
|
@ -322,7 +340,7 @@ suite('markdown: find all references', () => {
|
|||
`[other](./sub/other)`, // trigger here
|
||||
));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 15), new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(otherUri, joinLines(
|
||||
`# header`, // Definition should not be included since we triggered on a file link
|
||||
|
@ -330,28 +348,30 @@ suite('markdown: find all references', () => {
|
|||
)),
|
||||
]));
|
||||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 15), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: docUri, line: 0 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find explicit references to own file ', async () => {
|
||||
test('Should find explicit references to own file ', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[bare](doc.md)`, // trigger here
|
||||
`[rel](./doc.md)`,
|
||||
`[abs](/doc.md)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 12), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 12), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri, line: 0 },
|
||||
{ uri, line: 1 },
|
||||
{ uri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should support finding references to http uri', async () => {
|
||||
test('Should support finding references to http uri', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[1](http://example.com)`,
|
||||
|
@ -359,16 +379,17 @@ suite('markdown: find all references', () => {
|
|||
`[2](http://example.com)`,
|
||||
`[3]: http://example.com`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 13), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri, line: 0 },
|
||||
{ uri, line: 2 },
|
||||
{ uri, line: 3 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should consider authority, scheme and paths when finding references to http uri', async () => {
|
||||
test('Should consider authority, scheme and paths when finding references to http uri', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[1](http://example.com/cat)`,
|
||||
|
@ -379,50 +400,53 @@ suite('markdown: find all references', () => {
|
|||
`[6](http://other.com/cat)`,
|
||||
`[7](https://example.com/cat)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 13), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri, line: 0 },
|
||||
{ uri, line: 4 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should support finding references to http uri across files', async () => {
|
||||
test('Should support finding references to http uri across files', withStore(async (store) => {
|
||||
const uri1 = workspacePath('doc.md');
|
||||
const uri2 = workspacePath('doc2.md');
|
||||
const doc = new InMemoryDocument(uri1, joinLines(
|
||||
`[1](http://example.com)`,
|
||||
`[3]: http://example.com`,
|
||||
));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(uri2, joinLines(
|
||||
`[other](http://example.com)`,
|
||||
`[other](http://example.com)`
|
||||
))
|
||||
]));
|
||||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 13), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: uri1, line: 0 },
|
||||
{ uri: uri1, line: 1 },
|
||||
{ uri: uri2, line: 0 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should support finding references to autolinked http links', async () => {
|
||||
test('Should support finding references to autolinked http links', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[1](http://example.com)`,
|
||||
`<http://example.com>`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 13), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri, line: 0 },
|
||||
{ uri, line: 1 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should distinguish between references to file and to header within file', async () => {
|
||||
test('Should distinguish between references to file and to header within file', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const other1Uri = workspacePath('sub', 'other.md');
|
||||
|
||||
|
@ -435,13 +459,14 @@ suite('markdown: find all references', () => {
|
|||
`[link](/doc.md#abc)`,
|
||||
`[link no text](/doc#abc)`,
|
||||
));
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc,
|
||||
otherDoc,
|
||||
]);
|
||||
]));
|
||||
|
||||
{
|
||||
// Check refs to header fragment
|
||||
const headerRefs = await getReferences(otherDoc, new vscode.Position(0, 16), workspace);
|
||||
const headerRefs = await getReferences(store, otherDoc, new vscode.Position(0, 16), workspace);
|
||||
assertReferencesEqual(headerRefs!,
|
||||
{ uri: docUri, line: 0 }, // Header definition
|
||||
{ uri: docUri, line: 2 },
|
||||
|
@ -451,7 +476,7 @@ suite('markdown: find all references', () => {
|
|||
}
|
||||
{
|
||||
// Check refs to file itself from link with ext
|
||||
const fileRefs = await getReferences(otherDoc, new vscode.Position(0, 9), workspace);
|
||||
const fileRefs = await getReferences(store, otherDoc, new vscode.Position(0, 9), workspace);
|
||||
assertReferencesEqual(fileRefs!,
|
||||
{ uri: other1Uri, line: 0, endCharacter: 14 },
|
||||
{ uri: other1Uri, line: 1, endCharacter: 19 },
|
||||
|
@ -459,15 +484,15 @@ suite('markdown: find all references', () => {
|
|||
}
|
||||
{
|
||||
// Check refs to file itself from link without ext
|
||||
const fileRefs = await getReferences(otherDoc, new vscode.Position(1, 17), workspace);
|
||||
const fileRefs = await getReferences(store, otherDoc, new vscode.Position(1, 17), workspace);
|
||||
assertReferencesEqual(fileRefs!,
|
||||
{ uri: other1Uri, line: 0 },
|
||||
{ uri: other1Uri, line: 1 },
|
||||
);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should support finding references to unknown file', async () => {
|
||||
test('Should support finding references to unknown file', withStore(async (store) => {
|
||||
const uri1 = workspacePath('doc1.md');
|
||||
const doc1 = new InMemoryDocument(uri1, joinLines(
|
||||
`![img](/images/more/image.png)`,
|
||||
|
@ -480,17 +505,18 @@ suite('markdown: find all references', () => {
|
|||
`![img](/images/more/image.png)`,
|
||||
));
|
||||
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1, doc2]));
|
||||
|
||||
const refs = await getReferences(doc1, new vscode.Position(0, 10), new InMemoryMdWorkspace([doc1, doc2]));
|
||||
const refs = await getReferences(store, doc1, new vscode.Position(0, 10), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: uri1, line: 0 },
|
||||
{ uri: uri1, line: 2 },
|
||||
{ uri: uri2, line: 0 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
suite('Reference links', () => {
|
||||
test('Should find reference links within file from link', async () => {
|
||||
test('Should find reference links within file from link', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(docUri, joinLines(
|
||||
`[link 1][abc]`, // trigger here
|
||||
|
@ -498,14 +524,16 @@ suite('markdown: find all references', () => {
|
|||
`[abc]: https://example.com`,
|
||||
));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 12), new InMemoryMdWorkspace([doc]));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 12), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: docUri, line: 0 },
|
||||
{ uri: docUri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find reference links using shorthand', async () => {
|
||||
test('Should find reference links using shorthand', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(docUri, joinLines(
|
||||
`[ref]`, // trigger 1
|
||||
|
@ -514,9 +542,10 @@ suite('markdown: find all references', () => {
|
|||
``,
|
||||
`[ref]: /Hello.md` // trigger 3
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
{
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 2), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 2), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: docUri, line: 0 },
|
||||
{ uri: docUri, line: 2 },
|
||||
|
@ -524,7 +553,7 @@ suite('markdown: find all references', () => {
|
|||
);
|
||||
}
|
||||
{
|
||||
const refs = await getReferences(doc, new vscode.Position(2, 7), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(2, 7), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: docUri, line: 0 },
|
||||
{ uri: docUri, line: 2 },
|
||||
|
@ -532,31 +561,32 @@ suite('markdown: find all references', () => {
|
|||
);
|
||||
}
|
||||
{
|
||||
const refs = await getReferences(doc, new vscode.Position(4, 2), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(4, 2), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: docUri, line: 0 },
|
||||
{ uri: docUri, line: 2 },
|
||||
{ uri: docUri, line: 4 },
|
||||
);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should find reference links within file from definition', async () => {
|
||||
test('Should find reference links within file from definition', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(docUri, joinLines(
|
||||
`[link 1][abc]`,
|
||||
``,
|
||||
`[abc]: https://example.com`, // trigger here
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(2, 3), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(2, 3), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: docUri, line: 0 },
|
||||
{ uri: docUri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not find reference links across files', async () => {
|
||||
test('Should not find reference links across files', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(docUri, joinLines(
|
||||
`[link 1][abc]`,
|
||||
|
@ -564,21 +594,23 @@ suite('markdown: find all references', () => {
|
|||
`[abc]: https://example.com`,
|
||||
));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 12), new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(workspacePath('other.md'), joinLines(
|
||||
`[link 1][abc]`,
|
||||
``,
|
||||
`[abc]: https://example.com?bad`,
|
||||
`[abc]: https://example.com?bad`
|
||||
))
|
||||
]));
|
||||
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 12), workspace);
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: docUri, line: 0 },
|
||||
{ uri: docUri, line: 2 },
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should not consider checkboxes as reference links', async () => {
|
||||
test('Should not consider checkboxes as reference links', withStore(async (store) => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(docUri, joinLines(
|
||||
`- [x]`,
|
||||
|
@ -587,9 +619,10 @@ suite('markdown: find all references', () => {
|
|||
``,
|
||||
`[x]: https://example.com`
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 4), new InMemoryMdWorkspace([doc]));
|
||||
const refs = await getReferences(store, doc, new vscode.Position(0, 4), workspace);
|
||||
assert.strictEqual(refs?.length!, 0);
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,31 +11,34 @@ import { MdVsCodeRenameProvider, MdWorkspaceEdit } from '../languageFeatures/ren
|
|||
import { githubSlugifier } from '../slugify';
|
||||
import { MdTableOfContentsProvider } from '../tableOfContents';
|
||||
import { noopToken } from '../util/cancellation';
|
||||
import { DisposableStore } from '../util/dispose';
|
||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
||||
import { IMdWorkspace } from '../workspace';
|
||||
import { createNewMarkdownEngine } from './engine';
|
||||
import { InMemoryMdWorkspace } from './inMemoryWorkspace';
|
||||
import { nulLogger } from './nulLogging';
|
||||
import { assertRangeEqual, joinLines, workspacePath } from './util';
|
||||
import { assertRangeEqual, joinLines, withStore, workspacePath } from './util';
|
||||
|
||||
|
||||
/**
|
||||
* Get prepare rename info.
|
||||
*/
|
||||
function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspace: IMdWorkspace): Promise<undefined | { readonly range: vscode.Range; readonly placeholder: string }> {
|
||||
function prepareRename(store: DisposableStore, doc: InMemoryDocument, pos: vscode.Position, workspace: IMdWorkspace): Promise<undefined | { readonly range: vscode.Range; readonly placeholder: string }> {
|
||||
const engine = createNewMarkdownEngine();
|
||||
const referenceComputer = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace, nulLogger), nulLogger);
|
||||
const renameProvider = new MdVsCodeRenameProvider(workspace, referenceComputer, githubSlugifier);
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const referenceComputer = store.add(new MdReferencesProvider(engine, workspace, tocProvider, nulLogger));
|
||||
const renameProvider = store.add(new MdVsCodeRenameProvider(workspace, referenceComputer, githubSlugifier));
|
||||
return renameProvider.prepareRename(doc, pos, noopToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the edits for the rename.
|
||||
*/
|
||||
function getRenameEdits(doc: InMemoryDocument, pos: vscode.Position, newName: string, workspace: IMdWorkspace): Promise<MdWorkspaceEdit | undefined> {
|
||||
function getRenameEdits(store: DisposableStore, doc: InMemoryDocument, pos: vscode.Position, newName: string, workspace: IMdWorkspace): Promise<MdWorkspaceEdit | undefined> {
|
||||
const engine = createNewMarkdownEngine();
|
||||
const referencesProvider = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace, nulLogger), nulLogger);
|
||||
const renameProvider = new MdVsCodeRenameProvider(workspace, referencesProvider, githubSlugifier);
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const referencesProvider = store.add(new MdReferencesProvider(engine, workspace, tocProvider, nulLogger));
|
||||
const renameProvider = store.add(new MdVsCodeRenameProvider(workspace, referencesProvider, githubSlugifier));
|
||||
return renameProvider.provideRenameEditsImpl(doc, pos, newName, noopToken);
|
||||
}
|
||||
|
||||
|
@ -91,73 +94,78 @@ suite('markdown: rename', () => {
|
|||
await vscode.extensions.getExtension('vscode.markdown-language-features')!.activate();
|
||||
});
|
||||
|
||||
test('Rename on header should not include leading #', async () => {
|
||||
test('Rename on header should not include leading #', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`# abc`
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const info = await prepareRename(doc, new vscode.Position(0, 0), new InMemoryMdWorkspace([doc]));
|
||||
const info = await prepareRename(store, doc, new vscode.Position(0, 0), workspace);
|
||||
assertRangeEqual(info!.range, new vscode.Range(0, 2, 0, 5));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(0, 0), "New Header", new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(0, 0), "New Header", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(0, 2, 0, 5), 'New Header')
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on header should include leading or trailing #s', async () => {
|
||||
test('Rename on header should include leading or trailing #s', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`### abc ###`
|
||||
));
|
||||
|
||||
const info = await prepareRename(doc, new vscode.Position(0, 0), new InMemoryMdWorkspace([doc]));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const info = await prepareRename(store, doc, new vscode.Position(0, 0), workspace);
|
||||
assertRangeEqual(info!.range, new vscode.Range(0, 4, 0, 7));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(0, 0), "New Header", new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(0, 0), "New Header", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(0, 4, 0, 7), 'New Header')
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on header should pick up links in doc', async () => {
|
||||
test('Rename on header should pick up links in doc', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`### A b C`, // rename here
|
||||
`[text](#a-b-c)`,
|
||||
));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(0, 0), "New Header", new InMemoryMdWorkspace([doc]));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(0, 0), "New Header", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(0, 4, 0, 9), 'New Header'),
|
||||
new vscode.TextEdit(new vscode.Range(1, 8, 1, 13), 'new-header'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on link should use slug for link', async () => {
|
||||
test('Rename on link should use slug for link', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`### A b C`,
|
||||
`[text](#a-b-c)`, // rename here
|
||||
));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(1, 10), "New Header", new InMemoryMdWorkspace([doc]));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(1, 10), "New Header", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(0, 4, 0, 9), 'New Header'),
|
||||
new vscode.TextEdit(new vscode.Range(1, 8, 1, 13), 'new-header'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on link definition should work', async () => {
|
||||
test('Rename on link definition should work', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`### A b C`,
|
||||
|
@ -165,7 +173,8 @@ suite('markdown: rename', () => {
|
|||
`[ref]: #a-b-c`// rename here
|
||||
));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(2, 10), "New Header", new InMemoryMdWorkspace([doc]));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(2, 10), "New Header", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(0, 4, 0, 9), 'New Header'),
|
||||
|
@ -173,9 +182,9 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(2, 8, 2, 13), 'new-header'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on header should pick up links across files', async () => {
|
||||
test('Rename on header should pick up links across files', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const otherUri = workspacePath('other.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
|
@ -183,7 +192,7 @@ suite('markdown: rename', () => {
|
|||
`[text](#a-b-c)`,
|
||||
));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(0, 0), "New Header", new InMemoryMdWorkspace([
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(0, 0), "New Header", new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(otherUri, joinLines(
|
||||
`[text](#a-b-c)`, // Should not find this
|
||||
|
@ -202,9 +211,9 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(2, 13, 2, 18), 'new-header'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on link should pick up links across files', async () => {
|
||||
test('Rename on link should pick up links across files', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const otherUri = workspacePath('other.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
|
@ -212,7 +221,7 @@ suite('markdown: rename', () => {
|
|||
`[text](#a-b-c)`, // rename here
|
||||
));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(1, 10), "New Header", new InMemoryMdWorkspace([
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(1, 10), "New Header", new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(otherUri, joinLines(
|
||||
`[text](#a-b-c)`, // Should not find this
|
||||
|
@ -231,9 +240,9 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(2, 13, 2, 18), 'new-header'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on link in other file should pick up all refs', async () => {
|
||||
test('Rename on link in other file should pick up all refs', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const otherUri = workspacePath('other.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
|
@ -263,7 +272,7 @@ suite('markdown: rename', () => {
|
|||
|
||||
{
|
||||
// Rename on header with file extension
|
||||
const edit = await getRenameEdits(otherDoc, new vscode.Position(1, 17), "New Header", new InMemoryMdWorkspace([
|
||||
const edit = await getRenameEdits(store, otherDoc, new vscode.Position(1, 17), "New Header", new InMemoryMdWorkspace([
|
||||
doc,
|
||||
otherDoc
|
||||
]));
|
||||
|
@ -271,15 +280,15 @@ suite('markdown: rename', () => {
|
|||
}
|
||||
{
|
||||
// Rename on header without extension
|
||||
const edit = await getRenameEdits(otherDoc, new vscode.Position(2, 15), "New Header", new InMemoryMdWorkspace([
|
||||
const edit = await getRenameEdits(store, otherDoc, new vscode.Position(2, 15), "New Header", new InMemoryMdWorkspace([
|
||||
doc,
|
||||
otherDoc
|
||||
]));
|
||||
assertEditsEqual(edit!, ...expectedEdits);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on reference should rename references and definition', async () => {
|
||||
test('Rename on reference should rename references and definition', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[text][ref]`, // rename here
|
||||
|
@ -288,7 +297,8 @@ suite('markdown: rename', () => {
|
|||
`[ref]: https://example.com`,
|
||||
));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(0, 8), "new ref", new InMemoryMdWorkspace([doc]));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(0, 8), "new ref", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(0, 7, 0, 10), 'new ref'),
|
||||
|
@ -296,9 +306,9 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(3, 1, 3, 4), 'new ref'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on definition should rename references and definitions', async () => {
|
||||
test('Rename on definition should rename references and definitions', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[text][ref]`,
|
||||
|
@ -307,7 +317,8 @@ suite('markdown: rename', () => {
|
|||
`[ref]: https://example.com`, // rename here
|
||||
));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(3, 3), "new ref", new InMemoryMdWorkspace([doc]));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(3, 3), "new ref", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(0, 7, 0, 10), 'new ref'),
|
||||
|
@ -315,9 +326,9 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(3, 1, 3, 4), 'new ref'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on definition entry should rename header and references', async () => {
|
||||
test('Rename on definition entry should rename header and references', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`# a B c`,
|
||||
|
@ -325,12 +336,13 @@ suite('markdown: rename', () => {
|
|||
`[direct](#a-b-c)`,
|
||||
`[ref]: #a-b-c`, // rename here
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const preparedInfo = await prepareRename(doc, new vscode.Position(3, 10), new InMemoryMdWorkspace([doc]));
|
||||
const preparedInfo = await prepareRename(store, doc, new vscode.Position(3, 10), workspace);
|
||||
assert.strictEqual(preparedInfo!.placeholder, 'a B c');
|
||||
assertRangeEqual(preparedInfo!.range, new vscode.Range(3, 8, 3, 13));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(3, 10), "x Y z", new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(3, 10), "x Y z", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(0, 2, 0, 7), 'x Y z'),
|
||||
|
@ -338,50 +350,54 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(3, 8, 3, 13), 'x-y-z'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename should not be supported on link text', async () => {
|
||||
test('Rename should not be supported on link text', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`# Header`,
|
||||
`[text](#header)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
await assert.rejects(prepareRename(doc, new vscode.Position(1, 2), new InMemoryMdWorkspace([doc])));
|
||||
});
|
||||
await assert.rejects(prepareRename(store, doc, new vscode.Position(1, 2), workspace));
|
||||
}));
|
||||
|
||||
test('Path rename should use file path as range', async () => {
|
||||
test('Path rename should use file path as range', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[text](./doc.md)`,
|
||||
`[ref]: ./doc.md`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const info = await prepareRename(doc, new vscode.Position(0, 10), new InMemoryMdWorkspace([doc]));
|
||||
const info = await prepareRename(store, doc, new vscode.Position(0, 10), workspace);
|
||||
assert.strictEqual(info!.placeholder, './doc.md');
|
||||
assertRangeEqual(info!.range, new vscode.Range(0, 7, 0, 15));
|
||||
});
|
||||
}));
|
||||
|
||||
test('Path rename\'s range should excludes fragment', async () => {
|
||||
test('Path rename\'s range should excludes fragment', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[text](./doc.md#some-header)`,
|
||||
`[ref]: ./doc.md#some-header`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const info = await prepareRename(doc, new vscode.Position(0, 10), new InMemoryMdWorkspace([doc]));
|
||||
const info = await prepareRename(store, doc, new vscode.Position(0, 10), workspace);
|
||||
assert.strictEqual(info!.placeholder, './doc.md');
|
||||
assertRangeEqual(info!.range, new vscode.Range(0, 7, 0, 15));
|
||||
});
|
||||
}));
|
||||
|
||||
test('Path rename should update file and all refs', async () => {
|
||||
test('Path rename should update file and all refs', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[text](./doc.md)`,
|
||||
`[ref]: ./doc.md`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(0, 10), './sub/newDoc.md', new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(0, 10), './sub/newDoc.md', workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
originalUri: uri,
|
||||
newUri: workspacePath('sub', 'newDoc.md'),
|
||||
|
@ -391,16 +407,17 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(1, 7, 1, 15), './sub/newDoc.md'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Path rename using absolute file path should anchor to workspace root', async () => {
|
||||
test('Path rename using absolute file path should anchor to workspace root', withStore(async (store) => {
|
||||
const uri = workspacePath('sub', 'doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[text](/sub/doc.md)`,
|
||||
`[ref]: /sub/doc.md`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(0, 10), '/newSub/newDoc.md', new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(0, 10), '/newSub/newDoc.md', workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
originalUri: uri,
|
||||
newUri: workspacePath('newSub', 'newDoc.md'),
|
||||
|
@ -410,26 +427,28 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(1, 7, 1, 18), '/newSub/newDoc.md'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Path rename should use un-encoded paths as placeholder', async () => {
|
||||
test('Path rename should use un-encoded paths as placeholder', withStore(async (store) => {
|
||||
const uri = workspacePath('sub', 'doc with spaces.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[text](/sub/doc%20with%20spaces.md)`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const info = await prepareRename(doc, new vscode.Position(0, 10), new InMemoryMdWorkspace([doc]));
|
||||
const info = await prepareRename(store, doc, new vscode.Position(0, 10), workspace);
|
||||
assert.strictEqual(info!.placeholder, '/sub/doc with spaces.md');
|
||||
});
|
||||
}));
|
||||
|
||||
test('Path rename should encode paths', async () => {
|
||||
test('Path rename should encode paths', withStore(async (store) => {
|
||||
const uri = workspacePath('sub', 'doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[text](/sub/doc.md)`,
|
||||
`[ref]: /sub/doc.md`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(0, 10), '/NEW sub/new DOC.md', new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(0, 10), '/NEW sub/new DOC.md', workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
originalUri: uri,
|
||||
newUri: workspacePath('NEW sub', 'new DOC.md'),
|
||||
|
@ -439,9 +458,9 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(1, 7, 1, 18), '/NEW%20sub/new%20DOC.md'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Path rename should work with unknown files', async () => {
|
||||
test('Path rename should work with unknown files', withStore(async (store) => {
|
||||
const uri1 = workspacePath('doc1.md');
|
||||
const doc1 = new InMemoryDocument(uri1, joinLines(
|
||||
`![img](/images/more/image.png)`,
|
||||
|
@ -454,10 +473,12 @@ suite('markdown: rename', () => {
|
|||
`![img](/images/more/image.png)`,
|
||||
));
|
||||
|
||||
const edit = await getRenameEdits(doc1, new vscode.Position(0, 10), '/img/test/new.png', new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc1,
|
||||
doc2
|
||||
]));
|
||||
|
||||
const edit = await getRenameEdits(store, doc1, new vscode.Position(0, 10), '/img/test/new.png', workspace);
|
||||
assertEditsEqual(edit!,
|
||||
// Should not have file edits since the files don't exist here
|
||||
{
|
||||
|
@ -471,16 +492,17 @@ suite('markdown: rename', () => {
|
|||
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 () => {
|
||||
test('Path rename should use .md extension on extension-less link', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[text](/doc#header)`,
|
||||
`[ref]: /doc#other`,
|
||||
));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(0, 10), '/new File', new InMemoryMdWorkspace([doc]));
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(0, 10), '/new File', workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
originalUri: uri,
|
||||
newUri: workspacePath('new File.md'), // Rename on disk should use file extension
|
||||
|
@ -490,10 +512,10 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(1, 7, 1, 11), '/new%20File'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
// TODO: fails on windows
|
||||
test.skip('Path rename should use correctly resolved paths across files', async () => {
|
||||
test.skip('Path rename should use correctly resolved paths across files', withStore(async (store) => {
|
||||
const uri1 = workspacePath('sub', 'doc.md');
|
||||
const doc1 = new InMemoryDocument(uri1, joinLines(
|
||||
`[text](./doc.md)`,
|
||||
|
@ -518,9 +540,11 @@ suite('markdown: rename', () => {
|
|||
`[ref]: /sub/doc.md`,
|
||||
));
|
||||
|
||||
const edit = await getRenameEdits(doc1, new vscode.Position(0, 10), './new/new-doc.md', new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc1, doc2, doc3, doc4,
|
||||
]));
|
||||
|
||||
const edit = await getRenameEdits(store, doc1, new vscode.Position(0, 10), './new/new-doc.md', workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
originalUri: uri1,
|
||||
newUri: workspacePath('sub', 'new', 'new-doc.md'),
|
||||
|
@ -545,9 +569,9 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(1, 7, 1, 18), '/sub/new/new-doc.md'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Path rename should resolve on links without prefix', async () => {
|
||||
test('Path rename should resolve on links without prefix', withStore(async (store) => {
|
||||
const uri1 = workspacePath('sub', 'doc.md');
|
||||
const doc1 = new InMemoryDocument(uri1, joinLines(
|
||||
`![text](sub2/doc3.md)`,
|
||||
|
@ -561,9 +585,11 @@ suite('markdown: rename', () => {
|
|||
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 InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc1, doc2, doc3
|
||||
]));
|
||||
|
||||
const edit = await getRenameEdits(store, doc1, new vscode.Position(0, 10), 'sub2/cat.md', workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
originalUri: workspacePath('sub', 'sub2', 'doc3.md'),
|
||||
newUri: workspacePath('sub', 'sub2', 'cat.md'),
|
||||
|
@ -572,21 +598,22 @@ suite('markdown: rename', () => {
|
|||
}, {
|
||||
uri: uri2, edits: [new vscode.TextEdit(new vscode.Range(0, 8, 0, 24), 'sub/sub2/cat.md')]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on link should use header text as placeholder', async () => {
|
||||
test('Rename on link should use header text as placeholder', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`### a B c ###`,
|
||||
`[text](#a-b-c)`,
|
||||
));
|
||||
|
||||
const info = await prepareRename(doc, new vscode.Position(1, 10), new InMemoryMdWorkspace([doc]));
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
const info = await prepareRename(store, doc, new vscode.Position(1, 10), workspace);
|
||||
assert.strictEqual(info!.placeholder, 'a B c');
|
||||
assertRangeEqual(info!.range, new vscode.Range(1, 8, 1, 13));
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on http uri should work', async () => {
|
||||
test('Rename on http uri should work', withStore(async (store) => {
|
||||
const uri1 = workspacePath('doc.md');
|
||||
const uri2 = workspacePath('doc2.md');
|
||||
const doc = new InMemoryDocument(uri1, joinLines(
|
||||
|
@ -595,12 +622,14 @@ suite('markdown: rename', () => {
|
|||
`<http://example.com>`,
|
||||
));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(1, 10), "https://example.com/sub", new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
doc,
|
||||
new InMemoryDocument(uri2, joinLines(
|
||||
`[4](http://example.com)`,
|
||||
`[4](http://example.com)`
|
||||
))
|
||||
]));
|
||||
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(1, 10), "https://example.com/sub", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri: uri1, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(0, 4, 0, 22), 'https://example.com/sub'),
|
||||
|
@ -612,9 +641,9 @@ suite('markdown: rename', () => {
|
|||
new vscode.TextEdit(new vscode.Range(0, 4, 0, 22), 'https://example.com/sub'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on definition path should update all references to path', async () => {
|
||||
test('Rename on definition path should update all references to path', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[ref text][ref]`,
|
||||
|
@ -622,22 +651,22 @@ suite('markdown: rename', () => {
|
|||
`[ref]: /file`, // rename here
|
||||
));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc]);
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const preparedInfo = await prepareRename(doc, new vscode.Position(2, 10), workspace);
|
||||
const preparedInfo = await prepareRename(store, doc, new vscode.Position(2, 10), workspace);
|
||||
assert.strictEqual(preparedInfo!.placeholder, '/file');
|
||||
assertRangeEqual(preparedInfo!.range, new vscode.Range(2, 7, 2, 12));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(2, 10), "/newFile", workspace);
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(2, 10), "/newFile", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(1, 9, 1, 14), '/newFile'),
|
||||
new vscode.TextEdit(new vscode.Range(2, 7, 2, 12), '/newFile'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on definition path where file exists should also update file', async () => {
|
||||
test('Rename on definition path where file exists should also update file', withStore(async (store) => {
|
||||
const uri1 = workspacePath('doc.md');
|
||||
const doc1 = new InMemoryDocument(uri1, joinLines(
|
||||
`[ref text][ref]`,
|
||||
|
@ -648,13 +677,13 @@ suite('markdown: rename', () => {
|
|||
const uri2 = workspacePath('doc2.md');
|
||||
const doc2 = new InMemoryDocument(uri2, joinLines());
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc1, doc2]);
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc1, doc2]));
|
||||
|
||||
const preparedInfo = await prepareRename(doc1, new vscode.Position(2, 10), workspace);
|
||||
const preparedInfo = await prepareRename(store, doc1, new vscode.Position(2, 10), workspace);
|
||||
assert.strictEqual(preparedInfo!.placeholder, '/doc2');
|
||||
assertRangeEqual(preparedInfo!.range, new vscode.Range(2, 7, 2, 12));
|
||||
|
||||
const edit = await getRenameEdits(doc1, new vscode.Position(2, 10), "/new-doc", workspace);
|
||||
const edit = await getRenameEdits(store, doc1, new vscode.Position(2, 10), "/new-doc", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri: uri1, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(1, 9, 1, 14), '/new-doc'),
|
||||
|
@ -664,9 +693,9 @@ suite('markdown: rename', () => {
|
|||
originalUri: uri2,
|
||||
newUri: workspacePath('new-doc.md')
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
test('Rename on definition path header should update all references to header', async () => {
|
||||
test('Rename on definition path header should update all references to header', withStore(async (store) => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[ref text][ref]`,
|
||||
|
@ -674,18 +703,18 @@ suite('markdown: rename', () => {
|
|||
`[ref]: /file#header`, // rename here
|
||||
));
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([doc]);
|
||||
const workspace = store.add(new InMemoryMdWorkspace([doc]));
|
||||
|
||||
const preparedInfo = await prepareRename(doc, new vscode.Position(2, 16), workspace);
|
||||
const preparedInfo = await prepareRename(store, doc, new vscode.Position(2, 16), workspace);
|
||||
assert.strictEqual(preparedInfo!.placeholder, 'header');
|
||||
assertRangeEqual(preparedInfo!.range, new vscode.Range(2, 13, 2, 19));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(2, 16), "New Header", workspace);
|
||||
const edit = await getRenameEdits(store, doc, new vscode.Position(2, 16), "New Header", workspace);
|
||||
assertEditsEqual(edit!, {
|
||||
uri, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(1, 15, 1, 21), 'new-header'),
|
||||
new vscode.TextEdit(new vscode.Range(2, 13, 2, 19), 'new-header'),
|
||||
]
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import * as assert from 'assert';
|
||||
import * as os from 'os';
|
||||
import * as vscode from 'vscode';
|
||||
import { DisposableStore } from '../util/dispose';
|
||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
||||
|
||||
export const joinLines = (...args: string[]) =>
|
||||
|
@ -37,3 +38,14 @@ export function assertRangeEqual(expected: vscode.Range, actual: vscode.Range, m
|
|||
assert.strictEqual(expected.end.line, actual.end.line, message);
|
||||
assert.strictEqual(expected.end.character, actual.end.character, message);
|
||||
}
|
||||
|
||||
export function withStore<R>(fn: (this: Mocha.Context, store: DisposableStore) => Promise<R>) {
|
||||
return async function (this: Mocha.Context): Promise<R> {
|
||||
const store = new DisposableStore();
|
||||
try {
|
||||
return await fn.call(this, store);
|
||||
} finally {
|
||||
store.dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,37 +10,40 @@ import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbols';
|
|||
import { MdWorkspaceSymbolProvider } from '../languageFeatures/workspaceSymbols';
|
||||
import { MdTableOfContentsProvider } from '../tableOfContents';
|
||||
import { ITextDocument } from '../types/textDocument';
|
||||
import { DisposableStore } from '../util/dispose';
|
||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
||||
import { IMdWorkspace } from '../workspace';
|
||||
import { createNewMarkdownEngine } from './engine';
|
||||
import { InMemoryMdWorkspace } from './inMemoryWorkspace';
|
||||
import { nulLogger } from './nulLogging';
|
||||
import { workspacePath } from './util';
|
||||
import { withStore, workspacePath } from './util';
|
||||
|
||||
function getWorkspaceSymbols(workspace: IMdWorkspace, query = ''): Promise<vscode.SymbolInformation[]> {
|
||||
function getWorkspaceSymbols(store: DisposableStore, workspace: IMdWorkspace, query = ''): Promise<vscode.SymbolInformation[]> {
|
||||
const engine = createNewMarkdownEngine();
|
||||
const symbolProvider = new MdDocumentSymbolProvider(new MdTableOfContentsProvider(engine, workspace, nulLogger), nulLogger);
|
||||
return new MdWorkspaceSymbolProvider(symbolProvider, workspace).provideWorkspaceSymbols(query);
|
||||
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
|
||||
const symbolProvider = new MdDocumentSymbolProvider(tocProvider, nulLogger);
|
||||
const workspaceSymbolProvider = store.add(new MdWorkspaceSymbolProvider(symbolProvider, workspace));
|
||||
return workspaceSymbolProvider.provideWorkspaceSymbols(query);
|
||||
}
|
||||
|
||||
suite('markdown.WorkspaceSymbolProvider', () => {
|
||||
test('Should not return anything for empty workspace', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([]);
|
||||
assert.deepStrictEqual(await getWorkspaceSymbols(workspace, ''), []);
|
||||
});
|
||||
test('Should not return anything for empty workspace', withStore(async (store) => {
|
||||
const workspace = store.add(new InMemoryMdWorkspace([]));
|
||||
assert.deepStrictEqual(await getWorkspaceSymbols(store, workspace, ''), []);
|
||||
}));
|
||||
|
||||
test('Should return symbols from workspace with one markdown file', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
test('Should return symbols from workspace with one markdown file', withStore(async (store) => {
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(workspacePath('test.md'), `# header1\nabc\n## header2`)
|
||||
]);
|
||||
]));
|
||||
|
||||
const symbols = await getWorkspaceSymbols(workspace, '');
|
||||
const symbols = await getWorkspaceSymbols(store, workspace, '');
|
||||
assert.strictEqual(symbols.length, 2);
|
||||
assert.strictEqual(symbols[0].name, '# header1');
|
||||
assert.strictEqual(symbols[1].name, '## header2');
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should return all content basic workspace', async () => {
|
||||
test('Should return all content basic workspace', withStore(async (store) => {
|
||||
const fileNameCount = 10;
|
||||
const files: ITextDocument[] = [];
|
||||
for (let i = 0; i < fileNameCount; ++i) {
|
||||
|
@ -48,55 +51,55 @@ suite('markdown.WorkspaceSymbolProvider', () => {
|
|||
files.push(new InMemoryDocument(testFileName, `# common\nabc\n## header${i}`));
|
||||
}
|
||||
|
||||
const workspace = new InMemoryMdWorkspace(files);
|
||||
const workspace = store.add(new InMemoryMdWorkspace(files));
|
||||
|
||||
const symbols = await getWorkspaceSymbols(workspace, '');
|
||||
const symbols = await getWorkspaceSymbols(store, workspace, '');
|
||||
assert.strictEqual(symbols.length, fileNameCount * 2);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should update results when markdown file changes symbols', async () => {
|
||||
test('Should update results when markdown file changes symbols', withStore(async (store) => {
|
||||
const testFileName = workspacePath('test.md');
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(testFileName, `# header1`, 1 /* version */)
|
||||
]);
|
||||
]));
|
||||
|
||||
assert.strictEqual((await getWorkspaceSymbols(workspace, '')).length, 1);
|
||||
assert.strictEqual((await getWorkspaceSymbols(store, workspace, '')).length, 1);
|
||||
|
||||
// Update file
|
||||
workspace.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`, 2 /* version */));
|
||||
const newSymbols = await getWorkspaceSymbols(workspace, '');
|
||||
const newSymbols = await getWorkspaceSymbols(store, workspace, '');
|
||||
assert.strictEqual(newSymbols.length, 2);
|
||||
assert.strictEqual(newSymbols[0].name, '# new header');
|
||||
assert.strictEqual(newSymbols[1].name, '## header2');
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should remove results when file is deleted', async () => {
|
||||
test('Should remove results when file is deleted', withStore(async (store) => {
|
||||
const testFileName = workspacePath('test.md');
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(testFileName, `# header1`)
|
||||
]);
|
||||
]));
|
||||
|
||||
assert.strictEqual((await getWorkspaceSymbols(workspace, '')).length, 1);
|
||||
assert.strictEqual((await getWorkspaceSymbols(store, workspace, '')).length, 1);
|
||||
|
||||
// delete file
|
||||
workspace.deleteDocument(testFileName);
|
||||
const newSymbols = await getWorkspaceSymbols(workspace, '');
|
||||
const newSymbols = await getWorkspaceSymbols(store, workspace, '');
|
||||
assert.strictEqual(newSymbols.length, 0);
|
||||
});
|
||||
}));
|
||||
|
||||
test('Should update results when markdown file is created', async () => {
|
||||
test('Should update results when markdown file is created', withStore(async (store) => {
|
||||
const testFileName = workspacePath('test.md');
|
||||
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
const workspace = store.add(new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(testFileName, `# header1`)
|
||||
]);
|
||||
]));
|
||||
|
||||
assert.strictEqual((await getWorkspaceSymbols(workspace, '')).length, 1);
|
||||
assert.strictEqual((await getWorkspaceSymbols(store, workspace, '')).length, 1);
|
||||
|
||||
// Create file
|
||||
workspace.createDocument(new InMemoryDocument(workspacePath('test2.md'), `# new header\nabc\n## header2`));
|
||||
const newSymbols = await getWorkspaceSymbols(workspace, '');
|
||||
const newSymbols = await getWorkspaceSymbols(store, workspace, '');
|
||||
assert.strictEqual(newSymbols.length, 3);
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -5,13 +5,36 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export function disposeAll(disposables: vscode.Disposable[]) {
|
||||
while (disposables.length) {
|
||||
const item = disposables.pop();
|
||||
item?.dispose();
|
||||
export class MultiDisposeError extends Error {
|
||||
constructor(
|
||||
public readonly errors: any[]
|
||||
) {
|
||||
super(`Encountered errors while disposing of store. Errors: [${errors.join(', ')}]`);
|
||||
}
|
||||
}
|
||||
|
||||
export function disposeAll(disposables: Iterable<vscode.Disposable>) {
|
||||
const errors: any[] = [];
|
||||
|
||||
for (const disposable of disposables) {
|
||||
try {
|
||||
disposable.dispose();
|
||||
} catch (e) {
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length === 1) {
|
||||
throw errors[0];
|
||||
} else if (errors.length > 1) {
|
||||
throw new MultiDisposeError(errors);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IDisposable {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export abstract class Disposable {
|
||||
private _isDisposed = false;
|
||||
|
||||
|
@ -25,7 +48,7 @@ export abstract class Disposable {
|
|||
disposeAll(this._disposables);
|
||||
}
|
||||
|
||||
protected _register<T extends vscode.Disposable>(value: T): T {
|
||||
protected _register<T extends IDisposable>(value: T): T {
|
||||
if (this._isDisposed) {
|
||||
value.dispose();
|
||||
} else {
|
||||
|
@ -38,3 +61,22 @@ export abstract class Disposable {
|
|||
return this._isDisposed;
|
||||
}
|
||||
}
|
||||
|
||||
export class DisposableStore extends Disposable {
|
||||
private readonly items = new Set<IDisposable>();
|
||||
|
||||
public override dispose() {
|
||||
super.dispose();
|
||||
disposeAll(this.items);
|
||||
this.items.clear();
|
||||
}
|
||||
|
||||
public add<T extends IDisposable>(item: T): T {
|
||||
if (this.isDisposed) {
|
||||
console.warn('Adding to disposed store. Item will be leaked');
|
||||
}
|
||||
|
||||
this.items.add(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue