Compare commits

...

11 Commits

Author SHA1 Message Date
Riccardo Ferretti
23cf5a021e v0.18.3 2022-04-17 14:59:15 +01:00
Riccardo Ferretti
8231ed14c5 Prepare for 0.18.3 2022-04-17 14:58:16 +01:00
Riccardo Ferretti
3bea283c04 Better reporting when link parsing fails, and making it not fail the whole graph computation 2022-04-17 14:56:45 +01:00
Riccardo Ferretti
a3cffe8418 v0.18.2 2022-04-14 21:58:57 +01:00
Riccardo Ferretti
675e7fa216 Prepare 0.18.2 2022-04-14 21:58:24 +01:00
Riccardo Ferretti
87d12bf3af Use VS Code URI in backlink and tag explorer panels 2022-04-14 21:55:52 +01:00
allcontributors[bot]
e118ab74b5 docs: add josephdecock as a contributor for code (#984)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-04-14 22:45:21 +02:00
Joe DeCock
04a61eed0e Remove square brackets in preview on wikilinks with link definition (#979)
Remove references that are wiki links, they are not needed (because Foam will take care of the routing in the preview) and they cause the rendering of wiki links to be surrounded by square brackets.
2022-04-14 22:44:32 +02:00
allcontributors[bot]
350b3005f1 docs: add chrisUsick as a contributor for code (#983)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-04-14 22:33:25 +02:00
Riccardo
f7293b1eb4 Fix #974: restore proper handling of section-only wikilinks (#981) 2022-04-14 21:53:42 +02:00
Chris Usick
672eb6ed20 Support direct links without labels (#980)
Fixes #975
2022-04-14 17:21:36 +02:00
14 changed files with 158 additions and 54 deletions

View File

@@ -851,6 +851,24 @@
"contributions": [
"tool"
]
},
{
"login": "chrisUsick",
"name": "Chris Usick",
"avatar_url": "https://avatars.githubusercontent.com/u/6589365?v=4",
"profile": "http://cu-dev.ca",
"contributions": [
"code"
]
},
{
"login": "josephdecock",
"name": "Joe DeCock",
"avatar_url": "https://avatars.githubusercontent.com/u/1145533?v=4",
"profile": "https://github.com/josephdecock",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@@ -227,6 +227,8 @@ If that sounds like something you're interested in, I'd love to have you along o
</tr>
<tr>
<td align="center"><a href="http://Cliffordfajardo.com"><img src="https://avatars.githubusercontent.com/u/6743796?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Clifford Fajardo </b></sub></a><br /><a href="#tool-cliffordfajardo" title="Tools">🔧</a></td>
<td align="center"><a href="http://cu-dev.ca"><img src="https://avatars.githubusercontent.com/u/6589365?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Chris Usick</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chrisUsick" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/josephdecock"><img src="https://avatars.githubusercontent.com/u/1145533?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joe DeCock</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=josephdecock" title="Code">💻</a></td>
</tr>
</table>

View File

@@ -4,5 +4,5 @@
],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.18.1"
"version": "0.18.3"
}

View File

@@ -4,6 +4,19 @@ All notable changes to the "foam-vscode" extension will be documented in this fi
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [0.18.3] - 2022-04-17
Fixes and Improvements:
- Better reporting when links fail to resolve
- Failing link resolution during graph computation no longer fatal
## [0.18.2] - 2022-04-14
Fixes and Improvements:
- Fixed parsing error on empty direct links (#980 - thanks @chrisUsick)
- Improved rendering in preview of wikilinks that have link definitions (#979 - thanks @josephdecock)
- Restored handling of section-only wikilinks (#981)
## [0.18.1] - 2022-04-13
Fixes and Improvements:

View File

@@ -8,7 +8,7 @@
"type": "git"
},
"homepage": "https://github.com/foambubble/foam",
"version": "0.18.1",
"version": "0.18.3",
"license": "MIT",
"publisher": "foam",
"engines": {

View File

@@ -107,8 +107,18 @@ export class FoamGraph implements IDisposable {
for (const resource of this.workspace.resources()) {
for (const link of resource.links) {
const targetUri = this.workspace.resolveLink(resource, link);
this.connect(resource.uri, targetUri, link);
try {
const targetUri = this.workspace.resolveLink(resource, link);
this.connect(resource.uri, targetUri, link);
} catch (e) {
Logger.error(
`Error while resolving link ${
link.rawText
} in ${resource.uri.toFsPath()}, skipping.`,
link,
e
);
}
}
}

View File

@@ -12,8 +12,8 @@ describe('MarkdownLink', () => {
.links[0];
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toEqual('wikilink');
expect(parsed.section).toBeUndefined();
expect(parsed.alias).toBeUndefined();
expect(parsed.section).toEqual('');
expect(parsed.alias).toEqual('');
});
it('should parse target and section', () => {
const link = parser.parse(
@@ -23,14 +23,14 @@ describe('MarkdownLink', () => {
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toEqual('wikilink');
expect(parsed.section).toEqual('section');
expect(parsed.alias).toBeUndefined();
expect(parsed.alias).toEqual('');
});
it('should parse target and alias', () => {
const link = parser.parse(getRandomURI(), `this is a [[wikilink|alias]]`)
.links[0];
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toEqual('wikilink');
expect(parsed.section).toBeUndefined();
expect(parsed.section).toEqual('');
expect(parsed.alias).toEqual('alias');
});
it('should parse links with square brackets #975', () => {
@@ -40,8 +40,8 @@ describe('MarkdownLink', () => {
).links[0];
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toEqual('wikilink [with] brackets');
expect(parsed.section).toBeUndefined();
expect(parsed.alias).toBeUndefined();
expect(parsed.section).toEqual('');
expect(parsed.alias).toEqual('');
});
it('should parse links with square brackets in alias #975', () => {
const link = parser.parse(
@@ -50,7 +50,7 @@ describe('MarkdownLink', () => {
).links[0];
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toEqual('wikilink');
expect(parsed.section).toBeUndefined();
expect(parsed.section).toEqual('');
expect(parsed.alias).toEqual('alias [with] brackets');
});
it('should parse target and alias with escaped separator', () => {
@@ -60,7 +60,7 @@ describe('MarkdownLink', () => {
).links[0];
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toEqual('wikilink');
expect(parsed.section).toBeUndefined();
expect(parsed.section).toEqual('');
expect(parsed.alias).toEqual('alias');
});
it('should parse target section and alias', () => {
@@ -77,9 +77,9 @@ describe('MarkdownLink', () => {
const link = parser.parse(getRandomURI(), `this is a [[#section]]`)
.links[0];
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toBeUndefined();
expect(parsed.target).toEqual('');
expect(parsed.section).toEqual('section');
expect(parsed.alias).toBeUndefined();
expect(parsed.alias).toEqual('');
});
});
@@ -89,7 +89,7 @@ describe('MarkdownLink', () => {
.links[0];
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toEqual('to/path.md');
expect(parsed.section).toBeUndefined();
expect(parsed.section).toEqual('');
expect(parsed.alias).toEqual('link');
});
it('should parse target and section', () => {
@@ -109,7 +109,7 @@ describe('MarkdownLink', () => {
range: Range.create(0, 0),
};
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toBeUndefined();
expect(parsed.target).toEqual('');
expect(parsed.section).toEqual('section');
expect(parsed.alias).toEqual('link');
});
@@ -120,9 +120,17 @@ describe('MarkdownLink', () => {
).links[0];
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toEqual('to/path.md');
expect(parsed.section).toBeUndefined();
expect(parsed.section).toEqual('');
expect(parsed.alias).toEqual('inbox [xyz]');
});
it('should parse links with empty label #975', () => {
const link = parser.parse(getRandomURI(), `this is a [](to/path.md)`)
.links[0];
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toEqual('to/path.md');
expect(parsed.section).toEqual('');
expect(parsed.alias).toEqual('');
});
});
describe('rename wikilink', () => {

View File

@@ -5,27 +5,35 @@ export abstract class MarkdownLink {
/\[\[([^#|]+)?#?([^|]+)?\|?(.*)?\]\]/
);
private static directLinkRegex = new RegExp(
/\[(.+)\]\(([^#]*)?#?([^\]]+)?\)/
/\[(.*)\]\(([^#]*)?#?([^\]]+)?\)/
);
public static analyzeLink(link: ResourceLink) {
if (link.type === 'wikilink') {
const [, target, section, alias] = this.wikilinkRegex.exec(link.rawText);
return {
target: target?.replace(/\\/g, ''),
section,
alias,
};
try {
if (link.type === 'wikilink') {
const [, target, section, alias] = this.wikilinkRegex.exec(
link.rawText
);
return {
target: target?.replace(/\\/g, '') ?? '',
section: section ?? '',
alias: alias ?? '',
};
}
if (link.type === 'link') {
const [, alias, target, section] = this.directLinkRegex.exec(
link.rawText
);
return {
target: target ?? '',
section: section ?? '',
alias: alias ?? '',
};
}
throw new Error(`Link of type ${link.type} is not supported`);
} catch (e) {
throw new Error(`Couldn't parse link ${link.rawText} - ${e}`);
}
if (link.type === 'link') {
const [, alias, target, section] = this.directLinkRegex.exec(
link.rawText
);
return { target, section, alias };
}
throw new Error(
`Unexpected state: link of type ${link.type} is not supported`
);
}
public static createUpdateLinkEdit(

View File

@@ -147,6 +147,39 @@ describe('Link resolution', () => {
expect(ws.resolveLink(noteA, noteA.links[1])).toEqual(noteC.uri);
expect(ws.resolveLink(noteA, noteA.links[2])).toEqual(noteD.uri);
});
it('should resolve wikilink with section identifier', () => {
const noteA = createTestNote({
uri: '/path/to/page-a.md',
links: [
// uppercased filename, lowercased slug
{ slug: 'page-b#section' },
],
});
const noteB = createTestNote({ uri: '/somewhere/PAGE-B.md' });
const ws = createTestWorkspace()
.set(noteA)
.set(noteB);
expect(ws.resolveLink(noteA, noteA.links[0])).toEqual(
noteB.uri.withFragment('section')
);
});
it('should resolve section-only wikilinks', () => {
const noteA = createTestNote({
uri: '/path/to/page-a.md',
links: [
// uppercased filename, lowercased slug
{ slug: '#section' },
],
});
const ws = createTestWorkspace().set(noteA);
expect(ws.resolveLink(noteA, noteA.links[0])).toEqual(
noteA.uri.withFragment('section')
);
});
});
describe('Markdown direct links', () => {

View File

@@ -10,7 +10,7 @@ import { FoamWorkspace } from '../core/model/workspace';
import { FoamGraph } from '../core/model/graph';
import { Resource, ResourceLink } from '../core/model/note';
import { Range } from '../core/model/range';
import { fromVsCodeUri } from '../utils/vsc-utils';
import { fromVsCodeUri, toVsCodeUri } from '../utils/vsc-utils';
const feature: FoamFeature = {
activate: async (
@@ -133,7 +133,7 @@ export class BacklinkTreeItem extends vscode.TreeItem {
this.label = `${link.range.start.line}: ${this.label}`;
this.command = {
command: 'vscode.open',
arguments: [resource.uri, { selection: link.range }],
arguments: [toVsCodeUri(resource.uri), { selection: link.range }],
title: 'Go to link',
};
}

View File

@@ -11,6 +11,7 @@ import {
markdownItWithFoamLinks,
markdownItWithFoamTags,
markdownItWithNoteInclusion,
markdownItWithRemoveLinkReferences,
} from './preview-navigation';
describe('Link generation in preview', () => {
@@ -22,7 +23,11 @@ describe('Link generation in preview', () => {
links: [{ slug: 'placeholder' }],
});
const ws = new FoamWorkspace().set(noteA);
const md = markdownItWithFoamLinks(MarkdownIt(), ws);
const md = [
markdownItWithFoamLinks,
markdownItWithRemoveLinkReferences,
].reduce((acc, extension) => extension(acc, ws), MarkdownIt());
it('generates a link to a note', () => {
expect(md.render(`[[note-a]]`)).toEqual(
@@ -41,6 +46,14 @@ describe('Link generation in preview', () => {
`<p><a class='foam-placeholder-link' title="Link to non-existing resource" href="javascript:void(0);">random-text</a></p>\n`
);
});
it('generates a wikilink even when there is a link reference', () => {
const note = `[[note-a]]
[note-a]: <note-a.md> "Note A"`;
expect(md.render(note)).toEqual(
`<p><a class='foam-note-link' title='${noteA.title}' href='/path/to/note-a.md' data-href='/path/to/note-a.md'>note-a</a>\n[note-a]: &lt;note-a.md&gt; &quot;Note A&quot;</p>\n`
);
});
});
describe('Stylable tag generation in preview', () => {

View File

@@ -155,18 +155,17 @@ export const markdownItWithRemoveLinkReferences = (
) => {
md.inline.ruler.before('link', 'clear-references', state => {
if (state.env.references) {
Object.keys(state.env.references).forEach(refKey => {
// Forget about reference links that contain an alias divider
// Aliased reference links will lead the MarkdownParser to include wrong link references
if (refKey.includes(ALIAS_DIVIDER_CHAR)) {
delete state.env.references[refKey];
}
const src = state.src.toLowerCase();
const foamLinkRegEx = /\[\[([^[\]]+?)\]\]/g;
const foamLinks = [...src.matchAll(foamLinkRegEx)].map(m =>
m[1].toLowerCase()
);
// When the reference is present due to an inclusion of that note, we
// need to remove that reference. This ensures the MarkdownIt parser
// will not replace the wikilink syntax with an <a href> link and as a result
// break our inclusion logic.
if (state.src.toLowerCase().includes(`![[${refKey.toLowerCase()}]]`)) {
Object.keys(state.env.references).forEach(refKey => {
// Remove all references that have corresponding wikilinks.
// If the markdown parser sees a reference, it will format it before
// we get a chance to create the wikilink.
if (foamLinks.includes(refKey.toLowerCase())) {
delete state.env.references[refKey];
}
});

View File

@@ -164,16 +164,14 @@ export class TagReference extends vscode.TreeItem {
public readonly title: string;
constructor(public readonly tag: Tag, public readonly note: Resource) {
super(note.title, vscode.TreeItemCollapsibleState.None);
const uri = toVsCodeUri(note.uri);
this.title = note.title;
this.description = note.uri.path.replace(
vscode.workspace.getWorkspaceFolder(toVsCodeUri(note.uri))?.uri.path,
''
);
this.description = vscode.workspace.asRelativePath(uri);
this.tooltip = undefined;
this.command = {
command: 'vscode.open',
arguments: [
note.uri,
uri,
{
preview: true,
selection: toVsCodeRange(tag.range),

View File

@@ -5,7 +5,7 @@
👀*This is an early stage project under rapid development. For updates join the [Foam community Discord](https://foambubble.github.io/join-discord/g)! 💬*
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-92-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-94-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
[![Discord Chat](https://img.shields.io/discord/729975036148056075?color=748AD9&label=discord%20chat&style=flat-square)](https://foambubble.github.io/join-discord/g)
@@ -315,6 +315,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
</tr>
<tr>
<td align="center"><a href="http://Cliffordfajardo.com"><img src="https://avatars.githubusercontent.com/u/6743796?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Clifford Fajardo </b></sub></a><br /><a href="#tool-cliffordfajardo" title="Tools">🔧</a></td>
<td align="center"><a href="http://cu-dev.ca"><img src="https://avatars.githubusercontent.com/u/6589365?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Chris Usick</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chrisUsick" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/josephdecock"><img src="https://avatars.githubusercontent.com/u/1145533?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joe DeCock</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=josephdecock" title="Code">💻</a></td>
</tr>
</table>