mirror of
https://github.com/foambubble/foam.git
synced 2026-01-12 23:48:19 -05:00
Compare commits
24 Commits
chore/upda
...
v0.15.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f7b3b7c02 | ||
|
|
9ed0d6e18e | ||
|
|
0140748550 | ||
|
|
356dcc5579 | ||
|
|
265afdee19 | ||
|
|
de7c686f75 | ||
|
|
8dfc5bd2ff | ||
|
|
b3c5e75aa2 | ||
|
|
000da4bd1c | ||
|
|
86749940c2 | ||
|
|
27f9a08870 | ||
|
|
e791726692 | ||
|
|
a3c00744ca | ||
|
|
00220b1f6c | ||
|
|
759f4f1963 | ||
|
|
d86fc7f433 | ||
|
|
bd9c6806fa | ||
|
|
4c9a9cec56 | ||
|
|
8a91a6ab36 | ||
|
|
667037bc14 | ||
|
|
30cc9fc9f0 | ||
|
|
abed7be3ec | ||
|
|
d31e094358 | ||
|
|
f320af05c5 |
@@ -761,6 +761,33 @@
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "eltociear",
|
||||
"name": "Ikko Ashimine",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22633385?v=4",
|
||||
"profile": "https://bandism.net/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "memeplex",
|
||||
"name": "memeplex",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2845433?v=4",
|
||||
"profile": "https://github.com/memeplex",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "AndreiD049",
|
||||
"name": "AndreiD049",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/52671223?v=4",
|
||||
"profile": "https://github.com/AndreiD049",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -41,12 +41,42 @@ Templates can use all the variables available in [VS Code Snippets](https://code
|
||||
|
||||
In addition, you can also use variables provided by Foam:
|
||||
|
||||
| Name | Description |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `FOAM_SELECTED_TEXT` | Foam will fill it with selected text when creating a new note, if any text is selected. Selected text will be replaced with a wikilink to the new note. |
|
||||
| `FOAM_TITLE` | The title of the note. If used, Foam will prompt you to enter a title for the note. |
|
||||
| Name | Description |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `FOAM_SELECTED_TEXT` | Foam will fill it with selected text when creating a new note, if any text is selected. Selected text will be replaced with a wikilink to the new note. |
|
||||
| `FOAM_TITLE` | The title of the note. If used, Foam will prompt you to enter a title for the note. |
|
||||
| `FOAM_DATE_*` | `FOAM_DATE_YEAR`, `FOAM_DATE_MONTH`, etc. Foam-specific versions of [VS Code's datetime snippet variables](https://code.visualstudio.com/docs/editor/userdefinedsnippets#_variables). Prefer these versions over VS Code's. |
|
||||
|
||||
**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.
|
||||
**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. See [#693](https://github.com/foambubble/foam/issues/693).
|
||||
|
||||
### `FOAM_DATE_*` variables
|
||||
|
||||
Foam defines its own set of datetime variables that have a similar behaviour as [VS Code's datetime snippet variables](https://code.visualstudio.com/docs/editor/userdefinedsnippets#_variables).
|
||||
|
||||
For example, `FOAM_DATE_YEAR` has the same behaviour as VS Code's `CURRENT_YEAR`, `FOAM_DATE_SECONDS_UNIX` has the same behaviour as `CURRENT_SECONDS_UNIX`, etc.
|
||||
|
||||
By default, prefer using the `FOAM_DATE_` versions. The datetime used to compute the values will be the same for both `FOAM_DATE_` and VS Code's variables, with the exception of the creation notes using the daily note template.
|
||||
|
||||
#### Relative daily notes
|
||||
|
||||
When referring to daily notes, you can use the relative snippets (`/+1d`, `/tomorrow`, etc.). In these cases, the new notes will be created with the daily note template, but the datetime used should be the relative datetime, not the current datetime.
|
||||
By using the `FOAM_DATE_` versions of the variables, the correct relative date will populate the variables, instead of the current datetime.
|
||||
|
||||
For example, given this daily note template (`.foam/templates/daily-note.md`):
|
||||
|
||||
```markdown
|
||||
# $FOAM_DATE_YEAR-$FOAM_DATE_MONTH-$FOAM_DATE_DATE
|
||||
|
||||
## Here's what I'm going to do today
|
||||
|
||||
* Thing 1
|
||||
* Thing 2
|
||||
```
|
||||
|
||||
When the `/tomorrow` snippet is used, `FOAM_DATE_` variables will be populated with tomorrow's date, as expected.
|
||||
If instead you were to use the VS Code versions of these variables, they would be populated with today's date, not tomorrow's, causing unexpected behaviour.
|
||||
|
||||
When creating notes in any other scenario, the `FOAM_DATE_` values are computed using the same datetime as the VS Code ones, so the `FOAM_DATE_` versions can be used in all scenarios by default.
|
||||
|
||||
## Metadata
|
||||
|
||||
|
||||
@@ -213,6 +213,11 @@ If that sounds like something you're interested in, I'd love to have you along o
|
||||
<td align="center"><a href="http://www.prashu.com"><img src="https://avatars.githubusercontent.com/u/476729?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Prashanth Subrahmanyam</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ksprashu" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/JonasSprenger"><img src="https://avatars.githubusercontent.com/u/25108895?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jonas SPRENGER</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=JonasSprenger" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/Laptop765"><img src="https://avatars.githubusercontent.com/u/1468359?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Laptop765" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=eltociear" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/memeplex"><img src="https://avatars.githubusercontent.com/u/2845433?v=4?s=60" width="60px;" alt=""/><br /><sub><b>memeplex</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=memeplex" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/AndreiD049"><img src="https://avatars.githubusercontent.com/u/52671223?v=4?s=60" width="60px;" alt=""/><br /><sub><b>AndreiD049</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=AndreiD049" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ Once you've opened/created the Foam repository, it will appear in the `Repositor
|
||||
|
||||
## Editing your workspace
|
||||
|
||||
When you create or open a page, you can edit the markdown content as usual, as well as [paste images](https://github.com/vsls-contrib/gistpad#pasting-images-1), and create [`[[links]]` to other pages](https://github.com/vsls-contrib/gistpad#links). When you type `[[`, you'll recieve auto-completion for the existing pages in your workspace, and you can also automatically create new pages by simply creating a link to it.
|
||||
When you create or open a page, you can edit the markdown content as usual, as well as [paste images](https://github.com/vsls-contrib/gistpad#pasting-images-1), and create [`[[links]]` to other pages](https://github.com/vsls-contrib/gistpad#links). When you type `[[`, you'll receive auto-completion for the existing pages in your workspace, and you can also automatically create new pages by simply creating a link to it.
|
||||
|
||||
Since you're using the Visual Studio Code markdown editor, you can benefit from all of the rich language services (e.g. syntax highlighting, header collapsing), as well as the extension ecosystem (e.g. [Emojisense](https://marketplace.visualstudio.com/items?itemName=bierner.emojisense)).
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
Foam enables you to Link pages together using `[[file-name]]` annotations (i.e. `[[MediaWiki]]` links).
|
||||
|
||||
- Type `[[` and start typing a file name for autocompletion.
|
||||
- Note that your file names should be in `lower-dash-case.md`, and your wikilinks should reference file names exactly: `[[lower-dash-case]]`, not `[[Lower Dash Case]]`.
|
||||
- See [[link-formatting-and-autocompletion]] for more information, and how to setup your link autocompletions to make this easier.
|
||||
- `Cmd` + `Click` ( `Ctrl` + `Click` on Windows ) on file name to navigate to file (`F12` also works while your cursor is on the file name)
|
||||
- `Cmd` + `Click` ( `Ctrl` + `Click` on Windows ) on non-existent file to create that file in the workspace.
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
],
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.15.1"
|
||||
"version": "0.15.4"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,33 @@ 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.15.4] - 2021-11-09
|
||||
|
||||
Fixes and Improvements:
|
||||
|
||||
- Detached Foam URI from VS Code URI. This should improve several path related issues in Windows. Given how core this change is, the release is just about this refactoring to easily detect possible side effects.
|
||||
|
||||
## [0.15.3] - 2021-11-08
|
||||
|
||||
Fixes and Improvements:
|
||||
|
||||
- Avoid delaying decorations on editor switch (#811 - thanks @memeplex)
|
||||
- Fix preview issue when embedding a note and using reference definitions (#808 - thanks @pderaaij)
|
||||
|
||||
## [0.15.2] - 2021-10-27
|
||||
|
||||
Features:
|
||||
|
||||
- Added `FOAM_DATE_*` template variables (#781)
|
||||
|
||||
Fixes and Improvements:
|
||||
|
||||
- Dataviz: apply note type color to filter item label
|
||||
- Dataviz: optimized rendering of graph to reduce load on CPU (#795)
|
||||
- Preview: improved tag highlight in preview (#785 - thanks @pderaaij)
|
||||
- Better handling of link reference definition (#786 - thanks @pderaaij)
|
||||
- Link decorations are now enabled by default (can be turned off in settings)
|
||||
|
||||
## [0.15.1] - 2021-10-21
|
||||
|
||||
Fixes and Improvements:
|
||||
@@ -25,7 +52,7 @@ Fixes and Improvements:
|
||||
|
||||
## [0.14.2] - 2021-07-24
|
||||
|
||||
Features:
|
||||
Features:
|
||||
|
||||
- Autocompletion for tags (#708 - thanks @pderaaij)
|
||||
- Use templates for new note created from wikilink (#712 - thanks @movermeyer)
|
||||
@@ -42,7 +69,7 @@ Fixes and Improvements:
|
||||
|
||||
## [0.14.0] - 2021-07-13
|
||||
|
||||
Features:
|
||||
Features:
|
||||
|
||||
- Create new note from selection (#666 - thanks @pderaaij)
|
||||
- Use templates for daily notes (#700 - thanks @movermeyer)
|
||||
@@ -70,9 +97,9 @@ Fixes and Improvements:
|
||||
|
||||
- Fixed #667, incorrect resolution of foam-core library
|
||||
|
||||
Internal:
|
||||
Internal:
|
||||
|
||||
- BREAKING CHANGE: Removed Foam local plugins
|
||||
- BREAKING CHANGE: Removed Foam local plugins
|
||||
If you were previously using the alpha feature of Foam local plugins you will soon be able to migrate the functionality to the V1 API
|
||||
|
||||
## [0.13.6] - 2021-06-05
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
"type": "git"
|
||||
},
|
||||
"homepage": "https://github.com/foambubble/foam",
|
||||
"version": "0.15.1",
|
||||
"version": "0.15.4",
|
||||
"license": "MIT",
|
||||
"publisher": "foam",
|
||||
"engines": {
|
||||
"vscode": "^1.54.0"
|
||||
"vscode": "^1.47.1"
|
||||
},
|
||||
"icon": "icon/FOAM_ICON_256.png",
|
||||
"categories": [
|
||||
@@ -361,8 +361,8 @@
|
||||
"build": "tsc -p ./",
|
||||
"pretest": "yarn build",
|
||||
"test": "node ./out/test/run-tests.js",
|
||||
"unit": "node ./out/test/run-tests.js --unit",
|
||||
"e2e": "node ./out/test/run-tests.js --e2e",
|
||||
"test:unit": "node ./out/test/run-tests.js --unit",
|
||||
"test:e2e": "node ./out/test/run-tests.js --e2e",
|
||||
"lint": "tsdx lint src",
|
||||
"clean": "rimraf out",
|
||||
"watch": "tsc --build ./tsconfig.json --watch",
|
||||
@@ -390,7 +390,7 @@
|
||||
"@types/node": "^13.11.0",
|
||||
"@types/picomatch": "^2.2.1",
|
||||
"@types/remove-markdown": "^0.1.1",
|
||||
"@types/vscode": "^1.54.0",
|
||||
"@types/vscode": "^1.47.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.30.0",
|
||||
"@typescript-eslint/parser": "^2.30.0",
|
||||
"babel-jest": "^26.2.2",
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('generateLinkReferences', () => {
|
||||
});
|
||||
|
||||
it('initialised test graph correctly', () => {
|
||||
expect(_workspace.list().length).toEqual(6);
|
||||
expect(_workspace.list().length).toEqual(10);
|
||||
});
|
||||
|
||||
it('should add link references to a file that does not have them', () => {
|
||||
@@ -105,6 +105,54 @@ describe('generateLinkReferences', () => {
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should put links with spaces in angel brackets', () => {
|
||||
const note = findBySlug('angel-reference');
|
||||
const expected = {
|
||||
newText: textForNote(
|
||||
note,
|
||||
`
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[Note being refered as angel]: <Note being refered as angel> "Note being refered as angel"
|
||||
[//end]: # "Autogenerated link references"`
|
||||
),
|
||||
range: Range.create(3, 0, 3, 0),
|
||||
};
|
||||
|
||||
const actual = generateLinkReferences(note, _workspace, false);
|
||||
|
||||
expect(actual!.range.start).toEqual(expected.range.start);
|
||||
expect(actual!.range.end).toEqual(expected.range.end);
|
||||
expect(actual!.newText).toEqual(expected.newText);
|
||||
});
|
||||
|
||||
it('should not remove explicitly entered link references', () => {
|
||||
const note = findBySlug('file-with-explicit-link-references');
|
||||
const expected = null;
|
||||
|
||||
const actual = generateLinkReferences(note, _workspace, false);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should not remove explicitly entered link references and have an implicit link', () => {
|
||||
const note = findBySlug('file-with-explicit-and-implicit-link-references');
|
||||
const expected = {
|
||||
newText: textForNote(
|
||||
note,
|
||||
`[^footerlink]: https://foambubble.github.io/
|
||||
[linkrefenrece]: https://foambubble.github.io/
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[first-document]: first-document "First Document"
|
||||
[//end]: # "Autogenerated link references"`
|
||||
),
|
||||
range: Range.create(5, 0, 10, 42),
|
||||
};
|
||||
|
||||
const actual = generateLinkReferences(note, _workspace, false);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -59,17 +59,78 @@ export const generateLinkReferences = (
|
||||
} else {
|
||||
const first = note.definitions[0];
|
||||
const last = note.definitions[note.definitions.length - 1];
|
||||
|
||||
var nonGeneratedReferenceDefinitions = note.definitions;
|
||||
|
||||
// if we have more definitions then referenced pages AND the page refers to a page
|
||||
// we expect non-generated link definitions to be present
|
||||
// Collect all non-generated definitions, by removing the generated ones
|
||||
if (
|
||||
note.definitions.length > markdownReferences.length &&
|
||||
markdownReferences.length > 0
|
||||
) {
|
||||
// remove all autogenerated definitions
|
||||
const beginIndex = note.definitions.findIndex(
|
||||
({ label }) => label === '//begin'
|
||||
);
|
||||
const endIndex = note.definitions.findIndex(
|
||||
({ label }) => label === '//end'
|
||||
);
|
||||
|
||||
const generatedDefinitions = [...note.definitions].splice(
|
||||
beginIndex,
|
||||
endIndex - beginIndex + 1
|
||||
);
|
||||
|
||||
nonGeneratedReferenceDefinitions = note.definitions.filter(
|
||||
x => !generatedDefinitions.includes(x)
|
||||
);
|
||||
}
|
||||
|
||||
// When we only have explicitly defined link definitions &&
|
||||
// no indication of previously defined generated links &&
|
||||
// there is no reference to another page, return null
|
||||
if (
|
||||
nonGeneratedReferenceDefinitions.length > 0 &&
|
||||
note.definitions.findIndex(({ label }) => label === '//begin') < 0 &&
|
||||
markdownReferences.length === 0
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Format link definitions for non-generated links
|
||||
const nonGeneratedReferences = nonGeneratedReferenceDefinitions
|
||||
.map(stringifyMarkdownLinkReferenceDefinition)
|
||||
.join(note.source.eol);
|
||||
|
||||
const oldReferences = note.definitions
|
||||
.map(stringifyMarkdownLinkReferenceDefinition)
|
||||
.join(note.source.eol);
|
||||
|
||||
if (oldReferences === newReferences) {
|
||||
// When the newly formatted references match the old ones, OR
|
||||
// when non-generated references are present, but no new ones are generated
|
||||
// return null
|
||||
if (
|
||||
oldReferences === newReferences ||
|
||||
(nonGeneratedReferenceDefinitions.length > 0 &&
|
||||
newReferences === '' &&
|
||||
markdownReferences.length > 0)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var fullReferences = `${newReferences}`;
|
||||
// If there are any non-generated definitions, add those to the output as well
|
||||
if (
|
||||
nonGeneratedReferenceDefinitions.length > 0 &&
|
||||
markdownReferences.length > 0
|
||||
) {
|
||||
fullReferences = `${nonGeneratedReferences}${note.source.eol}${newReferences}`;
|
||||
}
|
||||
|
||||
return {
|
||||
// @todo: do we need to ensure new lines?
|
||||
newText: `${newReferences}`,
|
||||
newText: `${fullReferences}`,
|
||||
range: Range.createFromPosition(first.range!.start, last.range!.end),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -441,7 +441,9 @@ function getFoamDefinitions(
|
||||
export function stringifyMarkdownLinkReferenceDefinition(
|
||||
definition: NoteLinkDefinition
|
||||
) {
|
||||
let text = `[${definition.label}]: ${definition.url}`;
|
||||
let url =
|
||||
definition.url.indexOf(' ') > 0 ? `<${definition.url}>` : definition.url;
|
||||
let text = `[${definition.label}]: ${url}`;
|
||||
if (definition.title) {
|
||||
text = `${text} "${definition.title}"`;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { URI } from './uri';
|
||||
|
||||
Logger.setLevel('error');
|
||||
|
||||
describe('Foam URIs', () => {
|
||||
describe('Foam URI', () => {
|
||||
describe('URI parsing', () => {
|
||||
const base = URI.file('/path/to/file.md');
|
||||
test.each([
|
||||
@@ -24,20 +24,45 @@ describe('Foam URIs', () => {
|
||||
expect(result.query).toEqual(exp.query);
|
||||
expect(result.fragment).toEqual(exp.fragment);
|
||||
});
|
||||
});
|
||||
it('supports various cases', () => {
|
||||
expect(uriToSlug(URI.file('/this/is/a/path.md'))).toEqual('path');
|
||||
expect(uriToSlug(URI.file('../a/relative/path.md'))).toEqual('path');
|
||||
expect(uriToSlug(URI.file('another/relative/path.md'))).toEqual('path');
|
||||
expect(uriToSlug(URI.file('no-directory.markdown'))).toEqual(
|
||||
'no-directory'
|
||||
);
|
||||
expect(uriToSlug(URI.file('many.dots.name.markdown'))).toEqual(
|
||||
'manydotsname'
|
||||
);
|
||||
|
||||
it('normalizes the Windows drive letter to upper case', () => {
|
||||
const upperCase = URI.parse('file:///C:/this/is/a/Path');
|
||||
const lowerCase = URI.parse('file:///c:/this/is/a/Path');
|
||||
expect(upperCase.path).toEqual('/C:/this/is/a/Path');
|
||||
expect(lowerCase.path).toEqual('/C:/this/is/a/Path');
|
||||
expect(URI.toFsPath(upperCase)).toEqual('C:\\this\\is\\a\\Path');
|
||||
expect(URI.toFsPath(lowerCase)).toEqual('C:\\this\\is\\a\\Path');
|
||||
});
|
||||
|
||||
it('consistently parses file paths', () => {
|
||||
const win1 = URI.file('c:\\this\\is\\a\\path');
|
||||
const win2 = URI.parse('c:\\this\\is\\a\\path');
|
||||
expect(win1).toEqual(win2);
|
||||
|
||||
const unix1 = URI.file('/this/is/a/path');
|
||||
const unix2 = URI.parse('/this/is/a/path');
|
||||
expect(unix1).toEqual(unix2);
|
||||
});
|
||||
|
||||
it('correctly parses file paths', () => {
|
||||
const winUri = URI.file('c:\\this\\is\\a\\path');
|
||||
const unixUri = URI.file('/this/is/a/path');
|
||||
expect(winUri).toEqual(
|
||||
URI.create({
|
||||
scheme: 'file',
|
||||
path: '/C:/this/is/a/path',
|
||||
})
|
||||
);
|
||||
expect(unixUri).toEqual(
|
||||
URI.create({
|
||||
scheme: 'file',
|
||||
path: '/this/is/a/path',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('computes a relative uri using a slug', () => {
|
||||
it('supports computing relative paths', () => {
|
||||
expect(
|
||||
URI.computeRelativeURI(URI.file('/my/file.md'), '../hello.md')
|
||||
).toEqual(URI.file('/hello.md'));
|
||||
@@ -48,4 +73,16 @@ describe('Foam URIs', () => {
|
||||
URI.computeRelativeURI(URI.file('/my/file.markdown'), '../hello')
|
||||
).toEqual(URI.file('/hello.markdown'));
|
||||
});
|
||||
|
||||
it('can be slugified', () => {
|
||||
expect(uriToSlug(URI.file('/this/is/a/path.md'))).toEqual('path');
|
||||
expect(uriToSlug(URI.file('../a/relative/path.md'))).toEqual('path');
|
||||
expect(uriToSlug(URI.file('another/relative/path.md'))).toEqual('path');
|
||||
expect(uriToSlug(URI.file('no-directory.markdown'))).toEqual(
|
||||
'no-directory'
|
||||
);
|
||||
expect(uriToSlug(URI.file('many.dots.name.markdown'))).toEqual(
|
||||
'manydotsname'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
import * as paths from 'path';
|
||||
import { CharCode } from '../common/charCode';
|
||||
import { isWindows } from '../common/platform';
|
||||
|
||||
/**
|
||||
* Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
|
||||
@@ -39,6 +38,9 @@ const _regexp = /^(([^:/?#]{2,}?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
|
||||
|
||||
export abstract class URI {
|
||||
static create(from: Partial<URI>): URI {
|
||||
// When using this method we assume the path is already posix
|
||||
// so we don't check whether it's a Windows path, nor we do any
|
||||
// conversion
|
||||
return {
|
||||
scheme: from.scheme ?? _empty,
|
||||
authority: from.authority ?? _empty,
|
||||
@@ -53,10 +55,14 @@ export abstract class URI {
|
||||
if (!match) {
|
||||
return URI.create({});
|
||||
}
|
||||
let path = percentDecode(match[5] ?? _empty);
|
||||
if (URI.isWindowsPath(path)) {
|
||||
path = windowsPathToUriPath(path);
|
||||
}
|
||||
return URI.create({
|
||||
scheme: match[2] || 'file',
|
||||
authority: percentDecode(match[4] ?? _empty),
|
||||
path: percentDecode(match[5] ?? _empty),
|
||||
path: path,
|
||||
query: percentDecode(match[7] ?? _empty),
|
||||
fragment: percentDecode(match[9] ?? _empty),
|
||||
});
|
||||
@@ -104,12 +110,8 @@ export abstract class URI {
|
||||
// normalize to fwd-slashes on windows,
|
||||
// on other systems bwd-slashes are valid
|
||||
// filename character, eg /f\oo/ba\r.txt
|
||||
if (isWindows) {
|
||||
if (path.startsWith(_slash)) {
|
||||
path = `${path.replace(/\\/g, _slash)}`;
|
||||
} else {
|
||||
path = `/${path.replace(/\\/g, _slash)}`;
|
||||
}
|
||||
if (URI.isWindowsPath(path)) {
|
||||
path = windowsPathToUriPath(path);
|
||||
}
|
||||
|
||||
// check for authority as used in UNC shares
|
||||
@@ -185,7 +187,7 @@ export abstract class URI {
|
||||
throw new Error(`[UriError]: cannot call joinPath on URI without path`);
|
||||
}
|
||||
let newPath: string;
|
||||
if (isWindows && uri.scheme === 'file') {
|
||||
if (URI.isWindowsPath(uri.path) && uri.scheme === 'file') {
|
||||
newPath = URI.file(paths.win32.join(URI.toFsPath(uri), ...pathFragment))
|
||||
.path;
|
||||
} else {
|
||||
@@ -194,7 +196,7 @@ export abstract class URI {
|
||||
return URI.create({ ...uri, path: newPath });
|
||||
}
|
||||
|
||||
static toFsPath(uri: URI, keepDriveLetterCasing = true): string {
|
||||
static toFsPath(uri: URI): string {
|
||||
let value: string;
|
||||
if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') {
|
||||
// unc path: file://shares/c$/far/boo
|
||||
@@ -207,17 +209,13 @@ export abstract class URI {
|
||||
uri.path.charCodeAt(1) <= CharCode.z)) &&
|
||||
uri.path.charCodeAt(2) === CharCode.Colon
|
||||
) {
|
||||
if (!keepDriveLetterCasing) {
|
||||
// windows drive letter: file:///c:/far/boo
|
||||
value = uri.path[1].toLowerCase() + uri.path.substr(2);
|
||||
} else {
|
||||
value = uri.path.substr(1);
|
||||
}
|
||||
// windows drive letter: file:///C:/far/boo
|
||||
value = uri.path[1].toUpperCase() + uri.path.substr(2);
|
||||
} else {
|
||||
// other path
|
||||
value = uri.path;
|
||||
}
|
||||
if (isWindows) {
|
||||
if (URI.isWindowsPath(value)) {
|
||||
value = value.replace(/\//g, '\\');
|
||||
}
|
||||
return value;
|
||||
@@ -229,6 +227,15 @@ export abstract class URI {
|
||||
|
||||
// --- utility
|
||||
|
||||
static isWindowsPath(path: string) {
|
||||
return (
|
||||
(path.length >= 2 && path.charCodeAt(1) === CharCode.Colon) ||
|
||||
(path.length >= 3 &&
|
||||
path.charCodeAt(0) === CharCode.Slash &&
|
||||
path.charCodeAt(2) === CharCode.Colon)
|
||||
);
|
||||
}
|
||||
|
||||
static isUri(thing: any): thing is URI {
|
||||
if (!thing) {
|
||||
return false;
|
||||
@@ -285,6 +292,33 @@ function percentDecode(str: string): string {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a windows-like path to standard URI path
|
||||
* - Normalize the Windows drive letter to upper case
|
||||
* - replace \ with /
|
||||
* - always start with /
|
||||
*
|
||||
* see https://github.com/foambubble/foam/issues/813
|
||||
* see https://github.com/microsoft/vscode/issues/43959
|
||||
* see https://github.com/microsoft/vscode/issues/116298
|
||||
*
|
||||
* @param path the path to convert
|
||||
* @returns the URI compatible path
|
||||
*/
|
||||
function windowsPathToUriPath(path: string): string {
|
||||
path = path.charCodeAt(0) === CharCode.Slash ? path : `/${path}`;
|
||||
path = path.replace(/\\/g, _slash);
|
||||
const code = path.charCodeAt(1);
|
||||
if (
|
||||
path.charCodeAt(2) === CharCode.Colon &&
|
||||
code >= CharCode.a &&
|
||||
code <= CharCode.z
|
||||
) {
|
||||
path = `/${String.fromCharCode(code - 32)}:${path.substr(3)}`; // "/C:".length === 3
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the external version of a uri
|
||||
*/
|
||||
@@ -331,20 +365,20 @@ function encode(uri: URI, skipEncoding: boolean): string {
|
||||
}
|
||||
}
|
||||
if (path) {
|
||||
// lower-case windows drive letters in /C:/fff or C:/fff
|
||||
// upper-case windows drive letters in /c:/fff or c:/fff
|
||||
if (
|
||||
path.length >= 3 &&
|
||||
path.charCodeAt(0) === CharCode.Slash &&
|
||||
path.charCodeAt(2) === CharCode.Colon
|
||||
) {
|
||||
const code = path.charCodeAt(1);
|
||||
if (code >= CharCode.A && code <= CharCode.Z) {
|
||||
path = `/${String.fromCharCode(code + 32)}:${path.substr(3)}`; // "/c:".length === 3
|
||||
if (code >= CharCode.a && code <= CharCode.z) {
|
||||
path = `/${String.fromCharCode(code - 32)}:${path.substr(3)}`; // "/C:".length === 3
|
||||
}
|
||||
} else if (path.length >= 2 && path.charCodeAt(1) === CharCode.Colon) {
|
||||
const code = path.charCodeAt(0);
|
||||
if (code >= CharCode.A && code <= CharCode.Z) {
|
||||
path = `${String.fromCharCode(code + 32)}:${path.substr(2)}`; // "/c:".length === 3
|
||||
if (code >= CharCode.a && code <= CharCode.z) {
|
||||
path = `${String.fromCharCode(code - 32)}:${path.substr(2)}`; // "/C:".length === 3
|
||||
}
|
||||
}
|
||||
// encode the rest of the path
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { workspace } from 'vscode';
|
||||
import { getDailyNotePath } from './dated-notes';
|
||||
import { createDailyNoteIfNotExists, getDailyNotePath } from './dated-notes';
|
||||
import { URI } from './core/model/uri';
|
||||
import { isWindows } from './utils';
|
||||
import {
|
||||
cleanWorkspace,
|
||||
closeEditors,
|
||||
createFile,
|
||||
showInEditor,
|
||||
} from './test/test-utils-vscode';
|
||||
import { fromVsCodeUri } from './utils/vsc-utils';
|
||||
|
||||
describe('getDailyNotePath', () => {
|
||||
const date = new Date('2021-02-07T00:00:00Z');
|
||||
@@ -14,11 +21,14 @@ describe('getDailyNotePath', () => {
|
||||
const config = 'journal';
|
||||
|
||||
const expectedPath = URI.joinPath(
|
||||
workspace.workspaceFolders[0].uri,
|
||||
fromVsCodeUri(workspace.workspaceFolders[0].uri),
|
||||
config,
|
||||
`${isoDate}.md`
|
||||
);
|
||||
|
||||
const oldValue = await workspace
|
||||
.getConfiguration('foam')
|
||||
.get('openDailyNote.directory');
|
||||
await workspace
|
||||
.getConfiguration('foam')
|
||||
.update('openDailyNote.directory', config);
|
||||
@@ -27,16 +37,24 @@ describe('getDailyNotePath', () => {
|
||||
expect(URI.toFsPath(getDailyNotePath(foamConfiguration, date))).toEqual(
|
||||
URI.toFsPath(expectedPath)
|
||||
);
|
||||
|
||||
await workspace
|
||||
.getConfiguration('foam')
|
||||
.update('openDailyNote.directory', oldValue);
|
||||
});
|
||||
|
||||
test('Uses absolute directories without modification', async () => {
|
||||
const config = isWindows
|
||||
? 'c:\\absolute_path\\journal'
|
||||
? 'C:\\absolute_path\\journal'
|
||||
: '/absolute_path/journal';
|
||||
const expectedPath = isWindows
|
||||
? `${config}\\${isoDate}.md`
|
||||
: `${config}/${isoDate}.md`;
|
||||
|
||||
const oldValue = await workspace
|
||||
.getConfiguration('foam')
|
||||
.get('openDailyNote.directory');
|
||||
|
||||
await workspace
|
||||
.getConfiguration('foam')
|
||||
.update('openDailyNote.directory', config);
|
||||
@@ -45,5 +63,36 @@ describe('getDailyNotePath', () => {
|
||||
expect(URI.toFsPath(getDailyNotePath(foamConfiguration, date))).toMatch(
|
||||
expectedPath
|
||||
);
|
||||
|
||||
await workspace
|
||||
.getConfiguration('foam')
|
||||
.update('openDailyNote.directory', oldValue);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Daily note template', () => {
|
||||
it('Uses the daily note variables in the template', async () => {
|
||||
const targetDate = new Date(2021, 8, 12);
|
||||
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
await createFile('hello ${FOAM_DATE_MONTH_NAME} ${FOAM_DATE_DATE} hello', [
|
||||
'.foam',
|
||||
'templates',
|
||||
'daily-note.md',
|
||||
]);
|
||||
|
||||
const config = workspace.getConfiguration('foam');
|
||||
const uri = getDailyNotePath(config, targetDate);
|
||||
|
||||
await createDailyNoteIfNotExists(config, uri, targetDate);
|
||||
|
||||
const doc = await showInEditor(uri);
|
||||
const content = doc.editor.document.getText();
|
||||
expect(content).toEqual('hello September 12 hello');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanWorkspace();
|
||||
await closeEditors();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import { isAbsolute } from 'path';
|
||||
import { focusNote, pathExists } from './utils';
|
||||
import { URI } from './core/model/uri';
|
||||
import { createNoteFromDailyNoteTemplate } from './features/create-from-template';
|
||||
import { fromVsCodeUri } from './utils/vsc-utils';
|
||||
|
||||
/**
|
||||
* Open the daily note file.
|
||||
@@ -13,7 +14,7 @@ import { createNoteFromDailyNoteTemplate } from './features/create-from-template
|
||||
*
|
||||
* @param date A given date to be formatted as filename.
|
||||
*/
|
||||
async function openDailyNoteFor(date?: Date) {
|
||||
export async function openDailyNoteFor(date?: Date) {
|
||||
const foamConfiguration = workspace.getConfiguration('foam');
|
||||
const currentDate = date !== undefined ? date : new Date();
|
||||
|
||||
@@ -40,7 +41,7 @@ async function openDailyNoteFor(date?: Date) {
|
||||
* @param date A given date to be formatted as filename.
|
||||
* @returns The path to the daily note file.
|
||||
*/
|
||||
function getDailyNotePath(
|
||||
export function getDailyNotePath(
|
||||
configuration: WorkspaceConfiguration,
|
||||
date: Date
|
||||
): URI {
|
||||
@@ -52,7 +53,7 @@ function getDailyNotePath(
|
||||
return URI.joinPath(URI.file(dailyNoteDirectory), dailyNoteFilename);
|
||||
} else {
|
||||
return URI.joinPath(
|
||||
workspace.workspaceFolders[0].uri,
|
||||
fromVsCodeUri(workspace.workspaceFolders[0].uri),
|
||||
dailyNoteDirectory,
|
||||
dailyNoteFilename
|
||||
);
|
||||
@@ -70,7 +71,7 @@ function getDailyNotePath(
|
||||
* @param date A given date to be formatted as filename.
|
||||
* @returns The daily note's filename.
|
||||
*/
|
||||
function getDailyNoteFileName(
|
||||
export function getDailyNoteFileName(
|
||||
configuration: WorkspaceConfiguration,
|
||||
date: Date
|
||||
): string {
|
||||
@@ -95,10 +96,10 @@ function getDailyNoteFileName(
|
||||
* @param currentDate The current date, to be used as a title.
|
||||
* @returns Wether the file was created.
|
||||
*/
|
||||
async function createDailyNoteIfNotExists(
|
||||
export async function createDailyNoteIfNotExists(
|
||||
configuration: WorkspaceConfiguration,
|
||||
dailyNotePath: URI,
|
||||
currentDate: Date
|
||||
targetDate: Date
|
||||
) {
|
||||
if (await pathExists(dailyNotePath)) {
|
||||
return false;
|
||||
@@ -113,17 +114,14 @@ foam_template:
|
||||
name: New Daily Note
|
||||
description: Foam's default daily note template
|
||||
---
|
||||
# ${dateFormat(currentDate, titleFormat, false)}
|
||||
# ${dateFormat(targetDate, titleFormat, false)}
|
||||
`;
|
||||
|
||||
await createNoteFromDailyNoteTemplate(dailyNotePath, templateFallbackText);
|
||||
await createNoteFromDailyNoteTemplate(
|
||||
dailyNotePath,
|
||||
templateFallbackText,
|
||||
targetDate
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export {
|
||||
openDailyNoteFor,
|
||||
getDailyNoteFileName,
|
||||
createDailyNoteIfNotExists,
|
||||
getDailyNotePath,
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Logger } from './core/utils/log';
|
||||
import { features } from './features';
|
||||
import { getConfigFromVscode } from './services/config';
|
||||
import { VsCodeOutputLogger, exposeLogger } from './services/logging';
|
||||
import { fromVsCodeUri } from './utils/vsc-utils';
|
||||
|
||||
function createMarkdownProvider(config: FoamConfig): MarkdownResourceProvider {
|
||||
const matcher = new Matcher(
|
||||
@@ -18,9 +19,9 @@ function createMarkdownProvider(config: FoamConfig): MarkdownResourceProvider {
|
||||
const provider = new MarkdownResourceProvider(matcher, triggers => {
|
||||
const watcher = workspace.createFileSystemWatcher('**/*');
|
||||
return [
|
||||
watcher.onDidChange(triggers.onDidChange),
|
||||
watcher.onDidCreate(triggers.onDidCreate),
|
||||
watcher.onDidDelete(triggers.onDidDelete),
|
||||
watcher.onDidChange(uri => triggers.onDidChange(fromVsCodeUri(uri))),
|
||||
watcher.onDidCreate(uri => triggers.onDidCreate(fromVsCodeUri(uri))),
|
||||
watcher.onDidDelete(uri => triggers.onDidDelete(fromVsCodeUri(uri))),
|
||||
watcher,
|
||||
];
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { BacklinksTreeDataProvider, BacklinkTreeItem } from './backlinks';
|
||||
import { ResourceTreeItem } from '../utils/grouped-resources-tree-data-provider';
|
||||
import { OPEN_COMMAND } from './utility-commands';
|
||||
import { toVsCodeUri } from '../utils/vsc-utils';
|
||||
import { fromVsCodeUri, toVsCodeUri } from '../utils/vsc-utils';
|
||||
import { FoamGraph } from '../core/model/graph';
|
||||
import { URI } from '../core/model/uri';
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('Backlinks panel', () => {
|
||||
await cleanWorkspace();
|
||||
});
|
||||
|
||||
const rootUri = workspace.workspaceFolders[0].uri;
|
||||
const rootUri = fromVsCodeUri(workspace.workspaceFolders[0].uri);
|
||||
const ws = createTestWorkspace();
|
||||
|
||||
const noteA = createTestNote({
|
||||
|
||||
@@ -10,6 +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';
|
||||
|
||||
const feature: FoamFeature = {
|
||||
activate: async (
|
||||
@@ -21,7 +22,9 @@ const feature: FoamFeature = {
|
||||
const provider = new BacklinksTreeDataProvider(foam.workspace, foam.graph);
|
||||
|
||||
vscode.window.onDidChangeActiveTextEditor(async () => {
|
||||
provider.target = vscode.window.activeTextEditor?.document.uri;
|
||||
provider.target = vscode.window.activeTextEditor
|
||||
? fromVsCodeUri(vscode.window.activeTextEditor?.document.uri)
|
||||
: undefined;
|
||||
await provider.refresh();
|
||||
});
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import path from 'path';
|
||||
import { isWindows } from '../utils';
|
||||
import { URI } from '../core/model/uri';
|
||||
import { fromVsCodeUri } from '../utils/vsc-utils';
|
||||
|
||||
describe('substituteFoamVariables', () => {
|
||||
test('Does nothing if no Foam-specific variables are used', () => {
|
||||
@@ -91,6 +92,83 @@ describe('resolveFoamVariables', () => {
|
||||
expected
|
||||
);
|
||||
});
|
||||
|
||||
test('Resolves FOAM_DATE_* properties with current day by default', async () => {
|
||||
const variables = [
|
||||
'FOAM_DATE_YEAR',
|
||||
'FOAM_DATE_YEAR_SHORT',
|
||||
'FOAM_DATE_MONTH',
|
||||
'FOAM_DATE_MONTH_NAME',
|
||||
'FOAM_DATE_MONTH_NAME_SHORT',
|
||||
'FOAM_DATE_DATE',
|
||||
'FOAM_DATE_DAY_NAME',
|
||||
'FOAM_DATE_DAY_NAME_SHORT',
|
||||
'FOAM_DATE_HOUR',
|
||||
'FOAM_DATE_MINUTE',
|
||||
'FOAM_DATE_SECOND',
|
||||
'FOAM_DATE_SECONDS_UNIX',
|
||||
];
|
||||
|
||||
const expected = new Map<string, string>();
|
||||
expected.set(
|
||||
'FOAM_DATE_YEAR',
|
||||
new Date().toLocaleString('default', { year: 'numeric' })
|
||||
);
|
||||
expected.set(
|
||||
'FOAM_DATE_MONTH_NAME',
|
||||
new Date().toLocaleString('default', { month: 'long' })
|
||||
);
|
||||
expected.set(
|
||||
'FOAM_DATE_DATE',
|
||||
new Date().toLocaleString('default', { day: '2-digit' })
|
||||
);
|
||||
const givenValues = new Map<string, string>();
|
||||
|
||||
expect(await resolveFoamVariables(variables, givenValues)).toEqual(
|
||||
expect.objectContaining(expected)
|
||||
);
|
||||
});
|
||||
|
||||
test('Resolves FOAM_DATE_* properties with given date', async () => {
|
||||
const targetDate = new Date(2021, 9, 12, 1, 2, 3);
|
||||
const variables = [
|
||||
'FOAM_DATE_YEAR',
|
||||
'FOAM_DATE_YEAR_SHORT',
|
||||
'FOAM_DATE_MONTH',
|
||||
'FOAM_DATE_MONTH_NAME',
|
||||
'FOAM_DATE_MONTH_NAME_SHORT',
|
||||
'FOAM_DATE_DATE',
|
||||
'FOAM_DATE_DAY_NAME',
|
||||
'FOAM_DATE_DAY_NAME_SHORT',
|
||||
'FOAM_DATE_HOUR',
|
||||
'FOAM_DATE_MINUTE',
|
||||
'FOAM_DATE_SECOND',
|
||||
'FOAM_DATE_SECONDS_UNIX',
|
||||
];
|
||||
|
||||
const expected = new Map<string, string>();
|
||||
expected.set('FOAM_DATE_YEAR', '2021');
|
||||
expected.set('FOAM_DATE_YEAR_SHORT', '21');
|
||||
expected.set('FOAM_DATE_MONTH', '10');
|
||||
expected.set('FOAM_DATE_MONTH_NAME', 'October');
|
||||
expected.set('FOAM_DATE_MONTH_NAME_SHORT', 'Oct');
|
||||
expected.set('FOAM_DATE_DATE', '12');
|
||||
expected.set('FOAM_DATE_DAY_NAME', 'Tuesday');
|
||||
expected.set('FOAM_DATE_DAY_NAME_SHORT', 'Tue');
|
||||
expected.set('FOAM_DATE_HOUR', '01');
|
||||
expected.set('FOAM_DATE_MINUTE', '02');
|
||||
expected.set('FOAM_DATE_SECOND', '03');
|
||||
expected.set(
|
||||
'FOAM_DATE_SECONDS_UNIX',
|
||||
(targetDate.getTime() / 1000).toString()
|
||||
);
|
||||
|
||||
const givenValues = new Map<string, string>();
|
||||
|
||||
expect(
|
||||
await resolveFoamVariables(variables, givenValues, targetDate)
|
||||
).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveFoamTemplateVariables', () => {
|
||||
@@ -266,7 +344,7 @@ describe('resolveFoamTemplateVariables', () => {
|
||||
describe('determineDefaultFilepath', () => {
|
||||
test('Absolute filepath metadata is unchanged', () => {
|
||||
const absolutePath = isWindows
|
||||
? 'c:\\absolute_path\\journal\\My Note Title.md'
|
||||
? 'C:\\absolute_path\\journal\\My Note Title.md'
|
||||
: '/absolute_path/journal/My Note Title.md';
|
||||
|
||||
const resolvedValues = new Map<string, string>();
|
||||
@@ -296,7 +374,7 @@ describe('determineDefaultFilepath', () => {
|
||||
);
|
||||
|
||||
const expectedPath = path.join(
|
||||
workspace.workspaceFolders[0].uri.fsPath,
|
||||
URI.toFsPath(fromVsCodeUri(workspace.workspaceFolders[0].uri)),
|
||||
relativePath
|
||||
);
|
||||
|
||||
|
||||
@@ -28,21 +28,12 @@ describe('createFromTemplate', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it.skip('can be cancelled while resolving FOAM_TITLE', async () => {
|
||||
it('can be cancelled while resolving FOAM_TITLE', async () => {
|
||||
const spy = jest
|
||||
.spyOn(window, 'showInputBox')
|
||||
.mockImplementation(jest.fn(() => Promise.resolve(undefined)));
|
||||
|
||||
// The following is a workaround code to achieve this:
|
||||
// const fileWriteSpy = jest.spyOn(workspace.fs, 'writeFile');
|
||||
// as writeFile is a read-only property.
|
||||
// the approach is a bit risky and britte as it overwrites the whole module
|
||||
// but will be enough for now.
|
||||
const oldFs = workspace.fs;
|
||||
const fileWriteSpy = jest.fn(workspace.fs.writeFile);
|
||||
Object.defineProperty(workspace, 'fs', {
|
||||
value: { writeFile: fileWriteSpy },
|
||||
});
|
||||
const fileWriteSpy = jest.spyOn(workspace.fs, 'writeFile');
|
||||
|
||||
await commands.executeCommand(
|
||||
'foam-vscode.create-note-from-default-template'
|
||||
@@ -55,11 +46,6 @@ describe('createFromTemplate', () => {
|
||||
});
|
||||
|
||||
expect(fileWriteSpy).toHaveBeenCalledTimes(0);
|
||||
|
||||
// restore the old fs object or all tests will be affected by this one
|
||||
Object.defineProperty(workspace, 'fs', {
|
||||
value: oldFs,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ import {
|
||||
} from 'vscode';
|
||||
import { FoamFeature } from '../types';
|
||||
import { focusNote } from '../utils';
|
||||
import { toVsCodeUri } from '../utils/vsc-utils';
|
||||
import { fromVsCodeUri, toVsCodeUri } from '../utils/vsc-utils';
|
||||
import { extractFoamTemplateFrontmatterMetadata } from '../utils/template-frontmatter-parser';
|
||||
|
||||
const templatesDir = URI.joinPath(
|
||||
workspace.workspaceFolders[0].uri,
|
||||
fromVsCodeUri(workspace.workspaceFolders[0].uri),
|
||||
'.foam',
|
||||
'templates'
|
||||
);
|
||||
@@ -41,7 +41,22 @@ interface FoamSelectionContent {
|
||||
content: string;
|
||||
}
|
||||
|
||||
const knownFoamVariables = new Set(['FOAM_TITLE', 'FOAM_SELECTED_TEXT']);
|
||||
const knownFoamVariables = new Set([
|
||||
'FOAM_TITLE',
|
||||
'FOAM_SELECTED_TEXT',
|
||||
'FOAM_DATE_YEAR',
|
||||
'FOAM_DATE_YEAR_SHORT',
|
||||
'FOAM_DATE_MONTH',
|
||||
'FOAM_DATE_MONTH_NAME',
|
||||
'FOAM_DATE_MONTH_NAME_SHORT',
|
||||
'FOAM_DATE_DATE',
|
||||
'FOAM_DATE_DAY_NAME',
|
||||
'FOAM_DATE_DAY_NAME_SHORT',
|
||||
'FOAM_DATE_HOUR',
|
||||
'FOAM_DATE_MINUTE',
|
||||
'FOAM_DATE_SECOND',
|
||||
'FOAM_DATE_SECONDS_UNIX',
|
||||
]);
|
||||
|
||||
const wikilinkDefaultTemplateText = `# $\{1:$FOAM_TITLE}\n\n$0`;
|
||||
const defaultTemplateDefaultText: string = `---
|
||||
@@ -86,7 +101,9 @@ async function templateMetadata(
|
||||
}
|
||||
|
||||
async function getTemplates(): Promise<URI[]> {
|
||||
const templates = await workspace.findFiles('.foam/templates/**.md', null);
|
||||
const templates = await workspace
|
||||
.findFiles('.foam/templates/**.md', null)
|
||||
.then(v => v.map(uri => fromVsCodeUri(uri)));
|
||||
return templates;
|
||||
}
|
||||
|
||||
@@ -133,9 +150,14 @@ function resolveFoamSelectedText() {
|
||||
class Resolver {
|
||||
promises = new Map<string, Thenable<string>>();
|
||||
|
||||
resolve(name: string, givenValues: Map<string, string>): Thenable<string> {
|
||||
if (givenValues.has(name)) {
|
||||
this.promises.set(name, Promise.resolve(givenValues.get(name)));
|
||||
constructor(
|
||||
private givenValues: Map<string, string>,
|
||||
private foamDate: Date
|
||||
) {}
|
||||
|
||||
resolve(name: string): Thenable<string> {
|
||||
if (this.givenValues.has(name)) {
|
||||
this.promises.set(name, Promise.resolve(this.givenValues.get(name)));
|
||||
} else if (!this.promises.has(name)) {
|
||||
switch (name) {
|
||||
case 'FOAM_TITLE':
|
||||
@@ -144,6 +166,117 @@ class Resolver {
|
||||
case 'FOAM_SELECTED_TEXT':
|
||||
this.promises.set(name, Promise.resolve(resolveFoamSelectedText()));
|
||||
break;
|
||||
case 'FOAM_DATE_YEAR':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
this.foamDate.toLocaleString('default', { year: 'numeric' })
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'FOAM_DATE_YEAR_SHORT':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
this.foamDate.toLocaleString('default', { year: '2-digit' })
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'FOAM_DATE_MONTH':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
this.foamDate.toLocaleString('default', { month: '2-digit' })
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'FOAM_DATE_MONTH_NAME':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
this.foamDate.toLocaleString('default', { month: 'long' })
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'FOAM_DATE_MONTH_NAME_SHORT':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
this.foamDate.toLocaleString('default', { month: 'short' })
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'FOAM_DATE_DATE':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
this.foamDate.toLocaleString('default', { day: '2-digit' })
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'FOAM_DATE_DAY_NAME':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
this.foamDate.toLocaleString('default', { weekday: 'long' })
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'FOAM_DATE_DAY_NAME_SHORT':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
this.foamDate.toLocaleString('default', { weekday: 'short' })
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'FOAM_DATE_HOUR':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
this.foamDate
|
||||
.toLocaleString('default', {
|
||||
hour: '2-digit',
|
||||
hour12: false,
|
||||
})
|
||||
.padStart(2, '0')
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'FOAM_DATE_MINUTE':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
this.foamDate
|
||||
.toLocaleString('default', {
|
||||
minute: '2-digit',
|
||||
hour12: false,
|
||||
})
|
||||
.padStart(2, '0')
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'FOAM_DATE_SECOND':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
this.foamDate
|
||||
.toLocaleString('default', {
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
})
|
||||
.padStart(2, '0')
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'FOAM_DATE_SECONDS_UNIX':
|
||||
this.promises.set(
|
||||
name,
|
||||
Promise.resolve(
|
||||
(this.foamDate.getTime() / 1000).toString().padStart(2, '0')
|
||||
)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
this.promises.set(name, Promise.resolve(name));
|
||||
break;
|
||||
@@ -156,11 +289,12 @@ class Resolver {
|
||||
|
||||
export async function resolveFoamVariables(
|
||||
variables: string[],
|
||||
givenValues: Map<string, string>
|
||||
givenValues: Map<string, string>,
|
||||
foamDate: Date = new Date()
|
||||
) {
|
||||
const resolver = new Resolver();
|
||||
const resolver = new Resolver(givenValues, foamDate);
|
||||
const promises = variables.map(async variable =>
|
||||
Promise.resolve([variable, await resolver.resolve(variable, givenValues)])
|
||||
Promise.resolve([variable, await resolver.resolve(variable)])
|
||||
);
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
@@ -293,13 +427,18 @@ function appendSnippetVariableUsage(templateText: string, variable: string) {
|
||||
export async function resolveFoamTemplateVariables(
|
||||
templateText: string,
|
||||
extraVariablesToResolve: Set<string> = new Set(),
|
||||
givenValues: Map<string, string> = new Map()
|
||||
givenValues: Map<string, string> = new Map(),
|
||||
foamDate: Date = new Date()
|
||||
): Promise<[Map<string, string>, string]> {
|
||||
const variablesInTemplate = findFoamVariables(templateText.toString());
|
||||
const variables = variablesInTemplate.concat(...extraVariablesToResolve);
|
||||
const uniqVariables = [...new Set(variables)];
|
||||
|
||||
const resolvedValues = await resolveFoamVariables(uniqVariables, givenValues);
|
||||
const resolvedValues = await resolveFoamVariables(
|
||||
uniqVariables,
|
||||
givenValues,
|
||||
foamDate
|
||||
);
|
||||
|
||||
if (
|
||||
resolvedValues.get('FOAM_SELECTED_TEXT') &&
|
||||
@@ -340,7 +479,7 @@ function currentDirectoryFilepath(filename: string) {
|
||||
const currentDir =
|
||||
activeFile !== undefined
|
||||
? URI.parse(path.dirname(activeFile))
|
||||
: workspace.workspaceFolders[0].uri;
|
||||
: fromVsCodeUri(workspace.workspaceFolders[0].uri);
|
||||
|
||||
return URI.joinPath(currentDir, filename);
|
||||
}
|
||||
@@ -381,7 +520,7 @@ async function replaceSelectionWithWikiLink(
|
||||
function resolveFilepathAttribute(filepath) {
|
||||
return isAbsolute(filepath)
|
||||
? URI.file(filepath)
|
||||
: URI.joinPath(workspace.workspaceFolders[0].uri, filepath);
|
||||
: URI.joinPath(fromVsCodeUri(workspace.workspaceFolders[0].uri), filepath);
|
||||
}
|
||||
|
||||
export function determineDefaultFilepath(
|
||||
@@ -410,14 +549,16 @@ export function determineDefaultFilepath(
|
||||
*/
|
||||
export async function createNoteFromDailyNoteTemplate(
|
||||
filepathFallbackURI: URI,
|
||||
templateFallbackText: string
|
||||
templateFallbackText: string,
|
||||
targetDate: Date
|
||||
): Promise<void> {
|
||||
return await createNoteFromDefaultTemplate(
|
||||
new Map(),
|
||||
new Set(['FOAM_SELECTED_TEXT']),
|
||||
dailyNoteTemplateUri,
|
||||
filepathFallbackURI,
|
||||
templateFallbackText
|
||||
templateFallbackText,
|
||||
targetDate
|
||||
);
|
||||
}
|
||||
|
||||
@@ -455,7 +596,8 @@ async function createNoteFromDefaultTemplate(
|
||||
]),
|
||||
templateUri: URI = defaultTemplateUri,
|
||||
filepathFallbackURI: URI = undefined,
|
||||
templateFallbackText: string = defaultTemplateDefaultText
|
||||
templateFallbackText: string = defaultTemplateDefaultText,
|
||||
foamDate: Date = new Date()
|
||||
): Promise<void> {
|
||||
const templateText = existsSync(URI.toFsPath(templateUri))
|
||||
? await workspace.fs
|
||||
@@ -474,7 +616,8 @@ async function createNoteFromDefaultTemplate(
|
||||
] = await resolveFoamTemplateVariables(
|
||||
templateText,
|
||||
extraVariablesToResolve,
|
||||
givenValues.set('FOAM_SELECTED_TEXT', selectedContent?.content ?? '')
|
||||
givenValues.set('FOAM_SELECTED_TEXT', selectedContent?.content ?? ''),
|
||||
foamDate
|
||||
);
|
||||
} catch (err) {
|
||||
if (err instanceof UserCancelledOperation) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { getGraphStyle, getTitleMaxLength } from '../settings';
|
||||
import { isSome } from '../utils';
|
||||
import { Foam } from '../core/model/foam';
|
||||
import { Logger } from '../core/utils/log';
|
||||
import { fromVsCodeUri } from '../utils/vsc-utils';
|
||||
|
||||
const feature: FoamFeature = {
|
||||
activate: (context: vscode.ExtensionContext, foamPromise: Promise<Foam>) => {
|
||||
@@ -46,7 +47,7 @@ const feature: FoamFeature = {
|
||||
|
||||
vscode.window.onDidChangeActiveTextEditor(e => {
|
||||
if (e?.document?.uri?.scheme === 'file') {
|
||||
const note = foam.workspace.get(e.document.uri);
|
||||
const note = foam.workspace.get(fromVsCodeUri(e.document.uri));
|
||||
if (isSome(note)) {
|
||||
panel.webview.postMessage({
|
||||
type: 'didSelectNote',
|
||||
@@ -143,7 +144,7 @@ async function createGraphPanel(foam: Foam, context: vscode.ExtensionContext) {
|
||||
|
||||
case 'webviewDidSelectNode':
|
||||
const noteUri = vscode.Uri.parse(message.payload);
|
||||
const selectedNote = foam.workspace.get(noteUri);
|
||||
const selectedNote = foam.workspace.get(fromVsCodeUri(noteUri));
|
||||
|
||||
if (isSome(selectedNote)) {
|
||||
const doc = await vscode.workspace.openTextDocument(
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import { ResourceParser } from '../core/model/note';
|
||||
import { FoamWorkspace } from '../core/model/workspace';
|
||||
import { Foam } from '../core/model/foam';
|
||||
import { fromVsCodeUri } from '../utils/vsc-utils';
|
||||
|
||||
export const CONFIG_KEY = 'decorations.links.enable';
|
||||
|
||||
@@ -34,7 +35,10 @@ const updateDecorations = (
|
||||
if (!editor || !areDecorationsEnabled()) {
|
||||
return;
|
||||
}
|
||||
const note = parser.parse(editor.document.uri, editor.document.getText());
|
||||
const note = parser.parse(
|
||||
fromVsCodeUri(editor.document.uri),
|
||||
editor.document.getText()
|
||||
);
|
||||
let linkRanges = [];
|
||||
let placeholderRanges = [];
|
||||
note.links.forEach(link => {
|
||||
@@ -60,16 +64,18 @@ const feature: FoamFeature = {
|
||||
const foam = await foamPromise;
|
||||
let activeEditor = vscode.window.activeTextEditor;
|
||||
|
||||
const immediatelyUpdateDecorations = updateDecorations(
|
||||
areDecorationsEnabled,
|
||||
foam.services.parser,
|
||||
foam.workspace
|
||||
);
|
||||
|
||||
const debouncedUpdateDecorations = debounce(
|
||||
updateDecorations(
|
||||
areDecorationsEnabled,
|
||||
foam.services.parser,
|
||||
foam.workspace
|
||||
),
|
||||
immediatelyUpdateDecorations,
|
||||
500
|
||||
);
|
||||
|
||||
debouncedUpdateDecorations(activeEditor);
|
||||
immediatelyUpdateDecorations(activeEditor);
|
||||
|
||||
context.subscriptions.push(
|
||||
areDecorationsEnabled,
|
||||
@@ -77,7 +83,7 @@ const feature: FoamFeature = {
|
||||
placeholderDecoration,
|
||||
vscode.window.onDidChangeActiveTextEditor(editor => {
|
||||
activeEditor = editor;
|
||||
debouncedUpdateDecorations(activeEditor);
|
||||
immediatelyUpdateDecorations(activeEditor);
|
||||
}),
|
||||
vscode.workspace.onDidChangeTextDocument(event => {
|
||||
if (activeEditor && event.document === activeEditor.document) {
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('Document links provider', () => {
|
||||
const { uri, content } = await createFile('');
|
||||
const ws = new FoamWorkspace().set(parser.parse(uri, content));
|
||||
|
||||
const doc = await vscode.workspace.openTextDocument(uri);
|
||||
const doc = await vscode.workspace.openTextDocument(toVsCodeUri(uri));
|
||||
const provider = new LinkProvider(ws, parser);
|
||||
const links = provider.provideDocumentLinks(doc);
|
||||
|
||||
@@ -45,7 +45,7 @@ describe('Document links provider', () => {
|
||||
);
|
||||
const ws = new FoamWorkspace().set(parser.parse(uri, content));
|
||||
|
||||
const doc = await vscode.workspace.openTextDocument(uri);
|
||||
const doc = await vscode.workspace.openTextDocument(toVsCodeUri(uri));
|
||||
const provider = new LinkProvider(ws, parser);
|
||||
const links = provider.provideDocumentLinks(doc);
|
||||
|
||||
@@ -98,7 +98,7 @@ describe('Document links provider', () => {
|
||||
|
||||
expect(links.length).toEqual(1);
|
||||
expect(links[0].target).toEqual(
|
||||
OPEN_COMMAND.asURI(toVsCodeUri(URI.placeholder('a placeholder')))
|
||||
OPEN_COMMAND.asURI(URI.placeholder('a placeholder'))
|
||||
);
|
||||
expect(links[0].range).toEqual(new vscode.Range(0, 18, 0, 35));
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { URI } from '../core/model/uri';
|
||||
import { FoamFeature } from '../types';
|
||||
import { mdDocSelector } from '../utils';
|
||||
import { OPEN_COMMAND } from './utility-commands';
|
||||
import { toVsCodeRange, toVsCodeUri } from '../utils/vsc-utils';
|
||||
import { fromVsCodeUri, toVsCodeRange, toVsCodeUri } from '../utils/vsc-utils';
|
||||
import { getFoamVsCodeConfig } from '../services/config';
|
||||
import { Foam } from '../core/model/foam';
|
||||
import { FoamWorkspace } from '../core/model/workspace';
|
||||
@@ -38,11 +38,14 @@ export class LinkProvider implements vscode.DocumentLinkProvider {
|
||||
public provideDocumentLinks(
|
||||
document: vscode.TextDocument
|
||||
): vscode.DocumentLink[] {
|
||||
const resource = this.parser.parse(document.uri, document.getText());
|
||||
const resource = this.parser.parse(
|
||||
fromVsCodeUri(document.uri),
|
||||
document.getText()
|
||||
);
|
||||
|
||||
return resource.links.map(link => {
|
||||
const target = this.workspace.resolveLink(resource, link);
|
||||
const command = OPEN_COMMAND.asURI(toVsCodeUri(target));
|
||||
const command = OPEN_COMMAND.asURI(target);
|
||||
const documentLink = new vscode.DocumentLink(
|
||||
toVsCodeRange(link.range),
|
||||
command
|
||||
|
||||
@@ -4,27 +4,36 @@ import {
|
||||
MarkdownResourceProvider,
|
||||
} from '../core/markdown-provider';
|
||||
import { FoamGraph } from '../core/model/graph';
|
||||
import { URI } from '../core/model/uri';
|
||||
import { FoamWorkspace } from '../core/model/workspace';
|
||||
import { Matcher } from '../core/services/datastore';
|
||||
import { getConfigFromVscode } from '../services/config';
|
||||
import {
|
||||
cleanWorkspace,
|
||||
closeEditors,
|
||||
createFile,
|
||||
showInEditor,
|
||||
} from '../test/test-utils-vscode';
|
||||
import { toVsCodeUri } from '../utils/vsc-utils';
|
||||
import { HoverProvider } from './hover-provider';
|
||||
|
||||
// We can't use createTestWorkspace from /packages/foam-vscode/src/test/test-utils.ts
|
||||
// because we need a fully instantiated MarkdownResourceProvider (with a real instance of ResourceParser).
|
||||
// because we need a MarkdownResourceProvider with a real instance of FileDataStore.
|
||||
const createWorkspace = () => {
|
||||
const matcher = new Matcher([URI.file('/')], ['**/*']);
|
||||
const config = getConfigFromVscode();
|
||||
const matcher = new Matcher(
|
||||
config.workspaceFolders,
|
||||
config.includeGlobs,
|
||||
config.ignoreGlobs
|
||||
);
|
||||
const resourceProvider = new MarkdownResourceProvider(matcher);
|
||||
const workspace = new FoamWorkspace();
|
||||
workspace.registerProvider(resourceProvider);
|
||||
return workspace;
|
||||
};
|
||||
|
||||
const getValue = (value: vscode.MarkdownString | vscode.MarkedString) =>
|
||||
value instanceof vscode.MarkdownString ? value.value : value;
|
||||
|
||||
describe('Hover provider', () => {
|
||||
const noCancelToken: vscode.CancellationToken = {
|
||||
isCancellationRequested: false,
|
||||
@@ -33,36 +42,6 @@ describe('Hover provider', () => {
|
||||
const parser = createMarkdownParser([]);
|
||||
const hoverEnabled = () => true;
|
||||
|
||||
const fileBContent = `# File B Title
|
||||
---
|
||||
tags: my-tag1 my-tag2
|
||||
---
|
||||
|
||||
The content of file B
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
cccccccccccccccccccccccccccccccccccccccc
|
||||
dddddddddddddddddddddddddddddddddddddddd
|
||||
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`;
|
||||
|
||||
// Fixture needed as long tests are running with vscode 1.53.0 (MarkdownString is not available)
|
||||
const simpleTooltipExpectedFormat =
|
||||
'File B Title --- tags: my-tag1 my-tag2 --- The content of file B aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ' +
|
||||
'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb cccccccccccccccccccccccccccccccccccccccc dddddddddddd...';
|
||||
|
||||
// Fixture to use when tests are running with vscode version >= STABLE_MARKDOWN_STRING_API_VERSION (1.52.1)
|
||||
/*const markdownTooltipExpectedFormat = `# File B Title
|
||||
---
|
||||
tags: my-tag1 my-tag2
|
||||
---
|
||||
|
||||
The content of file B
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
cccccccccccccccccccccccccccccccccccccccc
|
||||
dddddddddddddddddddddddddddddddddddddddd
|
||||
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`;*/
|
||||
|
||||
beforeAll(async () => {
|
||||
await cleanWorkspace();
|
||||
});
|
||||
@@ -82,7 +61,7 @@ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`;*/
|
||||
const graph = FoamGraph.fromWorkspace(ws);
|
||||
const provider = new HoverProvider(hoverEnabled, ws, graph, parser);
|
||||
|
||||
const doc = await vscode.workspace.openTextDocument(uri);
|
||||
const doc = await vscode.workspace.openTextDocument(toVsCodeUri(uri));
|
||||
const pos = new vscode.Position(0, 0);
|
||||
const result = await provider.provideHover(doc, pos, noCancelToken);
|
||||
|
||||
@@ -100,7 +79,7 @@ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`;*/
|
||||
|
||||
const provider = new HoverProvider(hoverEnabled, ws, graph, parser);
|
||||
|
||||
const doc = await vscode.workspace.openTextDocument(uri);
|
||||
const doc = await vscode.workspace.openTextDocument(toVsCodeUri(uri));
|
||||
const pos = new vscode.Position(0, 0);
|
||||
const result = await provider.provideHover(doc, pos, noCancelToken);
|
||||
|
||||
@@ -200,8 +179,7 @@ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`;*/
|
||||
const result = await provider.provideHover(doc, pos, noCancelToken);
|
||||
|
||||
expect(result.contents).toHaveLength(2);
|
||||
expect(result.contents[0]).toHaveProperty(
|
||||
'value',
|
||||
expect(getValue(result.contents[0])).toEqual(
|
||||
`This is some content from file B`
|
||||
);
|
||||
ws.dispose();
|
||||
@@ -227,8 +205,7 @@ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`;*/
|
||||
const result = await provider.provideHover(doc, pos, noCancelToken);
|
||||
|
||||
expect(result.contents).toHaveLength(2);
|
||||
expect(result.contents[0]).toHaveProperty(
|
||||
'value',
|
||||
expect(getValue(result.contents[0])).toEqual(
|
||||
`This is some content from file B`
|
||||
);
|
||||
ws.dispose();
|
||||
@@ -258,10 +235,7 @@ The content of file B`);
|
||||
const result = await provider.provideHover(doc, pos, noCancelToken);
|
||||
|
||||
expect(result.contents).toHaveLength(2);
|
||||
expect(result.contents[0]).toHaveProperty(
|
||||
'value',
|
||||
`The content of file B`
|
||||
);
|
||||
expect(getValue(result.contents[0])).toEqual(`The content of file B`);
|
||||
ws.dispose();
|
||||
graph.dispose();
|
||||
});
|
||||
@@ -309,11 +283,8 @@ The content of file B`);
|
||||
const result = await provider.provideHover(doc, pos, noCancelToken);
|
||||
|
||||
expect(result.contents).toHaveLength(2);
|
||||
expect(result.contents[0]).toHaveProperty(
|
||||
'value',
|
||||
`This is some content`
|
||||
);
|
||||
expect((result.contents[1] as any).value).toMatch(
|
||||
expect(getValue(result.contents[0])).toEqual(`This is some content`);
|
||||
expect(getValue(result.contents[1])).toMatch(
|
||||
/^Also referenced in 1 note:/
|
||||
);
|
||||
ws.dispose();
|
||||
@@ -339,7 +310,7 @@ The content of file B`);
|
||||
|
||||
expect(result.contents).toHaveLength(2);
|
||||
expect(result.contents[0]).toEqual(null);
|
||||
expect((result.contents[1] as any).value).toMatch(
|
||||
expect(getValue(result.contents[1])).toMatch(
|
||||
/^Also referenced in 2 notes:/
|
||||
);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
|
||||
import { URI } from '../core/model/uri';
|
||||
import { FoamFeature } from '../types';
|
||||
import { getNoteTooltip, mdDocSelector, isSome } from '../utils';
|
||||
import { toVsCodeRange } from '../utils/vsc-utils';
|
||||
import { fromVsCodeUri, toVsCodeRange } from '../utils/vsc-utils';
|
||||
import {
|
||||
ConfigurationMonitor,
|
||||
monitorFoamVsCodeConfig,
|
||||
@@ -59,7 +59,10 @@ export class HoverProvider implements vscode.HoverProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
const startResource = this.parser.parse(document.uri, document.getText());
|
||||
const startResource = this.parser.parse(
|
||||
fromVsCodeUri(document.uri),
|
||||
document.getText()
|
||||
);
|
||||
|
||||
const targetLink: ResourceLink | undefined = startResource.links.find(
|
||||
link =>
|
||||
@@ -75,7 +78,7 @@ export class HoverProvider implements vscode.HoverProvider {
|
||||
const targetUri = this.workspace.resolveLink(startResource, targetLink);
|
||||
const refs = this.graph
|
||||
.getBacklinks(targetUri)
|
||||
.filter(link => !URI.isEqual(link.source, document.uri));
|
||||
.filter(link => !URI.isEqual(link.source, fromVsCodeUri(document.uri)));
|
||||
|
||||
const links = refs.slice(0, 10).map(link => {
|
||||
const command = OPEN_COMMAND.asURI(link.source);
|
||||
|
||||
@@ -8,10 +8,11 @@ import {
|
||||
createFile,
|
||||
showInEditor,
|
||||
} from '../test/test-utils-vscode';
|
||||
import { fromVsCodeUri } from '../utils/vsc-utils';
|
||||
import { CompletionProvider } from './link-completion';
|
||||
|
||||
describe('Link Completion', () => {
|
||||
const root = vscode.workspace.workspaceFolders[0].uri;
|
||||
const root = fromVsCodeUri(vscode.workspace.workspaceFolders[0].uri);
|
||||
const ws = new FoamWorkspace();
|
||||
ws.set(
|
||||
createTestNote({
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ResourceTreeItem,
|
||||
UriTreeItem,
|
||||
} from '../utils/grouped-resources-tree-data-provider';
|
||||
import { fromVsCodeUri } from '../utils/vsc-utils';
|
||||
|
||||
const feature: FoamFeature = {
|
||||
activate: async (
|
||||
@@ -17,8 +18,8 @@ const feature: FoamFeature = {
|
||||
) => {
|
||||
const foam = await foamPromise;
|
||||
|
||||
const workspacesURIs = vscode.workspace.workspaceFolders.map(
|
||||
dir => dir.uri
|
||||
const workspacesURIs = vscode.workspace.workspaceFolders.map(dir =>
|
||||
fromVsCodeUri(dir.uri)
|
||||
);
|
||||
|
||||
const provider = new GroupedResourcesTreeDataProvider(
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ResourceTreeItem,
|
||||
UriTreeItem,
|
||||
} from '../utils/grouped-resources-tree-data-provider';
|
||||
import { fromVsCodeUri } from '../utils/vsc-utils';
|
||||
|
||||
const feature: FoamFeature = {
|
||||
activate: async (
|
||||
@@ -16,8 +17,8 @@ const feature: FoamFeature = {
|
||||
foamPromise: Promise<Foam>
|
||||
) => {
|
||||
const foam = await foamPromise;
|
||||
const workspacesURIs = vscode.workspace.workspaceFolders.map(
|
||||
dir => dir.uri
|
||||
const workspacesURIs = vscode.workspace.workspaceFolders.map(dir =>
|
||||
fromVsCodeUri(dir.uri)
|
||||
);
|
||||
const provider = new GroupedResourcesTreeDataProvider(
|
||||
'placeholders',
|
||||
|
||||
@@ -144,13 +144,22 @@ export const markdownItWithRemoveLinkReferences = (
|
||||
md: markdownit,
|
||||
workspace: FoamWorkspace
|
||||
) => {
|
||||
// Forget about reference links that contain an alias divider
|
||||
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];
|
||||
}
|
||||
|
||||
// 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()}]]`)) {
|
||||
delete state.env.references[refKey];
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -8,10 +8,11 @@ import {
|
||||
createFile,
|
||||
showInEditor,
|
||||
} from '../test/test-utils-vscode';
|
||||
import { fromVsCodeUri } from '../utils/vsc-utils';
|
||||
import { TagCompletionProvider } from './tag-completion';
|
||||
|
||||
describe('Tag Completion', () => {
|
||||
const root = vscode.workspace.workspaceFolders[0].uri;
|
||||
const root = fromVsCodeUri(vscode.workspace.workspaceFolders[0].uri);
|
||||
const ws = new FoamWorkspace();
|
||||
ws.set(
|
||||
createTestNote({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { FoamFeature } from '../types';
|
||||
import { URI } from '../core/model/uri';
|
||||
import { toVsCodeUri } from '../utils/vsc-utils';
|
||||
import { fromVsCodeUri, toVsCodeUri } from '../utils/vsc-utils';
|
||||
import { createNoteForPlaceholderWikilink } from './create-from-template';
|
||||
|
||||
export const OPEN_COMMAND = {
|
||||
@@ -19,18 +19,18 @@ export const OPEN_COMMAND = {
|
||||
|
||||
const basedir =
|
||||
vscode.workspace.workspaceFolders.length > 0
|
||||
? vscode.workspace.workspaceFolders[0].uri
|
||||
: vscode.window.activeTextEditor?.document.uri
|
||||
? URI.getDir(vscode.window.activeTextEditor!.document.uri)
|
||||
? fromVsCodeUri(vscode.workspace.workspaceFolders[0].uri)
|
||||
: fromVsCodeUri(vscode.window.activeTextEditor?.document.uri)
|
||||
? URI.getDir(
|
||||
fromVsCodeUri(vscode.window.activeTextEditor!.document.uri)
|
||||
)
|
||||
: undefined;
|
||||
|
||||
if (basedir === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = toVsCodeUri(
|
||||
URI.createResourceUriFromPlaceholder(basedir, uri)
|
||||
);
|
||||
const target = URI.createResourceUriFromPlaceholder(basedir, uri);
|
||||
|
||||
await createNoteForPlaceholderWikilink(title, target);
|
||||
return;
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
LINK_REFERENCE_DEFINITION_FOOTER,
|
||||
LINK_REFERENCE_DEFINITION_HEADER,
|
||||
} from '../core/janitor';
|
||||
import { fromVsCodeUri } from '../utils/vsc-utils';
|
||||
|
||||
const feature: FoamFeature = {
|
||||
activate: async (context: ExtensionContext, foamPromise: Promise<Foam>) => {
|
||||
@@ -73,7 +74,7 @@ const feature: FoamFeature = {
|
||||
|
||||
function updateDocumentInNoteGraph(foam: Foam, document: TextDocument) {
|
||||
foam.workspace.set(
|
||||
foam.services.parser.parse(document.uri, document.getText())
|
||||
foam.services.parser.parse(fromVsCodeUri(document.uri), document.getText())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ function generateReferenceList(
|
||||
return [];
|
||||
}
|
||||
|
||||
const note = foam.get(doc.uri);
|
||||
const note = foam.get(fromVsCodeUri(doc.uri));
|
||||
|
||||
// Should never happen as `doc` is usually given by `editor.document`, which
|
||||
// binds to an opened note.
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { Disposable, workspace } from 'vscode';
|
||||
import { createConfigFromFolders, FoamConfig } from '../core/config';
|
||||
import { getIgnoredFilesSetting } from '../settings';
|
||||
import { fromVsCodeUri } from '../utils/vsc-utils';
|
||||
|
||||
// TODO this is still to be improved - foam config should
|
||||
// not be dependent on vscode but at the moment it's convenient
|
||||
// to leverage it
|
||||
export const getConfigFromVscode = (): FoamConfig => {
|
||||
const workspaceFolders = workspace.workspaceFolders.map(dir => dir.uri);
|
||||
const workspaceFolders = workspace.workspaceFolders.map(dir =>
|
||||
fromVsCodeUri(dir.uri)
|
||||
);
|
||||
const excludeGlobs = getIgnoredFilesSetting();
|
||||
|
||||
return createConfigFromFolders(workspaceFolders, {
|
||||
|
||||
@@ -46,6 +46,12 @@ async function main() {
|
||||
'--disable-extensions',
|
||||
'--disable-workspace-trust',
|
||||
],
|
||||
// Running the tests with vscode 1.53.0 is causing issues in the output/error stream management,
|
||||
// which is causing a stack overflow, possibly due to a recursive callback.
|
||||
// Also see https://github.com/foambubble/foam/pull/479#issuecomment-774167127
|
||||
// Forcing the version to 1.52.0 solves the problem.
|
||||
// TODO: to review, further investigate, and roll back this workaround.
|
||||
version: '1.52.0',
|
||||
});
|
||||
} catch (err) {
|
||||
console.log('Error occurred while running Foam e2e tests:', err);
|
||||
@@ -54,7 +60,7 @@ async function main() {
|
||||
}
|
||||
|
||||
if (!isSuccess) {
|
||||
process.exit(1);
|
||||
throw new Error('Some Foam tests failed');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,26 +15,15 @@ import { runCLI } from '@jest/core';
|
||||
|
||||
const rootDir = path.resolve(__dirname, '../..');
|
||||
|
||||
const bufferLinesAndLog = (out: (value: string) => void) => {
|
||||
let currentLine = '';
|
||||
return (buffer: string) => {
|
||||
const lines = buffer.split(EOL);
|
||||
const partialLine = lines.pop() ?? '';
|
||||
if (lines.length > 0) {
|
||||
const [endOfCurrentLine, ...otherFullLines] = lines;
|
||||
currentLine += endOfCurrentLine;
|
||||
[currentLine, ...otherFullLines].forEach(l => out(l));
|
||||
currentLine = '';
|
||||
}
|
||||
currentLine += partialLine;
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
export function run(): Promise<void> {
|
||||
const errWrite = process.stderr.write;
|
||||
process.stderr.write = bufferLinesAndLog(console.log.bind(console));
|
||||
|
||||
process.stderr.write = (buffer: string) => {
|
||||
console.log(buffer);
|
||||
return true;
|
||||
};
|
||||
// process.on('unhandledRejection', err => {
|
||||
// throw err;
|
||||
// });
|
||||
process.env.FORCE_COLOR = '1';
|
||||
process.env.NODE_ENV = 'test';
|
||||
process.env.BABEL_ENV = 'test';
|
||||
@@ -74,7 +63,7 @@ export function run(): Promise<void> {
|
||||
|
||||
if (failures.length > 0) {
|
||||
console.log('Some Foam tests failed: ', failures.length);
|
||||
reject(`Foam e2e tests failed: ${JSON.stringify(failures)}`);
|
||||
reject(`Some Foam tests failed: ${failures.length}`);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import path from 'path';
|
||||
import { TextEncoder } from 'util';
|
||||
import { toVsCodeUri } from '../utils/vsc-utils';
|
||||
import { fromVsCodeUri, toVsCodeUri } from '../utils/vsc-utils';
|
||||
import { Logger } from '../core/utils/log';
|
||||
import { URI } from '../core/model/uri';
|
||||
import { Resource } from '../core/model/note';
|
||||
@@ -35,12 +35,15 @@ export const closeEditors = async () => {
|
||||
* @param path relative file path
|
||||
* @returns an object containing various information about the file created
|
||||
*/
|
||||
export const createFile = async (content: string, filepath?: string) => {
|
||||
const rootUri = vscode.workspace.workspaceFolders[0].uri;
|
||||
filepath = filepath ?? randomString() + '.md';
|
||||
const uri = vscode.Uri.joinPath(rootUri, filepath);
|
||||
const filenameComponents = path.parse(uri.fsPath);
|
||||
await vscode.workspace.fs.writeFile(uri, new TextEncoder().encode(content));
|
||||
export const createFile = async (content: string, filepath?: string[]) => {
|
||||
const rootUri = fromVsCodeUri(vscode.workspace.workspaceFolders[0].uri);
|
||||
filepath = filepath ?? [randomString() + '.md'];
|
||||
const uri = URI.joinPath(rootUri, ...filepath);
|
||||
const filenameComponents = path.parse(URI.toFsPath(uri));
|
||||
await vscode.workspace.fs.writeFile(
|
||||
toVsCodeUri(uri),
|
||||
new TextEncoder().encode(content)
|
||||
);
|
||||
return { uri, content, ...filenameComponents };
|
||||
};
|
||||
|
||||
|
||||
@@ -198,12 +198,18 @@ export function getContainsTooltip(titles: string[]): string {
|
||||
* https://code.visualstudio.com/updates/v1_52#_markdown-tree-tooltip-api
|
||||
* @param note A Foam Note
|
||||
*/
|
||||
export function getNoteTooltip(content: string): MarkdownString {
|
||||
export function getNoteTooltip(content: string): string {
|
||||
const STABLE_MARKDOWN_STRING_API_VERSION = '1.52.1';
|
||||
const strippedContent = stripFrontMatter(stripImages(content));
|
||||
return formatMarkdownTooltip(strippedContent);
|
||||
|
||||
if (version >= STABLE_MARKDOWN_STRING_API_VERSION) {
|
||||
return formatMarkdownTooltip(strippedContent) as any;
|
||||
}
|
||||
|
||||
return formatSimpleTooltip(strippedContent);
|
||||
}
|
||||
|
||||
function formatMarkdownTooltip(content: string): MarkdownString {
|
||||
export function formatMarkdownTooltip(content: string): MarkdownString {
|
||||
const LINES_LIMIT = 16;
|
||||
const { excerpt, lines } = getExcerpt(content, LINES_LIMIT);
|
||||
const totalLines = content.split('\n').length;
|
||||
|
||||
@@ -1,60 +1,25 @@
|
||||
import os from 'os';
|
||||
import { workspace, Uri } from 'vscode';
|
||||
import { URI } from '../core/model/uri';
|
||||
import { Uri } from 'vscode';
|
||||
import { fromVsCodeUri, toVsCodeUri } from './vsc-utils';
|
||||
|
||||
describe('uri conversion', () => {
|
||||
it('uses drive letter casing in windows #488 #507', () => {
|
||||
if (os.platform() === 'win32') {
|
||||
const uri = workspace.workspaceFolders[0].uri;
|
||||
const isDriveUppercase =
|
||||
uri.fsPath.charCodeAt(0) >= 'A'.charCodeAt(0) &&
|
||||
uri.fsPath.charCodeAt(0) <= 'Z'.charCodeAt(0);
|
||||
const [drive, path] = uri.fsPath.split(':');
|
||||
const posixPath = path.replace(/\\/g, '/');
|
||||
describe('URI conversion', () => {
|
||||
it('converts between Foam and VS Code URI', () => {
|
||||
const vsUnixUri = Uri.file('/this/is/a/path');
|
||||
const fUnixUri = fromVsCodeUri(vsUnixUri);
|
||||
expect(toVsCodeUri(fUnixUri)).toEqual(expect.objectContaining(fUnixUri));
|
||||
|
||||
const withUppercase = `/${drive.toUpperCase()}:${posixPath}`;
|
||||
const withLowercase = `/${drive.toLowerCase()}:${posixPath}`;
|
||||
const expected = isDriveUppercase ? withUppercase : withLowercase;
|
||||
|
||||
expect(fromVsCodeUri(Uri.file(withUppercase)).path).toEqual(expected);
|
||||
expect(fromVsCodeUri(Uri.file(withLowercase)).path).toEqual(expected);
|
||||
}
|
||||
});
|
||||
|
||||
it('correctly parses file paths', () => {
|
||||
const test = workspace.workspaceFolders[0].uri;
|
||||
const uri = URI.file(test.fsPath);
|
||||
expect(uri).toEqual(
|
||||
URI.create({
|
||||
scheme: 'file',
|
||||
path: test.path,
|
||||
})
|
||||
const vsWinUpperDriveUri = Uri.file('C:\\this\\is\\a\\path');
|
||||
const fWinUpperUri = fromVsCodeUri(vsWinUpperDriveUri);
|
||||
expect(toVsCodeUri(fWinUpperUri)).toEqual(
|
||||
expect.objectContaining(fWinUpperUri)
|
||||
);
|
||||
});
|
||||
|
||||
it('creates a proper string representation for file uris', () => {
|
||||
const test = workspace.workspaceFolders[0].uri;
|
||||
const uri = URI.file(test.fsPath);
|
||||
expect(URI.toString(uri)).toEqual(test.toString());
|
||||
});
|
||||
|
||||
it('is consistent when converting from VS Code to Foam URI', () => {
|
||||
const vsUri = workspace.workspaceFolders[0].uri;
|
||||
const fUri = fromVsCodeUri(vsUri);
|
||||
expect(toVsCodeUri(fUri)).toEqual(expect.objectContaining(fUri));
|
||||
});
|
||||
|
||||
it('is consistent when converting from Foam to VS Code URI', () => {
|
||||
const test = workspace.workspaceFolders[0].uri;
|
||||
const uri = URI.file(test.fsPath);
|
||||
const fUri = toVsCodeUri(uri);
|
||||
expect(fUri).toEqual(
|
||||
const vsWinLowerUri = Uri.file('c:\\this\\is\\a\\path');
|
||||
const fWinLowerUri = fromVsCodeUri(vsWinLowerUri);
|
||||
expect(toVsCodeUri(fWinLowerUri)).toEqual(
|
||||
expect.objectContaining({
|
||||
scheme: 'file',
|
||||
path: test.path,
|
||||
...fWinLowerUri,
|
||||
path: fWinUpperUri.path, // path is normalized to upper case
|
||||
})
|
||||
);
|
||||
expect(fromVsCodeUri(fUri)).toEqual(uri);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -188,6 +188,7 @@ const Actions = {
|
||||
|
||||
function initDataviz(channel) {
|
||||
const elem = document.getElementById(CONTAINER_ID);
|
||||
const painter = new Painter();
|
||||
graph(elem)
|
||||
.graphData(model.data)
|
||||
.backgroundColor(model.style.background)
|
||||
@@ -222,10 +223,12 @@ function initDataviz(channel) {
|
||||
});
|
||||
const label = info.title;
|
||||
|
||||
Draw(ctx)
|
||||
.circle(node.x, node.y, size + 0.2, border)
|
||||
.circle(node.x, node.y, size, fill)
|
||||
.text(label, node.x, node.y + size + 1, fontSize, textColor.toString());
|
||||
painter
|
||||
.circle(node.x, node.y, size, fill, border)
|
||||
.text(label, node.x, node.y + size + 1, fontSize, textColor);
|
||||
})
|
||||
.onRenderFramePost(ctx => {
|
||||
painter.paint(ctx);
|
||||
})
|
||||
.linkColor(link => getLinkColor(link, model))
|
||||
.onNodeHover(node => {
|
||||
@@ -402,24 +405,69 @@ function getLinkState(link, model) {
|
||||
: 'lessened';
|
||||
}
|
||||
|
||||
const Draw = ctx => ({
|
||||
circle: function(x, y, radius, color) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
class Painter {
|
||||
circlesByColor = new Map();
|
||||
bordersByColor = new Map();
|
||||
texts = [];
|
||||
|
||||
_addCircle(x, y, radius, color, isBorder = false) {
|
||||
if (color.opacity > 0) {
|
||||
const target = isBorder ? this.bordersByColor : this.circlesByColor;
|
||||
if (!target.has(color)) {
|
||||
target.set(color, []);
|
||||
}
|
||||
target.get(color).push({ x, y, radius });
|
||||
}
|
||||
}
|
||||
|
||||
_areSameColor(a, b) {
|
||||
return a.r === b.r && a.g === b.g && a.b === b.b && a.opacity === b.opacity;
|
||||
}
|
||||
|
||||
circle(x, y, radius, fill, border) {
|
||||
this._addCircle(x, y, radius + 0.2, border, true);
|
||||
if (!this._areSameColor(border, fill)) {
|
||||
this._addCircle(x, y, radius, fill);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
text: function(text, x, y, size, color) {
|
||||
ctx.font = `${size}px Sans-Serif`;
|
||||
}
|
||||
|
||||
text(text, x, y, size, color) {
|
||||
if (color.opacity > 0) {
|
||||
this.texts.push({ x, y, text, size, color });
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
paint(ctx) {
|
||||
// Draw nodes
|
||||
// first draw borders, then draw contents over them
|
||||
for (const target of [this.bordersByColor, this.circlesByColor]) {
|
||||
for (const [color, circles] of target.entries()) {
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = color;
|
||||
for (const circle of circles) {
|
||||
ctx.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI, false);
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
target.clear();
|
||||
}
|
||||
|
||||
// Draw labels
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillStyle = color;
|
||||
ctx.fillText(text, x, y);
|
||||
for (const text of this.texts) {
|
||||
ctx.font = `${text.size}px Sans-Serif`;
|
||||
ctx.fillStyle = text.color;
|
||||
ctx.fillText(text.text, text.x, text.y);
|
||||
}
|
||||
this.texts = [];
|
||||
|
||||
return this;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// init the app
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Note being refered as angel
|
||||
|
||||
This is just a link target for now.
|
||||
|
||||
We can use it for other things later if needed.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Angel reference
|
||||
|
||||
[[Note being refered as angel]]
|
||||
@@ -0,0 +1,11 @@
|
||||
# File with explicit link references
|
||||
|
||||
A Bug [^footerlink]. Here is [Another link][linkreference].
|
||||
I also want a [[first-document]].
|
||||
|
||||
[^footerlink]: https://foambubble.github.io/
|
||||
|
||||
[linkrefenrece]: https://foambubble.github.io/
|
||||
[//begin]: # 'Autogenerated link references for markdown compatibility'
|
||||
[first-document]: first-document 'First Document'
|
||||
[//end]: # 'Autogenerated link references'
|
||||
@@ -0,0 +1,7 @@
|
||||
# File with explicit link references
|
||||
|
||||
A Bug [^footerlink]. Here is [Another link][linkreference]
|
||||
|
||||
[^footerlink]: https://foambubble.github.io/
|
||||
|
||||
[linkrefenrece]: https://foambubble.github.io/
|
||||
@@ -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 -->
|
||||
[](#contributors-)
|
||||
[](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
[](https://foambubble.github.io/join-discord/g)
|
||||
@@ -167,6 +167,11 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center"><a href="http://www.prashu.com"><img src="https://avatars.githubusercontent.com/u/476729?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Prashanth Subrahmanyam</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ksprashu" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/JonasSprenger"><img src="https://avatars.githubusercontent.com/u/25108895?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jonas SPRENGER</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=JonasSprenger" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/Laptop765"><img src="https://avatars.githubusercontent.com/u/1468359?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Laptop765" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=eltociear" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/memeplex"><img src="https://avatars.githubusercontent.com/u/2845433?v=4?s=60" width="60px;" alt=""/><br /><sub><b>memeplex</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=memeplex" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/AndreiD049"><img src="https://avatars.githubusercontent.com/u/52671223?v=4?s=60" width="60px;" alt=""/><br /><sub><b>AndreiD049</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=AndreiD049" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -2417,10 +2417,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
|
||||
integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==
|
||||
|
||||
"@types/vscode@^1.61.0":
|
||||
version "1.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.61.0.tgz#c54335b6f84c19c69b1435b17cc0ce3b2cecfeec"
|
||||
integrity sha512-9k5Nwq45hkRwdfCFY+eKXeQQSbPoA114mF7U/4uJXRBJeGIO7MuJdhF1PnaDN+lllL9iKGQtd6FFXShBXMNaFg==
|
||||
"@types/vscode@^1.47.1":
|
||||
version "1.54.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.54.0.tgz#d28e3b3614054b2d6543c29412f60a986cabd9bb"
|
||||
integrity sha512-sHHw9HG4bTrnKhLGgmEiOS88OLO/2RQytUN4COX9Djv81zc0FSZsSiYaVyjNidDzUSpXsySKBkZ31lk2/FbdCg==
|
||||
|
||||
"@types/yargs-parser@*":
|
||||
version "20.2.0"
|
||||
|
||||
Reference in New Issue
Block a user