Skip to content

Commit

Permalink
Improved wikilink definition resolution (fixes #499) (#502)
Browse files Browse the repository at this point in the history
* improved resolution for direct links and wikilink with definition

* if the definition of a wikilink points to a non-existing file, create a placeholder for the full path instead
* if a link doesn't point to a valid resource, create a placeholder for the full path instead

* commented out test-data.js import in dataviz.html

* moved test to more appropriate group
  • Loading branch information
riccardoferretti authored Feb 28, 2021
1 parent 69a5d82 commit a92ea7d
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 42 deletions.
9 changes: 6 additions & 3 deletions packages/foam-core/src/model/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,17 @@ export class FoamWorkspace implements IDisposable {
note: Note,
link: NoteLink
): URI {
let targetUri: URI | null = null;
let targetUri: URI | undefined;
switch (link.type) {
case 'wikilink':
const definitionUri = note.definitions.find(
def => def.label === link.slug
)?.url;
if (isSome(definitionUri)) {
targetUri = parseUri(note.uri, definitionUri!);
const definedUri = parseUri(note.uri, definitionUri);
targetUri =
FoamWorkspace.find(workspace, definedUri, note.uri)?.uri ??
placeholderUri(definedUri.path);
} else {
targetUri =
FoamWorkspace.find(workspace, link.slug, note.uri)?.uri ??
Expand All @@ -147,7 +150,7 @@ export class FoamWorkspace implements IDisposable {
case 'link':
targetUri =
FoamWorkspace.find(workspace, link.target, note.uri)?.uri ??
placeholderUri(link.target);
placeholderUri(parseUri(note.uri, link.target).path);
break;
}

Expand Down
165 changes: 127 additions & 38 deletions packages/foam-core/test/workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,22 @@ describe('Notes workspace', () => {
).toEqual(['/file.pdf', '/page-a.md', 'place-holder']);
});

it('Detects outbound wikilinks', () => {
it('Fails if getting non-existing note', () => {
const noteA = createTestNote({
uri: '/path/to/page-a.md',
});
const ws = new FoamWorkspace();
ws.set(noteA);

const uri = URI.file('/path/to/another/page-b.md');
expect(ws.exists(uri)).toBeFalsy();
expect(ws.find(uri)).toBeNull();
expect(() => ws.get(uri)).toThrow();
});
});

describe('Wikilinks', () => {
it('Can be defined with basename, relative path, absolute path, extension', () => {
const noteA = createTestNote({
uri: '/path/to/page-a.md',
links: [
Expand Down Expand Up @@ -91,7 +106,7 @@ describe('Notes workspace', () => {
]);
});

it('Detects inbound wikilinks', () => {
it('Creates inbound connections for target note', () => {
const noteA = createTestNote({
uri: '/path/to/page-a.md',
links: [{ slug: 'page-b' }],
Expand Down Expand Up @@ -126,38 +141,27 @@ describe('Notes workspace', () => {
).toEqual(['/path/another/page-c.md', '/somewhere/page-b.md']);
});

it('Detects markdown links', () => {
it('Uses wikilink definitions when available to resolve target', () => {
const ws = new FoamWorkspace();
const noteA = createTestNote({
uri: '/path/to/page-a.md',
links: [{ to: './another/page-b.md' }, { to: 'more/page-c.md' }],
uri: '/somewhere/from/page-a.md',
links: [{ slug: 'page-b' }],
});
const noteB = createTestNote({
uri: '/path/to/another/page-b.md',
links: [{ to: '../../to/page-a.md' }],
noteA.definitions.push({
label: 'page-b',
url: '../to/page-b.md',
});
const noteC = createTestNote({
uri: '/path/to/more/page-c.md',
const noteB = createTestNote({
uri: '/somewhere/to/page-b.md',
});
const ws = new FoamWorkspace();
ws.set(noteA)
.set(noteB)
.set(noteC)
.resolveLinks();

expect(
ws
.getLinks(noteA.uri)
.map(link => link.path)
.sort()
).toEqual(['/path/to/another/page-b.md', '/path/to/more/page-c.md']);

expect(ws.getLinks(noteB.uri)).toEqual([noteA.uri]);
expect(ws.getBacklinks(noteA.uri)).toEqual([noteB.uri]);
expect(ws.getConnections(noteA.uri)).toEqual([
{ source: noteA.uri, target: noteB.uri },
{ source: noteA.uri, target: noteC.uri },
{ source: noteB.uri, target: noteA.uri },
]);
expect(ws.getAllConnections()[0]).toEqual({
source: noteA.uri,
target: noteB.uri,
});
});

it('Resolves wikilink referencing more than one note', () => {
Expand Down Expand Up @@ -196,18 +200,6 @@ describe('Notes workspace', () => {
expect(ws.getLinks(noteA.uri)).toEqual([noteB2.uri, noteB3.uri]);
});

it('Fails if getting non-existing note', () => {
const noteA = createTestNote({
uri: '/path/to/page-a.md',
});
const ws = new FoamWorkspace();
ws.set(noteA);

const uri = URI.file('/path/to/another/page-b.md');
expect(ws.exists(uri)).toBeFalsy();
expect(ws.find(uri)).toBeNull();
expect(() => ws.get(uri)).toThrow();
});
it('Supports attachments', () => {
const noteA = createTestNote({
uri: '/path/to/page-a.md',
Expand Down Expand Up @@ -275,6 +267,103 @@ describe('Notes workspace', () => {
});
});

describe('markdown direct links', () => {
it('Support absolute and relative path', () => {
const noteA = createTestNote({
uri: '/path/to/page-a.md',
links: [{ to: './another/page-b.md' }, { to: 'more/page-c.md' }],
});
const noteB = createTestNote({
uri: '/path/to/another/page-b.md',
links: [{ to: '../../to/page-a.md' }],
});
const noteC = createTestNote({
uri: '/path/to/more/page-c.md',
});
const ws = new FoamWorkspace();
ws.set(noteA)
.set(noteB)
.set(noteC)
.resolveLinks();

expect(
ws
.getLinks(noteA.uri)
.map(link => link.path)
.sort()
).toEqual(['/path/to/another/page-b.md', '/path/to/more/page-c.md']);

expect(ws.getLinks(noteB.uri)).toEqual([noteA.uri]);
expect(ws.getBacklinks(noteA.uri)).toEqual([noteB.uri]);
expect(ws.getConnections(noteA.uri)).toEqual([
{ source: noteA.uri, target: noteB.uri },
{ source: noteA.uri, target: noteC.uri },
{ source: noteB.uri, target: noteA.uri },
]);
});
});

describe('Placeholders', () => {
it('Treats direct links to non-existing files as placeholders', () => {
const ws = new FoamWorkspace();
const noteA = createTestNote({
uri: '/somewhere/from/page-a.md',
links: [{ to: '../page-b.md' }, { to: '/path/to/page-c.md' }],
});
ws.set(noteA).resolveLinks();

expect(ws.getAllConnections()[0]).toEqual({
source: noteA.uri,
target: placeholderUri('/somewhere/page-b.md'),
});
expect(ws.getAllConnections()[1]).toEqual({
source: noteA.uri,
target: placeholderUri('/path/to/page-c.md'),
});
});

it('Treats wikilinks without matching file as placeholders', () => {
const ws = new FoamWorkspace();
const noteA = createTestNote({
uri: '/somewhere/page-a.md',
links: [{ slug: 'page-b' }],
});
ws.set(noteA).resolveLinks();

expect(ws.getAllConnections()[0]).toEqual({
source: noteA.uri,
target: placeholderUri('page-b'),
});
});
it('Treats wikilink with definition to non-existing file as placeholders', () => {
const ws = new FoamWorkspace();
const noteA = createTestNote({
uri: '/somewhere/page-a.md',
links: [{ slug: 'page-b' }, { slug: 'page-c' }],
});
noteA.definitions.push({
label: 'page-b',
url: './page-b.md',
});
noteA.definitions.push({
label: 'page-c',
url: '/path/to/page-c.md',
});
ws.set(noteA)
.set(createTestNote({ uri: '/different/location/for/note-b.md' }))
.resolveLinks();

expect(ws.getAllConnections()[0]).toEqual({
source: noteA.uri,
target: placeholderUri('/somewhere/page-b.md'),
});
expect(ws.getAllConnections()[1]).toEqual({
source: noteA.uri,
target: placeholderUri('/path/to/page-c.md'),
});
});
});

describe('Updating workspace happy path', () => {
it('Update links when modifying note', () => {
const noteA = createTestNote({
Expand Down
2 changes: 1 addition & 1 deletion packages/foam-vscode/static/dataviz.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
3. uncomment the next <script ...> line
4. open this file in a browser
-->
<script src="./test-data.js"></script>
<!-- <script src="./test-data.js"></script> -->
<script data-replace src="./graphs/default/graph.js"></script>
</body>
</html>

0 comments on commit a92ea7d

Please sign in to comment.