Compare commits
200 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
129482a43e | ||
|
|
0c1c4da154 | ||
|
|
7f4b700b21 | ||
|
|
686e05ed25 | ||
|
|
b2c7ecbb3d | ||
|
|
2c643e0c63 | ||
|
|
3b33d3d696 | ||
|
|
87633e68b1 | ||
|
|
6c7b558f36 | ||
|
|
12037704d7 | ||
|
|
e549fb8c21 | ||
|
|
ac7d3243c4 | ||
|
|
748df5e352 | ||
|
|
dcd46f1378 | ||
|
|
f9f751a27a | ||
|
|
0764da0dd6 | ||
|
|
f747d7445a | ||
|
|
eb74e57a9e | ||
|
|
a01cf8ec8d | ||
|
|
5b63fa8108 | ||
|
|
ddf7ddf7b3 | ||
|
|
4b263667ea | ||
|
|
309194b3c3 | ||
|
|
c4f35b7649 | ||
|
|
b9e18de7e7 | ||
|
|
23cf5a021e | ||
|
|
8231ed14c5 | ||
|
|
3bea283c04 | ||
|
|
a3cffe8418 | ||
|
|
675e7fa216 | ||
|
|
87d12bf3af | ||
|
|
e118ab74b5 | ||
|
|
04a61eed0e | ||
|
|
350b3005f1 | ||
|
|
f7293b1eb4 | ||
|
|
672eb6ed20 | ||
|
|
37a9bc49bc | ||
|
|
38741ca52e | ||
|
|
ed762618ed | ||
|
|
21a32382a2 | ||
|
|
7e6c041b87 | ||
|
|
c9a0a1d53c | ||
|
|
0516088656 | ||
|
|
f98ff336bf | ||
|
|
1b1396d949 | ||
|
|
ebaab2ee59 | ||
|
|
c6a754f1a8 | ||
|
|
3fb35494d4 | ||
|
|
a7af7689a4 | ||
|
|
5b7a2ab022 | ||
|
|
88227d4028 | ||
|
|
a531c9f9cd | ||
|
|
ff172dd709 | ||
|
|
8bad56f71e | ||
|
|
4e608a67a9 | ||
|
|
a2f7c8a549 | ||
|
|
63c6b7056e | ||
|
|
b48268e20f | ||
|
|
f5f476e717 | ||
|
|
25172ee100 | ||
|
|
cbb0dab124 | ||
|
|
d570983e16 | ||
|
|
b5e979ead6 | ||
|
|
aed907663a | ||
|
|
a65325a6e1 | ||
|
|
772cba4b43 | ||
|
|
f1a0054141 | ||
|
|
854e329c90 | ||
|
|
0978bebd5b | ||
|
|
6eaae23e19 | ||
|
|
4c615bdb02 | ||
|
|
3adf853b89 | ||
|
|
111c7718c4 | ||
|
|
9c7f03d62e | ||
|
|
0d90fc5c5a | ||
|
|
537c78b630 | ||
|
|
6d210590b2 | ||
|
|
ab8e97ce0b | ||
|
|
f756d9c966 | ||
|
|
aa7669f8ad | ||
|
|
38bd5f67f2 | ||
|
|
336b8cfbba | ||
|
|
ea03b86338 | ||
|
|
449c062566 | ||
|
|
880c2e3d3b | ||
|
|
17cb619480 | ||
|
|
6deae95d80 | ||
|
|
1c0ebb8af7 | ||
|
|
fe56823e76 | ||
|
|
6956e0779a | ||
|
|
c8f1f8e03a | ||
|
|
9589071760 | ||
|
|
6f65c10746 | ||
|
|
e04409e74f | ||
|
|
dba0a72d98 | ||
|
|
fd23b1d010 | ||
|
|
cfb946a5f2 | ||
|
|
32c3a484d6 | ||
|
|
4195797024 | ||
|
|
fa405f5f65 | ||
|
|
4fd573b9e4 | ||
|
|
f613e1b9e2 | ||
|
|
0ada7d8e2c | ||
|
|
8b39bcdf16 | ||
|
|
6073dc246d | ||
|
|
5b671d59a8 | ||
|
|
8abea48b5c | ||
|
|
2eeb2e156b | ||
|
|
dc76660a63 | ||
|
|
e8eeffa4ca | ||
|
|
7d4f5e1532 | ||
|
|
e7749cd52b | ||
|
|
c6a4eab744 | ||
|
|
c88bd6f2f0 | ||
|
|
304a803310 | ||
|
|
632c41ac5f | ||
|
|
ec636809d8 | ||
|
|
af43a31ae8 | ||
|
|
7235af70dd | ||
|
|
de84541692 | ||
|
|
84fab168ce | ||
|
|
4f116cfc88 | ||
|
|
fd71dbe557 | ||
|
|
df4bf5a5cb | ||
|
|
122db20695 | ||
|
|
3b40e26a83 | ||
|
|
bbe44ea21b | ||
|
|
59bb2eb38f | ||
|
|
97f87692b6 | ||
|
|
4f76a6b24a | ||
|
|
c822589733 | ||
|
|
b748629c68 | ||
|
|
b1aa182fac | ||
|
|
c7155d3956 | ||
|
|
91385fc937 | ||
|
|
9f42893d61 | ||
|
|
65497ba6d3 | ||
|
|
f5ad5245b4 | ||
|
|
d1a6412cb7 | ||
|
|
e03fcf5dfa | ||
|
|
f174aa7162 | ||
|
|
2d9e1f5903 | ||
|
|
cf5daa4d22 | ||
|
|
e9eb3032e8 | ||
|
|
a8a418824f | ||
|
|
dd06d0b805 | ||
|
|
11af331694 | ||
|
|
5da1012fab | ||
|
|
8015a35f39 | ||
|
|
587466a210 | ||
|
|
52bc1ba13d | ||
|
|
8f045a3ff4 | ||
|
|
b2be5a7311 | ||
|
|
87e2400070 | ||
|
|
78e946c177 | ||
|
|
80e46f7898 | ||
|
|
5f89a59b07 | ||
|
|
f921c095aa | ||
|
|
a51e0613ea | ||
|
|
9df71adb64 | ||
|
|
17c216736b | ||
|
|
66a8c3bd49 | ||
|
|
5f7b3b7c02 | ||
|
|
9ed0d6e18e | ||
|
|
0140748550 | ||
|
|
356dcc5579 | ||
|
|
265afdee19 | ||
|
|
de7c686f75 | ||
|
|
8dfc5bd2ff | ||
|
|
b3c5e75aa2 | ||
|
|
000da4bd1c | ||
|
|
86749940c2 | ||
|
|
27f9a08870 | ||
|
|
e791726692 | ||
|
|
a3c00744ca | ||
|
|
00220b1f6c | ||
|
|
759f4f1963 | ||
|
|
d86fc7f433 | ||
|
|
bd9c6806fa | ||
|
|
4c9a9cec56 | ||
|
|
8a91a6ab36 | ||
|
|
667037bc14 | ||
|
|
30cc9fc9f0 | ||
|
|
abed7be3ec | ||
|
|
d31e094358 | ||
|
|
f320af05c5 | ||
|
|
ee229dac84 | ||
|
|
877d843f60 | ||
|
|
af65e4d5f7 | ||
|
|
dd9fa0af79 | ||
|
|
14f68aea30 | ||
|
|
f68c2ab6db | ||
|
|
bae99a6184 | ||
|
|
861f7dbba7 | ||
|
|
6a4b90d6d7 | ||
|
|
f73ddb88d4 | ||
|
|
41ca70f23c | ||
|
|
7cf7811b85 | ||
|
|
eef0aa7f0b | ||
|
|
d222cfbbec |
@@ -752,6 +752,168 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Laptop765",
|
||||
"name": "Paul",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1468359?v=4",
|
||||
"profile": "https://github.com/Laptop765",
|
||||
"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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "iam-yan",
|
||||
"name": "Yan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/48427014?v=4",
|
||||
"profile": "https://github.com/iam-yan",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jimt",
|
||||
"name": "Jim Tittsler",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/180326?v=4",
|
||||
"profile": "https://WikiEducator.org/User:JimTittsler",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MalcolmMielle",
|
||||
"name": "Malcolm Mielle",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4457840?v=4",
|
||||
"profile": "http://malcolmmielle.wordpress.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "veesar",
|
||||
"name": "Veesar",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/74916913?v=4",
|
||||
"profile": "https://snippets.page/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bentongxyz",
|
||||
"name": "bentongxyz",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/60358804?v=4",
|
||||
"profile": "https://github.com/bentongxyz",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "techCarpenter",
|
||||
"name": "Brian DeVries",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/42778030?v=4",
|
||||
"profile": "https://brianjdevries.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cliffordfajardo",
|
||||
"name": "Clifford Fajardo ",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6743796?v=4",
|
||||
"profile": "http://Cliffordfajardo.com",
|
||||
"contributions": [
|
||||
"tool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "chrisUsick",
|
||||
"name": "Chris Usick",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6589365?v=4",
|
||||
"profile": "http://cu-dev.ca",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "josephdecock",
|
||||
"name": "Joe DeCock",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1145533?v=4",
|
||||
"profile": "https://github.com/josephdecock",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "drewtyler",
|
||||
"name": "Drew Tyler",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5640816?v=4",
|
||||
"profile": "http://www.drewtyler.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Lauviah0622",
|
||||
"name": "Lauviah0622",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/43416399?v=4",
|
||||
"profile": "https://github.com/Lauviah0622",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "joshdover",
|
||||
"name": "Josh Dover",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1813008?v=4",
|
||||
"profile": "https://www.elastic.co/elastic-agent",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "phelma",
|
||||
"name": "Phil Helm",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4057948?v=4",
|
||||
"profile": "http://phelm.co.uk",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lingyv-li",
|
||||
"name": "Larry Li",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8937944?v=4",
|
||||
"profile": "https://github.com/lingyv-li",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -1,20 +1,60 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/class-name-casing": "warn",
|
||||
"@typescript-eslint/semi": "warn",
|
||||
"curly": "warn",
|
||||
"eqeqeq": "warn",
|
||||
"no-throw-literal": "warn",
|
||||
"semi": "off",
|
||||
"require-await": "warn"
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"env": { "node": true, "es6": true },
|
||||
"plugins": ["@typescript-eslint", "import", "jest"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:import/recommended",
|
||||
"plugin:import/typescript",
|
||||
"plugin:jest/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"no-use-before-define": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/interface-name-prefix": "off",
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
{
|
||||
"devDependencies": ["**/src/test/**", "**/src/**/*{test,spec}.ts"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
// Restrict usage of fs module outside tests to keep foam compatible with the browser
|
||||
"files": ["**/src/**"],
|
||||
"excludedFiles": ["**/src/test/**", "**/src/**/*{test,spec}.ts"],
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"name": "fs",
|
||||
"message": "Extension code must not rely Node.js filesystem, use vscode.workspace.fs instead."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"import/core-modules": ["vscode"],
|
||||
"import/parsers": {
|
||||
"@typescript-eslint/parser": [".ts", ".tsx"]
|
||||
},
|
||||
"import/resolver": {
|
||||
"typescript": {
|
||||
"alwaysTryTypes": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ignorePatterns": ["**/core/common/**", "*.js"],
|
||||
"reportUnusedDisableDirectives": true
|
||||
}
|
||||
|
||||
23
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -1,23 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us be foamier
|
||||
labels: bug
|
||||
---
|
||||
|
||||
- Foam version: <!-- Check in the VSCode extension tab. -->
|
||||
- Platform: Windows | Mac | Linux
|
||||
- Issue occur on the [foam template](https://github.com/foambubble/foam-template) repo: Yes | No
|
||||
|
||||
**Summary**
|
||||
<!-- A clear and concise description of what the bug is.-->
|
||||
|
||||
**Steps to reproduce**
|
||||
1.
|
||||
2.
|
||||
|
||||
**Additional information**
|
||||
<!-- Add any other context about the problem here. -->
|
||||
Feel free to attach any of the following that might help with debugging the issue:
|
||||
- screenshots
|
||||
- a zip with a minimal repo to reproduce the issue
|
||||
- the Foam log in VsCode (see [instructions](https://github.com/foambubble/foam/blob/master/docs/features/foam-logging-in-vscode.md))
|
||||
97
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
name: 'Bug report'
|
||||
description: Create a report to help us improve
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for reporting an issue :pray:.
|
||||
|
||||
This issue tracker is for reporting bugs found in `foam` (https://github.com/foambubble).
|
||||
If you have a question about how to achieve something and are struggling, please post a question
|
||||
inside of either of the following places:
|
||||
- Foam's Discussion's tab: https://github.com/foambubble/foam/discussions
|
||||
- Foam's Discord channel: https://foambubble.github.io/join-discord/g
|
||||
|
||||
|
||||
Before submitting a new bug/issue, please check the links below to see if there is a solution or question posted there already:
|
||||
- Foam's Issue's tab: https://github.com/foambubble/foam/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc
|
||||
- Foam's closed issues tab: https://github.com/foambubble/foam/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed
|
||||
- Foam's Discussions tab: https://github.com/foambubble/foam/discussions
|
||||
|
||||
The more information you fill in, the better the community can help you.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: Provide a clear and concise description of the challenge you are running into.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: reproducible_example
|
||||
attributes:
|
||||
label: Small Reproducible Example
|
||||
description: |
|
||||
Note:
|
||||
- Your bug will may get fixed much faster if there is a way we can somehow run your example or code.
|
||||
- To create a shareable example, consider cloning the following Foam Github template: https://github.com/foambubble/foam-template
|
||||
- Please read these tips for providing a minimal example: https://stackoverflow.com/help/mcve.
|
||||
placeholder: |
|
||||
e.g. Link to your github repository containing a small reproducible example that the team can run.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: Steps to Reproduce the Bug or Issue
|
||||
description: Describe the steps we have to take to reproduce the behavior.
|
||||
placeholder: |
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: Provide a clear and concise description of what you expected to happen.
|
||||
placeholder: |
|
||||
As a user, I expected ___ behavior but i am seeing ___
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: screenshots_or_videos
|
||||
attributes:
|
||||
label: Screenshots or Videos
|
||||
description: |
|
||||
If applicable, add screenshots or a video to help explain your problem.
|
||||
For more information on the supported file image/file types and the file size limits, please refer
|
||||
to the following link: https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/attaching-files
|
||||
placeholder: |
|
||||
You can drag your video or image files inside of this editor ↓
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating System Version
|
||||
description: What opearting system are you using?
|
||||
placeholder: |
|
||||
- OS: [e.g. macOS, Windows, Linux]
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: vscode_version
|
||||
attributes:
|
||||
label: Visual Studio Code Version
|
||||
description: |
|
||||
What version of Visual Studio Code are you using?
|
||||
How to find Visual Studio Code Version: https://code.visualstudio.com/docs/supporting/FAQ#_how-do-i-find-the-version
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: |
|
||||
Add any other context about the problem here.
|
||||
The Foam log output for VSCode can be found here: https://github.com/foambubble/foam/blob/master/docs/features/foam-logging-in-vscode.md
|
||||
6
.github/ISSUE_TEMPLATE/feature.md
vendored
@@ -1,6 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea to help us be foamier
|
||||
---
|
||||
|
||||
<!-- Describe the feature you'd like. -->
|
||||
42
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Feature request
|
||||
description: Suggest an idea for the `Foam` project
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
This issue form is for requesting features only!
|
||||
If you want to report a bug, please use the [bug report](https://github.com/foambubble/foam/issues/new?assignees=&labels=&template=bug_report.yml) form.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe.
|
||||
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
description: A clear and concise description of what you want to happen.
|
||||
placeholder: |
|
||||
As a user, I expected ___ behavior but ___ ...
|
||||
|
||||
Ideal Steps I would like to see:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. ....
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots or Videos
|
||||
description: |
|
||||
If applicable, add screenshots or a video to help explain your problem.
|
||||
For more information on the supported file image/file types and the file size limits, please refer
|
||||
to the following link: https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/attaching-files
|
||||
placeholder: |
|
||||
You can drag your video or image files inside of this editor ↓
|
||||
19
.github/workflows/ci.yml
vendored
@@ -3,17 +3,16 @@ name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
# The following will also make the workflow run on all PRs, internal and external.
|
||||
# This would create duplicate runs, that we are skipping by adding the "if" to the jobs below.
|
||||
# See https://github.community/t/duplicate-checks-on-push-and-pull-request-simultaneous-event/18012
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-18.04
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'foambubble/foam'
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Setup Node
|
||||
@@ -27,7 +26,7 @@ jobs:
|
||||
node_modules
|
||||
*/*/node_modules
|
||||
packages/foam-vscode/.vscode-test
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock', 'packages/foam-vscode/src/test/run-tests.ts') }}
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock', 'packages/foam-vscode/src/test/run-tests.ts') }}-${{ secrets.CACHE_VERSION }}
|
||||
- name: Install Dependencies
|
||||
run: yarn
|
||||
- name: Check Lint Rules
|
||||
@@ -39,9 +38,9 @@ jobs:
|
||||
matrix:
|
||||
os: [macos-10.15, ubuntu-18.04, windows-2019]
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'foambubble/foam'
|
||||
env:
|
||||
OS: ${{ matrix.os }}
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Setup Node
|
||||
@@ -55,12 +54,12 @@ jobs:
|
||||
node_modules
|
||||
*/*/node_modules
|
||||
packages/foam-vscode/.vscode-test
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock', 'packages/foam-vscode/src/test/run-tests.ts') }}
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock', 'packages/foam-vscode/src/test/run-tests.ts') }}-${{ secrets.CACHE_VERSION }}
|
||||
- name: Install Dependencies
|
||||
run: yarn
|
||||
- name: Build Packages
|
||||
run: yarn build
|
||||
- name: Run Tests
|
||||
uses: GabrielBB/xvfb-action@v1.0
|
||||
uses: GabrielBB/xvfb-action@v1.4
|
||||
with:
|
||||
run: yarn test
|
||||
run: yarn test --stream
|
||||
|
||||
1
.gitignore
vendored
@@ -9,3 +9,4 @@ dist
|
||||
docs/_site
|
||||
docs/.sass-cache
|
||||
docs/.jekyll-metadata
|
||||
.test-workspace
|
||||
|
||||
122
.vscode/launch.json
vendored
@@ -3,83 +3,51 @@
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"inputs": [
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"id": "packageName",
|
||||
"type": "pickString",
|
||||
"description": "Select the package in which this test is located",
|
||||
"options": ["foam-core", "foam-vscode"],
|
||||
"default": "foam-core"
|
||||
"name": "Debug Jest Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"${workspaceFolder}/packages/foam-vscode/.test-workspace",
|
||||
"--disable-extensions",
|
||||
"--disable-workspace-trust",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/packages/foam-vscode",
|
||||
"--extensionTestsPath=${workspaceFolder}/packages/foam-vscode/out/test/suite"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/packages/foam-vscode/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
},
|
||||
{
|
||||
"name": "Run VSCode Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/packages/foam-vscode"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/packages/foam-vscode/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"name": "vscode-jest-tests",
|
||||
"request": "launch",
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"disableOptimisticBPs": true,
|
||||
"cwd": "${workspaceFolder}/packages/foam-vscode",
|
||||
"runtimeExecutable": "yarn",
|
||||
"args": [
|
||||
"jest",
|
||||
"--runInBand",
|
||||
"--watchAll=false"
|
||||
]
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"name": "vscode-jest-tests",
|
||||
"request": "launch",
|
||||
"runtimeArgs": ["workspace", "${input:packageName}", "run", "test"], // ${yarnWorkspaceName} is what we're missing
|
||||
"args": [
|
||||
"--runInBand"
|
||||
],
|
||||
"runtimeExecutable": "yarn",
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"disableOptimisticBPs": true,
|
||||
},
|
||||
{
|
||||
"name": "Debug Jest Tests",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeArgs": [
|
||||
"--inspect-brk",
|
||||
"${workspaceRoot}/node_modules/.bin/tsdx",
|
||||
"test",
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"cwd": "${workspaceFolder}/packages/foam-core",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
},
|
||||
{
|
||||
"name": "Run VSCode Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/packages/foam-vscode"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/packages/foam-vscode/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
},
|
||||
|
||||
// @NOTE: This task is broken. VSCode e2e tests are currently disabled
|
||||
// due to incompability of jest and mocha inside a typescript monorepo
|
||||
// Contributions to fix this are welcome!
|
||||
{
|
||||
"name": "Test VSCode Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/packages/foam-vscode",
|
||||
"--extensionTestsPath=${workspaceFolder}/packages/foam-vscode/out/test/suite/index"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/packages/foam-vscode/out/test/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
},
|
||||
{
|
||||
"name": "Test Core",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/node_modules/tsdx/dist/index.js",
|
||||
"args": ["test"],
|
||||
"cwd": "${workspaceFolder}/packages/foam-core",
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
12
.vscode/settings.json
vendored
@@ -20,13 +20,13 @@
|
||||
"**/node_modules/**/*",
|
||||
"packages/**/*"
|
||||
],
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"prettier.requireConfig": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabSize": 2,
|
||||
"jest.debugCodeLens.showWhenTestStateIn": ["fail", "unknown", "pass"],
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnSaveMode": "file",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"jest.autoRun": "off",
|
||||
"jest.rootPath": "packages/foam-vscode",
|
||||
"jest.jestCommandLine": "yarn jest",
|
||||
"gitdoc.enabled": false,
|
||||
"jest.autoEnable": false,
|
||||
"jest.runAllTestsFirst": false,
|
||||
"search.mode": "reuseEditor"
|
||||
}
|
||||
|
||||
54
.vscode/tasks.json
vendored
@@ -1,31 +1,31 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "watch: foam-vscode",
|
||||
"type": "npm",
|
||||
"script": "start:vscode",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "always"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "test: all packages",
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
}
|
||||
]
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "watch: foam-vscode",
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "always"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "test: all packages",
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
BIN
assets/screenshots/feature-backlinks-panel.gif
Normal file
|
After Width: | Height: | Size: 821 KiB |
BIN
assets/screenshots/feature-daily-note.gif
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
assets/screenshots/feature-definition-references.gif
Normal file
|
After Width: | Height: | Size: 859 KiB |
BIN
assets/screenshots/feature-definitions-generation.gif
Normal file
|
After Width: | Height: | Size: 621 KiB |
BIN
assets/screenshots/feature-link-autocompletion.gif
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/screenshots/feature-link-sync.gif
Normal file
|
After Width: | Height: | Size: 934 KiB |
BIN
assets/screenshots/feature-navigation.gif
Normal file
|
After Width: | Height: | Size: 935 KiB |
BIN
assets/screenshots/feature-note-embed.gif
Normal file
|
After Width: | Height: | Size: 298 KiB |
BIN
assets/screenshots/feature-placeholder-orphan-panel.gif
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
assets/screenshots/feature-preview-navigation.gif
Normal file
|
After Width: | Height: | Size: 369 KiB |
BIN
assets/screenshots/feature-show-graph.gif
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
assets/screenshots/feature-syntax-highlight.png
Normal file
|
After Width: | Height: | Size: 394 KiB |
BIN
assets/screenshots/feature-tags-panel.gif
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
assets/screenshots/feature-templates.gif
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
assets/screenshots/feature-unique-wikilink-completion.gif
Normal file
|
After Width: | Height: | Size: 306 KiB |
BIN
assets/screenshots/feature-wikilink-diagnostics.gif
Normal file
|
After Width: | Height: | Size: 202 KiB |
|
Before Width: | Height: | Size: 593 KiB After Width: | Height: | Size: 593 KiB |
@@ -18,8 +18,6 @@ Before you start contributing we recommend that you read the following links:
|
||||
We understand that diving in an unfamiliar codebase may seem scary,
|
||||
to make it easier for new contributors we provide some resources:
|
||||
|
||||
- [[architecture]] - This document describes the architecture of Foam and how the repository is structured.
|
||||
|
||||
You can also see [existing issues](https://github.com/foambubble/foam/issues) and help out!
|
||||
Finally, the easiest way to help, is to use it and provide feedback by [submitting issues](https://github.com/foambubble/foam/issues/new/choose) or participating in the [Foam Community Discord](https://foambubble.github.io/join-discord/g)!
|
||||
|
||||
@@ -35,22 +33,48 @@ If you're interested in contributing, this short guide will help you get things
|
||||
|
||||
`yarn install`
|
||||
|
||||
3. This project uses [Yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/). `foam-vscode` relies on `foam-core`. This means we need to compile it before we do any extension development. From the root, run the command:
|
||||
3. From the root, run the command:
|
||||
|
||||
`yarn build`
|
||||
|
||||
You should now be ready to start working!
|
||||
|
||||
### Structure of the project
|
||||
|
||||
Foam code and documentation live in the monorepo at [foambubble/foam](https://github.com/foambubble/foam/).
|
||||
|
||||
- [/docs](https://github.com/foambubble/foam/tree/master/docs): documentation and [[recipes]].
|
||||
|
||||
Exceptions to the monorepo are:
|
||||
|
||||
- The starter template at [foambubble/foam-template](https://github.com/foambubble/)
|
||||
- All other [[recommended-extensions]] live in their respective GitHub repos
|
||||
|
||||
This project uses [Yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/).
|
||||
|
||||
Originally Foam had:
|
||||
|
||||
- [/packages/foam-core](https://github.com/foambubble/foam/tree/master/packages/foam-core) - Powers the core functionality in Foam across all platforms.
|
||||
- [/packages/foam-vscode](https://github.com/foambubble/foam/tree/master/packages/foam-vscode) - The core VS Code plugin.
|
||||
|
||||
To improve DX we have moved the `foam-core` module into `packages/foam-vscode/src/core`, but from a development point of view it's useful to think of the `foam-vscode/src/core` "submodule" as something that might be extracted in the future.
|
||||
|
||||
For all intents and purposes this means two things:
|
||||
|
||||
1. nothing in `foam-vscode/src/core` should depend on files outside of this directory
|
||||
2. code in `foam-vscode/src/core` should NOT depend on `vscode` library
|
||||
|
||||
We have kept the yarn workspace for the time being as we might use it to pull out `foam-core` in the future, or we might need it for other packages that the VS Code plugin could depend upon (e.g. currently the graph visualization is inside the module, but it might be pulled out if its complexity increases).
|
||||
|
||||
### Testing
|
||||
|
||||
Code needs to come with tests.
|
||||
We use the following convention in Foam:
|
||||
|
||||
- *.test.ts are unit tests
|
||||
- *.spec.ts are integration tests
|
||||
- `*.test.ts` are unit tests
|
||||
- `*.spec.ts` are integration tests
|
||||
|
||||
Also, note that tests in `foam-core` live in the `test` directory.
|
||||
Tests in `foam-vscode` live alongside the code in `src`.
|
||||
Tests live alongside the code in `src`.
|
||||
|
||||
### The VS Code Extension
|
||||
|
||||
@@ -71,5 +95,6 @@ Feel free to modify and submit a PR if this guide is out-of-date or contains err
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[principles]: principles.md "Principles"
|
||||
[code-of-conduct]: code-of-conduct.md "Code of Conduct"
|
||||
[architecture]: dev/architecture.md "Architecture"
|
||||
[recipes]: recipes/recipes.md "Recipes"
|
||||
[recommended-extensions]: recommended-extensions.md "Recommended Extensions"
|
||||
[//end]: # "Autogenerated link references"
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
tags: architecture
|
||||
---
|
||||
# Architecture
|
||||
|
||||
This document aims to provide a quick overview of the Foam architecture!
|
||||
|
||||
Foam code and documentation live in the monorepo at [foambubble/foam](https://github.com/foambubble/foam/).
|
||||
|
||||
- [/docs](https://github.com/foambubble/foam/tree/master/docs): documentation and [[recipes]].
|
||||
- [/packages/foam-core](https://github.com/foambubble/foam/tree/master/packages/foam-core) - Powers the core functionality in Foam across all platforms.
|
||||
- [/packages/foam-vscode](https://github.com/foambubble/foam/tree/master/packages/foam-vscode) - The core VSCode plugin.
|
||||
|
||||
Exceptions to the monorepo are:
|
||||
|
||||
- The starter template at [foambubble/foam-template](https://github.com/foambubble/)
|
||||
- All other [[recommended-extensions]] live in their respective GitHub repos.
|
||||
- [foam-cli](https://github.com/foambubble/foam-cli) - The Foam CLI tool.
|
||||
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[recipes]: ../recipes/recipes.md "Recipes"
|
||||
[recommended-extensions]: ../recommended-extensions.md "Recommended Extensions"
|
||||
[//end]: # "Autogenerated link references"
|
||||
27
docs/dev/releasing-foam.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Releasing Foam
|
||||
|
||||
1. Get to the latest code
|
||||
- `git checkout master && git fetch && git rebase`
|
||||
2. Sanity checks
|
||||
- `yarn reset`
|
||||
- `yarn test`
|
||||
3. Update change log
|
||||
- `./packages/foam-vscode/CHANGELOG.md`
|
||||
- `git add *`
|
||||
- `git commit -m"Preparation for next release"`
|
||||
4. Update version
|
||||
- `$ cd packages/foam-vscode`
|
||||
- `foam-vscode$ yarn lerna version <version>` (where `version` is `patch/minor/major`)
|
||||
- `cd ../..`
|
||||
5. Package extension
|
||||
- `$ yarn vscode:package-extension`
|
||||
6. Publish extension
|
||||
- `$ yarn vscode:publish-extension`
|
||||
7. Update the release notes in GitHub
|
||||
- in GitHub, top right, click on "releases"
|
||||
- select "tags" in top left
|
||||
- select the tag that was just released, click "edit" and copy release information from changelog
|
||||
- publish (no need to attach artifacts)
|
||||
8. Annouce on Discord
|
||||
|
||||
Steps 1 to 6 should really be replaced by a GitHub action...
|
||||
@@ -1,6 +1,6 @@
|
||||
# Backlinking
|
||||
|
||||
When using [[wikilinks]], you can find all notes that link to a specific note in the [VS Code Markdown Notes](https://marketplace.visualstudio.com/items?itemName=kortina.vscode-markdown-notes) **Backlinks Explorer**
|
||||
When using [[wikilinks]], you can find all notes that link to a specific note in the **Backlinks Explorer**
|
||||
|
||||
- Run `Cmd` + `Shift` + `P` (`Ctrl` + `Shift` + `P` for Windows), type "backlinks" and run the **Explorer: Focus on Backlinks** view.
|
||||
- Keep this pane always visible to discover relationships between your thoughts
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Creating New Notes
|
||||
|
||||
- Write out a new `[[wikilink]]` and `Cmd` + `Click` to create a new file and enter it.
|
||||
- For keyboard navigation, use the 'Follow Definition' key `F12` (or [remap key binding](https://code.visualstudio.com/docs/getstarted/keybindings) to something more ergonomic)
|
||||
- For keyboard navigation, use the 'Follow Definition' key `F12` (or [remap the 'editor.action.revealDefinition' key binding](https://code.visualstudio.com/docs/getstarted/keybindings) to something more ergonomic)
|
||||
- `Cmd` + `Shift` + `P` (`Ctrl` + `Shift` + `P` for Windows), execute `Foam: Create New Note` and enter a **Title Case Name** to create `Title Case Name.md`
|
||||
- Add a keyboard binding to make creating new notes easier.
|
||||
- The [[note-templates]] used by this command can be customized.
|
||||
|
||||
@@ -41,12 +41,41 @@ 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_SLUG` | The sluggified title of the note (using the default github slug method). If used, Foam will prompt you to enter a title for the note unless `FOAM_TITLE` has already caused the prompt. |
|
||||
| `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.
|
||||
### `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
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
- Ensure that you have all the [[recommended-extensions]] installed in Visual Studio Code
|
||||
- Reload Visual Studio Code by running `Cmd` + `Shift` + `P` (`Ctrl` + `Shift` + `P` for Windows), type "reload" and run the **Developer: Reload Window** command to for the updated extensions take effect
|
||||
- Check the formatting rules for links on [[foam-file-format]], [[wikilinks]] and [[link-formatting-and-autocompletion]]
|
||||
- Check the formatting rules for links on [[foam-file-format]] and [[wikilinks]]
|
||||
|
||||
## I don't want Foam enabled for all my workspaces
|
||||
Any extension you install in Visual Studio Code is enabled by default. Give the philosophy of Foam it works out of the box without doing any configuration upfront. In case you want to disable Foam for a specific workspace, or disable Foam by default and enable it for specific workspaces, it is advised to follow the best practices as [documented by Visual Studio Code](https://code.visualstudio.com/docs/editor/extension-marketplace#_manage-extensions)
|
||||
|
||||
@@ -31,8 +31,6 @@ You can use **Foam** for organising your research, keeping re-discoverable notes
|
||||
|
||||
**Foam** is a tool that supports creating relationships between thoughts and information to help you think better.
|
||||
|
||||

|
||||
|
||||
Whether you want to build a [Second Brain](https://www.buildingasecondbrain.com/) or a [Zettelkasten](https://zettelkasten.de/posts/overview/), write a book, or just get better at long-term learning, **Foam** can help you organise your thoughts if you follow these simple rules:
|
||||
|
||||
1. Create a single **Foam** workspace for all your knowledge and research following the [Getting started](#getting-started) guide.
|
||||
@@ -72,6 +70,8 @@ These instructions assume you have a GitHub account, and you have Visual Studio
|
||||
|
||||
After setting up the repository, open `.vscode/settings.json` and edit, add or remove any settings you'd like for your Foam workspace.
|
||||
|
||||
* *If using a [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) as noted above, make sure that your **Foam** directory is first in the list. There are some settings that will need to be migrated from `.vscode/settings.json` to your `.code-workspace` file.*
|
||||
|
||||
To learn more about how to use **Foam**, read the [[recipes]].
|
||||
|
||||
Getting stuck in the setup? Read the [[frequently-asked-questions]].
|
||||
@@ -210,6 +210,30 @@ 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/theowenyoung"><img src="https://avatars.githubusercontent.com/u/62473795?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Owen Young</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=theowenyoung" title="Documentation">📖</a> <a href="#content-theowenyoung" title="Content">🖋</a></td>
|
||||
<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>
|
||||
<td align="center"><a href="https://github.com/iam-yan"><img src="https://avatars.githubusercontent.com/u/48427014?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Yan</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=iam-yan" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://WikiEducator.org/User:JimTittsler"><img src="https://avatars.githubusercontent.com/u/180326?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jim Tittsler</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jimt" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://malcolmmielle.wordpress.com/"><img src="https://avatars.githubusercontent.com/u/4457840?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Malcolm Mielle</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MalcolmMielle" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://snippets.page/"><img src="https://avatars.githubusercontent.com/u/74916913?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Veesar</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=veesar" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/bentongxyz"><img src="https://avatars.githubusercontent.com/u/60358804?v=4?s=60" width="60px;" alt=""/><br /><sub><b>bentongxyz</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bentongxyz" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://brianjdevries.com"><img src="https://avatars.githubusercontent.com/u/42778030?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Brian DeVries</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=techCarpenter" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://Cliffordfajardo.com"><img src="https://avatars.githubusercontent.com/u/6743796?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Clifford Fajardo </b></sub></a><br /><a href="#tool-cliffordfajardo" title="Tools">🔧</a></td>
|
||||
<td align="center"><a href="http://cu-dev.ca"><img src="https://avatars.githubusercontent.com/u/6589365?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Chris Usick</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chrisUsick" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/josephdecock"><img src="https://avatars.githubusercontent.com/u/1145533?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joe DeCock</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=josephdecock" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://www.drewtyler.com"><img src="https://avatars.githubusercontent.com/u/5640816?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Drew Tyler</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=drewtyler" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/Lauviah0622"><img src="https://avatars.githubusercontent.com/u/43416399?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Lauviah0622</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Lauviah0622" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.elastic.co/elastic-agent"><img src="https://avatars.githubusercontent.com/u/1813008?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Josh Dover</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=joshdover" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://phelm.co.uk"><img src="https://avatars.githubusercontent.com/u/4057948?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Phil Helm</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=phelma" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/lingyv-li"><img src="https://avatars.githubusercontent.com/u/8937944?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Larry Li</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=lingyv-li" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
93
docs/proposals/wikilinks-in-foam.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Wikilinks in Foam
|
||||
|
||||
Foam supports standard wikilinks in the format `[[wikilink]]`.
|
||||
|
||||
Wikilinks can refer to any note or attachment in the repo: `[[note.md]]`, `[[doc.pdf]]`, `[[image.jpg]]`.
|
||||
|
||||
The usual wikilink syntax without extension refers to notes: `[[wikilink]]` and `[[wikilink.md]]` are equivalent.
|
||||
|
||||
The goal of wikilinks is to uniquely identify a file in a repo, no matter in which directory it lives.
|
||||
|
||||
Sometimes in a repo you can have files with the same name in different directories.
|
||||
Foam allows you to identify those files using the minimum effort needed to disambiguate them.
|
||||
|
||||
This is achieved by adding as many directories above the file needed to uniquely identify the link, e.g. `[[house/todo]]`.
|
||||
|
||||
See below for more details.
|
||||
|
||||
## Goals for wikilinks in Foam
|
||||
|
||||
Wikilinks in Foam are meant to satisfy the following:
|
||||
- make it easy for users to identify a resource
|
||||
- make it interoperable with other similar note taking systems (Obsidian, Dendron, ...)
|
||||
- be easy to get started with, but satisfy growing needs
|
||||
|
||||
## Types of wikilinks supported in Foam
|
||||
|
||||
Foam supports two types of keys inside a wikilink: a **path** reference and an **identifier** reference:
|
||||
|
||||
- `[[./file]]` and `[[../to/another/file]]` are **path** links to a resource, relative _from the source_
|
||||
- `[[/path/to/file]]` is a **path** link to a resource, relative _from the repo root_
|
||||
- `[[file]]` is an **identifier** of a resource (based on the filename)
|
||||
- `[[path/to/file]]` is an **identifier** of a resource (based on the path), the same is true for `[[to/file]]`
|
||||
|
||||
It's important to note that sometimes identifier keys can't uniquely locale a resource.
|
||||
|
||||
A more concrete example will help:
|
||||
|
||||
```
|
||||
/
|
||||
projects/
|
||||
house/
|
||||
todo.md
|
||||
buy-car/
|
||||
todo.md
|
||||
cars.md
|
||||
work/
|
||||
todo.md
|
||||
notes.md
|
||||
```
|
||||
|
||||
In the above repo:
|
||||
|
||||
- `[[cars]]` is a unique identifier of a resource - it can be used anywhere in the repo
|
||||
- `[[todo]]` is an non-unique identifier as it can refer to multiple resources
|
||||
- `[[house/todo]]` is a unique identifier of a resource - it can be used anywhere in the repo
|
||||
- `[[projects/house/todo]]` is a unique identifier of a resource - it can be used anywhere in the repo
|
||||
- `[[/projects/house/todo]]` is a path reference to a resource
|
||||
- `[[./todo]]` is a path reference to a resource (e.g. from `/projects/buy-car/cars.md`)
|
||||
|
||||
Basically we could say as a rule:
|
||||
|
||||
- if the link starts with `/` or `.` we consider it a **path** reference, in the first case from the repo root, otherwise from the source note
|
||||
- if a link doesn't start with `/` or `.` it is an **identifier**
|
||||
- generally speaking we use the shortest identifier available to identify a resource, **but all are valid**
|
||||
- `[[projects/buy-car/cars]]`, `[[buy-car/cars]]`, `[[cars]]` are all unique identifier to the same resource, and are all valid in a document
|
||||
- the same can be said for `[[projects/house/todo]]` and `[[house/todo]]` - but not for `[[todo]]`, because it can refer to more than one resource
|
||||
|
||||
## Compatibility with other apps
|
||||
|
||||
| Scenario | Obsidian | Foam |
|
||||
| --------------------------- | ------------------------------- | ------------------------------- |
|
||||
| 1 `[[notes]]` | ✔ unique identifier in repo | ✔ unique identifier in repo |
|
||||
| 2 `[[/work/notes]]` | ✔ valid path from repo root | ✔ valid path from repo root |
|
||||
| 3 `[[work/notes]]` | ✔ valid path from repo root | ✔ valid identifier in repo |
|
||||
| 4 `[[project/house/todo]]` | ✔ valid path from repo root | ✔ valid unique identifier |
|
||||
| 5 `[[/project/house/todo]]` | ✔ valid path from repo root | ✔ valid path from repo root |
|
||||
| 6 `[[house/todo]]` | ✔ valid unique identifier | ✔ valid unique identifier |
|
||||
| 7 `[[todo]]` | ✘ ambiguous identifier | ✘ ambiguous identifier |
|
||||
| 8 `[[/house/todo]]` | ✘ incorrect path from repo root | ✘ incorrect path from repo root |
|
||||
|
||||
## Non-unique identifiers
|
||||
|
||||
We can't prevent non-unique identifiers from occurring in Foam (first and foremost because a file could be edited with another editor) but we can flag them.
|
||||
|
||||
Therefore Foam follows the following strategy instead:
|
||||
|
||||
1. there is a clear resolution mechanism (alphabetic) so that if nothing changes a non-unique identifier will always return the same note. Resolution has to be deterministic
|
||||
2. a diagnostic entry (warning or error) is showed to the user for non-unique identifiers, so she knows that she's using a "risky" identifier
|
||||
1. The quick resolution for this item will show the available unique identifiers matching the non-unique one
|
||||
|
||||
## Thanks
|
||||
|
||||
Thanks to [@memplex](https://github.com/memeplex) for helping with the thinking around this proposal.
|
||||
@@ -1,10 +1,10 @@
|
||||
# GitHub Pages
|
||||
|
||||
- In VSCode workspace settings set `"foam.edit.linkReferenceDefinitions": "withoutExtensions"`
|
||||
- Execute the “Foam: Run Janitor” command from the command palette.
|
||||
- [Turn **GitHub Pages** on in your repository settings](https://guides.github.com/features/pages/).
|
||||
- The default GitHub Pages template is called [Primer](https://github.com/pages-themes/primer). See Primer docs for how to customise html layouts and templates.
|
||||
- GitHub Pages is built on [Jekyll](https://jekyllrb.com/), so it supports things like permalinks, front matter metadata etc.
|
||||
1. In VSCode workspace settings set `"foam.edit.linkReferenceDefinitions": "withoutExtensions"`
|
||||
2. Execute the “Foam: Run Janitor” command from the command palette.
|
||||
3. [Turn **GitHub Pages** on in your repository settings](https://guides.github.com/features/pages/).
|
||||
- The default GitHub Pages template is called [Primer](https://github.com/pages-themes/primer). See Primer docs for how to customise html layouts and templates.
|
||||
- GitHub Pages is built on [Jekyll](https://jekyllrb.com/), so it supports things like permalinks, front matter metadata etc.
|
||||
|
||||
## How to publish locally
|
||||
|
||||
|
||||
@@ -2,13 +2,175 @@
|
||||
|
||||
You don't have to use GitHub to serve Foam pages. You can also use GitLab.
|
||||
|
||||
Gitlab pages can be kept private for private repo, so that your notes are still private.
|
||||
|
||||
## Setup a project
|
||||
|
||||
### Generate the directory from GitHub
|
||||
|
||||
Generate a solution using the [Foam template].
|
||||
Generate a solution using the [Foam template](https://github.com/foambubble/foam-template).
|
||||
|
||||
Change the remote to GitLab, or copy all the files into a new GitLab repo.
|
||||
Change the remote to GitLab, or copy all the files into a new GitLab repo
|
||||
|
||||
## Publishing pages with Gatsby
|
||||
|
||||
### Setup the Gatsby config
|
||||
|
||||
Add a .gatsby-config.js file where:
|
||||
|
||||
* `$REPO_NAME` correspond to the name of your gtlab repo.
|
||||
* `$USER_NAME` correspond to your gitlab username.
|
||||
|
||||
```js
|
||||
const path = require("path");
|
||||
const pathPrefix = `/$REPO_NAME`;
|
||||
|
||||
// Change me
|
||||
const siteMetadata = {
|
||||
title: "A title",
|
||||
shortName: "A short name",
|
||||
description: "",
|
||||
imageUrl: "/graph-visualisation.jpg",
|
||||
siteUrl: "https://$USER_NAME.gitlab.io",
|
||||
};
|
||||
module.exports = {
|
||||
siteMetadata,
|
||||
pathPrefix,
|
||||
flags: {
|
||||
DEV_SSR: true,
|
||||
},
|
||||
plugins: [
|
||||
`gatsby-plugin-sharp`,
|
||||
{
|
||||
resolve: "gatsby-theme-primer-wiki",
|
||||
options: {
|
||||
defaultColorMode: "night",
|
||||
icon: "./path_to/logo.png",
|
||||
sidebarComponents: ["tag", "category"],
|
||||
nav: [
|
||||
{
|
||||
title: "Github",
|
||||
url: "https://github.com/$USER_NAME/",
|
||||
},
|
||||
{
|
||||
title: "Gitlab",
|
||||
url: "https://gitlab.com/$USER_NAME/",
|
||||
},
|
||||
],
|
||||
editUrl:
|
||||
"https://gitlab.com/$USER_NAME/$REPO_NAME/tree/main/",
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: "gatsby-source-filesystem",
|
||||
options: {
|
||||
name: "content",
|
||||
path: `${__dirname}`,
|
||||
ignore: [`**/\.*/**/*`],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
resolve: "gatsby-plugin-manifest",
|
||||
options: {
|
||||
name: siteMetadata.title,
|
||||
short_name: siteMetadata.shortName,
|
||||
start_url: pathPrefix,
|
||||
background_color: `#f7f0eb`,
|
||||
display: `standalone`,
|
||||
icon: path.resolve(__dirname, "./path_to/logo.png"),
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-plugin-sitemap`,
|
||||
},
|
||||
{
|
||||
resolve: "gatsby-plugin-robots-txt",
|
||||
options: {
|
||||
host: siteMetadata.siteUrl,
|
||||
sitemap: `${siteMetadata.siteUrl}/sitemap/sitemap-index.xml`,
|
||||
policy: [{ userAgent: "*", allow: "/" }],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
And a `package.json` file containing:
|
||||
|
||||
```json
|
||||
{
|
||||
"private": true,
|
||||
"name": "wiki",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"develop": "gatsby develop -H 0.0.0.0",
|
||||
"start": "gatsby develop -H 0.0.0.0",
|
||||
"build": "gatsby build",
|
||||
"clean": "gatsby clean",
|
||||
"serve": "gatsby serve",
|
||||
"test": "echo test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@primer/react": "^34.1.0",
|
||||
"@primer/css": "^17.5.0",
|
||||
"foam-cli": "^0.11.0",
|
||||
"gatsby": "^3.12.0",
|
||||
"gatsby-plugin-manifest": "^3.12.0",
|
||||
"gatsby-plugin-robots-txt": "^1.6.9",
|
||||
"gatsby-plugin-sitemap": "^5.4.0",
|
||||
"gatsby-source-filesystem": "^3.12.0",
|
||||
"gatsby-theme-primer-wiki": "^1.14.5",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The theme will be based on [gatsby-theme-primer-wiki](https://github.com/theowenyoung/gatsby-theme-primer-wiki).
|
||||
|
||||
To test the theme locally first run `yarn install` and then use `gatsby develop` to serve the website.
|
||||
See gatsby documentation for more details.
|
||||
|
||||
### Set-up the CI for deployment
|
||||
|
||||
Create a `.gitlab-ci.yml` file containing:
|
||||
|
||||
```yml
|
||||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||
# This specific template is located at:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml
|
||||
|
||||
image: node:latest
|
||||
|
||||
stages:
|
||||
- deploy
|
||||
|
||||
pages:
|
||||
stage: deploy
|
||||
# This folder is cached between builds
|
||||
# https://docs.gitlab.com/ee/ci/yaml/index.html#cache
|
||||
cache:
|
||||
paths:
|
||||
- node_modules/
|
||||
# Enables git-lab CI caching. Both .cache and public must be cached, otherwise builds will fail.
|
||||
- .cache/
|
||||
- public/
|
||||
script:
|
||||
- yarn install
|
||||
- ./node_modules/.bin/gatsby build --prefix-paths
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
```
|
||||
|
||||
This pipeline will now serve your website on every push to the main branch of your project.
|
||||
|
||||
## Publish with Jekyll
|
||||
|
||||
### Add a _config.yaml
|
||||
|
||||
@@ -47,14 +209,14 @@ gem "jekyll-optional-front-matter"
|
||||
|
||||
Commit the file and push it to gitlab.
|
||||
|
||||
## Setup CI/CD
|
||||
### Setup CI/CD
|
||||
|
||||
1. From the project home in GitLab click `Set up CI/CD`
|
||||
2. Choose `Jekyll` as your template from the template dropdown
|
||||
3. Click `commit`
|
||||
4. Now when you go to CI / CD > Pipelines, you should see the code running
|
||||
|
||||
## Troubleshooting
|
||||
### Troubleshooting
|
||||
|
||||
- *Could not locate Gemfile* - You didn't follow the steps above to [#Add a Gemlock file]
|
||||
- *Conversion error: Jekyll::Converters::Scss encountered an error while converting* You need to reference a theme.
|
||||
|
||||
@@ -11,8 +11,6 @@ We have two alternative #recipe for displaying diagrams in markdown:
|
||||
|
||||
You can use [Mermaid](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid) plugin to draw and preview diagrams in your content.
|
||||
|
||||
⚠️ Be aware that Mermaid diagrams don't automatically get rendered in published Foams in [[publish-to-github-pages]], and would require you to eject to another static site generation approach that supports Mermaid plugins.
|
||||
|
||||
## Draw.io
|
||||
|
||||
[Draw.io](https://marketplace.visualstudio.com/items?itemName=hediet.vscode-drawio) extension allows you to create, edit, and display your diagrams without leaving Visual Studio Code. The `.drawio.svg` or `.drawio.png` files can be automatically embedded and displayed in published Foams, no export needed. FYI, the diagram below was made using Draw.io! You can check the diagram [here](../assets/images/diagram-drawio-demo.drawio.svg).
|
||||
|
||||
@@ -62,7 +62,7 @@ A #recipe is a guide, tip or strategy for getting the most out of your Foam work
|
||||
|
||||
- Quick commits with VS Code's built in [[git-integration]]
|
||||
- Store your workspace in an auto-synced GitHub repo with [[write-your-notes-in-github-gist]]
|
||||
- Sync your GitHub repo automatically [[todo]].
|
||||
- Sync your GitHub repo automatically using the [GitDoc VSCode Plugin](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.gitdoc).
|
||||
|
||||
## Publish
|
||||
|
||||
|
||||
@@ -9,3 +9,6 @@ There are a couple of options when it comes to clipping web pages:
|
||||
|
||||
- [Markdown Clipper](https://github.com/deathau/markdownload)
|
||||
- A Firefox and Google Chrome extension to clip websites and download them into a readable markdown file.
|
||||
|
||||
- [Web Clipper](https://clipper.website/)
|
||||
- A Firefox, Chrome and Edge extension to clip websites and save them directly to the GitHub repository into a readable markdown file.
|
||||
|
||||
@@ -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,14 +3,16 @@
|
||||
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.
|
||||
- The note creation makes use of the special [`new-note.md` note template](features/note-templates)
|
||||
|
||||
> If the `F12` shortcut feels unnatural you can rebind it at File > Preferences > Keyboard Shortcuts by searching for `editor.action.revealDefinition`.
|
||||
|
||||
## Support for sections
|
||||
|
||||
Foam supports autocompletion, navigation, embedding and diagnostics for note sections. Just use the standard wiki syntax of `[[resource#Section Title]]`.
|
||||
|
||||
## Markdown compatibility
|
||||
|
||||
The [Foam for VSCode](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode) extension automatically generates [[link-reference-definitions]] at the bottom of the file to make wikilinks compatible with Markdown tools and parsers.
|
||||
@@ -19,7 +21,6 @@ The [Foam for VSCode](https://marketplace.visualstudio.com/items?itemName=foam.f
|
||||
|
||||
- [[foam-file-format]]
|
||||
- [[note-templates]]
|
||||
- [[link-formatting-and-autocompletion]]
|
||||
- See [[link-reference-definition-improvements]] for further discussion on current problems and potential solutions.
|
||||
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
],
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.15.0"
|
||||
"version": "0.19.0"
|
||||
}
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"start:vscode": "yarn workspace foam-vscode vscode:start-debugging",
|
||||
"build:core": "yarn workspace foam-core build",
|
||||
"watch:core": "yarn workspace foam-core start",
|
||||
"test:core": "yarn workspace foam-core test",
|
||||
"vscode:package-extension": "yarn workspace foam-vscode package-extension",
|
||||
"vscode:install-extension": "yarn workspace foam-vscode install-extension",
|
||||
"vscode:publish-extension": "yarn workspace foam-vscode publish-extension",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: [['@babel/plugin-transform-runtime', { helpers: false }]],
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
# Foam Core
|
||||
|
||||
This module contains the core functions, model, and API of Foam.
|
||||
It is used by its clients to integrate Foam in various use cases, from VsCode extension, to CLI, to CI integrations.
|
||||
|
||||
## Local Development
|
||||
|
||||
Below is a list of commands you will probably find useful.
|
||||
|
||||
### `yarn watch`
|
||||
|
||||
Runs the project in development/watch mode. Your project will be rebuilt upon changes.
|
||||
|
||||
### `yarn build`
|
||||
|
||||
Bundles the package to the `dist` folder. The package is optimized and bundled with Rollup into multiple formats (CommonJS, UMD, and ES Module).
|
||||
|
||||
### `yarn test`
|
||||
|
||||
Runs the test watcher (Jest) in an interactive mode.
|
||||
By default, runs tests related to files changed since the last commit.
|
||||
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"name": "foam-core",
|
||||
"repository": "https://github.com/foambubble/foam",
|
||||
"version": "0.15.0",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"build": "tsdx build --tsconfig ./tsconfig.build.json",
|
||||
"test": "tsdx test",
|
||||
"lint": "tsdx lint src test",
|
||||
"watch": "tsdx watch",
|
||||
"prepare": "tsdx build --tsconfig ./tsconfig.build.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.4",
|
||||
"@babel/plugin-transform-runtime": "^7.10.4",
|
||||
"@babel/preset-env": "^7.10.4",
|
||||
"@babel/preset-typescript": "^7.10.4",
|
||||
"@types/github-slugger": "^1.3.0",
|
||||
"@types/lodash": "^4.14.157",
|
||||
"@types/micromatch": "^4.0.1",
|
||||
"@types/picomatch": "^2.2.1",
|
||||
"husky": "^4.2.5",
|
||||
"tsdx": "^0.13.2",
|
||||
"tslib": "^2.0.0",
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"detect-newline": "^3.1.0",
|
||||
"fast-array-diff": "^1.0.1",
|
||||
"github-slugger": "^1.3.0",
|
||||
"glob": "^7.1.6",
|
||||
"lodash": "^4.17.21",
|
||||
"micromatch": "^4.0.2",
|
||||
"remark-frontmatter": "^2.0.0",
|
||||
"remark-parse": "^8.0.2",
|
||||
"remark-wiki-link": "^0.0.4",
|
||||
"replace-ext": "^2.0.0",
|
||||
"title-case": "^3.0.2",
|
||||
"unified": "^9.0.0",
|
||||
"unist-util-visit": "^2.0.2",
|
||||
"yaml": "^1.10.0"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts"
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { merge } from 'lodash';
|
||||
import { Logger } from './utils/log';
|
||||
import { URI } from './model/uri';
|
||||
|
||||
export interface FoamConfig {
|
||||
workspaceFolders: URI[];
|
||||
includeGlobs: string[];
|
||||
ignoreGlobs: string[];
|
||||
get<T>(path: string): T | undefined;
|
||||
get<T>(path: string, defaultValue: T): T;
|
||||
}
|
||||
|
||||
const DEFAULT_INCLUDES = ['**/*'];
|
||||
|
||||
const DEFAULT_IGNORES = ['**/node_modules/**'];
|
||||
|
||||
export const createConfigFromObject = (
|
||||
workspaceFolders: URI[],
|
||||
include: string[],
|
||||
ignore: string[],
|
||||
settings: any
|
||||
) => {
|
||||
const config: FoamConfig = {
|
||||
workspaceFolders: workspaceFolders,
|
||||
includeGlobs: include,
|
||||
ignoreGlobs: ignore,
|
||||
get: <T>(path: string, defaultValue?: T) => {
|
||||
const tokens = path.split('.');
|
||||
const value = tokens.reduce((acc, t) => acc?.[t], settings);
|
||||
return value ?? defaultValue;
|
||||
},
|
||||
};
|
||||
return config;
|
||||
};
|
||||
|
||||
export const createConfigFromFolders = (
|
||||
workspaceFolders: URI[] | URI,
|
||||
options: {
|
||||
include?: string[];
|
||||
ignore?: string[];
|
||||
} = {}
|
||||
): FoamConfig => {
|
||||
if (!Array.isArray(workspaceFolders)) {
|
||||
workspaceFolders = [workspaceFolders];
|
||||
}
|
||||
const workspaceConfig: any = workspaceFolders.reduce(
|
||||
(acc, f) => merge(acc, parseConfig(URI.joinPath(f, 'config.json'))),
|
||||
{}
|
||||
);
|
||||
// For security reasons local plugins can only be
|
||||
// activated via user config
|
||||
if ('experimental' in workspaceConfig) {
|
||||
delete workspaceConfig['experimental']['localPlugins'];
|
||||
}
|
||||
|
||||
const userConfig = parseConfig(URI.file(`~/.foam/config.json`));
|
||||
|
||||
const settings = merge(workspaceConfig, userConfig);
|
||||
|
||||
return createConfigFromObject(
|
||||
workspaceFolders,
|
||||
options.include ?? DEFAULT_INCLUDES,
|
||||
options.ignore ?? DEFAULT_IGNORES,
|
||||
settings
|
||||
);
|
||||
};
|
||||
|
||||
const parseConfig = (path: URI) => {
|
||||
try {
|
||||
return JSON.parse(readFileSync(URI.toFsPath(path), 'utf8'));
|
||||
} catch {
|
||||
Logger.debug('Could not read configuration from ' + URI.toString(path));
|
||||
}
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
import {
|
||||
Resource,
|
||||
ResourceLink,
|
||||
NoteLinkDefinition,
|
||||
ResourceParser,
|
||||
Tag,
|
||||
} from './model/note';
|
||||
import { FoamConfig } from './config';
|
||||
import {
|
||||
IDataStore,
|
||||
FileDataStore,
|
||||
Matcher,
|
||||
IMatcher,
|
||||
} from './services/datastore';
|
||||
import { ILogger } from './utils/log';
|
||||
import { IDisposable, isDisposable } from './common/lifecycle';
|
||||
import { FoamWorkspace } from './model/workspace';
|
||||
import { FoamGraph } from '../src/model/graph';
|
||||
import { URI } from './model/uri';
|
||||
import { FoamTags } from '../src/model/tags';
|
||||
|
||||
export { Position } from './model/position';
|
||||
export { Range } from './model/range';
|
||||
export { IDataStore, FileDataStore, Matcher, IMatcher };
|
||||
export { ILogger };
|
||||
export { LogLevel, LogLevelThreshold, Logger, BaseLogger } from './utils/log';
|
||||
export { Event, Emitter } from './common/event';
|
||||
export { FoamConfig };
|
||||
export { ResourceProvider } from './model/provider';
|
||||
export { IDisposable, isDisposable };
|
||||
|
||||
export {
|
||||
createMarkdownReferences,
|
||||
stringifyMarkdownLinkReferenceDefinition,
|
||||
createMarkdownParser,
|
||||
MarkdownResourceProvider,
|
||||
} from './markdown-provider';
|
||||
|
||||
export {
|
||||
TextEdit,
|
||||
generateHeading,
|
||||
generateLinkReferences,
|
||||
getKebabCaseFileName,
|
||||
LINK_REFERENCE_DEFINITION_HEADER,
|
||||
LINK_REFERENCE_DEFINITION_FOOTER,
|
||||
} from './janitor';
|
||||
|
||||
export { applyTextEdit } from './janitor/apply-text-edit';
|
||||
|
||||
export { createConfigFromFolders } from './config';
|
||||
|
||||
export { Foam, Services, bootstrap } from './model/foam';
|
||||
|
||||
export {
|
||||
Resource,
|
||||
ResourceLink,
|
||||
URI,
|
||||
Tag,
|
||||
FoamWorkspace,
|
||||
FoamGraph,
|
||||
FoamTags,
|
||||
NoteLinkDefinition,
|
||||
ResourceParser,
|
||||
};
|
||||
@@ -1,524 +0,0 @@
|
||||
import { Node, Position as AstPosition } from 'unist';
|
||||
import unified from 'unified';
|
||||
import markdownParse from 'remark-parse';
|
||||
import wikiLinkPlugin from 'remark-wiki-link';
|
||||
import frontmatterPlugin from 'remark-frontmatter';
|
||||
import { parse as parseYAML } from 'yaml';
|
||||
import visit from 'unist-util-visit';
|
||||
import { Parent, Point } from 'unist';
|
||||
import detectNewline from 'detect-newline';
|
||||
import os from 'os';
|
||||
import {
|
||||
NoteLinkDefinition,
|
||||
Resource,
|
||||
ResourceLink,
|
||||
WikiLink,
|
||||
ResourceParser,
|
||||
} from './model/note';
|
||||
import { Position } from './model/position';
|
||||
import { Range } from './model/range';
|
||||
import {
|
||||
dropExtension,
|
||||
extractHashtags,
|
||||
extractTagsFromProp,
|
||||
isNone,
|
||||
isSome,
|
||||
} from './utils';
|
||||
import { Logger } from './utils/log';
|
||||
import { URI } from './model/uri';
|
||||
import { FoamWorkspace } from './model/workspace';
|
||||
import { ResourceProvider } from 'model/provider';
|
||||
import { IDataStore, FileDataStore, IMatcher } from './services/datastore';
|
||||
import { IDisposable } from 'common/lifecycle';
|
||||
|
||||
const ALIAS_DIVIDER_CHAR = '|';
|
||||
|
||||
export interface ParserPlugin {
|
||||
name?: string;
|
||||
visit?: (node: Node, note: Resource, noteSource: string) => void;
|
||||
onDidInitializeParser?: (parser: unified.Processor) => void;
|
||||
onWillParseMarkdown?: (markdown: string) => string;
|
||||
onWillVisitTree?: (tree: Node, note: Resource) => void;
|
||||
onDidVisitTree?: (tree: Node, note: Resource) => void;
|
||||
onDidFindProperties?: (properties: any, note: Resource, node: Node) => void;
|
||||
}
|
||||
|
||||
export class MarkdownResourceProvider implements ResourceProvider {
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly matcher: IMatcher,
|
||||
private readonly watcherInit?: (triggers: {
|
||||
onDidChange: (uri: URI) => void;
|
||||
onDidCreate: (uri: URI) => void;
|
||||
onDidDelete: (uri: URI) => void;
|
||||
}) => IDisposable[],
|
||||
private readonly parser: ResourceParser = createMarkdownParser([]),
|
||||
private readonly dataStore: IDataStore = new FileDataStore()
|
||||
) {}
|
||||
|
||||
async init(workspace: FoamWorkspace) {
|
||||
const filesByFolder = await Promise.all(
|
||||
this.matcher.include.map(glob =>
|
||||
this.dataStore.list(glob, this.matcher.exclude)
|
||||
)
|
||||
);
|
||||
const files = this.matcher
|
||||
.match(filesByFolder.flat())
|
||||
.filter(this.supports);
|
||||
|
||||
await Promise.all(
|
||||
files.map(async uri => {
|
||||
Logger.info('Found: ' + URI.toString(uri));
|
||||
const content = await this.dataStore.read(uri);
|
||||
if (isSome(content)) {
|
||||
workspace.set(this.parser.parse(uri, content));
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.disposables =
|
||||
this.watcherInit?.({
|
||||
onDidChange: async uri => {
|
||||
if (this.matcher.isMatch(uri) && this.supports(uri)) {
|
||||
const content = await this.dataStore.read(uri);
|
||||
isSome(content) &&
|
||||
workspace.set(await this.parser.parse(uri, content));
|
||||
}
|
||||
},
|
||||
onDidCreate: async uri => {
|
||||
if (this.matcher.isMatch(uri) && this.supports(uri)) {
|
||||
const content = await this.dataStore.read(uri);
|
||||
isSome(content) &&
|
||||
workspace.set(await this.parser.parse(uri, content));
|
||||
}
|
||||
},
|
||||
onDidDelete: uri => {
|
||||
this.supports(uri) && workspace.delete(uri);
|
||||
},
|
||||
}) ?? [];
|
||||
}
|
||||
|
||||
supports(uri: URI) {
|
||||
return URI.isMarkdownFile(uri);
|
||||
}
|
||||
|
||||
read(uri: URI): Promise<string | null> {
|
||||
return this.dataStore.read(uri);
|
||||
}
|
||||
|
||||
readAsMarkdown(uri: URI): Promise<string | null> {
|
||||
return this.dataStore.read(uri);
|
||||
}
|
||||
|
||||
async fetch(uri: URI) {
|
||||
const content = await this.read(uri);
|
||||
return isSome(content) ? this.parser.parse(uri, content) : null;
|
||||
}
|
||||
|
||||
resolveLink(
|
||||
workspace: FoamWorkspace,
|
||||
resource: Resource,
|
||||
link: ResourceLink
|
||||
) {
|
||||
let targetUri: URI | undefined;
|
||||
switch (link.type) {
|
||||
case 'wikilink':
|
||||
const definitionUri = resource.definitions.find(
|
||||
def => def.label === link.target
|
||||
)?.url;
|
||||
if (isSome(definitionUri)) {
|
||||
const definedUri = URI.resolve(definitionUri, resource.uri);
|
||||
targetUri =
|
||||
workspace.find(definedUri, resource.uri)?.uri ??
|
||||
URI.placeholder(definedUri.path);
|
||||
} else {
|
||||
targetUri =
|
||||
workspace.find(link.target, resource.uri)?.uri ??
|
||||
URI.placeholder(link.target);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'link':
|
||||
targetUri =
|
||||
workspace.find(link.target, resource.uri)?.uri ??
|
||||
URI.placeholder(URI.resolve(link.target, resource.uri).path);
|
||||
break;
|
||||
}
|
||||
return targetUri;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.disposables.forEach(d => d.dispose());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses all the children of the given node, extracts
|
||||
* the text from them, and returns it concatenated.
|
||||
*
|
||||
* @param root the node from which to start collecting text
|
||||
*/
|
||||
const getTextFromChildren = (root: Node): string => {
|
||||
let text = '';
|
||||
visit(root, 'text', node => {
|
||||
if (node.type === 'text') {
|
||||
text = text + node.value;
|
||||
}
|
||||
});
|
||||
return text;
|
||||
};
|
||||
|
||||
const tagsPlugin: ParserPlugin = {
|
||||
name: 'tags',
|
||||
onDidFindProperties: (props, note, node) => {
|
||||
if (isSome(props.tags)) {
|
||||
const yamlTags = extractTagsFromProp(props.tags);
|
||||
yamlTags.forEach(t => {
|
||||
note.tags.push({
|
||||
label: t,
|
||||
range: astPositionToFoamRange(node.position!),
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
visit: (node, note) => {
|
||||
if (node.type === 'text') {
|
||||
const tags = extractHashtags((node as any).value);
|
||||
tags.forEach(tag => {
|
||||
let start = astPointToFoamPosition(node.position!.start);
|
||||
start.character = start.character + tag.offset;
|
||||
const end: Position = {
|
||||
line: start.line,
|
||||
character: start.character + tag.label.length + 1,
|
||||
};
|
||||
note.tags.push({
|
||||
label: tag.label,
|
||||
range: Range.createFromPosition(start, end),
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const titlePlugin: ParserPlugin = {
|
||||
name: 'title',
|
||||
visit: (node, note) => {
|
||||
if (note.title === '' && node.type === 'heading' && node.depth === 1) {
|
||||
note.title =
|
||||
((node as Parent)!.children?.[0]?.value as string) || note.title;
|
||||
}
|
||||
},
|
||||
onDidFindProperties: (props, note) => {
|
||||
// Give precendence to the title from the frontmatter if it exists
|
||||
note.title = props.title?.toString() ?? note.title;
|
||||
},
|
||||
onDidVisitTree: (tree, note) => {
|
||||
if (note.title === '') {
|
||||
note.title = URI.getBasename(note.uri);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const wikilinkPlugin: ParserPlugin = {
|
||||
name: 'wikilink',
|
||||
visit: (node, note, noteSource) => {
|
||||
if (node.type === 'wikiLink') {
|
||||
const text = node.value as string;
|
||||
const alias = node.data?.alias as string;
|
||||
const literalContent = noteSource.substring(
|
||||
node.position!.start.offset!,
|
||||
node.position!.end.offset!
|
||||
);
|
||||
|
||||
const hasAlias =
|
||||
literalContent !== text && literalContent.includes(ALIAS_DIVIDER_CHAR);
|
||||
note.links.push({
|
||||
type: 'wikilink',
|
||||
rawText: literalContent,
|
||||
label: hasAlias
|
||||
? alias.trim()
|
||||
: literalContent.substring(2, literalContent.length - 2),
|
||||
target: hasAlias
|
||||
? literalContent
|
||||
.substring(2, literalContent.indexOf(ALIAS_DIVIDER_CHAR))
|
||||
.replace(/\\/g, '')
|
||||
.trim()
|
||||
: text.trim(),
|
||||
range: astPositionToFoamRange(node.position!),
|
||||
});
|
||||
}
|
||||
if (node.type === 'link') {
|
||||
const targetUri = (node as any).url;
|
||||
const uri = URI.resolve(targetUri, note.uri);
|
||||
if (uri.scheme !== 'file' || uri.path === note.uri.path) {
|
||||
return;
|
||||
}
|
||||
const label = getTextFromChildren(node);
|
||||
note.links.push({
|
||||
type: 'link',
|
||||
target: targetUri,
|
||||
label: label,
|
||||
range: astPositionToFoamRange(node.position!),
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const definitionsPlugin: ParserPlugin = {
|
||||
name: 'definitions',
|
||||
visit: (node, note) => {
|
||||
if (node.type === 'definition') {
|
||||
note.definitions.push({
|
||||
label: node.label as string,
|
||||
url: node.url as string,
|
||||
title: node.title as string,
|
||||
range: astPositionToFoamRange(node.position!),
|
||||
});
|
||||
}
|
||||
},
|
||||
onDidVisitTree: (tree, note) => {
|
||||
note.definitions = getFoamDefinitions(note.definitions, note.source.end);
|
||||
},
|
||||
};
|
||||
|
||||
const handleError = (
|
||||
plugin: ParserPlugin,
|
||||
fnName: string,
|
||||
uri: URI | undefined,
|
||||
e: Error
|
||||
): void => {
|
||||
const name = plugin.name || '';
|
||||
Logger.warn(
|
||||
`Error while executing [${fnName}] in plugin [${name}]. ${
|
||||
uri ? 'for file [' + URI.toString(uri) : ']'
|
||||
}.`,
|
||||
e
|
||||
);
|
||||
};
|
||||
|
||||
export function createMarkdownParser(
|
||||
extraPlugins: ParserPlugin[]
|
||||
): ResourceParser {
|
||||
const parser = unified()
|
||||
.use(markdownParse, { gfm: true })
|
||||
.use(frontmatterPlugin, ['yaml'])
|
||||
.use(wikiLinkPlugin, { aliasDivider: ALIAS_DIVIDER_CHAR });
|
||||
|
||||
const plugins = [
|
||||
titlePlugin,
|
||||
wikilinkPlugin,
|
||||
definitionsPlugin,
|
||||
tagsPlugin,
|
||||
...extraPlugins,
|
||||
];
|
||||
|
||||
plugins.forEach(plugin => {
|
||||
try {
|
||||
plugin.onDidInitializeParser?.(parser);
|
||||
} catch (e) {
|
||||
handleError(plugin, 'onDidInitializeParser', undefined, e);
|
||||
}
|
||||
});
|
||||
|
||||
const foamParser: ResourceParser = {
|
||||
parse: (uri: URI, markdown: string): Resource => {
|
||||
Logger.debug('Parsing:', URI.toString(uri));
|
||||
markdown = plugins.reduce((acc, plugin) => {
|
||||
try {
|
||||
return plugin.onWillParseMarkdown?.(acc) || acc;
|
||||
} catch (e) {
|
||||
handleError(plugin, 'onWillParseMarkdown', uri, e);
|
||||
return acc;
|
||||
}
|
||||
}, markdown);
|
||||
const tree = parser.parse(markdown);
|
||||
const eol = detectNewline(markdown) || os.EOL;
|
||||
|
||||
var note: Resource = {
|
||||
uri: uri,
|
||||
type: 'note',
|
||||
properties: {},
|
||||
title: '',
|
||||
tags: [],
|
||||
links: [],
|
||||
definitions: [],
|
||||
source: {
|
||||
text: markdown,
|
||||
contentStart: astPointToFoamPosition(tree.position!.start),
|
||||
end: astPointToFoamPosition(tree.position!.end),
|
||||
eol: eol,
|
||||
},
|
||||
};
|
||||
|
||||
plugins.forEach(plugin => {
|
||||
try {
|
||||
plugin.onWillVisitTree?.(tree, note);
|
||||
} catch (e) {
|
||||
handleError(plugin, 'onWillVisitTree', uri, e);
|
||||
}
|
||||
});
|
||||
visit(tree, node => {
|
||||
if (node.type === 'yaml') {
|
||||
try {
|
||||
const yamlProperties = parseYAML(node.value as string) ?? {};
|
||||
note.properties = {
|
||||
...note.properties,
|
||||
...yamlProperties,
|
||||
};
|
||||
// Update the start position of the note by exluding the metadata
|
||||
note.source.contentStart = Position.create(
|
||||
node.position!.end.line! + 2,
|
||||
0
|
||||
);
|
||||
|
||||
for (let i = 0, len = plugins.length; i < len; i++) {
|
||||
try {
|
||||
plugins[i].onDidFindProperties?.(yamlProperties, note, node);
|
||||
} catch (e) {
|
||||
handleError(plugins[i], 'onDidFindProperties', uri, e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.warn(
|
||||
`Error while parsing YAML for [${URI.toString(uri)}]`,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0, len = plugins.length; i < len; i++) {
|
||||
try {
|
||||
plugins[i].visit?.(node, note, markdown);
|
||||
} catch (e) {
|
||||
handleError(plugins[i], 'visit', uri, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
plugins.forEach(plugin => {
|
||||
try {
|
||||
plugin.onDidVisitTree?.(tree, note);
|
||||
} catch (e) {
|
||||
handleError(plugin, 'onDidVisitTree', uri, e);
|
||||
}
|
||||
});
|
||||
Logger.debug('Result:', note);
|
||||
return note;
|
||||
},
|
||||
};
|
||||
return foamParser;
|
||||
}
|
||||
|
||||
function getFoamDefinitions(
|
||||
defs: NoteLinkDefinition[],
|
||||
fileEndPoint: Position
|
||||
): NoteLinkDefinition[] {
|
||||
let previousLine = fileEndPoint.line;
|
||||
let foamDefinitions = [];
|
||||
|
||||
// walk through each definition in reverse order
|
||||
// (last one first)
|
||||
for (const def of defs.reverse()) {
|
||||
// if this definition is more than 2 lines above the
|
||||
// previous one below it (or file end), that means we
|
||||
// have exited the trailing definition block, and should bail
|
||||
const start = def.range!.start.line;
|
||||
if (start < previousLine - 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
foamDefinitions.unshift(def);
|
||||
previousLine = def.range!.end.line;
|
||||
}
|
||||
|
||||
return foamDefinitions;
|
||||
}
|
||||
|
||||
export function stringifyMarkdownLinkReferenceDefinition(
|
||||
definition: NoteLinkDefinition
|
||||
) {
|
||||
let text = `[${definition.label}]: ${definition.url}`;
|
||||
if (definition.title) {
|
||||
text = `${text} "${definition.title}"`;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
export function createMarkdownReferences(
|
||||
workspace: FoamWorkspace,
|
||||
noteUri: URI,
|
||||
includeExtension: boolean
|
||||
): NoteLinkDefinition[] {
|
||||
const source = workspace.find(noteUri);
|
||||
// Should never occur since we're already in a file,
|
||||
if (source?.type !== 'note') {
|
||||
console.warn(
|
||||
`Note ${URI.toString(
|
||||
noteUri
|
||||
)} note found in workspace when attempting to generate markdown reference list`
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
return source.links
|
||||
.filter(isWikilink)
|
||||
.map(link => {
|
||||
const targetUri = workspace.resolveLink(source, link);
|
||||
const target = workspace.find(targetUri);
|
||||
if (isNone(target)) {
|
||||
Logger.warn(
|
||||
`Link ${URI.toString(targetUri)} in ${URI.toString(
|
||||
noteUri
|
||||
)} is not valid.`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
if (target.type === 'placeholder') {
|
||||
// no need to create definitions for placeholders
|
||||
return null;
|
||||
}
|
||||
|
||||
const relativePath = URI.relativePath(noteUri, target.uri);
|
||||
const pathToNote = includeExtension
|
||||
? relativePath
|
||||
: dropExtension(relativePath);
|
||||
|
||||
// [wikilink-text]: path/to/file.md "Page title"
|
||||
return {
|
||||
label:
|
||||
link.rawText.indexOf('[[') > -1
|
||||
? link.rawText.substring(2, link.rawText.length - 2)
|
||||
: link.rawText || link.label,
|
||||
url: pathToNote,
|
||||
title: target.title,
|
||||
};
|
||||
})
|
||||
.filter(isSome)
|
||||
.sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the 1-index Point object into the VS Code 0-index Position object
|
||||
* @param point ast Point (1-indexed)
|
||||
* @returns Foam Position (0-indexed)
|
||||
*/
|
||||
const astPointToFoamPosition = (point: Point): Position => {
|
||||
return Position.create(point.line - 1, point.column - 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the 1-index Position object into the VS Code 0-index Range object
|
||||
* @param position an ast Position object (1-indexed)
|
||||
* @returns Foam Range (0-indexed)
|
||||
*/
|
||||
const astPositionToFoamRange = (pos: AstPosition): Range =>
|
||||
Range.create(
|
||||
pos.start.line - 1,
|
||||
pos.start.column - 1,
|
||||
pos.end.line - 1,
|
||||
pos.end.column - 1
|
||||
);
|
||||
|
||||
const isWikilink = (link: ResourceLink): link is WikiLink => {
|
||||
return link.type === 'wikilink';
|
||||
};
|
||||
@@ -1,240 +0,0 @@
|
||||
import { diff } from 'fast-array-diff';
|
||||
import { isEqual } from 'lodash';
|
||||
import { Resource, ResourceLink } from './note';
|
||||
import { URI } from './uri';
|
||||
import { IDisposable } from '../index';
|
||||
import { FoamWorkspace, uriToResourceName } from './workspace';
|
||||
import { Range } from './range';
|
||||
|
||||
export type Connection = {
|
||||
source: URI;
|
||||
target: URI;
|
||||
link: ResourceLink;
|
||||
};
|
||||
|
||||
const pathToPlaceholderId = (value: string) => value;
|
||||
const uriToPlaceholderId = (uri: URI) => pathToPlaceholderId(uri.path);
|
||||
|
||||
export class FoamGraph implements IDisposable {
|
||||
/**
|
||||
* Placehoders by key / slug / value
|
||||
*/
|
||||
public readonly placeholders: Map<string, URI> = new Map();
|
||||
/**
|
||||
* Maps the connections starting from a URI
|
||||
*/
|
||||
public readonly links: Map<string, Connection[]> = new Map();
|
||||
/**
|
||||
* Maps the connections arriving to a URI
|
||||
*/
|
||||
public readonly backlinks: Map<string, Connection[]> = new Map();
|
||||
|
||||
/**
|
||||
* List of disposables to destroy with the workspace
|
||||
*/
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(private readonly workspace: FoamWorkspace) {}
|
||||
|
||||
public contains(uri: URI): boolean {
|
||||
return this.getConnections(uri).length > 0;
|
||||
}
|
||||
|
||||
public getAllNodes(): URI[] {
|
||||
return [
|
||||
...Array.from(this.placeholders.values()),
|
||||
...this.workspace.list().map(r => r.uri),
|
||||
];
|
||||
}
|
||||
|
||||
public getAllConnections(): Connection[] {
|
||||
return Array.from(this.links.values()).flat();
|
||||
}
|
||||
|
||||
public getConnections(uri: URI): Connection[] {
|
||||
return [
|
||||
...(this.links.get(uri.path) || []),
|
||||
...(this.backlinks.get(uri.path) || []),
|
||||
];
|
||||
}
|
||||
|
||||
public getLinks(uri: URI): Connection[] {
|
||||
return this.links.get(uri.path) ?? [];
|
||||
}
|
||||
|
||||
public getBacklinks(uri: URI): Connection[] {
|
||||
return this.backlinks.get(uri.path) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes all the links in the workspace, connecting notes and
|
||||
* creating placeholders.
|
||||
*
|
||||
* @param workspace the target workspace
|
||||
* @param keepMonitoring whether to recompute the links when the workspace changes
|
||||
* @returns the FoamGraph
|
||||
*/
|
||||
public static fromWorkspace(
|
||||
workspace: FoamWorkspace,
|
||||
keepMonitoring: boolean = false
|
||||
): FoamGraph {
|
||||
let graph = new FoamGraph(workspace);
|
||||
|
||||
workspace.list().forEach(resource => graph.resolveResource(resource));
|
||||
if (keepMonitoring) {
|
||||
graph.disposables.push(
|
||||
workspace.onDidAdd(resource => {
|
||||
graph.updateLinksRelatedToAddedResource(resource);
|
||||
}),
|
||||
workspace.onDidUpdate(change => {
|
||||
graph.updateLinksForResource(change.old, change.new);
|
||||
}),
|
||||
workspace.onDidDelete(resource => {
|
||||
graph.updateLinksRelatedToDeletedResource(resource);
|
||||
})
|
||||
);
|
||||
}
|
||||
return graph;
|
||||
}
|
||||
|
||||
private updateLinksRelatedToAddedResource(resource: Resource) {
|
||||
// check if any existing connection can be filled by new resource
|
||||
const name = uriToResourceName(resource.uri);
|
||||
const placeholder = this.placeholders.get(name);
|
||||
if (placeholder) {
|
||||
this.placeholders.delete(name);
|
||||
const resourcesToUpdate = this.backlinks.get(placeholder.path) ?? [];
|
||||
resourcesToUpdate.forEach(res =>
|
||||
this.resolveResource(this.workspace.get(res.source))
|
||||
);
|
||||
}
|
||||
|
||||
// resolve the resource
|
||||
this.resolveResource(resource);
|
||||
}
|
||||
|
||||
private updateLinksForResource(oldResource: Resource, newResource: Resource) {
|
||||
if (oldResource.uri.path !== newResource.uri.path) {
|
||||
throw new Error(
|
||||
'Unexpected State: update should only be called on same resource ' +
|
||||
{
|
||||
old: oldResource,
|
||||
new: newResource,
|
||||
}
|
||||
);
|
||||
}
|
||||
if (oldResource.type === 'note' && newResource.type === 'note') {
|
||||
const patch = diff(oldResource.links, newResource.links, isEqual);
|
||||
patch.removed.forEach(link => {
|
||||
const target = this.workspace.resolveLink(oldResource, link);
|
||||
return this.disconnect(oldResource.uri, target, link);
|
||||
}, this);
|
||||
patch.added.forEach(link => {
|
||||
const target = this.workspace.resolveLink(newResource, link);
|
||||
return this.connect(newResource.uri, target, link);
|
||||
}, this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private updateLinksRelatedToDeletedResource(resource: Resource) {
|
||||
const uri = resource.uri;
|
||||
|
||||
// remove forward links from old resource
|
||||
const resourcesPointedByDeletedNote = this.links.get(uri.path) ?? [];
|
||||
this.links.delete(uri.path);
|
||||
resourcesPointedByDeletedNote.forEach(connection =>
|
||||
this.disconnect(uri, connection.target, connection.link)
|
||||
);
|
||||
|
||||
// recompute previous links to old resource
|
||||
const notesPointingToDeletedResource = this.backlinks.get(uri.path) ?? [];
|
||||
this.backlinks.delete(uri.path);
|
||||
notesPointingToDeletedResource.forEach(link =>
|
||||
this.resolveResource(this.workspace.get(link.source))
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
private connect(source: URI, target: URI, link: ResourceLink) {
|
||||
const connection = { source, target, link };
|
||||
|
||||
if (!this.links.has(source.path)) {
|
||||
this.links.set(source.path, []);
|
||||
}
|
||||
this.links.get(source.path)?.push(connection);
|
||||
|
||||
if (!this.backlinks.get(target.path)) {
|
||||
this.backlinks.set(target.path, []);
|
||||
}
|
||||
|
||||
this.backlinks.get(target.path)?.push(connection);
|
||||
|
||||
if (URI.isPlaceholder(target)) {
|
||||
this.placeholders.set(uriToPlaceholderId(target), target);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a connection, or all connections, between the source and
|
||||
* target resources
|
||||
*
|
||||
* @param workspace the Foam workspace
|
||||
* @param source the source resource
|
||||
* @param target the target resource
|
||||
* @param link the link reference, or `true` to remove all links
|
||||
* @returns the updated Foam workspace
|
||||
*/
|
||||
private disconnect(source: URI, target: URI, link: ResourceLink | true) {
|
||||
const connectionsToKeep =
|
||||
link === true
|
||||
? (c: Connection) =>
|
||||
!URI.isEqual(source, c.source) || !URI.isEqual(target, c.target)
|
||||
: (c: Connection) => !isSameConnection({ source, target, link }, c);
|
||||
|
||||
this.links.set(
|
||||
source.path,
|
||||
this.links.get(source.path)?.filter(connectionsToKeep) ?? []
|
||||
);
|
||||
if (this.links.get(source.path)?.length === 0) {
|
||||
this.links.delete(source.path);
|
||||
}
|
||||
this.backlinks.set(
|
||||
target.path,
|
||||
this.backlinks.get(target.path)?.filter(connectionsToKeep) ?? []
|
||||
);
|
||||
if (this.backlinks.get(target.path)?.length === 0) {
|
||||
this.backlinks.delete(target.path);
|
||||
if (URI.isPlaceholder(target)) {
|
||||
this.placeholders.delete(uriToPlaceholderId(target));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public resolveResource(resource: Resource) {
|
||||
this.links.delete(resource.uri.path);
|
||||
// prettier-ignore
|
||||
resource.links.forEach(link => {
|
||||
const targetUri = this.workspace.resolveLink(resource, link);
|
||||
this.connect(resource.uri, targetUri, link);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.disposables.forEach(d => d.dispose());
|
||||
this.disposables = [];
|
||||
}
|
||||
}
|
||||
|
||||
// TODO move these utility fns to appropriate places
|
||||
|
||||
const isSameConnection = (a: Connection, b: Connection) =>
|
||||
URI.isEqual(a.source, b.source) &&
|
||||
URI.isEqual(a.target, b.target) &&
|
||||
isSameLink(a.link, b.link);
|
||||
|
||||
const isSameLink = (a: ResourceLink, b: ResourceLink) =>
|
||||
a.type === b.type && Range.isEqual(a.range, b.range);
|
||||
@@ -1,81 +0,0 @@
|
||||
import { FoamWorkspace } from './workspace';
|
||||
import { URI } from './uri';
|
||||
import { IDisposable } from '../index';
|
||||
import { Resource } from './note';
|
||||
|
||||
export class FoamTags implements IDisposable {
|
||||
public readonly tags: Map<string, URI[]> = new Map();
|
||||
|
||||
/**
|
||||
* List of disposables to destroy with the tags
|
||||
*/
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
/**
|
||||
* Computes all tags in the workspace and keep them up-to-date
|
||||
*
|
||||
* @param workspace the target workspace
|
||||
* @param keepMonitoring whether to recompute the links when the workspace changes
|
||||
* @returns the FoamTags
|
||||
*/
|
||||
public static fromWorkspace(
|
||||
workspace: FoamWorkspace,
|
||||
keepMonitoring: boolean = false
|
||||
): FoamTags {
|
||||
let tags = new FoamTags();
|
||||
|
||||
workspace
|
||||
.list()
|
||||
.forEach(resource => tags.addResourceFromTagIndex(resource));
|
||||
|
||||
if (keepMonitoring) {
|
||||
tags.disposables.push(
|
||||
workspace.onDidAdd(resource => {
|
||||
tags.addResourceFromTagIndex(resource);
|
||||
}),
|
||||
workspace.onDidUpdate(change => {
|
||||
tags.updateResourceWithinTagIndex(change.old, change.new);
|
||||
}),
|
||||
workspace.onDidDelete(resource => {
|
||||
tags.removeResourceFromTagIndex(resource);
|
||||
})
|
||||
);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.forEach(d => d.dispose());
|
||||
this.disposables = [];
|
||||
}
|
||||
|
||||
updateResourceWithinTagIndex(oldResource: Resource, newResource: Resource) {
|
||||
this.removeResourceFromTagIndex(oldResource);
|
||||
this.addResourceFromTagIndex(newResource);
|
||||
}
|
||||
|
||||
addResourceFromTagIndex(resource: Resource) {
|
||||
new Set(resource.tags.map(t => t.label)).forEach(tag => {
|
||||
const tagMeta = this.tags.get(tag) ?? [];
|
||||
tagMeta.push(resource.uri);
|
||||
this.tags.set(tag, tagMeta);
|
||||
});
|
||||
}
|
||||
|
||||
removeResourceFromTagIndex(resource: Resource) {
|
||||
resource.tags.forEach(t => {
|
||||
const tag = t.label;
|
||||
if (this.tags.has(tag)) {
|
||||
const remainingLocations = this.tags
|
||||
.get(tag)
|
||||
?.filter(uri => !URI.isEqual(uri, resource.uri));
|
||||
|
||||
if (remainingLocations && remainingLocations.length > 0) {
|
||||
this.tags.set(tag, remainingLocations);
|
||||
} else {
|
||||
this.tags.delete(tag);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
import * as path from 'path';
|
||||
import { Resource, ResourceLink } from './note';
|
||||
import { URI } from './uri';
|
||||
import { isSome, isNone } from '../utils';
|
||||
import { Emitter } from '../common/event';
|
||||
import { IDisposable } from '../index';
|
||||
import { ResourceProvider } from './provider';
|
||||
|
||||
export function getReferenceType(
|
||||
reference: URI | string
|
||||
): 'uri' | 'absolute-path' | 'relative-path' | 'key' {
|
||||
if (URI.isUri(reference)) {
|
||||
return 'uri';
|
||||
}
|
||||
const isPath = reference.split('/').length > 1;
|
||||
if (!isPath) {
|
||||
return 'key';
|
||||
}
|
||||
const isAbsPath = isPath && reference.startsWith('/');
|
||||
return isAbsPath ? 'absolute-path' : 'relative-path';
|
||||
}
|
||||
|
||||
const pathToResourceId = (pathValue: string) => {
|
||||
const { ext } = path.parse(pathValue);
|
||||
return ext.length > 0 ? pathValue : pathValue + '.md';
|
||||
};
|
||||
const uriToResourceId = (uri: URI) => pathToResourceId(uri.path);
|
||||
|
||||
const pathToResourceName = (pathValue: string) =>
|
||||
path.parse(pathValue).name.toLowerCase();
|
||||
export const uriToResourceName = (uri: URI) => pathToResourceName(uri.path);
|
||||
|
||||
export class FoamWorkspace implements IDisposable {
|
||||
private onDidAddEmitter = new Emitter<Resource>();
|
||||
private onDidUpdateEmitter = new Emitter<{ old: Resource; new: Resource }>();
|
||||
private onDidDeleteEmitter = new Emitter<Resource>();
|
||||
onDidAdd = this.onDidAddEmitter.event;
|
||||
onDidUpdate = this.onDidUpdateEmitter.event;
|
||||
onDidDelete = this.onDidDeleteEmitter.event;
|
||||
|
||||
private providers: ResourceProvider[] = [];
|
||||
|
||||
/**
|
||||
* Resources by key / slug
|
||||
*/
|
||||
private resourcesByName: Map<string, string[]> = new Map();
|
||||
/**
|
||||
* Resources by URI
|
||||
*/
|
||||
private resources: Map<string, Resource> = new Map();
|
||||
|
||||
registerProvider(provider: ResourceProvider) {
|
||||
this.providers.push(provider);
|
||||
return provider.init(this);
|
||||
}
|
||||
|
||||
set(resource: Resource) {
|
||||
const id = uriToResourceId(resource.uri);
|
||||
const old = this.find(resource.uri);
|
||||
const name = uriToResourceName(resource.uri);
|
||||
this.resources.set(id, resource);
|
||||
if (!this.resourcesByName.has(name)) {
|
||||
this.resourcesByName.set(name, []);
|
||||
}
|
||||
this.resourcesByName.get(name)?.push(id);
|
||||
isSome(old)
|
||||
? this.onDidUpdateEmitter.fire({ old: old, new: resource })
|
||||
: this.onDidAddEmitter.fire(resource);
|
||||
return this;
|
||||
}
|
||||
|
||||
delete(uri: URI) {
|
||||
const id = uriToResourceId(uri);
|
||||
const deleted = this.resources.get(id);
|
||||
this.resources.delete(id);
|
||||
|
||||
const name = uriToResourceName(uri);
|
||||
this.resourcesByName.set(
|
||||
name,
|
||||
this.resourcesByName.get(name)?.filter(resId => resId !== id) ?? []
|
||||
);
|
||||
if (this.resourcesByName.get(name)?.length === 0) {
|
||||
this.resourcesByName.delete(name);
|
||||
}
|
||||
|
||||
isSome(deleted) && this.onDidDeleteEmitter.fire(deleted);
|
||||
return deleted ?? null;
|
||||
}
|
||||
|
||||
public exists(uri: URI): boolean {
|
||||
return (
|
||||
!URI.isPlaceholder(uri) &&
|
||||
isSome(this.resources.get(uriToResourceId(uri)))
|
||||
);
|
||||
}
|
||||
|
||||
public list(): Resource[] {
|
||||
return Array.from(this.resources.values());
|
||||
}
|
||||
|
||||
public get(uri: URI): Resource {
|
||||
const note = this.find(uri);
|
||||
if (isSome(note)) {
|
||||
return note;
|
||||
} else {
|
||||
throw new Error('Resource not found: ' + uri.path);
|
||||
}
|
||||
}
|
||||
|
||||
public find(resourceId: URI | string, reference?: URI): Resource | null {
|
||||
const refType = getReferenceType(resourceId);
|
||||
switch (refType) {
|
||||
case 'uri':
|
||||
const uri = resourceId as URI;
|
||||
return this.exists(uri)
|
||||
? this.resources.get(uriToResourceId(uri)) ?? null
|
||||
: null;
|
||||
|
||||
case 'key':
|
||||
const name = pathToResourceName(resourceId as string);
|
||||
let paths = this.resourcesByName.get(name);
|
||||
|
||||
if (isNone(paths) || paths.length === 0) {
|
||||
paths = this.resourcesByName.get(resourceId as string);
|
||||
}
|
||||
|
||||
if (isNone(paths) || paths.length === 0) {
|
||||
return null;
|
||||
}
|
||||
// prettier-ignore
|
||||
const sortedPaths = paths.length === 1
|
||||
? paths
|
||||
: paths.sort((a, b) => a.localeCompare(b));
|
||||
|
||||
return this.resources.get(sortedPaths[0]) ?? null;
|
||||
|
||||
case 'absolute-path':
|
||||
const resourceUri = URI.file(resourceId as string);
|
||||
return this.resources.get(uriToResourceId(resourceUri)) ?? null;
|
||||
|
||||
case 'relative-path':
|
||||
if (isNone(reference)) {
|
||||
return null;
|
||||
}
|
||||
const relativePath = resourceId as string;
|
||||
const targetUri = URI.computeRelativeURI(reference, relativePath);
|
||||
return this.resources.get(uriToResourceId(targetUri)) ?? null;
|
||||
|
||||
default:
|
||||
throw new Error('Unexpected reference type: ' + refType);
|
||||
}
|
||||
}
|
||||
|
||||
public resolveLink(resource: Resource, link: ResourceLink): URI {
|
||||
// TODO add tests
|
||||
const provider = this.providers.find(p => p.supports(resource.uri));
|
||||
return (
|
||||
provider?.resolveLink(this, resource, link) ??
|
||||
URI.placeholder(link.target)
|
||||
);
|
||||
}
|
||||
|
||||
public read(uri: URI): Promise<string | null> {
|
||||
const provider = this.providers.find(p => p.supports(uri));
|
||||
return provider?.read(uri) ?? Promise.resolve(null);
|
||||
}
|
||||
|
||||
public readAsMarkdown(uri: URI): Promise<string | null> {
|
||||
const provider = this.providers.find(p => p.supports(uri));
|
||||
return provider?.readAsMarkdown(uri) ?? Promise.resolve(null);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.onDidAddEmitter.dispose();
|
||||
this.onDidDeleteEmitter.dispose();
|
||||
this.onDidUpdateEmitter.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import GithubSlugger from 'github-slugger';
|
||||
import { URI } from '../model/uri';
|
||||
|
||||
export const uriToSlug = (uri: URI): string =>
|
||||
GithubSlugger.slug(URI.getBasename(uri));
|
||||
@@ -1,44 +0,0 @@
|
||||
import { createConfigFromFolders } from '../src/config';
|
||||
import { Logger } from '../src/utils/log';
|
||||
import { URI } from '../src/model/uri';
|
||||
|
||||
Logger.setLevel('error');
|
||||
|
||||
const testFolder = URI.joinPath(URI.file(__dirname), 'test-config');
|
||||
|
||||
describe('Foam configuration', () => {
|
||||
it('can read settings from config.json', () => {
|
||||
const config = createConfigFromFolders([
|
||||
URI.joinPath(testFolder, 'folder1'),
|
||||
]);
|
||||
expect(config.get('feature1.setting1.value')).toBeTruthy();
|
||||
expect(config.get('feature2.value')).toEqual(12);
|
||||
|
||||
const section = config.get<{ value: boolean }>('feature1.setting1');
|
||||
expect(section!.value).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can merge settings from multiple foam folders', () => {
|
||||
const config = createConfigFromFolders([
|
||||
URI.joinPath(testFolder, 'folder1'),
|
||||
URI.joinPath(testFolder, 'folder2'),
|
||||
]);
|
||||
|
||||
// override value
|
||||
expect(config.get('feature1.setting1.value')).toBe(false);
|
||||
// this was not overridden
|
||||
expect(config.get('feature1.setting1.extraValue')).toEqual('go foam');
|
||||
// new value from second config file
|
||||
expect(config.get('feature1.setting1.value2')).toBe('hello');
|
||||
|
||||
// this whole section doesn't exist in second file
|
||||
expect(config.get('feature2.value')).toEqual(12);
|
||||
});
|
||||
|
||||
it('cannot activate local plugins from workspace config', () => {
|
||||
const config = createConfigFromFolders([
|
||||
URI.joinPath(testFolder, 'enable-plugins'),
|
||||
]);
|
||||
expect(config.get('experimental.localPlugins.enabled')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -1,99 +0,0 @@
|
||||
import path from 'path';
|
||||
import { FoamWorkspace } from '../src';
|
||||
import { NoteLinkDefinition, Resource } from '../src/model/note';
|
||||
import { IDataStore, Matcher } from '../src/services/datastore';
|
||||
import { MarkdownResourceProvider } from '../src/markdown-provider';
|
||||
import { Range } from '../src/model/range';
|
||||
import { URI } from '../src/model/uri';
|
||||
import { Logger } from '../src/utils/log';
|
||||
|
||||
Logger.setLevel('error');
|
||||
|
||||
const position = Range.create(0, 0, 0, 100);
|
||||
|
||||
const documentStart = position.start;
|
||||
const documentEnd = position.end;
|
||||
const eol = '\n';
|
||||
|
||||
/**
|
||||
* Turns a string into a URI
|
||||
* The goal of this function is to make sure we are consistent in the
|
||||
* way we generate URIs (and therefore IDs) across the tests
|
||||
*/
|
||||
export const strToUri = URI.file;
|
||||
|
||||
export const noOpDataStore = (): IDataStore => ({
|
||||
read: _ => Promise.resolve(''),
|
||||
list: _ => Promise.resolve([]),
|
||||
});
|
||||
|
||||
export const createTestWorkspace = () => {
|
||||
const workspace = new FoamWorkspace();
|
||||
const matcher = new Matcher([URI.file('/')], ['**/*']);
|
||||
const provider = new MarkdownResourceProvider(
|
||||
matcher,
|
||||
undefined,
|
||||
undefined,
|
||||
noOpDataStore()
|
||||
);
|
||||
workspace.registerProvider(provider);
|
||||
return workspace;
|
||||
};
|
||||
|
||||
export const createTestNote = (params: {
|
||||
uri: string;
|
||||
title?: string;
|
||||
definitions?: NoteLinkDefinition[];
|
||||
links?: Array<{ slug: string } | { to: string }>;
|
||||
text?: string;
|
||||
root?: URI;
|
||||
tags?: string[];
|
||||
}): Resource => {
|
||||
const root = params.root ?? URI.file('/');
|
||||
return {
|
||||
uri: URI.resolve(params.uri, root),
|
||||
type: 'note',
|
||||
properties: {},
|
||||
title: params.title ?? path.parse(strToUri(params.uri).path).base,
|
||||
definitions: params.definitions ?? [],
|
||||
tags:
|
||||
params.tags?.map(t => ({
|
||||
label: t,
|
||||
range: Range.create(0, 0, 0, 0),
|
||||
})) ?? [],
|
||||
links: params.links
|
||||
? params.links.map((link, index) => {
|
||||
const range = Range.create(
|
||||
position.start.line + index,
|
||||
position.start.character,
|
||||
position.start.line + index,
|
||||
position.end.character
|
||||
);
|
||||
return 'slug' in link
|
||||
? {
|
||||
type: 'wikilink',
|
||||
target: link.slug,
|
||||
label: link.slug,
|
||||
range: range,
|
||||
rawText: 'link text',
|
||||
}
|
||||
: {
|
||||
type: 'link',
|
||||
target: link.to,
|
||||
label: 'link text',
|
||||
range: range,
|
||||
};
|
||||
})
|
||||
: [],
|
||||
source: {
|
||||
eol: eol,
|
||||
end: documentEnd,
|
||||
contentStart: documentStart,
|
||||
text: params.text ?? '',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
describe('Test utils', () => {
|
||||
it('are happy', () => {});
|
||||
});
|
||||
@@ -1,492 +0,0 @@
|
||||
import {
|
||||
createMarkdownParser,
|
||||
createMarkdownReferences,
|
||||
ParserPlugin,
|
||||
} from '../src/markdown-provider';
|
||||
import { DirectLink, WikiLink } from '../src/model/note';
|
||||
import { Logger } from '../src/utils/log';
|
||||
import { uriToSlug } from '../src/utils/slug';
|
||||
import { URI } from '../src/model/uri';
|
||||
import { FoamGraph } from '../src/model/graph';
|
||||
import { createTestWorkspace } from './core.test';
|
||||
import { Range } from '../src/model/range';
|
||||
|
||||
Logger.setLevel('error');
|
||||
|
||||
const pageA = `
|
||||
# Page A
|
||||
|
||||
## Section
|
||||
- [[page-b]]
|
||||
- [[page-c]]
|
||||
- [[Page D]]
|
||||
- [[page e]]
|
||||
`;
|
||||
|
||||
const pageB = `
|
||||
# Page B
|
||||
|
||||
This references [[page-a]]`;
|
||||
|
||||
const pageC = `
|
||||
# Page C
|
||||
`;
|
||||
|
||||
const pageD = `
|
||||
# Page D
|
||||
`;
|
||||
|
||||
const pageE = `
|
||||
# Page E
|
||||
`;
|
||||
|
||||
const createNoteFromMarkdown = (path: string, content: string) =>
|
||||
createMarkdownParser([]).parse(URI.file(path), content);
|
||||
|
||||
describe('Markdown loader', () => {
|
||||
it('Converts markdown to notes', () => {
|
||||
const workspace = createTestWorkspace();
|
||||
workspace.set(createNoteFromMarkdown('/page-a.md', pageA));
|
||||
workspace.set(createNoteFromMarkdown('/page-b.md', pageB));
|
||||
workspace.set(createNoteFromMarkdown('/page-c.md', pageC));
|
||||
workspace.set(createNoteFromMarkdown('/page-d.md', pageD));
|
||||
workspace.set(createNoteFromMarkdown('/page-e.md', pageE));
|
||||
|
||||
expect(
|
||||
workspace
|
||||
.list()
|
||||
.map(n => n.uri)
|
||||
.map(uriToSlug)
|
||||
.sort()
|
||||
).toEqual(['page-a', 'page-b', 'page-c', 'page-d', 'page-e']);
|
||||
});
|
||||
|
||||
it('Ingores external links', () => {
|
||||
const note = createNoteFromMarkdown(
|
||||
'/path/to/page-a.md',
|
||||
`
|
||||
this is a [link to google](https://www.google.com)
|
||||
`
|
||||
);
|
||||
expect(note.links.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('Ignores references to sections in the same file', () => {
|
||||
const note = createNoteFromMarkdown(
|
||||
'/path/to/page-a.md',
|
||||
`
|
||||
this is a [link to intro](#introduction)
|
||||
`
|
||||
);
|
||||
expect(note.links.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('Parses internal links correctly', () => {
|
||||
const note = createNoteFromMarkdown(
|
||||
'/path/to/page-a.md',
|
||||
'this is a [link to page b](../doc/page-b.md)'
|
||||
);
|
||||
expect(note.links.length).toEqual(1);
|
||||
const link = note.links[0] as DirectLink;
|
||||
expect(link.type).toEqual('link');
|
||||
expect(link.label).toEqual('link to page b');
|
||||
expect(link.target).toEqual('../doc/page-b.md');
|
||||
});
|
||||
|
||||
it('Parses links that have formatting in label', () => {
|
||||
const note = createNoteFromMarkdown(
|
||||
'/path/to/page-a.md',
|
||||
'this is [**link** with __formatting__](../doc/page-b.md)'
|
||||
);
|
||||
expect(note.links.length).toEqual(1);
|
||||
const link = note.links[0] as DirectLink;
|
||||
expect(link.type).toEqual('link');
|
||||
expect(link.label).toEqual('link with formatting');
|
||||
expect(link.target).toEqual('../doc/page-b.md');
|
||||
});
|
||||
|
||||
it('Parses wikilinks correctly', () => {
|
||||
const workspace = createTestWorkspace();
|
||||
const noteA = createNoteFromMarkdown('/page-a.md', pageA);
|
||||
const noteB = createNoteFromMarkdown('/page-b.md', pageB);
|
||||
const noteC = createNoteFromMarkdown('/page-c.md', pageC);
|
||||
const noteD = createNoteFromMarkdown('/Page D.md', pageD);
|
||||
const noteE = createNoteFromMarkdown('/page e.md', pageE);
|
||||
|
||||
workspace
|
||||
.set(noteA)
|
||||
.set(noteB)
|
||||
.set(noteC)
|
||||
.set(noteD)
|
||||
.set(noteE);
|
||||
const graph = FoamGraph.fromWorkspace(workspace);
|
||||
|
||||
expect(graph.getBacklinks(noteB.uri).map(l => l.source)).toEqual([
|
||||
noteA.uri,
|
||||
]);
|
||||
expect(graph.getLinks(noteA.uri).map(l => l.target)).toEqual([
|
||||
noteB.uri,
|
||||
noteC.uri,
|
||||
noteD.uri,
|
||||
noteE.uri,
|
||||
]);
|
||||
});
|
||||
|
||||
it('Parses backlinks with an alias', () => {
|
||||
const note = createNoteFromMarkdown(
|
||||
'/path/to/page-a.md',
|
||||
'this is [[link|link alias]]. A link with spaces [[other link | spaced]]'
|
||||
);
|
||||
expect(note.links.length).toEqual(2);
|
||||
let link = note.links[0] as WikiLink;
|
||||
expect(link.type).toEqual('wikilink');
|
||||
expect(link.rawText).toEqual('[[link|link alias]]');
|
||||
expect(link.label).toEqual('link alias');
|
||||
expect(link.target).toEqual('link');
|
||||
link = note.links[1] as WikiLink;
|
||||
expect(link.type).toEqual('wikilink');
|
||||
expect(link.rawText).toEqual('[[other link | spaced]]');
|
||||
expect(link.label).toEqual('spaced');
|
||||
expect(link.target).toEqual('other link');
|
||||
});
|
||||
|
||||
it('Skips wikilinks in codeblocks', () => {
|
||||
const noteA = createNoteFromMarkdown(
|
||||
'/dir1/page-a.md',
|
||||
`
|
||||
this is some text with our [[first-wikilink]].
|
||||
|
||||
\`\`\`
|
||||
this is inside a [[codeblock]]
|
||||
\`\`\`
|
||||
|
||||
this is some text with our [[second-wikilink]].
|
||||
`
|
||||
);
|
||||
expect(noteA.links.map(l => l.label)).toEqual([
|
||||
'first-wikilink',
|
||||
'second-wikilink',
|
||||
]);
|
||||
});
|
||||
|
||||
it('Skips wikilinks in inlined codeblocks', () => {
|
||||
const noteA = createNoteFromMarkdown(
|
||||
'/dir1/page-a.md',
|
||||
`
|
||||
this is some text with our [[first-wikilink]].
|
||||
|
||||
this is \`inside a [[codeblock]]\`
|
||||
|
||||
this is some text with our [[second-wikilink]].
|
||||
`
|
||||
);
|
||||
expect(noteA.links.map(l => l.label)).toEqual([
|
||||
'first-wikilink',
|
||||
'second-wikilink',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Note Title', () => {
|
||||
it('should initialize note title if heading exists', () => {
|
||||
const note = createNoteFromMarkdown(
|
||||
'/page-a.md',
|
||||
`
|
||||
# Page A
|
||||
this note has a title
|
||||
`
|
||||
);
|
||||
expect(note.title).toBe('Page A');
|
||||
});
|
||||
|
||||
it('should default to file name if heading does not exist', () => {
|
||||
const note = createNoteFromMarkdown(
|
||||
'/page-d.md',
|
||||
`
|
||||
This file has no heading.
|
||||
`
|
||||
);
|
||||
|
||||
expect(note.title).toEqual('page-d');
|
||||
});
|
||||
|
||||
it('should give precedence to frontmatter title over other headings', () => {
|
||||
const note = createNoteFromMarkdown(
|
||||
'/page-e.md',
|
||||
`
|
||||
---
|
||||
title: Note Title
|
||||
date: 20-12-12
|
||||
---
|
||||
|
||||
# Other Note Title
|
||||
`
|
||||
);
|
||||
|
||||
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',
|
||||
`
|
||||
#
|
||||
|
||||
this note has an empty title line
|
||||
`
|
||||
);
|
||||
expect(note.title).toEqual('Hello Page');
|
||||
});
|
||||
});
|
||||
|
||||
describe('frontmatter', () => {
|
||||
it('should parse yaml frontmatter', () => {
|
||||
const note = createNoteFromMarkdown(
|
||||
'/page-e.md',
|
||||
`
|
||||
---
|
||||
title: Note Title
|
||||
date: 20-12-12
|
||||
---
|
||||
|
||||
# Other Note Title`
|
||||
);
|
||||
|
||||
expect(note.properties.title).toBe('Note Title');
|
||||
expect(note.properties.date).toBe('20-12-12');
|
||||
});
|
||||
|
||||
it('should parse empty frontmatter', () => {
|
||||
const note = createNoteFromMarkdown(
|
||||
'/page-f.md',
|
||||
`
|
||||
---
|
||||
---
|
||||
|
||||
# Empty Frontmatter
|
||||
`
|
||||
);
|
||||
|
||||
expect(note.properties).toEqual({});
|
||||
});
|
||||
|
||||
it('should not fail when there are issues with parsing frontmatter', () => {
|
||||
const note = createNoteFromMarkdown(
|
||||
'/page-f.md',
|
||||
`
|
||||
---
|
||||
title: - one
|
||||
- two
|
||||
- #
|
||||
---
|
||||
|
||||
`
|
||||
);
|
||||
|
||||
expect(note.properties).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('wikilinks definitions', () => {
|
||||
it('can generate links without file extension when includeExtension = false', () => {
|
||||
const workspace = createTestWorkspace();
|
||||
const noteA = createNoteFromMarkdown('/dir1/page-a.md', pageA);
|
||||
workspace
|
||||
.set(noteA)
|
||||
.set(createNoteFromMarkdown('/dir1/page-b.md', pageB))
|
||||
.set(createNoteFromMarkdown('/dir1/page-c.md', pageC));
|
||||
|
||||
const noExtRefs = createMarkdownReferences(workspace, noteA.uri, false);
|
||||
expect(noExtRefs.map(r => r.url)).toEqual(['page-b', 'page-c']);
|
||||
});
|
||||
|
||||
it('can generate links with file extension when includeExtension = true', () => {
|
||||
const workspace = createTestWorkspace();
|
||||
const noteA = createNoteFromMarkdown('/dir1/page-a.md', pageA);
|
||||
workspace
|
||||
.set(noteA)
|
||||
.set(createNoteFromMarkdown('/dir1/page-b.md', pageB))
|
||||
.set(createNoteFromMarkdown('/dir1/page-c.md', pageC));
|
||||
|
||||
const extRefs = createMarkdownReferences(workspace, noteA.uri, true);
|
||||
expect(extRefs.map(r => r.url)).toEqual(['page-b.md', 'page-c.md']);
|
||||
});
|
||||
|
||||
it('use relative paths', () => {
|
||||
const workspace = createTestWorkspace();
|
||||
const noteA = createNoteFromMarkdown('/dir1/page-a.md', pageA);
|
||||
workspace
|
||||
.set(noteA)
|
||||
.set(createNoteFromMarkdown('/dir2/page-b.md', pageB))
|
||||
.set(createNoteFromMarkdown('/dir3/page-c.md', pageC));
|
||||
|
||||
const extRefs = createMarkdownReferences(workspace, noteA.uri, true);
|
||||
expect(extRefs.map(r => r.url)).toEqual([
|
||||
'../dir2/page-b.md',
|
||||
'../dir3/page-c.md',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tags plugin', () => {
|
||||
it('can find tags in the text of the note', () => {
|
||||
const noteA = createNoteFromMarkdown(
|
||||
'/dir1/page-a.md',
|
||||
`
|
||||
# this is a #heading
|
||||
#this is some #text that includes #tags we #care-about.
|
||||
`
|
||||
);
|
||||
expect(noteA.tags).toEqual([
|
||||
{ label: 'heading', range: Range.create(1, 12, 1, 20) },
|
||||
{ label: 'this', range: Range.create(2, 0, 2, 5) },
|
||||
{ label: 'text', range: Range.create(2, 14, 2, 19) },
|
||||
{ label: 'tags', range: Range.create(2, 34, 2, 39) },
|
||||
{ label: 'care-about', range: Range.create(2, 43, 2, 54) },
|
||||
]);
|
||||
});
|
||||
|
||||
it('will skip tags in codeblocks', () => {
|
||||
const noteA = createNoteFromMarkdown(
|
||||
'/dir1/page-a.md',
|
||||
`
|
||||
this is some #text that includes #tags we #care-about.
|
||||
|
||||
\`\`\`
|
||||
this is a #codeblock
|
||||
\`\`\`
|
||||
`
|
||||
);
|
||||
expect(noteA.tags.map(t => t.label)).toEqual([
|
||||
'text',
|
||||
'tags',
|
||||
'care-about',
|
||||
]);
|
||||
});
|
||||
|
||||
it('will skip tags in inlined codeblocks', () => {
|
||||
const noteA = createNoteFromMarkdown(
|
||||
'/dir1/page-a.md',
|
||||
`
|
||||
this is some #text that includes #tags we #care-about.
|
||||
this is a \`inlined #codeblock\`
|
||||
`
|
||||
);
|
||||
expect(noteA.tags.map(t => t.label)).toEqual([
|
||||
'text',
|
||||
'tags',
|
||||
'care-about',
|
||||
]);
|
||||
});
|
||||
it('can find tags as text in yaml', () => {
|
||||
const noteA = createNoteFromMarkdown(
|
||||
'/dir1/page-a.md',
|
||||
`
|
||||
---
|
||||
tags: hello, world this_is_good
|
||||
---
|
||||
# this is a heading
|
||||
this is some #text that includes #tags we #care-about.
|
||||
`
|
||||
);
|
||||
expect(noteA.tags.map(t => t.label)).toEqual([
|
||||
'hello',
|
||||
'world',
|
||||
'this_is_good',
|
||||
'text',
|
||||
'tags',
|
||||
'care-about',
|
||||
]);
|
||||
});
|
||||
|
||||
it('can find tags as array in yaml', () => {
|
||||
const noteA = createNoteFromMarkdown(
|
||||
'/dir1/page-a.md',
|
||||
`
|
||||
---
|
||||
tags: [hello, world, this_is_good]
|
||||
---
|
||||
# this is a heading
|
||||
this is some #text that includes #tags we #care-about.
|
||||
`
|
||||
);
|
||||
expect(noteA.tags.map(t => t.label)).toEqual([
|
||||
'hello',
|
||||
'world',
|
||||
'this_is_good',
|
||||
'text',
|
||||
'tags',
|
||||
'care-about',
|
||||
]);
|
||||
});
|
||||
|
||||
it('provides rough range for tags in yaml', () => {
|
||||
// For now it's enough to just get the YAML block range
|
||||
// in the future we might want to be more specific
|
||||
|
||||
const noteA = createNoteFromMarkdown(
|
||||
'/dir1/page-a.md',
|
||||
`
|
||||
---
|
||||
tags: [hello, world, this_is_good]
|
||||
---
|
||||
# this is a heading
|
||||
this is some text
|
||||
`
|
||||
);
|
||||
expect(noteA.tags[0]).toEqual({
|
||||
label: 'hello',
|
||||
range: Range.create(1, 0, 3, 3),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parser plugins', () => {
|
||||
const testPlugin: ParserPlugin = {
|
||||
visit: (node, note) => {
|
||||
if (node.type === 'heading') {
|
||||
note.properties.hasHeading = true;
|
||||
}
|
||||
},
|
||||
};
|
||||
const parser = createMarkdownParser([testPlugin]);
|
||||
|
||||
it('can augment the parsing of the file', () => {
|
||||
const note1 = parser.parse(
|
||||
URI.file('/path/to/a'),
|
||||
`
|
||||
This is a test note without headings.
|
||||
But with some content.
|
||||
`
|
||||
);
|
||||
expect(note1.properties.hasHeading).toBeUndefined();
|
||||
|
||||
const note2 = parser.parse(
|
||||
URI.file('/path/to/a'),
|
||||
`
|
||||
# This is a note with header
|
||||
and some content`
|
||||
);
|
||||
expect(note2.properties.hasHeading).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,51 +0,0 @@
|
||||
import { Logger } from '../src';
|
||||
import { URI } from '../src/model/uri';
|
||||
import { uriToSlug } from '../src/utils/slug';
|
||||
|
||||
Logger.setLevel('error');
|
||||
|
||||
describe('Foam URIs', () => {
|
||||
describe('URI parsing', () => {
|
||||
const base = URI.file('/path/to/file.md');
|
||||
test.each([
|
||||
['https://www.google.com', URI.parse('https://www.google.com')],
|
||||
['/path/to/a/file.md', URI.file('/path/to/a/file.md')],
|
||||
['../relative/file.md', URI.file('/path/relative/file.md')],
|
||||
['#section', URI.create({ ...base, fragment: 'section' })],
|
||||
[
|
||||
'../relative/file.md#section',
|
||||
URI.parse('file:/path/relative/file.md#section'),
|
||||
],
|
||||
])('URI Parsing (%s) - %s', (input, exp) => {
|
||||
const result = URI.resolve(input, base);
|
||||
expect(result.scheme).toEqual(exp.scheme);
|
||||
expect(result.authority).toEqual(exp.authority);
|
||||
expect(result.path).toEqual(exp.path);
|
||||
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('computes a relative uri using a slug', () => {
|
||||
expect(
|
||||
URI.computeRelativeURI(URI.file('/my/file.md'), '../hello.md')
|
||||
).toEqual(URI.file('/hello.md'));
|
||||
expect(URI.computeRelativeURI(URI.file('/my/file.md'), '../hello')).toEqual(
|
||||
URI.file('/hello.md')
|
||||
);
|
||||
expect(
|
||||
URI.computeRelativeURI(URI.file('/my/file.markdown'), '../hello')
|
||||
).toEqual(URI.file('/hello.markdown'));
|
||||
});
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "ESNext"
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"composite": true,
|
||||
"esModuleInterop": true,
|
||||
"importHelpers": true,
|
||||
"downlevelIteration": true,
|
||||
// commonjs module format is used so that the incremental
|
||||
// tsc build-mode ran during development can replace individual
|
||||
// files (as opposed to generate the .cjs.development.js bundle.
|
||||
//
|
||||
// this is overridden in tsconfig.build.json for distribution
|
||||
"module": "commonjs",
|
||||
|
||||
"moduleResolution": "node",
|
||||
"outDir": "dist",
|
||||
"rootDir": "./src",
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"lib": [
|
||||
"ES2019", "es2020.string"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
"types"
|
||||
]
|
||||
}
|
||||
@@ -8,3 +8,5 @@ vsc-extension-quickstart.md
|
||||
**/.eslintrc.json
|
||||
**/*.map
|
||||
**/*.ts
|
||||
assets/screenshots
|
||||
node_modules
|
||||
|
||||
@@ -4,6 +4,210 @@ 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.19.0] - 2022-07-07
|
||||
|
||||
New Features:
|
||||
- Support for attachments (PDF) and images (#1027)
|
||||
- Support for opening day notes for other days as well (#1026, thanks @alper)
|
||||
|
||||
## [0.18.5] - 2022-06-29
|
||||
|
||||
Fixes and Improvements:
|
||||
- Support for `alias` YAML property to define note alias (#1014 - thanks @lingyv-li)
|
||||
|
||||
Internal:
|
||||
- Improved extension bundling (#1015 - thanks @lingyv-li)
|
||||
- Use `vscode.workspace.fs` instead of `fs` (#1005 - thanks @joshdover)
|
||||
|
||||
## [0.18.4] - 2022-06-03
|
||||
|
||||
Fixes and Improvements:
|
||||
- move past `]]` when writing wikilinks (#998 - thanks @Lauviah0622)
|
||||
- highlight improvements (#890 - thanks @memeplex)
|
||||
|
||||
## [0.18.3] - 2022-04-17
|
||||
|
||||
Fixes and Improvements:
|
||||
- Better reporting when links fail to resolve
|
||||
- Failing link resolution during graph computation no longer fatal
|
||||
|
||||
## [0.18.2] - 2022-04-14
|
||||
|
||||
Fixes and Improvements:
|
||||
- Fixed parsing error on empty direct links (#980 - thanks @chrisUsick)
|
||||
- Improved rendering in preview of wikilinks that have link definitions (#979 - thanks @josephdecock)
|
||||
- Restored handling of section-only wikilinks (#981)
|
||||
|
||||
## [0.18.1] - 2022-04-13
|
||||
|
||||
Fixes and Improvements:
|
||||
- Fixed parsing error for direct links with square brackets in them (#977)
|
||||
- Improved markdown direct link resolution (#972)
|
||||
- Improved templates support for custom paths (#970)
|
||||
|
||||
## [0.18.0] - 2022-04-11
|
||||
|
||||
Features:
|
||||
- Link synchronization on file rename
|
||||
|
||||
Internal:
|
||||
- Changed graph computation on workspace change to simplify code
|
||||
|
||||
## [0.17.8] - 2022-04-01
|
||||
|
||||
Fixes and Improvements:
|
||||
- Do not add ignored files to Foam upon change (#480)
|
||||
- Restore full use of editor.action.openLink (#693)
|
||||
- Minor performance improvements
|
||||
|
||||
## [0.17.7] - 2022-03-29
|
||||
|
||||
Fixes and Improvements:
|
||||
- Include links with sections in backlinks (#895)
|
||||
- Improved navigation when document editor is already open
|
||||
|
||||
## [0.17.6] - 2022-03-03
|
||||
|
||||
Fixes and Improvements:
|
||||
- Don't fail on error when scannig workspace (#943 - thanks @develmusa)
|
||||
|
||||
## [0.17.5] - 2022-02-22
|
||||
|
||||
Fixes and Improvements:
|
||||
- Added FOAM_SLUG template variable (#865 - Thanks @techCarpenter)
|
||||
|
||||
## [0.17.4] - 2022-02-13
|
||||
|
||||
Fixes and Improvements:
|
||||
- Improvements to Foam variables in templates (#882 - thanks @movermeyer)
|
||||
- Foam variables can now be used just any other VS Code variables, including in combination with placeholders and transformers
|
||||
|
||||
## [0.17.3] - 2022-01-14
|
||||
|
||||
Fixes and Improvements:
|
||||
- Fixed autocompletion with tags (#885 - thanks @memeplex)
|
||||
- Improved "Open Daily Note" to be usabled in tasks (#897 - thanks @MCluck90)
|
||||
|
||||
## [0.17.2] - 2021-12-22
|
||||
|
||||
Fixes and Improvements:
|
||||
- Improved support for wikilinks in titles (#878)
|
||||
- Use syntax injection for wikilinks (#876 - thanks @memeplex)
|
||||
- Fix when applying text edits in last line
|
||||
|
||||
Internal:
|
||||
- DX: Clean up of testing setup (#881 - thanks @memeplex)
|
||||
|
||||
## [0.17.1] - 2021-12-16
|
||||
|
||||
Fixes and Improvements:
|
||||
- Decorate markdown files only (#857)
|
||||
- Fix template placeholders issue (#859)
|
||||
- Improved replacement range for link completion
|
||||
|
||||
Internal:
|
||||
- Major URI/path handling refactoring (#858 - thanks @memeplex)
|
||||
|
||||
## [0.17.0] - 2021-12-08
|
||||
|
||||
Features:
|
||||
|
||||
- Added first class support for sections (#856)
|
||||
- Sections can be referred to in wikilinks
|
||||
- Sections can be embedded
|
||||
- Autocompletion for sections
|
||||
- Diagnostic for sections
|
||||
- Embed sections
|
||||
|
||||
## [0.16.1] - 2021-11-30
|
||||
|
||||
Fixes and Improvements:
|
||||
|
||||
- Fixed diagnostic bug triggered when file had same suffix (#851)
|
||||
|
||||
## [0.16.0] - 2021-11-24
|
||||
|
||||
Features:
|
||||
|
||||
- Added support for unique wikilink identifiers (#841)
|
||||
- This change allows files that have the same name to be uniquely referenced as wikilinks
|
||||
- BREAKING CHANGE: wikilinks to attachments must now include the extension
|
||||
- Added diagnostics for ambiguous wikilinks, with quick fixes available (#844)
|
||||
- Added support for unique wikilinks in autocompletion (#845)
|
||||
|
||||
## [0.15.9] - 2021-11-23
|
||||
|
||||
Fixes and Improvements:
|
||||
|
||||
- Fixed filepath retrieval when creating note from template (#843)
|
||||
|
||||
## [0.15.8] - 2021-11-22
|
||||
|
||||
Fixes and Improvements:
|
||||
|
||||
- Re-enable link navigation for wikilinks (#840)
|
||||
|
||||
## [0.15.7] - 2021-11-21
|
||||
|
||||
Fixes and Improvements:
|
||||
|
||||
- Fixed template listing (#831)
|
||||
- Fixed note creation from template (#834)
|
||||
|
||||
## [0.15.6] - 2021-11-18
|
||||
|
||||
Fixes and Improvements:
|
||||
|
||||
- Link Reference Generation is now OFF by default
|
||||
- Fixed preview navigation (#830)
|
||||
|
||||
|
||||
## [0.15.5] - 2021-11-15
|
||||
|
||||
Fixes and Improvements:
|
||||
|
||||
- Major improvement in navigation. Use link definitions and link references (#821)
|
||||
- Fixed bug showing in hover reference the same more than once when it had multiple links to another (#822)
|
||||
|
||||
Internal:
|
||||
|
||||
- Foam URI refactoring (#820)
|
||||
- Template service refactoring (#825)
|
||||
|
||||
## [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:
|
||||
|
||||
- Improved filtering controls for graph (#782)
|
||||
- Link Hover: Include other connected notes to link target
|
||||
|
||||
## [0.15.0] - 2021-10-04
|
||||
|
||||
Features:
|
||||
@@ -18,7 +222,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)
|
||||
@@ -35,7 +239,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)
|
||||
@@ -63,9 +267,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
|
||||
|
||||
@@ -5,15 +5,133 @@
|
||||
[](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode)
|
||||
[](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode)
|
||||
|
||||
> ⚠️ This is an early stage software. Use at your own peril.
|
||||
> You can join the Foam Community on the [Foam Discord](https://foambubble.github.io/join-discord/e)
|
||||
|
||||
[Foam](https://foambubble.github.io/foam) is a note-taking tool that lives within VsCode, which means you can pair it with your favorite extensions for a great editing experience.
|
||||
[Foam](https://foambubble.github.io/foam) is a note-taking tool that lives within VS Code, which means you can pair it with your favorite extensions for a great editing experience.
|
||||
|
||||
Foam is open source, and allows you to create a local first, markdown based, personal knowledge base. You can also use it to publish your notes.
|
||||
|
||||
Foam is also meant to be extensible, so you can integrate with its internals to customize your knowledge base.
|
||||
|
||||
## Features
|
||||
|
||||
### Graph Visualization
|
||||
|
||||
See how your notes are connected via a [graph](https://foambubble.github.io/foam/features/graph-visualisation) with the `Foam: Show Graph` command.
|
||||
|
||||

|
||||
|
||||
### Link Autocompletion
|
||||
|
||||
Foam helps you create the connections between your notes, and your placeholders as well.
|
||||
|
||||

|
||||
|
||||
### Sync links on file rename
|
||||
|
||||
Foam updates the links to renamed files, so your notes stay consistent.
|
||||
|
||||

|
||||
|
||||
### Unique identifiers across directories
|
||||
|
||||
Foam supports files with the same name in multiple directories.
|
||||
It will use the minimum identifier required, and even report and help you fix existing ambiguous wikilinks.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Link Preview and Navigation
|
||||
|
||||

|
||||
|
||||
### Go to definition, Peek References
|
||||
|
||||
See where a note is being referenced in your knowledge base.
|
||||
|
||||

|
||||
|
||||
### Navigation in Preview
|
||||
|
||||
Navigate your rendered notes in the VS Code preview panel.
|
||||
|
||||

|
||||
|
||||
### Note embed
|
||||
|
||||
Embed the content from other notes.
|
||||
|
||||

|
||||
|
||||
### Support for sections
|
||||
|
||||
Foam supports autocompletion, navigation, embedding and diagnostics for note sections.
|
||||
Just use the standard wiki syntax of `[[resource#Section Title]]`.
|
||||
|
||||
### Link Alias
|
||||
|
||||
Foam supports link aliasing, so you can have a `[[wikilink]]`, or a `[[wikilink|alias]]`.
|
||||
|
||||
### Templates
|
||||
|
||||
Use [custom templates](https://foambubble.github.io/foam/features/note-templates) to have avoid repetitve work on your notes.
|
||||
|
||||

|
||||
|
||||
### Backlinks Panel
|
||||
|
||||
Quickly check which notes are referencing the currently active note.
|
||||
See for each occurrence the context in which it lives, as well as a preview of the note.
|
||||
|
||||

|
||||
|
||||
### Tag Explorer Panel
|
||||
|
||||
Tag your notes and navigate them with the [Tag Explorer](https://foambubble.github.io/foam/features/tags).
|
||||
Foam also supports hierarchical tags.
|
||||
|
||||

|
||||
|
||||
### Orphans and Placeholder Panels
|
||||
|
||||
Orphans are note that have no inbound nor outbound links.
|
||||
Placeholders are dangling links, or notes without content.
|
||||
Keep them under control, and your knowledge base in better state, by using this panel.
|
||||
|
||||

|
||||
|
||||
### Syntax highlight
|
||||
|
||||
Foam highlights wikilinks and placeholder differently, to help you visualize your knowledge base.
|
||||
|
||||

|
||||
|
||||
### Daily note
|
||||
|
||||
Create a journal with [daily notes](https://foambubble.github.io/foam/features/daily-notes).
|
||||
|
||||

|
||||
|
||||
### Generate references for your wikilinks
|
||||
|
||||
Create markdown [references](https://foambubble.github.io/foam/features/link-reference-definitions) for `[[wikilinks]]`, to use your notes in a non-Foam workspace.
|
||||
With references you can also make your notes navigable both in GitHub UI as well as GitHub Pages.
|
||||
|
||||

|
||||
|
||||
### Commands
|
||||
|
||||
- Explore your knowledge base with the `Foam: Open Random Note` command
|
||||
- Access your daily note with the `Foam: Open Daily Note` command
|
||||
- Create a new note with the `Foam: Create New Note` command
|
||||
- This becomes very powerful when combined with [note templates](https://foambubble.github.io/foam/features/note-templates) and the `Foam: Create New Note from Template` command
|
||||
- See your workspace as a connected graph with the `Foam: Show Graph` command
|
||||
|
||||
## Recipes
|
||||
|
||||
People use Foam in different ways for different use cases, check out the [recipes](https://foambubble.github.io/foam/recipes/recipes) page for inspiration!
|
||||
|
||||
## Getting started
|
||||
|
||||
You really, _really_, **really** should read [Foam documentation](https://foambubble.github.io/foam), but if you can't be bothered, this is how to get started:
|
||||
@@ -22,24 +140,13 @@ You really, _really_, **really** should read [Foam documentation](https://foambu
|
||||
2. Clone the repository and open it in VS Code.
|
||||
3. When prompted to install recommended extensions, click **Install all** (or **Show Recommendations** if you want to review and install them one by one).
|
||||
|
||||
This will also install `Foam for VSCode`, but if you already have it installed, that's ok, just make sure you're up to date on the latest version.
|
||||
|
||||
You really, _really_, **really** should read [Foam documentation](https://foambubble.github.io/foam), but if you can't be bothered, this is how to get started:
|
||||
|
||||
## Features
|
||||
|
||||
- Connect your notes using [`[[wikilinks]]`](https://foambubble.github.io/foam/features/backlinking)
|
||||
- Create markdown [references](https://foambubble.github.io/foam/features/link-reference-definitions) for `[[wikilinks]]`, to use your notes in a non-foam workspace
|
||||
- See how your notes are connected via a [graph](https://foambubble.github.io/foam/features/graph-visualisation) with the `Foam: Show Graph` command
|
||||
- Tag your notes and navigate them with the [Tag Explorer](https://foambubble.github.io/foam/features/tags)
|
||||
- Make your notes navigable both in GitHub UI as well as GitHub Pages
|
||||
- Use [custom templates](https://foambubble.github.io/foam/features/note-templates) for your notes
|
||||
- Create a journal with [daily notes](https://foambubble.github.io/foam/features/daily-notes)
|
||||
- Explore your knowledge base with the `Foam: Open Random Note` command
|
||||
This will also install `Foam`, but if you already have it installed, that's ok, just make sure you're up to date on the latest version.
|
||||
|
||||
## Requirements
|
||||
|
||||
High tolerance for alpha-grade software.
|
||||
Foam is still a Work in Progress.
|
||||
Rest assured it will never lock you in, nor compromise your files, but sometimes some features might break ;)
|
||||
|
||||
## Known Issues
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 604 KiB After Width: | Height: | Size: 604 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 821 KiB |
BIN
packages/foam-vscode/assets/screenshots/feature-daily-note.gif
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 859 KiB |
|
After Width: | Height: | Size: 621 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
BIN
packages/foam-vscode/assets/screenshots/feature-link-sync.gif
Normal file
|
After Width: | Height: | Size: 934 KiB |
BIN
packages/foam-vscode/assets/screenshots/feature-navigation.gif
Normal file
|
After Width: | Height: | Size: 935 KiB |
BIN
packages/foam-vscode/assets/screenshots/feature-note-embed.gif
Normal file
|
After Width: | Height: | Size: 298 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 369 KiB |
BIN
packages/foam-vscode/assets/screenshots/feature-show-graph.gif
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 394 KiB |
BIN
packages/foam-vscode/assets/screenshots/feature-tags-panel.gif
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
packages/foam-vscode/assets/screenshots/feature-templates.gif
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 306 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 593 KiB |
@@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
["@babel/preset-env", { targets: { node: "current" } }],
|
||||
"@babel/preset-typescript"
|
||||
],
|
||||
plugins: [["@babel/plugin-transform-runtime", { helpers: false }]]
|
||||
};
|
||||
@@ -82,7 +82,7 @@ module.exports = {
|
||||
// moduleNameMapper: {},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
modulePathIgnorePatterns: ['.vscode-test'],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
@@ -91,7 +91,7 @@ module.exports = {
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
// preset: undefined,
|
||||
preset: 'ts-jest',
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: undefined,
|
||||
@@ -126,13 +126,13 @@ module.exports = {
|
||||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
// setupFilesAfterEnv: [],
|
||||
setupFilesAfterEnv: ['jest-extended'],
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
testEnvironment: "node"
|
||||
testEnvironment: 'node',
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
@@ -152,7 +152,10 @@ module.exports = {
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
// This is overridden in every runCLI invocation but it's here as the default
|
||||
// for vscode-jest. We only want unit tests in the test explorer (sidebar),
|
||||
// since spec tests require the entire extension host to be launched before.
|
||||
testRegex: ['\\.test\\.ts$'],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: undefined,
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "foam-vscode",
|
||||
"displayName": "Foam for VSCode (Wikilinks to Markdown)",
|
||||
"description": "Generate markdown reference lists from wikilinks in a workspace",
|
||||
"displayName": "Foam",
|
||||
"description": "VS Code + Markdown + Wikilinks for your note taking and knowledge base",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"url": "https://github.com/foambubble/foam",
|
||||
"type": "git"
|
||||
},
|
||||
"homepage": "https://github.com/foambubble/foam",
|
||||
"version": "0.15.0",
|
||||
"version": "0.19.0",
|
||||
"license": "MIT",
|
||||
"publisher": "foam",
|
||||
"engines": {
|
||||
"vscode": "^1.47.1"
|
||||
},
|
||||
"icon": "icon/FOAM_ICON_256.png",
|
||||
"icon": "assets/icon/FOAM_ICON_256.png",
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
@@ -37,30 +37,50 @@
|
||||
"markdown.previewStyles": [
|
||||
"./static/preview/style.css"
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"path": "./syntaxes/injection.json",
|
||||
"scopeName": "foam.wikilink.injection",
|
||||
"injectTo": [
|
||||
"text.html.markdown"
|
||||
]
|
||||
}
|
||||
],
|
||||
"colors": [
|
||||
{
|
||||
"id": "foam.placeholder",
|
||||
"description": "Color of foam placeholders.",
|
||||
"defaults": {
|
||||
"dark": "editorWarning.foreground",
|
||||
"light": "editorWarning.foreground",
|
||||
"highContrast": "editorWarning.foreground"
|
||||
}
|
||||
}
|
||||
],
|
||||
"views": {
|
||||
"explorer": [
|
||||
{
|
||||
"id": "foam-vscode.backlinks",
|
||||
"name": "Backlinks",
|
||||
"icon": "media/dep.svg",
|
||||
"icon": "$(references)",
|
||||
"contextualTitle": "Backlinks"
|
||||
},
|
||||
{
|
||||
"id": "foam-vscode.tags-explorer",
|
||||
"name": "Tag Explorer",
|
||||
"icon": "media/dep.svg",
|
||||
"icon": "$(tag)",
|
||||
"contextualTitle": "Tags Explorer"
|
||||
},
|
||||
{
|
||||
"id": "foam-vscode.orphans",
|
||||
"name": "Orphans",
|
||||
"icon": "media/dep.svg",
|
||||
"icon": "$(debug-gripper)",
|
||||
"contextualTitle": "Orphans"
|
||||
},
|
||||
{
|
||||
"id": "foam-vscode.placeholders",
|
||||
"name": "Placeholders",
|
||||
"icon": "media/dep.svg",
|
||||
"icon": "$(debug-disconnect)",
|
||||
"contextualTitle": "Placeholders"
|
||||
}
|
||||
]
|
||||
@@ -126,6 +146,10 @@
|
||||
{
|
||||
"command": "foam-vscode.open-resource",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "foam-vscode.completion-move-cursor",
|
||||
"when": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -144,6 +168,10 @@
|
||||
},
|
||||
{
|
||||
"command": "foam-vscode.open-daily-note",
|
||||
"title": "Foam: Open Today's Note"
|
||||
},
|
||||
{
|
||||
"command": "foam-vscode.open-daily-note-for-date",
|
||||
"title": "Foam: Open Daily Note"
|
||||
},
|
||||
{
|
||||
@@ -193,6 +221,10 @@
|
||||
{
|
||||
"command": "foam-vscode.create-new-template",
|
||||
"title": "Foam: Create New Template"
|
||||
},
|
||||
{
|
||||
"command": "foam-vscode.completion-move-cursor",
|
||||
"title": "Foam: Move cursor after completion"
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
@@ -223,7 +255,7 @@
|
||||
},
|
||||
"foam.edit.linkReferenceDefinitions": {
|
||||
"type": "string",
|
||||
"default": "withoutExtensions",
|
||||
"default": "off",
|
||||
"enum": [
|
||||
"withExtensions",
|
||||
"withoutExtensions",
|
||||
@@ -235,8 +267,8 @@
|
||||
"Disable wikilink definitions generation"
|
||||
]
|
||||
},
|
||||
"foam.links.navigation.enable": {
|
||||
"description": "Enable navigation through links",
|
||||
"foam.links.sync.enable": {
|
||||
"description": "Enable synching links when moving/renaming notes",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
@@ -245,11 +277,6 @@
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"foam.decorations.links.enable": {
|
||||
"description": "Enable decorations for links",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"foam.openDailyNote.onStartup": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
@@ -338,6 +365,11 @@
|
||||
],
|
||||
"description": "Whether or not to navigate to the target daily note when a daily note snippet is selected."
|
||||
},
|
||||
"foam.preview.embedNoteInContainer": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Wrap embedded notes in a container when displayed in preview panel"
|
||||
},
|
||||
"foam.graph.titleMaxLength": {
|
||||
"type": "number",
|
||||
"default": 24,
|
||||
@@ -354,6 +386,10 @@
|
||||
{
|
||||
"command": "foam-vscode.open-daily-note",
|
||||
"key": "alt+d"
|
||||
},
|
||||
{
|
||||
"command": "foam-vscode.open-daily-note-for-date",
|
||||
"key": "alt+h"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -361,49 +397,69 @@
|
||||
"build": "tsc -p ./",
|
||||
"pretest": "yarn build",
|
||||
"test": "node ./out/test/run-tests.js",
|
||||
"lint": "tsdx lint",
|
||||
"pretest:unit": "yarn build",
|
||||
"test:unit": "node ./out/test/run-tests.js --unit",
|
||||
"pretest:e2e": "yarn build",
|
||||
"test:e2e": "node ./out/test/run-tests.js --e2e",
|
||||
"lint": "tsdx lint src",
|
||||
"clean": "rimraf out",
|
||||
"watch": "tsc --build ./tsconfig.json ../foam-core/tsconfig.json --watch",
|
||||
"watch": "tsc --build ./tsconfig.json --watch",
|
||||
"vscode:start-debugging": "yarn clean && yarn watch",
|
||||
"vscode:prepublish": "yarn npm-install && yarn run build",
|
||||
"npm-install": "rimraf node_modules && npm i",
|
||||
"npm-cleanup": "rimraf package-lock.json node_modules && yarn",
|
||||
"package-extension": "npx vsce package && yarn npm-cleanup",
|
||||
"esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node",
|
||||
"vscode:prepublish": "yarn run esbuild-base -- --minify",
|
||||
"package-extension": "npx vsce package --yarn",
|
||||
"install-extension": "code --install-extension ./foam-vscode-$npm_package_version.vsix",
|
||||
"publish-extension-openvsx": "npx ovsx publish foam-vscode-$npm_package_version.vsix -p $OPENVSX_TOKEN",
|
||||
"publish-extension-vscode": "npx vsce publish --packagePath foam-vscode-$npm_package_version.vsix",
|
||||
"publish-extension": "yarn publish-extension-vscode && yarn publish-extension-openvsx && yarn npm-cleanup"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.11.0",
|
||||
"@babel/preset-env": "^7.11.0",
|
||||
"@babel/preset-typescript": "^7.10.4",
|
||||
"@types/dateformat": "^3.0.1",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/lodash": "^4.14.157",
|
||||
"@types/markdown-it": "^12.0.1",
|
||||
"@types/micromatch": "^4.0.1",
|
||||
"@types/node": "^13.11.0",
|
||||
"@types/picomatch": "^2.2.1",
|
||||
"@types/remove-markdown": "^0.1.1",
|
||||
"@types/vscode": "^1.47.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.30.0",
|
||||
"@typescript-eslint/parser": "^2.30.0",
|
||||
"babel-jest": "^26.2.2",
|
||||
"esbuild": "^0.14.45",
|
||||
"eslint": "^6.8.0",
|
||||
"glob": "^7.1.6",
|
||||
"eslint-import-resolver-typescript": "^2.5.0",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-jest": "^25.3.0",
|
||||
"husky": "^4.2.5",
|
||||
"jest": "^26.2.2",
|
||||
"jest-environment-vscode": "^1.0.0",
|
||||
"jest-extended": "^0.11.5",
|
||||
"markdown-it": "^12.0.4",
|
||||
"rimraf": "^3.0.2",
|
||||
"ts-jest": "^26.4.4",
|
||||
"typescript": "^3.8.3",
|
||||
"vscode-test": "^1.3.0"
|
||||
"tsdx": "^0.13.2",
|
||||
"tslib": "^2.0.0",
|
||||
"typescript": "^3.9.5",
|
||||
"vscode-test": "^1.3.0",
|
||||
"wait-for-expect": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"dateformat": "^3.0.3",
|
||||
"foam-core": "^0.15.0",
|
||||
"detect-newline": "^3.1.0",
|
||||
"fast-array-diff": "^1.0.1",
|
||||
"github-slugger": "^1.4.0",
|
||||
"glob": "^7.1.6",
|
||||
"gray-matter": "^4.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-it-regex": "^0.2.0",
|
||||
"micromatch": "^4.0.2",
|
||||
"remove-markdown": "^0.3.0"
|
||||
"remark-frontmatter": "^2.0.0",
|
||||
"remark-parse": "^8.0.2",
|
||||
"remark-wiki-link": "^0.0.4",
|
||||
"remove-markdown": "^0.3.0",
|
||||
"replace-ext": "^2.0.0",
|
||||
"title-case": "^3.0.2",
|
||||
"unified": "^9.0.0",
|
||||
"unist-util-visit": "^2.0.2",
|
||||
"yaml": "^1.10.0"
|
||||
}
|
||||
}
|
||||
|
||||
36
packages/foam-vscode/src/core/.eslintrc.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"name": "vscode",
|
||||
"message": "Core submodule must not depend on VS Code."
|
||||
}
|
||||
]
|
||||
// Ideally we would also prevent the core module from depending on other modules
|
||||
// but I have been struggling to get it to work.
|
||||
// For future reference, below are some configurations I think should achieve this
|
||||
// (but I couldn't manage to get working).
|
||||
//
|
||||
// "import/no-internal-modules": [
|
||||
// "error",
|
||||
// {
|
||||
// "allow": ["./src/core"]
|
||||
// }
|
||||
// ]
|
||||
// "import/no-restricted-paths": [
|
||||
// "error",
|
||||
// {
|
||||
// "zones": [
|
||||
// {
|
||||
// "target": "./src/core",
|
||||
// "from": "./src/(!core)",
|
||||
// "message": "Core module can't have outside dependencies."
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// "import/no-relative-parent-imports": "error"
|
||||
// note: https://github.com/import-js/eslint-plugin-import/issues/1610
|
||||
}
|
||||
}
|
||||