mirror of
https://github.com/foambubble/foam.git
synced 2026-01-11 06:58:11 -05:00
Compare commits
38 Commits
sort-link-
...
spike/link
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08cfbed2ba | ||
|
|
26b162d540 | ||
|
|
3c5a81e7d6 | ||
|
|
9a3e2a0b10 | ||
|
|
49c6da07f0 | ||
|
|
75431c89ba | ||
|
|
9e064be3f0 | ||
|
|
2b3f351330 | ||
|
|
1c01c266d9 | ||
|
|
2501638a55 | ||
|
|
20ae1f2ad9 | ||
|
|
a94110898b | ||
|
|
3191d7ddf3 | ||
|
|
ab097e10d7 | ||
|
|
d09ccdbd29 | ||
|
|
df3b657d58 | ||
|
|
f1424c3a73 | ||
|
|
086884ff5c | ||
|
|
05cbdc1c04 | ||
|
|
5b423852d6 | ||
|
|
e1ac54c7e2 | ||
|
|
8bbf9727f6 | ||
|
|
debe94882f | ||
|
|
639edafe52 | ||
|
|
d7425f88e4 | ||
|
|
99d49a0120 | ||
|
|
e9150fb8ea | ||
|
|
57c34db6df | ||
|
|
3bdf90c8b4 | ||
|
|
566d85ec62 | ||
|
|
be704d7494 | ||
|
|
3c3da97190 | ||
|
|
0cdb875221 | ||
|
|
f2efd14186 | ||
|
|
b86edc4660 | ||
|
|
652dc7d845 | ||
|
|
d5fd5410f0 | ||
|
|
efb913028f |
192
.all-contributorsrc
Normal file
192
.all-contributorsrc
Normal file
@@ -0,0 +1,192 @@
|
||||
{
|
||||
"projectName": "foam",
|
||||
"projectOwner": "foambubble",
|
||||
"repoType": "github",
|
||||
"repoHost": "https://github.com",
|
||||
"files": [
|
||||
"docs/index.md",
|
||||
"readme.md"
|
||||
],
|
||||
"imageSize": 60,
|
||||
"commit": false,
|
||||
"commitConvention": "none",
|
||||
"contributors": [
|
||||
{
|
||||
"login": "jevakallio",
|
||||
"name": "Jani Eväkallio",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1203949?v=4",
|
||||
"profile": "https://jevakallio.dev/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jsjoeio",
|
||||
"name": "Joe Previte",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/3806031?v=4",
|
||||
"profile": "https://joeprevite.com/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "riccardoferretti",
|
||||
"name": "Riccardo",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/457005?v=4",
|
||||
"profile": "https://github.com/riccardoferretti",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jojanaho",
|
||||
"name": "Janne Ojanaho",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/2180090?v=4",
|
||||
"profile": "http://ojanaho.com/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "paulshen",
|
||||
"name": "Paul Shen",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/2266187?v=4",
|
||||
"profile": "http://bypaulshen.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "coffenbacher",
|
||||
"name": "coffenbacher",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/245867?v=4",
|
||||
"profile": "https://github.com/coffenbacher",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mathieudutour",
|
||||
"name": "Mathieu Dutour",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/3254314?v=4",
|
||||
"profile": "https://mathieu.dutour.me/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "presidentelect",
|
||||
"name": "Michael Hansen",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/1242300?v=4",
|
||||
"profile": "https://github.com/presidentelect",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dnadlinger",
|
||||
"name": "David Nadlinger",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/19335?v=4",
|
||||
"profile": "http://klickverbot.at/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MrCordeiro",
|
||||
"name": "Fernando",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/20598571?v=4",
|
||||
"profile": "https://pluckd.co/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jfgonzalez7",
|
||||
"name": "Juan Gonzalez",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/58857736?v=4",
|
||||
"profile": "https://github.com/jfgonzalez7",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "louiechristie",
|
||||
"name": "Louie Christie",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/6807448?v=4",
|
||||
"profile": "http://www.louiechristie.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SuperSandro2000",
|
||||
"name": "Sandro",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/7258858?v=4",
|
||||
"profile": "https://supersandro.de/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Skn0tt",
|
||||
"name": "Simon Knott",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/14912729?v=4",
|
||||
"profile": "https://github.com/Skn0tt",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "styfle",
|
||||
"name": "Steven",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/229881?v=4",
|
||||
"profile": "https://styfle.dev/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Georift",
|
||||
"name": "Tim",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/859430?v=4",
|
||||
"profile": "https://github.com/Georift",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sauravkhdoolia",
|
||||
"name": "Saurav Khdoolia",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/34188267?v=4",
|
||||
"profile": "https://github.com/sauravkhdoolia",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "anku255",
|
||||
"name": "Ankit Tiwari",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/22813027?v=4",
|
||||
"profile": "https://anku.netlify.com/",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ayushbaweja",
|
||||
"name": "Ayush Baweja",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/44344063?v=4",
|
||||
"profile": "https://github.com/ayushbaweja",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7
|
||||
}
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,7 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
.vscode-test/
|
||||
*.tsbuildinfo
|
||||
*.vsix
|
||||
*.log
|
||||
dist
|
||||
|
||||
9
.vscode/launch.json
vendored
9
.vscode/launch.json
vendored
@@ -35,6 +35,15 @@
|
||||
"${workspaceFolder}/packages/foam-vscode/out/test/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "Build foam-vscode"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Workspace Manager tests",
|
||||
"program": "${workspaceFolder}/node_modules/tsdx/dist/index.js",
|
||||
"args": ["test"],
|
||||
"cwd": "${workspaceFolder}/packages/foam-core",
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,9 +8,11 @@ layout: default
|
||||
|
||||
<script type="text/javascript">
|
||||
// Hack: Replace page-link with "Page Title"
|
||||
document.querySelectorAll(".markdown-body a[title]").forEach((a) => {
|
||||
a.innerText = a.title;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".markdown-body a[title]:not([href^=http])")
|
||||
.forEach((a) => {
|
||||
a.innerText = a.title;
|
||||
});
|
||||
|
||||
document.querySelectorAll(".github-only").forEach((el) => {
|
||||
el.remove();
|
||||
|
||||
@@ -6,9 +6,11 @@ layout: default
|
||||
|
||||
<script type="text/javascript">
|
||||
// Hack: Replace page-link with "Page Title"
|
||||
document.querySelectorAll(".markdown-body a[title]").forEach((a) => {
|
||||
a.innerText = a.title;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".markdown-body a[title]:not([href^=http])")
|
||||
.forEach((a) => {
|
||||
a.innerText = a.title;
|
||||
});
|
||||
|
||||
document.querySelectorAll(".github-only").forEach((el) => {
|
||||
el.remove();
|
||||
|
||||
BIN
docs/assets/images/demo-backlinks-explorer.gif
Normal file
BIN
docs/assets/images/demo-backlinks-explorer.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 MiB |
BIN
docs/assets/images/prettify-links-demo.gif
Normal file
BIN
docs/assets/images/prettify-links-demo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 430 KiB |
25
docs/automatically-expand-urls-to-well-titled-links.md
Normal file
25
docs/automatically-expand-urls-to-well-titled-links.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Automatically Expand URLs to Well-Titled Links
|
||||
|
||||
Convert a link to a fully-formed Markdown link, using the page's title as a display name. Useful for citations and creating link collections.
|
||||
|
||||
## Required Extensions
|
||||
|
||||
- [Markdown Link Expander](https://marketplace.visualstudio.com/items?itemName=skn0tt.markdown-link-expander) (not included in template)
|
||||
|
||||
Markdown Link Expander will scrape your URL's `<title>` tag to create a nice Markdown-style link.
|
||||
|
||||
## Instructions
|
||||
|
||||

|
||||
|
||||
1. Highlight desired URL
|
||||
2. `Cmd` + `Shift` + `P`
|
||||
3. `Expand URL to Markdown`
|
||||
4. Profit
|
||||
|
||||
Tip: If you paste a lot of links, give the action a custom [key binding](https://code.visualstudio.com/docs/getstarted/keybindings)
|
||||
|
||||
## Feedback and issues
|
||||
|
||||
Have an idea for the extension? [Feel free to share! 🎉](https://github.com/Skn0tt/markdown-link-expander/issues)
|
||||
|
||||
@@ -4,11 +4,11 @@ When using [[wiki-links]], you can find all notes that link to a specific note i
|
||||
|
||||
- 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
|
||||
- You can drag the backlinks pane to a different section in VS Code if you prefer.
|
||||
- You can drag the backlinks pane to a different section in VS Code if you prefer. See: [[make-backlinks-more-prominent]]
|
||||
- Finding backlinks in published Foam workspaces via [[materialized-backlinks]] is on the [[roadmap]] but not yet implemented.
|
||||
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[wiki-links]: wiki-links "Wiki Links"
|
||||
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks"
|
||||
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks (stub)"
|
||||
[roadmap]: roadmap "Roadmap"
|
||||
[//end]: # "Autogenerated link references"
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
- What use cases are we working towards?
|
||||
-[[todo]] User round table
|
||||
|
||||
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[todo]: todo "Todo"
|
||||
[//end]: # "Autogenerated link references"
|
||||
|
||||
7
docs/blog.md
Normal file
7
docs/blog.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Foam Blog
|
||||
|
||||
- [[2020-07-11-three-weeks-in]]
|
||||
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[2020-07-11-three-weeks-in]: blog/2020-07-11-three-weeks-in "Three Weeks In"
|
||||
[//end]: # "Autogenerated link references"
|
||||
3
docs/blog/2020-07-11-three-weeks-in.md
Normal file
3
docs/blog/2020-07-11-three-weeks-in.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Three Weeks In
|
||||
|
||||
Todo
|
||||
@@ -12,7 +12,7 @@ Foam is open to contributions of any kind, including but not limited to code, do
|
||||
- Foam code and documentation live in the monorepo at [foambubble/foam](https://github.com/foambubble/foam/)
|
||||
- [/docs](https://github.com/foambubble/foam/docs): documentation and [[recipes]]
|
||||
- [/packages/foam-vscode](https://github.com/foambubble/foam/tree/master/packages/foam-vscode): the core VSCode plugin
|
||||
- [/packages/foam-workspace-manager](https://github.com/foambubble/foam/tree/master/packages/foam-workspace-manager): Foam workspace automations
|
||||
- [/packages/foam-core](https://github.com/foambubble/foam/tree/master/packages/foam-core): powers the core functionality in Foam across all platforms
|
||||
- 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.
|
||||
@@ -29,9 +29,9 @@ If you're interested in contributing to the VS Code extension (aka `foam-vscode`
|
||||
|
||||
`yarn install`
|
||||
|
||||
3. This project uses [Yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/).`foam-vscode` relies on `foam-workspace-manager`. This means we need to compile it before we do any extension development. From the root, run the command:
|
||||
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:
|
||||
|
||||
`yarn workspace foam-workspace-manager build`
|
||||
`yarn workspace foam-core build`
|
||||
|
||||
4. Now we'll use the launch configuration defined at [`.vscode/launch.json`](https://github.com/foambubble/foam/blob/master/.vscode/launch.json) to start a new extension host of VS Code. From the root, or the `foam-vscode` workspace, press f5.
|
||||
5. In the new extension host of VS Code that launched, open a Foam workspace (e.g. your personal one, or a test-specific one created from foam-template). This is strictly not necessary, but the extension won't auto-run unless it's in a workspace with a `.vscode/foam.json` file.
|
||||
|
||||
15
docs/custom-markdown-preview-styles.md
Normal file
15
docs/custom-markdown-preview-styles.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Custom Markdown Preview Styles
|
||||
|
||||
Visual Studio Code allows you to use your own CSS in the Markdown preview tab.
|
||||
|
||||
## Instructions
|
||||
|
||||
Custom CSS for the Markdown preview can be implemented by using the `"markdown.styles": []` setting in `settings.json`. The stylesheets can either be https URLs or relative paths to local files in the current workspace.
|
||||
|
||||
For example, to load a stylesheet called `Style.css`, we can update `settings.json` with the following line:
|
||||
|
||||
```
|
||||
{
|
||||
"markdown.styles": ["Style.css"]
|
||||
}
|
||||
```
|
||||
@@ -44,7 +44,7 @@ Ideally, `foam-core` will be generic enough that in can be used by third parties
|
||||
- Aliasing, call the same thing by multiple names
|
||||
- Doesn't exist yet, should we support?
|
||||
- The graph should be observable (EventEmitter?) so changes can be applied
|
||||
- EventEmitted w/ cross platform dependency
|
||||
- EventEmitter w/ cross platform dependency
|
||||
- Wonka (staltz's callbag)
|
||||
- Can be a long term goal
|
||||
- Short term fix: Just run the build fully on every change
|
||||
@@ -67,6 +67,7 @@ Here are some example use cases that the core should support. They don't need to
|
||||
- Finding all documents with instances of `[[link]]`
|
||||
- Visualisations
|
||||
- Full text search
|
||||
|
||||
- Or, if search is too expensive/complex, when given a list of file names and line/column positions from VS Code search API, can return the document context (e.g. full paragraph, preceding/following line etc)
|
||||
|
||||
## Collaboration
|
||||
@@ -90,12 +91,17 @@ Here are some example use cases that the core should support. They don't need to
|
||||
|
||||
Useful for knowing what needs to be supported. See [[feature-comparison]].
|
||||
|
||||
## Meeting notes
|
||||
|
||||
- [[foam-core-2020-07-11]]
|
||||
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[workspace-janitor]: workspace-janitor "Workspace Janitor"
|
||||
[cli]: cli "Command Line Interface"
|
||||
[build-vs-assemble]: build-vs-assemble "Build vs Assemble"
|
||||
[wiki-links]: wiki-links "Wiki Links"
|
||||
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks (stub)"
|
||||
[build-vs-assemble]: build-vs-assemble "Build vs Assemble"
|
||||
[feature-comparison]: feature-comparison "Feature comparison"
|
||||
[todo]: todo "Todo"
|
||||
[feature-comparison]: feature-comparison "Feature comparison"
|
||||
[foam-core-2020-07-11]: meeting-notes/foam-core-2020-07-11 "Foam Core 2020-07-11"
|
||||
[//end]: # "Autogenerated link references"
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
# Inbox
|
||||
|
||||
Uncategorised thoughts
|
||||
Uncategorised thoughts, to be added
|
||||
|
||||
- Release notes
|
||||
- Automatic updates
|
||||
- Markdown Preview
|
||||
- It's possible to customise the markdown preview styling. **Maybe make it use local foam workspace styles for live preview of the site??**
|
||||
- See: https://marketplace.visualstudio.com/items?itemName=bierner.markdown-preview-github-styles
|
||||
- Use VS Code [CodeTour](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour) for onboarding
|
||||
- Investigate other similar extensions:
|
||||
- [Unotes](https://marketplace.visualstudio.com/items?itemName=ryanmcalister.Unotes)
|
||||
- [vscode-memo](https://github.com/svsool/vscode-memo)
|
||||
- Foam as a (VS Code) language
|
||||
- Syntax highlighting
|
||||
- Autocompletion
|
||||
|
||||
@@ -99,11 +99,46 @@ If that sounds like something you're interested in, I'd love to have you along o
|
||||
|
||||
## Thanks and attribution
|
||||
|
||||
**Foam** is built by [Jani Eväkallio](https://github.com/jevakallio) ([@jevakallio](https://twitter.com/jevakallio)).
|
||||
**Foam** is built by [Jani Eväkallio](https://github.com/jevakallio) ([@jevakallio](https://twitter.com/jevakallio)), and all our contributors:
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://jevakallio.dev/"><img src="https://avatars1.githubusercontent.com/u/1203949?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jani Eväkallio</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://joeprevite.com/"><img src="https://avatars3.githubusercontent.com/u/3806031?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joe Previte</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/riccardoferretti"><img src="https://avatars3.githubusercontent.com/u/457005?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Riccardo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://ojanaho.com/"><img src="https://avatars0.githubusercontent.com/u/2180090?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Janne Ojanaho</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://bypaulshen.com/"><img src="https://avatars3.githubusercontent.com/u/2266187?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul Shen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=paulshen" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/coffenbacher"><img src="https://avatars0.githubusercontent.com/u/245867?v=4?s=60" width="60px;" alt=""/><br /><sub><b>coffenbacher</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=coffenbacher" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://mathieu.dutour.me/"><img src="https://avatars2.githubusercontent.com/u/3254314?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Mathieu Dutour</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=mathieudutour" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/presidentelect"><img src="https://avatars2.githubusercontent.com/u/1242300?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Michael Hansen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=presidentelect" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://klickverbot.at/"><img src="https://avatars1.githubusercontent.com/u/19335?v=4?s=60" width="60px;" alt=""/><br /><sub><b>David Nadlinger</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dnadlinger" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://pluckd.co/"><img src="https://avatars2.githubusercontent.com/u/20598571?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Fernando</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MrCordeiro" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/jfgonzalez7"><img src="https://avatars3.githubusercontent.com/u/58857736?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Juan Gonzalez</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jfgonzalez7" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://www.louiechristie.com/"><img src="https://avatars1.githubusercontent.com/u/6807448?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Louie Christie</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=louiechristie" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://supersandro.de/"><img src="https://avatars2.githubusercontent.com/u/7258858?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Sandro</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=SuperSandro2000" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/Skn0tt"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Skn0tt" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://styfle.dev/"><img src="https://avatars1.githubusercontent.com/u/229881?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Steven</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=styfle" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/Georift"><img src="https://avatars2.githubusercontent.com/u/859430?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Tim</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Georift" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/sauravkhdoolia"><img src="https://avatars1.githubusercontent.com/u/34188267?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Saurav Khdoolia</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=sauravkhdoolia" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://anku.netlify.com/"><img src="https://avatars1.githubusercontent.com/u/22813027?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ankit Tiwari</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=anku255" title="Documentation">📖</a> <a href="https://github.com/foambubble/foam/commits?author=anku255" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/ayushbaweja"><img src="https://avatars1.githubusercontent.com/u/44344063?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ayush Baweja</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ayushbaweja" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
**Foam** was inspired by [Roam Research](https://roamresearch.com/) and the [Zettelkasten methodology](https://zettelkasten.de/posts/overview)
|
||||
|
||||
**Foam** wouldn't be possible without [Visual Studio Code](https://code.visualstudio.com/) and [GitHub](https://github.com/), and relies heavily on our fantastic open source [[recommended-extensions]] and all their contributors:
|
||||
**Foam** wouldn't be possible without [Visual Studio Code](https://code.visualstudio.com/) and [GitHub](https://github.com/), and relies heavily on our fantastic open source [[recommended-extensions]] and all their contributors!
|
||||
|
||||
## License
|
||||
|
||||
|
||||
24
docs/make-backlinks-more-prominent.md
Normal file
24
docs/make-backlinks-more-prominent.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Make Backlinks More Prominent
|
||||
|
||||
One of the most most common early feature requests in Foam is to make the Markdown Notes Backlinks Explorer more prominent.
|
||||
|
||||
At the moment, you can drag the explorer pane to your bottom pane, and either show it side by side with another pane, or have take the full width of the editor:
|
||||
|
||||

|
||||
|
||||
In the future we'll want to improve this feature by
|
||||
|
||||
- [[materialized-backlinks]]
|
||||
- Providing more context around back link reference
|
||||
- Could be done by tweaking Markdown Notes slightly. Maybe a user setting?
|
||||
- Make back links editable using [VS Code Search Editors](https://code.visualstudio.com/updates/v1_43#_search-editors)
|
||||
- [Suggested by @Jash on Discord](https://discordapp.com/channels/729975036148056075/729978910363746315/730999992419876956)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[materialized-backlinks]: materialized-backlinks "Materialized Backlinks (stub)"
|
||||
[//end]: # "Autogenerated link references"
|
||||
126
docs/meeting-notes/foam-core-2020-07-11.md
Normal file
126
docs/meeting-notes/foam-core-2020-07-11.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Foam Core 2020-07-11
|
||||
|
||||
Present: @jevakallio, @riccardoferretti
|
||||
|
||||
### Tests
|
||||
|
||||
- How do we know this approach works?
|
||||
- Supports renaming
|
||||
- Supports searching with (attribute-x)
|
||||
- Find dead links
|
||||
|
||||
### Getting started
|
||||
|
||||
- Land work to master
|
||||
- Create a foam-core package
|
||||
-
|
||||
|
||||
### Open questions
|
||||
|
||||
- How should writing to files work
|
||||
- What if affected notes have unsaved changes
|
||||
|
||||
### Graph methods
|
||||
|
||||
- get all
|
||||
- search by
|
||||
- tag
|
||||
- free text
|
||||
- [[todo]]: how do vs code search editors work? are they pluggable? what do they need?
|
||||
- find dead links
|
||||
- for linters
|
||||
- serialize/toJSON (for visualizers)
|
||||
- subscribe to changes
|
||||
- find if a link exists (and which link) in a given row / column position + return it's start and end position - this would probably be needed e.g. to CTRL-hovering to work properly
|
||||
|
||||
### Node methods
|
||||
|
||||
- rename node and all links to that node
|
||||
- get links
|
||||
- forward links (for link lists)
|
||||
- backlinks (with surrounding context)
|
||||
|
||||
### Node definition
|
||||
|
||||
What do we need the node (and edge metadata) to contain:
|
||||
|
||||
- `id`: tbd
|
||||
- should be unique, needs some kind of unique gen function
|
||||
- should be reconstructable even if links are not updated every time
|
||||
- what happens during rename? is reparenting the graph going to be hard?
|
||||
- do id's need to be persistent, or can we create them per in-memory session, keep them stable despite renames, and then next session generate a new id?
|
||||
- Ideally should be a path to file, so it's easy to look up from the graph by id for renaming
|
||||
- `type`: Note | Image | etc
|
||||
- `title`: can be read from markdown title or frontmatter metadata
|
||||
- `path`: full path to file, relative to workspace (graph) root
|
||||
- `links`:
|
||||
- `id`: File to link to
|
||||
- `text`: The link label
|
||||
- `type` markdown | mediawiki | image | http
|
||||
- `section`: : Anchor link to a heading in target note, if we want to add support for linking to sections
|
||||
- `block` (ref)
|
||||
- Positional data from AST?
|
||||
- `tags`
|
||||
|
||||
### Markdown layer
|
||||
|
||||
- `source`: raw markdown (rename?)
|
||||
- `ast`: raw markdown ast
|
||||
- `checksum`: if we do caching
|
||||
|
||||
### Link text
|
||||
|
||||
// some-file.md
|
||||
// # Some File
|
||||
|
||||
Write -> Store on disk
|
||||
[[Some File]] -> [Some File](some-file.md)
|
||||
|
||||
Editing
|
||||
[Some File](some-file.md)
|
||||
|
||||
On disk (could be solved by migration)
|
||||
[[some-file]]
|
||||
[[Some File]]
|
||||
|
||||
- docs/index.md -> Index
|
||||
- notes/index.md -> Index
|
||||
|
||||
[[Index]]
|
||||
[[Index | notes/index.md]]
|
||||
|
||||
[Index] docs/index.md
|
||||
[Index | notes/index.md]: notes/index.md
|
||||
|
||||
[[Some File | path/to/some-file.md]]
|
||||
|
||||
Do we apply any constraints:
|
||||
|
||||
- `[[file-name-without-extension]]`
|
||||
- `[[file-name-with-extension.md]]`
|
||||
- `[[Title Cased File Name]]`
|
||||
|
||||
Not supported by Markdown Notes:
|
||||
|
||||
- `[[path/to/file-name.md]]` - Just use markdown links
|
||||
- `[[Target Note Title]]`
|
||||
|
||||
Issues:
|
||||
|
||||
- Name clashes in directories
|
||||
- Name clashes between extensions
|
||||
- Renaming
|
||||
- Change filename/title needs to reflect everywhere
|
||||
- Orphaning
|
||||
|
||||
- If we can't rely on in-memory process to rename things correctly while changes happen (e.g. file is renamed, moved, deleted, or titled) <ref id="1" />
|
||||
|
||||
Solving this issue is necessarily heuristic. We could try to write smart solutions, plus a linter for orphans
|
||||
|
||||
How others solve this:
|
||||
|
||||
- Unique ids -- could support optionally as part of file name or front matter metadata. Should not be required.
|
||||
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[todo]: ../todo "Todo"
|
||||
[//end]: # "Autogenerated link references"
|
||||
@@ -1,48 +0,0 @@
|
||||
<p class="github-only">
|
||||
👋 <b>Hello friend! Looks like you're reading this page on GitHub. Please go to the 👉<a href="https://foambubble.github.io/foam">rendered Foam Workspace</a> for an improved experience! </b>
|
||||
</p>
|
||||
|
||||
<p class="github-only">
|
||||
👀<i>This is an early stage project under rapid development. For updates follow <a href="https://twitter.com/jevakallio" target="_blank">@jevakallio</a> on Twitter, or join the <a href="https://discord.gg/rtdZKgj" target="_blank">Foam community Discord</a>! 💬</i>
|
||||
</p>
|
||||
|
||||
# Foam
|
||||
|
||||
**Foam** is a personal knowledge management and sharing system inspired by [Roam Research](https://roamresearch.com/), built on [Visual Studio Code](https://code.visualstudio.com/) and [GitHub](https://github.com/).
|
||||
|
||||
You can use **Foam** for organising your research, keeping re-discoverable notes, writing long-form content and, optionally, publishing it to the web.
|
||||
|
||||
**Foam** is free, open source, and extremely extensible to suit your personal workflow. You own the information you create with Foam, and you're free to share it, and collaborate on it with anyone you want.
|
||||
|
||||
## How do I use Foam?
|
||||
|
||||
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](https://foambubble.github.io/foam#getting-started) guide .
|
||||
2. Write your thoughts in markdown documents (I like to call them **Bubbles**, but that might be more than a little twee). These documents should be atomic: Put things that belong together into a single document, and limit its content to that single topic. ([source](https://zettelkasten.de/posts/overview/#principles))
|
||||
3. Use Foam's shortcuts and autocompletions to link your thoughts together with `[[wiki-links]]`, and navigate between them to explore your knowledge graph.
|
||||
4. Get an overview of your **Foam** workspace using the [[Graph Visualisation](https://foambubble.github.io/foam/graph-visualisation)], and discover relationships between your thoughts with the use of [[Backlinking](https://foambubble.github.io/foam/backlinking)].
|
||||
|
||||

|
||||
|
||||
Foam is a like a bathtub: _What you get out of it depends on what you put into it._
|
||||
|
||||
## Learn more
|
||||
|
||||
**Head over to the 👉[Published version of this Foam workspace](https://foambubble.github.io/foam#whats-in-a-foam)** to see Foam in action and read the rest of the documentation!
|
||||
|
||||
Quick links to next documentation sections
|
||||
|
||||
- [What's in a Foam?](https://foambubble.github.io/foam#whats-in-a-foam)
|
||||
- [Getting started](https://foambubble.github.io/foam#getting-started)
|
||||
- [Features](https://foambubble.github.io/foam#features)
|
||||
- [Call To Adventure](https://foambubble.github.io/foam#call-to-adventure)
|
||||
- [Thanks and attribution](https://foambubble.github.io/foam#thanks-and-attribution)
|
||||
|
||||
## License
|
||||
|
||||
Foam is licensed under the [MIT license](license).
|
||||
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[wiki-links]: wiki-links "Wiki Links"
|
||||
[//end]: # "Autogenerated link references"
|
||||
@@ -36,6 +36,8 @@ Guides, tips and strategies for getting the most out of your Foam workspace!
|
||||
- Link documents with [[wiki-links]]
|
||||
- Use shortcuts for [[creating-new-notes]]
|
||||
- Draw [[diagrams-in-markdown]]
|
||||
- Prettify your links, [[automatically-expand-urls-to-well-titled-links]]
|
||||
- Style your environment with [[custom-markdown-preview-styles]]
|
||||
- [Markdown All-in-One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) features [[todo]] [[good-first-task]]
|
||||
- Manage checklists
|
||||
- Automatic Table of Contents
|
||||
@@ -92,8 +94,9 @@ _See [[contribution-guide]] and [[how-to-write-recipes]]._
|
||||
[wiki-links]: wiki-links "Wiki Links"
|
||||
[creating-new-notes]: creating-new-notes "Creating New Notes"
|
||||
[diagrams-in-markdown]: diagrams-in-markdown "Diagrams in Markdown"
|
||||
[good-first-task]: good-first-task "Good First Task"
|
||||
[automatically-expand-urls-to-well-titled-links]: automatically-expand-urls-to-well-titled-links "Automatically Expand URLs to Well-Titled Links"
|
||||
[custom-markdown-preview-styles]: custom-markdown-preview-styles "Custom Markdown Preview Styles"
|
||||
[good-first-task]: good-first-task "Good First Task"
|
||||
[git-integration]: git-integration "Git integration"
|
||||
[github-pages]: github-pages "Github Pages"
|
||||
[customising-styles]: customising-styles "Customising Styles"
|
||||
|
||||
@@ -38,6 +38,7 @@ If a roadmap item is a stub, **consider** opening a [GitHub issue](https://githu
|
||||
- [[daily-notes]]
|
||||
- [[block-references]]
|
||||
- [[improved-backlinking]]
|
||||
- UX: [[make-backlinks-more-prominent]]
|
||||
- [[materialized-backlinks]]
|
||||
- [[automatic-git-syncing]]
|
||||
- [[git-flows-for-teams]]
|
||||
@@ -81,7 +82,7 @@ If a roadmap item is a stub, **consider** opening a [GitHub issue](https://githu
|
||||
[build-vs-assemble]: build-vs-assemble "Build vs Assemble"
|
||||
[recipes]: recipes "Recipes"
|
||||
[cli]: cli "Command Line Interface"
|
||||
[workspace-janitor]: workspace-janitor "Workspace Janitor (stub)"
|
||||
[workspace-janitor]: workspace-janitor "Workspace Janitor"
|
||||
[contribution-guide]: contribution-guide "Contribution Guide"
|
||||
[improve-default-workspace-settings]: improve-default-workspace-settings "Improve Default Workspace Settings (stub)"
|
||||
[git-integration]: git-integration "Git integration"
|
||||
@@ -109,4 +110,5 @@ If a roadmap item is a stub, **consider** opening a [GitHub issue](https://githu
|
||||
[migrating-from-obsidian]: migrating-from-obsidian "Migrating from Obsidian (stub)"
|
||||
[foam-linter]: foam-linter "Foam Linter (stub)"
|
||||
[refactoring-via-language-server-protocol]: refactoring-via-language-server-protocol "Refactoring via Language Server Protocol (stub)"
|
||||
[//end]: # "Autogenerated link references"
|
||||
[make-backlinks-more-prominent]: make-backlinks-more-prominent "Make Backlinks More Prominent"
|
||||
[//end]: # "Autogenerated link references"
|
||||
8
lerna.json
Normal file
8
lerna.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.2.0"
|
||||
}
|
||||
24
package.json
24
package.json
@@ -1,13 +1,33 @@
|
||||
{
|
||||
"name": "foam",
|
||||
"version": "0.0.1",
|
||||
"version": "0.2.0",
|
||||
"description": "Foam",
|
||||
"repository": "git@github.com:foambubble/foam.git",
|
||||
"author": "Jani Eväkallio <jani.evakallio@gmail.com>",
|
||||
"license": "MIT",
|
||||
"private": "true",
|
||||
"workspaces": ["packages/*"],
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"watch": "yarn workspace foam-vscode watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"all-contributors-cli": "^6.16.1",
|
||||
"lerna": "^3.22.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "tsdx lint"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 80,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ $ npm install -g foam-cli
|
||||
$ foam COMMAND
|
||||
running command...
|
||||
$ foam (-v|--version|version)
|
||||
foam-cli/0.1.0 darwin-x64 node-v12.18.0
|
||||
foam-cli/0.2.0 darwin-x64 node-v12.18.0
|
||||
$ foam --help [COMMAND]
|
||||
USAGE
|
||||
$ foam COMMAND
|
||||
@@ -49,7 +49,7 @@ EXAMPLE
|
||||
hello world from ./src/hello.ts!
|
||||
```
|
||||
|
||||
_See code: [src/commands/hello.ts](https://github.com/foambubble/foam/blob/v0.1.0/src/commands/hello.ts)_
|
||||
_See code: [src/commands/hello.ts](https://github.com/foambubble/foam/blob/v0.2.0/src/commands/hello.ts)_
|
||||
|
||||
## `foam help [COMMAND]`
|
||||
|
||||
@@ -73,5 +73,5 @@ _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.1.0
|
||||
|
||||
- Run `yarn` somewhere in workspace (ideally root, see [yarn workspace docs](https://classic.yarnpkg.com/en/docs/workspaces/)
|
||||
- This will automatically symlink all package directories so you're using the local copy
|
||||
- In `packages/foam-workspace-manager`, run `yarn start` to rebuild the library on every change
|
||||
- In `packages/foam-core`, run `yarn start` to rebuild the library on every change
|
||||
- In `packages/foam-cli`, make changes and run with `yarn run cli`. This should use latest workspace manager changes.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "foam-cli",
|
||||
"description": "Foam CLI",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"author": "Jani Eväkallio @jevakallio",
|
||||
"bin": {
|
||||
"foam": "./bin/run"
|
||||
@@ -11,7 +11,6 @@
|
||||
"@oclif/command": "^1",
|
||||
"@oclif/config": "^1",
|
||||
"@oclif/plugin-help": "^3",
|
||||
"foam-workspace-manager": "^0.1.1",
|
||||
"tslib": "^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -21,10 +20,14 @@
|
||||
"eslint": "^5.13",
|
||||
"eslint-config-oclif": "^3.1",
|
||||
"eslint-config-oclif-typescript": "^0.1",
|
||||
"foam-core": "^0.2.0",
|
||||
"globby": "^10",
|
||||
"ts-node": "^8",
|
||||
"typescript": "^3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"foam-core": "^0.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Command, flags} from '@oclif/command'
|
||||
import { WorkspaceManager } from 'foam-workspace-manager';
|
||||
import { NoteGraph } from 'foam-core';
|
||||
export default class Hello extends Command {
|
||||
static description = 'describe the command here'
|
||||
|
||||
@@ -23,19 +23,19 @@ hello world from ./src/hello.ts!
|
||||
const {args, flags} = this.parse(Hello)
|
||||
const name = flags.name ?? 'world'
|
||||
|
||||
const wm = new WorkspaceManager('./foo');
|
||||
wm.addNoteFromMarkdown('page-a.md', `
|
||||
# Page A
|
||||
## Section
|
||||
- [[page-b]]
|
||||
- [[page-c]];
|
||||
`);
|
||||
// const wm = new NoteGraph();
|
||||
// wm.addNoteFromMarkdown('page-a.md', `
|
||||
// # Page A
|
||||
// ## Section
|
||||
// - [[page-b]]
|
||||
// - [[page-c]];
|
||||
// `);
|
||||
|
||||
wm.addNoteFromMarkdown('page-a.md', `
|
||||
# Page B
|
||||
This references [[page-a]]`);
|
||||
// wm.addNoteFromMarkdown('page-a.md', `
|
||||
// # Page B
|
||||
// This references [[page-a]]`);
|
||||
|
||||
console.log(wm.getNoteWithLinks('page-a'));
|
||||
// console.log(wm.getNoteWithLinks('page-a'));
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
],
|
||||
"references": [{ "path": "../foam-core" }]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Foam Workspace Manager
|
||||
# Foam Core
|
||||
|
||||
Repository for tooling user for managing Foam workspaces
|
||||
Repository for tooling used by the other modules
|
||||
|
||||
## Local Development
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
{
|
||||
"version": "0.1.1",
|
||||
"name": "foam-core",
|
||||
"author": "Jani Eväkallio",
|
||||
"repository": "https://github.com/foambubble/foam",
|
||||
"version": "0.2.0",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
"dist"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "tsdx watch",
|
||||
"build": "tsdx build",
|
||||
@@ -17,33 +14,22 @@
|
||||
"lint": "tsdx lint",
|
||||
"prepare": "tsdx build"
|
||||
},
|
||||
"peerDependencies": {},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "tsdx lint"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 80,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5"
|
||||
},
|
||||
"name": "foam-workspace-manager",
|
||||
"author": "Jani Eväkallio",
|
||||
"repository": "https://github.com/foambubble/foam",
|
||||
"module": "dist/foam-workspace-manager.esm.js",
|
||||
"devDependencies": {
|
||||
"@types/graphlib": "^2.1.6",
|
||||
"@types/lodash": "^4.14.157",
|
||||
"husky": "^4.2.5",
|
||||
"tsdx": "^0.13.2",
|
||||
"tslib": "^2.0.0",
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/lodash": "^4.14.157",
|
||||
"graphlib": "^2.1.8",
|
||||
"lodash": "^4.17.15",
|
||||
"remark-parse": "^8.0.2",
|
||||
"remark-wiki-link": "^0.0.4",
|
||||
"unified": "^9.0.0"
|
||||
}
|
||||
"unified": "^9.0.0",
|
||||
"unist-util-visit": "^2.0.2"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts"
|
||||
}
|
||||
22
packages/foam-core/src/index.ts
Normal file
22
packages/foam-core/src/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { NoteGraph, Note, NoteLink } from './note-graph';
|
||||
|
||||
export {
|
||||
createNoteFromMarkdown,
|
||||
createMarkdownReferences,
|
||||
} from './markdown-provider';
|
||||
|
||||
export { NoteGraph, Note, NoteLink }
|
||||
|
||||
export interface FoamConfig {
|
||||
// TODO
|
||||
}
|
||||
|
||||
export interface Foam {
|
||||
notes: NoteGraph
|
||||
// config: FoamConfig
|
||||
}
|
||||
|
||||
export const createFoam = (config: FoamConfig) => ({
|
||||
notes: new NoteGraph(),
|
||||
config: config,
|
||||
})
|
||||
95
packages/foam-core/src/markdown-provider.ts
Normal file
95
packages/foam-core/src/markdown-provider.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import unified from 'unified';
|
||||
import markdownParse from 'remark-parse';
|
||||
import wikiLinkPlugin from 'remark-wiki-link';
|
||||
import visit, { CONTINUE, EXIT } from 'unist-util-visit';
|
||||
import { Node, Parent } from 'unist';
|
||||
import * as path from 'path';
|
||||
import { Note, NoteLink, NoteGraph } from './note-graph';
|
||||
import { dropExtension } from './utils';
|
||||
|
||||
let processor: unified.Processor | null = null;
|
||||
|
||||
function parse(markdown: string): Node {
|
||||
processor =
|
||||
processor ||
|
||||
unified()
|
||||
.use(markdownParse, { gfm: true })
|
||||
.use(wikiLinkPlugin);
|
||||
return processor.parse(markdown);
|
||||
}
|
||||
|
||||
export function createNoteFromMarkdown(uri: string, markdown: string): Note {
|
||||
const filename = path.basename(uri);
|
||||
const id = path.parse(filename).name;
|
||||
const tree = parse(markdown);
|
||||
let title = id;
|
||||
visit(tree, node => {
|
||||
if (node.type === 'heading' && node.depth === 1) {
|
||||
title = ((node as Parent)!.children[0].value as string) || title;
|
||||
}
|
||||
return title === id ? CONTINUE : EXIT;
|
||||
});
|
||||
const links: NoteLink[] = [];
|
||||
visit(tree, node => {
|
||||
if (node.type === 'wikiLink') {
|
||||
links.push({
|
||||
to: node.value as string,
|
||||
text: node.value as string,
|
||||
position: node.position!
|
||||
});
|
||||
}
|
||||
});
|
||||
return new Note(id, title, links, uri, markdown);
|
||||
}
|
||||
|
||||
interface MarkdownReference {
|
||||
linkText: string;
|
||||
wikiLink: string;
|
||||
pageTitle: string;
|
||||
}
|
||||
|
||||
export function createMarkdownReferences(
|
||||
graph: NoteGraph,
|
||||
noteId: string
|
||||
): MarkdownReference[] {
|
||||
const source = graph.getNote(noteId);
|
||||
|
||||
// Should never occur since we're already in a file,
|
||||
// but better safe than sorry.
|
||||
if (!source) {
|
||||
console.warn(
|
||||
`Note ${noteId} was not added to NoteGraph before attempting to generate markdown reference list`
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
return graph
|
||||
.getForwardLinks(noteId)
|
||||
.map(link => {
|
||||
const target = graph.getNote(link.to);
|
||||
|
||||
// We are dropping links to non-existent notes here,
|
||||
// but int the future we may want to surface these too
|
||||
if (!target) {
|
||||
console.log(
|
||||
`Link '${link.to}' in '${noteId}' points to a non-existing note.`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const relativePath = path.relative(
|
||||
path.dirname(source.path),
|
||||
target.path
|
||||
);
|
||||
const relativePathWithoutExtension = dropExtension(relativePath);
|
||||
|
||||
// [wiki-link-text]: wiki-link "Page title"
|
||||
return {
|
||||
linkText: link.to,
|
||||
wikiLink: relativePathWithoutExtension,
|
||||
pageTitle: target.title,
|
||||
};
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort() as MarkdownReference[];
|
||||
}
|
||||
93
packages/foam-core/src/note-graph.ts
Normal file
93
packages/foam-core/src/note-graph.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { Graph, Edge } from 'graphlib';
|
||||
import { Position } from 'unist';
|
||||
|
||||
type ID = string;
|
||||
|
||||
export interface Link {
|
||||
from: ID;
|
||||
to: ID;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface NoteLink {
|
||||
to: ID;
|
||||
text: string;
|
||||
position: Position;
|
||||
}
|
||||
|
||||
export class Note {
|
||||
public id: ID;
|
||||
public title: string;
|
||||
public source: string;
|
||||
public path: string;
|
||||
public links: NoteLink[];
|
||||
|
||||
constructor(
|
||||
id: ID,
|
||||
title: string,
|
||||
links: NoteLink[],
|
||||
path: string,
|
||||
source: string
|
||||
) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.source = source;
|
||||
this.path = path;
|
||||
this.links = links;
|
||||
}
|
||||
}
|
||||
|
||||
export class NoteGraph {
|
||||
private graph: Graph;
|
||||
|
||||
constructor() {
|
||||
this.graph = new Graph();
|
||||
}
|
||||
|
||||
public setNote(note: Note) {
|
||||
if (this.graph.hasNode(note.id)) {
|
||||
(this.graph.outEdges(note.id) || []).forEach(edge => {
|
||||
this.graph.removeEdge(edge);
|
||||
});
|
||||
}
|
||||
this.graph.setNode(note.id, note);
|
||||
note.links.forEach(link => {
|
||||
this.graph.setEdge(note.id, link.to, link.text);
|
||||
});
|
||||
}
|
||||
|
||||
public getNotes(): Note[] {
|
||||
return this.graph.nodes().map(id => this.graph.node(id));
|
||||
}
|
||||
|
||||
public getNote(noteId: ID): Note | void {
|
||||
if (this.graph.hasNode(noteId)) {
|
||||
return this.graph.node(noteId);
|
||||
}
|
||||
throw new Error(`Note with ID [${noteId}] not found`);
|
||||
}
|
||||
|
||||
public getAllLinks(noteId: ID): Link[] {
|
||||
return (this.graph.nodeEdges(noteId) || []).map(edge =>
|
||||
convertEdgeToLink(edge)
|
||||
);
|
||||
}
|
||||
|
||||
public getForwardLinks(noteId: ID): Link[] {
|
||||
return (this.graph.outEdges(noteId) || []).map(edge =>
|
||||
convertEdgeToLink(edge)
|
||||
);
|
||||
}
|
||||
|
||||
public getBacklinks(noteId: ID): Link[] {
|
||||
return (this.graph.inEdges(noteId) || []).map(edge =>
|
||||
convertEdgeToLink(edge)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const convertEdgeToLink = (edge: Edge): Link => ({
|
||||
from: edge.v,
|
||||
to: edge.w,
|
||||
text: edge.name || edge.w,
|
||||
});
|
||||
5
packages/foam-core/src/utils.ts
Normal file
5
packages/foam-core/src/utils.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export function dropExtension(path: string): string {
|
||||
const parts = path.split('.');
|
||||
parts.pop();
|
||||
return parts.join('.');
|
||||
}
|
||||
151
packages/foam-core/test/core.test.ts
Normal file
151
packages/foam-core/test/core.test.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import { NoteGraph, Note } from '../src/note-graph';
|
||||
|
||||
const position = {
|
||||
start: { line: 0, column: 0},
|
||||
end: { line: 0, column: 0}
|
||||
};
|
||||
|
||||
describe('Note graph', () => {
|
||||
it('Adds notes to graph', () => {
|
||||
const graph = new NoteGraph();
|
||||
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
|
||||
graph.setNote(new Note('page-b', 'page-b', [], '/page-b.md', ''));
|
||||
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
|
||||
|
||||
expect(
|
||||
graph
|
||||
.getNotes()
|
||||
.map(n => n.id)
|
||||
.sort()
|
||||
).toEqual(['page-a', 'page-b', 'page-c']);
|
||||
});
|
||||
|
||||
it('Detects forward links', () => {
|
||||
const graph = new NoteGraph();
|
||||
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
|
||||
graph.setNote(
|
||||
new Note(
|
||||
'page-b',
|
||||
'page-b',
|
||||
[{ to: 'page-a', text: 'go', position }],
|
||||
'/page-b.md',
|
||||
''
|
||||
)
|
||||
);
|
||||
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
|
||||
|
||||
expect(
|
||||
graph
|
||||
.getForwardLinks('page-b')
|
||||
.map(link => link.to)
|
||||
.sort()
|
||||
).toEqual(['page-a']);
|
||||
});
|
||||
|
||||
it('Detects backlinks', () => {
|
||||
const graph = new NoteGraph();
|
||||
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
|
||||
graph.setNote(
|
||||
new Note(
|
||||
'page-b',
|
||||
'page-b',
|
||||
[{ to: 'page-a', text: 'go', position }],
|
||||
'/page-b.md',
|
||||
''
|
||||
)
|
||||
);
|
||||
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
|
||||
|
||||
expect(
|
||||
graph
|
||||
.getBacklinks('page-a')
|
||||
.map(link => link.from)
|
||||
.sort()
|
||||
).toEqual(['page-b']);
|
||||
});
|
||||
|
||||
it('Fails when accessing non-existing node', () => {
|
||||
expect(() => {
|
||||
const graph = new NoteGraph();
|
||||
graph.setNote(new Note('page-a', 'page-a', [], '/path-b.md', ''));
|
||||
graph.getNote('non-existing');
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('Allows adding edges to non-existing documents', () => {
|
||||
const graph = new NoteGraph();
|
||||
graph.setNote(
|
||||
new Note(
|
||||
'page-a',
|
||||
'page-a',
|
||||
[{ to: 'non-existing', text: 'does not exist', position }],
|
||||
'/path-b.md',
|
||||
''
|
||||
)
|
||||
);
|
||||
expect(graph.getNote('non-existing')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('Updates links when modifying note', () => {
|
||||
const graph = new NoteGraph();
|
||||
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
|
||||
graph.setNote(
|
||||
new Note(
|
||||
'page-b',
|
||||
'page-b',
|
||||
[{ to: 'page-a', text: 'go', position }],
|
||||
'/page-b.md',
|
||||
''
|
||||
)
|
||||
);
|
||||
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
|
||||
|
||||
expect(
|
||||
graph
|
||||
.getForwardLinks('page-b')
|
||||
.map(link => link.to)
|
||||
.sort()
|
||||
).toEqual(['page-a']);
|
||||
expect(
|
||||
graph
|
||||
.getBacklinks('page-a')
|
||||
.map(link => link.from)
|
||||
.sort()
|
||||
).toEqual(['page-b']);
|
||||
expect(
|
||||
graph
|
||||
.getBacklinks('page-c')
|
||||
.map(link => link.from)
|
||||
.sort()
|
||||
).toEqual([]);
|
||||
|
||||
graph.setNote(
|
||||
new Note(
|
||||
'page-b',
|
||||
'page-b',
|
||||
[{ to: 'page-c', text: 'go', position }],
|
||||
'/path-2b.md',
|
||||
''
|
||||
)
|
||||
);
|
||||
|
||||
expect(
|
||||
graph
|
||||
.getForwardLinks('page-b')
|
||||
.map(link => link.to)
|
||||
.sort()
|
||||
).toEqual(['page-c']);
|
||||
expect(
|
||||
graph
|
||||
.getBacklinks('page-a')
|
||||
.map(link => link.from)
|
||||
.sort()
|
||||
).toEqual([]);
|
||||
expect(
|
||||
graph
|
||||
.getBacklinks('page-c')
|
||||
.map(link => link.from)
|
||||
.sort()
|
||||
).toEqual(['page-b']);
|
||||
});
|
||||
});
|
||||
50
packages/foam-core/test/markdown-provider.test.ts
Normal file
50
packages/foam-core/test/markdown-provider.test.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { createNoteFromMarkdown } from '../src/markdown-provider';
|
||||
import { NoteGraph } from '../src/note-graph';
|
||||
|
||||
const pageA = `
|
||||
# Page A
|
||||
|
||||
## Section
|
||||
- [[page-b]]
|
||||
- [[page-c]]
|
||||
`;
|
||||
|
||||
const pageB = `
|
||||
# Page B
|
||||
|
||||
This references [[page-a]]`;
|
||||
|
||||
const pageC = `
|
||||
# Page C
|
||||
`;
|
||||
|
||||
describe('Markdown loader', () => {
|
||||
it('Converts markdown to notes', () => {
|
||||
const graph = new NoteGraph();
|
||||
graph.setNote(createNoteFromMarkdown('page-a', pageA));
|
||||
graph.setNote(createNoteFromMarkdown('page-b', pageB));
|
||||
graph.setNote(createNoteFromMarkdown('page-c', pageC));
|
||||
|
||||
expect(
|
||||
graph
|
||||
.getNotes()
|
||||
.map(n => n.id)
|
||||
.sort()
|
||||
).toEqual(['page-a', 'page-b', 'page-c']);
|
||||
});
|
||||
|
||||
it('Parses wikilinks correctly', () => {
|
||||
const graph = new NoteGraph();
|
||||
graph.setNote(createNoteFromMarkdown('page-a', pageA));
|
||||
graph.setNote(createNoteFromMarkdown('page-b', pageB));
|
||||
graph.setNote(createNoteFromMarkdown('page-c', pageC));
|
||||
|
||||
expect(graph.getBacklinks('page-b').map(link => link.from)).toEqual([
|
||||
'page-a',
|
||||
]);
|
||||
expect(graph.getForwardLinks('page-a').map(link => link.to)).toEqual([
|
||||
'page-b',
|
||||
'page-c',
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,23 +1,30 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"include": ["src", "types"],
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"lib": ["dom", "esnext"],
|
||||
"importHelpers": true,
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"declarationMap": true,
|
||||
// to override config from tsconfig.base.json
|
||||
"outDir": "dist",
|
||||
"rootDir": "./src",
|
||||
// for references
|
||||
"baseUrl": "src",
|
||||
"lib": ["esnext"],
|
||||
|
||||
"module": "esnext",
|
||||
"importHelpers": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"moduleResolution": "node",
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"*": ["src/*", "node_modules/*"]
|
||||
},
|
||||
"jsx": "react",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
// "paths": {
|
||||
// "*": ["src/*", "node_modules/*"]
|
||||
// },
|
||||
// "jsx": "react",
|
||||
},
|
||||
}
|
||||
1
packages/foam-vscode/.gitignore
vendored
1
packages/foam-vscode/.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
out
|
||||
node_modules
|
||||
.vscode-test/
|
||||
*.vsix
|
||||
|
||||
@@ -4,6 +4,22 @@ 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.2.0] - 2020-07-12
|
||||
|
||||
Improvements:
|
||||
|
||||
- Order link references alphabetically to cause smaller diffs
|
||||
- Remove link references when links are removed
|
||||
- Documentation improvements
|
||||
|
||||
Underneath, everything has changed:
|
||||
|
||||
- Published from [Foam monorepo](https://github.com/foambubble/foam)
|
||||
- Rewrote markdown parsing to use unifiedjs AST
|
||||
- Rewrote workspace index to user graphlib graph data structures
|
||||
|
||||
These changes will enable to make more robust and ambitious releases more frequently 🎉
|
||||
|
||||
## [0.1.7] - 2020-07-04
|
||||
|
||||
- Support paths to files in subdirectories
|
||||
@@ -19,7 +35,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
|
||||
## [0.1.4] - 2020-06-25
|
||||
|
||||
- Fix flaky reference block replacement logic that would occasionally leave
|
||||
trailing fragments in the end of the document ([#3](https://github.com/foambubble/foam-vscode/issues/3))
|
||||
trailing fragments in the end of the document ([#3](https://github.com/foambubble/foam-vscode/issues/3))
|
||||
|
||||
## 0.1.3 - 2020-06-25
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# foam-vscode
|
||||
|
||||
[](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode)
|
||||
[](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode)
|
||||
[](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode)
|
||||
[](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode)
|
||||
|
||||
|
||||
**foam-vscode** is the VS Code extension for [Foam](https://foambubble.github.io/foam).
|
||||
|
||||
> ℹ️ foam-vscode doesn't do much on it's own. To learn how to use it, read [Foam documentation](https://foambubble.github.io/foam) and the [Getting started](https://foambubble.github.io/foam/#getting-started) guide.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "Generate markdown reference lists from wikilinks in a workspace",
|
||||
"author": "Jani Eväkallio",
|
||||
"repository": "https://github.com/foambubble/foam",
|
||||
"version": "0.1.7",
|
||||
"version": "0.2.0",
|
||||
"license": "MIT",
|
||||
"publisher": "foam",
|
||||
"engines": {
|
||||
@@ -49,6 +49,6 @@
|
||||
"vscode-test": "^1.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"foam-workspace-manager": "^0.1.1"
|
||||
"foam-core": "^0.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,246 +4,35 @@
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
import {
|
||||
CancellationToken,
|
||||
CodeLens,
|
||||
CodeLensProvider,
|
||||
commands,
|
||||
EndOfLine,
|
||||
ExtensionContext,
|
||||
languages,
|
||||
Range,
|
||||
TextEditor,
|
||||
TextDocument,
|
||||
TextDocumentWillSaveEvent,
|
||||
window,
|
||||
workspace,
|
||||
Position,
|
||||
} from "vscode";
|
||||
import * as fs from "fs";
|
||||
import { workspace, ExtensionContext } from "vscode";
|
||||
|
||||
import { basename, dirname, relative } from "path";
|
||||
import * as ws from "./workspace";
|
||||
|
||||
/**
|
||||
* Workspace config
|
||||
*/
|
||||
const docConfig = { tab: " ", eol: "\r\n" };
|
||||
const mdDocSelector = [
|
||||
{ language: "markdown", scheme: "file" },
|
||||
{ language: "markdown", scheme: "untitled" },
|
||||
];
|
||||
|
||||
function loadDocConfig() {
|
||||
// Load workspace config
|
||||
let activeEditor = window.activeTextEditor;
|
||||
if (!activeEditor) {
|
||||
console.log("Failed to load config, no active editor");
|
||||
return;
|
||||
}
|
||||
|
||||
docConfig.eol = activeEditor.document.eol === EndOfLine.CRLF ? "\r\n" : "\n";
|
||||
|
||||
let tabSize = Number(activeEditor.options.tabSize);
|
||||
let insertSpaces = activeEditor.options.insertSpaces;
|
||||
if (insertSpaces) {
|
||||
docConfig.tab = " ".repeat(tabSize);
|
||||
} else {
|
||||
docConfig.tab = "\t";
|
||||
}
|
||||
}
|
||||
|
||||
const REFERENCE_HEADER = `[//begin]: # "Autogenerated link references for markdown compatibility"`;
|
||||
const REFERENCE_FOOTER = `[//end]: # "Autogenerated link references"`;
|
||||
import { createNoteFromMarkdown, createFoam, FoamConfig } from "foam-core";
|
||||
import { features } from "./features";
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
context.subscriptions.push(
|
||||
commands.registerCommand(
|
||||
"foam-vscode.update-wikilinks",
|
||||
updateReferenceList
|
||||
),
|
||||
workspace.onWillSaveTextDocument(onWillSave),
|
||||
languages.registerCodeLensProvider(
|
||||
mdDocSelector,
|
||||
new WikilinkReferenceCodeLensProvider()
|
||||
)
|
||||
const foamPromise = bootstrap(getConfig());
|
||||
features.forEach(f => {
|
||||
f.activate(context, foamPromise);
|
||||
});
|
||||
}
|
||||
|
||||
const bootstrap = async (config: FoamConfig) => {
|
||||
const files = await workspace.findFiles("**/*");
|
||||
const foam = createFoam(config);
|
||||
await Promise.all(
|
||||
files
|
||||
.filter(f => f.scheme === "file" && f.path.match(/\.(md|mdx|markdown)/i))
|
||||
.map(f => {
|
||||
return fs.promises.readFile(f.fsPath).then(data => {
|
||||
const markdown = (data || "").toString();
|
||||
foam.notes.setNote(createNoteFromMarkdown(f.fsPath, markdown));
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
return foam;
|
||||
};
|
||||
|
||||
async function createReferenceList() {
|
||||
let editor = window.activeTextEditor;
|
||||
if (!editor || !isMdEditor(editor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let refs = await generateReferenceList(editor.document);
|
||||
if (refs && refs.length) {
|
||||
await editor.edit(function (editBuilder) {
|
||||
if (editor) {
|
||||
const spacing = hasEmptyTrailing
|
||||
? docConfig.eol
|
||||
: docConfig.eol + docConfig.eol;
|
||||
|
||||
editBuilder.insert(
|
||||
new Position(editor.document.lineCount, 0),
|
||||
spacing + refs.join(docConfig.eol)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function updateReferenceList() {
|
||||
const editor = window.activeTextEditor;
|
||||
|
||||
if (!editor || !isMdEditor(editor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadDocConfig();
|
||||
|
||||
const doc = editor.document;
|
||||
const range = detectReferenceListRange(doc);
|
||||
|
||||
if (!range) {
|
||||
await createReferenceList();
|
||||
} else {
|
||||
const refs = await generateReferenceList(doc);
|
||||
|
||||
// references must always be preceded by an empty line
|
||||
const spacing = doc.lineAt(range.start.line - 1).isEmptyOrWhitespace
|
||||
? ""
|
||||
: docConfig.eol;
|
||||
|
||||
await editor.edit((editBuilder) => {
|
||||
editBuilder.replace(range, spacing + refs.join(docConfig.eol));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function generateReferenceList(doc: TextDocument): Promise<string[]> {
|
||||
const filePath = doc.fileName;
|
||||
|
||||
const id = dropExtension(basename(filePath));
|
||||
|
||||
// @todo fix hack
|
||||
await ws.ready;
|
||||
|
||||
// update file in index for future reference
|
||||
// @todo should probably be an update method instead
|
||||
// so we can prune existing references
|
||||
ws.manager.addNoteFromMarkdown(filePath, doc.getText());
|
||||
|
||||
// find note by id
|
||||
const note = ws.manager.getNoteWithLinks(id);
|
||||
|
||||
if (note.linkedNotes.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const references = [];
|
||||
|
||||
for (const link of note.linkedNotes) {
|
||||
const relativePath = relative(dirname(filePath), link.absolutePath);
|
||||
if (relativePath) {
|
||||
const relativePathWithoutExtension = dropExtension(relativePath);
|
||||
|
||||
// [wiki-link-text]: wiki-link "Page title"
|
||||
references.push(
|
||||
`[${link.id}]: ${relativePathWithoutExtension} "${link.title}"`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
references.sort()
|
||||
|
||||
// for (const backlink of note.backlinks) {
|
||||
// references.push(
|
||||
// `[backlink:${backlink.id}]: ${backlink.filename} "${backlink.title}"`
|
||||
// );
|
||||
// }
|
||||
|
||||
return [REFERENCE_HEADER, ...references, REFERENCE_FOOTER];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the range of existing reference list
|
||||
* @param doc
|
||||
*/
|
||||
function detectReferenceListRange(doc: TextDocument): Range {
|
||||
const fullText = doc.getText();
|
||||
|
||||
const headerIndex = fullText.indexOf(REFERENCE_HEADER);
|
||||
const footerIndex = fullText.lastIndexOf(REFERENCE_FOOTER);
|
||||
|
||||
if (headerIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const headerLine =
|
||||
fullText.substring(0, headerIndex).split(docConfig.eol).length - 1;
|
||||
|
||||
const footerLine =
|
||||
fullText.substring(0, footerIndex).split(docConfig.eol).length - 1;
|
||||
|
||||
if (headerLine >= footerLine) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Range(
|
||||
new Position(headerLine, 0),
|
||||
new Position(footerLine, REFERENCE_FOOTER.length)
|
||||
);
|
||||
}
|
||||
|
||||
function onWillSave(e: TextDocumentWillSaveEvent) {
|
||||
if (e.document.languageId === "markdown") {
|
||||
e.waitUntil(updateReferenceList());
|
||||
}
|
||||
}
|
||||
|
||||
function hasEmptyTrailing(doc: TextDocument): boolean {
|
||||
return doc.lineAt(doc.lineCount - 1).isEmptyOrWhitespace;
|
||||
}
|
||||
|
||||
function getText(range: Range): string {
|
||||
return window.activeTextEditor.document.getText(range);
|
||||
}
|
||||
|
||||
function isMdEditor(editor: TextEditor) {
|
||||
return editor && editor.document && editor.document.languageId === "markdown";
|
||||
}
|
||||
|
||||
function dropExtension(path: string): string {
|
||||
const parts = path.split(".");
|
||||
parts.pop();
|
||||
return parts.join(".");
|
||||
}
|
||||
|
||||
class WikilinkReferenceCodeLensProvider implements CodeLensProvider {
|
||||
public provideCodeLenses(
|
||||
document: TextDocument,
|
||||
_: CancellationToken
|
||||
): CodeLens[] | Thenable<CodeLens[]> {
|
||||
loadDocConfig();
|
||||
|
||||
let range = detectReferenceListRange(document);
|
||||
if (!range) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return generateReferenceList(document).then((refs) => {
|
||||
const oldRefs = getText(range).replace(/\r?\n|\r/g, docConfig.eol);
|
||||
const newRefs = refs.join(docConfig.eol);
|
||||
|
||||
let status = oldRefs === newRefs ? "up to date" : "out of date";
|
||||
|
||||
return [
|
||||
new CodeLens(range, {
|
||||
arguments: [],
|
||||
title: `Link references (${status})`,
|
||||
command: "",
|
||||
}),
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
const getConfig = () => {
|
||||
return {};
|
||||
};
|
||||
|
||||
9
packages/foam-vscode/src/features/index.ts
Normal file
9
packages/foam-vscode/src/features/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
import createReferences from './wikilink-reference-generation';
|
||||
import createWikiDocumentLinkProvider from './wiki-document-link-provider';
|
||||
import { FoamFeature } from '../types'
|
||||
|
||||
export const features: FoamFeature[] = [
|
||||
createReferences,
|
||||
createWikiDocumentLinkProvider
|
||||
];
|
||||
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
DocumentLink,
|
||||
DocumentLinkProvider,
|
||||
ExtensionContext,
|
||||
languages,
|
||||
Range,
|
||||
TextDocument,
|
||||
Uri,
|
||||
} from 'vscode';
|
||||
import { Foam, NoteGraph, createNoteFromMarkdown } from 'foam-core';
|
||||
|
||||
export const LINK_SELECTOR = {
|
||||
scheme: 'file',
|
||||
language: 'markdown',
|
||||
};
|
||||
|
||||
export const LINK_PREFIX = '[[';
|
||||
export const LINK_SUFFIX = ']]';
|
||||
|
||||
const positionToLinkRange = (p) => {
|
||||
return new Range(
|
||||
p.start.line - 1,
|
||||
p.start.column + LINK_PREFIX.length - 1,
|
||||
p.end.line - 1,
|
||||
p.end.column - LINK_SUFFIX.length
|
||||
);
|
||||
};
|
||||
|
||||
class WikiDocumentLinkProvider implements DocumentLinkProvider {
|
||||
notes: NoteGraph;
|
||||
|
||||
constructor(_notes: NoteGraph) {
|
||||
this.notes = _notes;
|
||||
}
|
||||
|
||||
public provideDocumentLinks(
|
||||
document: TextDocument
|
||||
): DocumentLink[] | undefined {
|
||||
|
||||
const note = createNoteFromMarkdown(document.fileName, document.getText());
|
||||
|
||||
// update note in the graph
|
||||
this.notes.setNote(note);
|
||||
|
||||
return note.links.map((link, index) => {
|
||||
const p = link.position;
|
||||
|
||||
const to = this.notes.getNote(link.to);
|
||||
if (to) {
|
||||
const uri = Uri.parse(to.path);
|
||||
const linkRange = positionToLinkRange(link.position);
|
||||
const docLink = new DocumentLink(linkRange, uri);
|
||||
docLink.tooltip = to.title;
|
||||
return docLink;
|
||||
}
|
||||
|
||||
}).filter(Boolean);
|
||||
}
|
||||
}
|
||||
|
||||
const feature = {
|
||||
activate: async function createWikiDocumentLinkProvider(
|
||||
_context: ExtensionContext,
|
||||
foamPromise: Promise<Foam>
|
||||
) {
|
||||
languages.registerDocumentLinkProvider(
|
||||
LINK_SELECTOR,
|
||||
new WikiDocumentLinkProvider((await foamPromise).notes)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default feature;
|
||||
@@ -0,0 +1,183 @@
|
||||
import { uniq } from "lodash";
|
||||
import {
|
||||
CancellationToken,
|
||||
CodeLens,
|
||||
CodeLensProvider,
|
||||
commands,
|
||||
ExtensionContext,
|
||||
languages,
|
||||
Range,
|
||||
TextDocument,
|
||||
window,
|
||||
workspace,
|
||||
Position
|
||||
} from "vscode";
|
||||
|
||||
import { createMarkdownReferences, createNoteFromMarkdown, NoteGraph, Foam } from "foam-core";
|
||||
import { basename } from "path";
|
||||
import { hasEmptyTrailing, docConfig, loadDocConfig, isMdEditor, mdDocSelector, getText, dropExtension } from "../utils";
|
||||
import { FoamFeature } from "../types";
|
||||
|
||||
const feature: FoamFeature = {
|
||||
activate: async (context: ExtensionContext, foamPromise: Promise<Foam>) => {
|
||||
const foam = await foamPromise;
|
||||
context.subscriptions.push(
|
||||
commands.registerCommand(
|
||||
"foam-vscode.update-wikilinks",
|
||||
() => updateReferenceList(foam.notes)
|
||||
),
|
||||
workspace.onWillSaveTextDocument(e => {
|
||||
if (e.document.languageId === "markdown") {
|
||||
foam.notes.setNote(
|
||||
createNoteFromMarkdown(e.document.fileName, e.document.getText())
|
||||
);
|
||||
e.waitUntil(updateReferenceList(foam.notes));
|
||||
}
|
||||
}),
|
||||
languages.registerCodeLensProvider(
|
||||
mdDocSelector,
|
||||
new WikilinkReferenceCodeLensProvider(foam.notes)
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const REFERENCE_HEADER = `[//begin]: # "Autogenerated link references for markdown compatibility"`;
|
||||
const REFERENCE_FOOTER = `[//end]: # "Autogenerated link references"`;
|
||||
|
||||
async function createReferenceList(foam: NoteGraph) {
|
||||
let editor = window.activeTextEditor;
|
||||
if (!editor || !isMdEditor(editor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let refs = await generateReferenceList(foam, editor.document);
|
||||
if (refs && refs.length) {
|
||||
await editor.edit(function(editBuilder) {
|
||||
if (editor) {
|
||||
const spacing = hasEmptyTrailing
|
||||
? docConfig.eol
|
||||
: docConfig.eol + docConfig.eol;
|
||||
|
||||
editBuilder.insert(
|
||||
new Position(editor.document.lineCount, 0),
|
||||
spacing + refs.join(docConfig.eol)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function updateReferenceList(foam: NoteGraph) {
|
||||
const editor = window.activeTextEditor;
|
||||
|
||||
if (!editor || !isMdEditor(editor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadDocConfig();
|
||||
|
||||
const doc = editor.document;
|
||||
const range = detectReferenceListRange(doc);
|
||||
|
||||
if (!range) {
|
||||
await createReferenceList(foam);
|
||||
} else {
|
||||
const refs = await generateReferenceList(foam, doc);
|
||||
|
||||
// references must always be preceded by an empty line
|
||||
const spacing = doc.lineAt(range.start.line - 1).isEmptyOrWhitespace
|
||||
? ""
|
||||
: docConfig.eol;
|
||||
|
||||
await editor.edit(editBuilder => {
|
||||
editBuilder.replace(range, spacing + refs.join(docConfig.eol));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function generateReferenceList(foam: NoteGraph, doc: TextDocument): Promise<string[]> {
|
||||
const filePath = doc.fileName;
|
||||
|
||||
const id = dropExtension(basename(filePath));
|
||||
|
||||
const references = uniq(
|
||||
createMarkdownReferences(foam, id).map(
|
||||
link => `[${link.linkText}]: ${link.wikiLink} "${link.pageTitle}"`
|
||||
)
|
||||
);
|
||||
|
||||
if (references.length) {
|
||||
return [REFERENCE_HEADER, ...references, REFERENCE_FOOTER];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the range of existing reference list
|
||||
* @param doc
|
||||
*/
|
||||
function detectReferenceListRange(doc: TextDocument): Range {
|
||||
const fullText = doc.getText();
|
||||
|
||||
const headerIndex = fullText.indexOf(REFERENCE_HEADER);
|
||||
const footerIndex = fullText.lastIndexOf(REFERENCE_FOOTER);
|
||||
|
||||
if (headerIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const headerLine =
|
||||
fullText.substring(0, headerIndex).split(docConfig.eol).length - 1;
|
||||
|
||||
const footerLine =
|
||||
fullText.substring(0, footerIndex).split(docConfig.eol).length - 1;
|
||||
|
||||
if (headerLine >= footerLine) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Range(
|
||||
new Position(headerLine, 0),
|
||||
new Position(footerLine, REFERENCE_FOOTER.length)
|
||||
);
|
||||
}
|
||||
|
||||
class WikilinkReferenceCodeLensProvider implements CodeLensProvider {
|
||||
private foam: NoteGraph
|
||||
|
||||
constructor(foam: NoteGraph) {
|
||||
this.foam = foam
|
||||
}
|
||||
|
||||
public provideCodeLenses(
|
||||
document: TextDocument,
|
||||
_: CancellationToken
|
||||
): CodeLens[] | Thenable<CodeLens[]> {
|
||||
loadDocConfig();
|
||||
|
||||
let range = detectReferenceListRange(document);
|
||||
if (!range) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return generateReferenceList(this.foam, document).then(refs => {
|
||||
const oldRefs = getText(range).replace(/\r?\n|\r/g, docConfig.eol);
|
||||
const newRefs = refs.join(docConfig.eol);
|
||||
|
||||
let status = oldRefs === newRefs ? "up to date" : "out of date";
|
||||
|
||||
return [
|
||||
new CodeLens(range, {
|
||||
arguments: [],
|
||||
title: `Link references (${status})`,
|
||||
command: ""
|
||||
})
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default feature;
|
||||
6
packages/foam-vscode/src/types.d.ts
vendored
Normal file
6
packages/foam-vscode/src/types.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ExtensionContext } from "vscode";
|
||||
import { Foam } from "foam-core";
|
||||
|
||||
export interface FoamFeature {
|
||||
activate: (context: ExtensionContext, foamPromise: Promise<Foam>) => void
|
||||
}
|
||||
71
packages/foam-vscode/src/utils.ts
Normal file
71
packages/foam-vscode/src/utils.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { EndOfLine, Range, TextDocument, window, Position, TextEditor } from "vscode";
|
||||
|
||||
export const docConfig = { tab: " ", eol: "\r\n" };
|
||||
|
||||
export const mdDocSelector = [
|
||||
{ language: "markdown", scheme: "file" },
|
||||
{ language: "markdown", scheme: "untitled" }
|
||||
];
|
||||
|
||||
export function loadDocConfig() {
|
||||
// Load workspace config
|
||||
let activeEditor = window.activeTextEditor;
|
||||
if (!activeEditor) {
|
||||
console.log("Failed to load config, no active editor");
|
||||
return;
|
||||
}
|
||||
|
||||
docConfig.eol = activeEditor.document.eol === EndOfLine.CRLF ? "\r\n" : "\n";
|
||||
|
||||
let tabSize = Number(activeEditor.options.tabSize);
|
||||
let insertSpaces = activeEditor.options.insertSpaces;
|
||||
if (insertSpaces) {
|
||||
docConfig.tab = " ".repeat(tabSize);
|
||||
} else {
|
||||
docConfig.tab = "\t";
|
||||
}
|
||||
}
|
||||
|
||||
export function isMdEditor(editor: TextEditor) {
|
||||
return editor && editor.document && editor.document.languageId === "markdown";
|
||||
}
|
||||
|
||||
export function detectGeneratedCode(
|
||||
fullText: string,
|
||||
header: string,
|
||||
footer: string
|
||||
): { range: Range | null; lines: string[] } {
|
||||
const lines = fullText.split(docConfig.eol);
|
||||
|
||||
const headerLine = lines.findIndex(line => line === header);
|
||||
const footerLine = lines.findIndex(line => line === footer);
|
||||
|
||||
if (headerLine < 0 || headerLine >= footerLine) {
|
||||
return {
|
||||
range: null,
|
||||
lines: []
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
range: new Range(
|
||||
new Position(headerLine, 0),
|
||||
new Position(footerLine, lines[footerLine].length + 1)
|
||||
),
|
||||
lines: lines.slice(headerLine + 1, footerLine + 1)
|
||||
};
|
||||
}
|
||||
|
||||
export function hasEmptyTrailing(doc: TextDocument): boolean {
|
||||
return doc.lineAt(doc.lineCount - 1).isEmptyOrWhitespace;
|
||||
}
|
||||
|
||||
export function getText(range: Range): string {
|
||||
return window.activeTextEditor.document.getText(range);
|
||||
}
|
||||
|
||||
export function dropExtension(path: string): string {
|
||||
const parts = path.split(".");
|
||||
parts.pop();
|
||||
return parts.join(".");
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import * as fs from "fs";
|
||||
import { basename } from "path";
|
||||
import { workspace } from "vscode";
|
||||
import { WorkspaceManager } from "foam-workspace-manager";
|
||||
|
||||
// build initial index
|
||||
export const manager = new WorkspaceManager(workspace.rootPath);
|
||||
export const ready = (async () => {
|
||||
const files = await workspace.findFiles("**/*");
|
||||
await Promise.all(
|
||||
files
|
||||
.filter(
|
||||
(f) => f.scheme === "file" && f.path.match(/\.(md|mdx|markdown)/i)
|
||||
)
|
||||
.map((f) => {
|
||||
return fs.promises.readFile(f.fsPath).then((data) => {
|
||||
let markdown = (data || "").toString();
|
||||
manager.addNoteFromMarkdown(f.fsPath, markdown);
|
||||
});
|
||||
})
|
||||
);
|
||||
})();
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "es6",
|
||||
"outDir": "out",
|
||||
"lib": ["es6"],
|
||||
@@ -12,5 +14,9 @@
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
},
|
||||
"exclude": ["node_modules", ".vscode-test"]
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", ".vscode-test"],
|
||||
"references": [{
|
||||
"path": "../foam-core/tsconfig.json"
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
name: CI
|
||||
on: [push]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Begin CI...
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node 12
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
|
||||
- name: Use cached node_modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: node_modules
|
||||
key: nodeModules-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
nodeModules-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: Lint
|
||||
run: yarn lint
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: Test
|
||||
run: yarn test --ci --coverage --maxWorkers=2
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: Build
|
||||
run: yarn build
|
||||
env:
|
||||
CI: true
|
||||
4
packages/foam-workspace-manager/.gitignore
vendored
4
packages/foam-workspace-manager/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
*.log
|
||||
.DS_Store
|
||||
node_modules
|
||||
dist
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Jani Eväkallio
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,179 +0,0 @@
|
||||
// for readability
|
||||
import { basename } from 'path';
|
||||
import {
|
||||
readWorkspaceFile,
|
||||
parseNoteTitleFromMarkdown,
|
||||
parseNoteLinksFromMarkdown,
|
||||
} from './utils/utils';
|
||||
|
||||
type ID = string;
|
||||
type Index = Map<ID, Set<ID>>;
|
||||
|
||||
export interface Note {
|
||||
/**
|
||||
* Base name of the file without extension, e.g. wiki-link
|
||||
*/
|
||||
id: ID;
|
||||
title: string;
|
||||
filename: string;
|
||||
extension: string;
|
||||
absolutePath: string;
|
||||
markdown: string; // do we need this?
|
||||
}
|
||||
|
||||
export interface NoteWithLinks extends Note {
|
||||
/**
|
||||
* Notes referenced from this note (wikilinks)
|
||||
*/
|
||||
linkedNotes: Note[];
|
||||
|
||||
/**
|
||||
* Notes that reference this note (backlinks) */
|
||||
backlinks: Note[];
|
||||
}
|
||||
|
||||
function getOrInitializeIndexForId(index: Index, id: ID): Set<ID> {
|
||||
let links: Set<ID>;
|
||||
if (index.has(id)) {
|
||||
links = index.get(id)!;
|
||||
} else {
|
||||
index.set(id, (links = new Set<ID>()));
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
export class WorkspaceManager {
|
||||
/**
|
||||
* Workspace base path
|
||||
*/
|
||||
path: string;
|
||||
/**
|
||||
* Note metadata for files in this workspace, keyed by id
|
||||
*/
|
||||
notes: Map<ID, Note> = new Map();
|
||||
|
||||
/**
|
||||
* Link index A->B
|
||||
*/
|
||||
linksFromNoteById: Index = new Map();
|
||||
|
||||
/**
|
||||
* Reverse backlinks B->A
|
||||
*/
|
||||
linksBackToNoteById: Index = new Map();
|
||||
|
||||
constructor(path: string, notes: Note[] = []) {
|
||||
this.path = path;
|
||||
this.notes = new Map<ID, Note>(notes.map(note => [note.id, note]));
|
||||
}
|
||||
|
||||
public getNoteWithLinks(id: ID): NoteWithLinks | null {
|
||||
const note = this.notes.get(id);
|
||||
if (!note) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const linkedNotes = Array.from(
|
||||
getOrInitializeIndexForId(this.linksFromNoteById, id)
|
||||
)
|
||||
.map(id => this.notes.get(id))
|
||||
.filter(Boolean) as Note[];
|
||||
|
||||
const backlinks = Array.from(
|
||||
getOrInitializeIndexForId(this.linksBackToNoteById, id)
|
||||
)
|
||||
.map(id => this.notes.get(id))
|
||||
.filter(Boolean) as Note[];
|
||||
|
||||
return {
|
||||
...note,
|
||||
linkedNotes,
|
||||
backlinks,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param filename File name relative to workspace path
|
||||
*/
|
||||
public async addNoteByFilePath(filePath: string): Promise<Note> {
|
||||
return await this.addNoteFromMarkdown(
|
||||
this.path,
|
||||
await readWorkspaceFile(filePath)
|
||||
);
|
||||
}
|
||||
|
||||
public addNoteFromMarkdown(absolutePath: string, markdown: string): Note {
|
||||
// parse markdown
|
||||
const filename = basename(absolutePath);
|
||||
const parts = filename.split('.');
|
||||
const extension = parts.pop()!;
|
||||
const id = parts.join('.');
|
||||
const title = parseNoteTitleFromMarkdown(markdown);
|
||||
const note: Note = {
|
||||
id,
|
||||
title: title || id,
|
||||
filename,
|
||||
absolutePath,
|
||||
extension,
|
||||
markdown,
|
||||
};
|
||||
|
||||
// extract linksTo
|
||||
return this.addNote(note);
|
||||
}
|
||||
|
||||
public addNote(note: Note): Note {
|
||||
const linkIds = parseNoteLinksFromMarkdown(note.markdown);
|
||||
|
||||
this.notes.set(note.id, note);
|
||||
|
||||
if (linkIds.length > 0) {
|
||||
let linksFromNote = getOrInitializeIndexForId(
|
||||
this.linksFromNoteById,
|
||||
note.id
|
||||
);
|
||||
|
||||
for (const id of linkIds) {
|
||||
linksFromNote.add(id);
|
||||
getOrInitializeIndexForId(this.linksBackToNoteById, id).add(note.id);
|
||||
}
|
||||
}
|
||||
|
||||
return note;
|
||||
}
|
||||
|
||||
/*
|
||||
// Clearly I'm too tired to do this right now
|
||||
public removeNote(a: ID): Note | null {
|
||||
let note = this.notes.get(a);
|
||||
if (!note) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// find references from this note to others
|
||||
let linksFromNote = getOrInitializeIndexForId(this.linksFromNoteById, a);
|
||||
|
||||
// remove the index
|
||||
this.linksFromNoteById.delete(a);
|
||||
|
||||
// find all notes that reference the note we are deleting
|
||||
for (const b in linksFromNote) {
|
||||
const backlinks = getOrInitializeIndexForId(this.linksBackToNoteById, b);
|
||||
if (backlinks.has(a)) {
|
||||
// @todo, trigger event?
|
||||
backlinks.delete(a);
|
||||
}
|
||||
}
|
||||
|
||||
return note;
|
||||
}
|
||||
|
||||
|
||||
// @ts-expect-error
|
||||
public renameNote(note: Note, newFilename: string) {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { WorkspaceManager, Note, NoteWithLinks } from './WorkspaceManager';
|
||||
@@ -1,63 +0,0 @@
|
||||
/**
|
||||
* Adapted from vscode-markdown/src/util.ts
|
||||
* https://github.com/yzhang-gh/vscode-markdown/blob/master/src/util.ts
|
||||
*/
|
||||
|
||||
export const REGEX_FENCED_CODE_BLOCK = /^( {0,3}|\t)```[^`\r\n]*$[\w\W]+?^( {0,3}|\t)``` *$/gm;
|
||||
|
||||
export function markdownHeadingToPlainText(text: string) {
|
||||
// Remove Markdown syntax (bold, italic, links etc.) in a heading
|
||||
// For example: `_italic_` -> `italic`
|
||||
return text.replace(/\[([^\]]*)\]\[[^\]]*\]/, (_, g1) => g1);
|
||||
}
|
||||
|
||||
export function rxWikiLink(): RegExp {
|
||||
const pattern = '\\[\\[([^\\]]+)\\]\\]'; // [[wiki-link-regex]]
|
||||
return new RegExp(pattern, 'ig');
|
||||
}
|
||||
|
||||
export function rxMarkdownHeading(level: number): RegExp {
|
||||
const pattern = `^#{${level}}\\s+(.+)$`;
|
||||
return new RegExp(pattern, 'im');
|
||||
}
|
||||
|
||||
export const mdDocSelector = [
|
||||
{ language: 'markdown', scheme: 'file' },
|
||||
{ language: 'markdown', scheme: 'untitled' },
|
||||
];
|
||||
|
||||
export function findTopLevelHeading(md: string): string | null {
|
||||
const regex = rxMarkdownHeading(1);
|
||||
const match = regex.exec(md);
|
||||
if (match) {
|
||||
return markdownHeadingToPlainText(match[1]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function cleanupMarkdown(markdown: string) {
|
||||
const replacer = (foundStr: string) => foundStr.replace(/[^\r\n]/g, '');
|
||||
return markdown
|
||||
.replace(REGEX_FENCED_CODE_BLOCK, replacer) //// Remove fenced code blocks (and #603, #675)
|
||||
.replace(/<!-- omit in (toc|TOC) -->/g, '< omit in toc >') //// Escape magic comment
|
||||
.replace(/<!--[\W\w]+?-->/g, replacer) //// Remove comments
|
||||
.replace(/^---[\W\w]+?(\r?\n)---/, replacer); //// Remove YAML front matter
|
||||
}
|
||||
|
||||
export function findWikilinksInMarkdown(markdown: string): string[] {
|
||||
const md = cleanupMarkdown(markdown);
|
||||
const regex = rxWikiLink();
|
||||
const unique = new Set<string>();
|
||||
|
||||
let match;
|
||||
while ((match = regex.exec(md))) {
|
||||
// can be file-name or file.name.ext
|
||||
const [, name] = match;
|
||||
if (name) {
|
||||
unique.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(unique);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// @todo convert this to use ast parsing
|
||||
|
||||
// import unified from 'unified';
|
||||
// import markdown from 'remark-parse';
|
||||
// import wikiLinkPlugin from 'remark-wiki-link';
|
||||
// let processor = unified()
|
||||
// .use(markdown, { gfm: true })
|
||||
// .use(wikiLinkPlugin);
|
||||
|
||||
import { findTopLevelHeading, findWikilinksInMarkdown } from './markdown-utils';
|
||||
|
||||
// @ts-expect-error
|
||||
export function readWorkspaceFile(filename: string): string {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
export function parseNoteTitleFromMarkdown(markdown: string): string | null {
|
||||
return findTopLevelHeading(markdown);
|
||||
}
|
||||
|
||||
export function parseNoteLinksFromMarkdown(markdown: string): string[] {
|
||||
return findWikilinksInMarkdown(markdown);
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
import { WorkspaceManager } from '../src/WorkspaceManager';
|
||||
|
||||
const pageA = `
|
||||
# Page A
|
||||
|
||||
## Section
|
||||
- [[page-b]]
|
||||
- [[page-c]];
|
||||
`;
|
||||
|
||||
const pageB = `
|
||||
# Page B
|
||||
|
||||
This references [[page-a]]`;
|
||||
|
||||
const pageC = `
|
||||
# Page C
|
||||
`;
|
||||
|
||||
describe('WorkspaceManager', () => {
|
||||
it('links things correctly when added in order', () => {
|
||||
const ws = new WorkspaceManager('dir/');
|
||||
|
||||
ws.addNoteFromMarkdown('page-a.md', pageA);
|
||||
ws.addNoteFromMarkdown('page-b.md', pageB);
|
||||
ws.addNoteFromMarkdown('page-c.md', pageC);
|
||||
|
||||
const note = ws.getNoteWithLinks('page-a');
|
||||
expect(note).not.toBeNull();
|
||||
expect(note!.linkedNotes.map(n => n.id)).toEqual(['page-b', 'page-c']);
|
||||
expect(note!.backlinks.map(n => n.id)).toEqual(['page-b']);
|
||||
});
|
||||
|
||||
it('links things correctly when added out of order', () => {
|
||||
const ws = new WorkspaceManager('dir/');
|
||||
|
||||
ws.addNoteFromMarkdown('page-b.md', pageB);
|
||||
ws.addNoteFromMarkdown('page-a.md', pageA);
|
||||
ws.addNoteFromMarkdown('page-c.md', pageC);
|
||||
|
||||
const note = ws.getNoteWithLinks('page-a');
|
||||
|
||||
expect(note).not.toBeNull();
|
||||
expect(note!.linkedNotes.map(n => n.id)).toEqual(['page-b', 'page-c']);
|
||||
expect(note!.backlinks.map(n => n.id)).toEqual(['page-b']);
|
||||
});
|
||||
|
||||
it('updates links when adding a changed document', () => {
|
||||
const ws = new WorkspaceManager('dir/');
|
||||
|
||||
ws.addNoteFromMarkdown('page-b.md', pageB);
|
||||
ws.addNoteFromMarkdown('page-a.md', pageA);
|
||||
ws.addNoteFromMarkdown('page-c.md', pageC);
|
||||
|
||||
const before = ws.getNoteWithLinks('page-a');
|
||||
|
||||
// change document
|
||||
ws.addNoteFromMarkdown(
|
||||
'page-c.md',
|
||||
`
|
||||
# Page C
|
||||
[[page-a]]
|
||||
[[page-b]]
|
||||
`
|
||||
);
|
||||
|
||||
const after = ws.getNoteWithLinks('page-a');
|
||||
|
||||
expect(before).not.toEqual(after);
|
||||
expect(before!.linkedNotes.map(n => n.id)).toEqual(['page-b', 'page-c']);
|
||||
expect(before!.backlinks.map(n => n.id)).toEqual(['page-b']);
|
||||
|
||||
expect(after!.linkedNotes.map(n => n.id)).toEqual(['page-b', 'page-c']);
|
||||
expect(after!.backlinks.map(n => n.id)).toEqual(['page-b', 'page-c']);
|
||||
});
|
||||
|
||||
/*
|
||||
it('updates links correctly when page is removed', () => {
|
||||
const ws = new WorkspaceManager('dir/');
|
||||
|
||||
ws.addNoteFromMarkdown('page-a.md', pageA);
|
||||
ws.addNoteFromMarkdown('page-b.md', pageB);
|
||||
ws.addNoteFromMarkdown('page-c.md', pageC);
|
||||
|
||||
|
||||
ws.removeNote('page-c');
|
||||
|
||||
const note = ws.getNoteWithLinks('page-a');
|
||||
|
||||
console.log(note);
|
||||
expect(note).not.toBeNull();
|
||||
expect(note!.linkedNotes.map(n => n.id)).toEqual(['page-b']);
|
||||
expect(note!.backlinks.map(n => n.id)).toEqual(['page-b']);
|
||||
});
|
||||
*/
|
||||
});
|
||||
55
readme.md
55
readme.md
@@ -1,13 +1,15 @@
|
||||
<p class="github-only">
|
||||
👋 <b>Hello friend! Looks like you're reading this page on GitHub. Please go to the 👉<a href="https://foambubble.github.io/foam">rendered Foam Workspace</a> for an improved experience! </b>
|
||||
</p>
|
||||
👋 **Hello friend! Looks like you're reading this page on GitHub. Please go to the 👉[rendered Foam Workspace](https://foambubble.github.io/foam) for an improved experience!**
|
||||
|
||||
|
||||
👀*This is an early stage project under rapid development. For updates follow [@jevakallio](https://twitter.com/jevakallio) on Twitter, or join the [Foam community Discord](https://discord.gg/rtdZKgj)! 💬*
|
||||
|
||||
<p class="github-only">
|
||||
👀<i>This is an early stage project under rapid development. For updates follow <a href="https://twitter.com/jevakallio" target="_blank">@jevakallio</a> on Twitter, or join the <a href="https://discord.gg/rtdZKgj" target="_blank">Foam community Discord</a>! 💬</i>
|
||||
</p>
|
||||
|
||||
# Foam
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
**Foam** is a personal knowledge management and sharing system inspired by [Roam Research](https://roamresearch.com/), built on [Visual Studio Code](https://code.visualstudio.com/) and [GitHub](https://github.com/).
|
||||
|
||||
You can use **Foam** for organising your research, keeping re-discoverable notes, writing long-form content and, optionally, publishing it to the web.
|
||||
@@ -46,3 +48,44 @@ Foam is licensed under the [MIT license](license).
|
||||
[//begin]: # "Autogenerated link references for markdown compatibility"
|
||||
[wiki-links]: wiki-links "Wiki Links"
|
||||
[//end]: # "Autogenerated link references"
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://jevakallio.dev/"><img src="https://avatars1.githubusercontent.com/u/1203949?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jani Eväkallio</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://joeprevite.com/"><img src="https://avatars3.githubusercontent.com/u/3806031?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joe Previte</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/riccardoferretti"><img src="https://avatars3.githubusercontent.com/u/457005?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Riccardo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://ojanaho.com/"><img src="https://avatars0.githubusercontent.com/u/2180090?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Janne Ojanaho</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://bypaulshen.com/"><img src="https://avatars3.githubusercontent.com/u/2266187?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul Shen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=paulshen" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/coffenbacher"><img src="https://avatars0.githubusercontent.com/u/245867?v=4?s=60" width="60px;" alt=""/><br /><sub><b>coffenbacher</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=coffenbacher" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://mathieu.dutour.me/"><img src="https://avatars2.githubusercontent.com/u/3254314?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Mathieu Dutour</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=mathieudutour" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/presidentelect"><img src="https://avatars2.githubusercontent.com/u/1242300?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Michael Hansen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=presidentelect" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://klickverbot.at/"><img src="https://avatars1.githubusercontent.com/u/19335?v=4?s=60" width="60px;" alt=""/><br /><sub><b>David Nadlinger</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dnadlinger" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://pluckd.co/"><img src="https://avatars2.githubusercontent.com/u/20598571?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Fernando</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MrCordeiro" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/jfgonzalez7"><img src="https://avatars3.githubusercontent.com/u/58857736?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Juan Gonzalez</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jfgonzalez7" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://www.louiechristie.com/"><img src="https://avatars1.githubusercontent.com/u/6807448?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Louie Christie</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=louiechristie" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://supersandro.de/"><img src="https://avatars2.githubusercontent.com/u/7258858?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Sandro</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=SuperSandro2000" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/Skn0tt"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Skn0tt" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://styfle.dev/"><img src="https://avatars1.githubusercontent.com/u/229881?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Steven</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=styfle" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/Georift"><img src="https://avatars2.githubusercontent.com/u/859430?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Tim</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Georift" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/sauravkhdoolia"><img src="https://avatars1.githubusercontent.com/u/34188267?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Saurav Khdoolia</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=sauravkhdoolia" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://anku.netlify.com/"><img src="https://avatars1.githubusercontent.com/u/22813027?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ankit Tiwari</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=anku255" title="Documentation">📖</a> <a href="https://github.com/foambubble/foam/commits?author=anku255" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/ayushbaweja"><img src="https://avatars1.githubusercontent.com/u/44344063?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ayush Baweja</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ayushbaweja" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
13
tsconfig.base.json
Normal file
13
tsconfig.base.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"include": ["./packages/*/src"],
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"foam-core": ["./packages/foam-core/src"],
|
||||
"foam-cli": ["./packages/foam-cli/src"],
|
||||
"foam-vscode": ["./packages/foam-vscode/src"]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user