Compare commits

...

15 Commits

Author SHA1 Message Date
Riccardo Ferretti
7a5f45c0ce v0.21.2 2023-04-11 13:51:30 -06:00
Riccardo Ferretti
df32d9e708 Preparation for next release 2023-04-11 13:51:13 -06:00
Riccardo Ferretti
b3d4691bfa Fix #1170 newNotePath not always working
This commit fixes an issue in the note creation flow where a template is used. Before the fix, when a template is present, Foam would force an absolute path resolution instead of delegating that step to the note creation. After the fix this step is removed and the resolution is left to the note creation code
2023-04-11 13:45:14 -06:00
allcontributors[bot]
f5260f7d3f add jimgraham as a contributor for code (#1185)
* update docs/index.md [skip ci]

* update readme.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-04-09 03:36:21 +02:00
Jim Graham
9b4b7ec84d Use the HASHTAG_REGEX to identify tags (#1183)
Instead of a generic `TAG_REGEX`, use the more specific `HASHTAG_REGEX` to identify a tag when launching tab completion.

Also add a new test case with the issue number in the test case description
2023-04-09 03:35:48 +02:00
Riccardo Ferretti
52b7f86a9f Updated TOC in FAQ docs 2023-03-22 10:56:11 -06:00
Joe Taber
2db7060124 Add GH Action workflow to sync user docs to template repo on change (#1180)
Also strips wikilinks during docs sync
fixes #1177
2023-03-22 17:47:22 +01:00
Riccardo
a4f04b3b6b Fixed temporary template generated (#1175) 2023-02-26 23:44:09 +01:00
Riccardo
b5a8a5d7c7 Chore - improved Text Edit API (#1174) 2023-02-26 23:37:50 +01:00
Riccardo
f5a29e431c Fix 1168 - relative path in embed (#1173)
* added isEmbed flag to ResourceLink
* Added tests for new embed support
2023-02-26 23:02:16 +01:00
Riccardo Ferretti
5a7a1ba89f v0.21.1 2023-02-24 17:32:15 -06:00
Riccardo Ferretti
b054bafc78 Preparation for next release 2023-02-24 17:31:55 -06:00
Riccardo
8acb60253a fix #1171 - add extension to notes created from placeholders (#1172) 2023-02-24 23:45:44 +01:00
allcontributors[bot]
3c69508dcb add Skakerman as a contributor for code (#1166)
* update docs/index.md [skip ci]

* update readme.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-02-16 21:32:28 +01:00
Scott Akerman
a368be9b47 Fix Previews of Embedded Sections (#1162) 2023-02-16 21:31:48 +01:00
33 changed files with 362 additions and 124 deletions

View File

@@ -1004,6 +1004,24 @@
"contributions": [
"doc"
]
},
{
"login": "Skakerman",
"name": "Scott Akerman",
"avatar_url": "https://avatars.githubusercontent.com/u/15224439?v=4",
"profile": "http://scottakerman.com",
"contributions": [
"code"
]
},
{
"login": "jimgraham",
"name": "Jim Graham",
"avatar_url": "https://avatars.githubusercontent.com/u/430293?v=4",
"profile": "http://www.jim-graham.net/",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

43
.github/workflows/update-docs.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Update Docs
on:
push:
branches:
- master
paths:
- docs/user/**/*
workflow_dispatch:
jobs:
update-docs:
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
with:
repository: foambubble/foam-template
path: foam-template
- uses: actions/checkout@v3
with:
path: foam
- name: Copy and fixup user docs files
id: copy
run: |
rm -r foam-template/docs
cp -r foam/docs/user foam-template/docs
# Strip autogenerated wikileaks references because
# they are not an appropriate default user experience.
(cd foam-template/docs; sed -i '/\[\/\/begin\]/,/\[\/\/end\]/d' $(find . -type f -name \*.md))
# Set the commit message format
echo "message=Docs sync @ $(cd foam; git log --pretty='format:%h %s')" >> $GITHUB_OUTPUT
- uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.FOAM_DOCS_SYNC_TOKEN }}
path: foam-template
commit-message: ${{ steps.copy.outputs.message }}
branch: bot/foam-docs-sync
delete-branch: true
title: Sync docs from foam
body: Copy docs from main foam repo

View File

@@ -247,6 +247,8 @@ If that sounds like something you're interested in, I'd love to have you along o
<td align="center" valign="top" width="14.28%"><a href="http://jonathanpberger.com/"><img src="https://avatars.githubusercontent.com/u/41085?v=4?s=60" width="60px;" alt="jonathan berger"/><br /><sub><b>jonathan berger</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jonathanpberger" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/badsketch"><img src="https://avatars.githubusercontent.com/u/8953212?v=4?s=60" width="60px;" alt="Daniel Wang"/><br /><sub><b>Daniel Wang</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=badsketch" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://yongliangliu.com"><img src="https://avatars.githubusercontent.com/u/41845017?v=4?s=60" width="60px;" alt="Liu YongLiang"/><br /><sub><b>Liu YongLiang</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=tlylt" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://scottakerman.com"><img src="https://avatars.githubusercontent.com/u/15224439?v=4?s=60" width="60px;" alt="Scott Akerman"/><br /><sub><b>Scott Akerman</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Skakerman" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.jim-graham.net/"><img src="https://avatars.githubusercontent.com/u/430293?v=4?s=60" width="60px;" alt="Jim Graham"/><br /><sub><b>Jim Graham</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jimgraham" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -5,6 +5,7 @@
- [Frequently Asked Questions](#frequently-asked-questions)
- [Links/Graphs/BackLinks don't work. How do I enable them?](#linksgraphsbacklinks-dont-work-how-do-i-enable-them)
- [I don't want Foam enabled for all my workspaces](#i-dont-want-foam-enabled-for-all-my-workspaces)
- [I want to publish the graph view to GitHub pages or Vercel](#i-want-to-publish-the-graph-view-to-github-pages-or-vercel)
## Links/Graphs/BackLinks don't work. How do I enable them?

View File

@@ -1,8 +1,10 @@
# Using Foam
Foam is a collection VS Code extensions and recipes that power up the editor into a full-blown note taking system.
This folder contains user documentation describing how to get started using Foam, what its main features are, and strategies for getting the most out of Foam.
The full docs are included in the `foam-template` repo that most users start from.
Foam is a collection VS Code extensions and recipes that power up the editor
into a full-blown note taking system. This folder contains user documentation
describing how to get started using Foam, what its main features are, and
strategies for getting the most out of Foam. The full docs are included in the
`foam-template` repo that most users start from.
> See also [[frequently-asked-questions]].

View File

@@ -4,5 +4,5 @@
],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.21.0"
"version": "0.21.2"
}

View File

@@ -4,6 +4,25 @@ 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.21.2] - 2023-04-11
Fixes and Improvements:
- Fixed embed with relative paths (#1168, #1170)
- Improved multi-root folder support for daily notes (#1126, #1175)
- Improved use of tag completion (#1183 - thanks @jimgraham)
- Fixed relative path use in note creation when using templates (#1170)
Internal:
- Sync user docs with foam-template docs (#1180 - thanks @infogulch)
## [0.21.1] - 2023-02-24
Fixes and Improvements:
- Fixed note creation from placeholder (#1172)
## [0.21.0] - 2023-02-16
Features:

View File

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

View File

@@ -1,43 +0,0 @@
import os from 'os';
import detectNewline from 'detect-newline';
import { Position } from '../model/position';
import { Range } from '../model/range';
export interface TextEdit {
range: Range;
newText: string;
}
/**
*
* @param text text on which the textEdit will be applied
* @param textEdit
* @returns {string} text with the applied textEdit
*/
export const applyTextEdit = (text: string, textEdit: TextEdit): string => {
const eol = detectNewline(text) || os.EOL;
const lines = text.split(eol);
const characters = text.split('');
const startOffset = getOffset(lines, textEdit.range.start, eol);
const endOffset = getOffset(lines, textEdit.range.end, eol);
const deleteCount = endOffset - startOffset;
const textToAppend = `${textEdit.newText}`;
characters.splice(startOffset, deleteCount, textToAppend);
return characters.join('');
};
const getOffset = (
lines: string[],
position: Position,
eol: string
): number => {
const eolLen = eol.length;
let offset = 0;
let i = 0;
while (i < position.line && i < lines.length) {
offset = offset + lines[i].length + eolLen;
i++;
}
return offset + Math.min(position.character, lines[i]?.length ?? 0);
};

View File

@@ -1,7 +1,7 @@
import matter from 'gray-matter';
import { TextEdit } from './apply-text-edit';
import { Resource } from '../model/note';
import { Range } from '../model/range';
import { TextEdit } from '../services/text-edit';
import { getHeadingFromFileName } from '../utils';
export const generateHeading = async (

View File

@@ -5,8 +5,8 @@ import {
stringifyMarkdownLinkReferenceDefinition,
} from '../services/markdown-provider';
import { FoamWorkspace } from '../model/workspace';
import { TextEdit } from './apply-text-edit';
import { Position } from '../model/position';
import { TextEdit } from '../services/text-edit';
export const LINK_REFERENCE_DEFINITION_HEADER = `[//begin]: # "Autogenerated link references for markdown compatibility"`;
export const LINK_REFERENCE_DEFINITION_FOOTER = `[//end]: # "Autogenerated link references"`;

View File

@@ -5,6 +5,7 @@ export interface ResourceLink {
type: 'wikilink' | 'link';
rawText: string;
range: Range;
isEmbed: boolean;
}
export interface NoteLinkDefinition {

View File

@@ -107,6 +107,7 @@ describe('MarkdownLink', () => {
type: 'link',
rawText: '[link](#section)',
range: Range.create(0, 0),
isEmbed: false,
};
const parsed = MarkdownLink.analyzeLink(link);
expect(parsed.target).toEqual('');
@@ -161,7 +162,7 @@ describe('MarkdownLink', () => {
target: 'new-link',
});
expect(edit.newText).toEqual(`[[new-link#section]]`);
expect(edit.selection).toEqual(link.range);
expect(edit.range).toEqual(link.range);
});
it('should rename the section only', () => {
const link = parser.parse(
@@ -172,7 +173,7 @@ describe('MarkdownLink', () => {
section: 'new-section',
});
expect(edit.newText).toEqual(`[[wikilink#new-section]]`);
expect(edit.selection).toEqual(link.range);
expect(edit.range).toEqual(link.range);
});
it('should rename both target and section', () => {
const link = parser.parse(
@@ -184,7 +185,7 @@ describe('MarkdownLink', () => {
section: 'new-section',
});
expect(edit.newText).toEqual(`[[new-link#new-section]]`);
expect(edit.selection).toEqual(link.range);
expect(edit.range).toEqual(link.range);
});
it('should be able to remove the section', () => {
const link = parser.parse(
@@ -195,7 +196,7 @@ describe('MarkdownLink', () => {
section: '',
});
expect(edit.newText).toEqual(`[[wikilink]]`);
expect(edit.selection).toEqual(link.range);
expect(edit.range).toEqual(link.range);
});
it('should be able to rename the alias', () => {
const link = parser.parse(getRandomURI(), `this is a [[wikilink|alias]]`)
@@ -204,7 +205,7 @@ describe('MarkdownLink', () => {
alias: 'new-alias',
});
expect(edit.newText).toEqual(`[[wikilink|new-alias]]`);
expect(edit.selection).toEqual(link.range);
expect(edit.range).toEqual(link.range);
});
});
@@ -216,7 +217,7 @@ describe('MarkdownLink', () => {
target: 'to/another-path.md',
});
expect(edit.newText).toEqual(`[link](to/another-path.md)`);
expect(edit.selection).toEqual(link.range);
expect(edit.range).toEqual(link.range);
});
it('should rename the section only', () => {
const link = parser.parse(
@@ -227,7 +228,7 @@ describe('MarkdownLink', () => {
section: 'section2',
});
expect(edit.newText).toEqual(`[link](to/path.md#section2)`);
expect(edit.selection).toEqual(link.range);
expect(edit.range).toEqual(link.range);
});
it('should rename both target and section', () => {
const link = parser.parse(
@@ -239,7 +240,7 @@ describe('MarkdownLink', () => {
section: 'section2',
});
expect(edit.newText).toEqual(`[link](to/another-path.md#section2)`);
expect(edit.selection).toEqual(link.range);
expect(edit.range).toEqual(link.range);
});
it('should be able to remove the section', () => {
const link = parser.parse(
@@ -250,7 +251,7 @@ describe('MarkdownLink', () => {
section: '',
});
expect(edit.newText).toEqual(`[link](to/path.md)`);
expect(edit.selection).toEqual(link.range);
expect(edit.range).toEqual(link.range);
});
});
});

View File

@@ -46,16 +46,17 @@ export abstract class MarkdownLink {
const newAlias = delta.alias ?? alias ?? '';
const sectionDivider = newSection ? '#' : '';
const aliasDivider = newAlias ? '|' : '';
const embed = link.isEmbed ? '!' : '';
if (link.type === 'wikilink') {
return {
newText: `[[${newTarget}${sectionDivider}${newSection}${aliasDivider}${newAlias}]]`,
selection: link.range,
newText: `${embed}[[${newTarget}${sectionDivider}${newSection}${aliasDivider}${newAlias}]]`,
range: link.range,
};
}
if (link.type === 'link') {
return {
newText: `[${newAlias}](${newTarget}${sectionDivider}${newSection})`,
selection: link.range,
newText: `${embed}[${newAlias}](${newTarget}${sectionDivider}${newSection})`,
range: link.range,
};
}
throw new Error(

View File

@@ -39,6 +39,7 @@ describe('Markdown parsing', () => {
const link = note.links[0];
expect(link.type).toEqual('link');
expect(link.rawText).toEqual('[link to page b](../doc/page-b.md)');
expect(link.isEmbed).toBeFalsy();
});
it('should detect links that have formatting in label', () => {
@@ -48,6 +49,15 @@ describe('Markdown parsing', () => {
expect(note.links.length).toEqual(1);
const link = note.links[0];
expect(link.type).toEqual('link');
expect(link.isEmbed).toBeFalsy();
});
it('should detect embed links', () => {
const note = createNoteFromMarkdown('this is ![link](../doc/page-b.md)');
expect(note.links.length).toEqual(1);
const link = note.links[0];
expect(link.type).toEqual('link');
expect(link.isEmbed).toBeTruthy();
});
it('should detect wikilinks', () => {
@@ -61,6 +71,16 @@ describe('Markdown parsing', () => {
link = note.links[1];
expect(link.type).toEqual('wikilink');
expect(link.rawText).toEqual('[[a file]]');
expect(link.isEmbed).toBeFalsy();
});
it('should detect wikilink embeds', () => {
const note = createNoteFromMarkdown('Some content and ![[an embed]]');
expect(note.links.length).toEqual(1);
const link = note.links[0];
expect(link.type).toEqual('wikilink');
expect(link.rawText).toEqual('![[an embed]]');
expect(link.isEmbed).toBeTruthy();
});
it('should detect wikilinks that have aliases', () => {
@@ -74,6 +94,7 @@ describe('Markdown parsing', () => {
link = note.links[1];
expect(link.type).toEqual('wikilink');
expect(link.rawText).toEqual('[[other link | spaced]]');
expect(link.isEmbed).toBeFalsy();
});
it('should skip wikilinks in codeblocks', () => {

View File

@@ -299,18 +299,33 @@ const wikilinkPlugin: ParserPlugin = {
name: 'wikilink',
visit: (node, note, noteSource) => {
if (node.type === 'wikiLink') {
const isEmbed =
noteSource.charAt(node.position!.start.offset - 1) === '!';
const literalContent = noteSource.substring(
node.position!.start.offset!,
isEmbed
? node.position!.start.offset! - 1
: node.position!.start.offset!,
node.position!.end.offset!
);
const range = isEmbed
? Range.create(
node.position.start.line - 1,
node.position.start.column - 2,
node.position.end.line - 1,
node.position.end.column - 1
)
: astPositionToFoamRange(node.position!);
note.links.push({
type: 'wikilink',
rawText: literalContent,
range: astPositionToFoamRange(node.position!),
range,
isEmbed,
});
}
if (node.type === 'link') {
if (node.type === 'link' || node.type === 'image') {
const targetUri = (node as any).url;
const uri = note.uri.resolve(targetUri);
if (uri.scheme !== 'file' || uri.path === note.uri.path) {
@@ -324,6 +339,7 @@ const wikilinkPlugin: ParserPlugin = {
type: 'link',
rawText: literalContent,
range: astPositionToFoamRange(node.position!),
isEmbed: literalContent.startsWith('!'),
});
}
},

View File

@@ -1,6 +1,6 @@
import { Range } from '../model/range';
import { Logger } from '../utils/log';
import { applyTextEdit } from './apply-text-edit';
import { TextEdit } from './text-edit';
Logger.setLevel('error');
@@ -23,7 +23,7 @@ describe('applyTextEdit', () => {
3. this is third line
4. this is fourth line`;
const actual = applyTextEdit(text, textEdit);
const actual = TextEdit.apply(text, textEdit);
expect(actual).toBe(expected);
});
@@ -45,7 +45,7 @@ describe('applyTextEdit', () => {
3. this is third line
`;
const actual = applyTextEdit(text, textEdit);
const actual = TextEdit.apply(text, textEdit);
expect(actual).toBe(expected);
});
@@ -68,7 +68,7 @@ describe('applyTextEdit', () => {
3. this is third line
`;
const actual = applyTextEdit(text, textEdit);
const actual = TextEdit.apply(text, textEdit);
expect(actual).toBe(expected);
});

View File

@@ -0,0 +1,44 @@
import detectNewline from 'detect-newline';
import { Position } from '../model/position';
import { Range } from '../model/range';
export interface TextEdit {
range: Range;
newText: string;
}
export abstract class TextEdit {
/**
*
* @param text text on which the textEdit will be applied
* @param textEdit
* @returns {string} text with the applied textEdit
*/
public static apply(text: string, textEdit: TextEdit): string {
const eol = detectNewline.graceful(text);
const lines = text.split(eol);
const characters = text.split('');
const startOffset = getOffset(lines, textEdit.range.start, eol);
const endOffset = getOffset(lines, textEdit.range.end, eol);
const deleteCount = endOffset - startOffset;
const textToAppend = `${textEdit.newText}`;
characters.splice(startOffset, deleteCount, textToAppend);
return characters.join('');
}
}
const getOffset = (
lines: string[],
position: Position,
eol: string
): number => {
const eolLen = eol.length;
let offset = 0;
let i = 0;
while (i < position.line && i < lines.length) {
offset = offset + lines[i].length + eolLen;
i++;
}
return offset + Math.min(position.character, lines[i]?.length ?? 0);
};

View File

@@ -1,5 +1,5 @@
import { isSome } from './core';
const HASHTAG_REGEX =
export const HASHTAG_REGEX =
/(?<=^|\s)#([0-9]*[\p{L}\p{Emoji_Presentation}/_-][\p{L}\p{Emoji_Presentation}\p{N}/_-]*)/gmu;
const WORD_REGEX =
/(?<=^|\s)([0-9]*[\p{L}\p{Emoji_Presentation}/_-][\p{L}\p{Emoji_Presentation}\p{N}/_-]*)/gmu;

View File

@@ -86,9 +86,7 @@ export async function createDailyNoteIfNotExists(targetDate: Date) {
const templateFallbackText = `---
foam_template:
filepath: "${workspace.asRelativePath(
toVsCodeUri(pathFromLegacyConfiguration)
)}"
filepath: "${pathFromLegacyConfiguration.toFsPath()}"
---
# ${dateFormat(targetDate, titleFormat, false)}
`;

View File

@@ -10,6 +10,7 @@ import {
showInEditor,
} from '../../test/test-utils-vscode';
import { fromVsCodeUri } from '../../utils/vsc-utils';
import { CREATE_NOTE_COMMAND } from './create-note';
describe('create-note command', () => {
afterEach(() => {
@@ -188,3 +189,17 @@ describe('create-note command', () => {
await deleteFile(base);
});
});
describe('factories', () => {
describe('forPlaceholder', () => {
it('adds the .md extension to notes created for placeholders', async () => {
await closeEditors();
const command = CREATE_NOTE_COMMAND.forPlaceholder('my-placeholder');
await commands.executeCommand(command.name, command.params);
const doc = window.activeTextEditor.document;
expect(doc.uri.path).toMatch(/my-placeholder.md$/);
expect(doc.getText()).toMatch(/^# my-placeholder/);
});
});
});

View File

@@ -114,11 +114,17 @@ export const CREATE_NOTE_COMMAND = {
placeholder: string,
extra: Partial<CreateNoteArgs> = {}
): CommandDescriptor<CreateNoteArgs> => {
const title = placeholder.endsWith('.md')
? placeholder.replace(/\.md$/, '')
: placeholder;
const notePath = placeholder.endsWith('.md')
? placeholder
: placeholder + '.md';
return {
name: CREATE_NOTE_COMMAND.command,
params: {
title: placeholder,
notePath: placeholder,
title,
notePath,
...extra,
},
};

View File

@@ -20,8 +20,8 @@ import { Foam } from '../../core/model/foam';
import { Resource } from '../../core/model/note';
import { generateHeading, generateLinkReferences } from '../../core/janitor';
import { Range } from '../../core/model/range';
import { applyTextEdit } from '../../core/janitor/apply-text-edit';
import detectNewline from 'detect-newline';
import { TextEdit } from '../../core/services/text-edit';
const feature: FoamFeature = {
activate: (context: ExtensionContext, foamPromise: Promise<Foam>) => {
@@ -130,8 +130,8 @@ async function runJanitor(foam: Foam) {
// Note: The ordering matters. Definitions need to be inserted
// before heading, since inserting a heading changes line numbers below
let text = noteText;
text = definitions ? applyTextEdit(text, definitions) : text;
text = heading ? applyTextEdit(text, heading) : text;
text = definitions ? TextEdit.apply(text, definitions) : text;
text = heading ? TextEdit.apply(text, heading) : text;
return workspace.fs.writeFile(toVsCodeUri(note.uri), Buffer.from(text));
});

View File

@@ -22,7 +22,11 @@ const feature: FoamFeature = {
markdownItFoamTags,
markdownItWikilinkNavigation,
markdownItRemoveLinkReferences,
].reduce((acc, extension) => extension(acc, foam.workspace), md);
].reduce(
(acc, extension) =>
extension(acc, foam.workspace, foam.services.parser),
md
);
},
};
},

View File

@@ -24,7 +24,7 @@ describe('Displaying included notes in preview', () => {
CONFIG_EMBED_NOTE_IN_CONTAINER,
false,
() => {
const md = markdownItWikilinkEmbed(MarkdownIt(), ws);
const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser);
expect(
md.render(`This is the root node.
@@ -51,7 +51,7 @@ describe('Displaying included notes in preview', () => {
CONFIG_EMBED_NOTE_IN_CONTAINER,
true,
() => {
const md = markdownItWikilinkEmbed(MarkdownIt(), ws);
const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser);
const res = md.render(`This is the root node. ![[note-a]]`);
expect(res).toContain('This is the root node');
@@ -68,19 +68,19 @@ describe('Displaying included notes in preview', () => {
const note = await createFile(
`
# Section 1
This is the first section of note D
This is the first section of note E
# Section 2
This is the second section of note D
This is the second section of note E
# Section 3
This is the third section of note D
This is the third section of note E
`,
['note-e.md']
);
const parser = createMarkdownParser([]);
const ws = new FoamWorkspace().set(parser.parse(note.uri, note.content));
const md = markdownItWikilinkEmbed(MarkdownIt(), ws);
const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser);
await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_IN_CONTAINER,
@@ -93,7 +93,7 @@ This is the third section of note D
).toMatch(
`<p>This is the root node.</p>
<p><h1>Section 2</h1>
<p>This is the second section of note D</p>
<p>This is the second section of note E</p>
</p>`
);
}
@@ -102,8 +102,48 @@ This is the third section of note D
await deleteFile(note);
});
it('should render an included section in container mode', async () => {
const note = await createFile(
`
# Section 1
This is the first section of note E
# Section 2
This is the second section of note E
# Section 3
This is the third section of note E
`,
['note-e-container.md']
);
const parser = createMarkdownParser([]);
const ws = new FoamWorkspace().set(parser.parse(note.uri, note.content));
await withModifiedFoamConfiguration(
CONFIG_EMBED_NOTE_IN_CONTAINER,
true,
() => {
const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser);
const res = md.render(
`This is the root node. ![[note-e-container#Section 3]]`
);
expect(res).toContain('This is the root node');
expect(res).toContain('embed-container-note');
expect(res).toContain('Section 3');
expect(res).toContain('This is the third section of note E');
}
);
await deleteFile(note);
});
it('should fallback to the bare text when the note is not found', () => {
const md = markdownItWikilinkEmbed(MarkdownIt(), new FoamWorkspace());
const md = markdownItWikilinkEmbed(
MarkdownIt(),
new FoamWorkspace(),
parser
);
expect(md.render(`This is the root node. ![[non-existing-note]]`)).toMatch(
`<p>This is the root node. ![[non-existing-note]]</p>`
@@ -122,12 +162,12 @@ This is the third section of note D
const ws = new FoamWorkspace()
.set(parser.parse(noteA.uri, noteA.content))
.set(parser.parse(noteB.uri, noteB.content));
const md = markdownItWikilinkEmbed(MarkdownIt(), ws);
const md = markdownItWikilinkEmbed(MarkdownIt(), ws, parser);
const res = md.render(noteBText);
expect(res).toContain('This is the text of note B which includes');
expect(res).toContain('This is the text of note A which includes');
expect(res).toContain('Cyclic link detected for wikilink: note-a');
expect(res).toContain('Cyclic link detected for wikilink');
deleteFile(noteA);
deleteFile(noteB);

View File

@@ -1,20 +1,26 @@
/*global markdownit:readonly*/
// eslint-disable-next-line no-restricted-imports
import { readFileSync } from 'fs';
import { workspace as vsWorkspace } from 'vscode';
import markdownItRegex from 'markdown-it-regex';
import { isSome } from '../../utils';
import { FoamWorkspace } from '../../core/model/workspace';
import { Logger } from '../../core/utils/log';
import { Resource } from '../../core/model/note';
import { Resource, ResourceParser } from '../../core/model/note';
import { getFoamVsCodeConfig } from '../../services/config';
// eslint-disable-next-line no-restricted-imports
import { readFileSync } from 'fs';
import { fromVsCodeUri, toVsCodeUri } from '../../utils/vsc-utils';
import { MarkdownLink } from '../../core/services/markdown-link';
import { Position } from '../../core/model/position';
import { TextEdit } from '../../core/services/text-edit';
export const CONFIG_EMBED_NOTE_IN_CONTAINER = 'preview.embedNoteInContainer';
const refsStack: string[] = [];
export const markdownItWikilinkEmbed = (
md: markdownit,
workspace: FoamWorkspace
workspace: FoamWorkspace,
parser: ResourceParser
) => {
return md.use(markdownItRegex, {
name: 'embed-wikilinks',
@@ -41,9 +47,22 @@ export const markdownItWikilinkEmbed = (
let content = `Embed for [[${wikilink}]]`;
switch (includedNote.type) {
case 'note': {
const noteText = readFileSync(
includedNote.uri.toFsPath()
).toString();
let noteText = readFileSync(includedNote.uri.toFsPath()).toString();
const section = Resource.findSection(
includedNote,
includedNote.uri.fragment
);
if (isSome(section)) {
const rows = noteText.split('\n');
noteText = rows
.slice(section.range.start.line, section.range.end.line)
.join('\n');
}
noteText = withLinksRelativeToWorkspaceRoot(
noteText,
parser,
workspace
);
content = getFoamVsCodeConfig(CONFIG_EMBED_NOTE_IN_CONTAINER)
? `<div class="embed-container-note">${md.render(noteText)}</div>`
: noteText;
@@ -62,16 +81,6 @@ Embed for attachments is not supported
)}</div>`;
break;
}
const section = Resource.findSection(
includedNote,
includedNote.uri.fragment
);
if (isSome(section)) {
const rows = content.split('\n');
content = rows
.slice(section.range.start.line, section.range.end.line)
.join('\n');
}
const html = md.render(content);
refsStack.pop();
return html;
@@ -86,4 +95,32 @@ Embed for attachments is not supported
});
};
function withLinksRelativeToWorkspaceRoot(
noteText: string,
parser: ResourceParser,
workspace: FoamWorkspace
) {
const note = parser.parse(
fromVsCodeUri(vsWorkspace.workspaceFolders[0].uri),
noteText
);
const edits = note.links
.map(link => {
const info = MarkdownLink.analyzeLink(link);
const resource = workspace.find(info.target);
const pathFromRoot = vsWorkspace.asRelativePath(
toVsCodeUri(resource.uri)
);
return MarkdownLink.createUpdateLinkEdit(link, {
target: pathFromRoot,
});
})
.sort((a, b) => Position.compareTo(b.range.start, a.range.start));
const text = edits.reduce(
(text, edit) => TextEdit.apply(text, edit),
noteText
);
return text;
}
export default markdownItWikilinkEmbed;

View File

@@ -23,6 +23,7 @@ export const markdownItWikilinkNavigation = (
rawText: '[[' + wikilink + ']]',
type: 'wikilink',
range: Range.create(0, 0),
isEmbed: false,
});
const formattedSection = section ? `#${section}` : '';
const label = isEmpty(alias) ? `${target}${formattedSection}` : alias;

View File

@@ -45,7 +45,7 @@ const feature: FoamFeature = {
);
renameEdits.replace(
toVsCodeUri(connection.source),
toVsCodeRange(edit.selection),
toVsCodeRange(edit.range),
edit.newText
);
break;
@@ -62,7 +62,7 @@ const feature: FoamFeature = {
);
renameEdits.replace(
toVsCodeUri(connection.source),
toVsCodeRange(edit.selection),
toVsCodeRange(edit.range),
edit.newText
);
break;

View File

@@ -93,4 +93,18 @@ describe('Tag Completion', () => {
expect(foamTags.tags.get('primary')).toBeTruthy();
expect(tags).toBeNull();
});
it('should not provide suggestions when inside a markdown heading #1182', async () => {
const { uri } = await createFile('# primary heading 1');
const { doc } = await showInEditor(uri);
const provider = new TagCompletionProvider(foamTags);
const tags = await provider.provideCompletionItems(
doc,
new vscode.Position(0, 7)
);
expect(foamTags.tags.get('primary')).toBeTruthy();
expect(tags).toBeNull();
});
});

View File

@@ -1,11 +1,9 @@
import * as vscode from 'vscode';
import { Foam } from '../core/model/foam';
import { FoamTags } from '../core/model/tags';
import { HASHTAG_REGEX } from '../core/utils/hashtags';
import { FoamFeature } from '../types';
import { mdDocSelector } from '../utils';
import { SECTION_REGEX } from './link-completion';
export const TAG_REGEX = /#(.*)/;
const feature: FoamFeature = {
activate: async (
@@ -36,8 +34,7 @@ export class TagCompletionProvider
.lineAt(position)
.text.substr(0, position.character);
const requiresAutocomplete =
cursorPrefix.match(TAG_REGEX) && !cursorPrefix.match(SECTION_REGEX);
const requiresAutocomplete = cursorPrefix.match(HASHTAG_REGEX);
if (!requiresAutocomplete) {
return null;

View File

@@ -349,13 +349,11 @@ export const NoteFactory = {
resolver
);
const newFilePath = asAbsoluteWorkspaceUri(
template.metadata.has('filepath')
? URI.file(template.metadata.get('filepath'))
: isSome(filepathFallbackURI)
? filepathFallbackURI
: await getPathFromTitle(resolver)
);
const newFilePath = template.metadata.has('filepath')
? URI.file(template.metadata.get('filepath'))
: isSome(filepathFallbackURI)
? filepathFallbackURI
: await getPathFromTitle(resolver);
return NoteFactory.createNote(
newFilePath,

View File

@@ -89,11 +89,13 @@ export const createTestNote = (params: {
type: 'wikilink',
range: range,
rawText: `[[${link.slug}]]`,
isEmbed: false,
}
: {
type: 'link',
range: range,
rawText: `[link text](${link.to})`,
isEmbed: false,
};
})
: [],

View File

@@ -5,9 +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-109-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-111-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
[![Visual Studio Marketplace Installs](https://img.shields.io/visual-studio-marketplace/i/foam.foam-vscode?label=VS%20Code%20Installs)](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode)
@@ -341,6 +339,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="http://jonathanpberger.com/"><img src="https://avatars.githubusercontent.com/u/41085?v=4?s=60" width="60px;" alt="jonathan berger"/><br /><sub><b>jonathan berger</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jonathanpberger" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/badsketch"><img src="https://avatars.githubusercontent.com/u/8953212?v=4?s=60" width="60px;" alt="Daniel Wang"/><br /><sub><b>Daniel Wang</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=badsketch" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://yongliangliu.com"><img src="https://avatars.githubusercontent.com/u/41845017?v=4?s=60" width="60px;" alt="Liu YongLiang"/><br /><sub><b>Liu YongLiang</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=tlylt" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://scottakerman.com"><img src="https://avatars.githubusercontent.com/u/15224439?v=4?s=60" width="60px;" alt="Scott Akerman"/><br /><sub><b>Scott Akerman</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Skakerman" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.jim-graham.net/"><img src="https://avatars.githubusercontent.com/u/430293?v=4?s=60" width="60px;" alt="Jim Graham"/><br /><sub><b>Jim Graham</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jimgraham" title="Code">💻</a></td>
</tr>
</tbody>
</table>