Compare commits

...

23 Commits

Author SHA1 Message Date
Riccardo Ferretti
34c179123e v0.13.5 2021-06-05 00:30:24 +02:00
Riccardo Ferretti
c34394a0ea prepare for 0.13.5 2021-06-05 00:28:38 +02:00
Riccardo Ferretti
26c38a06ff fixes #665 - always access files in .foam/templates directory when looking for templates 2021-06-04 15:29:33 +02:00
Michael Overmeyer
d4623a2d91 Allow for YAML metadata in templates (#655)
* Add a frontmatter metadata parser

* Use the template metadata to determine the filepath to use

* Document template metadata

* Add name and description template metadata attributes

These are displayed in the template picker

* Document name and description template metadata attributes
2021-06-04 15:27:46 +02:00
dependabot[bot]
2f9507dc87 Bump ws from 7.3.1 to 7.4.6 in /packages/foam-vscode (#662)
Bumps [ws](https://github.com/websockets/ws) from 7.3.1 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.3.1...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-31 20:59:09 +02:00
Paul de Raaij
4db09070a8 Filter out tag references that are part of a nested tag (#661)
* Filter out tagreferences that are part fo a nested tag

* add test case for notes with parent and child tag
2021-05-31 20:57:40 +02:00
Riccardo Ferretti
5f99d9d5c6 v0.13.4 2021-05-26 13:39:18 +02:00
Riccardo Ferretti
9c1480197c Preparation for 0.13.4 2021-05-26 13:16:49 +02:00
Riccardo Ferretti
f1a6426046 updated documentation to reflect nested tags 2021-05-26 09:18:30 +02:00
Riccardo Ferretti
6de8baa6b5 updated yarn.lock 2021-05-26 09:18:01 +02:00
Riccardo Ferretti
27ff023a26 fix loading of markdownIt extensions 2021-05-26 09:12:14 +02:00
Paul de Raaij
690eb10856 Add support for nested tags (#643)
* Add support for nested tags

* Adds test for feature
2021-05-26 09:11:39 +02:00
Michael Overmeyer
1cb8174a9f Always ask for the note title when creating from template (#645)
* Fix snake_case to camelCase

* Always ask for the note title when creating from the default template

In the future, we'll make this conditional on whether the template provides the filepath to use in its metadata block
2021-05-22 21:46:47 +02:00
allcontributors[bot]
2141bac24a docs: add pderaaij as a contributor (#650)
* 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>
2021-05-22 21:39:50 +02:00
Paul de Raaij
6abef8f8e7 Allow for optionality in title prop in frontmatter (#647) 2021-05-22 21:39:03 +02:00
dependabot[bot]
c797a00223 Bump nokogiri from 1.11.1 to 1.11.5 in /docs (#646)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.11.1 to 1.11.5.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.11.1...v1.11.5)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-20 15:35:51 +02:00
José Duarte
7c01fb13f0 Upgrade force-graph to 1.40.5 (#642) 2021-05-20 11:14:15 +02:00
dependabot[bot]
abbc2bbb14 Bump lodash from 4.17.19 to 4.17.21 in /packages/foam-core (#628)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-14 11:28:11 +02:00
dependabot[bot]
bb7fee24bb Bump hosted-git-info from 2.8.8 to 2.8.9 in /packages/foam-core (#629)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-14 11:27:22 +02:00
dependabot[bot]
43ef3a3e2b Bump lodash from 4.17.20 to 4.17.21 in /packages/foam-vscode (#630)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-14 11:26:59 +02:00
dependabot[bot]
da69a3057f Bump hosted-git-info from 2.8.8 to 2.8.9 in /packages/foam-vscode (#631)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-14 11:26:29 +02:00
Riccardo
2d06f26bbf fix #638 link to license page 2021-05-13 15:55:54 +02:00
Riccardo
edbe128e1e fix #546 - title yaml property can also be a number 2021-05-13 13:09:09 +02:00
31 changed files with 912 additions and 101 deletions

View File

@@ -688,6 +688,15 @@
"contributions": [
"doc"
]
},
{
"login": "pderaaij",
"name": "Paul de Raaij",
"avatar_url": "https://avatars.githubusercontent.com/u/495374?v=4",
"profile": "http://www.paulderaaij.nl",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@@ -203,14 +203,14 @@ GEM
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
mini_portile2 (2.5.0)
mini_portile2 (2.5.1)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.14.2)
multipart-post (2.1.1)
nokogiri (1.11.1)
nokogiri (1.11.5)
mini_portile2 (~> 2.5.0)
racc (~> 1.4)
octokit (4.19.0)

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -40,3 +40,110 @@ In addition, you can also use variables provided by Foam:
| `FOAM_TITLE` | The title of the note. If used, Foam will prompt you to enter a title for the note. |
**Note:** neither the defaulting feature (eg. `${variable:default}`) nor the format feature (eg. `${variable/(.*)/${1:/upcase}/}`) (available to other variables) are available for these Foam-provided variables.
## Metadata
Templates can also contain metadata about the templates themselves. The metadata is defined in YAML "Frontmatter" blocks within the templates.
| Name | Description |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `filepath` | The filepath to use when creating the new note. If the filepath is a relative filepath, it is relative to the current workspace. |
| `name` | A human readable name to show in the template picker. |
| `description` | A human readable description to show in the template picker. |
Foam-specific variables (e.g. `$FOAM_TITLE`) can be used within template metadata. However, VS Code snippet variables are ([currently](https://github.com/foambubble/foam/pull/655)) not supported.
### `filepath` attribute
The `filepath` metadata attribute allows you to define a relative or absolute filepath to use when creating a note using the template.
If the filepath is a relative filepath, it is relative to the current workspace.
#### Example of relative `filepath`
For example, `filepath` can be used to customize `.foam/templates/new-note.md`, overriding the default `Foam: Create New Note` behaviour of opening the file in the same directory as the active file:
```yaml
---
# This will create the note in the "journal" subdirectory of the current workspace,
# regardless of which file is the active file.
foam_template:
filepath: 'journal/$FOAM_TITLE.md'
---
```
#### Example of absolute `filepath`
`filepath` can be an absolute filepath, so that the notes get created in the same location, regardless of which file or workspace the editor currently has open.
The format of an absolute filepath may vary depending on the filesystem used.
```yaml
---
foam_template:
# Unix / MacOS filesystems
filepath: '/Users/john.smith/foam/journal/$FOAM_TITLE.md'
# Windows filesystems
filepath: 'C:\Users\john.smith\Documents\foam\journal\$FOAM_TITLE.md'
---
```
### `name` and `description` attributes
These attributes provide a human readable name and description to be shown in the template picker (e.g. When a user uses the `Foam: Create New Note From Template` command):
![Template Picker annotated with attributes](../assets/images/template-picker-annotated.png)
### Adding template metadata to an existing YAML Frontmatter block
If your template already has a YAML Frontmatter block, you can add the Foam template metadata to it.
#### Limitations
Foam only supports adding the template metadata to *YAML* Frontmatter blocks. If the existing Frontmatter block uses some other format (e.g. JSON), you will have to add the template metadata to its own YAML Frontmatter block.
Further, the template metadata must be provided as a [YAML block mapping](https://yaml.org/spec/1.2/spec.html#id2798057), with the attributes placed on the lines immediately following the `foam_template` line:
```yaml
---
existing_frontmatter: "Existing Frontmatter block"
foam_template: # this is a YAML "Block" mapping ("Flow" mappings aren't supported)
name: My Note Template # Attributes must be on the lines immediately following `foam_template`
description: This is my note template
filepath: `journal/$FOAM_TITLE.md`
---
This is the rest of the template
```
Due to the technical limitations of parsing the complex YAML format, unless the metadata is provided this specific form, Foam is unable to correctly remove the template metadata before creating the resulting note.
If this limitation proves inconvenient to you, please let us know. We may be able to extend our parsing capabilities to cover your use case. In the meantime, you can add the template metadata without this limitation by providing it in its own YAML Frontmatter block.
### Adding template metadata to its own YAML Frontmatter block
You can add the template metadata to its own YAML Frontmatter block at the start of the template:
```yaml
---
foam_template:
name: My Note Template
description: This is my note template
filepath: `journal/$FOAM_TITLE.md`
---
This is the rest of the template
```
If the note already has a Frontmatter block, a Foam-specific Frontmatter block can be added to the start of the template. The Foam-specific Frontmatter block must always be placed at the very beginning of the file, and only whitespace can separate the two Frontmatter blocks.
```yaml
---
foam_template:
name: My Note Template
description: This is my note template
filepath: `journal/$FOAM_TITLE.md`
---
---
existing_frontmatter: "Existing Frontmatter block"
---
This is the rest of the template
```

View File

@@ -9,6 +9,8 @@ There are two ways of creating a tag:
- adding a `#tag` anywhere in the text of the note
- using the `tags: tag1, tag2` property in frontmatter
Tags can also be hierarchical, so you can have `#parent/child`.
## Navigating tags
It's possible to navigate tags via the Tag Explorer panel.

View File

@@ -201,6 +201,7 @@ If that sounds like something you're interested in, I'd love to have you along o
<td align="center"><a href="https://github.com/daniel-vera-g"><img src="https://avatars.githubusercontent.com/u/28257108?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Daniel VG</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=daniel-vera-g" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Barabazs"><img src="https://avatars.githubusercontent.com/u/31799121?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Barabas</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Barabazs" title="Code">💻</a></td>
<td align="center"><a href="http://enginveske@gmail.com"><img src="https://avatars.githubusercontent.com/u/43685404?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Engincan VESKE</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=EngincanV" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.paulderaaij.nl"><img src="https://avatars.githubusercontent.com/u/495374?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul de Raaij</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=pderaaij" title="Code">💻</a></td>
</tr>
</table>

View File

@@ -37,7 +37,7 @@ This principle may seem like it contradicts [Foam wants you to own your thoughts
- **Foam is a collection of ideas.** Foam was released to the public not to share the few good ideas in it, but to learn many good ideas from others. As you improve your own workflow, share your work on your own Foam blog.
- **Foam is open for contributions.** If you use a tool or workflow that you like that fits these principles, please contribute them back to the Foam template as [[recipes]], [[recommended-extensions]] or documentation in [this workspace](https://github.com/foambubble/foam). See also: [[contribution-guide]].
- **Foam is open source.** Feel free to fork it, improve it and remix it. Just don't sell it, as per our [license](license).
- **Foam is open source.** Feel free to fork it, improve it and remix it. Just don't sell it, as per our [license](LICENSE.txt).
- **Foam is not Roam.** This project was inspired by Roam Research, but we're not limited by what Roam does. No idea is too big (though if it doesn't fit with Foam's core workflow, we might make it a [[recipes]] page instead).
## Foam is for hackers, not only for programmers

View File

@@ -4,5 +4,5 @@
],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.13.3"
"version": "0.13.5"
}

View File

@@ -1,7 +1,7 @@
{
"name": "foam-core",
"repository": "https://github.com/foambubble/foam",
"version": "0.13.3",
"version": "0.13.4",
"license": "MIT",
"files": [
"dist"
@@ -33,7 +33,7 @@
"fast-array-diff": "^1.0.0",
"github-slugger": "^1.3.0",
"glob": "^7.1.6",
"lodash": "^4.17.19",
"lodash": "^4.17.21",
"micromatch": "^4.0.2",
"remark-frontmatter": "^2.0.0",
"remark-parse": "^8.0.2",

View File

@@ -177,7 +177,7 @@ const titlePlugin: ParserPlugin = {
},
onDidFindProperties: (props, note) => {
// Give precendence to the title from the frontmatter if it exists
note.title = props.title ?? note.title;
note.title = props.title?.toString() ?? note.title;
},
onDidVisitTree: (tree, note) => {
if (note.title === '') {
@@ -313,8 +313,6 @@ export function createMarkdownParser(
...note.properties,
...yamlProperties,
};
// Give precendence to the title from the frontmatter if it exists
note.title = note.properties.title ?? note.title;
// Update the start position of the note by exluding the metadata
note.source.contentStart = Position.create(
node.position!.end.line! + 2,

View File

@@ -1,6 +1,6 @@
import { isSome } from './core';
const HASHTAG_REGEX = /(^|\s)#([0-9]*[\p{L}_-][\p{L}\p{N}_-]*)/gmu;
const WORD_REGEX = /(^|\s)([0-9]*[\p{L}_-][\p{L}\p{N}_-]*)/gmu;
const HASHTAG_REGEX = /(^|\s)#([0-9]*[\p{L}/_-][\p{L}\p{N}/_-]*)/gmu;
const WORD_REGEX = /(^|\s)([0-9]*[\p{L}/_-][\p{L}\p{N}/_-]*)/gmu;
export const extractHashtags = (text: string): Set<string> => {
return isSome(text)

View File

@@ -171,6 +171,26 @@ date: 20-12-12
expect(note.title).toBe('Note Title');
});
it('should support numbers', () => {
const note1 = createNoteFromMarkdown('/157.md', `hello`);
expect(note1.title).toBe('157');
const note2 = createNoteFromMarkdown('/157.md', `# 158`);
expect(note2.title).toBe('158');
const note3 = createNoteFromMarkdown(
'/157.md',
`
---
title: 159
---
# 158
`
);
expect(note3.title).toBe('159');
});
it('should not break on empty titles (see #276)', () => {
const note = createNoteFromMarkdown(
'/Hello Page.md',
@@ -316,6 +336,19 @@ this is some #text that includes #tags we #care-about.
new Set(['text', 'tags', 'care-about', 'hello', 'world', 'this_is_good'])
);
});
it('can find nested tags as array in yaml', () => {
const noteA = createNoteFromMarkdown(
'/dir1/page-a.md',
`
---
tags: [hello, world, parent/child]
---
# this is a heading
`
);
expect(noteA.tags).toEqual(new Set(['hello', 'world', 'parent/child']));
});
});
describe('parser plugins', () => {

View File

@@ -19,6 +19,11 @@ describe('hashtag extraction', () => {
new Set(['hello-world', 'this_planet'])
);
});
it('supports nested tags', () => {
expect(extractHashtags('#parent/child on #planet')).toEqual(
new Set(['parent/child', 'planet'])
);
});
it('ignores tags that only have numbers in text', () => {
expect(
extractHashtags('this #123 tag should be ignore, but not #123four')

View File

@@ -3870,9 +3870,9 @@ has@^1.0.3:
function-bind "^1.1.1"
hosted-git-info@^2.1.4:
version "2.8.8"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
html-encoding-sniffer@^1.0.2:
version "1.0.2"
@@ -4944,10 +4944,10 @@ lodash.sortby@^4.7.0:
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4:
version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-symbols@^2.2.0:
version "2.2.0"

View File

@@ -4,6 +4,26 @@ 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.13.5] - 2021-06-05
Fixes and Improvements:
- Improved support for nested tags (#661 - thanks @pderaaij)
- Allow YAML metadata in templates (#655 - thanks @movermeyer)
- Fixed template exclusion globs (#665)
## [0.13.4] - 2021-05-26
Fixes and Improvements:
- Added support for nested tags (#643 - thanks @pderaaij)
- Improved the flow of creating note from template (#645 - thanks @movermeyer)
- Fixed handling of title property in YAML (#647 - thanks @pderaaij and #546)
Internal:
- Updated various dependencies
## [0.13.3] - 2021-05-09
Fixes and Improvements:

View File

@@ -8,7 +8,7 @@
"type": "git"
},
"homepage": "https://github.com/foambubble/foam",
"version": "0.13.3",
"version": "0.13.5",
"license": "MIT",
"publisher": "foam",
"engines": {
@@ -395,7 +395,7 @@
},
"dependencies": {
"dateformat": "^3.0.3",
"foam-core": "^0.13.3",
"foam-core": "^0.13.4",
"gray-matter": "^4.0.2",
"markdown-it-regex": "^0.2.0",
"micromatch": "^4.0.2",

View File

@@ -1,9 +1,13 @@
import { SnippetString, window } from 'vscode';
import { window, workspace } from 'vscode';
import {
resolveFoamVariables,
resolveFoamTemplateVariables,
substituteFoamVariables,
determineDefaultFilepath,
} from './create-from-template';
import path from 'path';
import { isWindows } from '../utils';
import { URI } from 'foam-core';
describe('substituteFoamVariables', () => {
test('Does nothing if no Foam-specific variables are used', () => {
@@ -58,15 +62,15 @@ describe('resolveFoamVariables', () => {
});
test('Resolves FOAM_TITLE', async () => {
const foam_title = 'My note title';
const foamTitle = 'My note title';
const variables = ['FOAM_TITLE'];
jest
.spyOn(window, 'showInputBox')
.mockImplementationOnce(jest.fn(() => Promise.resolve(foam_title)));
.mockImplementationOnce(jest.fn(() => Promise.resolve(foamTitle)));
const expected = new Map<string, string>();
expected.set('FOAM_TITLE', foam_title);
expected.set('FOAM_TITLE', foamTitle);
const givenValues = new Map<string, string>();
expect(await resolveFoamVariables(variables, givenValues)).toEqual(
@@ -75,14 +79,14 @@ describe('resolveFoamVariables', () => {
});
test('Resolves FOAM_TITLE without asking the user when it is provided', async () => {
const foam_title = 'My note title';
const foamTitle = 'My note title';
const variables = ['FOAM_TITLE'];
const expected = new Map<string, string>();
expected.set('FOAM_TITLE', foam_title);
expected.set('FOAM_TITLE', foamTitle);
const givenValues = new Map<string, string>();
givenValues.set('FOAM_TITLE', foam_title);
givenValues.set('FOAM_TITLE', foamTitle);
expect(await resolveFoamVariables(variables, givenValues)).toEqual(
expected
);
@@ -100,8 +104,8 @@ describe('resolveFoamTemplateVariables', () => {
`;
const expectedMap = new Map<string, string>();
const expectedSnippet = new SnippetString(input);
const expected = [expectedMap, expectedSnippet];
const expectedString = input;
const expected = [expectedMap, expectedString];
expect(await resolveFoamTemplateVariables(input)).toEqual(expected);
});
@@ -115,9 +119,75 @@ describe('resolveFoamTemplateVariables', () => {
`;
const expectedMap = new Map<string, string>();
const expectedSnippet = new SnippetString(input);
const expected = [expectedMap, expectedSnippet];
const expectedString = input;
const expected = [expectedMap, expectedString];
expect(await resolveFoamTemplateVariables(input)).toEqual(expected);
});
test('Allows extra variables to be provided; only resolves the unique set', async () => {
const foamTitle = 'My note title';
jest
.spyOn(window, 'showInputBox')
.mockImplementationOnce(jest.fn(() => Promise.resolve(foamTitle)));
const input = `
# $FOAM_TITLE
`;
const expectedOutput = `
# My note title
`;
const expectedMap = new Map<string, string>();
expectedMap.set('FOAM_TITLE', foamTitle);
const expected = [expectedMap, expectedOutput];
expect(
await resolveFoamTemplateVariables(input, new Set(['FOAM_TITLE']))
).toEqual(expected);
});
});
describe('determineDefaultFilepath', () => {
test('Absolute filepath metadata is unchanged', () => {
const absolutePath = isWindows
? 'c:\\absolute_path\\journal\\My Note Title.md'
: '/absolute_path/journal/My Note Title.md';
const resolvedValues = new Map<string, string>();
const templateMetadata = new Map<string, string>();
templateMetadata.set('filepath', absolutePath);
const resultFilepath = determineDefaultFilepath(
resolvedValues,
templateMetadata
);
expect(URI.toFsPath(resultFilepath)).toMatch(absolutePath);
});
test('Relative filepath metadata is appended to current directory', () => {
const relativePath = isWindows
? 'journal\\My Note Title.md'
: 'journal/My Note Title.md';
const resolvedValues = new Map<string, string>();
const templateMetadata = new Map<string, string>();
templateMetadata.set('filepath', relativePath);
const resultFilepath = determineDefaultFilepath(
resolvedValues,
templateMetadata
);
const expectedPath = path.join(
workspace.workspaceFolders[0].uri.fsPath,
relativePath
);
expect(URI.toFsPath(resultFilepath)).toMatch(expectedPath);
});
});

View File

@@ -1,16 +1,19 @@
import {
window,
commands,
ExtensionContext,
workspace,
QuickPickItem,
SnippetString,
Uri,
window,
workspace,
} from 'vscode';
import * as path from 'path';
import { FoamFeature } from '../types';
import { TextEncoder } from 'util';
import { focusNote } from '../utils';
import { existsSync } from 'fs';
import { isAbsolute } from 'path';
import { extractFoamTemplateFrontmatterMetadata } from '../utils/template-frontmatter-parser';
const templatesDir = Uri.joinPath(
workspace.workspaceFolders[0].uri,
@@ -29,7 +32,13 @@ export class UserCancelledOperation extends Error {
const knownFoamVariables = new Set(['FOAM_TITLE']);
const defaultTemplateDefaultText: string = '# ${FOAM_TITLE}'; // eslint-disable-line no-template-curly-in-string
const defaultTemplateDefaultText: string = `---
foam_template:
name: New Note
description: Foam's default new note template
---
# \${FOAM_TITLE}
`;
const defaultTemplateUri = Uri.joinPath(templatesDir, 'new-note.md');
const templateContent = `# \${1:$TM_FILENAME_BASE}
@@ -51,9 +60,19 @@ For a full list of features see [the VS Code snippets page](https://code.visuals
2. create a note from this template by running the \`Foam: Create New Note From Template\` command
`;
async function getTemplates(): Promise<string[]> {
const templates = await workspace.findFiles('.foam/templates/**.md');
return templates.map(template => path.basename(template.path));
async function templateMetadata(
templateUri: Uri
): Promise<Map<string, string>> {
const contents = await workspace.fs
.readFile(templateUri)
.then(bytes => bytes.toString());
const [templateMetadata] = extractFoamTemplateFrontmatterMetadata(contents);
return templateMetadata;
}
async function getTemplates(): Promise<Uri[]> {
const templates = await workspace.findFiles('.foam/templates/**.md', null);
return templates;
}
async function offerToCreateTemplate(): Promise<void> {
@@ -153,12 +172,71 @@ export function substituteFoamVariables(
return templateText;
}
function sortTemplatesMetadata(
t1: Map<string, string>,
t2: Map<string, string>
) {
// Sort by name's existence, then name, then path
if (t1.get('name') === undefined && t2.get('name') !== undefined) {
return 1;
}
if (t1.get('name') !== undefined && t2.get('name') === undefined) {
return -1;
}
const pathSortOrder = t1
.get('templatePath')
.localeCompare(t2.get('templatePath'));
if (t1.get('name') === undefined && t2.get('name') === undefined) {
return pathSortOrder;
}
const nameSortOrder = t1.get('name').localeCompare(t2.get('name'));
return nameSortOrder || pathSortOrder;
}
async function askUserForTemplate() {
const templates = await getTemplates();
if (templates.length === 0) {
return offerToCreateTemplate();
}
return await window.showQuickPick(templates, {
const templatesMetadata = (
await Promise.all(
templates.map(async templateUri => {
const metadata = await templateMetadata(templateUri);
metadata.set('templatePath', path.basename(templateUri.path));
return metadata;
})
)
).sort(sortTemplatesMetadata);
const items: QuickPickItem[] = await Promise.all(
templatesMetadata.map(metadata => {
const label = metadata.get('name') || metadata.get('templatePath');
const description = metadata.get('name')
? metadata.get('templatePath')
: null;
const detail = metadata.get('description');
const item = {
label: label,
description: description,
detail: detail,
};
Object.keys(item).forEach(key => {
if (!item[key]) {
delete item[key];
}
});
return item;
})
);
return await window.showQuickPick(items, {
placeHolder: 'Select a template to use.',
});
}
@@ -184,18 +262,21 @@ async function askUserForFilepathConfirmation(
}
export async function resolveFoamTemplateVariables(
templateText: string
): Promise<[Map<string, string>, SnippetString]> {
templateText: string,
extraVariablesToResolve: Set<string> = new Set()
): Promise<[Map<string, string>, string]> {
const givenValues = new Map<string, string>();
const variables = findFoamVariables(templateText.toString());
const variables = findFoamVariables(templateText.toString()).concat(
...extraVariablesToResolve
);
const uniqVariables = [...new Set(variables)];
const resolvedValues = await resolveFoamVariables(variables, givenValues);
const resolvedValues = await resolveFoamVariables(uniqVariables, givenValues);
const subbedText = substituteFoamVariables(
templateText.toString(),
resolvedValues
);
const snippet = new SnippetString(subbedText);
return [resolvedValues, snippet];
return [resolvedValues, subbedText];
}
async function writeTemplate(templateSnippet: SnippetString, filepath: Uri) {
@@ -214,16 +295,43 @@ function currentDirectoryFilepath(filename: string) {
return Uri.joinPath(currentDir, filename);
}
export function determineDefaultFilepath(
resolvedValues: Map<string, string>,
templateMetadata: Map<string, string>
) {
let defaultFilepath: Uri;
if (templateMetadata.get('filepath')) {
const filepathFromMetadata = templateMetadata.get('filepath');
if (isAbsolute(filepathFromMetadata)) {
defaultFilepath = Uri.file(filepathFromMetadata);
} else {
defaultFilepath = Uri.joinPath(
workspace.workspaceFolders[0].uri,
filepathFromMetadata
);
}
} else {
const defaultSlug = resolvedValues.get('FOAM_TITLE') || 'New Note';
defaultFilepath = currentDirectoryFilepath(`${defaultSlug}.md`);
}
return defaultFilepath;
}
async function createNoteFromDefaultTemplate(): Promise<void> {
const templateUri = defaultTemplateUri;
const templateText = existsSync(templateUri.fsPath)
? await workspace.fs.readFile(templateUri).then(bytes => bytes.toString())
: defaultTemplateDefaultText;
let resolvedValues, templateSnippet;
let resolvedValues: Map<string, string>,
templateWithResolvedVariables: string;
try {
[resolvedValues, templateSnippet] = await resolveFoamTemplateVariables(
templateText
[
resolvedValues,
templateWithResolvedVariables,
] = await resolveFoamTemplateVariables(
templateText,
new Set(['FOAM_TITLE'])
);
} catch (err) {
if (err instanceof UserCancelledOperation) {
@@ -233,9 +341,17 @@ async function createNoteFromDefaultTemplate(): Promise<void> {
}
}
const defaultSlug = resolvedValues.get('FOAM_TITLE') || 'New Note';
const defaultFilename = `${defaultSlug}.md`;
const defaultFilepath = currentDirectoryFilepath(defaultFilename);
const [
templateMetadata,
templateWithFoamFrontmatterRemoved,
] = extractFoamTemplateFrontmatterMetadata(templateWithResolvedVariables);
const templateSnippet = new SnippetString(templateWithFoamFrontmatterRemoved);
const defaultFilepath = determineDefaultFilepath(
resolvedValues,
templateMetadata
);
const defaultFilename = path.basename(defaultFilepath.path);
let filepath = defaultFilepath;
if (existsSync(filepath.fsPath)) {
@@ -259,17 +375,20 @@ async function createNoteFromTemplate(
if (selectedTemplate === undefined) {
return;
}
templateFilename = selectedTemplate as string;
templateFilename =
(selectedTemplate as QuickPickItem).description ||
(selectedTemplate as QuickPickItem).label;
const templateUri = Uri.joinPath(templatesDir, templateFilename);
const templateText = await workspace.fs
.readFile(templateUri)
.then(bytes => bytes.toString());
let resolvedValues, templateSnippet;
let resolvedValues, templateWithResolvedVariables;
try {
[resolvedValues, templateSnippet] = await resolveFoamTemplateVariables(
templateText
);
[
resolvedValues,
templateWithResolvedVariables,
] = await resolveFoamTemplateVariables(templateText);
} catch (err) {
if (err instanceof UserCancelledOperation) {
return;
@@ -278,9 +397,17 @@ async function createNoteFromTemplate(
}
}
const defaultSlug = resolvedValues.get('FOAM_TITLE') || 'New Note';
const defaultFilename = `${defaultSlug}.md`;
const defaultFilepath = currentDirectoryFilepath(defaultFilename);
const [
templateMetadata,
templateWithFoamFrontmatterRemoved,
] = extractFoamTemplateFrontmatterMetadata(templateWithResolvedVariables);
const templateSnippet = new SnippetString(templateWithFoamFrontmatterRemoved);
const defaultFilepath = determineDefaultFilepath(
resolvedValues,
templateMetadata
);
const defaultFilename = path.basename(defaultFilepath.path);
const filepath = await askUserForFilepathConfirmation(
defaultFilepath,

View File

@@ -2,7 +2,6 @@ import * as vscode from 'vscode';
import markdownItRegex from 'markdown-it-regex';
import { Foam, FoamWorkspace, Logger, URI } from 'foam-core';
import { FoamFeature } from '../types';
import fp from 'lodash/fp';
const feature: FoamFeature = {
activate: async (
@@ -12,13 +11,11 @@ const feature: FoamFeature = {
const foam = await foamPromise;
return {
extendMarkdownIt: (md: markdownit) => {
const markdownItExtends = fp.compose(
markdownItWithFoamLinks,
markdownItWithFoamTags
);
return markdownItExtends(md, foam.workspace);
},
extendMarkdownIt: (md: markdownit) =>
[markdownItWithFoamTags, markdownItWithFoamLinks].reduce(
(acc, extension) => extension(acc, foam.workspace),
md
),
};
},
};

View File

@@ -0,0 +1,152 @@
import {
cleanWorkspace,
closeEditors,
createTestNote,
} from '../../test/test-utils';
import { Tag, TagReference, TagsProvider } from '.';
import {
bootstrap,
createConfigFromFolders,
Foam,
FileDataStore,
FoamConfig,
MarkdownResourceProvider,
Matcher,
} from 'foam-core';
describe('Tags tree panel', () => {
let _foam: Foam;
let provider: TagsProvider;
const config: FoamConfig = createConfigFromFolders([]);
const mdProvider = new MarkdownResourceProvider(
new Matcher(
config.workspaceFolders,
config.includeGlobs,
config.ignoreGlobs
)
);
beforeAll(async () => {
await cleanWorkspace();
});
afterAll(async () => {
_foam.dispose();
await cleanWorkspace();
});
beforeEach(async () => {
_foam = await bootstrap(config, new FileDataStore(), [mdProvider]);
provider = new TagsProvider(_foam, _foam.workspace);
await closeEditors();
});
afterEach(() => {
_foam.dispose();
});
it('correctly provides a tag from a set of notes', async () => {
const noteA = createTestNote({
tags: new Set(['test']),
uri: './note-a.md',
});
_foam.workspace.set(noteA);
provider.refresh();
const treeItems = (await provider.getChildren()) as Tag[];
treeItems.map(item => expect(item.tag).toContain('test'));
});
it('correctly handles a parent and child tag', async () => {
const noteA = createTestNote({
tags: new Set(['parent/child']),
uri: './note-a.md',
});
_foam.workspace.set(noteA);
provider.refresh();
const parentTreeItems = (await provider.getChildren()) as Tag[];
const parentTagItem = parentTreeItems.pop();
expect(parentTagItem.title).toEqual('parent');
const childTreeItems = (await provider.getChildren(parentTagItem)) as Tag[];
childTreeItems.forEach(child => {
if (child instanceof Tag) {
expect(child.title).toEqual('child');
}
});
});
it('correctly handles a single parent and multiple child tag', async () => {
const noteA = createTestNote({
tags: new Set(['parent/child']),
uri: './note-a.md',
});
_foam.workspace.set(noteA);
const noteB = createTestNote({
tags: new Set(['parent/subchild']),
uri: './note-b.md',
});
_foam.workspace.set(noteB);
provider.refresh();
const parentTreeItems = (await provider.getChildren()) as Tag[];
const parentTagItem = parentTreeItems.filter(
item => item instanceof Tag
)[0];
expect(parentTagItem.title).toEqual('parent');
expect(parentTreeItems).toHaveLength(1);
const childTreeItems = (await provider.getChildren(parentTagItem)) as Tag[];
childTreeItems.forEach(child => {
if (child instanceof Tag) {
expect(['child', 'subchild']).toContain(child.title);
expect(child.title).not.toEqual('parent');
}
});
expect(childTreeItems).toHaveLength(3);
});
it('correctly handles a single parent and child tag in the same note', async () => {
const noteC = createTestNote({
tags: new Set(['main', 'main/subtopic']),
title: 'Test note',
uri: './note-c.md',
});
_foam.workspace.set(noteC);
provider.refresh();
const parentTreeItems = (await provider.getChildren()) as Tag[];
const parentTagItem = parentTreeItems.filter(
item => item instanceof Tag
)[0];
expect(parentTagItem.title).toEqual('main');
const childTreeItems = (await provider.getChildren(parentTagItem)) as Tag[];
childTreeItems
.filter(item => item instanceof TagReference)
.forEach(item => {
expect(item.title).toEqual('Test note');
});
childTreeItems
.filter(item => item instanceof Tag)
.forEach(item => {
expect(['main/subtopic']).toContain(item.tag);
expect(item.title).toEqual('subtopic');
});
expect(childTreeItems).toHaveLength(3);
});
});

View File

@@ -3,6 +3,7 @@ import { Foam, Resource, URI, FoamWorkspace } from 'foam-core';
import { FoamFeature } from '../../types';
import { getNoteTooltip, getContainsTooltip, isSome } from '../../utils';
const TAG_SEPARATOR = '/';
const feature: FoamFeature = {
activate: async (
context: vscode.ExtensionContext,
@@ -67,19 +68,45 @@ export class TagsProvider implements vscode.TreeDataProvider<TagTreeItem> {
getChildren(element?: Tag): Thenable<TagTreeItem[]> {
if (element) {
const references: TagReference[] = element.notes
const nestedTagItems: TagTreeItem[] = this.tags
.filter(item => item.tag.indexOf(element.title + TAG_SEPARATOR) > -1)
.map(
item =>
new Tag(
item.tag,
item.tag.substring(item.tag.indexOf(TAG_SEPARATOR) + 1),
item.notes
)
)
.sort((a, b) => a.title.localeCompare(b.title));
const references: TagTreeItem[] = element.notes
.map(({ uri }) => this.foam.workspace.get(uri))
.map(note => new TagReference(element.tag, note));
.filter(note => note.tags.has(element.tag))
.map(note => new TagReference(element.tag, note))
.sort((a, b) => a.title.localeCompare(b.title));
return Promise.resolve([
new TagSearch(element.tag),
...references.sort((a, b) => a.title.localeCompare(b.title)),
new TagSearch(element.title),
...nestedTagItems,
...references,
]);
}
if (!element) {
const tags: Tag[] = this.tags.map(
({ tag, notes }) => new Tag(tag, notes)
);
const tags: Tag[] = this.tags
.map(({ tag, notes }) => {
const parentTag =
tag.indexOf(TAG_SEPARATOR) > 0
? tag.substring(0, tag.indexOf(TAG_SEPARATOR))
: tag;
return new Tag(parentTag, parentTag, notes);
})
.filter(
(value, index, array) =>
array.findIndex(tag => tag.title === value.title) === index
);
return Promise.resolve(tags.sort((a, b) => a.tag.localeCompare(b.tag)));
}
}
@@ -102,9 +129,10 @@ type TagMetadata = { title: string; uri: URI };
export class Tag extends vscode.TreeItem {
constructor(
public readonly tag: string,
public readonly title: string,
public readonly notes: TagMetadata[]
) {
super(tag, vscode.TreeItemCollapsibleState.Collapsed);
super(title, vscode.TreeItemCollapsibleState.Collapsed);
this.description = `${this.notes.length} reference${
this.notes.length !== 1 ? 's' : ''
}`;
@@ -116,6 +144,7 @@ export class Tag extends vscode.TreeItem {
}
export class TagSearch extends vscode.TreeItem {
public readonly title: string;
constructor(public readonly tag: string) {
super(`Search #${tag}`, vscode.TreeItemCollapsibleState.None);
const searchString = `#${tag}`;

View File

@@ -47,6 +47,7 @@ export const createTestNote = (params: {
title?: string;
definitions?: NoteLinkDefinition[];
links?: Array<{ slug: string } | { to: string }>;
tags?: Set<string>;
text?: string;
root?: URI;
}): Resource => {
@@ -57,7 +58,7 @@ export const createTestNote = (params: {
properties: {},
title: params.title ?? path.parse(strToUri(params.uri).path).base,
definitions: params.definitions ?? [],
tags: new Set(),
tags: params.tags ?? new Set(),
links: params.links
? params.links.map((link, index) => {
const range = Range.create(

View File

@@ -1,4 +1,4 @@
import { FoamGraph, FoamWorkspace, URI } from 'foam-core';
import { FoamWorkspace } from 'foam-core';
import { OPEN_COMMAND } from '../features/utility-commands';
import {
GroupedResoucesConfigGroupBy,

View File

@@ -0,0 +1,207 @@
import {
extractFoamTemplateFrontmatterMetadata,
removeFoamMetadata,
} from './template-frontmatter-parser';
describe('extractFoamTemplateFrontmatterMetadata', () => {
test('Returns an empty object if there is not frontmatter', () => {
const input = `# $FOAM_TITLE`;
const expectedMetadata = new Map<string, string>();
const expected = [expectedMetadata, input];
const result = extractFoamTemplateFrontmatterMetadata(input);
expect(result).toEqual(expected);
});
test('Returns an empty object if `foam_template` is not used', () => {
const input = `---
foo: bar
---
# $FOAM_TITLE
`;
const expectedMetadata = new Map<string, string>();
const expected = [expectedMetadata, input];
const result = extractFoamTemplateFrontmatterMetadata(input);
expect(result).toEqual(expected);
});
test('Returns an empty object if foam_template is not a YAML mapping', () => {
const input = `---json
{
"foo": "bar",
"foam_template": 4
}
---
# $FOAM_TITLE
`;
const expectedMetadata = new Map<string, string>();
const expected = [expectedMetadata, input];
const result = extractFoamTemplateFrontmatterMetadata(input);
expect(result).toEqual(expected);
});
test('Returns an empty object if frontmatter is not YAML', () => {
const input = `---json
{
"foo": "bar",
"foam_template": {
"filepath": "journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md"
}
}
---
# $FOAM_TITLE
`;
const expectedMetadata = new Map<string, string>();
const expected = [expectedMetadata, input];
const result = extractFoamTemplateFrontmatterMetadata(input);
expect(result).toEqual(expected);
});
test('Returns the `foam_template` metadata when it is used in its own frontmatter block', () => {
const input = `---
foam_template:
name: My Note Template
description: This is my note template
filepath: journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md
---
# $FOAM_TITLE
`;
const output = `
# $FOAM_TITLE
`;
const expectedMetadata = new Map<string, string>();
expectedMetadata.set('name', 'My Note Template');
expectedMetadata.set('description', 'This is my note template');
expectedMetadata.set(
'filepath',
'journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md'
);
const expected = [expectedMetadata, output];
const result = extractFoamTemplateFrontmatterMetadata(input);
expect(result).toEqual(expected);
});
test('Returns the `foam_template` metadata when it is used in its own frontmatter block (and there is another frontmatter block after)', () => {
const input = `---
foam_template:
filepath: journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md
description: This is my note template
name: My Note Template
---
---
foo: bar
# A YAML comment
metadata: &info
title: The Gentlemen
year: 2019
more_metadata: *info
---
# $FOAM_TITLE
`;
const output = `---
foo: bar
# A YAML comment
metadata: &info
title: The Gentlemen
year: 2019
more_metadata: *info
---
# $FOAM_TITLE
`;
const expectedMetadata = new Map<string, string>();
expectedMetadata.set('name', 'My Note Template');
expectedMetadata.set('description', 'This is my note template');
expectedMetadata.set(
'filepath',
'journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md'
);
const expected = [expectedMetadata, output];
const result = extractFoamTemplateFrontmatterMetadata(input);
expect(result).toEqual(expected);
});
test('Returns the `foam_template` metadata when it is used in a shared frontmatter block', () => {
const input = `---
foo: bar
foam_template:
name: My Note Template
filepath: journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md
description: This is my note template
# A YAML comment
metadata: &info
title: The Gentlemen
year: 2019
more_metadata: *info
---
# $FOAM_TITLE`;
const output = `---
foo: bar
# A YAML comment
metadata: &info
title: The Gentlemen
year: 2019
more_metadata: *info
---
# $FOAM_TITLE`;
const expectedMetadata = new Map<string, string>();
expectedMetadata.set('name', 'My Note Template');
expectedMetadata.set('description', 'This is my note template');
expectedMetadata.set(
'filepath',
'journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md'
);
const expected = [expectedMetadata, output];
const result = extractFoamTemplateFrontmatterMetadata(input);
expect(result).toEqual(expected);
});
});
describe('removeFoamMetadata', () => {
test('Removes Foam specific frontmatter without messing up non-Foam frontmatter', () => {
const input = `---
foo: bar
foam_template: &foam_template # A YAML comment
description: This is my note template
filepath: journal/$CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE_$FOAM_TITLE.md # A YAML comment
name: My Note Template
# A YAML comment
metadata: &info
title: The Gentlemen
year: 2019
more_metadata: *info
---
# $FOAM_TITLE`;
const expected = `---
foo: bar
# A YAML comment
metadata: &info
title: The Gentlemen
year: 2019
more_metadata: *info
---
# $FOAM_TITLE`;
const result = removeFoamMetadata(input);
expect(result).toEqual(expected);
});
});

View File

@@ -0,0 +1,57 @@
import matter from 'gray-matter';
export function extractFoamTemplateFrontmatterMetadata(
contents: string
): [Map<string, string>, string] {
// Need to pass in empty options object, in order to bust a cache
// See https://github.com/jonschlinkert/gray-matter/issues/124
const parsed = matter(contents, {});
let metadata = new Map<string, string>();
if (parsed.language !== 'yaml') {
// We might allow this in the future, once it has been tested adequately.
// But for now we'll be safe and prevent people from using anything else.
return [metadata, contents];
}
const frontmatter = parsed.data;
const frontmatterKeys = Object.keys(frontmatter);
const foamMetadata = frontmatter['foam_template'];
if (typeof foamMetadata !== 'object') {
return [metadata, contents];
}
const containsFoam = foamMetadata !== undefined;
const onlyFoam = containsFoam && frontmatterKeys.length === 1;
metadata = new Map<string, string>(
Object.entries((foamMetadata as object) || {})
);
let newContents = contents;
if (containsFoam) {
if (onlyFoam) {
// We'll remove the entire frontmatter block
newContents = parsed.content;
// If there is another frontmatter block, we need to remove
// the leading space left behind.
const anotherFrontmatter = matter(newContents.trimStart()).matter !== '';
if (anotherFrontmatter) {
newContents = newContents.trimStart();
}
} else {
// We'll remove only the Foam bits
newContents = removeFoamMetadata(contents);
}
}
return [metadata, newContents];
}
export function removeFoamMetadata(contents: string) {
return contents.replace(
/^\s*foam_template:.*?\n(?:\s*(?:filepath|name|description):.*\n)+/gm,
''
);
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<script data-replace src="./d3.v6.min.js"></script>
<script data-replace src="./force-graph.1.34.1.min.js"></script>
<script data-replace src="./force-graph.1.40.5.min.js"></script>
<style>
body {
overflow: hidden;

View File

@@ -2552,16 +2552,16 @@ flatted@^2.0.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
foam-core@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/foam-core/-/foam-core-0.12.0.tgz#2bbdc5b4883daba0b9ff038183013fab927a1451"
integrity sha512-R0pFgibZbp/4PcZ7F17n+OQrKvl/kuCoCkUB/dEtZSJD1n8ND1lZp7yllQT8Y8+S1EEzoJD46mK8DttTwAsdMg==
foam-core@^0.13.4:
version "0.13.4"
resolved "https://registry.yarnpkg.com/foam-core/-/foam-core-0.13.4.tgz#2520f9aa70f4fd23e1f6d8614445d44c68931034"
integrity sha512-5EydR6kIPAZdHSaTWVaQjLXb+GqFkV3jYW989RIRRX7ad7g0dzIigtd3iXtU2bUX6e+Wne7pjlxeLWvYICbvZA==
dependencies:
detect-newline "^3.1.0"
fast-array-diff "^1.0.0"
github-slugger "^1.3.0"
glob "^7.1.6"
lodash "^4.17.19"
lodash "^4.17.21"
micromatch "^4.0.2"
remark-frontmatter "^2.0.0"
remark-parse "^8.0.2"
@@ -2786,9 +2786,9 @@ has-values@^1.0.0:
kind-of "^4.0.0"
hosted-git-info@^2.1.4:
version "2.8.8"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
html-encoding-sniffer@^2.0.1:
version "2.0.1"
@@ -3822,16 +3822,11 @@ lodash.sortby@^4.7.0:
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
lodash@4.x:
lodash@4.x, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
loose-envify@^1.0.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
@@ -5586,9 +5581,9 @@ write@1.0.3:
mkdirp "^0.5.1"
ws@^7.2.3:
version "7.3.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
version "7.4.6"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
xml-name-validator@^3.0.0:
version "3.0.0"

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-74-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-75-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)
@@ -157,6 +157,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center"><a href="https://github.com/daniel-vera-g"><img src="https://avatars.githubusercontent.com/u/28257108?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Daniel VG</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=daniel-vera-g" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Barabazs"><img src="https://avatars.githubusercontent.com/u/31799121?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Barabas</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Barabazs" title="Code">💻</a></td>
<td align="center"><a href="http://enginveske@gmail.com"><img src="https://avatars.githubusercontent.com/u/43685404?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Engincan VESKE</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=EngincanV" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.paulderaaij.nl"><img src="https://avatars.githubusercontent.com/u/495374?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul de Raaij</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=pderaaij" title="Code">💻</a></td>
</tr>
</table>

View File

@@ -7356,7 +7356,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@4.x, lodash@^4.11.2, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4, lodash@^4.2.1:
lodash@4.x, lodash@^4.11.2, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.2.1:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==